@pasko70/pibo 1.0.4 → 1.1.0

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.
Files changed (34) hide show
  1. package/README.md +5 -1
  2. package/dist/apps/chat/agent-profiles.js +30 -4
  3. package/dist/apps/chat/agent-store.js +46 -3
  4. package/dist/apps/chat/model-catalog.js +42 -0
  5. package/dist/apps/chat/web-app.js +232 -7
  6. package/dist/apps/chat-ui/assets/{dist-DHE-yBLH.js → dist-8e4L8ciw.js} +1 -1
  7. package/dist/apps/chat-ui/assets/{dist-BvO6SpaM.js → dist-B7bbI3Qz.js} +1 -1
  8. package/dist/apps/chat-ui/assets/{dist-_DPv0-QF.js → dist-CMZOa5ik.js} +1 -1
  9. package/dist/apps/chat-ui/assets/{dist-_oM7g0eh.js → dist-CNLWH2qh.js} +1 -1
  10. package/dist/apps/chat-ui/assets/{dist-CG_gRSwv.js → dist-CUqpST5w.js} +1 -1
  11. package/dist/apps/chat-ui/assets/{dist-laadbMWg.js → dist-CqqUC8Ce.js} +1 -1
  12. package/dist/apps/chat-ui/assets/{dist-L1B-KWZO.js → dist-CspjOeBj.js} +1 -1
  13. package/dist/apps/chat-ui/assets/{dist-nFui3f-r.js → dist-D2N4-LIc.js} +1 -1
  14. package/dist/apps/chat-ui/assets/{dist-BPv5MdlL.js → dist-D9L4MSRF.js} +1 -1
  15. package/dist/apps/chat-ui/assets/{dist-D4HhiFWB.js → dist-DNcnQRPF.js} +1 -1
  16. package/dist/apps/chat-ui/assets/{dist-B5rcjVCj.js → dist-v4pjHlB7.js} +1 -1
  17. package/dist/apps/chat-ui/assets/index-D2Qxambd.js +151 -0
  18. package/dist/apps/chat-ui/assets/{index-Dk-opWl3.css → index-DXx368XK.css} +1 -1
  19. package/dist/apps/chat-ui/index.html +2 -2
  20. package/dist/core/model-defaults.js +53 -0
  21. package/dist/core/profiles.js +16 -0
  22. package/dist/core/runtime.js +8 -6
  23. package/dist/core/session-router.js +2 -0
  24. package/dist/gateway/server.js +2 -0
  25. package/dist/plugins/builtin.js +2 -1
  26. package/dist/plugins/registry.js +20 -1
  27. package/dist/user-skills/installer.js +211 -0
  28. package/dist/user-skills/manager.js +35 -0
  29. package/dist/user-skills/store.js +228 -0
  30. package/dist/user-skills/types.js +1 -0
  31. package/package.json +3 -3
  32. package/dist/apps/chat-ui/assets/index-BdcqTugD.js +0 -151
  33. /package/{.codex/skills → skills/builtin}/pi-agent-harness/SKILL.md +0 -0
  34. /package/{.codex/skills → skills/builtin}/pi-agent-harness/agents/openai.yaml +0 -0
package/README.md CHANGED
@@ -111,6 +111,10 @@ Custom agents can be archived before deletion. Archived custom agents are remove
111
111
 
112
112
  The designer configures native Pibo agent capabilities only: plugin-registered tools, skills, context files, subagents, automatic local context-file loading, built-in Pi tool visibility, and capability packages such as `pibo-run-control`. Curated external CLI tools from `pibo tools` remain global operator tooling and are not selected per agent.
113
113
 
114
+ The Chat Web App also supports User Skills. Operators can create local skills or import them from `skills.sh` or GitHub, and Pibo stores them under `.pibo/user-skills` plus `.pibo/user-skills.json`. Enabled user skills are registered live so they are selectable in the Agent Designer and available to routed sessions without a manual restart.
115
+
116
+ User Skill names are guarded at two layers: the web app no longer re-registers an already-synced skill on subsequent requests, and create/rename/enable/import flows reject names that would collide with an already registered skill. Imports also refuse to silently overwrite an existing skill name.
117
+
114
118
  The Chat Web App now also has a dedicated Context area at `/apps/chat/context`. It reuses the managed context-file APIs inside the main Chat UI shell so operators can create, edit, relocate, and remove managed context files without leaving the authenticated Chat Web App. Plugin context files are shown there as read-only sources, and both the Context area and Agent Designer can create linked managed copies when a user wants to customize shipped content safely.
115
119
 
116
120
  The Context area also exposes the Pibo Base Prompt. The library prompt lives at `context/pibo-system-prompt.md`; the Chat Web App can switch between that read-only library prompt and a persisted custom prompt stored under `.pibo/base-prompt.md`. Runtime prompt templates replace `{{availableTools}}` and `{{guidelines}}` from the active Pi/Pibo tool surface before project context and skills are appended.
@@ -119,7 +123,7 @@ The Context area also exposes the Pibo Compaction Prompt. The library prompt liv
119
123
 
120
124
  ## Profiles
121
125
 
122
- The default profile is registered by the core plugin. It loads the local `pi-agent-harness` skill and uses Pi Coding Agent's built-in tools for normal coding work.
126
+ The default profile is registered by the core plugin. It loads the built-in `pi-agent-harness` skill from `skills/builtin/pi-agent-harness/SKILL.md` and uses Pi Coding Agent's built-in tools for normal coding work.
123
127
 
124
128
  The `codex` alias resolves to the `codex-compat-openai-web` profile. It keeps the Pibo runtime boundary while exposing Codex-like coding affordances: Pi/Pibo `read`, `edit`, and `write`; Pibo run-control `bash`; native `web_search`; `apply_patch` and `view_image`; generated `pibo_subagent_default`, `pibo_subagent_explorer`, and `pibo_subagent_worker`; and the `pibo_run_*` lifecycle tools. `web_search` is a normal Pibo native tool with a provider adapter; the default adapter injects OpenAI Responses hosted `web_search` into the model request instead of using a local DuckDuckGo function tool. It intentionally does not expose Pi's separate `grep`, `find`, or `ls` tools by default; codebase search remains Codex-style through `bash` and commands such as `rg`.
125
129
 
@@ -12,10 +12,30 @@ export function createCustomAgentProfileDefinition(agent) {
12
12
  .withMcpServers(agent.mcpServers)
13
13
  .withPiPackages(agent.piPackages.map((id) => ({ id })))
14
14
  .withToolPackages({ runControl: agent.runControl });
15
- for (const skillName of agent.skills)
16
- builder.addSkill(context.getSkill(skillName));
17
- for (const contextFileKey of agent.contextFiles)
18
- builder.addContextFile(context.getContextFile(contextFileKey));
15
+ if (agent.mainModel)
16
+ builder.withMainModel(agent.mainModel);
17
+ if (agent.subagentModel)
18
+ builder.withSubagentModel(agent.subagentModel);
19
+ for (const skillName of agent.skills) {
20
+ try {
21
+ builder.addSkill(context.getSkill(skillName));
22
+ }
23
+ catch (error) {
24
+ if (!isUnknownSkillError(error, skillName))
25
+ throw error;
26
+ console.warn(`Skipping unknown skill "${skillName}" for custom agent "${agent.profileName}"`);
27
+ }
28
+ }
29
+ for (const contextFileKey of agent.contextFiles) {
30
+ try {
31
+ builder.addContextFile(context.getContextFile(contextFileKey));
32
+ }
33
+ catch (error) {
34
+ if (!isUnknownContextFileError(error, contextFileKey))
35
+ throw error;
36
+ console.warn(`Skipping unknown context file "${contextFileKey}" for custom agent "${agent.profileName}"`);
37
+ }
38
+ }
19
39
  for (const toolName of agent.nativeTools)
20
40
  builder.addTool(context.getTool(toolName));
21
41
  for (const subagent of agent.subagents)
@@ -24,3 +44,9 @@ export function createCustomAgentProfileDefinition(agent) {
24
44
  },
25
45
  };
26
46
  }
47
+ function isUnknownContextFileError(error, contextFileKey) {
48
+ return error instanceof Error && error.message === `Unknown context file "${contextFileKey}"`;
49
+ }
50
+ function isUnknownSkillError(error, skillName) {
51
+ return error instanceof Error && error.message === `Unknown skill "${skillName}"`;
52
+ }
@@ -28,6 +28,8 @@ export class CustomAgentStore {
28
28
  subagents_json TEXT NOT NULL,
29
29
  mcp_servers_json TEXT NOT NULL DEFAULT '[]',
30
30
  pi_packages_json TEXT NOT NULL DEFAULT '[]',
31
+ main_model_json TEXT,
32
+ subagent_model_json TEXT,
31
33
  builtin_tools TEXT NOT NULL,
32
34
  builtin_tool_names_json TEXT NOT NULL DEFAULT '["read","bash","edit","write"]',
33
35
  auto_context_files INTEGER NOT NULL DEFAULT 1,
@@ -44,6 +46,7 @@ export class CustomAgentStore {
44
46
  this.migrateAutoContextFilesColumn();
45
47
  this.migrateMcpServersColumn();
46
48
  this.migratePiPackagesColumn();
49
+ this.migrateModelColumns();
47
50
  this.migrateBuiltinToolNamesColumn();
48
51
  this.migrateLegacyProfileNames();
49
52
  }
@@ -78,6 +81,8 @@ export class CustomAgentStore {
78
81
  subagents: sanitizeSubagents(input.subagents ?? []),
79
82
  mcpServers: uniqueStrings(input.mcpServers ?? []),
80
83
  piPackages: sanitizePiPackages(input.piPackages ?? []),
84
+ mainModel: sanitizeModelProfile(input.mainModel),
85
+ subagentModel: sanitizeModelProfile(input.subagentModel),
81
86
  builtinTools: input.builtinTools ?? "default",
82
87
  builtinToolNames: sanitizeBuiltinToolNames(input.builtinToolNames),
83
88
  autoContextFiles: input.autoContextFiles ?? true,
@@ -109,6 +114,8 @@ export class CustomAgentStore {
109
114
  subagents: input.subagents ? sanitizeSubagents(input.subagents) : existing.subagents,
110
115
  mcpServers: input.mcpServers ? uniqueStrings(input.mcpServers) : existing.mcpServers,
111
116
  piPackages: input.piPackages ? sanitizePiPackages(input.piPackages) : existing.piPackages,
117
+ mainModel: input.mainModel === undefined ? existing.mainModel : sanitizeModelProfile(input.mainModel),
118
+ subagentModel: input.subagentModel === undefined ? existing.subagentModel : sanitizeModelProfile(input.subagentModel),
112
119
  builtinTools: input.builtinTools ?? existing.builtinTools,
113
120
  builtinToolNames: input.builtinToolNames ? sanitizeBuiltinToolNames(input.builtinToolNames) : existing.builtinToolNames,
114
121
  autoContextFiles: input.autoContextFiles ?? existing.autoContextFiles,
@@ -127,6 +134,8 @@ export class CustomAgentStore {
127
134
  subagents_json = ?,
128
135
  mcp_servers_json = ?,
129
136
  pi_packages_json = ?,
137
+ main_model_json = ?,
138
+ subagent_model_json = ?,
130
139
  builtin_tools = ?,
131
140
  builtin_tool_names_json = ?,
132
141
  auto_context_files = ?,
@@ -134,7 +143,7 @@ export class CustomAgentStore {
134
143
  updated_at = ?
135
144
  WHERE id = ?
136
145
  `)
137
- .run(updated.profileName, updated.displayName, updated.description ?? null, JSON.stringify(updated.nativeTools), JSON.stringify(updated.skills), JSON.stringify(updated.contextFiles), JSON.stringify(sanitizeSubagents(updated.subagents)), JSON.stringify(updated.mcpServers), JSON.stringify(updated.piPackages), updated.builtinTools, JSON.stringify(updated.builtinToolNames), updated.autoContextFiles ? 1 : 0, updated.runControl ? 1 : 0, updated.updatedAt, id);
146
+ .run(updated.profileName, updated.displayName, updated.description ?? null, JSON.stringify(updated.nativeTools), JSON.stringify(updated.skills), JSON.stringify(updated.contextFiles), JSON.stringify(sanitizeSubagents(updated.subagents)), JSON.stringify(updated.mcpServers), JSON.stringify(updated.piPackages), updated.mainModel ? JSON.stringify(updated.mainModel) : null, updated.subagentModel ? JSON.stringify(updated.subagentModel) : null, updated.builtinTools, JSON.stringify(updated.builtinToolNames), updated.autoContextFiles ? 1 : 0, updated.runControl ? 1 : 0, updated.updatedAt, id);
138
147
  return this.get(id);
139
148
  }
140
149
  setArchived(id, archived) {
@@ -170,6 +179,8 @@ export class CustomAgentStore {
170
179
  subagents_json,
171
180
  mcp_servers_json,
172
181
  pi_packages_json,
182
+ main_model_json,
183
+ subagent_model_json,
173
184
  builtin_tools,
174
185
  builtin_tool_names_json,
175
186
  auto_context_files,
@@ -177,9 +188,9 @@ export class CustomAgentStore {
177
188
  created_at,
178
189
  updated_at,
179
190
  archived_at
180
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
191
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
181
192
  `)
182
- .run(agent.id, agent.profileName, agent.ownerScope, agent.displayName, agent.description ?? null, JSON.stringify(agent.nativeTools), JSON.stringify(agent.skills), JSON.stringify(agent.contextFiles), JSON.stringify(sanitizeSubagents(agent.subagents)), JSON.stringify(agent.mcpServers), JSON.stringify(agent.piPackages), agent.builtinTools, JSON.stringify(agent.builtinToolNames), agent.autoContextFiles ? 1 : 0, agent.runControl ? 1 : 0, agent.createdAt, agent.updatedAt, agent.archivedAt ?? null);
193
+ .run(agent.id, agent.profileName, agent.ownerScope, agent.displayName, agent.description ?? null, JSON.stringify(agent.nativeTools), JSON.stringify(agent.skills), JSON.stringify(agent.contextFiles), JSON.stringify(sanitizeSubagents(agent.subagents)), JSON.stringify(agent.mcpServers), JSON.stringify(agent.piPackages), agent.mainModel ? JSON.stringify(agent.mainModel) : null, agent.subagentModel ? JSON.stringify(agent.subagentModel) : null, agent.builtinTools, JSON.stringify(agent.builtinToolNames), agent.autoContextFiles ? 1 : 0, agent.runControl ? 1 : 0, agent.createdAt, agent.updatedAt, agent.archivedAt ?? null);
183
194
  }
184
195
  requireProfileNameAvailable(profileName, currentId) {
185
196
  const row = this.db.prepare("SELECT id FROM chat_agents WHERE profile_name = ?").get(profileName);
@@ -224,6 +235,15 @@ export class CustomAgentStore {
224
235
  this.db.prepare("ALTER TABLE chat_agents ADD COLUMN pi_packages_json TEXT NOT NULL DEFAULT '[]'").run();
225
236
  }
226
237
  }
238
+ migrateModelColumns() {
239
+ const columns = new Set(this.db.prepare("PRAGMA table_info(chat_agents)").all().map((column) => column.name));
240
+ if (!columns.has("main_model_json")) {
241
+ this.db.prepare("ALTER TABLE chat_agents ADD COLUMN main_model_json TEXT").run();
242
+ }
243
+ if (!columns.has("subagent_model_json")) {
244
+ this.db.prepare("ALTER TABLE chat_agents ADD COLUMN subagent_model_json TEXT").run();
245
+ }
246
+ }
227
247
  migrateBuiltinToolNamesColumn() {
228
248
  const columns = new Set(this.db.prepare("PRAGMA table_info(chat_agents)").all().map((column) => column.name));
229
249
  if (!columns.has("builtin_tool_names_json")) {
@@ -247,6 +267,8 @@ function agentFromRow(row) {
247
267
  subagents: parseSubagents(row.subagents_json),
248
268
  mcpServers: parseStringArray(row.mcp_servers_json),
249
269
  piPackages: parseStringArray(row.pi_packages_json),
270
+ mainModel: parseModelProfile(row.main_model_json),
271
+ subagentModel: parseModelProfile(row.subagent_model_json),
250
272
  builtinTools: row.builtin_tools,
251
273
  builtinToolNames: sanitizeBuiltinToolNames(parseStringArray(row.builtin_tool_names_json)),
252
274
  autoContextFiles: row.auto_context_files !== 0,
@@ -291,6 +313,27 @@ function parseSubagents(value) {
291
313
  return [];
292
314
  }
293
315
  }
316
+ function parseModelProfile(value) {
317
+ if (!value)
318
+ return undefined;
319
+ try {
320
+ return sanitizeModelProfile(JSON.parse(value));
321
+ }
322
+ catch {
323
+ return undefined;
324
+ }
325
+ }
326
+ function sanitizeModelProfile(value) {
327
+ if (!value)
328
+ return undefined;
329
+ if (typeof value.provider !== "string" || typeof value.id !== "string")
330
+ return undefined;
331
+ const provider = value.provider.trim();
332
+ const id = value.id.trim();
333
+ if (!provider || !id)
334
+ return undefined;
335
+ return { provider, id };
336
+ }
294
337
  function sanitizeSubagents(value) {
295
338
  return value.flatMap((item) => {
296
339
  if (!item || typeof item !== "object" || Array.isArray(item))
@@ -0,0 +1,42 @@
1
+ import { createAgentSessionServices } from "@mariozechner/pi-coding-agent";
2
+ export function buildModelCatalogFromRegistry(registry) {
3
+ const providers = new Map();
4
+ for (const model of registry.getAll()) {
5
+ const providerId = model.provider;
6
+ let provider = providers.get(providerId);
7
+ if (!provider) {
8
+ const authConfigured = registry.getProviderAuthStatus?.(providerId).configured ?? false;
9
+ provider = {
10
+ id: providerId,
11
+ label: registry.getProviderDisplayName?.(providerId) ?? providerId,
12
+ authConfigured,
13
+ models: [],
14
+ };
15
+ providers.set(providerId, provider);
16
+ }
17
+ provider.models.push({
18
+ provider: providerId,
19
+ id: model.id,
20
+ label: model.name || model.id,
21
+ authConfigured: provider.authConfigured,
22
+ supportsReasoning: model.reasoning || undefined,
23
+ });
24
+ }
25
+ return {
26
+ providers: [...providers.values()]
27
+ .sort((left, right) => left.label.localeCompare(right.label) || left.id.localeCompare(right.id))
28
+ .map((provider) => ({
29
+ ...provider,
30
+ models: [...provider.models].sort((left, right) => left.label.localeCompare(right.label) || left.id.localeCompare(right.id)),
31
+ })),
32
+ };
33
+ }
34
+ export async function loadModelCatalog(cwd = process.cwd()) {
35
+ try {
36
+ const services = await createAgentSessionServices({ cwd });
37
+ return buildModelCatalogFromRegistry(services.modelRegistry);
38
+ }
39
+ catch {
40
+ return { providers: [] };
41
+ }
42
+ }
@@ -11,7 +11,9 @@ import { chatStreamFramesFromOutputEvent, createChatStreamState } from "./stream
11
11
  import { buildSessionNodes, buildTraceView, createTraceViewVersion, loadPiSessionMetadata } from "./trace.js";
12
12
  import { isChatWebSessionArchived, withChatWebArchived } from "./session-metadata.js";
13
13
  import { CustomAgentStore, createDefaultCustomAgentStore, isValidCustomAgentName, } from "./agent-store.js";
14
+ import { loadPiboModelDefaults, savePiboModelDefaults, } from "../../core/model-defaults.js";
14
15
  import { createCustomAgentProfileDefinition } from "./agent-profiles.js";
16
+ import { loadModelCatalog } from "./model-catalog.js";
15
17
  import { createDefaultPiboReliabilityStore, PiboReliabilityStore } from "../../reliability/store.js";
16
18
  import { listMcpServerInfos, setMcpServerDescription } from "../../mcp/agent-context.js";
17
19
  import { readPiboBasePrompt, savePiboCustomBasePrompt, setPiboBasePromptMode, } from "../../core/base-prompt.js";
@@ -19,6 +21,7 @@ import { readPiboCompactionPrompt, savePiboCustomCompactionPrompt, setPiboCompac
19
21
  import { getDefaultPiboWorkspace } from "../../core/workspace.js";
20
22
  import { inspectPiPackageSource } from "../../pi-packages/metadata.js";
21
23
  import { findPiPackage, listPiPackages, removePiPackage, setPiPackageEnabled, upsertPiPackage } from "../../pi-packages/store.js";
24
+ import { UserSkillManager } from "../../user-skills/manager.js";
22
25
  export const CHAT_WEB_APP_NAME = "pibo.chat-web";
23
26
  export const CHAT_WEB_CHANNEL = "pibo.chat-web";
24
27
  export const CHAT_WEB_MOUNT_PATH = "/apps/chat";
@@ -342,6 +345,23 @@ function normalizeRunControl(value) {
342
345
  throw new PiboWebHttpError("runControl must be a boolean", 400);
343
346
  return value;
344
347
  }
348
+ function normalizeModelProfile(value, fieldName) {
349
+ if (value === undefined || value === null)
350
+ return undefined;
351
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
352
+ throw new PiboWebHttpError(`${fieldName} must be an object`, 400);
353
+ }
354
+ const raw = value;
355
+ if (typeof raw.provider !== "string" || typeof raw.id !== "string") {
356
+ throw new PiboWebHttpError(`${fieldName} must include provider and id`, 400);
357
+ }
358
+ const provider = raw.provider.trim();
359
+ const id = raw.id.trim();
360
+ if (!provider || !id) {
361
+ throw new PiboWebHttpError(`${fieldName} must include provider and id`, 400);
362
+ }
363
+ return { provider, id };
364
+ }
345
365
  function normalizeMcpServerDescriptionBody(value) {
346
366
  if (typeof value !== "string")
347
367
  throw new PiboWebHttpError("MCP server description must be a string", 400);
@@ -372,6 +392,59 @@ function normalizeCompactionPromptMarkdown(value) {
372
392
  throw new PiboWebHttpError("markdown must be a string", 400);
373
393
  return value;
374
394
  }
395
+ function normalizeUserSkillName(value) {
396
+ if (typeof value !== "string" || value.trim().length === 0) {
397
+ throw new PiboWebHttpError("Skill name is required", 400);
398
+ }
399
+ const name = value.trim();
400
+ if (name.length > 64)
401
+ throw new PiboWebHttpError("Skill name is too long", 400);
402
+ if (!/^[a-z][a-z0-9-]*$/.test(name)) {
403
+ throw new PiboWebHttpError("Skill name must be lowercase kebab-case, e.g. my-skill", 400);
404
+ }
405
+ return name;
406
+ }
407
+ function normalizeUserSkillDescription(value) {
408
+ if (typeof value !== "string")
409
+ throw new PiboWebHttpError("Skill description must be a string", 400);
410
+ return value.trim();
411
+ }
412
+ function normalizeUserSkillMarkdown(value) {
413
+ if (typeof value !== "string")
414
+ throw new PiboWebHttpError("Skill markdown must be a string", 400);
415
+ return value;
416
+ }
417
+ function normalizeUserSkillEnabled(value) {
418
+ if (value === undefined)
419
+ return undefined;
420
+ if (typeof value !== "boolean")
421
+ throw new PiboWebHttpError("enabled must be a boolean", 400);
422
+ return value;
423
+ }
424
+ function normalizeUserSkillUrl(value) {
425
+ if (typeof value !== "string" || value.trim().length === 0) {
426
+ throw new PiboWebHttpError("Skill URL is required", 400);
427
+ }
428
+ const url = value.trim();
429
+ if (!url.startsWith("http://") && !url.startsWith("https://") && !url.includes("/")) {
430
+ throw new PiboWebHttpError("Skill URL must be a valid URL or owner/repo shorthand", 400);
431
+ }
432
+ return url;
433
+ }
434
+ function userSkillResourceId(pathname) {
435
+ const prefix = `${CHAT_WEB_API_PREFIX}/user-skills/`;
436
+ if (!pathname.startsWith(prefix))
437
+ return undefined;
438
+ const encodedId = pathname.slice(prefix.length);
439
+ if (!encodedId || encodedId.includes("/"))
440
+ return undefined;
441
+ try {
442
+ return decodeURIComponent(encodedId);
443
+ }
444
+ catch {
445
+ throw new PiboWebHttpError("Invalid user skill id", 400);
446
+ }
447
+ }
375
448
  function normalizeAgentArchived(value) {
376
449
  if (value === undefined)
377
450
  return undefined;
@@ -456,6 +529,15 @@ function createRoomStore(path) {
456
529
  function createAgentStore(path) {
457
530
  return path ? new CustomAgentStore(path) : createDefaultCustomAgentStore();
458
531
  }
532
+ function loadChatModelDefaults(cwd = process.cwd()) {
533
+ return loadPiboModelDefaults(cwd);
534
+ }
535
+ function updateChatModelDefaults(body, cwd = process.cwd()) {
536
+ return savePiboModelDefaults({
537
+ main: normalizeModelProfile(body.main, "main"),
538
+ subagent: normalizeModelProfile(body.subagent, "subagent"),
539
+ }, cwd);
540
+ }
459
541
  function createReliabilityStore(path) {
460
542
  return path ? new PiboReliabilityStore(path) : createDefaultPiboReliabilityStore();
461
543
  }
@@ -716,6 +798,52 @@ function ensureCustomAgentProfiles(state, context) {
716
798
  context.channelContext.upsertProfile(createCustomAgentProfileDefinition(agent));
717
799
  }
718
800
  }
801
+ function serializeCustomAgent(agent, context) {
802
+ return {
803
+ ...agent,
804
+ brokenContextFiles: listBrokenContextFiles(agent.contextFiles, context),
805
+ };
806
+ }
807
+ function serializeCustomAgents(agents, context) {
808
+ return agents.map((agent) => serializeCustomAgent(agent, context));
809
+ }
810
+ function listBrokenContextFiles(keys, context) {
811
+ const catalog = context.channelContext.getCapabilityCatalog?.();
812
+ if (!catalog)
813
+ return [];
814
+ const knownKeys = new Set(catalog.contextFiles.map((contextFile) => contextFile.key));
815
+ return keys.filter((key) => !knownKeys.has(key));
816
+ }
817
+ function syncUserSkills(state, context) {
818
+ const registerSkill = context.channelContext.registerSkill;
819
+ const unregisterSkill = context.channelContext.unregisterSkill;
820
+ if (!registerSkill || !unregisterSkill)
821
+ return;
822
+ const userSkills = state.userSkillManager.list();
823
+ const enabledNames = new Set(userSkills.filter((s) => s.enabled).map((s) => s.name));
824
+ const previouslySyncedNames = state.syncedUserSkillNames ?? new Set();
825
+ // Unregister disabled or removed user skills
826
+ for (const name of previouslySyncedNames) {
827
+ if (!enabledNames.has(name)) {
828
+ unregisterSkill(name);
829
+ }
830
+ }
831
+ // Register only newly enabled user skills. Re-registering an already synced
832
+ // skill would trip the registry duplicate-skill guard and break the web UI.
833
+ for (const skill of userSkills) {
834
+ if (skill.enabled && !previouslySyncedNames.has(skill.name)) {
835
+ registerSkill({ name: skill.name, path: skill.path, enabled: true });
836
+ }
837
+ }
838
+ state.syncedUserSkillNames = enabledNames;
839
+ }
840
+ function assertUserSkillNameIsAvailable(state, context, name, currentSkillId) {
841
+ const currentSkill = currentSkillId ? state.userSkillManager.get(currentSkillId) : undefined;
842
+ const conflict = (context.channelContext.getCapabilityCatalog?.().skills ?? []).find((skill) => (skill.name === name && (!currentSkill || currentSkill.name !== name)));
843
+ if (conflict) {
844
+ throw new PiboWebHttpError(`Skill name "${name}" conflicts with an existing registered skill`, 409);
845
+ }
846
+ }
719
847
  function createAgentInput(ownerScope, body) {
720
848
  return {
721
849
  ownerScope,
@@ -727,6 +855,8 @@ function createAgentInput(ownerScope, body) {
727
855
  subagents: normalizeAgentSubagents(body.subagents),
728
856
  mcpServers: normalizeNameArray(body.mcpServers, "mcpServers"),
729
857
  piPackages: normalizeRegisteredPiPackages(body.piPackages),
858
+ mainModel: normalizeModelProfile(body.mainModel, "mainModel"),
859
+ subagentModel: normalizeModelProfile(body.subagentModel, "subagentModel"),
730
860
  builtinTools: normalizeBuiltinTools(body.builtinTools),
731
861
  builtinToolNames: normalizeBuiltinToolNames(body.builtinToolNames),
732
862
  autoContextFiles: normalizeAutoContextFiles(body.autoContextFiles),
@@ -751,6 +881,10 @@ function createAgentUpdate(body) {
751
881
  update.mcpServers = normalizeNameArray(body.mcpServers, "mcpServers");
752
882
  if (body.piPackages !== undefined)
753
883
  update.piPackages = normalizeRegisteredPiPackages(body.piPackages);
884
+ if (body.mainModel !== undefined)
885
+ update.mainModel = normalizeModelProfile(body.mainModel, "mainModel");
886
+ if (body.subagentModel !== undefined)
887
+ update.subagentModel = normalizeModelProfile(body.subagentModel, "subagentModel");
754
888
  if (body.builtinTools !== undefined)
755
889
  update.builtinTools = normalizeBuiltinTools(body.builtinTools);
756
890
  if (body.builtinToolNames !== undefined)
@@ -783,7 +917,7 @@ function requireOwnedAgent(agent, webSession) {
783
917
  }
784
918
  return agent;
785
919
  }
786
- async function buildAgentCatalog(context) {
920
+ async function buildAgentCatalog(context, state) {
787
921
  return {
788
922
  ...(context.channelContext.getCapabilityCatalog?.() ?? {
789
923
  nativeTools: [],
@@ -797,6 +931,7 @@ async function buildAgentCatalog(context) {
797
931
  }),
798
932
  mcpServers: await listMcpServerInfos(),
799
933
  piPackages: listPiPackages(),
934
+ userSkills: state.userSkillManager.list(),
800
935
  };
801
936
  }
802
937
  function agentsSelectingPiPackage(state, packageId) {
@@ -2070,6 +2205,7 @@ export function createChatWebApp(options = {}) {
2070
2205
  reliabilityStore: createReliabilityStore(options.reliabilityStorePath),
2071
2206
  traceCache: new Map(),
2072
2207
  liveListeners: new Set(),
2208
+ userSkillManager: new UserSkillManager(process.cwd()),
2073
2209
  };
2074
2210
  const requireSession = (request, context) => context.requireSession({
2075
2211
  request,
@@ -2082,6 +2218,7 @@ export function createChatWebApp(options = {}) {
2082
2218
  const url = new URL(request.url);
2083
2219
  ensureEventIndexing(state, context);
2084
2220
  ensureCustomAgentProfiles(state, context);
2221
+ syncUserSkills(state, context);
2085
2222
  const builtAsset = responseBuiltChatAsset(request, url.pathname);
2086
2223
  if (builtAsset)
2087
2224
  return builtAsset;
@@ -2129,8 +2266,10 @@ export function createChatWebApp(options = {}) {
2129
2266
  rooms,
2130
2267
  sessions,
2131
2268
  agents: context.channelContext.getProfiles?.() ?? [],
2132
- customAgents: state.agentStore.list(webSession.ownerScope, { includeArchived: true }),
2133
- agentCatalog: await buildAgentCatalog(context),
2269
+ customAgents: serializeCustomAgents(state.agentStore.list(webSession.ownerScope, { includeArchived: true }), context),
2270
+ modelDefaults: loadChatModelDefaults(process.cwd()),
2271
+ modelCatalog: await loadModelCatalog(process.cwd()),
2272
+ agentCatalog: await buildAgentCatalog(context, state),
2134
2273
  capabilities: {
2135
2274
  actions: context.channelContext.getGatewayActions(),
2136
2275
  },
@@ -2139,10 +2278,16 @@ export function createChatWebApp(options = {}) {
2139
2278
  if (url.pathname === `${CHAT_WEB_API_PREFIX}/agent-catalog` && request.method === "GET") {
2140
2279
  await requireSession(request, context);
2141
2280
  return responseJson({
2142
- catalog: await buildAgentCatalog(context),
2281
+ catalog: await buildAgentCatalog(context, state),
2143
2282
  profiles: context.channelContext.getProfiles?.() ?? [],
2144
2283
  });
2145
2284
  }
2285
+ if (url.pathname === `${CHAT_WEB_API_PREFIX}/model-defaults` && request.method === "PATCH") {
2286
+ requireSameOriginJsonRequest(request);
2287
+ await requireSession(request, context);
2288
+ const body = await readJsonBody(request);
2289
+ return responseJson({ modelDefaults: updateChatModelDefaults(body, process.cwd()) });
2290
+ }
2146
2291
  if (url.pathname === `${CHAT_WEB_API_PREFIX}/pi-packages` && request.method === "GET") {
2147
2292
  await requireSession(request, context);
2148
2293
  return responseJson({ packages: listPiPackages() });
@@ -2203,6 +2348,86 @@ export function createChatWebApp(options = {}) {
2203
2348
  const removed = removePiPackage(piPackageId);
2204
2349
  return responseJson({ removedPackage: removed });
2205
2350
  }
2351
+ if (url.pathname === `${CHAT_WEB_API_PREFIX}/user-skills` && request.method === "GET") {
2352
+ await requireSession(request, context);
2353
+ return responseJson({ skills: state.userSkillManager.list() });
2354
+ }
2355
+ if (url.pathname === `${CHAT_WEB_API_PREFIX}/user-skills` && request.method === "POST") {
2356
+ requireSameOriginJsonRequest(request);
2357
+ await requireSession(request, context);
2358
+ const body = await readJsonBody(request);
2359
+ const name = normalizeUserSkillName(body.name);
2360
+ assertUserSkillNameIsAvailable(state, context, name);
2361
+ const skill = state.userSkillManager.create({
2362
+ name,
2363
+ description: normalizeUserSkillDescription(body.description ?? ""),
2364
+ markdown: normalizeUserSkillMarkdown(body.markdown ?? ""),
2365
+ });
2366
+ syncUserSkills(state, context);
2367
+ return responseJson({ skill }, { status: 201 });
2368
+ }
2369
+ if (url.pathname === `${CHAT_WEB_API_PREFIX}/user-skills/install` && request.method === "POST") {
2370
+ requireSameOriginJsonRequest(request);
2371
+ await requireSession(request, context);
2372
+ const body = await readJsonBody(request);
2373
+ const skill = await state.userSkillManager.installFromUrl(normalizeUserSkillUrl(body.url));
2374
+ try {
2375
+ assertUserSkillNameIsAvailable(state, context, skill.name, skill.id);
2376
+ }
2377
+ catch (error) {
2378
+ state.userSkillManager.remove(skill.id);
2379
+ throw error;
2380
+ }
2381
+ syncUserSkills(state, context);
2382
+ return responseJson({ skill }, { status: 201 });
2383
+ }
2384
+ const userSkillId = userSkillResourceId(url.pathname);
2385
+ if (userSkillId && request.method === "GET") {
2386
+ await requireSession(request, context);
2387
+ const skill = state.userSkillManager.get(userSkillId);
2388
+ if (!skill)
2389
+ throw new PiboWebHttpError("Skill not found", 404);
2390
+ const markdown = state.userSkillManager.getSkillMarkdown(skill.id);
2391
+ return responseJson({ skill, markdown });
2392
+ }
2393
+ if (userSkillId && request.method === "PATCH") {
2394
+ requireSameOriginJsonRequest(request);
2395
+ await requireSession(request, context);
2396
+ const existing = state.userSkillManager.get(userSkillId);
2397
+ if (!existing)
2398
+ throw new PiboWebHttpError("Skill not found", 404);
2399
+ const body = await readJsonBody(request);
2400
+ const input = {};
2401
+ if (body.name !== undefined)
2402
+ input.name = normalizeUserSkillName(body.name);
2403
+ if (body.description !== undefined)
2404
+ input.description = normalizeUserSkillDescription(body.description);
2405
+ if (body.markdown !== undefined)
2406
+ input.markdown = normalizeUserSkillMarkdown(body.markdown);
2407
+ if (body.enabled !== undefined)
2408
+ input.enabled = normalizeUserSkillEnabled(body.enabled);
2409
+ if (Object.keys(input).length === 0) {
2410
+ throw new PiboWebHttpError("No skill update fields provided", 400);
2411
+ }
2412
+ const nextName = input.name ?? existing.name;
2413
+ const nextEnabled = input.enabled ?? existing.enabled;
2414
+ if (nextEnabled) {
2415
+ assertUserSkillNameIsAvailable(state, context, nextName, existing.id);
2416
+ }
2417
+ const skill = state.userSkillManager.update(existing.id, input);
2418
+ syncUserSkills(state, context);
2419
+ return responseJson({ skill });
2420
+ }
2421
+ if (userSkillId && request.method === "DELETE") {
2422
+ requireSameOriginJsonRequest(request);
2423
+ await requireSession(request, context);
2424
+ const existing = state.userSkillManager.get(userSkillId);
2425
+ if (!existing)
2426
+ throw new PiboWebHttpError("Skill not found", 404);
2427
+ state.userSkillManager.remove(existing.id);
2428
+ syncUserSkills(state, context);
2429
+ return responseJson({ removedSkillId: existing.id });
2430
+ }
2206
2431
  if (url.pathname === `${CHAT_WEB_API_PREFIX}/base-prompt` && request.method === "GET") {
2207
2432
  await requireSession(request, context);
2208
2433
  return responseJson({ basePrompt: await readPiboBasePrompt(process.cwd()) });
@@ -2246,7 +2471,7 @@ export function createChatWebApp(options = {}) {
2246
2471
  if (url.pathname === `${CHAT_WEB_API_PREFIX}/agents` && request.method === "GET") {
2247
2472
  const webSession = await requireSession(request, context);
2248
2473
  const includeArchived = parseBooleanSearchParam(url, "includeArchived");
2249
- return responseJson({ agents: state.agentStore.list(webSession.ownerScope, { includeArchived }) });
2474
+ return responseJson({ agents: serializeCustomAgents(state.agentStore.list(webSession.ownerScope, { includeArchived }), context) });
2250
2475
  }
2251
2476
  if (url.pathname === `${CHAT_WEB_API_PREFIX}/agents` && request.method === "POST") {
2252
2477
  requireSameOriginJsonRequest(request);
@@ -2256,7 +2481,7 @@ export function createChatWebApp(options = {}) {
2256
2481
  requireAgentProfileNameAvailable(state, context, input.displayName);
2257
2482
  const agent = state.agentStore.create(input);
2258
2483
  context.channelContext.upsertProfile?.(createCustomAgentProfileDefinition(agent));
2259
- return responseJson({ agent }, { status: 201 });
2484
+ return responseJson({ agent: serializeCustomAgent(agent, context) }, { status: 201 });
2260
2485
  }
2261
2486
  const patchAgentId = agentResourceId(url.pathname);
2262
2487
  if (patchAgentId && request.method === "PATCH") {
@@ -2280,7 +2505,7 @@ export function createChatWebApp(options = {}) {
2280
2505
  else {
2281
2506
  context.channelContext.upsertProfile?.(createCustomAgentProfileDefinition(owned));
2282
2507
  }
2283
- return responseJson({ agent: owned });
2508
+ return responseJson({ agent: serializeCustomAgent(owned, context) });
2284
2509
  }
2285
2510
  if (patchAgentId && request.method === "DELETE") {
2286
2511
  requireSameOriginJsonRequest(request);
@@ -1 +1 @@
1
- import{N as e,P as t,U as n,o as r,r as i}from"./dist-SVPsM5Oi.js";import{n as a,r as o}from"./dist-tGfufz3b.js";import{r as s}from"./dist-CG_gRSwv.js";import{html as c}from"./dist-laadbMWg.js";var l=1,u=33,d=34,f=35,p=36,m=new a(e=>{let t=e.pos;for(;;){if(e.next==10){e.advance();break}else if(e.next==123&&e.peek(1)==123||e.next<0)break;e.advance()}e.pos>t&&e.acceptToken(l)});function h(e,t,n){return new a(r=>{let i=r.pos;for(;r.next!=e&&r.next>=0&&(n||r.next!=38&&(r.next!=123||r.peek(1)!=123));)r.advance();r.pos>i&&r.acceptToken(t)})}var g=h(39,u,!1),_=h(34,d,!1),v=h(39,f,!0),y=h(34,p,!0),b=o.deserialize({version:14,states:"(jOVOqOOOeQpOOOvO!bO'#CaOOOP'#Cx'#CxQVOqOOO!OQpO'#CfO!WQpO'#ClO!]QpO'#CrO!bQpO'#CsOOQO'#Cv'#CvQ!gQpOOQ!lQpOOQ!qQpOOOOOV,58{,58{O!vOpO,58{OOOP-E6v-E6vO!{QpO,59QO#TQpO,59QOOQO,59W,59WO#YQpO,59^OOQO,59_,59_O#_QpOOO#_QpOOO#gQpOOOOOV1G.g1G.gO#oQpO'#CyO#tQpO1G.lOOQO1G.l1G.lO#|QpO1G.lOOQO1G.x1G.xO$UO`O'#DUO$ZOWO'#DUOOQO'#Co'#CoQOQpOOOOQO'#Cu'#CuO$`OtO'#CwO$qOrO'#CwOOQO,59e,59eOOQO-E6w-E6wOOQO7+$W7+$WO%SQpO7+$WO%[QpO7+$WOOOO'#Cp'#CpO%aOpO,59pOOOO'#Cq'#CqO%fOpO,59pOOOS'#Cz'#CzO%kOtO,59cOOQO,59c,59cOOOQ'#C{'#C{O%|OrO,59cO&_QpO<<GrOOQO<<Gr<<GrOOQO1G/[1G/[OOOS-E6x-E6xOOQO1G.}1G.}OOOQ-E6y-E6yOOQOAN=^AN=^",stateData:"&d~OvOS~OPROSQOVROWRO~OZTO[XO^VOaUOhWO~OR]OU^O~O[`O^aO~O[bO~O[cO~O[dO~ObeO~ObfO~ObgO~ORhO~O]kOwiO~O[lO~O_mO~OynOzoO~OysOztO~O[uO~O]wOwiO~O_yOwiO~OtzO~Os|O~OSQOV!OOW!OOr!OOy!QO~OSQOV!ROW!ROq!ROz!QO~O_!TOwiO~O]!UO~Oy!VO~Oz!VO~OSQOV!OOW!OOr!OOy!XO~OSQOV!ROW!ROq!ROz!XO~O]!ZO~O",goto:"#dyPPPPPzPPPP!WPPPPP!WPP!Z!^!a!d!dP!g!j!m!p!v#Q#WPPPPPPPP#^SROSS!Os!PT!Rt!SRYPRqeR{nR}oRZPRqfR[PRqgQSOR_SQj`SvjxRxlQ!PsR!W!PQ!StR!Y!SQpeRrf",nodeNames:`⚠ Text Content }} {{ Interpolation InterpolationContent Entity InvalidEntity Attribute BoundAttributeName [ Identifier ] ( ) ReferenceName # Is ExpressionAttributeValue AttributeInterpolation AttributeInterpolation EventName DirectiveName * StatementAttributeValue AttributeName AttributeValue`,maxTerm:42,nodeProps:[[`openedBy`,3,`{{`,15,`(`],[`closedBy`,4,`}}`,14,`)`],[`isolate`,-4,5,19,25,27,``]],skippedNodes:[0],repeatNodeCount:4,tokenData:"0r~RyOX#rXY$mYZ$mZ]#r]^$m^p#rpq$mqr#rrs%jst&Qtv#rvw&hwx)zxy*byz*xz{+`{}#r}!O+v!O!P-]!P!Q#r!Q![+v![!]+v!]!_#r!_!`-s!`!c#r!c!}+v!}#O.Z#O#P#r#P#Q.q#Q#R#r#R#S+v#S#T#r#T#o+v#o#p/X#p#q#r#q#r0Z#r%W#r%W;'S+v;'S;:j-V;:j;=`$g<%lO+vQ#wTUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rQ$ZSO#q#r#r;'S#r;'S;=`$g<%lO#rQ$jP;=`<%l#rR$t[UQvPOX#rXY$mYZ$mZ]#r]^$m^p#rpq$mq#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR%qTyPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR&XTaPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR&oXUQWPOp'[pq#rq!]'[!]!^#r!^#q'[#q#r(d#r;'S'[;'S;=`)t<%lO'[R'aXUQOp'[pq#rq!]'[!]!^'|!^#q'[#q#r(d#r;'S'[;'S;=`)t<%lO'[R(TTVPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR(gXOp'[pq#rq!]'[!]!^'|!^#q'[#q#r)S#r;'S'[;'S;=`)t<%lO'[P)VUOp)Sq!])S!]!^)i!^;'S)S;'S;=`)n<%lO)SP)nOVPP)qP;=`<%l)SR)wP;=`<%l'[R*RTzPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR*iT^PUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR+PT_PUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR+gThPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR+}b[PUQO}#r}!O+v!O!Q#r!Q![+v![!]+v!]!c#r!c!}+v!}#R#r#R#S+v#S#T#r#T#o+v#o#q#r#q#r$W#r%W#r%W;'S+v;'S;:j-V;:j;=`$g<%lO+vR-YP;=`<%l+vR-dTwPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR-zTUQbPO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR.bTZPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR.xT]PUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR/^VUQO#o#r#o#p/s#p#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR/zTSPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#r~0^TO#q#r#q#r0m#r;'S#r;'S;=`$g<%lO#r~0rOR~",tokenizers:[m,g,_,v,y,0,1],topRules:{Content:[0,2],Attribute:[1,9]},tokenPrec:0}),x=s.parser.configure({top:`SingleExpression`}),S=b.configure({props:[e({Text:t.content,Is:t.definitionOperator,AttributeName:t.attributeName,"AttributeValue ExpressionAttributeValue StatementAttributeValue":t.attributeValue,Entity:t.character,InvalidEntity:t.invalid,"BoundAttributeName/Identifier":t.attributeName,"EventName/Identifier":t.special(t.attributeName),"ReferenceName/Identifier":t.variableName,"DirectiveName/Identifier":t.keyword,"{{ }}":t.brace,"( )":t.paren,"[ ]":t.bracket,"# '*'":t.punctuation})]}),C={parser:x},w={parser:s.parser},T=S.configure({wrap:n((e,t)=>e.name==`InterpolationContent`?C:null)}),E=S.configure({wrap:n((e,t)=>e.name==`InterpolationContent`?C:e.name==`AttributeInterpolation`?e.node.parent?.name==`StatementAttributeValue`?w:C:null),top:`Attribute`}),D={parser:T},O={parser:E},k=c({selfClosingTags:!0});function A(e){return e.configure({wrap:n(M)},`angular`)}var j=A(k.language);function M(e,t){switch(e.name){case`Attribute`:return/^[*#(\[]|\{\{/.test(t.read(e.from,e.to))?O:null;case`Text`:return D}return null}function N(e={}){let t=k;if(e.base){if(e.base.language.name!=`html`||!(e.base.language instanceof i))throw RangeError(`The base option must be the result of calling html(...)`);t=e.base}return new r(t.language==k.language?j:A(t.language),[t.support,t.language.data.of({closeBrackets:{brackets:[`[`,`{`,`"`]},indentOnInput:/^\s*[\}\]]$/})])}export{N as angular};
1
+ import{N as e,P as t,U as n,o as r,r as i}from"./dist-SVPsM5Oi.js";import{n as a,r as o}from"./dist-tGfufz3b.js";import{r as s}from"./dist-CUqpST5w.js";import{html as c}from"./dist-CqqUC8Ce.js";var l=1,u=33,d=34,f=35,p=36,m=new a(e=>{let t=e.pos;for(;;){if(e.next==10){e.advance();break}else if(e.next==123&&e.peek(1)==123||e.next<0)break;e.advance()}e.pos>t&&e.acceptToken(l)});function h(e,t,n){return new a(r=>{let i=r.pos;for(;r.next!=e&&r.next>=0&&(n||r.next!=38&&(r.next!=123||r.peek(1)!=123));)r.advance();r.pos>i&&r.acceptToken(t)})}var g=h(39,u,!1),_=h(34,d,!1),v=h(39,f,!0),y=h(34,p,!0),b=o.deserialize({version:14,states:"(jOVOqOOOeQpOOOvO!bO'#CaOOOP'#Cx'#CxQVOqOOO!OQpO'#CfO!WQpO'#ClO!]QpO'#CrO!bQpO'#CsOOQO'#Cv'#CvQ!gQpOOQ!lQpOOQ!qQpOOOOOV,58{,58{O!vOpO,58{OOOP-E6v-E6vO!{QpO,59QO#TQpO,59QOOQO,59W,59WO#YQpO,59^OOQO,59_,59_O#_QpOOO#_QpOOO#gQpOOOOOV1G.g1G.gO#oQpO'#CyO#tQpO1G.lOOQO1G.l1G.lO#|QpO1G.lOOQO1G.x1G.xO$UO`O'#DUO$ZOWO'#DUOOQO'#Co'#CoQOQpOOOOQO'#Cu'#CuO$`OtO'#CwO$qOrO'#CwOOQO,59e,59eOOQO-E6w-E6wOOQO7+$W7+$WO%SQpO7+$WO%[QpO7+$WOOOO'#Cp'#CpO%aOpO,59pOOOO'#Cq'#CqO%fOpO,59pOOOS'#Cz'#CzO%kOtO,59cOOQO,59c,59cOOOQ'#C{'#C{O%|OrO,59cO&_QpO<<GrOOQO<<Gr<<GrOOQO1G/[1G/[OOOS-E6x-E6xOOQO1G.}1G.}OOOQ-E6y-E6yOOQOAN=^AN=^",stateData:"&d~OvOS~OPROSQOVROWRO~OZTO[XO^VOaUOhWO~OR]OU^O~O[`O^aO~O[bO~O[cO~O[dO~ObeO~ObfO~ObgO~ORhO~O]kOwiO~O[lO~O_mO~OynOzoO~OysOztO~O[uO~O]wOwiO~O_yOwiO~OtzO~Os|O~OSQOV!OOW!OOr!OOy!QO~OSQOV!ROW!ROq!ROz!QO~O_!TOwiO~O]!UO~Oy!VO~Oz!VO~OSQOV!OOW!OOr!OOy!XO~OSQOV!ROW!ROq!ROz!XO~O]!ZO~O",goto:"#dyPPPPPzPPPP!WPPPPP!WPP!Z!^!a!d!dP!g!j!m!p!v#Q#WPPPPPPPP#^SROSS!Os!PT!Rt!SRYPRqeR{nR}oRZPRqfR[PRqgQSOR_SQj`SvjxRxlQ!PsR!W!PQ!StR!Y!SQpeRrf",nodeNames:`⚠ Text Content }} {{ Interpolation InterpolationContent Entity InvalidEntity Attribute BoundAttributeName [ Identifier ] ( ) ReferenceName # Is ExpressionAttributeValue AttributeInterpolation AttributeInterpolation EventName DirectiveName * StatementAttributeValue AttributeName AttributeValue`,maxTerm:42,nodeProps:[[`openedBy`,3,`{{`,15,`(`],[`closedBy`,4,`}}`,14,`)`],[`isolate`,-4,5,19,25,27,``]],skippedNodes:[0],repeatNodeCount:4,tokenData:"0r~RyOX#rXY$mYZ$mZ]#r]^$m^p#rpq$mqr#rrs%jst&Qtv#rvw&hwx)zxy*byz*xz{+`{}#r}!O+v!O!P-]!P!Q#r!Q![+v![!]+v!]!_#r!_!`-s!`!c#r!c!}+v!}#O.Z#O#P#r#P#Q.q#Q#R#r#R#S+v#S#T#r#T#o+v#o#p/X#p#q#r#q#r0Z#r%W#r%W;'S+v;'S;:j-V;:j;=`$g<%lO+vQ#wTUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rQ$ZSO#q#r#r;'S#r;'S;=`$g<%lO#rQ$jP;=`<%l#rR$t[UQvPOX#rXY$mYZ$mZ]#r]^$m^p#rpq$mq#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR%qTyPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR&XTaPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR&oXUQWPOp'[pq#rq!]'[!]!^#r!^#q'[#q#r(d#r;'S'[;'S;=`)t<%lO'[R'aXUQOp'[pq#rq!]'[!]!^'|!^#q'[#q#r(d#r;'S'[;'S;=`)t<%lO'[R(TTVPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR(gXOp'[pq#rq!]'[!]!^'|!^#q'[#q#r)S#r;'S'[;'S;=`)t<%lO'[P)VUOp)Sq!])S!]!^)i!^;'S)S;'S;=`)n<%lO)SP)nOVPP)qP;=`<%l)SR)wP;=`<%l'[R*RTzPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR*iT^PUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR+PT_PUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR+gThPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR+}b[PUQO}#r}!O+v!O!Q#r!Q![+v![!]+v!]!c#r!c!}+v!}#R#r#R#S+v#S#T#r#T#o+v#o#q#r#q#r$W#r%W#r%W;'S+v;'S;:j-V;:j;=`$g<%lO+vR-YP;=`<%l+vR-dTwPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR-zTUQbPO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR.bTZPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR.xT]PUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR/^VUQO#o#r#o#p/s#p#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#rR/zTSPUQO#q#r#q#r$W#r;'S#r;'S;=`$g<%lO#r~0^TO#q#r#q#r0m#r;'S#r;'S;=`$g<%lO#r~0rOR~",tokenizers:[m,g,_,v,y,0,1],topRules:{Content:[0,2],Attribute:[1,9]},tokenPrec:0}),x=s.parser.configure({top:`SingleExpression`}),S=b.configure({props:[e({Text:t.content,Is:t.definitionOperator,AttributeName:t.attributeName,"AttributeValue ExpressionAttributeValue StatementAttributeValue":t.attributeValue,Entity:t.character,InvalidEntity:t.invalid,"BoundAttributeName/Identifier":t.attributeName,"EventName/Identifier":t.special(t.attributeName),"ReferenceName/Identifier":t.variableName,"DirectiveName/Identifier":t.keyword,"{{ }}":t.brace,"( )":t.paren,"[ ]":t.bracket,"# '*'":t.punctuation})]}),C={parser:x},w={parser:s.parser},T=S.configure({wrap:n((e,t)=>e.name==`InterpolationContent`?C:null)}),E=S.configure({wrap:n((e,t)=>e.name==`InterpolationContent`?C:e.name==`AttributeInterpolation`?e.node.parent?.name==`StatementAttributeValue`?w:C:null),top:`Attribute`}),D={parser:T},O={parser:E},k=c({selfClosingTags:!0});function A(e){return e.configure({wrap:n(M)},`angular`)}var j=A(k.language);function M(e,t){switch(e.name){case`Attribute`:return/^[*#(\[]|\{\{/.test(t.read(e.from,e.to))?O:null;case`Text`:return D}return null}function N(e={}){let t=k;if(e.base){if(e.base.language.name!=`html`||!(e.base.language instanceof i))throw RangeError(`The base option must be the result of calling html(...)`);t=e.base}return new r(t.language==k.language?j:A(t.language),[t.support,t.language.data.of({closeBrackets:{brackets:[`[`,`{`,`"`]},indentOnInput:/^\s*[\}\]]$/})])}export{N as angular};