@zihanw/pi-forge 0.1.0 → 0.2.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/README.md +303 -184
- package/README.zh-CN.md +405 -0
- package/examples/default-prompt-stack.json +44 -0
- package/package.json +2 -1
- package/src/compiler.ts +163 -33
- package/src/index.ts +761 -52
- package/src/loader.ts +83 -1
- package/src/sillytavern-importer.ts +83 -34
- package/src/types.ts +41 -2
- package/src/web-editor.ts +1491 -0
package/src/loader.ts
CHANGED
|
@@ -6,6 +6,10 @@ import type {
|
|
|
6
6
|
PromptStackDiagnostic,
|
|
7
7
|
PromptStackItem,
|
|
8
8
|
PromptStackRole,
|
|
9
|
+
PromptStateConfig,
|
|
10
|
+
PromptStateDefinition,
|
|
11
|
+
PromptStateScope,
|
|
12
|
+
PromptStateValue,
|
|
9
13
|
} from "./types.ts";
|
|
10
14
|
import { SUPPORTED_SLOTS } from "./types.ts";
|
|
11
15
|
|
|
@@ -26,7 +30,30 @@ export function loadPromptStacks(cwd: string): LoadedPromptStack[] {
|
|
|
26
30
|
return [];
|
|
27
31
|
}
|
|
28
32
|
|
|
29
|
-
|
|
33
|
+
const loaded = entries.sort().map((name) => loadPromptStackFile(join(dir, name)));
|
|
34
|
+
annotateDuplicateStackIds(loaded);
|
|
35
|
+
return loaded;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function annotateDuplicateStackIds(stacks: LoadedPromptStack[]): void {
|
|
39
|
+
const byId = new Map<string, LoadedPromptStack[]>();
|
|
40
|
+
for (const loaded of stacks) {
|
|
41
|
+
const id = loaded.stack.id;
|
|
42
|
+
const matches = byId.get(id) ?? [];
|
|
43
|
+
matches.push(loaded);
|
|
44
|
+
byId.set(id, matches);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
for (const [id, matches] of byId) {
|
|
48
|
+
if (matches.length <= 1) continue;
|
|
49
|
+
const files = matches.map((loaded) => basename(loaded.filePath)).join(", ");
|
|
50
|
+
for (const loaded of matches) {
|
|
51
|
+
loaded.diagnostics.push({
|
|
52
|
+
level: "error",
|
|
53
|
+
message: `Duplicate stack id: ${id} appears in multiple files (${files}).`,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
30
57
|
}
|
|
31
58
|
|
|
32
59
|
export function chooseDefaultStack(
|
|
@@ -123,6 +150,7 @@ function normalizeStack(raw: unknown, filePath: string, diagnostics: PromptStack
|
|
|
123
150
|
defaults: isPlainObject(obj.defaults) ? (obj.defaults as PromptStack["defaults"]) : undefined,
|
|
124
151
|
context: isPlainObject(obj.context) ? (obj.context as PromptStack["context"]) : undefined,
|
|
125
152
|
variables: normalizeStringRecord(obj.variables),
|
|
153
|
+
state: normalizeStateConfig(obj.state, diagnostics),
|
|
126
154
|
items,
|
|
127
155
|
import: isPlainObject(obj.import) ? (obj.import as Record<string, unknown>) : undefined,
|
|
128
156
|
};
|
|
@@ -225,3 +253,57 @@ function normalizeStringRecord(value: unknown): Record<string, string> | undefin
|
|
|
225
253
|
}
|
|
226
254
|
return result;
|
|
227
255
|
}
|
|
256
|
+
|
|
257
|
+
function normalizeStateConfig(value: unknown, diagnostics: PromptStackDiagnostic[]): PromptStateConfig | undefined {
|
|
258
|
+
if (value === undefined) return undefined;
|
|
259
|
+
if (!isPlainObject(value)) {
|
|
260
|
+
diagnostics.push({ level: "warning", message: "state must be an object when provided." });
|
|
261
|
+
return undefined;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const definitionsRaw = value.definitions;
|
|
265
|
+
const definitions: Record<string, PromptStateDefinition> = {};
|
|
266
|
+
|
|
267
|
+
if (definitionsRaw !== undefined && !isPlainObject(definitionsRaw)) {
|
|
268
|
+
diagnostics.push({ level: "warning", message: "state.definitions must be an object when provided." });
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (isPlainObject(definitionsRaw)) {
|
|
272
|
+
for (const [name, rawDefinition] of Object.entries(definitionsRaw)) {
|
|
273
|
+
if (!isPlainObject(rawDefinition)) {
|
|
274
|
+
diagnostics.push({ level: "warning", message: `state definition for ${name} must be an object.` });
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const definition: PromptStateDefinition = {};
|
|
279
|
+
if (typeof rawDefinition.type === "string") definition.type = rawDefinition.type;
|
|
280
|
+
if (isStateScope(rawDefinition.scope)) definition.scope = rawDefinition.scope;
|
|
281
|
+
if (typeof rawDefinition.description === "string") definition.description = rawDefinition.description;
|
|
282
|
+
if (typeof rawDefinition.agentWritable === "boolean") definition.agentWritable = rawDefinition.agentWritable;
|
|
283
|
+
if (typeof rawDefinition.userWritable === "boolean") definition.userWritable = rawDefinition.userWritable;
|
|
284
|
+
if (isPromptStateValue(rawDefinition.default)) definition.default = rawDefinition.default;
|
|
285
|
+
else if (rawDefinition.default !== undefined) {
|
|
286
|
+
diagnostics.push({ level: "warning", message: `state definition default for ${name} is not a JSON-compatible value.` });
|
|
287
|
+
}
|
|
288
|
+
definitions[name] = definition;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return {
|
|
293
|
+
schemaVersion: value.schemaVersion === 1 ? 1 : undefined,
|
|
294
|
+
definitions: Object.keys(definitions).length > 0 ? definitions : undefined,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function isStateScope(value: unknown): value is PromptStateScope {
|
|
299
|
+
return value === "static" || value === "session" || value === "turn";
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function isPromptStateValue(value: unknown): value is PromptStateValue {
|
|
303
|
+
if (value === null) return true;
|
|
304
|
+
const type = typeof value;
|
|
305
|
+
if (type === "string" || type === "number" || type === "boolean") return Number.isFinite(value as number) || type !== "number";
|
|
306
|
+
if (Array.isArray(value)) return value.every(isPromptStateValue);
|
|
307
|
+
if (!isPlainObject(value)) return false;
|
|
308
|
+
return Object.values(value).every(isPromptStateValue);
|
|
309
|
+
}
|
|
@@ -55,14 +55,33 @@ const MARKER_SKIP = new Set([
|
|
|
55
55
|
// ── ST macros that pi-forge handles natively ───────────────────────────────
|
|
56
56
|
|
|
57
57
|
const NATIVE_MACROS = new Set([
|
|
58
|
-
"char",
|
|
59
|
-
"user",
|
|
60
|
-
"
|
|
58
|
+
"char", // → static variable
|
|
59
|
+
"user", // → static variable
|
|
60
|
+
"lastusermessage", // → handled by pi-forge runtime
|
|
61
61
|
"cwd",
|
|
62
62
|
"date",
|
|
63
63
|
"time",
|
|
64
64
|
]);
|
|
65
65
|
|
|
66
|
+
const MACRO_DISPLAY_NAMES: Record<string, string> = {
|
|
67
|
+
lastusermessage: "lastUserMessage",
|
|
68
|
+
groupnotmuted: "groupNotMuted",
|
|
69
|
+
notchar: "notChar",
|
|
70
|
+
charifnotgroup: "charIfNotGroup",
|
|
71
|
+
mesexamples: "mesExamples",
|
|
72
|
+
mesexamplesraw: "mesExamplesRaw",
|
|
73
|
+
charprompt: "charPrompt",
|
|
74
|
+
charinstruction: "charInstruction",
|
|
75
|
+
charversion: "charVersion",
|
|
76
|
+
charfirstmessage: "charFirstMessage",
|
|
77
|
+
wibefore: "wiBefore",
|
|
78
|
+
wiafter: "wiAfter",
|
|
79
|
+
lorebefore: "loreBefore",
|
|
80
|
+
loreafter: "loreAfter",
|
|
81
|
+
anchorbefore: "anchorBefore",
|
|
82
|
+
anchorafter: "anchorAfter",
|
|
83
|
+
};
|
|
84
|
+
|
|
66
85
|
// ── ST macros that need manual migration ───────────────────────────────────
|
|
67
86
|
|
|
68
87
|
const MACRO_NEEDS_MIGRATION: Record<string, string> = {
|
|
@@ -78,26 +97,26 @@ const MACRO_NEEDS_MIGRATION: Record<string, string> = {
|
|
|
78
97
|
"original": "SillyTavern-specific — no direct equivalent; set as session variable if needed",
|
|
79
98
|
"outlet": "SillyTavern extension outlet — no pi-forge equivalent",
|
|
80
99
|
"group": "SillyTavern group chat — no pi-forge equivalent",
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"
|
|
100
|
+
"groupnotmuted": "SillyTavern group chat — no pi-forge equivalent",
|
|
101
|
+
"notchar": "SillyTavern group chat — no pi-forge equivalent",
|
|
102
|
+
"charifnotgroup": "SillyTavern group chat — no pi-forge equivalent",
|
|
84
103
|
"description": "ST character card field — set as session variable or static variable if needed",
|
|
85
104
|
"personality": "ST character card field — set as session variable or static variable if needed",
|
|
86
105
|
"scenario": "ST character card field — set as session variable or static variable if needed",
|
|
87
106
|
"persona": "ST persona field — set as session variable or static variable if needed",
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
"
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
107
|
+
"mesexamples": "ST character dialogue examples — add inline or as context file if needed",
|
|
108
|
+
"mesexamplesraw": "ST character dialogue examples — add inline or as context file if needed",
|
|
109
|
+
"charprompt": "ST character prompt override — merge into system prompt blocks if needed",
|
|
110
|
+
"charinstruction": "ST character instruction override — merge into post-history blocks if needed",
|
|
111
|
+
"charversion": "ST character version — set as static variable if needed",
|
|
112
|
+
"charfirstmessage": "ST character first message — add as static variable if needed",
|
|
94
113
|
"system": "ST context template system prompt — handled by pi-forge system prompt replacement",
|
|
95
|
-
"
|
|
96
|
-
"
|
|
97
|
-
"
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
"
|
|
114
|
+
"wibefore": "ST world info — no pi-forge equivalent; merge relevant lore into static blocks",
|
|
115
|
+
"wiafter": "ST world info — no pi-forge equivalent; merge relevant lore into static blocks",
|
|
116
|
+
"lorebefore": "ST world info — no pi-forge equivalent; merge relevant lore into static blocks",
|
|
117
|
+
"loreafter": "ST world info — no pi-forge equivalent; merge relevant lore into static blocks",
|
|
118
|
+
"anchorbefore": "ST extension injection point — no pi-forge equivalent",
|
|
119
|
+
"anchorafter": "ST extension injection point — no pi-forge equivalent",
|
|
101
120
|
};
|
|
102
121
|
|
|
103
122
|
// ── Macros we can strip entirely (produce no output) ───────────────────────
|
|
@@ -118,6 +137,12 @@ export interface SillyTavernImportError {
|
|
|
118
137
|
|
|
119
138
|
export type SillyTavernImportOutcome = SillyTavernImportResult | SillyTavernImportError;
|
|
120
139
|
|
|
140
|
+
export interface SillyTavernConvertOptions {
|
|
141
|
+
sourceName?: string;
|
|
142
|
+
sourcePath?: string;
|
|
143
|
+
characterId?: number;
|
|
144
|
+
}
|
|
145
|
+
|
|
121
146
|
// ── Importer ───────────────────────────────────────────────────────────────
|
|
122
147
|
|
|
123
148
|
export function importSillyTavernPreset(
|
|
@@ -135,7 +160,25 @@ export function importSillyTavernPreset(
|
|
|
135
160
|
return { error: "Preset root must be a JSON object." };
|
|
136
161
|
}
|
|
137
162
|
|
|
163
|
+
return convertSillyTavernPreset(raw, { sourceName: filePath, sourcePath: filePath, characterId });
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function convertSillyTavernPreset(
|
|
167
|
+
raw: unknown,
|
|
168
|
+
options: SillyTavernConvertOptions = {},
|
|
169
|
+
): SillyTavernImportOutcome {
|
|
170
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
171
|
+
return { error: "Preset root must be a JSON object." };
|
|
172
|
+
}
|
|
173
|
+
|
|
138
174
|
const preset = raw as StPreset;
|
|
175
|
+
const fallbackName = typeof preset.preset_name === "string" && preset.preset_name.trim()
|
|
176
|
+
? preset.preset_name
|
|
177
|
+
: typeof preset.name === "string" && preset.name.trim()
|
|
178
|
+
? preset.name
|
|
179
|
+
: "imported.json";
|
|
180
|
+
const sourceName = options.sourceName ?? fallbackName;
|
|
181
|
+
const sourcePath = options.sourcePath ?? sourceName;
|
|
139
182
|
const allPrompts = preset.prompts ?? [];
|
|
140
183
|
const promptOrder = preset.prompt_order ?? [];
|
|
141
184
|
|
|
@@ -144,14 +187,14 @@ export function importSillyTavernPreset(
|
|
|
144
187
|
}
|
|
145
188
|
|
|
146
189
|
// Select character_id
|
|
147
|
-
const selectedEntry = selectCharacterEntry(promptOrder, characterId);
|
|
190
|
+
const selectedEntry = selectCharacterEntry(promptOrder, options.characterId);
|
|
148
191
|
if (!selectedEntry) {
|
|
149
192
|
const ids = promptOrder.map((entry) => entry.character_id);
|
|
150
193
|
if (ids.length === 0) {
|
|
151
|
-
return { error: "Preset has no prompt_order entries.
|
|
194
|
+
return { error: "Preset has no prompt_order entries. Choose a character_id and retry." };
|
|
152
195
|
}
|
|
153
196
|
return {
|
|
154
|
-
error: `Multiple character configs found: [${ids.join(", ")}].
|
|
197
|
+
error: `Multiple character configs found: [${ids.join(", ")}]. Choose a character_id and retry.`,
|
|
155
198
|
};
|
|
156
199
|
}
|
|
157
200
|
|
|
@@ -219,8 +262,8 @@ export function importSillyTavernPreset(
|
|
|
219
262
|
continue;
|
|
220
263
|
}
|
|
221
264
|
|
|
222
|
-
// Detect {{lastUserMessage}} usage
|
|
223
|
-
if (
|
|
265
|
+
// Detect {{lastUserMessage}} usage, case-insensitively.
|
|
266
|
+
if (/\{\{\s*lastUserMessage\b/i.test(content)) {
|
|
224
267
|
usesLastUserMessage = true;
|
|
225
268
|
}
|
|
226
269
|
|
|
@@ -258,7 +301,7 @@ export function importSillyTavernPreset(
|
|
|
258
301
|
}
|
|
259
302
|
|
|
260
303
|
// Build stack name from file
|
|
261
|
-
const fileName = basename(
|
|
304
|
+
const fileName = basename(sourceName).replace(/\.json$/i, "") || "imported";
|
|
262
305
|
const styleName = preset.names_behavior === 1 ? "names" : preset.names_behavior === 2 ? "nonames" : "default";
|
|
263
306
|
const stackId = fileName.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").toLowerCase() || "imported";
|
|
264
307
|
|
|
@@ -285,7 +328,8 @@ export function importSillyTavernPreset(
|
|
|
285
328
|
},
|
|
286
329
|
};
|
|
287
330
|
|
|
288
|
-
// Add chat-history option if {{lastUserMessage}} is used after history
|
|
331
|
+
// Add chat-history option if {{lastUserMessage}} is used after history.
|
|
332
|
+
if (macroUsage.detected.has("lastusermessage")) usesLastUserMessage = true;
|
|
289
333
|
if (usesLastUserMessage) {
|
|
290
334
|
const chatHistoryItem = items.find(
|
|
291
335
|
(item) => item.kind === "slot" && item.slot === "chat-history",
|
|
@@ -299,7 +343,7 @@ export function importSillyTavernPreset(
|
|
|
299
343
|
const reportLines: string[] = [];
|
|
300
344
|
reportLines.push(`# SillyTavern Import Report: ${fileName}`);
|
|
301
345
|
reportLines.push("");
|
|
302
|
-
reportLines.push(`- **Source file**: ${
|
|
346
|
+
reportLines.push(`- **Source file**: ${sourcePath}`);
|
|
303
347
|
reportLines.push(`- **Character ID**: ${selectedEntry.character_id}`);
|
|
304
348
|
reportLines.push(`- **Output stack ID**: ${stackId}`);
|
|
305
349
|
reportLines.push(`- **Names behavior**: ${styleName} (ST value ${preset.names_behavior ?? "?"})`);
|
|
@@ -353,7 +397,7 @@ export function importSillyTavernPreset(
|
|
|
353
397
|
reportLines.push(`- \`${key}\` = \`${val}\` — replace with your character/persona name`);
|
|
354
398
|
}
|
|
355
399
|
reportLines.push("");
|
|
356
|
-
reportLines.push("Use `/
|
|
400
|
+
reportLines.push("Use `/state set <name> <value>` or edit the stack JSON to set real values.");
|
|
357
401
|
reportLines.push("");
|
|
358
402
|
}
|
|
359
403
|
|
|
@@ -366,7 +410,7 @@ export function importSillyTavernPreset(
|
|
|
366
410
|
reportLines.push("");
|
|
367
411
|
for (const [name, count] of migrationEntries) {
|
|
368
412
|
const note = MACRO_NEEDS_MIGRATION[name] ?? "no mapping available";
|
|
369
|
-
reportLines.push(`- **\`{{${name}}}\`** (${count} occurrence${count > 1 ? "s" : ""}) — ${note}`);
|
|
413
|
+
reportLines.push(`- **\`{{${displayMacroName(name)}}}\`** (${count} occurrence${count > 1 ? "s" : ""}) — ${note}`);
|
|
370
414
|
}
|
|
371
415
|
reportLines.push("");
|
|
372
416
|
|
|
@@ -377,7 +421,7 @@ export function importSillyTavernPreset(
|
|
|
377
421
|
reportLines.push("ST uses `setvar`/`getvar` for ephemeral state. pi-forge has a different variable model:");
|
|
378
422
|
reportLines.push("");
|
|
379
423
|
reportLines.push("- **Static variables** — set in the stack JSON's `variables` object (character names, fixed config)");
|
|
380
|
-
reportLines.push("- **Session
|
|
424
|
+
reportLines.push("- **Session state** — persists across turns, set by the user via `/state set` or by the agent via `forge_state_set` for `agent.*` names");
|
|
381
425
|
reportLines.push("- **Turn variables** — ephemeral, set via `{{setvar::name::value}}` macros in blocks");
|
|
382
426
|
reportLines.push("");
|
|
383
427
|
reportLines.push("Review ST `setvar`/`getvar` calls and migrate to the appropriate pi-forge scope.");
|
|
@@ -391,12 +435,13 @@ export function importSillyTavernPreset(
|
|
|
391
435
|
reportLines.push("## Handled macros");
|
|
392
436
|
reportLines.push("");
|
|
393
437
|
for (const name of nativeDetected) {
|
|
438
|
+
const displayName = displayMacroName(name);
|
|
394
439
|
if (name === "char" || name === "user") {
|
|
395
|
-
reportLines.push(`- \`{{${
|
|
396
|
-
} else if (name === "
|
|
397
|
-
reportLines.push(`- \`{{${
|
|
440
|
+
reportLines.push(`- \`{{${displayName}}}\` → auto-populated as static variable`);
|
|
441
|
+
} else if (name === "lastusermessage") {
|
|
442
|
+
reportLines.push(`- \`{{${displayName}}}\` → handled by pi-forge runtime (chat-history slot)`);
|
|
398
443
|
} else {
|
|
399
|
-
reportLines.push(`- \`{{${
|
|
444
|
+
reportLines.push(`- \`{{${displayName}}}\` → handled natively by pi-forge`);
|
|
400
445
|
}
|
|
401
446
|
}
|
|
402
447
|
reportLines.push("");
|
|
@@ -434,7 +479,7 @@ export function importSillyTavernPreset(
|
|
|
434
479
|
reportLines.push("");
|
|
435
480
|
reportLines.push("## Suggested next steps");
|
|
436
481
|
reportLines.push("");
|
|
437
|
-
reportLines.push("1. Set real values for auto-populated variables with `/
|
|
482
|
+
reportLines.push("1. Set real values for auto-populated variables with `/state set`.");
|
|
438
483
|
reportLines.push("2. Review items with migration-needed macros and rewrite for pi-forge's macro system.");
|
|
439
484
|
reportLines.push("3. Consider adding a `variables` slot for agent state visibility.");
|
|
440
485
|
reportLines.push(`4. Run \`/preset validate ${stackId}\` to check for issues.`);
|
|
@@ -495,6 +540,10 @@ function detectMacros(conversionItems: StConversionItem[]): MacroDetection {
|
|
|
495
540
|
|
|
496
541
|
// ── Content cleaning ──────────────────────────────────────────────────────
|
|
497
542
|
|
|
543
|
+
function displayMacroName(name: string): string {
|
|
544
|
+
return MACRO_DISPLAY_NAMES[name] ?? name;
|
|
545
|
+
}
|
|
546
|
+
|
|
498
547
|
function cleanContent(content: string): string {
|
|
499
548
|
// Collapse multiple blank lines (max 1 blank line between paragraphs)
|
|
500
549
|
return content.replace(/\n{3,}/g, "\n\n").trim();
|
package/src/types.ts
CHANGED
|
@@ -50,6 +50,18 @@ export interface VariablesSlotOptions {
|
|
|
50
50
|
includeSession?: boolean;
|
|
51
51
|
/** Include turn variables. Default: true. */
|
|
52
52
|
includeTurn?: boolean;
|
|
53
|
+
/** Include only these scopes. Overrides includeStatic/includeSession/includeTurn when present. */
|
|
54
|
+
includeScopes?: PromptStateScope[];
|
|
55
|
+
/** Include variable names matching these exact names or wildcard prefixes, e.g. agent.*. */
|
|
56
|
+
includeNamespaces?: string[];
|
|
57
|
+
/** Exclude variable names matching these exact names or wildcard prefixes. */
|
|
58
|
+
excludeNamespaces?: string[];
|
|
59
|
+
/** Include type and description metadata from stack.state.definitions. Default: false. */
|
|
60
|
+
includeMetadata?: boolean;
|
|
61
|
+
/** Render format. Default: xml. */
|
|
62
|
+
format?: "xml" | "json";
|
|
63
|
+
/** Truncate each rendered value after this many characters. */
|
|
64
|
+
maxValueChars?: number;
|
|
53
65
|
}
|
|
54
66
|
|
|
55
67
|
export interface PromptStackSlotOptions {
|
|
@@ -59,6 +71,12 @@ export interface PromptStackSlotOptions {
|
|
|
59
71
|
includeStatic?: boolean;
|
|
60
72
|
includeSession?: boolean;
|
|
61
73
|
includeTurn?: boolean;
|
|
74
|
+
includeScopes?: PromptStateScope[];
|
|
75
|
+
includeNamespaces?: string[];
|
|
76
|
+
excludeNamespaces?: string[];
|
|
77
|
+
includeMetadata?: boolean;
|
|
78
|
+
format?: "xml" | "json";
|
|
79
|
+
maxValueChars?: number;
|
|
62
80
|
[key: string]: unknown;
|
|
63
81
|
}
|
|
64
82
|
|
|
@@ -70,6 +88,26 @@ export interface PromptStackSlotItem extends PromptStackBaseItem {
|
|
|
70
88
|
|
|
71
89
|
export type PromptStackItem = PromptStackBlockItem | PromptStackSlotItem;
|
|
72
90
|
|
|
91
|
+
export type PromptStatePrimitive = string | number | boolean | null;
|
|
92
|
+
|
|
93
|
+
export type PromptStateValue = PromptStatePrimitive | PromptStateValue[] | { [key: string]: PromptStateValue };
|
|
94
|
+
|
|
95
|
+
export type PromptStateScope = "static" | "session" | "turn";
|
|
96
|
+
|
|
97
|
+
export interface PromptStateDefinition {
|
|
98
|
+
type?: string;
|
|
99
|
+
scope?: PromptStateScope;
|
|
100
|
+
description?: string;
|
|
101
|
+
agentWritable?: boolean;
|
|
102
|
+
userWritable?: boolean;
|
|
103
|
+
default?: PromptStateValue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface PromptStateConfig {
|
|
107
|
+
schemaVersion?: 1;
|
|
108
|
+
definitions?: Record<string, PromptStateDefinition>;
|
|
109
|
+
}
|
|
110
|
+
|
|
73
111
|
export interface PromptStack {
|
|
74
112
|
schemaVersion: 1;
|
|
75
113
|
type?: "pi-forge.prompt-stack";
|
|
@@ -81,6 +119,7 @@ export interface PromptStack {
|
|
|
81
119
|
defaults?: PromptStackDefaults;
|
|
82
120
|
context?: PromptStackContextOptions;
|
|
83
121
|
variables?: Record<string, string>;
|
|
122
|
+
state?: PromptStateConfig;
|
|
84
123
|
items: PromptStackItem[];
|
|
85
124
|
import?: Record<string, unknown>;
|
|
86
125
|
}
|
|
@@ -100,8 +139,8 @@ export interface PromptStackDiagnostic {
|
|
|
100
139
|
}
|
|
101
140
|
|
|
102
141
|
export interface PromptVariableStore {
|
|
103
|
-
turn: Record<string,
|
|
104
|
-
session: Record<string,
|
|
142
|
+
turn: Record<string, PromptStateValue>;
|
|
143
|
+
session: Record<string, PromptStateValue>;
|
|
105
144
|
sessionDirty?: boolean;
|
|
106
145
|
}
|
|
107
146
|
|