@ziggs-ai/agent-sdk 0.1.3 → 0.1.4

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 (85) hide show
  1. package/README.md +1 -1
  2. package/package.json +9 -4
  3. package/src/AgentHost.ts +342 -0
  4. package/src/adapters/OpenAIAdapter.ts +125 -0
  5. package/src/agent/Agent.ts +98 -0
  6. package/src/cognition/validateContext.ts +95 -0
  7. package/src/context/applyEffects.ts +80 -0
  8. package/src/context/batch.ts +17 -0
  9. package/src/context/classifyEnvelope.ts +38 -0
  10. package/src/context/routingLabels.ts +46 -0
  11. package/src/defineAgent.ts +62 -0
  12. package/src/formatters/AgreementFormatter.ts +111 -0
  13. package/src/formatters/HistoryFormatter.ts +166 -0
  14. package/src/formatters/index.ts +2 -0
  15. package/src/index.ts +86 -0
  16. package/src/ingress/normalizeIncoming.ts +119 -0
  17. package/src/memory/MemoryStore.ts +104 -0
  18. package/src/runtime/AgentMachine.ts +298 -0
  19. package/src/runtime/PromptBuilder.ts +461 -0
  20. package/src/runtime/buildOutcome.ts +488 -0
  21. package/src/runtime/defaults.ts +72 -0
  22. package/src/runtime/runTurn.ts +637 -0
  23. package/src/runtime/validateWorkflow.ts +165 -0
  24. package/src/server/ConnectionPool.ts +155 -0
  25. package/src/server/EventQueue.ts +119 -0
  26. package/src/server/OutboxBuffer.ts +90 -0
  27. package/src/server/ZiggsEffectHandler.ts +335 -0
  28. package/src/server/agreements/AgreementService.ts +111 -0
  29. package/src/server/createHealthServer.ts +8 -0
  30. package/src/server/proactive/ProactiveTrigger.ts +83 -0
  31. package/src/server/runLauncher.ts +131 -0
  32. package/src/server/tasks/TaskService.ts +111 -0
  33. package/src/server/tasks/index.ts +4 -0
  34. package/src/server/tasks/paymentTools.ts +156 -0
  35. package/src/server/tasks/protocolRunner.ts +101 -0
  36. package/src/server/tasks/protocolTools.ts +96 -0
  37. package/src/server/ziggspay/ZiggsPayClient.ts +193 -0
  38. package/src/shared/ids.ts +3 -0
  39. package/src/shared/runtimeLog.ts +72 -0
  40. package/src/shared/types.ts +31 -0
  41. package/src/tasks/protocolRegistry.ts +25 -0
  42. package/src/tasks/taskCore.ts +139 -0
  43. package/src/tools/ToolManager.ts +95 -0
  44. package/src/tools/{ToolProvider.js → ToolProvider.ts} +5 -15
  45. package/src/tools/defineTool.ts +90 -0
  46. package/src/tools/index.ts +5 -0
  47. package/src/types.ts +368 -0
  48. package/src/utils/jsonExtractor.ts +100 -0
  49. package/src/ConnectionPool.js +0 -133
  50. package/src/adapters/OpenAIAdapter.js +0 -73
  51. package/src/agent/Agent.js +0 -121
  52. package/src/agent/EventQueue.js +0 -68
  53. package/src/agent/OutboxBuffer.js +0 -62
  54. package/src/cognition/PromptBuilder.js +0 -312
  55. package/src/cognition/resolveActionTool.js +0 -12
  56. package/src/cognition/runTurn.js +0 -578
  57. package/src/context/applyEffects.js +0 -133
  58. package/src/context/batch.js +0 -25
  59. package/src/context/classifyEnvelope.js +0 -82
  60. package/src/context/routingLabels.js +0 -54
  61. package/src/createHealthServer.js +0 -28
  62. package/src/formatters/HistoryFormatter.js +0 -257
  63. package/src/formatters/TaskFormatter.js +0 -180
  64. package/src/formatters/index.js +0 -9
  65. package/src/index.js +0 -76
  66. package/src/ingress/normalizeIncoming.js +0 -70
  67. package/src/runLauncher.js +0 -159
  68. package/src/shared/ids.js +0 -7
  69. package/src/shared/types.js +0 -86
  70. package/src/tasks/TaskService.js +0 -247
  71. package/src/tasks/index.js +0 -9
  72. package/src/tasks/taskCore.js +0 -229
  73. package/src/tasks/taskProtocolRegistry.js +0 -22
  74. package/src/tasks/taskProtocolRunner.js +0 -107
  75. package/src/tasks/taskProtocolTools.js +0 -87
  76. package/src/tools/ToolManager.js +0 -79
  77. package/src/tools/defineTool.js +0 -82
  78. package/src/tools/index.js +0 -11
  79. package/src/utils/jsonExtractor.js +0 -139
  80. package/src/workflow/AgentMachine.js +0 -250
  81. package/src/workflow/WorkflowRuntime.js +0 -63
  82. package/src/workflow/dsl.js +0 -287
  83. package/src/workflow/motifs.js +0 -435
  84. package/src/ziggs/runtime.js +0 -192
  85. /package/src/adapters/{index.js → index.ts} +0 -0
@@ -1,435 +0,0 @@
1
- /**
2
- * Motifs — higher-level DSL over the AgentMachine "assembly".
3
- *
4
- * Each motif returns a Fragment:
5
- * { entry: string, states: { [name]: stateDef } }
6
- *
7
- * A Fragment's transitions use the sentinel target '__NEXT__' for outbound
8
- * edges. `seq(...)` rewrites '__NEXT__' to the next fragment's entry; the
9
- * final fragment rewrites to opts.end (default 'idle').
10
- *
11
- * `compile(shape, { initial, end })` returns a states map ready for
12
- * defineAgent.
13
- */
14
-
15
- const NEXT = '__NEXT__';
16
-
17
- // ── Low-level helpers ─────────────────────────────────────────────────────
18
-
19
- const prefixState = (prefix, name) => (prefix ? `${prefix}_${name}` : name);
20
-
21
- function mapTargets(stateDef, fn) {
22
- if (!stateDef.transitions) return stateDef;
23
- return {
24
- ...stateDef,
25
- transitions: stateDef.transitions.map(t =>
26
- typeof t === 'string' ? { to: fn(t) } : { ...t, to: fn(t.to) }
27
- ),
28
- };
29
- }
30
-
31
- function withPrefix(fragment, prefix) {
32
- if (!prefix) return fragment;
33
- const states = {};
34
- for (const [name, def] of Object.entries(fragment.states)) {
35
- const rewritten = mapTargets(def, t => (t === NEXT ? NEXT : prefixState(prefix, t)));
36
- states[prefixState(prefix, name)] = rewritten;
37
- }
38
- return { entry: prefixState(prefix, fragment.entry), states };
39
- }
40
-
41
- // ── Service-tier motifs ───────────────────────────────────────────────────
42
-
43
- /**
44
- * listen() — idle-style park. Routes to __NEXT__ on a delegated task,
45
- * otherwise parks waiting for incoming messages that populate ctx flags.
46
- */
47
- export function listen() {
48
- return {
49
- entry: 'idle',
50
- states: {
51
- idle: {
52
- transitions: [
53
- {
54
- to: NEXT,
55
- when: ctx =>
56
- ctx.approval &&
57
- ctx.taskAssignment &&
58
- (ctx.taskAssignment.state === 'active' ||
59
- ctx.taskAssignment.state === 'in-progress') &&
60
- !!ctx.taskAssignment.parentTaskId,
61
- },
62
- { to: 'listening_hold' },
63
- ],
64
- },
65
- listening_hold: {
66
- // Placeholder: a paired `propose()` replaces this with the real
67
- // listening state. If compiled alone, stays parked.
68
- transitions: [{ to: 'idle' }],
69
- },
70
- },
71
- };
72
- }
73
-
74
- /**
75
- * propose({ prompt, constraints }) — user-facing listening + approval gate.
76
- */
77
- export function propose({ role, goal, context = '', constraints = '' } = {}) {
78
- return {
79
- entry: 'listening',
80
- states: {
81
- listening: {
82
- prompt: {
83
- role: role ?? 'A helpful assistant',
84
- goal: goal ?? 'Understand the user and propose a task for concrete requests.',
85
- context,
86
- constraints:
87
- constraints ||
88
- 'Respond in the user\'s language. Only propose once per pending request.',
89
- },
90
- actions: {
91
- proposeTask: {
92
- prompt: {
93
- instruction:
94
- 'Create a task proposal. Set proposedTo to the human user id from <recipients><users>.',
95
- format: '{"description": "...", "proposedTo": "<userId>"}',
96
- when: 'The user has a concrete request and there is no pending delegation.',
97
- },
98
- tool: 'task_make_task',
99
- },
100
- respondToProposal: {
101
- prompt: {
102
- instruction: 'Accept or reject a pending delegation directed at you.',
103
- when: 'A proposal is pending and directed at you (taskAssignment has parentTaskId).',
104
- },
105
- tool: 'task_respond_proposal',
106
- },
107
- sendMessage: {
108
- prompt: {
109
- instruction: 'Respond directly for greetings or trivial queries.',
110
- when: 'The request does not need a task.',
111
- },
112
- },
113
- },
114
- transitions: [
115
- { to: 'awaitingApproval', when: ctx => ctx.proposal != null },
116
- {
117
- to: NEXT,
118
- when: ctx =>
119
- ctx.respondedProposal != null &&
120
- ctx.respondedProposal.action !== 'reject',
121
- },
122
- { to: 'idle', when: ctx => ctx.messageSent || ctx.activeWait },
123
- ],
124
- },
125
- awaitingApproval: {
126
- transitions: [
127
- { to: NEXT, when: ctx => ctx.approval },
128
- { to: 'idle', when: ctx => ctx.rejection },
129
- { to: 'listening' },
130
- ],
131
- },
132
- },
133
- };
134
- }
135
-
136
- /**
137
- * execute({ role, goal, context, constraints, toolHint, askWhen }) —
138
- * the "do the work" state. Calls tools, messages user, closes task. If
139
- * details are missing, loops through an `ask()`-compatible park state.
140
- */
141
- export function execute({
142
- role = 'Executing a task',
143
- goal = 'Use the available tools to complete the task, report to the user, then close the task.',
144
- context = '',
145
- constraints = 'Report results to the user before closing the task. Fail with a clear reason on domain mismatch.',
146
- toolHint,
147
- name = 'executing',
148
- } = {}) {
149
- const parkName = `${name}_await`;
150
- return {
151
- entry: name,
152
- states: {
153
- [name]: {
154
- prompt: { role, goal, context, constraints },
155
- actions: {
156
- ...(toolHint && {
157
- runTool: {
158
- prompt: {
159
- instruction: `Call ${toolHint} to execute the task.`,
160
- when: 'You have enough details.',
161
- },
162
- tool: toolHint,
163
- },
164
- }),
165
- askForDetails: {
166
- prompt: {
167
- instruction: 'Ask the user (from <recipients><users>) for the missing details.',
168
- when: 'The task is underspecified and you have NOT already asked.',
169
- },
170
- },
171
- task_update_task: {
172
- prompt: {
173
- instruction:
174
- 'Close the task: status "completed" with a result summary on success, or "failed" with a reason.',
175
- when: 'After tool execution you are done or must abandon.',
176
- },
177
- tool: 'task_update_task',
178
- },
179
- sendMessage: {
180
- prompt: {
181
- instruction: 'Send the outcome to the user.',
182
- when: 'You have results or need a quick update.',
183
- },
184
- },
185
- },
186
- transitions: [
187
- { to: NEXT, when: ctx => ctx.taskCompleted || ctx.taskFailed },
188
- { to: NEXT, when: ctx => ctx.activeWait },
189
- { to: parkName, when: ctx => ctx.messageSent },
190
- { to: name, when: ctx => ctx.toolResults?.length > 0 },
191
- { to: parkName }, // catch-all park
192
- ],
193
- },
194
- [parkName]: {
195
- transitions: [{ to: name }],
196
- },
197
- },
198
- };
199
- }
200
-
201
- // ── Orchestrator motifs ───────────────────────────────────────────────────
202
-
203
- /**
204
- * plan({ maxItems, label }) — discovery + prepare_agents stage.
205
- */
206
- export function plan({
207
- role = 'An orchestrator planning work',
208
- goal = 'Identify the steps and specialists needed. Use list_agents then prepare_agents.',
209
- context = '',
210
- constraints = 'Match agents to steps by domain.',
211
- maxItems = 5,
212
- name = 'planning',
213
- } = {}) {
214
- return {
215
- entry: name,
216
- states: {
217
- [name]: {
218
- prompt: {
219
- role,
220
- goal,
221
- context: context || `Up to ${maxItems} items.`,
222
- constraints,
223
- },
224
- actions: {
225
- list_agents: {
226
- prompt: {
227
- instruction: 'Search for specialists by domain.',
228
- when: 'You need to find agents.',
229
- },
230
- tool: 'list_agents',
231
- },
232
- prepare_agents: {
233
- prompt: {
234
- instruction: 'Wake the chosen agents.',
235
- when: 'You have identified all agents needed.',
236
- },
237
- tool: 'prepare_agents',
238
- },
239
- sendMessage: {
240
- prompt: {
241
- instruction: 'Explain the plan to the user.',
242
- when: 'You have a plan ready.',
243
- },
244
- },
245
- },
246
- transitions: [
247
- {
248
- to: NEXT,
249
- when: ctx => ctx.toolResults?.some(r => r.name === 'prepare_agents'),
250
- },
251
- { to: name, when: ctx => ctx.toolResults?.length > 0 },
252
- { to: NEXT, when: ctx => ctx.messageSent },
253
- ],
254
- },
255
- },
256
- };
257
- }
258
-
259
- /**
260
- * dispatch({ parallel, label }) — send subtasks to workers.
261
- */
262
- export function dispatch({
263
- parallel = false,
264
- role,
265
- goal,
266
- context = '',
267
- constraints,
268
- name = parallel ? 'mapping' : 'delegating',
269
- } = {}) {
270
- return {
271
- entry: name,
272
- states: {
273
- [name]: {
274
- prompt: {
275
- role: role ?? (parallel ? 'Dispatching parallel work' : 'Executing steps sequentially'),
276
- goal: goal ?? (parallel
277
- ? 'Send all chunks in a single dispatch_to_agents call.'
278
- : 'Send the current step. Include prior results as context.'),
279
- context,
280
- constraints: constraints ?? (parallel
281
- ? 'Dispatch all in one call. Each chunk must stand alone.'
282
- : 'One step at a time. Include previous step outputs.'),
283
- },
284
- actions: {
285
- dispatch_to_agents: {
286
- prompt: {
287
- instruction: parallel
288
- ? 'Send all chunks to their agents in parallel.'
289
- : 'Send the current step to the specialist.',
290
- when: 'Ready to execute.',
291
- },
292
- tool: 'dispatch_to_agents',
293
- },
294
- sendMessage: {
295
- prompt: { instruction: 'Update the user on progress.', when: 'A step completed.' },
296
- },
297
- },
298
- transitions: [
299
- {
300
- to: NEXT,
301
- when: ctx => ctx.toolResults?.some(r => r.name === 'dispatch_to_agents'),
302
- },
303
- { to: NEXT },
304
- ],
305
- },
306
- },
307
- };
308
- }
309
-
310
- /**
311
- * awaitSubtasks({ mode }) — park until subtaskResult(s) arrive.
312
- * mode='one' (pipeline step), mode='all' (map-reduce / voting).
313
- */
314
- export function awaitSubtasks({ mode = 'one', name = 'awaitingSubtasks' } = {}) {
315
- return {
316
- entry: name,
317
- states: {
318
- [name]: {
319
- transitions: [
320
- { to: NEXT, when: ctx => ctx.subtaskResult },
321
- { to: NEXT, when: ctx => ctx.subtaskFailed },
322
- ...(mode === 'all'
323
- ? [{ to: name }]
324
- : [{ to: NEXT }]),
325
- ],
326
- },
327
- },
328
- };
329
- }
330
-
331
- /**
332
- * synthesize({ role, goal }) — merge + close task.
333
- */
334
- export function synthesize({
335
- role = 'Synthesizing results',
336
- goal = 'Merge results into a coherent final answer, present to user, then close the task.',
337
- context = '',
338
- constraints = 'Synthesize — do not concatenate. Dedupe. Resolve conflicts explicitly.',
339
- name = 'synthesizing',
340
- } = {}) {
341
- return {
342
- entry: name,
343
- states: {
344
- [name]: {
345
- prompt: { role, goal, context, constraints },
346
- actions: {
347
- sendMessage: {
348
- prompt: {
349
- instruction: 'Present the final result to the user.',
350
- when: 'You have everything merged.',
351
- },
352
- },
353
- task_update_task: {
354
- prompt: {
355
- instruction: 'Close the task with the final result.',
356
- when: 'After presenting the result.',
357
- },
358
- tool: 'task_update_task',
359
- },
360
- },
361
- transitions: [
362
- { to: NEXT, when: ctx => ctx.taskCompleted || ctx.taskFailed },
363
- { to: NEXT, when: ctx => ctx.messageSent || ctx.activeWait },
364
- ],
365
- },
366
- },
367
- };
368
- }
369
-
370
- // ── Composition ───────────────────────────────────────────────────────────
371
-
372
- /**
373
- * seq(...fragments) — chain fragments: each fragment's __NEXT__ rewrites to
374
- * the next fragment's entry. Last fragment's __NEXT__ is left unresolved
375
- * (compile() will rewrite to `end`).
376
- */
377
- export function seq(...fragments) {
378
- if (!fragments.length) throw new Error('seq() needs at least one fragment');
379
- const states = {};
380
- for (let i = 0; i < fragments.length; i++) {
381
- const frag = fragments[i];
382
- const nextEntry = i + 1 < fragments.length ? fragments[i + 1].entry : NEXT;
383
- for (const [name, def] of Object.entries(frag.states)) {
384
- states[name] = mapTargets(def, t => (t === NEXT ? nextEntry : t));
385
- }
386
- }
387
- return { entry: fragments[0].entry, states };
388
- }
389
-
390
- /**
391
- * loop(n, ...fragments) — repeat a sub-sequence n times. Each iteration is
392
- * state-prefixed so states don't collide. Exit after iteration n.
393
- */
394
- export function loop(n, ...fragments) {
395
- if (n < 1) throw new Error('loop() needs n >= 1');
396
- const iterations = [];
397
- for (let i = 0; i < n; i++) {
398
- iterations.push(withPrefix(seq(...fragments), `i${i}`));
399
- }
400
- return seq(...iterations);
401
- }
402
-
403
- /**
404
- * compile(fragment, { end }) — rewrite trailing __NEXT__ to `end` (default
405
- * 'idle') and return { initial, states }.
406
- */
407
- export function compile(fragment, { end = 'idle' } = {}) {
408
- const states = {};
409
- for (const [name, def] of Object.entries(fragment.states)) {
410
- states[name] = mapTargets(def, t => (t === NEXT ? end : t));
411
- }
412
- // Ensure an `idle` terminal exists if `end` is 'idle' and absent.
413
- if (end === 'idle' && !states.idle) {
414
- states.idle = { transitions: [{ to: fragment.entry }] };
415
- }
416
- return { initial: 'idle', states };
417
- }
418
-
419
- // Convenience: the ubiquitous "service agent" shape in one call.
420
- export function service({
421
- role,
422
- goal,
423
- context,
424
- constraints,
425
- toolHint,
426
- workName = 'executing',
427
- proposeContext,
428
- proposeConstraints,
429
- } = {}) {
430
- return seq(
431
- listen(),
432
- propose({ role, goal, context: proposeContext ?? context, constraints: proposeConstraints }),
433
- execute({ role, goal: undefined, context, constraints, toolHint, name: workName }),
434
- );
435
- }
@@ -1,192 +0,0 @@
1
- import { Agent } from '../agent/Agent.js';
2
- import { ToolManager } from '../tools/ToolManager.js';
3
- import { TASK_PROTOCOL_TOOLS } from '../tasks/taskProtocolTools.js';
4
- import { OpenAIAdapter } from '../adapters/OpenAIAdapter.js';
5
- import { WebSocketClient, ContextReader, ContextWriter, TelemetryClient } from '@ziggs-ai/api-client';
6
- import { PromptBuilder } from '../cognition/PromptBuilder.js';
7
- import { TaskService } from '../tasks/TaskService.js';
8
- import { ConnectionPool } from '../ConnectionPool.js';
9
-
10
- function createToolManager(options) {
11
- const toolManager = new ToolManager();
12
- const userTools = options.tools || options.services?.tools || [];
13
- const taskTools = options.taskTools !== false ? TASK_PROTOCOL_TOOLS : [];
14
- toolManager.registerAll([...taskTools, ...(Array.isArray(userTools) ? userTools : [])]);
15
- return toolManager;
16
- }
17
-
18
- function createServices(options) {
19
- const toolManager = createToolManager(options);
20
- const llm = new OpenAIAdapter({ key: options.openaiKey, model: options.services?.llm?.model || options.model });
21
- const contextReader = new ContextReader(options.operatorKey, options.agentId);
22
- const contextWriter = new ContextWriter(options.operatorKey, options.agentId);
23
- const promptBuilder = new PromptBuilder({ description: options.description, specialization: options.specialization });
24
- const taskService = new TaskService(options.operatorKey, options.agentId);
25
- const telemetryClient = new TelemetryClient(options.operatorKey, options.agentId);
26
- return { toolManager, llm, contextReader, contextWriter, promptBuilder, taskService, telemetryClient };
27
- }
28
-
29
- export class ZiggsAgent {
30
- constructor(options = {}) {
31
- this.options = {
32
- openaiKey: options.openaiKey,
33
- model: options.model || options.services?.llm?.model || 'gpt-4o-mini',
34
- operatorKey: options.operatorKey || process.env.ZIGGS_OPERATOR_KEY || null,
35
- agentId: options.agentId || null,
36
- name: options.name || null,
37
- wsUrl: options.wsUrl,
38
- description: options.description || 'A helpful assistant',
39
- specialization: options.specialization || null,
40
- tools: options.tools || [],
41
- taskTools: options.taskTools,
42
- states: options.states || null,
43
- initial: options.initial || null,
44
- services: options.services || null,
45
- workflow: options.workflow || null,
46
- };
47
- this._validate();
48
- this._setup();
49
- }
50
-
51
- _validate() {
52
- if (!this.options.openaiKey) throw new Error('openaiKey is required');
53
- if (!this.options.operatorKey) {
54
- throw new Error(
55
- 'operatorKey is required — pass { operatorKey } or set ZIGGS_OPERATOR_KEY. ' +
56
- 'Mint one via Agents/scripts/mint-operator-key.js (scope: agents:impersonate).',
57
- );
58
- }
59
- if (!this.options.agentId) {
60
- throw new Error('agentId is required — every agent must declare its stable kebab-case agentId in its config.');
61
- }
62
- }
63
-
64
- _setup() {
65
- const services = createServices(this.options);
66
- this.toolManager = services.toolManager;
67
- this.llm = services.llm;
68
-
69
- this.wsClient = new WebSocketClient({
70
- wsUrl: this.options.wsUrl,
71
- operatorKey: this.options.operatorKey,
72
- agentId: this.options.agentId,
73
- label: this.options.name || 'agent',
74
- name: this.options.name || null,
75
- });
76
-
77
- const messageSender = (message, receiverId, chatId) => {
78
- this.wsClient.sendResponse(message, { receiverId, chatId });
79
- };
80
-
81
- let definition = this.options.workflow;
82
- if (!definition && this.options.states) {
83
- definition = {
84
- id: this.options.agentId,
85
- description: this.options.description,
86
- initial: this.options.initial || 'idle',
87
- states: this.options.states,
88
- };
89
- }
90
- if (!definition?.states) {
91
- throw new Error('ZiggsAgent: states definition or workflow is required');
92
- }
93
-
94
- this.agent = new Agent({
95
- llm: services.llm,
96
- toolManager: services.toolManager,
97
- contextReader: services.contextReader,
98
- contextWriter: services.contextWriter,
99
- taskService: services.taskService,
100
- messageSender,
101
- promptBuilder: services.promptBuilder,
102
- operatorKey: this.options.operatorKey,
103
- agentId: this.options.agentId,
104
- workflow: definition,
105
- });
106
-
107
- this.wsClient.setMessageHandler(async (text, metadata) => {
108
- await this.agent.handleMessage(text, metadata);
109
- });
110
- }
111
-
112
- connect() { this.wsClient.connect(); return this; }
113
- connectAsync(timeout) { return this.wsClient.connectAsync(timeout); }
114
- disconnect() { this.wsClient.disconnect(); }
115
- registerTool(tool) { return this.toolManager.register(tool); }
116
- async handleMessage(text, metadata = {}) { return await this.agent.handleMessage(text, metadata); }
117
- isConnected() { return this.wsClient.isConnected(); }
118
- }
119
-
120
- /**
121
- * Factory for a single agent or a fleet.
122
- *
123
- * createAgent(config) → ZiggsAgent (hello-world / single agent)
124
- * createAgent([cfg1, cfg2]) → ConnectionPool (any fleet — lazy, one control socket)
125
- *
126
- * Pool agents stay disconnected until `pool.wake(id)` — or until the backend
127
- * pushes a `launcher:wake` frame over the control socket opened by
128
- * `pool.startControl({ wsUrl, operatorKey })`. For the full boot sequence
129
- * (control socket, health server, orchestrator, SIGTERM), use `runLauncher`.
130
- *
131
- * @param {object|object[]} options
132
- * @param {object} [opts]
133
- * @param {object} [opts.poolOptions] Forwarded to `new ConnectionPool(...)` when options is an array
134
- */
135
- export function createAgent(options = {}, { poolOptions } = {}) {
136
- if (Array.isArray(options)) {
137
- const pool = new ConnectionPool(poolOptions);
138
- pool.register(options);
139
- return pool;
140
- }
141
- return new ZiggsAgent(options);
142
- }
143
-
144
- const DEFAULT_WAIT_PROMPT = Object.freeze({
145
- instruction: 'Do nothing - wait for the next event.',
146
- when: 'No action is needed, you are waiting for external input, or you should pause without repeating yourself.',
147
- format: '{"thought": "...", "action": "wait"}',
148
- });
149
-
150
- function injectWaitActions(states, initial) {
151
- for (const [, stateDef] of Object.entries(states)) {
152
- if (!stateDef.prompt || !stateDef.actions) continue;
153
- if (!stateDef.actions.wait) {
154
- stateDef.actions.wait = { prompt: { ...DEFAULT_WAIT_PROMPT } };
155
- }
156
- if (Array.isArray(stateDef.transitions)) {
157
- const checksActiveWait = stateDef.transitions.some(
158
- d => typeof d !== 'string' && d.when && /activeWait/.test(d.when.toString())
159
- );
160
- if (!checksActiveWait) {
161
- stateDef.transitions.push({ to: initial, when: ctx => ctx.activeWait });
162
- }
163
- }
164
- }
165
- }
166
-
167
- export function defineAgent({
168
- agentId, description, specialization, services: agentServices,
169
- initial, states, tools = [], operatorKey, ...rest
170
- }) {
171
- if (!agentId) throw new Error('[defineAgent] agentId is required (stable kebab-case, same id registered with backend)');
172
- if (!description) throw new Error('[defineAgent] description is required');
173
- if (!states) throw new Error('[defineAgent] states is required');
174
- if (!initial) throw new Error('[defineAgent] initial state is required');
175
-
176
- injectWaitActions(states, initial);
177
-
178
- return {
179
- openaiKey: process.env.OPENAI_API_KEY,
180
- ...(operatorKey && { operatorKey }),
181
- agentId,
182
- description,
183
- ...(specialization && { specialization }),
184
- tools: [...tools, ...(agentServices?.tools || [])],
185
- model: agentServices?.llm?.model || process.env.OPENAI_MODEL || 'gpt-4o-mini',
186
- workflow: { id: agentId, description, initial, states },
187
- services: agentServices,
188
- ...rest,
189
- };
190
- }
191
-
192
- export default ZiggsAgent;
File without changes