@guuey/config 0.16.7 → 0.16.9

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/app.d.ts CHANGED
@@ -9,6 +9,7 @@
9
9
  * (agent or mcp-server) is what's deployable; this section is presentation.
10
10
  */
11
11
  import { z } from 'zod';
12
+ import { type GuueyAppTheme } from './theme.js';
12
13
  /**
13
14
  * End-user auth modes an app record accepts — mirrors the platform's
14
15
  * `USER_AUTH_MODES` (`@guuey-private/types` `security/user-auth-mode.ts`),
@@ -47,6 +48,55 @@ export declare const AppAccessV1: z.ZodObject<{
47
48
  }, z.core.$strict>;
48
49
  /** Static TypeScript type for `app.access`. */
49
50
  export type GuueyAppAccess = z.infer<typeof AppAccessV1>;
51
+ /**
52
+ * `guuey.json#app.page` — the STANDALONE PAGE's posture, declared in the
53
+ * manifest so `guuey agent apply` converges it (posture-as-code, guuey#286;
54
+ * requested by the first agents-as-code consumer, ggui#558). Field names ARE
55
+ * the platform's `standalonePage` patch vocabulary (`PUT /v1/apps/:id` /
56
+ * the reconcile `config` block, cli-wire `STANDALONE_PAGE_FIELDS`) — no
57
+ * translation; the backend pins the two lists equal
58
+ * (`handlers/reconcile.test.ts`, the APP_USER_AUTH_MODES pattern).
59
+ *
60
+ * Patch semantics, same as the PUT: an absent member is left untouched by a
61
+ * reconcile, `null` clears the member to its default (page on, indexable, no
62
+ * copy/CTA/endpoint). Values are shape-checked only — caps, the https rule
63
+ * and the CTA both-or-neither pairing are the platform validator's
64
+ * (`validateStandalonePagePatch`), so the CLI surfaces its exact message
65
+ * (the same split as `app.theme`'s colour grammar).
66
+ *
67
+ * `slug` stays OUT: claiming has uniqueness/release semantics (a ceremony,
68
+ * not a converged field). But the posture here REACHES the slug ceremony's
69
+ * automatic arm: the first-Live default-slug claim (guuey#249) consults it
70
+ * and skips when `enabled: false` — `--page off` in git means no auto-named
71
+ * public host, ever.
72
+ */
73
+ export declare const AppPageV1: z.ZodObject<{
74
+ enabled: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
75
+ welcomeCopy: z.ZodOptional<z.ZodNullable<z.ZodString>>;
76
+ ctaLabel: z.ZodOptional<z.ZodNullable<z.ZodString>>;
77
+ ctaUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
78
+ identityEndpointUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
79
+ noindex: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
80
+ }, z.core.$strict>;
81
+ /** Static TypeScript type for `app.page`. */
82
+ export type GuueyAppPage = z.infer<typeof AppPageV1>;
83
+ /**
84
+ * `app.theme` as a file reference — `{ "file": "theme.json" }`, the same
85
+ * convention as `agent.systemPrompt` (guuey#400). The file's CONTENT is the
86
+ * same {@link AppThemeV1} vocabulary — no new grammar. Resolution is
87
+ * CLI-side (the loader reads the file relative to `guuey.json`); the server
88
+ * only ever sees the inline document and 400s an unresolved reference.
89
+ *
90
+ * Discrimination against the inline form is unambiguous: a `{file}`-only
91
+ * strict object can never collide with the theme vocabulary (which
92
+ * requires `mode`/`colors`).
93
+ */
94
+ export declare const ThemeFileRefV1: z.ZodObject<{
95
+ file: z.ZodString;
96
+ }, z.core.$strict>;
97
+ export type ThemeFileRef = z.infer<typeof ThemeFileRefV1>;
98
+ /** Narrow an `app.theme` value to the `{ file }` reference form. */
99
+ export declare function isThemeFileRef(theme: ThemeFileRef | GuueyAppTheme): theme is ThemeFileRef;
50
100
  /**
51
101
  * The app section schema. All fields optional in v1 — a project may carry
52
102
  * the bare minimum at first and grow the listing as it publishes.
@@ -71,7 +121,9 @@ export declare const AppSectionV1: z.ZodObject<{
71
121
  allowedDomains: z.ZodOptional<z.ZodArray<z.ZodString>>;
72
122
  guestAccess: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
73
123
  }, z.core.$strict>>;
74
- theme: z.ZodOptional<z.ZodObject<{
124
+ theme: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
125
+ file: z.ZodString;
126
+ }, z.core.$strict>, z.ZodObject<{
75
127
  name: z.ZodOptional<z.ZodString>;
76
128
  mode: z.ZodEnum<{
77
129
  light: "light";
@@ -167,6 +219,14 @@ export declare const AppSectionV1: z.ZodObject<{
167
219
  comfortable: "comfortable";
168
220
  }>;
169
221
  }, z.core.$strict>>;
222
+ }, z.core.$strict>]>>;
223
+ page: z.ZodOptional<z.ZodObject<{
224
+ enabled: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
225
+ welcomeCopy: z.ZodOptional<z.ZodNullable<z.ZodString>>;
226
+ ctaLabel: z.ZodOptional<z.ZodNullable<z.ZodString>>;
227
+ ctaUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
228
+ identityEndpointUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
229
+ noindex: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
170
230
  }, z.core.$strict>>;
171
231
  }, z.core.$strict>;
172
232
  /** Static TypeScript type for the app section. */
package/dist/app.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA2CxB;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,8CAA+C,CAAC;AAChF,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnE;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;kBAWtB,CAAC;AAEH,+CAA+C;AAC/C,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEzD;;;GAGG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAWvB,CAAC;AAEH,kDAAkD;AAClD,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC"}
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AA0C5D;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,8CAA+C,CAAC;AAChF,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnE;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;kBAWtB,CAAC;AAEH,+CAA+C;AAC/C,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,SAAS;;;;;;;kBAOpB,CAAC;AAEH,6CAA6C;AAC7C,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AAErD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc;;kBAA8C,CAAC;AAC1E,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE1D,oEAAoE;AACpE,wBAAgB,cAAc,CAC5B,KAAK,EAAE,YAAY,GAAG,aAAa,GAClC,KAAK,IAAI,YAAY,CAEvB;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAiBvB,CAAC;AAEH,kDAAkD;AAClD,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC"}
package/dist/app.js CHANGED
@@ -75,6 +75,52 @@ export const AppAccessV1 = z.strictObject({
75
75
  allowedDomains: z.array(z.string().min(1)).optional(),
76
76
  guestAccess: z.boolean().nullable().optional(),
77
77
  });
78
+ /**
79
+ * `guuey.json#app.page` — the STANDALONE PAGE's posture, declared in the
80
+ * manifest so `guuey agent apply` converges it (posture-as-code, guuey#286;
81
+ * requested by the first agents-as-code consumer, ggui#558). Field names ARE
82
+ * the platform's `standalonePage` patch vocabulary (`PUT /v1/apps/:id` /
83
+ * the reconcile `config` block, cli-wire `STANDALONE_PAGE_FIELDS`) — no
84
+ * translation; the backend pins the two lists equal
85
+ * (`handlers/reconcile.test.ts`, the APP_USER_AUTH_MODES pattern).
86
+ *
87
+ * Patch semantics, same as the PUT: an absent member is left untouched by a
88
+ * reconcile, `null` clears the member to its default (page on, indexable, no
89
+ * copy/CTA/endpoint). Values are shape-checked only — caps, the https rule
90
+ * and the CTA both-or-neither pairing are the platform validator's
91
+ * (`validateStandalonePagePatch`), so the CLI surfaces its exact message
92
+ * (the same split as `app.theme`'s colour grammar).
93
+ *
94
+ * `slug` stays OUT: claiming has uniqueness/release semantics (a ceremony,
95
+ * not a converged field). But the posture here REACHES the slug ceremony's
96
+ * automatic arm: the first-Live default-slug claim (guuey#249) consults it
97
+ * and skips when `enabled: false` — `--page off` in git means no auto-named
98
+ * public host, ever.
99
+ */
100
+ export const AppPageV1 = z.strictObject({
101
+ enabled: z.boolean().nullable().optional(),
102
+ welcomeCopy: z.string().nullable().optional(),
103
+ ctaLabel: z.string().nullable().optional(),
104
+ ctaUrl: z.string().nullable().optional(),
105
+ identityEndpointUrl: z.string().nullable().optional(),
106
+ noindex: z.boolean().nullable().optional(),
107
+ });
108
+ /**
109
+ * `app.theme` as a file reference — `{ "file": "theme.json" }`, the same
110
+ * convention as `agent.systemPrompt` (guuey#400). The file's CONTENT is the
111
+ * same {@link AppThemeV1} vocabulary — no new grammar. Resolution is
112
+ * CLI-side (the loader reads the file relative to `guuey.json`); the server
113
+ * only ever sees the inline document and 400s an unresolved reference.
114
+ *
115
+ * Discrimination against the inline form is unambiguous: a `{file}`-only
116
+ * strict object can never collide with the theme vocabulary (which
117
+ * requires `mode`/`colors`).
118
+ */
119
+ export const ThemeFileRefV1 = z.strictObject({ file: z.string().min(1) });
120
+ /** Narrow an `app.theme` value to the `{ file }` reference form. */
121
+ export function isThemeFileRef(theme) {
122
+ return 'file' in theme;
123
+ }
78
124
  /**
79
125
  * The app section schema. All fields optional in v1 — a project may carry
80
126
  * the bare minimum at first and grow the listing as it publishes.
@@ -88,6 +134,12 @@ export const AppSectionV1 = z.strictObject({
88
134
  customDomain: CustomDomainSchema.optional(),
89
135
  /** Access policy converged by `guuey agent apply` — see {@link AppAccessV1}. */
90
136
  access: AppAccessV1.optional(),
91
- /** Chat theme AS CODE, converged by `guuey agent apply` — see {@link AppThemeV1}. */
92
- theme: AppThemeV1.optional(),
137
+ /**
138
+ * Chat theme AS CODE, converged by `guuey agent apply` — the inline
139
+ * document ({@link AppThemeV1}) or a `{ file }` reference the CLI
140
+ * resolves ({@link ThemeFileRefV1}).
141
+ */
142
+ theme: z.union([ThemeFileRefV1, AppThemeV1]).optional(),
143
+ /** Standalone-page posture converged by `guuey agent apply` — see {@link AppPageV1}. */
144
+ page: AppPageV1.optional(),
93
145
  });
package/dist/loader.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { GuueyJsonV1 } from './schema.js';
2
+ import { type GuueyAppTheme } from './theme.js';
2
3
  /** How many parent directories `findGuueyJson` will walk by default. */
3
4
  export declare const DEFAULT_FIND_MAX_DEPTH = 8;
4
5
  /**
@@ -45,6 +46,12 @@ export interface ResolvedGuueyJson {
45
46
  * (caller falls back to `GUUEY_DEFAULT_SYSTEM_PROMPT`).
46
47
  */
47
48
  resolvedSystemPrompt: string | undefined;
49
+ /**
50
+ * The resolved app theme — the inline document from `app.theme`, or the
51
+ * file's parsed content when it was a `{ file: '...' }` reference
52
+ * (guuey#400), or `undefined` when no theme was set.
53
+ */
54
+ resolvedTheme: GuueyAppTheme | undefined;
48
55
  /** Absolute path the document was loaded from (for diagnostics). */
49
56
  sourcePath: string;
50
57
  }
@@ -57,15 +64,16 @@ export interface ResolvedGuueyJson {
57
64
  * shape; the pod runtime reads the resolved prompt directly.
58
65
  *
59
66
  * Throws if the file is missing, unreadable, malformed, fails schema
60
- * validation, OR the systemPrompt.file path resolves to a missing or
61
- * unreadable file.
67
+ * validation, OR the systemPrompt.file / app.theme.file path resolves to
68
+ * a missing or unreadable file.
62
69
  */
63
70
  export declare function loadGuueyJson(path: string): ResolvedGuueyJson;
64
71
  /**
65
72
  * Build the snapshot the deploy upload + pod boot consume.
66
73
  *
67
74
  * Replaces `agent.systemPrompt = { file }` with `agent.systemPrompt = <inlined>`
68
- * so the snapshot is self-contained. Returns a deep-cloned document
75
+ * and `app.theme = { file }` with the file's parsed document so the
76
+ * snapshot is self-contained. Returns a deep-cloned document
69
77
  * (caller mutations don't leak back).
70
78
  */
71
79
  export declare function buildDeploySnapshot(loaded: ResolvedGuueyJson): GuueyJsonV1;
@@ -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,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,oEAAoE;IACpE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAI7D;AAyCD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,iBAAiB,GAAG,WAAW,CAM1E"}
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"}
package/dist/loader.js CHANGED
@@ -22,6 +22,8 @@
22
22
  import { existsSync, readFileSync, writeFileSync } from 'node:fs';
23
23
  import { dirname, isAbsolute, join, resolve } from 'node:path';
24
24
  import { GUUEY_JSON_FILENAME, assertSupportedGuueyJsonSchema, parseGuueyJson, } from './schema.js';
25
+ import { isThemeFileRef } from './app.js';
26
+ import { AppThemeV1 } from './theme.js';
25
27
  /** How many parent directories `findGuueyJson` will walk by default. */
26
28
  export const DEFAULT_FIND_MAX_DEPTH = 8;
27
29
  /**
@@ -94,13 +96,14 @@ export function writeGuueyJsonFile(path, doc) {
94
96
  * shape; the pod runtime reads the resolved prompt directly.
95
97
  *
96
98
  * Throws if the file is missing, unreadable, malformed, fails schema
97
- * validation, OR the systemPrompt.file path resolves to a missing or
98
- * unreadable file.
99
+ * validation, OR the systemPrompt.file / app.theme.file path resolves to
100
+ * a missing or unreadable file.
99
101
  */
100
102
  export function loadGuueyJson(path) {
101
103
  const doc = readGuueyJsonFile(path);
102
104
  const resolvedSystemPrompt = resolveSystemPrompt(doc, path);
103
- return { doc, resolvedSystemPrompt, sourcePath: path };
105
+ const resolvedTheme = resolveAppTheme(doc, path);
106
+ return { doc, resolvedSystemPrompt, resolvedTheme, sourcePath: path };
104
107
  }
105
108
  /**
106
109
  * Resolve `agent.systemPrompt` to a final string (or undefined).
@@ -133,11 +136,61 @@ function resolveSystemPrompt(doc, guueyJsonPath) {
133
136
  }
134
137
  return readFileSync(resolved, 'utf-8');
135
138
  }
139
+ /**
140
+ * Resolve `app.theme` to the inline document (or undefined).
141
+ *
142
+ * - Absent → undefined.
143
+ * - Inline document → returned as-is.
144
+ * - `{ file }` → resolved relative to `guueyJsonPath`'s directory, file
145
+ * read + parsed against the STRICT {@link AppThemeV1} shape (guuey#400).
146
+ * Shape only — colour VALUES stay with the server's one grammar
147
+ * (`validateChatTheme`), so plan/apply surfaces its exact message.
148
+ *
149
+ * File paths must be relative + must not escape the project root (no
150
+ * `..` traversal). Absolute paths are rejected — keeps the snapshot
151
+ * portable across deploy environments.
152
+ */
153
+ function resolveAppTheme(doc, guueyJsonPath) {
154
+ const theme = doc.app?.theme;
155
+ if (theme === undefined)
156
+ return undefined;
157
+ if (!isThemeFileRef(theme))
158
+ return theme;
159
+ // theme = { file: '...' }
160
+ if (isAbsolute(theme.file)) {
161
+ throw new Error(`app.theme.file must be a relative path (got absolute: ${theme.file})`);
162
+ }
163
+ if (theme.file.split('/').includes('..')) {
164
+ throw new Error(`app.theme.file must not traverse parent directories (got: ${theme.file})`);
165
+ }
166
+ const baseDir = dirname(guueyJsonPath);
167
+ const resolved = resolve(baseDir, theme.file);
168
+ if (!existsSync(resolved)) {
169
+ throw new Error(`app.theme.file references missing file: ${theme.file} (resolved to ${resolved})`);
170
+ }
171
+ const raw = readFileSync(resolved, 'utf-8');
172
+ let json;
173
+ try {
174
+ json = JSON.parse(raw);
175
+ }
176
+ catch (err) {
177
+ const msg = err instanceof Error ? err.message : String(err);
178
+ throw new Error(`app.theme.file ${theme.file} is not valid JSON: ${msg}`);
179
+ }
180
+ const parsed = AppThemeV1.safeParse(json);
181
+ if (!parsed.success) {
182
+ const first = parsed.error.issues[0];
183
+ const path = first?.path.join('.') || '(root)';
184
+ throw new Error(`app.theme.file ${theme.file} failed theme validation at "${path}": ${first?.message ?? 'unknown error'}`);
185
+ }
186
+ return parsed.data;
187
+ }
136
188
  /**
137
189
  * Build the snapshot the deploy upload + pod boot consume.
138
190
  *
139
191
  * Replaces `agent.systemPrompt = { file }` with `agent.systemPrompt = <inlined>`
140
- * so the snapshot is self-contained. Returns a deep-cloned document
192
+ * and `app.theme = { file }` with the file's parsed document so the
193
+ * snapshot is self-contained. Returns a deep-cloned document
141
194
  * (caller mutations don't leak back).
142
195
  */
143
196
  export function buildDeploySnapshot(loaded) {
@@ -145,5 +198,13 @@ export function buildDeploySnapshot(loaded) {
145
198
  if (loaded.resolvedSystemPrompt !== undefined) {
146
199
  cloned.agent.systemPrompt = loaded.resolvedSystemPrompt;
147
200
  }
201
+ const theme = cloned.app?.theme;
202
+ if (cloned.app !== undefined &&
203
+ theme !== undefined &&
204
+ isThemeFileRef(theme) &&
205
+ loaded.resolvedTheme !== undefined) {
206
+ const themeClone = JSON.parse(JSON.stringify(loaded.resolvedTheme));
207
+ cloned.app.theme = themeClone;
208
+ }
148
209
  return cloned;
149
210
  }
package/dist/schema.d.ts CHANGED
@@ -256,7 +256,9 @@ export declare const GuueyJsonV1: z.ZodObject<{
256
256
  allowedDomains: z.ZodOptional<z.ZodArray<z.ZodString>>;
257
257
  guestAccess: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
258
258
  }, z.core.$strict>>;
259
- theme: z.ZodOptional<z.ZodObject<{
259
+ theme: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
260
+ file: z.ZodString;
261
+ }, z.core.$strict>, z.ZodObject<{
260
262
  name: z.ZodOptional<z.ZodString>;
261
263
  mode: z.ZodEnum<{
262
264
  light: "light";
@@ -352,6 +354,14 @@ export declare const GuueyJsonV1: z.ZodObject<{
352
354
  comfortable: "comfortable";
353
355
  }>;
354
356
  }, z.core.$strict>>;
357
+ }, z.core.$strict>]>>;
358
+ page: z.ZodOptional<z.ZodObject<{
359
+ enabled: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
360
+ welcomeCopy: z.ZodOptional<z.ZodNullable<z.ZodString>>;
361
+ ctaLabel: z.ZodOptional<z.ZodNullable<z.ZodString>>;
362
+ ctaUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
363
+ identityEndpointUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
364
+ noindex: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
355
365
  }, z.core.$strict>>;
356
366
  }, z.core.$strict>>;
357
367
  ggui: z.ZodOptional<z.ZodObject<{
@@ -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"}
@@ -10,4 +10,21 @@
10
10
  * handshake.
11
11
  */
12
12
  export declare const GUUEY_DEFAULT_SYSTEM_PROMPT: string;
13
+ /**
14
+ * The system prompt the `@guuey/create-agentic-app` scaffold ships in
15
+ * `prompts/system.md` (guuey#463) — distinct from
16
+ * {@link GUUEY_DEFAULT_SYSTEM_PROMPT}, which is the RUNTIME fallback for an
17
+ * agent that supplies no prompt at all. This constant is the single source
18
+ * for the scaffold text: `create-agentic-app`'s `build-templates.mjs` stamps
19
+ * `templates-src/core/prompts/system.md` from it (as `text + '\n'`) and
20
+ * `check-templates.mjs` asserts byte-equality as the publish guard.
21
+ *
22
+ * Why it must be single-sourced: `guuey pull`'s known-default replace rule
23
+ * (guuey#463, the #455 rider) may only overwrite a local `prompts/system.md`
24
+ * whose content is byte-identical to one of these two known defaults —
25
+ * anything else is a builder's own edit and is never clobbered. Editing this
26
+ * text therefore changes what `pull` recognizes as "untouched scaffold";
27
+ * rebuild the templates after any edit and commit the restamped file.
28
+ */
29
+ export declare const GUUEY_SCAFFOLD_SYSTEM_PROMPT: string;
13
30
  //# sourceMappingURL=system-prompt.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"system-prompt.d.ts","sourceRoot":"","sources":["../src/system-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,2BAA2B,QAOhC,CAAC"}
1
+ {"version":3,"file":"system-prompt.d.ts","sourceRoot":"","sources":["../src/system-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,2BAA2B,QAOhC,CAAC;AAET;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,4BAA4B,QAgBjC,CAAC"}
@@ -17,3 +17,36 @@ environment. Follow each tool's own description for guidance on when and how
17
17
  to use it. Maintain the thread of conversation across turns and ask
18
18
  clarifying questions when intent is ambiguous.
19
19
  `.trim();
20
+ /**
21
+ * The system prompt the `@guuey/create-agentic-app` scaffold ships in
22
+ * `prompts/system.md` (guuey#463) — distinct from
23
+ * {@link GUUEY_DEFAULT_SYSTEM_PROMPT}, which is the RUNTIME fallback for an
24
+ * agent that supplies no prompt at all. This constant is the single source
25
+ * for the scaffold text: `create-agentic-app`'s `build-templates.mjs` stamps
26
+ * `templates-src/core/prompts/system.md` from it (as `text + '\n'`) and
27
+ * `check-templates.mjs` asserts byte-equality as the publish guard.
28
+ *
29
+ * Why it must be single-sourced: `guuey pull`'s known-default replace rule
30
+ * (guuey#463, the #455 rider) may only overwrite a local `prompts/system.md`
31
+ * whose content is byte-identical to one of these two known defaults —
32
+ * anything else is a builder's own edit and is never clobbered. Editing this
33
+ * text therefore changes what `pull` recognizes as "untouched scaffold";
34
+ * rebuild the templates after any edit and commit the restamped file.
35
+ */
36
+ export const GUUEY_SCAFFOLD_SYSTEM_PROMPT = `
37
+ You are the agentic-app-template assistant. You have a todo tool server —
38
+ use it to create, list, toggle, and delete the user's todos.
39
+
40
+ Show, don't tell: when a result has structure — a todo list, a form, a
41
+ confirmation — render it as an interactive surface with the ggui render
42
+ loop by default; keep plain prose for one-line answers with nothing to
43
+ show. After changing todos (create, toggle, delete), render the updated
44
+ list, so every turn ends on a surface that reflects the current state.
45
+ Use only the fields the surface's schema declares.
46
+
47
+ An under-specified ask is NEVER answered with a text-only list of
48
+ questions: render the closest useful surface FIRST — the current list,
49
+ the options, a form — and ask your one narrowing question in a short
50
+ line beside it. Clarification happens ON a surface, from the very first
51
+ turn.
52
+ `.trim();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guuey/config",
3
- "version": "0.16.7",
3
+ "version": "0.16.9",
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.7"
38
+ "@guuey/chat": "0.16.9"
39
39
  },
40
40
  "publishConfig": {
41
41
  "access": "public"