@ziggs-ai/agent-sdk 0.1.12 → 0.2.1

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 +214 -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 +53 -0
  124. package/dist/server/ziggscontext/ZiggsContextClient.js +124 -0
  125. package/dist/server/ziggspay/ZiggsPayClient.d.ts +82 -0
  126. package/dist/server/ziggspay/ZiggsPayClient.js +203 -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 +14 -6
  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
@@ -1,427 +0,0 @@
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 {
14
- Effect, EffectHandler,
15
- ContextSnapshot, LlmMessage, LlmResponse, LlmToolSchema, ToolCallResult,
16
- } from '../types.js';
17
- import { OutboxBuffer } from './OutboxBuffer.js';
18
- import { runtimeLog } from '../shared/runtimeLog.js';
19
- /** Minimal LLM adapter contract — OpenAIAdapter satisfies this. */
20
- interface LlmAdapter {
21
- chatMessages(messages: LlmMessage[], tools?: LlmToolSchema[], options?: Record<string, unknown>): Promise<LlmResponse>;
22
- chatMessagesStream?(messages: LlmMessage[], onChunk: (text: string) => void): Promise<{ text: string; usage?: LlmResponse['usage'] }>;
23
- }
24
- import type { TaskService } from './tasks/TaskService.js';
25
- import type { AgreementService } from './agreements/AgreementService.js';
26
- import type { ToolManager } from '../tools/ToolManager.js';
27
- import type {
28
- ArtifactsClient,
29
- ContextReadClient,
30
- TelemetryClient,
31
- } from '@ziggs-ai/api-client';
32
- import {
33
- buildLlmIngestPayload,
34
- buildToolIngestPayload,
35
- isTelemetryIngestEnabled,
36
- sendTelemetry,
37
- } from './telemetryIngest.js';
38
-
39
- export interface ZiggsEffectDeps {
40
- /**
41
- * LLM adapter for `llm-call` / `stream-text`. Optional: the Claude brain
42
- * drives its own model via `query()` and emits neither effect, so it wires
43
- * no adapter. Those two cases throw if reached without one.
44
- */
45
- llm?: LlmAdapter;
46
- artifactsClient: ArtifactsClient;
47
- /** Grant-fenced aggregate read backbone — sources the read-context snapshot. */
48
- contextReadClient: ContextReadClient;
49
- taskService: TaskService;
50
- agreementService: AgreementService;
51
- messageSender: (text: string, receiverId: string, sessionId: string, messageId?: string) => Promise<unknown>;
52
- /** Sends a streaming chunk to a client. Called per-token during stream-text effects. */
53
- chunkSender?: (sessionId: string, receiverId: string, text: string, messageId: string) => void;
54
- toolManager: ToolManager;
55
- operatorKey: string;
56
- agentId: string;
57
- telemetryClient?: TelemetryClient;
58
- /** When false, skips POST /agents/monitoring/ingest (default: env on). */
59
- telemetryIngest?: boolean;
60
- effectTap?: (effect: Effect, response: unknown) => void;
61
- }
62
-
63
- const CONTEXT_CACHE_MAX = 256;
64
-
65
- /**
66
- * Compose a `ContextSnapshot` for a chatId via the grant-fenced aggregate read
67
- * (`GET /context/snapshot`, ZIG-775 follow-up). The backend assembles history +
68
- * agreements + roster through the same `assertScopeAccess` + temporal fence as
69
- * the per-type context reads, and annotates `*IsYou` / self server-side against
70
- * the acting agent. This function only preserves the never-throw/degrade
71
- * contract that the cognition code depends on — the shape is unchanged.
72
- */
73
- const emptySnapshot = (agentId: string, chatMissing = false): ContextSnapshot => ({
74
- history: [],
75
- agreements: [],
76
- users: [],
77
- agents: [{ agentId, role: 'self', isYou: true }],
78
- latestSequence: null,
79
- ...(chatMissing ? { chatMissing: true } : {}),
80
- });
81
-
82
- async function buildSnapshot(
83
- deps: ZiggsEffectDeps,
84
- chatId: string,
85
- agentId: string,
86
- opts: { limit?: number } = {},
87
- ): Promise<ContextSnapshot> {
88
- try {
89
- // Server returns 200 + chatMissing:true for a deleted chat, so its
90
- // annotations (roster, *IsYou, self) are already applied — pass through.
91
- const snap = await deps.contextReadClient.snapshot(chatId, {
92
- maxMessages: opts.limit,
93
- });
94
- return snap as ContextSnapshot;
95
- } catch (err: unknown) {
96
- // Defensive: server normally signals a dead chat as 200 + chatMissing, but
97
- // a raw 404 degrades the same way so message wakes still skip it.
98
- if ((err as { status?: number })?.status === 404) {
99
- return emptySnapshot(agentId, true);
100
- }
101
- runtimeLog.warn(
102
- 'ZiggsEffectHandler',
103
- `snapshot fetch failed: ${(err as Error)?.message ?? err}`,
104
- );
105
- return emptySnapshot(agentId);
106
- }
107
- }
108
-
109
- const OP_ARTIFACT_MAX_CHARS = 900;
110
-
111
- function clipForOperationArtifact(value: unknown): unknown {
112
- if (value == null) return value;
113
- if (typeof value === 'string') {
114
- return value.length > OP_ARTIFACT_MAX_CHARS ? `${value.slice(0, OP_ARTIFACT_MAX_CHARS - 1)}…` : value;
115
- }
116
- try {
117
- const s = JSON.stringify(value);
118
- if (s.length <= OP_ARTIFACT_MAX_CHARS) return value;
119
- return `${s.slice(0, OP_ARTIFACT_MAX_CHARS - 1)}…`;
120
- } catch {
121
- return String(value).slice(0, OP_ARTIFACT_MAX_CHARS);
122
- }
123
- }
124
-
125
- function previewToolArgs(args: Record<string, unknown>): Record<string, unknown> | undefined {
126
- const keys = Object.keys(args);
127
- if (keys.length === 0) return undefined;
128
- const out: Record<string, unknown> = {};
129
- for (let i = 0; i < keys.length && i < 16; i++) {
130
- const k = keys[i];
131
- out[k] = clipForOperationArtifact(args[k]);
132
- }
133
- return out;
134
- }
135
-
136
- /**
137
- * Chat timeline: `Message.tsx` parses `entryType: artifact`, `content_type: operation` with
138
- * `operation_*:{json}` where json includes `state` and `tool`.
139
- */
140
- async function emitToolOperationArtifact(
141
- client: ArtifactsClient,
142
- chatId: string,
143
- phase: 'started' | 'completed' | 'error',
144
- payload: Record<string, unknown>,
145
- ): Promise<void> {
146
- const prefix =
147
- phase === 'started' ? 'operation_started' : phase === 'completed' ? 'operation_completed' : 'operation_error';
148
- const text = `${prefix}:${JSON.stringify(payload)}`;
149
- try {
150
- await client.write({
151
- chatId,
152
- text,
153
- content_type: 'operation',
154
- visibility: 'agent-private',
155
- });
156
- } catch (e: unknown) {
157
- runtimeLog.warn(
158
- 'ZiggsEffectHandler',
159
- `tool operation artifact (${phase}): ${(e as Error)?.message ?? String(e)}`,
160
- );
161
- }
162
- }
163
-
164
- /**
165
- * The single tool-execution path for BOTH cognitions (ZIG-775).
166
- *
167
- * Executing a tool is more than calling its handler: it emits the operation
168
- * timeline artifacts (started / completed / error) and tool telemetry, and
169
- * resolves the invoking user + roster. The FSM reaches this via
170
- * `eff({ kind: 'tool-call' })` inside `runTurn`; the Claude Agent SDK bridge
171
- * (`toolManagerToSdkMcp`) emits the SAME effect — so a hosted Claude-brain
172
- * agent's tool activity is as observable as an FSM agent's (needed for
173
- * hired-agent visibility, ZIG-535). A caller cannot run a tool and skip these
174
- * side effects — that is the whole point of funneling both paths here.
175
- *
176
- * `eff` is threaded into the tool context so eff-backed tools
177
- * (`ziggs_context_snapshot`, `ziggs_send_message`) resolve through this same path
178
- * regardless of which cognition invoked them.
179
- */
180
- export async function executeToolWithSideEffects(
181
- deps: Pick<
182
- ZiggsEffectDeps,
183
- | 'toolManager'
184
- | 'artifactsClient'
185
- | 'telemetryClient'
186
- | 'telemetryIngest'
187
- | 'operatorKey'
188
- | 'agentId'
189
- | 'taskService'
190
- | 'agreementService'
191
- | 'contextReadClient'
192
- | 'messageSender'
193
- >,
194
- eff: EffectHandler,
195
- effect: Extract<Effect, { kind: 'tool-call' }>,
196
- roster: { agents: unknown[]; users: unknown[] },
197
- ): Promise<ToolCallResult> {
198
- const toolT0 = Date.now();
199
- const args = (effect.args as Record<string, unknown>) || {};
200
- const argsPreview = previewToolArgs(args);
201
- await emitToolOperationArtifact(deps.artifactsClient, effect.sessionId, 'started', {
202
- state: 'started',
203
- tool: effect.name,
204
- ...(argsPreview ? { args: argsPreview } : {}),
205
- });
206
- try {
207
- const lastUser = (roster.users as Array<{ userId?: string; id?: string }>).find(
208
- (u) => u.userId || u.id,
209
- );
210
- // Accumulate token usage from any nested LLM call the tool makes (llmTool),
211
- // so it can be folded into the turn's authoritative token count (ZIG-813).
212
- let nestedUsage: { prompt_tokens: number; completion_tokens: number; total_tokens: number } | undefined;
213
- const reportUsage = (u: LlmResponse['usage']): void => {
214
- if (!u) return;
215
- const p = u.prompt_tokens ?? u.promptTokens ?? 0;
216
- const c = u.completion_tokens ?? u.completionTokens ?? 0;
217
- const t = u.total_tokens ?? u.totalTokens ?? p + c;
218
- nestedUsage = {
219
- prompt_tokens: (nestedUsage?.prompt_tokens ?? 0) + p,
220
- completion_tokens: (nestedUsage?.completion_tokens ?? 0) + c,
221
- total_tokens: (nestedUsage?.total_tokens ?? 0) + t,
222
- };
223
- };
224
- const result = await deps.toolManager.executeTool(effect.name, args, {
225
- operatorKey: deps.operatorKey,
226
- agentId: deps.agentId,
227
- chatId: effect.sessionId,
228
- taskService: deps.taskService,
229
- agreementService: deps.agreementService,
230
- contextReadClient: deps.contextReadClient,
231
- sendMessage: deps.messageSender,
232
- eff,
233
- agents: roster.agents,
234
- users: roster.users,
235
- senderId: effect.senderId ?? lastUser?.userId ?? lastUser?.id,
236
- senderType: effect.senderType ?? (lastUser ? 'USER' : undefined),
237
- onBehalfOfAgreementId: effect.onBehalfOfAgreementId,
238
- memory: effect.memory,
239
- reportUsage,
240
- turnScratch: {},
241
- });
242
- await emitToolOperationArtifact(deps.artifactsClient, effect.sessionId, 'completed', {
243
- state: 'completed',
244
- tool: effect.name,
245
- result: clipForOperationArtifact(result),
246
- });
247
- sendTelemetry(
248
- deps.telemetryClient,
249
- buildToolIngestPayload({
250
- sessionId: effect.sessionId,
251
- tool: effect.name,
252
- durationMs: Date.now() - toolT0,
253
- ok: true,
254
- runId: effect.runId,
255
- stateId: effect.stateId,
256
- }),
257
- isTelemetryIngestEnabled(deps.telemetryIngest),
258
- );
259
- return { ok: true, result, ...(nestedUsage ? { usage: nestedUsage } : {}) } satisfies ToolCallResult;
260
- } catch (error: unknown) {
261
- const errMsg = (error as Error)?.message ?? String(error);
262
- runtimeLog.warn('ZiggsEffectHandler', `tool-call "${effect.name}" failed: ${errMsg}`);
263
- await emitToolOperationArtifact(deps.artifactsClient, effect.sessionId, 'error', {
264
- state: 'failed',
265
- tool: effect.name,
266
- error: errMsg,
267
- });
268
- sendTelemetry(
269
- deps.telemetryClient,
270
- buildToolIngestPayload({
271
- sessionId: effect.sessionId,
272
- tool: effect.name,
273
- durationMs: Date.now() - toolT0,
274
- ok: false,
275
- runId: effect.runId,
276
- stateId: effect.stateId,
277
- }),
278
- isTelemetryIngestEnabled(deps.telemetryIngest),
279
- );
280
- return { ok: false, error: errMsg } satisfies ToolCallResult;
281
- }
282
- }
283
-
284
- /**
285
- * Build a single shared EffectHandler. The handler is stateful — it caches
286
- * the latest ContextSnapshot per sessionId so tool-call effects can resolve
287
- * session-coupled deps (the `agents` roster) without forcing the Agent to
288
- * pass them through. Cache is bounded with simple LRU eviction (re-set on
289
- * read promotes the entry; oldest entry drops when size exceeds the cap).
290
- */
291
- export function createZiggsEffectHandler(deps: ZiggsEffectDeps): EffectHandler {
292
- const contextCache = new Map<string, ContextSnapshot>();
293
- const outbox = new OutboxBuffer();
294
-
295
- const cachePut = (sessionId: string, snap: ContextSnapshot): void => {
296
- if (contextCache.has(sessionId)) contextCache.delete(sessionId);
297
- contextCache.set(sessionId, snap);
298
- if (contextCache.size > CONTEXT_CACHE_MAX) {
299
- const oldest = contextCache.keys().next().value;
300
- if (oldest !== undefined) contextCache.delete(oldest);
301
- }
302
- };
303
- const cacheGet = (sessionId: string): ContextSnapshot | undefined => {
304
- const snap = contextCache.get(sessionId);
305
- if (snap !== undefined) {
306
- contextCache.delete(sessionId);
307
- contextCache.set(sessionId, snap);
308
- }
309
- return snap;
310
- };
311
-
312
- const dispatch = async (effect: Effect): Promise<unknown> => {
313
- switch (effect.kind) {
314
- case 'read-context': {
315
- // Grant-fenced aggregate read: the backend composes the ContextSnapshot
316
- // (GET /context/snapshot) through the same access + temporal fence as
317
- // the per-type reads. Every producer — ClaudeSdkAgent.tick, runTurn,
318
- // and the on-demand ziggs_context_snapshot tool — routes through here.
319
- const snap = await buildSnapshot(deps, effect.sessionId, deps.agentId, {
320
- limit: effect.opts?.maxMessages,
321
- });
322
- const me = (snap.agents as Array<{ isYou?: boolean; agentId?: string }>).find((a) => a?.isYou)?.agentId || null;
323
- outbox.merge(effect.sessionId, snap.history as Record<string, unknown>[], me);
324
- cachePut(effect.sessionId, snap);
325
- return snap;
326
- }
327
-
328
-
329
- case 'llm-call': {
330
- if (!deps.llm) throw new Error('llm-call effect requires an LLM adapter — none configured for this cognition');
331
- const t0 = Date.now();
332
- const r: LlmResponse = await deps.llm.chatMessages(effect.messages, effect.tools);
333
- sendTelemetry(
334
- deps.telemetryClient,
335
- buildLlmIngestPayload(r, {
336
- sessionId: effect.sessionId,
337
- durationMs: Date.now() - t0,
338
- runId: effect.runId,
339
- stateId: effect.stateId,
340
- }),
341
- isTelemetryIngestEnabled(deps.telemetryIngest),
342
- );
343
- return r;
344
- }
345
-
346
- case 'stream-text': {
347
- if (!deps.llm) throw new Error('stream-text effect requires an LLM adapter — none configured for this cognition');
348
- const { sessionId, receiverId, messageId, messages } = effect;
349
- let text = '';
350
- let usage: LlmResponse['usage'];
351
- if (deps.llm.chatMessagesStream) {
352
- const streamed = await deps.llm.chatMessagesStream(messages as LlmMessage[], (chunk) => {
353
- // Live-stream display is best-effort. The transport's send rejects
354
- // empty/whitespace-only content (WebSocketClient: content.trim() === ''),
355
- // and a whitespace-only chunk (e.g. a leading newline) is truthy yet
356
- // still throws — which, uncaught, aborts the whole stream and loses the
357
- // entire reply (e.g. a composer's final synthesis). Swallow chunk-send
358
- // failures; `text` accumulates independently and the final send-message
359
- // (below, in runTurn) carries the full reply regardless.
360
- try {
361
- if (chunk && chunk.trim()) deps.chunkSender?.(sessionId, receiverId, chunk, messageId);
362
- } catch { /* ignore — streaming display only */ }
363
- });
364
- text = streamed.text;
365
- usage = streamed.usage;
366
- } else {
367
- const r = await deps.llm.chatMessages(messages as LlmMessage[]);
368
- text = r.content ?? '';
369
- usage = r.usage;
370
- if (text) deps.chunkSender?.(sessionId, receiverId, text, messageId);
371
- }
372
- return { text, usage };
373
- }
374
-
375
- case 'tool-call': {
376
- // Roster comes from the cached snapshot (populated by read-context). Both
377
- // cognitions land here — the FSM via runTurn's executeTool, the Claude
378
- // Agent SDK via the toolManagerToSdkMcp bridge — so tool side effects are
379
- // emitted exactly once, in one place (ZIG-775).
380
- const cached = cacheGet(effect.sessionId);
381
- return executeToolWithSideEffects(deps, handler as EffectHandler, effect, {
382
- agents: (cached?.agents as unknown[]) || [],
383
- users: (cached?.users as unknown[]) || [],
384
- });
385
- }
386
-
387
- case 'send-message': {
388
- await deps.messageSender(effect.text, effect.receiverId, effect.sessionId, effect.messageId);
389
- outbox.track(effect.sessionId, { text: effect.text, receiverId: effect.receiverId });
390
- return { ok: true };
391
- }
392
-
393
- case 'record-event': {
394
- // Routes through ArtifactsClient as an agent-private artifact —
395
- // visible to this agent across turns, not to other chat parties.
396
- try {
397
- if (effect.entry.kind === 'thought' && typeof effect.entry.text === 'string') {
398
- await deps.artifactsClient.recordThought(effect.sessionId, effect.entry.text);
399
- } else {
400
- await deps.artifactsClient.write({
401
- chatId: effect.sessionId,
402
- text: JSON.stringify(effect.entry),
403
- content_type: String(effect.entry.kind ?? 'event'),
404
- visibility: 'agent-private',
405
- });
406
- }
407
- } catch (err: unknown) {
408
- runtimeLog.warn('ZiggsEffectHandler', `record-event failed: ${(err as Error)?.message ?? String(err)}`);
409
- }
410
- return { ok: true };
411
- }
412
-
413
- default: {
414
- const exhaustive: never = effect;
415
- throw new Error(`ZiggsEffectHandler: unknown effect kind ${(exhaustive as Record<string, unknown>).kind}`);
416
- }
417
- }
418
- };
419
-
420
- const handler = async (effect: Effect): Promise<unknown> => {
421
- const response = await dispatch(effect);
422
- deps.effectTap?.(effect, response);
423
- return response;
424
- };
425
-
426
- return handler as EffectHandler;
427
- }
@@ -1,118 +0,0 @@
1
- import {
2
- proposeAgreement, proposeBroadcast as proposeBroadcastApi, delegateAgreement, respondToAgreement, counterAgreement,
3
- getAgreementStatus, listAgreements, getMyAgreements, getAgreement,
4
- publishOffer as publishOfferApi, createAgreement, revokeAgreement, fulfillAgreement, linkAgreementToChat,
5
- getChatsForAgreement, joinAgreement, linkArtifactToAgreement, getArtifactsForAgreement,
6
- linkUserToAgreement, getUsersForAgreement,
7
- type Creds, type Agreement,
8
- type ProposeAgreementData, type ProposeDirectInput, type ProposeBroadcastInput,
9
- type PublishOfferPayload,
10
- type DelegateAgreementData, type CounterAgreementData,
11
- type CreateAgreementBody, type ListAgreementsFilters, type GetMyAgreementsFilters,
12
- type ChatLinkType, type ArtifactLinkType, type UserRole,
13
- } from '@ziggs-ai/api-client';
14
-
15
- export class AgreementService {
16
- private creds: Creds;
17
-
18
- constructor(operatorKey: string, agentId: string) {
19
- if (!operatorKey) throw new Error('AgreementService: operatorKey is required');
20
- if (!agentId) throw new Error('AgreementService: agentId is required (operator-token impersonation)');
21
- this.creds = { operatorKey, agentId };
22
- }
23
-
24
- /** 1:1 proposal. Server defaults engagementKind to `service` when omitted. */
25
- proposeDirect(payload: ProposeDirectInput): Promise<Agreement> {
26
- return proposeAgreement(payload, this.creds);
27
- }
28
-
29
- /**
30
- * Open buyer-broadcast. `payload.audience` selects who may see/claim it:
31
- * 'everyone' (default, fully public) or 'org' (members of your active org).
32
- * Server defaults engagementKind to `service`.
33
- */
34
- proposeBroadcast(payload: ProposeBroadcastInput): Promise<Agreement> {
35
- return proposeBroadcastApi(payload, this.creds);
36
- }
37
-
38
- /** @deprecated Alias for {@link proposeDirect}. */
39
- propose(payload: ProposeAgreementData): Promise<Agreement> {
40
- return proposeAgreement(payload, this.creds);
41
- }
42
-
43
- /**
44
- * Seller-broadcast marketplace offer. `payload.audience` sets the `payer`
45
- * sentinel: 'everyone' (default, public) or 'org' (your active org only).
46
- */
47
- publishOffer(payload: PublishOfferPayload): Promise<Agreement> {
48
- return publishOfferApi(payload, this.creds);
49
- }
50
-
51
- delegate(payload: DelegateAgreementData): Promise<Agreement> {
52
- return delegateAgreement(payload, this.creds);
53
- }
54
-
55
- respond(agreementId: string, action: 'approve' | 'reject'): Promise<Agreement> {
56
- return respondToAgreement(agreementId, action, this.creds);
57
- }
58
-
59
- counter(agreementId: string, counter: CounterAgreementData): Promise<Agreement> {
60
- return counterAgreement(agreementId, counter || {}, this.creds);
61
- }
62
-
63
- getStatus(agreementId: string): Promise<unknown | null> {
64
- return getAgreementStatus(agreementId, this.creds);
65
- }
66
-
67
- list(filters: ListAgreementsFilters = {}): Promise<Agreement[]> {
68
- return listAgreements(filters, this.creds);
69
- }
70
-
71
- mine(filters: GetMyAgreementsFilters = {}): Promise<Agreement[]> {
72
- return getMyAgreements(filters, this.creds);
73
- }
74
-
75
- get(agreementId: string): Promise<Agreement | null> {
76
- return getAgreement(agreementId, this.creds);
77
- }
78
-
79
- create(body: CreateAgreementBody): Promise<{ ok: boolean; agreement: Agreement }> {
80
- return createAgreement(body, this.creds);
81
- }
82
-
83
- revoke(agreementId: string): Promise<{ ok: boolean; agreement: Agreement }> {
84
- return revokeAgreement(agreementId, this.creds);
85
- }
86
-
87
- fulfill(agreementId: string): Promise<{ ok: boolean; agreement: Agreement }> {
88
- return fulfillAgreement(agreementId, this.creds);
89
- }
90
-
91
- linkToChat(agreementId: string, chatId: string, linkType: ChatLinkType = 'mention'): Promise<unknown | null> {
92
- return linkAgreementToChat(agreementId, chatId, linkType, this.creds);
93
- }
94
-
95
- getChats(agreementId: string): Promise<unknown[]> {
96
- return getChatsForAgreement(agreementId, this.creds);
97
- }
98
-
99
- join(agreementId: string): Promise<{ chatId: string; agentId: string | null; isNew: boolean }> {
100
- return joinAgreement(agreementId, this.creds);
101
- }
102
-
103
- linkArtifact(agreementId: string, artifactId: string, linkType: ArtifactLinkType = 'produced'): Promise<unknown | null> {
104
- return linkArtifactToAgreement(agreementId, artifactId, linkType, this.creds);
105
- }
106
-
107
- getArtifacts(agreementId: string): Promise<unknown[]> {
108
- return getArtifactsForAgreement(agreementId, this.creds);
109
- }
110
-
111
- linkUser(agreementId: string, userId: string, role: UserRole): Promise<unknown | null> {
112
- return linkUserToAgreement(agreementId, userId, role, this.creds);
113
- }
114
-
115
- getUsers(agreementId: string): Promise<unknown[]> {
116
- return getUsersForAgreement(agreementId, this.creds);
117
- }
118
- }
@@ -1,85 +0,0 @@
1
- import { createServer, type Server } from 'http';
2
- import type { ConnectionPool } from './ConnectionPool.js';
3
- import type { AgentHost } from '../AgentHost.js';
4
- import { runtimeLog } from '../shared/runtimeLog.js';
5
-
6
- function readJsonBody(req: import('http').IncomingMessage): Promise<Record<string, unknown>> {
7
- return new Promise((resolve, reject) => {
8
- const chunks: Buffer[] = [];
9
- req.on('data', (c) => chunks.push(c));
10
- req.on('end', () => {
11
- try {
12
- const raw = Buffer.concat(chunks).toString('utf8');
13
- resolve(raw ? (JSON.parse(raw) as Record<string, unknown>) : {});
14
- } catch (err) {
15
- reject(err);
16
- }
17
- });
18
- req.on('error', reject);
19
- });
20
- }
21
-
22
- export function createHealthServer({
23
- port = Number(process.env.PORT) || 8080,
24
- label = 'agents',
25
- pool,
26
- }: {
27
- port?: number;
28
- label?: string;
29
- pool?: ConnectionPool;
30
- } = {}): Server {
31
- const server = createServer(async (req, res) => {
32
- const url = req.url ?? '/';
33
- const wakeMatch = url.match(/^\/fleet-wake\/([^/?]+)/);
34
- if (req.method === 'POST' && wakeMatch && pool) {
35
- const agentId = decodeURIComponent(wakeMatch[1]);
36
- try {
37
- await pool.wake(agentId);
38
- res.writeHead(200, { 'Content-Type': 'application/json' });
39
- res.end(JSON.stringify({ ok: true, agentId, active: pool.listActive().includes(agentId) }));
40
- } catch (err) {
41
- res.writeHead(500, { 'Content-Type': 'application/json' });
42
- res.end(JSON.stringify({ ok: false, error: (err as Error).message }));
43
- }
44
- return;
45
- }
46
-
47
- if (req.method === 'POST' && url === '/fleet-deliver' && pool) {
48
- try {
49
- const body = await readJsonBody(req);
50
- const agentId = String(body.agentId ?? '');
51
- const chatId = String(body.chatId ?? '');
52
- const text = String(body.text ?? '');
53
- const senderId = String(body.senderId ?? '');
54
- if (!agentId || !chatId || !text || !senderId) {
55
- res.writeHead(400, { 'Content-Type': 'application/json' });
56
- res.end(JSON.stringify({ ok: false, error: 'agentId, chatId, text, senderId required' }));
57
- return;
58
- }
59
- const host = (await pool.wake(agentId)) as AgentHost;
60
- await host.handleMessage(text, {
61
- chatId,
62
- entryType: 'message',
63
- content_type: 'text',
64
- sender: { id: senderId, type: 'USER' },
65
- receiver: { id: agentId, type: 'AGENT' },
66
- });
67
- res.writeHead(200, { 'Content-Type': 'application/json' });
68
- res.end(JSON.stringify({ ok: true, agentId }));
69
- } catch (err) {
70
- res.writeHead(500, { 'Content-Type': 'application/json' });
71
- res.end(JSON.stringify({ ok: false, error: (err as Error).message }));
72
- }
73
- return;
74
- }
75
- if (url === '/health' || url === '/') {
76
- res.writeHead(200);
77
- res.end('ok');
78
- return;
79
- }
80
- res.writeHead(404);
81
- res.end();
82
- });
83
- server.listen(port, () => runtimeLog.info(label, `health server on port ${port}`));
84
- return server;
85
- }