appostle-installer 0.0.19 → 0.0.21

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.
@@ -1,48 +1,37 @@
1
1
  ---
2
- description: Named animation patterns (distinct from motion timing tokens)
3
- variables:
4
- - key: animation.entry
5
- type: text
6
- label: Entry Pattern
7
- value: ""
8
- section: visual
9
- - key: animation.entry.distance
10
- type: number
11
- label: Entry TranslateY (px)
12
- value: ""
13
- section: visual
14
- - key: animation.entry.stagger
15
- type: number
16
- label: Sibling Stagger (ms)
17
- value: ""
18
- section: visual
19
- - key: animation.hover
20
- type: text
21
- label: Hover Pattern
22
- value: ""
23
- section: visual
24
- - key: animation.scroll
25
- type: text
26
- label: Scroll Pattern
27
- value: ""
28
- section: visual
29
- - key: animation.status
30
- type: text
31
- label: Status Pattern
32
- value: ""
33
- section: visual
34
- - key: animation.signature
35
- type: text
36
- label: Signature Pattern
37
- value: ""
38
- section: visual
39
- - key: animation.principles
40
- type: text
41
- label: Principles
42
- value: ""
43
- section: visual
2
+ description: Brand animation language. Each `## <name>` block is one animation with embedded HTML + CSS preview. Variable count per brand.
3
+ variables: []
44
4
  ---
45
5
 
46
6
  # Animations
47
7
 
48
- [1–2 sentence description of the animation system and what it communicates.]
8
+ [1–2 sentence description of the motion language this brand uses: when motion matters, what it communicates, what stays still.]
9
+
10
+ ## <animation-name>
11
+
12
+ Role: <signature|entry|hover|scroll|status|loop|other> (optional role hint — used by the brand-guide to mark emphasis)
13
+
14
+ [Optional 1–2 sentence description of when and where this animation is used.]
15
+
16
+ ```html
17
+ <!-- The demo HTML for the brand-guide preview. Use a unique-enough class name. -->
18
+ <div class="my-anim-demo">Hello</div>
19
+ ```
20
+
21
+ ```css
22
+ /* The CSS that drives the demo. Trust your class names — they live inside an
23
+ isolated card. Reference the brand's CSS vars (--font-hero, --accent-base,
24
+ --ease-default, etc.) so the demo feels branded. Keyframe names must be
25
+ unique within this brand's animations.md. */
26
+ .my-anim-demo {
27
+ animation: my-anim 800ms var(--ease-default, ease-out) forwards;
28
+ }
29
+ @keyframes my-anim {
30
+ from { opacity: 0; transform: translateY(12px); }
31
+ to { opacity: 1; transform: translateY(0); }
32
+ }
33
+ ```
34
+
35
+ ## <next-animation-name>
36
+
37
+ …declare as many `## <name>` sections as the brand actually uses. A brand with one distinctive animation declares one. A brand with twelve declares twelve.
@@ -21,6 +21,11 @@ variables:
21
21
  label: Glow / Focus
22
22
  value: ""
23
23
  section: visual
24
+ - key: shadow.glass.enabled
25
+ type: text
26
+ label: Glass Overlay Enabled
27
+ value: ""
28
+ section: visual
24
29
  - key: shadow.principles
25
30
  type: text
26
31
  label: Principles
@@ -24,12 +24,12 @@ variables:
24
24
  value: medium
25
25
  options: [sparse, medium, a-lot, loads]
26
26
  section: visual
27
- - key: brand.shape.shape-1.min-size
27
+ - key: brand.shape.shape-1.size-range.min
28
28
  type: text
29
29
  label: Min Size — % of Page Height
30
30
  value: "10"
31
31
  section: visual
32
- - key: brand.shape.shape-1.max-size
32
+ - key: brand.shape.shape-1.size-range.max
33
33
  type: text
34
34
  label: Max Size — % of Page Height
35
35
  value: "60"
@@ -62,12 +62,12 @@ variables:
62
62
  value: medium
63
63
  options: [sparse, medium, a-lot, loads]
64
64
  section: visual
65
- - key: brand.shape.shape-2.min-size
65
+ - key: brand.shape.shape-2.size-range.min
66
66
  type: text
67
67
  label: Min Size — % of Page Height
68
68
  value: "10"
69
69
  section: visual
70
- - key: brand.shape.shape-2.max-size
70
+ - key: brand.shape.shape-2.size-range.max
71
71
  type: text
72
72
  label: Max Size — % of Page Height
73
73
  value: "60"
@@ -100,12 +100,12 @@ variables:
100
100
  value: medium
101
101
  options: [sparse, medium, a-lot, loads]
102
102
  section: visual
103
- - key: brand.shape.shape-3.min-size
103
+ - key: brand.shape.shape-3.size-range.min
104
104
  type: text
105
105
  label: Min Size — % of Page Height
106
106
  value: "10"
107
107
  section: visual
108
- - key: brand.shape.shape-3.max-size
108
+ - key: brand.shape.shape-3.size-range.max
109
109
  type: text
110
110
  label: Max Size — % of Page Height
111
111
  value: "60"
@@ -138,12 +138,12 @@ variables:
138
138
  value: medium
139
139
  options: [sparse, medium, a-lot, loads]
140
140
  section: visual
141
- - key: brand.shape.shape-4.min-size
141
+ - key: brand.shape.shape-4.size-range.min
142
142
  type: text
143
143
  label: Min Size — % of Page Height
144
144
  value: "10"
145
145
  section: visual
146
- - key: brand.shape.shape-4.max-size
146
+ - key: brand.shape.shape-4.size-range.max
147
147
  type: text
148
148
  label: Max Size — % of Page Height
149
149
  value: "60"
package/dist/worker.js CHANGED
@@ -2578,6 +2578,10 @@ function toAgentPayload(agent, options) {
2578
2578
  title: options?.title ?? null,
2579
2579
  labels: agent.labels,
2580
2580
  internal: agent.internal,
2581
+ // Forward archivedAt on the live broadcast so paired clients (e.g. mobile)
2582
+ // see the archive land — without this, only the device that ran the
2583
+ // archive mutation knows about it.
2584
+ archivedAt: agent.archivedAt ?? null,
2581
2585
  // Surface ownership so the client can render an owner badge / detect
2582
2586
  // "shared with me" agents. `sharedWithUserIds` deliberately stays off
2583
2587
  // the snapshot — only owners read the full ACL, via the dedicated
@@ -4235,6 +4239,7 @@ var GoogleFontsDownloadResponseSchema = z10.object({
4235
4239
  });
4236
4240
 
4237
4241
  // ../server/src/shared/messages.ts
4242
+ var InitialAgentSnapshotPageLimit = 200;
4238
4243
  var MutableDaemonConfigSchema = z11.object({
4239
4244
  mcp: z11.object({
4240
4245
  injectIntoAgents: z11.boolean()
@@ -6727,6 +6732,17 @@ var FetchAgentsResponseMessageSchema = z11.object({
6727
6732
  })
6728
6733
  })
6729
6734
  });
6735
+ var InitialAgentSnapshotMessageSchema = z11.object({
6736
+ type: z11.literal("initial_agent_snapshot"),
6737
+ payload: z11.object({
6738
+ entries: z11.array(
6739
+ z11.object({
6740
+ agent: AgentSnapshotPayloadSchema,
6741
+ project: ProjectPlacementPayloadSchema
6742
+ })
6743
+ )
6744
+ })
6745
+ });
6730
6746
  var FetchWorkspacesResponseMessageSchema = z11.object({
6731
6747
  type: z11.literal("fetch_workspaces_response"),
6732
6748
  payload: z11.object({
@@ -7768,6 +7784,7 @@ var SessionOutboundMessageSchema = z11.discriminatedUnion("type", [
7768
7784
  AgentStreamMessageSchema,
7769
7785
  AgentStatusMessageSchema,
7770
7786
  FetchAgentsResponseMessageSchema,
7787
+ InitialAgentSnapshotMessageSchema,
7771
7788
  FetchWorkspacesResponseMessageSchema,
7772
7789
  OpenProjectResponseMessageSchema,
7773
7790
  StartWorkspaceScriptResponseMessageSchema,
@@ -20929,11 +20946,53 @@ var ClaudeAgentSession = class {
20929
20946
  // sub-agents stop announcing them as suspected prompt injections. See
20930
20947
  // getSystemReminderGuidance for the full rationale.
20931
20948
  getSystemReminderGuidance(),
20932
- "Default response shape: open with a short, scannable plain-English read. 2\u20134 short sentences, one idea each, no comma-chained clauses or em-dash pile-ups. When the read covers 3+ discrete points, use bullets instead of prose. Then the technical detail in dense form (paths, line refs, code) without narration. Don't explain what well-named code already explains. Skip the shape for trivial questions.",
20933
- "For multi-step work with independent chunks, spawn Task subagents instead of doing every tool call yourself. Run them in parallel when chunks don't depend on each other. Keeps your main context lean.",
20949
+ //
20950
+ //
20951
+ //
20952
+ //
20953
+ // ╔══════════════════════════════════════════════════════════════════╗
20954
+ // ║ CUSTOM INSTRUCTIONS — add new system prompt lines below here ║
20955
+ // ║ Each line is a quoted string: "your instruction here", ║
20956
+ // ╚══════════════════════════════════════════════════════════════════╝
20957
+ //
20958
+ //
20959
+ //
20960
+ //
20961
+ "Default response shape: open with a short, scannable plain-English read. 2\u20134 short sentences, one idea each, no comma-chained clauses or em-dash pile-ups. When the read covers 3+ discrete points, use bullets instead of prose. Then the technical detail in dense form (paths, line refs, code) without narration. Don\u2019t explain what well-named code already explains. Skip the shape for trivial questions.",
20962
+ //
20934
20963
  "When the user sends `>learn`, re-explain your previous message in plain English only \u2014 1 short paragraph that helps them build the mental model. No code, no file references, no technical repeat.",
20964
+ //
20965
+ //
20966
+ //
20967
+ //
20968
+ // ╔══════════════════════════════════════════════════════════════════╗
20969
+ // ║ END CUSTOM INSTRUCTIONS — don't edit below this line ║
20970
+ // ╚══════════════════════════════════════════════════════════════════╝
20971
+ //
20972
+ //
20973
+ //
20974
+ //
20935
20975
  this.config.systemPrompt?.trim()
20936
20976
  ].filter((entry) => typeof entry === "string" && entry.length > 0).join("\n\n");
20977
+ const appostleAgents = {
20978
+ researcher: {
20979
+ description: "Use this agent when you need to explore unfamiliar code, trace data flow across modules, search broadly across the codebase, or read more than 2 files you haven't seen yet. Delegate investigation work here to keep the main conversation context lean.",
20980
+ prompt: "You are a codebase researcher. Your job is to investigate, trace, and report findings \u2014 never edit files. Read code, grep for patterns, follow imports, and build a clear picture. Report back a concise summary of what you found: key files, relevant code paths, and your conclusion. Keep your report under 300 words unless the investigation is complex.",
20981
+ model: "sonnet"
20982
+ },
20983
+ refactorer: {
20984
+ description: "Use this agent for refactors that touch more than 2 files \u2014 renames, migrations, pattern replacements, moving code between modules. Delegate multi-file changes here to isolate the blast radius.",
20985
+ prompt: "You are a refactoring specialist. Make the requested changes across all affected files. Be thorough \u2014 update imports, references, types, and tests. Run typecheck after changes if available. Report what you changed and any issues found."
20986
+ },
20987
+ reviewer: {
20988
+ description: "Use this agent to review code for bugs, security issues, performance problems, or style violations. Use it before committing large changes or when the user asks for a review.",
20989
+ prompt: "You are a code reviewer. Analyze the code for correctness, security vulnerabilities (OWASP top 10), performance issues, and adherence to the project's coding standards. Be specific \u2014 cite file paths and line numbers. Flag severity: critical, warning, or nitpick. Keep the review focused and actionable."
20990
+ },
20991
+ debugger: {
20992
+ description: "Use this agent to investigate bugs \u2014 read logs, trace error paths, check database state, reproduce issues. Delegate debugging here when the root cause isn't obvious from a quick look.",
20993
+ prompt: "You are a debugger. Your job is to find the root cause, not patch symptoms. Check real data first \u2014 logs, network requests, database state. Trace the full lifecycle of the bug. Present your findings: root cause, evidence, and a proposed minimal fix. Do not edit files unless explicitly asked."
20994
+ }
20995
+ };
20937
20996
  const claudeBinary = await findExecutable("claude");
20938
20997
  this.logger.debug(
20939
20998
  {
@@ -20951,7 +21010,7 @@ var ClaudeAgentSession = class {
20951
21010
  // bypass launch capability available so later setPermissionMode("bypassPermissions")
20952
21011
  // calls do not fail after a model/thinking/rewind-driven restart.
20953
21012
  allowDangerouslySkipPermissions: true,
20954
- agents: this.defaults?.agents,
21013
+ agents: { ...appostleAgents, ...this.defaults?.agents },
20955
21014
  canUseTool: this.handlePermissionRequest,
20956
21015
  ...claudeBinary ? { pathToClaudeCodeExecutable: claudeBinary } : {},
20957
21016
  // Use Claude Code preset system prompt and load CLAUDE.md files
@@ -33556,7 +33615,7 @@ function serializeFrontmatter(input) {
33556
33615
 
33557
33616
  // ../server/src/server/agent/handoff-mcp.ts
33558
33617
  function buildHandoffMcpServer(callerAgentId, options) {
33559
- const { agentManager, appostleHome, logger } = options;
33618
+ const { agentManager, appostleHome, logger, agentStorage } = options;
33560
33619
  const log = logger.child({ module: "handoff-mcp", callerAgentId });
33561
33620
  const handoffTool = tool2(
33562
33621
  "handoff",
@@ -33635,6 +33694,22 @@ function buildHandoffMcpServer(callerAgentId, options) {
33635
33694
  } catch (err) {
33636
33695
  log.error({ err, agentId: snapshot.id }, "handoff: failed to start run");
33637
33696
  }
33697
+ if (agentStorage) {
33698
+ try {
33699
+ setupFinishNotification({
33700
+ agentManager,
33701
+ agentStorage,
33702
+ childAgentId: snapshot.id,
33703
+ callerAgentId,
33704
+ logger: log
33705
+ });
33706
+ } catch (err) {
33707
+ log.error(
33708
+ { err, agentId: snapshot.id },
33709
+ "handoff: failed to wire auto-return notification"
33710
+ );
33711
+ }
33712
+ }
33638
33713
  return {
33639
33714
  content: [
33640
33715
  {
@@ -33645,11 +33720,97 @@ function buildHandoffMcpServer(callerAgentId, options) {
33645
33720
  };
33646
33721
  }
33647
33722
  );
33723
+ const reportTool = tool2(
33724
+ "report",
33725
+ [
33726
+ "Send a curated message from this handoff child session back to its parent.",
33727
+ "Use this when the user types `>report` \u2014 they want to deliver a specific update",
33728
+ "to the parent right now, in their own words.",
33729
+ "",
33730
+ "Independent of the automatic on-finish notification: the parent still receives",
33731
+ "the auto-return when this child eventually finishes. You can call this tool any",
33732
+ "number of times over the lifetime of the handoff \u2014 each call is a separate",
33733
+ "update.",
33734
+ "",
33735
+ "Only works in sessions that were spawned via `handoff`. Errors otherwise."
33736
+ ].join("\n"),
33737
+ {
33738
+ message: z33.string().min(1).describe(
33739
+ "The curated report to deliver to the parent session. Self-contained: the parent does not see this conversation's timeline."
33740
+ )
33741
+ },
33742
+ async (args) => {
33743
+ const childAgent = agentManager.getAgent(callerAgentId);
33744
+ if (!childAgent) {
33745
+ return {
33746
+ isError: true,
33747
+ content: [
33748
+ { type: "text", text: `Caller agent ${callerAgentId} not found in agent manager` }
33749
+ ]
33750
+ };
33751
+ }
33752
+ const parentAgentId = childAgent.labels?.["appostle.parent-agent-id"];
33753
+ if (!parentAgentId) {
33754
+ return {
33755
+ isError: true,
33756
+ content: [
33757
+ {
33758
+ type: "text",
33759
+ text: "This session was not spawned via `handoff`, so there is no parent to report to."
33760
+ }
33761
+ ]
33762
+ };
33763
+ }
33764
+ const parentAgent = agentManager.getAgent(parentAgentId);
33765
+ if (!parentAgent) {
33766
+ return {
33767
+ isError: true,
33768
+ content: [
33769
+ {
33770
+ type: "text",
33771
+ text: `Parent agent ${parentAgentId} is no longer available.`
33772
+ }
33773
+ ]
33774
+ };
33775
+ }
33776
+ const childTitle = childAgent.config?.title ?? callerAgentId;
33777
+ const trimmedMessage = args.message.trim();
33778
+ const prompt = `<appostle-system>
33779
+ Agent ${callerAgentId} (${childTitle}) reported back:
33780
+
33781
+ ${trimmedMessage}
33782
+ </appostle-system>`;
33783
+ try {
33784
+ startAgentRun(agentManager, parentAgentId, prompt, log, {
33785
+ replaceRunning: true
33786
+ });
33787
+ } catch (err) {
33788
+ log.error({ err, parentAgentId }, "report: failed to inject prompt into parent");
33789
+ return {
33790
+ isError: true,
33791
+ content: [
33792
+ {
33793
+ type: "text",
33794
+ text: `report failed: could not wake parent \u2014 ${err instanceof Error ? err.message : String(err)}`
33795
+ }
33796
+ ]
33797
+ };
33798
+ }
33799
+ return {
33800
+ content: [
33801
+ {
33802
+ type: "text",
33803
+ text: `Reported to parent ${parentAgentId}.`
33804
+ }
33805
+ ]
33806
+ };
33807
+ }
33808
+ );
33648
33809
  const writePlanTool = buildWritePlanTool(callerAgentId, { agentManager, logger });
33649
33810
  const sdkInstance = createSdkMcpServer({
33650
33811
  name: "appostle",
33651
33812
  version: "0.1.0",
33652
- tools: [handoffTool, writePlanTool],
33813
+ tools: [handoffTool, reportTool, writePlanTool],
33653
33814
  alwaysLoad: true
33654
33815
  });
33655
33816
  return {
@@ -34210,7 +34371,8 @@ var AgentManager = class {
34210
34371
  mcpServers.appostle = buildHandoffMcpServer(agentId, {
34211
34372
  agentManager: this,
34212
34373
  appostleHome: this.appostleHome,
34213
- logger: this.logger
34374
+ logger: this.logger,
34375
+ agentStorage: this.registry
34214
34376
  });
34215
34377
  }
34216
34378
  return { ...config, mcpServers };
@@ -34809,6 +34971,7 @@ var AgentManager = class {
34809
34971
  attentionReason: null,
34810
34972
  attentionTimestamp: null
34811
34973
  });
34974
+ agent.archivedAt = archivedAt;
34812
34975
  this.notifyAgentState(agentId);
34813
34976
  await this.closeAgent(agentId);
34814
34977
  return { archivedAt };
@@ -40565,71 +40728,28 @@ function buildStructuralContext(allBrands) {
40565
40728
  return lines.length > 0 ? lines.join("\n") : "(No structural context established yet.)";
40566
40729
  }
40567
40730
  var TARGET_QUESTIONS = 7;
40568
- var EMBEDDED_QA_FALLBACK = `## Compositional Philosophy
40569
- - What's the single-sentence design philosophy that should govern every layout decision?
40570
- - What's the ratio of whitespace to content density you're after?
40571
-
40572
- ## Hero Behavior
40573
- - How should the hero section behave? Full viewport takeover, contained module, asymmetric split?
40574
- - Should the hero have scroll-triggered behavior or stay static?
40575
-
40576
- ## Section Variation & Flow
40577
- - How many distinct section types should the page cycle through?
40578
- - Should any sections break out of the main container (full-bleed moments)?
40579
-
40580
- ## Density & Spacing
40581
- - How tight should content be packed within sections?
40582
- - How much vertical breathing room between major sections?
40583
-
40584
- ## Grid System
40585
- - What grid philosophy \u2014 strict 12-column, asymmetric, modular, or freeform?
40586
-
40587
- ## Containers & Cards
40588
- - What's your card philosophy \u2014 flat, elevated, outlined, or glassmorphic?
40589
-
40590
- ## Image Treatment
40591
- - What role do images play \u2014 hero-level, supporting, or minimal?
40592
-
40593
- ## CTA Strategy
40594
- - How many CTAs per page and what's the hierarchy?
40595
-
40596
- ## Mobile Behavior
40597
- - How should the desktop layout transform on mobile?
40598
-
40599
- ## Prohibitions & Bans
40600
- - What design patterns are absolutely forbidden?
40601
- - Any CSS properties or techniques that are banned?`;
40602
- async function loadQaQuestions(logger) {
40603
- const filePath = await findFileUpward(QA_FILENAME);
40604
- if (filePath) {
40605
- try {
40606
- const content = await fs15.readFile(filePath, "utf8");
40607
- logger.debug({ filePath }, "layout-generator: loaded Q&A questions from disk");
40608
- return content;
40609
- } catch (err) {
40610
- logger.warn(
40611
- { err, filePath },
40612
- "layout-generator: failed to read Q&A file; using embedded fallback"
40613
- );
40614
- }
40731
+ async function loadSpecFile(filename, logger) {
40732
+ const filePath = await findFileUpward(filename);
40733
+ if (!filePath) {
40734
+ throw new Error(
40735
+ `layout-generator: ${filename} not found by walking up from ${fileURLToPath3(import.meta.url)}. This file is the canonical spec and must exist at the appostle repo root.`
40736
+ );
40615
40737
  }
40616
- return EMBEDDED_QA_FALLBACK;
40738
+ try {
40739
+ const content = await fs15.readFile(filePath, "utf8");
40740
+ logger.debug({ filePath }, `layout-generator: loaded ${filename} from disk`);
40741
+ return content;
40742
+ } catch (err) {
40743
+ throw new Error(
40744
+ `layout-generator: failed to read ${filename} at ${filePath}: ${err.message}`
40745
+ );
40746
+ }
40747
+ }
40748
+ async function loadQaQuestions(logger) {
40749
+ return loadSpecFile(QA_FILENAME, logger);
40617
40750
  }
40618
40751
  async function loadLayoutPrompt(logger) {
40619
- const filePath = await findFileUpward(PROMPT_FILENAME);
40620
- if (filePath) {
40621
- try {
40622
- const content = await fs15.readFile(filePath, "utf8");
40623
- logger.debug({ filePath }, "layout-generator: loaded layout prompt from disk");
40624
- return content;
40625
- } catch (err) {
40626
- logger.warn(
40627
- { err, filePath },
40628
- "layout-generator: failed to read layout-prompt file; using embedded fallback"
40629
- );
40630
- }
40631
- }
40632
- return EMBEDDED_LAYOUT_PROMPT_FALLBACK;
40752
+ return loadSpecFile(PROMPT_FILENAME, logger);
40633
40753
  }
40634
40754
  function interpolateTemplate(template, vars) {
40635
40755
  let out = template;
@@ -40638,66 +40758,6 @@ function interpolateTemplate(template, vars) {
40638
40758
  }
40639
40759
  return out;
40640
40760
  }
40641
- var EMBEDDED_LAYOUT_PROMPT_FALLBACK = `You are an elite art director refining the layout and composition rules for a brand. This is ONLY about layout \u2014 structure, zones, grid, density, rhythm, containers, image placement, CTAs. Typography, colors, motion, animations, shadows are handled by separate brand files \u2014 do not duplicate them here.
40642
-
40643
- These rules must work across media \u2014 web pages, print, PDF, presentations. Use proportional language (fractions, ratios, percentages) as the primary system. CSS values are welcome as concrete examples but should not be the only expression of a rule.
40644
-
40645
- Your job: update the design role document based on the user's refinement prompt. This document will be injected wholesale into an AI builder's context. If your rules are vague, the output will be generic. If your rules are specific and opinionated, the output will be distinctive.
40646
-
40647
- The document must be a complete markdown document covering:
40648
- - Enemy (a specific design this must NOT resemble, with 2-3 specific layout patterns that make it wrong \u2014 and for each, the exact counter-pattern this brand uses instead)
40649
- - Signature Anomaly (the one structural layout choice that makes this unmistakably distinctive)
40650
- - Compositional Philosophy (one structural law + the structural spine mechanism that enforces it across every zone)
40651
- - Intensity Dials (Density 1-10, Grid Variance 1-10 \u2014 with concrete implications for THIS brand)
40652
- - Opening Zone / Hero Behavior (surface coverage, bleed, content placement, containment)
40653
- - Zone Inventory (ordered list of all major zones: name, layout job, dense/airy, full-bleed/contained \u2014 this is the master reference for the rhythm)
40654
- - Zone Variation & Flow (how zones differ, transition strategy, full-bleed vs contained \u2014 references the Zone Inventory)
40655
- - Density Philosophy (spacing values; the oscillation rhythm derived from the Zone Inventory \u2014 name which zones are dense, which are airy, in sequence)
40656
- - Vertical Rhythm (three values: zone padding, element gap within zones, and the one zone that breaks the rhythm and why)
40657
- - Grid System (exact column structure, asymmetry, proportions)
40658
- - Content Width Strategy (max-width of the primary content area, which zones break it and why)
40659
- - Container & Card Rules (borders, corners, nesting, exact border-radius)
40660
- - Dividers & Graphic Structure (structural vs decorative, background strategy)
40661
- - Image Treatment \u2014 Layout Only (placement, grid relationship, overlap behavior)
40662
- - CTA Strategy (position in layout referencing Zone Inventory, container strategy, frequency)
40663
- - Responsive Behavior (how the layout adapts across sizes \u2014 web: breakpoints and stacking; print: scale and margin strategy)
40664
- - Bans (at least 20 specific layout prohibitions)
40665
-
40666
- ## Critical rules
40667
-
40668
- 1. This is a REFINEMENT. The user already has a role document (shown below). Their prompt refines, evolves, or redirects \u2014 it does NOT start from scratch unless they explicitly say so.
40669
-
40670
- 2. SPECIFICITY IS MANDATORY. Every rule must be actionable at the structural level. Generic adjectives ("clean", "minimal", "modern", "elegant") are BANNED from the output.
40671
-
40672
- 3. THE DOCUMENT MUST BE OPINIONATED, NOT HEDGED. Every sentence must prescribe or ban \u2014 never suggest. Use "Always", "Never", "Must", "Banned", "Required".
40673
-
40674
- 4. STAY IN YOUR LANE. Do NOT include rules about typography, colors, motion/animation, or shadows \u2014 those belong in their own brand files.
40675
-
40676
- 5. THE BANS ARE THE MOST IMPORTANT SECTION. At least 20 bans. The 7 universal AI-layout cliches MUST each appear as an explicit named ban: centered headline over full-width image; three equal-width feature cards in a row; alternating left-right image/text rows with identical padding; uniform zone height and padding; every block wrapped in a card with shadow + radius; CTA with gradient fill; full-width "Why Choose Us" icon grid. Plus 3-5 brand-specific AI tells.
40677
-
40678
- 6. THE ENEMY IS MANDATORY. Name a specific real design and 2-3 exact layout patterns from it. Vague enemies fail.
40679
-
40680
- 7. THE SIGNATURE ANOMALY IS MANDATORY. One specific structural choice. Apply the removal test: if removing this single decision would make the layout indistinguishable from a generic design \u2014 it qualifies. Must appear as a constraint in at least 2 other sections.
40681
-
40682
- 8. USE THE STRUCTURAL CONTEXT. The {{structuralContext}} contains sibling brand files. Do not duplicate their rules \u2014 derive layout implications from them.
40683
-
40684
- ## Current role document
40685
-
40686
- {{currentValues}}
40687
-
40688
- ## Structural context (sibling brand files)
40689
-
40690
- {{structuralContext}}
40691
-
40692
- ## User's refinement prompt
40693
-
40694
- {{userPrompt}}
40695
-
40696
- ## Response format
40697
-
40698
- Return ONLY a JSON object: { "roleDocument": "# Brand Layout Role\\n\\n## Enemy\\n..." }
40699
-
40700
- Output ONLY the JSON object. No markdown fences. No explanation.`;
40701
40761
  function buildQaSystemPrompt(questions, layoutPromptSpec) {
40702
40762
  return `You are a chill creative director doing a quick vibe check on someone's layout taste.
40703
40763
 
@@ -42619,9 +42679,35 @@ var Session = class _Session {
42619
42679
  this.emit(message);
42620
42680
  }
42621
42681
  /**
42622
- * Send initial state to client after connection
42682
+ * Push the first page of the agent directory unsolicited right after
42683
+ * `server_info` so the client can paint attention state before its own
42684
+ * paginated `fetch_agents_request` round-trip lands. The regular fetch
42685
+ * still runs in parallel — it establishes the live-update subscription
42686
+ * and paginates beyond the first page. This push exists purely to shave
42687
+ * one relay round-trip from time-to-first-paint.
42688
+ *
42689
+ * Errors are swallowed: a failed push degrades to "wait for the client
42690
+ * to ask," which is still correct.
42623
42691
  */
42624
42692
  async sendInitialState() {
42693
+ try {
42694
+ const synthetic = {
42695
+ type: "fetch_agents_request",
42696
+ requestId: "internal:initial-snapshot",
42697
+ filter: { includeArchived: true },
42698
+ page: { limit: InitialAgentSnapshotPageLimit }
42699
+ };
42700
+ const { entries } = await this.listFetchAgentsEntries(synthetic);
42701
+ this.emit({
42702
+ type: "initial_agent_snapshot",
42703
+ payload: { entries }
42704
+ });
42705
+ } catch (error) {
42706
+ this.sessionLogger.warn(
42707
+ { err: error },
42708
+ "Failed to push initial agent snapshot \u2014 client will fall back to fetch_agents_request"
42709
+ );
42710
+ }
42625
42711
  }
42626
42712
  /**
42627
42713
  * Normalize a user prompt (with optional image metadata) for AgentManager
@@ -53017,6 +53103,7 @@ var VoiceAssistantWebSocketServer = class {
53017
53103
  this.sessions.set(ws, connection);
53018
53104
  this.externalSessionsByKey.set(clientId, connection);
53019
53105
  this.sendToClient(ws, this.createServerInfoMessage());
53106
+ void connection.session.sendInitialState();
53020
53107
  connection.connectionLogger.trace(
53021
53108
  {
53022
53109
  clientId,