@reactive-skills/runtime 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.
@@ -0,0 +1,167 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import Handlebars from 'handlebars';
4
+ /**
5
+ * Deliverable Projection Engine (Event-Sourced Read Models)
6
+ * Synthesizes persistent deliverables continuously from the event log.
7
+ */
8
+ export class ProjectionEngine {
9
+ skillDir;
10
+ workspaceDir;
11
+ projections;
12
+ compiledTemplates = new Map();
13
+ eventCaches = new WeakMap();
14
+ constructor(skillDir, projections = [], workspaceDir = process.cwd()) {
15
+ this.skillDir = skillDir;
16
+ this.workspaceDir = path.resolve(workspaceDir);
17
+ this.projections = projections;
18
+ this.registerHelpers();
19
+ this.compileTemplates();
20
+ }
21
+ resolveOutputPath(output) {
22
+ if (path.isAbsolute(output)) {
23
+ throw new Error('Projection output must be relative to the workspace.');
24
+ }
25
+ const outputPath = path.resolve(this.workspaceDir, output);
26
+ const relativePath = path.relative(this.workspaceDir, outputPath);
27
+ if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
28
+ throw new Error('Projection output escapes the workspace.');
29
+ }
30
+ const outputDir = path.dirname(outputPath);
31
+ const existingDir = fs.existsSync(outputDir) ? fs.realpathSync(outputDir) : outputDir;
32
+ const realWorkspace = fs.realpathSync(this.workspaceDir);
33
+ const realRelativePath = path.relative(realWorkspace, existingDir);
34
+ if (realRelativePath.startsWith('..') || path.isAbsolute(realRelativePath)) {
35
+ throw new Error('Projection output resolves outside the workspace.');
36
+ }
37
+ if (fs.existsSync(outputPath)) {
38
+ const realOutput = fs.realpathSync(outputPath);
39
+ const outputRelativePath = path.relative(realWorkspace, realOutput);
40
+ if (outputRelativePath.startsWith('..') || path.isAbsolute(outputRelativePath)) {
41
+ throw new Error('Projection output resolves outside the workspace.');
42
+ }
43
+ }
44
+ return outputPath;
45
+ }
46
+ getProjectionEvents(eventStore) {
47
+ const latestSeq = eventStore.getLatestSequence();
48
+ const cached = this.eventCaches.get(eventStore);
49
+ if (!cached) {
50
+ const events = eventStore.getAll();
51
+ this.eventCaches.set(eventStore, { lastSeq: latestSeq, events });
52
+ return events;
53
+ }
54
+ if (latestSeq <= cached.lastSeq)
55
+ return cached.events;
56
+ const events = [...cached.events, ...eventStore.getSince(cached.lastSeq)];
57
+ this.eventCaches.set(eventStore, { lastSeq: latestSeq, events });
58
+ return events;
59
+ }
60
+ matchesTrigger(proj, triggerSignal) {
61
+ if (!proj.trigger_on || proj.trigger_on.length === 0)
62
+ return true;
63
+ return proj.trigger_on.includes('*') ||
64
+ proj.trigger_on.includes('STATE_TRANSITION') ||
65
+ Boolean(triggerSignal && proj.trigger_on.includes(triggerSignal));
66
+ }
67
+ registerHelpers() {
68
+ Handlebars.registerHelper('json', (context) => {
69
+ return JSON.stringify(context, null, 2);
70
+ });
71
+ Handlebars.registerHelper('eq', (a, b) => a === b);
72
+ Handlebars.registerHelper('gt', (a, b) => a > b);
73
+ Handlebars.registerHelper('lt', (a, b) => a < b);
74
+ Handlebars.registerHelper('formatDate', (dateStr) => {
75
+ if (!dateStr)
76
+ return '';
77
+ return new Date(dateStr).toISOString();
78
+ });
79
+ }
80
+ compileTemplates() {
81
+ for (const proj of this.projections) {
82
+ try {
83
+ const templatePath = path.resolve(this.skillDir, proj.template);
84
+ if (fs.existsSync(templatePath)) {
85
+ const raw = fs.readFileSync(templatePath, 'utf8');
86
+ this.compiledTemplates.set(proj.template, Handlebars.compile(raw));
87
+ }
88
+ }
89
+ catch (err) {
90
+ // Will be caught and reported gracefully during project()
91
+ }
92
+ }
93
+ }
94
+ /**
95
+ * Render all deliverables matching the given event trigger safely with an error boundary
96
+ */
97
+ project(eventStore, currentState, skillName, context, triggerSignal) {
98
+ const writtenFiles = [];
99
+ const latestSeq = eventStore.getLatestSequence();
100
+ const eligibleProjections = this.projections.filter(proj => this.matchesTrigger(proj, triggerSignal));
101
+ const unchanged = eligibleProjections.length > 0 && eligibleProjections.every(proj => {
102
+ const watermark = eventStore.getProjectionWatermark(proj.template);
103
+ return watermark?.eventSeq === latestSeq && watermark.projectionVersion === `${this.skillDir}:${proj.template}`;
104
+ });
105
+ if (unchanged)
106
+ return writtenFiles;
107
+ const events = this.getProjectionEvents(eventStore);
108
+ // Extract state transitions for convenient template consumption
109
+ const transitions = events
110
+ .filter(e => e.type === 'STATE_TRANSITION')
111
+ .map(e => ({
112
+ from: e.payload.from,
113
+ to: e.payload.to,
114
+ timestamp: e.timestamp,
115
+ signal: e.payload.signal,
116
+ }));
117
+ const projContext = {
118
+ skillName,
119
+ currentState,
120
+ context,
121
+ events,
122
+ transitions,
123
+ lastUpdated: new Date().toISOString(),
124
+ };
125
+ for (const proj of this.projections) {
126
+ try {
127
+ // Check if trigger matches
128
+ if (!this.matchesTrigger(proj, triggerSignal))
129
+ continue;
130
+ let templateFn = this.compiledTemplates.get(proj.template);
131
+ if (!templateFn) {
132
+ const templatePath = path.resolve(this.skillDir, proj.template);
133
+ if (fs.existsSync(templatePath)) {
134
+ const raw = fs.readFileSync(templatePath, 'utf8');
135
+ templateFn = Handlebars.compile(raw);
136
+ this.compiledTemplates.set(proj.template, templateFn);
137
+ }
138
+ }
139
+ if (templateFn) {
140
+ const outputContent = templateFn(projContext);
141
+ const outputPath = this.resolveOutputPath(proj.output);
142
+ const outputDir = path.dirname(outputPath);
143
+ if (!fs.existsSync(outputDir)) {
144
+ fs.mkdirSync(outputDir, { recursive: true });
145
+ }
146
+ fs.writeFileSync(outputPath, outputContent, 'utf8');
147
+ writtenFiles.push(outputPath);
148
+ eventStore.saveProjectionWatermark(proj.template, eventStore.getLatestSequence(), `${this.skillDir}:${proj.template}`);
149
+ }
150
+ }
151
+ catch (err) {
152
+ // Error boundary: Record projection failure without tearing down the state machine transition
153
+ try {
154
+ eventStore.append('PROJECTION_FAILED', {
155
+ template: proj.template,
156
+ output: proj.output,
157
+ error: err.message,
158
+ });
159
+ }
160
+ catch {
161
+ // Swallow secondary logging error
162
+ }
163
+ }
164
+ }
165
+ return writtenFiles;
166
+ }
167
+ }
@@ -0,0 +1,51 @@
1
+ import { FSMEngine } from './fsm-engine.js';
2
+ import { PromptSlice } from './types.js';
3
+ export interface InterceptorHookResult {
4
+ injectedPrompt: string;
5
+ allowedTools: string[];
6
+ currentState: string;
7
+ slice: PromptSlice;
8
+ }
9
+ export interface ToolExecutionEventData {
10
+ tool: string;
11
+ args?: any;
12
+ result?: any;
13
+ exitCode?: number;
14
+ }
15
+ /**
16
+ * In-Harness Interceptor Hooks
17
+ * Plugs directly into the agent reasoning loop without requiring changes to base models.
18
+ */
19
+ export declare class ReactiveRuntimeHooks {
20
+ /**
21
+ * Pre-Turn Hook: Prepares the active state prompt slice and scopes allowed tools
22
+ */
23
+ static onBeforeAgentTurn(engine: FSMEngine, baseSystemPrompt?: string): InterceptorHookResult;
24
+ static auditToolExecution(engine: FSMEngine, toolData: ToolExecutionEventData): {
25
+ bypassed: boolean;
26
+ reason?: string;
27
+ };
28
+ /**
29
+ * Post-Turn Tool Hook: Translates tool execution results into reactive signals
30
+ */
31
+ static onAfterToolExecution(engine: FSMEngine, toolData: ToolExecutionEventData): Promise<{
32
+ signalsEmitted: string[];
33
+ transitioned: boolean;
34
+ currentState: string;
35
+ deliverablesWritten: string[];
36
+ }>;
37
+ /**
38
+ * Human Ingress Hook: Directly ingests human input from UI, Lavish, or CLI
39
+ */
40
+ static onHumanResponse(engine: FSMEngine, responseData: {
41
+ choice?: string;
42
+ feedback?: string;
43
+ approved?: boolean;
44
+ data?: Record<string, any>;
45
+ }): Promise<{
46
+ signalsEmitted: string[];
47
+ transitioned: boolean;
48
+ currentState: string;
49
+ deliverablesWritten: string[];
50
+ }>;
51
+ }
@@ -0,0 +1,195 @@
1
+ const RUNTIME_TOOLS = new Set([
2
+ 'reactive_state',
3
+ 'reactive_emit_signal',
4
+ 'reactive_query',
5
+ 'reactive_query_events',
6
+ 'reactive_list_skills',
7
+ 'reactive_inspect',
8
+ 'reactive_invoke_skill',
9
+ 'reactive_respond_human',
10
+ 'reactive_migrate',
11
+ ]);
12
+ /**
13
+ * In-Harness Interceptor Hooks
14
+ * Plugs directly into the agent reasoning loop without requiring changes to base models.
15
+ */
16
+ export class ReactiveRuntimeHooks {
17
+ /**
18
+ * Pre-Turn Hook: Prepares the active state prompt slice and scopes allowed tools
19
+ */
20
+ static onBeforeAgentTurn(engine, baseSystemPrompt = '') {
21
+ const slice = engine.generatePromptSlice();
22
+ let bypassWarning = '';
23
+ if (engine.isStrictExecution() && engine.getTurnsSinceLastSignal() > 0) {
24
+ bypassWarning = `<!-- BYPASS_WARNING: This turn will count against the idle budget. You have ${engine.getTurnsSinceLastSignal()} turn(s) since your last signal. Emit a signal via reactive_emit_signal to reset the counter. -->\n`;
25
+ }
26
+ const injectedPrompt = [
27
+ bypassWarning,
28
+ baseSystemPrompt,
29
+ '',
30
+ '<!-- REACTIVE SKILL CONTROL SLICE -->',
31
+ slice.formattedXml,
32
+ '<!-- END REACTIVE SKILL CONTROL SLICE -->',
33
+ ].filter(Boolean).join('\n');
34
+ return {
35
+ injectedPrompt,
36
+ allowedTools: slice.allowedTools,
37
+ currentState: slice.state,
38
+ slice,
39
+ };
40
+ }
41
+ static auditToolExecution(engine, toolData) {
42
+ const allowedTools = engine.generatePromptSlice().allowedTools;
43
+ const isRuntimeTool = RUNTIME_TOOLS.has(toolData.tool);
44
+ const isAllowedTool = allowedTools.includes(toolData.tool);
45
+ if (!isAllowedTool && !isRuntimeTool) {
46
+ const eventType = 'BYPASS_DETECTED';
47
+ engine.getEventStore().append(eventType, {
48
+ tool: toolData.tool,
49
+ reason: 'Tool not in allowed_tools and not a runtime tool',
50
+ allowed_tools: allowedTools,
51
+ }, { state: engine.getCurrentState() });
52
+ if (engine.isStrictExecution()) {
53
+ throw new Error(`BYPASS_DETECTED: Tool '${toolData.tool}' is not in the allowed tools list. ` +
54
+ `Allowed tools: ${allowedTools.join(', ')}. ` +
55
+ `To recover, run: reactive-skills-axi reset ${engine.getManifest().name} then re-invoke.`);
56
+ }
57
+ return { bypassed: true, reason: `Tool '${toolData.tool}' not in allowed_tools` };
58
+ }
59
+ return { bypassed: false };
60
+ }
61
+ /**
62
+ * Post-Turn Tool Hook: Translates tool execution results into reactive signals
63
+ */
64
+ static async onAfterToolExecution(engine, toolData) {
65
+ const signalsEmitted = [];
66
+ let transitioned = false;
67
+ let deliverablesWritten = [];
68
+ // Helper to dispatch and aggregate results
69
+ const dispatch = async (signalName, payload) => {
70
+ signalsEmitted.push(signalName);
71
+ const res = await engine.handleSignal(signalName, payload, { source: `tool:${toolData.tool}` });
72
+ if (res.transitioned) {
73
+ transitioned = true;
74
+ }
75
+ if (res.deliverablesWritten.length > 0) {
76
+ deliverablesWritten.push(...res.deliverablesWritten);
77
+ }
78
+ };
79
+ // Audit tool execution before dispatching auto-mapped signal
80
+ const auditResult = ReactiveRuntimeHooks.auditToolExecution(engine, toolData);
81
+ if (auditResult.bypassed) {
82
+ return {
83
+ signalsEmitted: [],
84
+ transitioned: false,
85
+ currentState: engine.getCurrentState(),
86
+ deliverablesWritten: [],
87
+ };
88
+ }
89
+ // Auto-map common tool outputs to signals
90
+ if (toolData.tool === 'run_command') {
91
+ const cmd = String(toolData.args?.CommandLine || toolData.args || '');
92
+ const output = String(toolData.result?.output || toolData.result || '');
93
+ const exitCode = typeof toolData.exitCode === 'number'
94
+ ? toolData.exitCode
95
+ : (output.includes('FAIL') || output.includes('Error') || output.includes('failed') ? 1 : 0);
96
+ // Check if this was a test run
97
+ if (cmd.includes('test') || cmd.includes('vitest') || cmd.includes('jest') || cmd.includes('pytest')) {
98
+ await dispatch('TEST_RAN', {
99
+ command: cmd,
100
+ exit_code: exitCode,
101
+ output,
102
+ });
103
+ }
104
+ else {
105
+ await dispatch('COMMAND_RAN', {
106
+ command: cmd,
107
+ exit_code: exitCode,
108
+ output,
109
+ });
110
+ }
111
+ }
112
+ else if (toolData.tool === 'write_to_file' || toolData.tool === 'replace_file_content') {
113
+ await dispatch('FILE_MODIFIED', {
114
+ tool: toolData.tool,
115
+ path: toolData.args?.TargetFile || toolData.args?.path,
116
+ });
117
+ }
118
+ else if (toolData.tool === 'ask_question') {
119
+ const choice = toolData.result?.choice || toolData.result?.selected || toolData.result?.answer || String(toolData.result || '');
120
+ engine.recordDecision({ choice, result: toolData.result }, `tool:${toolData.tool}`);
121
+ await dispatch('USER_DECISION', {
122
+ choice,
123
+ result: toolData.result,
124
+ });
125
+ if (/approve|yes|accept|confirm|proceed/i.test(choice)) {
126
+ await dispatch('USER_APPROVED', { choice, result: toolData.result });
127
+ }
128
+ else if (/reject|no|abort|cancel/i.test(choice)) {
129
+ await dispatch('USER_REJECTED', { choice, result: toolData.result });
130
+ }
131
+ else if (/revision|change|modify|fix/i.test(choice)) {
132
+ await dispatch('USER_REVISION_REQUESTED', { choice, result: toolData.result });
133
+ }
134
+ }
135
+ else {
136
+ await dispatch('TOOL_EXECUTED', {
137
+ tool: toolData.tool,
138
+ args: toolData.args,
139
+ });
140
+ }
141
+ return {
142
+ signalsEmitted,
143
+ transitioned,
144
+ currentState: engine.getCurrentState(),
145
+ deliverablesWritten,
146
+ };
147
+ }
148
+ /**
149
+ * Human Ingress Hook: Directly ingests human input from UI, Lavish, or CLI
150
+ */
151
+ static async onHumanResponse(engine, responseData) {
152
+ const signalsEmitted = [];
153
+ let transitioned = false;
154
+ let deliverablesWritten = [];
155
+ const dispatch = async (signalName, payload) => {
156
+ signalsEmitted.push(signalName);
157
+ const res = await engine.handleSignal(signalName, payload, { source: 'human_ingress' });
158
+ if (res.transitioned)
159
+ transitioned = true;
160
+ if (res.deliverablesWritten.length > 0)
161
+ deliverablesWritten.push(...res.deliverablesWritten);
162
+ };
163
+ // Update context if feedback or decision provided
164
+ if (responseData.feedback) {
165
+ engine.updateContext({ user_feedback: responseData.feedback });
166
+ }
167
+ if (responseData.choice) {
168
+ engine.updateContext({ user_choice: responseData.choice });
169
+ }
170
+ if (responseData.choice || responseData.feedback) {
171
+ engine.recordDecision({
172
+ choice: responseData.choice || '',
173
+ feedback: responseData.feedback,
174
+ approved: responseData.approved,
175
+ result: responseData.data,
176
+ });
177
+ }
178
+ await dispatch('USER_RESPONSE', responseData);
179
+ if (responseData.approved === true || (responseData.choice && /approve|yes|accept|confirm|proceed/i.test(responseData.choice))) {
180
+ await dispatch('USER_APPROVED', responseData);
181
+ }
182
+ else if (responseData.approved === false || (responseData.choice && /reject|no|abort|cancel/i.test(responseData.choice))) {
183
+ await dispatch('USER_REJECTED', responseData);
184
+ }
185
+ else if (responseData.choice && /revision|change|modify|fix/i.test(responseData.choice)) {
186
+ await dispatch('USER_REVISION_REQUESTED', responseData);
187
+ }
188
+ return {
189
+ signalsEmitted,
190
+ transitioned,
191
+ currentState: engine.getCurrentState(),
192
+ deliverablesWritten,
193
+ };
194
+ }
195
+ }
@@ -0,0 +1,238 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Signal Event Schema: Immutable envelope for all events in the reactive skill bus
4
+ */
5
+ export interface SignalEvent<T = Record<string, any>> {
6
+ id: string;
7
+ event_id?: string;
8
+ seq: number;
9
+ timestamp: string;
10
+ occurred_at?: string;
11
+ type: string;
12
+ event_type?: string;
13
+ source?: string;
14
+ causationId?: string;
15
+ causation_id?: string;
16
+ correlation_id?: string;
17
+ request_id?: string;
18
+ trace_parent?: string;
19
+ skill_id?: string;
20
+ run_id?: string;
21
+ parent_run_id?: string;
22
+ schema_version?: string;
23
+ payload: T;
24
+ state?: string;
25
+ }
26
+ export interface EventContext {
27
+ skill_id?: string;
28
+ run_id?: string;
29
+ runId?: string;
30
+ correlation_id?: string;
31
+ correlationId?: string;
32
+ request_id?: string;
33
+ requestId?: string;
34
+ trace_parent?: string;
35
+ traceParent?: string;
36
+ parent_run_id?: string;
37
+ parentRunId?: string;
38
+ schema_version?: string;
39
+ schemaVersion?: string;
40
+ }
41
+ export interface ChildRunSummary {
42
+ child_skill_id: string;
43
+ child_run_id: string;
44
+ outcome: 'completed' | 'failed';
45
+ failed_state?: string;
46
+ error_code?: string;
47
+ summary?: string;
48
+ last_command?: string;
49
+ evidence_ref?: string;
50
+ suggested_action?: string;
51
+ }
52
+ export interface DecisionRecord {
53
+ choice: string;
54
+ approved?: boolean;
55
+ feedback?: string;
56
+ result?: Record<string, any>;
57
+ }
58
+ /**
59
+ * Transition Guard definition
60
+ */
61
+ export interface TransitionDefinition {
62
+ target: string;
63
+ guard?: string;
64
+ guardFunction?: string;
65
+ description?: string;
66
+ invoke?: string;
67
+ }
68
+ export interface StateLifecycleAction {
69
+ emit_signal?: string;
70
+ set_context?: Record<string, any>;
71
+ action?: string;
72
+ }
73
+ export interface HumanGateDefinition {
74
+ type: 'approval' | 'choice' | 'text' | 'visual_review' | 'form';
75
+ tool?: 'ask_question' | 'lavish' | 'chat' | string;
76
+ prompt?: string;
77
+ options?: string[];
78
+ auto_stop?: boolean;
79
+ }
80
+ /**
81
+ * State Definition in a Reactive Skill
82
+ */
83
+ export interface StateDefinition {
84
+ description?: string;
85
+ prompt_template?: string;
86
+ tools?: string[];
87
+ human_gate?: HumanGateDefinition;
88
+ on_enter?: StateLifecycleAction[];
89
+ on_exit?: StateLifecycleAction[];
90
+ transitions?: Record<string, TransitionDefinition | string>;
91
+ substates?: Record<string, StateDefinition>;
92
+ initial_substate?: string;
93
+ max_idle_turns?: number;
94
+ bypass_target?: string;
95
+ }
96
+ /**
97
+ * Deliverable Projection definition (Event-Sourced Read Models)
98
+ */
99
+ export interface DeliverableProjection {
100
+ template: string;
101
+ output: string;
102
+ trigger_on?: string[];
103
+ }
104
+ /**
105
+ * Reactive Skill Manifest (skill.yaml)
106
+ */
107
+ export interface SkillManifest {
108
+ schema_version: string;
109
+ name: string;
110
+ version?: string;
111
+ description: string;
112
+ initial_state: string;
113
+ strict_execution?: boolean;
114
+ context_keys?: string[];
115
+ default_context?: Record<string, any>;
116
+ states: Record<string, StateDefinition>;
117
+ deliverable_projections?: DeliverableProjection[];
118
+ }
119
+ /**
120
+ * Hydrated prompt slice generated for an active LLM turn
121
+ */
122
+ export interface PromptSlice {
123
+ state: string;
124
+ rawPrompt: string;
125
+ formattedXml: string;
126
+ allowedTools: string[];
127
+ context: Record<string, any>;
128
+ exitConditions: string[];
129
+ }
130
+ /**
131
+ * Zod Schema for validation of skill.yaml
132
+ */
133
+ export declare const TransitionSchema: z.ZodUnion<[z.ZodString, z.ZodObject<{
134
+ target: z.ZodString;
135
+ guard: z.ZodOptional<z.ZodString>;
136
+ guardFunction: z.ZodOptional<z.ZodString>;
137
+ description: z.ZodOptional<z.ZodString>;
138
+ invoke: z.ZodOptional<z.ZodString>;
139
+ }, "strip", z.ZodTypeAny, {
140
+ target: string;
141
+ guard?: string | undefined;
142
+ guardFunction?: string | undefined;
143
+ description?: string | undefined;
144
+ invoke?: string | undefined;
145
+ }, {
146
+ target: string;
147
+ guard?: string | undefined;
148
+ guardFunction?: string | undefined;
149
+ description?: string | undefined;
150
+ invoke?: string | undefined;
151
+ }>]>;
152
+ export declare const StateLifecycleActionSchema: z.ZodObject<{
153
+ emit_signal: z.ZodOptional<z.ZodString>;
154
+ set_context: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
155
+ action: z.ZodOptional<z.ZodString>;
156
+ }, "strip", z.ZodTypeAny, {
157
+ emit_signal?: string | undefined;
158
+ set_context?: Record<string, any> | undefined;
159
+ action?: string | undefined;
160
+ }, {
161
+ emit_signal?: string | undefined;
162
+ set_context?: Record<string, any> | undefined;
163
+ action?: string | undefined;
164
+ }>;
165
+ export declare const HumanGateSchema: z.ZodObject<{
166
+ type: z.ZodEnum<["approval", "choice", "text", "visual_review", "form"]>;
167
+ tool: z.ZodOptional<z.ZodString>;
168
+ prompt: z.ZodOptional<z.ZodString>;
169
+ options: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
170
+ auto_stop: z.ZodOptional<z.ZodBoolean>;
171
+ }, "strip", z.ZodTypeAny, {
172
+ type: "approval" | "choice" | "text" | "visual_review" | "form";
173
+ options?: string[] | undefined;
174
+ tool?: string | undefined;
175
+ prompt?: string | undefined;
176
+ auto_stop?: boolean | undefined;
177
+ }, {
178
+ type: "approval" | "choice" | "text" | "visual_review" | "form";
179
+ options?: string[] | undefined;
180
+ tool?: string | undefined;
181
+ prompt?: string | undefined;
182
+ auto_stop?: boolean | undefined;
183
+ }>;
184
+ export declare const StateSchema: z.ZodType<StateDefinition>;
185
+ export declare const SkillManifestSchema: z.ZodObject<{
186
+ schema_version: z.ZodString;
187
+ name: z.ZodString;
188
+ version: z.ZodOptional<z.ZodString>;
189
+ description: z.ZodString;
190
+ initial_state: z.ZodString;
191
+ strict_execution: z.ZodOptional<z.ZodBoolean>;
192
+ context_keys: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
193
+ default_context: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
194
+ states: z.ZodRecord<z.ZodString, z.ZodType<StateDefinition, z.ZodTypeDef, StateDefinition>>;
195
+ deliverable_projections: z.ZodOptional<z.ZodArray<z.ZodObject<{
196
+ template: z.ZodString;
197
+ output: z.ZodString;
198
+ trigger_on: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
199
+ }, "strip", z.ZodTypeAny, {
200
+ template: string;
201
+ output: string;
202
+ trigger_on?: string[] | undefined;
203
+ }, {
204
+ template: string;
205
+ output: string;
206
+ trigger_on?: string[] | undefined;
207
+ }>, "many">>;
208
+ }, "strip", z.ZodTypeAny, {
209
+ description: string;
210
+ schema_version: string;
211
+ name: string;
212
+ initial_state: string;
213
+ states: Record<string, StateDefinition>;
214
+ version?: string | undefined;
215
+ strict_execution?: boolean | undefined;
216
+ context_keys?: string[] | undefined;
217
+ default_context?: Record<string, any> | undefined;
218
+ deliverable_projections?: {
219
+ template: string;
220
+ output: string;
221
+ trigger_on?: string[] | undefined;
222
+ }[] | undefined;
223
+ }, {
224
+ description: string;
225
+ schema_version: string;
226
+ name: string;
227
+ initial_state: string;
228
+ states: Record<string, StateDefinition>;
229
+ version?: string | undefined;
230
+ strict_execution?: boolean | undefined;
231
+ context_keys?: string[] | undefined;
232
+ default_context?: Record<string, any> | undefined;
233
+ deliverable_projections?: {
234
+ template: string;
235
+ output: string;
236
+ trigger_on?: string[] | undefined;
237
+ }[] | undefined;
238
+ }>;