@ziggs-ai/agent-sdk 0.1.11 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -3
- package/src/AgentHost.ts +297 -28
- package/src/adapters/AnthropicAdapter.ts +42 -10
- package/src/adapters/McpAdapter.ts +350 -20
- package/src/adapters/OpenAIAdapter.ts +9 -2
- package/src/agent/Agent.ts +10 -2
- package/src/agent/ClaudeSdkAgent.ts +401 -0
- package/src/agent/claudeCognition.ts +139 -0
- package/src/agent/claudeSdkProtocol.ts +27 -0
- package/src/agent/claudeSdkTools.ts +81 -0
- package/src/agent/hostedAgentConnection.ts +41 -0
- package/src/agent/toolManagerToSdkMcp.ts +136 -0
- package/src/defineAgent.ts +9 -2
- package/src/index.ts +11 -5
- package/src/runtime/AgentMachine.ts +104 -22
- package/src/runtime/PromptBuilder.ts +42 -5
- package/src/runtime/bootstrapCtx.ts +39 -0
- package/src/runtime/defaults.ts +12 -1
- package/src/runtime/deriveRespondsToProposals.ts +35 -0
- package/src/runtime/runMechanical.ts +106 -0
- package/src/runtime/runTurn.ts +65 -8
- package/src/runtime/validateWorkflow.ts +33 -14
- package/src/server/InboxCatchUp.ts +37 -0
- package/src/server/ZiggsEffectHandler.ts +188 -179
- package/src/server/agreements/AgreementService.ts +3 -3
- package/src/server/tasks/TaskService.ts +2 -1
- package/src/server/tools/tier1/protocolRunner.ts +212 -3
- package/src/server/tools/tier1/protocolTools.ts +18 -3
- package/src/types.ts +49 -22
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Ziggs-flavored EffectHandler.
|
|
3
3
|
*
|
|
4
|
-
* Translates the Agent's pure Effect requests into concrete calls
|
|
5
|
-
*
|
|
6
|
-
* taskService
|
|
7
|
-
* is the only place where Agent intent meets
|
|
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.
|
|
8
9
|
*
|
|
9
10
|
* Lives in agent-sdk for now alongside `Agent`; destined for a separate
|
|
10
11
|
* `@ziggs-ai/agent-server` package once the SDK split is complete.
|
|
@@ -18,15 +19,14 @@ import { runtimeLog } from '../shared/runtimeLog.js';
|
|
|
18
19
|
/** Minimal LLM adapter contract — OpenAIAdapter satisfies this. */
|
|
19
20
|
interface LlmAdapter {
|
|
20
21
|
chatMessages(messages: LlmMessage[], tools?: LlmToolSchema[], options?: Record<string, unknown>): Promise<LlmResponse>;
|
|
21
|
-
chatMessagesStream?(messages: LlmMessage[], onChunk: (text: string) => void): Promise<string>;
|
|
22
|
+
chatMessagesStream?(messages: LlmMessage[], onChunk: (text: string) => void): Promise<{ text: string; usage?: LlmResponse['usage'] }>;
|
|
22
23
|
}
|
|
23
24
|
import type { TaskService } from './tasks/TaskService.js';
|
|
24
25
|
import type { AgreementService } from './agreements/AgreementService.js';
|
|
25
26
|
import type { ToolManager } from '../tools/ToolManager.js';
|
|
26
27
|
import type {
|
|
27
|
-
MessagesClient,
|
|
28
28
|
ArtifactsClient,
|
|
29
|
-
|
|
29
|
+
ContextReadClient,
|
|
30
30
|
TelemetryClient,
|
|
31
31
|
} from '@ziggs-ai/api-client';
|
|
32
32
|
import {
|
|
@@ -37,10 +37,15 @@ import {
|
|
|
37
37
|
} from './telemetryIngest.js';
|
|
38
38
|
|
|
39
39
|
export interface ZiggsEffectDeps {
|
|
40
|
-
|
|
41
|
-
|
|
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;
|
|
42
46
|
artifactsClient: ArtifactsClient;
|
|
43
|
-
|
|
47
|
+
/** Grant-fenced aggregate read backbone — sources the read-context snapshot. */
|
|
48
|
+
contextReadClient: ContextReadClient;
|
|
44
49
|
taskService: TaskService;
|
|
45
50
|
agreementService: AgreementService;
|
|
46
51
|
messageSender: (text: string, receiverId: string, sessionId: string, messageId?: string) => Promise<unknown>;
|
|
@@ -58,69 +63,47 @@ export interface ZiggsEffectDeps {
|
|
|
58
63
|
const CONTEXT_CACHE_MAX = 256;
|
|
59
64
|
|
|
60
65
|
/**
|
|
61
|
-
* Compose a `ContextSnapshot` for a chatId
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
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.
|
|
65
72
|
*/
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
};
|
|
75
|
-
};
|
|
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
|
+
});
|
|
76
81
|
|
|
77
82
|
async function buildSnapshot(
|
|
78
83
|
deps: ZiggsEffectDeps,
|
|
79
84
|
chatId: string,
|
|
80
85
|
agentId: string,
|
|
81
|
-
opts: {
|
|
86
|
+
opts: { limit?: number } = {},
|
|
82
87
|
): Promise<ContextSnapshot> {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
role: string;
|
|
103
|
-
principalType?: 'user' | 'agent';
|
|
104
|
-
}>;
|
|
105
|
-
|
|
106
|
-
const others = counterparties.filter((c) => c.id !== agentId);
|
|
107
|
-
const users = others
|
|
108
|
-
.filter((c) => c.principalType === 'user')
|
|
109
|
-
.map((c) => ({ userId: c.id, role: c.role }));
|
|
110
|
-
const agents = others
|
|
111
|
-
.filter((c) => c.principalType !== 'user') // 'agent' or missing → treat as agent
|
|
112
|
-
.map((c) => ({ agentId: c.id, role: c.role, isYou: false }));
|
|
113
|
-
agents.push({ agentId, role: 'self', isYou: true });
|
|
114
|
-
|
|
115
|
-
const agreements = (chatAgreements as Record<string, unknown>[]).map((ag) => annotateAgreement(ag, agentId));
|
|
116
|
-
|
|
117
|
-
return {
|
|
118
|
-
history: msgs.messages as Record<string, unknown>[],
|
|
119
|
-
agreements,
|
|
120
|
-
agents,
|
|
121
|
-
users,
|
|
122
|
-
latestSequence: msgs.latestSequence,
|
|
123
|
-
};
|
|
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
|
+
}
|
|
124
107
|
}
|
|
125
108
|
|
|
126
109
|
const OP_ARTIFACT_MAX_CHARS = 900;
|
|
@@ -178,6 +161,126 @@ async function emitToolOperationArtifact(
|
|
|
178
161
|
}
|
|
179
162
|
}
|
|
180
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
|
+
|
|
181
284
|
/**
|
|
182
285
|
* Build a single shared EffectHandler. The handler is stateful — it caches
|
|
183
286
|
* the latest ContextSnapshot per sessionId so tool-call effects can resolve
|
|
@@ -209,10 +312,10 @@ export function createZiggsEffectHandler(deps: ZiggsEffectDeps): EffectHandler {
|
|
|
209
312
|
const dispatch = async (effect: Effect): Promise<unknown> => {
|
|
210
313
|
switch (effect.kind) {
|
|
211
314
|
case 'read-context': {
|
|
212
|
-
//
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
//
|
|
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.
|
|
216
319
|
const snap = await buildSnapshot(deps, effect.sessionId, deps.agentId, {
|
|
217
320
|
limit: effect.opts?.maxMessages,
|
|
218
321
|
});
|
|
@@ -222,44 +325,9 @@ export function createZiggsEffectHandler(deps: ZiggsEffectDeps): EffectHandler {
|
|
|
222
325
|
return snap;
|
|
223
326
|
}
|
|
224
327
|
|
|
225
|
-
case 'list-messages': {
|
|
226
|
-
return deps.messagesClient.list(effect.chatId, {
|
|
227
|
-
after: effect.after,
|
|
228
|
-
limit: effect.limit,
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
case 'list-artifacts': {
|
|
233
|
-
return deps.artifactsClient.list(
|
|
234
|
-
{ chatId: effect.chatId, agreementId: effect.agreementId },
|
|
235
|
-
{ after: effect.after, limit: effect.limit },
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
case 'list-task-history': {
|
|
240
|
-
const url = new URL(
|
|
241
|
-
`${process.env.ZIGGS_BACKEND_URL ?? 'http://localhost:3000'}/tasks/${effect.taskId}/history`,
|
|
242
|
-
);
|
|
243
|
-
if (effect.after) url.searchParams.set('after', effect.after);
|
|
244
|
-
if (effect.limit != null) url.searchParams.set('limit', String(effect.limit));
|
|
245
|
-
const res = await fetch(url.toString(), {
|
|
246
|
-
headers: {
|
|
247
|
-
'content-type': 'application/json',
|
|
248
|
-
Authorization: `Bearer ${deps.operatorKey}`,
|
|
249
|
-
'X-Agent-Id': deps.agentId,
|
|
250
|
-
},
|
|
251
|
-
});
|
|
252
|
-
if (!res.ok) {
|
|
253
|
-
throw new Error(`list-task-history ${res.status} ${res.statusText}`);
|
|
254
|
-
}
|
|
255
|
-
return res.json();
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
case 'get-scope': {
|
|
259
|
-
return deps.scopeClient.get(effect.via.kind, effect.via.id);
|
|
260
|
-
}
|
|
261
328
|
|
|
262
329
|
case 'llm-call': {
|
|
330
|
+
if (!deps.llm) throw new Error('llm-call effect requires an LLM adapter — none configured for this cognition');
|
|
263
331
|
const t0 = Date.now();
|
|
264
332
|
const r: LlmResponse = await deps.llm.chatMessages(effect.messages, effect.tools);
|
|
265
333
|
sendTelemetry(
|
|
@@ -276,10 +344,12 @@ export function createZiggsEffectHandler(deps: ZiggsEffectDeps): EffectHandler {
|
|
|
276
344
|
}
|
|
277
345
|
|
|
278
346
|
case 'stream-text': {
|
|
347
|
+
if (!deps.llm) throw new Error('stream-text effect requires an LLM adapter — none configured for this cognition');
|
|
279
348
|
const { sessionId, receiverId, messageId, messages } = effect;
|
|
280
349
|
let text = '';
|
|
350
|
+
let usage: LlmResponse['usage'];
|
|
281
351
|
if (deps.llm.chatMessagesStream) {
|
|
282
|
-
|
|
352
|
+
const streamed = await deps.llm.chatMessagesStream(messages as LlmMessage[], (chunk) => {
|
|
283
353
|
// Live-stream display is best-effort. The transport's send rejects
|
|
284
354
|
// empty/whitespace-only content (WebSocketClient: content.trim() === ''),
|
|
285
355
|
// and a whitespace-only chunk (e.g. a leading newline) is truthy yet
|
|
@@ -291,88 +361,27 @@ export function createZiggsEffectHandler(deps: ZiggsEffectDeps): EffectHandler {
|
|
|
291
361
|
if (chunk && chunk.trim()) deps.chunkSender?.(sessionId, receiverId, chunk, messageId);
|
|
292
362
|
} catch { /* ignore — streaming display only */ }
|
|
293
363
|
});
|
|
364
|
+
text = streamed.text;
|
|
365
|
+
usage = streamed.usage;
|
|
294
366
|
} else {
|
|
295
367
|
const r = await deps.llm.chatMessages(messages as LlmMessage[]);
|
|
296
368
|
text = r.content ?? '';
|
|
369
|
+
usage = r.usage;
|
|
297
370
|
if (text) deps.chunkSender?.(sessionId, receiverId, text, messageId);
|
|
298
371
|
}
|
|
299
|
-
return { text };
|
|
372
|
+
return { text, usage };
|
|
300
373
|
}
|
|
301
374
|
|
|
302
375
|
case 'tool-call': {
|
|
303
|
-
|
|
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).
|
|
304
380
|
const cached = cacheGet(effect.sessionId);
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
const argsPreview = previewToolArgs(args);
|
|
309
|
-
await emitToolOperationArtifact(deps.artifactsClient, effect.sessionId, 'started', {
|
|
310
|
-
state: 'started',
|
|
311
|
-
tool: effect.name,
|
|
312
|
-
...(argsPreview ? { args: argsPreview } : {}),
|
|
381
|
+
return executeToolWithSideEffects(deps, handler as EffectHandler, effect, {
|
|
382
|
+
agents: (cached?.agents as unknown[]) || [],
|
|
383
|
+
users: (cached?.users as unknown[]) || [],
|
|
313
384
|
});
|
|
314
|
-
try {
|
|
315
|
-
const lastUser = (users as Array<{ userId?: string; id?: string }>).find(
|
|
316
|
-
(u) => u.userId || u.id,
|
|
317
|
-
);
|
|
318
|
-
const result = await deps.toolManager.executeTool(
|
|
319
|
-
effect.name,
|
|
320
|
-
args,
|
|
321
|
-
{
|
|
322
|
-
operatorKey: deps.operatorKey,
|
|
323
|
-
agentId: deps.agentId,
|
|
324
|
-
chatId: effect.sessionId,
|
|
325
|
-
taskService: deps.taskService,
|
|
326
|
-
agreementService: deps.agreementService,
|
|
327
|
-
sendMessage: deps.messageSender,
|
|
328
|
-
agents,
|
|
329
|
-
users,
|
|
330
|
-
senderId: effect.senderId ?? lastUser?.userId ?? lastUser?.id,
|
|
331
|
-
senderType: effect.senderType ?? (lastUser ? 'USER' : undefined),
|
|
332
|
-
memory: effect.memory,
|
|
333
|
-
turnScratch: {},
|
|
334
|
-
},
|
|
335
|
-
);
|
|
336
|
-
await emitToolOperationArtifact(deps.artifactsClient, effect.sessionId, 'completed', {
|
|
337
|
-
state: 'completed',
|
|
338
|
-
tool: effect.name,
|
|
339
|
-
result: clipForOperationArtifact(result),
|
|
340
|
-
});
|
|
341
|
-
sendTelemetry(
|
|
342
|
-
deps.telemetryClient,
|
|
343
|
-
buildToolIngestPayload({
|
|
344
|
-
sessionId: effect.sessionId,
|
|
345
|
-
tool: effect.name,
|
|
346
|
-
durationMs: Date.now() - toolT0,
|
|
347
|
-
ok: true,
|
|
348
|
-
runId: effect.runId,
|
|
349
|
-
stateId: effect.stateId,
|
|
350
|
-
}),
|
|
351
|
-
isTelemetryIngestEnabled(deps.telemetryIngest),
|
|
352
|
-
);
|
|
353
|
-
return { ok: true, result } satisfies ToolCallResult;
|
|
354
|
-
} catch (error: unknown) {
|
|
355
|
-
const errMsg = (error as Error)?.message ?? String(error);
|
|
356
|
-
runtimeLog.warn('ZiggsEffectHandler', `tool-call "${effect.name}" failed: ${errMsg}`);
|
|
357
|
-
await emitToolOperationArtifact(deps.artifactsClient, effect.sessionId, 'error', {
|
|
358
|
-
state: 'failed',
|
|
359
|
-
tool: effect.name,
|
|
360
|
-
error: errMsg,
|
|
361
|
-
});
|
|
362
|
-
sendTelemetry(
|
|
363
|
-
deps.telemetryClient,
|
|
364
|
-
buildToolIngestPayload({
|
|
365
|
-
sessionId: effect.sessionId,
|
|
366
|
-
tool: effect.name,
|
|
367
|
-
durationMs: Date.now() - toolT0,
|
|
368
|
-
ok: false,
|
|
369
|
-
runId: effect.runId,
|
|
370
|
-
stateId: effect.stateId,
|
|
371
|
-
}),
|
|
372
|
-
isTelemetryIngestEnabled(deps.telemetryIngest),
|
|
373
|
-
);
|
|
374
|
-
return { ok: false, error: errMsg } satisfies ToolCallResult;
|
|
375
|
-
}
|
|
376
385
|
}
|
|
377
386
|
|
|
378
387
|
case 'send-message': {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
proposeAgreement, proposeBroadcast as proposeBroadcastApi, delegateAgreement, respondToAgreement, counterAgreement,
|
|
3
3
|
getAgreementStatus, listAgreements, getMyAgreements, getAgreement,
|
|
4
|
-
publishOffer as publishOfferApi, createAgreement, revokeAgreement,
|
|
4
|
+
publishOffer as publishOfferApi, createAgreement, revokeAgreement, fulfillAgreement, linkAgreementToChat,
|
|
5
5
|
getChatsForAgreement, joinAgreement, linkArtifactToAgreement, getArtifactsForAgreement,
|
|
6
6
|
linkUserToAgreement, getUsersForAgreement,
|
|
7
7
|
type Creds, type Agreement,
|
|
@@ -84,8 +84,8 @@ export class AgreementService {
|
|
|
84
84
|
return revokeAgreement(agreementId, this.creds);
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
return
|
|
87
|
+
fulfill(agreementId: string): Promise<{ ok: boolean; agreement: Agreement }> {
|
|
88
|
+
return fulfillAgreement(agreementId, this.creds);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
linkToChat(agreementId: string, chatId: string, linkType: ChatLinkType = 'mention'): Promise<unknown | null> {
|
|
@@ -57,7 +57,8 @@ export class TaskService {
|
|
|
57
57
|
runtimeLog.info('TaskService', `Task spawned under agreement ${agreementId}: ${result?.taskId || 'unknown'}`);
|
|
58
58
|
return result;
|
|
59
59
|
} catch (error) {
|
|
60
|
-
|
|
60
|
+
const e = error as { message?: string; status?: number; body?: string };
|
|
61
|
+
runtimeLog.error('TaskService', `Task spawn failed (agreement ${agreementId}, assignee ${taskData.assigneeId ?? '-'}): status=${e.status ?? '?'} ${e.message}${e.body ? ` | body=${e.body}` : ''}`);
|
|
61
62
|
throw error;
|
|
62
63
|
}
|
|
63
64
|
}
|