@ziggs-ai/agent-sdk 0.15.0 → 0.17.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.
- package/CHANGELOG.md +10 -0
- package/README.md +110 -88
- package/dist/{AgentHost.d.ts → Agent.d.ts} +66 -27
- package/dist/{AgentHost.js → Agent.js} +221 -119
- package/dist/adapters/AnthropicAdapter.d.ts +1 -1
- package/dist/adapters/AnthropicAdapter.js +1 -1
- package/dist/{agent/ClaudeSdkAgent.d.ts → brain/ClaudeBrain.d.ts} +23 -11
- package/dist/{agent/ClaudeSdkAgent.js → brain/ClaudeBrain.js} +145 -95
- package/dist/{agent/Agent.d.ts → brain/ZiggsBrain.d.ts} +23 -20
- package/dist/{agent/Agent.js → brain/ZiggsBrain.js} +12 -11
- package/dist/{agent → brain}/claudeCognition.d.ts +1 -1
- package/dist/{agent → brain}/claudeCognition.js +1 -1
- package/dist/{agent → brain}/claudeSdkProtocol.d.ts +2 -2
- package/dist/{agent → brain}/claudeSdkProtocol.js +2 -2
- package/dist/{agent → brain}/claudeSdkTools.d.ts +2 -1
- package/dist/{agent → brain}/claudeSdkTools.js +9 -65
- package/dist/{agent → brain}/hostedAgentConnection.d.ts +1 -1
- package/dist/{agent → brain}/hostedAgentConnection.js +3 -3
- package/dist/brain/index.d.ts +6 -6
- package/dist/brain/index.js +2 -2
- package/dist/brain/onBehalfAgreement.d.ts +51 -0
- package/dist/brain/onBehalfAgreement.js +99 -0
- package/dist/{agent → brain}/toolManagerToSdkMcp.js +10 -1
- package/dist/defineAgent.js +4 -4
- package/dist/formatters/HistoryFormatter.js +2 -1
- package/dist/index.d.ts +30 -31
- package/dist/index.js +24 -25
- package/dist/{adapters/McpAdapter.d.ts → mcp/createMcpAgent.d.ts} +16 -15
- package/dist/{adapters/McpAdapter.js → mcp/createMcpAgent.js} +54 -40
- package/dist/memory/MemoryStore.d.ts +18 -1
- package/dist/memory/MemoryStore.js +43 -0
- package/dist/{server → platform}/ConnectionPool.d.ts +9 -3
- package/dist/{server → platform}/ConnectionPool.js +16 -4
- package/dist/{server → platform}/EventQueue.d.ts +1 -1
- package/dist/{server → platform}/StateTimers.d.ts +1 -1
- package/dist/{server → platform}/StateTimers.js +1 -1
- package/dist/{server → platform}/ZiggsEffectHandler.d.ts +4 -4
- package/dist/{server → platform}/ZiggsEffectHandler.js +2 -2
- package/dist/{server → platform}/agreements/AgreementService.d.ts +10 -1
- package/dist/{server → platform}/agreements/AgreementService.js +9 -1
- package/dist/{server → platform}/inboxLoop.d.ts +34 -1
- package/dist/{server → platform}/inboxLoop.js +122 -14
- package/dist/{server → platform}/polls/PollKind.d.ts +4 -7
- package/dist/{server → platform}/polls/PollKind.js +3 -6
- package/dist/{server → platform}/polls/PollRunner.d.ts +5 -6
- package/dist/{server → platform}/polls/PollRunner.js +24 -12
- package/dist/platform/polls/declaredPollRunner.d.ts +4 -0
- package/dist/platform/polls/declaredPollRunner.js +29 -0
- package/dist/{server → platform}/tools/index.d.ts +1 -0
- package/dist/{server → platform}/tools/index.js +1 -0
- package/dist/{server → platform}/tools/tier1/protocolRunner.js +33 -27
- package/dist/{server → platform}/tools/tier1/protocolTools.js +2 -7
- package/dist/{server → platform}/tools/tier2/mcpConnectionTools.js +6 -3
- package/dist/platform/tools/tier2/taskTools.d.ts +3 -0
- package/dist/platform/tools/tier2/taskTools.js +6 -0
- package/dist/runtime/bootstrapCtx.d.ts +1 -1
- package/dist/runtime/bootstrapCtx.js +1 -1
- package/dist/runtime/buildOutcome.d.ts +0 -8
- package/dist/runtime/buildOutcome.js +20 -6
- package/dist/runtime/kickoff.js +1 -1
- package/dist/runtime/runTurn.d.ts +1 -1
- package/dist/runtime/runTurn.js +18 -18
- package/dist/runtime/validateWorkflow.js +1 -1
- package/dist/runtime/wakeOutcome.d.ts +27 -0
- package/dist/runtime/wakeOutcome.js +43 -0
- package/dist/skills/parseSkill.d.ts +7 -2
- package/dist/skills/parseSkill.js +22 -5
- package/dist/skills/skillReadReference.d.ts +6 -0
- package/dist/skills/skillReadReference.js +27 -0
- package/dist/tools/needs.js +2 -2
- package/dist/tools/recordReport.d.ts +5 -0
- package/dist/tools/recordReport.js +5 -0
- package/dist/tools/toolCreds.d.ts +1 -1
- package/dist/tools/toolCreds.js +1 -1
- package/dist/types.d.ts +3 -3
- package/package.json +2 -2
- package/dist/server/createHealthServer.d.ts +0 -7
- package/dist/server/createHealthServer.js +0 -75
- package/dist/server/runLauncher.d.ts +0 -22
- package/dist/server/runLauncher.js +0 -160
- /package/dist/{agent → brain}/toolManagerToSdkMcp.d.ts +0 -0
- /package/dist/{server → platform}/EventQueue.js +0 -0
- /package/dist/{server → platform}/OutboxBuffer.d.ts +0 -0
- /package/dist/{server → platform}/OutboxBuffer.js +0 -0
- /package/dist/{server → platform}/SeenMessages.d.ts +0 -0
- /package/dist/{server → platform}/SeenMessages.js +0 -0
- /package/dist/{server → platform}/proactive/ProactiveTrigger.d.ts +0 -0
- /package/dist/{server → platform}/proactive/ProactiveTrigger.js +0 -0
- /package/dist/{server → platform}/requestMatch.d.ts +0 -0
- /package/dist/{server → platform}/requestMatch.js +0 -0
- /package/dist/{server → platform}/tasks/TaskService.d.ts +0 -0
- /package/dist/{server → platform}/tasks/TaskService.js +0 -0
- /package/dist/{server → platform}/tasks/index.d.ts +0 -0
- /package/dist/{server → platform}/tasks/index.js +0 -0
- /package/dist/{server → platform}/telemetryIngest.d.ts +0 -0
- /package/dist/{server → platform}/telemetryIngest.js +0 -0
- /package/dist/{server → platform}/tools/tier1/protocolRunner.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier1/protocolTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/artifactTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/artifactTools.js +0 -0
- /package/dist/{server → platform}/tools/tier2/chatTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/chatTools.js +0 -0
- /package/dist/{server → platform}/tools/tier2/connectionTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/connectionTools.js +0 -0
- /package/dist/{server → platform}/tools/tier2/contextTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/contextTools.js +0 -0
- /package/dist/{server → platform}/tools/tier2/discoveryTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/discoveryTools.js +0 -0
- /package/dist/{server → platform}/tools/tier2/grantsTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/grantsTools.js +0 -0
- /package/dist/{server → platform}/tools/tier2/introductionTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/introductionTools.js +0 -0
- /package/dist/{server → platform}/tools/tier2/linkTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/linkTools.js +0 -0
- /package/dist/{server → platform}/tools/tier2/marketplaceTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/marketplaceTools.js +0 -0
- /package/dist/{server → platform}/tools/tier2/mcpConnectionTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/paymentTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/paymentTools.js +0 -0
|
@@ -7,7 +7,7 @@ interface AdapterOptions {
|
|
|
7
7
|
/**
|
|
8
8
|
* Anthropic adapter satisfying the same LlmAdapter contract as OpenAIAdapter:
|
|
9
9
|
* OpenAI-wire-shaped LlmMessage[] in, { content, tool_calls, usage } out.
|
|
10
|
-
* Selected by
|
|
10
|
+
* Selected by Agent when the configured model id starts with "claude".
|
|
11
11
|
*/
|
|
12
12
|
export declare class AnthropicAdapter {
|
|
13
13
|
private client;
|
|
@@ -8,7 +8,7 @@ const MAX_COMPLETION_TOKENS = 16000;
|
|
|
8
8
|
/**
|
|
9
9
|
* Anthropic adapter satisfying the same LlmAdapter contract as OpenAIAdapter:
|
|
10
10
|
* OpenAI-wire-shaped LlmMessage[] in, { content, tool_calls, usage } out.
|
|
11
|
-
* Selected by
|
|
11
|
+
* Selected by Agent when the configured model id starts with "claude".
|
|
12
12
|
*/
|
|
13
13
|
export class AnthropicAdapter {
|
|
14
14
|
client;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import type { Brain } from './index.js';
|
|
1
2
|
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
2
3
|
import type { Workflow } from '../types.js';
|
|
3
4
|
import type { ToolManager } from '../tools/ToolManager.js';
|
|
4
5
|
import { type RawEvent } from '../runtime/buildOutcome.js';
|
|
5
|
-
import type { TickInput, TickOutput } from './
|
|
6
|
+
import type { TickInput, TickOutput } from './ZiggsBrain.js';
|
|
6
7
|
/**
|
|
7
8
|
* status notices must not spend a Claude turn answering
|
|
8
9
|
* nobody. Real work wakes arrive as chat `message`, synthetic `task_result`
|
|
@@ -21,9 +22,15 @@ import type { TickInput, TickOutput } from './Agent.js';
|
|
|
21
22
|
* message still lands in chat history and arrives as context on the next
|
|
22
23
|
* real wake — skipped here is the LLM turn, not the information.
|
|
23
24
|
*/
|
|
25
|
+
/**
|
|
26
|
+
* Store publish chats the agent in a room named `probe_<agentId>_<ts>`.
|
|
27
|
+
* That check is "are you connected and can you reply", not a hired job —
|
|
28
|
+
* spending a model turn on it is how a long skill prompt 504s the gate.
|
|
29
|
+
*/
|
|
30
|
+
export declare function isCapabilityProbeSession(sessionId: string | undefined | null): boolean;
|
|
24
31
|
export declare function isNoticeOnlyWake(event: RawEvent | undefined, ownAgentId?: string | null): boolean;
|
|
25
32
|
export type CognitionKind = 'fsm' | 'claude-sdk';
|
|
26
|
-
export interface
|
|
33
|
+
export interface ClaudeBrainOptions {
|
|
27
34
|
workflow: Workflow;
|
|
28
35
|
toolManager: ToolManager;
|
|
29
36
|
/** Store-facing one-liner. Used as the role block only when `systemPrompt` is absent. */
|
|
@@ -63,15 +70,18 @@ export interface ClaudeSdkAgentOptions {
|
|
|
63
70
|
export declare function isMaxTurnsError(err: unknown): boolean;
|
|
64
71
|
export declare function isMissingSessionError(err: unknown): boolean;
|
|
65
72
|
/**
|
|
66
|
-
* A
|
|
67
|
-
* side channel, same tool surface as
|
|
73
|
+
* A brain driven by the Claude Agent SDK. Same body, same `eff`
|
|
74
|
+
* side channel, same tool surface as `ZiggsBrain` — the ONLY difference is
|
|
68
75
|
* the loop: instead of the FSM + `runTurn`, a wake drives a headless Claude
|
|
69
76
|
* `query()` (see "host is the product; the brain is how it decides").
|
|
70
77
|
*/
|
|
71
|
-
export declare class
|
|
78
|
+
export declare class ClaudeBrain implements Brain {
|
|
79
|
+
readonly kind: "claude";
|
|
72
80
|
workflow: Workflow;
|
|
73
81
|
toolManager: ToolManager;
|
|
74
82
|
private rolePrompt;
|
|
83
|
+
/** Store one-liner — used when the publish probe asks who we are. */
|
|
84
|
+
private listingBlurb;
|
|
75
85
|
private specialization;
|
|
76
86
|
private model;
|
|
77
87
|
private anthropicKey;
|
|
@@ -81,12 +91,14 @@ export declare class ClaudeSdkAgent {
|
|
|
81
91
|
private statelessPerWake;
|
|
82
92
|
private queryFn;
|
|
83
93
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
94
|
+
* Per-lane tool context. One agent object serves every room, and a
|
|
95
|
+
* wake's query can still be running when another lane starts. A single
|
|
96
|
+
* `current` field meant B's room/sender/write keys overwrote A's
|
|
97
|
+
* mid-query. Wakes on one lane stay serialized; one server per lane is
|
|
98
|
+
* enough, and getContext closes over that lane's key.
|
|
87
99
|
*/
|
|
88
|
-
private
|
|
89
|
-
private
|
|
90
|
-
constructor(opts:
|
|
100
|
+
private toolContextByLane;
|
|
101
|
+
private sdkMcpByLane;
|
|
102
|
+
constructor(opts: ClaudeBrainOptions);
|
|
91
103
|
tick({ event, ctx, state, identity, eff, signal }: TickInput): Promise<TickOutput>;
|
|
92
104
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isAgreementId } from '@ziggs-ai/api-client';
|
|
1
2
|
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
2
3
|
import { isMessageAddressedElsewhere, outcomeFromEvent } from '../runtime/buildOutcome.js';
|
|
3
4
|
import { getBatchEvents } from '../context/batch.js';
|
|
@@ -8,6 +9,7 @@ import { buildZiggsSdkMcpServer } from './toolManagerToSdkMcp.js';
|
|
|
8
9
|
import { claudeQueryOptions, buildClaudeEventPrompt, summarizeContext, extractAssistantText, } from './claudeCognition.js';
|
|
9
10
|
import { assertHostedAgentZiggsTools } from './hostedAgentConnection.js';
|
|
10
11
|
import { runtimeLog } from '../shared/runtimeLog.js';
|
|
12
|
+
import { resolveOnBehalfAgreementId, } from './onBehalfAgreement.js';
|
|
11
13
|
/**
|
|
12
14
|
* status notices must not spend a Claude turn answering
|
|
13
15
|
* nobody. Real work wakes arrive as chat `message`, synthetic `task_result`
|
|
@@ -26,6 +28,14 @@ import { runtimeLog } from '../shared/runtimeLog.js';
|
|
|
26
28
|
* message still lands in chat history and arrives as context on the next
|
|
27
29
|
* real wake — skipped here is the LLM turn, not the information.
|
|
28
30
|
*/
|
|
31
|
+
/**
|
|
32
|
+
* Store publish chats the agent in a room named `probe_<agentId>_<ts>`.
|
|
33
|
+
* That check is "are you connected and can you reply", not a hired job —
|
|
34
|
+
* spending a model turn on it is how a long skill prompt 504s the gate.
|
|
35
|
+
*/
|
|
36
|
+
export function isCapabilityProbeSession(sessionId) {
|
|
37
|
+
return typeof sessionId === 'string' && sessionId.startsWith('probe_');
|
|
38
|
+
}
|
|
29
39
|
export function isNoticeOnlyWake(event, ownAgentId) {
|
|
30
40
|
const events = getBatchEvents(event);
|
|
31
41
|
if (events.length === 0)
|
|
@@ -164,61 +174,18 @@ function extractMetrics(message) {
|
|
|
164
174
|
};
|
|
165
175
|
}
|
|
166
176
|
/**
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
* `senderId` — which, under a delegation, is the delegating agent (who owns no
|
|
170
|
-
* connection) → a spurious NO_USER_CONNECTION even though the payer IS connected.
|
|
171
|
-
*
|
|
172
|
-
* The id must be found on ANY wake the worker might fetch on, not just
|
|
173
|
-
* `task_result` events. In practice the worker often fetches while reacting to
|
|
174
|
-
* the proposal-approved / agreement-active lifecycle events, which carry the id
|
|
175
|
-
* at the event top level rather than under `result`. Reading only
|
|
176
|
-
* `result.agreementId` (the original wiring) left those wakes with no
|
|
177
|
-
* agreement → the false "authorize your connection" prompt. Resolve from every
|
|
178
|
-
* reliable source instead:
|
|
179
|
-
* 1. task_result events → `result.agreementId`
|
|
180
|
-
* 2. agreement-lifecycle events → top-level `agreementId`
|
|
181
|
-
* 3. task lanes → `sessionId` IS the agreement id
|
|
182
|
-
* 4. fallback → the single active agreement in context this agent provides
|
|
183
|
-
*/
|
|
184
|
-
function resolveOnBehalfAgreementId(rawEvent, contextSnap, agentId, sessionId) {
|
|
185
|
-
const str = (v) => typeof v === 'string' && v ? v : undefined;
|
|
186
|
-
const fromResult = str(rawEvent?.result?.agreementId);
|
|
187
|
-
if (fromResult)
|
|
188
|
-
return fromResult;
|
|
189
|
-
const fromEvent = str(rawEvent?.agreementId);
|
|
190
|
-
if (fromEvent)
|
|
191
|
-
return fromEvent;
|
|
192
|
-
if (typeof sessionId === 'string' && sessionId.startsWith('agr_'))
|
|
193
|
-
return sessionId;
|
|
194
|
-
// Fallback: exactly one active agreement in context where this agent is the
|
|
195
|
-
// provider. Guarded to a single match so we never send the wrong agreement.
|
|
196
|
-
const ags = Array.isArray(contextSnap?.agreements)
|
|
197
|
-
? contextSnap.agreements
|
|
198
|
-
: [];
|
|
199
|
-
const provides = ags.filter((a) => {
|
|
200
|
-
const provider = a?.parties?.provider;
|
|
201
|
-
const providerIds = [provider?.actor, a?.providerId, a?.providerAgentId, a?.executorId];
|
|
202
|
-
const isProvider = providerIds.some((x) => x === agentId);
|
|
203
|
-
const status = a?.status != null ? String(a.status).toLowerCase() : '';
|
|
204
|
-
const active = !status || status === 'active' || status === 'approved';
|
|
205
|
-
return isProvider && active;
|
|
206
|
-
});
|
|
207
|
-
if (provides.length === 1) {
|
|
208
|
-
return str(provides[0].agreementId) ?? str(provides[0].id) ?? str(provides[0]._id);
|
|
209
|
-
}
|
|
210
|
-
return undefined;
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* A Ziggs agent whose brain is the Claude Agent SDK. Same host, same `eff`
|
|
214
|
-
* side channel, same tool surface as the FSM `Agent` — the ONLY difference is
|
|
177
|
+
* A brain driven by the Claude Agent SDK. Same body, same `eff`
|
|
178
|
+
* side channel, same tool surface as `ZiggsBrain` — the ONLY difference is
|
|
215
179
|
* the loop: instead of the FSM + `runTurn`, a wake drives a headless Claude
|
|
216
180
|
* `query()` (see "host is the product; the brain is how it decides").
|
|
217
181
|
*/
|
|
218
|
-
export class
|
|
182
|
+
export class ClaudeBrain {
|
|
183
|
+
kind = 'claude';
|
|
219
184
|
workflow;
|
|
220
185
|
toolManager;
|
|
221
186
|
rolePrompt;
|
|
187
|
+
/** Store one-liner — used when the publish probe asks who we are. */
|
|
188
|
+
listingBlurb;
|
|
222
189
|
specialization;
|
|
223
190
|
model;
|
|
224
191
|
anthropicKey;
|
|
@@ -228,25 +195,28 @@ export class ClaudeSdkAgent {
|
|
|
228
195
|
statelessPerWake;
|
|
229
196
|
queryFn;
|
|
230
197
|
/**
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
198
|
+
* Per-lane tool context. One agent object serves every room, and a
|
|
199
|
+
* wake's query can still be running when another lane starts. A single
|
|
200
|
+
* `current` field meant B's room/sender/write keys overwrote A's
|
|
201
|
+
* mid-query. Wakes on one lane stay serialized; one server per lane is
|
|
202
|
+
* enough, and getContext closes over that lane's key.
|
|
234
203
|
*/
|
|
235
|
-
|
|
236
|
-
|
|
204
|
+
toolContextByLane = new Map();
|
|
205
|
+
sdkMcpByLane = new Map();
|
|
237
206
|
constructor(opts) {
|
|
238
207
|
if (!opts.workflow?.states)
|
|
239
|
-
throw new Error('
|
|
208
|
+
throw new Error('ClaudeBrain: workflow with states is required');
|
|
240
209
|
if (!opts.toolManager)
|
|
241
|
-
throw new Error('
|
|
210
|
+
throw new Error('ClaudeBrain: toolManager is required');
|
|
242
211
|
if (!opts.anthropicKey)
|
|
243
|
-
throw new Error('
|
|
212
|
+
throw new Error('ClaudeBrain: anthropicKey is required');
|
|
244
213
|
this.workflow = opts.workflow;
|
|
245
214
|
this.toolManager = opts.toolManager;
|
|
246
215
|
// the prompt and the store blurb are two different strings. Agents
|
|
247
216
|
// written before the split still carry the prompt in `description`, so fall
|
|
248
217
|
// back to it rather than silently dropping their role.
|
|
249
218
|
this.rolePrompt = opts.systemPrompt || opts.description;
|
|
219
|
+
this.listingBlurb = opts.description;
|
|
250
220
|
this.specialization = opts.specialization ?? null;
|
|
251
221
|
this.model = opts.model;
|
|
252
222
|
this.anthropicKey = opts.anthropicKey;
|
|
@@ -258,18 +228,15 @@ export class ClaudeSdkAgent {
|
|
|
258
228
|
}
|
|
259
229
|
async tick({ event, ctx, state, identity, eff, signal }) {
|
|
260
230
|
if (!identity?.sessionId)
|
|
261
|
-
throw new Error('
|
|
231
|
+
throw new Error('ClaudeBrain.tick: identity.sessionId is required');
|
|
262
232
|
if (!eff)
|
|
263
|
-
throw new Error('
|
|
233
|
+
throw new Error('ClaudeBrain.tick: eff is required');
|
|
264
234
|
const rawEvent = event;
|
|
265
235
|
const nextCtx = bootstrapCtx(identity, ctx);
|
|
266
236
|
nextCtx.lastOutcome = outcomeFromEvent(rawEvent ?? null, identity.agentId);
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
runtimeLog.warn('ClaudeSdkAgent', `wake-rate breaker tripped: ${recentDeliveries.length} wakes in ${WAKE_WINDOW_MS / 60_000}min ` +
|
|
271
|
-
`session=${identity.sessionId} agent=${this.agentId} — skipping LLM for this wake`);
|
|
272
|
-
nextCtx.recentDeliveries = recentDeliveries;
|
|
237
|
+
if (isNoticeOnlyWake(rawEvent, identity.agentId)) {
|
|
238
|
+
runtimeLog.info('ClaudeBrain', `notice-only wake — skipping LLM session=${identity.sessionId} agent=${this.agentId} ` +
|
|
239
|
+
`type=${rawEvent?.type ?? 'unknown'}`);
|
|
273
240
|
return {
|
|
274
241
|
ctx: nextCtx,
|
|
275
242
|
state: state ?? DEFAULT_STATE,
|
|
@@ -278,10 +245,42 @@ export class ClaudeSdkAgent {
|
|
|
278
245
|
metrics: { llmCalls: 0, tokens: { total: 0, input: 0, output: 0 } },
|
|
279
246
|
};
|
|
280
247
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
248
|
+
// Store publish must hear a reply even when leftover mail already tripped
|
|
249
|
+
// the wake-rate breaker. The breaker used to sit above this and park the
|
|
250
|
+
// probe with no send-message — Register then died on ALB 504.
|
|
251
|
+
if (nextCtx.lastOutcome?.kind === 'message-received' &&
|
|
252
|
+
isCapabilityProbeSession(identity.sessionId)) {
|
|
253
|
+
const text = `I received your message. ${this.listingBlurb.trim().split(/\r?\n/)[0]?.trim() || 'I am online.'}`;
|
|
254
|
+
const writeKeys = createDeliveryWriteKeys(identity.laneKey, rawEvent);
|
|
255
|
+
const senderId = rawEvent?.senderId != null && String(rawEvent.senderId).trim() !== ''
|
|
256
|
+
? String(rawEvent.senderId)
|
|
257
|
+
: undefined;
|
|
258
|
+
await eff({
|
|
259
|
+
kind: 'send-message',
|
|
260
|
+
sessionId: identity.sessionId,
|
|
261
|
+
...(senderId ? { receiverId: senderId } : {}),
|
|
262
|
+
text,
|
|
263
|
+
messageId: writeKeys.messageKey({
|
|
264
|
+
text,
|
|
265
|
+
sessionId: identity.sessionId,
|
|
266
|
+
receiverId: senderId,
|
|
267
|
+
}),
|
|
268
|
+
});
|
|
269
|
+
runtimeLog.info('ClaudeBrain', `capability-probe reply — skipping LLM session=${identity.sessionId} agent=${this.agentId}`);
|
|
270
|
+
return {
|
|
271
|
+
ctx: nextCtx,
|
|
272
|
+
state: state ?? DEFAULT_STATE,
|
|
273
|
+
parked: true,
|
|
274
|
+
stateTimeoutUnhandled: false,
|
|
275
|
+
metrics: { llmCalls: 0, tokens: { total: 0, input: 0, output: 0 } },
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
const now = Date.now();
|
|
279
|
+
const recentDeliveries = (nextCtx.recentDeliveries ?? []).filter((t) => now - t < WAKE_WINDOW_MS);
|
|
280
|
+
if (recentDeliveries.length >= MAX_WAKES_PER_WINDOW) {
|
|
281
|
+
runtimeLog.warn('ClaudeBrain', `wake-rate breaker tripped: ${recentDeliveries.length} wakes in ${WAKE_WINDOW_MS / 60_000}min ` +
|
|
282
|
+
`session=${identity.sessionId} agent=${this.agentId} — skipping LLM for this wake`);
|
|
283
|
+
nextCtx.recentDeliveries = recentDeliveries;
|
|
285
284
|
return {
|
|
286
285
|
ctx: nextCtx,
|
|
287
286
|
state: state ?? DEFAULT_STATE,
|
|
@@ -290,6 +289,7 @@ export class ClaudeSdkAgent {
|
|
|
290
289
|
metrics: { llmCalls: 0, tokens: { total: 0, input: 0, output: 0 } },
|
|
291
290
|
};
|
|
292
291
|
}
|
|
292
|
+
nextCtx.recentDeliveries = [...recentDeliveries, now];
|
|
293
293
|
let contextSnap = null;
|
|
294
294
|
try {
|
|
295
295
|
// #5: pre-read is for wake gating + summarizeContext (a count) +
|
|
@@ -302,7 +302,7 @@ export class ClaudeSdkAgent {
|
|
|
302
302
|
});
|
|
303
303
|
}
|
|
304
304
|
catch (err) {
|
|
305
|
-
runtimeLog.warn('
|
|
305
|
+
runtimeLog.warn('ClaudeBrain', `read-context failed session=${identity.sessionId}: ${err.message}`);
|
|
306
306
|
}
|
|
307
307
|
// Same guard as the FSM's runTurn: a message wake for a chat the backend
|
|
308
308
|
// no longer resolves (deleted / not visible) has no world to act in —
|
|
@@ -314,17 +314,15 @@ export class ClaudeSdkAgent {
|
|
|
314
314
|
// signal. Loud-log that mis-key; message wakes still skip below.
|
|
315
315
|
if (contextSnap?.chatMissing === true) {
|
|
316
316
|
const sessionId = identity.sessionId;
|
|
317
|
-
const looksLikeBareAgreement =
|
|
318
|
-
sessionId.startsWith('agr_') &&
|
|
319
|
-
!sessionId.startsWith('agrn-');
|
|
317
|
+
const looksLikeBareAgreement = isAgreementId(sessionId);
|
|
320
318
|
if (looksLikeBareAgreement) {
|
|
321
|
-
runtimeLog.warn('
|
|
319
|
+
runtimeLog.warn('ClaudeBrain', `chatMissing on non-chat sessionId=${sessionId} agent=${this.agentId} ` +
|
|
322
320
|
`outcome=${nextCtx.lastOutcome?.kind ?? 'none'} — lane was keyed by ` +
|
|
323
321
|
`agreementId instead of the agreement's origin/notification chat; ` +
|
|
324
322
|
`wake continues with an empty snapshot`);
|
|
325
323
|
}
|
|
326
324
|
if (nextCtx.lastOutcome?.kind === 'message-received') {
|
|
327
|
-
runtimeLog.warn('
|
|
325
|
+
runtimeLog.warn('ClaudeBrain', `chat ${sessionId} not found on backend — skipping message wake agent=${this.agentId}`);
|
|
328
326
|
return {
|
|
329
327
|
ctx: nextCtx,
|
|
330
328
|
state: state ?? DEFAULT_STATE,
|
|
@@ -359,26 +357,33 @@ export class ClaudeSdkAgent {
|
|
|
359
357
|
// MCP call resolve the originating end-user (payer) rather than the
|
|
360
358
|
// delegating agent (senderId). Resolved from every reliable wire source,
|
|
361
359
|
// not just task_result events — see resolveOnBehalfAgreementId.
|
|
362
|
-
|
|
360
|
+
// Skip payer-only subs named on the worker result; bind to the
|
|
361
|
+
// provider hire (see onBehalfAgreement.ts).
|
|
362
|
+
onBehalfOfAgreementId: resolveOnBehalfAgreementId(rawEvent, Array.isArray(contextSnap?.agreements)
|
|
363
|
+
? contextSnap.agreements
|
|
364
|
+
: null, this.agentId, identity.sessionId),
|
|
363
365
|
runId,
|
|
364
366
|
writeKeys,
|
|
365
367
|
};
|
|
366
|
-
|
|
367
|
-
|
|
368
|
+
const lane = identity.laneKey;
|
|
369
|
+
this.toolContextByLane.set(lane, toolContext);
|
|
370
|
+
let sdkMcpServer = this.sdkMcpByLane.get(lane);
|
|
371
|
+
if (!sdkMcpServer) {
|
|
368
372
|
const availableTools = await this.toolManager.getAvailableTools();
|
|
369
|
-
|
|
370
|
-
const ctx = this.
|
|
373
|
+
sdkMcpServer = buildZiggsSdkMcpServer(availableTools, () => {
|
|
374
|
+
const ctx = this.toolContextByLane.get(lane);
|
|
371
375
|
if (!ctx) {
|
|
372
|
-
throw new Error(
|
|
376
|
+
throw new Error(`ClaudeBrain: tool context missing for lane ${lane}`);
|
|
373
377
|
}
|
|
374
378
|
return ctx;
|
|
375
379
|
}, {
|
|
376
380
|
name: 'ziggs',
|
|
377
381
|
instructions: 'In-process Ziggs tools backed by api-client. No external MCP process.',
|
|
378
382
|
});
|
|
383
|
+
this.sdkMcpByLane.set(lane, sdkMcpServer);
|
|
379
384
|
}
|
|
380
385
|
const prompt = buildClaudeEventPrompt(rawEvent, summarizeContext(contextSnap));
|
|
381
|
-
const mcpServers = { ziggs:
|
|
386
|
+
const mcpServers = { ziggs: sdkMcpServer };
|
|
382
387
|
assertHostedAgentZiggsTools(mcpServers);
|
|
383
388
|
const queryOptions = claudeQueryOptions({
|
|
384
389
|
systemPrompt,
|
|
@@ -389,7 +394,7 @@ export class ClaudeSdkAgent {
|
|
|
389
394
|
stderr: (data) => {
|
|
390
395
|
const line = data.trim();
|
|
391
396
|
if (line)
|
|
392
|
-
runtimeLog.warn('
|
|
397
|
+
runtimeLog.warn('ClaudeBrain', `stderr session=${identity.sessionId}: ${line}`);
|
|
393
398
|
},
|
|
394
399
|
});
|
|
395
400
|
let metrics = { llmCalls: 0, tokens: { total: 0, input: 0, output: 0, cacheRead: 0, cacheCreation: 0 } };
|
|
@@ -424,7 +429,7 @@ export class ClaudeSdkAgent {
|
|
|
424
429
|
try {
|
|
425
430
|
for await (const message of query) {
|
|
426
431
|
if (signal?.aborted) {
|
|
427
|
-
runtimeLog.warn('
|
|
432
|
+
runtimeLog.warn('ClaudeBrain', `host watchdog aborted wake session=${identity.sessionId} agent=${this.agentId} — stopping query`);
|
|
428
433
|
break;
|
|
429
434
|
}
|
|
430
435
|
if ('session_id' in message && typeof message.session_id === 'string') {
|
|
@@ -450,14 +455,14 @@ export class ClaudeSdkAgent {
|
|
|
450
455
|
// reorder key assignment against other writes of this wake.
|
|
451
456
|
const idempotencyKey = writeKeys.artifactKey({ kind: 'thought', text });
|
|
452
457
|
void eff({ kind: 'record-event', sessionId: identity.sessionId, entry: { kind: 'thought', text }, idempotencyKey })
|
|
453
|
-
.catch((err) => runtimeLog.warn('
|
|
458
|
+
.catch((err) => runtimeLog.warn('ClaudeBrain', `record thought failed session=${identity.sessionId}: ${err.message}`));
|
|
454
459
|
}
|
|
455
460
|
}
|
|
456
461
|
const m = extractMetrics(message);
|
|
457
462
|
if (m)
|
|
458
463
|
metrics = m;
|
|
459
464
|
if (message.type === 'result' && message.subtype !== 'success') {
|
|
460
|
-
runtimeLog.warn('
|
|
465
|
+
runtimeLog.warn('ClaudeBrain', `query ended without success subtype=${message.subtype} session=${identity.sessionId}`);
|
|
461
466
|
}
|
|
462
467
|
// Runaway guard: abort before an unbounded loop drains the
|
|
463
468
|
// key. Breaking the for-await calls the generator's return(),
|
|
@@ -475,7 +480,7 @@ export class ClaudeSdkAgent {
|
|
|
475
480
|
}
|
|
476
481
|
if (spentInput > MAX_TASK_INPUT_TOKENS) {
|
|
477
482
|
abortedBy = 'input-budget';
|
|
478
|
-
runtimeLog.warn('
|
|
483
|
+
runtimeLog.warn('ClaudeBrain', `input-token budget exceeded (~${Math.round(spentInput)} cost-weighted > ${MAX_TASK_INPUT_TOKENS}) session=${identity.sessionId} agent=${this.agentId} — aborting query to cap spend`);
|
|
479
484
|
break;
|
|
480
485
|
}
|
|
481
486
|
}
|
|
@@ -492,6 +497,44 @@ export class ClaudeSdkAgent {
|
|
|
492
497
|
* why. The lane's history carries the reason instead, so a wedged-task
|
|
493
498
|
* detector has something to read.
|
|
494
499
|
*/
|
|
500
|
+
/**
|
|
501
|
+
* Close the task this wake was woken for, when a guard stopped the wake
|
|
502
|
+
* before the agent could close it itself.
|
|
503
|
+
*
|
|
504
|
+
* The note above records the reason where a detector could read it. Nothing
|
|
505
|
+
* reads it, and the hirer reads none of it: the task stays `active` with no
|
|
506
|
+
* result, the wake returns parked like any other, its inbox item is acked,
|
|
507
|
+
* and the engagement looks alive forever. Measured on production — a
|
|
508
|
+
* connector agent whose tool schema costs ~20-45k input per wake spent the
|
|
509
|
+
* session budget across four wakes on one lane and its task sat untouched
|
|
510
|
+
* for as long as anyone cared to watch. The buyer had a greeting, a hire,
|
|
511
|
+
* and silence.
|
|
512
|
+
*
|
|
513
|
+
* So the guard now says the same thing the prompt tells every agent to say
|
|
514
|
+
* when it cannot do the work: fail the task, with the reason, because a
|
|
515
|
+
* visible failure the counterparty can re-route beats a silent drop.
|
|
516
|
+
* Best-effort — a wake already cut short must not throw on the way out.
|
|
517
|
+
*/
|
|
518
|
+
const failTaskCutShort = async (reason) => {
|
|
519
|
+
const taskId = nextCtx.activeTaskId;
|
|
520
|
+
if (!taskId)
|
|
521
|
+
return;
|
|
522
|
+
await eff({
|
|
523
|
+
kind: 'tool-call',
|
|
524
|
+
sessionId: identity.sessionId,
|
|
525
|
+
name: 'task_set_result',
|
|
526
|
+
args: {
|
|
527
|
+
taskId,
|
|
528
|
+
state: 'failed',
|
|
529
|
+
result: {
|
|
530
|
+
summary: `Stopped before finishing: ${reason}. Nothing was filed for this task.`,
|
|
531
|
+
status: 'failed',
|
|
532
|
+
},
|
|
533
|
+
},
|
|
534
|
+
}).catch((err) => runtimeLog.warn('ClaudeBrain', `could not fail task ${taskId} after a cut-short wake ` +
|
|
535
|
+
`session=${identity.sessionId}: ${err.message}`));
|
|
536
|
+
runtimeLog.warn('ClaudeBrain', `failed task ${taskId} because the wake was cut short session=${identity.sessionId} agent=${this.agentId}`);
|
|
537
|
+
};
|
|
495
538
|
const noteWakeCutShort = async (reason) => {
|
|
496
539
|
const text = `Wake stopped early: ${reason}. Work in progress was NOT filed — no ` +
|
|
497
540
|
`artifact was recorded and no task was closed by this wake.`;
|
|
@@ -500,7 +543,7 @@ export class ClaudeSdkAgent {
|
|
|
500
543
|
sessionId: identity.sessionId,
|
|
501
544
|
entry: { kind: 'thought', text },
|
|
502
545
|
idempotencyKey: writeKeys.artifactKey({ kind: 'thought', text }),
|
|
503
|
-
}).catch((err) => runtimeLog.warn('
|
|
546
|
+
}).catch((err) => runtimeLog.warn('ClaudeBrain', `record abort note failed session=${identity.sessionId}: ${err.message}`));
|
|
504
547
|
};
|
|
505
548
|
try {
|
|
506
549
|
await consumeQuery(resumeId);
|
|
@@ -510,7 +553,12 @@ export class ClaudeSdkAgent {
|
|
|
510
553
|
// needs its own note before the throw propagates.
|
|
511
554
|
if (isMaxTurnsError(err)) {
|
|
512
555
|
abortedBy = 'max-turns';
|
|
513
|
-
|
|
556
|
+
// Same reasoning as the spend guard below: a cap that stops the work
|
|
557
|
+
// has to close the work, or the hirer is left watching an active task
|
|
558
|
+
// nobody is on.
|
|
559
|
+
const reason = `the turn cap (${this.maxTurns}) was reached`;
|
|
560
|
+
await noteWakeCutShort(reason);
|
|
561
|
+
await failTaskCutShort(reason);
|
|
514
562
|
}
|
|
515
563
|
// the resumed transcript is gone. Claude Code stores transcripts
|
|
516
564
|
// on the container's own disk while the session id outlives it — persisted
|
|
@@ -525,10 +573,10 @@ export class ClaudeSdkAgent {
|
|
|
525
573
|
// attempt — a mid-wake failure must keep propagating, since its partial
|
|
526
574
|
// writes are already out.
|
|
527
575
|
if (!isMissingSessionError(err) || !resumeId) {
|
|
528
|
-
runtimeLog.warn('
|
|
576
|
+
runtimeLog.warn('ClaudeBrain', `query failed session=${identity.sessionId}: ${err.message}`);
|
|
529
577
|
throw err;
|
|
530
578
|
}
|
|
531
|
-
runtimeLog.info('
|
|
579
|
+
runtimeLog.info('ClaudeBrain', `resumed Claude session ${resumeId} no longer exists (container replaced?) ` +
|
|
532
580
|
`session=${identity.sessionId} agent=${this.agentId} — retrying once on a fresh session`);
|
|
533
581
|
nextCtx.claudeSessionId = undefined;
|
|
534
582
|
sessionId = undefined;
|
|
@@ -542,7 +590,7 @@ export class ClaudeSdkAgent {
|
|
|
542
590
|
await consumeQuery(undefined);
|
|
543
591
|
}
|
|
544
592
|
catch (retryErr) {
|
|
545
|
-
runtimeLog.warn('
|
|
593
|
+
runtimeLog.warn('ClaudeBrain', `query failed session=${identity.sessionId}: ${retryErr.message}`);
|
|
546
594
|
throw retryErr;
|
|
547
595
|
}
|
|
548
596
|
}
|
|
@@ -564,13 +612,15 @@ export class ClaudeSdkAgent {
|
|
|
564
612
|
// re-reads the world from scratch and runs out of budget at the same step,
|
|
565
613
|
// and a standing job cannot advance.
|
|
566
614
|
if (nextCtx.claudeSessionId && peakTurnInput > MAX_SESSION_RESUME_INPUT_TOKENS) {
|
|
567
|
-
runtimeLog.info('
|
|
615
|
+
runtimeLog.info('ClaudeBrain', `resetting Claude session (transcript ~${peakTurnInput} > ${MAX_SESSION_RESUME_INPUT_TOKENS}) ` +
|
|
568
616
|
`session=${identity.sessionId} agent=${this.agentId} — next wake starts fresh`);
|
|
569
617
|
nextCtx.claudeSessionId = undefined;
|
|
570
618
|
}
|
|
571
619
|
if (abortedBy === 'input-budget') {
|
|
572
|
-
|
|
573
|
-
`cost-weighted input tokens > ${MAX_TASK_INPUT_TOKENS})
|
|
620
|
+
const reason = `the per-wake spend guard cut this run off (~${Math.round(spentInput)} ` +
|
|
621
|
+
`cost-weighted input tokens > ${MAX_TASK_INPUT_TOKENS})`;
|
|
622
|
+
await noteWakeCutShort(reason);
|
|
623
|
+
await failTaskCutShort(reason);
|
|
574
624
|
}
|
|
575
625
|
return {
|
|
576
626
|
ctx: nextCtx,
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import type { Brain } from './index.js';
|
|
1
2
|
import type { Ctx, Identity, Workflow, EffectHandler } from '../types.js';
|
|
2
3
|
import { type TransitionEvent } from '../runtime/AgentMachine.js';
|
|
3
4
|
import { PromptBuilder } from '../runtime/PromptBuilder.js';
|
|
4
5
|
import type { MemoryStore } from '../memory/MemoryStore.js';
|
|
5
6
|
import type { ToolManager } from '../tools/ToolManager.js';
|
|
6
|
-
export interface
|
|
7
|
+
export interface ZiggsBrainOptions {
|
|
7
8
|
workflow: Workflow;
|
|
8
9
|
toolManager: ToolManager;
|
|
9
10
|
promptBuilder?: PromptBuilder;
|
|
@@ -11,26 +12,27 @@ export interface AgentOptions {
|
|
|
11
12
|
export interface TickInput {
|
|
12
13
|
/** Wire event (already normalized: a `message` or `task_result`-style envelope). */
|
|
13
14
|
event: unknown;
|
|
14
|
-
/**
|
|
15
|
+
/** Agent-provided per-session state. Omit on the first tick — a fresh ctx is constructed. */
|
|
15
16
|
ctx?: Ctx;
|
|
16
|
-
/**
|
|
17
|
+
/** Agent-provided workflow state name. Omit on the first tick — defaults to `workflow.initial`. */
|
|
17
18
|
state?: string;
|
|
18
|
-
/** Who am I and what session am I running in. The
|
|
19
|
+
/** Who am I and what session am I running in. The ZiggsBrain carries this through Ctx. */
|
|
19
20
|
identity: Identity;
|
|
20
21
|
/** The single side channel for I/O — LLM, context, tools, send, record. */
|
|
21
22
|
eff: EffectHandler;
|
|
22
23
|
/**
|
|
23
24
|
* Agent-private long-term memory. Operator-provided (default
|
|
24
|
-
* `InMemoryStore` when omitted by `
|
|
25
|
-
*
|
|
25
|
+
* `InMemoryStore` when omitted by `Agent`). The agent already scopes
|
|
26
|
+
* this to the hire the wake is inside; key shape inside that section is
|
|
27
|
+
* the agent's choice — `counterparty:<id>`, `agreement:<id>:notes`, etc.
|
|
26
28
|
*/
|
|
27
29
|
memory?: MemoryStore;
|
|
28
30
|
/** Optional tap for FSM transition events. */
|
|
29
31
|
transitionTap?: (event: TransitionEvent) => void;
|
|
30
32
|
/**
|
|
31
|
-
* Aborted by the
|
|
32
|
-
* external loop (the Claude
|
|
33
|
-
* abort — the
|
|
33
|
+
* Aborted by the agent when the tick watchdog fires. A brain driving an
|
|
34
|
+
* external loop (the Claude ZiggsBrain SDK query) MUST stop consuming it on
|
|
35
|
+
* abort — the agent's Promise.race only abandons the tick promise, so
|
|
34
36
|
* without this the query keeps running as a zombie: burning tokens and
|
|
35
37
|
* racing its own redelivered retry (observed as duplicate kickoffs and a
|
|
36
38
|
* 363k-token wake, 2026-07-26).
|
|
@@ -38,13 +40,13 @@ export interface TickInput {
|
|
|
38
40
|
signal?: AbortSignal;
|
|
39
41
|
}
|
|
40
42
|
export interface TickOutput {
|
|
41
|
-
/** Updated ctx —
|
|
43
|
+
/** Updated ctx — Agent persists this against the session. */
|
|
42
44
|
ctx: Ctx;
|
|
43
|
-
/** Workflow state name after the tick —
|
|
45
|
+
/** Workflow state name after the tick — Agent persists this against the session. */
|
|
44
46
|
state: string;
|
|
45
47
|
/** Whether the machine settled at a parked state. */
|
|
46
48
|
parked: boolean;
|
|
47
|
-
/** True when a state-timeout outcome matched no transition this tick — the
|
|
49
|
+
/** True when a state-timeout outcome matched no transition this tick — the agent must NOT re-arm. */
|
|
48
50
|
stateTimeoutUnhandled: boolean;
|
|
49
51
|
/**
|
|
50
52
|
* Accumulated LLM call count and token usage for this tick.
|
|
@@ -64,10 +66,10 @@ export interface TickOutput {
|
|
|
64
66
|
};
|
|
65
67
|
}
|
|
66
68
|
/**
|
|
67
|
-
*
|
|
68
|
-
* no services. The
|
|
69
|
-
* on each `tick`.
|
|
70
|
-
* `
|
|
69
|
+
* Workflow brain: workflow definition + cognition machinery. No I/O, no transport,
|
|
70
|
+
* no services. The brain's only side channel is the `eff` handler it receives
|
|
71
|
+
* on each `tick`. Body state (sessions, lanes, queue, outbox) lives in
|
|
72
|
+
* `Agent` — not here.
|
|
71
73
|
*
|
|
72
74
|
* The contract:
|
|
73
75
|
*
|
|
@@ -75,13 +77,14 @@ export interface TickOutput {
|
|
|
75
77
|
* `{ ctx, state, parked }`
|
|
76
78
|
*
|
|
77
79
|
* Reads top-to-bottom like a synchronous function. Every `await eff(...)`
|
|
78
|
-
* inside the cognition core is a yield to the
|
|
79
|
-
* impure operation and resumes the
|
|
80
|
+
* inside the cognition core is a yield to the body, which performs the
|
|
81
|
+
* impure operation and resumes the ZiggsBrain with the result.
|
|
80
82
|
*/
|
|
81
|
-
export declare class
|
|
83
|
+
export declare class ZiggsBrain implements Brain {
|
|
84
|
+
readonly kind: "ziggs";
|
|
82
85
|
workflow: Workflow;
|
|
83
86
|
toolManager: ToolManager;
|
|
84
87
|
promptBuilder: PromptBuilder;
|
|
85
|
-
constructor({ workflow, toolManager, promptBuilder }:
|
|
88
|
+
constructor({ workflow, toolManager, promptBuilder }: ZiggsBrainOptions);
|
|
86
89
|
tick({ event, ctx, state, identity, eff, memory, transitionTap }: TickInput): Promise<TickOutput>;
|
|
87
90
|
}
|