@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.
- package/README.md +1 -1
- package/package.json +9 -4
- package/src/AgentHost.ts +342 -0
- package/src/adapters/OpenAIAdapter.ts +125 -0
- package/src/agent/Agent.ts +98 -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 +86 -0
- package/src/ingress/normalizeIncoming.ts +119 -0
- package/src/memory/MemoryStore.ts +104 -0
- package/src/runtime/AgentMachine.ts +298 -0
- package/src/runtime/PromptBuilder.ts +461 -0
- package/src/runtime/buildOutcome.ts +488 -0
- package/src/runtime/defaults.ts +72 -0
- package/src/runtime/runTurn.ts +637 -0
- package/src/runtime/validateWorkflow.ts +165 -0
- package/src/server/ConnectionPool.ts +155 -0
- package/src/server/EventQueue.ts +119 -0
- package/src/server/OutboxBuffer.ts +90 -0
- package/src/server/ZiggsEffectHandler.ts +335 -0
- package/src/server/agreements/AgreementService.ts +111 -0
- package/src/server/createHealthServer.ts +8 -0
- package/src/server/proactive/ProactiveTrigger.ts +83 -0
- package/src/server/runLauncher.ts +131 -0
- package/src/server/tasks/TaskService.ts +111 -0
- package/src/server/tasks/index.ts +4 -0
- package/src/server/tasks/paymentTools.ts +156 -0
- package/src/server/tasks/protocolRunner.ts +101 -0
- package/src/server/tasks/protocolTools.ts +96 -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 +31 -0
- package/src/tasks/protocolRegistry.ts +25 -0
- package/src/tasks/taskCore.ts +139 -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 +368 -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,133 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Maps action results into context fields for transition conditions, and holds CONTEXT_RESET.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { isTaskProtocolToolName, mapTaskProtocolToolToOperation } from '../tasks/taskProtocolRegistry.js';
|
|
6
|
-
|
|
7
|
-
function applyTaskOperationResultToContext(updates, operation, result) {
|
|
8
|
-
const r = result || {};
|
|
9
|
-
if (operation === 'make-task' || operation === 'make-sub-tasks') {
|
|
10
|
-
const isProposal = r.state === 'proposal' || r.proposal?.status === 'pending';
|
|
11
|
-
if (isProposal) {
|
|
12
|
-
// executorIdIsYou is a backend read-time field — absent from creation responses.
|
|
13
|
-
// Fallback: proposeToDoWork sets agentId === executorId (you are both owner and executor);
|
|
14
|
-
// delegateToAgent sets agentId !== executorId (specialist is the executor).
|
|
15
|
-
const isOwnProposal = r.executorIdIsYou || (r.agentId && r.agentId === r.executorId);
|
|
16
|
-
if (isOwnProposal) {
|
|
17
|
-
updates.proposal = r;
|
|
18
|
-
} else if (r.executorId) {
|
|
19
|
-
updates.delegatedTask = r;
|
|
20
|
-
if (r.taskId) {
|
|
21
|
-
if (!updates._delegatedTaskIds) updates._delegatedTaskIds = [];
|
|
22
|
-
updates._delegatedTaskIds.push(r.taskId);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
} else if (r.executorId) {
|
|
26
|
-
updates.delegatedTask = r;
|
|
27
|
-
// Accumulate all delegated task IDs for parallel batch delegation
|
|
28
|
-
if (r.taskId) {
|
|
29
|
-
if (!updates._delegatedTaskIds) updates._delegatedTaskIds = [];
|
|
30
|
-
updates._delegatedTaskIds.push(r.taskId);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
if (operation === 'update-task') {
|
|
35
|
-
const status = r.state || r.status;
|
|
36
|
-
if (status === 'completed') updates.taskCompleted = true;
|
|
37
|
-
if (status === 'failed') updates.taskFailed = true;
|
|
38
|
-
}
|
|
39
|
-
if (operation === 'respond-proposal') {
|
|
40
|
-
updates.respondedProposal = r;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export const CONTEXT_RESET = Object.freeze({
|
|
45
|
-
messageSent: false,
|
|
46
|
-
activeWait: false,
|
|
47
|
-
proposal: null,
|
|
48
|
-
delegatedTask: null,
|
|
49
|
-
taskCompleted: false,
|
|
50
|
-
taskFailed: false,
|
|
51
|
-
subtaskResult: null,
|
|
52
|
-
subtaskFailed: false,
|
|
53
|
-
toolResults: null,
|
|
54
|
-
lastError: null,
|
|
55
|
-
lastAction: null,
|
|
56
|
-
lastActionResult: null,
|
|
57
|
-
approval: false,
|
|
58
|
-
rejection: false,
|
|
59
|
-
taskAssignment: null,
|
|
60
|
-
/** User-approved orchestration root: specialist id from task.contract (see AgentMachine). */
|
|
61
|
-
orchestrationSpecialistAgentId: null,
|
|
62
|
-
/** When true, a delegated subtask finished and another specialist in the chain should run next. */
|
|
63
|
-
orchestrationContinueChain: false,
|
|
64
|
-
incomingMessage: false,
|
|
65
|
-
respondedProposal: null,
|
|
66
|
-
searchCompleted: false,
|
|
67
|
-
boardResults: null,
|
|
68
|
-
taskPublished: false,
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
export function buildContextUpdates(actionName, emittedEvents) {
|
|
72
|
-
const updates = { ...CONTEXT_RESET, lastAction: actionName };
|
|
73
|
-
|
|
74
|
-
if (!Array.isArray(emittedEvents)) return updates;
|
|
75
|
-
|
|
76
|
-
const toolResults = [];
|
|
77
|
-
|
|
78
|
-
for (const ev of emittedEvents) {
|
|
79
|
-
if (!ev || typeof ev !== 'object') continue;
|
|
80
|
-
const events = Array.isArray(ev) ? ev : [ev];
|
|
81
|
-
|
|
82
|
-
for (const e of events) {
|
|
83
|
-
switch (e.type) {
|
|
84
|
-
case 'tool_result':
|
|
85
|
-
toolResults.push({ tool: e.tool, result: e.result });
|
|
86
|
-
updates.lastActionResult = e;
|
|
87
|
-
if (isTaskProtocolToolName(e.tool)) {
|
|
88
|
-
applyTaskOperationResultToContext(updates, mapTaskProtocolToolToOperation(e.tool), e.result);
|
|
89
|
-
}
|
|
90
|
-
break;
|
|
91
|
-
|
|
92
|
-
case 'tool_error':
|
|
93
|
-
toolResults.push({ tool: e.tool, error: e.error });
|
|
94
|
-
updates.lastActionResult = e;
|
|
95
|
-
updates.lastError = e.error;
|
|
96
|
-
break;
|
|
97
|
-
|
|
98
|
-
case 'message_sent':
|
|
99
|
-
updates.messageSent = true;
|
|
100
|
-
updates.lastActionResult = e;
|
|
101
|
-
break;
|
|
102
|
-
|
|
103
|
-
case 'message_duplicate_skipped':
|
|
104
|
-
updates.messageSent = true;
|
|
105
|
-
break;
|
|
106
|
-
|
|
107
|
-
case 'waited':
|
|
108
|
-
updates.activeWait = true;
|
|
109
|
-
break;
|
|
110
|
-
|
|
111
|
-
case 'task_result': {
|
|
112
|
-
const result = e.result || {};
|
|
113
|
-
const operation = e.operation;
|
|
114
|
-
updates.lastActionResult = e;
|
|
115
|
-
applyTaskOperationResultToContext(updates, operation, result);
|
|
116
|
-
break;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
case 'task_error':
|
|
120
|
-
updates.lastError = e.error;
|
|
121
|
-
updates.lastActionResult = e;
|
|
122
|
-
break;
|
|
123
|
-
|
|
124
|
-
default:
|
|
125
|
-
break;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (toolResults.length > 0) updates.toolResults = toolResults;
|
|
131
|
-
|
|
132
|
-
return updates;
|
|
133
|
-
}
|
package/src/context/batch.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unwrap batched events and task-result relevance for routing / workflow.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export function getBatchEvents(rawEvent) {
|
|
6
|
-
if (!rawEvent) return [];
|
|
7
|
-
if (rawEvent.type === 'batch' && Array.isArray(rawEvent.events)) {
|
|
8
|
-
return rawEvent.events;
|
|
9
|
-
}
|
|
10
|
-
return [rawEvent];
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Whether a task_result payload is relevant to the current agent (routing / workflow).
|
|
15
|
-
*/
|
|
16
|
-
export function isTaskResultRelevantToAgent(result, ownAgentId) {
|
|
17
|
-
if (!ownAgentId) return true;
|
|
18
|
-
return (
|
|
19
|
-
result.executorIdIsYou || result.agentIdIsYou || result.proposedToIsYou
|
|
20
|
-
|| result.payerIdIsYou || result.createdByIsYou
|
|
21
|
-
|| result.executorId === ownAgentId || result.agentId === ownAgentId
|
|
22
|
-
|| result.payerId === ownAgentId || result.proposedTo === ownAgentId
|
|
23
|
-
|| result.createdBy === ownAgentId || result.proposedTo === 'everyone'
|
|
24
|
-
);
|
|
25
|
-
}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Classifies incoming events (routing states) into flags merged into machine context.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { CONTEXT_RESET } from './applyEffects.js';
|
|
6
|
-
import { getBatchEvents, isTaskResultRelevantToAgent } from './batch.js';
|
|
7
|
-
|
|
8
|
-
function isTaskAssignment(result, ownAgentId) {
|
|
9
|
-
return (
|
|
10
|
-
(result.executorIdIsYou || result.executorId === ownAgentId)
|
|
11
|
-
&& (result.state === 'active' || result.state === 'in-progress')
|
|
12
|
-
);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function classifyIncomingEvent(rawEvent, ownAgentId) {
|
|
16
|
-
const flags = { ...CONTEXT_RESET };
|
|
17
|
-
|
|
18
|
-
if (!rawEvent) return flags;
|
|
19
|
-
|
|
20
|
-
const events = getBatchEvents(rawEvent);
|
|
21
|
-
|
|
22
|
-
for (const ev of events) {
|
|
23
|
-
if (!ev) continue;
|
|
24
|
-
|
|
25
|
-
if (ev.type === 'task_result') {
|
|
26
|
-
const result = ev.result || {};
|
|
27
|
-
if (
|
|
28
|
-
ownAgentId &&
|
|
29
|
-
!isTaskResultRelevantToAgent(result, ownAgentId) &&
|
|
30
|
-
ev.receiverId !== ownAgentId
|
|
31
|
-
) {
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const state = result.state;
|
|
36
|
-
const proposalStatus = result.proposal?.status;
|
|
37
|
-
|
|
38
|
-
// User approved your orchestration proposal: you remain agentId + executorId on the task.
|
|
39
|
-
// Keep this before isTaskAssignment so we still classify if IsYou flags were computed for another viewer.
|
|
40
|
-
if (
|
|
41
|
-
state === 'active' &&
|
|
42
|
-
proposalStatus === 'approved' &&
|
|
43
|
-
ownAgentId &&
|
|
44
|
-
result.agentId === ownAgentId
|
|
45
|
-
) {
|
|
46
|
-
flags.approval = true;
|
|
47
|
-
flags.taskAssignment = result;
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (isTaskAssignment(result, ownAgentId)) {
|
|
52
|
-
flags.taskAssignment = result;
|
|
53
|
-
flags.approval = true;
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
if (proposalStatus === 'approved' || (state === 'active' && proposalStatus === 'pending')) {
|
|
57
|
-
flags.approval = true;
|
|
58
|
-
flags.taskAssignment = result;
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
if (proposalStatus === 'rejected' || state === 'cancelled') {
|
|
62
|
-
flags.rejection = true;
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
if (state === 'completed') {
|
|
66
|
-
flags.subtaskResult = result;
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
if (state === 'failed') {
|
|
70
|
-
flags.subtaskResult = result;
|
|
71
|
-
flags.subtaskFailed = true;
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
flags.subtaskResult = result;
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
flags.incomingMessage = true;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return flags;
|
|
82
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Higher-level workflow labels derived from batched incoming events.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { isTaskProtocolToolName } from '../tasks/taskProtocolRegistry.js';
|
|
6
|
-
import { getBatchEvents, isTaskResultRelevantToAgent } from './batch.js';
|
|
7
|
-
|
|
8
|
-
export function unwrapBatchEvent(event) {
|
|
9
|
-
if (event?.type === 'batch' && event.events?.[0]) return event.events[0];
|
|
10
|
-
return event;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function classifyWorkflowEvent(event, ownAgentId = null) {
|
|
14
|
-
const events = getBatchEvents(event);
|
|
15
|
-
if (events.length === 0) return 'message';
|
|
16
|
-
|
|
17
|
-
let sawTaskResult = false;
|
|
18
|
-
let sawOwnTaskResult = false;
|
|
19
|
-
|
|
20
|
-
for (const ev of events) {
|
|
21
|
-
if (ev?.type !== 'task_result') continue;
|
|
22
|
-
sawTaskResult = true;
|
|
23
|
-
|
|
24
|
-
const result = ev.result || {};
|
|
25
|
-
if (!isTaskResultRelevantToAgent(result, ownAgentId)) continue;
|
|
26
|
-
sawOwnTaskResult = true;
|
|
27
|
-
|
|
28
|
-
const state = result.state;
|
|
29
|
-
const proposalStatus = result.proposal?.status;
|
|
30
|
-
|
|
31
|
-
if (
|
|
32
|
-
(result.executorIdIsYou || (!result.executorIdIsYou && result.executorId === ownAgentId))
|
|
33
|
-
&& (state === 'active' || state === 'in-progress')
|
|
34
|
-
) {
|
|
35
|
-
return 'task_assignment';
|
|
36
|
-
}
|
|
37
|
-
if (proposalStatus === 'approved' || (state === 'active' && proposalStatus === 'pending')) return 'task_approved';
|
|
38
|
-
if (proposalStatus === 'rejected' || state === 'cancelled') return 'task_rejected';
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (sawOwnTaskResult || sawTaskResult) return 'task_update';
|
|
42
|
-
return 'message';
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function findTaskResult(output) {
|
|
46
|
-
return output?.emittedEvents?.find(e =>
|
|
47
|
-
e.type === 'task_result' || (e.type === 'tool_result' && isTaskProtocolToolName(e.tool))
|
|
48
|
-
) ?? null;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function findIncomingTaskResult(incomingEvent) {
|
|
52
|
-
const events = getBatchEvents(incomingEvent);
|
|
53
|
-
return events.find(e => e?.type === 'task_result') ?? null;
|
|
54
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* createHealthServer — minimal HTTP server for platform health checks.
|
|
3
|
-
*
|
|
4
|
-
* Previously this file also served a wake webhook; wake events now travel over
|
|
5
|
-
* the launcher's control WebSocket (see ConnectionPool.startControl), so this
|
|
6
|
-
* server only answers 200 OK for anything. Keep it for Cloud Run-style
|
|
7
|
-
* platforms that require a listening HTTP port on the launcher container.
|
|
8
|
-
*
|
|
9
|
-
* Usage:
|
|
10
|
-
* createHealthServer({ label: 'launcher' });
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { createServer } from 'http';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @param {object} [opts]
|
|
17
|
-
* @param {number} [opts.port] - defaults to process.env.PORT || 8080
|
|
18
|
-
* @param {string} [opts.label] - log prefix
|
|
19
|
-
* @returns {import('http').Server}
|
|
20
|
-
*/
|
|
21
|
-
export function createHealthServer({ port = process.env.PORT || 8080, label = 'agents' } = {}) {
|
|
22
|
-
const server = createServer((_req, res) => {
|
|
23
|
-
res.writeHead(200);
|
|
24
|
-
res.end('ok');
|
|
25
|
-
});
|
|
26
|
-
server.listen(port, () => console.log(`[${label}] health server on port ${port}`));
|
|
27
|
-
return server;
|
|
28
|
-
}
|
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HistoryFormatter - Pluggable formatter for converting raw MongoDB history 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 HistoryFormatter {
|
|
8
|
-
constructor(options = {}) {
|
|
9
|
-
this.options = options;
|
|
10
|
-
this.showTimestamps = options.showTimestamps ?? true;
|
|
11
|
-
this.shortIds = options.shortIds ?? false; // Always show full IDs by default
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Format an array of history entries into readable text
|
|
16
|
-
* @param {Array} history - Array of history entries from context
|
|
17
|
-
* @param {string} agentId - The current agent's ID (for "You" labels)
|
|
18
|
-
* @param {Object} options - Optional: { mode } - when mode is 'mission', prefixes each line with [CHAT], [TOOL], or [TASK]
|
|
19
|
-
* @returns {string} Formatted history text
|
|
20
|
-
*/
|
|
21
|
-
format(history, agentId, options = {}) {
|
|
22
|
-
if (!history?.length) return 'No previous activity.';
|
|
23
|
-
|
|
24
|
-
const labelTypes = options.mode === 'mission';
|
|
25
|
-
const sorted = [...history].sort((a, b) => {
|
|
26
|
-
const tA = a.timestamp ? new Date(a.timestamp).getTime() : 0;
|
|
27
|
-
const tB = b.timestamp ? new Date(b.timestamp).getTime() : 0;
|
|
28
|
-
return tA - tB;
|
|
29
|
-
});
|
|
30
|
-
return sorted.map(entry => this.formatEntry(entry, agentId, labelTypes)).join('\n');
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Format a single history entry based on its type
|
|
35
|
-
* @param {boolean} labelTypes - When true, prefix with [CHAT], [TOOL], or [TASK]
|
|
36
|
-
*/
|
|
37
|
-
formatEntry(entry, agentId, labelTypes = false) {
|
|
38
|
-
const timestamp = this.showTimestamps ? this.formatTime(entry.timestamp) : '';
|
|
39
|
-
const timePrefix = timestamp ? `[${timestamp}] ` : '';
|
|
40
|
-
let typeLabel = '';
|
|
41
|
-
|
|
42
|
-
// Message entries
|
|
43
|
-
if (entry.entryType === 'message' || !entry.entryType) {
|
|
44
|
-
const line = this.formatMessage(entry, agentId, timePrefix);
|
|
45
|
-
typeLabel = labelTypes ? '[CHAT] ' : '';
|
|
46
|
-
return `${typeLabel}${line}`;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Task history entries
|
|
50
|
-
if (entry.entryType === 'task_history') {
|
|
51
|
-
const line = this.formatTaskHistory(entry, agentId, timePrefix);
|
|
52
|
-
typeLabel = labelTypes ? '[TASK] ' : '';
|
|
53
|
-
return `${typeLabel}${line}`;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Artifact entries (operation tracking, or agent thought)
|
|
57
|
-
if (entry.entryType === 'artifact') {
|
|
58
|
-
const line = this.formatArtifact(entry, agentId, timePrefix);
|
|
59
|
-
if (labelTypes) {
|
|
60
|
-
if (entry.content_type === 'thought') {
|
|
61
|
-
typeLabel = '[THOUGHT] ';
|
|
62
|
-
} else {
|
|
63
|
-
const text = entry.text || '';
|
|
64
|
-
const json = text.replace(/^operation_(?:started|completed|error):/, '');
|
|
65
|
-
const data = json ? this.safeParseJSON(json) : null;
|
|
66
|
-
typeLabel = (data?.type === 'tool' || text.includes('operation_error')) ? '[TOOL] ' : '[TASK] ';
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return `${typeLabel}${line}`;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return `${timePrefix}${entry.text || 'Unknown entry'}`;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Format a message entry
|
|
77
|
-
*/
|
|
78
|
-
formatMessage(entry, agentId, timePrefix) {
|
|
79
|
-
const from = this.formatParticipant(entry.sender, agentId);
|
|
80
|
-
const to = entry.receiver ? ` → ${this.formatParticipant(entry.receiver, agentId)}` : '';
|
|
81
|
-
const text = entry.text || '';
|
|
82
|
-
|
|
83
|
-
return `${timePrefix}${from}${to}: ${text}`;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Format a task history entry (created, state_changed, proposal_responded, etc.)
|
|
88
|
-
*/
|
|
89
|
-
formatTaskHistory(entry, agentId, timePrefix) {
|
|
90
|
-
const task = entry.service?.task;
|
|
91
|
-
if (!task) return `${timePrefix}Task update`;
|
|
92
|
-
|
|
93
|
-
const changeType = entry.service?.changeType;
|
|
94
|
-
const taskId = this.shortIds ? this.shortId(task.taskId) : task.taskId;
|
|
95
|
-
const desc = task.description || 'No description';
|
|
96
|
-
|
|
97
|
-
switch (changeType) {
|
|
98
|
-
case 'created':
|
|
99
|
-
return `${timePrefix}Task created: "${desc}" (${taskId})
|
|
100
|
-
Owner: ${this.formatOwner(task, agentId)}
|
|
101
|
-
Executor: ${this.formatExecutor(task, agentId)}
|
|
102
|
-
Proposed to: ${task.proposal?.proposedTo || 'N/A'}`;
|
|
103
|
-
|
|
104
|
-
case 'state_changed': {
|
|
105
|
-
const oldState = entry.service?.previousState?.state || '?';
|
|
106
|
-
const newState = entry.service?.newState?.state || '?';
|
|
107
|
-
return `${timePrefix}Task "${desc}" (${taskId}): ${oldState} → ${newState}`;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
case 'proposal_responded': {
|
|
111
|
-
const action = entry.service?.metadata?.action;
|
|
112
|
-
return `${timePrefix}Proposal ${action}: "${desc}" (${taskId})`;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
case 'processing_changed': {
|
|
116
|
-
const processing = entry.service?.newState?.processing;
|
|
117
|
-
return `${timePrefix}Task "${desc}" (${taskId}): processing ${processing ? 'started' : 'stopped'}`;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
default:
|
|
121
|
-
return `${timePrefix}Task ${changeType}: "${desc}" (${taskId})`;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Format an artifact entry (operation tracking, or agent thought)
|
|
127
|
-
*/
|
|
128
|
-
formatArtifact(entry, agentId, timePrefix) {
|
|
129
|
-
const text = entry.text || '';
|
|
130
|
-
|
|
131
|
-
// Agent thought (chain-of-thought reasoning)
|
|
132
|
-
if (entry.content_type === 'thought') {
|
|
133
|
-
return `${timePrefix}Thought: ${text}`;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Parse and simplify operation_started messages
|
|
137
|
-
if (text.startsWith('operation_started:')) {
|
|
138
|
-
const data = this.safeParseJSON(text.replace('operation_started:', ''));
|
|
139
|
-
if (data?.type === 'tool') {
|
|
140
|
-
const argsStr = this.formatArgs(data.args);
|
|
141
|
-
return `${timePrefix}Tool starting: ${data.tool}(${argsStr})`;
|
|
142
|
-
}
|
|
143
|
-
if (data?.type === 'task') {
|
|
144
|
-
return `${timePrefix}Task operation: ${data.operation}`;
|
|
145
|
-
}
|
|
146
|
-
return `${timePrefix}Operation started: ${data?.type || 'unknown'}`;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Parse and simplify operation_completed messages
|
|
150
|
-
if (text.startsWith('operation_completed:')) {
|
|
151
|
-
const data = this.safeParseJSON(text.replace('operation_completed:', ''));
|
|
152
|
-
if (data?.type === 'tool') {
|
|
153
|
-
return `${timePrefix}Tool completed: ${data.tool}`;
|
|
154
|
-
}
|
|
155
|
-
if (data?.type === 'task') {
|
|
156
|
-
return `${timePrefix}Task operation completed: ${data.operation}`;
|
|
157
|
-
}
|
|
158
|
-
return `${timePrefix}Operation completed: ${data?.type || 'unknown'}`;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Parse and simplify operation_error messages
|
|
162
|
-
if (text.startsWith('operation_error:')) {
|
|
163
|
-
const data = this.safeParseJSON(text.replace('operation_error:', ''));
|
|
164
|
-
return `${timePrefix}ERROR: ${data?.error || 'Unknown error'}`;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Default: return text as-is
|
|
168
|
-
return `${timePrefix}${text}`;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Format a participant (sender/receiver) with "You" label if applicable
|
|
173
|
-
*/
|
|
174
|
-
formatParticipant(participant, agentId) {
|
|
175
|
-
if (!participant) return 'unknown';
|
|
176
|
-
const id = participant.id || 'unknown';
|
|
177
|
-
const type = participant.type || 'unknown';
|
|
178
|
-
const isYou = id === agentId;
|
|
179
|
-
|
|
180
|
-
if (type === 'system') return 'System';
|
|
181
|
-
if (isYou) return 'You';
|
|
182
|
-
return `${id} (${type})`;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Format task owner with "You" label if applicable
|
|
187
|
-
*/
|
|
188
|
-
formatOwner(task, agentId) {
|
|
189
|
-
return task.agentId === agentId ? 'You' : task.agentId;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Format task executor with "You" label if applicable
|
|
194
|
-
*/
|
|
195
|
-
formatExecutor(task, agentId) {
|
|
196
|
-
return task.executorId === agentId ? 'You' : task.executorId;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Return full task ID (or shortened if shortIds option is enabled)
|
|
201
|
-
*/
|
|
202
|
-
shortId(taskId) {
|
|
203
|
-
if (!taskId) return '?';
|
|
204
|
-
|
|
205
|
-
// If shortIds is disabled, always return full ID
|
|
206
|
-
if (!this.shortIds) return taskId;
|
|
207
|
-
|
|
208
|
-
// Otherwise, shorten it
|
|
209
|
-
const parts = taskId.split('_');
|
|
210
|
-
if (parts.length >= 3) {
|
|
211
|
-
return `task_...${parts[parts.length - 1]}`;
|
|
212
|
-
}
|
|
213
|
-
return taskId.length > 20 ? taskId.slice(0, 20) + '...' : taskId;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Format timestamp as readable time
|
|
218
|
-
*/
|
|
219
|
-
formatTime(timestamp) {
|
|
220
|
-
if (!timestamp) return '??:??';
|
|
221
|
-
const date = new Date(timestamp);
|
|
222
|
-
if (isNaN(date.getTime())) return '??:??';
|
|
223
|
-
return date.toLocaleTimeString('en-US', {
|
|
224
|
-
hour: '2-digit',
|
|
225
|
-
minute: '2-digit',
|
|
226
|
-
second: '2-digit',
|
|
227
|
-
hour12: false
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Format args object for display
|
|
233
|
-
*/
|
|
234
|
-
formatArgs(args) {
|
|
235
|
-
if (!args || typeof args !== 'object') return '';
|
|
236
|
-
const entries = Object.entries(args);
|
|
237
|
-
if (entries.length === 0) return '';
|
|
238
|
-
if (entries.length <= 3) {
|
|
239
|
-
return entries.map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(', ');
|
|
240
|
-
}
|
|
241
|
-
return JSON.stringify(args);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Safely parse JSON, returning null on failure
|
|
246
|
-
*/
|
|
247
|
-
safeParseJSON(str) {
|
|
248
|
-
try {
|
|
249
|
-
return JSON.parse(str);
|
|
250
|
-
} catch {
|
|
251
|
-
return null;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Default instance for easy import
|
|
257
|
-
export const historyFormatter = new HistoryFormatter();
|