@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,6 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
-
INTELLIGENCE_SCHEMA_VERSION
|
|
3
|
-
|
|
2
|
+
INTELLIGENCE_SCHEMA_VERSION,
|
|
3
|
+
asNumber,
|
|
4
|
+
asRecord,
|
|
5
|
+
asString,
|
|
6
|
+
contextTarget,
|
|
7
|
+
frustrationAnchorIndex,
|
|
8
|
+
frustrationsFromEmotions
|
|
9
|
+
} from "./cli-fwpqghgc.js";
|
|
4
10
|
import {
|
|
5
11
|
renderMarkdownForTerminal,
|
|
6
12
|
shouldColorTerminalOutput,
|
|
@@ -10,26 +16,23 @@ import {
|
|
|
10
16
|
import {
|
|
11
17
|
callControlAPI,
|
|
12
18
|
callDataAPI
|
|
13
|
-
} from "./cli-
|
|
19
|
+
} from "./cli-e23g9ywa.js";
|
|
14
20
|
import {
|
|
15
|
-
CliAuthError,
|
|
16
|
-
CliInputError,
|
|
17
21
|
EXIT_DEGRADED,
|
|
18
22
|
EXIT_OK,
|
|
19
|
-
apiErrorStatus,
|
|
20
23
|
apiKeyHeaders,
|
|
21
|
-
asNumber,
|
|
22
|
-
asRecord,
|
|
23
|
-
asString,
|
|
24
|
-
contextTarget,
|
|
25
|
-
frustrationAnchorIndex,
|
|
26
|
-
frustrationsFromEmotions,
|
|
27
24
|
recordApiRequest,
|
|
28
25
|
resolveApiKey,
|
|
29
26
|
resolveModaBaseUrl,
|
|
30
27
|
safeWriteStderr,
|
|
31
|
-
|
|
32
|
-
} from "./cli-
|
|
28
|
+
shellArg
|
|
29
|
+
} from "./cli-99p2jh28.js";
|
|
30
|
+
import {
|
|
31
|
+
CliAuthError,
|
|
32
|
+
CliInputError,
|
|
33
|
+
apiErrorStatus
|
|
34
|
+
} from "./cli-k4tqwah4.js";
|
|
35
|
+
import"./cli-ns5fcnkz.js";
|
|
33
36
|
import"./cli-0v6na3yp.js";
|
|
34
37
|
|
|
35
38
|
// src/production-intelligence.ts
|
|
@@ -538,7 +541,7 @@ async function investigateProduction(options) {
|
|
|
538
541
|
warnings.push(`No failures found for tool "${options.toolName}" in the last ${options.daysBack} day(s) — the tool may have no failures in this window, or the name may not exist in this tenant. ` + "Run `moda detections tools` to list tools with recorded failures, or widen --days-back.");
|
|
539
542
|
}
|
|
540
543
|
if (options.conversationId) {
|
|
541
|
-
warnings.push(`Scoped to trace ${options.conversationId} for frustration evidence only; overview and tool failures are tenant-wide. ` + `Open the trace itself with \`moda traces spans ${options.conversationId}\`.`);
|
|
544
|
+
warnings.push(`Scoped to trace ${options.conversationId} for frustration evidence only; overview and tool failures are tenant-wide. ` + `Open the trace itself with \`moda traces spans ${shellArg(options.conversationId)}\`.`);
|
|
542
545
|
}
|
|
543
546
|
const evidenceRefs = [];
|
|
544
547
|
const findings = [];
|
|
@@ -872,7 +875,7 @@ function toolFailureFindings(data, daysBack, hints, evidenceRefs, scopedTool) {
|
|
|
872
875
|
id: `action_tool_detail_${slug(name)}`,
|
|
873
876
|
title: `Inspect ${name} failure examples.`,
|
|
874
877
|
rationale: "Examples include trace anchors and error subtypes.",
|
|
875
|
-
command: `moda detections tools ${name} --days-back=${daysBack} --include-window`,
|
|
878
|
+
command: `moda detections tools ${shellArg(name)} --days-back=${daysBack} --include-window`,
|
|
876
879
|
mutability: "read",
|
|
877
880
|
requiresApproval: false
|
|
878
881
|
}]
|
|
@@ -918,7 +921,7 @@ function frustrationFindings(data, daysBack, evidenceRefs) {
|
|
|
918
921
|
id: "action_frustration_window",
|
|
919
922
|
title: "Read the frustration window.",
|
|
920
923
|
rationale: "The user quote and nearby turns reveal what confused or blocked the user.",
|
|
921
|
-
command: conversationId ? `moda traces get ${
|
|
924
|
+
command: conversationId ? `moda traces get ${shellArg(contextTarget(conversationId))}${anchorIndex !== null ? ` --msg-index=${anchorIndex}` : ""} --window=3` : `moda detections list --family=frustration --days-back=${daysBack} --include-window`,
|
|
922
925
|
mutability: "read",
|
|
923
926
|
requiresApproval: false
|
|
924
927
|
}]
|
|
@@ -994,7 +997,7 @@ function problemFindings(data, daysBack, evidenceRefs) {
|
|
|
994
997
|
id: `action_problem_${slug(problem.id)}`,
|
|
995
998
|
title: `Open the Problem dossier for ${problem.name}.`,
|
|
996
999
|
rationale: "The dossier has the cause, evidence, affected traces and verification state.",
|
|
997
|
-
command: `moda problems get ${problem.id}`,
|
|
1000
|
+
command: `moda problems get ${shellArg(problem.id)}`,
|
|
998
1001
|
mutability: "read",
|
|
999
1002
|
requiresApproval: false
|
|
1000
1003
|
}]
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
probeApiKey,
|
|
3
3
|
redactApiKey,
|
|
4
4
|
resolveApiKeyCredential
|
|
5
|
-
} from "./cli-
|
|
5
|
+
} from "./cli-97jew729.js";
|
|
6
6
|
import {
|
|
7
7
|
authFetch,
|
|
8
8
|
clearAuthSession,
|
|
@@ -10,9 +10,8 @@ import {
|
|
|
10
10
|
loadAuthSession,
|
|
11
11
|
login,
|
|
12
12
|
revokeCliSessionToken
|
|
13
|
-
} from "./cli-
|
|
13
|
+
} from "./cli-9bgnvth2.js";
|
|
14
14
|
import {
|
|
15
|
-
CliInputError,
|
|
16
15
|
DEFAULT_INGEST_URL,
|
|
17
16
|
EXIT_AUTH_REQUIRED,
|
|
18
17
|
EXIT_DEGRADED,
|
|
@@ -34,8 +33,13 @@ import {
|
|
|
34
33
|
saveCliConfig,
|
|
35
34
|
secretFilePathFromRef,
|
|
36
35
|
setActiveProfile,
|
|
36
|
+
shellArg,
|
|
37
37
|
upsertProfile
|
|
38
|
-
} from "./cli-
|
|
38
|
+
} from "./cli-99p2jh28.js";
|
|
39
|
+
import {
|
|
40
|
+
CliInputError
|
|
41
|
+
} from "./cli-k4tqwah4.js";
|
|
42
|
+
import"./cli-ns5fcnkz.js";
|
|
39
43
|
import"./cli-0v6na3yp.js";
|
|
40
44
|
|
|
41
45
|
// src/profile-commands.ts
|
|
@@ -326,7 +330,7 @@ async function runProfilesCommand(context) {
|
|
|
326
330
|
default_profile: config.active_profile,
|
|
327
331
|
profiles: Object.values(config.profiles).map((profile) => serializeProfile(profile, effective)),
|
|
328
332
|
migrations: config.migrations
|
|
329
|
-
}, missing ? { warnings: [`The active profile '${effective}' (from ${source}) does not exist. Create it with \`moda profiles create ${effective}\` or select an existing profile.`] } : undefined);
|
|
333
|
+
}, missing ? { warnings: [`The active profile '${effective}' (from ${source}) does not exist. Create it with \`moda profiles create ${shellArg(effective)}\` or select an existing profile.`] } : undefined);
|
|
330
334
|
return { exitCode: 0 };
|
|
331
335
|
}
|
|
332
336
|
case "use": {
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import {
|
|
2
|
+
inventoryFiles,
|
|
3
|
+
safePromptEvidencePath
|
|
4
|
+
} from "./cli-3n0gz4d0.js";
|
|
5
|
+
import"./cli-xq45etg1.js";
|
|
6
|
+
import"./cli-97fnztpy.js";
|
|
7
|
+
import"./cli-cesds766.js";
|
|
8
|
+
import {
|
|
9
|
+
require_typescript
|
|
10
|
+
} from "./cli-qpq84eee.js";
|
|
11
|
+
import"./cli-dr6x0zv7.js";
|
|
12
|
+
import"./cli-n2hng3gv.js";
|
|
13
|
+
import"./cli-e23g9ywa.js";
|
|
14
|
+
import"./cli-99p2jh28.js";
|
|
15
|
+
import"./cli-k4tqwah4.js";
|
|
16
|
+
import"./cli-9kfxkxmk.js";
|
|
17
|
+
import"./cli-ns5fcnkz.js";
|
|
18
|
+
import {
|
|
19
|
+
__toESM
|
|
20
|
+
} from "./cli-0v6na3yp.js";
|
|
21
|
+
|
|
22
|
+
// src/prompt-attribution.ts
|
|
23
|
+
var import_typescript = __toESM(require_typescript(), 1);
|
|
24
|
+
import { dirname, extname, resolve } from "node:path";
|
|
25
|
+
function checkPromptAttribution(rootDir, keys) {
|
|
26
|
+
const result = { references: [], coverage: "partial", issues: [] };
|
|
27
|
+
const known = new Set(keys), lock = resolve(rootDir, ".moda/prompts.lock.json");
|
|
28
|
+
let files;
|
|
29
|
+
try {
|
|
30
|
+
files = inventoryFiles(rootDir, []);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
result.issues.push({ path: ".", reason: `Attribution scan incomplete: ${error instanceof Error ? error.message : String(error)}` });
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
for (const file of files) {
|
|
36
|
+
if (![".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"].includes(extname(file.path)) || !file.text?.includes("prompts.lock.json"))
|
|
37
|
+
continue;
|
|
38
|
+
if (/(^|\/)(?:tests?|__tests__|fixtures?)(\/|$)|\.(?:test|spec)\.[^.]+$/.test(file.path))
|
|
39
|
+
continue;
|
|
40
|
+
try {
|
|
41
|
+
const absolute = safePromptEvidencePath(rootDir, file.path);
|
|
42
|
+
const program = import_typescript.default.createProgram([absolute], { noResolve: true, noLib: true, types: [], allowJs: true });
|
|
43
|
+
const sf = program.getSourceFile(absolute);
|
|
44
|
+
if (!sf || program.getSyntacticDiagnostics(sf).length) {
|
|
45
|
+
result.issues.push({ path: file.path, reason: "Attribution source could not be parsed" });
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const checker = program.getTypeChecker();
|
|
49
|
+
const declaration = (node) => {
|
|
50
|
+
const all = checker.getSymbolAtLocation(node)?.declarations;
|
|
51
|
+
return all?.length === 1 ? all[0] : undefined;
|
|
52
|
+
};
|
|
53
|
+
const imported = (node) => {
|
|
54
|
+
if (import_typescript.default.isImportSpecifier(node) && node.isTypeOnly)
|
|
55
|
+
return false;
|
|
56
|
+
let parent = node;
|
|
57
|
+
while (parent && !import_typescript.default.isImportDeclaration(parent))
|
|
58
|
+
parent = parent.parent;
|
|
59
|
+
return !!parent && import_typescript.default.isImportDeclaration(parent) && !parent.importClause?.isTypeOnly && import_typescript.default.isStringLiteral(parent.moduleSpecifier) && parent.moduleSpecifier.text.startsWith(".") && resolve(dirname(absolute), parent.moduleSpecifier.text) === lock;
|
|
60
|
+
};
|
|
61
|
+
const initializer = (node) => {
|
|
62
|
+
const decl = declaration(node);
|
|
63
|
+
return decl && import_typescript.default.isVariableDeclaration(decl) && import_typescript.default.isVariableDeclarationList(decl.parent) && !!(decl.parent.flags & import_typescript.default.NodeFlags.Const) ? decl.initializer : undefined;
|
|
64
|
+
};
|
|
65
|
+
const unwrap = (node) => {
|
|
66
|
+
while (import_typescript.default.isParenthesizedExpression(node) || import_typescript.default.isAsExpression(node) || import_typescript.default.isTypeAssertionExpression(node) || import_typescript.default.isNonNullExpression(node))
|
|
67
|
+
node = node.expression;
|
|
68
|
+
return node;
|
|
69
|
+
};
|
|
70
|
+
const literal = (expression, seen = new Set) => {
|
|
71
|
+
const node = unwrap(expression);
|
|
72
|
+
if (seen.has(node))
|
|
73
|
+
return;
|
|
74
|
+
seen.add(node);
|
|
75
|
+
if (import_typescript.default.isStringLiteral(node) || import_typescript.default.isNoSubstitutionTemplateLiteral(node))
|
|
76
|
+
return node.text;
|
|
77
|
+
if (import_typescript.default.isIdentifier(node)) {
|
|
78
|
+
const init = initializer(node);
|
|
79
|
+
return init ? literal(init, seen) : undefined;
|
|
80
|
+
}
|
|
81
|
+
if (import_typescript.default.isBinaryExpression(node) && node.operatorToken.kind === import_typescript.default.SyntaxKind.PlusToken) {
|
|
82
|
+
const a = literal(node.left, new Set(seen)), b = literal(node.right, new Set(seen));
|
|
83
|
+
return a !== undefined && b !== undefined ? a + b : undefined;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const lockPath = (node) => import_typescript.default.isStringLiteral(node) && node.text.startsWith(".") && resolve(dirname(absolute), node.text) === lock;
|
|
87
|
+
const isConstBinding = (node) => import_typescript.default.isVariableDeclarationList(node.parent) && !!(node.parent.flags & import_typescript.default.NodeFlags.Const);
|
|
88
|
+
const isLock = (expression, seen = new Set) => {
|
|
89
|
+
const node = unwrap(expression);
|
|
90
|
+
if (seen.has(node))
|
|
91
|
+
return false;
|
|
92
|
+
seen.add(node);
|
|
93
|
+
if (import_typescript.default.isCallExpression(node) && import_typescript.default.isIdentifier(node.expression) && node.expression.text === "require" && !declaration(node.expression) && node.arguments.length === 1)
|
|
94
|
+
return lockPath(node.arguments[0]);
|
|
95
|
+
if (!import_typescript.default.isIdentifier(node))
|
|
96
|
+
return false;
|
|
97
|
+
const decl = declaration(node);
|
|
98
|
+
if (decl && (import_typescript.default.isImportClause(decl) || import_typescript.default.isNamespaceImport(decl)))
|
|
99
|
+
return imported(decl);
|
|
100
|
+
const init = initializer(node);
|
|
101
|
+
return !!init && isLock(init, seen);
|
|
102
|
+
};
|
|
103
|
+
const isTable = (expression, seen = new Set) => {
|
|
104
|
+
const node = unwrap(expression);
|
|
105
|
+
if (seen.has(node))
|
|
106
|
+
return false;
|
|
107
|
+
seen.add(node);
|
|
108
|
+
if (import_typescript.default.isPropertyAccessExpression(node))
|
|
109
|
+
return node.name.text === "prompts" && isLock(node.expression);
|
|
110
|
+
if (import_typescript.default.isElementAccessExpression(node))
|
|
111
|
+
return literal(node.argumentExpression) === "prompts" && isLock(node.expression);
|
|
112
|
+
if (import_typescript.default.isIdentifier(node)) {
|
|
113
|
+
const decl = declaration(node);
|
|
114
|
+
if (decl && import_typescript.default.isBindingElement(decl) && import_typescript.default.isObjectBindingPattern(decl.parent) && import_typescript.default.isVariableDeclaration(decl.parent.parent)) {
|
|
115
|
+
const owner = decl.parent.parent;
|
|
116
|
+
const name = decl.propertyName ?? decl.name;
|
|
117
|
+
if (!decl.dotDotDotToken && !decl.initializer && (import_typescript.default.isIdentifier(name) || import_typescript.default.isStringLiteral(name)) && name.text === "prompts" && isConstBinding(owner) && owner.initializer)
|
|
118
|
+
return isLock(owner.initializer);
|
|
119
|
+
}
|
|
120
|
+
if (decl && import_typescript.default.isImportSpecifier(decl))
|
|
121
|
+
return (decl.propertyName?.text ?? decl.name.text) === "prompts" && imported(decl);
|
|
122
|
+
const init = initializer(node);
|
|
123
|
+
return !!init && isTable(init, seen);
|
|
124
|
+
}
|
|
125
|
+
return false;
|
|
126
|
+
};
|
|
127
|
+
const visit = (node) => {
|
|
128
|
+
if ((import_typescript.default.isElementAccessExpression(node) || import_typescript.default.isPropertyAccessExpression(node)) && isTable(node.expression)) {
|
|
129
|
+
const key = import_typescript.default.isElementAccessExpression(node) ? literal(node.argumentExpression) : node.name.text;
|
|
130
|
+
result.references.push({
|
|
131
|
+
path: file.path,
|
|
132
|
+
line: sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1,
|
|
133
|
+
key,
|
|
134
|
+
status: key === undefined ? "unresolved" : known.has(key) ? "mapped" : "missing"
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
import_typescript.default.forEachChild(node, visit);
|
|
138
|
+
};
|
|
139
|
+
visit(sf);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
result.issues.push({ path: file.path, reason: error instanceof Error ? error.message : String(error) });
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
export {
|
|
147
|
+
checkPromptAttribution
|
|
148
|
+
};
|
|
@@ -2,15 +2,22 @@
|
|
|
2
2
|
import {
|
|
3
3
|
checkPromptMapping,
|
|
4
4
|
discoverHarnessPromptSources,
|
|
5
|
-
inspectCodeSelectors,
|
|
6
|
-
inspectPythonSelectors,
|
|
7
5
|
safePromptEvidencePath
|
|
8
|
-
} from "./cli-
|
|
6
|
+
} from "./cli-3n0gz4d0.js";
|
|
7
|
+
import"./cli-xq45etg1.js";
|
|
9
8
|
import"./cli-97fnztpy.js";
|
|
10
|
-
import"./cli-
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
import"./cli-cesds766.js";
|
|
10
|
+
import {
|
|
11
|
+
inspectCodeSelectors,
|
|
12
|
+
inspectPythonSelectors
|
|
13
|
+
} from "./cli-qpq84eee.js";
|
|
14
|
+
import"./cli-dr6x0zv7.js";
|
|
15
|
+
import"./cli-n2hng3gv.js";
|
|
16
|
+
import"./cli-e23g9ywa.js";
|
|
17
|
+
import"./cli-99p2jh28.js";
|
|
18
|
+
import"./cli-k4tqwah4.js";
|
|
19
|
+
import"./cli-9kfxkxmk.js";
|
|
20
|
+
import"./cli-ns5fcnkz.js";
|
|
14
21
|
import {
|
|
15
22
|
__commonJS,
|
|
16
23
|
__toESM
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
backfillPromptSource,
|
|
3
|
+
discoverCodePromptCandidates,
|
|
4
|
+
discoverCodePromptSources,
|
|
5
|
+
discoverStructuredPromptSources,
|
|
6
|
+
resolvePromptSource,
|
|
7
|
+
validatePromptSource
|
|
8
|
+
} from "./cli-qpq84eee.js";
|
|
9
|
+
import"./cli-9kfxkxmk.js";
|
|
10
|
+
import"./cli-ns5fcnkz.js";
|
|
11
|
+
import"./cli-0v6na3yp.js";
|
|
12
|
+
export {
|
|
13
|
+
validatePromptSource,
|
|
14
|
+
resolvePromptSource,
|
|
15
|
+
discoverStructuredPromptSources,
|
|
16
|
+
discoverCodePromptSources,
|
|
17
|
+
discoverCodePromptCandidates,
|
|
18
|
+
backfillPromptSource
|
|
19
|
+
};
|
|
@@ -11,16 +11,17 @@ import {
|
|
|
11
11
|
runPromptSync,
|
|
12
12
|
runPromptsCommand,
|
|
13
13
|
writePromptSyncProgress
|
|
14
|
-
} from "./cli-
|
|
14
|
+
} from "./cli-3retgh64.js";
|
|
15
15
|
import {
|
|
16
16
|
promptSourceHashPayload
|
|
17
17
|
} from "./cli-mm48br00.js";
|
|
18
|
-
import"./cli-
|
|
19
|
-
import"./cli-
|
|
20
|
-
import"./cli-
|
|
21
|
-
import"./cli-
|
|
22
|
-
import"./cli-
|
|
23
|
-
import"./cli-
|
|
18
|
+
import"./cli-dr6x0zv7.js";
|
|
19
|
+
import"./cli-n2hng3gv.js";
|
|
20
|
+
import"./cli-e23g9ywa.js";
|
|
21
|
+
import"./cli-99p2jh28.js";
|
|
22
|
+
import"./cli-k4tqwah4.js";
|
|
23
|
+
import"./cli-9kfxkxmk.js";
|
|
24
|
+
import"./cli-ns5fcnkz.js";
|
|
24
25
|
import"./cli-0v6na3yp.js";
|
|
25
26
|
export {
|
|
26
27
|
writePromptSyncProgress,
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_PROBLEM_MAX_CASES,
|
|
3
|
+
MAX_AUTO_GENERATE_CASES,
|
|
4
|
+
MAX_REPLAY_TRACES,
|
|
5
|
+
PLAYOUT_CONFIRM_THRESHOLD,
|
|
6
|
+
REPLAY_MODEL_ALLOWLIST,
|
|
7
|
+
TERMINAL_RUN_STATUSES,
|
|
8
|
+
assertAllowedModel,
|
|
9
|
+
assertArmSourceShape,
|
|
10
|
+
assertPlayoutBudget,
|
|
11
|
+
candidateOutcome,
|
|
12
|
+
describePlayouts,
|
|
13
|
+
describeProblemCases,
|
|
14
|
+
enqueueAndPollComparison,
|
|
15
|
+
fetchProblemCases,
|
|
16
|
+
fetchReplaySetCases,
|
|
17
|
+
formatVerdict,
|
|
18
|
+
parseTraceRef,
|
|
19
|
+
planReplaySet,
|
|
20
|
+
replayCasePin,
|
|
21
|
+
rerunWithYes,
|
|
22
|
+
resolvePromptArmSpec,
|
|
23
|
+
runPromptAb,
|
|
24
|
+
selectProblemCases,
|
|
25
|
+
waitLabel
|
|
26
|
+
} from "./cli-yf39avhh.js";
|
|
27
|
+
import {
|
|
28
|
+
DEFAULT_POLL_INTERVAL_MS,
|
|
29
|
+
DEFAULT_WAIT_TIMEOUT_MS,
|
|
30
|
+
fetchLatestRun
|
|
31
|
+
} from "./cli-yz8xcpyk.js";
|
|
32
|
+
import"./cli-3retgh64.js";
|
|
33
|
+
import"./cli-mm48br00.js";
|
|
34
|
+
import"./cli-dr6x0zv7.js";
|
|
35
|
+
import"./cli-n2hng3gv.js";
|
|
36
|
+
import"./cli-e23g9ywa.js";
|
|
37
|
+
import"./cli-99p2jh28.js";
|
|
38
|
+
import"./cli-k4tqwah4.js";
|
|
39
|
+
import"./cli-9kfxkxmk.js";
|
|
40
|
+
import"./cli-ns5fcnkz.js";
|
|
41
|
+
import"./cli-0v6na3yp.js";
|
|
42
|
+
export {
|
|
43
|
+
waitLabel,
|
|
44
|
+
selectProblemCases,
|
|
45
|
+
runPromptAb,
|
|
46
|
+
resolvePromptArmSpec,
|
|
47
|
+
rerunWithYes,
|
|
48
|
+
replayCasePin,
|
|
49
|
+
planReplaySet,
|
|
50
|
+
parseTraceRef,
|
|
51
|
+
formatVerdict,
|
|
52
|
+
fetchReplaySetCases,
|
|
53
|
+
fetchProblemCases,
|
|
54
|
+
fetchLatestRun,
|
|
55
|
+
enqueueAndPollComparison,
|
|
56
|
+
describeProblemCases,
|
|
57
|
+
describePlayouts,
|
|
58
|
+
candidateOutcome,
|
|
59
|
+
assertPlayoutBudget,
|
|
60
|
+
assertArmSourceShape,
|
|
61
|
+
assertAllowedModel,
|
|
62
|
+
TERMINAL_RUN_STATUSES,
|
|
63
|
+
REPLAY_MODEL_ALLOWLIST,
|
|
64
|
+
PLAYOUT_CONFIRM_THRESHOLD,
|
|
65
|
+
MAX_REPLAY_TRACES,
|
|
66
|
+
MAX_AUTO_GENERATE_CASES,
|
|
67
|
+
DEFAULT_WAIT_TIMEOUT_MS,
|
|
68
|
+
DEFAULT_PROBLEM_MAX_CASES,
|
|
69
|
+
DEFAULT_POLL_INTERVAL_MS
|
|
70
|
+
};
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assertAllowedModel,
|
|
3
|
+
assertPlayoutBudget,
|
|
4
|
+
candidateOutcome,
|
|
5
|
+
describePlayouts,
|
|
6
|
+
enqueueAndPollComparison,
|
|
7
|
+
formatVerdict,
|
|
8
|
+
rerunWithYes,
|
|
9
|
+
resolvePromptArmSpec
|
|
10
|
+
} from "./cli-yf39avhh.js";
|
|
11
|
+
import"./cli-yz8xcpyk.js";
|
|
12
|
+
import"./cli-3retgh64.js";
|
|
13
|
+
import"./cli-mm48br00.js";
|
|
14
|
+
import"./cli-dr6x0zv7.js";
|
|
15
|
+
import"./cli-n2hng3gv.js";
|
|
16
|
+
import {
|
|
17
|
+
callControlAPI
|
|
18
|
+
} from "./cli-e23g9ywa.js";
|
|
19
|
+
import {
|
|
20
|
+
resolveApiTenantId,
|
|
21
|
+
shellArg,
|
|
22
|
+
validateConfig
|
|
23
|
+
} from "./cli-99p2jh28.js";
|
|
24
|
+
import {
|
|
25
|
+
CliInputError
|
|
26
|
+
} from "./cli-k4tqwah4.js";
|
|
27
|
+
import"./cli-9kfxkxmk.js";
|
|
28
|
+
import"./cli-ns5fcnkz.js";
|
|
29
|
+
import"./cli-0v6na3yp.js";
|
|
30
|
+
|
|
31
|
+
// src/prompts-propose.ts
|
|
32
|
+
import { writeFileSync } from "node:fs";
|
|
33
|
+
import { isAbsolute, resolve } from "node:path";
|
|
34
|
+
var PROPOSE_TIMEOUT_MS = 300000;
|
|
35
|
+
async function runPromptPropose(positionals, flags, profileOptions, context) {
|
|
36
|
+
validateConfig();
|
|
37
|
+
const promptKey = flags["prompt-key"] || positionals[1];
|
|
38
|
+
const fromRunId = flags["from-run"] || flags["from-run-id"] || flags["run-id"];
|
|
39
|
+
const replaySetId = flags["set-id"] || flags["replay-set-id"];
|
|
40
|
+
if (!promptKey || !fromRunId || !replaySetId) {
|
|
41
|
+
throw new Error("Usage: moda prompts propose --prompt-key=<key> --from-run=<run_id> --set-id=<replay_set_id> [--max-dossiers=8] [--model=<slug>] [--out=path.prompt.md] [--gate [--seeds=1-10] [--assistant-model=<slug>] [--yes]] [--promote-on-win] [--allow-any-model]");
|
|
42
|
+
}
|
|
43
|
+
const maxDossiers = parsePositiveInt(flags["max-dossiers"], 8, 16, "--max-dossiers");
|
|
44
|
+
parsePositiveInt(flags["gate-timeout"] ?? flags.timeout, 2 * 60 * 60 * 1000, 24 * 60 * 60 * 1000, flags["gate-timeout"] !== undefined ? "--gate-timeout" : "--timeout");
|
|
45
|
+
parsePositiveInt(flags["poll-interval"], 15000, 120000, "--poll-interval");
|
|
46
|
+
const model = assertAllowedModel(flags.model ?? flags["revise-model"], flags, "--model");
|
|
47
|
+
const wantGate = flags.gate === "true";
|
|
48
|
+
let gatePlan;
|
|
49
|
+
if (wantGate) {
|
|
50
|
+
const tenantId = flags["tenant-id"] || resolveApiTenantId(profileOptions);
|
|
51
|
+
if (!tenantId) {
|
|
52
|
+
throw new Error("Missing tenant id for --gate. Run `moda init` or pass --tenant-id=<id>.");
|
|
53
|
+
}
|
|
54
|
+
const seedsPerCase = parsePositiveInt(flags.seeds ?? flags["seeds-per-case"], 3, 10, flags.seeds !== undefined ? "--seeds" : "--seeds-per-case");
|
|
55
|
+
const assistantModel = assertAllowedModel(flags["assistant-model"], flags, "--assistant-model");
|
|
56
|
+
const snapshot = await fetchGateSetCases(tenantId, replaySetId, profileOptions);
|
|
57
|
+
const cases = snapshot?.ids.length ?? null;
|
|
58
|
+
const plannedPlayouts = cases === null ? null : cases * seedsPerCase * 2;
|
|
59
|
+
assertPlayoutBudget(plannedPlayouts, { seedsPerCase, cases }, context.yes, rerunWithYes("prompts propose", positionals.slice(1), flags));
|
|
60
|
+
if (context.outputMode === "human") {
|
|
61
|
+
process.stderr.write(`Gate: ${describePlayouts(plannedPlayouts, cases, seedsPerCase)}
|
|
62
|
+
`);
|
|
63
|
+
}
|
|
64
|
+
gatePlan = { tenantId, seedsPerCase, assistantModel, plannedPlayouts, cases, caseIds: snapshot?.ids };
|
|
65
|
+
}
|
|
66
|
+
const result = await callControlAPI(`/prompts/${encodeURIComponent(promptKey)}/propose`, {
|
|
67
|
+
method: "POST",
|
|
68
|
+
body: JSON.stringify({ fromRunId, replaySetId, maxDossiers, model: model || undefined })
|
|
69
|
+
}, profileOptions, { timeoutMs: PROPOSE_TIMEOUT_MS, retries: 0 });
|
|
70
|
+
let outPath;
|
|
71
|
+
if (result.status === "proposed" && result.content && flags.out) {
|
|
72
|
+
outPath = isAbsolute(flags.out) ? flags.out : resolve(process.cwd(), flags.out);
|
|
73
|
+
writeFileSync(outPath, result.content, "utf8");
|
|
74
|
+
}
|
|
75
|
+
const promoteOnWin = flags["promote-on-win"] === "true";
|
|
76
|
+
let gate;
|
|
77
|
+
if (gatePlan) {
|
|
78
|
+
if (result.status !== "proposed" || !result.content) {
|
|
79
|
+
gate = {
|
|
80
|
+
ran: false,
|
|
81
|
+
scope: "full-set",
|
|
82
|
+
scopeNote: `no candidate to gate (${result.status})`,
|
|
83
|
+
verdict: "skipped",
|
|
84
|
+
outcome: "no-run",
|
|
85
|
+
runId: null,
|
|
86
|
+
promoted: false,
|
|
87
|
+
plannedPlayouts: gatePlan.plannedPlayouts
|
|
88
|
+
};
|
|
89
|
+
} else {
|
|
90
|
+
gate = await runGate({
|
|
91
|
+
result,
|
|
92
|
+
promptKey,
|
|
93
|
+
replaySetId,
|
|
94
|
+
flags,
|
|
95
|
+
plan: gatePlan,
|
|
96
|
+
promoteOnWin,
|
|
97
|
+
profileOptions,
|
|
98
|
+
context
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (context.outputMode === "human") {
|
|
103
|
+
printHuman(result, outPath, gate);
|
|
104
|
+
}
|
|
105
|
+
context.output.writeData({
|
|
106
|
+
...result,
|
|
107
|
+
...outPath ? { outPath } : {},
|
|
108
|
+
...gate ? { gate } : {}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
async function fetchGateSetCases(tenantId, replaySetId, profileOptions) {
|
|
112
|
+
let set;
|
|
113
|
+
try {
|
|
114
|
+
set = await callControlAPI(`/tenants/${encodeURIComponent(tenantId)}/replay-sets/${encodeURIComponent(replaySetId)}`, { method: "GET" }, profileOptions);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
if (error?.statusCode === 404) {
|
|
117
|
+
throw new CliInputError(`Replay set '${replaySetId}' not found for tenant ${tenantId}, so nothing was sent.`, "Check --set-id: use the replay set the --from-run run was scored on (see `moda prompts replay-runs <key>`).");
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
if (!Array.isArray(set?.cases))
|
|
122
|
+
return null;
|
|
123
|
+
return {
|
|
124
|
+
ids: set.cases.filter((c) => String(c?.scenario ?? "").trim()).map((c, index) => String(c?.id ?? `case_${index}`))
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
async function runGate(args) {
|
|
128
|
+
const { result, promptKey, replaySetId, flags, plan, promoteOnWin, profileOptions, context } = args;
|
|
129
|
+
const { tenantId, seedsPerCase, assistantModel } = plan;
|
|
130
|
+
const prodArm = await resolvePromptArmSpec(promptKey, "baseline", { preferKey: true });
|
|
131
|
+
const proposedArm = {
|
|
132
|
+
content: result.content ?? "",
|
|
133
|
+
prompt_version_id: result.versionId ?? "",
|
|
134
|
+
prompt_id: "",
|
|
135
|
+
prompt_label: "candidate"
|
|
136
|
+
};
|
|
137
|
+
const timeoutMs = parsePositiveInt(flags["gate-timeout"] ?? flags.timeout, 2 * 60 * 60 * 1000, 24 * 60 * 60 * 1000, flags["gate-timeout"] !== undefined ? "--gate-timeout" : "--timeout");
|
|
138
|
+
const pollIntervalMs = parsePositiveInt(flags["poll-interval"], 15000, 120000, "--poll-interval");
|
|
139
|
+
const scopeNote = result.holdoutCaseIds.length ? `gated over the FULL replay set (${result.holdoutCaseIds.length} holdout case(s) could not be isolated — the run endpoint scores every case)` : "gated over the FULL replay set";
|
|
140
|
+
if (context.outputMode === "human") {
|
|
141
|
+
process.stderr.write(`
|
|
142
|
+
Gating candidate for '${promptKey}' — ${scopeNote}...
|
|
143
|
+
`);
|
|
144
|
+
}
|
|
145
|
+
const payload = await enqueueAndPollComparison({
|
|
146
|
+
tenantId,
|
|
147
|
+
replaySetId,
|
|
148
|
+
promptArms: { prod: prodArm, proposed: proposedArm },
|
|
149
|
+
seedsPerCase,
|
|
150
|
+
assistantModel: assistantModel || undefined,
|
|
151
|
+
promotePrimary: false,
|
|
152
|
+
caseIds: plan.caseIds,
|
|
153
|
+
timeoutMs,
|
|
154
|
+
pollIntervalMs,
|
|
155
|
+
onWaiting: context.outputMode === "human" ? (status) => process.stderr.write(`Waiting for gate run (${status})...
|
|
156
|
+
`) : undefined
|
|
157
|
+
}, profileOptions);
|
|
158
|
+
const outcome = candidateOutcome(payload);
|
|
159
|
+
const gate = {
|
|
160
|
+
ran: true,
|
|
161
|
+
scope: "full-set",
|
|
162
|
+
scopeNote,
|
|
163
|
+
verdict: formatVerdict(payload),
|
|
164
|
+
outcome,
|
|
165
|
+
runId: payload.run?.runId ?? null,
|
|
166
|
+
promoted: false,
|
|
167
|
+
plannedPlayouts: plan.plannedPlayouts
|
|
168
|
+
};
|
|
169
|
+
if (promoteOnWin) {
|
|
170
|
+
if (outcome === "candidate" && result.versionId) {
|
|
171
|
+
await callControlAPI(`/prompts/${encodeURIComponent(promptKey)}/labels`, { method: "POST", body: JSON.stringify({ label: "prod", versionId: result.versionId }) }, profileOptions);
|
|
172
|
+
gate.promoted = true;
|
|
173
|
+
} else {
|
|
174
|
+
gate.promoteSkippedReason = outcome === "candidate" ? "no candidate versionId to promote" : `candidate did not strictly win (${outcome})`;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return gate;
|
|
178
|
+
}
|
|
179
|
+
function printHuman(result, outPath, gate) {
|
|
180
|
+
const w = (s) => process.stderr.write(s + `
|
|
181
|
+
`);
|
|
182
|
+
if (result.status !== "proposed") {
|
|
183
|
+
w(`No candidate produced (${result.status}): ${result.note}`);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
w(`Proposed candidate for '${result.promptKey}'`);
|
|
187
|
+
w(` new version: ${result.versionId} (unlabeled — not promoted)`);
|
|
188
|
+
w(` base version: ${result.baseVersionId ?? "(none)"}`);
|
|
189
|
+
w(` from run: built from ${result.dossierCount} failure dossier(s)`);
|
|
190
|
+
if (result.changelog)
|
|
191
|
+
w(` changelog: ${result.changelog}`);
|
|
192
|
+
if (result.risks.length)
|
|
193
|
+
w(` risks: ${result.risks.map((r) => `- ${r}`).join(`
|
|
194
|
+
`)}`);
|
|
195
|
+
if (outPath)
|
|
196
|
+
w(` written to: ${outPath}`);
|
|
197
|
+
if (result.lowN) {
|
|
198
|
+
w("");
|
|
199
|
+
w(` ⚠ low sample: ${result.lowN}`);
|
|
200
|
+
}
|
|
201
|
+
w("");
|
|
202
|
+
if (gate?.ran) {
|
|
203
|
+
w("Gate (automatic A/B of candidate vs baseline):");
|
|
204
|
+
w(` scope: ${gate.scopeNote}`);
|
|
205
|
+
w(` verdict: ${gate.verdict}`);
|
|
206
|
+
if (gate.plannedPlayouts !== undefined)
|
|
207
|
+
w(` playouts: ${gate.plannedPlayouts ?? "unknown"} planned`);
|
|
208
|
+
if (gate.runId)
|
|
209
|
+
w(` run id: ${gate.runId}`);
|
|
210
|
+
if (gate.promoted) {
|
|
211
|
+
w(` promoted: candidate won — promoted version ${result.versionId} to label 'prod'`);
|
|
212
|
+
} else if (gate.promoteSkippedReason) {
|
|
213
|
+
w(` promote: skipped (${gate.promoteSkippedReason})`);
|
|
214
|
+
}
|
|
215
|
+
w("");
|
|
216
|
+
if (!gate.promoted) {
|
|
217
|
+
w(` moda prompts promote ${shellArg(result.promptKey)} --label=prod --version=${result.versionId ? shellArg(result.versionId) : "<version_id>"}`);
|
|
218
|
+
}
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (gate && !gate.ran) {
|
|
222
|
+
w(`Gate not run: ${gate.scopeNote}`);
|
|
223
|
+
w("");
|
|
224
|
+
}
|
|
225
|
+
w("Next — gate on holdout cases the revision never saw, then promote if it wins:");
|
|
226
|
+
if (result.holdoutCaseIds.length) {
|
|
227
|
+
w(` holdout cases (${result.holdoutCaseIds.length}): ${result.holdoutCaseIds.join(", ")}`);
|
|
228
|
+
} else {
|
|
229
|
+
w(" (too few failures to reserve a holdout — gate on a fresh --auto-generate set)");
|
|
230
|
+
}
|
|
231
|
+
if (outPath) {
|
|
232
|
+
w(` moda prompts ab --baseline-key=${shellArg(result.promptKey)} --candidate=${shellArg(outPath)} --auto-generate`);
|
|
233
|
+
} else {
|
|
234
|
+
w(` # re-run with --out=<path.prompt.md> to materialize the candidate, then:`);
|
|
235
|
+
w(` moda prompts ab --baseline-key=${shellArg(result.promptKey)} --candidate=<path.prompt.md> --auto-generate`);
|
|
236
|
+
}
|
|
237
|
+
w(` # or: moda prompts propose ... --gate --promote-on-win (auto-A/B, promote on a strict win)`);
|
|
238
|
+
w(` moda prompts promote ${shellArg(result.promptKey)} --label=prod --version=${result.versionId ? shellArg(result.versionId) : "<version_id>"}`);
|
|
239
|
+
}
|
|
240
|
+
function parsePositiveInt(raw, fallback, max, flagName) {
|
|
241
|
+
if (raw === undefined)
|
|
242
|
+
return fallback;
|
|
243
|
+
const text = String(raw).trim();
|
|
244
|
+
const parsed = Number(text);
|
|
245
|
+
if (!/^\d+$/.test(text) || !Number.isSafeInteger(parsed) || parsed < 1) {
|
|
246
|
+
throw new CliInputError(`${flagName}=${raw} must be a whole number of at least 1, so nothing was sent.`, `Use ${flagName}=N with 1 <= N <= ${max}.`);
|
|
247
|
+
}
|
|
248
|
+
if (parsed > max) {
|
|
249
|
+
throw new CliInputError(`${flagName}=${parsed} is above the maximum of ${max}, so nothing was sent.`, `Use ${flagName}=${max} or less.`);
|
|
250
|
+
}
|
|
251
|
+
return parsed;
|
|
252
|
+
}
|
|
253
|
+
export {
|
|
254
|
+
runPromptPropose
|
|
255
|
+
};
|