@ggui-ai/ui-gen 0.26.0 → 0.27.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.
Files changed (60) hide show
  1. package/dist/{adapter-BJwn74rx.d.ts → adapter-h4_V69LI.d.ts} +1 -1
  2. package/dist/adapters/base.d.ts +6 -1
  3. package/dist/adapters/base.js.map +1 -1
  4. package/dist/adapters/generation-dispatch.d.ts +2 -2
  5. package/dist/adapters/generation-dispatch.js +61 -10
  6. package/dist/adapters/generation-dispatch.js.map +1 -1
  7. package/dist/adapters/index.js +36 -14
  8. package/dist/adapters/index.js.map +1 -1
  9. package/dist/advanced/index.d.ts +4 -4
  10. package/dist/advanced/index.js +94 -19
  11. package/dist/advanced/index.js.map +1 -1
  12. package/dist/boilerplate.js +4 -6
  13. package/dist/boilerplate.js.map +1 -1
  14. package/dist/check/index.d.ts +1 -7
  15. package/dist/check/index.js +50 -4
  16. package/dist/check/index.js.map +1 -1
  17. package/dist/{create-ui-generator-N4gRXpcL.d.ts → create-ui-generator-BrZHF0cf.d.ts} +2 -2
  18. package/dist/{dispatch-BUoXRj_r.d.ts → dispatch-B07TI30d.d.ts} +1 -1
  19. package/dist/evaluation/axis-checks/index.d.ts +3 -3
  20. package/dist/evaluation/axis-checks/index.js.map +1 -1
  21. package/dist/evaluation/axis-checks/registry.d.ts +1 -1
  22. package/dist/evaluation/index.d.ts +22 -12
  23. package/dist/evaluation/index.js +36 -10
  24. package/dist/evaluation/index.js.map +1 -1
  25. package/dist/evaluation/types.d.ts +1 -1
  26. package/dist/harness/check/runtime-render/index.d.ts +3 -3
  27. package/dist/harness/index.d.ts +1 -1
  28. package/dist/harness/index.js +4 -6
  29. package/dist/harness/index.js.map +1 -1
  30. package/dist/harness/prompts.js +1 -4
  31. package/dist/harness/prompts.js.map +1 -1
  32. package/dist/harness/result-types.d.ts +2 -2
  33. package/dist/harness/runtime.d.ts +1 -1
  34. package/dist/harness/runtime.js +6 -7
  35. package/dist/harness/runtime.js.map +1 -1
  36. package/dist/harness/types-public.d.ts +1 -1
  37. package/dist/harness/types-public.js +4 -6
  38. package/dist/harness/types-public.js.map +1 -1
  39. package/dist/harness/validator-trace-sink.d.ts +1 -1
  40. package/dist/index.d.ts +5 -5
  41. package/dist/index.js +94 -19
  42. package/dist/index.js.map +1 -1
  43. package/dist/patch.d.ts +1 -1
  44. package/dist/{types-public-B7wn4ce5.d.ts → types-public-h8IMnYuh.d.ts} +4 -0
  45. package/dist/{visual-evaluator-C9L-o6ap.d.ts → visual-evaluator-C2strxqD.d.ts} +1 -1
  46. package/dist/workflows.d.ts +1 -1
  47. package/package.json +12 -12
  48. package/src/adapters/base.ts +6 -1
  49. package/src/adapters/claude/message-parser.ts +5 -6
  50. package/src/adapters/claude/sdk-tool-pins.ts +44 -0
  51. package/src/adapters/claude/sdk.ts +3 -3
  52. package/src/adapters/claude/source-capture.ts +20 -0
  53. package/src/boilerplate/system-prompt.ts +3 -2
  54. package/src/check/run-tier0.ts +61 -0
  55. package/src/design-system-docs.ts +2 -1
  56. package/src/evaluation/criteria/resolve.ts +19 -1
  57. package/src/evaluation/loop.ts +25 -4
  58. package/src/evaluation/message-parsing.ts +11 -12
  59. package/src/evaluation/types-public.ts +4 -0
  60. package/src/taught-tokens.ts +5 -6
@@ -1,7 +1,7 @@
1
1
  import { RuntimeRenderCheck } from './harness/types-public.js';
2
2
  import { GenerationRuntimeProbeCheck } from '@ggui-ai/mcp-server-core';
3
3
  import { JsonObject, DataContract } from '@ggui-ai/protocol';
4
- import { f as ProbeHostLoad, g as ProbePendingAffordance } from './types-public-B7wn4ce5.js';
4
+ import { f as ProbeHostLoad, g as ProbePendingAffordance } from './types-public-h8IMnYuh.js';
5
5
 
6
6
  /**
7
7
  * The subprocess bounds of one isolated check (ggui#1380). They are
@@ -41,8 +41,13 @@ interface ClaudeSdkConfig extends AdapterConfig {
41
41
  env?: Record<string, string>;
42
42
  /** MCP server configs for Claude Agent SDK (keyed by name) */
43
43
  mcpServers?: Record<string, McpServerConfig>;
44
- /** Allowed tool names for Claude Agent SDK */
44
+ /** Tool names to approve. Absent → every tool of every server in `mcpServers` (ggui#1714). */
45
45
  allowedTools?: string[];
46
+ /**
47
+ * CLI built-in tools to offer the model (e.g. `['Write']`). Default: none.
48
+ * An explicit opt-in: a built-in that writes or executes acts on the host (ggui#1714).
49
+ */
50
+ builtinTools?: readonly string[];
46
51
  /** Subagent definitions for Claude Agent SDK */
47
52
  agents?: Record<string, AgentDefinition>;
48
53
  /** Custom CLI path for Docker deployments */
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/adapters/base.ts"],"names":[],"mappings":";AA2FO,IAAe,mBAAf,MAAgC;AAAA,EAKrC,YAA+B,MAAA,EAA0B;AAA1B,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAA2B;AAAA,EAA3B,MAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiB/B,eAAe,cAAA,EAAgC;AAC7C,IAAA,OAAO,iBAAiB,cAAc,CAAA;AAAA,EACxC;AACF;AAQA,SAAS,iBAAiB,OAAA,EAAyB;AACjD,EAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,OAAA,CAAQ,GAAG,CAAA;AACtC,EAAA,OAAO,cAAc,CAAA,GAAI,OAAA,CAAQ,KAAA,CAAM,UAAA,GAAa,CAAC,CAAA,GAAI,OAAA;AAC3D;AAKO,SAAS,cAAA,CACd,WACG,WAAA,EACM;AACT,EAAA,IAAI,MAAA,CAAO,QAAQ,OAAO,IAAA;AAC1B,EAAA,IAAI,MAAA,CAAO,YAAY,OAAO,IAAA;AAC9B,EAAA,IAAI,MAAA,CAAO,iBAAiB,OAAO,IAAA;AACnC,EAAA,OAAO,WAAA,CAAY,KAAK,CAAC,IAAA,KAAS,CAAC,CAAC,OAAA,CAAQ,GAAA,CAAI,IAAI,CAAC,CAAA;AACvD","file":"base.js","sourcesContent":["// packages/ui-gen/src/adapters/base.ts\n//\n// Abstract base class for all generator adapters.\n\nimport type { McpServerConfig, AgentDefinition } from '@anthropic-ai/claude-agent-sdk';\nimport type { JsonObject } from '@ggui-ai/protocol';\nimport type { AdapterResult, ToolDefinition, ProviderName, AdapterMode } from './types';\n\n/**\n * Core configuration shared by all adapters.\n */\nexport interface AdapterConfig {\n /** API key for the provider (BYOK) */\n apiKey?: string;\n /** Use Bedrock instead of direct API (Claude only) */\n useBedrock?: boolean;\n /**\n * ggui#1185 — run on the machine's own Claude Code login instead of a key.\n * Claude SDK adapter only. The adapter strips every provider key from the\n * spawned env and pins the run tool-less, config-less and non-bare (see\n * `claude/claude-code-login.ts`). Opt-in; absent ⇒ key-based auth.\n */\n claudeCodeLogin?: boolean;\n /**\n * Thinking level for models with extended thinking (Gemini 3.x Flash\n * via the Interactions API). Mirrors the API's `thinking_level` enum —\n * `'minimal'` is the floor (the API has NO true off), then `'low'` /\n * `'medium'` / `'high'`. Omitted → provider default, and Gemini 3.5\n * Flash defaults thinking ON with no cap — the dominant latency cost\n * in contract-bounded UI generation. Only the Google adapter consumes\n * this today.\n */\n thinkingLevel?: 'minimal' | 'low' | 'medium' | 'high';\n}\n\n/**\n * Extended configuration for Claude Agent SDK adapter.\n * Separated from AdapterConfig to avoid polluting the base interface\n * with provider-specific concerns.\n */\nexport interface ClaudeSdkConfig extends AdapterConfig {\n /** Working directory for SDK subprocess */\n cwd?: string;\n /** Environment variables for SDK subprocess */\n env?: Record<string, string>;\n /** MCP server configs for Claude Agent SDK (keyed by name) */\n mcpServers?: Record<string, McpServerConfig>;\n /** Allowed tool names for Claude Agent SDK */\n allowedTools?: string[];\n /** Subagent definitions for Claude Agent SDK */\n agents?: Record<string, AgentDefinition>;\n /** Custom CLI path for Docker deployments */\n cliPath?: string;\n /** stderr callback for debug logging */\n stderr?: (data: string) => void;\n /** Progress callback */\n onProgress?: (event: { type: string; [key: string]: unknown }) => void;\n}\n\n/**\n * Union config type — adapters accept their specific config\n * but the registry uses this union so callers can pass any config.\n */\nexport type AnyAdapterConfig = AdapterConfig | ClaudeSdkConfig;\n\n/**\n * Parameters for a single generation run.\n */\nexport interface GenerateParams {\n /** System prompt (HOW to build — shared across all SDKs) */\n systemPrompt: string;\n /** User prompt (WHAT to build — the specific request) */\n userPrompt: string;\n /** Model identifier (provider-native format, e.g., 'claude-sonnet-4-6') */\n model: string;\n /** SDK-agnostic tool definitions */\n tools: ToolDefinition[];\n /** Maximum agentic loop turns */\n maxTurns: number;\n /** Model-specific options (e.g., speed_priority for Claude fast mode) */\n modelOptions?: JsonObject;\n}\n\n/**\n * Abstract base class for generator adapters.\n *\n * Each adapter wraps a specific provider SDK (Claude, OpenAI, Google)\n * in either raw API mode or agent SDK mode, and implements the tool-use\n * loop using the SDK's native API. All adapters produce the same\n * output format (AdapterResult).\n */\nexport abstract class GeneratorAdapter {\n abstract readonly provider: ProviderName;\n abstract readonly mode: AdapterMode;\n abstract readonly displayName: string;\n\n constructor(protected readonly config: AnyAdapterConfig) {}\n\n /**\n * Run UI generation with the given tools, prompt, and model.\n */\n abstract generate(params: GenerateParams): Promise<AdapterResult>;\n\n /**\n * Check if this adapter can run (SDK installed, credentials available).\n */\n abstract isAvailable(): boolean;\n\n /**\n * Map a LiteLLM-format model ID to this SDK's native format.\n * Default: strips the provider prefix (e.g., 'anthropic/claude-sonnet-4-6' -> 'claude-sonnet-4-6').\n * Override only if the prefix convention differs.\n */\n resolveModelId(litellmModelId: string): string {\n return stripModelPrefix(litellmModelId);\n }\n}\n\n/**\n * Strip provider prefix from LiteLLM-format model IDs.\n * 'anthropic/claude-sonnet-4-6' -> 'claude-sonnet-4-6'\n * 'openai/gpt-5.3-codex' -> 'gpt-5.3-codex'\n * 'gemini/gemini-3-flash-preview' -> 'gemini-3-flash-preview'\n */\nfunction stripModelPrefix(modelId: string): string {\n const slashIndex = modelId.indexOf('/');\n return slashIndex >= 0 ? modelId.slice(slashIndex + 1) : modelId;\n}\n\n/**\n * Check if credentials are available from config or environment variables.\n */\nexport function hasCredentials(\n config: AdapterConfig,\n ...envVarNames: string[]\n): boolean {\n if (config.apiKey) return true;\n if (config.useBedrock) return true;\n if (config.claudeCodeLogin) return true;\n return envVarNames.some((name) => !!process.env[name]);\n}\n"]}
1
+ {"version":3,"sources":["../../src/adapters/base.ts"],"names":[],"mappings":";AAgGO,IAAe,mBAAf,MAAgC;AAAA,EAKrC,YAA+B,MAAA,EAA0B;AAA1B,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAA2B;AAAA,EAA3B,MAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiB/B,eAAe,cAAA,EAAgC;AAC7C,IAAA,OAAO,iBAAiB,cAAc,CAAA;AAAA,EACxC;AACF;AAQA,SAAS,iBAAiB,OAAA,EAAyB;AACjD,EAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,OAAA,CAAQ,GAAG,CAAA;AACtC,EAAA,OAAO,cAAc,CAAA,GAAI,OAAA,CAAQ,KAAA,CAAM,UAAA,GAAa,CAAC,CAAA,GAAI,OAAA;AAC3D;AAKO,SAAS,cAAA,CACd,WACG,WAAA,EACM;AACT,EAAA,IAAI,MAAA,CAAO,QAAQ,OAAO,IAAA;AAC1B,EAAA,IAAI,MAAA,CAAO,YAAY,OAAO,IAAA;AAC9B,EAAA,IAAI,MAAA,CAAO,iBAAiB,OAAO,IAAA;AACnC,EAAA,OAAO,WAAA,CAAY,KAAK,CAAC,IAAA,KAAS,CAAC,CAAC,OAAA,CAAQ,GAAA,CAAI,IAAI,CAAC,CAAA;AACvD","file":"base.js","sourcesContent":["// packages/ui-gen/src/adapters/base.ts\n//\n// Abstract base class for all generator adapters.\n\nimport type { McpServerConfig, AgentDefinition } from '@anthropic-ai/claude-agent-sdk';\nimport type { JsonObject } from '@ggui-ai/protocol';\nimport type { AdapterResult, ToolDefinition, ProviderName, AdapterMode } from './types';\n\n/**\n * Core configuration shared by all adapters.\n */\nexport interface AdapterConfig {\n /** API key for the provider (BYOK) */\n apiKey?: string;\n /** Use Bedrock instead of direct API (Claude only) */\n useBedrock?: boolean;\n /**\n * ggui#1185 — run on the machine's own Claude Code login instead of a key.\n * Claude SDK adapter only. The adapter strips every provider key from the\n * spawned env and pins the run tool-less, config-less and non-bare (see\n * `claude/claude-code-login.ts`). Opt-in; absent ⇒ key-based auth.\n */\n claudeCodeLogin?: boolean;\n /**\n * Thinking level for models with extended thinking (Gemini 3.x Flash\n * via the Interactions API). Mirrors the API's `thinking_level` enum —\n * `'minimal'` is the floor (the API has NO true off), then `'low'` /\n * `'medium'` / `'high'`. Omitted → provider default, and Gemini 3.5\n * Flash defaults thinking ON with no cap — the dominant latency cost\n * in contract-bounded UI generation. Only the Google adapter consumes\n * this today.\n */\n thinkingLevel?: 'minimal' | 'low' | 'medium' | 'high';\n}\n\n/**\n * Extended configuration for Claude Agent SDK adapter.\n * Separated from AdapterConfig to avoid polluting the base interface\n * with provider-specific concerns.\n */\nexport interface ClaudeSdkConfig extends AdapterConfig {\n /** Working directory for SDK subprocess */\n cwd?: string;\n /** Environment variables for SDK subprocess */\n env?: Record<string, string>;\n /** MCP server configs for Claude Agent SDK (keyed by name) */\n mcpServers?: Record<string, McpServerConfig>;\n /** Tool names to approve. Absent → every tool of every server in `mcpServers` (ggui#1714). */\n allowedTools?: string[];\n /**\n * CLI built-in tools to offer the model (e.g. `['Write']`). Default: none.\n * An explicit opt-in: a built-in that writes or executes acts on the host (ggui#1714).\n */\n builtinTools?: readonly string[];\n /** Subagent definitions for Claude Agent SDK */\n agents?: Record<string, AgentDefinition>;\n /** Custom CLI path for Docker deployments */\n cliPath?: string;\n /** stderr callback for debug logging */\n stderr?: (data: string) => void;\n /** Progress callback */\n onProgress?: (event: { type: string; [key: string]: unknown }) => void;\n}\n\n/**\n * Union config type — adapters accept their specific config\n * but the registry uses this union so callers can pass any config.\n */\nexport type AnyAdapterConfig = AdapterConfig | ClaudeSdkConfig;\n\n/**\n * Parameters for a single generation run.\n */\nexport interface GenerateParams {\n /** System prompt (HOW to build — shared across all SDKs) */\n systemPrompt: string;\n /** User prompt (WHAT to build — the specific request) */\n userPrompt: string;\n /** Model identifier (provider-native format, e.g., 'claude-sonnet-4-6') */\n model: string;\n /** SDK-agnostic tool definitions */\n tools: ToolDefinition[];\n /** Maximum agentic loop turns */\n maxTurns: number;\n /** Model-specific options (e.g., speed_priority for Claude fast mode) */\n modelOptions?: JsonObject;\n}\n\n/**\n * Abstract base class for generator adapters.\n *\n * Each adapter wraps a specific provider SDK (Claude, OpenAI, Google)\n * in either raw API mode or agent SDK mode, and implements the tool-use\n * loop using the SDK's native API. All adapters produce the same\n * output format (AdapterResult).\n */\nexport abstract class GeneratorAdapter {\n abstract readonly provider: ProviderName;\n abstract readonly mode: AdapterMode;\n abstract readonly displayName: string;\n\n constructor(protected readonly config: AnyAdapterConfig) {}\n\n /**\n * Run UI generation with the given tools, prompt, and model.\n */\n abstract generate(params: GenerateParams): Promise<AdapterResult>;\n\n /**\n * Check if this adapter can run (SDK installed, credentials available).\n */\n abstract isAvailable(): boolean;\n\n /**\n * Map a LiteLLM-format model ID to this SDK's native format.\n * Default: strips the provider prefix (e.g., 'anthropic/claude-sonnet-4-6' -> 'claude-sonnet-4-6').\n * Override only if the prefix convention differs.\n */\n resolveModelId(litellmModelId: string): string {\n return stripModelPrefix(litellmModelId);\n }\n}\n\n/**\n * Strip provider prefix from LiteLLM-format model IDs.\n * 'anthropic/claude-sonnet-4-6' -> 'claude-sonnet-4-6'\n * 'openai/gpt-5.3-codex' -> 'gpt-5.3-codex'\n * 'gemini/gemini-3-flash-preview' -> 'gemini-3-flash-preview'\n */\nfunction stripModelPrefix(modelId: string): string {\n const slashIndex = modelId.indexOf('/');\n return slashIndex >= 0 ? modelId.slice(slashIndex + 1) : modelId;\n}\n\n/**\n * Check if credentials are available from config or environment variables.\n */\nexport function hasCredentials(\n config: AdapterConfig,\n ...envVarNames: string[]\n): boolean {\n if (config.apiKey) return true;\n if (config.useBedrock) return true;\n if (config.claudeCodeLogin) return true;\n return envVarNames.some((name) => !!process.env[name]);\n}\n"]}
@@ -2,7 +2,7 @@ import { EvaluationConfig } from '../evaluation/types.js';
2
2
  import { RuntimeRenderCheck } from '../harness/types-public.js';
3
3
  import { ModelRoles, GenerationResult } from '../harness/result-types.js';
4
4
  import { ProviderName, ToolDefinition } from './types.js';
5
- import { Q as QualityConfig, b as AgentConfig, V as VisionProvider } from '../types-public-B7wn4ce5.js';
5
+ import { Q as QualityConfig, b as AgentConfig, V as VisionProvider } from '../types-public-h8IMnYuh.js';
6
6
  import { DataContract, JsonObject, GadgetDescriptor } from '@ggui-ai/protocol';
7
7
  import { C as CanvasClass, D as DesignMode } from '../design-mode-DGWN_Gkm.js';
8
8
  import { C as CriteriaBank } from '../bank-Ctdp3FUA.js';
@@ -12,7 +12,7 @@ import '../axes-CKVCzR_x.js';
12
12
  import '../llm.js';
13
13
  import '../types-Dh1s4K9j.js';
14
14
  import '../policy.js';
15
- import '../visual-evaluator-C9L-o6ap.js';
15
+ import '../visual-evaluator-C2strxqD.js';
16
16
  import 'puppeteer-core';
17
17
  import 'zod';
18
18
  import '@ggui-ai/mcp-server-core';
@@ -532,10 +532,7 @@ var init_json_schema_ts = __esm({
532
532
  var NOT_YET_TAUGHT_TOKENS, taughtTokenManifest;
533
533
  var init_taught_tokens = __esm({
534
534
  "src/taught-tokens.ts"() {
535
- NOT_YET_TAUGHT_TOKENS = /* @__PURE__ */ new Set([
536
- "--ggui-color-controlAccentOutline",
537
- "--ggui-color-controlOutline"
538
- ]);
535
+ NOT_YET_TAUGHT_TOKENS = /* @__PURE__ */ new Set([]);
539
536
  taughtTokenManifest = consumedTokenManifest.filter(
540
537
  (token) => !NOT_YET_TAUGHT_TOKENS.has(token)
541
538
  );
@@ -4626,6 +4623,40 @@ function detectComponentNeverReturns(sourceCode) {
4626
4623
  for (const st of body.statements) if (!isFunctionLike(st)) visit(st);
4627
4624
  return returnsValue ? null : { jsxStatementLine };
4628
4625
  }
4626
+ function kitControlTags(source) {
4627
+ const out = [];
4628
+ const open = new RegExp(`<(${KIT_CONTROLS.join("|")})\\b`, "g");
4629
+ for (const m of source.matchAll(open)) {
4630
+ const start = (m.index ?? 0) + m[0].length;
4631
+ let depth = 0;
4632
+ let quote = null;
4633
+ let i = start;
4634
+ for (; i < source.length; i += 1) {
4635
+ const c = source[i];
4636
+ if (quote !== null) {
4637
+ if (c === quote && source[i - 1] !== "\\") quote = null;
4638
+ } else if (c === '"' || c === "'" || c === "`") quote = c;
4639
+ else if (c === "{") depth += 1;
4640
+ else if (c === "}") depth -= 1;
4641
+ else if (c === ">" && depth === 0) break;
4642
+ }
4643
+ out.push({ name: m[1] ?? "", attrs: source.slice(start, i), line: source.slice(0, m.index ?? 0).split("\n").length });
4644
+ }
4645
+ return out;
4646
+ }
4647
+ function inlineBorderKey(attrs, source) {
4648
+ const BORDER = /\b(border(?:Top|Right|Bottom|Left)?(?:Color|Width|Style)?)\s*:/;
4649
+ const style = attrs.match(/\bstyle=\{\{([\s\S]*)\}\}/);
4650
+ if (style !== null) {
4651
+ const hit2 = (style[1] ?? "").match(BORDER);
4652
+ if (hit2 !== null) return hit2[1] ?? null;
4653
+ }
4654
+ const named = attrs.match(/\bstyle=\{\s*([A-Za-z_$][\w$]*)\s*\}/);
4655
+ if (named === null) return null;
4656
+ const decl = source.match(new RegExp(`\\b(?:const|let)\\s+${named[1]}\\b[^=]*=\\s*\\{([^{}]*)\\}`));
4657
+ const hit = decl !== null ? (decl[1] ?? "").match(BORDER) : null;
4658
+ return hit !== null ? hit[1] ?? null : null;
4659
+ }
4629
4660
  async function runTier0Checks(sourceCode, contract, buildErrors, gadgetTypes, designMode = DEFAULT_DESIGN_MODE) {
4630
4661
  const issues = [];
4631
4662
  const lines = sourceCode.split("\n");
@@ -5023,6 +5054,20 @@ ${errorDetail}`,
5023
5054
  });
5024
5055
  }
5025
5056
  }
5057
+ for (const tag of enforceDesignVocabulary ? kitControlTags(sourceCode) : []) {
5058
+ const key = inlineBorderKey(tag.attrs, sourceCode);
5059
+ if (key === null) continue;
5060
+ issues.push({
5061
+ tier: 0,
5062
+ result: "fail",
5063
+ category: "tokens",
5064
+ subcategory: "control-inline-border",
5065
+ severity: "critical",
5066
+ description: `control-inline-border: <${tag.name}> sets an inline \`${key}\` \u2014 a kit control draws its own boundary, and an inline border paints over it`,
5067
+ 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).`,
5068
+ line: tag.line
5069
+ });
5070
+ }
5026
5071
  if (!sourceCode.includes("interface Props") && !sourceCode.includes("type Props")) {
5027
5072
  issues.push({
5028
5073
  tier: 0,
@@ -5294,7 +5339,7 @@ async function runTier0(sourceCode, contract, designMode = DEFAULT_DESIGN_MODE)
5294
5339
  }
5295
5340
  return { issues, pass };
5296
5341
  }
5297
- var MANIFEST_TOKENS, RETIRED_TOKEN_SUCCESSORS, TOKEN_REF_RE, HOOK_NAME_FOR, CONTRACT_FIELD_FOR;
5342
+ var MANIFEST_TOKENS, RETIRED_TOKEN_SUCCESSORS, TOKEN_REF_RE, HOOK_NAME_FOR, CONTRACT_FIELD_FOR, KIT_CONTROLS;
5298
5343
  var init_run_tier0 = __esm({
5299
5344
  "src/check/run-tier0.ts"() {
5300
5345
  init_design_mode();
@@ -5323,6 +5368,7 @@ var init_run_tier0 = __esm({
5323
5368
  stream: "streamSpec",
5324
5369
  context: "contextSpec"
5325
5370
  };
5371
+ KIT_CONTROLS = ["Button", "Input", "Select", "TextArea", "Checkbox", "RadioGroup", "Toggle", "SearchField"];
5326
5372
  }
5327
5373
  });
5328
5374
 
@@ -6567,6 +6613,7 @@ function instrumentRead(kind, m) {
6567
6613
  return { verdict: "n/a", evidence: `fill ${m.inkRatio.toFixed(3)} reported; no floor ruled` };
6568
6614
  }
6569
6615
  function readRow(row, answers, m, unansweredReason) {
6616
+ if (row.evaluation === "judge" && REQUIRES_POST_ACTION_CAPTURE.has(row.id) && m.postActionCapture !== true) return { verdict: "n/a", evidence: NO_POST_ACTION };
6570
6617
  if (row.evaluation === "judge") return majority(row.id, answers, unansweredReason);
6571
6618
  if (row.evaluation === "human") return { verdict: "n/a", evidence: `reader's column (status ${row.status})` };
6572
6619
  if (row.status !== "live") return { verdict: "n/a", evidence: `not implemented (status ${row.status})` };
@@ -6625,14 +6672,14 @@ ${text}
6625
6672
  }
6626
6673
  function buildCriteriaJudgeBlock(bank, selected, inputs = {}) {
6627
6674
  const byId = new Map(bankRows(bank).map((r) => [r.id, r]));
6628
- const asked = selected.selection.map((s) => byId.get(s.id)).filter((r) => r !== void 0 && r.evaluation === "judge");
6675
+ const asked = selected.selection.map((s) => byId.get(s.id)).filter((r) => r !== void 0 && r.evaluation === "judge").filter((r) => inputs.postActionCapture === true || !REQUIRES_POST_ACTION_CAPTURE.has(r.id));
6629
6676
  if (asked.length === 0) return "";
6630
6677
  const lines = asked.map((r) => `- ${r.id} (${r.severity}): ${r.text} Evidence must name: ${r.evidence}`);
6631
6678
  return "## Criteria \u2014 answer each one from the FRAME only\nRead these off the screenshot. Never infer them from the request, the styling profile or any direction text above.\n" + (inputs.frame !== void 0 ? `${criteriaFrameLine(inputs.frame)}
6632
6679
  ` : "") + (inputs.propsJson !== void 0 ? criteriaPropsSection(inputs.propsJson) : "") + `${lines.join("\n")}
6633
6680
  Add to your JSON a top-level "criteria" array with exactly these ids: [{ "id": "<id>", "verdict": "pass" | "fail" | "n/a", "evidence": "<one sentence naming the region or element you read it from>" }]. Every row here was selected as applicable to this card, so "n/a" never means "does not apply": use it ONLY when this frame cannot show the criterion \u2014 it is then recorded as not evaluated.`;
6634
6681
  }
6635
- var INSTRUMENT_BY_ID, INSTRUMENT_BESIDE_JUDGE, CRITERIA_PROPS_MAX_CHARS;
6682
+ var INSTRUMENT_BY_ID, INSTRUMENT_BESIDE_JUDGE, REQUIRES_POST_ACTION_CAPTURE, NO_POST_ACTION, CRITERIA_PROPS_MAX_CHARS;
6636
6683
  var init_resolve = __esm({
6637
6684
  "src/evaluation/criteria/resolve.ts"() {
6638
6685
  init_bank();
@@ -6645,6 +6692,8 @@ var init_resolve = __esm({
6645
6692
  INSTRUMENT_BESIDE_JUDGE = {
6646
6693
  "space.edge": "edge"
6647
6694
  };
6695
+ REQUIRES_POST_ACTION_CAPTURE = /* @__PURE__ */ new Set(["state.feedback"]);
6696
+ NO_POST_ACTION = "not evaluated: no post-action capture";
6648
6697
  CRITERIA_PROPS_MAX_CHARS = 2e3;
6649
6698
  }
6650
6699
  });
@@ -12969,13 +13018,14 @@ MANDATORY:
12969
13018
  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))\`.
12970
13019
  3. NEVER invent your own palette. The system provides primary, neutral, success, warning, error, and info \u2014 use ONLY these.
12971
13020
  4. NEVER include literal fallback values in ANY token reference \u2014 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.
13021
+ 5. NEVER set an inline \`border\`, \`borderColor\`, \`borderWidth\` or \`borderStyle\` on a kit control \u2014 \`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).
12972
13022
 
12973
13023
  Token categories:
12974
13024
  - Brand fills: \`var(--ggui-color-primary-600)\`, \`var(--ggui-color-primary-50)\`
12975
13025
  - Accent text: \`var(--ggui-color-link)\` \u2014 eyebrows, taglines, links, labels; readable per mode (never a bare \`primary-<stop>\` as a text color)
12976
13026
  - Text: \`var(--ggui-color-onContainer)\`, \`var(--ggui-color-onSunken)\`
12977
13027
  - Backgrounds: \`var(--ggui-color-container)\`, \`var(--ggui-color-sunken)\`
12978
- - Borders: \`var(--ggui-color-outline)\`
13028
+ - Borders: \`var(--ggui-color-outline)\` \u2014 dividers and the edges of non-interactive boxes; a control's edge is the kit's (rule 5)
12979
13029
  - Spacing: \`var(--ggui-spacing-4)\`, \`var(--ggui-spacing-6)\`
12980
13030
  - Typography: \`var(--ggui-font-size-sm)\`, \`var(--ggui-font-weight-semibold)\`
12981
13031
  - Shadows: \`var(--ggui-shape-shadow-sm)\`, \`var(--ggui-shape-shadow-md)\`, \`var(--ggui-shape-shadow-lg)\`
@@ -12990,7 +13040,7 @@ Use the FULL primary palette throughout the component \u2014 NOT only on submit
12990
13040
  | Element | Token | Purpose |
12991
13041
  |---------|-------|---------|
12992
13042
  | Section headers, hero areas, highlight strips | \`primary-50\` / \`primary-100\` | Subtle branded backgrounds |
12993
- | Borders, dividers, focus rings, input focus | \`primary-200\` / \`primary-300\` | Branded structure |
13043
+ | Dividers, section rules, edges of non-interactive boxes | \`primary-200\` / \`primary-300\` | Branded structure \u2014 never a control's border (rule 5) |
12994
13044
  | Accent TEXT \u2014 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 \u2014 a bare \`primary-300\u2026600\` as a text color fails tier-0 |
12995
13045
  | Icon fills, badges, active indicators | \`primary-500\` / \`primary-600\` | Core accent fill |
12996
13046
  | Buttons, CTAs, filled interactive elements | \`primary-600\` / \`primary-700\` | Primary actions |
@@ -13663,6 +13713,7 @@ These tokens adapt automatically to any theme (light, dark, branded). **ALWAYS u
13663
13713
  | link | var(--ggui-color-link) | Accent TEXT \u2014 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 |
13664
13714
  | outline | var(--ggui-color-outline) | Borders, dividers |
13665
13715
  | outlineVariant | var(--ggui-color-outlineVariant) | Subtle borders |
13716
+ | controlOutline | var(--ggui-color-controlOutline) | A control's edge \u2014 clears 3:1 on its surface; kit controls draw it themselves |
13666
13717
 
13667
13718
  **Usage pattern:**
13668
13719
  - Page/section background \u2192 \`var(--ggui-color-ground)\`
@@ -13787,7 +13838,7 @@ Use \`var(--ggui-spacing-N)\` for all padding, gap, and margin values. **Never u
13787
13838
  ### Color usage guide
13788
13839
  - **Layering roles** (ground, container, elevated, sunken + their on* inks; outline): one kind of AREA each \u2014 use them for every background/text/border decision; they adapt to any theme
13789
13840
  - **primary-50/100**: Section backgrounds, highlight strips, card headers
13790
- - **primary-200/300**: Borders, dividers, focus rings, input outlines
13841
+ - **primary-200/300**: Dividers and section rules \u2014 never a control's border: kit controls draw their own edge (\`controlOutline\` / \`controlAccentOutline\`), so set no inline border on them
13791
13842
  - **primary-500/600**: Filled buttons, CTAs, badges, icon fills, active indicators \u2014 fills and borders, NOT a text color
13792
13843
  - **link**: Accent TEXT \u2014 eyebrows, taglines, links, labels, inline arrows; the brand's readable ink per mode (a bare \`primary-300\u2026600\` as a text color fails tier-0: \`universal.accent_text_ink\`)
13793
13844
  - **primary-700/800/900**: Headings and text on light primary backgrounds