Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ Open `http://localhost:5173/generate.html`.
4. Open `http://localhost:5173/adversarial.html` and confirm the sandbox
boundary still holds.

To steer generation from a Ghost fingerprint, set `SUMMON_GHOST_ROOTS` in
`apps/server/.env` before starting the demos. Each configured root should use
the canonical `.ghost/fingerprint/manifest.yml` package layout; legacy
`.ghost/fingerprint.yml` roots are bridged for compatibility. The Surface
Gallery adds a Ghost preset for each root, and the Generate workbench adds both
the Ghost scenario and the `Ghost · <id>` direction option.

The full guided path lives in
[docs/adoption/quickstart.md](docs/adoption/quickstart.md).

Expand All @@ -76,9 +83,10 @@ registered host tools.

## Demo Map

- `examples/surface-gallery` - first-run OSS gallery with curated live presets,
compact host tools, a sandboxed surface, and a small event strip.
- `/generate.html` - maintainer workbench for surface configs, allowed host
- `examples/surface-gallery` - primary adopter gallery with curated live
presets, compact host tools, Ghost-root presets when configured, a sandboxed
surface, and a small event strip.
- `/generate.html` - diagnostic maintainer workbench for surface configs, allowed host
tools, trusted host components, token overrides, validation retries,
edit/replay, Ghost steering, Devtools, and stream diagnostics.
- `/batch.html` - parallel prompt harness for prompt coverage, host tool wiring,
Expand Down
33 changes: 21 additions & 12 deletions apps/demo/src/generate-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
} from '@anarchitecture/summon/engine';
import {
PolicyEngine,
type SurfacePolicy,
} from '@anarchitecture/summon';
import { createEventStore, type DevtoolsEvent } from '@anarchitecture/summon/devtools';
import bootstrapSource from '@anarchitecture/summon/bootstrap.js?raw';
Expand Down Expand Up @@ -531,6 +532,7 @@ function readActiveContract(): ActiveContract {
mode: readMode(),
capabilityNames: scenario.capabilityNames,
componentNames: scenario.componentNames,
...(customContractEnabledEl.checked ? {} : { surfacePolicy: scenario.surfacePolicy }),
surfacePlan,
scriptPolicy: deriveSurfacePlanControls(surfacePlan).scriptPolicy,
...(layoutSel.value ? { layoutId: layoutSel.value } : {}),
Expand Down Expand Up @@ -823,6 +825,7 @@ function respawn(
directionId: string | null,
mode: Mode,
active: ActiveContract = readActiveContract(),
initialHtml = '',
): SandboxHandle {
if (componentIslands) {
componentIslands.destroy();
Expand Down Expand Up @@ -895,7 +898,7 @@ function respawn(
intents: policy?.intents ?? [],
capabilities: grantedCapabilities,
components: grantedComponents,
html: '',
html: initialHtml,
initialState: policy?.getState(),
},
// Read from the host-owned engine, not from the (LLM-influenced) artifact,
Expand Down Expand Up @@ -1093,7 +1096,7 @@ function applyLineTo(target: SandboxTarget, line: ProtocolLine, context: Surface
targetPath?: unknown;
layers?: unknown;
baseDirectionId?: unknown;
styleSource?: unknown;
styleSource?: unknown;
}
| undefined;
const product = typeof value?.product === 'string' ? value.product : 'Ghost';
Expand Down Expand Up @@ -1221,6 +1224,7 @@ interface StreamOptions {
directionId: string | null;
layout?: SummonLayout | null;
scriptPolicy?: ScriptPolicy;
surfacePolicy?: SurfacePolicy;
surfacePlan?: SurfacePlan;
tokenOverrides?: Record<string, string>;
edit?: {
Expand Down Expand Up @@ -1271,6 +1275,7 @@ async function streamGenerationInto(target: SandboxTarget, opts: StreamOptions):
...(target.components ? { components: target.components } : {}),
surfaceCeiling: demoSurfaceCeiling,
...(opts.scriptPolicy ? { scriptPolicy: opts.scriptPolicy } : {}),
...(opts.surfacePolicy ? { surfacePolicy: opts.surfacePolicy } : {}),
...(opts.surfacePlan ? { surfacePlan: opts.surfacePlan } : {}),
...(opts.tokenOverrides ? { tokenOverrides: opts.tokenOverrides } : {}),
...(opts.layout ? { layout: opts.layout } : {}),
Expand Down Expand Up @@ -1380,6 +1385,13 @@ async function streamGenerationInto(target: SandboxTarget, opts: StreamOptions):
};
}

function surfaceRequestFor(active: ActiveContract): Pick<StreamOptions, 'surfacePolicy' | 'surfacePlan'> {
if (!customContractEnabledEl.checked && active.surfacePolicy) {
return { surfacePolicy: active.surfacePolicy };
}
return { surfacePlan: active.surfacePlan };
}

function createParentTarget(active: ActiveContract): SandboxTarget {
return {
getHandle: () => handle,
Expand All @@ -1403,15 +1415,13 @@ function createParentTarget(active: ActiveContract): SandboxTarget {
onTokenOverrides: (applied) => {
activeTokensSourceOverride = applyTokenOverrideCss(tokensFor(currentDirectionId), applied);
const composed = acc.hasAnySection() ? acc.compose() : null;
respawn(currentDirectionId, currentMode, active);
if (composed) handle?.render(composed);
respawn(currentDirectionId, currentMode, active, composed ?? '');
renderContractSummary();
},
onGhostTokenSource: (css) => {
activeTokensSourceOverride = css;
const composed = acc.hasAnySection() ? acc.compose() : null;
respawn(currentDirectionId, currentMode, active);
if (composed) handle?.render(composed);
respawn(currentDirectionId, currentMode, active, composed ?? '');
},
onValidationSummary: (value) => {
currentValidationSummary = summarizeValidationMeta(value);
Expand Down Expand Up @@ -1602,7 +1612,7 @@ async function generate(prompt: string) {
directionId: currentDirectionId,
layout: readLayout(),
scriptPolicy: active.scriptPolicy,
surfacePlan: active.surfacePlan,
...surfaceRequestFor(active),
tokenOverrides: active.tokenOverrides,
repair: active.repair,
signal: abortController.signal,
Expand Down Expand Up @@ -1661,7 +1671,7 @@ async function editArtifact(instruction: string) {
directionId: currentDirectionId,
layout: readLayout(),
scriptPolicy: active.scriptPolicy,
surfacePlan: active.surfacePlan,
...surfaceRequestFor(active),
tokenOverrides: active.tokenOverrides,
repair: active.repair,
edit,
Expand Down Expand Up @@ -1764,14 +1774,14 @@ function summonChild(childPrompt: string, title?: string) {
events,
});

const spawnChildSandbox = () => {
const spawnChildSandbox = (initialHtml = '') => {
childHandle?.dispose();
childHandle = spawnSandbox({
iframe: childIframe,
artifact: {
intents: childPolicy.intents,
capabilities: childGrantedCapabilities,
html: '',
html: initialHtml,
initialState: childPolicy.getState(),
},
grantedIntents: childPolicy.intents,
Expand Down Expand Up @@ -1805,8 +1815,7 @@ function summonChild(childPrompt: string, title?: string) {
onGhostTokenSource: (css) => {
childTokensSourceOverride = css;
const composed = childAcc.hasAnySection() ? childAcc.compose() : null;
spawnChildSandbox();
if (composed) childHandle?.render(composed);
spawnChildSandbox(composed ?? '');
},
};

Expand Down
60 changes: 60 additions & 0 deletions apps/demo/src/showcase.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import {
compileSurfacePolicy,
normalizeSurfacePolicy,
} from '@anarchitecture/summon';
import { deriveSurfacePlanControls } from '@anarchitecture/summon/engine';
import { GALLERY_PRESETS } from '../../../examples/surface-gallery/src/presets.js';
import { baseDemoComponentPack } from './components.js';
import {
createScopedDemoRegistry,
narrowCapabilityPack,
Expand Down Expand Up @@ -50,10 +56,41 @@ test('narrowCapabilityPack keeps only allowed intents and non-leaking patterns',
});

test('showcase scenarios declare contract-complete surfaces', () => {
const capabilities = createScopedDemoRegistry(
{ onSummon: () => {} },
allDemoCapabilityNames,
).toContract().pack;
const components = baseDemoComponentPack();

for (const scenario of SHOWCASE_SCENARIOS) {
assert.ok(scenario.id);
assert.ok(scenario.label);
assert.ok(scenario.prompt);
assert.deepEqual(normalizeSurfacePolicy(scenario.surfacePolicy), {
tier: scenario.surfacePolicy.tier,
purpose: scenario.surfacePolicy.purpose ?? 'inform',
grants: scenario.surfacePolicy.grants ?? [],
components: scenario.surfacePolicy.components ?? [],
persistence: scenario.surfacePolicy.persistence ?? 'replayable',
});

const compiled = compileSurfacePolicy(scenario.surfacePolicy, {
capabilities,
components,
});
assert.deepEqual(compiled.issues, [], `${scenario.id} has invalid SurfacePolicy`);
assert.deepEqual(compiled.surfacePlan, scenario.surfacePlan, `${scenario.id} policy does not compile to its SurfacePlan`);
assert.deepEqual(
compiled.capabilities?.intents.map((intent) => intent.name) ?? [],
scenario.capabilityNames,
`${scenario.id} grants do not match capability names`,
);
assert.deepEqual(
compiled.components?.components.map((component) => component.name) ?? [],
scenario.componentNames ?? [],
`${scenario.id} trusted components do not match component names`,
);

assert.ok(scenario.surfacePlan.purpose);
assert.ok(scenario.surfacePlan.runtime);
assert.ok(scenario.surfacePlan.data);
Expand All @@ -65,6 +102,29 @@ test('showcase scenarios declare contract-complete surfaces', () => {
}
});

test('generate showcase mirrors surface gallery presets for shared sandbox paths', () => {
const galleryById = new Map(GALLERY_PRESETS.map((preset) => [preset.id, preset]));
const sharedPresetIds = [
'static-summary',
'host-resource-search',
'decision-picker',
'approval-publish',
'component-islands',
'worker-analysis',
];

for (const id of sharedPresetIds) {
const gallery = galleryById.get(id);
const scenario = SHOWCASE_SCENARIOS.find((item) => item.id === id);
assert.ok(gallery, `missing gallery preset ${id}`);
assert.ok(scenario, `missing generate scenario ${id}`);
assert.equal(scenario.label, gallery.title, `${id} label drift`);
assert.deepEqual(scenario.surfacePolicy, gallery.surfacePolicy, `${id} SurfacePolicy drift`);
assert.deepEqual(scenario.capabilityNames, gallery.surfacePolicy.grants ?? [], `${id} grant drift`);
assert.deepEqual(scenario.componentNames ?? [], gallery.surfacePolicy.components ?? [], `${id} component drift`);
}
});

test('showcase scenarios reference known demo capabilities', () => {
const known = new Set(allDemoCapabilityNames);
for (const scenario of SHOWCASE_SCENARIOS) {
Expand Down
Loading
Loading