@moda-ai/cli 1.55.26 → 1.55.28
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/{agent-nqmha8ck.js → agent-0w9rtdsw.js} +22 -16
- package/dist/cli-3n0gz4d0.js +17971 -0
- package/dist/{cli-b4740tnv.js → cli-3retgh64.js} +85 -1165
- package/dist/{cli-1mbb0ct0.js → cli-97jew729.js} +1 -1
- package/dist/{cli-sqsjd08w.js → cli-99p2jh28.js} +105 -893
- package/dist/{cli-k6q8dbpk.js → cli-9bgk5jeh.js} +8 -6
- package/dist/{cli-q6f6vrrr.js → cli-9bgnvth2.js} +5 -3
- package/dist/cli-9kfxkxmk.js +6994 -0
- package/dist/cli-cesds766.js +1516 -0
- package/dist/{cli-cdrvjva5.js → cli-dr6x0zv7.js} +28 -6
- package/dist/{cli-jtyvbd8d.js → cli-e23g9ywa.js} +1 -1
- package/dist/cli-fwpqghgc.js +569 -0
- package/dist/cli-k4tqwah4.js +89 -0
- package/dist/{cli-8tfk8qeb.js → cli-mg5zv4s6.js} +18 -15
- package/dist/{cli-jgfm7w1r.js → cli-n2hng3gv.js} +9 -6
- package/dist/cli-ns5fcnkz.js +341 -0
- package/dist/{cli-5g1tv80h.js → cli-ny48a8de.js} +12 -9
- package/dist/{cli-d577kq6h.js → cli-qpq84eee.js} +5220 -32715
- package/dist/cli-xq45etg1.js +1098 -0
- package/dist/cli-yf39avhh.js +754 -0
- package/dist/cli-yz8xcpyk.js +12 -0
- package/dist/cli.js +355 -283
- package/dist/doctor-k3ta6h2y.js +24 -0
- package/dist/{fix-crkwdkt4.js → fix-w5z02wt4.js} +47 -50
- package/dist/{harness-x3e7d68b.js → harness-7azmzytc.js} +15 -7
- package/dist/{harness-github-actions-fjhvhgpj.js → harness-github-actions-bv2qrnqb.js} +11 -5
- package/dist/harness-report-rd9c4e9v.js +54 -0
- package/dist/{index-2kf3fnnb.js → index-d1z50dsq.js} +45 -36
- package/dist/{production-intelligence-7n83c701.js → production-intelligence-wvsgjxth.js} +21 -18
- package/dist/{profile-commands-ct2t0bfr.js → profile-commands-h8evz9x0.js} +9 -5
- package/dist/prompt-attribution-1nytc9wj.js +148 -0
- package/dist/prompt-source-mcp.js +14 -7
- package/dist/prompt-sources-07tczyqa.js +19 -0
- package/dist/{prompts-92aqqb2k.js → prompts-6y9wfq5z.js} +8 -7
- package/dist/prompts-ab-ezrd63sk.js +70 -0
- package/dist/prompts-propose-2k3enm2q.js +255 -0
- package/dist/{provision-gn8raqpt.js → provision-y5bdbh4y.js} +9 -6
- package/dist/{registry-ex162j5y.js → registry-96aze5wp.js} +7 -4
- package/dist/{skills-2mbsj9bt.js → skills-j8865009.js} +7 -5
- package/dist/{sync-kghmmmk5.js → sync-ew7yvy0e.js} +16 -10
- package/package.json +1 -1
- package/skills/integration/index.json +2 -2
- package/dist/cli-bb7133m7.js +0 -144
- package/dist/doctor-w913fj4g.js +0 -22
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createCliOutput,
|
|
3
3
|
outputModeFromFlags
|
|
4
|
-
} from "./cli-
|
|
4
|
+
} from "./cli-99p2jh28.js";
|
|
5
5
|
|
|
6
|
-
// src/
|
|
7
|
-
function flagEnabled(flags, key) {
|
|
8
|
-
return flags[key] === "true";
|
|
9
|
-
}
|
|
6
|
+
// src/boolean-words.ts
|
|
10
7
|
var BOOLEAN_TRUE_WORDS = new Set(["true", "yes", "1", "on"]);
|
|
11
8
|
var BOOLEAN_FALSE_WORDS = new Set(["false", "no", "0", "off"]);
|
|
12
9
|
function parseBooleanFlagValue(value) {
|
|
@@ -23,6 +20,31 @@ function parseBooleanFlagValue(value) {
|
|
|
23
20
|
return false;
|
|
24
21
|
return;
|
|
25
22
|
}
|
|
23
|
+
function flagValue(flags, name) {
|
|
24
|
+
return flags[name] ?? flags[name.replace(/-/g, "_")];
|
|
25
|
+
}
|
|
26
|
+
function withoutFlag(flags, name) {
|
|
27
|
+
const snake = name.replace(/-/g, "_");
|
|
28
|
+
return Object.fromEntries(Object.entries(flags).filter(([key]) => key !== name && key !== snake));
|
|
29
|
+
}
|
|
30
|
+
function optOutFlagSet(flags, name, strict = true) {
|
|
31
|
+
const raw = flagValue(flags, name);
|
|
32
|
+
if (raw === undefined)
|
|
33
|
+
return false;
|
|
34
|
+
const value = parseBooleanFlagValue(raw);
|
|
35
|
+
return strict ? value === true : value !== false;
|
|
36
|
+
}
|
|
37
|
+
function envSwitchedOff(value) {
|
|
38
|
+
return parseBooleanFlagValue(value) === false;
|
|
39
|
+
}
|
|
40
|
+
function envOptOutSet(value) {
|
|
41
|
+
return value !== undefined && value.trim() !== "" && parseBooleanFlagValue(value) !== false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/command-context.ts
|
|
45
|
+
function flagEnabled(flags, key) {
|
|
46
|
+
return flags[key] === "true";
|
|
47
|
+
}
|
|
26
48
|
function stringOption(value) {
|
|
27
49
|
if (value === undefined || value === null)
|
|
28
50
|
return;
|
|
@@ -77,4 +99,4 @@ function createCommandContext(input, options = {}) {
|
|
|
77
99
|
};
|
|
78
100
|
}
|
|
79
101
|
|
|
80
|
-
export { parseBooleanFlagValue, stringOption, createCommandContext };
|
|
102
|
+
export { parseBooleanFlagValue, withoutFlag, optOutFlagSet, envSwitchedOff, envOptOutSet, stringOption, createCommandContext };
|
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
import {
|
|
2
|
+
shellArg
|
|
3
|
+
} from "./cli-99p2jh28.js";
|
|
4
|
+
import {
|
|
5
|
+
CliInputError
|
|
6
|
+
} from "./cli-k4tqwah4.js";
|
|
7
|
+
|
|
8
|
+
// src/batch-reads.ts
|
|
9
|
+
import { readFile } from "node:fs/promises";
|
|
10
|
+
var BATCH_MAX_TARGETS = 25;
|
|
11
|
+
var BATCH_CONCURRENCY = 4;
|
|
12
|
+
var ANCHOR_SUFFIX = /^(.+)@(\d+)$/;
|
|
13
|
+
function parseBatchTarget(text, anchors, usage) {
|
|
14
|
+
const raw = text.trim();
|
|
15
|
+
if (raw.startsWith("{")) {
|
|
16
|
+
let parsed;
|
|
17
|
+
try {
|
|
18
|
+
parsed = JSON.parse(raw);
|
|
19
|
+
} catch {
|
|
20
|
+
throw new CliInputError(`Could not parse the JSON target ${JSON.stringify(raw)}.`, usage, "bad_batch_target");
|
|
21
|
+
}
|
|
22
|
+
const record = parsed;
|
|
23
|
+
const id = record?.id ?? record?.conversation_id ?? record?.trace_id;
|
|
24
|
+
if (typeof id !== "string" || id.trim() === "") {
|
|
25
|
+
throw new CliInputError(`JSON target ${JSON.stringify(raw)} has no "id" string.`, usage, "bad_batch_target");
|
|
26
|
+
}
|
|
27
|
+
const index = record.msg_index ?? record.message_index;
|
|
28
|
+
if (index !== undefined && index !== null) {
|
|
29
|
+
if (!anchors) {
|
|
30
|
+
throw new CliInputError(`JSON target ${JSON.stringify(raw)} has a msg_index, which this command does not take.`, usage, "bad_batch_target");
|
|
31
|
+
}
|
|
32
|
+
if (typeof index !== "number" || !Number.isSafeInteger(index) || index < 0) {
|
|
33
|
+
throw new CliInputError(`JSON target ${JSON.stringify(raw)} has an invalid msg_index (want an integer >= 0).`, usage, "bad_batch_target");
|
|
34
|
+
}
|
|
35
|
+
return { raw, id, msgIndex: index };
|
|
36
|
+
}
|
|
37
|
+
return { raw, id };
|
|
38
|
+
}
|
|
39
|
+
if (anchors) {
|
|
40
|
+
const match = ANCHOR_SUFFIX.exec(raw);
|
|
41
|
+
if (match) {
|
|
42
|
+
const msgIndex = Number(match[2]);
|
|
43
|
+
if (!Number.isSafeInteger(msgIndex)) {
|
|
44
|
+
throw new CliInputError(`Target ${JSON.stringify(raw)} has an @msg_index past ${Number.MAX_SAFE_INTEGER}. ` + 'If the id itself ends in @<digits>, pass it as {"id": "..."}.', usage, "bad_batch_target");
|
|
45
|
+
}
|
|
46
|
+
return { raw, id: match[1], msgIndex };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return { raw, id: raw };
|
|
50
|
+
}
|
|
51
|
+
function parseTargetLines(text, anchors, usage) {
|
|
52
|
+
return text.split(/\r?\n/).map((line) => line.trim()).filter((line) => line !== "").map((line) => parseBatchTarget(line, anchors, usage));
|
|
53
|
+
}
|
|
54
|
+
async function readStdinText() {
|
|
55
|
+
if (process.stdin.isTTY) {
|
|
56
|
+
throw new CliInputError("--ids-from=- reads targets from stdin, but stdin is a terminal. Pipe ids in (one per line) or pass a file path.", undefined, "ids_from_tty");
|
|
57
|
+
}
|
|
58
|
+
const chunks = [];
|
|
59
|
+
for await (const chunk of process.stdin)
|
|
60
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
61
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
62
|
+
}
|
|
63
|
+
async function readIdsFrom(source, usage) {
|
|
64
|
+
if (source === "" || source === "true") {
|
|
65
|
+
throw new CliInputError("--ids-from needs a value: - (stdin) or a file path.", usage, "missing_flag_value");
|
|
66
|
+
}
|
|
67
|
+
if (source === "-")
|
|
68
|
+
return readStdinText();
|
|
69
|
+
try {
|
|
70
|
+
return await readFile(source, "utf8");
|
|
71
|
+
} catch (error) {
|
|
72
|
+
const code = error.code;
|
|
73
|
+
throw new CliInputError(`Could not read --ids-from file ${JSON.stringify(source)}${typeof code === "string" ? ` (${code})` : ""}.`, usage, "ids_from_unreadable");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function collectBatchTargets(positionals, idsFrom, options) {
|
|
77
|
+
const targets = positionals.map((value) => parseBatchTarget(value, options.anchors, options.usage));
|
|
78
|
+
if (idsFrom !== undefined) {
|
|
79
|
+
targets.push(...parseTargetLines(await readIdsFrom(idsFrom, options.usage), options.anchors, options.usage));
|
|
80
|
+
}
|
|
81
|
+
const seen = new Set;
|
|
82
|
+
const unique = targets.filter((target) => {
|
|
83
|
+
const key = `${target.id}\x00${target.msgIndex ?? ""}`;
|
|
84
|
+
if (seen.has(key))
|
|
85
|
+
return false;
|
|
86
|
+
seen.add(key);
|
|
87
|
+
return true;
|
|
88
|
+
});
|
|
89
|
+
if (unique.length === 0) {
|
|
90
|
+
throw new CliInputError(`No targets: --ids-from gave no ids (one per line).`, options.usage, "missing_positional");
|
|
91
|
+
}
|
|
92
|
+
if (unique.length > BATCH_MAX_TARGETS) {
|
|
93
|
+
throw new CliInputError(`Got ${unique.length} targets; 'moda ${options.command}' reads at most ${BATCH_MAX_TARGETS} per call, so nothing was read. ` + `Split the ids into calls of ${BATCH_MAX_TARGETS} or fewer.`, options.usage, "batch_too_large");
|
|
94
|
+
}
|
|
95
|
+
return unique;
|
|
96
|
+
}
|
|
97
|
+
async function mapWithConcurrency(items, limit, fn, options = {}) {
|
|
98
|
+
const results = new Array(items.length);
|
|
99
|
+
let next = 0;
|
|
100
|
+
let failure;
|
|
101
|
+
const worker = async () => {
|
|
102
|
+
while (!failure && next < items.length && (options.shouldStart?.(next) ?? true)) {
|
|
103
|
+
const index = next++;
|
|
104
|
+
try {
|
|
105
|
+
results[index] = await fn(items[index], index);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
failure ??= { error };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
await Promise.all(Array.from({ length: Math.max(1, Math.min(limit, items.length)) }, worker));
|
|
112
|
+
if (failure)
|
|
113
|
+
throw failure.error;
|
|
114
|
+
return next < items.length ? results.slice(0, next) : results;
|
|
115
|
+
}
|
|
116
|
+
function idNeedsJsonTarget(id) {
|
|
117
|
+
return ANCHOR_SUFFIX.test(id);
|
|
118
|
+
}
|
|
119
|
+
function contextTarget(id) {
|
|
120
|
+
const verbatim = id === id.trim() && !id.startsWith("{") && !idNeedsJsonTarget(id);
|
|
121
|
+
return verbatim ? id : JSON.stringify({ id });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// src/transforms.ts
|
|
125
|
+
function asRecord(value) {
|
|
126
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
function asString(value) {
|
|
132
|
+
return typeof value === "string" ? value : null;
|
|
133
|
+
}
|
|
134
|
+
function asNumber(value) {
|
|
135
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
136
|
+
}
|
|
137
|
+
function frustrationAnchorIndex(row) {
|
|
138
|
+
if (!row)
|
|
139
|
+
return null;
|
|
140
|
+
const userQuotes = Array.isArray(row.user_quotes) ? row.user_quotes : [];
|
|
141
|
+
const firstQuote = asRecord(userQuotes[0]);
|
|
142
|
+
const resolved = asNumber(firstQuote?.msg_index) ?? asNumber(row.anchor_msg_index);
|
|
143
|
+
if (resolved !== null)
|
|
144
|
+
return resolved;
|
|
145
|
+
const keyTurns = Array.isArray(row.key_turns) ? row.key_turns : [];
|
|
146
|
+
const firstTurn = keyTurns.find((t) => typeof t === "number");
|
|
147
|
+
if (firstTurn !== undefined)
|
|
148
|
+
return firstTurn;
|
|
149
|
+
return asNumber(firstQuote?.turn);
|
|
150
|
+
}
|
|
151
|
+
function buildFrustrationAnchor(row) {
|
|
152
|
+
const conversation_id = asString(row.conversation_id) ?? "";
|
|
153
|
+
const userQuotes = Array.isArray(row.user_quotes) ? row.user_quotes : [];
|
|
154
|
+
const firstQuote = asRecord(userQuotes[0]);
|
|
155
|
+
const keyTurnsRaw = Array.isArray(row.key_turns) ? row.key_turns : [];
|
|
156
|
+
const all_turns = keyTurnsRaw.filter((t) => typeof t === "number");
|
|
157
|
+
const msg_index = frustrationAnchorIndex(row);
|
|
158
|
+
let signal = null;
|
|
159
|
+
let quote_preview = null;
|
|
160
|
+
if (firstQuote) {
|
|
161
|
+
signal = asString(firstQuote.signal);
|
|
162
|
+
const quote = asString(firstQuote.quote);
|
|
163
|
+
if (quote !== null) {
|
|
164
|
+
let end = 0;
|
|
165
|
+
let points = 0;
|
|
166
|
+
for (const ch of quote) {
|
|
167
|
+
if (points++ === 200)
|
|
168
|
+
break;
|
|
169
|
+
end += ch.length;
|
|
170
|
+
}
|
|
171
|
+
quote_preview = quote.slice(0, end);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
kind: "frustration",
|
|
176
|
+
conversation_id,
|
|
177
|
+
msg_index,
|
|
178
|
+
signal,
|
|
179
|
+
quote_preview,
|
|
180
|
+
all_turns,
|
|
181
|
+
no_anchor: msg_index === null
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function buildToolFailureAnchor(row, toolName) {
|
|
185
|
+
const conversation_id = asString(row.conversation_id) ?? "";
|
|
186
|
+
const msg_index = asNumber(row.msg_index);
|
|
187
|
+
const tool_use_id = asString(row.tool_use_id);
|
|
188
|
+
const error_subtype = asString(row.error_subtype);
|
|
189
|
+
return {
|
|
190
|
+
kind: "tool_failure",
|
|
191
|
+
conversation_id,
|
|
192
|
+
msg_index,
|
|
193
|
+
tool_name: toolName,
|
|
194
|
+
tool_use_id,
|
|
195
|
+
error_subtype,
|
|
196
|
+
no_anchor: msg_index === null
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
function frustrationsFromEmotions(response) {
|
|
200
|
+
if (!Array.isArray(response.detections) || Array.isArray(response.frustrations))
|
|
201
|
+
return response;
|
|
202
|
+
const summary = asRecord(response.summary) ?? {};
|
|
203
|
+
const pagination = asRecord(response.pagination) ?? {};
|
|
204
|
+
const byFamily = asRecord(summary.by_family) ?? {};
|
|
205
|
+
const totalAnalyzed = asNumber(summary.conversations_analyzed) ?? 0;
|
|
206
|
+
const byFamilyConversations = asRecord(summary.conversations_by_family);
|
|
207
|
+
const frustrated = asNumber(byFamilyConversations?.frustration) ?? asNumber(byFamily.frustration) ?? 0;
|
|
208
|
+
const frustrations = response.detections.map((item) => asRecord(item)).filter((row) => row !== null && (row.family === undefined || row.family === "frustration")).map(({ conversation_context, score, is_detected, ...row }) => ({
|
|
209
|
+
...row,
|
|
210
|
+
is_frustrated: is_detected === true,
|
|
211
|
+
frustration_score: score,
|
|
212
|
+
target: row.elicitor === "bot" || row.elicitor === "external" ? row.elicitor : "ambiguous",
|
|
213
|
+
conversation: conversation_context ?? null
|
|
214
|
+
}));
|
|
215
|
+
const atRisk = asNumber(pagination.at_risk) ?? Math.max(0, (asNumber(pagination.total) ?? 0) - frustrated);
|
|
216
|
+
const signalBreakdown = {};
|
|
217
|
+
for (const item of Array.isArray(response.signal_breakdown) ? response.signal_breakdown : []) {
|
|
218
|
+
const row = asRecord(item);
|
|
219
|
+
if (row?.family === "frustration" && typeof row.signal === "string") {
|
|
220
|
+
signalBreakdown[row.signal] = asNumber(row.count) ?? 0;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const { detections: _detections, ...rest } = response;
|
|
224
|
+
return {
|
|
225
|
+
...rest,
|
|
226
|
+
summary: {
|
|
227
|
+
total_analyzed: totalAnalyzed,
|
|
228
|
+
frustrated_count: frustrated,
|
|
229
|
+
at_risk_count: atRisk,
|
|
230
|
+
frustration_rate_pct: totalAnalyzed > 0 ? Math.round(frustrated / totalAnalyzed * 1000) / 10 : 0
|
|
231
|
+
},
|
|
232
|
+
signal_breakdown: signalBreakdown,
|
|
233
|
+
frustrations,
|
|
234
|
+
source: "emotion_detections"
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
function withFrustrationAnchors(response) {
|
|
238
|
+
if (!Array.isArray(response.frustrations))
|
|
239
|
+
return response;
|
|
240
|
+
const frustrations = response.frustrations.map((item) => {
|
|
241
|
+
const row = asRecord(item);
|
|
242
|
+
if (!row)
|
|
243
|
+
return item;
|
|
244
|
+
const anchor = buildFrustrationAnchor(row);
|
|
245
|
+
return { ...row, anchor };
|
|
246
|
+
});
|
|
247
|
+
return { ...response, frustrations };
|
|
248
|
+
}
|
|
249
|
+
var TOOL_FAILURE_CONTEXT_RADIUS = 2;
|
|
250
|
+
function trimFailureWindow(value, radius, fallbackCenter) {
|
|
251
|
+
const conv = asRecord(value);
|
|
252
|
+
if (!conv)
|
|
253
|
+
return value ?? null;
|
|
254
|
+
const { coverage: _coverage, context, ...convRest } = conv;
|
|
255
|
+
const ctx = asRecord(context);
|
|
256
|
+
if (!ctx)
|
|
257
|
+
return convRest;
|
|
258
|
+
const center = asNumber(ctx.center_index) ?? fallbackCenter;
|
|
259
|
+
const messages = (Array.isArray(ctx.messages) ? ctx.messages : []).map((message) => asRecord(message)).filter((message) => {
|
|
260
|
+
const index = asNumber(message?.index);
|
|
261
|
+
return message !== null && (center === null || index === null || Math.abs(index - center) <= radius);
|
|
262
|
+
}).map(({ content_blocks, ...message }) => {
|
|
263
|
+
const covered = typeof message.content === "string" && message.content.length > 0 || Array.isArray(message.tool_calls) && message.tool_calls.length > 0 || Array.isArray(message.tool_results) && message.tool_results.length > 0;
|
|
264
|
+
return covered || content_blocks === undefined ? message : { ...message, content_blocks };
|
|
265
|
+
});
|
|
266
|
+
const indexes = messages.map((message) => asNumber(message.index)).filter((index) => index !== null);
|
|
267
|
+
return {
|
|
268
|
+
...convRest,
|
|
269
|
+
context: {
|
|
270
|
+
...ctx,
|
|
271
|
+
...indexes.length > 0 ? { from_index: Math.min(...indexes), to_index: Math.max(...indexes) } : {},
|
|
272
|
+
messages
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function trimToolFailureConversations(response, opts) {
|
|
277
|
+
if (!Array.isArray(response.examples))
|
|
278
|
+
return response;
|
|
279
|
+
const examples = response.examples.map((item) => {
|
|
280
|
+
const row = asRecord(item);
|
|
281
|
+
if (!row)
|
|
282
|
+
return item;
|
|
283
|
+
const { conversation, window, ...rest } = row;
|
|
284
|
+
const conversationId = asString(row.conversation_id);
|
|
285
|
+
const msgIndex = asNumber(asRecord(row.anchor)?.msg_index) ?? asNumber(row.msg_index);
|
|
286
|
+
return {
|
|
287
|
+
...rest,
|
|
288
|
+
...conversationId && msgIndex !== null ? { context_command: `moda traces get ${shellArg(contextTarget(conversationId))} --msg-index=${msgIndex} --window=${TOOL_FAILURE_CONTEXT_RADIUS}` } : {},
|
|
289
|
+
...opts.drop ? {} : { conversation: trimFailureWindow(conversation, TOOL_FAILURE_CONTEXT_RADIUS, msgIndex) },
|
|
290
|
+
..."window" in row ? { window: trimFailureWindow(window, opts.windowRadius ?? TOOL_FAILURE_CONTEXT_RADIUS, msgIndex) } : {}
|
|
291
|
+
};
|
|
292
|
+
});
|
|
293
|
+
return { ...response, examples };
|
|
294
|
+
}
|
|
295
|
+
function withToolFailureAnchors(response) {
|
|
296
|
+
if (!Array.isArray(response.examples))
|
|
297
|
+
return response;
|
|
298
|
+
const toolName = asString(response.tool_name) ?? "";
|
|
299
|
+
const examples = response.examples.map((item) => {
|
|
300
|
+
const row = asRecord(item);
|
|
301
|
+
if (!row)
|
|
302
|
+
return item;
|
|
303
|
+
const anchor = buildToolFailureAnchor(row, toolName);
|
|
304
|
+
return { ...row, anchor };
|
|
305
|
+
});
|
|
306
|
+
return { ...response, examples };
|
|
307
|
+
}
|
|
308
|
+
function embeddedWindowSize(conversation) {
|
|
309
|
+
if (!conversation)
|
|
310
|
+
return null;
|
|
311
|
+
const ctx = asRecord(conversation.context);
|
|
312
|
+
if (!ctx)
|
|
313
|
+
return null;
|
|
314
|
+
const center = asNumber(ctx.center_index);
|
|
315
|
+
const from = asNumber(ctx.from_index);
|
|
316
|
+
const to = asNumber(ctx.to_index);
|
|
317
|
+
if (center === null || from === null || to === null)
|
|
318
|
+
return null;
|
|
319
|
+
return Math.min(center - from, to - center);
|
|
320
|
+
}
|
|
321
|
+
var DETECTION_CONTEXT_RADIUS = 2;
|
|
322
|
+
function withoutDetectionWindows(response, listKey, windowKey) {
|
|
323
|
+
const list = response[listKey];
|
|
324
|
+
if (!Array.isArray(list))
|
|
325
|
+
return response;
|
|
326
|
+
const rows = list.map((item) => {
|
|
327
|
+
const row = asRecord(item);
|
|
328
|
+
if (!row)
|
|
329
|
+
return item;
|
|
330
|
+
const { [windowKey]: _window, ...rest } = row;
|
|
331
|
+
const conversationId = asString(row.conversation_id);
|
|
332
|
+
const msgIndex = asNumber(asRecord(row.anchor)?.msg_index) ?? frustrationAnchorIndex(row);
|
|
333
|
+
return {
|
|
334
|
+
...rest,
|
|
335
|
+
...conversationId && msgIndex !== null ? { context_command: `moda traces get ${shellArg(contextTarget(conversationId))} --msg-index=${msgIndex} --window=${DETECTION_CONTEXT_RADIUS}` } : {}
|
|
336
|
+
};
|
|
337
|
+
});
|
|
338
|
+
return { ...response, [listKey]: rows };
|
|
339
|
+
}
|
|
340
|
+
var PROBLEM_COMPACT_EVIDENCE = 5;
|
|
341
|
+
var PROBLEM_COMPACT_OMITTED_BLOCKS = [
|
|
342
|
+
"rubric",
|
|
343
|
+
"trend",
|
|
344
|
+
"sub_problems",
|
|
345
|
+
"affected_conversations",
|
|
346
|
+
"remainder",
|
|
347
|
+
"confidence",
|
|
348
|
+
"change_history",
|
|
349
|
+
"stories"
|
|
350
|
+
];
|
|
351
|
+
function readCommand(command, purpose) {
|
|
352
|
+
return { command, purpose, mutability: "read", requires_approval: false };
|
|
353
|
+
}
|
|
354
|
+
function evidenceMsgIndex(row) {
|
|
355
|
+
return asNumber(row.anchor_msg_index) ?? asNumber(asRecord(asRecord(row.snippet_parsed)?.anchor)?.msg_index);
|
|
356
|
+
}
|
|
357
|
+
function compactProblemDossier(response, problemId) {
|
|
358
|
+
const id = shellArg(asString(response.problem_id) ?? problemId);
|
|
359
|
+
const evidence = (Array.isArray(response.evidence) ? response.evidence : []).map((item) => asRecord(item)).filter((row) => row !== null);
|
|
360
|
+
const topEvidence = evidence.slice(0, PROBLEM_COMPACT_EVIDENCE).map((row) => {
|
|
361
|
+
const conversationId = asString(row.conversation_id);
|
|
362
|
+
const msgIndex = evidenceMsgIndex(row);
|
|
363
|
+
return {
|
|
364
|
+
attribution_id: row.attribution_id ?? null,
|
|
365
|
+
conversation_id: conversationId,
|
|
366
|
+
msg_index: msgIndex,
|
|
367
|
+
signal_family: row.signal_family ?? null,
|
|
368
|
+
signal_type: row.signal_type ?? null,
|
|
369
|
+
confidence: row.confidence ?? null,
|
|
370
|
+
causal_rationale: row.causal_rationale ?? null,
|
|
371
|
+
door: row.door ?? null,
|
|
372
|
+
replay_verified: row.replay_verified ?? null,
|
|
373
|
+
...conversationId && msgIndex !== null ? { context_command: `moda traces get ${shellArg(contextTarget(conversationId))} --msg-index=${msgIndex}` } : {}
|
|
374
|
+
};
|
|
375
|
+
});
|
|
376
|
+
const reportsBlock = asRecord(response.investigation_reports);
|
|
377
|
+
const reports = (Array.isArray(reportsBlock?.items) ? reportsBlock.items : []).map((item) => asRecord(item)).filter((row) => row !== null).map((row) => ({
|
|
378
|
+
report_id: row.report_id ?? null,
|
|
379
|
+
conversation_id: row.conversation_id ?? null,
|
|
380
|
+
mechanism: row.mechanism ?? null,
|
|
381
|
+
suspected_surface: row.suspected_surface ?? null,
|
|
382
|
+
severity: row.severity ?? null,
|
|
383
|
+
problem_found: row.problem_found ?? null
|
|
384
|
+
}));
|
|
385
|
+
const verification = asRecord(response.verification);
|
|
386
|
+
const { samples: _samples, ...verificationCounts } = verification ?? {};
|
|
387
|
+
const dossier = asRecord(response.dossier);
|
|
388
|
+
const { representative_story_keys: _storyKeys, ...dossierSummary } = dossier ?? {};
|
|
389
|
+
const trend = Array.isArray(response.trend) ? response.trend : [];
|
|
390
|
+
const data = {
|
|
391
|
+
found: response.found ?? true,
|
|
392
|
+
problem_id: response.problem_id ?? problemId,
|
|
393
|
+
requested_problem_id: response.requested_problem_id ?? problemId,
|
|
394
|
+
alias_resolved: response.alias_resolved ?? false,
|
|
395
|
+
view: "compact",
|
|
396
|
+
header: response.header ?? null,
|
|
397
|
+
dossier: dossier ? dossierSummary : null,
|
|
398
|
+
verification: verification ? verificationCounts : null,
|
|
399
|
+
...response.impact_identity !== undefined ? { impact_identity: response.impact_identity } : {},
|
|
400
|
+
...response.hierarchy !== undefined ? { hierarchy: response.hierarchy } : {},
|
|
401
|
+
...trend.length > 0 ? { trend_latest: trend[trend.length - 1], trend_points: trend.length } : {},
|
|
402
|
+
top_evidence: topEvidence,
|
|
403
|
+
evidence_in_dossier: evidence.length,
|
|
404
|
+
reports,
|
|
405
|
+
omitted: {
|
|
406
|
+
evidence_rows: Math.max(0, evidence.length - topEvidence.length),
|
|
407
|
+
report_narratives: reports.length,
|
|
408
|
+
reports_has_more: reportsBlock?.has_more === true,
|
|
409
|
+
blocks: PROBLEM_COMPACT_OMITTED_BLOCKS.filter((key) => (key in response))
|
|
410
|
+
},
|
|
411
|
+
full_command: `moda problems get ${id} --full`
|
|
412
|
+
};
|
|
413
|
+
const nextCommands = [
|
|
414
|
+
readCommand(`moda problems get ${id} --evidence --limit=20`, "Page every evidence row with its anchor (follow next_cursor)."),
|
|
415
|
+
readCommand(`moda problems get ${id} --reports`, "Page investigation reports with narratives and citations."),
|
|
416
|
+
readCommand(`moda problems get ${id} --traces`, "Page affected traces with their latest anchor."),
|
|
417
|
+
readCommand(`moda problems get ${id} --full`, "The complete dossier, every block (large).")
|
|
418
|
+
];
|
|
419
|
+
const first = topEvidence.find((row) => typeof row.context_command === "string");
|
|
420
|
+
if (first?.context_command) {
|
|
421
|
+
nextCommands.push(readCommand(first.context_command, "Read the trace around the top evidence anchor."));
|
|
422
|
+
}
|
|
423
|
+
return { data, nextCommands };
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// src/intelligence-schema.ts
|
|
427
|
+
var INTELLIGENCE_SCHEMA_VERSION = "moda.intelligence.v1";
|
|
428
|
+
var provenanceSchema = { type: "string", enum: ["measured", "inferred"] };
|
|
429
|
+
var mutabilitySchema = {
|
|
430
|
+
type: "string",
|
|
431
|
+
enum: ["read", "write", "sync", "unknown"]
|
|
432
|
+
};
|
|
433
|
+
var confidenceSchema = {
|
|
434
|
+
type: "string",
|
|
435
|
+
enum: ["high", "medium", "low", "unknown"]
|
|
436
|
+
};
|
|
437
|
+
var MODA_INTELLIGENCE_V1_JSON_SCHEMA = {
|
|
438
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
439
|
+
$id: "moda.intelligence.v1",
|
|
440
|
+
title: "Moda Intelligence v1",
|
|
441
|
+
type: "object",
|
|
442
|
+
additionalProperties: false,
|
|
443
|
+
required: [
|
|
444
|
+
"schema_version",
|
|
445
|
+
"generated_at",
|
|
446
|
+
"command",
|
|
447
|
+
"source",
|
|
448
|
+
"degraded",
|
|
449
|
+
"question",
|
|
450
|
+
"summary",
|
|
451
|
+
"answer_markdown",
|
|
452
|
+
"answer_claims",
|
|
453
|
+
"findings",
|
|
454
|
+
"evidence_refs",
|
|
455
|
+
"next_commands",
|
|
456
|
+
"warnings"
|
|
457
|
+
],
|
|
458
|
+
properties: {
|
|
459
|
+
schema_version: { const: "moda.intelligence.v1" },
|
|
460
|
+
generated_at: { type: "string", format: "date-time" },
|
|
461
|
+
command: { type: "string" },
|
|
462
|
+
source: { type: "string", enum: ["cloud", "local_fallback"] },
|
|
463
|
+
degraded: { type: "boolean" },
|
|
464
|
+
question: { type: "string" },
|
|
465
|
+
summary: {
|
|
466
|
+
type: "object",
|
|
467
|
+
additionalProperties: false,
|
|
468
|
+
required: ["text", "confidence"],
|
|
469
|
+
properties: { text: { type: "string" }, confidence: confidenceSchema }
|
|
470
|
+
},
|
|
471
|
+
answer_markdown: { type: "string" },
|
|
472
|
+
answer_claims: {
|
|
473
|
+
type: "array",
|
|
474
|
+
items: {
|
|
475
|
+
type: "object",
|
|
476
|
+
additionalProperties: false,
|
|
477
|
+
required: ["text", "kind", "status", "evidence_ref_ids"],
|
|
478
|
+
properties: {
|
|
479
|
+
text: { type: "string" },
|
|
480
|
+
value: { type: "number" },
|
|
481
|
+
kind: { type: "string", enum: ["percentage", "count", "qualitative"] },
|
|
482
|
+
status: {
|
|
483
|
+
type: "string",
|
|
484
|
+
enum: ["grounded", "ungrounded", "unverifiable"]
|
|
485
|
+
},
|
|
486
|
+
evidence_ref_ids: { type: "array", items: { type: "string" } },
|
|
487
|
+
matched_path: { type: "string" }
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
},
|
|
491
|
+
findings: {
|
|
492
|
+
type: "array",
|
|
493
|
+
items: {
|
|
494
|
+
type: "object",
|
|
495
|
+
additionalProperties: false,
|
|
496
|
+
required: ["id", "title", "evidence_ref_ids", "recommended_actions"],
|
|
497
|
+
properties: {
|
|
498
|
+
id: { type: "string" },
|
|
499
|
+
title: { type: "string" },
|
|
500
|
+
severity: {
|
|
501
|
+
type: "string",
|
|
502
|
+
enum: ["critical", "high", "medium", "low", "info"]
|
|
503
|
+
},
|
|
504
|
+
description: { type: "string" },
|
|
505
|
+
evidence_ref_ids: { type: "array", items: { type: "string" } },
|
|
506
|
+
recommended_actions: {
|
|
507
|
+
type: "array",
|
|
508
|
+
items: {
|
|
509
|
+
type: "object",
|
|
510
|
+
additionalProperties: false,
|
|
511
|
+
required: ["id", "title", "mutability", "requires_approval"],
|
|
512
|
+
properties: {
|
|
513
|
+
id: { type: "string" },
|
|
514
|
+
title: { type: "string" },
|
|
515
|
+
rationale: { type: "string" },
|
|
516
|
+
command: { type: "string" },
|
|
517
|
+
mutability: mutabilitySchema,
|
|
518
|
+
requires_approval: { type: "boolean" }
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
},
|
|
525
|
+
evidence_refs: {
|
|
526
|
+
type: "array",
|
|
527
|
+
items: {
|
|
528
|
+
type: "object",
|
|
529
|
+
additionalProperties: false,
|
|
530
|
+
required: ["id", "kind", "label", "grounding"],
|
|
531
|
+
properties: {
|
|
532
|
+
id: { type: "string" },
|
|
533
|
+
kind: { type: "string" },
|
|
534
|
+
label: { type: "string" },
|
|
535
|
+
anchor: {
|
|
536
|
+
type: "object",
|
|
537
|
+
additionalProperties: false,
|
|
538
|
+
properties: {
|
|
539
|
+
conversation_id: { type: "string" },
|
|
540
|
+
message_index: { type: "number" },
|
|
541
|
+
run_id: { type: "string" },
|
|
542
|
+
tool_name: { type: "string" }
|
|
543
|
+
}
|
|
544
|
+
},
|
|
545
|
+
snippet: { type: "string" },
|
|
546
|
+
grounding: provenanceSchema
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
},
|
|
550
|
+
next_commands: {
|
|
551
|
+
type: "array",
|
|
552
|
+
items: {
|
|
553
|
+
type: "object",
|
|
554
|
+
additionalProperties: false,
|
|
555
|
+
required: ["command", "mutability", "requires_approval"],
|
|
556
|
+
properties: {
|
|
557
|
+
command: { type: "string" },
|
|
558
|
+
purpose: { type: "string" },
|
|
559
|
+
mutability: mutabilitySchema,
|
|
560
|
+
requires_approval: { type: "boolean" }
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
},
|
|
564
|
+
warnings: { type: "array", items: { type: "string" } },
|
|
565
|
+
meta: { type: "object" }
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
export { BATCH_MAX_TARGETS, BATCH_CONCURRENCY, parseBatchTarget, collectBatchTargets, mapWithConcurrency, contextTarget, asRecord, asString, asNumber, frustrationAnchorIndex, frustrationsFromEmotions, withFrustrationAnchors, trimToolFailureConversations, withToolFailureAnchors, embeddedWindowSize, withoutDetectionWindows, compactProblemDossier, INTELLIGENCE_SCHEMA_VERSION, MODA_INTELLIGENCE_V1_JSON_SCHEMA };
|