@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.
- package/README.md +82 -0
- package/package.json +26 -0
- package/src/ConnectionPool.js +133 -0
- package/src/adapters/OpenAIAdapter.js +73 -0
- package/src/adapters/index.js +1 -0
- package/src/agent/Agent.js +121 -0
- package/src/agent/EventQueue.js +68 -0
- package/src/agent/OutboxBuffer.js +62 -0
- package/src/cognition/PromptBuilder.js +312 -0
- package/src/cognition/resolveActionTool.js +12 -0
- package/src/cognition/runTurn.js +578 -0
- package/src/context/applyEffects.js +133 -0
- package/src/context/batch.js +25 -0
- package/src/context/classifyEnvelope.js +82 -0
- package/src/context/routingLabels.js +54 -0
- package/src/createHealthServer.js +28 -0
- package/src/formatters/HistoryFormatter.js +257 -0
- package/src/formatters/TaskFormatter.js +180 -0
- package/src/formatters/index.js +9 -0
- package/src/index.js +76 -0
- package/src/ingress/normalizeIncoming.js +70 -0
- package/src/runLauncher.js +159 -0
- package/src/shared/ids.js +7 -0
- package/src/shared/types.js +86 -0
- package/src/tasks/TaskService.js +247 -0
- package/src/tasks/index.js +9 -0
- package/src/tasks/taskCore.js +229 -0
- package/src/tasks/taskProtocolRegistry.js +22 -0
- package/src/tasks/taskProtocolRunner.js +107 -0
- package/src/tasks/taskProtocolTools.js +87 -0
- package/src/tools/ToolManager.js +79 -0
- package/src/tools/ToolProvider.js +29 -0
- package/src/tools/defineTool.js +82 -0
- package/src/tools/index.js +11 -0
- package/src/utils/jsonExtractor.js +139 -0
- package/src/workflow/AgentMachine.js +250 -0
- package/src/workflow/WorkflowRuntime.js +63 -0
- package/src/workflow/dsl.js +287 -0
- package/src/workflow/motifs.js +435 -0
- package/src/ziggs/runtime.js +192 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { HistoryFormatter, TaskFormatter } from '../formatters/index.js';
|
|
2
|
+
import { resolveActionTool } from './resolveActionTool.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Builds agent prompts from state/action definitions (`buildFromState`).
|
|
6
|
+
*
|
|
7
|
+
* Prompt structure:
|
|
8
|
+
* <agent>
|
|
9
|
+
* <self>identity + state prompt</self>
|
|
10
|
+
* <world>event + history + tasks + recipients</world>
|
|
11
|
+
* <decision_schema>actions with prompts</decision_schema>
|
|
12
|
+
* </agent>
|
|
13
|
+
*/
|
|
14
|
+
export class PromptBuilder {
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
this.description = options.description || 'A capable agent ready to assist';
|
|
17
|
+
this.specialization = options.specialization || null;
|
|
18
|
+
this.maxHistoryEntries = options.maxHistoryEntries ?? 50;
|
|
19
|
+
this.historyFormatter = options.historyFormatter || new HistoryFormatter();
|
|
20
|
+
this.taskFormatter = options.taskFormatter || new TaskFormatter();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
buildFromState({
|
|
24
|
+
statePrompt, actions, serverContext, incomingEvent, definition, chatId,
|
|
25
|
+
stateId, machineContext,
|
|
26
|
+
}) {
|
|
27
|
+
const ctx = serverContext?.context || serverContext || {};
|
|
28
|
+
const agentId = this._getMyAgentId(ctx);
|
|
29
|
+
const senderMeta = this._getSenderMeta(incomingEvent, ctx);
|
|
30
|
+
const allTasks = ctx.activeTasks || [];
|
|
31
|
+
const otherAgents = (ctx.agents || []).filter(a => !a.isYou);
|
|
32
|
+
const users = ctx.users || [];
|
|
33
|
+
const desc = definition?.description || this.description;
|
|
34
|
+
|
|
35
|
+
const prompt = `<agent>
|
|
36
|
+
|
|
37
|
+
${this._renderSelfFromState(desc, statePrompt, serverContext?.tools)}
|
|
38
|
+
|
|
39
|
+
${this._renderWorld(incomingEvent, ctx, agentId, senderMeta, allTasks, otherAgents, users, stateId, machineContext)}
|
|
40
|
+
|
|
41
|
+
${this._renderDecisionSchemaFromActions(actions, senderMeta, ctx)}
|
|
42
|
+
|
|
43
|
+
</agent>`;
|
|
44
|
+
|
|
45
|
+
return prompt;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
_renderSelfFromState(description, statePrompt, tools) {
|
|
49
|
+
const lines = ['<self>'];
|
|
50
|
+
lines.push(`<identity>\nYou are an Autonomous Agent.\n${description}\n</identity>`);
|
|
51
|
+
|
|
52
|
+
lines.push('\n<state>');
|
|
53
|
+
if (statePrompt.role) lines.push(`Role: ${statePrompt.role}`);
|
|
54
|
+
if (statePrompt.goal) lines.push(`Goal: ${statePrompt.goal}`);
|
|
55
|
+
if (statePrompt.context) lines.push(`Context: ${statePrompt.context}`);
|
|
56
|
+
if (statePrompt.constraints) lines.push(`Constraints: ${statePrompt.constraints}`);
|
|
57
|
+
lines.push('</state>');
|
|
58
|
+
|
|
59
|
+
if (tools?.length) {
|
|
60
|
+
lines.push('\n<tools>');
|
|
61
|
+
for (const tool of tools) {
|
|
62
|
+
const fn = tool.schema?.function || tool.schema;
|
|
63
|
+
const name = fn?.name;
|
|
64
|
+
const desc = fn?.description;
|
|
65
|
+
if (name) lines.push(`- ${name}${desc ? ': ' + desc : ''}`);
|
|
66
|
+
}
|
|
67
|
+
lines.push('</tools>');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
lines.push('</self>');
|
|
71
|
+
return lines.join('\n');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
_renderWorld(event, ctx, agentId, senderMeta, allTasks, otherAgents, users, stateId, machineContext) {
|
|
75
|
+
const lines = ['<world>'];
|
|
76
|
+
|
|
77
|
+
const machineBlock = this._renderMachineContext(stateId, machineContext);
|
|
78
|
+
if (machineBlock) {
|
|
79
|
+
lines.push(machineBlock);
|
|
80
|
+
lines.push('');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
lines.push(this._renderEvent(event, senderMeta));
|
|
84
|
+
lines.push('');
|
|
85
|
+
lines.push(this._renderHistory(ctx.history, agentId));
|
|
86
|
+
lines.push('');
|
|
87
|
+
lines.push(this._renderTasks(allTasks, agentId));
|
|
88
|
+
|
|
89
|
+
if (users.length > 0 || otherAgents.length > 0) {
|
|
90
|
+
lines.push('');
|
|
91
|
+
lines.push(this._renderRecipients(users, otherAgents));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
lines.push(`\n<context>\n currentTime: ${new Date().toISOString()}\n</context>`);
|
|
95
|
+
lines.push('</world>');
|
|
96
|
+
return lines.join('\n');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
_renderMachineContext(stateId, machineContext) {
|
|
100
|
+
const picked = pickAllowedMachineFields(stateId, machineContext);
|
|
101
|
+
if (!picked) return '';
|
|
102
|
+
const json = JSON.stringify(picked, null, 2);
|
|
103
|
+
return `<machine_context>\n${json}\n</machine_context>`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
_renderEvent(event, senderMeta) {
|
|
107
|
+
if (!event) return '<event type="none">No incoming event.</event>';
|
|
108
|
+
|
|
109
|
+
if (event.type === 'batch' && Array.isArray(event.events) && event.events.length > 0) {
|
|
110
|
+
const parts = event.events.map((e, i) => {
|
|
111
|
+
const sub = { ...senderMeta, senderId: e.senderId ?? senderMeta.senderId, senderType: e.senderType ?? senderMeta.senderType };
|
|
112
|
+
return (i > 0 ? '\n---\n' : '') + this._renderEvent(e, sub);
|
|
113
|
+
});
|
|
114
|
+
return `<event type="batch" count="${event.events.length}">\n${parts.join('')}\n</event>`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const eventType = event.type || 'message';
|
|
118
|
+
const from = `${senderMeta.senderId} (${(senderMeta.senderType || 'unknown').toLowerCase()})`;
|
|
119
|
+
|
|
120
|
+
if (eventType === 'task_result' && event.result?.taskId) {
|
|
121
|
+
return this._renderTaskResultEvent(event.result, from);
|
|
122
|
+
}
|
|
123
|
+
if (eventType === 'tool_result') {
|
|
124
|
+
const r = typeof event.result === 'object' ? JSON.stringify(event.result, null, 2) : String(event.result);
|
|
125
|
+
return `<event type="${eventType}" from="${from}">\nTool: ${event.tool}\nResult:\n${r}\n</event>`;
|
|
126
|
+
}
|
|
127
|
+
if (eventType === 'tool_error') {
|
|
128
|
+
return `<event type="${eventType}" from="${from}">\nTool Error: ${event.tool}\nMessage: ${event.error}\n</event>`;
|
|
129
|
+
}
|
|
130
|
+
if (eventType === 'message_sent') {
|
|
131
|
+
return `<event type="${eventType}" from="${from}">\nYour message was delivered. Do not repeat it.\n</event>`;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const content = event.text || event.result || event.content;
|
|
135
|
+
if (!content) return `<event type="${eventType}" from="${from}">(Empty)</event>`;
|
|
136
|
+
const str = typeof content === 'object' ? JSON.stringify(content) : content;
|
|
137
|
+
const display = str.length > 1000 ? str.slice(0, 1000) + '...' : str;
|
|
138
|
+
return `<event type="${eventType}" from="${from}">\n${display}\n</event>`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
_renderTaskResultEvent(task, from) {
|
|
142
|
+
const lines = [];
|
|
143
|
+
if (task.agentIdIsYou) { lines.push('YOUR TASK'); lines.push(''); }
|
|
144
|
+
lines.push(`Task: ${task.description}`);
|
|
145
|
+
lines.push(`ID: ${task.taskId}`);
|
|
146
|
+
lines.push(`State: ${task.state}`);
|
|
147
|
+
lines.push(`Owner: ${task.agentId}${task.agentIdIsYou ? ' (You)' : ''}`);
|
|
148
|
+
lines.push(`Executor: ${task.executorId}${task.executorIdIsYou ? ' (You)' : ''}`);
|
|
149
|
+
if (task.proposal) {
|
|
150
|
+
lines.push(`Proposal: ${task.proposal.status} (to ${task.proposal.proposedTo}${task.proposedToIsYou ? ' - You' : ''})`);
|
|
151
|
+
}
|
|
152
|
+
if (task.parentTaskId) lines.push(`Parent: ${task.parentTaskId}`);
|
|
153
|
+
|
|
154
|
+
if (task.contract?.specialistAgentId) {
|
|
155
|
+
lines.push(`contract.specialistAgentId: ${task.contract.specialistAgentId}`);
|
|
156
|
+
}
|
|
157
|
+
if (Array.isArray(task.contract?.specialistAgentIds) && task.contract.specialistAgentIds.length > 0) {
|
|
158
|
+
lines.push(`contract.specialistAgentIds: ${task.contract.specialistAgentIds.join(', ')}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (task.state === 'proposal' && task.proposal?.status === 'pending' && (task.proposedToIsYou || task.proposal?.proposedTo === 'everyone')) {
|
|
162
|
+
lines.push('');
|
|
163
|
+
lines.push(`Action: Use task_respond_proposal with taskId="${task.taskId}" and action approve or reject.`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return `<event type="task_result" from="${from}"${task.agentIdIsYou ? ' own_task="true"' : ''}>\n${lines.join('\n')}\n</event>`;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
_renderHistory(history, agentId) {
|
|
170
|
+
let entries = history || [];
|
|
171
|
+
let truncNote = '';
|
|
172
|
+
if (entries.length > this.maxHistoryEntries) {
|
|
173
|
+
truncNote = `[Showing last ${this.maxHistoryEntries} of ${entries.length} entries]\n\n`;
|
|
174
|
+
entries = entries.slice(-this.maxHistoryEntries);
|
|
175
|
+
}
|
|
176
|
+
const formatted = this.historyFormatter.format(entries, agentId, {});
|
|
177
|
+
return `<history>\n${truncNote}${formatted}\n</history>`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
_renderTasks(tasks, agentId) {
|
|
181
|
+
const formatted = this.taskFormatter.format(tasks || [], agentId);
|
|
182
|
+
return `<tasks>\n${formatted}\n</tasks>`;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
_renderRecipients(users, otherAgents) {
|
|
186
|
+
const lines = ['<recipients>'];
|
|
187
|
+
if (users.length > 0) {
|
|
188
|
+
lines.push('<users>');
|
|
189
|
+
users.forEach(u => lines.push(` - ${u.userName || u.userId} (${u.userId})`));
|
|
190
|
+
lines.push('</users>');
|
|
191
|
+
}
|
|
192
|
+
if (otherAgents.length > 0) {
|
|
193
|
+
lines.push('<agents>');
|
|
194
|
+
otherAgents.forEach(a => {
|
|
195
|
+
const desc = a.description ? `: ${a.description.slice(0, 80)}` : '';
|
|
196
|
+
const caps = a.capabilities && Object.keys(a.capabilities).length > 0
|
|
197
|
+
? ` [capabilities: ${JSON.stringify(a.capabilities)}]`
|
|
198
|
+
: '';
|
|
199
|
+
lines.push(` - ${a.name || a.agentId} (${a.agentId})${desc}${caps}`);
|
|
200
|
+
});
|
|
201
|
+
lines.push('</agents>');
|
|
202
|
+
}
|
|
203
|
+
if (users.length === 0 && otherAgents.length === 0) {
|
|
204
|
+
lines.push('No other recipients available.');
|
|
205
|
+
}
|
|
206
|
+
lines.push('</recipients>');
|
|
207
|
+
return lines.join('\n');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
_renderDecisionSchemaFromActions(actions, senderMeta, ctx) {
|
|
211
|
+
const lines = ['<decision_schema>'];
|
|
212
|
+
lines.push('Return ONE JSON object. ALWAYS include "thought" as the FIRST field — briefly explain your reasoning before choosing an action.');
|
|
213
|
+
lines.push('Set "action" to exactly one of the action names below.\n');
|
|
214
|
+
|
|
215
|
+
const replyTo = senderMeta.defaultReplyReceiverId || '<receiverId>';
|
|
216
|
+
const pendingProposals = (ctx.activeTasks || []).filter(
|
|
217
|
+
t => t.state === 'proposal' && t.proposal?.status === 'pending' && (t.proposedToIsYou || t.proposal?.proposedTo === 'everyone')
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
if (pendingProposals.length > 0) {
|
|
221
|
+
lines.push(`PRIORITY: You have ${pendingProposals.length} pending proposal(s) directed at you. Respond BEFORE other actions.`);
|
|
222
|
+
lines.push(`{"thought": "...", "action": "<action_bound_to_task_respond_proposal>", "args": {"taskId": "<id>", "action": "approve|reject"}}\n`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
for (const [name, actionDef] of Object.entries(actions)) {
|
|
226
|
+
lines.push(`─── "${name}" ───`);
|
|
227
|
+
if (actionDef.prompt?.when) lines.push(`Use when: ${actionDef.prompt.when}`);
|
|
228
|
+
if (actionDef.prompt?.instruction) lines.push(`Instruction: ${actionDef.prompt.instruction}`);
|
|
229
|
+
if (actionDef.prompt?.format) lines.push(`Format: ${actionDef.prompt.format}`);
|
|
230
|
+
|
|
231
|
+
const boundTool = resolveActionTool(actionDef, name);
|
|
232
|
+
if (boundTool) {
|
|
233
|
+
lines.push(`Tool: ${boundTool}`);
|
|
234
|
+
lines.push(`Response: {"thought": "...", "action": "${name}", "args": {...}}`);
|
|
235
|
+
} else {
|
|
236
|
+
lines.push(this._buildActionSchema(name, replyTo));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (actionDef.prompt?.examples?.length) {
|
|
240
|
+
lines.push('Examples:');
|
|
241
|
+
for (const ex of actionDef.prompt.examples) {
|
|
242
|
+
const desc = ex.scenario || ex.input || '';
|
|
243
|
+
const out = ex.output || ex.query || '';
|
|
244
|
+
lines.push(` - ${desc}${out ? ' → ' + out : ''}`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
lines.push('');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
lines.push('</decision_schema>');
|
|
252
|
+
return lines.join('\n');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
_buildActionSchema(name, replyTo) {
|
|
256
|
+
if (name === 'wait') {
|
|
257
|
+
return `Response: {"thought": "...", "action": "${name}"}`;
|
|
258
|
+
}
|
|
259
|
+
return `Response: {"thought": "...", "action": "${name}", "receiverId": "${replyTo}", "message": "..."}`;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
_getSenderMeta(event, ctx) {
|
|
263
|
+
const senderId = event?.senderId || 'system';
|
|
264
|
+
const senderType = event?.senderType ? event.senderType.toUpperCase() : 'SYSTEM';
|
|
265
|
+
const myId = this._getMyAgentId(ctx);
|
|
266
|
+
|
|
267
|
+
let defaultReplyReceiverId = senderId;
|
|
268
|
+
const isNonRoutable = !senderId || senderId === 'system' || senderType === 'SERVICE';
|
|
269
|
+
if (isNonRoutable) {
|
|
270
|
+
const history = ctx.history || [];
|
|
271
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
272
|
+
const id = history[i].sender?.id || history[i].senderId;
|
|
273
|
+
const type = (history[i].sender?.type || history[i].senderType || '').toUpperCase();
|
|
274
|
+
if (id && id !== 'system' && id !== myId && type !== 'SERVICE') {
|
|
275
|
+
defaultReplyReceiverId = id;
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return { senderId, senderType, myAgentId: myId, defaultReplyReceiverId: defaultReplyReceiverId || senderId };
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
_getMyAgentId(ctx) {
|
|
285
|
+
const agents = ctx.agents || [];
|
|
286
|
+
const me = agents.find(a => a.isYou);
|
|
287
|
+
return me?.agentId || null;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const MACHINE_CONTEXT_KEYS = [
|
|
292
|
+
'chatId', 'laneKey', 'ownAgentId', 'activeTaskId', 'pendingProposalId', 'perspectiveId',
|
|
293
|
+
'delegatedTaskIds', 'orchestrationSpecialistAgentId', 'orchestrationSpecialistAgentIds',
|
|
294
|
+
'orchestrationActiveStepIndex', 'orchestrationContinueChain', 'lastError', 'lastAction',
|
|
295
|
+
];
|
|
296
|
+
|
|
297
|
+
function pickAllowedMachineFields(stateId, machineContext) {
|
|
298
|
+
if (!machineContext || typeof machineContext !== 'object') {
|
|
299
|
+
return stateId != null ? { stateId } : null;
|
|
300
|
+
}
|
|
301
|
+
const out = {};
|
|
302
|
+
if (stateId != null) out.stateId = stateId;
|
|
303
|
+
for (const k of MACHINE_CONTEXT_KEYS) {
|
|
304
|
+
const v = machineContext[k];
|
|
305
|
+
if (v !== undefined && v !== null) out[k] = v;
|
|
306
|
+
}
|
|
307
|
+
if (machineContext.incomingEvent != null) {
|
|
308
|
+
const ev = machineContext.incomingEvent;
|
|
309
|
+
out.incomingEventType = typeof ev === 'object' && ev && ev.type != null ? String(ev.type) : '(present)';
|
|
310
|
+
}
|
|
311
|
+
return Object.keys(out).length ? out : null;
|
|
312
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Each action may bind at most one registered tool. Args are chosen by the LLM;
|
|
3
|
+
* the tool name is fixed by the action definition's explicit `tool` property.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export function resolveActionTool(actionDef, actionKey) {
|
|
7
|
+
if (!actionDef) return null;
|
|
8
|
+
if (actionDef.tool === null) return null;
|
|
9
|
+
if (typeof actionDef.tool === 'string') return actionDef.tool;
|
|
10
|
+
if (Array.isArray(actionDef.tools) && actionDef.tools.length >= 1) return actionDef.tools[0];
|
|
11
|
+
return null;
|
|
12
|
+
}
|