@ggui-ai/ui-gen 0.26.0 → 0.28.0
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/{adapter-BJwn74rx.d.ts → adapter-h4_V69LI.d.ts} +1 -1
- package/dist/adapters/base.d.ts +6 -1
- package/dist/adapters/base.js.map +1 -1
- package/dist/adapters/generation-dispatch.d.ts +2 -2
- package/dist/adapters/generation-dispatch.js +233 -17
- package/dist/adapters/generation-dispatch.js.map +1 -1
- package/dist/adapters/index.js +36 -14
- package/dist/adapters/index.js.map +1 -1
- package/dist/advanced/index.d.ts +4 -4
- package/dist/advanced/index.js +268 -28
- package/dist/advanced/index.js.map +1 -1
- package/dist/boilerplate.js +4 -6
- package/dist/boilerplate.js.map +1 -1
- package/dist/check/index.d.ts +1 -7
- package/dist/check/index.js +50 -4
- package/dist/check/index.js.map +1 -1
- package/dist/{create-ui-generator-N4gRXpcL.d.ts → create-ui-generator-BrZHF0cf.d.ts} +2 -2
- package/dist/{dispatch-BUoXRj_r.d.ts → dispatch-B07TI30d.d.ts} +1 -1
- package/dist/evaluation/axis-checks/index.d.ts +3 -3
- package/dist/evaluation/axis-checks/index.js +97 -1
- package/dist/evaluation/axis-checks/index.js.map +1 -1
- package/dist/evaluation/axis-checks/registry.d.ts +1 -1
- package/dist/evaluation/axis-checks/registry.js +97 -1
- package/dist/evaluation/axis-checks/registry.js.map +1 -1
- package/dist/evaluation/index.d.ts +22 -12
- package/dist/evaluation/index.js +138 -14
- package/dist/evaluation/index.js.map +1 -1
- package/dist/evaluation/types.d.ts +1 -1
- package/dist/harness/check/runtime-render/index.d.ts +3 -3
- package/dist/harness/index.d.ts +1 -1
- package/dist/harness/index.js +6 -6
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/prompts.js +1 -4
- package/dist/harness/prompts.js.map +1 -1
- package/dist/harness/result-types.d.ts +14 -3
- package/dist/harness/result-types.js.map +1 -1
- package/dist/harness/runtime.d.ts +1 -1
- package/dist/harness/runtime.js +6 -7
- package/dist/harness/runtime.js.map +1 -1
- package/dist/harness/types-public.d.ts +1 -1
- package/dist/harness/types-public.js +4 -6
- package/dist/harness/types-public.js.map +1 -1
- package/dist/harness/validator-trace-sink.d.ts +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +268 -28
- package/dist/index.js.map +1 -1
- package/dist/patch.d.ts +1 -1
- package/dist/{types-public-B7wn4ce5.d.ts → types-public-h8IMnYuh.d.ts} +4 -0
- package/dist/{visual-evaluator-C9L-o6ap.d.ts → visual-evaluator-C2strxqD.d.ts} +1 -1
- package/dist/workflows.d.ts +1 -1
- package/package.json +13 -13
- package/src/adapters/base.ts +6 -1
- package/src/adapters/claude/message-parser.ts +5 -6
- package/src/adapters/claude/sdk-tool-pins.ts +44 -0
- package/src/adapters/claude/sdk.ts +3 -3
- package/src/adapters/claude/source-capture.ts +20 -0
- package/src/adapters/provider-router.ts +1 -0
- package/src/boilerplate/system-prompt.ts +3 -2
- package/src/check/run-tier0.ts +61 -0
- package/src/create-ui-generator.ts +4 -2
- package/src/design-system-docs.ts +2 -1
- package/src/evaluation/axis-checks/checks/state-merge.ts +1 -1
- package/src/evaluation/axis-checks/helpers.ts +125 -0
- package/src/evaluation/criteria/resolve.ts +19 -1
- package/src/evaluation/evaluator.ts +2 -2
- package/src/evaluation/llm-evaluator.ts +2 -2
- package/src/evaluation/loop.ts +25 -4
- package/src/evaluation/message-parsing.ts +11 -12
- package/src/evaluation/types-public.ts +4 -0
- package/src/evaluation/visual-evaluator.ts +1 -1
- package/src/harness/coding/assemble-result.ts +1 -0
- package/src/harness/coding/dupe-break.ts +70 -0
- package/src/harness/coding/generate-task-runner.ts +5 -1
- package/src/harness/coding/run-coding-turn.ts +56 -1
- package/src/harness/result-types.ts +12 -0
- package/src/index.ts +1 -1
- package/src/taught-tokens.ts +5 -6
package/dist/patch.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { PatchFn } from './harness/types-public.js';
|
|
|
2
2
|
import '@ggui-ai/protocol';
|
|
3
3
|
import './styling-profile-CxfvV48g.js';
|
|
4
4
|
import './axes-CKVCzR_x.js';
|
|
5
|
-
import './types-public-
|
|
5
|
+
import './types-public-h8IMnYuh.js';
|
|
6
6
|
import './llm.js';
|
|
7
7
|
import '@ggui-ai/mcp-server-core';
|
|
8
8
|
import './design-mode-DGWN_Gkm.js';
|
|
@@ -599,6 +599,10 @@ interface CanvasJudgeRecord {
|
|
|
599
599
|
/**
|
|
600
600
|
* ggui#1687 — the model asked for, and every model the provider said answered this canvas's calls (scoring and
|
|
601
601
|
* criteria), deduplicated. `served` is empty when no provider named one: unknown, never "the requested model".
|
|
602
|
+
* When the request names an alias and the provider echoes the alias (measured on a hosted route, 125 of 125
|
|
603
|
+
* canvases), `served` confirms the alias was honoured but cannot tell two snapshots behind it apart. A judge that
|
|
604
|
+
* must is pinned to a dated snapshot id instead. With the alias, a snapshot change shows only as a step on a date in
|
|
605
|
+
* the judge's pooled within-call spread, read by week. That is a read someone runs, not a monitor.
|
|
602
606
|
*/
|
|
603
607
|
readonly models?: {
|
|
604
608
|
readonly requested: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { h as CriteriaContext, i as CriteriaChroma, j as CanvasJudgeRecord, k as CriteriaBlock, l as CanvasPresentationOutcome, n as CanvasHostPresentation, b as AgentConfig, L as LLMResponse, J as JudgeSalvageCounts, E as EvalIssue, o as VisualEvalSummary, p as VisualCoverage } from './types-public-
|
|
1
|
+
import { h as CriteriaContext, i as CriteriaChroma, j as CanvasJudgeRecord, k as CriteriaBlock, l as CanvasPresentationOutcome, n as CanvasHostPresentation, b as AgentConfig, L as LLMResponse, J as JudgeSalvageCounts, E as EvalIssue, o as VisualEvalSummary, p as VisualCoverage } from './types-public-h8IMnYuh.js';
|
|
2
2
|
import { G as GenerationProfileInput } from './styling-profile-CxfvV48g.js';
|
|
3
3
|
import { C as CriteriaBank } from './bank-Ctdp3FUA.js';
|
|
4
4
|
import { DataContract, JsonObject } from '@ggui-ai/protocol';
|
package/dist/workflows.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { C as Classification } from './axes-CKVCzR_x.js';
|
|
|
2
2
|
import { Workflow } from './harness/types-public.js';
|
|
3
3
|
import '@ggui-ai/protocol';
|
|
4
4
|
import './styling-profile-CxfvV48g.js';
|
|
5
|
-
import './types-public-
|
|
5
|
+
import './types-public-h8IMnYuh.js';
|
|
6
6
|
import './llm.js';
|
|
7
7
|
import '@ggui-ai/mcp-server-core';
|
|
8
8
|
import './design-mode-DGWN_Gkm.js';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ggui-ai/ui-gen",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "UI generation harness for
|
|
3
|
+
"version": "0.28.0",
|
|
4
|
+
"description": "UI generation harness for GGUI — the LLM system prompt, multi-turn coding loop, and evaluation engine that turn a DataContract into a React component. Implements the UiGenerator contract from @ggui-ai/mcp-server-core; consumed by @ggui-ai/mcp-server.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"ggui",
|
|
@@ -189,9 +189,9 @@
|
|
|
189
189
|
"puppeteer-core": "^24.40.0 || ^25.0.0",
|
|
190
190
|
"react": "^18.0.0 || ^19.0.0",
|
|
191
191
|
"react-dom": "^18.0.0 || ^19.0.0",
|
|
192
|
-
"@ggui-ai/
|
|
193
|
-
"@ggui-ai/
|
|
194
|
-
"@ggui-ai/
|
|
192
|
+
"@ggui-ai/project-config": "0.28.0",
|
|
193
|
+
"@ggui-ai/mcp-server-core": "0.28.0",
|
|
194
|
+
"@ggui-ai/protocol": "0.28.0"
|
|
195
195
|
},
|
|
196
196
|
"peerDependenciesMeta": {
|
|
197
197
|
"@google/adk": {
|
|
@@ -233,11 +233,11 @@
|
|
|
233
233
|
"openai": "^7.9.0",
|
|
234
234
|
"typescript": "^5.0.0",
|
|
235
235
|
"zod": "^4.3.6",
|
|
236
|
-
"@ggui-ai/
|
|
237
|
-
"@ggui-ai/gadgets": "0.
|
|
238
|
-
"@ggui-ai/
|
|
239
|
-
"@ggui-ai/
|
|
240
|
-
"@ggui-ai/
|
|
236
|
+
"@ggui-ai/sandbox": "0.28.0",
|
|
237
|
+
"@ggui-ai/gadgets": "0.28.0",
|
|
238
|
+
"@ggui-ai/wire": "0.28.0",
|
|
239
|
+
"@ggui-ai/ui-visual-tester": "0.28.0",
|
|
240
|
+
"@ggui-ai/design": "0.28.0"
|
|
241
241
|
},
|
|
242
242
|
"devDependencies": {
|
|
243
243
|
"@google/adk": "^2.0.0",
|
|
@@ -252,9 +252,9 @@
|
|
|
252
252
|
"react-dom": "^19.0.0",
|
|
253
253
|
"tsup": "^8.5.0",
|
|
254
254
|
"vitest": "^3.2.6",
|
|
255
|
-
"@ggui-ai/
|
|
256
|
-
"@ggui-ai/
|
|
257
|
-
"@ggui-ai/protocol": "0.
|
|
255
|
+
"@ggui-ai/mcp-server-core": "0.28.0",
|
|
256
|
+
"@ggui-ai/project-config": "0.28.0",
|
|
257
|
+
"@ggui-ai/protocol": "0.28.0"
|
|
258
258
|
},
|
|
259
259
|
"engines": {
|
|
260
260
|
"node": ">=22.0.0"
|
package/src/adapters/base.ts
CHANGED
|
@@ -45,8 +45,13 @@ export interface ClaudeSdkConfig extends AdapterConfig {
|
|
|
45
45
|
env?: Record<string, string>;
|
|
46
46
|
/** MCP server configs for Claude Agent SDK (keyed by name) */
|
|
47
47
|
mcpServers?: Record<string, McpServerConfig>;
|
|
48
|
-
/**
|
|
48
|
+
/** Tool names to approve. Absent → every tool of every server in `mcpServers` (ggui#1714). */
|
|
49
49
|
allowedTools?: string[];
|
|
50
|
+
/**
|
|
51
|
+
* CLI built-in tools to offer the model (e.g. `['Write']`). Default: none.
|
|
52
|
+
* An explicit opt-in: a built-in that writes or executes acts on the host (ggui#1714).
|
|
53
|
+
*/
|
|
54
|
+
builtinTools?: readonly string[];
|
|
50
55
|
/** Subagent definitions for Claude Agent SDK */
|
|
51
56
|
agents?: Record<string, AgentDefinition>;
|
|
52
57
|
/** Custom CLI path for Docker deployments */
|
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
SDKUserMessage,
|
|
14
14
|
} from '@anthropic-ai/claude-agent-sdk';
|
|
15
15
|
import type { JsonObject } from '@ggui-ai/protocol';
|
|
16
|
+
import { sourceCodeFromToolUse } from './source-capture';
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Mutable accumulator for artifacts extracted from the SDK message stream.
|
|
@@ -197,12 +198,10 @@ function extractSourceCode(message: SDKMessage, artifacts: MessageArtifacts): vo
|
|
|
197
198
|
if (!Array.isArray(content)) return;
|
|
198
199
|
|
|
199
200
|
for (const block of content) {
|
|
200
|
-
if (block.type
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
205
|
-
}
|
|
201
|
+
if (block.type !== 'tool_use') continue;
|
|
202
|
+
// ggui#1714 — the rule lives in source-capture.ts, shared with the evaluation loop.
|
|
203
|
+
const value = sourceCodeFromToolUse(block.name, block.input);
|
|
204
|
+
if (value !== undefined) artifacts.sourceCode = value;
|
|
206
205
|
}
|
|
207
206
|
}
|
|
208
207
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// packages/ui-gen/src/adapters/claude/sdk-tool-pins.ts
|
|
2
|
+
//
|
|
3
|
+
// ggui#1714 — the tool surface of every Agent SDK `query()` this package runs.
|
|
4
|
+
//
|
|
5
|
+
// Left to its defaults, a `query()` session offers the model the CLI's whole
|
|
6
|
+
// built-in set (Bash, Edit, Read, Write, WebFetch, Task, …) and loads the host
|
|
7
|
+
// user's own Claude configuration (plugins, MCP servers from `~/.claude`).
|
|
8
|
+
// `allowedTools` does not narrow that: it only approves the names it lists.
|
|
9
|
+
// Measured on SDK 0.3.229 from the session's `system`/`init` message:
|
|
10
|
+
//
|
|
11
|
+
// as before this file → every built-in + the caller's tools
|
|
12
|
+
// `tools: []` alone → the caller's tools + a user-level plugin server
|
|
13
|
+
// `tools: []` + `settingSources: []` → the caller's tools only
|
|
14
|
+
//
|
|
15
|
+
// So the pins are both, on every path. With them in place no permission
|
|
16
|
+
// bypass is needed: a tool named in `allowedTools` runs, and a server-level
|
|
17
|
+
// entry (`mcp__<server>`) approves every tool of that server (both measured
|
|
18
|
+
// on a live turn; with no entry the call is denied and the run reports it).
|
|
19
|
+
|
|
20
|
+
import type { McpServerConfig, Options } from '@anthropic-ai/claude-agent-sdk';
|
|
21
|
+
|
|
22
|
+
export type SdkToolPins = Required<Pick<Options, 'tools' | 'settingSources' | 'allowedTools'>>;
|
|
23
|
+
|
|
24
|
+
export interface SdkToolPinInput {
|
|
25
|
+
/** The MCP servers the session gets, keyed by name. */
|
|
26
|
+
readonly mcpServers?: Record<string, McpServerConfig>;
|
|
27
|
+
/** Tool names to approve. Absent → every tool of every server in `mcpServers`. */
|
|
28
|
+
readonly allowedTools?: readonly string[];
|
|
29
|
+
/**
|
|
30
|
+
* The CLI built-ins to offer (e.g. `['Write']`) — an explicit opt-in.
|
|
31
|
+
* Each one is also approved, so it runs without a prompt; a built-in that
|
|
32
|
+
* writes or executes acts on the host with the session's permissions.
|
|
33
|
+
*/
|
|
34
|
+
readonly builtinTools?: readonly string[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function sdkToolPins(input: SdkToolPinInput): SdkToolPins {
|
|
38
|
+
const builtins = [...(input.builtinTools ?? [])];
|
|
39
|
+
const approved =
|
|
40
|
+
input.allowedTools !== undefined
|
|
41
|
+
? [...input.allowedTools]
|
|
42
|
+
: Object.keys(input.mcpServers ?? {}).map((name) => `mcp__${name}`);
|
|
43
|
+
return { tools: builtins, settingSources: [], allowedTools: [...approved, ...builtins] };
|
|
44
|
+
}
|
|
@@ -14,6 +14,7 @@ import type { AdapterResult, ProviderName, AdapterMode } from '../types';
|
|
|
14
14
|
import { createArtifacts, processMessage } from './message-parser';
|
|
15
15
|
import { createToolMcpServer } from './tool-bridge';
|
|
16
16
|
import { claudeCodeLoginQueryOptions, loginChildEnv } from './claude-code-login';
|
|
17
|
+
import { sdkToolPins } from './sdk-tool-pins';
|
|
17
18
|
|
|
18
19
|
export class ClaudeSdkAdapter extends GeneratorAdapter {
|
|
19
20
|
readonly provider: ProviderName = 'claude';
|
|
@@ -73,9 +74,8 @@ export class ClaudeSdkAdapter extends GeneratorAdapter {
|
|
|
73
74
|
systemPrompt: params.systemPrompt,
|
|
74
75
|
...(mcpServers && { mcpServers }),
|
|
75
76
|
...(cfg.agents && { agents: cfg.agents }),
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
allowDangerouslySkipPermissions: true,
|
|
77
|
+
// ggui#1714 — no CLI built-ins unless asked for, no host settings, no permission bypass.
|
|
78
|
+
...sdkToolPins({ mcpServers, allowedTools, builtinTools: cfg.builtinTools }),
|
|
79
79
|
...(cfg.cliPath && { pathToClaudeCodeExecutable: cfg.cliPath }),
|
|
80
80
|
...(cfg.stderr && { stderr: cfg.stderr }),
|
|
81
81
|
// ggui#1185 — tool-less, config-less, non-bare; the model is pinned above.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// packages/ui-gen/src/adapters/claude/source-capture.ts
|
|
2
|
+
//
|
|
3
|
+
// ggui#1714 — which tool call carries a component's source, decided once.
|
|
4
|
+
// Both Agent SDK paths (ClaudeSdkAdapter's message parser and the evaluation
|
|
5
|
+
// loop's extractor) read source through this function, so the rule cannot be
|
|
6
|
+
// fixed on one path and left stale on the other.
|
|
7
|
+
//
|
|
8
|
+
// `*__compile_component` → `input.code` (the bridged compile tool)
|
|
9
|
+
// `Write` → `input.content` (a CLI built-in, offered only by opt-in)
|
|
10
|
+
|
|
11
|
+
const COMPILE_TOOL_SUFFIX = '__compile_component';
|
|
12
|
+
|
|
13
|
+
/** The source a tool_use block carries, or undefined when it carries none. */
|
|
14
|
+
export function sourceCodeFromToolUse(name: unknown, input: unknown): string | undefined {
|
|
15
|
+
if (typeof name !== 'string') return undefined;
|
|
16
|
+
const field = name === 'Write' ? 'content' : name.endsWith(COMPILE_TOOL_SUFFIX) ? 'code' : undefined;
|
|
17
|
+
if (field === undefined || typeof input !== 'object' || input === null) return undefined;
|
|
18
|
+
const value: unknown = Reflect.get(input, field);
|
|
19
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
20
|
+
}
|
|
@@ -29,6 +29,7 @@ import type { LlmRoute } from '@ggui-ai/protocol';
|
|
|
29
29
|
export function getBedrockModelId(model: string): string {
|
|
30
30
|
const BEDROCK_MAP: Record<string, string> = {
|
|
31
31
|
'anthropic/claude-haiku-4-5': 'us.anthropic.claude-haiku-4-5-20251001-v1:0',
|
|
32
|
+
'anthropic/claude-haiku-5-5': 'us.anthropic.claude-haiku-5-5', // listed ACTIVE in us-east-1 (ggui#1743)
|
|
32
33
|
'anthropic/claude-sonnet-4-6': 'us.anthropic.claude-sonnet-4-6',
|
|
33
34
|
'anthropic/claude-opus-4-6': 'us.anthropic.claude-opus-4-6-v1:0',
|
|
34
35
|
// Claude 5-family (ggui#706, strings verified 2026-09-02 against
|
|
@@ -481,13 +481,14 @@ MANDATORY:
|
|
|
481
481
|
2. NEVER use CSS gradients with custom colors. If you need a gradient: \`linear-gradient(to bottom, var(--ggui-color-primary-500), var(--ggui-color-primary-700))\`.
|
|
482
482
|
3. NEVER invent your own palette. The system provides primary, neutral, success, warning, error, and info — use ONLY these.
|
|
483
483
|
4. NEVER include literal fallback values in ANY token reference — colors, spacing, typography, radius, shadows alike. Write \`var(--ggui-spacing-4)\` bare, never \`var(--ggui-spacing-4, 16px)\`; \`var(--ggui-color-primary-600)\` bare, never \`var(--ggui-color-primary-600, #0284c7)\`. The runtime injects every token; a literal fallback paints the wrong value exactly when the operator's theme matters most.
|
|
484
|
+
5. NEVER set an inline \`border\`, \`borderColor\`, \`borderWidth\` or \`borderStyle\` on a kit control — \`Button\`, \`Input\`, \`Select\`, \`TextArea\`, \`Checkbox\`, \`RadioGroup\`, \`Toggle\`, \`SearchField\`. The control draws its own boundary, and it clears 3:1 on its surface in every theme: an outline chip is \`<Button variant="outline">\` exactly as it comes (its edge is \`controlAccentOutline\`). Where you draw a control-like edge on your own element, use \`var(--ggui-color-controlOutline)\`. A ramp step such as \`primary-200\` as a control's border is nearly invisible (about 1.1:1).
|
|
484
485
|
|
|
485
486
|
Token categories:
|
|
486
487
|
- Brand fills: \`var(--ggui-color-primary-600)\`, \`var(--ggui-color-primary-50)\`
|
|
487
488
|
- Accent text: \`var(--ggui-color-link)\` — eyebrows, taglines, links, labels; readable per mode (never a bare \`primary-<stop>\` as a text color)
|
|
488
489
|
- Text: \`var(--ggui-color-onContainer)\`, \`var(--ggui-color-onSunken)\`
|
|
489
490
|
- Backgrounds: \`var(--ggui-color-container)\`, \`var(--ggui-color-sunken)\`
|
|
490
|
-
- Borders: \`var(--ggui-color-outline)\`
|
|
491
|
+
- Borders: \`var(--ggui-color-outline)\` — dividers and the edges of non-interactive boxes; a control's edge is the kit's (rule 5)
|
|
491
492
|
- Spacing: \`var(--ggui-spacing-4)\`, \`var(--ggui-spacing-6)\`
|
|
492
493
|
- Typography: \`var(--ggui-font-size-sm)\`, \`var(--ggui-font-weight-semibold)\`
|
|
493
494
|
- Shadows: \`var(--ggui-shape-shadow-sm)\`, \`var(--ggui-shape-shadow-md)\`, \`var(--ggui-shape-shadow-lg)\`
|
|
@@ -502,7 +503,7 @@ Use the FULL primary palette throughout the component — NOT only on submit but
|
|
|
502
503
|
| Element | Token | Purpose |
|
|
503
504
|
|---------|-------|---------|
|
|
504
505
|
| Section headers, hero areas, highlight strips | \`primary-50\` / \`primary-100\` | Subtle branded backgrounds |
|
|
505
|
-
|
|
|
506
|
+
| Dividers, section rules, edges of non-interactive boxes | \`primary-200\` / \`primary-300\` | Branded structure — never a control's border (rule 5) |
|
|
506
507
|
| Accent TEXT — eyebrows, taglines, links, labels, inline arrows | \`link\` (\`var(--ggui-color-link)\`) or \`<Text tone="emphasized">\` | The brand's READABLE ink, derived per mode to clear 4.5:1 — a bare \`primary-300…600\` as a text color fails tier-0 |
|
|
507
508
|
| Icon fills, badges, active indicators | \`primary-500\` / \`primary-600\` | Core accent fill |
|
|
508
509
|
| Buttons, CTAs, filled interactive elements | \`primary-600\` / \`primary-700\` | Primary actions |
|
package/src/check/run-tier0.ts
CHANGED
|
@@ -571,6 +571,50 @@ function detectComponentNeverReturns(
|
|
|
571
571
|
* Wraps the same logic as runSelfChecks (adapters/tools.ts) but emits EvalIssue[].
|
|
572
572
|
* Also runs contract validation when contract are provided.
|
|
573
573
|
*/
|
|
574
|
+
/** The kit controls whose boundary the design system draws (ggui#1697). */
|
|
575
|
+
const KIT_CONTROLS = ['Button', 'Input', 'Select', 'TextArea', 'Checkbox', 'RadioGroup', 'Toggle', 'SearchField'] as const;
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Opening tags of kit controls, read brace- and quote-aware: an attribute like `onClick={() => x}` holds a `>` that a
|
|
579
|
+
* plain `[^>]*` pattern stops at, which would hide every attribute after it.
|
|
580
|
+
*/
|
|
581
|
+
function kitControlTags(source: string): { name: string; attrs: string; line: number }[] {
|
|
582
|
+
const out: { name: string; attrs: string; line: number }[] = [];
|
|
583
|
+
const open = new RegExp(`<(${KIT_CONTROLS.join('|')})\\b`, 'g');
|
|
584
|
+
for (const m of source.matchAll(open)) {
|
|
585
|
+
const start = (m.index ?? 0) + m[0].length;
|
|
586
|
+
let depth = 0;
|
|
587
|
+
let quote: string | null = null;
|
|
588
|
+
let i = start;
|
|
589
|
+
for (; i < source.length; i += 1) {
|
|
590
|
+
const c = source[i];
|
|
591
|
+
if (quote !== null) {
|
|
592
|
+
if (c === quote && source[i - 1] !== '\\') quote = null;
|
|
593
|
+
} else if (c === '"' || c === "'" || c === '`') quote = c;
|
|
594
|
+
else if (c === '{') depth += 1;
|
|
595
|
+
else if (c === '}') depth -= 1;
|
|
596
|
+
else if (c === '>' && depth === 0) break;
|
|
597
|
+
}
|
|
598
|
+
out.push({ name: m[1] ?? '', attrs: source.slice(start, i), line: source.slice(0, m.index ?? 0).split('\n').length });
|
|
599
|
+
}
|
|
600
|
+
return out;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/** A border key (not a radius) in the tag's inline style, or in a same-file object its `style={name}` points at. */
|
|
604
|
+
function inlineBorderKey(attrs: string, source: string): string | null {
|
|
605
|
+
const BORDER = /\b(border(?:Top|Right|Bottom|Left)?(?:Color|Width|Style)?)\s*:/;
|
|
606
|
+
const style = attrs.match(/\bstyle=\{\{([\s\S]*)\}\}/);
|
|
607
|
+
if (style !== null) {
|
|
608
|
+
const hit = (style[1] ?? '').match(BORDER);
|
|
609
|
+
if (hit !== null) return hit[1] ?? null;
|
|
610
|
+
}
|
|
611
|
+
const named = attrs.match(/\bstyle=\{\s*([A-Za-z_$][\w$]*)\s*\}/);
|
|
612
|
+
if (named === null) return null;
|
|
613
|
+
const decl = source.match(new RegExp(`\\b(?:const|let)\\s+${named[1]}\\b[^=]*=\\s*\\{([^{}]*)\\}`));
|
|
614
|
+
const hit = decl !== null ? (decl[1] ?? '').match(BORDER) : null;
|
|
615
|
+
return hit !== null ? hit[1] ?? null : null;
|
|
616
|
+
}
|
|
617
|
+
|
|
574
618
|
export async function runTier0Checks(
|
|
575
619
|
sourceCode: string,
|
|
576
620
|
contract?: DataContract,
|
|
@@ -1169,6 +1213,23 @@ export async function runTier0Checks(
|
|
|
1169
1213
|
}
|
|
1170
1214
|
}
|
|
1171
1215
|
|
|
1216
|
+
// ── A kit control's boundary is the kit's (ggui#1697) ──────
|
|
1217
|
+
// A control draws its own edge, derived to clear 3:1 on its surface (`controlOutline`, or
|
|
1218
|
+
// `controlAccentOutline` on an outline Button); an inline border paints over it, and the ramp step
|
|
1219
|
+
// the model reached for (`primary-200`) measured about 1.1:1. The prompt's rule 5 says the same, so
|
|
1220
|
+
// this check enforces what the model was told. The description starts with the check's id so the
|
|
1221
|
+
// generator's logged violation lines (`✗ …`) count its fires per generation, in-turn repairs included.
|
|
1222
|
+
for (const tag of enforceDesignVocabulary ? kitControlTags(sourceCode) : []) {
|
|
1223
|
+
const key = inlineBorderKey(tag.attrs, sourceCode);
|
|
1224
|
+
if (key === null) continue;
|
|
1225
|
+
issues.push({
|
|
1226
|
+
tier: 0, result: 'fail', category: 'tokens', subcategory: 'control-inline-border', severity: 'critical',
|
|
1227
|
+
description: `control-inline-border: <${tag.name}> sets an inline \`${key}\` — a kit control draws its own boundary, and an inline border paints over it`,
|
|
1228
|
+
fix: `Remove the border from <${tag.name}>'s style (keep borderRadius if you need it). An outline chip is <Button variant="outline"> exactly as it comes; a control-like edge on your own element uses var(--ggui-color-controlOutline).`,
|
|
1229
|
+
line: tag.line,
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1172
1233
|
// ── Missing Props interface ───────────────────────────────
|
|
1173
1234
|
if (!sourceCode.includes('interface Props') && !sourceCode.includes('type Props')) {
|
|
1174
1235
|
issues.push({
|
|
@@ -40,7 +40,7 @@ import type {
|
|
|
40
40
|
ModelRef,
|
|
41
41
|
AppGenerationProfileEffort,
|
|
42
42
|
} from "@ggui-ai/protocol";
|
|
43
|
-
import { isGeneratorId, modelRefOfRoute } from "@ggui-ai/protocol";
|
|
43
|
+
import { DEFAULT_MODEL as PROTOCOL_DEFAULT_MODEL, isGeneratorId, modelRefOfRoute } from "@ggui-ai/protocol";
|
|
44
44
|
import type { GadgetCatalogAdapter } from "@ggui-ai/gadgets";
|
|
45
45
|
import type {
|
|
46
46
|
GenerationMetadata,
|
|
@@ -85,7 +85,9 @@ import type { AgentConfig } from "./harness/llm-router.js";
|
|
|
85
85
|
|
|
86
86
|
/** The slug for the OSS default seed generator. */
|
|
87
87
|
const DEFAULT_TIER: GeneratorTier = "default";
|
|
88
|
-
|
|
88
|
+
// ggui#1794 — the open-source default is protocol's, one source: a default switch moves one constant
|
|
89
|
+
// (`@ggui-ai/protocol` `DEFAULT_MODEL`), never two. ggui#1743 moved it to Haiku 5.5 on benchmark's receipt.
|
|
90
|
+
const DEFAULT_MODEL: ModelRef = PROTOCOL_DEFAULT_MODEL;
|
|
89
91
|
|
|
90
92
|
export interface CreateUiGeneratorOptions {
|
|
91
93
|
/**
|
|
@@ -49,6 +49,7 @@ These tokens adapt automatically to any theme (light, dark, branded). **ALWAYS u
|
|
|
49
49
|
| link | var(--ggui-color-link) | Accent TEXT — eyebrows, taglines, links, labels, inline arrows: the brand's readable ink, derived per mode to clear 4.5:1 on the container and the ground |
|
|
50
50
|
| outline | var(--ggui-color-outline) | Borders, dividers |
|
|
51
51
|
| outlineVariant | var(--ggui-color-outlineVariant) | Subtle borders |
|
|
52
|
+
| controlOutline | var(--ggui-color-controlOutline) | A control's edge — clears 3:1 on its surface; kit controls draw it themselves |
|
|
52
53
|
|
|
53
54
|
**Usage pattern:**
|
|
54
55
|
- Page/section background → \`var(--ggui-color-ground)\`
|
|
@@ -173,7 +174,7 @@ Use \`var(--ggui-spacing-N)\` for all padding, gap, and margin values. **Never u
|
|
|
173
174
|
### Color usage guide
|
|
174
175
|
- **Layering roles** (ground, container, elevated, sunken + their on* inks; outline): one kind of AREA each — use them for every background/text/border decision; they adapt to any theme
|
|
175
176
|
- **primary-50/100**: Section backgrounds, highlight strips, card headers
|
|
176
|
-
- **primary-200/300**:
|
|
177
|
+
- **primary-200/300**: Dividers and section rules — never a control's border: kit controls draw their own edge (\`controlOutline\` / \`controlAccentOutline\`), so set no inline border on them
|
|
177
178
|
- **primary-500/600**: Filled buttons, CTAs, badges, icon fills, active indicators — fills and borders, NOT a text color
|
|
178
179
|
- **link**: Accent TEXT — eyebrows, taglines, links, labels, inline arrows; the brand's readable ink per mode (a bare \`primary-300…600\` as a text color fails tier-0: \`universal.accent_text_ink\`)
|
|
179
180
|
- **primary-700/800/900**: Headings and text on light primary backgrounds
|
|
@@ -57,7 +57,7 @@ function runNoHardcodedEntities(input: AxisCheckInput): EvalIssue[] {
|
|
|
57
57
|
mkIssue(
|
|
58
58
|
"state.merge.no_hardcoded_entities",
|
|
59
59
|
`Hardcoded entity array literal (multiple objects with "${idField}") in the component — entity data should come from state/props.`,
|
|
60
|
-
`Remove the literal array
|
|
60
|
+
`Remove the literal array: this data comes from ${all.filter((e) => e.idField === idField).map((e) => `props.${e.name}`).join(" / ") || "props"}. Read it there (\`?? []\` for an absent value, never a literal default), seed state from it, and merge updates via stream.`,
|
|
61
61
|
),
|
|
62
62
|
);
|
|
63
63
|
}
|
|
@@ -249,14 +249,128 @@ export function getInitialValuePropNames(contract?: DataContract): string[] {
|
|
|
249
249
|
// Source scanning helpers
|
|
250
250
|
// =============================================================================
|
|
251
251
|
|
|
252
|
+
/**
|
|
253
|
+
* The top-level property keys of the object literal whose `{` is at `open`, read by
|
|
254
|
+
* walking brackets and skipping strings, so a nested object or a `}` inside a string
|
|
255
|
+
* neither ends the literal early nor contributes keys. `undefined` when `open` is not a `{`.
|
|
256
|
+
*/
|
|
257
|
+
function objectLiteralKeysAt(src: string, open: number): string[] | undefined {
|
|
258
|
+
if (src[open] !== "{") return undefined;
|
|
259
|
+
const keys: string[] = [];
|
|
260
|
+
let depth = 0;
|
|
261
|
+
let expectKey = true;
|
|
262
|
+
for (let i = open; i < src.length; i++) {
|
|
263
|
+
const c = src[i];
|
|
264
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
265
|
+
// Skip the string; a quoted top-level key ("name": …) is a key too.
|
|
266
|
+
let j = i + 1;
|
|
267
|
+
while (j < src.length && src[j] !== c) j += src[j] === "\\" ? 2 : 1;
|
|
268
|
+
if (depth === 1 && expectKey && /^\s*:/.test(src.slice(j + 1))) keys.push(src.slice(i + 1, j));
|
|
269
|
+
i = j;
|
|
270
|
+
expectKey = false;
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
if (c === "{" || c === "[" || c === "(") {
|
|
274
|
+
depth++;
|
|
275
|
+
if (depth === 1) expectKey = true;
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
if (c === "}" || c === "]" || c === ")") {
|
|
279
|
+
depth--;
|
|
280
|
+
if (depth === 0) return keys;
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
if (depth !== 1) continue;
|
|
284
|
+
if (c === ",") {
|
|
285
|
+
expectKey = true;
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
if (expectKey && /[A-Za-z_$]/.test(c ?? "")) {
|
|
289
|
+
const m = /^([A-Za-z_$][\w$]*)\s*(:|,|\})/.exec(src.slice(i));
|
|
290
|
+
// `key: value`, or the shorthand `key,` / `key }`.
|
|
291
|
+
if (m !== null && m[1] !== undefined) keys.push(m[1]);
|
|
292
|
+
expectKey = false;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return keys;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/** The argument texts of the call whose `(` is at `open`, split on top-level commas. */
|
|
299
|
+
function callArgsAt(src: string, open: number): string[] {
|
|
300
|
+
const args: string[] = [];
|
|
301
|
+
let depth = 0;
|
|
302
|
+
let start = open + 1;
|
|
303
|
+
for (let i = open; i < src.length; i++) {
|
|
304
|
+
const c = src[i];
|
|
305
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
306
|
+
let j = i + 1;
|
|
307
|
+
while (j < src.length && src[j] !== c) j += src[j] === "\\" ? 2 : 1;
|
|
308
|
+
i = j;
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
if (c === "(" || c === "{" || c === "[") depth++;
|
|
312
|
+
else if (c === ")" || c === "}" || c === "]") {
|
|
313
|
+
depth--;
|
|
314
|
+
if (depth === 0) {
|
|
315
|
+
args.push(src.slice(start, i));
|
|
316
|
+
return args;
|
|
317
|
+
}
|
|
318
|
+
} else if (c === "," && depth === 1) {
|
|
319
|
+
args.push(src.slice(start, i));
|
|
320
|
+
start = i + 1;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return args;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* The keys an initial-state argument provides: an object literal (`{ … }`), a lazy
|
|
328
|
+
* initializer returning one (`() => ({ … })`), or an identifier bound to one in the same
|
|
329
|
+
* file (`const INITIAL: Form = { … }`, also behind a lazy `() => INITIAL`).
|
|
330
|
+
*/
|
|
331
|
+
function initialStateKeys(src: string, argStart: number, argText: string): string[] {
|
|
332
|
+
const lead = argText.length - argText.trimStart().length;
|
|
333
|
+
let at = argStart + lead;
|
|
334
|
+
let text = argText.trim();
|
|
335
|
+
const lazy = /^\(\s*\)\s*=>\s*\(?\s*/.exec(text);
|
|
336
|
+
if (lazy !== null) {
|
|
337
|
+
at += lazy[0].length;
|
|
338
|
+
text = text.slice(lazy[0].length);
|
|
339
|
+
}
|
|
340
|
+
if (text.startsWith("{")) return objectLiteralKeysAt(src, at) ?? [];
|
|
341
|
+
const ident = /^([A-Za-z_$][\w$]*)\s*\)?\s*$/.exec(text);
|
|
342
|
+
if (ident === null || ident[1] === undefined) return [];
|
|
343
|
+
// One binding, or none: a name declared twice (shadowed, or re-declared in another scope) is
|
|
344
|
+
// ambiguous, and ambiguity reads as absence, so the check can never pass on the wrong binding.
|
|
345
|
+
const decls = [...src.matchAll(new RegExp(`(?:const|let|var)\\s+${ident[1]}\\s*(?::[^=]{1,200})?=\\s*`, "g"))];
|
|
346
|
+
const decl = decls.length === 1 ? decls[0] : undefined;
|
|
347
|
+
if (decl === undefined) return [];
|
|
348
|
+
return objectLiteralKeysAt(src, (decl.index ?? 0) + decl[0].length) ?? [];
|
|
349
|
+
}
|
|
350
|
+
|
|
252
351
|
/**
|
|
253
352
|
* Collect all key names that appear as state slots — either keys inside a
|
|
254
353
|
* `useState({ ... })` object literal, or standalone state variables.
|
|
354
|
+
*
|
|
355
|
+
* ggui#1790 — also the initial-state shapes the inline-literal patterns cannot see, each
|
|
356
|
+
* idiomatic React: `useState(INITIAL)` with `const INITIAL = { … }` in the file, a lazy
|
|
357
|
+
* initializer `useState(() => ({ … }))`, and `useReducer(reducer, { … } | INITIAL)`. Missing
|
|
358
|
+
* them made `state.payload.covers_submit` report every payload key missing on a form that held
|
|
359
|
+
* them all, and the model, with nothing real to fix, spent the turn cap rewording a comment.
|
|
255
360
|
*/
|
|
256
361
|
export function collectStateKeys(src: string): Set<string> {
|
|
257
362
|
const keys = new Set<string>();
|
|
258
363
|
const varRe = /const\s*\[\s*(\w+)\s*,\s*(\w+)\s*\]\s*=\s*useState/g;
|
|
259
364
|
for (const m of src.matchAll(varRe)) keys.add(m[1]);
|
|
365
|
+
// ggui#1791 — a contextSpec slot is state too: the boilerplate binds each slot as
|
|
366
|
+
// `const [x, setX] = useGguiContext<T>('x')` under a DO-NOT-EDIT banner, so a form
|
|
367
|
+
// whose fields are context slots holds every payload key there and cannot move them
|
|
368
|
+
// into useState. Both the binding's name and the slot's name count.
|
|
369
|
+
const contextRe = /const\s*\[\s*(\w+)\s*,\s*\w+\s*\]\s*=\s*useGguiContext\s*(?:<[^>]*>)?\s*\(\s*['"`](\w+)['"`]/g;
|
|
370
|
+
for (const m of src.matchAll(contextRe)) {
|
|
371
|
+
keys.add(m[1]);
|
|
372
|
+
keys.add(m[2]);
|
|
373
|
+
}
|
|
260
374
|
const objRe = /useState(?:<[^>]*>)?\s*\(\s*\{([\s\S]*?)\}\s*\)/g;
|
|
261
375
|
for (const m of src.matchAll(objRe)) {
|
|
262
376
|
const keyRe = /(?:^|,)\s*(\w+)\s*:/g;
|
|
@@ -267,6 +381,17 @@ export function collectStateKeys(src: string): Set<string> {
|
|
|
267
381
|
const keyRe = /(?:^|,)\s*(\w+)\s*:/g;
|
|
268
382
|
for (const km of m[1].matchAll(keyRe)) keys.add(km[1]);
|
|
269
383
|
}
|
|
384
|
+
// ggui#1790 — the initial-state argument, resolved: useState's first, useReducer's second.
|
|
385
|
+
for (const m of src.matchAll(/\b(useState|useReducer)\s*(?:<[^>]*>)?\s*\(/g)) {
|
|
386
|
+
const open = (m.index ?? 0) + m[0].length - 1;
|
|
387
|
+
const args = callArgsAt(src, open);
|
|
388
|
+
const which = m[1] === "useReducer" ? 1 : 0;
|
|
389
|
+
const arg = args[which];
|
|
390
|
+
if (arg === undefined) continue;
|
|
391
|
+
let argStart = open + 1;
|
|
392
|
+
for (let k = 0; k < which; k++) argStart += (args[k]?.length ?? 0) + 1;
|
|
393
|
+
for (const key of initialStateKeys(src, argStart, arg)) keys.add(key);
|
|
394
|
+
}
|
|
270
395
|
return keys;
|
|
271
396
|
}
|
|
272
397
|
|
|
@@ -30,6 +30,16 @@ export const INSTRUMENT_BESIDE_JUDGE: Readonly<Record<string, 'edge'>> = {
|
|
|
30
30
|
'space.edge': 'edge',
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* ggui#1711 — judge rows a single pre-action frame cannot answer. Their own text makes them not-evaluated "when the
|
|
35
|
+
* capture has no post-action state", and every judged frame today is one capture taken before any action. Asked
|
|
36
|
+
* anyway, the judge answered `n/a` 94 of 94 times and left the row out 15 more times (a dropped row reads like a
|
|
37
|
+
* skip). So the absence is decided here, from the capture: such a row is never asked without a post-action capture,
|
|
38
|
+
* and it reads `n/a` with its reason, still in the tally. With one, the judge is asked as for any row.
|
|
39
|
+
*/
|
|
40
|
+
export const REQUIRES_POST_ACTION_CAPTURE: ReadonlySet<string> = new Set(['state.feedback']);
|
|
41
|
+
const NO_POST_ACTION = 'not evaluated: no post-action capture';
|
|
42
|
+
|
|
33
43
|
export interface CriteriaMeasurements {
|
|
34
44
|
readonly overflow: boolean;
|
|
35
45
|
readonly contentHeight: number | null;
|
|
@@ -40,6 +50,8 @@ export interface CriteriaMeasurements {
|
|
|
40
50
|
* `null` when the probe ran and failed.
|
|
41
51
|
*/
|
|
42
52
|
readonly edge?: EdgeVerdict | null;
|
|
53
|
+
/** ggui#1711 — the capture includes a state after an action ran. Absent = no (every capture today). */
|
|
54
|
+
readonly postActionCapture?: boolean;
|
|
43
55
|
}
|
|
44
56
|
|
|
45
57
|
function besideRead(kind: 'edge', m: CriteriaMeasurements): Read {
|
|
@@ -73,6 +85,7 @@ function instrumentRead(kind: 'fit' | 'fill', m: CriteriaMeasurements): Read {
|
|
|
73
85
|
}
|
|
74
86
|
|
|
75
87
|
function readRow(row: BankRow, answers: readonly CriteriaAnswer[][], m: CriteriaMeasurements, unansweredReason: string | undefined): Read {
|
|
88
|
+
if (row.evaluation === 'judge' && REQUIRES_POST_ACTION_CAPTURE.has(row.id) && m.postActionCapture !== true) return { verdict: 'n/a', evidence: NO_POST_ACTION };
|
|
76
89
|
if (row.evaluation === 'judge') return majority(row.id, answers, unansweredReason);
|
|
77
90
|
if (row.evaluation === 'human') return { verdict: 'n/a', evidence: `reader's column (status ${row.status})` };
|
|
78
91
|
if (row.status !== 'live') return { verdict: 'n/a', evidence: `not implemented (status ${row.status})` };
|
|
@@ -147,6 +160,8 @@ export interface CriteriaJudgeInputs {
|
|
|
147
160
|
readonly frame?: CriteriaJudgeFrame;
|
|
148
161
|
/** JSON text of the props the card was rendered with; bounded by {@link CRITERIA_PROPS_MAX_CHARS}. */
|
|
149
162
|
readonly propsJson?: string;
|
|
163
|
+
/** ggui#1711 — the capture includes a post-action state; absent = no, and rows needing one are not asked. */
|
|
164
|
+
readonly postActionCapture?: boolean;
|
|
150
165
|
}
|
|
151
166
|
|
|
152
167
|
/** Bound on the props text handed to the judge beside the frame, in characters. */
|
|
@@ -190,7 +205,10 @@ function criteriaPropsSection(propsJson: string): string {
|
|
|
190
205
|
/** The judge's prompt block for the selected `judge` rows; empty when none is selected. */
|
|
191
206
|
export function buildCriteriaJudgeBlock(bank: CriteriaBank, selected: CriteriaSelectionResult, inputs: CriteriaJudgeInputs = {}): string {
|
|
192
207
|
const byId = new Map<string, BankRow>(bankRows(bank).map((r) => [r.id, r]));
|
|
193
|
-
const asked = selected.selection
|
|
208
|
+
const asked = selected.selection
|
|
209
|
+
.map((s) => byId.get(s.id))
|
|
210
|
+
.filter((r): r is BankRow => r !== undefined && r.evaluation === 'judge')
|
|
211
|
+
.filter((r) => inputs.postActionCapture === true || !REQUIRES_POST_ACTION_CAPTURE.has(r.id));
|
|
194
212
|
if (asked.length === 0) return '';
|
|
195
213
|
const lines = asked.map((r) => `- ${r.id} (${r.severity}): ${r.text} Evidence must name: ${r.evidence}`);
|
|
196
214
|
return (
|
|
@@ -218,9 +218,9 @@ function buildEvalResult(
|
|
|
218
218
|
|
|
219
219
|
function getDefaultEvalModel(provider: string): string {
|
|
220
220
|
switch (provider) {
|
|
221
|
-
case 'claude': return 'claude-haiku-
|
|
221
|
+
case 'claude': return 'claude-haiku-5-5';
|
|
222
222
|
case 'openai': return 'gpt-5.6-luna';
|
|
223
223
|
case 'google': return 'gemini-3.5-flash-lite';
|
|
224
|
-
default: return 'claude-haiku-
|
|
224
|
+
default: return 'claude-haiku-5-5';
|
|
225
225
|
}
|
|
226
226
|
}
|
|
@@ -568,13 +568,13 @@ Use 'fail' only for pervasive design system violations. Use 'warn' for isolated
|
|
|
568
568
|
function getDefaultEvalModel(provider: string): string {
|
|
569
569
|
switch (provider) {
|
|
570
570
|
case 'claude':
|
|
571
|
-
return 'claude-haiku-
|
|
571
|
+
return 'claude-haiku-5-5';
|
|
572
572
|
case 'openai':
|
|
573
573
|
return 'gpt-5.6-luna';
|
|
574
574
|
case 'google':
|
|
575
575
|
return 'gemini-3.5-flash-lite';
|
|
576
576
|
default:
|
|
577
|
-
return 'claude-haiku-
|
|
577
|
+
return 'claude-haiku-5-5';
|
|
578
578
|
}
|
|
579
579
|
}
|
|
580
580
|
|