@ggui-ai/ui-gen 0.27.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.
@@ -50,6 +50,15 @@ interface SameExchangeBreak {
50
50
  readonly tool: string;
51
51
  readonly repeats: number;
52
52
  }
53
+ /**
54
+ * ggui#1791 — the same-failure guard ended the run: one failing check
55
+ * (`signature`, its first 160 characters) was reported by this many failed
56
+ * writes since the last passing one, whatever the patch.
57
+ */
58
+ interface SameFailureBreak {
59
+ readonly signature: string;
60
+ readonly repeats: number;
61
+ }
53
62
  interface GenerationResult extends AdapterResult {
54
63
  /** Number of generation passes the harness performed */
55
64
  passesUsed: number;
@@ -65,6 +74,8 @@ interface GenerationResult extends AdapterResult {
65
74
  inLoopVisualTokens?: VisualLegTokens;
66
75
  /** Set only when the same-exchange guard ended the run (ggui#404); no key otherwise. */
67
76
  sameExchangeBreak?: SameExchangeBreak;
77
+ /** ggui#1791 — set only when the same-failure guard ended the run. */
78
+ sameFailureBreak?: SameFailureBreak;
68
79
  /** Whether background improvement should be spawned (auto-improve mode) */
69
80
  needsBackgroundImprovement?: boolean;
70
81
  /** Whether at least one commit passed the self-check (compile + type check + lint) */
@@ -156,4 +167,4 @@ interface EvaluationConfig {
156
167
  */
157
168
  declare function resolveModelForRole(role: ModelRole, models: ModelRoles | undefined, fallback: string): string;
158
169
 
159
- export { type EvaluationConfig, type GenerationContext, type GenerationResult, type ModelRole, type ModelRoles, RenderingContext, type SameExchangeBreak, resolveModelForRole };
170
+ export { type EvaluationConfig, type GenerationContext, type GenerationResult, type ModelRole, type ModelRoles, RenderingContext, type SameExchangeBreak, type SameFailureBreak, resolveModelForRole };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/harness/result-types.ts"],"names":[],"mappings":";AA2LA,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;AAOO,SAAS,mBAAA,CACd,IAAA,EACA,MAAA,EACA,QAAA,EACQ;AACR,EAAA,IAAI,CAAC,QAAQ,OAAO,QAAA;AACpB,EAAA,MAAM,QAAA,GAAW,MAAA,CAAO,IAAI,CAAA,IAAK,OAAO,OAAA,IAAW,QAAA;AACnD,EAAA,OAAO,iBAAiB,QAAQ,CAAA;AAClC","file":"result-types.js","sourcesContent":["// packages/ui-gen/src/harness/result-types.ts\n//\n// Result shapes returned by the harness runtime.\n//\n// Two output types travel the dispatch surface:\n// - AdapterResult — raw adapter output (provider-direct path)\n// - GenerationResult — full harness output (run-loop, eval, breakdown)\n//\n// ModelRoles + resolveModelForRole live here too (the harness decides\n// which model to use per internal task).\n\nimport type { DataContract, JsonObject } from \"@ggui-ai/protocol\";\nimport type { AdapterResult } from \"../adapters/types.js\";\nimport type { RenderingContext } from \"../contract-context.js\";\nimport type { EvalResult } from \"../evaluation/types-public.js\";\nimport type { VisualLegTokens } from \"../evaluation/visual-evaluator.js\";\nimport type { CanvasClass, DesignMode } from \"../design-mode.js\";\n\n// =============================================================================\n// Model Roles\n// =============================================================================\n\n/**\n * Model role — each internal task in the harness can use a different model.\n *\n * - thinking: planning, design decisions, architecture (use frontier/thinking model)\n * - coding: writing TSX, implementing the design (use fast model)\n * - evaluation: scoring quality, finding issues (use balanced model)\n * - default: fallback for any role not explicitly set\n */\nexport type ModelRole = \"thinking\" | \"coding\" | \"evaluation\" | \"default\";\n\n/**\n * Maps model roles to model IDs.\n * Any role not specified falls back to 'default', then to params.model.\n *\n * @example\n * // Opus plans, Haiku codes, Sonnet evaluates\n * { thinking: 'claude-opus-4-6', coding: 'claude-haiku-4-5', default: 'claude-sonnet-4-6' }\n *\n * // All Haiku (cheapest)\n * { default: 'claude-haiku-4-5' }\n *\n * // All Opus (highest quality)\n * { default: 'claude-opus-4-6' }\n */\nexport type ModelRoles = Partial<Record<ModelRole, string>>;\n\n// =============================================================================\n// Generation Result\n// =============================================================================\n\n/**\n * The same-exchange guard (ggui#404) ended this generation: the coding model\n * made one identical tool exchange (same tool, same input, same result)\n * `repeats` times, so the loop stopped instead of burning its remaining turns.\n * Present ONLY when that guard ended the run — whether anything shipped is\n * the compiled code's to say (the loop falls back to an earlier build).\n */\nexport interface SameExchangeBreak {\n readonly tool: string;\n readonly repeats: number;\n}\n\nexport interface GenerationResult extends AdapterResult {\n /** Number of generation passes the harness performed */\n passesUsed: number;\n /** Three-tier evaluation result (tier 0 + LLM tier 1+2 + visual) */\n evalResult?: EvalResult;\n /**\n * ggui#1522 — what the IN-LOOP visual judge spent during this generation (its scoring calls, and its report-only\n * criteria calls apart), priced at the visual agent's model. NOT inside `tokens`, which covers the coding turns\n * and the text evaluator. And NOT the same calls as a post-generation judge's own visual pass (a benchmark\n * report's `meta.costs.visualUsd`): a cost rule that adds both is correct, one read as the other double-counts.\n * Absent when no in-loop visual leg ran or it reported no tokens.\n */\n inLoopVisualTokens?: VisualLegTokens;\n /** Set only when the same-exchange guard ended the run (ggui#404); no key otherwise. */\n sameExchangeBreak?: SameExchangeBreak;\n /** Whether background improvement should be spawned (auto-improve mode) */\n needsBackgroundImprovement?: boolean;\n /** Whether at least one commit passed the self-check (compile + type check + lint) */\n selfCheckPassed?: boolean;\n /** Which model was used for each role (for benchmark reporting) */\n modelRolesUsed?: Record<string, string>;\n /**\n * The triad this generation ran under — recorded ONLY when the caller\n * set `designMode` on the dispatch (a bench arm switch, an operator\n * option). Absent = the constrained default path, byte-identical to\n * the pre-`designMode` result.\n */\n designMode?: DesignMode;\n /**\n * The canvas class the `free` prompt stated — the explicit `canvas`\n * when the caller passed one, else the shell × screen derivation the\n * prompt itself used. Absent in constrained mode (that prompt states\n * no canvas) unless the caller passed one explicitly.\n */\n canvas?: CanvasClass;\n /** Phase-level timing breakdown (ms) */\n timing?: Record<string, number>;\n /** Per-turn/per-outcome breakdown for benchmark analysis.\n * Machine-parseable summary — phases counted by role, outcomes counted by rejection class. */\n breakdown?: {\n phases: { impl: number; patch: number; evalFix: number; scaffold?: number; fill?: number };\n outcomes: { pass: number; patchInvalid: number; selfCheckFail: number; diffFail: number };\n evalRounds: number;\n /** Coding-turn LLM wall-time (sum of per-turn llm=Xms). Stable semantics. */\n llmMs: number;\n /** Eval-round LLM wall-time (parallel LLM+visual). Added 2026-04-14. */\n evalLlmMs?: number;\n toolMs: number;\n /** Eval-round wall-clock (fix: was structurally 0 before 2026-04-14). */\n evalMs: number;\n /** Coding-only wall-time (inner loop minus eval). Added 2026-04-14. */\n codingMs?: number;\n /** Session setup+cleanup wall-time (outside inner loop). Added 2026-04-14. */\n setupMs?: number;\n };\n}\n\n// =============================================================================\n// Generation Context\n// =============================================================================\n\nexport interface GenerationContext {\n /** Progress callback */\n onProgress?: (event: unknown) => void;\n\n /** Called with initial result before evaluation loop (for early delivery) */\n onInitialResult?: (result: {\n componentCode: string;\n sourceCode?: string;\n }) => void | Promise<void>;\n\n /** Original user prompt (for evaluation context) */\n originalPrompt: string;\n\n /** Design context for evaluation */\n designContext?: string;\n\n /** Model roles — which model to use for each internal task */\n models?: ModelRoles;\n\n /** Rendering context — device and shell type affect layout strategy */\n rendering?: RenderingContext;\n\n /** Data contract — bind the component to its consumers */\n contract?: DataContract;\n\n /** Evaluation configuration */\n evaluation?: EvaluationConfig;\n}\n\n// Rendering context — canonical definition lives in\n// `../contract-context.ts` (one type, one prompt-builder family).\n// Re-exported here because this subpath is the harness's type hub.\nexport type { RenderingContext } from \"../contract-context.js\";\n\n// Data contract — re-exported from protocol (canonical types)\nexport type {\n ActionSpec,\n DataContract,\n JsonSchema,\n PropsSpec,\n StreamSpec,\n} from \"@ggui-ai/protocol\";\n\nexport interface EvaluationConfig {\n enabled: boolean;\n passThreshold: number;\n maxRounds?: number;\n model?: string;\n provider?: \"claude\" | \"openai\" | \"google\" | \"openrouter\";\n maxBudgetPerEval?: number;\n maxBudgetPerFix?: number;\n generatorOptions?: JsonObject;\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/**\n * Strip provider prefix from LiteLLM-format model IDs.\n * 'anthropic/claude-sonnet-4-6' -> 'claude-sonnet-4-6'\n */\nfunction stripModelPrefix(modelId: string): string {\n const slashIndex = modelId.indexOf(\"/\");\n return slashIndex >= 0 ? modelId.slice(slashIndex + 1) : modelId;\n}\n\n/**\n * Resolve the model ID for a given role.\n * Priority: role-specific → default role → params.model\n * Strips provider prefix (e.g., 'anthropic/') so the result is a native API model ID.\n */\nexport function resolveModelForRole(\n role: ModelRole,\n models: ModelRoles | undefined,\n fallback: string\n): string {\n if (!models) return fallback;\n const resolved = models[role] ?? models.default ?? fallback;\n return stripModelPrefix(resolved);\n}\n"]}
1
+ {"version":3,"sources":["../../src/harness/result-types.ts"],"names":[],"mappings":";AAuMA,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;AAOO,SAAS,mBAAA,CACd,IAAA,EACA,MAAA,EACA,QAAA,EACQ;AACR,EAAA,IAAI,CAAC,QAAQ,OAAO,QAAA;AACpB,EAAA,MAAM,QAAA,GAAW,MAAA,CAAO,IAAI,CAAA,IAAK,OAAO,OAAA,IAAW,QAAA;AACnD,EAAA,OAAO,iBAAiB,QAAQ,CAAA;AAClC","file":"result-types.js","sourcesContent":["// packages/ui-gen/src/harness/result-types.ts\n//\n// Result shapes returned by the harness runtime.\n//\n// Two output types travel the dispatch surface:\n// - AdapterResult — raw adapter output (provider-direct path)\n// - GenerationResult — full harness output (run-loop, eval, breakdown)\n//\n// ModelRoles + resolveModelForRole live here too (the harness decides\n// which model to use per internal task).\n\nimport type { DataContract, JsonObject } from \"@ggui-ai/protocol\";\nimport type { AdapterResult } from \"../adapters/types.js\";\nimport type { RenderingContext } from \"../contract-context.js\";\nimport type { EvalResult } from \"../evaluation/types-public.js\";\nimport type { VisualLegTokens } from \"../evaluation/visual-evaluator.js\";\nimport type { CanvasClass, DesignMode } from \"../design-mode.js\";\n\n// =============================================================================\n// Model Roles\n// =============================================================================\n\n/**\n * Model role — each internal task in the harness can use a different model.\n *\n * - thinking: planning, design decisions, architecture (use frontier/thinking model)\n * - coding: writing TSX, implementing the design (use fast model)\n * - evaluation: scoring quality, finding issues (use balanced model)\n * - default: fallback for any role not explicitly set\n */\nexport type ModelRole = \"thinking\" | \"coding\" | \"evaluation\" | \"default\";\n\n/**\n * Maps model roles to model IDs.\n * Any role not specified falls back to 'default', then to params.model.\n *\n * @example\n * // Opus plans, Haiku codes, Sonnet evaluates\n * { thinking: 'claude-opus-4-6', coding: 'claude-haiku-4-5', default: 'claude-sonnet-4-6' }\n *\n * // All Haiku (cheapest)\n * { default: 'claude-haiku-4-5' }\n *\n * // All Opus (highest quality)\n * { default: 'claude-opus-4-6' }\n */\nexport type ModelRoles = Partial<Record<ModelRole, string>>;\n\n// =============================================================================\n// Generation Result\n// =============================================================================\n\n/**\n * The same-exchange guard (ggui#404) ended this generation: the coding model\n * made one identical tool exchange (same tool, same input, same result)\n * `repeats` times, so the loop stopped instead of burning its remaining turns.\n * Present ONLY when that guard ended the run — whether anything shipped is\n * the compiled code's to say (the loop falls back to an earlier build).\n */\nexport interface SameExchangeBreak {\n readonly tool: string;\n readonly repeats: number;\n}\n\n/**\n * ggui#1791 — the same-failure guard ended the run: one failing check\n * (`signature`, its first 160 characters) was reported by this many failed\n * writes since the last passing one, whatever the patch.\n */\nexport interface SameFailureBreak {\n readonly signature: string;\n readonly repeats: number;\n}\n\nexport interface GenerationResult extends AdapterResult {\n /** Number of generation passes the harness performed */\n passesUsed: number;\n /** Three-tier evaluation result (tier 0 + LLM tier 1+2 + visual) */\n evalResult?: EvalResult;\n /**\n * ggui#1522 — what the IN-LOOP visual judge spent during this generation (its scoring calls, and its report-only\n * criteria calls apart), priced at the visual agent's model. NOT inside `tokens`, which covers the coding turns\n * and the text evaluator. And NOT the same calls as a post-generation judge's own visual pass (a benchmark\n * report's `meta.costs.visualUsd`): a cost rule that adds both is correct, one read as the other double-counts.\n * Absent when no in-loop visual leg ran or it reported no tokens.\n */\n inLoopVisualTokens?: VisualLegTokens;\n /** Set only when the same-exchange guard ended the run (ggui#404); no key otherwise. */\n sameExchangeBreak?: SameExchangeBreak;\n /** ggui#1791 — set only when the same-failure guard ended the run. */\n sameFailureBreak?: SameFailureBreak;\n /** Whether background improvement should be spawned (auto-improve mode) */\n needsBackgroundImprovement?: boolean;\n /** Whether at least one commit passed the self-check (compile + type check + lint) */\n selfCheckPassed?: boolean;\n /** Which model was used for each role (for benchmark reporting) */\n modelRolesUsed?: Record<string, string>;\n /**\n * The triad this generation ran under — recorded ONLY when the caller\n * set `designMode` on the dispatch (a bench arm switch, an operator\n * option). Absent = the constrained default path, byte-identical to\n * the pre-`designMode` result.\n */\n designMode?: DesignMode;\n /**\n * The canvas class the `free` prompt stated — the explicit `canvas`\n * when the caller passed one, else the shell × screen derivation the\n * prompt itself used. Absent in constrained mode (that prompt states\n * no canvas) unless the caller passed one explicitly.\n */\n canvas?: CanvasClass;\n /** Phase-level timing breakdown (ms) */\n timing?: Record<string, number>;\n /** Per-turn/per-outcome breakdown for benchmark analysis.\n * Machine-parseable summary — phases counted by role, outcomes counted by rejection class. */\n breakdown?: {\n phases: { impl: number; patch: number; evalFix: number; scaffold?: number; fill?: number };\n outcomes: { pass: number; patchInvalid: number; selfCheckFail: number; diffFail: number };\n evalRounds: number;\n /** Coding-turn LLM wall-time (sum of per-turn llm=Xms). Stable semantics. */\n llmMs: number;\n /** Eval-round LLM wall-time (parallel LLM+visual). Added 2026-04-14. */\n evalLlmMs?: number;\n toolMs: number;\n /** Eval-round wall-clock (fix: was structurally 0 before 2026-04-14). */\n evalMs: number;\n /** Coding-only wall-time (inner loop minus eval). Added 2026-04-14. */\n codingMs?: number;\n /** Session setup+cleanup wall-time (outside inner loop). Added 2026-04-14. */\n setupMs?: number;\n };\n}\n\n// =============================================================================\n// Generation Context\n// =============================================================================\n\nexport interface GenerationContext {\n /** Progress callback */\n onProgress?: (event: unknown) => void;\n\n /** Called with initial result before evaluation loop (for early delivery) */\n onInitialResult?: (result: {\n componentCode: string;\n sourceCode?: string;\n }) => void | Promise<void>;\n\n /** Original user prompt (for evaluation context) */\n originalPrompt: string;\n\n /** Design context for evaluation */\n designContext?: string;\n\n /** Model roles — which model to use for each internal task */\n models?: ModelRoles;\n\n /** Rendering context — device and shell type affect layout strategy */\n rendering?: RenderingContext;\n\n /** Data contract — bind the component to its consumers */\n contract?: DataContract;\n\n /** Evaluation configuration */\n evaluation?: EvaluationConfig;\n}\n\n// Rendering context — canonical definition lives in\n// `../contract-context.ts` (one type, one prompt-builder family).\n// Re-exported here because this subpath is the harness's type hub.\nexport type { RenderingContext } from \"../contract-context.js\";\n\n// Data contract — re-exported from protocol (canonical types)\nexport type {\n ActionSpec,\n DataContract,\n JsonSchema,\n PropsSpec,\n StreamSpec,\n} from \"@ggui-ai/protocol\";\n\nexport interface EvaluationConfig {\n enabled: boolean;\n passThreshold: number;\n maxRounds?: number;\n model?: string;\n provider?: \"claude\" | \"openai\" | \"google\" | \"openrouter\";\n maxBudgetPerEval?: number;\n maxBudgetPerFix?: number;\n generatorOptions?: JsonObject;\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/**\n * Strip provider prefix from LiteLLM-format model IDs.\n * 'anthropic/claude-sonnet-4-6' -> 'claude-sonnet-4-6'\n */\nfunction stripModelPrefix(modelId: string): string {\n const slashIndex = modelId.indexOf(\"/\");\n return slashIndex >= 0 ? modelId.slice(slashIndex + 1) : modelId;\n}\n\n/**\n * Resolve the model ID for a given role.\n * Priority: role-specific → default role → params.model\n * Strips provider prefix (e.g., 'anthropic/') so the result is a native API model ID.\n */\nexport function resolveModelForRole(\n role: ModelRole,\n models: ModelRoles | undefined,\n fallback: string\n): string {\n if (!models) return fallback;\n const resolved = models[role] ?? models.default ?? fallback;\n return stripModelPrefix(resolved);\n}\n"]}