signalwire-agents 0.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.
- package/CLAUDE.md +112 -0
- package/README.md +417 -0
- package/dist/AgentBase.d.ts +481 -0
- package/dist/AgentBase.js +1155 -0
- package/dist/AgentBase.js.map +1 -0
- package/dist/AgentServer.d.ts +60 -0
- package/dist/AgentServer.js +128 -0
- package/dist/AgentServer.js.map +1 -0
- package/dist/AuthHandler.d.ts +56 -0
- package/dist/AuthHandler.js +119 -0
- package/dist/AuthHandler.js.map +1 -0
- package/dist/ConfigLoader.d.ts +68 -0
- package/dist/ConfigLoader.js +150 -0
- package/dist/ConfigLoader.js.map +1 -0
- package/dist/ContextBuilder.d.ts +431 -0
- package/dist/ContextBuilder.js +751 -0
- package/dist/ContextBuilder.js.map +1 -0
- package/dist/DataMap.d.ts +179 -0
- package/dist/DataMap.js +321 -0
- package/dist/DataMap.js.map +1 -0
- package/dist/Logger.d.ts +91 -0
- package/dist/Logger.js +191 -0
- package/dist/Logger.js.map +1 -0
- package/dist/PomBuilder.d.ts +144 -0
- package/dist/PomBuilder.js +249 -0
- package/dist/PomBuilder.js.map +1 -0
- package/dist/PromptManager.d.ts +85 -0
- package/dist/PromptManager.js +108 -0
- package/dist/PromptManager.js.map +1 -0
- package/dist/SchemaUtils.d.ts +42 -0
- package/dist/SchemaUtils.js +148 -0
- package/dist/SchemaUtils.js.map +1 -0
- package/dist/ServerlessAdapter.d.ts +100 -0
- package/dist/ServerlessAdapter.js +166 -0
- package/dist/ServerlessAdapter.js.map +1 -0
- package/dist/SessionManager.d.ts +86 -0
- package/dist/SessionManager.js +155 -0
- package/dist/SessionManager.js.map +1 -0
- package/dist/SslConfig.d.ts +77 -0
- package/dist/SslConfig.js +100 -0
- package/dist/SslConfig.js.map +1 -0
- package/dist/SwaigFunction.d.ts +88 -0
- package/dist/SwaigFunction.js +125 -0
- package/dist/SwaigFunction.js.map +1 -0
- package/dist/SwaigFunctionResult.d.ts +442 -0
- package/dist/SwaigFunctionResult.js +735 -0
- package/dist/SwaigFunctionResult.js.map +1 -0
- package/dist/SwmlBuilder.d.ts +37 -0
- package/dist/SwmlBuilder.js +55 -0
- package/dist/SwmlBuilder.js.map +1 -0
- package/dist/cli/agent-loader.d.ts +22 -0
- package/dist/cli/agent-loader.js +106 -0
- package/dist/cli/agent-loader.js.map +1 -0
- package/dist/cli/mock-data.d.ts +37 -0
- package/dist/cli/mock-data.js +79 -0
- package/dist/cli/mock-data.js.map +1 -0
- package/dist/cli/swaig-test.d.ts +32 -0
- package/dist/cli/swaig-test.js +405 -0
- package/dist/cli/swaig-test.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +44 -0
- package/dist/index.js.map +1 -0
- package/dist/prefabs/ConciergeAgent.d.ts +64 -0
- package/dist/prefabs/ConciergeAgent.js +188 -0
- package/dist/prefabs/ConciergeAgent.js.map +1 -0
- package/dist/prefabs/FAQBotAgent.d.ts +68 -0
- package/dist/prefabs/FAQBotAgent.js +206 -0
- package/dist/prefabs/FAQBotAgent.js.map +1 -0
- package/dist/prefabs/InfoGathererAgent.d.ts +73 -0
- package/dist/prefabs/InfoGathererAgent.js +202 -0
- package/dist/prefabs/InfoGathererAgent.js.map +1 -0
- package/dist/prefabs/ReceptionistAgent.d.ts +65 -0
- package/dist/prefabs/ReceptionistAgent.js +205 -0
- package/dist/prefabs/ReceptionistAgent.js.map +1 -0
- package/dist/prefabs/SurveyAgent.d.ts +84 -0
- package/dist/prefabs/SurveyAgent.js +324 -0
- package/dist/prefabs/SurveyAgent.js.map +1 -0
- package/dist/prefabs/index.d.ts +13 -0
- package/dist/prefabs/index.js +14 -0
- package/dist/prefabs/index.js.map +1 -0
- package/dist/skills/SkillBase.d.ts +134 -0
- package/dist/skills/SkillBase.js +105 -0
- package/dist/skills/SkillBase.js.map +1 -0
- package/dist/skills/SkillManager.d.ts +83 -0
- package/dist/skills/SkillManager.js +159 -0
- package/dist/skills/SkillManager.js.map +1 -0
- package/dist/skills/SkillRegistry.d.ts +105 -0
- package/dist/skills/SkillRegistry.js +195 -0
- package/dist/skills/SkillRegistry.js.map +1 -0
- package/dist/skills/builtin/api_ninjas_trivia.d.ts +35 -0
- package/dist/skills/builtin/api_ninjas_trivia.js +161 -0
- package/dist/skills/builtin/api_ninjas_trivia.js.map +1 -0
- package/dist/skills/builtin/claude_skills.d.ts +34 -0
- package/dist/skills/builtin/claude_skills.js +156 -0
- package/dist/skills/builtin/claude_skills.js.map +1 -0
- package/dist/skills/builtin/custom_skills.d.ts +59 -0
- package/dist/skills/builtin/custom_skills.js +231 -0
- package/dist/skills/builtin/custom_skills.js.map +1 -0
- package/dist/skills/builtin/datasphere.d.ts +34 -0
- package/dist/skills/builtin/datasphere.js +163 -0
- package/dist/skills/builtin/datasphere.js.map +1 -0
- package/dist/skills/builtin/datasphere_serverless.d.ts +54 -0
- package/dist/skills/builtin/datasphere_serverless.js +155 -0
- package/dist/skills/builtin/datasphere_serverless.js.map +1 -0
- package/dist/skills/builtin/datetime.d.ts +31 -0
- package/dist/skills/builtin/datetime.js +95 -0
- package/dist/skills/builtin/datetime.js.map +1 -0
- package/dist/skills/builtin/google_maps.d.ts +33 -0
- package/dist/skills/builtin/google_maps.js +233 -0
- package/dist/skills/builtin/google_maps.js.map +1 -0
- package/dist/skills/builtin/index.d.ts +28 -0
- package/dist/skills/builtin/index.js +75 -0
- package/dist/skills/builtin/index.js.map +1 -0
- package/dist/skills/builtin/info_gatherer.d.ts +52 -0
- package/dist/skills/builtin/info_gatherer.js +237 -0
- package/dist/skills/builtin/info_gatherer.js.map +1 -0
- package/dist/skills/builtin/joke.d.ts +32 -0
- package/dist/skills/builtin/joke.js +142 -0
- package/dist/skills/builtin/joke.js.map +1 -0
- package/dist/skills/builtin/math.d.ts +33 -0
- package/dist/skills/builtin/math.js +120 -0
- package/dist/skills/builtin/math.js.map +1 -0
- package/dist/skills/builtin/mcp_gateway.d.ts +35 -0
- package/dist/skills/builtin/mcp_gateway.js +85 -0
- package/dist/skills/builtin/mcp_gateway.js.map +1 -0
- package/dist/skills/builtin/native_vector_search.d.ts +44 -0
- package/dist/skills/builtin/native_vector_search.js +248 -0
- package/dist/skills/builtin/native_vector_search.js.map +1 -0
- package/dist/skills/builtin/play_background_file.d.ts +34 -0
- package/dist/skills/builtin/play_background_file.js +135 -0
- package/dist/skills/builtin/play_background_file.js.map +1 -0
- package/dist/skills/builtin/spider.d.ts +34 -0
- package/dist/skills/builtin/spider.js +164 -0
- package/dist/skills/builtin/spider.js.map +1 -0
- package/dist/skills/builtin/swml_transfer.d.ts +44 -0
- package/dist/skills/builtin/swml_transfer.js +204 -0
- package/dist/skills/builtin/swml_transfer.js.map +1 -0
- package/dist/skills/builtin/weather_api.d.ts +34 -0
- package/dist/skills/builtin/weather_api.js +136 -0
- package/dist/skills/builtin/weather_api.js.map +1 -0
- package/dist/skills/builtin/web_search.d.ts +34 -0
- package/dist/skills/builtin/web_search.js +141 -0
- package/dist/skills/builtin/web_search.js.map +1 -0
- package/dist/skills/builtin/wikipedia_search.d.ts +34 -0
- package/dist/skills/builtin/wikipedia_search.js +166 -0
- package/dist/skills/builtin/wikipedia_search.js.map +1 -0
- package/dist/skills/index.d.ts +11 -0
- package/dist/skills/index.js +10 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/types.d.ts +85 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/docs/agent-guide.md +869 -0
- package/docs/api-reference.md +2419 -0
- package/docs/architecture.md +843 -0
- package/docs/cli-guide.md +579 -0
- package/docs/configuration.md +422 -0
- package/docs/contexts-guide.md +787 -0
- package/docs/datamap-guide.md +1141 -0
- package/docs/prefabs-guide.md +830 -0
- package/docs/security.md +436 -0
- package/docs/serverless-guide.md +513 -0
- package/docs/skills-guide.md +845 -0
- package/docs/swaig-reference.md +1627 -0
- package/examples/call-flow.ts +60 -0
- package/examples/contexts-steps.ts +62 -0
- package/examples/datamap-tools.ts +52 -0
- package/examples/dynamic-config.ts +51 -0
- package/examples/multi-agent.ts +67 -0
- package/examples/pom-prompt.ts +68 -0
- package/examples/simple-agent.ts +33 -0
- package/package.json +35 -0
- package/src/AgentBase.ts +1279 -0
- package/src/AgentServer.ts +149 -0
- package/src/AuthHandler.ts +141 -0
- package/src/ConfigLoader.ts +165 -0
- package/src/ContextBuilder.ts +796 -0
- package/src/DataMap.ts +365 -0
- package/src/Logger.ts +197 -0
- package/src/PomBuilder.ts +298 -0
- package/src/PromptManager.ts +133 -0
- package/src/SchemaUtils.ts +170 -0
- package/src/ServerlessAdapter.ts +218 -0
- package/src/SessionManager.ts +161 -0
- package/src/SslConfig.ts +118 -0
- package/src/SwaigFunction.ts +168 -0
- package/src/SwaigFunctionResult.ts +864 -0
- package/src/SwmlBuilder.ts +62 -0
- package/src/cli/agent-loader.ts +122 -0
- package/src/cli/mock-data.ts +110 -0
- package/src/cli/swaig-test.ts +440 -0
- package/src/index.ts +99 -0
- package/src/prefabs/ConciergeAgent.ts +251 -0
- package/src/prefabs/FAQBotAgent.ts +263 -0
- package/src/prefabs/InfoGathererAgent.ts +276 -0
- package/src/prefabs/ReceptionistAgent.ts +277 -0
- package/src/prefabs/SurveyAgent.ts +413 -0
- package/src/prefabs/index.ts +23 -0
- package/src/skills/SkillBase.ts +184 -0
- package/src/skills/SkillManager.ts +177 -0
- package/src/skills/SkillRegistry.ts +231 -0
- package/src/skills/builtin/api_ninjas_trivia.ts +230 -0
- package/src/skills/builtin/claude_skills.ts +239 -0
- package/src/skills/builtin/custom_skills.ts +326 -0
- package/src/skills/builtin/datasphere.ts +225 -0
- package/src/skills/builtin/datasphere_serverless.ts +194 -0
- package/src/skills/builtin/datetime.ts +115 -0
- package/src/skills/builtin/google_maps.ts +347 -0
- package/src/skills/builtin/index.ts +79 -0
- package/src/skills/builtin/info_gatherer.ts +331 -0
- package/src/skills/builtin/joke.ts +180 -0
- package/src/skills/builtin/math.ts +150 -0
- package/src/skills/builtin/mcp_gateway.ts +99 -0
- package/src/skills/builtin/native_vector_search.ts +327 -0
- package/src/skills/builtin/play_background_file.ts +189 -0
- package/src/skills/builtin/spider.ts +232 -0
- package/src/skills/builtin/swml_transfer.ts +285 -0
- package/src/skills/builtin/weather_api.ts +193 -0
- package/src/skills/builtin/web_search.ts +212 -0
- package/src/skills/builtin/wikipedia_search.ts +248 -0
- package/src/skills/index.ts +12 -0
- package/src/types.ts +99 -0
- package/tests/AgentBase.test.ts +693 -0
- package/tests/AgentServer.test.ts +102 -0
- package/tests/AuthHandler.test.ts +121 -0
- package/tests/ConfigLoader.test.ts +121 -0
- package/tests/DataMap.test.ts +286 -0
- package/tests/Logger.test.ts +218 -0
- package/tests/PomBuilder.test.ts +164 -0
- package/tests/ProxyDetection.test.ts +185 -0
- package/tests/SchemaUtils.test.ts +106 -0
- package/tests/ServerlessAdapter.test.ts +200 -0
- package/tests/SessionManager.test.ts +139 -0
- package/tests/SslConfig.test.ts +97 -0
- package/tests/SwaigFunctionResult.test.ts +298 -0
- package/tests/cli/mock-data.test.ts +94 -0
- package/tests/cli/swaig-test.test.ts +96 -0
- package/tests/prefabs/prefabs.test.ts +854 -0
- package/tests/setup.ts +6 -0
- package/tests/skills/SkillBase.test.ts +118 -0
- package/tests/skills/SkillManager.test.ts +164 -0
- package/tests/skills/SkillRegistry.test.ts +124 -0
- package/tests/skills/builtin.test.ts +924 -0
- package/tsconfig.json +18 -0
- package/vitest.config.ts +9 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PomBuilder - Prompt Object Model for structured prompt sections.
|
|
3
|
+
*
|
|
4
|
+
* Built-in replacement for the external signalwire-pom package.
|
|
5
|
+
* Sections have a title, body, bullets (optionally numbered),
|
|
6
|
+
* and nested subsections.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** Serializable representation of a POM section, used for JSON export. */
|
|
10
|
+
export interface PomSectionData {
|
|
11
|
+
/** Section heading text. */
|
|
12
|
+
title?: string;
|
|
13
|
+
/** Section body paragraph text. */
|
|
14
|
+
body?: string;
|
|
15
|
+
/** List of bullet point strings. */
|
|
16
|
+
bullets?: string[];
|
|
17
|
+
/** Whether subsections are numbered. */
|
|
18
|
+
numbered?: boolean;
|
|
19
|
+
/** Whether bullet points are rendered as a numbered list. */
|
|
20
|
+
numberedBullets?: boolean;
|
|
21
|
+
/** Nested child sections. */
|
|
22
|
+
subsections?: PomSectionData[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** A single section in a Prompt Object Model, with optional title, body, bullets, and nested subsections. */
|
|
26
|
+
export class PomSection {
|
|
27
|
+
/** Section heading text, or null if untitled. */
|
|
28
|
+
title: string | null;
|
|
29
|
+
/** Section body paragraph text. */
|
|
30
|
+
body: string;
|
|
31
|
+
/** List of bullet point strings. */
|
|
32
|
+
bullets: string[];
|
|
33
|
+
/** Nested child sections. */
|
|
34
|
+
subsections: PomSection[];
|
|
35
|
+
/** Whether this section is numbered when rendered; null means inherit from parent context. */
|
|
36
|
+
numbered: boolean | null;
|
|
37
|
+
/** Whether bullet points are rendered as a numbered list. */
|
|
38
|
+
numberedBullets: boolean;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Creates a new PomSection.
|
|
42
|
+
* @param opts - Optional section configuration.
|
|
43
|
+
*/
|
|
44
|
+
constructor(opts?: {
|
|
45
|
+
title?: string | null;
|
|
46
|
+
body?: string;
|
|
47
|
+
bullets?: string[];
|
|
48
|
+
numbered?: boolean | null;
|
|
49
|
+
numberedBullets?: boolean;
|
|
50
|
+
}) {
|
|
51
|
+
this.title = opts?.title ?? null;
|
|
52
|
+
this.body = opts?.body ?? '';
|
|
53
|
+
this.bullets = opts?.bullets ?? [];
|
|
54
|
+
this.subsections = [];
|
|
55
|
+
this.numbered = opts?.numbered ?? null;
|
|
56
|
+
this.numberedBullets = opts?.numberedBullets ?? false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Adds a nested subsection to this section.
|
|
61
|
+
* @param opts - Subsection configuration including title and optional body/bullets.
|
|
62
|
+
* @returns The newly created child PomSection.
|
|
63
|
+
*/
|
|
64
|
+
addSubsection(opts: {
|
|
65
|
+
title: string;
|
|
66
|
+
body?: string;
|
|
67
|
+
bullets?: string[];
|
|
68
|
+
numbered?: boolean;
|
|
69
|
+
numberedBullets?: boolean;
|
|
70
|
+
}): PomSection {
|
|
71
|
+
const sub = new PomSection({
|
|
72
|
+
title: opts.title,
|
|
73
|
+
body: opts.body,
|
|
74
|
+
bullets: opts.bullets,
|
|
75
|
+
numbered: opts.numbered ?? false,
|
|
76
|
+
numberedBullets: opts.numberedBullets ?? false,
|
|
77
|
+
});
|
|
78
|
+
this.subsections.push(sub);
|
|
79
|
+
return sub;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Serializes this section to a plain data object.
|
|
84
|
+
* @returns A PomSectionData representation of this section and its subsections.
|
|
85
|
+
*/
|
|
86
|
+
toDict(): PomSectionData {
|
|
87
|
+
const data: PomSectionData = {};
|
|
88
|
+
if (this.body) data.body = this.body;
|
|
89
|
+
if (this.bullets.length) data.bullets = this.bullets;
|
|
90
|
+
if (this.subsections.length) data.subsections = this.subsections.map((s) => s.toDict());
|
|
91
|
+
if (this.title !== null) data.title = this.title;
|
|
92
|
+
if (this.numbered) data.numbered = this.numbered;
|
|
93
|
+
if (this.numberedBullets) data.numberedBullets = this.numberedBullets;
|
|
94
|
+
return data;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Renders this section and its subsections as a Markdown string.
|
|
99
|
+
* @param level - The heading level to start at (default 2 for ##).
|
|
100
|
+
* @param sectionNumber - Hierarchical numbering prefix for numbered sections.
|
|
101
|
+
* @returns The rendered Markdown string.
|
|
102
|
+
*/
|
|
103
|
+
renderMarkdown(level = 2, sectionNumber: number[] = []): string {
|
|
104
|
+
const lines: string[] = [];
|
|
105
|
+
|
|
106
|
+
if (this.title !== null) {
|
|
107
|
+
const prefix = sectionNumber.length ? `${sectionNumber.join('.')}. ` : '';
|
|
108
|
+
lines.push(`${'#'.repeat(level)} ${prefix}${this.title}\n`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (this.body) {
|
|
112
|
+
lines.push(`${this.body}\n`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
for (let i = 0; i < this.bullets.length; i++) {
|
|
116
|
+
if (this.numberedBullets) {
|
|
117
|
+
lines.push(`${i + 1}. ${this.bullets[i]}`);
|
|
118
|
+
} else {
|
|
119
|
+
lines.push(`- ${this.bullets[i]}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (this.bullets.length) lines.push('');
|
|
123
|
+
|
|
124
|
+
const anyNumbered = this.subsections.some((s) => s.numbered);
|
|
125
|
+
for (let i = 0; i < this.subsections.length; i++) {
|
|
126
|
+
const sub = this.subsections[i];
|
|
127
|
+
let newNumber: number[];
|
|
128
|
+
let nextLevel: number;
|
|
129
|
+
if (this.title !== null || sectionNumber.length) {
|
|
130
|
+
if (anyNumbered && sub.numbered !== false) {
|
|
131
|
+
newNumber = [...sectionNumber, i + 1];
|
|
132
|
+
} else {
|
|
133
|
+
newNumber = sectionNumber;
|
|
134
|
+
}
|
|
135
|
+
nextLevel = level + 1;
|
|
136
|
+
} else {
|
|
137
|
+
newNumber = sectionNumber;
|
|
138
|
+
nextLevel = level;
|
|
139
|
+
}
|
|
140
|
+
lines.push(sub.renderMarkdown(nextLevel, newNumber));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return lines.join('\n');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Builds a structured prompt by composing named POM sections, with Markdown and dict export. */
|
|
148
|
+
export class PomBuilder {
|
|
149
|
+
private sections: PomSection[] = [];
|
|
150
|
+
private sectionMap: Map<string, PomSection> = new Map();
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Adds a new top-level section to the prompt.
|
|
154
|
+
* @param title - The section heading.
|
|
155
|
+
* @param opts - Optional body, bullets, numbering, and subsection configuration.
|
|
156
|
+
* @returns This builder for chaining.
|
|
157
|
+
*/
|
|
158
|
+
addSection(
|
|
159
|
+
title: string,
|
|
160
|
+
opts?: {
|
|
161
|
+
body?: string;
|
|
162
|
+
bullets?: string[];
|
|
163
|
+
numbered?: boolean;
|
|
164
|
+
numberedBullets?: boolean;
|
|
165
|
+
subsections?: { title: string; body?: string; bullets?: string[] }[];
|
|
166
|
+
},
|
|
167
|
+
): this {
|
|
168
|
+
const section = new PomSection({
|
|
169
|
+
title,
|
|
170
|
+
body: opts?.body,
|
|
171
|
+
bullets: opts?.bullets,
|
|
172
|
+
numbered: opts?.numbered,
|
|
173
|
+
numberedBullets: opts?.numberedBullets,
|
|
174
|
+
});
|
|
175
|
+
this.sections.push(section);
|
|
176
|
+
this.sectionMap.set(title, section);
|
|
177
|
+
|
|
178
|
+
if (opts?.subsections) {
|
|
179
|
+
for (const sub of opts.subsections) {
|
|
180
|
+
section.addSubsection({
|
|
181
|
+
title: sub.title,
|
|
182
|
+
body: sub.body,
|
|
183
|
+
bullets: sub.bullets,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return this;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Appends body text or bullets to an existing section, creating it if absent.
|
|
192
|
+
* @param title - The section heading to append to.
|
|
193
|
+
* @param opts - Body text and/or bullets to add.
|
|
194
|
+
* @returns This builder for chaining.
|
|
195
|
+
*/
|
|
196
|
+
addToSection(
|
|
197
|
+
title: string,
|
|
198
|
+
opts?: { body?: string; bullet?: string; bullets?: string[] },
|
|
199
|
+
): this {
|
|
200
|
+
if (!this.sectionMap.has(title)) {
|
|
201
|
+
this.addSection(title);
|
|
202
|
+
}
|
|
203
|
+
const section = this.sectionMap.get(title)!;
|
|
204
|
+
if (opts?.body) {
|
|
205
|
+
section.body = section.body ? `${section.body}\n\n${opts.body}` : opts.body;
|
|
206
|
+
}
|
|
207
|
+
if (opts?.bullet) section.bullets.push(opts.bullet);
|
|
208
|
+
if (opts?.bullets) section.bullets.push(...opts.bullets);
|
|
209
|
+
return this;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Adds a subsection under an existing parent section, creating the parent if absent.
|
|
214
|
+
* @param parentTitle - The heading of the parent section.
|
|
215
|
+
* @param title - The subsection heading.
|
|
216
|
+
* @param opts - Optional body text and bullets for the subsection.
|
|
217
|
+
* @returns This builder for chaining.
|
|
218
|
+
*/
|
|
219
|
+
addSubsection(
|
|
220
|
+
parentTitle: string,
|
|
221
|
+
title: string,
|
|
222
|
+
opts?: { body?: string; bullets?: string[] },
|
|
223
|
+
): this {
|
|
224
|
+
if (!this.sectionMap.has(parentTitle)) {
|
|
225
|
+
this.addSection(parentTitle);
|
|
226
|
+
}
|
|
227
|
+
this.sectionMap.get(parentTitle)!.addSubsection({
|
|
228
|
+
title,
|
|
229
|
+
body: opts?.body,
|
|
230
|
+
bullets: opts?.bullets,
|
|
231
|
+
});
|
|
232
|
+
return this;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Checks whether a top-level section with the given title exists.
|
|
237
|
+
* @param title - The section heading to look for.
|
|
238
|
+
* @returns True if the section exists.
|
|
239
|
+
*/
|
|
240
|
+
hasSection(title: string): boolean {
|
|
241
|
+
return this.sectionMap.has(title);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Returns a top-level section by title.
|
|
246
|
+
* @param title - The section heading to retrieve.
|
|
247
|
+
* @returns The matching PomSection, or undefined if not found.
|
|
248
|
+
*/
|
|
249
|
+
getSection(title: string): PomSection | undefined {
|
|
250
|
+
return this.sectionMap.get(title);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Recursively searches all sections and subsections for one matching the given title.
|
|
255
|
+
* @param title - The section heading to search for.
|
|
256
|
+
* @returns The matching PomSection, or undefined if not found.
|
|
257
|
+
*/
|
|
258
|
+
findSection(title: string): PomSection | undefined {
|
|
259
|
+
const recurse = (sections: PomSection[]): PomSection | undefined => {
|
|
260
|
+
for (const s of sections) {
|
|
261
|
+
if (s.title === title) return s;
|
|
262
|
+
const found = recurse(s.subsections);
|
|
263
|
+
if (found) return found;
|
|
264
|
+
}
|
|
265
|
+
return undefined;
|
|
266
|
+
};
|
|
267
|
+
return recurse(this.sections);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Serializes all sections to an array of plain data objects.
|
|
272
|
+
* @returns An array of PomSectionData representing all top-level sections.
|
|
273
|
+
*/
|
|
274
|
+
toDict(): PomSectionData[] {
|
|
275
|
+
return this.sections.map((s) => s.toDict());
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Renders all sections as a combined Markdown string.
|
|
280
|
+
* @returns The complete rendered Markdown prompt text.
|
|
281
|
+
*/
|
|
282
|
+
renderMarkdown(): string {
|
|
283
|
+
const anyNumbered = this.sections.some((s) => s.numbered);
|
|
284
|
+
const parts: string[] = [];
|
|
285
|
+
let counter = 0;
|
|
286
|
+
for (const section of this.sections) {
|
|
287
|
+
let sectionNumber: number[] = [];
|
|
288
|
+
if (section.title !== null) {
|
|
289
|
+
counter++;
|
|
290
|
+
if (anyNumbered && section.numbered !== false) {
|
|
291
|
+
sectionNumber = [counter];
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
parts.push(section.renderMarkdown(2, sectionNumber));
|
|
295
|
+
}
|
|
296
|
+
return parts.join('\n');
|
|
297
|
+
}
|
|
298
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PromptManager - Manages agent prompts with POM support.
|
|
3
|
+
*
|
|
4
|
+
* Holds either raw prompt text or a PomBuilder for structured prompts.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { PomBuilder } from './PomBuilder.js';
|
|
8
|
+
|
|
9
|
+
/** Manages agent prompt text, supporting both raw text and structured POM-based prompts. */
|
|
10
|
+
export class PromptManager {
|
|
11
|
+
private rawText: string | null = null;
|
|
12
|
+
private postPrompt: string | null = null;
|
|
13
|
+
private pom: PomBuilder | null = null;
|
|
14
|
+
private usePom: boolean;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Creates a new PromptManager.
|
|
18
|
+
* @param usePom - Whether to use structured POM sections (default true).
|
|
19
|
+
*/
|
|
20
|
+
constructor(usePom = true) {
|
|
21
|
+
this.usePom = usePom;
|
|
22
|
+
if (usePom) {
|
|
23
|
+
this.pom = new PomBuilder();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Sets the raw prompt text, bypassing POM rendering.
|
|
29
|
+
* @param text - The raw prompt string.
|
|
30
|
+
*/
|
|
31
|
+
setPromptText(text: string): void {
|
|
32
|
+
this.rawText = text;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Sets the post-prompt text appended after the main prompt.
|
|
37
|
+
* @param text - The post-prompt string.
|
|
38
|
+
*/
|
|
39
|
+
setPostPrompt(text: string): void {
|
|
40
|
+
this.postPrompt = text;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Adds a POM section to the prompt, initializing the PomBuilder if needed.
|
|
45
|
+
* @param title - The section heading.
|
|
46
|
+
* @param opts - Optional body, bullets, numbering, and subsection configuration.
|
|
47
|
+
*/
|
|
48
|
+
addSection(
|
|
49
|
+
title: string,
|
|
50
|
+
opts?: {
|
|
51
|
+
body?: string;
|
|
52
|
+
bullets?: string[];
|
|
53
|
+
numbered?: boolean;
|
|
54
|
+
numberedBullets?: boolean;
|
|
55
|
+
subsections?: { title: string; body?: string; bullets?: string[] }[];
|
|
56
|
+
},
|
|
57
|
+
): void {
|
|
58
|
+
if (!this.pom) {
|
|
59
|
+
this.pom = new PomBuilder();
|
|
60
|
+
this.usePom = true;
|
|
61
|
+
}
|
|
62
|
+
this.pom.addSection(title, opts);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Appends body text or bullets to an existing POM section, creating it if absent.
|
|
67
|
+
* @param title - The section heading to append to.
|
|
68
|
+
* @param opts - Body text and/or bullets to add.
|
|
69
|
+
*/
|
|
70
|
+
addToSection(
|
|
71
|
+
title: string,
|
|
72
|
+
opts?: { body?: string; bullet?: string; bullets?: string[] },
|
|
73
|
+
): void {
|
|
74
|
+
if (!this.pom) {
|
|
75
|
+
this.pom = new PomBuilder();
|
|
76
|
+
this.usePom = true;
|
|
77
|
+
}
|
|
78
|
+
this.pom.addToSection(title, opts);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Adds a subsection under a parent POM section, creating the parent if absent.
|
|
83
|
+
* @param parentTitle - The heading of the parent section.
|
|
84
|
+
* @param title - The subsection heading.
|
|
85
|
+
* @param opts - Optional body text and bullets for the subsection.
|
|
86
|
+
*/
|
|
87
|
+
addSubsection(
|
|
88
|
+
parentTitle: string,
|
|
89
|
+
title: string,
|
|
90
|
+
opts?: { body?: string; bullets?: string[] },
|
|
91
|
+
): void {
|
|
92
|
+
if (!this.pom) {
|
|
93
|
+
this.pom = new PomBuilder();
|
|
94
|
+
this.usePom = true;
|
|
95
|
+
}
|
|
96
|
+
this.pom.addSubsection(parentTitle, title, opts);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Checks whether a POM section with the given title exists.
|
|
101
|
+
* @param title - The section heading to look for.
|
|
102
|
+
* @returns True if the section exists.
|
|
103
|
+
*/
|
|
104
|
+
hasSection(title: string): boolean {
|
|
105
|
+
return this.pom?.hasSection(title) ?? false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Returns the fully rendered prompt text, either raw text or POM-rendered Markdown.
|
|
110
|
+
* @returns The prompt string, or empty string if nothing is set.
|
|
111
|
+
*/
|
|
112
|
+
getPrompt(): string {
|
|
113
|
+
if (this.rawText !== null) return this.rawText;
|
|
114
|
+
if (this.pom) return this.pom.renderMarkdown();
|
|
115
|
+
return '';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Returns the post-prompt text.
|
|
120
|
+
* @returns The post-prompt string, or null if not set.
|
|
121
|
+
*/
|
|
122
|
+
getPostPrompt(): string | null {
|
|
123
|
+
return this.postPrompt;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Returns the underlying PomBuilder instance, if POM mode is active.
|
|
128
|
+
* @returns The PomBuilder, or null if POM is not in use.
|
|
129
|
+
*/
|
|
130
|
+
getPomBuilder(): PomBuilder | null {
|
|
131
|
+
return this.pom;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SchemaUtils - JSON Schema validation for SWML documents.
|
|
3
|
+
*
|
|
4
|
+
* Provides basic structural validation of rendered SWML.
|
|
5
|
+
* Set SWML_SKIP_SCHEMA_VALIDATION=true to disable.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** Result of validating a SWML document. */
|
|
9
|
+
export interface ValidationResult {
|
|
10
|
+
/** Whether the document passed all validation checks. */
|
|
11
|
+
valid: boolean;
|
|
12
|
+
/** List of human-readable error messages; empty when valid. */
|
|
13
|
+
errors: string[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Basic SWML structure expectations
|
|
17
|
+
const REQUIRED_TOP_LEVEL = ['version', 'sections'];
|
|
18
|
+
const VALID_VERSIONS = ['1.0.0'];
|
|
19
|
+
|
|
20
|
+
/** Validates SWML documents against structural rules with an LRU-style result cache. */
|
|
21
|
+
export class SchemaUtils {
|
|
22
|
+
private skipValidation: boolean;
|
|
23
|
+
private cache: Map<string, ValidationResult> = new Map();
|
|
24
|
+
private maxCacheSize: number;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Create a SchemaUtils instance.
|
|
28
|
+
* @param opts - Optional settings for skipping validation or limiting cache size.
|
|
29
|
+
*/
|
|
30
|
+
constructor(opts?: { skipValidation?: boolean; maxCacheSize?: number }) {
|
|
31
|
+
this.skipValidation = opts?.skipValidation ?? (process.env['SWML_SKIP_SCHEMA_VALIDATION'] === 'true');
|
|
32
|
+
this.maxCacheSize = opts?.maxCacheSize ?? 100;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Validate a SWML document against structural rules.
|
|
37
|
+
* @param swml - The SWML document as a JSON string or parsed object.
|
|
38
|
+
* @returns The validation result indicating success or a list of errors.
|
|
39
|
+
*/
|
|
40
|
+
validate(swml: string | Record<string, unknown>): ValidationResult {
|
|
41
|
+
if (this.skipValidation) {
|
|
42
|
+
return { valid: true, errors: [] };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const swmlStr = typeof swml === 'string' ? swml : JSON.stringify(swml);
|
|
46
|
+
|
|
47
|
+
// Check cache
|
|
48
|
+
const cached = this.cache.get(swmlStr);
|
|
49
|
+
if (cached) return cached;
|
|
50
|
+
|
|
51
|
+
const errors: string[] = [];
|
|
52
|
+
let doc: Record<string, unknown>;
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
doc = typeof swml === 'string' ? JSON.parse(swml) : swml;
|
|
56
|
+
} catch {
|
|
57
|
+
const result: ValidationResult = { valid: false, errors: ['Invalid JSON'] };
|
|
58
|
+
this.cacheResult(swmlStr, result);
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Check top-level required keys
|
|
63
|
+
for (const key of REQUIRED_TOP_LEVEL) {
|
|
64
|
+
if (!(key in doc)) {
|
|
65
|
+
errors.push(`Missing required top-level key: ${key}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Check version
|
|
70
|
+
if (doc['version'] && !VALID_VERSIONS.includes(doc['version'] as string)) {
|
|
71
|
+
errors.push(`Invalid version: ${doc['version']}. Expected one of: ${VALID_VERSIONS.join(', ')}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Check sections
|
|
75
|
+
if (doc['sections']) {
|
|
76
|
+
if (typeof doc['sections'] !== 'object' || Array.isArray(doc['sections'])) {
|
|
77
|
+
errors.push('sections must be an object');
|
|
78
|
+
} else {
|
|
79
|
+
const sections = doc['sections'] as Record<string, unknown>;
|
|
80
|
+
if (!('main' in sections)) {
|
|
81
|
+
errors.push('sections must contain a "main" section');
|
|
82
|
+
}
|
|
83
|
+
// Validate each section is an array
|
|
84
|
+
for (const [name, section] of Object.entries(sections)) {
|
|
85
|
+
if (!Array.isArray(section)) {
|
|
86
|
+
errors.push(`Section "${name}" must be an array`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Validate AI verb structure if present
|
|
93
|
+
if (doc['sections'] && typeof doc['sections'] === 'object') {
|
|
94
|
+
const sections = doc['sections'] as Record<string, unknown[]>;
|
|
95
|
+
const main = sections['main'];
|
|
96
|
+
if (Array.isArray(main)) {
|
|
97
|
+
for (const verb of main) {
|
|
98
|
+
if (typeof verb === 'object' && verb !== null && 'ai' in verb) {
|
|
99
|
+
this.validateAiVerb((verb as Record<string, unknown>)['ai'] as Record<string, unknown>, errors);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const result: ValidationResult = {
|
|
106
|
+
valid: errors.length === 0,
|
|
107
|
+
errors,
|
|
108
|
+
};
|
|
109
|
+
this.cacheResult(swmlStr, result);
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private validateAiVerb(ai: Record<string, unknown>, errors: string[]): void {
|
|
114
|
+
if (!ai) return;
|
|
115
|
+
|
|
116
|
+
// Prompt must have text
|
|
117
|
+
if (ai['prompt']) {
|
|
118
|
+
const prompt = ai['prompt'] as Record<string, unknown>;
|
|
119
|
+
if (typeof prompt === 'object' && !prompt['text']) {
|
|
120
|
+
errors.push('AI prompt must have a "text" field');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// SWAIG validation
|
|
125
|
+
if (ai['SWAIG']) {
|
|
126
|
+
const swaig = ai['SWAIG'] as Record<string, unknown>;
|
|
127
|
+
if (swaig['functions'] && !Array.isArray(swaig['functions'])) {
|
|
128
|
+
errors.push('SWAIG functions must be an array');
|
|
129
|
+
}
|
|
130
|
+
if (Array.isArray(swaig['functions'])) {
|
|
131
|
+
for (let i = 0; i < (swaig['functions'] as unknown[]).length; i++) {
|
|
132
|
+
const fn = (swaig['functions'] as Record<string, unknown>[])[i];
|
|
133
|
+
if (!fn['function']) {
|
|
134
|
+
errors.push(`SWAIG function at index ${i} missing "function" name`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Post-prompt validation
|
|
141
|
+
if (ai['post_prompt']) {
|
|
142
|
+
const pp = ai['post_prompt'] as Record<string, unknown>;
|
|
143
|
+
if (typeof pp === 'object' && !pp['text']) {
|
|
144
|
+
errors.push('AI post_prompt must have a "text" field');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Clear the validation cache */
|
|
150
|
+
clearCache(): void {
|
|
151
|
+
this.cache.clear();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get the number of cached validation results.
|
|
156
|
+
* @returns The current cache entry count.
|
|
157
|
+
*/
|
|
158
|
+
getCacheSize(): number {
|
|
159
|
+
return this.cache.size;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private cacheResult(key: string, result: ValidationResult): void {
|
|
163
|
+
if (this.cache.size >= this.maxCacheSize) {
|
|
164
|
+
// Remove oldest entry
|
|
165
|
+
const firstKey = this.cache.keys().next().value;
|
|
166
|
+
if (firstKey) this.cache.delete(firstKey);
|
|
167
|
+
}
|
|
168
|
+
this.cache.set(key, result);
|
|
169
|
+
}
|
|
170
|
+
}
|