@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,464 @@
1
+ import { isProtocolToolName, mapProtocolToolToOperation } from '../tasks/protocolRegistry.js';
2
+ import { getBatchEvents, isTaskResultRelevantToAgent } from '../context/batch.js';
3
+ // ── outcomeFromEvent: incoming wire event → Outcome ──────────────────────────
4
+ /**
5
+ * Converts a raw incoming event into the single Outcome that re-enters the
6
+ * machine.
7
+ *
8
+ * For batched events, returns the *most significant* outcome (task assignments
9
+ * win over messages). EventQueue already flattened multiple distinct events
10
+ * via re-entry, so a batch here usually means events that arrived together
11
+ * within a single backend frame.
12
+ */
13
+ export function outcomeFromEvent(rawEvent, ownAgentId) {
14
+ if (!rawEvent)
15
+ return { kind: 'enter' };
16
+ const events = getBatchEvents(rawEvent);
17
+ let best = null;
18
+ for (const ev of events) {
19
+ if (!ev)
20
+ continue;
21
+ const candidate = singleEventToOutcome(ev, ownAgentId);
22
+ if (!candidate)
23
+ continue;
24
+ if (!best || outcomeRank(candidate) > outcomeRank(best))
25
+ best = candidate;
26
+ }
27
+ return best ?? { kind: 'enter' };
28
+ }
29
+ function singleEventToOutcome(ev, ownAgentId) {
30
+ if (ev.type === 'agreement_lifecycle') {
31
+ const operation = ev.operation;
32
+ const agreementId = ev.agreementId ?? '';
33
+ const action = operation === 'proposal_rejected'
34
+ ? 'reject'
35
+ : operation === 'proposal_approved' || operation === 'proposal_approved_execute'
36
+ ? 'approve'
37
+ : null;
38
+ if (action && agreementId) {
39
+ return {
40
+ kind: 'proposal-resolved',
41
+ action,
42
+ proposal: { agreementId },
43
+ proposedByMe: false, // real value stamped in AgentMachine._handleFrame from ctx (needs pendingProposalAgreementId, unavailable here)
44
+ };
45
+ }
46
+ return null;
47
+ }
48
+ if (ev.type === 'task_result') {
49
+ const result = ev.result || {};
50
+ if (ownAgentId &&
51
+ !isTaskResultRelevantToAgent(result, ownAgentId) &&
52
+ ev.receiverId !== ownAgentId) {
53
+ return null;
54
+ }
55
+ // Agreement lifecycle notification routed as task_result by normalizeIncomingEvent.
56
+ // _operation is set to the backend operation name (e.g. 'proposal_approved').
57
+ const inboundOp = result._operation;
58
+ if (inboundOp) {
59
+ const proposalAction = inboundOp === 'proposal_approved' ? 'approve'
60
+ : inboundOp === 'proposal_rejected' ? 'reject'
61
+ : null;
62
+ if (proposalAction) {
63
+ const agId = result.agreementId;
64
+ const proposal = agId
65
+ ? { agreementId: agId, agreement: result }
66
+ : { agreementId: '' };
67
+ return { kind: 'proposal-resolved', action: proposalAction, proposal, proposedByMe: false }; // real value stamped in AgentMachine._handleFrame from ctx
68
+ }
69
+ // Other lifecycle ops (countered, expired) treated as enter — no FSM action needed here.
70
+ return null;
71
+ }
72
+ // Tasks are born active in the new model — no `proposal` task state.
73
+ // The wire shape `task_result` always carries an active or terminal task.
74
+ const task = result;
75
+ const ag = result.agreement;
76
+ const parties = ag?.parties || {};
77
+ const state = result.state;
78
+ // Direct assignment to me (provider) — task spawned, citing an active agreement.
79
+ const isAssignedToMe = (result.providerIsYou || parties.provider === ownAgentId) &&
80
+ (state === 'active' || state === 'in-progress');
81
+ if (isAssignedToMe) {
82
+ return { kind: 'task-assigned', task };
83
+ }
84
+ if (state === 'active' || state === 'in-progress') {
85
+ return { kind: 'task-assigned', task };
86
+ }
87
+ if (state === 'completed' || state === 'failed') {
88
+ return {
89
+ kind: 'subtask-finished',
90
+ status: state === 'completed' ? 'completed' : 'failed',
91
+ task,
92
+ result,
93
+ };
94
+ }
95
+ if (state === 'cancelled') {
96
+ return {
97
+ kind: 'subtask-finished',
98
+ status: 'failed',
99
+ task,
100
+ result,
101
+ };
102
+ }
103
+ // Fallback: addressed to us with no clear state.
104
+ if (ownAgentId && ev.receiverId === ownAgentId) {
105
+ return { kind: 'task-assigned', task };
106
+ }
107
+ return { kind: 'subtask-finished', status: 'completed', task, result };
108
+ }
109
+ if (ev.type === 'resource_changed') {
110
+ const resource = (ev.resource ?? ev);
111
+ const reason = resource.reason;
112
+ if (resource.kind === 'agreement' &&
113
+ (reason === 'proposal_approved' || reason === 'proposal_rejected')) {
114
+ const agreementId = resource.agreementId ?? resource.resourceId ?? '';
115
+ const proposal = { agreementId };
116
+ return {
117
+ kind: 'proposal-resolved',
118
+ action: reason === 'proposal_approved' ? 'approve' : 'reject',
119
+ proposal,
120
+ proposedByMe: false, // real value stamped in AgentMachine._handleFrame from ctx
121
+ };
122
+ }
123
+ if (resource.kind === 'task-state' && reason === 'subtask_completed') {
124
+ const taskId = resource.taskId ?? resource.resourceId ?? '';
125
+ const task = { taskId, state: 'completed' };
126
+ return { kind: 'subtask-finished', status: 'completed', task, result: task };
127
+ }
128
+ return null;
129
+ }
130
+ if (ev.type === 'state-timeout') {
131
+ // Synthetic frame from AgentHost's StateTimers — host-constructed, fields always present.
132
+ return { kind: 'state-timeout', state: ev.state, waitedMs: ev.waitedMs };
133
+ }
134
+ // Only real inbound chat messages should drive the FSM. Backend bookkeeping
135
+ // (presence/heartbeat, raw `batch` outer frames, etc.) are NOT chat traffic.
136
+ if (ev.type !== 'message')
137
+ return null;
138
+ // Suppress our own messages echoed back through the chat fanout.
139
+ if (ownAgentId && ev.senderId && ev.senderId === ownAgentId)
140
+ return null;
141
+ const text = typeof ev.text === 'string' ? ev.text : undefined;
142
+ const senderType = typeof ev.senderType === 'string' ? ev.senderType : undefined;
143
+ return { kind: 'message-received', text, senderId: ev.senderId, senderType };
144
+ }
145
+ // Higher rank wins when collapsing batched events into one outcome.
146
+ function outcomeRank(o) {
147
+ switch (o.kind) {
148
+ case 'task-assigned': return 100;
149
+ case 'proposal-resolved': return 90;
150
+ case 'subtask-finished': return 80;
151
+ case 'message-received': return 10;
152
+ // Any real event coalesced into the same batch supersedes the timer —
153
+ // waking to "nothing happened" is moot when something did. Must stay below
154
+ // message-received (10).
155
+ case 'state-timeout': return 1;
156
+ default: return 50;
157
+ }
158
+ }
159
+ // ── outcomeFromActionResult: turn output → Outcome ───────────────────────────
160
+ /**
161
+ * Builds the single Outcome that ends a thinking-state turn from the
162
+ * action that ran and the events it emitted.
163
+ *
164
+ * Action's `produces` declares the outcome kind — runtime fills in payload.
165
+ * - static string: kind is fixed; runtime constructs payload from events.
166
+ * - function: receives the tool result and args, returns the kind.
167
+ *
168
+ * If `produces` is missing or doesn't match the events, falls back to a
169
+ * best-effort inference.
170
+ */
171
+ export function outcomeFromActionResult(actionName, action, args, emittedEvents, toolResultsOut) {
172
+ if (!emittedEvents.length) {
173
+ return { kind: 'error', source: 'unknown', cause: `action "${actionName}" emitted nothing`, retryable: false };
174
+ }
175
+ // Collect tool results in caller's accumulator (used in prompts and ctx).
176
+ for (const e of emittedEvents) {
177
+ if (e?.type === 'tool_result')
178
+ toolResultsOut.push({ tool: e.tool || '', result: e.result });
179
+ if (e?.type === 'tool_error')
180
+ toolResultsOut.push({ tool: e.tool || '', error: e.error });
181
+ }
182
+ // Pick the "primary" event the outcome describes.
183
+ //
184
+ // Protocol-level state changes (agreement creation, task assignment/closure,
185
+ // proposal response) outrank EVERYTHING else, including message_sent and
186
+ // waited. Why: when the LLM batches `delegate` + `wait` in one round, the
187
+ // sub-agreement DOES get created server-side, but if `waited` wins the FSM
188
+ // sees `wait` and bounces back to `idle` instead of transitioning to
189
+ // `awaitingSubAgreementApproval` — and the chain stalls. The meaningful
190
+ // turn outcome is the work that landed, not the LLM's terminal decision.
191
+ //
192
+ // Among non-protocol events: message_sent / waited still outrank pure
193
+ // query tool_results (e.g. agent_network search) and tool_errors, because
194
+ // those are the LLM's explicit final intent in turns that did no protocol
195
+ // work.
196
+ const primary = emittedEvents.find(e => e.type === 'task_result' && isStateChangingProtocolEvent(e)) ??
197
+ emittedEvents.find(e => e.type === 'tool_result' && isStateChangingProtocolEvent(e)) ??
198
+ emittedEvents.find(e => e.type === 'message_sent') ??
199
+ emittedEvents.find(e => e.type === 'message_duplicate_skipped') ??
200
+ emittedEvents.find(e => e.type === 'waited') ??
201
+ emittedEvents.find(e => e.type === 'tool_result') ??
202
+ emittedEvents.find(e => e.type === 'tool_error') ??
203
+ emittedEvents.find(e => e.type === 'task_result') ??
204
+ emittedEvents.find(e => e.type === 'task_error') ??
205
+ emittedEvents[0];
206
+ // Resolve the kind via produces (preferred) or by inferring from primary.
207
+ let kind = null;
208
+ if (action?.produces) {
209
+ const p = action.produces;
210
+ const result = primary?.type === 'tool_result' ? primary.result : undefined;
211
+ kind = typeof p === 'function' ? p(result, args) : p;
212
+ }
213
+ if (!kind)
214
+ kind = inferKindFromEvent(primary);
215
+ return buildOutcomeOfKind(kind, primary, action);
216
+ }
217
+ /**
218
+ * Whether a tool_result / task_result represents a protocol state change
219
+ * (agreement creation, task assignment / closure, proposal response). Used
220
+ * to outrank pure query tool_results (e.g. `agent_network search`) when the
221
+ * same turn batches multiple tools.
222
+ */
223
+ function isStateChangingProtocolEvent(ev) {
224
+ const op = ev.type === 'task_result'
225
+ ? ev.operation
226
+ : (ev.tool && isProtocolToolName(ev.tool) ? mapProtocolToolToOperation(ev.tool) : null);
227
+ if (!op)
228
+ return false;
229
+ const r = ev.result ?? {};
230
+ if (r.success === false || r.error)
231
+ return false;
232
+ const ag = r.agreement ?? (r.agreementId ? r : null);
233
+ const task = r.task || (r.taskId ? r : null);
234
+ const proposalStatus = ag?.proposal?.status;
235
+ const state = task?.state || r.state;
236
+ if (op === 'agreement-propose' || op === 'agreement-subcontract') {
237
+ return proposalStatus === 'pending' || proposalStatus === 'approved' || !!ag?.agreementId;
238
+ }
239
+ if (op === 'agreement-respond')
240
+ return true;
241
+ if (op === 'task-update') {
242
+ return state === 'completed' || state === 'failed' || state === 'cancelled';
243
+ }
244
+ if (op === 'task-spawn')
245
+ return !!task?.taskId;
246
+ return false;
247
+ }
248
+ function inferKindFromEvent(primary) {
249
+ if (!primary)
250
+ return 'error';
251
+ switch (primary.type) {
252
+ case 'message_sent': return 'message-sent';
253
+ case 'message_duplicate_skipped': return 'message-sent';
254
+ case 'waited': return 'wait';
255
+ case 'tool_error': return 'error';
256
+ case 'task_error': return 'error';
257
+ case 'tool_result': return inferKindFromToolResult(primary);
258
+ case 'task_result': return inferKindFromTaskResult(primary);
259
+ default: return 'tool-result';
260
+ }
261
+ }
262
+ function inferKindFromToolResult(ev) {
263
+ if (!isProtocolToolName(ev.tool ?? ''))
264
+ return 'tool-result';
265
+ const op = mapProtocolToolToOperation(ev.tool ?? '');
266
+ return inferKindFromProtocolOp(op ?? undefined, ev.result);
267
+ }
268
+ function inferKindFromTaskResult(ev) {
269
+ return inferKindFromProtocolOp(ev.operation, ev.result);
270
+ }
271
+ function inferKindFromProtocolOp(op, result) {
272
+ const r = result || {};
273
+ const ag = r.agreement ?? (r.agreementId ? r : undefined);
274
+ const task = r.task || (r.taskId ? r : undefined);
275
+ // Agreement creation verbs no longer create paired tasks. The caller (creator)
276
+ // has made a proposal awaiting response from the proposedTo party.
277
+ if (op === 'agreement-propose' ||
278
+ op === 'agreement-subcontract') {
279
+ return 'proposal-made';
280
+ }
281
+ if (op === 'task-spawn') {
282
+ // A task was just spawned under an active agreement. From the spawner's
283
+ // side this is a delegation; the executor sees `task-assigned` via the
284
+ // task_result event channel.
285
+ return 'task-delegated';
286
+ }
287
+ if (op === 'task-update') {
288
+ const status = task?.state || r.state || r.status;
289
+ if (status === 'completed' || status === 'failed')
290
+ return 'task-closed';
291
+ return 'tool-result';
292
+ }
293
+ if (op === 'agreement-respond') {
294
+ // The responder's tool result is the agreement after approve/reject.
295
+ return 'proposal-resolved';
296
+ }
297
+ void ag;
298
+ return 'tool-result';
299
+ }
300
+ function buildOutcomeOfKind(kind, primary, action) {
301
+ const tool = action?.tool || primary?.tool || '';
302
+ const result = primary?.type === 'tool_result' ? primary.result : primary?.result;
303
+ switch (kind) {
304
+ case 'enter':
305
+ case 'wait':
306
+ return { kind };
307
+ case 'state-timeout':
308
+ // Runtime-generated only (validateWorkflow rejects produces:'state-timeout').
309
+ // Break-fast backstop if an action somehow declares it.
310
+ throw new Error(`action produced reserved outcome kind "state-timeout" — runtime-generated only`);
311
+ case 'message-sent':
312
+ return { kind, text: primary?.message, receiverId: primary?.receiverId };
313
+ case 'message-received':
314
+ return { kind, text: primary?.text };
315
+ case 'task-assigned': {
316
+ const task = (result?.task || result || {});
317
+ return { kind, task };
318
+ }
319
+ case 'task-delegated': {
320
+ const task = (result?.task || result || {});
321
+ return { kind, task };
322
+ }
323
+ case 'task-closed': {
324
+ const raw = result?.task?.state || result?.state;
325
+ const status = raw === 'failed' ? 'failed' : 'completed';
326
+ return { kind, status, result };
327
+ }
328
+ case 'subtask-finished': {
329
+ const task = (result?.task || result || {});
330
+ const status = (task.state === 'failed' ? 'failed' : 'completed');
331
+ return { kind, status, task, result };
332
+ }
333
+ case 'proposal-made': {
334
+ const ag = result?.agreement ?? (result?.agreementId ? result : undefined);
335
+ const proposal = ag
336
+ ? { agreementId: ag.agreementId, agreement: ag }
337
+ : { agreementId: result?.agreementId || '' };
338
+ return { kind, proposal };
339
+ }
340
+ case 'proposal-resolved': {
341
+ const ag = result?.agreement ?? (result?.agreementId ? result : undefined);
342
+ const status = ag?.proposal?.status;
343
+ const action_ = status === 'rejected' ? 'reject' : 'approve';
344
+ const proposal = ag
345
+ ? { agreementId: ag.agreementId, agreement: ag }
346
+ : { agreementId: result?.agreementId || '' };
347
+ return { kind, action: action_, proposal, proposedByMe: false }; // turn path = our own agreement_respond ran — we are the responder, by definition not the proposer
348
+ }
349
+ case 'tool-result':
350
+ return { kind, tool, result };
351
+ case 'error': {
352
+ const cause = primary?.error || primary?.result || 'unknown error';
353
+ const source = primary?.type === 'tool_error' || primary?.type === 'tool_result' ? 'tool' : 'unknown';
354
+ return { kind, source, cause, retryable: false };
355
+ }
356
+ case 'extension':
357
+ return { kind, name: tool || 'unknown', payload: result };
358
+ }
359
+ }
360
+ // ── applyOutcomeToCtx: persistent ctx updates from a new outcome ─────────────
361
+ /**
362
+ * Mutates ctx to absorb persistent state from a new outcome:
363
+ * - active agreement/task ids on assignment, cleared on close
364
+ * - pending proposal id on proposal-made, cleared on resolution
365
+ * - delegated task ids accumulated on delegation, used to match later subtask events
366
+ */
367
+ export function applyOutcomeToCtx(ctx, outcome) {
368
+ ctx.lastOutcome = outcome;
369
+ switch (outcome.kind) {
370
+ case 'task-assigned': {
371
+ const t = outcome.task || {};
372
+ const tid = t.taskId || t.id;
373
+ const agId = t.agreementId || t.agreement?.agreementId || ctx.pendingProposalAgreementId;
374
+ if (tid)
375
+ ctx.activeTaskId = tid;
376
+ if (agId)
377
+ ctx.activeAgreementId = agId;
378
+ ctx.pendingProposalAgreementId = null;
379
+ return;
380
+ }
381
+ case 'task-delegated': {
382
+ const t = outcome.task || {};
383
+ const tid = t.taskId;
384
+ const agId = t.agreementId || t.agreement?.agreementId;
385
+ if (tid && !(tid in ctx.pendingSubtasks)) {
386
+ ctx.pendingSubtasks[tid] = {
387
+ agentId: t.assigneeId ?? t.executorId ?? t.agreement?.parties?.provider ?? null,
388
+ spawnedAt: Date.now(),
389
+ };
390
+ }
391
+ if (agId && !ctx.delegatedAgreementIds.includes(agId))
392
+ ctx.delegatedAgreementIds.push(agId);
393
+ return;
394
+ }
395
+ case 'proposal-made': {
396
+ const agId = outcome.proposal?.agreementId;
397
+ if (agId)
398
+ ctx.pendingProposalAgreementId = agId;
399
+ return;
400
+ }
401
+ case 'proposal-resolved': {
402
+ // Approval of the agent's PRIMARY proposal (the one binding it to its
403
+ // counterparty — e.g. Ziggs ↔ user) promotes the pending id into
404
+ // activeAgreementId so transitions gated on activeAgreementId fire.
405
+ // We only do this when no agreement is currently active, so that
406
+ // approvals of CHILD sub-agreements (e.g. coffee-agent approving the
407
+ // Ziggs-issued subcontract) don't overwrite the root the primary agent
408
+ // is bound to. Sub-agreement bookkeeping lives in delegatedAgreementIds
409
+ // (recorded on task-delegated) — not here.
410
+ if (outcome.action === 'approve' && !ctx.activeAgreementId) {
411
+ const agId = outcome.proposal?.agreementId || ctx.pendingProposalAgreementId;
412
+ if (agId)
413
+ ctx.activeAgreementId = agId;
414
+ }
415
+ ctx.pendingProposalAgreementId = null;
416
+ return;
417
+ }
418
+ case 'subtask-finished': {
419
+ const t = outcome.task;
420
+ const taskId = t?.taskId;
421
+ const agId = t?.agreementId || t?.agreement?.agreementId;
422
+ if (taskId) {
423
+ const pending = ctx.pendingSubtasks[taskId];
424
+ delete ctx.pendingSubtasks[taskId];
425
+ const payload = t?.result ?? null;
426
+ const existing = ctx.collectedResults.find(r => r.taskId === taskId);
427
+ if (!existing) {
428
+ ctx.collectedResults.push({
429
+ taskId,
430
+ agentId: pending?.agentId ?? t?.assigneeId ?? t?.executorId ?? t?.agreement?.parties?.provider ?? null,
431
+ status: outcome.status,
432
+ result: payload,
433
+ });
434
+ }
435
+ else if (existing.result == null && payload != null) {
436
+ // A bare resource_changed(subtask_completed) skeleton ({taskId, state})
437
+ // can arrive BEFORE the full task_result for the same completion.
438
+ // Upgrade the placeholder in place instead of dropping the real payload.
439
+ // The skeleton often lacks executor identity, so also fill agentId
440
+ // from the fuller frame when the placeholder recorded none.
441
+ existing.status = outcome.status;
442
+ existing.result = payload;
443
+ if (existing.agentId == null) {
444
+ existing.agentId = t?.assigneeId ?? t?.executorId ?? t?.agreement?.parties?.provider ?? null;
445
+ }
446
+ }
447
+ }
448
+ if (agId)
449
+ ctx.delegatedAgreementIds = ctx.delegatedAgreementIds.filter(id => id !== agId);
450
+ return;
451
+ }
452
+ case 'task-closed': {
453
+ ctx.activeAgreementId = null;
454
+ ctx.activeTaskId = null;
455
+ ctx.pendingProposalAgreementId = null;
456
+ ctx.pendingSubtasks = {};
457
+ ctx.collectedResults = [];
458
+ ctx.delegatedAgreementIds = [];
459
+ return;
460
+ }
461
+ default:
462
+ return;
463
+ }
464
+ }
@@ -0,0 +1,54 @@
1
+ import type { Action, Ctx, MechanicalState, Outcome, Produces, Transition, ThinkingState } from '../types.js';
2
+ /**
3
+ * Defaults that workflow authors spread into thinking states explicitly.
4
+ * No auto-injection — what you read in the file is what runs.
5
+ *
6
+ * const dflt = thinkingDefaults({ initial: 'idle' });
7
+ *
8
+ * listening: {
9
+ * kind: 'thinking',
10
+ * ...dflt,
11
+ * prompt: { ... },
12
+ * actions: { ...dflt.actions, greetUser: { ... } },
13
+ * transitions: [
14
+ * { to: 'executing', when: o => o.kind === 'task-assigned' },
15
+ * ...dflt.transitions,
16
+ * ],
17
+ * }
18
+ */
19
+ export interface ThinkingDefaults {
20
+ actions: Record<string, Action>;
21
+ transitions: Transition[];
22
+ }
23
+ export declare const DEFAULT_WAIT_PROMPT: Readonly<{
24
+ instruction: "Do nothing - wait for the next event.";
25
+ when: "No action is needed, you are waiting for external input, or you should pause without repeating yourself.";
26
+ format: "{\"thought\": \"...\", \"action\": \"wait\"}";
27
+ }>;
28
+ export declare function thinkingDefaults(opts: {
29
+ initial: string;
30
+ }): ThinkingDefaults;
31
+ /**
32
+ * Convenience builder for a `thinking` state that always carries the defaults.
33
+ * Equivalent to spreading manually but the spread points are in one place.
34
+ *
35
+ * defineThinkingState({ initial: 'idle' }, {
36
+ * prompt: { ... },
37
+ * actions: { greetUser: { ... } },
38
+ * transitions: [ { to: 'executing', when: o => o.kind === 'task-assigned' } ],
39
+ * })
40
+ */
41
+ export declare function defineThinkingState(opts: {
42
+ initial: string;
43
+ }, state: Omit<ThinkingState, 'kind' | 'actions' | 'transitions'> & {
44
+ actions: Record<string, Action>;
45
+ transitions: Transition[];
46
+ }): ThinkingState;
47
+ /** ZIG-764: mechanical state — one tool call, zero LLM turns. */
48
+ export declare function defineMechanicalState(state: {
49
+ tool: string;
50
+ args?: Record<string, unknown>;
51
+ buildArgs?: (ctx: Ctx, outcome: Outcome) => Record<string, unknown>;
52
+ produces: Produces;
53
+ transitions: Transition[];
54
+ }): MechanicalState;
@@ -0,0 +1,47 @@
1
+ export const DEFAULT_WAIT_PROMPT = Object.freeze({
2
+ instruction: 'Do nothing - wait for the next event.',
3
+ when: 'No action is needed, you are waiting for external input, or you should pause without repeating yourself.',
4
+ format: '{"thought": "...", "action": "wait"}',
5
+ });
6
+ export function thinkingDefaults(opts) {
7
+ const { initial } = opts;
8
+ return {
9
+ actions: {
10
+ wait: {
11
+ prompt: { ...DEFAULT_WAIT_PROMPT },
12
+ produces: 'wait',
13
+ },
14
+ },
15
+ transitions: [
16
+ { to: initial, when: o => o.kind === 'wait' },
17
+ { to: initial, when: o => o.kind === 'message-sent' },
18
+ // Errors (empty/failed turns) fall back to the initial state instead of
19
+ // silently re-entering and recursing until the depth guard (ZIG-303).
20
+ { to: initial, when: o => o.kind === 'error' },
21
+ ],
22
+ };
23
+ }
24
+ /**
25
+ * Convenience builder for a `thinking` state that always carries the defaults.
26
+ * Equivalent to spreading manually but the spread points are in one place.
27
+ *
28
+ * defineThinkingState({ initial: 'idle' }, {
29
+ * prompt: { ... },
30
+ * actions: { greetUser: { ... } },
31
+ * transitions: [ { to: 'executing', when: o => o.kind === 'task-assigned' } ],
32
+ * })
33
+ */
34
+ export function defineThinkingState(opts, state) {
35
+ const dflt = thinkingDefaults(opts);
36
+ return {
37
+ kind: 'thinking',
38
+ prompt: state.prompt,
39
+ allowNoWait: state.allowNoWait,
40
+ actions: { ...dflt.actions, ...state.actions },
41
+ transitions: [...state.transitions, ...dflt.transitions],
42
+ };
43
+ }
44
+ /** ZIG-764: mechanical state — one tool call, zero LLM turns. */
45
+ export function defineMechanicalState(state) {
46
+ return { kind: 'mechanical', ...state };
47
+ }
@@ -0,0 +1,9 @@
1
+ import type { State, Workflow } from '../types.js';
2
+ /**
3
+ * ZIG-826: true when the workflow exposes `agreement_respond` on a thinking
4
+ * action — the agent can approve/reject inbound proposals directed at it.
5
+ * Kickoff-only states (proposer-side approval → task_spawn) do not count.
6
+ */
7
+ export declare function deriveRespondsToProposals(workflow: Workflow): boolean;
8
+ /** Scan custom states (pre-defineAgent) for agreement_respond actions. */
9
+ export declare function statesRespondToProposals(states: Record<string, State> | undefined): boolean;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * ZIG-826: true when the workflow exposes `agreement_respond` on a thinking
3
+ * action — the agent can approve/reject inbound proposals directed at it.
4
+ * Kickoff-only states (proposer-side approval → task_spawn) do not count.
5
+ */
6
+ export function deriveRespondsToProposals(workflow) {
7
+ if (!workflow?.states)
8
+ return false;
9
+ for (const state of Object.values(workflow.states)) {
10
+ if (state.kind !== 'thinking')
11
+ continue;
12
+ const actions = state.actions;
13
+ if (!actions)
14
+ continue;
15
+ for (const action of Object.values(actions)) {
16
+ if (action?.tool === 'agreement_respond')
17
+ return true;
18
+ }
19
+ }
20
+ return false;
21
+ }
22
+ /** Scan custom states (pre-defineAgent) for agreement_respond actions. */
23
+ export function statesRespondToProposals(states) {
24
+ if (!states)
25
+ return false;
26
+ for (const state of Object.values(states)) {
27
+ if (state.kind !== 'thinking')
28
+ continue;
29
+ const actions = state.actions;
30
+ if (!actions)
31
+ continue;
32
+ for (const action of Object.values(actions)) {
33
+ if (action?.tool === 'agreement_respond')
34
+ return true;
35
+ }
36
+ }
37
+ return false;
38
+ }
@@ -0,0 +1,15 @@
1
+ import type { ThinkingState, Workflow } from '../types.js';
2
+ /** Reserved state name for the injected agreement-kickoff default. */
3
+ export declare const KICKOFF_STATE = "kickingOff";
4
+ export declare function buildKickoffState(initial: string): ThinkingState;
5
+ /**
6
+ * Agreement-kickoff default. For every state whose transitions do not already
7
+ * match a proposer-side approval (probe above), append a transition to the
8
+ * injected `kickingOff` state. Appended LAST so any user transition on the same
9
+ * outcome always wins (first match wins in AgentMachine._evaluateTransitions).
10
+ * Returns a NEW workflow — input states are not mutated. After kickoff the
11
+ * machine returns to `initial`; when this agent is itself the provider the
12
+ * backend then assigns the spawned task back to it as a normal task-assigned
13
+ * event, which the author's existing work loop handles.
14
+ */
15
+ export declare function injectKickoffDefault(workflow: Workflow): Workflow;