@ziggs-ai/agent-sdk 0.1.11 → 0.2.0
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/CHANGELOG.md +14 -0
- package/README.md +41 -2
- package/dist/AgentHost.d.ts +201 -0
- package/dist/AgentHost.js +652 -0
- package/dist/adapters/AnthropicAdapter.d.ts +39 -0
- package/dist/adapters/AnthropicAdapter.js +200 -0
- package/dist/adapters/McpAdapter.d.ts +199 -0
- package/dist/adapters/McpAdapter.js +468 -0
- package/dist/adapters/OpenAIAdapter.d.ts +26 -0
- package/dist/adapters/OpenAIAdapter.js +158 -0
- package/dist/adapters/index.js +2 -0
- package/dist/agent/Agent.d.ts +78 -0
- package/dist/agent/Agent.js +58 -0
- package/dist/agent/ClaudeSdkAgent.d.ts +38 -0
- package/dist/agent/ClaudeSdkAgent.js +322 -0
- package/dist/agent/claudeCognition.d.ts +67 -0
- package/dist/agent/claudeCognition.js +103 -0
- package/dist/agent/claudeSdkProtocol.d.ts +11 -0
- package/dist/agent/claudeSdkProtocol.js +27 -0
- package/dist/agent/claudeSdkTools.d.ts +19 -0
- package/dist/agent/claudeSdkTools.js +57 -0
- package/dist/agent/hostedAgentConnection.d.ts +15 -0
- package/dist/agent/hostedAgentConnection.js +29 -0
- package/dist/agent/toolManagerToSdkMcp.d.ts +12 -0
- package/dist/agent/toolManagerToSdkMcp.js +123 -0
- package/dist/brain/index.d.ts +50 -0
- package/dist/brain/index.js +45 -0
- package/dist/cognition/validateContext.d.ts +11 -0
- package/dist/cognition/validateContext.js +117 -0
- package/dist/context/applyEffects.d.ts +2 -0
- package/dist/context/applyEffects.js +89 -0
- package/dist/context/batch.d.ts +2 -0
- package/dist/context/batch.js +18 -0
- package/dist/context/classifyEnvelope.d.ts +1 -0
- package/dist/context/classifyEnvelope.js +58 -0
- package/dist/context/routingLabels.d.ts +4 -0
- package/dist/context/routingLabels.js +48 -0
- package/dist/defineAgent.d.ts +14 -0
- package/dist/defineAgent.js +68 -0
- package/dist/formatters/AgreementFormatter.d.ts +18 -0
- package/dist/formatters/AgreementFormatter.js +130 -0
- package/dist/formatters/HistoryFormatter.d.ts +26 -0
- package/dist/formatters/HistoryFormatter.js +173 -0
- package/dist/formatters/index.js +2 -0
- package/{src/index.ts → dist/index.d.ts} +13 -53
- package/dist/index.js +66 -0
- package/dist/ingress/normalizeIncoming.d.ts +65 -0
- package/dist/ingress/normalizeIncoming.js +88 -0
- package/dist/memory/MemoryStore.d.ts +55 -0
- package/dist/memory/MemoryStore.js +78 -0
- package/dist/pricing/fleetDefaults.d.ts +46 -0
- package/dist/pricing/fleetDefaults.js +168 -0
- package/dist/pricing/fleetEvalFree.d.ts +7 -0
- package/dist/pricing/fleetEvalFree.js +24 -0
- package/dist/pricing/fleetFreeTierA.gen.d.ts +6 -0
- package/{src/pricing/fleetFreeTierA.gen.ts → dist/pricing/fleetFreeTierA.gen.js} +5 -6
- package/dist/pricing/fleetTierByAgentId.gen.d.ts +7 -0
- package/dist/pricing/fleetTierByAgentId.gen.js +768 -0
- package/dist/runtime/AgentMachine.d.ts +143 -0
- package/dist/runtime/AgentMachine.js +400 -0
- package/dist/runtime/PromptBuilder.d.ts +96 -0
- package/dist/runtime/PromptBuilder.js +439 -0
- package/dist/runtime/bootstrapCtx.d.ts +18 -0
- package/dist/runtime/bootstrapCtx.js +37 -0
- package/dist/runtime/buildOutcome.d.ts +68 -0
- package/dist/runtime/buildOutcome.js +464 -0
- package/dist/runtime/defaults.d.ts +54 -0
- package/dist/runtime/defaults.js +47 -0
- package/dist/runtime/deriveRespondsToProposals.d.ts +9 -0
- package/dist/runtime/deriveRespondsToProposals.js +38 -0
- package/dist/runtime/kickoff.d.ts +15 -0
- package/dist/runtime/kickoff.js +112 -0
- package/dist/runtime/runMechanical.d.ts +25 -0
- package/dist/runtime/runMechanical.js +74 -0
- package/dist/runtime/runTurn.d.ts +63 -0
- package/dist/runtime/runTurn.js +800 -0
- package/dist/runtime/validateWorkflow.d.ts +17 -0
- package/dist/runtime/validateWorkflow.js +209 -0
- package/dist/server/ConnectionPool.d.ts +68 -0
- package/dist/server/ConnectionPool.js +166 -0
- package/dist/server/EventQueue.d.ts +33 -0
- package/dist/server/EventQueue.js +98 -0
- package/dist/server/InboxCatchUp.d.ts +24 -0
- package/dist/server/InboxCatchUp.js +213 -0
- package/dist/server/OutboxBuffer.d.ts +33 -0
- package/dist/server/OutboxBuffer.js +68 -0
- package/dist/server/SeenMessages.d.ts +13 -0
- package/dist/server/SeenMessages.js +31 -0
- package/dist/server/StateTimers.d.ts +34 -0
- package/dist/server/StateTimers.js +70 -0
- package/dist/server/ZiggsEffectHandler.d.ts +79 -0
- package/dist/server/ZiggsEffectHandler.js +319 -0
- package/dist/server/agreements/AgreementService.d.ts +50 -0
- package/dist/server/agreements/AgreementService.js +85 -0
- package/dist/server/createHealthServer.d.ts +7 -0
- package/dist/server/createHealthServer.js +75 -0
- package/dist/server/proactive/ProactiveTrigger.d.ts +33 -0
- package/dist/server/proactive/ProactiveTrigger.js +71 -0
- package/dist/server/runLauncher.d.ts +28 -0
- package/dist/server/runLauncher.js +93 -0
- package/dist/server/tasks/TaskService.d.ts +25 -0
- package/dist/server/tasks/TaskService.js +103 -0
- package/dist/server/tasks/index.js +1 -0
- package/dist/server/telemetryIngest.d.ts +24 -0
- package/dist/server/telemetryIngest.js +62 -0
- package/dist/server/tools/index.d.ts +24 -0
- package/{src/server/tools/index.ts → dist/server/tools/index.js} +4 -24
- package/dist/server/tools/tier1/protocolRunner.d.ts +39 -0
- package/dist/server/tools/tier1/protocolRunner.js +288 -0
- package/dist/server/tools/tier1/protocolTools.d.ts +12 -0
- package/dist/server/tools/tier1/protocolTools.js +37 -0
- package/dist/server/tools/tier2/connectionTools.d.ts +4 -0
- package/dist/server/tools/tier2/connectionTools.js +63 -0
- package/dist/server/tools/tier2/contextTools.d.ts +4 -0
- package/dist/server/tools/tier2/contextTools.js +46 -0
- package/dist/server/tools/tier2/discoveryTools.d.ts +4 -0
- package/dist/server/tools/tier2/discoveryTools.js +25 -0
- package/dist/server/tools/tier2/marketplaceTools.d.ts +3 -0
- package/dist/server/tools/tier2/marketplaceTools.js +22 -0
- package/dist/server/tools/tier2/paymentTools.d.ts +12 -0
- package/dist/server/tools/tier2/paymentTools.js +165 -0
- package/dist/server/ziggsconnect/ZiggsConnectClient.d.ts +35 -0
- package/dist/server/ziggsconnect/ZiggsConnectClient.js +95 -0
- package/dist/server/ziggscontext/ZiggsContextClient.d.ts +48 -0
- package/dist/server/ziggscontext/ZiggsContextClient.js +90 -0
- package/dist/server/ziggspay/ZiggsPayClient.d.ts +82 -0
- package/dist/server/ziggspay/ZiggsPayClient.js +188 -0
- package/dist/shared/ids.d.ts +1 -0
- package/dist/shared/ids.js +3 -0
- package/dist/shared/runtimeLog.d.ts +18 -0
- package/dist/shared/runtimeLog.js +68 -0
- package/dist/shared/types.d.ts +22 -0
- package/dist/shared/types.js +23 -0
- package/dist/tasks/protocolRegistry.d.ts +16 -0
- package/dist/tasks/protocolRegistry.js +20 -0
- package/dist/tools/ToolManager.d.ts +12 -0
- package/dist/tools/ToolManager.js +74 -0
- package/dist/tools/ToolProvider.d.ts +6 -0
- package/dist/tools/ToolProvider.js +21 -0
- package/dist/tools/defineTool.d.ts +27 -0
- package/dist/tools/defineTool.js +62 -0
- package/{src/tools/index.ts → dist/tools/index.d.ts} +0 -1
- package/dist/tools/index.js +6 -0
- package/dist/tools/recordReport.d.ts +41 -0
- package/dist/tools/recordReport.js +30 -0
- package/dist/types.d.ts +466 -0
- package/dist/types.js +7 -0
- package/dist/utils/jsonExtractor.d.ts +2 -0
- package/dist/utils/jsonExtractor.js +111 -0
- package/package.json +17 -7
- package/src/AgentHost.ts +0 -545
- package/src/adapters/AnthropicAdapter.ts +0 -206
- package/src/adapters/McpAdapter.ts +0 -348
- package/src/adapters/OpenAIAdapter.ts +0 -187
- package/src/agent/Agent.ts +0 -104
- package/src/cognition/validateContext.ts +0 -95
- package/src/context/applyEffects.ts +0 -80
- package/src/context/batch.ts +0 -19
- package/src/context/classifyEnvelope.ts +0 -38
- package/src/context/routingLabels.ts +0 -46
- package/src/defineAgent.ts +0 -70
- package/src/formatters/AgreementFormatter.ts +0 -111
- package/src/formatters/HistoryFormatter.ts +0 -172
- package/src/ingress/normalizeIncoming.ts +0 -162
- package/src/memory/MemoryStore.ts +0 -104
- package/src/pricing/fleetDefaults.ts +0 -225
- package/src/pricing/fleetEvalFree.ts +0 -24
- package/src/pricing/fleetTierByAgentId.gen.ts +0 -775
- package/src/runtime/AgentMachine.ts +0 -402
- package/src/runtime/PromptBuilder.ts +0 -485
- package/src/runtime/buildOutcome.ts +0 -563
- package/src/runtime/defaults.ts +0 -75
- package/src/runtime/kickoff.ts +0 -117
- package/src/runtime/runTurn.ts +0 -813
- package/src/runtime/validateWorkflow.ts +0 -202
- package/src/server/ConnectionPool.ts +0 -206
- package/src/server/EventQueue.ts +0 -133
- package/src/server/InboxCatchUp.ts +0 -254
- package/src/server/OutboxBuffer.ts +0 -90
- package/src/server/SeenMessages.ts +0 -27
- package/src/server/StateTimers.ts +0 -85
- package/src/server/ZiggsEffectHandler.ts +0 -418
- package/src/server/agreements/AgreementService.ts +0 -118
- package/src/server/createHealthServer.ts +0 -85
- package/src/server/proactive/ProactiveTrigger.ts +0 -83
- package/src/server/runLauncher.ts +0 -146
- package/src/server/tasks/TaskService.ts +0 -110
- package/src/server/telemetryIngest.ts +0 -91
- package/src/server/tools/tier1/protocolRunner.ts +0 -144
- package/src/server/tools/tier1/protocolTools.ts +0 -99
- package/src/server/tools/tier2/connectionTools.ts +0 -75
- package/src/server/tools/tier2/contextTools.ts +0 -63
- package/src/server/tools/tier2/discoveryTools.ts +0 -34
- package/src/server/tools/tier2/marketplaceTools.ts +0 -27
- package/src/server/tools/tier2/paymentTools.ts +0 -170
- package/src/server/ziggsconnect/ZiggsConnectClient.ts +0 -126
- package/src/server/ziggscontext/ZiggsContextClient.ts +0 -129
- package/src/server/ziggspay/ZiggsPayClient.ts +0 -193
- package/src/shared/ids.ts +0 -3
- package/src/shared/runtimeLog.ts +0 -72
- package/src/shared/types.ts +0 -29
- package/src/tasks/protocolRegistry.ts +0 -25
- package/src/tools/ToolManager.ts +0 -95
- package/src/tools/ToolProvider.ts +0 -19
- package/src/tools/defineTool.ts +0 -90
- package/src/tools/recordReport.ts +0 -82
- package/src/types.ts +0 -456
- package/src/utils/jsonExtractor.ts +0 -100
- /package/{src/adapters/index.ts → dist/adapters/index.d.ts} +0 -0
- /package/{src/formatters/index.ts → dist/formatters/index.d.ts} +0 -0
- /package/{src/server/tasks/index.ts → dist/server/tasks/index.d.ts} +0 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { ContextReadClient, ContextDiscoveryClient, InboxClient, getChatsForAgreement, getAgreementStatus, } from '@ziggs-ai/api-client';
|
|
2
|
+
import { runtimeLog } from '../shared/runtimeLog.js';
|
|
3
|
+
const READ_PAGE = 100;
|
|
4
|
+
function log(label, msg) {
|
|
5
|
+
runtimeLog.info(label, msg);
|
|
6
|
+
}
|
|
7
|
+
function warn(label, msg) {
|
|
8
|
+
runtimeLog.warn(label, msg);
|
|
9
|
+
}
|
|
10
|
+
const TERMINAL_AGREEMENT_STATUSES = new Set(['fulfilled', 'cancelled', 'refunded']);
|
|
11
|
+
/** ZIG-832 P4: is this agreement finished (so its backlog must not be re-run)? */
|
|
12
|
+
async function isTerminalAgreement(agreementId, creds) {
|
|
13
|
+
try {
|
|
14
|
+
const res = (await getAgreementStatus(agreementId, creds));
|
|
15
|
+
const status = res?.status ?? res?.agreement?.status;
|
|
16
|
+
return status != null && TERMINAL_AGREEMENT_STATUSES.has(status);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// On lookup failure, don't skip — fall through to normal delivery.
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** Map Mongo message row → wire metadata for normalizeIncomingEvent. */
|
|
24
|
+
function rowToMetadata(row, chatId) {
|
|
25
|
+
const senderRaw = row.sender;
|
|
26
|
+
const senderId = String(senderRaw?.agentId ?? senderRaw?.id ?? '');
|
|
27
|
+
const receiver = row.receiver;
|
|
28
|
+
const senderType = senderRaw?.type ??
|
|
29
|
+
(receiver?.type === 'agent' ? 'agent' : 'user');
|
|
30
|
+
return {
|
|
31
|
+
chatId,
|
|
32
|
+
chat_id: chatId,
|
|
33
|
+
messageId: row.messageId,
|
|
34
|
+
sender: { id: senderId, type: senderType },
|
|
35
|
+
senderId,
|
|
36
|
+
senderType: String(senderType).toUpperCase(),
|
|
37
|
+
receiver: receiver ?? null,
|
|
38
|
+
receiverId: receiver?.id ?? null,
|
|
39
|
+
entryType: row.entryType ?? 'message',
|
|
40
|
+
content_type: row.content_type ?? row.contentType ?? 'text',
|
|
41
|
+
contentType: row.content_type ?? row.contentType ?? 'text',
|
|
42
|
+
task: row.task ?? null,
|
|
43
|
+
agreement: row.agreement ?? null,
|
|
44
|
+
operation: row.operation ?? null,
|
|
45
|
+
agreementId: row.agreementId ?? null,
|
|
46
|
+
timestamp: row.timestamp,
|
|
47
|
+
text: row.text,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async function chatIdsForScope(scope, creds, baseUrl, discovery) {
|
|
51
|
+
const { kind, id } = scope.scope;
|
|
52
|
+
switch (kind) {
|
|
53
|
+
case 'chat':
|
|
54
|
+
return [id];
|
|
55
|
+
case 'agreement': {
|
|
56
|
+
const links = (await getChatsForAgreement(id, creds));
|
|
57
|
+
return [...new Set(links.map((l) => l.chatId).filter(Boolean))];
|
|
58
|
+
}
|
|
59
|
+
case 'org': {
|
|
60
|
+
const reach = await discovery.discover();
|
|
61
|
+
return [
|
|
62
|
+
...new Set(reach
|
|
63
|
+
.filter((r) => r.scope.kind === 'chat')
|
|
64
|
+
.map((r) => r.scope.id)),
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
default:
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async function deliverMessagesForScope(deps, scope, chatIds, reader, countCap) {
|
|
72
|
+
let delivered = 0;
|
|
73
|
+
const label = deps.label ?? 'InboxCatchUp';
|
|
74
|
+
const expected = scope.newMessages;
|
|
75
|
+
let remaining = expected >= countCap ? Number.POSITIVE_INFINITY : expected;
|
|
76
|
+
for (const chatId of chatIds) {
|
|
77
|
+
if (remaining <= 0)
|
|
78
|
+
break;
|
|
79
|
+
let after = scope.since;
|
|
80
|
+
let pages = 0;
|
|
81
|
+
const maxPages = 20;
|
|
82
|
+
while (remaining > 0 && pages < maxPages) {
|
|
83
|
+
const limit = Math.min(READ_PAGE, remaining === Number.POSITIVE_INFINITY ? READ_PAGE : remaining);
|
|
84
|
+
const page = await reader.read('messages', {
|
|
85
|
+
via: `chat:${chatId}`,
|
|
86
|
+
after,
|
|
87
|
+
direction: 'forward',
|
|
88
|
+
limit,
|
|
89
|
+
});
|
|
90
|
+
for (const row of page.items) {
|
|
91
|
+
const messageId = String(row.messageId ?? '');
|
|
92
|
+
if (deps.seen.has(messageId))
|
|
93
|
+
continue;
|
|
94
|
+
const text = typeof row.text === 'string' ? row.text : '';
|
|
95
|
+
if (!text.trim())
|
|
96
|
+
continue;
|
|
97
|
+
const metadata = rowToMetadata(row, chatId);
|
|
98
|
+
await deps.onMessage(text, metadata);
|
|
99
|
+
delivered++;
|
|
100
|
+
if (remaining !== Number.POSITIVE_INFINITY)
|
|
101
|
+
remaining--;
|
|
102
|
+
}
|
|
103
|
+
if (!page.hasMore || !page.latestSequence)
|
|
104
|
+
break;
|
|
105
|
+
after = page.latestSequence;
|
|
106
|
+
pages++;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (delivered < expected && expected >= countCap) {
|
|
110
|
+
warn(label, `scope ${scope.scope.kind}:${scope.scope.id} may be incomplete (count cap)`);
|
|
111
|
+
}
|
|
112
|
+
return delivered;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Pull-based recovery after reconnect (ZIG-434 / ZIG-446 / ZIG-454).
|
|
116
|
+
* Inbox → read message deltas → deliver → ack. Push remains a hint;
|
|
117
|
+
* this path survives backend restarts that drop in-memory pending queues.
|
|
118
|
+
*/
|
|
119
|
+
export async function runInboxCatchUp(deps) {
|
|
120
|
+
const label = deps.label ?? 'InboxCatchUp';
|
|
121
|
+
const inbox = new InboxClient(deps.creds.operatorKey, deps.creds.agentId, deps.baseUrl);
|
|
122
|
+
const reader = new ContextReadClient(deps.creds.operatorKey, deps.creds.agentId, deps.baseUrl);
|
|
123
|
+
const discovery = new ContextDiscoveryClient(deps.creds.operatorKey, deps.creds.agentId, deps.baseUrl);
|
|
124
|
+
const envelope = await inbox.getInbox();
|
|
125
|
+
const countCap = envelope.countCap;
|
|
126
|
+
let messagesDelivered = 0;
|
|
127
|
+
let resourceEvents = 0;
|
|
128
|
+
const toAck = [];
|
|
129
|
+
if (envelope.truncatedScopes > 0) {
|
|
130
|
+
// Informational only — does NOT block acking the scopes actually
|
|
131
|
+
// returned below. truncatedScopes just means the caller's total reach
|
|
132
|
+
// exceeded the server's per-call scope cap (backend/src/inbox/inbox.service.ts,
|
|
133
|
+
// INBOX_MAX_SCOPES); each entry in envelope.scopes is still an
|
|
134
|
+
// independently accurate, safe-to-ack unit of news for that scope. Never
|
|
135
|
+
// acking here meant a busy long-lived agent (reach > cap) would never ack
|
|
136
|
+
// anything and re-deliver its entire backlog on every reconnect forever.
|
|
137
|
+
warn(label, `inbox truncatedScopes=${envelope.truncatedScopes} (reach exceeds per-call cap — examined scopes are still acked normally)`);
|
|
138
|
+
}
|
|
139
|
+
for (const scope of envelope.scopes) {
|
|
140
|
+
// ZIG-832 P4: a re-host runs catch-up from the server watermark and would
|
|
141
|
+
// re-deliver an already-finished agreement's backlog, re-running a completed
|
|
142
|
+
// pipeline and burning tokens. If this is an agreement scope that has reached
|
|
143
|
+
// a terminal status, ack it (so it drops out of future catch-ups) and skip
|
|
144
|
+
// re-delivery. Only agreement scopes — chat scopes may carry genuinely new
|
|
145
|
+
// conversation, so those are never skipped on status. (The deeper fix, acking
|
|
146
|
+
// the watermark on the live-push path so nothing re-delivers, is tracked
|
|
147
|
+
// separately.)
|
|
148
|
+
if (scope.scope.kind === 'agreement' &&
|
|
149
|
+
(scope.newMessages > 0 || scope.newArtifacts > 0) &&
|
|
150
|
+
(await isTerminalAgreement(scope.scope.id, deps.creds))) {
|
|
151
|
+
if (scope.latestAt) {
|
|
152
|
+
toAck.push({ kind: scope.scope.kind, id: scope.scope.id, upTo: scope.latestAt });
|
|
153
|
+
}
|
|
154
|
+
log(label, `scope agreement:${scope.scope.id} terminal — acked, not re-delivered`);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const chatIds = await chatIdsForScope(scope, deps.creds, deps.baseUrl, discovery);
|
|
158
|
+
if (!chatIds.length && (scope.newMessages > 0 || scope.newArtifacts > 0)) {
|
|
159
|
+
warn(label, `no chat ids for scope ${scope.scope.kind}:${scope.scope.id} (${scope.newMessages} msgs)`);
|
|
160
|
+
}
|
|
161
|
+
if (scope.newMessages > 0 && chatIds.length) {
|
|
162
|
+
const n = await deliverMessagesForScope(deps, scope, chatIds, reader, countCap);
|
|
163
|
+
messagesDelivered += n;
|
|
164
|
+
log(label, `scope ${scope.scope.kind}:${scope.scope.id} delivered ${n} message(s)`);
|
|
165
|
+
}
|
|
166
|
+
if (scope.newArtifacts > 0) {
|
|
167
|
+
for (const chatId of chatIds) {
|
|
168
|
+
await deps.onResourceEvent({
|
|
169
|
+
kind: 'artifact',
|
|
170
|
+
ts: scope.latestAt ?? new Date().toISOString(),
|
|
171
|
+
resourceId: chatId,
|
|
172
|
+
chatId,
|
|
173
|
+
change: 'created',
|
|
174
|
+
});
|
|
175
|
+
resourceEvents++;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (scope.latestAt && (scope.newMessages > 0 || scope.newArtifacts > 0)) {
|
|
179
|
+
toAck.push({
|
|
180
|
+
kind: scope.scope.kind,
|
|
181
|
+
id: scope.scope.id,
|
|
182
|
+
upTo: scope.latestAt,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
for (const proposal of envelope.proposalsAwaitingMe) {
|
|
187
|
+
await deps.onResourceEvent({
|
|
188
|
+
kind: 'agreement',
|
|
189
|
+
ts: proposal.proposedAt ?? new Date().toISOString(),
|
|
190
|
+
resourceId: proposal.agreementId,
|
|
191
|
+
agreementId: proposal.agreementId,
|
|
192
|
+
change: 'created',
|
|
193
|
+
reason: 'proposal_pending',
|
|
194
|
+
});
|
|
195
|
+
resourceEvents++;
|
|
196
|
+
}
|
|
197
|
+
let acked = 0;
|
|
198
|
+
if (toAck.length) {
|
|
199
|
+
await inbox.ack(toAck);
|
|
200
|
+
acked = toAck.length;
|
|
201
|
+
log(label, `acked ${acked} scope(s)`);
|
|
202
|
+
}
|
|
203
|
+
if (messagesDelivered > 0 || resourceEvents > 0) {
|
|
204
|
+
log(label, `catch-up complete: ${messagesDelivered} message(s), ${resourceEvents} resource hint(s)`);
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
scopes: envelope.scopes.length,
|
|
208
|
+
messagesDelivered,
|
|
209
|
+
resourceEvents,
|
|
210
|
+
acked,
|
|
211
|
+
truncated: envelope.truncatedScopes > 0,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface OutboxBufferOptions {
|
|
2
|
+
ttlMs?: number;
|
|
3
|
+
}
|
|
4
|
+
interface HistoryEntryShape {
|
|
5
|
+
entryType?: string;
|
|
6
|
+
text?: string;
|
|
7
|
+
sender?: {
|
|
8
|
+
id?: string;
|
|
9
|
+
type?: string;
|
|
10
|
+
};
|
|
11
|
+
receiver?: {
|
|
12
|
+
id?: string;
|
|
13
|
+
};
|
|
14
|
+
timestamp?: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Tracks outbound messages locally until server history syncs them, then
|
|
18
|
+
* merges any unmatched ones into the next read context. Prevents the LLM
|
|
19
|
+
* from re-saying things it already said when the server is slow to publish.
|
|
20
|
+
*/
|
|
21
|
+
export declare class OutboxBuffer {
|
|
22
|
+
private _pending;
|
|
23
|
+
private _ttlMs;
|
|
24
|
+
constructor(options?: OutboxBufferOptions);
|
|
25
|
+
track(chatId: string, msg: {
|
|
26
|
+
text: string;
|
|
27
|
+
receiverId: string;
|
|
28
|
+
}): void;
|
|
29
|
+
merge(chatId: string, history: HistoryEntryShape[], agentId: string | null): void;
|
|
30
|
+
gc(): void;
|
|
31
|
+
private _gcChat;
|
|
32
|
+
}
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { runtimeLog } from '../shared/runtimeLog.js';
|
|
2
|
+
/**
|
|
3
|
+
* Tracks outbound messages locally until server history syncs them, then
|
|
4
|
+
* merges any unmatched ones into the next read context. Prevents the LLM
|
|
5
|
+
* from re-saying things it already said when the server is slow to publish.
|
|
6
|
+
*/
|
|
7
|
+
export class OutboxBuffer {
|
|
8
|
+
_pending = new Map();
|
|
9
|
+
_ttlMs;
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
this._ttlMs = Number.isFinite(options.ttlMs) ? Math.max(1_000, options.ttlMs) : 10 * 60 * 1000;
|
|
12
|
+
}
|
|
13
|
+
track(chatId, msg) {
|
|
14
|
+
this._gcChat(chatId);
|
|
15
|
+
if (!this._pending.has(chatId))
|
|
16
|
+
this._pending.set(chatId, []);
|
|
17
|
+
this._pending.get(chatId).push({ text: msg.text, receiverId: msg.receiverId, timestamp: Date.now() });
|
|
18
|
+
}
|
|
19
|
+
merge(chatId, history, agentId) {
|
|
20
|
+
this._gcChat(chatId);
|
|
21
|
+
const pending = this._pending.get(chatId);
|
|
22
|
+
if (!pending || pending.length === 0)
|
|
23
|
+
return;
|
|
24
|
+
const matchedIndices = new Set();
|
|
25
|
+
const remaining = [];
|
|
26
|
+
for (const msg of pending) {
|
|
27
|
+
const matchIdx = history.findIndex((entry, idx) => !matchedIndices.has(idx) &&
|
|
28
|
+
entry.text === msg.text &&
|
|
29
|
+
entry.sender?.id === agentId);
|
|
30
|
+
if (matchIdx !== -1)
|
|
31
|
+
matchedIndices.add(matchIdx);
|
|
32
|
+
else
|
|
33
|
+
remaining.push(msg);
|
|
34
|
+
}
|
|
35
|
+
for (const msg of remaining) {
|
|
36
|
+
history.push({
|
|
37
|
+
entryType: 'message',
|
|
38
|
+
text: msg.text,
|
|
39
|
+
sender: { id: agentId || undefined, type: 'agent' },
|
|
40
|
+
receiver: { id: msg.receiverId },
|
|
41
|
+
timestamp: msg.timestamp,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
if (remaining.length === 0)
|
|
45
|
+
this._pending.delete(chatId);
|
|
46
|
+
else
|
|
47
|
+
this._pending.set(chatId, remaining);
|
|
48
|
+
}
|
|
49
|
+
gc() {
|
|
50
|
+
for (const chatId of this._pending.keys())
|
|
51
|
+
this._gcChat(chatId);
|
|
52
|
+
}
|
|
53
|
+
_gcChat(chatId) {
|
|
54
|
+
const pending = this._pending.get(chatId);
|
|
55
|
+
if (!pending?.length)
|
|
56
|
+
return;
|
|
57
|
+
const cutoff = Date.now() - this._ttlMs;
|
|
58
|
+
const fresh = pending.filter(msg => msg.timestamp >= cutoff);
|
|
59
|
+
if (fresh.length === 0) {
|
|
60
|
+
this._pending.delete(chatId);
|
|
61
|
+
runtimeLog.warn('OutboxBuffer', `expired all pending messages chatId=${chatId}`);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (fresh.length !== pending.length) {
|
|
65
|
+
this._pending.set(chatId, fresh);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dedupes delivery across push (WS flush) and pull (inbox catch-up).
|
|
3
|
+
* ZIG-434 / ZIG-454: inbox is reconciliation source; stable message ids
|
|
4
|
+
* prevent double-processing when both paths fire for the same row.
|
|
5
|
+
*/
|
|
6
|
+
export declare class SeenMessages {
|
|
7
|
+
private readonly maxSize;
|
|
8
|
+
private readonly order;
|
|
9
|
+
private readonly seen;
|
|
10
|
+
constructor(maxSize?: number);
|
|
11
|
+
has(messageId: string | null | undefined): boolean;
|
|
12
|
+
mark(messageId: string | null | undefined): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dedupes delivery across push (WS flush) and pull (inbox catch-up).
|
|
3
|
+
* ZIG-434 / ZIG-454: inbox is reconciliation source; stable message ids
|
|
4
|
+
* prevent double-processing when both paths fire for the same row.
|
|
5
|
+
*/
|
|
6
|
+
export class SeenMessages {
|
|
7
|
+
maxSize;
|
|
8
|
+
order = [];
|
|
9
|
+
seen = new Set();
|
|
10
|
+
constructor(maxSize = 5_000) {
|
|
11
|
+
this.maxSize = maxSize;
|
|
12
|
+
}
|
|
13
|
+
has(messageId) {
|
|
14
|
+
if (!messageId || typeof messageId !== 'string')
|
|
15
|
+
return false;
|
|
16
|
+
return this.seen.has(messageId);
|
|
17
|
+
}
|
|
18
|
+
mark(messageId) {
|
|
19
|
+
if (!messageId || typeof messageId !== 'string')
|
|
20
|
+
return;
|
|
21
|
+
if (this.seen.has(messageId))
|
|
22
|
+
return;
|
|
23
|
+
this.seen.add(messageId);
|
|
24
|
+
this.order.push(messageId);
|
|
25
|
+
while (this.order.length > this.maxSize) {
|
|
26
|
+
const oldest = this.order.shift();
|
|
27
|
+
if (oldest)
|
|
28
|
+
this.seen.delete(oldest);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface StateTimeoutEvent {
|
|
2
|
+
type: 'state-timeout';
|
|
3
|
+
state: string;
|
|
4
|
+
waitedMs: number;
|
|
5
|
+
epoch: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Per-lane liveness timers for parked states declaring timeoutMs. Owned by
|
|
9
|
+
* AgentHost. A firing timer enqueues a state-timeout frame on the SAME lane
|
|
10
|
+
* (caller-provided enqueue → EventQueue), so it serializes behind any real
|
|
11
|
+
* event and can never race one. Epochs make stale frames identifiable: a
|
|
12
|
+
* queued timeout frame whose epoch is not the lane's currently-armed epoch was
|
|
13
|
+
* superseded (a tick ran after it fired) and must be dropped.
|
|
14
|
+
*
|
|
15
|
+
* In-memory only: process restart loses all timers; each lane re-arms on its
|
|
16
|
+
* next real event (sessionStore persists { ctx, state } but not timers).
|
|
17
|
+
*/
|
|
18
|
+
export declare class StateTimers {
|
|
19
|
+
private _timers;
|
|
20
|
+
private _epoch;
|
|
21
|
+
private _enqueue;
|
|
22
|
+
constructor(enqueue: (event: StateTimeoutEvent, laneKey: string) => void);
|
|
23
|
+
/** Re-arm is idempotent: any previously armed timer for the lane is cancelled first. */
|
|
24
|
+
arm(laneKey: string, state: string, timeoutMs: number): void;
|
|
25
|
+
clear(laneKey: string): void;
|
|
26
|
+
clearAll(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Drop stale state-timeout events from an incoming frame (single event or an
|
|
29
|
+
* EventQueue-coalesced { type: 'batch', events } frame). Returns null when
|
|
30
|
+
* nothing survives (pure stale-timeout frame — the lane's live timer, if any,
|
|
31
|
+
* must be left untouched by the caller in that case).
|
|
32
|
+
*/
|
|
33
|
+
stripStale(event: unknown, laneKey: string): unknown | null;
|
|
34
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { runtimeLog } from '../shared/runtimeLog.js';
|
|
2
|
+
/**
|
|
3
|
+
* Per-lane liveness timers for parked states declaring timeoutMs. Owned by
|
|
4
|
+
* AgentHost. A firing timer enqueues a state-timeout frame on the SAME lane
|
|
5
|
+
* (caller-provided enqueue → EventQueue), so it serializes behind any real
|
|
6
|
+
* event and can never race one. Epochs make stale frames identifiable: a
|
|
7
|
+
* queued timeout frame whose epoch is not the lane's currently-armed epoch was
|
|
8
|
+
* superseded (a tick ran after it fired) and must be dropped.
|
|
9
|
+
*
|
|
10
|
+
* In-memory only: process restart loses all timers; each lane re-arms on its
|
|
11
|
+
* next real event (sessionStore persists { ctx, state } but not timers).
|
|
12
|
+
*/
|
|
13
|
+
export class StateTimers {
|
|
14
|
+
_timers = new Map();
|
|
15
|
+
_epoch = 0;
|
|
16
|
+
_enqueue;
|
|
17
|
+
constructor(enqueue) {
|
|
18
|
+
this._enqueue = enqueue;
|
|
19
|
+
}
|
|
20
|
+
/** Re-arm is idempotent: any previously armed timer for the lane is cancelled first. */
|
|
21
|
+
arm(laneKey, state, timeoutMs) {
|
|
22
|
+
this.clear(laneKey);
|
|
23
|
+
const epoch = ++this._epoch;
|
|
24
|
+
const handle = setTimeout(() => {
|
|
25
|
+
runtimeLog.debug('StateTimers', `fired laneKey=${laneKey} state=${state} waitedMs=${timeoutMs} epoch=${epoch}`);
|
|
26
|
+
this._enqueue({ type: 'state-timeout', state, waitedMs: timeoutMs, epoch }, laneKey);
|
|
27
|
+
}, timeoutMs);
|
|
28
|
+
handle.unref?.();
|
|
29
|
+
this._timers.set(laneKey, { epoch, state, timeoutMs, armedAt: Date.now(), handle });
|
|
30
|
+
}
|
|
31
|
+
clear(laneKey) {
|
|
32
|
+
const t = this._timers.get(laneKey);
|
|
33
|
+
if (!t)
|
|
34
|
+
return;
|
|
35
|
+
clearTimeout(t.handle);
|
|
36
|
+
this._timers.delete(laneKey);
|
|
37
|
+
}
|
|
38
|
+
clearAll() {
|
|
39
|
+
for (const key of [...this._timers.keys()])
|
|
40
|
+
this.clear(key);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Drop stale state-timeout events from an incoming frame (single event or an
|
|
44
|
+
* EventQueue-coalesced { type: 'batch', events } frame). Returns null when
|
|
45
|
+
* nothing survives (pure stale-timeout frame — the lane's live timer, if any,
|
|
46
|
+
* must be left untouched by the caller in that case).
|
|
47
|
+
*/
|
|
48
|
+
stripStale(event, laneKey) {
|
|
49
|
+
const pending = this._timers.get(laneKey);
|
|
50
|
+
const fresh = (ev) => {
|
|
51
|
+
const e = ev;
|
|
52
|
+
if (e?.type !== 'state-timeout')
|
|
53
|
+
return true;
|
|
54
|
+
const ok = pending !== undefined && e.epoch === pending.epoch;
|
|
55
|
+
if (!ok)
|
|
56
|
+
runtimeLog.debug('StateTimers', `dropping stale state-timeout laneKey=${laneKey} epoch=${e.epoch} armed=${pending?.epoch ?? 'none'}`);
|
|
57
|
+
return ok;
|
|
58
|
+
};
|
|
59
|
+
const b = event;
|
|
60
|
+
if (b?.type === 'batch' && Array.isArray(b.events)) {
|
|
61
|
+
const kept = b.events.filter(fresh);
|
|
62
|
+
if (kept.length === 0)
|
|
63
|
+
return null;
|
|
64
|
+
if (kept.length === b.events.length)
|
|
65
|
+
return event;
|
|
66
|
+
return kept.length === 1 ? kept[0] : { type: 'batch', events: kept };
|
|
67
|
+
}
|
|
68
|
+
return fresh(event) ? event : null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ziggs-flavored EffectHandler.
|
|
3
|
+
*
|
|
4
|
+
* Translates the Agent's pure Effect requests into concrete backend calls:
|
|
5
|
+
* reads go through the grant-fenced ContextReadClient snapshot, writes through
|
|
6
|
+
* artifactsClient / taskService / agreementService / messageSender, and LLM
|
|
7
|
+
* turns through the adapter. This is the only place where Agent intent meets
|
|
8
|
+
* backend reality.
|
|
9
|
+
*
|
|
10
|
+
* Lives in agent-sdk for now alongside `Agent`; destined for a separate
|
|
11
|
+
* `@ziggs-ai/agent-server` package once the SDK split is complete.
|
|
12
|
+
*/
|
|
13
|
+
import type { Effect, EffectHandler, LlmMessage, LlmResponse, LlmToolSchema, ToolCallResult } from '../types.js';
|
|
14
|
+
/** Minimal LLM adapter contract — OpenAIAdapter satisfies this. */
|
|
15
|
+
interface LlmAdapter {
|
|
16
|
+
chatMessages(messages: LlmMessage[], tools?: LlmToolSchema[], options?: Record<string, unknown>): Promise<LlmResponse>;
|
|
17
|
+
chatMessagesStream?(messages: LlmMessage[], onChunk: (text: string) => void): Promise<{
|
|
18
|
+
text: string;
|
|
19
|
+
usage?: LlmResponse['usage'];
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
import type { TaskService } from './tasks/TaskService.js';
|
|
23
|
+
import type { AgreementService } from './agreements/AgreementService.js';
|
|
24
|
+
import type { ToolManager } from '../tools/ToolManager.js';
|
|
25
|
+
import type { ArtifactsClient, ContextReadClient, TelemetryClient } from '@ziggs-ai/api-client';
|
|
26
|
+
export interface ZiggsEffectDeps {
|
|
27
|
+
/**
|
|
28
|
+
* LLM adapter for `llm-call` / `stream-text`. Optional: the Claude brain
|
|
29
|
+
* drives its own model via `query()` and emits neither effect, so it wires
|
|
30
|
+
* no adapter. Those two cases throw if reached without one.
|
|
31
|
+
*/
|
|
32
|
+
llm?: LlmAdapter;
|
|
33
|
+
artifactsClient: ArtifactsClient;
|
|
34
|
+
/** Grant-fenced aggregate read backbone — sources the read-context snapshot. */
|
|
35
|
+
contextReadClient: ContextReadClient;
|
|
36
|
+
taskService: TaskService;
|
|
37
|
+
agreementService: AgreementService;
|
|
38
|
+
messageSender: (text: string, receiverId: string, sessionId: string, messageId?: string) => Promise<unknown>;
|
|
39
|
+
/** Sends a streaming chunk to a client. Called per-token during stream-text effects. */
|
|
40
|
+
chunkSender?: (sessionId: string, receiverId: string, text: string, messageId: string) => void;
|
|
41
|
+
toolManager: ToolManager;
|
|
42
|
+
operatorKey: string;
|
|
43
|
+
agentId: string;
|
|
44
|
+
telemetryClient?: TelemetryClient;
|
|
45
|
+
/** When false, skips POST /agents/monitoring/ingest (default: env on). */
|
|
46
|
+
telemetryIngest?: boolean;
|
|
47
|
+
effectTap?: (effect: Effect, response: unknown) => void;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The single tool-execution path for BOTH cognitions (ZIG-775).
|
|
51
|
+
*
|
|
52
|
+
* Executing a tool is more than calling its handler: it emits the operation
|
|
53
|
+
* timeline artifacts (started / completed / error) and tool telemetry, and
|
|
54
|
+
* resolves the invoking user + roster. The FSM reaches this via
|
|
55
|
+
* `eff({ kind: 'tool-call' })` inside `runTurn`; the Claude Agent SDK bridge
|
|
56
|
+
* (`toolManagerToSdkMcp`) emits the SAME effect — so a hosted Claude-brain
|
|
57
|
+
* agent's tool activity is as observable as an FSM agent's (needed for
|
|
58
|
+
* hired-agent visibility, ZIG-535). A caller cannot run a tool and skip these
|
|
59
|
+
* side effects — that is the whole point of funneling both paths here.
|
|
60
|
+
*
|
|
61
|
+
* `eff` is threaded into the tool context so eff-backed tools
|
|
62
|
+
* (`ziggs_context_snapshot`, `ziggs_send_message`) resolve through this same path
|
|
63
|
+
* regardless of which cognition invoked them.
|
|
64
|
+
*/
|
|
65
|
+
export declare function executeToolWithSideEffects(deps: Pick<ZiggsEffectDeps, 'toolManager' | 'artifactsClient' | 'telemetryClient' | 'telemetryIngest' | 'operatorKey' | 'agentId' | 'taskService' | 'agreementService' | 'contextReadClient' | 'messageSender'>, eff: EffectHandler, effect: Extract<Effect, {
|
|
66
|
+
kind: 'tool-call';
|
|
67
|
+
}>, roster: {
|
|
68
|
+
agents: unknown[];
|
|
69
|
+
users: unknown[];
|
|
70
|
+
}): Promise<ToolCallResult>;
|
|
71
|
+
/**
|
|
72
|
+
* Build a single shared EffectHandler. The handler is stateful — it caches
|
|
73
|
+
* the latest ContextSnapshot per sessionId so tool-call effects can resolve
|
|
74
|
+
* session-coupled deps (the `agents` roster) without forcing the Agent to
|
|
75
|
+
* pass them through. Cache is bounded with simple LRU eviction (re-set on
|
|
76
|
+
* read promotes the entry; oldest entry drops when size exceeds the cap).
|
|
77
|
+
*/
|
|
78
|
+
export declare function createZiggsEffectHandler(deps: ZiggsEffectDeps): EffectHandler;
|
|
79
|
+
export {};
|