@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.
Files changed (211) 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/claudeSdkProtocol.js +27 -0
  20. package/dist/agent/claudeSdkTools.d.ts +19 -0
  21. package/dist/agent/claudeSdkTools.js +57 -0
  22. package/dist/agent/hostedAgentConnection.d.ts +15 -0
  23. package/dist/agent/hostedAgentConnection.js +29 -0
  24. package/dist/agent/toolManagerToSdkMcp.d.ts +12 -0
  25. package/dist/agent/toolManagerToSdkMcp.js +123 -0
  26. package/dist/brain/index.d.ts +50 -0
  27. package/dist/brain/index.js +45 -0
  28. package/dist/cognition/validateContext.d.ts +11 -0
  29. package/dist/cognition/validateContext.js +117 -0
  30. package/dist/context/applyEffects.d.ts +2 -0
  31. package/dist/context/applyEffects.js +89 -0
  32. package/dist/context/batch.d.ts +2 -0
  33. package/dist/context/batch.js +18 -0
  34. package/dist/context/classifyEnvelope.d.ts +1 -0
  35. package/dist/context/classifyEnvelope.js +58 -0
  36. package/dist/context/routingLabels.d.ts +4 -0
  37. package/dist/context/routingLabels.js +48 -0
  38. package/dist/defineAgent.d.ts +14 -0
  39. package/dist/defineAgent.js +68 -0
  40. package/dist/formatters/AgreementFormatter.d.ts +18 -0
  41. package/dist/formatters/AgreementFormatter.js +130 -0
  42. package/dist/formatters/HistoryFormatter.d.ts +26 -0
  43. package/dist/formatters/HistoryFormatter.js +173 -0
  44. package/dist/formatters/index.js +2 -0
  45. package/{src/index.ts → dist/index.d.ts} +13 -53
  46. package/dist/index.js +66 -0
  47. package/dist/ingress/normalizeIncoming.d.ts +65 -0
  48. package/dist/ingress/normalizeIncoming.js +88 -0
  49. package/dist/memory/MemoryStore.d.ts +55 -0
  50. package/dist/memory/MemoryStore.js +78 -0
  51. package/dist/pricing/fleetDefaults.d.ts +46 -0
  52. package/dist/pricing/fleetDefaults.js +168 -0
  53. package/dist/pricing/fleetEvalFree.d.ts +7 -0
  54. package/dist/pricing/fleetEvalFree.js +24 -0
  55. package/dist/pricing/fleetFreeTierA.gen.d.ts +6 -0
  56. package/{src/pricing/fleetFreeTierA.gen.ts → dist/pricing/fleetFreeTierA.gen.js} +5 -6
  57. package/dist/pricing/fleetTierByAgentId.gen.d.ts +7 -0
  58. package/dist/pricing/fleetTierByAgentId.gen.js +768 -0
  59. package/dist/runtime/AgentMachine.d.ts +143 -0
  60. package/dist/runtime/AgentMachine.js +400 -0
  61. package/dist/runtime/PromptBuilder.d.ts +96 -0
  62. package/dist/runtime/PromptBuilder.js +439 -0
  63. package/dist/runtime/bootstrapCtx.d.ts +18 -0
  64. package/dist/runtime/bootstrapCtx.js +37 -0
  65. package/dist/runtime/buildOutcome.d.ts +68 -0
  66. package/dist/runtime/buildOutcome.js +464 -0
  67. package/dist/runtime/defaults.d.ts +54 -0
  68. package/dist/runtime/defaults.js +47 -0
  69. package/dist/runtime/deriveRespondsToProposals.d.ts +9 -0
  70. package/dist/runtime/deriveRespondsToProposals.js +38 -0
  71. package/dist/runtime/kickoff.d.ts +15 -0
  72. package/dist/runtime/kickoff.js +112 -0
  73. package/dist/runtime/runMechanical.d.ts +25 -0
  74. package/dist/runtime/runMechanical.js +74 -0
  75. package/dist/runtime/runTurn.d.ts +63 -0
  76. package/dist/runtime/runTurn.js +800 -0
  77. package/dist/runtime/validateWorkflow.d.ts +17 -0
  78. package/dist/runtime/validateWorkflow.js +209 -0
  79. package/dist/server/ConnectionPool.d.ts +68 -0
  80. package/dist/server/ConnectionPool.js +166 -0
  81. package/dist/server/EventQueue.d.ts +33 -0
  82. package/dist/server/EventQueue.js +98 -0
  83. package/dist/server/InboxCatchUp.d.ts +24 -0
  84. package/dist/server/InboxCatchUp.js +213 -0
  85. package/dist/server/OutboxBuffer.d.ts +33 -0
  86. package/dist/server/OutboxBuffer.js +68 -0
  87. package/dist/server/SeenMessages.d.ts +13 -0
  88. package/dist/server/SeenMessages.js +31 -0
  89. package/dist/server/StateTimers.d.ts +34 -0
  90. package/dist/server/StateTimers.js +70 -0
  91. package/dist/server/ZiggsEffectHandler.d.ts +79 -0
  92. package/dist/server/ZiggsEffectHandler.js +319 -0
  93. package/dist/server/agreements/AgreementService.d.ts +50 -0
  94. package/dist/server/agreements/AgreementService.js +85 -0
  95. package/dist/server/createHealthServer.d.ts +7 -0
  96. package/dist/server/createHealthServer.js +75 -0
  97. package/dist/server/proactive/ProactiveTrigger.d.ts +33 -0
  98. package/dist/server/proactive/ProactiveTrigger.js +71 -0
  99. package/dist/server/runLauncher.d.ts +28 -0
  100. package/dist/server/runLauncher.js +93 -0
  101. package/dist/server/tasks/TaskService.d.ts +25 -0
  102. package/dist/server/tasks/TaskService.js +103 -0
  103. package/dist/server/tasks/index.js +1 -0
  104. package/dist/server/telemetryIngest.d.ts +24 -0
  105. package/dist/server/telemetryIngest.js +62 -0
  106. package/dist/server/tools/index.d.ts +24 -0
  107. package/{src/server/tools/index.ts → dist/server/tools/index.js} +4 -24
  108. package/dist/server/tools/tier1/protocolRunner.d.ts +39 -0
  109. package/dist/server/tools/tier1/protocolRunner.js +288 -0
  110. package/dist/server/tools/tier1/protocolTools.d.ts +12 -0
  111. package/dist/server/tools/tier1/protocolTools.js +37 -0
  112. package/dist/server/tools/tier2/connectionTools.d.ts +4 -0
  113. package/dist/server/tools/tier2/connectionTools.js +63 -0
  114. package/dist/server/tools/tier2/contextTools.d.ts +4 -0
  115. package/dist/server/tools/tier2/contextTools.js +46 -0
  116. package/dist/server/tools/tier2/discoveryTools.d.ts +4 -0
  117. package/dist/server/tools/tier2/discoveryTools.js +25 -0
  118. package/dist/server/tools/tier2/marketplaceTools.d.ts +3 -0
  119. package/dist/server/tools/tier2/marketplaceTools.js +22 -0
  120. package/dist/server/tools/tier2/paymentTools.d.ts +12 -0
  121. package/dist/server/tools/tier2/paymentTools.js +165 -0
  122. package/dist/server/ziggsconnect/ZiggsConnectClient.d.ts +35 -0
  123. package/dist/server/ziggsconnect/ZiggsConnectClient.js +95 -0
  124. package/dist/server/ziggscontext/ZiggsContextClient.d.ts +48 -0
  125. package/dist/server/ziggscontext/ZiggsContextClient.js +90 -0
  126. package/dist/server/ziggspay/ZiggsPayClient.d.ts +82 -0
  127. package/dist/server/ziggspay/ZiggsPayClient.js +188 -0
  128. package/dist/shared/ids.d.ts +1 -0
  129. package/dist/shared/ids.js +3 -0
  130. package/dist/shared/runtimeLog.d.ts +18 -0
  131. package/dist/shared/runtimeLog.js +68 -0
  132. package/dist/shared/types.d.ts +22 -0
  133. package/dist/shared/types.js +23 -0
  134. package/dist/tasks/protocolRegistry.d.ts +16 -0
  135. package/dist/tasks/protocolRegistry.js +20 -0
  136. package/dist/tools/ToolManager.d.ts +12 -0
  137. package/dist/tools/ToolManager.js +74 -0
  138. package/dist/tools/ToolProvider.d.ts +6 -0
  139. package/dist/tools/ToolProvider.js +21 -0
  140. package/dist/tools/defineTool.d.ts +27 -0
  141. package/dist/tools/defineTool.js +62 -0
  142. package/{src/tools/index.ts → dist/tools/index.d.ts} +0 -1
  143. package/dist/tools/index.js +6 -0
  144. package/dist/tools/recordReport.d.ts +41 -0
  145. package/dist/tools/recordReport.js +30 -0
  146. package/dist/types.d.ts +466 -0
  147. package/dist/types.js +7 -0
  148. package/dist/utils/jsonExtractor.d.ts +2 -0
  149. package/dist/utils/jsonExtractor.js +111 -0
  150. package/package.json +17 -7
  151. package/src/AgentHost.ts +0 -545
  152. package/src/adapters/AnthropicAdapter.ts +0 -206
  153. package/src/adapters/McpAdapter.ts +0 -348
  154. package/src/adapters/OpenAIAdapter.ts +0 -187
  155. package/src/agent/Agent.ts +0 -104
  156. package/src/cognition/validateContext.ts +0 -95
  157. package/src/context/applyEffects.ts +0 -80
  158. package/src/context/batch.ts +0 -19
  159. package/src/context/classifyEnvelope.ts +0 -38
  160. package/src/context/routingLabels.ts +0 -46
  161. package/src/defineAgent.ts +0 -70
  162. package/src/formatters/AgreementFormatter.ts +0 -111
  163. package/src/formatters/HistoryFormatter.ts +0 -172
  164. package/src/ingress/normalizeIncoming.ts +0 -162
  165. package/src/memory/MemoryStore.ts +0 -104
  166. package/src/pricing/fleetDefaults.ts +0 -225
  167. package/src/pricing/fleetEvalFree.ts +0 -24
  168. package/src/pricing/fleetTierByAgentId.gen.ts +0 -775
  169. package/src/runtime/AgentMachine.ts +0 -402
  170. package/src/runtime/PromptBuilder.ts +0 -485
  171. package/src/runtime/buildOutcome.ts +0 -563
  172. package/src/runtime/defaults.ts +0 -75
  173. package/src/runtime/kickoff.ts +0 -117
  174. package/src/runtime/runTurn.ts +0 -813
  175. package/src/runtime/validateWorkflow.ts +0 -202
  176. package/src/server/ConnectionPool.ts +0 -206
  177. package/src/server/EventQueue.ts +0 -133
  178. package/src/server/InboxCatchUp.ts +0 -254
  179. package/src/server/OutboxBuffer.ts +0 -90
  180. package/src/server/SeenMessages.ts +0 -27
  181. package/src/server/StateTimers.ts +0 -85
  182. package/src/server/ZiggsEffectHandler.ts +0 -418
  183. package/src/server/agreements/AgreementService.ts +0 -118
  184. package/src/server/createHealthServer.ts +0 -85
  185. package/src/server/proactive/ProactiveTrigger.ts +0 -83
  186. package/src/server/runLauncher.ts +0 -146
  187. package/src/server/tasks/TaskService.ts +0 -110
  188. package/src/server/telemetryIngest.ts +0 -91
  189. package/src/server/tools/tier1/protocolRunner.ts +0 -144
  190. package/src/server/tools/tier1/protocolTools.ts +0 -99
  191. package/src/server/tools/tier2/connectionTools.ts +0 -75
  192. package/src/server/tools/tier2/contextTools.ts +0 -63
  193. package/src/server/tools/tier2/discoveryTools.ts +0 -34
  194. package/src/server/tools/tier2/marketplaceTools.ts +0 -27
  195. package/src/server/tools/tier2/paymentTools.ts +0 -170
  196. package/src/server/ziggsconnect/ZiggsConnectClient.ts +0 -126
  197. package/src/server/ziggscontext/ZiggsContextClient.ts +0 -129
  198. package/src/server/ziggspay/ZiggsPayClient.ts +0 -193
  199. package/src/shared/ids.ts +0 -3
  200. package/src/shared/runtimeLog.ts +0 -72
  201. package/src/shared/types.ts +0 -29
  202. package/src/tasks/protocolRegistry.ts +0 -25
  203. package/src/tools/ToolManager.ts +0 -95
  204. package/src/tools/ToolProvider.ts +0 -19
  205. package/src/tools/defineTool.ts +0 -90
  206. package/src/tools/recordReport.ts +0 -82
  207. package/src/types.ts +0 -456
  208. package/src/utils/jsonExtractor.ts +0 -100
  209. /package/{src/adapters/index.ts → dist/adapters/index.d.ts} +0 -0
  210. /package/{src/formatters/index.ts → dist/formatters/index.d.ts} +0 -0
  211. /package/{src/server/tasks/index.ts → dist/server/tasks/index.d.ts} +0 -0
@@ -1,254 +0,0 @@
1
- import {
2
- ContextReadClient,
3
- ContextDiscoveryClient,
4
- InboxClient,
5
- getChatsForAgreement,
6
- type InboxScopeEntry,
7
- type InboxAck,
8
- type Creds,
9
- } from '@ziggs-ai/api-client';
10
- import { SeenMessages } from './SeenMessages.js';
11
- import { runtimeLog } from '../shared/runtimeLog.js';
12
-
13
- export interface InboxCatchUpDeps {
14
- creds: Creds;
15
- ownAgentId: string;
16
- baseUrl?: string;
17
- seen: SeenMessages;
18
- onMessage: (text: string, metadata: Record<string, unknown>) => Promise<void>;
19
- onResourceEvent: (event: Record<string, unknown>) => Promise<void>;
20
- label?: string;
21
- }
22
-
23
- export interface InboxCatchUpResult {
24
- scopes: number;
25
- messagesDelivered: number;
26
- resourceEvents: number;
27
- acked: number;
28
- truncated: boolean;
29
- }
30
-
31
- type WireRow = Record<string, unknown>;
32
-
33
- const READ_PAGE = 100;
34
-
35
- function log(label: string, msg: string): void {
36
- runtimeLog.info(label, msg);
37
- }
38
-
39
- function warn(label: string, msg: string): void {
40
- runtimeLog.warn(label, msg);
41
- }
42
-
43
- /** Map Mongo message row → wire metadata for normalizeIncomingEvent. */
44
- function rowToMetadata(row: WireRow, chatId: string): Record<string, unknown> {
45
- const senderRaw = row.sender as { agentId?: string; id?: string; type?: string } | undefined;
46
- const senderId = String(senderRaw?.agentId ?? senderRaw?.id ?? '');
47
- const receiver = row.receiver as { id?: string; type?: string } | undefined;
48
- const senderType =
49
- senderRaw?.type ??
50
- (receiver?.type === 'agent' ? 'agent' : 'user');
51
-
52
- return {
53
- chatId,
54
- chat_id: chatId,
55
- messageId: row.messageId,
56
- sender: { id: senderId, type: senderType },
57
- senderId,
58
- senderType: String(senderType).toUpperCase(),
59
- receiver: receiver ?? null,
60
- receiverId: receiver?.id ?? null,
61
- entryType: row.entryType ?? 'message',
62
- content_type: row.content_type ?? row.contentType ?? 'text',
63
- contentType: row.content_type ?? row.contentType ?? 'text',
64
- task: row.task ?? null,
65
- agreement: row.agreement ?? null,
66
- operation: row.operation ?? null,
67
- agreementId: row.agreementId ?? null,
68
- timestamp: row.timestamp,
69
- text: row.text,
70
- };
71
- }
72
-
73
- async function chatIdsForScope(
74
- scope: InboxScopeEntry,
75
- creds: Creds,
76
- baseUrl: string | undefined,
77
- discovery: ContextDiscoveryClient,
78
- ): Promise<string[]> {
79
- const { kind, id } = scope.scope;
80
- switch (kind) {
81
- case 'chat':
82
- return [id];
83
- case 'agreement': {
84
- const links = (await getChatsForAgreement(id, creds)) as Array<{ chatId?: string }>;
85
- return [...new Set(links.map((l) => l.chatId).filter(Boolean) as string[])];
86
- }
87
- case 'org': {
88
- const reach = await discovery.discover();
89
- return [
90
- ...new Set(
91
- reach
92
- .filter((r) => r.scope.kind === 'chat')
93
- .map((r) => r.scope.id),
94
- ),
95
- ];
96
- }
97
- default:
98
- return [];
99
- }
100
- }
101
-
102
- async function deliverMessagesForScope(
103
- deps: InboxCatchUpDeps,
104
- scope: InboxScopeEntry,
105
- chatIds: string[],
106
- reader: ContextReadClient,
107
- countCap: number,
108
- ): Promise<number> {
109
- let delivered = 0;
110
- const label = deps.label ?? 'InboxCatchUp';
111
- const expected = scope.newMessages;
112
- let remaining = expected >= countCap ? Number.POSITIVE_INFINITY : expected;
113
-
114
- for (const chatId of chatIds) {
115
- if (remaining <= 0) break;
116
- let after = scope.since;
117
- let pages = 0;
118
- const maxPages = 20;
119
-
120
- while (remaining > 0 && pages < maxPages) {
121
- const limit = Math.min(READ_PAGE, remaining === Number.POSITIVE_INFINITY ? READ_PAGE : remaining);
122
- const page = await reader.read<WireRow>('messages', {
123
- via: `chat:${chatId}`,
124
- after,
125
- direction: 'forward',
126
- limit,
127
- });
128
-
129
- for (const row of page.items) {
130
- const messageId = String(row.messageId ?? '');
131
- if (deps.seen.has(messageId)) continue;
132
-
133
- const text = typeof row.text === 'string' ? row.text : '';
134
- if (!text.trim()) continue;
135
-
136
- const metadata = rowToMetadata(row, chatId);
137
- await deps.onMessage(text, metadata);
138
- delivered++;
139
- if (remaining !== Number.POSITIVE_INFINITY) remaining--;
140
- }
141
-
142
- if (!page.hasMore || !page.latestSequence) break;
143
- after = page.latestSequence;
144
- pages++;
145
- }
146
- }
147
-
148
- if (delivered < expected && expected >= countCap) {
149
- warn(label, `scope ${scope.scope.kind}:${scope.scope.id} may be incomplete (count cap)`);
150
- }
151
-
152
- return delivered;
153
- }
154
-
155
- /**
156
- * Pull-based recovery after reconnect (ZIG-434 / ZIG-446 / ZIG-454).
157
- * Inbox → read message deltas → deliver → ack. Push remains a hint;
158
- * this path survives backend restarts that drop in-memory pending queues.
159
- */
160
- export async function runInboxCatchUp(deps: InboxCatchUpDeps): Promise<InboxCatchUpResult> {
161
- const label = deps.label ?? 'InboxCatchUp';
162
- const inbox = new InboxClient(deps.creds.operatorKey, deps.creds.agentId, deps.baseUrl);
163
- const reader = new ContextReadClient(deps.creds.operatorKey, deps.creds.agentId, deps.baseUrl);
164
- const discovery = new ContextDiscoveryClient(deps.creds.operatorKey, deps.creds.agentId, deps.baseUrl);
165
-
166
- const envelope = await inbox.getInbox();
167
- const countCap = envelope.countCap;
168
-
169
- let messagesDelivered = 0;
170
- let resourceEvents = 0;
171
- const toAck: InboxAck[] = [];
172
-
173
- if (envelope.truncatedScopes > 0) {
174
- // Informational only — does NOT block acking the scopes actually
175
- // returned below. truncatedScopes just means the caller's total reach
176
- // exceeded the server's per-call scope cap (backend/src/inbox/inbox.service.ts,
177
- // INBOX_MAX_SCOPES); each entry in envelope.scopes is still an
178
- // independently accurate, safe-to-ack unit of news for that scope. Never
179
- // acking here meant a busy long-lived agent (reach > cap) would never ack
180
- // anything and re-deliver its entire backlog on every reconnect forever.
181
- warn(label, `inbox truncatedScopes=${envelope.truncatedScopes} (reach exceeds per-call cap — examined scopes are still acked normally)`);
182
- }
183
-
184
- for (const scope of envelope.scopes) {
185
- const chatIds = await chatIdsForScope(scope, deps.creds, deps.baseUrl, discovery);
186
- if (!chatIds.length && (scope.newMessages > 0 || scope.newArtifacts > 0)) {
187
- warn(
188
- label,
189
- `no chat ids for scope ${scope.scope.kind}:${scope.scope.id} (${scope.newMessages} msgs)`,
190
- );
191
- }
192
-
193
- if (scope.newMessages > 0 && chatIds.length) {
194
- const n = await deliverMessagesForScope(deps, scope, chatIds, reader, countCap);
195
- messagesDelivered += n;
196
- log(label, `scope ${scope.scope.kind}:${scope.scope.id} delivered ${n} message(s)`);
197
- }
198
-
199
- if (scope.newArtifacts > 0) {
200
- for (const chatId of chatIds) {
201
- await deps.onResourceEvent({
202
- kind: 'artifact',
203
- ts: scope.latestAt ?? new Date().toISOString(),
204
- resourceId: chatId,
205
- chatId,
206
- change: 'created',
207
- });
208
- resourceEvents++;
209
- }
210
- }
211
-
212
- if (scope.latestAt && (scope.newMessages > 0 || scope.newArtifacts > 0)) {
213
- toAck.push({
214
- kind: scope.scope.kind,
215
- id: scope.scope.id,
216
- upTo: scope.latestAt,
217
- });
218
- }
219
- }
220
-
221
- for (const proposal of envelope.proposalsAwaitingMe) {
222
- await deps.onResourceEvent({
223
- kind: 'agreement',
224
- ts: proposal.proposedAt ?? new Date().toISOString(),
225
- resourceId: proposal.agreementId,
226
- agreementId: proposal.agreementId,
227
- change: 'created',
228
- reason: 'proposal_pending',
229
- });
230
- resourceEvents++;
231
- }
232
-
233
- let acked = 0;
234
- if (toAck.length) {
235
- await inbox.ack(toAck);
236
- acked = toAck.length;
237
- log(label, `acked ${acked} scope(s)`);
238
- }
239
-
240
- if (messagesDelivered > 0 || resourceEvents > 0) {
241
- log(
242
- label,
243
- `catch-up complete: ${messagesDelivered} message(s), ${resourceEvents} resource hint(s)`,
244
- );
245
- }
246
-
247
- return {
248
- scopes: envelope.scopes.length,
249
- messagesDelivered,
250
- resourceEvents,
251
- acked,
252
- truncated: envelope.truncatedScopes > 0,
253
- };
254
- }
@@ -1,90 +0,0 @@
1
- import { runtimeLog } from '../shared/runtimeLog.js';
2
-
3
- interface PendingMessage {
4
- text: string;
5
- receiverId: string;
6
- timestamp: number;
7
- }
8
-
9
- export interface OutboxBufferOptions {
10
- ttlMs?: number;
11
- }
12
-
13
- interface HistoryEntryShape {
14
- entryType?: string;
15
- text?: string;
16
- sender?: { id?: string; type?: string };
17
- receiver?: { id?: string };
18
- timestamp?: number;
19
- }
20
-
21
- /**
22
- * Tracks outbound messages locally until server history syncs them, then
23
- * merges any unmatched ones into the next read context. Prevents the LLM
24
- * from re-saying things it already said when the server is slow to publish.
25
- */
26
- export class OutboxBuffer {
27
- private _pending: Map<string, PendingMessage[]> = new Map();
28
- private _ttlMs: number;
29
-
30
- constructor(options: OutboxBufferOptions = {}) {
31
- this._ttlMs = Number.isFinite(options.ttlMs) ? Math.max(1_000, options.ttlMs!) : 10 * 60 * 1000;
32
- }
33
-
34
- track(chatId: string, msg: { text: string; receiverId: string }): void {
35
- this._gcChat(chatId);
36
- if (!this._pending.has(chatId)) this._pending.set(chatId, []);
37
- this._pending.get(chatId)!.push({ text: msg.text, receiverId: msg.receiverId, timestamp: Date.now() });
38
- }
39
-
40
- merge(chatId: string, history: HistoryEntryShape[], agentId: string | null): void {
41
- this._gcChat(chatId);
42
- const pending = this._pending.get(chatId);
43
- if (!pending || pending.length === 0) return;
44
-
45
- const matchedIndices = new Set<number>();
46
- const remaining: PendingMessage[] = [];
47
-
48
- for (const msg of pending) {
49
- const matchIdx = history.findIndex((entry, idx) =>
50
- !matchedIndices.has(idx) &&
51
- entry.text === msg.text &&
52
- entry.sender?.id === agentId,
53
- );
54
- if (matchIdx !== -1) matchedIndices.add(matchIdx);
55
- else remaining.push(msg);
56
- }
57
-
58
- for (const msg of remaining) {
59
- history.push({
60
- entryType: 'message',
61
- text: msg.text,
62
- sender: { id: agentId || undefined, type: 'agent' },
63
- receiver: { id: msg.receiverId },
64
- timestamp: msg.timestamp,
65
- });
66
- }
67
-
68
- if (remaining.length === 0) this._pending.delete(chatId);
69
- else this._pending.set(chatId, remaining);
70
- }
71
-
72
- gc(): void {
73
- for (const chatId of this._pending.keys()) this._gcChat(chatId);
74
- }
75
-
76
- private _gcChat(chatId: string): void {
77
- const pending = this._pending.get(chatId);
78
- if (!pending?.length) return;
79
- const cutoff = Date.now() - this._ttlMs;
80
- const fresh = pending.filter(msg => msg.timestamp >= cutoff);
81
- if (fresh.length === 0) {
82
- this._pending.delete(chatId);
83
- runtimeLog.warn('OutboxBuffer', `expired all pending messages chatId=${chatId}`);
84
- return;
85
- }
86
- if (fresh.length !== pending.length) {
87
- this._pending.set(chatId, fresh);
88
- }
89
- }
90
- }
@@ -1,27 +0,0 @@
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
- private readonly order: string[] = [];
8
- private readonly seen = new Set<string>();
9
-
10
- constructor(private readonly maxSize = 5_000) {}
11
-
12
- has(messageId: string | null | undefined): boolean {
13
- if (!messageId || typeof messageId !== 'string') return false;
14
- return this.seen.has(messageId);
15
- }
16
-
17
- mark(messageId: string | null | undefined): void {
18
- if (!messageId || typeof messageId !== 'string') return;
19
- if (this.seen.has(messageId)) return;
20
- this.seen.add(messageId);
21
- this.order.push(messageId);
22
- while (this.order.length > this.maxSize) {
23
- const oldest = this.order.shift();
24
- if (oldest) this.seen.delete(oldest);
25
- }
26
- }
27
- }
@@ -1,85 +0,0 @@
1
- import { runtimeLog } from '../shared/runtimeLog.js';
2
-
3
- export interface StateTimeoutEvent {
4
- type: 'state-timeout';
5
- state: string;
6
- waitedMs: number;
7
- epoch: number;
8
- }
9
-
10
- interface ArmedTimer {
11
- epoch: number;
12
- state: string;
13
- timeoutMs: number;
14
- armedAt: number;
15
- handle: ReturnType<typeof setTimeout>;
16
- }
17
-
18
- /**
19
- * Per-lane liveness timers for parked states declaring timeoutMs. Owned by
20
- * AgentHost. A firing timer enqueues a state-timeout frame on the SAME lane
21
- * (caller-provided enqueue → EventQueue), so it serializes behind any real
22
- * event and can never race one. Epochs make stale frames identifiable: a
23
- * queued timeout frame whose epoch is not the lane's currently-armed epoch was
24
- * superseded (a tick ran after it fired) and must be dropped.
25
- *
26
- * In-memory only: process restart loses all timers; each lane re-arms on its
27
- * next real event (sessionStore persists { ctx, state } but not timers).
28
- */
29
- export class StateTimers {
30
- private _timers = new Map<string, ArmedTimer>();
31
- private _epoch = 0;
32
- private _enqueue: (event: StateTimeoutEvent, laneKey: string) => void;
33
-
34
- constructor(enqueue: (event: StateTimeoutEvent, laneKey: string) => void) {
35
- this._enqueue = enqueue;
36
- }
37
-
38
- /** Re-arm is idempotent: any previously armed timer for the lane is cancelled first. */
39
- arm(laneKey: string, state: string, timeoutMs: number): void {
40
- this.clear(laneKey);
41
- const epoch = ++this._epoch;
42
- const handle = setTimeout(() => {
43
- runtimeLog.debug('StateTimers', `fired laneKey=${laneKey} state=${state} waitedMs=${timeoutMs} epoch=${epoch}`);
44
- this._enqueue({ type: 'state-timeout', state, waitedMs: timeoutMs, epoch }, laneKey);
45
- }, timeoutMs);
46
- (handle as unknown as { unref?: () => void }).unref?.();
47
- this._timers.set(laneKey, { epoch, state, timeoutMs, armedAt: Date.now(), handle });
48
- }
49
-
50
- clear(laneKey: string): void {
51
- const t = this._timers.get(laneKey);
52
- if (!t) return;
53
- clearTimeout(t.handle);
54
- this._timers.delete(laneKey);
55
- }
56
-
57
- clearAll(): void {
58
- for (const key of [...this._timers.keys()]) this.clear(key);
59
- }
60
-
61
- /**
62
- * Drop stale state-timeout events from an incoming frame (single event or an
63
- * EventQueue-coalesced { type: 'batch', events } frame). Returns null when
64
- * nothing survives (pure stale-timeout frame — the lane's live timer, if any,
65
- * must be left untouched by the caller in that case).
66
- */
67
- stripStale(event: unknown, laneKey: string): unknown | null {
68
- const pending = this._timers.get(laneKey);
69
- const fresh = (ev: unknown): boolean => {
70
- const e = ev as { type?: string; epoch?: number };
71
- if (e?.type !== 'state-timeout') return true;
72
- const ok = pending !== undefined && e.epoch === pending.epoch;
73
- if (!ok) runtimeLog.debug('StateTimers', `dropping stale state-timeout laneKey=${laneKey} epoch=${e.epoch} armed=${pending?.epoch ?? 'none'}`);
74
- return ok;
75
- };
76
- const b = event as { type?: string; events?: unknown[] };
77
- if (b?.type === 'batch' && Array.isArray(b.events)) {
78
- const kept = b.events.filter(fresh);
79
- if (kept.length === 0) return null;
80
- if (kept.length === b.events.length) return event;
81
- return kept.length === 1 ? kept[0] : { type: 'batch', events: kept };
82
- }
83
- return fresh(event) ? event : null;
84
- }
85
- }