@@ -2,14 +2,14 @@ import express from 'express';
22import cors from 'cors' ;
33import Anthropic from '@anthropic-ai/sdk' ;
44import {
5- type CapabilityPack ,
6- type ScriptPolicy ,
7- } from '@anarchitecture/summon' ;
8- import {
5+ compileSurfacePolicy ,
96 parseTokenValues ,
7+ type CapabilityPack ,
108 type ContractPromptBlock ,
119 type ProtocolLine ,
10+ type ScriptPolicy ,
1211 type SummonLayout ,
12+ type SurfacePlan ,
1313 type TokenOverride ,
1414} from '@anarchitecture/summon/engine' ;
1515import {
@@ -516,6 +516,8 @@ app.post('/api/generate', async (req, res) => {
516516 const edit = parsedEdit . edit ;
517517 const repairOptions = parseRepairOptions ( req . body ?. repair ) ;
518518
519+ const hasSurfacePolicy =
520+ req . body ?. surfacePolicy !== undefined && req . body . surfacePolicy !== null ;
519521 const requestedMode : 'static' | 'interactive' =
520522 req . body ?. mode === 'interactive' ? 'interactive' : 'static' ;
521523 let scriptPolicy : ScriptPolicy | undefined =
@@ -527,6 +529,7 @@ app.post('/api/generate', async (req, res) => {
527529 let pack : CapabilityPack | null = null ;
528530 let modeUpgraded = false ;
529531 let inferenceUsed = false ;
532+ let surfacePlan : SurfacePlan ;
530533
531534 // Shape classification — picks ONE response shape so the per-direction
532535 // block ships only the matching shape exemplar (atoms always ship). Falls
@@ -539,53 +542,64 @@ app.post('/api/generate', async (req, res) => {
539542 shape = await inferShape ( anthropic , prompt ) ;
540543 }
541544
542- // Layer 3: Haiku-based capability inference. Decides mode + narrows the
543- // pack to the minimal subset of intents the prompt actually needs. The
544- // pack is treated as a ceiling — inference can only narrow, never expand.
545- // Falls through to the Layer 2 regex on timeout or error.
546- if ( process . env . SUMMON_INFER_CAPABILITIES === '1' && capabilityCeiling ) {
547- const inferred = await inferPack ( anthropic , prompt , capabilityCeiling ) ;
548- if ( inferred ) {
549- inferenceUsed = true ;
550- if ( requestedMode === 'interactive' ) {
551- // Respect the user's explicit interactive choice. Haiku may narrow
552- // the pack, but won't downgrade to static.
553- mode = 'interactive' ;
554- pack = inferred . pack ?? capabilityCeiling ;
555- } else {
556- mode = inferred . mode ;
557- pack = inferred . pack ;
558- modeUpgraded = mode === 'interactive' ;
545+ if ( hasSurfacePolicy ) {
546+ const compiledPolicy = compileSurfacePolicy ( req . body . surfacePolicy , {
547+ capabilities : capabilityCeiling ,
548+ components : componentPack ,
549+ } ) ;
550+ mode = compiledPolicy . mode ;
551+ scriptPolicy = compiledPolicy . scriptPolicy ;
552+ pack = compiledPolicy . capabilities ;
553+ surfacePlan = compiledPolicy . surfacePlan ;
554+ } else {
555+ // Layer 3: Haiku-based capability inference. Decides mode + narrows the
556+ // pack to the minimal subset of intents the prompt actually needs. The
557+ // pack is treated as a ceiling — inference can only narrow, never expand.
558+ // Falls through to the Layer 2 regex on timeout or error.
559+ if ( process . env . SUMMON_INFER_CAPABILITIES === '1' && capabilityCeiling ) {
560+ const inferred = await inferPack ( anthropic , prompt , capabilityCeiling ) ;
561+ if ( inferred ) {
562+ inferenceUsed = true ;
563+ if ( requestedMode === 'interactive' ) {
564+ // Respect the user's explicit interactive choice. Haiku may narrow
565+ // the pack, but won't downgrade to static.
566+ mode = 'interactive' ;
567+ pack = inferred . pack ?? capabilityCeiling ;
568+ } else {
569+ mode = inferred . mode ;
570+ pack = inferred . pack ;
571+ modeUpgraded = mode === 'interactive' ;
572+ }
559573 }
560574 }
561- }
562575
563- // Layer 2 regex fallback — runs when inference is disabled, ceiling is
564- // missing, or Haiku returned null (timeout/parse failure). Only upgrades
565- // when a ceiling exists; without one there's no Capabilities block to emit.
566- if ( ! inferenceUsed ) {
567- if ( requestedMode === 'static' && capabilityCeiling && detectsInteractiveIntent ( prompt ) ) {
568- mode = 'interactive' ;
569- modeUpgraded = true ;
576+ // Layer 2 regex fallback — runs when inference is disabled, ceiling is
577+ // missing, or Haiku returned null (timeout/parse failure). Only upgrades
578+ // when a ceiling exists; without one there's no Capabilities block to emit.
579+ if ( ! inferenceUsed ) {
580+ if ( requestedMode === 'static' && capabilityCeiling && detectsInteractiveIntent ( prompt ) ) {
581+ mode = 'interactive' ;
582+ modeUpgraded = true ;
583+ }
584+ pack = mode === 'interactive' ? capabilityCeiling : null ;
570585 }
571- pack = mode === 'interactive' ? capabilityCeiling : null ;
572- }
573586
574- const resolvedSurface = resolveSurfaceGenerationPlan ( {
575- prompt,
576- mode,
577- scriptPolicy,
578- capabilities : pack ,
579- rawSurfacePlan : req . body ?. surfacePlan ,
580- rawSurfaceCeiling : req . body ?. surfaceCeiling ,
581- } ) ;
582- if ( mode !== resolvedSurface . mode ) {
583- modeUpgraded = mode === 'static' && resolvedSurface . mode === 'interactive' ? true : modeUpgraded ;
584- mode = resolvedSurface . mode ;
585- pack = mode === 'interactive' ? pack ?? capabilityCeiling : null ;
587+ const resolvedSurface = resolveSurfaceGenerationPlan ( {
588+ prompt,
589+ mode,
590+ scriptPolicy,
591+ capabilities : pack ,
592+ rawSurfacePlan : req . body ?. surfacePlan ,
593+ rawSurfaceCeiling : req . body ?. surfaceCeiling ,
594+ } ) ;
595+ if ( mode !== resolvedSurface . mode ) {
596+ modeUpgraded = mode === 'static' && resolvedSurface . mode === 'interactive' ? true : modeUpgraded ;
597+ mode = resolvedSurface . mode ;
598+ pack = mode === 'interactive' ? pack ?? capabilityCeiling : null ;
599+ }
600+ scriptPolicy = resolvedSurface . scriptPolicy ;
601+ surfacePlan = resolvedSurface . surfacePlan ;
586602 }
587- scriptPolicy = resolvedSurface . scriptPolicy ;
588- const surfacePlan = resolvedSurface . surfacePlan ;
589603
590604 res . setHeader ( 'Content-Type' , 'text/plain; charset=utf-8' ) ;
591605 res . setHeader ( 'Cache-Control' , 'no-cache, no-transform' ) ;
@@ -666,10 +680,11 @@ app.post('/api/generate', async (req, res) => {
666680 ghostPrompt : ghostContext ?. prompt ?? null ,
667681 layout,
668682 edit,
669- capabilities : pack ,
683+ capabilities : hasSurfacePolicy ? capabilityCeiling : pack ,
670684 components : componentPack ,
671- scriptPolicy,
672- surfacePlan,
685+ surfacePolicy : hasSurfacePolicy ? req . body . surfacePolicy : null ,
686+ scriptPolicy : hasSurfacePolicy ? undefined : scriptPolicy ,
687+ surfacePlan : hasSurfacePolicy ? null : surfacePlan ,
673688 tokenOverrides : overrides . applied ,
674689 activeTokensCss : ghostContext ?. tokenSource . css ?? direction ?. tokensCss ?? null ,
675690 preludeLines,
0 commit comments