@guuey/config 0.16.11 → 0.16.14

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.
package/dist/agent.d.ts CHANGED
@@ -191,6 +191,42 @@ declare const RuntimeConfigSchema: z.ZodObject<{
191
191
  declare const SystemPromptSchema: z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
192
192
  file: z.ZodString;
193
193
  }, z.core.$strict>]>;
194
+ /**
195
+ * One mode of a multi-mode agent (guuey#527). `systemPromptAppend` extends
196
+ * the base prompt (inline string or a `{ file }` the loader inlines);
197
+ * `systemPrompt` replaces it wholesale — exactly ONE of the two (the
198
+ * refine on {@link ModesSchema} enforces the xor). `tools.allowlist`, when
199
+ * present, must be a subset of the base allowlist — the WRITE GATE enforces
200
+ * that (the schema can't see the base), so a mode only ever narrows.
201
+ * `audience` is accepted for forward-compat but the SERVER hardcodes the
202
+ * binding for the recognized pair in tonight's slice.
203
+ */
204
+ declare const ModeSchema: z.ZodObject<{
205
+ systemPromptAppend: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
206
+ file: z.ZodString;
207
+ }, z.core.$strict>]>>;
208
+ systemPrompt: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
209
+ file: z.ZodString;
210
+ }, z.core.$strict>]>>;
211
+ tools: z.ZodOptional<z.ZodObject<{
212
+ allowlist: z.ZodOptional<z.ZodArray<z.ZodString>>;
213
+ denylist: z.ZodOptional<z.ZodArray<z.ZodString>>;
214
+ }, z.core.$strict>>;
215
+ audience: z.ZodOptional<z.ZodArray<z.ZodEnum<{
216
+ guest: "guest";
217
+ authenticated: "authenticated";
218
+ byo: "byo";
219
+ }>>>;
220
+ }, z.core.$strict>;
221
+ /**
222
+ * Does the BASE tool allowlist permit `entry`? (guuey#527 subset rule.)
223
+ * A base pattern covers `entry` when it is `*`, an exact match, or a
224
+ * `prefix.*` wildcard whose prefix `entry` falls under. This is the SAME
225
+ * coverage semantics the pod's gate applies at call time, so "the mode's
226
+ * allowlist is a subset" means exactly "every mode tool the base already
227
+ * permits" — a mode can only ever NARROW, never widen.
228
+ */
229
+ export declare function baseAllowlistPermits(baseAllowlist: readonly string[] | undefined, entry: string): boolean;
194
230
  /**
195
231
  * Bedrock-style invocation endpoint config.
196
232
  *
@@ -328,6 +364,25 @@ export declare const AgentSectionV1: z.ZodObject<{
328
364
  allowlist: z.ZodOptional<z.ZodArray<z.ZodString>>;
329
365
  denylist: z.ZodOptional<z.ZodArray<z.ZodString>>;
330
366
  }, z.core.$strict>>;
367
+ modes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
368
+ systemPromptAppend: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
369
+ file: z.ZodString;
370
+ }, z.core.$strict>]>>;
371
+ systemPrompt: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
372
+ file: z.ZodString;
373
+ }, z.core.$strict>]>>;
374
+ tools: z.ZodOptional<z.ZodObject<{
375
+ allowlist: z.ZodOptional<z.ZodArray<z.ZodString>>;
376
+ denylist: z.ZodOptional<z.ZodArray<z.ZodString>>;
377
+ }, z.core.$strict>>;
378
+ audience: z.ZodOptional<z.ZodArray<z.ZodEnum<{
379
+ guest: "guest";
380
+ authenticated: "authenticated";
381
+ byo: "byo";
382
+ }>>>;
383
+ }, z.core.$strict>>>;
384
+ defaultMode: z.ZodOptional<z.ZodString>;
385
+ surfaceHints: z.ZodOptional<z.ZodBoolean>;
331
386
  runtime: z.ZodOptional<z.ZodObject<{
332
387
  maxTurns: z.ZodOptional<z.ZodNumber>;
333
388
  temperature: z.ZodOptional<z.ZodNumber>;
@@ -387,6 +442,45 @@ export type GuueyAgentRuntime = z.infer<typeof RuntimeConfigSchema>;
387
442
  export type GuueyAgentSystemPrompt = z.infer<typeof SystemPromptSchema>;
388
443
  /** Endpoint config type. */
389
444
  export type GuueyAgentEndpoint = z.infer<typeof EndpointConfigSchema>;
445
+ /** One declared mode's shape. */
446
+ export type GuueyAgentMode = z.infer<typeof ModeSchema>;
447
+ /** The v1 mode axis — the caller's REAL auth state, server-derived. */
448
+ export type ModeAudienceClass = 'guest' | 'auth';
449
+ /** The result of {@link applyAgentMode} — the effective snapshot + provenance. */
450
+ export interface AppliedAgentMode {
451
+ /** The snapshot to SERVE this invoke (a new object when a mode applied; the same reference otherwise). */
452
+ agent: GuueyAgent;
453
+ /** The mode key actually applied, or `null` when serving the bare base. */
454
+ applied: string | null;
455
+ /** Present when the resolution took a non-obvious branch (all fail-soft). */
456
+ fallback?: 'unknown-mode' | 'pin-clamped' | 'auth-undeclared';
457
+ }
458
+ /**
459
+ * Resolve + apply one invoke's agent mode (guuey#527/#566 — the guest/auth
460
+ * axis, founder-refined 2026-08-31: "letting them use guest mode vs auth
461
+ * mode is the correct definition").
462
+ *
463
+ * SELECTION is SERVER-DERIVED: `audience` is the caller's REAL auth state
464
+ * (the pod maps anonymous → 'guest', authenticated → 'auth') — never a
465
+ * client claim. The `pin` (the widget/SDK `mode` param) may only choose
466
+ * WITHIN permission: an authed caller may pin 'guest' (preview-as-visitor);
467
+ * a guest pinning 'auth' is CLAMPED to guest ('pin-clamped'), and an
468
+ * unrecognized pin key is ignored ('unknown-mode') — fail-soft always, an
469
+ * embed never breaks.
470
+ *
471
+ * Fallback chain: the selected key's def; an undeclared 'auth' falls to
472
+ * 'guest''s def ('auth-undeclared' — an app that only configures the
473
+ * visitor rep serves everyone that rep, which IS hire-a-rep); still
474
+ * nothing → base. `defaultMode` is deprecated and not consulted.
475
+ *
476
+ * Application (unchanged from the first cut, test-pinned): `systemPrompt`
477
+ * REPLACES the base; `systemPromptAppend` = base + "\n\n" + append; an
478
+ * EMPTY def serves the base by SAME REFERENCE (callers detect no-override
479
+ * by identity); `tools` replaces with the write-gate-proven subset; a
480
+ * `{ file }` prompt is unusable at serve time → base. Pure — never
481
+ * mutates inputs.
482
+ */
483
+ export declare function applyAgentMode(agent: GuueyAgent, pin: string | undefined, audience: ModeAudienceClass): AppliedAgentMode;
390
484
  /** Deploy config type. */
391
485
  export type GuueyAgentDeploy = z.infer<typeof DeploySchema>;
392
486
  /**
@@ -427,6 +521,14 @@ export declare const RESERVED_MEMORY_SERVER_NAME = "guuey-memory";
427
521
  * platform entry at invoke time.
428
522
  */
429
523
  export declare const RESERVED_PROFILE_SERVER_NAME = "guuey-profile";
524
+ /**
525
+ * The reserved colocated key the auto-injected human-handoff MCP is booted +
526
+ * spliced under (guuey#552, spec 2026-08-31-human-handoff-v1-design.md).
527
+ * NEVER builder-declarable — mirrors {@link RESERVED_MEMORY_SERVER_NAME}'s
528
+ * reservation for the same reason; the handoff child's own server advertises
529
+ * this name and its aud is `colocatedResourceUrl(appId, this)`.
530
+ */
531
+ export declare const RESERVED_HANDOFF_SERVER_NAME = "guuey-handoff";
430
532
  /**
431
533
  * Every `mcpServers` map key the platform reserves. Extensible — one entry
432
534
  * today ({@link RESERVED_MEMORY_SERVER_NAME}); future platform-injected
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
package/dist/agent.js CHANGED
@@ -279,6 +279,58 @@ const SystemPromptSchema = z.union([
279
279
  z.string().min(1),
280
280
  z.strictObject({ file: z.string().min(1) }),
281
281
  ]);
282
+ /**
283
+ * One mode of a multi-mode agent (guuey#527). `systemPromptAppend` extends
284
+ * the base prompt (inline string or a `{ file }` the loader inlines);
285
+ * `systemPrompt` replaces it wholesale — exactly ONE of the two (the
286
+ * refine on {@link ModesSchema} enforces the xor). `tools.allowlist`, when
287
+ * present, must be a subset of the base allowlist — the WRITE GATE enforces
288
+ * that (the schema can't see the base), so a mode only ever narrows.
289
+ * `audience` is accepted for forward-compat but the SERVER hardcodes the
290
+ * binding for the recognized pair in tonight's slice.
291
+ */
292
+ const ModeSchema = z.strictObject({
293
+ systemPromptAppend: SystemPromptSchema.optional(),
294
+ systemPrompt: SystemPromptSchema.optional(),
295
+ tools: ToolGatesSchema.optional(),
296
+ audience: z.array(z.enum(['guest', 'authenticated', 'byo'])).optional(),
297
+ });
298
+ /**
299
+ * The `agent.modes` map (guuey#527). Mode keys are short machine
300
+ * identifiers (`rep`, `agent`, …) — the court-key grammar, reused. Each
301
+ * value is a {@link ModeSchema}; a mode declares AT MOST one of
302
+ * `systemPromptAppend` / `systemPrompt` (both = a contradiction the
303
+ * manifest must not carry).
304
+ */
305
+ const MODE_KEY_RE = /^[a-z][a-z0-9-]{0,31}$/;
306
+ const ModesSchema = z
307
+ .record(z.string().regex(MODE_KEY_RE), ModeSchema)
308
+ .refine((modes) => Object.values(modes).every((m) => !(m.systemPromptAppend !== undefined && m.systemPrompt !== undefined)), { message: 'a mode declares at most one of systemPromptAppend / systemPrompt, never both' });
309
+ /**
310
+ * Does the BASE tool allowlist permit `entry`? (guuey#527 subset rule.)
311
+ * A base pattern covers `entry` when it is `*`, an exact match, or a
312
+ * `prefix.*` wildcard whose prefix `entry` falls under. This is the SAME
313
+ * coverage semantics the pod's gate applies at call time, so "the mode's
314
+ * allowlist is a subset" means exactly "every mode tool the base already
315
+ * permits" — a mode can only ever NARROW, never widen.
316
+ */
317
+ export function baseAllowlistPermits(baseAllowlist, entry) {
318
+ // No base allowlist = the model may call anything, so any mode entry is
319
+ // trivially permitted (the mode is still a real narrowing of "anything").
320
+ if (baseAllowlist === undefined)
321
+ return true;
322
+ return baseAllowlist.some((b) => {
323
+ if (b === '*')
324
+ return true;
325
+ if (b === entry)
326
+ return true;
327
+ if (b.endsWith('.*')) {
328
+ const prefix = b.slice(0, -1); // keep the dot: "platform." covers "platform.whoami"
329
+ return entry.startsWith(prefix);
330
+ }
331
+ return false;
332
+ });
333
+ }
282
334
  /**
283
335
  * Bedrock-style invocation endpoint config.
284
336
  *
@@ -423,6 +475,40 @@ export const AgentSectionV1 = z.strictObject({
423
475
  })
424
476
  .optional(),
425
477
  tools: ToolGatesSchema.optional(),
478
+ /**
479
+ * Multi-mode agent (guuey#527) — ONE agent, per-mode prompt overlays on
480
+ * the base `systemPrompt` + a per-mode tool SUBSET of the base allowlist.
481
+ * THE AXIS (founder-refined 2026-08-31, the final word): modes are the
482
+ * AUDIENCE'S AUTH STATE — v1 recognizes exactly `guest` (website
483
+ * visitors; the hire-a-rep mode) and `auth` (signed-in users; navigated
484
+ * separately in studio). SELECTION is SERVER-DERIVED from the caller's
485
+ * REAL auth state ({@link applyAgentMode}) — an anonymous caller
486
+ * structurally cannot claim auth mode; the widget/SDK `mode` param is a
487
+ * PIN within permission (authed may pin `guest` to preview-as-visitor;
488
+ * a guest pinning `auth` clamps). `defaultMode` and per-mode `audience`
489
+ * are v1-DEPRECATED (parseable, not consulted — derivation replaced
490
+ * both); the grammar stays key-agnostic for future axes.
491
+ *
492
+ * Each mode: exactly one of `systemPromptAppend` (extend the base — the
493
+ * common case) or `systemPrompt` (full replace); an optional `tools`
494
+ * allowlist that must be a SUBSET of the base `agent.tools.allowlist`
495
+ * (the write gate enforces it — a mode can only ever NARROW).
496
+ */
497
+ modes: ModesSchema.optional(),
498
+ /** The mode a caller with no audience match resolves to (a declared mode key). */
499
+ defaultMode: z.string().min(1).optional(),
500
+ /**
501
+ * The platform wrapper's SURFACE-FORMATTING section (guuey#531) —
502
+ * default ON, opt-out only: a few invoke-constant lines telling the
503
+ * model that guuey's chat surfaces render markdown (code fences +
504
+ * inline code, autolinked bare URLs, native tables), so every builder
505
+ * doesn't have to discover that independently. Set `false` for BYO
506
+ * surfaces (`@guuey/agent-client` custom clients — SMS/voice/
507
+ * plain-text) where markdown is not the contract. The exact text
508
+ * lives at `@guuey/host` `SURFACE_FORMATTING_SECTION` and is
509
+ * published verbatim in the docs — never tone/brand/behavior.
510
+ */
511
+ surfaceHints: z.boolean().optional(),
426
512
  runtime: RuntimeConfigSchema.optional(),
427
513
  /** Claude Agent SDK-specific knobs. Only read when `framework: 'claude-agent-sdk'`. */
428
514
  claude: ClaudeFrameworkConfigSchema.optional(),
@@ -444,7 +530,122 @@ export const AgentSectionV1 = z.strictObject({
444
530
  endpoint: EndpointConfigSchema.optional(),
445
531
  // ── Deploy ──
446
532
  deploy: DeploySchema.optional(),
533
+ }).superRefine((agent, ctx) => {
534
+ // guuey#527 — the mode tool SUBSET rule, enforced at parse time (CLI
535
+ // loader AND server both run this): a mode's tool allowlist may only
536
+ // contain tools the BASE allowlist already permits. A mode narrows,
537
+ // never widens — the one privilege-adjacent axis, closed here.
538
+ if (agent.modes === undefined)
539
+ return;
540
+ const base = agent.tools?.allowlist;
541
+ for (const [mode, def] of Object.entries(agent.modes)) {
542
+ const modeAllow = def.tools?.allowlist;
543
+ if (modeAllow === undefined)
544
+ continue;
545
+ for (const entry of modeAllow) {
546
+ if (!baseAllowlistPermits(base, entry)) {
547
+ ctx.addIssue({
548
+ code: z.ZodIssueCode.custom,
549
+ path: ['modes', mode, 'tools', 'allowlist'],
550
+ message: `mode "${mode}" allows tool "${entry}" which the base agent.tools.allowlist does not permit — a mode can only narrow the base tools, never widen them`,
551
+ });
552
+ }
553
+ }
554
+ }
555
+ // A declared defaultMode must name a declared mode.
556
+ if (agent.defaultMode !== undefined && agent.modes[agent.defaultMode] === undefined) {
557
+ ctx.addIssue({
558
+ code: z.ZodIssueCode.custom,
559
+ path: ['defaultMode'],
560
+ message: `defaultMode "${agent.defaultMode}" is not a declared mode (${Object.keys(agent.modes).join(', ') || 'none'})`,
561
+ });
562
+ }
447
563
  });
564
+ /**
565
+ * Resolve + apply one invoke's agent mode (guuey#527/#566 — the guest/auth
566
+ * axis, founder-refined 2026-08-31: "letting them use guest mode vs auth
567
+ * mode is the correct definition").
568
+ *
569
+ * SELECTION is SERVER-DERIVED: `audience` is the caller's REAL auth state
570
+ * (the pod maps anonymous → 'guest', authenticated → 'auth') — never a
571
+ * client claim. The `pin` (the widget/SDK `mode` param) may only choose
572
+ * WITHIN permission: an authed caller may pin 'guest' (preview-as-visitor);
573
+ * a guest pinning 'auth' is CLAMPED to guest ('pin-clamped'), and an
574
+ * unrecognized pin key is ignored ('unknown-mode') — fail-soft always, an
575
+ * embed never breaks.
576
+ *
577
+ * Fallback chain: the selected key's def; an undeclared 'auth' falls to
578
+ * 'guest''s def ('auth-undeclared' — an app that only configures the
579
+ * visitor rep serves everyone that rep, which IS hire-a-rep); still
580
+ * nothing → base. `defaultMode` is deprecated and not consulted.
581
+ *
582
+ * Application (unchanged from the first cut, test-pinned): `systemPrompt`
583
+ * REPLACES the base; `systemPromptAppend` = base + "\n\n" + append; an
584
+ * EMPTY def serves the base by SAME REFERENCE (callers detect no-override
585
+ * by identity); `tools` replaces with the write-gate-proven subset; a
586
+ * `{ file }` prompt is unusable at serve time → base. Pure — never
587
+ * mutates inputs.
588
+ */
589
+ export function applyAgentMode(agent, pin, audience) {
590
+ const modes = agent.modes;
591
+ if (modes === undefined)
592
+ return { agent, applied: null };
593
+ // ── Selection: derived, then the pin within permission ──────────────
594
+ let fallback;
595
+ let selected = audience;
596
+ if (pin !== undefined && pin !== audience) {
597
+ if (pin !== 'guest' && pin !== 'auth') {
598
+ fallback = 'unknown-mode'; // ignored; pure derivation stands
599
+ }
600
+ else if (pin === 'auth' && audience === 'guest') {
601
+ fallback = 'pin-clamped'; // a guest cannot claim auth mode
602
+ }
603
+ else {
604
+ selected = pin; // authed pinning 'guest' — preview-as-visitor
605
+ }
606
+ }
607
+ // ── Fallback chain: selected → guest → base ─────────────────────────
608
+ let applied = selected;
609
+ let def = modes[selected];
610
+ if (def === undefined && selected === 'auth') {
611
+ def = modes['guest'];
612
+ if (def !== undefined) {
613
+ applied = 'guest';
614
+ fallback = fallback ?? 'auth-undeclared';
615
+ }
616
+ }
617
+ if (def === undefined) {
618
+ return { agent, applied: null, ...(fallback !== undefined ? { fallback } : {}) };
619
+ }
620
+ // ── Application (unchanged) ─────────────────────────────────────────
621
+ // An EMPTY def (legal — "at most one") IS the base: same reference out,
622
+ // so callers can cheaply detect "nothing to override" by identity.
623
+ if (def.systemPrompt === undefined && def.systemPromptAppend === undefined && def.tools === undefined) {
624
+ return { agent, applied, ...(fallback !== undefined ? { fallback } : {}) };
625
+ }
626
+ const basePrompt = typeof agent.systemPrompt === 'string' ? agent.systemPrompt : undefined;
627
+ let systemPrompt = agent.systemPrompt;
628
+ if (typeof def.systemPrompt === 'string') {
629
+ systemPrompt = def.systemPrompt;
630
+ }
631
+ else if (typeof def.systemPromptAppend === 'string') {
632
+ systemPrompt =
633
+ basePrompt !== undefined ? `${basePrompt}\n\n${def.systemPromptAppend}` : def.systemPromptAppend;
634
+ }
635
+ else if (def.systemPrompt !== undefined || def.systemPromptAppend !== undefined) {
636
+ // { file } shape — unusable at serve time; base verbatim.
637
+ return { agent, applied: null, ...(fallback !== undefined ? { fallback } : {}) };
638
+ }
639
+ return {
640
+ agent: {
641
+ ...agent,
642
+ ...(systemPrompt !== undefined ? { systemPrompt } : {}),
643
+ ...(def.tools !== undefined ? { tools: def.tools } : {}),
644
+ },
645
+ applied,
646
+ ...(fallback !== undefined ? { fallback } : {}),
647
+ };
648
+ }
448
649
  // ── No-literal-secrets validation (deploy-time contract enforcement) ──────────
449
650
  //
450
651
  // The schema (McpServerSchema JSDoc) requires secrets in `mcpServers[].headers`
@@ -613,6 +814,14 @@ export const RESERVED_MEMORY_SERVER_NAME = 'guuey-memory';
613
814
  * platform entry at invoke time.
614
815
  */
615
816
  export const RESERVED_PROFILE_SERVER_NAME = 'guuey-profile';
817
+ /**
818
+ * The reserved colocated key the auto-injected human-handoff MCP is booted +
819
+ * spliced under (guuey#552, spec 2026-08-31-human-handoff-v1-design.md).
820
+ * NEVER builder-declarable — mirrors {@link RESERVED_MEMORY_SERVER_NAME}'s
821
+ * reservation for the same reason; the handoff child's own server advertises
822
+ * this name and its aud is `colocatedResourceUrl(appId, this)`.
823
+ */
824
+ export const RESERVED_HANDOFF_SERVER_NAME = 'guuey-handoff';
616
825
  /**
617
826
  * Every `mcpServers` map key the platform reserves. Extensible — one entry
618
827
  * today ({@link RESERVED_MEMORY_SERVER_NAME}); future platform-injected
@@ -621,6 +830,7 @@ export const RESERVED_PROFILE_SERVER_NAME = 'guuey-profile';
621
830
  export const RESERVED_MCP_SERVER_NAMES = [
622
831
  RESERVED_MEMORY_SERVER_NAME,
623
832
  RESERVED_PROFILE_SERVER_NAME,
833
+ RESERVED_HANDOFF_SERVER_NAME,
624
834
  ];
625
835
  /**
626
836
  * Validate that no `agent.mcpServers` entry uses a platform-RESERVED name
package/dist/app.d.ts CHANGED
@@ -35,9 +35,9 @@ export type AppUserAuthMode = (typeof APP_USER_AUTH_MODES)[number];
35
35
  */
36
36
  export declare const AppAccessV1: z.ZodObject<{
37
37
  userAuthMode: z.ZodOptional<z.ZodEnum<{
38
+ byo: "byo";
38
39
  anonymous: "anonymous";
39
40
  native_pool: "native_pool";
40
- byo: "byo";
41
41
  }>>;
42
42
  userAuthConfig: z.ZodOptional<z.ZodNullable<z.ZodObject<{
43
43
  issuerUrl: z.ZodString;
@@ -110,9 +110,9 @@ export declare const AppSectionV1: z.ZodObject<{
110
110
  customDomain: z.ZodOptional<z.ZodString>;
111
111
  access: z.ZodOptional<z.ZodObject<{
112
112
  userAuthMode: z.ZodOptional<z.ZodEnum<{
113
+ byo: "byo";
113
114
  anonymous: "anonymous";
114
115
  native_pool: "native_pool";
115
- byo: "byo";
116
116
  }>>;
117
117
  userAuthConfig: z.ZodOptional<z.ZodNullable<z.ZodObject<{
118
118
  issuerUrl: z.ZodString;
package/dist/loader.d.ts CHANGED
@@ -52,6 +52,19 @@ export interface ResolvedGuueyJson {
52
52
  * (guuey#400), or `undefined` when no theme was set.
53
53
  */
54
54
  resolvedTheme: GuueyAppTheme | undefined;
55
+ /**
56
+ * Resolved per-mode prompts (guuey#545): for every `agent.modes[key]`
57
+ * whose `systemPrompt` / `systemPromptAppend` is present, the FINAL
58
+ * string — the inline value as-is, or the file contents when it was a
59
+ * `{ file: '...' }` reference. Before this, a file-shaped mode prompt
60
+ * rode into the snapshot unresolved and reached the pod (which has no
61
+ * repo filesystem) — a silently broken deploy. `undefined` when the
62
+ * document declares no modes.
63
+ */
64
+ resolvedModePrompts: Record<string, {
65
+ systemPrompt?: string;
66
+ systemPromptAppend?: string;
67
+ }> | undefined;
55
68
  /** Absolute path the document was loaded from (for diagnostics). */
56
69
  sourcePath: string;
57
70
  }
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAuBA,OAAO,EAEL,WAAW,EAGZ,MAAM,aAAa,CAAC;AAErB,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAE5D,wEAAwE;AACxE,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,GAAE,MAAsB,EAChC,QAAQ,GAAE,MAA+B,GACxC,MAAM,GAAG,IAAI,CAUf;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAgB3D;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CAIvE;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,oCAAoC;IACpC,GAAG,EAAE,WAAW,CAAC;IACjB;;;;;OAKG;IACH,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC;;;;OAIG;IACH,aAAa,EAAE,aAAa,GAAG,SAAS,CAAC;IACzC,oEAAoE;IACpE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAK7D;AAmGD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,iBAAiB,GAAG,WAAW,CAgB1E"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AA0BA,OAAO,EAEL,WAAW,EAGZ,MAAM,aAAa,CAAC;AAErB,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAE5D,wEAAwE;AACxE,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,GAAE,MAAsB,EAChC,QAAQ,GAAE,MAA+B,GACxC,MAAM,GAAG,IAAI,CAUf;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAgB3D;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CAIvE;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,oCAAoC;IACpC,GAAG,EAAE,WAAW,CAAC;IACjB;;;;;OAKG;IACH,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC;;;;OAIG;IACH,aAAa,EAAE,aAAa,GAAG,SAAS,CAAC;IACzC;;;;;;;;OAQG;IACH,mBAAmB,EACf,MAAM,CAAC,MAAM,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,kBAAkB,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GACtE,SAAS,CAAC;IACd,oEAAoE;IACpE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAY7D;AA2JD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,iBAAiB,GAAG,WAAW,CAiC1E"}
package/dist/loader.js CHANGED
@@ -4,8 +4,11 @@
4
4
  * Pure-parse helpers (`parseGuueyJson` / `safeParseGuueyJson`) live in
5
5
  * `./schema.ts` and are safe to import from non-Node contexts. This
6
6
  * module adds the file-resolution layer: reading `guuey.json` from disk,
7
- * inlining `agent.systemPrompt.file` references, and producing the
8
- * snapshot the deploy upload + pod boot both read.
7
+ * inlining `agent.systemPrompt.file` AND every
8
+ * `agent.modes[*].{systemPrompt,systemPromptAppend}.file` reference
9
+ * (guuey#545 — a file-shaped mode prompt used to ride into the snapshot
10
+ * unresolved), and producing the snapshot the deploy upload + pod boot
11
+ * both read.
9
12
  *
10
13
  * Intended callers:
11
14
  *
@@ -103,7 +106,64 @@ export function loadGuueyJson(path) {
103
106
  const doc = readGuueyJsonFile(path);
104
107
  const resolvedSystemPrompt = resolveSystemPrompt(doc, path);
105
108
  const resolvedTheme = resolveAppTheme(doc, path);
106
- return { doc, resolvedSystemPrompt, resolvedTheme, sourcePath: path };
109
+ const resolvedModePrompts = resolveModePrompts(doc, path);
110
+ return {
111
+ doc,
112
+ resolvedSystemPrompt,
113
+ resolvedTheme,
114
+ resolvedModePrompts,
115
+ sourcePath: path,
116
+ };
117
+ }
118
+ /**
119
+ * Shared relative-file guard + read for prompt `{ file }` references
120
+ * (guuey#545 extraction — the base prompt, and now every mode prompt,
121
+ * enforce the identical rules): the path must be relative, must not
122
+ * traverse parent directories, and must exist. `label` names the field
123
+ * in every error so the failure reads like the document, not the loader.
124
+ */
125
+ function readPromptFileRef(label, file, guueyJsonPath) {
126
+ if (isAbsolute(file)) {
127
+ throw new Error(`${label} must be a relative path (got absolute: ${file})`);
128
+ }
129
+ if (file.split('/').includes('..')) {
130
+ throw new Error(`${label} must not traverse parent directories (got: ${file})`);
131
+ }
132
+ const resolved = resolve(dirname(guueyJsonPath), file);
133
+ if (!existsSync(resolved)) {
134
+ throw new Error(`${label} references missing file: ${file} (resolved to ${resolved})`);
135
+ }
136
+ return readFileSync(resolved, 'utf-8');
137
+ }
138
+ /**
139
+ * Resolve every mode's `systemPrompt` / `systemPromptAppend` to final
140
+ * strings (guuey#545). Inline strings pass through; `{ file }` references
141
+ * resolve under {@link readPromptFileRef}'s guards — the SAME contract as
142
+ * the base prompt, which the ModeSchema doc always promised. Absent
143
+ * modes → undefined.
144
+ */
145
+ function resolveModePrompts(doc, guueyJsonPath) {
146
+ const modes = doc.agent.modes;
147
+ if (modes === undefined)
148
+ return undefined;
149
+ const out = {};
150
+ for (const [key, mode] of Object.entries(modes)) {
151
+ const entry = {};
152
+ if (mode.systemPrompt !== undefined) {
153
+ entry.systemPrompt =
154
+ typeof mode.systemPrompt === 'string'
155
+ ? mode.systemPrompt
156
+ : readPromptFileRef(`agent.modes.${key}.systemPrompt.file`, mode.systemPrompt.file, guueyJsonPath);
157
+ }
158
+ if (mode.systemPromptAppend !== undefined) {
159
+ entry.systemPromptAppend =
160
+ typeof mode.systemPromptAppend === 'string'
161
+ ? mode.systemPromptAppend
162
+ : readPromptFileRef(`agent.modes.${key}.systemPromptAppend.file`, mode.systemPromptAppend.file, guueyJsonPath);
163
+ }
164
+ out[key] = entry;
165
+ }
166
+ return out;
107
167
  }
108
168
  /**
109
169
  * Resolve `agent.systemPrompt` to a final string (or undefined).
@@ -122,19 +182,8 @@ function resolveSystemPrompt(doc, guueyJsonPath) {
122
182
  return undefined;
123
183
  if (typeof sp === 'string')
124
184
  return sp;
125
- // sp = { file: '...' }
126
- if (isAbsolute(sp.file)) {
127
- throw new Error(`agent.systemPrompt.file must be a relative path (got absolute: ${sp.file})`);
128
- }
129
- if (sp.file.split('/').includes('..')) {
130
- throw new Error(`agent.systemPrompt.file must not traverse parent directories (got: ${sp.file})`);
131
- }
132
- const baseDir = dirname(guueyJsonPath);
133
- const resolved = resolve(baseDir, sp.file);
134
- if (!existsSync(resolved)) {
135
- throw new Error(`agent.systemPrompt.file references missing file: ${sp.file} (resolved to ${resolved})`);
136
- }
137
- return readFileSync(resolved, 'utf-8');
185
+ // sp = { file: '...' } — shared guard+read (guuey#545 extraction).
186
+ return readPromptFileRef('agent.systemPrompt.file', sp.file, guueyJsonPath);
138
187
  }
139
188
  /**
140
189
  * Resolve `app.theme` to the inline document (or undefined).
@@ -198,6 +247,24 @@ export function buildDeploySnapshot(loaded) {
198
247
  if (loaded.resolvedSystemPrompt !== undefined) {
199
248
  cloned.agent.systemPrompt = loaded.resolvedSystemPrompt;
200
249
  }
250
+ // guuey#545 — mode prompts inline the same way the base prompt does:
251
+ // a `{ file }` reference must never ride into the snapshot (the pod
252
+ // has no repo filesystem; an unresolved object was a silently broken
253
+ // deploy). Only prompt fields are touched — tools/audience and every
254
+ // unexposed mode key survive verbatim.
255
+ if (loaded.resolvedModePrompts !== undefined && cloned.agent.modes !== undefined) {
256
+ for (const [key, resolved] of Object.entries(loaded.resolvedModePrompts)) {
257
+ const mode = cloned.agent.modes[key];
258
+ if (mode === undefined)
259
+ continue;
260
+ if (resolved.systemPrompt !== undefined) {
261
+ mode.systemPrompt = resolved.systemPrompt;
262
+ }
263
+ if (resolved.systemPromptAppend !== undefined) {
264
+ mode.systemPromptAppend = resolved.systemPromptAppend;
265
+ }
266
+ }
267
+ }
201
268
  const theme = cloned.app?.theme;
202
269
  if (cloned.app !== undefined &&
203
270
  theme !== undefined &&
package/dist/schema.d.ts CHANGED
@@ -189,6 +189,25 @@ export declare const GuueyJsonV1: z.ZodObject<{
189
189
  allowlist: z.ZodOptional<z.ZodArray<z.ZodString>>;
190
190
  denylist: z.ZodOptional<z.ZodArray<z.ZodString>>;
191
191
  }, z.core.$strict>>;
192
+ modes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
193
+ systemPromptAppend: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
194
+ file: z.ZodString;
195
+ }, z.core.$strict>]>>;
196
+ systemPrompt: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
197
+ file: z.ZodString;
198
+ }, z.core.$strict>]>>;
199
+ tools: z.ZodOptional<z.ZodObject<{
200
+ allowlist: z.ZodOptional<z.ZodArray<z.ZodString>>;
201
+ denylist: z.ZodOptional<z.ZodArray<z.ZodString>>;
202
+ }, z.core.$strict>>;
203
+ audience: z.ZodOptional<z.ZodArray<z.ZodEnum<{
204
+ guest: "guest";
205
+ authenticated: "authenticated";
206
+ byo: "byo";
207
+ }>>>;
208
+ }, z.core.$strict>>>;
209
+ defaultMode: z.ZodOptional<z.ZodString>;
210
+ surfaceHints: z.ZodOptional<z.ZodBoolean>;
192
211
  runtime: z.ZodOptional<z.ZodObject<{
193
212
  maxTurns: z.ZodOptional<z.ZodNumber>;
194
213
  temperature: z.ZodOptional<z.ZodNumber>;
@@ -245,9 +264,9 @@ export declare const GuueyJsonV1: z.ZodObject<{
245
264
  customDomain: z.ZodOptional<z.ZodString>;
246
265
  access: z.ZodOptional<z.ZodObject<{
247
266
  userAuthMode: z.ZodOptional<z.ZodEnum<{
267
+ byo: "byo";
248
268
  anonymous: "anonymous";
249
269
  native_pool: "native_pool";
250
- byo: "byo";
251
270
  }>>;
252
271
  userAuthConfig: z.ZodOptional<z.ZodNullable<z.ZodObject<{
253
272
  issuerUrl: z.ZodString;
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,EAAiB,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,sBAAsB,GAC9B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEnD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,GAAG,sBAAsB,CAY5E;AAED;;;;;GAKG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;aAE3B,IAAI,EAAE,gBAAgB,GAAG,oBAAoB;aAC7C,KAAK,EAAE,MAAM;gBADb,IAAI,EAAE,gBAAgB,GAAG,oBAAoB,EAC7C,KAAK,EAAE,MAAM,EAC7B,OAAO,EAAE,MAAM;CAKlB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAkBjE;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAmDtB,CAAC;AAEH,kDAAkD;AAClD,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAG3D,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;AAEvD;;;GAGG;AACH,eAAO,MAAM,mBAAmB,eAAe,CAAC;AAEhD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,CAExD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,OAAO,GACX,UAAU,CAAC,OAAO,WAAW,CAAC,SAAS,CAAC,CAE1C"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,EAAiB,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,sBAAsB,GAC9B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEnD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,GAAG,sBAAsB,CAY5E;AAED;;;;;GAKG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;aAE3B,IAAI,EAAE,gBAAgB,GAAG,oBAAoB;aAC7C,KAAK,EAAE,MAAM;gBADb,IAAI,EAAE,gBAAgB,GAAG,oBAAoB,EAC7C,KAAK,EAAE,MAAM,EAC7B,OAAO,EAAE,MAAM;CAKlB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAkBjE;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAmDtB,CAAC;AAEH,kDAAkD;AAClD,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAG3D,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;AAEvD;;;GAGG;AACH,eAAO,MAAM,mBAAmB,eAAe,CAAC;AAEhD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,CAExD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,OAAO,GACX,UAAU,CAAC,OAAO,WAAW,CAAC,SAAS,CAAC,CAE1C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guuey/config",
3
- "version": "0.16.11",
3
+ "version": "0.16.14",
4
4
  "description": "Open-source schemas + loaders for the three guuey config files: agent.json (declarative agent definition), guuey.json (hosted-deploy overlay), and the helper types around them. Consumed by @guuey/cli, the guuey backend, and devs writing their own agent or MCP server.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -35,7 +35,7 @@
35
35
  "@types/node": "^24.0.0",
36
36
  "typescript": "^5.0.0",
37
37
  "vitest": "^3.2.7",
38
- "@guuey/chat": "0.16.11"
38
+ "@guuey/chat": "0.16.14"
39
39
  },
40
40
  "publishConfig": {
41
41
  "access": "public"