@momentiq/dark-factory-cli 2.2.2 → 2.2.4
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/onboard/generate-plan.d.ts.map +1 -1
- package/dist/onboard/generate-plan.js +100 -30
- package/dist/onboard/generate-plan.js.map +1 -1
- package/dist/onboard/llm-client.d.ts +49 -20
- package/dist/onboard/llm-client.d.ts.map +1 -1
- package/dist/onboard/llm-client.js +21 -2
- package/dist/onboard/llm-client.js.map +1 -1
- package/dist/onboard/prompts/scaffold.md +9 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-plan.d.ts","sourceRoot":"","sources":["../../src/onboard/generate-plan.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"generate-plan.d.ts","sourceRoot":"","sources":["../../src/onboard/generate-plan.ts"],"names":[],"mappings":"AAsBA,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAEL,KAAK,SAAS,EAEd,KAAK,SAAS,EACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB;;;gCAG4B;IAC5B,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC;IAE3B,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC;CAEpE;AAYD,wBAAsB,YAAY,CAChC,QAAQ,EAAE,YAAY,EACtB,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,YAAY,CAAC,CAoGvB"}
|
|
@@ -1,21 +1,46 @@
|
|
|
1
1
|
// packages/cli/src/onboard/generate-plan.ts
|
|
2
2
|
//
|
|
3
3
|
// Stage B entry point. Composes the prompt renderer, the LLM client, and
|
|
4
|
-
// the ScaffoldPlanSchema validator into one async function.
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
4
|
+
// the ScaffoldPlanSchema validator into one async function.
|
|
5
|
+
//
|
|
6
|
+
// Corrective-retry loop (#158): on Zod validation failure we don't re-ask
|
|
7
|
+
// blind. We replay the model's own malformed `tool_use` block as an
|
|
8
|
+
// `assistant` turn and reply with a `tool_result` user turn carrying the
|
|
9
|
+
// specific Zod issues. That gives the model *patch mode* (fix exactly the
|
|
10
|
+
// fields the validator flagged) instead of regen mode (rewrite the whole
|
|
11
|
+
// plan and risk re-breaking parts that were already valid). Empirically on
|
|
12
|
+
// sage-blueprint the first attempt returned `files` as a string + summary
|
|
13
|
+
// >800 chars; the second attempt (text-only feedback) converged on `files`
|
|
14
|
+
// as an array but dropped `rationale` on some items — convergence proves
|
|
15
|
+
// the model *can* correct, but one round of abstract text-only feedback
|
|
16
|
+
// wasn't enough. Multi-turn replay with the model's actual prior output
|
|
17
|
+
// pushes per-round effectiveness up; a small bounded loop (MAX_ATTEMPTS)
|
|
18
|
+
// then catches the residual cases without ballooning cost (each round is
|
|
19
|
+
// ~5 min wallclock on the full template).
|
|
8
20
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
9
21
|
import { ScaffoldPlanSchema, SCAFFOLD_PLAN_BYTE_BUDGET, } from "./scaffold-schema.js";
|
|
10
22
|
import { renderScaffoldPrompt } from "./prompts.js";
|
|
11
23
|
import { callScaffoldLlm, } from "./llm-client.js";
|
|
12
24
|
const TOOL_NAME = "emit_scaffold_plan";
|
|
25
|
+
// Total LLM calls (initial attempt + up to MAX_ATTEMPTS-1 corrective
|
|
26
|
+
// rounds). Each round on the sage-blueprint template costs ~5 min wallclock
|
|
27
|
+
// + ~960k input tokens, so this is the dominant cost knob. 3 attempts is
|
|
28
|
+
// the sweet spot empirically: attempt 1 makes the bulk of the plan;
|
|
29
|
+
// attempt 2 corrects most violations; attempt 3 catches the long tail
|
|
30
|
+
// without inflating cost in the happy path (which exits after attempt 1).
|
|
31
|
+
const MAX_ATTEMPTS = 3;
|
|
13
32
|
export async function generatePlan(analysis, template, opts) {
|
|
14
33
|
const { systemPrompt, userMessage } = await renderScaffoldPrompt(analysis, template.files, { profile: opts.profile });
|
|
15
34
|
// Anthropic's tool-use `input_schema` requires `type: "object"` at the top
|
|
16
35
|
// level. zodToJsonSchema(s, NAME) emits `{$ref, definitions: {NAME: ...}}`
|
|
17
36
|
// — no top-level type, so the API rejects it with `tools.0.custom.input_schema.type:
|
|
18
37
|
// Field required`. Omit the name to get the schema inlined at top level.
|
|
38
|
+
// Empirical verification (2026-06-06, #158): the derived schema already
|
|
39
|
+
// mirrors the zod contract — `files: {type: array, items: {required:
|
|
40
|
+
// [..., "rationale"]}}`, `summary: {maxLength: 800}`. Tool-call schemas
|
|
41
|
+
// are advisory in the Anthropic API (not enforced at the boundary like
|
|
42
|
+
// Vertex's strict mode), so a tight schema steers the model but doesn't
|
|
43
|
+
// *guarantee* a valid response — hence the corrective-retry loop below.
|
|
19
44
|
const inputSchema = zodToJsonSchema(ScaffoldPlanSchema);
|
|
20
45
|
const callLlm = opts.callLlm ?? ((i) => callScaffoldLlm(i));
|
|
21
46
|
// exactOptionalPropertyTypes forbids assigning `undefined` to an optional
|
|
@@ -28,34 +53,65 @@ export async function generatePlan(analysis, template, opts) {
|
|
|
28
53
|
modelId: opts.modelId,
|
|
29
54
|
...(opts.apiKey !== undefined ? { apiKey: opts.apiKey } : {}),
|
|
30
55
|
};
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
56
|
+
// Conversational state grows across corrective rounds. Initial turn is the
|
|
57
|
+
// rendered user message (analysis + template bodies + tailoring rules).
|
|
58
|
+
const messages = [{ role: "user", content: userMessage }];
|
|
59
|
+
const attemptIssues = [];
|
|
60
|
+
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
61
|
+
const result = await callLlm({ ...baseInputs, messages });
|
|
62
|
+
const candidate = stampTemplateRef(result.planJson, template);
|
|
63
|
+
const parsed = ScaffoldPlanSchema.safeParse(candidate);
|
|
64
|
+
if (parsed.success) {
|
|
65
|
+
// Byte-budget backstop (per B-D2). Per-array caps and per-field
|
|
66
|
+
// maxLength catch most overflows; this is the final guardrail before
|
|
67
|
+
// the writer.
|
|
68
|
+
const serialized = JSON.stringify(parsed.data);
|
|
69
|
+
if (serialized.length > SCAFFOLD_PLAN_BYTE_BUDGET) {
|
|
70
|
+
throw new Error(`df onboard: ScaffoldPlan exceeds ${SCAFFOLD_PLAN_BYTE_BUDGET}-byte (64 KB) budget: ` +
|
|
71
|
+
`produced ${serialized.length} bytes. Tighten per-file tailored_content or files[] count.`);
|
|
72
|
+
}
|
|
73
|
+
return parsed.data;
|
|
49
74
|
}
|
|
75
|
+
attemptIssues.push(parsed.error.issues);
|
|
76
|
+
if (attempt === MAX_ATTEMPTS)
|
|
77
|
+
break;
|
|
78
|
+
// Build the corrective turn pair: replay the assistant's actual tool_use
|
|
79
|
+
// block (so the model sees its *own* prior output verbatim, not an
|
|
80
|
+
// abstract description of it) + a tool_result user turn carrying the
|
|
81
|
+
// specific Zod issues. The Anthropic API is stateless; we own both
|
|
82
|
+
// sides of the conversation, so a synthesized `tool_use_id` is fine —
|
|
83
|
+
// it just has to be consistent between the assistant `tool_use` and
|
|
84
|
+
// the user `tool_result` in the same round.
|
|
85
|
+
const synthesizedToolUseId = `toolu_retry_${attempt}`;
|
|
86
|
+
messages.push({
|
|
87
|
+
role: "assistant",
|
|
88
|
+
content: [
|
|
89
|
+
{
|
|
90
|
+
type: "tool_use",
|
|
91
|
+
id: synthesizedToolUseId,
|
|
92
|
+
name: TOOL_NAME,
|
|
93
|
+
input: result.planJson,
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
}, {
|
|
97
|
+
role: "user",
|
|
98
|
+
content: [
|
|
99
|
+
{
|
|
100
|
+
type: "tool_result",
|
|
101
|
+
tool_use_id: synthesizedToolUseId,
|
|
102
|
+
is_error: true,
|
|
103
|
+
content: formatCorrectiveFeedback(parsed.error.issues),
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
});
|
|
50
107
|
}
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
return parsed.data;
|
|
108
|
+
// Exhausted retries — surface every round's issues so the operator (or a
|
|
109
|
+
// critic) can see *what* the model kept getting wrong, not just that it
|
|
110
|
+
// failed N times.
|
|
111
|
+
const summary = attemptIssues
|
|
112
|
+
.map((issues, i) => `attempt ${i + 1}: ${formatZodIssues(issues)}`)
|
|
113
|
+
.join(" | ");
|
|
114
|
+
throw new Error(`df onboard: scaffold plan validation failed after ${MAX_ATTEMPTS} attempts. ${summary}`);
|
|
59
115
|
}
|
|
60
116
|
// stampTemplateRef forces the plan's templateRef to be the loader's canonicalRef,
|
|
61
117
|
// not the LLM's value — the LLM could echo a stale or fabricated ref, and the
|
|
@@ -69,4 +125,18 @@ function stampTemplateRef(planJson, template) {
|
|
|
69
125
|
function formatZodIssues(issues) {
|
|
70
126
|
return issues.map((i) => `${i.path.join(".") || "<root>"}: ${i.message}`).join("; ");
|
|
71
127
|
}
|
|
128
|
+
// Format the Zod issues for the corrective tool_result turn. The model sees
|
|
129
|
+
// its own prior output (via the assistant `tool_use` block we replay), so the
|
|
130
|
+
// feedback only needs to enumerate *what to fix*, not echo *what was wrong* —
|
|
131
|
+
// which the model can read off its own output. Keep it terse: each issue is
|
|
132
|
+
// path + message, and we anchor the instruction so the model patches in place
|
|
133
|
+
// instead of regenerating the whole plan from scratch.
|
|
134
|
+
function formatCorrectiveFeedback(issues) {
|
|
135
|
+
const lines = issues.map((i) => `- ${i.path.join(".") || "<root>"}: ${i.message}`);
|
|
136
|
+
return ("Your previous `emit_scaffold_plan` call failed schema validation. " +
|
|
137
|
+
"Fix ONLY the issues below; keep every other field unchanged. " +
|
|
138
|
+
"Emit a corrected `emit_scaffold_plan` tool call.\n\n" +
|
|
139
|
+
"Issues:\n" +
|
|
140
|
+
lines.join("\n"));
|
|
141
|
+
}
|
|
72
142
|
//# sourceMappingURL=generate-plan.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-plan.js","sourceRoot":"","sources":["../../src/onboard/generate-plan.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,EAAE;AACF,yEAAyE;AACzE,yEAAyE;AACzE,
|
|
1
|
+
{"version":3,"file":"generate-plan.js","sourceRoot":"","sources":["../../src/onboard/generate-plan.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,31 @@
|
|
|
1
|
+
export type LlmContentBlock = {
|
|
2
|
+
type: "text";
|
|
3
|
+
text: string;
|
|
4
|
+
} | {
|
|
5
|
+
type: "tool_use";
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
input: unknown;
|
|
9
|
+
} | {
|
|
10
|
+
type: "tool_result";
|
|
11
|
+
tool_use_id: string;
|
|
12
|
+
is_error?: boolean;
|
|
13
|
+
content: string | Array<{
|
|
14
|
+
type: "text";
|
|
15
|
+
text: string;
|
|
16
|
+
}>;
|
|
17
|
+
};
|
|
18
|
+
export interface LlmMessage {
|
|
19
|
+
role: "user" | "assistant";
|
|
20
|
+
content: string | LlmContentBlock[];
|
|
21
|
+
}
|
|
1
22
|
export interface LlmInputs {
|
|
2
23
|
systemPrompt: string;
|
|
3
|
-
|
|
24
|
+
/** Conversational turn list. The first turn is conventionally the user
|
|
25
|
+
* prompt; for corrective retries, callers append an `assistant` turn that
|
|
26
|
+
* replays the prior malformed `tool_use` block + a `user` turn whose
|
|
27
|
+
* content is a `tool_result` carrying the error feedback. */
|
|
28
|
+
messages: LlmMessage[];
|
|
4
29
|
toolName: string;
|
|
5
30
|
toolInputSchema: object;
|
|
6
31
|
apiKey?: string;
|
|
@@ -14,27 +39,31 @@ export interface LlmResult {
|
|
|
14
39
|
outputTokens: number;
|
|
15
40
|
attempts: number;
|
|
16
41
|
}
|
|
42
|
+
export interface LlmStreamResult {
|
|
43
|
+
id: string;
|
|
44
|
+
model: string;
|
|
45
|
+
content: Array<{
|
|
46
|
+
type: "tool_use";
|
|
47
|
+
name: string;
|
|
48
|
+
input: unknown;
|
|
49
|
+
} | {
|
|
50
|
+
type: "text";
|
|
51
|
+
text: string;
|
|
52
|
+
} | {
|
|
53
|
+
type: string;
|
|
54
|
+
}>;
|
|
55
|
+
usage: {
|
|
56
|
+
input_tokens: number;
|
|
57
|
+
output_tokens: number;
|
|
58
|
+
};
|
|
59
|
+
stop_reason: string;
|
|
60
|
+
}
|
|
61
|
+
export interface LlmStreamHandle {
|
|
62
|
+
finalMessage(): Promise<LlmStreamResult>;
|
|
63
|
+
}
|
|
17
64
|
export interface LlmClientLike {
|
|
18
65
|
messages: {
|
|
19
|
-
|
|
20
|
-
id: string;
|
|
21
|
-
model: string;
|
|
22
|
-
content: Array<{
|
|
23
|
-
type: "tool_use";
|
|
24
|
-
name: string;
|
|
25
|
-
input: unknown;
|
|
26
|
-
} | {
|
|
27
|
-
type: "text";
|
|
28
|
-
text: string;
|
|
29
|
-
} | {
|
|
30
|
-
type: string;
|
|
31
|
-
}>;
|
|
32
|
-
usage: {
|
|
33
|
-
input_tokens: number;
|
|
34
|
-
output_tokens: number;
|
|
35
|
-
};
|
|
36
|
-
stop_reason: string;
|
|
37
|
-
}>;
|
|
66
|
+
stream(params: unknown): LlmStreamHandle;
|
|
38
67
|
};
|
|
39
68
|
}
|
|
40
69
|
export type LlmClientFactory = (apiKey: string) => LlmClientLike;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm-client.d.ts","sourceRoot":"","sources":["../../src/onboard/llm-client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"llm-client.d.ts","sourceRoot":"","sources":["../../src/onboard/llm-client.ts"],"names":[],"mappings":"AAyCA,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAC9D;IACE,IAAI,EAAE,aAAa,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACzD,CAAC;AAEN,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,eAAe,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB;;;kEAG8D;IAC9D,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,KAAK,CACV;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,GAClD;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAC9B;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CACnB,CAAC;IACF,KAAK,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IACvD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,YAAY,IAAI,OAAO,CAAC,eAAe,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE;QAIR,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,eAAe,CAAC;KAC1C,CAAC;CACH;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,aAAa,CAAC;AAkBjE,MAAM,WAAW,WAAW;IAC1B,YAAY,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,SAAS,EACjB,IAAI,GAAE,WAAgB,GACrB,OAAO,CAAC,SAAS,CAAC,CAsFpB"}
|
|
@@ -14,6 +14,17 @@
|
|
|
14
14
|
// block's `input` as the candidate JSON. The SDK validates the JSON
|
|
15
15
|
// shape at the API boundary; downstream Zod validation re-checks the
|
|
16
16
|
// semantics (discriminated-union, byte budgets).
|
|
17
|
+
//
|
|
18
|
+
// Why streaming (client.messages.stream) instead of client.messages.create:
|
|
19
|
+
// the inlined sage-blueprint template pushes Phase B's request to ~960k
|
|
20
|
+
// input tokens + 32k output, which routinely exceeds Anthropic's 10-minute
|
|
21
|
+
// hard guard on non-streaming requests ("Streaming is required for
|
|
22
|
+
// operations that may take longer than 10 minutes"). The SDK refused the
|
|
23
|
+
// request pre-flight before any retry logic could fire — see #147 for the
|
|
24
|
+
// full empirical trace. The stream() helper auto-assembles tool_use input
|
|
25
|
+
// from input_json_delta events into the final Message; finalMessage() then
|
|
26
|
+
// returns the same { content, usage, stop_reason, model } shape we already
|
|
27
|
+
// read from create(), so the rest of the call site is unchanged.
|
|
17
28
|
import Anthropic from "@anthropic-ai/sdk";
|
|
18
29
|
const DEFAULT_FACTORY = (apiKey) => new Anthropic({ apiKey });
|
|
19
30
|
const TRANSIENT_STATUSES = new Set([429, 500, 502, 503, 504]);
|
|
@@ -56,14 +67,22 @@ export async function callScaffoldLlm(inputs, opts = {}) {
|
|
|
56
67
|
},
|
|
57
68
|
],
|
|
58
69
|
tool_choice: { type: "tool", name: inputs.toolName },
|
|
59
|
-
messages:
|
|
70
|
+
messages: inputs.messages,
|
|
60
71
|
};
|
|
61
72
|
let attempts = 0;
|
|
62
73
|
let lastErr = null;
|
|
63
74
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
64
75
|
attempts++;
|
|
65
76
|
try {
|
|
66
|
-
|
|
77
|
+
// Both the stream() call and finalMessage() awaiting must live inside
|
|
78
|
+
// the try block — with streaming, the SDK's API errors (401/429/5xx,
|
|
79
|
+
// network failures) now surface as rejections on finalMessage() rather
|
|
80
|
+
// than on the synchronous stream() call. The SDK's APIError subclasses
|
|
81
|
+
// still carry .status, so isTransient() classification continues to
|
|
82
|
+
// work; tests must mock by throwing from finalMessage() with .status
|
|
83
|
+
// set to keep that contract verified.
|
|
84
|
+
const stream = client.messages.stream(request);
|
|
85
|
+
const r = await stream.finalMessage();
|
|
67
86
|
// Opt-in diagnostic — stop_reason + token counts + content shape are the
|
|
68
87
|
// discriminating signals when downstream Zod validation fails on the
|
|
69
88
|
// returned plan (truncation vs. malformed output vs. wrong block type).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm-client.js","sourceRoot":"","sources":["../../src/onboard/llm-client.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,kEAAkE;AAClE,EAAE;AACF,uEAAuE;AACvE,wEAAwE;AACxE,oEAAoE;AACpE,wEAAwE;AACxE,0EAA0E;AAC1E,EAAE;AACF,qEAAqE;AACrE,yEAAyE;AACzE,qEAAqE;AACrE,oEAAoE;AACpE,qEAAqE;AACrE,iDAAiD;
|
|
1
|
+
{"version":3,"file":"llm-client.js","sourceRoot":"","sources":["../../src/onboard/llm-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,6 +14,15 @@ prose text outside of the tool call. The tool's `input_schema` is strict; fields
|
|
|
14
14
|
outside the schema are rejected, and the action discriminator forces an unambiguous
|
|
15
15
|
choice per file.
|
|
16
16
|
|
|
17
|
+
**Shape reminders (empirical failure modes #158):**
|
|
18
|
+
|
|
19
|
+
- `files` is a JSON **array** of objects. Never a string, never a stringified
|
|
20
|
+
array. Each item is a real object.
|
|
21
|
+
- Every file item REQUIRES `rationale` (a non-empty string ≤ 800 chars).
|
|
22
|
+
`emit` and `merge` items additionally require `tailored_content`; `skip`
|
|
23
|
+
items must NOT include `tailored_content`.
|
|
24
|
+
- `summary` is ≤ 800 characters. If you need to say more, trim — don't exceed.
|
|
25
|
+
|
|
17
26
|
For every file in the input template, choose exactly ONE action:
|
|
18
27
|
|
|
19
28
|
| Action | Use when | Required fields |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@momentiq/dark-factory-cli",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.4",
|
|
4
4
|
"description": "Dark Factory OSS CLI — multi-vendor adversarial critic orchestration (Cursor, Codex, Gemini, Grok) with min-complete-quorum aggregation and trusted-surface rebind",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://momentiq.ai/dark-factory",
|