@rolealpha/n8n-nodes-rolealpha 0.0.0-stage → 0.5.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.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +339 -2
  3. package/dist/credentials/ApprovalSealApi.credentials.d.ts +7 -0
  4. package/dist/credentials/ApprovalSealApi.credentials.js +29 -0
  5. package/dist/credentials/ApprovalSealApi.credentials.js.map +1 -0
  6. package/dist/credentials/RoleAlphaAgentKeysApi.credentials.d.ts +11 -0
  7. package/dist/credentials/RoleAlphaAgentKeysApi.credentials.js +35 -0
  8. package/dist/credentials/RoleAlphaAgentKeysApi.credentials.js.map +1 -0
  9. package/dist/credentials/RoleAlphaWebhookApi.credentials.d.ts +8 -0
  10. package/dist/credentials/RoleAlphaWebhookApi.credentials.js +23 -0
  11. package/dist/credentials/RoleAlphaWebhookApi.credentials.js.map +1 -0
  12. package/dist/lib/agentRun.d.ts +73 -0
  13. package/dist/lib/agentRun.js +170 -0
  14. package/dist/lib/agentRun.js.map +1 -0
  15. package/dist/lib/agents.d.ts +19 -0
  16. package/dist/lib/agents.js +91 -0
  17. package/dist/lib/agents.js.map +1 -0
  18. package/dist/lib/graph.d.ts +155 -0
  19. package/dist/lib/graph.js +380 -0
  20. package/dist/lib/graph.js.map +1 -0
  21. package/dist/lib/keys.d.ts +12 -0
  22. package/dist/lib/keys.js +53 -0
  23. package/dist/lib/keys.js.map +1 -0
  24. package/dist/lib/mcp.d.ts +52 -0
  25. package/dist/lib/mcp.js +201 -0
  26. package/dist/lib/mcp.js.map +1 -0
  27. package/dist/lib/prompt.d.ts +52 -0
  28. package/dist/lib/prompt.js +180 -0
  29. package/dist/lib/prompt.js.map +1 -0
  30. package/dist/lib/schedule.d.ts +37 -0
  31. package/dist/lib/schedule.js +106 -0
  32. package/dist/lib/schedule.js.map +1 -0
  33. package/dist/lib/seal.d.ts +22 -0
  34. package/dist/lib/seal.js +79 -0
  35. package/dist/lib/seal.js.map +1 -0
  36. package/dist/lib/settings.d.ts +55 -0
  37. package/dist/lib/settings.js +105 -0
  38. package/dist/lib/settings.js.map +1 -0
  39. package/dist/lib/share.d.ts +156 -0
  40. package/dist/lib/share.js +208 -0
  41. package/dist/lib/share.js.map +1 -0
  42. package/dist/lib/waits.d.ts +142 -0
  43. package/dist/lib/waits.js +339 -0
  44. package/dist/lib/waits.js.map +1 -0
  45. package/dist/lib/webhook.d.ts +12 -0
  46. package/dist/lib/webhook.js +50 -0
  47. package/dist/lib/webhook.js.map +1 -0
  48. package/dist/nodes/ApprovalSeal/ApprovalSeal.node.d.ts +9 -0
  49. package/dist/nodes/ApprovalSeal/ApprovalSeal.node.js +141 -0
  50. package/dist/nodes/ApprovalSeal/ApprovalSeal.node.js.map +1 -0
  51. package/dist/nodes/ApprovalSeal/approvalSeal.svg +1 -0
  52. package/dist/nodes/RoleAlpha/RoleAlpha.node.d.ts +5 -0
  53. package/dist/nodes/RoleAlpha/RoleAlpha.node.js +1099 -0
  54. package/dist/nodes/RoleAlpha/RoleAlpha.node.js.map +1 -0
  55. package/dist/nodes/RoleAlpha/roleAlpha.svg +1 -0
  56. package/dist/nodes/RoleAlphaTrigger/RoleAlphaTrigger.node.d.ts +9 -0
  57. package/dist/nodes/RoleAlphaTrigger/RoleAlphaTrigger.node.js +61 -0
  58. package/dist/nodes/RoleAlphaTrigger/RoleAlphaTrigger.node.js.map +1 -0
  59. package/dist/nodes/RoleAlphaTrigger/roleAlpha.svg +1 -0
  60. package/package.json +56 -4
@@ -0,0 +1,1099 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RoleAlpha = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const mcp_1 = require("../../lib/mcp");
6
+ const graph_1 = require("../../lib/graph");
7
+ const agents_1 = require("../../lib/agents");
8
+ const keys_1 = require("../../lib/keys");
9
+ const prompt_1 = require("../../lib/prompt");
10
+ const schedule_1 = require("../../lib/schedule");
11
+ const share_1 = require("../../lib/share");
12
+ const waits_1 = require("../../lib/waits");
13
+ const settings_1 = require("../../lib/settings");
14
+ const agentRun_1 = require("../../lib/agentRun");
15
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
16
+ const show = (resource, operation) => ({ show: { resource: [resource], operation } });
17
+ const stepParam = (operations) => ({
18
+ displayName: 'Step',
19
+ name: 'step',
20
+ type: 'string',
21
+ default: '',
22
+ required: true,
23
+ placeholder: 'UUID or custom ID, e.g. marketing:pr-approval',
24
+ description: 'Process step by UUID or by its custom ID in roleALPHA',
25
+ displayOptions: show('responsibility', operations),
26
+ });
27
+ const properties = [
28
+ {
29
+ displayName: 'Resource',
30
+ name: 'resource',
31
+ type: 'options',
32
+ noDataExpression: true,
33
+ options: [
34
+ { name: 'Agent', value: 'agent', description: 'Read an agent\'s context and list the agents of the organisation' },
35
+ { name: 'Responsibility', value: 'responsibility', description: 'Traverse roles, steps and deciders' },
36
+ { name: 'Prompt', value: 'prompt', description: 'Build a system prompt from roleALPHA data' },
37
+ { name: 'Schedule', value: 'schedule', description: 'Decide whether a step is due' },
38
+ { name: 'Settings', value: 'settings', description: 'Runner limits from defaults and a settings table (global, step, agent)' },
39
+ { name: 'Share', value: 'share', description: 'The part of a step that belongs to the agent\'s roles, and what it needs from other roles' },
40
+ { name: 'Tool', value: 'tool', description: 'List and call roleALPHA MCP tools, e.g. as the read tool of an AI agent' },
41
+ { name: 'Wait', value: 'wait', description: 'Wait for and wake other agents within one step' },
42
+ ],
43
+ default: 'agent',
44
+ },
45
+ // ---------------------------------------------------------------- agent
46
+ {
47
+ displayName: 'Operation',
48
+ name: 'operation',
49
+ type: 'options',
50
+ noDataExpression: true,
51
+ displayOptions: { show: { resource: ['agent'] } },
52
+ options: [
53
+ { name: 'Get Context', value: 'getContext', action: 'Get the context of an agent', description: 'Identity, roles and guardrails of one agent' },
54
+ { name: 'Get Context Version', value: 'getContextVersion', action: 'Get the context version of an agent', description: 'Change check; cheap and not audited with agent keys' },
55
+ { name: 'List Agents', value: 'listAgents', action: 'List agents', description: 'One item per agent of the organisation (MCP) or per key in the credential' },
56
+ { name: 'Start Run', value: 'startRun', action: 'Start a run as an agent', description: 'Swaps the function account token for a run token of the agent (agent:run). A refusal comes back as data with its code.' },
57
+ ],
58
+ default: 'getContext',
59
+ },
60
+ {
61
+ displayName: 'Source',
62
+ name: 'source',
63
+ type: 'options',
64
+ noDataExpression: true,
65
+ displayOptions: { show: { resource: ['agent'] } },
66
+ options: [
67
+ { name: 'MCP (OAuth2)', value: 'mcp', description: 'One connection for all agents of the organisation; new agents run without extra setup' },
68
+ { name: 'Agent Keys', value: 'agentKey', description: 'One agt_ key per agent, read over the data API' },
69
+ ],
70
+ default: 'mcp',
71
+ },
72
+ {
73
+ displayName: 'Agent UUID',
74
+ name: 'agentUuid',
75
+ type: 'string',
76
+ default: '',
77
+ required: true,
78
+ displayOptions: show('agent', ['getContext', 'getContextVersion']),
79
+ },
80
+ {
81
+ displayName: 'Options',
82
+ name: 'agentOptions',
83
+ type: 'collection',
84
+ placeholder: 'Add Option',
85
+ default: {},
86
+ displayOptions: show('agent', ['getContext', 'listAgents']),
87
+ options: [
88
+ {
89
+ displayName: 'Require Active',
90
+ name: 'requireActive',
91
+ type: 'boolean',
92
+ default: true,
93
+ description: 'Whether to fail (Get Context) or skip (List) agents that are not active in roleALPHA',
94
+ },
95
+ {
96
+ displayName: 'Require Guardrails',
97
+ name: 'requireGuardrails',
98
+ type: 'boolean',
99
+ default: true,
100
+ description: 'Whether to fail when the agent is bound by no policy',
101
+ },
102
+ {
103
+ displayName: 'Include Context',
104
+ name: 'includeContext',
105
+ type: 'boolean',
106
+ default: true,
107
+ description: 'Whether List Agents loads each context (status, roles, guardrails)',
108
+ },
109
+ {
110
+ displayName: 'View',
111
+ name: 'view',
112
+ type: 'options',
113
+ options: [
114
+ { name: 'Agent', value: 'agent', description: 'Exactly the bundle the agent receives over its own key (needs admin rights on the tenant)' },
115
+ { name: 'Caller', value: 'caller', description: 'The bundle as visible through the connection\'s own permissions' },
116
+ ],
117
+ default: 'agent',
118
+ description: 'MCP only: which view of the context bundle to read',
119
+ },
120
+ ],
121
+ },
122
+ // ---------------------------------------------------------------- responsibility
123
+ {
124
+ displayName: 'Operation',
125
+ name: 'operation',
126
+ type: 'options',
127
+ noDataExpression: true,
128
+ displayOptions: { show: { resource: ['responsibility'] } },
129
+ options: [
130
+ { name: 'Check Approver', value: 'checkApprover', action: 'Check whether a person may decide on a step', description: 'Fail-closed: deciding role first, then the person filling it' },
131
+ { name: 'Check Responsibility', value: 'checkResponsibility', action: 'Check whether roles are responsible for a step' },
132
+ { name: 'Find Step by Custom ID', value: 'findStep', action: 'Find a step by custom ID' },
133
+ { name: 'Get Circle Lead', value: 'getCircleLead', action: 'Get the role leading the circle', description: 'One item per circle of the roles: the leading role and its holders, e.g. to escalate to' },
134
+ { name: 'Get Decider', value: 'getDecider', action: 'Get the deciding role and its holders', description: 'DECIDES_ON, else ACCOUNTABLE_FOR' },
135
+ { name: 'Get Duties', value: 'getDuties', action: 'Get the steps an agent or roles are responsible for' },
136
+ { name: 'Get Role Holders', value: 'getRoleHolders', action: 'Get persons and agents filling a role' },
137
+ { name: 'Get Step', value: 'getStep', action: 'Get a step with neighbours, roles and policies' },
138
+ ],
139
+ default: 'getDuties',
140
+ },
141
+ {
142
+ displayName: 'Connect As',
143
+ name: 'connectAs',
144
+ type: 'options',
145
+ noDataExpression: true,
146
+ displayOptions: { show: { resource: ['agent', 'responsibility', 'tool'] }, hide: { source: ['agentKey'], operation: ['startRun', 'listAgents'] } },
147
+ options: [
148
+ { name: 'Function Account', value: 'account', description: 'The view of the connection itself' },
149
+ { name: 'Agent (Run Token)', value: 'agent', description: 'Exactly the view of the agent of this run (needs agent:run and a function account with client credentials)' },
150
+ ],
151
+ default: 'account',
152
+ },
153
+ {
154
+ displayName: 'Run Agent UUID',
155
+ name: 'runAgentUuid',
156
+ type: 'string',
157
+ default: '',
158
+ required: true,
159
+ description: 'The agent this run acts as',
160
+ displayOptions: { show: { resource: ['agent', 'responsibility', 'tool'], connectAs: ['agent'] }, hide: { source: ['agentKey'], operation: ['startRun', 'listAgents'] } },
161
+ },
162
+ {
163
+ displayName: 'Run Agent UUID',
164
+ name: 'runAgentUuid',
165
+ type: 'string',
166
+ default: '',
167
+ required: true,
168
+ description: 'The agent this run acts as',
169
+ displayOptions: show('agent', ['startRun']),
170
+ },
171
+ {
172
+ displayName: 'Run ID',
173
+ name: 'runId',
174
+ type: 'string',
175
+ default: '={{ $execution.id }}',
176
+ description: 'Names the run in every audit entry; 1-128 characters of A-Z a-z 0-9 . _ : -. Sub-workflows of the run pass the same ID.',
177
+ displayOptions: { show: { resource: ['agent', 'responsibility', 'tool'], connectAs: ['agent'] }, hide: { source: ['agentKey'], operation: ['startRun', 'listAgents'] } },
178
+ },
179
+ {
180
+ displayName: 'Run ID',
181
+ name: 'runId',
182
+ type: 'string',
183
+ default: '={{ $execution.id }}',
184
+ description: 'Names the run in every audit entry; 1-128 characters of A-Z a-z 0-9 . _ : -. Sub-workflows of the run pass the same ID.',
185
+ displayOptions: show('agent', ['startRun']),
186
+ },
187
+ {
188
+ displayName: 'MCP Endpoint URL',
189
+ name: 'endpointUrl',
190
+ type: 'string',
191
+ default: 'https://rolealpha.app/api/mcp',
192
+ required: true,
193
+ displayOptions: { show: { resource: ['agent', 'responsibility', 'tool'] }, hide: { source: ['agentKey'] } },
194
+ },
195
+ {
196
+ displayName: 'Organisation',
197
+ name: 'organisation',
198
+ type: 'string',
199
+ default: '',
200
+ required: true,
201
+ description: 'Organisation (tenant) name or ID as roleALPHA knows it',
202
+ displayOptions: { show: { resource: ['agent', 'responsibility', 'tool'] }, hide: { source: ['agentKey'] } },
203
+ },
204
+ {
205
+ displayName: 'Agent UUID',
206
+ name: 'agentUuid',
207
+ type: 'string',
208
+ default: '',
209
+ description: 'Read the roles this agent fulfils. Leave empty to pass role UUIDs instead.',
210
+ displayOptions: show('responsibility', ['getDuties', 'checkResponsibility', 'getCircleLead']),
211
+ },
212
+ {
213
+ displayName: 'Role UUIDs',
214
+ name: 'roleUuids',
215
+ type: 'string',
216
+ default: '',
217
+ description: 'Comma-separated role UUIDs, or an array via expression. Used when no agent UUID is given.',
218
+ displayOptions: show('responsibility', ['getDuties', 'checkResponsibility', 'getCircleLead']),
219
+ },
220
+ {
221
+ displayName: 'Custom ID',
222
+ name: 'customId',
223
+ type: 'string',
224
+ default: '',
225
+ required: true,
226
+ displayOptions: show('responsibility', ['findStep']),
227
+ },
228
+ stepParam(['getStep', 'getDecider', 'checkApprover', 'checkResponsibility']),
229
+ {
230
+ displayName: 'Role UUID',
231
+ name: 'roleUuid',
232
+ type: 'string',
233
+ default: '',
234
+ required: true,
235
+ displayOptions: show('responsibility', ['getRoleHolders']),
236
+ },
237
+ {
238
+ displayName: 'Acting Person',
239
+ name: 'actor',
240
+ type: 'string',
241
+ default: '',
242
+ required: true,
243
+ placeholder: 'name@example.com or Name <name@example.com>',
244
+ description: 'Address of the person who wants to decide, as the channel reports it',
245
+ displayOptions: show('responsibility', ['checkApprover']),
246
+ },
247
+ {
248
+ displayName: 'Options',
249
+ name: 'responsibilityOptions',
250
+ type: 'collection',
251
+ placeholder: 'Add Option',
252
+ default: {},
253
+ displayOptions: { show: { resource: ['responsibility'] } },
254
+ options: [
255
+ {
256
+ displayName: 'Resolve Holders',
257
+ name: 'resolveHolders',
258
+ type: 'boolean',
259
+ default: true,
260
+ description: 'Whether Get Step also reads the persons and agents filling each role',
261
+ },
262
+ {
263
+ displayName: 'Role Details',
264
+ name: 'roleDetails',
265
+ type: 'boolean',
266
+ default: false,
267
+ description: 'Whether Get Step also reads purpose, accountabilities and domains of each responsible role (needed for Share → Build Input)',
268
+ },
269
+ {
270
+ displayName: 'Fail If Not Found',
271
+ name: 'failIfNotFound',
272
+ type: 'boolean',
273
+ default: true,
274
+ description: 'Whether Find Step fails when no step has this custom ID',
275
+ },
276
+ {
277
+ displayName: 'Fail If Denied',
278
+ name: 'failIfDenied',
279
+ type: 'boolean',
280
+ default: false,
281
+ description: 'Whether Check Approver / Check Responsibility fail instead of returning allowed = false',
282
+ },
283
+ {
284
+ displayName: 'One Item per Duty',
285
+ name: 'splitDuties',
286
+ type: 'boolean',
287
+ default: true,
288
+ description: 'Whether Get Duties returns one item per role and step',
289
+ },
290
+ ],
291
+ },
292
+ // ---------------------------------------------------------------- prompt
293
+ {
294
+ displayName: 'Operation',
295
+ name: 'operation',
296
+ type: 'options',
297
+ noDataExpression: true,
298
+ displayOptions: { show: { resource: ['prompt'] } },
299
+ options: [{ name: 'Build System Prompt', value: 'buildSystemPrompt', action: 'Build a system prompt' }],
300
+ default: 'buildSystemPrompt',
301
+ },
302
+ {
303
+ displayName: 'Context Bundle',
304
+ name: 'bundle',
305
+ type: 'json',
306
+ default: '={{ $json }}',
307
+ required: true,
308
+ description: 'Output of Agent → Get Context',
309
+ displayOptions: { show: { resource: ['prompt', 'share'], operation: ['buildSystemPrompt', 'buildInput'] } },
310
+ },
311
+ {
312
+ displayName: 'Step',
313
+ name: 'stepJson',
314
+ type: 'json',
315
+ default: '',
316
+ description: 'Output of Responsibility → Get Step (optional for the prompt, required for Share)',
317
+ displayOptions: { show: { resource: ['prompt', 'share'], operation: ['buildSystemPrompt', 'buildInput'] } },
318
+ },
319
+ {
320
+ displayName: 'Duties',
321
+ name: 'duties',
322
+ type: 'json',
323
+ default: '',
324
+ description: 'Array of duties from Responsibility → Get Duties (optional)',
325
+ displayOptions: show('prompt', ['buildSystemPrompt']),
326
+ },
327
+ {
328
+ displayName: 'Trigger Type',
329
+ name: 'triggerType',
330
+ type: 'string',
331
+ default: '',
332
+ placeholder: 'schedule, event, chat …',
333
+ displayOptions: { show: { resource: ['prompt', 'share'], operation: ['buildSystemPrompt', 'buildInput'] } },
334
+ },
335
+ {
336
+ displayName: 'Event Data',
337
+ name: 'event',
338
+ type: 'json',
339
+ default: '',
340
+ description: 'Shown to the model as data, never as instructions',
341
+ displayOptions: { show: { resource: ['prompt', 'share'], operation: ['buildSystemPrompt', 'buildInput'] } },
342
+ },
343
+ {
344
+ displayName: 'Share',
345
+ name: 'share',
346
+ type: 'json',
347
+ default: '',
348
+ description: 'Output of Share → Check (optional). Adds the agent\'s part, the rules for waiting within the step and a task text.',
349
+ displayOptions: show('prompt', ['buildSystemPrompt']),
350
+ },
351
+ {
352
+ displayName: 'Additional Rules',
353
+ name: 'extraRules',
354
+ type: 'string',
355
+ typeOptions: { rows: 4 },
356
+ default: '',
357
+ description: 'One technical rule per line, e.g. output format. No roles, persons or duties.',
358
+ displayOptions: show('prompt', ['buildSystemPrompt']),
359
+ },
360
+ // ---------------------------------------------------------------- share
361
+ {
362
+ displayName: 'Operation',
363
+ name: 'operation',
364
+ type: 'options',
365
+ noDataExpression: true,
366
+ displayOptions: { show: { resource: ['share'] } },
367
+ options: [
368
+ { name: 'Build Input', value: 'buildInput', action: 'Build the input for the share decision', description: 'System message, prompt and ids for a model call; the step needs Role Details' },
369
+ { name: 'Check', value: 'check', action: 'Check the share decision', description: 'Keeps only real accountabilities and real other roles of the step' },
370
+ ],
371
+ default: 'buildInput',
372
+ },
373
+ {
374
+ displayName: 'Responsible Roles',
375
+ name: 'roles',
376
+ type: 'json',
377
+ default: '',
378
+ description: 'The agent\'s roles that are responsible for the step: output of Responsibility → Check Responsibility, an array of roles or of UUIDs. Empty: every role of the bundle on the step.',
379
+ displayOptions: show('share', ['buildInput']),
380
+ },
381
+ {
382
+ displayName: 'Share Input',
383
+ name: 'shareInput',
384
+ type: 'json',
385
+ default: '',
386
+ required: true,
387
+ description: 'Output of Share → Build Input',
388
+ displayOptions: show('share', ['check']),
389
+ },
390
+ {
391
+ displayName: 'Model Answer',
392
+ name: 'answer',
393
+ type: 'json',
394
+ default: '={{ $json.output }}',
395
+ required: true,
396
+ description: 'The structured answer of the model (duties, notMine, reason)',
397
+ displayOptions: show('share', ['check']),
398
+ },
399
+ // ---------------------------------------------------------------- wait
400
+ {
401
+ displayName: 'Operation',
402
+ name: 'operation',
403
+ type: 'options',
404
+ noDataExpression: true,
405
+ displayOptions: { show: { resource: ['wait'] } },
406
+ options: [
407
+ { name: 'Plan After Run', value: 'planAfterRun', action: 'Plan what follows a run', description: 'Whether the agent waited, which row to add, whom to wake and which rows to remove' },
408
+ { name: 'Check Start', value: 'checkStart', action: 'Check whether a run may start', description: 'Duplicate-run guard: after adding this run\'s lease, the oldest live lease wins; a later start is queued' },
409
+ { name: 'Find Stale Waits', value: 'findStale', action: 'Find waits nobody answered', description: 'For a scheduler: unanswered waits, orphaned queued starts, dead leases, expired rows and waits to escalate' },
410
+ ],
411
+ default: 'planAfterRun',
412
+ },
413
+ {
414
+ displayName: 'Rows',
415
+ name: 'rows',
416
+ type: 'json',
417
+ default: '={{ $input.all().map(item => item.json) }}',
418
+ description: 'Stored wait rows (state, step_uuid, agent_uuid, roles, waits_for, record_keys, event, run_id, attempts, progress, at)',
419
+ displayOptions: { show: { resource: ['wait'] } },
420
+ },
421
+ {
422
+ displayName: 'Finished Run',
423
+ name: 'run',
424
+ type: 'json',
425
+ default: '',
426
+ required: true,
427
+ placeholder: '{"runId":"","stepUuid":"","agentUuid":"","roleUuids":[],"recordKeys":[],"startedAt":"","event":{},"attempt":0}',
428
+ description: 'The run that just ended (Plan After Run) or wants to start (Check Start)',
429
+ displayOptions: show('wait', ['planAfterRun', 'checkStart']),
430
+ },
431
+ {
432
+ displayName: 'Settings Rows',
433
+ name: 'settingRows',
434
+ type: 'json',
435
+ default: '',
436
+ description: 'Optional rows of the settings table (scope, target, key, value). Limits per agent and step from these rows win over the options below.',
437
+ displayOptions: { show: { resource: ['wait'] } },
438
+ },
439
+ {
440
+ displayName: 'Options',
441
+ name: 'waitOptions',
442
+ type: 'collection',
443
+ placeholder: 'Add Option',
444
+ default: {},
445
+ displayOptions: { show: { resource: ['wait'] } },
446
+ options: [
447
+ { displayName: 'Max Attempts', name: 'maxAttempts', type: 'number', typeOptions: { minValue: 1 }, default: 5, description: 'How often one agent is woken for the same step and records' },
448
+ { displayName: 'Keep Done Rows (Hours)', name: 'keepDoneHours', type: 'number', typeOptions: { minValue: 1 }, default: 168 },
449
+ { displayName: 'Older Than (Minutes)', name: 'olderThanMinutes', type: 'number', typeOptions: { minValue: 1 }, default: 60, description: 'Find Stale Waits: only waits at least this old' },
450
+ { displayName: 'Run Timeout (Minutes)', name: 'runTimeoutMinutes', type: 'number', typeOptions: { minValue: 1 }, default: 15, description: 'Leases older than this count as dead' },
451
+ ],
452
+ },
453
+ // ---------------------------------------------------------------- settings
454
+ {
455
+ displayName: 'Operation',
456
+ name: 'operation',
457
+ type: 'options',
458
+ noDataExpression: true,
459
+ displayOptions: { show: { resource: ['settings'] } },
460
+ options: [{ name: 'Resolve', value: 'resolve', action: 'Resolve runner settings', description: `Defaults < global < step < agent; every value clamped to fixed bounds (${Object.entries(settings_1.SETTING_BOUNDS).map(([k, b]) => `${k} ${b.min}–${b.max}`).join(', ')})` }],
461
+ default: 'resolve',
462
+ },
463
+ {
464
+ displayName: 'Settings Rows',
465
+ name: 'settingRows',
466
+ type: 'json',
467
+ default: '',
468
+ description: 'Rows of the settings table: scope (global, step, agent), target (step UUID or custom ID, agent UUID), key, value. Empty: defaults only.',
469
+ displayOptions: show('settings', ['resolve']),
470
+ },
471
+ {
472
+ displayName: 'Agent UUID',
473
+ name: 'settingsAgent',
474
+ type: 'string',
475
+ default: '',
476
+ displayOptions: show('settings', ['resolve']),
477
+ },
478
+ {
479
+ displayName: 'Step',
480
+ name: 'settingsStep',
481
+ type: 'string',
482
+ default: '',
483
+ description: 'Step UUID',
484
+ displayOptions: show('settings', ['resolve']),
485
+ },
486
+ {
487
+ displayName: 'Step Custom ID',
488
+ name: 'settingsStepCustomId',
489
+ type: 'string',
490
+ default: '',
491
+ displayOptions: show('settings', ['resolve']),
492
+ },
493
+ // ---------------------------------------------------------------- schedule
494
+ {
495
+ displayName: 'Operation',
496
+ name: 'operation',
497
+ type: 'options',
498
+ noDataExpression: true,
499
+ displayOptions: { show: { resource: ['schedule'] } },
500
+ options: [{ name: 'Is Due', value: 'isDue', action: 'Decide whether a step is due' }],
501
+ default: 'isDue',
502
+ },
503
+ {
504
+ displayName: 'Frequency',
505
+ name: 'frequency',
506
+ type: 'string',
507
+ default: '={{ $json.step?.frequency }}',
508
+ description: 'daily, weekly, biweekly, monthly, quarterly, semiannual, annual; event_driven and on_demand never run on a schedule',
509
+ displayOptions: show('schedule', ['isDue']),
510
+ },
511
+ {
512
+ displayName: 'Due Date',
513
+ name: 'dueDate',
514
+ type: 'string',
515
+ default: '={{ $json.step?.dueDate }}',
516
+ description: 'Optional date (YYYY-MM-DD) from the step; if set, the step runs once on or after it',
517
+ displayOptions: show('schedule', ['isDue']),
518
+ },
519
+ {
520
+ displayName: 'Last Run At',
521
+ name: 'lastRunAt',
522
+ type: 'string',
523
+ default: '',
524
+ description: 'When this agent last ran this step (ISO date). Empty means never.',
525
+ displayOptions: show('schedule', ['isDue']),
526
+ },
527
+ {
528
+ displayName: 'Options',
529
+ name: 'scheduleOptions',
530
+ type: 'collection',
531
+ placeholder: 'Add Option',
532
+ default: {},
533
+ displayOptions: show('schedule', ['isDue']),
534
+ options: [
535
+ { displayName: 'Time Zone', name: 'timeZone', type: 'string', default: 'Europe/Berlin' },
536
+ { displayName: 'Earliest ISO Weekday', name: 'weekday', type: 'number', typeOptions: { minValue: 1, maxValue: 7 }, default: 1, description: 'Weekly and biweekly steps start on or after this day (1 = Monday)' },
537
+ { displayName: 'Not Before Hour', name: 'notBeforeHour', type: 'number', typeOptions: { minValue: 0, maxValue: 23 }, default: 0 },
538
+ { displayName: 'Only Due Items', name: 'onlyDue', type: 'boolean', default: false, description: 'Whether to drop items that are not due' },
539
+ ],
540
+ },
541
+ // ---------------------------------------------------------------- tool
542
+ {
543
+ displayName: 'Operation',
544
+ name: 'operation',
545
+ type: 'options',
546
+ noDataExpression: true,
547
+ displayOptions: { show: { resource: ['tool'] } },
548
+ options: [
549
+ { name: 'Call Tool', value: 'callTool', action: 'Call an MCP tool', description: 'Calls one allowed tool; tool name "list" returns the allowed tools with their arguments. Errors come back as data, so a model can react.' },
550
+ { name: 'List Tools', value: 'listTools', action: 'List the allowed MCP tools' },
551
+ ],
552
+ default: 'callTool',
553
+ },
554
+ {
555
+ displayName: 'Allowed Tools',
556
+ name: 'allowedTools',
557
+ type: 'string',
558
+ default: 'get_entity_details,get_entity_relationships,search_entities,search_persons,find_responsible',
559
+ required: true,
560
+ description: 'Comma-separated tool names. Any other tool is refused, even if the token could call it.',
561
+ displayOptions: { show: { resource: ['tool'] } },
562
+ },
563
+ {
564
+ displayName: 'Tool Name',
565
+ name: 'toolName',
566
+ type: 'string',
567
+ default: '',
568
+ description: 'One of the allowed tools, or "list"',
569
+ displayOptions: show('tool', ['callTool']),
570
+ },
571
+ {
572
+ displayName: 'Tool Arguments',
573
+ name: 'toolArguments',
574
+ type: 'string',
575
+ default: '{}',
576
+ description: 'Arguments as JSON text. The organisation is added when missing.',
577
+ displayOptions: show('tool', ['callTool']),
578
+ },
579
+ ];
580
+ // -------------------------------------------------------------------- helpers
581
+ function parseJsonParam(ctx, name, i) {
582
+ const value = ctx.getNodeParameter(name, i, '');
583
+ if (value === '' || value === null || value === undefined)
584
+ return undefined;
585
+ if (typeof value !== 'string')
586
+ return value;
587
+ try {
588
+ return JSON.parse(value);
589
+ }
590
+ catch {
591
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Parameter "${name}" is not valid JSON`, { itemIndex: i });
592
+ }
593
+ }
594
+ function uuidList(value) {
595
+ const raw = Array.isArray(value) ? value : String(value ?? '').split(',');
596
+ return raw.map((v) => String(v).trim()).filter((v) => UUID.test(v));
597
+ }
598
+ async function agentRequest(ctx, baseUrl, keyring, agentUuid, path) {
599
+ const uuid = agentUuid.trim().toLowerCase();
600
+ if (!UUID.test(uuid))
601
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Not an agent UUID: "${agentUuid}"`);
602
+ return (await ctx.helpers.httpRequest({
603
+ method: 'GET',
604
+ url: `${baseUrl.replace(/\/+$/, '')}/api/agents/${uuid}${path}`,
605
+ headers: { 'x-agent-key': (0, keys_1.keyFor)(keyring, uuid), Accept: 'application/json' },
606
+ json: true,
607
+ }));
608
+ }
609
+ function summarizeBundle(bundle) {
610
+ const roles = (bundle.context ?? []).filter((c) => c.kind === 'role');
611
+ return {
612
+ agentUuid: bundle.agent?.uuid,
613
+ agentName: bundle.agent?.name,
614
+ status: bundle.agent?.status ?? null,
615
+ contextVersion: bundle.contextVersion ?? null,
616
+ roleUuids: roles.map((r) => r.uuid),
617
+ roleNames: roles.map((r) => r.name),
618
+ guardrailCount: (bundle.guardrails ?? []).length,
619
+ };
620
+ }
621
+ const formPost = (ctx) => async (url, form, headers) => {
622
+ const res = (await ctx.helpers.httpRequest({
623
+ method: 'POST',
624
+ url,
625
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json', ...headers },
626
+ body: new URLSearchParams(form).toString(),
627
+ json: false,
628
+ encoding: 'text',
629
+ returnFullResponse: true,
630
+ ignoreHttpStatusErrors: true,
631
+ }));
632
+ return { status: res.statusCode ?? 0, body: typeof res.body === 'string' ? res.body : JSON.stringify(res.body ?? '') };
633
+ };
634
+ /** The function account behind the MCP credential; a run token needs client credentials. */
635
+ async function functionAccount(ctx) {
636
+ const c = await ctx.getCredentials('mcpOAuth2Api');
637
+ if (c.grantType !== 'clientCredentials' || !c.accessTokenUrl || !c.clientId || !c.clientSecret) {
638
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'Acting as an agent needs a function account: an MCP OAuth2 credential with grant type Client Credentials');
639
+ }
640
+ return { tokenUrl: String(c.accessTokenUrl), clientId: String(c.clientId), clientSecret: String(c.clientSecret), authentication: c.authentication === 'body' ? 'body' : 'header' };
641
+ }
642
+ function refusedRun(ctx, e, i) {
643
+ return new n8n_workflow_1.NodeOperationError(ctx.getNode(), `roleALPHA refused the run: ${e.code}`, { itemIndex: i, description: `${e.description} (reaction: ${e.reaction})` });
644
+ }
645
+ /** MCP under a run token: one swap per run, swapped again on 401; a refusal ends the run. */
646
+ function agentTransport(ctx, endpointUrl, auth) {
647
+ return async (body, headers) => {
648
+ const send = async (token) => (await ctx.helpers.httpRequest({
649
+ method: 'POST',
650
+ url: endpointUrl,
651
+ headers: { ...headers, Authorization: `Bearer ${token}` },
652
+ body: JSON.stringify(body),
653
+ json: false,
654
+ encoding: 'text',
655
+ returnFullResponse: true,
656
+ ignoreHttpStatusErrors: true,
657
+ }));
658
+ let res = await send((await auth.tokens.runToken(auth.account, auth.agentUuid, auth.runId)).token);
659
+ if (res.statusCode === 401) {
660
+ auth.tokens.forget(auth.account, auth.agentUuid, auth.runId);
661
+ res = await send((await auth.tokens.runToken(auth.account, auth.agentUuid, auth.runId, { fresh: true })).token);
662
+ }
663
+ const text = typeof res.body === 'string' ? res.body : res.body === undefined ? '' : JSON.stringify(res.body);
664
+ if (res.statusCode === 401 || res.statusCode === 403)
665
+ throw new agentRun_1.AgentRunError((0, agentRun_1.mcpRefusalCode)(text) || `http_${res.statusCode}`, res.statusCode, text.slice(0, 300));
666
+ return { status: res.statusCode ?? 200, headers: res.headers ?? {}, body: text };
667
+ };
668
+ }
669
+ function mcpClientFor(ctx, endpointUrl, auth) {
670
+ if (auth)
671
+ return new mcp_1.McpClient(agentTransport(ctx, endpointUrl, auth));
672
+ const transport = async (body, headers) => {
673
+ try {
674
+ const res = (await ctx.helpers.httpRequestWithAuthentication.call(ctx, 'mcpOAuth2Api', {
675
+ method: 'POST',
676
+ url: endpointUrl,
677
+ headers,
678
+ body: JSON.stringify(body),
679
+ json: false,
680
+ encoding: 'text',
681
+ returnFullResponse: true,
682
+ }));
683
+ const text = typeof res.body === 'string' ? res.body : res.body === undefined ? '' : JSON.stringify(res.body);
684
+ return { status: res.statusCode ?? 200, headers: res.headers ?? {}, body: text };
685
+ }
686
+ catch (error) {
687
+ const e = error;
688
+ return { status: Number(e.httpCode) || 500, headers: {}, body: String(e.description ?? e.message ?? 'request failed') };
689
+ }
690
+ };
691
+ return new mcp_1.McpClient(transport);
692
+ }
693
+ async function resolveStepUuid(ctx, graph, value, i) {
694
+ const v = value.trim();
695
+ if (UUID.test(v))
696
+ return v;
697
+ const step = await (0, graph_1.findStepByCustomId)(graph, v);
698
+ if (!step)
699
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `No step with custom ID "${v}"`, { itemIndex: i });
700
+ return step.uuid;
701
+ }
702
+ // -------------------------------------------------------------------- node
703
+ class RoleAlpha {
704
+ description = {
705
+ displayName: 'roleALPHA',
706
+ name: 'roleAlpha',
707
+ icon: 'file:roleAlpha.svg',
708
+ group: ['transform'],
709
+ version: 1,
710
+ subtitle: '={{ $parameter["resource"] + ": " + $parameter["operation"] }}',
711
+ description: 'Run agents from roleALPHA: own context, duties, deciders and system prompts',
712
+ defaults: { name: 'roleALPHA' },
713
+ usableAsTool: true,
714
+ inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
715
+ outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
716
+ credentials: [
717
+ { name: 'roleAlphaAgentKeysApi', required: true, displayOptions: { show: { resource: ['agent'], source: ['agentKey'] } } },
718
+ { name: 'mcpOAuth2Api', required: true, displayOptions: { show: { resource: ['agent', 'responsibility', 'tool'] }, hide: { source: ['agentKey'] } } },
719
+ ],
720
+ properties,
721
+ };
722
+ async execute() {
723
+ const items = this.getInputData();
724
+ const out = [];
725
+ const resource = this.getNodeParameter('resource', 0);
726
+ const operation = this.getNodeParameter('operation', 0);
727
+ const source = resource === 'agent' ? this.getNodeParameter('source', 0, 'mcp') || 'mcp' : '';
728
+ let keyring = [];
729
+ let baseUrl = '';
730
+ if (source === 'agentKey') {
731
+ const creds = await this.getCredentials('roleAlphaAgentKeysApi');
732
+ baseUrl = String(creds.baseUrl ?? '');
733
+ try {
734
+ keyring = (0, keys_1.parseKeyring)(String(creds.keys ?? ''));
735
+ }
736
+ catch (error) {
737
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `roleALPHA Agent Keys: ${error.message}`);
738
+ }
739
+ }
740
+ // Acting as an agent: every MCP call of this node runs under the run token of that agent.
741
+ const startRun = resource === 'agent' && operation === 'startRun';
742
+ const usesMcp = ['agent', 'responsibility', 'tool'].includes(resource) && source !== 'agentKey';
743
+ const asAgent = startRun || (usesMcp && operation !== 'listAgents' && this.getNodeParameter('connectAs', 0, 'account') === 'agent');
744
+ let runAuth;
745
+ if (asAgent) {
746
+ runAuth = {
747
+ tokens: new agentRun_1.AgentRunTokens(formPost(this)),
748
+ account: await functionAccount(this),
749
+ agentUuid: String(this.getNodeParameter('runAgentUuid', 0, '') ?? '').trim().toLowerCase(),
750
+ runId: String(this.getNodeParameter('runId', 0, '') ?? '').trim(),
751
+ };
752
+ }
753
+ if (startRun) {
754
+ const auth = runAuth;
755
+ try {
756
+ const t = await auth.tokens.runToken(auth.account, auth.agentUuid, auth.runId);
757
+ // The token itself never leaves the node.
758
+ out.push({ json: { started: true, agentUuid: t.agent.uuid, agentName: t.agent.name, runId: t.runId, expiresAt: new Date(t.expiresAt).toISOString() } });
759
+ }
760
+ catch (error) {
761
+ if (!(error instanceof agentRun_1.AgentRunError))
762
+ throw error;
763
+ out.push({ json: { started: false, agentUuid: auth.agentUuid, runId: auth.runId, code: error.code, reaction: error.reaction, reason: error.description } });
764
+ }
765
+ return [out];
766
+ }
767
+ // One MCP session (and graph cache) per execution keeps traversals consistent and cheap.
768
+ let client;
769
+ let graph;
770
+ const organisationAt = (i) => String(this.getNodeParameter('organisation', i) ?? '').trim();
771
+ const getClient = (i) => (client ??= mcpClientFor(this, this.getNodeParameter('endpointUrl', i), runAuth));
772
+ const getGraph = (i) => (graph ??= new graph_1.McpGraphSource(getClient(i), organisationAt(i)));
773
+ // Reads one context bundle from the chosen source.
774
+ const loadBundle = async (agentUuid, i, opts) => {
775
+ if (source === 'agentKey')
776
+ return (await agentRequest(this, baseUrl, keyring, agentUuid, '/context'));
777
+ const uuid = agentUuid.trim().toLowerCase();
778
+ if (!UUID.test(uuid))
779
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Not an agent UUID: "${agentUuid}"`, { itemIndex: i });
780
+ // Under a run token the caller is the agent, so its own view needs no admin switch.
781
+ return (0, agents_1.getAgentContext)(getClient(i), organisationAt(i), uuid, (asAgent ? 'caller' : opts.view || 'agent'));
782
+ };
783
+ if (resource === 'agent' && operation === 'listAgents' && source === 'mcp') {
784
+ const opts = this.getNodeParameter('agentOptions', 0, {});
785
+ const includeContext = opts.includeContext !== false;
786
+ const requireActive = opts.requireActive !== false;
787
+ for (const entry of await (0, agents_1.listAgents)(getClient(0), organisationAt(0))) {
788
+ // Inactive agents are skipped early; Get Context checks the status again (fail closed).
789
+ if (requireActive && entry.status !== 'active')
790
+ continue;
791
+ const row = { ...entry };
792
+ if (includeContext) {
793
+ try {
794
+ Object.assign(row, summarizeBundle(await loadBundle(entry.agentUuid, 0, opts)));
795
+ }
796
+ catch (error) {
797
+ if (!this.continueOnFail())
798
+ throw error;
799
+ row.error = error.message;
800
+ }
801
+ if (requireActive && row.status !== 'active' && !row.error)
802
+ continue;
803
+ }
804
+ out.push({ json: row });
805
+ }
806
+ return [out];
807
+ }
808
+ if (resource === 'agent' && operation === 'listAgents') {
809
+ const opts = this.getNodeParameter('agentOptions', 0, {});
810
+ const includeContext = opts.includeContext !== false;
811
+ const requireActive = opts.requireActive !== false;
812
+ for (const entry of keyring) {
813
+ const row = { agentUuid: entry.agentUuid, label: entry.label };
814
+ if (includeContext) {
815
+ try {
816
+ const bundle = (await agentRequest(this, baseUrl, keyring, entry.agentUuid, '/context'));
817
+ Object.assign(row, summarizeBundle(bundle));
818
+ }
819
+ catch (error) {
820
+ if (!this.continueOnFail())
821
+ throw error;
822
+ row.error = error.message;
823
+ }
824
+ if (requireActive && row.status !== 'active' && !row.error)
825
+ continue;
826
+ }
827
+ out.push({ json: row });
828
+ }
829
+ return [out];
830
+ }
831
+ // Wait decisions look at all stored rows at once and return one plan.
832
+ if (resource === 'wait') {
833
+ const rowsRaw = parseJsonParam(this, 'rows', 0);
834
+ const rows = (Array.isArray(rowsRaw) ? rowsRaw : []).filter((r) => r && typeof r === 'object' && r.state);
835
+ const opts = this.getNodeParameter('waitOptions', 0, {});
836
+ const num = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : undefined);
837
+ const settingRowsRaw = parseJsonParam(this, 'settingRows', 0);
838
+ const settingRows = (Array.isArray(settingRowsRaw) ? settingRowsRaw : []);
839
+ // The options are the workflow's defaults; table rows (global < step < agent) win. Both are clamped to the same bounds.
840
+ const optionRows = Object.entries({ maxAttempts: opts.maxAttempts, keepDoneHours: opts.keepDoneHours, maxWaitMinutes: opts.olderThanMinutes, runTimeoutMinutes: opts.runTimeoutMinutes })
841
+ .filter(([, v]) => num(v) !== undefined)
842
+ .map(([key, value]) => ({ scope: 'global', key, value }));
843
+ const limitsFor = (t) => {
844
+ const r = (0, settings_1.resolveSettings)([...optionRows, ...settingRows], t).settings;
845
+ return { maxAttempts: r.maxAttempts, keepDoneHours: r.keepDoneHours, maxWaitMinutes: r.maxWaitMinutes, runTimeoutMinutes: r.runTimeoutMinutes };
846
+ };
847
+ const waitOpts = { ...limitsFor({ agentUuid: '', stepUuid: '', stepCustomId: '' }), limitsFor };
848
+ if (operation === 'findStale') {
849
+ const res = (0, waits_1.findStaleWaits)(rows, waitOpts);
850
+ return [[{ json: res }]];
851
+ }
852
+ const run = parseJsonParam(this, 'run', 0);
853
+ if (operation === 'checkStart') {
854
+ if (!run || !run.runId || !run.stepUuid || !run.agentUuid) {
855
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Run needs runId, stepUuid and agentUuid');
856
+ }
857
+ return [[{ json: (0, waits_1.checkStart)(rows, run, waitOpts) }]];
858
+ }
859
+ if (!run || !run.runId || !run.stepUuid || !run.agentUuid || !run.startedAt) {
860
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Finished Run needs runId, stepUuid, agentUuid and startedAt');
861
+ }
862
+ const plan = (0, waits_1.planAfterRun)(rows, run, waitOpts);
863
+ return [[{ json: plan }]];
864
+ }
865
+ for (let i = 0; i < items.length; i++) {
866
+ try {
867
+ if (resource === 'agent') {
868
+ const agentUuid = this.getNodeParameter('agentUuid', i);
869
+ if (operation === 'getContextVersion') {
870
+ if (source === 'agentKey') {
871
+ const res = await agentRequest(this, baseUrl, keyring, agentUuid, '/context/version');
872
+ out.push({ json: { agentUuid: agentUuid.trim().toLowerCase(), ...res }, pairedItem: { item: i } });
873
+ }
874
+ else {
875
+ const b = await loadBundle(agentUuid, i, {});
876
+ out.push({ json: { agentUuid: agentUuid.trim().toLowerCase(), contextVersion: b.contextVersion ?? null }, pairedItem: { item: i } });
877
+ }
878
+ continue;
879
+ }
880
+ const opts = this.getNodeParameter('agentOptions', i, {});
881
+ const bundle = await loadBundle(agentUuid, i, opts);
882
+ const summary = summarizeBundle(bundle);
883
+ if (opts.requireActive !== false && summary.status !== 'active') {
884
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Agent ${summary.agentName ?? agentUuid} is not active in roleALPHA (status: ${summary.status ?? 'unknown'})`, { itemIndex: i });
885
+ }
886
+ if (opts.requireGuardrails !== false && summary.guardrailCount === 0) {
887
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Agent ${summary.agentName ?? agentUuid} is bound by no policy; refusing to run without guardrails`, { itemIndex: i });
888
+ }
889
+ out.push({ json: { ...summary, bundle: bundle }, pairedItem: { item: i } });
890
+ continue;
891
+ }
892
+ if (resource === 'responsibility') {
893
+ const g = getGraph(i);
894
+ const opts = this.getNodeParameter('responsibilityOptions', i, {});
895
+ const failIfDenied = opts.failIfDenied === true;
896
+ const roleUuidsFor = async () => {
897
+ const agentUuid = String(this.getNodeParameter('agentUuid', i, '') ?? '').trim();
898
+ if (agentUuid)
899
+ return (await (0, graph_1.getAgentRoles)(g, agentUuid)).map((r) => r.uuid);
900
+ const roles = uuidList(this.getNodeParameter('roleUuids', i, ''));
901
+ if (roles.length === 0)
902
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Give an agent UUID or at least one role UUID', { itemIndex: i });
903
+ return roles;
904
+ };
905
+ if (operation === 'findStep') {
906
+ const customId = this.getNodeParameter('customId', i);
907
+ const step = await (0, graph_1.findStepByCustomId)(g, customId);
908
+ if (!step && opts.failIfNotFound !== false)
909
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `No step with custom ID "${customId}"`, { itemIndex: i });
910
+ out.push({ json: { found: !!step, customId, step: step }, pairedItem: { item: i } });
911
+ }
912
+ else if (operation === 'getStep') {
913
+ const uuid = await resolveStepUuid(this, g, this.getNodeParameter('step', i), i);
914
+ const step = await (0, graph_1.getStep)(g, uuid, { resolveHolders: opts.resolveHolders !== false, roleDetails: opts.roleDetails === true });
915
+ out.push({ json: { step: step }, pairedItem: { item: i } });
916
+ }
917
+ else if (operation === 'getDecider') {
918
+ const uuid = await resolveStepUuid(this, g, this.getNodeParameter('step', i), i);
919
+ out.push({ json: { stepUuid: uuid, ...(await (0, graph_1.getDecider)(g, uuid)) }, pairedItem: { item: i } });
920
+ }
921
+ else if (operation === 'checkApprover') {
922
+ const uuid = await resolveStepUuid(this, g, this.getNodeParameter('step', i), i);
923
+ const res = await (0, graph_1.checkApprover)(g, uuid, this.getNodeParameter('actor', i));
924
+ if (!res.allowed && failIfDenied)
925
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Not allowed: ${res.reason}`, { itemIndex: i });
926
+ out.push({ json: { stepUuid: uuid, ...res }, pairedItem: { item: i } });
927
+ }
928
+ else if (operation === 'checkResponsibility') {
929
+ const uuid = await resolveStepUuid(this, g, this.getNodeParameter('step', i), i);
930
+ const roles = await (0, graph_1.responsibleRolesFor)(g, uuid, await roleUuidsFor());
931
+ if (roles.length === 0 && failIfDenied)
932
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'None of the roles is responsible for this step', { itemIndex: i });
933
+ out.push({ json: { stepUuid: uuid, responsible: roles.length > 0, roles }, pairedItem: { item: i } });
934
+ }
935
+ else if (operation === 'getDuties') {
936
+ const duties = await (0, graph_1.getDuties)(g, await roleUuidsFor());
937
+ if (opts.splitDuties === false)
938
+ out.push({ json: { duties }, pairedItem: { item: i } });
939
+ else
940
+ for (const d of duties)
941
+ out.push({ json: d, pairedItem: { item: i } });
942
+ }
943
+ else if (operation === 'getCircleLead') {
944
+ for (const lead of await (0, graph_1.getCircleLeads)(g, await roleUuidsFor()))
945
+ out.push({ json: lead, pairedItem: { item: i } });
946
+ }
947
+ else if (operation === 'getRoleHolders') {
948
+ const roleUuid = this.getNodeParameter('roleUuid', i);
949
+ out.push({ json: { roleUuid, ...(await (0, graph_1.getRoleHolders)(g, roleUuid)) }, pairedItem: { item: i } });
950
+ }
951
+ continue;
952
+ }
953
+ if (resource === 'prompt') {
954
+ const raw = parseJsonParam(this, 'bundle', i);
955
+ const bundle = (raw && typeof raw.bundle === 'object' ? raw.bundle : raw);
956
+ if (!bundle || !bundle.agent)
957
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Context Bundle has no agent', { itemIndex: i });
958
+ const stepRaw = parseJsonParam(this, 'stepJson', i);
959
+ const step = (stepRaw && typeof stepRaw.step === 'object' ? stepRaw.step : stepRaw);
960
+ const duties = parseJsonParam(this, 'duties', i);
961
+ const shareRaw = parseJsonParam(this, 'share', i);
962
+ const share = (shareRaw && Array.isArray(shareRaw.duties) ? shareRaw : null);
963
+ const rules = String(this.getNodeParameter('extraRules', i, '') ?? '')
964
+ .split(/\r?\n/)
965
+ .map((r) => r.trim())
966
+ .filter(Boolean);
967
+ const systemPrompt = (0, prompt_1.buildSystemPrompt)({
968
+ bundle: { ...bundle, guardrails: bundle.guardrails ?? [], context: bundle.context ?? [] },
969
+ step: step ?? null,
970
+ duties: Array.isArray(duties) ? duties : [],
971
+ trigger: { type: this.getNodeParameter('triggerType', i, '') || undefined, event: parseJsonParam(this, 'event', i) },
972
+ extraRules: rules,
973
+ share,
974
+ });
975
+ const json = { ...items[i].json, systemPrompt };
976
+ if (share && step)
977
+ json.task = (0, prompt_1.buildTask)({ step, share, triggerType: this.getNodeParameter('triggerType', i, '') || undefined });
978
+ out.push({ json, pairedItem: { item: i } });
979
+ continue;
980
+ }
981
+ if (resource === 'share') {
982
+ if (operation === 'buildInput') {
983
+ const raw = parseJsonParam(this, 'bundle', i);
984
+ const bundle = (raw && typeof raw.bundle === 'object' ? raw.bundle : raw);
985
+ if (!bundle || !bundle.agent)
986
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Context Bundle has no agent', { itemIndex: i });
987
+ const stepRaw = parseJsonParam(this, 'stepJson', i);
988
+ const step = (stepRaw && typeof stepRaw.step === 'object' ? stepRaw.step : stepRaw);
989
+ if (!step || !Array.isArray(step.responsibleRoles))
990
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Step is missing or has no responsible roles', { itemIndex: i });
991
+ if (step.responsibleRoles.some((r) => r.accountabilities === undefined)) {
992
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'The step has no role details; turn on Role Details in Get Step', { itemIndex: i });
993
+ }
994
+ const rolesRaw = parseJsonParam(this, 'roles', i);
995
+ const rolesList = Array.isArray(rolesRaw)
996
+ ? rolesRaw
997
+ : rolesRaw && typeof rolesRaw === 'object' && Array.isArray(rolesRaw.roles)
998
+ ? rolesRaw.roles
999
+ : [];
1000
+ const roleUuids = rolesList.map((r) => (r && typeof r === 'object' ? String(r.uuid ?? '') : String(r))).filter(Boolean);
1001
+ const input = (0, share_1.buildShareInput)({ ...bundle, guardrails: bundle.guardrails ?? [], context: bundle.context ?? [] }, step, roleUuids, {
1002
+ type: this.getNodeParameter('triggerType', i, '') || undefined,
1003
+ event: parseJsonParam(this, 'event', i),
1004
+ });
1005
+ out.push({ json: { ...input, schema: share_1.SHARE_SCHEMA }, pairedItem: { item: i } });
1006
+ }
1007
+ else {
1008
+ const input = parseJsonParam(this, 'shareInput', i);
1009
+ if (!input || !Array.isArray(input.accountabilities) || !Array.isArray(input.others)) {
1010
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Share Input is not the output of Share → Build Input', { itemIndex: i });
1011
+ }
1012
+ let answer = parseJsonParam(this, 'answer', i);
1013
+ if (answer && typeof answer === 'object' && answer.output && typeof answer.output === 'object')
1014
+ answer = answer.output;
1015
+ out.push({ json: (0, share_1.checkShare)(input, answer), pairedItem: { item: i } });
1016
+ }
1017
+ continue;
1018
+ }
1019
+ if (resource === 'tool') {
1020
+ const allowed = String(this.getNodeParameter('allowedTools', i, '') ?? '')
1021
+ .split(',')
1022
+ .map((t) => t.trim())
1023
+ .filter(Boolean);
1024
+ const name = operation === 'listTools' ? 'list' : String(this.getNodeParameter('toolName', i, '') ?? '').trim();
1025
+ try {
1026
+ if (name === '' || name === 'list') {
1027
+ const tools = (await getClient(i).listTools()).filter((t) => allowed.includes(t.name));
1028
+ out.push({ json: { ok: true, tools: tools.map((t) => ({ name: t.name, description: t.description ?? '', arguments: (t.inputSchema ?? {}) })) }, pairedItem: { item: i } });
1029
+ }
1030
+ else if (!allowed.includes(name)) {
1031
+ out.push({ json: { ok: false, error: `Tool "${name}" is not allowed. Allowed: ${allowed.join(', ')}` }, pairedItem: { item: i } });
1032
+ }
1033
+ else {
1034
+ const raw = this.getNodeParameter('toolArguments', i, '{}');
1035
+ let args;
1036
+ try {
1037
+ args = (typeof raw === 'string' ? (raw.trim() ? JSON.parse(raw) : {}) : raw ?? {});
1038
+ }
1039
+ catch {
1040
+ out.push({ json: { ok: false, error: 'Tool Arguments are not valid JSON' }, pairedItem: { item: i } });
1041
+ continue;
1042
+ }
1043
+ if (!args || typeof args !== 'object' || Array.isArray(args))
1044
+ args = {};
1045
+ if (args.organisation === undefined)
1046
+ args.organisation = organisationAt(i);
1047
+ out.push({ json: { ok: true, tool: name, result: (await getClient(i).callTool(name, args)) }, pairedItem: { item: i } });
1048
+ }
1049
+ }
1050
+ catch (error) {
1051
+ // A refused run ends the run; any other error goes back to the caller as data.
1052
+ if (error instanceof agentRun_1.AgentRunError)
1053
+ throw refusedRun(this, error, i);
1054
+ out.push({ json: { ok: false, tool: name, error: error.message.slice(0, 1000) }, pairedItem: { item: i } });
1055
+ }
1056
+ continue;
1057
+ }
1058
+ if (resource === 'settings') {
1059
+ const raw = parseJsonParam(this, 'settingRows', i);
1060
+ const res = (0, settings_1.resolveSettings)((Array.isArray(raw) ? raw : []), {
1061
+ agentUuid: String(this.getNodeParameter('settingsAgent', i, '') ?? ''),
1062
+ stepUuid: String(this.getNodeParameter('settingsStep', i, '') ?? ''),
1063
+ stepCustomId: String(this.getNodeParameter('settingsStepCustomId', i, '') ?? ''),
1064
+ });
1065
+ out.push({ json: { ...items[i].json, settings: res.settings, settingSources: res.sources, settingProblems: res.problems, settingDefaults: settings_1.DEFAULT_SETTINGS }, pairedItem: { item: i } });
1066
+ continue;
1067
+ }
1068
+ if (resource === 'schedule') {
1069
+ const opts = this.getNodeParameter('scheduleOptions', i, {});
1070
+ const res = (0, schedule_1.isDue)(this.getNodeParameter('frequency', i, ''), this.getNodeParameter('lastRunAt', i, ''), new Date(), {
1071
+ timeZone: opts.timeZone || 'Europe/Berlin',
1072
+ dueDate: this.getNodeParameter('dueDate', i, '') || null,
1073
+ weekday: typeof opts.weekday === 'number' ? opts.weekday : null,
1074
+ notBeforeHour: typeof opts.notBeforeHour === 'number' ? opts.notBeforeHour : null,
1075
+ });
1076
+ if (opts.onlyDue === true && !res.due)
1077
+ continue;
1078
+ out.push({ json: { ...items[i].json, schedule: res }, pairedItem: { item: i } });
1079
+ continue;
1080
+ }
1081
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown operation ${resource}/${operation}`, { itemIndex: i });
1082
+ }
1083
+ catch (error) {
1084
+ if (this.continueOnFail()) {
1085
+ out.push({ json: { error: error.message }, pairedItem: { item: i } });
1086
+ continue;
1087
+ }
1088
+ if (error instanceof n8n_workflow_1.NodeOperationError)
1089
+ throw error;
1090
+ if (error instanceof agentRun_1.AgentRunError)
1091
+ throw refusedRun(this, error, i);
1092
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex: i });
1093
+ }
1094
+ }
1095
+ return [out];
1096
+ }
1097
+ }
1098
+ exports.RoleAlpha = RoleAlpha;
1099
+ //# sourceMappingURL=RoleAlpha.node.js.map