@positronic/core 0.0.56 → 0.0.57

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 (60) hide show
  1. package/dist/src/dsl/agent-messages.js +4 -75
  2. package/dist/src/dsl/brain-runner.js +131 -47
  3. package/dist/src/dsl/brain-state-machine.js +318 -482
  4. package/dist/src/dsl/builder/brain.js +35 -1
  5. package/dist/src/dsl/constants.js +14 -2
  6. package/dist/src/dsl/create-brain.js +4 -1
  7. package/dist/src/dsl/execution/constants.js +2 -2
  8. package/dist/src/dsl/execution/event-stream.js +812 -270
  9. package/dist/src/dsl/signal-validation.js +157 -0
  10. package/dist/src/dsl/types.js +2 -2
  11. package/dist/src/index.js +5 -2
  12. package/dist/src/memory/scoped-memory.js +176 -0
  13. package/dist/src/memory/types.js +12 -0
  14. package/dist/src/tools/index.js +90 -37
  15. package/dist/src/ui/generate-ui.js +6 -3
  16. package/dist/src/yaml/data-validator.js +195 -0
  17. package/dist/types/clients/types.d.ts +39 -2
  18. package/dist/types/clients/types.d.ts.map +1 -1
  19. package/dist/types/dsl/agent-messages.d.ts +8 -14
  20. package/dist/types/dsl/agent-messages.d.ts.map +1 -1
  21. package/dist/types/dsl/brain-runner.d.ts +27 -7
  22. package/dist/types/dsl/brain-runner.d.ts.map +1 -1
  23. package/dist/types/dsl/brain-state-machine.d.ts +92 -23
  24. package/dist/types/dsl/brain-state-machine.d.ts.map +1 -1
  25. package/dist/types/dsl/brain.d.ts +2 -2
  26. package/dist/types/dsl/brain.d.ts.map +1 -1
  27. package/dist/types/dsl/builder/brain.d.ts +51 -3
  28. package/dist/types/dsl/builder/brain.d.ts.map +1 -1
  29. package/dist/types/dsl/constants.d.ts +8 -0
  30. package/dist/types/dsl/constants.d.ts.map +1 -1
  31. package/dist/types/dsl/create-brain.d.ts +13 -1
  32. package/dist/types/dsl/create-brain.d.ts.map +1 -1
  33. package/dist/types/dsl/definitions/blocks.d.ts +3 -3
  34. package/dist/types/dsl/definitions/blocks.d.ts.map +1 -1
  35. package/dist/types/dsl/definitions/events.d.ts +40 -3
  36. package/dist/types/dsl/definitions/events.d.ts.map +1 -1
  37. package/dist/types/dsl/definitions/run-params.d.ts +17 -9
  38. package/dist/types/dsl/definitions/run-params.d.ts.map +1 -1
  39. package/dist/types/dsl/execution/constants.d.ts +2 -2
  40. package/dist/types/dsl/execution/constants.d.ts.map +1 -1
  41. package/dist/types/dsl/execution/event-stream.d.ts +12 -5
  42. package/dist/types/dsl/execution/event-stream.d.ts.map +1 -1
  43. package/dist/types/dsl/signal-validation.d.ts +36 -0
  44. package/dist/types/dsl/signal-validation.d.ts.map +1 -0
  45. package/dist/types/dsl/types.d.ts +57 -1
  46. package/dist/types/dsl/types.d.ts.map +1 -1
  47. package/dist/types/index.d.ts +12 -7
  48. package/dist/types/index.d.ts.map +1 -1
  49. package/dist/types/memory/scoped-memory.d.ts +22 -0
  50. package/dist/types/memory/scoped-memory.d.ts.map +1 -0
  51. package/dist/types/memory/types.d.ts +106 -0
  52. package/dist/types/memory/types.d.ts.map +1 -0
  53. package/dist/types/tools/index.d.ts +82 -15
  54. package/dist/types/tools/index.d.ts.map +1 -1
  55. package/dist/types/ui/generate-ui.d.ts.map +1 -1
  56. package/dist/types/yaml/data-validator.d.ts +27 -1
  57. package/dist/types/yaml/data-validator.d.ts.map +1 -1
  58. package/dist/types/yaml/types.d.ts +10 -0
  59. package/dist/types/yaml/types.d.ts.map +1 -1
  60. package/package.json +1 -1
@@ -0,0 +1,157 @@
1
+ function _array_like_to_array(arr, len) {
2
+ if (len == null || len > arr.length) len = arr.length;
3
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
4
+ return arr2;
5
+ }
6
+ function _array_with_holes(arr) {
7
+ if (Array.isArray(arr)) return arr;
8
+ }
9
+ function _define_property(obj, key, value) {
10
+ if (key in obj) {
11
+ Object.defineProperty(obj, key, {
12
+ value: value,
13
+ enumerable: true,
14
+ configurable: true,
15
+ writable: true
16
+ });
17
+ } else {
18
+ obj[key] = value;
19
+ }
20
+ return obj;
21
+ }
22
+ function _iterable_to_array_limit(arr, i) {
23
+ var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
24
+ if (_i == null) return;
25
+ var _arr = [];
26
+ var _n = true;
27
+ var _d = false;
28
+ var _s, _e;
29
+ try {
30
+ for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
31
+ _arr.push(_s.value);
32
+ if (i && _arr.length === i) break;
33
+ }
34
+ } catch (err) {
35
+ _d = true;
36
+ _e = err;
37
+ } finally{
38
+ try {
39
+ if (!_n && _i["return"] != null) _i["return"]();
40
+ } finally{
41
+ if (_d) throw _e;
42
+ }
43
+ }
44
+ return _arr;
45
+ }
46
+ function _non_iterable_rest() {
47
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
48
+ }
49
+ function _sliced_to_array(arr, i) {
50
+ return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
51
+ }
52
+ function _unsupported_iterable_to_array(o, minLen) {
53
+ if (!o) return;
54
+ if (typeof o === "string") return _array_like_to_array(o, minLen);
55
+ var n = Object.prototype.toString.call(o).slice(8, -1);
56
+ if (n === "Object" && o.constructor) n = o.constructor.name;
57
+ if (n === "Map" || n === "Set") return Array.from(n);
58
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
59
+ }
60
+ import { BRAIN_EVENTS, STATUS } from './constants.js';
61
+ /**
62
+ * Map signal types to the events they would emit.
63
+ * These events must have valid transitions from the current state.
64
+ */ var signalToEvent = {
65
+ 'KILL': BRAIN_EVENTS.CANCELLED,
66
+ 'PAUSE': BRAIN_EVENTS.PAUSED,
67
+ 'RESUME': BRAIN_EVENTS.RESUMED,
68
+ 'USER_MESSAGE': BRAIN_EVENTS.AGENT_USER_MESSAGE,
69
+ 'WEBHOOK_RESPONSE': BRAIN_EVENTS.WEBHOOK_RESPONSE
70
+ };
71
+ var _obj;
72
+ /**
73
+ * Map brain status (from MonitorDO) to state machine state names.
74
+ * The state machine uses different internal names than the public status values.
75
+ */ var statusToState = (_obj = {}, _define_property(_obj, STATUS.PENDING, 'idle'), _define_property(_obj, STATUS.RUNNING, 'running'), _define_property(_obj, STATUS.PAUSED, 'paused'), _define_property(_obj, STATUS.WAITING, 'waiting'), _define_property(_obj, STATUS.COMPLETE, 'complete'), _define_property(_obj, STATUS.ERROR, 'error'), _define_property(_obj, STATUS.CANCELLED, 'cancelled'), _obj);
76
+ /**
77
+ * Check if a signal is valid for the current brain state.
78
+ * Uses the state machine definition as the source of truth.
79
+ *
80
+ * @param machineDefinition - The state machine definition with states and transitions
81
+ * @param brainStatus - The current brain status (e.g., 'running', 'paused', 'complete')
82
+ * @param signalType - The signal type to validate (e.g., 'PAUSE', 'KILL', 'RESUME')
83
+ * @returns Validation result with valid flag and optional reason for rejection
84
+ */ export function isSignalValid(machineDefinition, brainStatus, signalType) {
85
+ var eventName = signalToEvent[signalType];
86
+ if (!eventName) {
87
+ return {
88
+ valid: false,
89
+ reason: "Unknown signal type: ".concat(signalType)
90
+ };
91
+ }
92
+ var stateName = statusToState[brainStatus];
93
+ if (!stateName) {
94
+ return {
95
+ valid: false,
96
+ reason: "Unknown brain status: ".concat(brainStatus)
97
+ };
98
+ }
99
+ var stateObj = machineDefinition.states[stateName];
100
+ if (!stateObj) {
101
+ return {
102
+ valid: false,
103
+ reason: "State '".concat(stateName, "' not found in machine")
104
+ };
105
+ }
106
+ var hasTransition = stateObj.transitions.has(eventName);
107
+ if (!hasTransition) {
108
+ return {
109
+ valid: false,
110
+ reason: "Cannot ".concat(signalType, " brain in '").concat(brainStatus, "' state")
111
+ };
112
+ }
113
+ return {
114
+ valid: true
115
+ };
116
+ }
117
+ /**
118
+ * Get the list of valid signals for a given brain status.
119
+ * Useful for debugging and for providing user feedback.
120
+ *
121
+ * @param machineDefinition - The state machine definition with states and transitions
122
+ * @param brainStatus - The current brain status
123
+ * @returns Array of valid signal types
124
+ */ export function getValidSignals(machineDefinition, brainStatus) {
125
+ var stateName = statusToState[brainStatus];
126
+ if (!stateName) {
127
+ return [];
128
+ }
129
+ var stateObj = machineDefinition.states[stateName];
130
+ if (!stateObj) {
131
+ return [];
132
+ }
133
+ var validSignals = [];
134
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
135
+ try {
136
+ for(var _iterator = Object.entries(signalToEvent)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
137
+ var _step_value = _sliced_to_array(_step.value, 2), signalType = _step_value[0], eventName = _step_value[1];
138
+ if (stateObj.transitions.has(eventName)) {
139
+ validSignals.push(signalType);
140
+ }
141
+ }
142
+ } catch (err) {
143
+ _didIteratorError = true;
144
+ _iteratorError = err;
145
+ } finally{
146
+ try {
147
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
148
+ _iterator.return();
149
+ }
150
+ } finally{
151
+ if (_didIteratorError) {
152
+ throw _iteratorError;
153
+ }
154
+ }
155
+ }
156
+ return validSignals;
157
+ }
@@ -1,4 +1,4 @@
1
1
  /**
2
- * Configuration for retry behavior with exponential backoff.
3
- * Used by batch prompt execution.
2
+ * Interface for providing signals to a running brain.
3
+ * Implementations are backend-specific (in-memory, database, KV store, etc.)
4
4
  */ export { };
package/dist/src/index.js CHANGED
@@ -12,6 +12,9 @@ export { createResources } from './resources/resources.js';
12
12
  export { createWebhook } from './dsl/webhook.js';
13
13
  export { RESOURCE_TYPES } from './resources/resources.js';
14
14
  // Default tools
15
- export { createTool, defaultTools, generateUI, consoleLog, done } from './tools/index.js';
15
+ export { createTool, defaultTools, defaultDoneSchema, generateUI, waitForWebhook, print, consoleLog } from './tools/index.js';
16
+ export { createScopedMemory } from './memory/scoped-memory.js';
16
17
  // Brain state machine
17
- export { createBrainExecutionMachine, createBrainMachine, sendEvent, getDepth, isTopLevel, getCurrentStep, getBrainStack, getBrainRunId, getExecutionState, getPendingWebhooks, getError, getCompletedSteps } from './dsl/brain-state-machine.js';
18
+ export { createBrainExecutionMachine, createBrainMachine, sendEvent, reconstructBrainTree, brainMachineDefinition } from './dsl/brain-state-machine.js';
19
+ // Signal validation
20
+ export { isSignalValid, getValidSignals } from './dsl/signal-validation.js';
@@ -0,0 +1,176 @@
1
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
2
+ try {
3
+ var info = gen[key](arg);
4
+ var value = info.value;
5
+ } catch (error) {
6
+ reject(error);
7
+ return;
8
+ }
9
+ if (info.done) {
10
+ resolve(value);
11
+ } else {
12
+ Promise.resolve(value).then(_next, _throw);
13
+ }
14
+ }
15
+ function _async_to_generator(fn) {
16
+ return function() {
17
+ var self = this, args = arguments;
18
+ return new Promise(function(resolve, reject) {
19
+ var gen = fn.apply(self, args);
20
+ function _next(value) {
21
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
22
+ }
23
+ function _throw(err) {
24
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
25
+ }
26
+ _next(undefined);
27
+ });
28
+ };
29
+ }
30
+ function _ts_generator(thisArg, body) {
31
+ var f, y, t, _ = {
32
+ label: 0,
33
+ sent: function() {
34
+ if (t[0] & 1) throw t[1];
35
+ return t[1];
36
+ },
37
+ trys: [],
38
+ ops: []
39
+ }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
40
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() {
41
+ return this;
42
+ }), g;
43
+ function verb(n) {
44
+ return function(v) {
45
+ return step([
46
+ n,
47
+ v
48
+ ]);
49
+ };
50
+ }
51
+ function step(op) {
52
+ if (f) throw new TypeError("Generator is already executing.");
53
+ while(g && (g = 0, op[0] && (_ = 0)), _)try {
54
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
55
+ if (y = 0, t) op = [
56
+ op[0] & 2,
57
+ t.value
58
+ ];
59
+ switch(op[0]){
60
+ case 0:
61
+ case 1:
62
+ t = op;
63
+ break;
64
+ case 4:
65
+ _.label++;
66
+ return {
67
+ value: op[1],
68
+ done: false
69
+ };
70
+ case 5:
71
+ _.label++;
72
+ y = op[1];
73
+ op = [
74
+ 0
75
+ ];
76
+ continue;
77
+ case 7:
78
+ op = _.ops.pop();
79
+ _.trys.pop();
80
+ continue;
81
+ default:
82
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
83
+ _ = 0;
84
+ continue;
85
+ }
86
+ if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
87
+ _.label = op[1];
88
+ break;
89
+ }
90
+ if (op[0] === 6 && _.label < t[1]) {
91
+ _.label = t[1];
92
+ t = op;
93
+ break;
94
+ }
95
+ if (t && _.label < t[2]) {
96
+ _.label = t[2];
97
+ _.ops.push(op);
98
+ break;
99
+ }
100
+ if (t[2]) _.ops.pop();
101
+ _.trys.pop();
102
+ continue;
103
+ }
104
+ op = body.call(thisArg, _);
105
+ } catch (e) {
106
+ op = [
107
+ 6,
108
+ e
109
+ ];
110
+ y = 0;
111
+ } finally{
112
+ f = t = 0;
113
+ }
114
+ if (op[0] & 5) throw op[1];
115
+ return {
116
+ value: op[0] ? op[1] : void 0,
117
+ done: true
118
+ };
119
+ }
120
+ }
121
+ /**
122
+ * Creates a scoped memory instance with the agentId pre-bound.
123
+ *
124
+ * This wraps a MemoryProvider and automatically includes the agentId
125
+ * in all calls, so brain steps don't need to pass it explicitly.
126
+ *
127
+ * @param provider - The underlying memory provider
128
+ * @param agentId - The agent/brain ID to scope memories to
129
+ * @returns A ScopedMemory instance
130
+ *
131
+ * @example
132
+ * ```typescript
133
+ * const provider = createMem0Provider({ apiKey: '...' });
134
+ * const scopedMemory = createScopedMemory(provider, 'my-brain');
135
+ *
136
+ * // Now search without passing agentId
137
+ * const memories = await scopedMemory.search('user preferences');
138
+ * ```
139
+ */ export function createScopedMemory(provider, agentId) {
140
+ return {
141
+ search: function search(query, options) {
142
+ return _async_to_generator(function() {
143
+ var scope;
144
+ return _ts_generator(this, function(_state) {
145
+ scope = {
146
+ agentId: agentId,
147
+ userId: options === null || options === void 0 ? void 0 : options.userId
148
+ };
149
+ return [
150
+ 2,
151
+ provider.search(query, scope, {
152
+ limit: options === null || options === void 0 ? void 0 : options.limit
153
+ })
154
+ ];
155
+ });
156
+ })();
157
+ },
158
+ add: function add(messages, options) {
159
+ return _async_to_generator(function() {
160
+ var scope;
161
+ return _ts_generator(this, function(_state) {
162
+ scope = {
163
+ agentId: agentId,
164
+ userId: options === null || options === void 0 ? void 0 : options.userId
165
+ };
166
+ return [
167
+ 2,
168
+ provider.add(messages, scope, {
169
+ metadata: options === null || options === void 0 ? void 0 : options.metadata
170
+ })
171
+ ];
172
+ });
173
+ })();
174
+ }
175
+ };
176
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Memory types for the Positronic memory system.
3
+ *
4
+ * The memory system provides a provider-agnostic interface for storing and
5
+ * retrieving long-term memories. Memory providers implement the raw interface,
6
+ * and brain steps receive a scoped memory instance with the agent ID pre-bound.
7
+ */ /**
8
+ * A single memory entry returned from search operations.
9
+ */ /**
10
+ * Scoped memory interface with agentId pre-bound.
11
+ * This is what brain steps receive - they don't need to pass agentId.
12
+ */ export { };
@@ -145,30 +145,37 @@ import { generatePageHtml } from '../ui/generate-page-html.js';
145
145
  return config;
146
146
  }
147
147
  var generateUIInputSchema = z.object({
148
- prompt: z.string().describe('Instructions for what UI to generate. Describe the form fields, layout, and purpose. ' + 'Be specific about what data you want to collect from the user.')
148
+ prompt: z.string().describe('Natural language instructions describing the UI to generate. ' + 'Include: (1) the purpose of the page, (2) what information to display from the data parameter, ' + '(3) what input fields are needed if collecting data, (4) labels and placeholders for fields, ' + '(5) any specific layout preferences. Example: "Create a form to collect user feedback with ' + 'fields for rating (1-5 scale), comments (text area), and email. Show the product name at the top."'),
149
+ hasForm: z.boolean().optional().describe('Whether to include a form that can be submitted. Defaults to true. ' + 'Set to true when: collecting user input, approval workflows, any interactive data entry. ' + 'Set to false when: displaying read-only information, confirmation pages, dashboards. ' + 'When true, the tool returns webhook info that must be used with waitForWebhook to receive the submission.'),
150
+ persist: z.boolean().optional().describe('Whether to keep the page after the brain run completes. Defaults to false. ' + 'Set to false (default): Page is automatically cleaned up when the brain run finishes. Use for one-time forms, approvals, or temporary displays. ' + 'Set to true: Page survives brain completion and remains accessible. Use for shared dashboards, permanent reference pages, or pages that need to outlive the workflow.'),
151
+ data: z.record(z.unknown()).optional().describe('Structured data for template bindings ({{path.to.value}} syntax). ' + 'Pass the objects/arrays you want to display. The UI generator infers the shape ' + 'and creates bindings to render all items. Example: { articles: [...], user: { name: "..." } }')
149
152
  });
150
153
  /**
151
- * Generate UI tool - creates an interactive UI page and waits for user response.
154
+ * Generate UI tool - creates an interactive UI page.
152
155
  *
153
156
  * This tool:
154
157
  * 1. Uses an LLM to generate a UI page based on your prompt
155
158
  * 2. Creates an HTML page with the generated components
156
- * 3. Pauses agent execution until the user submits the form
157
- * 4. Returns the form data as the tool result when resumed
159
+ * 3. Returns the page URL and webhook info (if hasForm is true)
160
+ *
161
+ * IMPORTANT: This tool does NOT pause execution. After generating a page with a form,
162
+ * you must call waitForWebhook to pause and wait for the form submission.
163
+ * Before calling waitForWebhook, ensure the user knows the page URL.
158
164
  *
159
165
  * Requires components and pages to be configured via createBrain or withComponents().
160
166
  *
161
167
  * The description is enriched at runtime with available component information.
162
168
  */ export var generateUI = {
163
- description: 'Generate a UI page to display to the user and wait for their response. ' + 'Available components will be listed in the system prompt. ' + 'After the page is created, execution pauses until the user submits the form.',
169
+ description: "Generate a web page for displaying rich content or collecting user input.\n\nSometimes you need more than simple notifications to communicate with users. This tool creates web pages that can display formatted content, dashboards, or forms to collect information.\n\nPass structured data via the 'data' parameter to populate the page with dynamic content. The UI generator uses {{path.to.value}} template bindings to render your data.\n\nRETURNS: { url: string, webhook: { slug: string, identifier: string } | null }\n- url: The page URL\n- webhook: For forms (hasForm=true), contains slug and identifier that can be passed to waitForWebhook to pause execution until the user submits the form\n\nIMPORTANT: Users have no way to discover the page URL on their own. After generating a page, you must tell them the URL using whatever communication tools are available.",
164
170
  inputSchema: generateUIInputSchema,
165
171
  execute: function execute(input, context) {
166
172
  return _async_to_generator(function() {
167
- var components, pages, client, state, env, brainRunId, stepId, uiResult, placementCount, webhookIdentifier, formAction, html, page, webhook;
173
+ var components, pages, client, state, env, brainRunId, stepId, _input_hasForm, hasForm, _input_data, uiResult, placementCount, webhookInfo, formAction, webhookIdentifier, _input_data1, html, page;
168
174
  return _ts_generator(this, function(_state) {
169
175
  switch(_state.label){
170
176
  case 0:
171
177
  components = context.components, pages = context.pages, client = context.client, state = context.state, env = context.env, brainRunId = context.brainRunId, stepId = context.stepId;
178
+ hasForm = (_input_hasForm = input.hasForm) !== null && _input_hasForm !== void 0 ? _input_hasForm : true;
172
179
  if (!components || Object.keys(components).length === 0) {
173
180
  throw new Error('generateUI requires components to be configured. ' + 'Use createBrain({ components }) or brain.withComponents() to register UI components.');
174
181
  }
@@ -181,7 +188,7 @@ var generateUIInputSchema = z.object({
181
188
  client: client,
182
189
  prompt: input.prompt,
183
190
  components: components,
184
- data: state
191
+ data: (_input_data = input.data) !== null && _input_data !== void 0 ? _input_data : {}
185
192
  })
186
193
  ];
187
194
  case 1:
@@ -194,36 +201,38 @@ var generateUIInputSchema = z.object({
194
201
  throw new Error("UI generation failed - no root component found. " + "".concat(placementCount, " component(s) were placed but all have a parentId."));
195
202
  }
196
203
  }
197
- // Create unique identifier for the webhook
198
- webhookIdentifier = "".concat(brainRunId, "-").concat(stepId, "-generateui-").concat(Date.now());
199
- // Build form action URL
200
- formAction = "".concat(env.origin, "/webhooks/system/ui-form?identifier=").concat(encodeURIComponent(webhookIdentifier));
204
+ // Create webhook info only if hasForm is true
205
+ webhookInfo = null;
206
+ if (hasForm) {
207
+ webhookIdentifier = "".concat(brainRunId, "-").concat(stepId, "-generateui-").concat(Date.now());
208
+ formAction = "".concat(env.origin, "/webhooks/system/ui-form?identifier=").concat(encodeURIComponent(webhookIdentifier));
209
+ webhookInfo = {
210
+ slug: 'ui-form',
211
+ identifier: webhookIdentifier
212
+ };
213
+ }
201
214
  // Generate HTML page
202
215
  html = generatePageHtml({
203
216
  placements: uiResult.placements,
204
217
  rootId: uiResult.rootId,
205
- data: state,
206
- title: 'Generated Form',
218
+ data: (_input_data1 = input.data) !== null && _input_data1 !== void 0 ? _input_data1 : {},
219
+ title: hasForm ? 'Generated Form' : 'Generated Page',
207
220
  formAction: formAction
208
221
  });
209
222
  return [
210
223
  4,
211
- pages.create(html)
224
+ pages.create(html, {
225
+ persist: input.persist
226
+ })
212
227
  ];
213
228
  case 2:
214
229
  page = _state.sent();
215
- // Create webhook registration for form submission
216
- webhook = {
217
- slug: 'ui-form',
218
- identifier: webhookIdentifier,
219
- schema: z.record(z.unknown())
220
- };
221
- // Return URL and waitFor - the URL is included so it can be logged/emitted
230
+ // Return URL and webhook info (no waitFor - does not pause)
222
231
  return [
223
232
  2,
224
233
  {
225
234
  url: page.url,
226
- waitFor: webhook
235
+ webhook: webhookInfo
227
236
  }
228
237
  ];
229
238
  }
@@ -231,36 +240,75 @@ var generateUIInputSchema = z.object({
231
240
  })();
232
241
  }
233
242
  };
243
+ var waitForWebhookInputSchema = z.object({
244
+ slug: z.string().describe('The webhook slug that identifies the type of webhook. ' + 'For generateUI forms, this is always "ui-form". ' + 'Use the exact slug value returned by the tool that created the webhook.'),
245
+ identifier: z.string().describe('The unique identifier for this specific webhook instance. ' + 'This is returned by generateUI in webhook.identifier. ' + 'Each generateUI call creates a unique identifier - use the one from the specific page you want to wait for.')
246
+ });
247
+ /**
248
+ * Wait for webhook tool - pauses execution until a webhook receives a response.
249
+ *
250
+ * Use this after generating a UI page with a form to wait for the user's submission.
251
+ * The form data will be returned as the tool result when the webhook fires.
252
+ *
253
+ * IMPORTANT: Before calling this tool, ensure the user knows the page URL
254
+ * so they can access and submit the form.
255
+ */ export var waitForWebhook = {
256
+ description: 'Pause agent execution and wait for an external event (webhook response).\n\nPURPOSE: Suspend the agent until a user action occurs, such as submitting a form generated by generateUI.\n\n⚠️ CRITICAL - BEFORE CALLING THIS TOOL:\nYou MUST have already communicated the page URL to the user in your response. The user has no other way to discover the URL. If you call this tool without first telling the user where to go, the job will freeze indefinitely with no easy recovery.\n\nCORRECT SEQUENCE:\n1. Call generateUI to create the page\n2. In your response text, tell the user the URL (e.g., "Please complete the form at: {url}")\n3. THEN call waitForWebhook\n\nBEHAVIOR:\n- Calling this tool immediately pauses execution\n- The agent will NOT continue until the webhook receives data\n- This pause is indefinite - there is no timeout\n- When the webhook fires, execution resumes with the webhook payload as this tool\'s result\n- The form data will be available as key-value pairs (e.g., { name: "John", email: "john@example.com" })\n\nWHEN TO USE:\n- After generateUI with hasForm=true, to wait for form submission\n- Any workflow requiring human input or approval before continuing\n\nFAILURE MODE: If the user doesn\'t know the URL, they cannot submit the form, and the agent waits forever. The only recovery is to kill the job or manually inspect the event stream for the URL.\n\nRETURNS: The webhook payload when triggered. For UI forms, this contains all form field values as an object.',
257
+ inputSchema: waitForWebhookInputSchema,
258
+ execute: function execute(input) {
259
+ var webhook = {
260
+ slug: input.slug,
261
+ identifier: input.identifier,
262
+ schema: z.record(z.unknown())
263
+ };
264
+ return {
265
+ waitFor: webhook
266
+ };
267
+ }
268
+ };
234
269
  /**
235
- * Console log tool - useful for debugging and logging information during agent execution.
270
+ * Print tool - the simplest way for agents to communicate with users.
271
+ * Like PRINT in BASIC - outputs a message that users can see.
272
+ */ export var print = createTool({
273
+ description: "Display a message to the user. This is the simplest way to communicate with users.\n\nUse this tool whenever you need to tell the user something - status updates, instructions, results, or any information they should see. The message will be displayed to users watching this workflow.",
274
+ inputSchema: z.object({
275
+ message: z.string().describe('The message to display to the user')
276
+ }),
277
+ execute: function(param) {
278
+ var message = param.message;
279
+ console.log("[Print] ".concat(message));
280
+ return {
281
+ printed: true
282
+ };
283
+ }
284
+ });
285
+ /**
286
+ * Console log tool - for internal server-side debugging and logging.
287
+ * NOT for user communication - use print for that.
236
288
  */ export var consoleLog = createTool({
237
- description: 'Log a message to the console for debugging or informational purposes',
289
+ description: "Write a debug log message to the server console. For internal debugging only - users do not see these messages. Use 'print' to communicate with users.",
238
290
  inputSchema: z.object({
239
- message: z.string().describe('The message to log'),
291
+ message: z.string().describe('The debug message to log'),
240
292
  level: z.enum([
241
293
  'info',
242
294
  'warn',
243
295
  'error'
244
- ]).optional().describe('Log level (defaults to info)')
296
+ ]).optional().describe('Log level: info (default), warn, or error')
245
297
  }),
246
298
  execute: function(param) {
247
299
  var message = param.message, _param_level = param.level, level = _param_level === void 0 ? 'info' : _param_level;
248
300
  var logFn = level === 'error' ? console.error : level === 'warn' ? console.warn : console.log;
249
- logFn("[Agent] ".concat(message));
301
+ logFn("[Debug] ".concat(message));
250
302
  return {
251
303
  logged: true
252
304
  };
253
305
  }
254
306
  });
255
307
  /**
256
- * Done tool - a simple terminal tool for completing agent execution.
257
- * The result becomes part of the brain state.
258
- */ export var done = createTool({
259
- description: 'Complete the task and return a result',
260
- inputSchema: z.object({
261
- result: z.string().describe('The final result or summary of the completed task')
262
- }),
263
- terminal: true
308
+ * Default schema for the auto-generated 'done' tool when no outputSchema is provided.
309
+ * Used internally by the framework.
310
+ */ export var defaultDoneSchema = z.object({
311
+ result: z.string().describe('A clear summary of what was accomplished. ' + 'Include: key outcomes, any important values or findings, and confirmation that the task is complete. ' + 'Be specific but concise. Example: "Successfully processed 15 orders totaling $1,234.56. All items shipped."')
264
312
  });
265
313
  /**
266
314
  * Default tools bundle.
@@ -269,6 +317,10 @@ var generateUIInputSchema = z.object({
269
317
  * standard tools in your brain. Tools can be extended or overridden in
270
318
  * individual agent steps.
271
319
  *
320
+ * Note: A 'done' terminal tool is automatically generated for every agent.
321
+ * If you provide an outputSchema, 'done' will use that schema. Otherwise,
322
+ * it uses a default schema expecting { result: string }.
323
+ *
272
324
  * @example
273
325
  * ```typescript
274
326
  * import { createBrain, defaultTools } from '@positronic/core';
@@ -289,6 +341,7 @@ var generateUIInputSchema = z.object({
289
341
  * ```
290
342
  */ export var defaultTools = {
291
343
  generateUI: generateUI,
292
- consoleLog: consoleLog,
293
- done: done
344
+ waitForWebhook: waitForWebhook,
345
+ print: print,
346
+ consoleLog: consoleLog
294
347
  };
@@ -186,7 +186,7 @@ function _ts_generator(thisArg, body) {
186
186
  import { v4 as uuidv4 } from 'uuid';
187
187
  import { z } from 'zod';
188
188
  import { parseTemplate } from '../yaml/parser.js';
189
- import { inferDataType, validateDataBindings } from '../yaml/data-validator.js';
189
+ import { inferDataType, resolveBindings, validateDataBindings } from '../yaml/data-validator.js';
190
190
  import { extractFormSchema, validateAgainstZod } from '../yaml/schema-extractor.js';
191
191
  import { describeDataShape } from '../yaml/type-inference.js';
192
192
  /**
@@ -319,7 +319,7 @@ import { describeDataShape } from '../yaml/type-inference.js';
319
319
  */ function createValidateTemplateTool(components, schema, data) {
320
320
  var dataType = inferDataType(data);
321
321
  return {
322
- description: "Validate a YAML template. Checks that:\n1. The YAML is valid and can be parsed\n2. All component names are valid\n3. All data bindings (like {{email.subject}}) reference valid paths in the provided data\n4. Form components have a Button for submission\n5. The form fields will produce data matching the expected schema (if schema provided)\n\nCall this after generating your YAML template to verify it's correct before finalizing.",
322
+ description: "Validate a YAML template. Checks that:\n1. The YAML is valid and can be parsed\n2. All component names are valid\n3. All data bindings (like {{email.subject}}) reference valid paths in the provided data\n4. Form components have a Button for submission\n5. The form fields will produce data matching the expected schema (if schema provided)\n\nReturns resolvedBindings showing what each binding resolves to in the actual data. Use this to verify bindings point to the right values.\n\nCall this after generating your YAML template to verify it's correct before finalizing.",
323
323
  inputSchema: z.object({
324
324
  yaml: z.string().describe('The complete YAML template to validate')
325
325
  }),
@@ -390,6 +390,8 @@ import { describeDataShape } from '../yaml/type-inference.js';
390
390
  var schemaErrors = validateAgainstZod(extracted, schema);
391
391
  (_errors2 = errors).push.apply(_errors2, _to_consumable_array(schemaErrors));
392
392
  }
393
+ // 6. Resolve bindings against actual data
394
+ var resolved = resolveBindings(root, data);
393
395
  return {
394
396
  valid: errors.length === 0,
395
397
  errors: errors.map(function(e) {
@@ -398,7 +400,8 @@ import { describeDataShape } from '../yaml/type-inference.js';
398
400
  message: e.message
399
401
  };
400
402
  }),
401
- extractedFields: extractedFields
403
+ extractedFields: extractedFields,
404
+ resolvedBindings: resolved
402
405
  };
403
406
  }
404
407
  };