@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.
- package/dist/src/dsl/agent-messages.js +4 -75
- package/dist/src/dsl/brain-runner.js +131 -47
- package/dist/src/dsl/brain-state-machine.js +318 -482
- package/dist/src/dsl/builder/brain.js +35 -1
- package/dist/src/dsl/constants.js +14 -2
- package/dist/src/dsl/create-brain.js +4 -1
- package/dist/src/dsl/execution/constants.js +2 -2
- package/dist/src/dsl/execution/event-stream.js +812 -270
- package/dist/src/dsl/signal-validation.js +157 -0
- package/dist/src/dsl/types.js +2 -2
- package/dist/src/index.js +5 -2
- package/dist/src/memory/scoped-memory.js +176 -0
- package/dist/src/memory/types.js +12 -0
- package/dist/src/tools/index.js +90 -37
- package/dist/src/ui/generate-ui.js +6 -3
- package/dist/src/yaml/data-validator.js +195 -0
- package/dist/types/clients/types.d.ts +39 -2
- package/dist/types/clients/types.d.ts.map +1 -1
- package/dist/types/dsl/agent-messages.d.ts +8 -14
- package/dist/types/dsl/agent-messages.d.ts.map +1 -1
- package/dist/types/dsl/brain-runner.d.ts +27 -7
- package/dist/types/dsl/brain-runner.d.ts.map +1 -1
- package/dist/types/dsl/brain-state-machine.d.ts +92 -23
- package/dist/types/dsl/brain-state-machine.d.ts.map +1 -1
- package/dist/types/dsl/brain.d.ts +2 -2
- package/dist/types/dsl/brain.d.ts.map +1 -1
- package/dist/types/dsl/builder/brain.d.ts +51 -3
- package/dist/types/dsl/builder/brain.d.ts.map +1 -1
- package/dist/types/dsl/constants.d.ts +8 -0
- package/dist/types/dsl/constants.d.ts.map +1 -1
- package/dist/types/dsl/create-brain.d.ts +13 -1
- package/dist/types/dsl/create-brain.d.ts.map +1 -1
- package/dist/types/dsl/definitions/blocks.d.ts +3 -3
- package/dist/types/dsl/definitions/blocks.d.ts.map +1 -1
- package/dist/types/dsl/definitions/events.d.ts +40 -3
- package/dist/types/dsl/definitions/events.d.ts.map +1 -1
- package/dist/types/dsl/definitions/run-params.d.ts +17 -9
- package/dist/types/dsl/definitions/run-params.d.ts.map +1 -1
- package/dist/types/dsl/execution/constants.d.ts +2 -2
- package/dist/types/dsl/execution/constants.d.ts.map +1 -1
- package/dist/types/dsl/execution/event-stream.d.ts +12 -5
- package/dist/types/dsl/execution/event-stream.d.ts.map +1 -1
- package/dist/types/dsl/signal-validation.d.ts +36 -0
- package/dist/types/dsl/signal-validation.d.ts.map +1 -0
- package/dist/types/dsl/types.d.ts +57 -1
- package/dist/types/dsl/types.d.ts.map +1 -1
- package/dist/types/index.d.ts +12 -7
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/memory/scoped-memory.d.ts +22 -0
- package/dist/types/memory/scoped-memory.d.ts.map +1 -0
- package/dist/types/memory/types.d.ts +106 -0
- package/dist/types/memory/types.d.ts.map +1 -0
- package/dist/types/tools/index.d.ts +82 -15
- package/dist/types/tools/index.d.ts.map +1 -1
- package/dist/types/ui/generate-ui.d.ts.map +1 -1
- package/dist/types/yaml/data-validator.d.ts +27 -1
- package/dist/types/yaml/data-validator.d.ts.map +1 -1
- package/dist/types/yaml/types.d.ts +10 -0
- package/dist/types/yaml/types.d.ts.map +1 -1
- 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
|
+
}
|
package/dist/src/dsl/types.js
CHANGED
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,
|
|
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,
|
|
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 { };
|
package/dist/src/tools/index.js
CHANGED
|
@@ -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('
|
|
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
|
|
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.
|
|
157
|
-
*
|
|
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:
|
|
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,
|
|
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:
|
|
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
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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:
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
*
|
|
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:
|
|
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 (
|
|
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("[
|
|
301
|
+
logFn("[Debug] ".concat(message));
|
|
250
302
|
return {
|
|
251
303
|
logged: true
|
|
252
304
|
};
|
|
253
305
|
}
|
|
254
306
|
});
|
|
255
307
|
/**
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*/ export var
|
|
259
|
-
|
|
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
|
-
|
|
293
|
-
|
|
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
|
};
|