@ziggs-ai/agent-sdk 0.1.3

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 (40) hide show
  1. package/README.md +82 -0
  2. package/package.json +26 -0
  3. package/src/ConnectionPool.js +133 -0
  4. package/src/adapters/OpenAIAdapter.js +73 -0
  5. package/src/adapters/index.js +1 -0
  6. package/src/agent/Agent.js +121 -0
  7. package/src/agent/EventQueue.js +68 -0
  8. package/src/agent/OutboxBuffer.js +62 -0
  9. package/src/cognition/PromptBuilder.js +312 -0
  10. package/src/cognition/resolveActionTool.js +12 -0
  11. package/src/cognition/runTurn.js +578 -0
  12. package/src/context/applyEffects.js +133 -0
  13. package/src/context/batch.js +25 -0
  14. package/src/context/classifyEnvelope.js +82 -0
  15. package/src/context/routingLabels.js +54 -0
  16. package/src/createHealthServer.js +28 -0
  17. package/src/formatters/HistoryFormatter.js +257 -0
  18. package/src/formatters/TaskFormatter.js +180 -0
  19. package/src/formatters/index.js +9 -0
  20. package/src/index.js +76 -0
  21. package/src/ingress/normalizeIncoming.js +70 -0
  22. package/src/runLauncher.js +159 -0
  23. package/src/shared/ids.js +7 -0
  24. package/src/shared/types.js +86 -0
  25. package/src/tasks/TaskService.js +247 -0
  26. package/src/tasks/index.js +9 -0
  27. package/src/tasks/taskCore.js +229 -0
  28. package/src/tasks/taskProtocolRegistry.js +22 -0
  29. package/src/tasks/taskProtocolRunner.js +107 -0
  30. package/src/tasks/taskProtocolTools.js +87 -0
  31. package/src/tools/ToolManager.js +79 -0
  32. package/src/tools/ToolProvider.js +29 -0
  33. package/src/tools/defineTool.js +82 -0
  34. package/src/tools/index.js +11 -0
  35. package/src/utils/jsonExtractor.js +139 -0
  36. package/src/workflow/AgentMachine.js +250 -0
  37. package/src/workflow/WorkflowRuntime.js +63 -0
  38. package/src/workflow/dsl.js +287 -0
  39. package/src/workflow/motifs.js +435 -0
  40. package/src/ziggs/runtime.js +192 -0
@@ -0,0 +1,180 @@
1
+ /**
2
+ * TaskFormatter - Pluggable formatter for converting raw task data to readable text.
3
+ *
4
+ * This formatter is external and pluggable - can be swapped with custom implementations
5
+ * by passing a different formatter to PromptBuilder.
6
+ */
7
+ export class TaskFormatter {
8
+ constructor(options = {}) {
9
+ this.options = options;
10
+ this.shortIds = options.shortIds ?? false; // Always show full IDs by default
11
+ this.indentSize = options.indentSize ?? 2;
12
+ }
13
+
14
+ /**
15
+ * Format an array of tasks into readable text
16
+ * @param {Array} tasks - Array of tasks from context
17
+ * @param {string} agentId - The current agent's ID (for "You" labels)
18
+ * @returns {string} Formatted tasks text
19
+ */
20
+ format(tasks, agentId) {
21
+ if (!tasks?.length) return 'No active tasks.';
22
+
23
+ // Separate pending proposals from active/other tasks
24
+ const pending = tasks.filter(t =>
25
+ t.state === 'proposal' && t.proposal?.status === 'pending'
26
+ );
27
+ const active = tasks.filter(t =>
28
+ t.state !== 'proposal' || t.proposal?.status !== 'pending'
29
+ );
30
+
31
+ const sections = [];
32
+
33
+ // Show pending proposals first (needs attention)
34
+ if (pending.length > 0) {
35
+ sections.push('Pending Proposals (waiting for approval):');
36
+ pending.forEach(t => {
37
+ sections.push(this.formatTask(t, agentId, this._indent(1)));
38
+ });
39
+ }
40
+
41
+ // Show active tasks
42
+ if (active.length > 0) {
43
+ if (sections.length > 0) sections.push('');
44
+ sections.push('Active Tasks:');
45
+ active.forEach(t => {
46
+ sections.push(this.formatTask(t, agentId, this._indent(1)));
47
+ });
48
+ }
49
+
50
+ return sections.join('\n');
51
+ }
52
+
53
+ /**
54
+ * Format a single task
55
+ */
56
+ formatTask(task, agentId, indent = '') {
57
+ const state = (task.state || 'unknown').toUpperCase();
58
+ const id = this.shortIds ? this.shortId(task.taskId) : task.taskId;
59
+ const desc = task.description || 'No description';
60
+
61
+ const isOwner = task.agentId === agentId;
62
+ const isExecutor = task.executorId === agentId;
63
+ const proposedToYou = task.proposal?.proposedTo === agentId;
64
+
65
+ const lines = [
66
+ `${indent}[${state}] ${desc}`,
67
+ `${indent} ID: ${id}`,
68
+ `${indent} Owner: ${isOwner ? 'You' : task.agentId}`,
69
+ `${indent} Executor: ${isExecutor ? 'You' : task.executorId}`,
70
+ `${indent} Payer: ${task.payerId || 'N/A'}`
71
+ ];
72
+
73
+ // Add proposal info if present
74
+ if (task.proposal) {
75
+ const proposalStatus = task.proposal.status || 'unknown';
76
+ const proposedTo = proposedToYou ? 'You' : task.proposal.proposedTo;
77
+ lines.push(`${indent} Proposal: ${proposalStatus} (to ${proposedTo})`);
78
+ }
79
+
80
+ // Add parent task reference if this is a subtask
81
+ if (task.parentTaskId) {
82
+ const parentId = this.shortIds ? this.shortId(task.parentTaskId) : task.parentTaskId;
83
+ lines.push(`${indent} Parent: ${parentId}`);
84
+ }
85
+
86
+ // Orchestration uses contract.specialistAgentId without lifecycle
87
+ const c = task.contract;
88
+ if (c && typeof c === 'object') {
89
+ const contractParts = [];
90
+ if (c.specialistAgentId) contractParts.push(`specialistAgentId: ${c.specialistAgentId}`);
91
+ if (Array.isArray(c.specialistAgentIds) && c.specialistAgentIds.length > 0) {
92
+ contractParts.push(`specialistAgentIds: [${c.specialistAgentIds.join(', ')}]`);
93
+ }
94
+ if (c.lifecycle && c.lifecycle !== 'open') {
95
+ contractParts.push(`lifecycle: ${c.lifecycle}`);
96
+ if (c.maxExecutions != null) contractParts.push(`maxExecutions: ${c.maxExecutions}`);
97
+ if (c.expiresAt) contractParts.push(`expiresAt: ${c.expiresAt}`);
98
+ }
99
+ if (contractParts.length > 0) {
100
+ lines.push(`${indent} Contract: ${contractParts.join(', ')}`);
101
+ }
102
+ }
103
+
104
+ // Add perspective info if present (cross-chat reporting)
105
+ if (task.perspective?.ownerChatId) {
106
+ lines.push(`${indent} Report to: ${task.perspective.ownerChatId}`);
107
+ }
108
+
109
+
110
+ // Add result if completed/failed
111
+ if (task.result && (task.state === 'completed' || task.state === 'failed')) {
112
+ const resultPreview = task.result.length > 100
113
+ ? task.result.slice(0, 100) + '...'
114
+ : task.result;
115
+ lines.push(`${indent} Result: ${resultPreview}`);
116
+ }
117
+
118
+ // Add plan steps if present
119
+ if (task.plan?.steps?.length > 0) {
120
+ const sorted = [...task.plan.steps].sort((a, b) => a.order - b.order);
121
+ lines.push(`${indent} Plan (${sorted.filter(s => s.status === 'completed').length}/${sorted.length} completed):`);
122
+ sorted.forEach(step => {
123
+ const icon = { pending: '○', in_progress: '●', completed: '✓', skipped: '–' }[step.status] || '?';
124
+ const resultNote = step.status === 'completed' && step.result
125
+ ? ` → ${String(step.result).slice(0, 60)}${String(step.result).length > 60 ? '...' : ''}`
126
+ : '';
127
+ lines.push(`${indent} ${step.order}. [${icon}] ${step.description || step.stepId}${resultNote}`);
128
+ });
129
+ }
130
+
131
+ // Add subtasks if present
132
+ if (task.subtasks?.length > 0) {
133
+ lines.push(`${indent} Subtasks (${task.subtasks.length}):`);
134
+ task.subtasks.forEach(subtask => {
135
+ lines.push(this.formatSubtask(subtask, agentId, indent + ' '));
136
+ });
137
+ }
138
+
139
+ return lines.join('\n');
140
+ }
141
+
142
+ /**
143
+ * Format a subtask (simplified version)
144
+ */
145
+ formatSubtask(subtask, agentId, indent = '') {
146
+ const state = (subtask.state || 'unknown').toUpperCase();
147
+ const id = this.shortIds ? this.shortId(subtask.taskId) : subtask.taskId;
148
+ const desc = subtask.description || 'No description';
149
+ const isExecutor = subtask.executorId === agentId;
150
+
151
+ return `${indent}[${state}] ${desc} (${id}, executor: ${isExecutor ? 'You' : subtask.executorId})`;
152
+ }
153
+
154
+ /**
155
+ * Return full task ID (or shortened if shortIds option is enabled)
156
+ */
157
+ shortId(taskId) {
158
+ if (!taskId) return '?';
159
+
160
+ // If shortIds is disabled, always return full ID
161
+ if (!this.shortIds) return taskId;
162
+
163
+ // Otherwise, shorten it
164
+ const parts = taskId.split('_');
165
+ if (parts.length >= 3) {
166
+ return `task_...${parts[parts.length - 1]}`;
167
+ }
168
+ return taskId.length > 20 ? taskId.slice(0, 20) + '...' : taskId;
169
+ }
170
+
171
+ /**
172
+ * Generate indentation string
173
+ */
174
+ _indent(level) {
175
+ return ' '.repeat(level * this.indentSize);
176
+ }
177
+ }
178
+
179
+ // Default instance for easy import
180
+ export const taskFormatter = new TaskFormatter();
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Formatters - Pluggable formatting utilities for agent prompts
3
+ *
4
+ * These formatters convert raw data into readable text for the prompt.
5
+ * They are external and pluggable - can be swapped with custom implementations.
6
+ */
7
+
8
+ export { HistoryFormatter, historyFormatter } from './HistoryFormatter.js';
9
+ export { TaskFormatter, taskFormatter } from './TaskFormatter.js';
package/src/index.js ADDED
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Package entry. Domain layout (imports flow downward only):
3
+ *
4
+ * ziggs/ WebSocket + DI + ZiggsAgent / createAgent / defineAgent
5
+ * agent/ Process: Agent, EventQueue, OutboxBuffer
6
+ * workflow/ AgentMachine, WorkflowRuntime
7
+ * cognition/ runTurn, PromptBuilder, resolveActionTool
8
+ * ingress/ normalizeIncomingEvent (wire → envelope)
9
+ * context/ batch, routing flags, applyEffects, workflow labels
10
+ * tasks/ TaskService + Ziggs task protocol tools
11
+ * tools/ ToolManager, defineTool
12
+ * adapters/ OpenAIAdapter
13
+ * formatters/ history + task text for prompts
14
+ * utils/ jsonExtractor
15
+ * shared/ types, ids
16
+ */
17
+
18
+ export {
19
+ ZiggsAgent,
20
+ createAgent,
21
+ defineAgent,
22
+ } from './ziggs/runtime.js';
23
+
24
+ export { Agent } from './agent/Agent.js';
25
+ export { EventQueue } from './agent/EventQueue.js';
26
+ export { OutboxBuffer } from './agent/OutboxBuffer.js';
27
+ export { normalizeIncomingEvent } from './ingress/normalizeIncoming.js';
28
+ export * from './shared/types.js';
29
+
30
+ export { AgentMachine } from './workflow/AgentMachine.js';
31
+ export { WorkflowRuntime } from './workflow/WorkflowRuntime.js';
32
+ export { agent, parseDsl } from './workflow/dsl.js';
33
+ export { runTurn } from './cognition/runTurn.js';
34
+ export { resolveActionTool } from './cognition/resolveActionTool.js';
35
+ export { PromptBuilder } from './cognition/PromptBuilder.js';
36
+ export {
37
+ TASK_PROTOCOL_TOOLS,
38
+ taskMakeTaskTool,
39
+ taskMakeSubTasksTool,
40
+ taskUpdateTaskTool,
41
+ taskRespondProposalTool,
42
+ taskUpdatePlanStepTool,
43
+ } from './tasks/taskProtocolTools.js';
44
+ export {
45
+ TASK_PROTOCOL_TOOL_NAMES,
46
+ TASK_PROTOCOL_TOOL_TO_OPERATION,
47
+ mapTaskProtocolToolToOperation,
48
+ isTaskProtocolToolName,
49
+ } from './tasks/taskProtocolRegistry.js';
50
+ export { executeTaskPayload } from './tasks/taskProtocolRunner.js';
51
+ export { classifyIncomingEvent } from './context/classifyEnvelope.js';
52
+ export { buildContextUpdates, CONTEXT_RESET } from './context/applyEffects.js';
53
+ export { getBatchEvents, isTaskResultRelevantToAgent } from './context/batch.js';
54
+ export {
55
+ classifyWorkflowEvent,
56
+ findTaskResult,
57
+ unwrapBatchEvent,
58
+ findIncomingTaskResult,
59
+ } from './context/routingLabels.js';
60
+
61
+ export { TaskService } from './tasks/TaskService.js';
62
+ export { ToolManager } from './tools/ToolManager.js';
63
+ export { ToolProvider } from './tools/ToolProvider.js';
64
+ export { defineTool } from './tools/defineTool.js';
65
+ export { OpenAIAdapter } from './adapters/OpenAIAdapter.js';
66
+ export { extractJSON, safeParseJSON } from './utils/jsonExtractor.js';
67
+ export { HistoryFormatter, historyFormatter, TaskFormatter, taskFormatter } from './formatters/index.js';
68
+ export { WebSocketClient, ContextReader, ContextWriter, AgentSearchClient, getBackendUrl, getWebSocketUrl } from '@ziggs-ai/api-client';
69
+
70
+ export { default } from './ziggs/runtime.js';
71
+
72
+ // ConnectionPool — opt-in lazy WebSocket pool for large-scale dynamic agent fleets.
73
+ // Use via createAgent(configs, { lazy: true }) or by direct construction.
74
+ export { ConnectionPool } from './ConnectionPool.js';
75
+ export { createHealthServer } from './createHealthServer.js';
76
+ export { runLauncher } from './runLauncher.js';
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Converts external metadata + text into canonical event payloads and routing hints.
3
+ */
4
+
5
+ import { OPEN_PROPOSAL_TARGET } from '../tasks/taskCore.js';
6
+
7
+ function normalizeChatId(metadata, taskData) {
8
+ return metadata.chatId || metadata.chat_id || taskData?.chatId || taskData?.perspective?.ownerChatId || null;
9
+ }
10
+
11
+ function normalizeReceiverId(metadata, taskData) {
12
+ return metadata.receiver?.id || metadata.receiverId || metadata.to?.id || taskData?.executorId || taskData?.proposedTo || null;
13
+ }
14
+
15
+ function isTaskRelated(metadata, taskData) {
16
+ if (!taskData?.taskId) return false;
17
+ const entryType = metadata.entryType || 'message';
18
+ const contentType = metadata.content_type || metadata.contentType || 'text';
19
+ return (
20
+ entryType === 'notification' || entryType === 'task_history'
21
+ || contentType === 'task' || contentType === 'task_update'
22
+ || Boolean(taskData.state || taskData.proposal || taskData.parentTaskId || taskData.executorId || taskData.agentId || taskData.payerId || taskData.createdBy)
23
+ );
24
+ }
25
+
26
+ function isRelevantForAgent(ids, ownAgentId) {
27
+ if (!ownAgentId) return true;
28
+ return ids.includes(ownAgentId) || ids.includes(OPEN_PROPOSAL_TARGET);
29
+ }
30
+
31
+ export function normalizeIncomingEvent({ text, metadata = {}, ownAgentId = null }) {
32
+ const taskData = metadata.service?.task || metadata.task || null;
33
+ const chatId = normalizeChatId(metadata, taskData);
34
+ const receiverId = normalizeReceiverId(metadata, taskData);
35
+ const senderType = metadata.sender?.type ? String(metadata.sender.type).toUpperCase() : undefined;
36
+
37
+ const base = {
38
+ senderId: metadata.sender?.id,
39
+ senderType,
40
+ receiverId: receiverId || undefined,
41
+ timestamp: Date.now(),
42
+ };
43
+
44
+ if (isTaskRelated(metadata, taskData)) {
45
+ const receiverFromMeta = metadata.receiver?.id || metadata.receiverId || null;
46
+ const candidateIds = [
47
+ taskData.agentId,
48
+ taskData.executorId,
49
+ taskData.payerId,
50
+ taskData.proposedTo,
51
+ taskData.createdBy,
52
+ receiverFromMeta,
53
+ ].filter(Boolean);
54
+ const relevant = isRelevantForAgent(candidateIds, ownAgentId);
55
+ return {
56
+ chatId,
57
+ event: { ...base, type: 'task_result', result: taskData, text },
58
+ shouldProcess: relevant,
59
+ reason: relevant ? null : 'task_not_targeted_to_agent',
60
+ };
61
+ }
62
+
63
+ const relevant = !ownAgentId || !receiverId || receiverId === ownAgentId || receiverId === OPEN_PROPOSAL_TARGET;
64
+ return {
65
+ chatId,
66
+ event: { ...base, type: 'message', text },
67
+ shouldProcess: relevant,
68
+ reason: relevant ? null : 'message_not_targeted_to_agent',
69
+ };
70
+ }
@@ -0,0 +1,159 @@
1
+ /**
2
+ * runLauncher — one entry point to run an agent or a fleet.
3
+ *
4
+ * runLauncher(config, opts) → single eager agent (hello-world)
5
+ * runLauncher([c1, c2, …], opts) → fleet: ConnectionPool + control socket + health server
6
+ *
7
+ * For a single config, the agent connects directly and stays online. No pool,
8
+ * no wake-on-demand, no yellow-dot "available" state — just one green socket.
9
+ *
10
+ * For a fleet, agents register lazily: they show as "available" until messaged,
11
+ * then the backend pushes a wake frame over the one launcher control socket
12
+ * and the addressed agent's own socket opens on demand.
13
+ *
14
+ * Both shapes share: SIGTERM cleanup, optional health server, optional extra
15
+ * `onShutdown` hook. The fleet form additionally wires control socket,
16
+ * optional always-on orchestrator, and optional preWake.
17
+ *
18
+ * Usage (single):
19
+ * await runLauncher({ ...config, operatorKey, openaiKey });
20
+ *
21
+ * Usage (fleet):
22
+ * await runLauncher(configs, {
23
+ * operatorKey: process.env.ZIGGS_OPERATOR_KEY,
24
+ * label: 'council',
25
+ * orchestrator: orchConfig,
26
+ * preWake: ['newton', 'einstein'],
27
+ * onShutdown: async () => mongo.close(),
28
+ * });
29
+ */
30
+
31
+ import { ConnectionPool } from './ConnectionPool.js';
32
+ import { ZiggsAgent } from './ziggs/runtime.js';
33
+ import { createHealthServer } from './createHealthServer.js';
34
+
35
+ /**
36
+ * @param {object|object[]} configOrConfigs - a single defineAgent config or an array of them
37
+ * @param {object} [opts]
38
+ * @param {string} [opts.operatorKey] Credential for the control socket (fleet form only). Falls back to scanning configs.
39
+ * @param {string} [opts.wsUrl=process.env.WS_URL] Backend WebSocket URL
40
+ * @param {number} [opts.port=process.env.PORT||8080] Health check port (set to 0 to skip)
41
+ * @param {boolean} [opts.healthServer=true] Start a 200-OK HTTP server on `port`. Default true for both shapes; set false to skip.
42
+ * @param {string} [opts.label='launcher'] Log prefix
43
+ * @param {Array<object>} [opts.agentMeta] (fleet only) Metadata keyed by agentId for pool.query()
44
+ * @param {object|Function} [opts.orchestrator] (fleet only) Always-on extra agent config, or a builder (pool) => config
45
+ * @param {Array<string>} [opts.preWake=[]] (fleet only) AgentIds to wake on boot
46
+ * @param {object} [opts.poolOptions] (fleet only) { maxActive, idleTimeoutMs }
47
+ * @param {Function} [opts.onShutdown] Extra async cleanup on SIGTERM
48
+ * @returns {Promise<
49
+ * { agent: ZiggsAgent, healthServer?: import('http').Server } |
50
+ * { pool: ConnectionPool, orchestrator: ZiggsAgent|null, healthServer?: import('http').Server }
51
+ * >}
52
+ */
53
+ export async function runLauncher(configOrConfigs, opts = {}) {
54
+ if (Array.isArray(configOrConfigs)) {
55
+ return runFleet(configOrConfigs, opts);
56
+ }
57
+ return runSingle(configOrConfigs, opts);
58
+ }
59
+
60
+ async function runSingle(config, {
61
+ operatorKey,
62
+ label = 'agent',
63
+ port = process.env.PORT || 8080,
64
+ healthServer = true,
65
+ onShutdown,
66
+ } = {}) {
67
+ const effectiveKey = operatorKey ?? process.env.ZIGGS_OPERATOR_KEY;
68
+ const decorated = effectiveKey && !config.operatorKey
69
+ ? { ...config, operatorKey: effectiveKey }
70
+ : config;
71
+ const agent = new ZiggsAgent(decorated);
72
+ await agent.connectAsync();
73
+ console.log(`[${label}] "${agent.options.ownAgentId ?? agent.options.name ?? 'agent'}" connected`);
74
+
75
+ const server = healthServer ? createHealthServer({ port, label }) : null;
76
+
77
+ process.on('SIGTERM', async () => {
78
+ console.log(`[${label}] SIGTERM — shutting down`);
79
+ agent.disconnect();
80
+ try { await onShutdown?.(); } catch (err) { console.warn(`[${label}] onShutdown error: ${err.message}`); }
81
+ server?.close();
82
+ process.exit(0);
83
+ });
84
+
85
+ return { agent, healthServer: server ?? undefined };
86
+ }
87
+
88
+ async function runFleet(configs, {
89
+ operatorKey,
90
+ wsUrl = process.env.WS_URL,
91
+ port = process.env.PORT || 8080,
92
+ healthServer = true,
93
+ label = 'launcher',
94
+ agentMeta,
95
+ orchestrator: orchestratorConfig,
96
+ preWake = [],
97
+ poolOptions,
98
+ onShutdown,
99
+ } = {}) {
100
+ const pool = new ConnectionPool(poolOptions);
101
+ // Decorate each config with operatorKey so agent sockets pick it up when they connect.
102
+ const effectiveKey = operatorKey ?? process.env.ZIGGS_OPERATOR_KEY;
103
+ const decoratedConfigs = effectiveKey
104
+ ? configs.map(c => (c.operatorKey ? c : { ...c, operatorKey: effectiveKey }))
105
+ : configs;
106
+ pool.register(decoratedConfigs, agentMeta);
107
+ const server = healthServer ? createHealthServer({ port, label }) : null;
108
+
109
+ // Optional always-on orchestrator (own socket; not managed by pool idle-eviction).
110
+ // Accept either a plain config or a builder (pool) => config — orchestrators
111
+ // whose tools reference the pool need the builder form.
112
+ let resolvedOrchConfig = typeof orchestratorConfig === 'function'
113
+ ? orchestratorConfig(pool)
114
+ : orchestratorConfig;
115
+ if (resolvedOrchConfig && effectiveKey && !resolvedOrchConfig.operatorKey) {
116
+ resolvedOrchConfig = { ...resolvedOrchConfig, operatorKey: effectiveKey };
117
+ }
118
+
119
+ // Control socket: single WS that registers all agentIds as "available" and
120
+ // receives launcher:wake frames from the backend. Prefer explicit opts.operatorKey;
121
+ // fall back to scanning configs so hello-world callers need no extra wiring.
122
+ const controlKey = effectiveKey
123
+ ?? configs.find(c => c.operatorKey)?.operatorKey
124
+ ?? resolvedOrchConfig?.operatorKey;
125
+ if (controlKey) {
126
+ pool.startControl({ wsUrl, operatorKey: controlKey });
127
+ } else {
128
+ console.warn(`[${label}] No operatorKey provided — skipping control socket`);
129
+ }
130
+
131
+ let orchestrator = null;
132
+ if (resolvedOrchConfig) {
133
+ orchestrator = new ZiggsAgent(resolvedOrchConfig);
134
+ await orchestrator.connectAsync();
135
+ console.log(`[${label}] Orchestrator "${resolvedOrchConfig.name || resolvedOrchConfig.ownAgentId || 'orchestrator'}" connected`);
136
+ }
137
+
138
+ if (preWake.length > 0) {
139
+ await Promise.all(preWake.map(id =>
140
+ pool.wake(id)
141
+ .then(() => console.log(`[${label}] Pre-woke "${id}"`))
142
+ .catch(err => console.warn(`[${label}] Could not pre-wake "${id}": ${err.message}`))
143
+ ));
144
+ }
145
+
146
+ console.log(`[${label}] Ready — ${pool.size} agent(s) registered, ${pool.listActive().length} connected`);
147
+
148
+ process.on('SIGTERM', async () => {
149
+ console.log(`[${label}] SIGTERM — shutting down`);
150
+ pool.stopControl();
151
+ orchestrator?.disconnect();
152
+ await pool.disconnectAll();
153
+ try { await onShutdown?.(); } catch (err) { console.warn(`[${label}] onShutdown error: ${err.message}`); }
154
+ server?.close();
155
+ process.exit(0);
156
+ });
157
+
158
+ return { pool, orchestrator, healthServer: server ?? undefined };
159
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Shared id helpers (e.g. operation ids for tool runs).
3
+ */
4
+
5
+ export function generateId() {
6
+ return `${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
7
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Centralized type constants and magic strings used throughout the Agent system
3
+ *
4
+ * This file contains two main type systems:
5
+ * 1. Agent Types: Types used by the Agent core system (events, tasks, etc.)
6
+ * 2. Timeline Types: Re-exported from @ziggs-ai/api-client for convenience
7
+ */
8
+
9
+ // Re-export timeline types from api-client
10
+ export { EntryTypes, ContentTypes } from '@ziggs-ai/api-client';
11
+
12
+ // ============================================================================
13
+ // AGENT TYPES SYSTEM
14
+ // ============================================================================
15
+
16
+ // Event Types
17
+ export const EventTypes = {
18
+ MESSAGE: 'message',
19
+ TOOL_RESULT: 'tool_result',
20
+ TOOL_ERROR: 'tool_error',
21
+ TASK_RESULT: 'task_result',
22
+ TASK_ERROR: 'task_error',
23
+ };
24
+
25
+ // ⚠️ SYNC: Task states re-exported from taskCore.js — see backend/src/tasks/task-core.ts
26
+ export { TaskState as TaskStates, TERMINAL_STATES, OPEN_PROPOSAL_TARGET } from '../tasks/taskCore.js';
27
+
28
+ // Task Operations
29
+ export const TaskOperations = {
30
+ MAKE_TASK: 'make-task',
31
+ MAKE_SUB_TASKS: 'make-sub-tasks',
32
+ UPDATE_TASK: 'update-task',
33
+ RESPOND_PROPOSAL: 'respond-proposal',
34
+ };
35
+
36
+ // Context Properties
37
+ export const ContextProperties = {
38
+ HISTORY: 'history',
39
+ ACTIVE_TASKS: 'activeTasks',
40
+ AGENTS: 'agents',
41
+ USERS: 'users',
42
+ };
43
+
44
+ // Decision Properties
45
+ export const DecisionProperties = {
46
+ TASK: 'task',
47
+ ACTION: 'action',
48
+ REASONING: 'reasoning',
49
+ };
50
+
51
+ // Task Properties
52
+ export const TaskProperties = {
53
+ STATE: 'state',
54
+ SUBTASKS: 'subtasks',
55
+ TASK_ID: 'taskId',
56
+ DESCRIPTION: 'description',
57
+ OPERATION: 'operation',
58
+ STATUS: 'status',
59
+ RESULT: 'result',
60
+ EXECUTOR_ID: 'executorId',
61
+ TYPE: 'type',
62
+ };
63
+
64
+ // Operation Types
65
+ export const OperationTypes = {
66
+ TOOL: 'tool',
67
+ };
68
+
69
+ // Operation States
70
+ export const OperationStates = {
71
+ STARTED: 'started',
72
+ COMPLETED: 'completed',
73
+ ERROR: 'error',
74
+ };
75
+
76
+ // Participant Types
77
+ // Sender IDs
78
+ export const SenderIds = {
79
+ SYSTEM: 'system',
80
+ AGENT: 'agent',
81
+ };
82
+
83
+ // Task Types
84
+ export const TaskTypes = {
85
+ CONVERSATION: 'conversation',
86
+ };