@ziggs-ai/agent-sdk 0.1.3 → 0.1.5
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 +3 -1
- package/package.json +9 -4
- package/src/AgentHost.ts +495 -0
- package/src/adapters/OpenAIAdapter.ts +146 -0
- package/src/agent/Agent.ts +101 -0
- package/src/cognition/validateContext.ts +95 -0
- package/src/context/applyEffects.ts +80 -0
- package/src/context/batch.ts +17 -0
- package/src/context/classifyEnvelope.ts +38 -0
- package/src/context/routingLabels.ts +46 -0
- package/src/defineAgent.ts +62 -0
- package/src/formatters/AgreementFormatter.ts +111 -0
- package/src/formatters/HistoryFormatter.ts +166 -0
- package/src/formatters/index.ts +2 -0
- package/src/index.ts +105 -0
- package/src/ingress/normalizeIncoming.ts +162 -0
- package/src/memory/MemoryStore.ts +104 -0
- package/src/pricing/fleetDefaults.ts +218 -0
- package/src/pricing/fleetEvalFree.ts +24 -0
- package/src/pricing/fleetFreeTierA.gen.ts +12 -0
- package/src/pricing/fleetTierByAgentId.gen.ts +1022 -0
- package/src/runtime/AgentMachine.ts +364 -0
- package/src/runtime/PromptBuilder.ts +463 -0
- package/src/runtime/buildOutcome.ts +518 -0
- package/src/runtime/defaults.ts +75 -0
- package/src/runtime/runTurn.ts +691 -0
- package/src/runtime/validateWorkflow.ts +181 -0
- package/src/server/ConnectionPool.ts +155 -0
- package/src/server/EventQueue.ts +133 -0
- package/src/server/InboxCatchUp.ts +251 -0
- package/src/server/OutboxBuffer.ts +90 -0
- package/src/server/SeenMessages.ts +27 -0
- package/src/server/ZiggsEffectHandler.ts +409 -0
- package/src/server/agreements/AgreementService.ts +117 -0
- package/src/server/createHealthServer.ts +85 -0
- package/src/server/proactive/ProactiveTrigger.ts +83 -0
- package/src/server/runLauncher.ts +146 -0
- package/src/server/tasks/TaskService.ts +110 -0
- package/src/server/tasks/index.ts +1 -0
- package/src/server/telemetryIngest.ts +91 -0
- package/src/server/tools/index.ts +46 -0
- package/src/server/tools/tier1/protocolRunner.ts +133 -0
- package/src/server/tools/tier1/protocolTools.ts +99 -0
- package/src/server/tools/tier2/connectionTools.ts +75 -0
- package/src/server/tools/tier2/contextTools.ts +74 -0
- package/src/server/tools/tier2/discoveryTools.ts +34 -0
- package/src/server/tools/tier2/marketplaceTools.ts +25 -0
- package/src/server/tools/tier2/paymentTools.ts +193 -0
- package/src/server/ziggsconnect/ZiggsConnectClient.ts +126 -0
- package/src/server/ziggscontext/ZiggsContextClient.ts +137 -0
- package/src/server/ziggspay/ZiggsPayClient.ts +193 -0
- package/src/shared/ids.ts +3 -0
- package/src/shared/runtimeLog.ts +72 -0
- package/src/shared/types.ts +29 -0
- package/src/tasks/protocolRegistry.ts +25 -0
- package/src/tools/ToolManager.ts +95 -0
- package/src/tools/{ToolProvider.js → ToolProvider.ts} +5 -15
- package/src/tools/defineTool.ts +90 -0
- package/src/tools/index.ts +5 -0
- package/src/types.ts +407 -0
- package/src/utils/jsonExtractor.ts +100 -0
- package/src/ConnectionPool.js +0 -133
- package/src/adapters/OpenAIAdapter.js +0 -73
- package/src/agent/Agent.js +0 -121
- package/src/agent/EventQueue.js +0 -68
- package/src/agent/OutboxBuffer.js +0 -62
- package/src/cognition/PromptBuilder.js +0 -312
- package/src/cognition/resolveActionTool.js +0 -12
- package/src/cognition/runTurn.js +0 -578
- package/src/context/applyEffects.js +0 -133
- package/src/context/batch.js +0 -25
- package/src/context/classifyEnvelope.js +0 -82
- package/src/context/routingLabels.js +0 -54
- package/src/createHealthServer.js +0 -28
- package/src/formatters/HistoryFormatter.js +0 -257
- package/src/formatters/TaskFormatter.js +0 -180
- package/src/formatters/index.js +0 -9
- package/src/index.js +0 -76
- package/src/ingress/normalizeIncoming.js +0 -70
- package/src/runLauncher.js +0 -159
- package/src/shared/ids.js +0 -7
- package/src/shared/types.js +0 -86
- package/src/tasks/TaskService.js +0 -247
- package/src/tasks/index.js +0 -9
- package/src/tasks/taskCore.js +0 -229
- package/src/tasks/taskProtocolRegistry.js +0 -22
- package/src/tasks/taskProtocolRunner.js +0 -107
- package/src/tasks/taskProtocolTools.js +0 -87
- package/src/tools/ToolManager.js +0 -79
- package/src/tools/defineTool.js +0 -82
- package/src/tools/index.js +0 -11
- package/src/utils/jsonExtractor.js +0 -139
- package/src/workflow/AgentMachine.js +0 -250
- package/src/workflow/WorkflowRuntime.js +0 -63
- package/src/workflow/dsl.js +0 -287
- package/src/workflow/motifs.js +0 -435
- package/src/ziggs/runtime.js +0 -192
- /package/src/adapters/{index.js → index.ts} +0 -0
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import OpenAI from "openai";
|
|
2
|
-
|
|
3
|
-
export class OpenAIAdapter {
|
|
4
|
-
constructor({key, model = "gpt-4o-mini"}) {
|
|
5
|
-
this.client = new OpenAI({apiKey: key});
|
|
6
|
-
this.model = model;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Single-turn chat. `prompt` is a string; wraps it as a user message.
|
|
11
|
-
* Kept for backward compatibility.
|
|
12
|
-
*/
|
|
13
|
-
async chat(prompt, tools = [], temperature = 0.2, systemHints = [], options = {}) {
|
|
14
|
-
const messages = [
|
|
15
|
-
{ role: "system", content: "Respond with valid JSON." },
|
|
16
|
-
...systemHints,
|
|
17
|
-
{ role: "user", content: prompt }
|
|
18
|
-
];
|
|
19
|
-
return this.chatMessages(messages, tools, options);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Low-level chat that accepts a full messages array.
|
|
24
|
-
* Returns { content, tool_calls, message } where message is the raw assistant message.
|
|
25
|
-
*/
|
|
26
|
-
async chatMessages(messages, tools = [], options = {}) {
|
|
27
|
-
const openAITools = tools.length > 0
|
|
28
|
-
? tools.map(t => {
|
|
29
|
-
const schema = t.schema || t;
|
|
30
|
-
const fn = schema.function || schema;
|
|
31
|
-
const usageWhen = t.usage?.when ? ` Use when: ${t.usage.when}` : '';
|
|
32
|
-
return {
|
|
33
|
-
type: 'function',
|
|
34
|
-
function: {
|
|
35
|
-
...fn,
|
|
36
|
-
description: (fn.description || '') + usageWhen
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
})
|
|
40
|
-
: undefined;
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
const res = await this.client.chat.completions.create({
|
|
44
|
-
model: this.model,
|
|
45
|
-
messages,
|
|
46
|
-
max_completion_tokens: 3000,
|
|
47
|
-
response_format: openAITools ? undefined : (options.response_format ?? { type: 'json_object' }),
|
|
48
|
-
...(openAITools && { tools: openAITools }),
|
|
49
|
-
...options,
|
|
50
|
-
usage: undefined, // strip internal field if accidentally passed
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
const msg = res.choices[0].message;
|
|
54
|
-
return {
|
|
55
|
-
content: msg.content?.trim() ?? null,
|
|
56
|
-
tool_calls: msg.tool_calls?.length ? msg.tool_calls : null,
|
|
57
|
-
message: msg,
|
|
58
|
-
usage: res.usage,
|
|
59
|
-
};
|
|
60
|
-
} catch (error) {
|
|
61
|
-
console.error(`❌ [OpenAIAdapter] LLM API call failed: ${error.message}`, {
|
|
62
|
-
model: this.model,
|
|
63
|
-
error: error.message,
|
|
64
|
-
errorType: error.constructor?.name || 'Error',
|
|
65
|
-
status: error.status || error.statusCode || 'unknown',
|
|
66
|
-
code: error.code || 'unknown',
|
|
67
|
-
...(error.response && { response: error.response }),
|
|
68
|
-
stack: error.stack
|
|
69
|
-
});
|
|
70
|
-
throw error;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
package/src/agent/Agent.js
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { runTurn } from '../cognition/runTurn.js';
|
|
2
|
-
import { OutboxBuffer } from './OutboxBuffer.js';
|
|
3
|
-
import { EventQueue } from './EventQueue.js';
|
|
4
|
-
import { WorkflowRuntime } from '../workflow/WorkflowRuntime.js';
|
|
5
|
-
import { normalizeIncomingEvent } from '../ingress/normalizeIncoming.js';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Orchestrates one workflow instance per lane (ownAgentId:chatId).
|
|
9
|
-
* Each lane gets an AgentMachine; thinking states invoke `runTurn`.
|
|
10
|
-
*/
|
|
11
|
-
export class Agent {
|
|
12
|
-
constructor(options = {}) {
|
|
13
|
-
const {
|
|
14
|
-
llm, toolManager, contextReader, contextWriter,
|
|
15
|
-
taskService, messageSender, promptBuilder, operatorKey, agentId,
|
|
16
|
-
} = options;
|
|
17
|
-
|
|
18
|
-
if (!llm) throw new Error('Agent: llm is required');
|
|
19
|
-
if (!operatorKey) throw new Error('Agent: operatorKey is required');
|
|
20
|
-
if (!agentId) throw new Error('Agent: agentId is required');
|
|
21
|
-
if (!contextReader) throw new Error('Agent: contextReader is required');
|
|
22
|
-
if (!promptBuilder) throw new Error('Agent: promptBuilder is required');
|
|
23
|
-
|
|
24
|
-
this.ownAgentId = agentId;
|
|
25
|
-
this.messageBuffer = new OutboxBuffer();
|
|
26
|
-
this._laneMeta = new Map();
|
|
27
|
-
this.definition = options.workflow;
|
|
28
|
-
if (!this.definition?.states) throw new Error('Agent: workflow definition with states is required');
|
|
29
|
-
|
|
30
|
-
this._services = {
|
|
31
|
-
llm, toolManager, contextReader, contextWriter,
|
|
32
|
-
taskService, messageSender, messageBuffer: this.messageBuffer,
|
|
33
|
-
promptBuilder, operatorKey, agentId: this.ownAgentId,
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
this.runtime = new WorkflowRuntime({
|
|
37
|
-
definition: this.definition,
|
|
38
|
-
executionCore: runTurn,
|
|
39
|
-
services: this._services,
|
|
40
|
-
onSnapshot: (laneKey, snapshot) => this._onWorkflowChange(laneKey, snapshot),
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
this.eventQueue = new EventQueue((event, laneKey) => this._processEvent(event, laneKey));
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async handleMessage(text, metadata = {}) {
|
|
47
|
-
const ownAgentId = this._resolveOwnAgentId(metadata);
|
|
48
|
-
const normalized = normalizeIncomingEvent({ text, metadata, ownAgentId });
|
|
49
|
-
if (!normalized.shouldProcess) {
|
|
50
|
-
console.log(`[Agent] Skip reason=${normalized.reason || 'not_relevant'} ownAgentId=${ownAgentId || 'unknown'}`);
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
const chatId = normalized.chatId;
|
|
54
|
-
if (!chatId) { console.warn('[Agent] No chatId, skipping'); return; }
|
|
55
|
-
|
|
56
|
-
const laneKey = this._buildLaneKey(ownAgentId, chatId);
|
|
57
|
-
this._laneMeta.set(laneKey, { chatId, ownAgentId });
|
|
58
|
-
return this.eventQueue.enqueue(normalized.event, laneKey);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async _processEvent(event, laneKey) {
|
|
62
|
-
const meta = this._laneMeta.get(laneKey) || this._parseLaneKey(laneKey);
|
|
63
|
-
const chatId = meta?.chatId || null;
|
|
64
|
-
const ownAgentId = meta?.ownAgentId || this.ownAgentId || null;
|
|
65
|
-
if (!chatId) return;
|
|
66
|
-
|
|
67
|
-
await this.runtime.sendAndWaitForPark({
|
|
68
|
-
laneKey,
|
|
69
|
-
event: { type: 'INCOMING_EVENT', event, chatId, laneKey, ownAgentId },
|
|
70
|
-
timeoutMs: 120_000,
|
|
71
|
-
machineInput: { chatId, laneKey, ownAgentId },
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
_onWorkflowChange(laneKey, snapshot) {
|
|
76
|
-
if (!snapshot.context) return;
|
|
77
|
-
const machine = this.runtime.actors.get(laneKey);
|
|
78
|
-
if (!machine) return;
|
|
79
|
-
|
|
80
|
-
const prev = machine._prevSnapshot;
|
|
81
|
-
machine._prevSnapshot = snapshot;
|
|
82
|
-
if (!prev) return;
|
|
83
|
-
|
|
84
|
-
const wasActive = !this._isParkedSnapshot(prev);
|
|
85
|
-
const nowParked = this._isParkedSnapshot(snapshot);
|
|
86
|
-
|
|
87
|
-
if (wasActive && nowParked) {
|
|
88
|
-
console.log(`[workflow] parked laneKey=${laneKey} state=${JSON.stringify(snapshot.value)}`);
|
|
89
|
-
this._trackMessages(snapshot);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
_isParkedSnapshot(snapshot) {
|
|
94
|
-
const sd = this.definition.states[snapshot.value];
|
|
95
|
-
return !(sd?.prompt && sd?.actions);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
_trackMessages(snapshot) {
|
|
99
|
-
const ctx = snapshot.context;
|
|
100
|
-
if (!ctx?.chatId || !ctx.messageSent) return;
|
|
101
|
-
const r = ctx.lastActionResult;
|
|
102
|
-
if (r?.type === 'message_sent') {
|
|
103
|
-
this.messageBuffer.track(r.chatId || ctx.chatId, { text: r.message, receiverId: r.receiverId });
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
_resolveOwnAgentId(metadata = {}) {
|
|
108
|
-
return this.ownAgentId || metadata.self?.id || metadata.agent?.id || metadata.ziggsAgentId || null;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
_buildLaneKey(ownAgentId, chatId) {
|
|
112
|
-
return `${ownAgentId || 'unknown-agent'}:${chatId}`;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
_parseLaneKey(laneKey) {
|
|
116
|
-
if (!laneKey || typeof laneKey !== 'string') return null;
|
|
117
|
-
const idx = laneKey.lastIndexOf(':');
|
|
118
|
-
if (idx === -1) return { ownAgentId: null, chatId: laneKey };
|
|
119
|
-
return { ownAgentId: laneKey.slice(0, idx) || null, chatId: laneKey.slice(idx + 1) || null };
|
|
120
|
-
}
|
|
121
|
-
}
|
package/src/agent/EventQueue.js
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Serializes and coalesces events per lane to prevent duplicate LLM turns.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export class EventQueue {
|
|
6
|
-
constructor(processEventFn) {
|
|
7
|
-
this._processEvent = processEventFn;
|
|
8
|
-
/** @type {Map<string, { events: object[], processing: boolean }>} */
|
|
9
|
-
this._state = new Map();
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
enqueue(event, laneKey) {
|
|
13
|
-
if (!laneKey) {
|
|
14
|
-
console.warn('[EventQueue] enqueue called without laneKey, skipping');
|
|
15
|
-
return Promise.resolve();
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
let state = this._state.get(laneKey);
|
|
19
|
-
if (!state) {
|
|
20
|
-
state = { events: [], processing: false };
|
|
21
|
-
this._state.set(laneKey, state);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return new Promise((resolve) => {
|
|
25
|
-
state.events.push({ event, resolve });
|
|
26
|
-
console.log(`[EventQueue] enqueue laneKey=${laneKey}, queueLen=${state.events.length}, processing=${state.processing}`);
|
|
27
|
-
this._processIfNeeded(laneKey);
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
_processIfNeeded(laneKey) {
|
|
32
|
-
const state = this._state.get(laneKey);
|
|
33
|
-
if (!state || state.processing || state.events.length === 0) {
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
state.processing = true;
|
|
38
|
-
this._processLoop(laneKey);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async _processLoop(laneKey) {
|
|
42
|
-
const state = this._state.get(laneKey);
|
|
43
|
-
if (!state) return;
|
|
44
|
-
|
|
45
|
-
while (state.events.length > 0) {
|
|
46
|
-
const batch = state.events.splice(0, state.events.length);
|
|
47
|
-
const events = batch.map(e => e.event);
|
|
48
|
-
const resolvers = batch.map(e => e.resolve);
|
|
49
|
-
|
|
50
|
-
const coalesced = events.length === 1
|
|
51
|
-
? events[0]
|
|
52
|
-
: { type: 'batch', events };
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
await this._processEvent(coalesced, laneKey);
|
|
56
|
-
} catch (error) {
|
|
57
|
-
console.error(`[EventQueue] processEvent error for laneKey=${laneKey}:`, error.message);
|
|
58
|
-
console.error(error.stack);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Resolve all waiters for this batch (success or failure)
|
|
62
|
-
for (const r of resolvers) r();
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
state.processing = false;
|
|
66
|
-
console.log(`[EventQueue] processLoop done laneKey=${laneKey}`);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tracks outbound messages locally until server history syncs, then merges into read context.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export class OutboxBuffer {
|
|
6
|
-
constructor() {
|
|
7
|
-
this._pending = new Map();
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
track(chatId, { text, receiverId }) {
|
|
11
|
-
if (!this._pending.has(chatId)) {
|
|
12
|
-
this._pending.set(chatId, []);
|
|
13
|
-
}
|
|
14
|
-
this._pending.get(chatId).push({
|
|
15
|
-
text,
|
|
16
|
-
receiverId,
|
|
17
|
-
timestamp: Date.now()
|
|
18
|
-
});
|
|
19
|
-
console.log(`[OutboxBuffer] track: chatId=${chatId}, pending=${this._pending.get(chatId).length}, text="${text.slice(0, 60)}..."`);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
merge(chatId, history, agentId) {
|
|
23
|
-
const pending = this._pending.get(chatId);
|
|
24
|
-
console.log(`[OutboxBuffer] merge: chatId=${chatId}, pending=${pending?.length || 0}, historyLen=${history.length}, agentId=${agentId}`);
|
|
25
|
-
if (!pending || pending.length === 0) return;
|
|
26
|
-
|
|
27
|
-
const matchedIndices = new Set();
|
|
28
|
-
const remaining = [];
|
|
29
|
-
|
|
30
|
-
for (const msg of pending) {
|
|
31
|
-
const matchIdx = history.findIndex((entry, idx) =>
|
|
32
|
-
!matchedIndices.has(idx) &&
|
|
33
|
-
entry.text === msg.text &&
|
|
34
|
-
entry.sender?.id === agentId
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
if (matchIdx !== -1) {
|
|
38
|
-
matchedIndices.add(matchIdx);
|
|
39
|
-
} else {
|
|
40
|
-
remaining.push(msg);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
for (const msg of remaining) {
|
|
45
|
-
history.push({
|
|
46
|
-
entryType: 'message',
|
|
47
|
-
text: msg.text,
|
|
48
|
-
sender: { id: agentId, type: 'agent' },
|
|
49
|
-
receiver: { id: msg.receiverId },
|
|
50
|
-
timestamp: msg.timestamp
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
console.log(`[OutboxBuffer] merge result: matched=${pending.length - remaining.length}, appended=${remaining.length}, newHistoryLen=${history.length}`);
|
|
55
|
-
|
|
56
|
-
if (remaining.length === 0) {
|
|
57
|
-
this._pending.delete(chatId);
|
|
58
|
-
} else {
|
|
59
|
-
this._pending.set(chatId, remaining);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
@@ -1,312 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
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
|
-
}
|