@ziggs-ai/agent-sdk 0.1.12 → 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.
Files changed (218) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +41 -2
  3. package/dist/AgentHost.d.ts +201 -0
  4. package/dist/AgentHost.js +652 -0
  5. package/dist/adapters/AnthropicAdapter.d.ts +39 -0
  6. package/dist/adapters/AnthropicAdapter.js +200 -0
  7. package/dist/adapters/McpAdapter.d.ts +199 -0
  8. package/dist/adapters/McpAdapter.js +468 -0
  9. package/dist/adapters/OpenAIAdapter.d.ts +26 -0
  10. package/dist/adapters/OpenAIAdapter.js +158 -0
  11. package/dist/adapters/index.js +2 -0
  12. package/dist/agent/Agent.d.ts +78 -0
  13. package/dist/agent/Agent.js +58 -0
  14. package/dist/agent/ClaudeSdkAgent.d.ts +38 -0
  15. package/dist/agent/ClaudeSdkAgent.js +322 -0
  16. package/dist/agent/claudeCognition.d.ts +67 -0
  17. package/dist/agent/claudeCognition.js +103 -0
  18. package/dist/agent/claudeSdkProtocol.d.ts +11 -0
  19. package/dist/agent/claudeSdkTools.d.ts +19 -0
  20. package/dist/agent/claudeSdkTools.js +57 -0
  21. package/dist/agent/hostedAgentConnection.d.ts +15 -0
  22. package/dist/agent/hostedAgentConnection.js +29 -0
  23. package/dist/agent/toolManagerToSdkMcp.d.ts +12 -0
  24. package/dist/agent/toolManagerToSdkMcp.js +123 -0
  25. package/dist/brain/index.d.ts +50 -0
  26. package/dist/brain/index.js +45 -0
  27. package/dist/cognition/validateContext.d.ts +11 -0
  28. package/dist/cognition/validateContext.js +117 -0
  29. package/dist/context/applyEffects.d.ts +2 -0
  30. package/dist/context/applyEffects.js +89 -0
  31. package/dist/context/batch.d.ts +2 -0
  32. package/dist/context/batch.js +18 -0
  33. package/dist/context/classifyEnvelope.d.ts +1 -0
  34. package/dist/context/classifyEnvelope.js +58 -0
  35. package/dist/context/routingLabels.d.ts +4 -0
  36. package/dist/context/routingLabels.js +48 -0
  37. package/dist/defineAgent.d.ts +14 -0
  38. package/dist/defineAgent.js +68 -0
  39. package/dist/formatters/AgreementFormatter.d.ts +18 -0
  40. package/dist/formatters/AgreementFormatter.js +130 -0
  41. package/dist/formatters/HistoryFormatter.d.ts +26 -0
  42. package/dist/formatters/HistoryFormatter.js +173 -0
  43. package/dist/formatters/index.js +2 -0
  44. package/{src/index.ts → dist/index.d.ts} +7 -53
  45. package/dist/index.js +66 -0
  46. package/dist/ingress/normalizeIncoming.d.ts +65 -0
  47. package/dist/ingress/normalizeIncoming.js +88 -0
  48. package/dist/memory/MemoryStore.d.ts +55 -0
  49. package/dist/memory/MemoryStore.js +78 -0
  50. package/dist/pricing/fleetDefaults.d.ts +46 -0
  51. package/dist/pricing/fleetDefaults.js +168 -0
  52. package/dist/pricing/fleetEvalFree.d.ts +7 -0
  53. package/dist/pricing/fleetEvalFree.js +24 -0
  54. package/dist/pricing/fleetFreeTierA.gen.d.ts +6 -0
  55. package/{src/pricing/fleetFreeTierA.gen.ts → dist/pricing/fleetFreeTierA.gen.js} +5 -6
  56. package/dist/pricing/fleetTierByAgentId.gen.d.ts +7 -0
  57. package/dist/pricing/fleetTierByAgentId.gen.js +768 -0
  58. package/dist/runtime/AgentMachine.d.ts +143 -0
  59. package/dist/runtime/AgentMachine.js +400 -0
  60. package/dist/runtime/PromptBuilder.d.ts +96 -0
  61. package/dist/runtime/PromptBuilder.js +439 -0
  62. package/{src/runtime/bootstrapCtx.ts → dist/runtime/bootstrapCtx.d.ts} +1 -22
  63. package/dist/runtime/bootstrapCtx.js +37 -0
  64. package/dist/runtime/buildOutcome.d.ts +68 -0
  65. package/dist/runtime/buildOutcome.js +464 -0
  66. package/dist/runtime/defaults.d.ts +54 -0
  67. package/dist/runtime/defaults.js +47 -0
  68. package/dist/runtime/deriveRespondsToProposals.d.ts +9 -0
  69. package/dist/runtime/deriveRespondsToProposals.js +38 -0
  70. package/dist/runtime/kickoff.d.ts +15 -0
  71. package/dist/runtime/kickoff.js +112 -0
  72. package/dist/runtime/runMechanical.d.ts +25 -0
  73. package/dist/runtime/runMechanical.js +74 -0
  74. package/dist/runtime/runTurn.d.ts +63 -0
  75. package/dist/runtime/runTurn.js +800 -0
  76. package/dist/runtime/validateWorkflow.d.ts +17 -0
  77. package/dist/runtime/validateWorkflow.js +209 -0
  78. package/dist/server/ConnectionPool.d.ts +68 -0
  79. package/dist/server/ConnectionPool.js +166 -0
  80. package/dist/server/EventQueue.d.ts +33 -0
  81. package/dist/server/EventQueue.js +98 -0
  82. package/dist/server/InboxCatchUp.d.ts +24 -0
  83. package/dist/server/InboxCatchUp.js +213 -0
  84. package/dist/server/OutboxBuffer.d.ts +33 -0
  85. package/dist/server/OutboxBuffer.js +68 -0
  86. package/dist/server/SeenMessages.d.ts +13 -0
  87. package/dist/server/SeenMessages.js +31 -0
  88. package/dist/server/StateTimers.d.ts +34 -0
  89. package/dist/server/StateTimers.js +70 -0
  90. package/dist/server/ZiggsEffectHandler.d.ts +79 -0
  91. package/dist/server/ZiggsEffectHandler.js +319 -0
  92. package/dist/server/agreements/AgreementService.d.ts +50 -0
  93. package/dist/server/agreements/AgreementService.js +85 -0
  94. package/dist/server/createHealthServer.d.ts +7 -0
  95. package/dist/server/createHealthServer.js +75 -0
  96. package/dist/server/proactive/ProactiveTrigger.d.ts +33 -0
  97. package/dist/server/proactive/ProactiveTrigger.js +71 -0
  98. package/dist/server/runLauncher.d.ts +28 -0
  99. package/dist/server/runLauncher.js +93 -0
  100. package/dist/server/tasks/TaskService.d.ts +25 -0
  101. package/dist/server/tasks/TaskService.js +103 -0
  102. package/dist/server/tasks/index.js +1 -0
  103. package/dist/server/telemetryIngest.d.ts +24 -0
  104. package/dist/server/telemetryIngest.js +62 -0
  105. package/dist/server/tools/index.d.ts +24 -0
  106. package/{src/server/tools/index.ts → dist/server/tools/index.js} +4 -24
  107. package/dist/server/tools/tier1/protocolRunner.d.ts +39 -0
  108. package/dist/server/tools/tier1/protocolRunner.js +288 -0
  109. package/dist/server/tools/tier1/protocolTools.d.ts +12 -0
  110. package/dist/server/tools/tier1/protocolTools.js +37 -0
  111. package/dist/server/tools/tier2/connectionTools.d.ts +4 -0
  112. package/dist/server/tools/tier2/connectionTools.js +63 -0
  113. package/dist/server/tools/tier2/contextTools.d.ts +4 -0
  114. package/dist/server/tools/tier2/contextTools.js +46 -0
  115. package/dist/server/tools/tier2/discoveryTools.d.ts +4 -0
  116. package/dist/server/tools/tier2/discoveryTools.js +25 -0
  117. package/dist/server/tools/tier2/marketplaceTools.d.ts +3 -0
  118. package/dist/server/tools/tier2/marketplaceTools.js +22 -0
  119. package/dist/server/tools/tier2/paymentTools.d.ts +12 -0
  120. package/dist/server/tools/tier2/paymentTools.js +165 -0
  121. package/dist/server/ziggsconnect/ZiggsConnectClient.d.ts +35 -0
  122. package/dist/server/ziggsconnect/ZiggsConnectClient.js +95 -0
  123. package/dist/server/ziggscontext/ZiggsContextClient.d.ts +48 -0
  124. package/dist/server/ziggscontext/ZiggsContextClient.js +90 -0
  125. package/dist/server/ziggspay/ZiggsPayClient.d.ts +82 -0
  126. package/dist/server/ziggspay/ZiggsPayClient.js +188 -0
  127. package/dist/shared/ids.d.ts +1 -0
  128. package/dist/shared/ids.js +3 -0
  129. package/dist/shared/runtimeLog.d.ts +18 -0
  130. package/dist/shared/runtimeLog.js +68 -0
  131. package/dist/shared/types.d.ts +22 -0
  132. package/dist/shared/types.js +23 -0
  133. package/dist/tasks/protocolRegistry.d.ts +16 -0
  134. package/dist/tasks/protocolRegistry.js +20 -0
  135. package/dist/tools/ToolManager.d.ts +12 -0
  136. package/dist/tools/ToolManager.js +74 -0
  137. package/dist/tools/ToolProvider.d.ts +6 -0
  138. package/dist/tools/ToolProvider.js +21 -0
  139. package/dist/tools/defineTool.d.ts +27 -0
  140. package/dist/tools/defineTool.js +62 -0
  141. package/{src/tools/index.ts → dist/tools/index.d.ts} +0 -1
  142. package/dist/tools/index.js +6 -0
  143. package/dist/tools/recordReport.d.ts +41 -0
  144. package/dist/tools/recordReport.js +30 -0
  145. package/dist/types.d.ts +466 -0
  146. package/dist/types.js +7 -0
  147. package/dist/utils/jsonExtractor.d.ts +2 -0
  148. package/dist/utils/jsonExtractor.js +111 -0
  149. package/package.json +13 -5
  150. package/src/AgentHost.ts +0 -814
  151. package/src/adapters/AnthropicAdapter.ts +0 -238
  152. package/src/adapters/McpAdapter.ts +0 -678
  153. package/src/adapters/OpenAIAdapter.ts +0 -194
  154. package/src/agent/Agent.ts +0 -112
  155. package/src/agent/ClaudeSdkAgent.ts +0 -401
  156. package/src/agent/claudeCognition.ts +0 -139
  157. package/src/agent/claudeSdkTools.ts +0 -81
  158. package/src/agent/hostedAgentConnection.ts +0 -41
  159. package/src/agent/toolManagerToSdkMcp.ts +0 -136
  160. package/src/cognition/validateContext.ts +0 -95
  161. package/src/context/applyEffects.ts +0 -80
  162. package/src/context/batch.ts +0 -19
  163. package/src/context/classifyEnvelope.ts +0 -38
  164. package/src/context/routingLabels.ts +0 -46
  165. package/src/defineAgent.ts +0 -77
  166. package/src/formatters/AgreementFormatter.ts +0 -111
  167. package/src/formatters/HistoryFormatter.ts +0 -172
  168. package/src/ingress/normalizeIncoming.ts +0 -162
  169. package/src/memory/MemoryStore.ts +0 -104
  170. package/src/pricing/fleetDefaults.ts +0 -225
  171. package/src/pricing/fleetEvalFree.ts +0 -24
  172. package/src/pricing/fleetTierByAgentId.gen.ts +0 -775
  173. package/src/runtime/AgentMachine.ts +0 -484
  174. package/src/runtime/PromptBuilder.ts +0 -522
  175. package/src/runtime/buildOutcome.ts +0 -563
  176. package/src/runtime/defaults.ts +0 -86
  177. package/src/runtime/deriveRespondsToProposals.ts +0 -35
  178. package/src/runtime/kickoff.ts +0 -117
  179. package/src/runtime/runMechanical.ts +0 -106
  180. package/src/runtime/runTurn.ts +0 -870
  181. package/src/runtime/validateWorkflow.ts +0 -221
  182. package/src/server/ConnectionPool.ts +0 -206
  183. package/src/server/EventQueue.ts +0 -133
  184. package/src/server/InboxCatchUp.ts +0 -291
  185. package/src/server/OutboxBuffer.ts +0 -90
  186. package/src/server/SeenMessages.ts +0 -27
  187. package/src/server/StateTimers.ts +0 -85
  188. package/src/server/ZiggsEffectHandler.ts +0 -427
  189. package/src/server/agreements/AgreementService.ts +0 -118
  190. package/src/server/createHealthServer.ts +0 -85
  191. package/src/server/proactive/ProactiveTrigger.ts +0 -83
  192. package/src/server/runLauncher.ts +0 -146
  193. package/src/server/tasks/TaskService.ts +0 -111
  194. package/src/server/telemetryIngest.ts +0 -91
  195. package/src/server/tools/tier1/protocolRunner.ts +0 -353
  196. package/src/server/tools/tier1/protocolTools.ts +0 -114
  197. package/src/server/tools/tier2/connectionTools.ts +0 -75
  198. package/src/server/tools/tier2/contextTools.ts +0 -63
  199. package/src/server/tools/tier2/discoveryTools.ts +0 -34
  200. package/src/server/tools/tier2/marketplaceTools.ts +0 -27
  201. package/src/server/tools/tier2/paymentTools.ts +0 -170
  202. package/src/server/ziggsconnect/ZiggsConnectClient.ts +0 -126
  203. package/src/server/ziggscontext/ZiggsContextClient.ts +0 -129
  204. package/src/server/ziggspay/ZiggsPayClient.ts +0 -193
  205. package/src/shared/ids.ts +0 -3
  206. package/src/shared/runtimeLog.ts +0 -72
  207. package/src/shared/types.ts +0 -29
  208. package/src/tasks/protocolRegistry.ts +0 -25
  209. package/src/tools/ToolManager.ts +0 -95
  210. package/src/tools/ToolProvider.ts +0 -19
  211. package/src/tools/defineTool.ts +0 -90
  212. package/src/tools/recordReport.ts +0 -82
  213. package/src/types.ts +0 -483
  214. package/src/utils/jsonExtractor.ts +0 -100
  215. /package/{src/adapters/index.ts → dist/adapters/index.d.ts} +0 -0
  216. /package/{src/agent/claudeSdkProtocol.ts → dist/agent/claudeSdkProtocol.js} +0 -0
  217. /package/{src/formatters/index.ts → dist/formatters/index.d.ts} +0 -0
  218. /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 {};