@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
|
@@ -1,30 +1,34 @@
|
|
|
1
|
+
import { agreementLaneId, agreementIdFromLane } from '@ziggs-ai/api-client';
|
|
2
|
+
import { createDeclaredPollRunner } from './platform/polls/declaredPollRunner.js';
|
|
1
3
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { CLAUDE_SDK_TOOLS } from './
|
|
6
|
-
import { resolveClaudeModel } from './
|
|
7
|
-
import { CHAT_TOOLS } from './
|
|
8
|
-
import { EventQueue } from './
|
|
9
|
-
import { StateTimers } from './
|
|
10
|
-
import { createZiggsEffectHandler } from './
|
|
4
|
+
import { ZiggsBrain } from './brain/ZiggsBrain.js';
|
|
5
|
+
import { ClaudeBrain } from './brain/ClaudeBrain.js';
|
|
6
|
+
import { applyBrainToAgentOptions, isClaudeCognition, } from './brain/index.js';
|
|
7
|
+
import { CLAUDE_SDK_TOOLS } from './brain/claudeSdkTools.js';
|
|
8
|
+
import { resolveClaudeModel } from './brain/claudeCognition.js';
|
|
9
|
+
import { CHAT_TOOLS } from './platform/tools/tier2/chatTools.js';
|
|
10
|
+
import { EventQueue } from './platform/EventQueue.js';
|
|
11
|
+
import { StateTimers } from './platform/StateTimers.js';
|
|
12
|
+
import { createZiggsEffectHandler } from './platform/ZiggsEffectHandler.js';
|
|
11
13
|
import { normalizeIncomingEvent } from './ingress/normalizeIncoming.js';
|
|
12
14
|
import { PromptBuilder } from './runtime/PromptBuilder.js';
|
|
13
|
-
import { InMemoryStore } from './memory/MemoryStore.js';
|
|
15
|
+
import { InMemoryStore, memorySectionForWake, partitionMemoryStore, } from './memory/MemoryStore.js';
|
|
14
16
|
import { ToolManager } from './tools/ToolManager.js';
|
|
15
17
|
import { toolName } from './tools/defineTool.js';
|
|
16
|
-
import { PROTOCOL_TOOLS } from './
|
|
17
|
-
import { marketplaceViewTool } from './
|
|
18
|
+
import { PROTOCOL_TOOLS } from './platform/tools/tier1/protocolTools.js';
|
|
19
|
+
import { marketplaceViewTool } from './platform/tools/tier2/marketplaceTools.js';
|
|
18
20
|
import { OpenAIAdapter } from './adapters/OpenAIAdapter.js';
|
|
19
21
|
import { AnthropicAdapter } from './adapters/AnthropicAdapter.js';
|
|
20
22
|
import { ArtifactsClient, ConnectionsClient, ContextReadClient, TelemetryClient, sendChatMessage, } from '@ziggs-ai/api-client';
|
|
21
23
|
import { normalizeNeeds, unmetNeeds, unmetNeedsMessage, } from './tools/needs.js';
|
|
22
|
-
import { TaskService } from './
|
|
23
|
-
import { AgreementService } from './
|
|
24
|
-
import { runInboxLoop } from './
|
|
25
|
-
import { requestExactMatch } from './
|
|
26
|
-
import { SeenMessages } from './
|
|
24
|
+
import { TaskService } from './platform/tasks/TaskService.js';
|
|
25
|
+
import { AgreementService } from './platform/agreements/AgreementService.js';
|
|
26
|
+
import { runInboxLoop } from './platform/inboxLoop.js';
|
|
27
|
+
import { requestExactMatch } from './platform/requestMatch.js';
|
|
28
|
+
import { SeenMessages } from './platform/SeenMessages.js';
|
|
27
29
|
import { runtimeLog } from './shared/runtimeLog.js';
|
|
30
|
+
import { isTelemetryIngestEnabled, sendTelemetry, } from './platform/telemetryIngest.js';
|
|
31
|
+
import { buildWakeOutcomePayload, classifyWakeOutcome, } from './runtime/wakeOutcome.js';
|
|
28
32
|
/**
|
|
29
33
|
* The single fleet-wide default model — the one place the fallback lives.
|
|
30
34
|
* Change this (or set `LLM_MODEL`) to move the whole fleet; nothing hardcodes a
|
|
@@ -34,9 +38,9 @@ import { runtimeLog } from './shared/runtimeLog.js';
|
|
|
34
38
|
*/
|
|
35
39
|
export const DEFAULT_MODEL = 'claude-haiku-4-5';
|
|
36
40
|
// ConnectionPool lives in a separate module; resolved via a computed-path
|
|
37
|
-
// dynamic import to avoid a circular import with
|
|
41
|
+
// dynamic import to avoid a circular import with Agent.
|
|
38
42
|
async function loadConnectionPool() {
|
|
39
|
-
const modPath = './
|
|
43
|
+
const modPath = './platform/ConnectionPool.js';
|
|
40
44
|
const mod = await import(/* @vite-ignore */ modPath);
|
|
41
45
|
return mod.ConnectionPool;
|
|
42
46
|
}
|
|
@@ -55,10 +59,30 @@ export function laneSessionIdForTask(task) {
|
|
|
55
59
|
return task.originChatId;
|
|
56
60
|
}
|
|
57
61
|
if (typeof task.agreementId === 'string' && task.agreementId) {
|
|
58
|
-
return
|
|
62
|
+
return agreementLaneId(task.agreementId);
|
|
59
63
|
}
|
|
60
64
|
return 'default';
|
|
61
65
|
}
|
|
66
|
+
/** Pull the cost-record keys off a wake event or session id. */
|
|
67
|
+
export function workScopeFromWake(event, sessionId) {
|
|
68
|
+
const rec = event && typeof event === 'object' ? event : {};
|
|
69
|
+
const resource = rec.resource && typeof rec.resource === 'object'
|
|
70
|
+
? rec.resource
|
|
71
|
+
: {};
|
|
72
|
+
const agreementId = (typeof rec.agreementId === 'string' && rec.agreementId) ||
|
|
73
|
+
(typeof resource.agreementId === 'string' && resource.agreementId) ||
|
|
74
|
+
agreementIdFromLane(rec.chatId) ||
|
|
75
|
+
agreementIdFromLane(resource.chatId) ||
|
|
76
|
+
agreementIdFromLane(sessionId) ||
|
|
77
|
+
undefined;
|
|
78
|
+
const taskId = (typeof rec.taskId === 'string' && rec.taskId) ||
|
|
79
|
+
(typeof resource.taskId === 'string' && resource.taskId) ||
|
|
80
|
+
undefined;
|
|
81
|
+
return {
|
|
82
|
+
...(agreementId ? { agreementId } : {}),
|
|
83
|
+
...(taskId ? { taskId } : {}),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
62
86
|
function createToolManager(options) {
|
|
63
87
|
const toolManager = new ToolManager();
|
|
64
88
|
const userTools = options.tools || options.services?.tools || [];
|
|
@@ -105,7 +129,7 @@ export function warnOnShadowedPlatformTools(platformTools, ownTools, agentId) {
|
|
|
105
129
|
.filter((name) => platform.has(name));
|
|
106
130
|
if (shadowed.length === 0)
|
|
107
131
|
return;
|
|
108
|
-
console.warn(`[
|
|
132
|
+
console.warn(`[Agent] ${agentId}: tool name(s) ${shadowed.join(', ')} shadow the platform tools of the same name — ` +
|
|
109
133
|
'the agent will call yours, not Ziggs\'s. Rename them unless that is deliberate.');
|
|
110
134
|
}
|
|
111
135
|
function isLlmAdapter(v) {
|
|
@@ -151,19 +175,18 @@ function createBackendServices(options) {
|
|
|
151
175
|
};
|
|
152
176
|
}
|
|
153
177
|
/**
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
* transport (WS) → handleMessage → agent.tick(.. , eff) → ..
|
|
178
|
+
* One agent identity and its body: inbox, tools, contracts, memory and lanes.
|
|
179
|
+
* The brain decides on each wake; platform services perform its effects.
|
|
180
|
+
* connectAsync starts outbound inbox reads and declared external polls.
|
|
181
|
+
* An Agent never opens a listening socket.
|
|
159
182
|
*/
|
|
160
|
-
export class
|
|
183
|
+
export class Agent {
|
|
161
184
|
options;
|
|
162
185
|
toolManager;
|
|
163
186
|
// Retained for external inspection. Undefined for the Claude brain, which
|
|
164
187
|
// drives query() directly and needs no LLM adapter.
|
|
165
188
|
llm;
|
|
166
|
-
|
|
189
|
+
brain;
|
|
167
190
|
eff;
|
|
168
191
|
eventQueue;
|
|
169
192
|
sessions = new Map();
|
|
@@ -173,12 +196,15 @@ export class AgentHost {
|
|
|
173
196
|
sessionStore;
|
|
174
197
|
taskService;
|
|
175
198
|
agreementService;
|
|
199
|
+
telemetryClient;
|
|
176
200
|
messageSender;
|
|
177
201
|
seenMessages = new SeenMessages();
|
|
178
202
|
stateTimers;
|
|
179
203
|
/** The per-agent inbox long-poll loop — this agent's sole wake source. */
|
|
180
204
|
inboxLoop = null;
|
|
181
205
|
inboxLoopEnabled;
|
|
206
|
+
started = false;
|
|
207
|
+
pollRunner;
|
|
182
208
|
/** Short-lived grant-provider cache for the declared-needs gate. */
|
|
183
209
|
/**
|
|
184
210
|
* Lanes where an unmet-need refusal has already been allowed its one turn to
|
|
@@ -190,7 +216,7 @@ export class AgentHost {
|
|
|
190
216
|
unmetTasksPendingAsk = new Map();
|
|
191
217
|
grantProviderCache = null;
|
|
192
218
|
constructor(options = {}) {
|
|
193
|
-
const resolved =
|
|
219
|
+
const resolved = applyBrainToAgentOptions(options);
|
|
194
220
|
this.options = {
|
|
195
221
|
openaiKey: resolved.openaiKey || process.env.OPENAI_API_KEY,
|
|
196
222
|
anthropicKey: resolved.anthropicKey || process.env.ANTHROPIC_API_KEY,
|
|
@@ -223,6 +249,8 @@ export class AgentHost {
|
|
|
223
249
|
inboxCatchUp: resolved.inboxCatchUp,
|
|
224
250
|
telemetryIngest: resolved.telemetryIngest,
|
|
225
251
|
brain: resolved.brain,
|
|
252
|
+
polls: resolved.polls,
|
|
253
|
+
pollKinds: resolved.pollKinds,
|
|
226
254
|
cognition: resolved.cognition ?? 'fsm',
|
|
227
255
|
claudeMaxTurns: resolved.claudeMaxTurns,
|
|
228
256
|
claudeStatelessPerWake: resolved.claudeStatelessPerWake,
|
|
@@ -235,12 +263,15 @@ export class AgentHost {
|
|
|
235
263
|
// default.
|
|
236
264
|
this.tickTimeoutMs =
|
|
237
265
|
options.tickTimeoutMs ??
|
|
238
|
-
(isClaudeCognition(
|
|
266
|
+
(isClaudeCognition(resolved.cognition)
|
|
239
267
|
? Number(process.env.CLAUDE_SDK_TICK_TIMEOUT_MS ?? 300_000)
|
|
240
268
|
: 120_000);
|
|
241
269
|
this.memory = options.memory ?? new InMemoryStore();
|
|
242
270
|
this.sessionStore = options.sessionStore ?? null;
|
|
243
271
|
this._validate();
|
|
272
|
+
this.pollRunner = createDeclaredPollRunner(this.options, {
|
|
273
|
+
sendTo: (_agentId, text, metadata) => this.handleMessage(text, metadata ?? {}),
|
|
274
|
+
});
|
|
244
275
|
this._setup();
|
|
245
276
|
}
|
|
246
277
|
_validate() {
|
|
@@ -290,6 +321,7 @@ export class AgentHost {
|
|
|
290
321
|
}, creds);
|
|
291
322
|
this.taskService = services.taskService;
|
|
292
323
|
this.agreementService = services.agreementService;
|
|
324
|
+
this.telemetryClient = services.telemetryClient;
|
|
293
325
|
this.messageSender = messageSender;
|
|
294
326
|
let workflow = this.options.workflow;
|
|
295
327
|
if (!workflow && this.options.states) {
|
|
@@ -302,7 +334,7 @@ export class AgentHost {
|
|
|
302
334
|
}
|
|
303
335
|
if (!workflow?.states) {
|
|
304
336
|
// The Claude brain has no FSM — it drives query() per wake and parks. It
|
|
305
|
-
// still plugs into the
|
|
337
|
+
// still plugs into the agent's lane/timer machinery via a trivial parked
|
|
306
338
|
// workflow, synthesized here so callers don't hand-write a dummy one.
|
|
307
339
|
if (isClaudeCognition(this.options.cognition)) {
|
|
308
340
|
workflow = {
|
|
@@ -313,7 +345,7 @@ export class AgentHost {
|
|
|
313
345
|
};
|
|
314
346
|
}
|
|
315
347
|
else {
|
|
316
|
-
throw new Error('
|
|
348
|
+
throw new Error('Agent: workflow (or states + initial) is required');
|
|
317
349
|
}
|
|
318
350
|
}
|
|
319
351
|
this.eff = createZiggsEffectHandler({
|
|
@@ -330,8 +362,8 @@ export class AgentHost {
|
|
|
330
362
|
telemetryIngest: this.options.telemetryIngest,
|
|
331
363
|
effectTap: this.options.effectTap,
|
|
332
364
|
});
|
|
333
|
-
this.
|
|
334
|
-
? new
|
|
365
|
+
this.brain = isClaudeCognition(this.options.cognition)
|
|
366
|
+
? new ClaudeBrain({
|
|
335
367
|
workflow,
|
|
336
368
|
toolManager: services.toolManager,
|
|
337
369
|
description: this.options.description,
|
|
@@ -347,7 +379,7 @@ export class AgentHost {
|
|
|
347
379
|
maxTurns: this.options.claudeMaxTurns,
|
|
348
380
|
statelessPerWake: this.options.claudeStatelessPerWake,
|
|
349
381
|
})
|
|
350
|
-
: new
|
|
382
|
+
: new ZiggsBrain({
|
|
351
383
|
workflow,
|
|
352
384
|
toolManager: services.toolManager,
|
|
353
385
|
promptBuilder: services.promptBuilder,
|
|
@@ -358,7 +390,7 @@ export class AgentHost {
|
|
|
358
390
|
// than left as an unhandled rejection (made enqueue reject).
|
|
359
391
|
this.stateTimers = new StateTimers((ev, laneKey) => {
|
|
360
392
|
void this.eventQueue.enqueue(ev, laneKey).catch((err) => {
|
|
361
|
-
runtimeLog.warn('
|
|
393
|
+
runtimeLog.warn('Agent', `state-timer frame failed laneKey=${laneKey}: ${err?.message || err}`);
|
|
362
394
|
});
|
|
363
395
|
});
|
|
364
396
|
}
|
|
@@ -400,7 +432,7 @@ export class AgentHost {
|
|
|
400
432
|
event.resourceId) ||
|
|
401
433
|
null;
|
|
402
434
|
if (agreementId)
|
|
403
|
-
sessionId =
|
|
435
|
+
sessionId = agreementLaneId(agreementId);
|
|
404
436
|
}
|
|
405
437
|
if (!sessionId)
|
|
406
438
|
return;
|
|
@@ -427,14 +459,14 @@ export class AgentHost {
|
|
|
427
459
|
async handleMessage(text, metadata = {}) {
|
|
428
460
|
const messageId = metadata.messageId;
|
|
429
461
|
if (messageId && this.seenMessages.has(messageId)) {
|
|
430
|
-
runtimeLog.debug('
|
|
462
|
+
runtimeLog.debug('Agent', `skip duplicate messageId=${messageId}`);
|
|
431
463
|
return;
|
|
432
464
|
}
|
|
433
465
|
if (messageId)
|
|
434
466
|
this.seenMessages.mark(messageId);
|
|
435
467
|
const ownAgentId = this._resolveOwnAgentId();
|
|
436
468
|
if (!ownAgentId) {
|
|
437
|
-
runtimeLog.warn('
|
|
469
|
+
runtimeLog.warn('Agent', 'handleMessage: ownAgentId missing — dropping event');
|
|
438
470
|
return;
|
|
439
471
|
}
|
|
440
472
|
let enrichedText = typeof text === 'string' ? text : '';
|
|
@@ -454,14 +486,14 @@ export class AgentHost {
|
|
|
454
486
|
const normalized = normalizeIncomingEvent({ text: enrichedText, metadata, ownAgentId });
|
|
455
487
|
const sessionId = this.sessionResolver(normalized, ownAgentId);
|
|
456
488
|
if (!sessionId) {
|
|
457
|
-
runtimeLog.warn('
|
|
489
|
+
runtimeLog.warn('Agent', `session resolver returned null ownAgentId=${ownAgentId} chatId=${normalized.chatId ?? 'null'}`);
|
|
458
490
|
return;
|
|
459
491
|
}
|
|
460
492
|
const laneKey = `${ownAgentId}:${sessionId}`;
|
|
461
493
|
if (!this.sessions.has(laneKey)) {
|
|
462
494
|
this.sessions.set(laneKey, { sessionId, ownAgentId });
|
|
463
495
|
}
|
|
464
|
-
// The inbox loop owns ack (act-before-ack, delivery law); the
|
|
496
|
+
// The inbox loop owns ack (act-before-ack, delivery law); the agent only
|
|
465
497
|
// routes the event onto its lane. No live-path watermark ack — there is no
|
|
466
498
|
// separate live/push channel to reconcile against anymore.
|
|
467
499
|
//
|
|
@@ -494,10 +526,55 @@ export class AgentHost {
|
|
|
494
526
|
* Failing open is deliberate. If the lookup errors we run the turn — a missed
|
|
495
527
|
* suppression costs tokens, while a wrongly suppressed turn drops work.
|
|
496
528
|
*/
|
|
529
|
+
recordWakeOutcome(session, outcome, llmCalls = 0) {
|
|
530
|
+
const agentId = session.ownAgentId || this.options.agentId;
|
|
531
|
+
const laneKey = `${agentId}:${session.sessionId}`;
|
|
532
|
+
runtimeLog.info(this.options.name || 'agent', `wake-outcome agent=${agentId} lane=${laneKey} kind=${outcome.kind}` +
|
|
533
|
+
(outcome.reason ? ` reason=${outcome.reason}` : ''));
|
|
534
|
+
sendTelemetry(this.telemetryClient, buildWakeOutcomePayload({
|
|
535
|
+
agentId,
|
|
536
|
+
sessionId: session.sessionId,
|
|
537
|
+
laneKey,
|
|
538
|
+
outcome,
|
|
539
|
+
llmCalls,
|
|
540
|
+
}), isTelemetryIngestEnabled(this.options.telemetryIngest));
|
|
541
|
+
}
|
|
542
|
+
memoryForWake(session) {
|
|
543
|
+
return partitionMemoryStore(this.memory, memorySectionForWake({
|
|
544
|
+
sessionId: session.sessionId,
|
|
545
|
+
activeAgreementId: session.ctx
|
|
546
|
+
?.activeAgreementId,
|
|
547
|
+
}));
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* A chat-lane session that still carries a cancelled hire from a previous
|
|
551
|
+
* engagement will agreement_get that dead row and stop. Drop it so this
|
|
552
|
+
* wake keys on the live room / hire.
|
|
553
|
+
*/
|
|
554
|
+
async dropStaleCancelledAgreementSession(session) {
|
|
555
|
+
const ctx = session.ctx;
|
|
556
|
+
const pinned = ctx?.activeAgreementId;
|
|
557
|
+
if (!pinned || !this.agreementService)
|
|
558
|
+
return;
|
|
559
|
+
if (session.sessionId === agreementLaneId(pinned))
|
|
560
|
+
return;
|
|
561
|
+
try {
|
|
562
|
+
const agreement = await this.agreementService.get(pinned);
|
|
563
|
+
const status = String(agreement?.status ?? '');
|
|
564
|
+
if (status === 'cancelled' ||
|
|
565
|
+
status === 'refunded' ||
|
|
566
|
+
status === 'fulfilled') {
|
|
567
|
+
runtimeLog.info(this.options.name || 'agent', `dropping session pinned to terminal agreement ${pinned} lane=${session.sessionId}`);
|
|
568
|
+
ctx.activeAgreementId = null;
|
|
569
|
+
ctx.claudeSessionId = undefined;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
catch {
|
|
573
|
+
// Fail open: a lookup error must not drop a live hire's session.
|
|
574
|
+
}
|
|
575
|
+
}
|
|
497
576
|
async laneAgreementIsTerminal(sessionId) {
|
|
498
|
-
|
|
499
|
-
return false;
|
|
500
|
-
const agreementId = sessionId.slice('agrn-'.length);
|
|
577
|
+
const agreementId = agreementIdFromLane(sessionId);
|
|
501
578
|
if (!agreementId || !this.agreementService)
|
|
502
579
|
return false;
|
|
503
580
|
try {
|
|
@@ -693,10 +770,12 @@ export class AgentHost {
|
|
|
693
770
|
return;
|
|
694
771
|
if (await this.laneAgreementIsTerminal(session.sessionId)) {
|
|
695
772
|
runtimeLog.info(this.options.name || 'agent', `skipping wake on ${session.sessionId} — its agreement is terminal`);
|
|
773
|
+
this.recordWakeOutcome(session, { kind: 'no-op', reason: 'agreement_terminal' });
|
|
696
774
|
return;
|
|
697
775
|
}
|
|
698
776
|
if (this.isSettledTaskEcho(event)) {
|
|
699
777
|
runtimeLog.info(this.options.name || 'agent', `skipping wake on ${session.sessionId} — its task already reported a result`);
|
|
778
|
+
this.recordWakeOutcome(session, { kind: 'no-op', reason: 'task_already_settled' });
|
|
700
779
|
return;
|
|
701
780
|
}
|
|
702
781
|
// Before the tick spends anything: work this agent declared it cannot do
|
|
@@ -722,14 +801,16 @@ export class AgentHost {
|
|
|
722
801
|
}
|
|
723
802
|
}
|
|
724
803
|
catch (err) {
|
|
725
|
-
runtimeLog.warn('
|
|
804
|
+
runtimeLog.warn('Agent', `sessionStore.load failed laneKey=${laneKey}: ${err?.message || err}`);
|
|
726
805
|
}
|
|
727
806
|
}
|
|
807
|
+
await this.dropStaleCancelledAgreementSession(session);
|
|
728
808
|
const identity = {
|
|
729
809
|
agentId: session.ownAgentId || this.options.agentId,
|
|
730
810
|
sessionId: session.sessionId,
|
|
731
811
|
laneKey,
|
|
732
812
|
};
|
|
813
|
+
const memory = this.memoryForWake(session);
|
|
733
814
|
// Recorded here rather than after the tick: this is the last point where the
|
|
734
815
|
// input is still exactly what the brain will see, and a wake that goes on to
|
|
735
816
|
// throw is the one most worth replaying.
|
|
@@ -745,7 +826,7 @@ export class AgentHost {
|
|
|
745
826
|
});
|
|
746
827
|
}
|
|
747
828
|
catch (err) {
|
|
748
|
-
runtimeLog.warn('
|
|
829
|
+
runtimeLog.warn('Agent', `wakeTap failed: ${err?.message || err}`);
|
|
749
830
|
}
|
|
750
831
|
}
|
|
751
832
|
// The watchdog timer must be cleared once the tick settles — leaving it
|
|
@@ -757,9 +838,20 @@ export class AgentHost {
|
|
|
757
838
|
// a zombie — burning tokens and racing the redelivered retry of the same
|
|
758
839
|
// event (duplicate sends, runaway per-wake input).
|
|
759
840
|
const tickAbort = new AbortController();
|
|
841
|
+
const marks = { sent: 0, tasks: 0 };
|
|
842
|
+
const eff = async (effect) => {
|
|
843
|
+
if (effect.kind === 'send-message')
|
|
844
|
+
marks.sent += 1;
|
|
845
|
+
if (effect.kind === 'tool-call') {
|
|
846
|
+
const name = 'name' in effect ? String(effect.name) : '';
|
|
847
|
+
if (name.startsWith('task_'))
|
|
848
|
+
marks.tasks += 1;
|
|
849
|
+
}
|
|
850
|
+
return this.eff(effect);
|
|
851
|
+
};
|
|
760
852
|
try {
|
|
761
853
|
const result = await Promise.race([
|
|
762
|
-
this.
|
|
854
|
+
this.brain.tick({ event: frame, ctx: session.ctx, state: session.state, identity, eff, memory, transitionTap: this.options.transitionTap, signal: tickAbort.signal }),
|
|
763
855
|
new Promise((_, reject) => {
|
|
764
856
|
tickWatchdog = setTimeout(() => {
|
|
765
857
|
tickAbort.abort(new Error('tick timeout'));
|
|
@@ -770,6 +862,13 @@ export class AgentHost {
|
|
|
770
862
|
session.ctx = result.ctx;
|
|
771
863
|
session.state = result.state;
|
|
772
864
|
this.sessions.set(laneKey, session);
|
|
865
|
+
this.recordWakeOutcome(session, classifyWakeOutcome({
|
|
866
|
+
lastOutcomeKind: result.ctx
|
|
867
|
+
?.lastOutcome?.kind,
|
|
868
|
+
llmCalls: result.metrics?.llmCalls ?? 0,
|
|
869
|
+
sentMessage: marks.sent > 0,
|
|
870
|
+
taskWrite: marks.tasks > 0,
|
|
871
|
+
}), result.metrics?.llmCalls ?? 0);
|
|
773
872
|
// One line per wake that actually spent LLM budget — the handle for
|
|
774
873
|
// tracking a hosted agent live (grep `agent=<id>`). Parked no-op ticks
|
|
775
874
|
// (FSM exits, wake-rate-skipped Claude wakes) report zero tokens and
|
|
@@ -777,7 +876,7 @@ export class AgentHost {
|
|
|
777
876
|
// these lines for one lane. Tokens are billable input (incl. cache).
|
|
778
877
|
const m = result.metrics;
|
|
779
878
|
if (m && (m.tokens?.total ?? 0) > 0) {
|
|
780
|
-
runtimeLog.info('
|
|
879
|
+
runtimeLog.info('Agent', `wake agent=${identity.agentId} lane=${laneKey} llmCalls=${m.llmCalls} ` +
|
|
781
880
|
`tokens_in=${m.tokens.input} tokens_out=${m.tokens.output} total=${m.tokens.total}`);
|
|
782
881
|
}
|
|
783
882
|
// Single source of truth for usage/cost: emit the tick's
|
|
@@ -791,15 +890,16 @@ export class AgentHost {
|
|
|
791
890
|
model: this.options.model ?? 'unknown',
|
|
792
891
|
cognition: this.options.cognition ?? 'fsm',
|
|
793
892
|
metrics: result.metrics,
|
|
893
|
+
...workScopeFromWake(event, session.sessionId),
|
|
794
894
|
});
|
|
795
895
|
}
|
|
796
896
|
catch (err) {
|
|
797
|
-
runtimeLog.warn('
|
|
897
|
+
runtimeLog.warn('Agent', `metricsTap failed: ${err?.message || err}`);
|
|
798
898
|
}
|
|
799
899
|
}
|
|
800
900
|
if (this.sessionStore) {
|
|
801
901
|
this.sessionStore.save(laneKey, { ctx: session.ctx, state: session.state }).catch((err) => {
|
|
802
|
-
runtimeLog.warn('
|
|
902
|
+
runtimeLog.warn('Agent', `sessionStore.save failed laneKey=${laneKey}: ${err?.message || err}`);
|
|
803
903
|
});
|
|
804
904
|
}
|
|
805
905
|
// Arm only when the timeout was handled: an unhandled state-timeout must
|
|
@@ -824,12 +924,16 @@ export class AgentHost {
|
|
|
824
924
|
}
|
|
825
925
|
}
|
|
826
926
|
catch (err) {
|
|
827
|
-
|
|
927
|
+
this.recordWakeOutcome(session, {
|
|
928
|
+
kind: 'no-op',
|
|
929
|
+
reason: 'tick_failed',
|
|
930
|
+
});
|
|
931
|
+
runtimeLog.warn('Agent', `tick failed laneKey=${laneKey}: ${err?.message || err}`);
|
|
828
932
|
// Deliberate re-arm on failure. Exact scenario: the tick timed out at
|
|
829
933
|
// tickTimeoutMs (or sessionStore.load threw) leaving session.state at a
|
|
830
934
|
// timed parked state — without re-arming, that lane would never wake
|
|
831
935
|
// again unless a real event happens to arrive.
|
|
832
|
-
this._armIfTimedParked(laneKey, session.state ?? this.
|
|
936
|
+
this._armIfTimedParked(laneKey, session.state ?? this.brain.workflow.initial);
|
|
833
937
|
// Best-effort: also notify the user so the conversation isn't silently
|
|
834
938
|
// stuck. Complements the timer re-arm above — that recovers a
|
|
835
939
|
// parked lane; this tells a waiting human.
|
|
@@ -855,12 +959,12 @@ export class AgentHost {
|
|
|
855
959
|
}
|
|
856
960
|
}
|
|
857
961
|
/**
|
|
858
|
-
* Re-run one recorded wake against THIS
|
|
962
|
+
* Re-run one recorded wake against THIS agent, and nothing else.
|
|
859
963
|
*
|
|
860
964
|
* The dev loop for a prompt: record a wake off a live run, edit the agent, and
|
|
861
965
|
* see what it does differently in seconds instead of standing a world back up
|
|
862
966
|
* for one turn. The brain, tools and effect handler are the real ones, so the
|
|
863
|
-
* turn is real — its tool calls hit the backend this
|
|
967
|
+
* turn is real — its tool calls hit the backend this agent is pointed at, which
|
|
864
968
|
* is why it belongs against a disposable world and not a shared one.
|
|
865
969
|
*
|
|
866
970
|
* Deliberately NOT routed through `_processEvent`: everything that method does
|
|
@@ -876,19 +980,23 @@ export class AgentHost {
|
|
|
876
980
|
sessionId: record.sessionId,
|
|
877
981
|
laneKey: record.laneKey || record.sessionId,
|
|
878
982
|
};
|
|
983
|
+
const memory = this.memoryForWake({
|
|
984
|
+
sessionId: record.sessionId,
|
|
985
|
+
ctx: record.ctx,
|
|
986
|
+
});
|
|
879
987
|
// Same watchdog as a live wake, for the same reason: an unaborted Claude
|
|
880
988
|
// query outlives the promise that abandoned it and keeps spending.
|
|
881
989
|
const abort = new AbortController();
|
|
882
990
|
let watchdog;
|
|
883
991
|
try {
|
|
884
992
|
const result = await Promise.race([
|
|
885
|
-
this.
|
|
993
|
+
this.brain.tick({
|
|
886
994
|
event: record.event,
|
|
887
995
|
ctx: record.ctx,
|
|
888
996
|
state: record.state,
|
|
889
997
|
identity,
|
|
890
998
|
eff: this.eff,
|
|
891
|
-
memory
|
|
999
|
+
memory,
|
|
892
1000
|
transitionTap: this.options.transitionTap,
|
|
893
1001
|
signal: abort.signal,
|
|
894
1002
|
}),
|
|
@@ -907,10 +1015,11 @@ export class AgentHost {
|
|
|
907
1015
|
model: this.options.model ?? 'unknown',
|
|
908
1016
|
cognition: this.options.cognition ?? 'fsm',
|
|
909
1017
|
metrics: result.metrics,
|
|
1018
|
+
...workScopeFromWake(record.event, identity.sessionId),
|
|
910
1019
|
});
|
|
911
1020
|
}
|
|
912
1021
|
catch (err) {
|
|
913
|
-
runtimeLog.warn('
|
|
1022
|
+
runtimeLog.warn('Agent', `metricsTap failed: ${err?.message || err}`);
|
|
914
1023
|
}
|
|
915
1024
|
}
|
|
916
1025
|
return result;
|
|
@@ -923,25 +1032,33 @@ export class AgentHost {
|
|
|
923
1032
|
_armIfTimedParked(laneKey, stateName) {
|
|
924
1033
|
if (!stateName)
|
|
925
1034
|
return;
|
|
926
|
-
const sd = this.
|
|
1035
|
+
const sd = this.brain.workflow.states[stateName];
|
|
927
1036
|
if (sd?.kind !== 'parked' || sd.timeoutMs === undefined)
|
|
928
1037
|
return;
|
|
929
1038
|
this.stateTimers.arm(laneKey, stateName, sd.timeoutMs);
|
|
930
1039
|
}
|
|
931
1040
|
_resolveOwnAgentId() {
|
|
932
|
-
// The
|
|
1041
|
+
// The body is constructed for exactly one identity; its own id is the only
|
|
933
1042
|
// source. (The socket-frame-era metadata fallbacks — self/agent/ziggsAgentId
|
|
934
1043
|
// — are gone: nothing on the inbox wire ever carried them.)
|
|
935
1044
|
return this.options.agentId || null;
|
|
936
1045
|
}
|
|
937
1046
|
async connectAsync(_timeout) {
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
//
|
|
941
|
-
//
|
|
942
|
-
//
|
|
1047
|
+
if (this.started)
|
|
1048
|
+
return this;
|
|
1049
|
+
// Start the per-agent inbox long-poll loop — the agent's sole wake source.
|
|
1050
|
+
// There is no socket to connect and no reconnect: a crash is just the
|
|
1051
|
+
// loop's next iteration (the cursor is server-side). `_timeout` is accepted
|
|
1052
|
+
// for backward-compatible call sites but no longer gates a handshake.
|
|
1053
|
+
//
|
|
1054
|
+
// There is no separate catch-up sweep here any more. `tasksAwaitingMe` is
|
|
1055
|
+
// re-derived from the collection on every pass and redelivers an open
|
|
1056
|
+
// assigned task until it closes, so the loop's first pass IS the catch-up —
|
|
1057
|
+
// a restarted agent sees its standing work with nothing extra to run. The
|
|
1058
|
+
// sweep that used to sit here pulled the same list over
|
|
1059
|
+
// `GET /agents/:id/tasks` and reached one gate the inbox does not, which is
|
|
1060
|
+
// how a withheld join node got handed to its assignee before its turn.
|
|
943
1061
|
if (this.inboxLoopEnabled) {
|
|
944
|
-
await this._bootstrapAssignedActiveTasks();
|
|
945
1062
|
const label = this.options.name || 'agent';
|
|
946
1063
|
this.inboxLoop = runInboxLoop({
|
|
947
1064
|
creds: {
|
|
@@ -974,10 +1091,12 @@ export class AgentHost {
|
|
|
974
1091
|
},
|
|
975
1092
|
});
|
|
976
1093
|
}
|
|
1094
|
+
this.pollRunner?.start();
|
|
1095
|
+
this.started = true;
|
|
977
1096
|
return this;
|
|
978
1097
|
}
|
|
979
1098
|
/**
|
|
980
|
-
* plain string compare against {@link
|
|
1099
|
+
* plain string compare against {@link AgentOptions.requestMatchTags}
|
|
981
1100
|
* and `specialization`. No match → return (caller acks). Match → normal wake.
|
|
982
1101
|
*/
|
|
983
1102
|
async triageRequestDelivery(request) {
|
|
@@ -997,52 +1116,6 @@ export class AgentHost {
|
|
|
997
1116
|
agreementId: request.agreementId,
|
|
998
1117
|
});
|
|
999
1118
|
}
|
|
1000
|
-
async _bootstrapAssignedActiveTasks() {
|
|
1001
|
-
const agentId = this.options.agentId;
|
|
1002
|
-
if (!agentId || !this.taskService)
|
|
1003
|
-
return;
|
|
1004
|
-
let tasks = [];
|
|
1005
|
-
try {
|
|
1006
|
-
tasks = await this.taskService.getActiveTasksForAgent(agentId);
|
|
1007
|
-
}
|
|
1008
|
-
catch (err) {
|
|
1009
|
-
runtimeLog.warn(this.options.name || 'agent', `active-task bootstrap skipped: ${err?.message ?? err}`);
|
|
1010
|
-
return;
|
|
1011
|
-
}
|
|
1012
|
-
for (const row of tasks) {
|
|
1013
|
-
const taskId = row.taskId;
|
|
1014
|
-
if (!taskId)
|
|
1015
|
-
continue;
|
|
1016
|
-
if (row.assigneeId && row.assigneeId !== agentId)
|
|
1017
|
-
continue;
|
|
1018
|
-
let full;
|
|
1019
|
-
try {
|
|
1020
|
-
full = (await this.taskService.getTask(taskId));
|
|
1021
|
-
}
|
|
1022
|
-
catch {
|
|
1023
|
-
continue;
|
|
1024
|
-
}
|
|
1025
|
-
const state = String(full.state ?? '');
|
|
1026
|
-
if (state !== 'active' && state !== 'in-progress')
|
|
1027
|
-
continue;
|
|
1028
|
-
const sessionId = laneSessionIdForTask(full);
|
|
1029
|
-
const laneKey = `${agentId}:${sessionId}`;
|
|
1030
|
-
if (!this.sessions.has(laneKey)) {
|
|
1031
|
-
this.sessions.set(laneKey, { sessionId, ownAgentId: agentId });
|
|
1032
|
-
}
|
|
1033
|
-
runtimeLog.info(this.options.name || 'agent', `bootstrap task-assigned ${taskId} (state=${state})`);
|
|
1034
|
-
await this.eventQueue.enqueue({
|
|
1035
|
-
type: 'task_result',
|
|
1036
|
-
result: {
|
|
1037
|
-
...full,
|
|
1038
|
-
taskId: full.taskId ?? taskId,
|
|
1039
|
-
state,
|
|
1040
|
-
providerIsYou: true,
|
|
1041
|
-
},
|
|
1042
|
-
chatId: sessionId,
|
|
1043
|
-
}, laneKey);
|
|
1044
|
-
}
|
|
1045
|
-
}
|
|
1046
1119
|
/** replay task-assigned for an existing active task (demo recovery). */
|
|
1047
1120
|
async wakeTaskAssignment(taskId) {
|
|
1048
1121
|
const agentId = this.options.agentId;
|
|
@@ -1070,7 +1143,13 @@ export class AgentHost {
|
|
|
1070
1143
|
chatId: sessionId,
|
|
1071
1144
|
}, laneKey);
|
|
1072
1145
|
}
|
|
1073
|
-
disconnect() {
|
|
1146
|
+
disconnect() {
|
|
1147
|
+
this.started = false;
|
|
1148
|
+
this.pollRunner?.stop();
|
|
1149
|
+
this.stateTimers.clearAll();
|
|
1150
|
+
void this.inboxLoop?.stop();
|
|
1151
|
+
this.inboxLoop = null;
|
|
1152
|
+
}
|
|
1074
1153
|
/**
|
|
1075
1154
|
* Waits for in-flight ticks to finish. Call before process.exit() on SIGTERM.
|
|
1076
1155
|
* A timeout is returned, not thrown — the caller names it (lab verdict, fleet
|
|
@@ -1078,20 +1157,43 @@ export class AgentHost {
|
|
|
1078
1157
|
*/
|
|
1079
1158
|
drain(timeoutMs = 30_000) { return this.eventQueue.waitForIdle(timeoutMs); }
|
|
1080
1159
|
registerTool(tool) { return this.toolManager.register(tool); }
|
|
1081
|
-
isConnected() { return this.inboxLoop?.running ?? false; }
|
|
1160
|
+
isConnected() { return this.started && (!this.inboxLoopEnabled || (this.inboxLoop?.running ?? false)); }
|
|
1161
|
+
/**
|
|
1162
|
+
* Whether this agent is still reading its inbox, and when it last did. One
|
|
1163
|
+
* signal, three readers: the store's online badge, an operator's health
|
|
1164
|
+
* check, and monitoring. `lastPassAt` is the read itself; `lastAttemptAt`
|
|
1165
|
+
* says the loop still turns when the server is the thing that is down.
|
|
1166
|
+
* A `pollingInbox: false` agent (inboxCatchUp disabled, lab and eval paths)
|
|
1167
|
+
* has nothing to be stale about and reports no timestamps.
|
|
1168
|
+
*/
|
|
1169
|
+
inboxLiveness() {
|
|
1170
|
+
return {
|
|
1171
|
+
agentId: this.options.agentId,
|
|
1172
|
+
connected: this.isConnected(),
|
|
1173
|
+
pollingInbox: this.inboxLoopEnabled,
|
|
1174
|
+
lastPassAt: this.inboxLoop?.lastPassAt ?? null,
|
|
1175
|
+
lastAttemptAt: this.inboxLoop?.lastAttemptAt ?? null,
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1082
1178
|
}
|
|
1083
1179
|
/**
|
|
1084
|
-
* createAgent(config) →
|
|
1180
|
+
* createAgent(config) → Agent
|
|
1085
1181
|
* createAgentPool([cfg, ...]) → ConnectionPool (lazy fleet, async-loaded)
|
|
1086
1182
|
*/
|
|
1087
1183
|
export function createAgent(options = {}) {
|
|
1088
|
-
return new
|
|
1184
|
+
return new Agent(options);
|
|
1089
1185
|
}
|
|
1090
1186
|
export async function createAgentPool(configs) {
|
|
1091
1187
|
const ConnectionPool = await loadConnectionPool();
|
|
1092
1188
|
const pool = new ConnectionPool();
|
|
1093
1189
|
pool.register(configs);
|
|
1094
|
-
|
|
1095
|
-
|
|
1190
|
+
try {
|
|
1191
|
+
await pool.connectAll();
|
|
1192
|
+
return pool;
|
|
1193
|
+
}
|
|
1194
|
+
catch (err) {
|
|
1195
|
+
await pool.shutdown();
|
|
1196
|
+
throw err;
|
|
1197
|
+
}
|
|
1096
1198
|
}
|
|
1097
|
-
export default
|
|
1199
|
+
export default Agent;
|