@ziggs-ai/agent-sdk 0.38.2 → 0.38.3
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/README.md +4 -4
- package/dist/brain/ClaudeBrain.d.ts +1 -1
- package/dist/brain/ClaudeBrain.js +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.js +4 -2
- package/dist/launcher/createHealthServer.d.ts +50 -0
- package/dist/launcher/createHealthServer.js +68 -0
- package/dist/launcher/runAgents.d.ts +32 -0
- package/dist/launcher/runAgents.js +119 -0
- package/dist/platform/tools/index.d.ts +1 -1
- package/dist/platform/tools/index.js +1 -1
- package/dist/platform/tools/tier1/protocolTools.js +2 -2
- package/dist/platform/tools/tier2/contextTools.d.ts +0 -1
- package/dist/platform/tools/tier2/contextTools.js +1 -2
- package/dist/probeChat.d.ts +4 -6
- package/dist/probeChat.js +4 -6
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -131,9 +131,9 @@ configureApiClient({ httpUrl: 'https://api.example.com' });
|
|
|
131
131
|
|
|
132
132
|
A **runner** is the operator’s process. It starts agents, serves health and
|
|
133
133
|
coordinates shutdown. It is not another part of the customer’s agent model.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
134
|
+
`runAgents` and `createHealthServer` are exported from this package. The health
|
|
135
|
+
page answers `/` and `/health`. A process that needs another route, such as
|
|
136
|
+
starting one of our hosted agents, adds it itself.
|
|
137
137
|
|
|
138
138
|
## Migration
|
|
139
139
|
|
|
@@ -146,7 +146,7 @@ This is a breaking pre-beta rename; update imports before publishing this releas
|
|
|
146
146
|
| `ClaudeSdkAgent`, `ClaudeSdkAgentOptions` | `ClaudeBrain`, `ClaudeBrainOptions` |
|
|
147
147
|
| `host.agent` | `agent.brain` |
|
|
148
148
|
| `createMcpAgent(...).host` | `createMcpAgent(...).agent` |
|
|
149
|
-
| SDK `runLauncher`, `createHealthServer` |
|
|
149
|
+
| SDK `runLauncher`, `createHealthServer` | SDK `runAgents`, `createHealthServer` |
|
|
150
150
|
|
|
151
151
|
`createAgent`, `ziggsBrain`, `claudeBrain` and agent IDs on the wire retain their
|
|
152
152
|
names. No backend identity or stored data migration is required.
|
|
@@ -5,7 +5,7 @@ import type { ToolManager } from '../tools/ToolManager.js';
|
|
|
5
5
|
import { type RawEvent } from '../runtime/buildOutcome.js';
|
|
6
6
|
import type { TickInput, TickOutput } from './ZiggsBrain.js';
|
|
7
7
|
import { type AskWakeGate } from './wakeGate.js';
|
|
8
|
-
export { isPublishProbeChat
|
|
8
|
+
export { isPublishProbeChat } from '../probeChat.js';
|
|
9
9
|
/**
|
|
10
10
|
* status notices must not spend a Claude turn answering
|
|
11
11
|
* nobody. Real work wakes arrive as chat `message`, synthetic `task_result`
|
|
@@ -17,7 +17,7 @@ import { filterStandingOfferNotices, isReadableAgreementNoticeLane } from './sta
|
|
|
17
17
|
import { formedByFromWake, shouldSkipAgentFormedHireActivation, } from './hireActivationNotice.js';
|
|
18
18
|
import { isPublishProbeChat } from '../probeChat.js';
|
|
19
19
|
import { gateableMessages, wakeAgreementId } from './wakeGate.js';
|
|
20
|
-
export { isPublishProbeChat
|
|
20
|
+
export { isPublishProbeChat } from '../probeChat.js';
|
|
21
21
|
/**
|
|
22
22
|
* status notices must not spend a Claude turn answering
|
|
23
23
|
* nobody. Real work wakes arrive as chat `message`, synthetic `task_result`
|
package/dist/index.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ export { runMechanical } from './runtime/runMechanical.js';
|
|
|
41
41
|
export { PromptBuilder } from './runtime/PromptBuilder.js';
|
|
42
42
|
export { thinkingDefaults, defineThinkingState, defineMechanicalState, DEFAULT_WAIT_PROMPT } from './runtime/defaults.js';
|
|
43
43
|
export { KICKOFF_STATE, CLAIM_KICKOFF_STATE, injectKickoffDefault, buildKickoffState, buildClaimKickoffState, handlesClaimWake, } from './runtime/kickoff.js';
|
|
44
|
-
export { validateWorkflow, WorkflowValidationError } from './runtime/validateWorkflow.js';
|
|
44
|
+
export { validateWorkflow, WorkflowValidationError, PROBE_CTX } from './runtime/validateWorkflow.js';
|
|
45
45
|
export { outcomeFromEvent, outcomeFromActionResult, applyOutcomeToCtx } from './runtime/buildOutcome.js';
|
|
46
46
|
export { LANE_BOOK_KEY, allCollectedResults, allDelegatedAgreementIds, allPendingSubtasks, } from './runtime/taskBookkeeping.js';
|
|
47
47
|
export { createDeliveryWriteKeys } from './runtime/deliveryWriteKeys.js';
|
|
@@ -67,7 +67,7 @@ export { HistoryFormatter, historyFormatter, AgreementFormatter, agreementFormat
|
|
|
67
67
|
export { ProactiveTrigger } from './platform/proactive/ProactiveTrigger.js';
|
|
68
68
|
export { PollRegistry, declaredPolls, type PollBinding, type PollContext, type PollKind, type PollResult, } from './platform/polls/PollKind.js';
|
|
69
69
|
export { PollRunner, type PollRunnerOptions, type WakeTarget } from './platform/polls/PollRunner.js';
|
|
70
|
-
export { PROTOCOL_TOOLS, agreementProposeTool, agreementSubcontractTool, agreementRespondTool, agreementClaimTool, agreementCounterTool, agreementGetTool, agreementRevokeTool, agreementFulfillTool, taskCreateTool, taskSetResultTool, taskUpdatePlanStepTool, dispatchProtocolOp, PAYMENT_TOOLS, paymentBalanceTool, CONNECTION_TOOLS, connectionProxyTool, connectionListGrantsTool, requestConnectionTool, MCP_CONNECTION_TOOLS, mcpToolCallTool, mcpToolsListTool, DISCOVERY_TOOLS, agentSearchTool, agentGetTool, MARKETPLACE_TOOLS, marketplaceViewTool, CONTEXT_TOOLS, contextReadTool,
|
|
70
|
+
export { PROTOCOL_TOOLS, agreementProposeTool, agreementSubcontractTool, agreementRespondTool, agreementClaimTool, agreementCounterTool, agreementGetTool, agreementRevokeTool, agreementFulfillTool, taskCreateTool, taskSetResultTool, taskUpdatePlanStepTool, dispatchProtocolOp, PAYMENT_TOOLS, paymentBalanceTool, CONNECTION_TOOLS, connectionProxyTool, connectionListGrantsTool, requestConnectionTool, MCP_CONNECTION_TOOLS, mcpToolCallTool, mcpToolsListTool, DISCOVERY_TOOLS, agentSearchTool, agentGetTool, MARKETPLACE_TOOLS, marketplaceViewTool, CONTEXT_TOOLS, contextReadTool, accessRequestTool, contextDelegateTool, contextExpandReachTool, contextDiscoverGrantableTool, GRANTS_TOOLS, listGrantsTool, PLAN_TOOLS, taskReplacePlanTool, LINK_TOOLS, linkProposeTool, linkListTool, INTRODUCTION_TOOLS, introductionMintTool, introductionRedeemTool, introductionListTool, introductionRevokeTool, ARTIFACT_TOOLS, recordArtifactTool, CHAT_TOOLS, openConversationTool, LISTING_TOOLS, } from './platform/tools/index.js';
|
|
71
71
|
export { CLAUDE_SDK_TOOLS } from './brain/claudeSdkTools.js';
|
|
72
72
|
export { hostedModelTools, isHostedDeferred, hostedToolLane } from './brain/hostedToolSurface.js';
|
|
73
73
|
export { PROTOCOL_TOOL_NAMES, PROTOCOL_TOOL_TO_OPERATION, mapProtocolToolToOperation, isProtocolToolName } from './tasks/protocolRegistry.js';
|
|
@@ -83,3 +83,7 @@ export type { ApiClientConfig } from '@ziggs-ai/api-client';
|
|
|
83
83
|
export { SeenMessages } from './platform/SeenMessages.js';
|
|
84
84
|
export { runtimeLog, resetRuntimeLogLevelCache } from './shared/runtimeLog.js';
|
|
85
85
|
export type { RuntimeLogLevel } from './shared/runtimeLog.js';
|
|
86
|
+
export { runAgents } from './launcher/runAgents.js';
|
|
87
|
+
export type { RunnerOptions, SingleAgentRun, FleetRun } from './launcher/runAgents.js';
|
|
88
|
+
export { createHealthServer, inboxHealth } from './launcher/createHealthServer.js';
|
|
89
|
+
export type { AgentLiveness, HealthRequest, HealthRequestHandler } from './launcher/createHealthServer.js';
|
package/dist/index.js
CHANGED
|
@@ -42,7 +42,7 @@ export { runMechanical } from './runtime/runMechanical.js';
|
|
|
42
42
|
export { PromptBuilder } from './runtime/PromptBuilder.js';
|
|
43
43
|
export { thinkingDefaults, defineThinkingState, defineMechanicalState, DEFAULT_WAIT_PROMPT } from './runtime/defaults.js';
|
|
44
44
|
export { KICKOFF_STATE, CLAIM_KICKOFF_STATE, injectKickoffDefault, buildKickoffState, buildClaimKickoffState, handlesClaimWake, } from './runtime/kickoff.js';
|
|
45
|
-
export { validateWorkflow, WorkflowValidationError } from './runtime/validateWorkflow.js';
|
|
45
|
+
export { validateWorkflow, WorkflowValidationError, PROBE_CTX } from './runtime/validateWorkflow.js';
|
|
46
46
|
export { outcomeFromEvent, outcomeFromActionResult, applyOutcomeToCtx } from './runtime/buildOutcome.js';
|
|
47
47
|
export { LANE_BOOK_KEY, allCollectedResults, allDelegatedAgreementIds, allPendingSubtasks, } from './runtime/taskBookkeeping.js';
|
|
48
48
|
export { createDeliveryWriteKeys } from './runtime/deliveryWriteKeys.js';
|
|
@@ -79,7 +79,7 @@ export {
|
|
|
79
79
|
// Tier 1 — protocol grammar (framework-managed via taskTools).
|
|
80
80
|
PROTOCOL_TOOLS, agreementProposeTool, agreementSubcontractTool, agreementRespondTool, agreementClaimTool, agreementCounterTool, agreementGetTool, agreementRevokeTool, agreementFulfillTool, taskCreateTool, taskSetResultTool, taskUpdatePlanStepTool, dispatchProtocolOp,
|
|
81
81
|
// Tier 2 — opt-in capability bundles (spread into `tools:`).
|
|
82
|
-
PAYMENT_TOOLS, paymentBalanceTool, CONNECTION_TOOLS, connectionProxyTool, connectionListGrantsTool, requestConnectionTool, MCP_CONNECTION_TOOLS, mcpToolCallTool, mcpToolsListTool, DISCOVERY_TOOLS, agentSearchTool, agentGetTool, MARKETPLACE_TOOLS, marketplaceViewTool, CONTEXT_TOOLS, contextReadTool,
|
|
82
|
+
PAYMENT_TOOLS, paymentBalanceTool, CONNECTION_TOOLS, connectionProxyTool, connectionListGrantsTool, requestConnectionTool, MCP_CONNECTION_TOOLS, mcpToolCallTool, mcpToolsListTool, DISCOVERY_TOOLS, agentSearchTool, agentGetTool, MARKETPLACE_TOOLS, marketplaceViewTool, CONTEXT_TOOLS, contextReadTool, accessRequestTool, contextDelegateTool, contextExpandReachTool, contextDiscoverGrantableTool, GRANTS_TOOLS, listGrantsTool, PLAN_TOOLS, taskReplacePlanTool, LINK_TOOLS, linkProposeTool, linkListTool, INTRODUCTION_TOOLS, introductionMintTool, introductionRedeemTool, introductionListTool, introductionRevokeTool, ARTIFACT_TOOLS, recordArtifactTool, CHAT_TOOLS, openConversationTool, LISTING_TOOLS, } from './platform/tools/index.js';
|
|
83
83
|
// The always-on toolset for the hosted Claude cognition (parity guard
|
|
84
84
|
// reads this as part of the real SDK registry).
|
|
85
85
|
export { CLAUDE_SDK_TOOLS } from './brain/claudeSdkTools.js';
|
|
@@ -96,3 +96,5 @@ export { MessagesClient, ArtifactsClient, AgentSearchClient, getBackendUrl,
|
|
|
96
96
|
configureApiClient, } from '@ziggs-ai/api-client';
|
|
97
97
|
export { SeenMessages } from './platform/SeenMessages.js';
|
|
98
98
|
export { runtimeLog, resetRuntimeLogLevelCache } from './shared/runtimeLog.js';
|
|
99
|
+
export { runAgents } from './launcher/runAgents.js';
|
|
100
|
+
export { createHealthServer, inboxHealth } from './launcher/createHealthServer.js';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type IncomingMessage, type Server, type ServerResponse } from 'node:http';
|
|
2
|
+
import type { Agent } from '../Agent.js';
|
|
3
|
+
import type { ConnectionPool } from '../platform/ConnectionPool.js';
|
|
4
|
+
/** What one agent reports about its own inbox reading. */
|
|
5
|
+
export type AgentLiveness = ReturnType<Agent['inboxLiveness']>;
|
|
6
|
+
/**
|
|
7
|
+
* The health verdict. A process is alive when its agents are still reading,
|
|
8
|
+
* so the answer is derived from the inbox loop rather than from the fact that
|
|
9
|
+
* this HTTP server replied — a wedged process replies happily while none of
|
|
10
|
+
* its agents read anything.
|
|
11
|
+
*
|
|
12
|
+
* The restart decision reads `lastAttemptAt`, not `lastPassAt`. When the
|
|
13
|
+
* backend is down every pass fails while the loops are perfectly healthy, and
|
|
14
|
+
* restarting the fleet would fix nothing and lose in-flight turns. Read
|
|
15
|
+
* staleness is still reported per agent, for the store and for monitoring.
|
|
16
|
+
*/
|
|
17
|
+
export declare function inboxHealth(rows: AgentLiveness[], now?: number, staleMs?: number): {
|
|
18
|
+
ok: boolean;
|
|
19
|
+
reason?: string;
|
|
20
|
+
staleMs: number;
|
|
21
|
+
watching: number;
|
|
22
|
+
oldestAttemptAgeMs: number | null;
|
|
23
|
+
oldestPassAgeMs: number | null;
|
|
24
|
+
agents: (AgentLiveness & {
|
|
25
|
+
passAgeMs: number | null;
|
|
26
|
+
attemptAgeMs: number | null;
|
|
27
|
+
})[];
|
|
28
|
+
};
|
|
29
|
+
export type HealthRequest = {
|
|
30
|
+
req: IncomingMessage;
|
|
31
|
+
res: ServerResponse;
|
|
32
|
+
pool?: ConnectionPool;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* A route the embedding process adds. Return true once the response is sent.
|
|
36
|
+
* The health page itself only answers `/` and `/health`.
|
|
37
|
+
*/
|
|
38
|
+
export type HealthRequestHandler = (call: HealthRequest) => Promise<boolean>;
|
|
39
|
+
export declare function createHealthServer({ port, label, pool, liveness, onRequest, }?: {
|
|
40
|
+
port?: number;
|
|
41
|
+
label?: string;
|
|
42
|
+
pool?: ConnectionPool;
|
|
43
|
+
/**
|
|
44
|
+
* Every agent this process is responsible for. Omit only when there is
|
|
45
|
+
* nothing to watch: without it `/health` can say no more than "this server
|
|
46
|
+
* replied", which is the answer this route exists to stop giving.
|
|
47
|
+
*/
|
|
48
|
+
liveness?: () => AgentLiveness[];
|
|
49
|
+
onRequest?: HealthRequestHandler;
|
|
50
|
+
}): Server;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { runtimeLog } from '../shared/runtimeLog.js';
|
|
3
|
+
/**
|
|
4
|
+
* How long an agent's inbox loop may go without turning before this process is
|
|
5
|
+
* called unhealthy. An idle loop completes a pass every `DEFAULT_WAIT_SECONDS`
|
|
6
|
+
* (110s), so this is three missed cycles plus slack: long enough that a slow
|
|
7
|
+
* server or a long turn never flaps, short enough that a wedged process is
|
|
8
|
+
* replaced within minutes.
|
|
9
|
+
*/
|
|
10
|
+
const STALE_MS = Number(process.env.FLEET_INBOX_STALE_MS) || 360_000;
|
|
11
|
+
function age(now, at) {
|
|
12
|
+
return at === null ? null : now - at;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The health verdict. A process is alive when its agents are still reading,
|
|
16
|
+
* so the answer is derived from the inbox loop rather than from the fact that
|
|
17
|
+
* this HTTP server replied — a wedged process replies happily while none of
|
|
18
|
+
* its agents read anything.
|
|
19
|
+
*
|
|
20
|
+
* The restart decision reads `lastAttemptAt`, not `lastPassAt`. When the
|
|
21
|
+
* backend is down every pass fails while the loops are perfectly healthy, and
|
|
22
|
+
* restarting the fleet would fix nothing and lose in-flight turns. Read
|
|
23
|
+
* staleness is still reported per agent, for the store and for monitoring.
|
|
24
|
+
*/
|
|
25
|
+
export function inboxHealth(rows, now = Date.now(), staleMs = STALE_MS) {
|
|
26
|
+
const agents = rows.map((r) => ({
|
|
27
|
+
...r,
|
|
28
|
+
passAgeMs: age(now, r.lastPassAt),
|
|
29
|
+
attemptAgeMs: age(now, r.lastAttemptAt),
|
|
30
|
+
}));
|
|
31
|
+
// Only agents that are supposed to be polling can be stale. A connected
|
|
32
|
+
// agent with the inbox loop off (lab and eval paths) has nothing to report,
|
|
33
|
+
// and an agent that was deliberately stopped is not this process failing.
|
|
34
|
+
const watched = agents.filter((r) => r.pollingInbox && r.connected);
|
|
35
|
+
const turning = watched.filter((r) => r.attemptAgeMs !== null && r.attemptAgeMs <= staleMs);
|
|
36
|
+
const ages = watched.map((r) => r.attemptAgeMs).filter((n) => n !== null);
|
|
37
|
+
const passAges = watched.map((r) => r.passAgeMs).filter((n) => n !== null);
|
|
38
|
+
const ok = watched.length === 0 || turning.length > 0;
|
|
39
|
+
return {
|
|
40
|
+
ok,
|
|
41
|
+
...(ok ? {} : { reason: `no inbox pass attempted by any of ${watched.length} agent(s) within ${staleMs}ms` }),
|
|
42
|
+
staleMs,
|
|
43
|
+
watching: watched.length,
|
|
44
|
+
oldestAttemptAgeMs: ages.length ? Math.max(...ages) : null,
|
|
45
|
+
oldestPassAgeMs: passAges.length ? Math.max(...passAges) : null,
|
|
46
|
+
agents,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export function createHealthServer({ port = Number(process.env.PORT) || 8080, label = 'agents', pool, liveness, onRequest, } = {}) {
|
|
50
|
+
const server = createServer(async (req, res) => {
|
|
51
|
+
if (await onRequest?.({ req, res, pool }))
|
|
52
|
+
return;
|
|
53
|
+
const url = req.url ?? '/';
|
|
54
|
+
if (url === '/health' || url === '/') {
|
|
55
|
+
const report = inboxHealth(liveness?.() ?? []);
|
|
56
|
+
if (!report.ok) {
|
|
57
|
+
runtimeLog.warn(label, `health: ${report.reason}`);
|
|
58
|
+
}
|
|
59
|
+
res.writeHead(report.ok ? 200 : 503, { 'Content-Type': 'application/json' });
|
|
60
|
+
res.end(JSON.stringify(report));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
res.writeHead(404);
|
|
64
|
+
res.end();
|
|
65
|
+
});
|
|
66
|
+
server.listen(port, () => runtimeLog.info(label, `health server on port ${port}`));
|
|
67
|
+
return server;
|
|
68
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type Agent, type AgentOptions } from '../Agent.js';
|
|
2
|
+
import { ConnectionPool } from '../platform/ConnectionPool.js';
|
|
3
|
+
import { type HealthRequestHandler } from './createHealthServer.js';
|
|
4
|
+
import type { Server } from 'node:http';
|
|
5
|
+
/** Process concerns only. Every Agent owns its inbox and external polls. */
|
|
6
|
+
export interface RunnerOptions {
|
|
7
|
+
operatorKey?: string;
|
|
8
|
+
label?: string;
|
|
9
|
+
port?: number | string;
|
|
10
|
+
healthServer?: boolean;
|
|
11
|
+
/** Disable when the embedding process owns its signal handlers. */
|
|
12
|
+
handleSignals?: boolean;
|
|
13
|
+
drainTimeoutMs?: number;
|
|
14
|
+
onShutdown?: () => Promise<void>;
|
|
15
|
+
agentMeta?: Record<string, unknown>[];
|
|
16
|
+
primary?: AgentOptions | ((pool: ConnectionPool) => AgentOptions);
|
|
17
|
+
/** A route this process adds. The health page itself only answers `/` and `/health`. */
|
|
18
|
+
onRequest?: HealthRequestHandler;
|
|
19
|
+
}
|
|
20
|
+
export interface SingleAgentRun {
|
|
21
|
+
agent: Agent;
|
|
22
|
+
healthServer?: Server;
|
|
23
|
+
shutdown(): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
export interface FleetRun {
|
|
26
|
+
pool: ConnectionPool;
|
|
27
|
+
primary: Agent | null;
|
|
28
|
+
healthServer?: Server;
|
|
29
|
+
shutdown(): Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
export declare function runAgents(config: AgentOptions, options?: RunnerOptions): Promise<SingleAgentRun>;
|
|
32
|
+
export declare function runAgents(configs: AgentOptions[], options?: RunnerOptions): Promise<FleetRun>;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { createAgent } from '../Agent.js';
|
|
2
|
+
import { ConnectionPool } from '../platform/ConnectionPool.js';
|
|
3
|
+
import { boundKeyFleetWarning } from '../shared/operatorKeyClaims.js';
|
|
4
|
+
import { runtimeLog } from '../shared/runtimeLog.js';
|
|
5
|
+
import { createHealthServer } from './createHealthServer.js';
|
|
6
|
+
const DRAIN_TIMEOUT_MS = Number(process.env.FLEET_DRAIN_TIMEOUT_MS) || 90_000;
|
|
7
|
+
function shutdownHandler(stop, server, options) {
|
|
8
|
+
let pending;
|
|
9
|
+
const shutdown = () => {
|
|
10
|
+
if (!pending) {
|
|
11
|
+
process.off('SIGTERM', signal);
|
|
12
|
+
process.off('SIGINT', signal);
|
|
13
|
+
pending = (async () => {
|
|
14
|
+
try {
|
|
15
|
+
await stop();
|
|
16
|
+
await options.onShutdown?.();
|
|
17
|
+
}
|
|
18
|
+
finally {
|
|
19
|
+
if (server)
|
|
20
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
21
|
+
}
|
|
22
|
+
})();
|
|
23
|
+
}
|
|
24
|
+
return pending;
|
|
25
|
+
};
|
|
26
|
+
const signal = () => {
|
|
27
|
+
runtimeLog.info(options.label || 'runner', 'Shutting down');
|
|
28
|
+
void shutdown().then(() => process.exit(0), (err) => {
|
|
29
|
+
runtimeLog.error(options.label || 'runner', `Shutdown failed: ${err.message}`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
if (options.handleSignals !== false) {
|
|
34
|
+
process.on('SIGTERM', signal);
|
|
35
|
+
process.on('SIGINT', signal);
|
|
36
|
+
}
|
|
37
|
+
return shutdown;
|
|
38
|
+
}
|
|
39
|
+
async function healthServer(options, liveness, pool) {
|
|
40
|
+
if (options.healthServer === false)
|
|
41
|
+
return undefined;
|
|
42
|
+
const server = createHealthServer({
|
|
43
|
+
port: Number(options.port ?? process.env.PORT ?? 8080),
|
|
44
|
+
label: options.label || 'runner',
|
|
45
|
+
pool,
|
|
46
|
+
liveness,
|
|
47
|
+
onRequest: options.onRequest,
|
|
48
|
+
});
|
|
49
|
+
await new Promise((resolve, reject) => {
|
|
50
|
+
server.once('error', reject);
|
|
51
|
+
server.once('listening', () => { server.off('error', reject); resolve(); });
|
|
52
|
+
});
|
|
53
|
+
return server;
|
|
54
|
+
}
|
|
55
|
+
export async function runAgents(configOrConfigs, options = {}) {
|
|
56
|
+
const effectiveKey = options.operatorKey ?? process.env.ZIGGS_OPERATOR_KEY;
|
|
57
|
+
const decorate = (config) => effectiveKey && !config.operatorKey ? { ...config, operatorKey: effectiveKey } : config;
|
|
58
|
+
const drainTimeout = options.drainTimeoutMs ?? DRAIN_TIMEOUT_MS;
|
|
59
|
+
if (!Array.isArray(configOrConfigs)) {
|
|
60
|
+
const agent = createAgent(decorate(configOrConfigs));
|
|
61
|
+
let server;
|
|
62
|
+
// One budget for stopping intake and finishing in-flight work: the inbox
|
|
63
|
+
// pass gets up to the whole of it to acknowledge, the queue what is left.
|
|
64
|
+
const stop = async () => {
|
|
65
|
+
const deadline = Date.now() + drainTimeout;
|
|
66
|
+
await agent.disconnect(drainTimeout);
|
|
67
|
+
await agent.drain(Math.max(0, deadline - Date.now()));
|
|
68
|
+
};
|
|
69
|
+
try {
|
|
70
|
+
await agent.connectAsync();
|
|
71
|
+
server = await healthServer(options, () => [agent.inboxLiveness()]);
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
await stop();
|
|
75
|
+
throw err;
|
|
76
|
+
}
|
|
77
|
+
const shutdown = shutdownHandler(stop, server, options);
|
|
78
|
+
return { agent, healthServer: server, shutdown };
|
|
79
|
+
}
|
|
80
|
+
const pool = new ConnectionPool();
|
|
81
|
+
const configs = configOrConfigs.map(decorate);
|
|
82
|
+
pool.register(configs, options.agentMeta);
|
|
83
|
+
const primaryConfig = typeof options.primary === 'function' ? options.primary(pool) : options.primary;
|
|
84
|
+
let primary = null;
|
|
85
|
+
let server;
|
|
86
|
+
// Same single budget as the one-agent path, shared by the pool and the primary.
|
|
87
|
+
const stop = async () => {
|
|
88
|
+
const deadline = Date.now() + drainTimeout;
|
|
89
|
+
await Promise.all([primary?.disconnect(drainTimeout), pool.shutdown(drainTimeout)]);
|
|
90
|
+
await primary?.drain(Math.max(0, deadline - Date.now()));
|
|
91
|
+
};
|
|
92
|
+
try {
|
|
93
|
+
if (primaryConfig)
|
|
94
|
+
primary = createAgent(decorate(primaryConfig));
|
|
95
|
+
const controlKey = effectiveKey ?? configs.find(c => c.operatorKey)?.operatorKey ?? primary?.options.operatorKey;
|
|
96
|
+
if (controlKey) {
|
|
97
|
+
const warning = boundKeyFleetWarning(controlKey, pool.size + (primary ? 1 : 0));
|
|
98
|
+
if (warning)
|
|
99
|
+
runtimeLog.warn(options.label || 'runner', warning);
|
|
100
|
+
}
|
|
101
|
+
await primary?.connectAsync();
|
|
102
|
+
await pool.connectAll();
|
|
103
|
+
const connected = pool.listActive().length;
|
|
104
|
+
if (configs.length > 0 && connected === 0) {
|
|
105
|
+
throw new Error(`0/${configs.length} agents started; process must restart`);
|
|
106
|
+
}
|
|
107
|
+
if (connected !== pool.size || pool.size !== configs.length) {
|
|
108
|
+
runtimeLog.warn(options.label || 'runner', `Only ${connected}/${configs.length} agents started — hosting the ones that came up`);
|
|
109
|
+
}
|
|
110
|
+
server = await healthServer(options, () => [...(primary ? [primary.inboxLiveness()] : []), ...pool.liveness()], pool);
|
|
111
|
+
runtimeLog.info(options.label || 'runner', `Ready — ${connected}/${pool.size} agents started`);
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
await stop();
|
|
115
|
+
throw err;
|
|
116
|
+
}
|
|
117
|
+
const shutdown = shutdownHandler(stop, server, options);
|
|
118
|
+
return { pool, primary, healthServer: server, shutdown };
|
|
119
|
+
}
|
|
@@ -21,7 +21,7 @@ export { DISCOVERY_TOOLS, agentSearchTool, agentGetTool } from './tier2/discover
|
|
|
21
21
|
export { MARKETPLACE_TOOLS, marketplaceViewTool } from './tier2/marketplaceTools.js';
|
|
22
22
|
export { PAYMENT_TOOLS, paymentBalanceTool } from './tier2/paymentTools.js';
|
|
23
23
|
export { CONNECTION_TOOLS, connectionProxyTool, connectionListGrantsTool, requestConnectionTool, } from './tier2/connectionTools.js';
|
|
24
|
-
export { CONTEXT_TOOLS, contextReadTool,
|
|
24
|
+
export { CONTEXT_TOOLS, contextReadTool, accessRequestTool, contextDelegateTool, contextExpandReachTool, contextDiscoverGrantableTool, } from './tier2/contextTools.js';
|
|
25
25
|
export { GRANTS_TOOLS, listGrantsTool } from './tier2/grantsTools.js';
|
|
26
26
|
export { TASK_TOOLS, listTasksTool, cancelTaskTool } from './tier2/taskTools.js';
|
|
27
27
|
export { PLAN_TOOLS, taskReplacePlanTool } from './tier2/planTools.js';
|
|
@@ -23,7 +23,7 @@ export { DISCOVERY_TOOLS, agentSearchTool, agentGetTool } from './tier2/discover
|
|
|
23
23
|
export { MARKETPLACE_TOOLS, marketplaceViewTool } from './tier2/marketplaceTools.js';
|
|
24
24
|
export { PAYMENT_TOOLS, paymentBalanceTool } from './tier2/paymentTools.js';
|
|
25
25
|
export { CONNECTION_TOOLS, connectionProxyTool, connectionListGrantsTool, requestConnectionTool, } from './tier2/connectionTools.js';
|
|
26
|
-
export { CONTEXT_TOOLS, contextReadTool,
|
|
26
|
+
export { CONTEXT_TOOLS, contextReadTool, accessRequestTool, contextDelegateTool, contextExpandReachTool, contextDiscoverGrantableTool, } from './tier2/contextTools.js';
|
|
27
27
|
// one grant lister across every rail (retires context_discover +
|
|
28
28
|
// payment_list_grants). Always-available on the hosted agent — see CLAUDE_SDK_TOOLS.
|
|
29
29
|
export { GRANTS_TOOLS, listGrantsTool } from './tier2/grantsTools.js';
|
|
@@ -67,13 +67,13 @@ export const agreementClaimTool = defineTool('agreement_claim', agreementClaimCa
|
|
|
67
67
|
export const taskSetResultTool = defineTool('task_set_result', {
|
|
68
68
|
taskId: { type: 'string', required: true },
|
|
69
69
|
// same param name as MCP ziggs_task_set_result / PATCH /tasks/:id/state.
|
|
70
|
-
state: { type: 'string', required: true, enum: ['completed', 'failed'
|
|
70
|
+
state: { type: 'string', required: true, enum: ['completed', 'failed'] },
|
|
71
71
|
// Same object contract as MCP ({ summary, status, links, … }).
|
|
72
72
|
result: {
|
|
73
73
|
type: 'object',
|
|
74
74
|
description: 'Outcome payload (summary, status, links, …). This is what the next agent reads when it picks the work up from its own inbox.',
|
|
75
75
|
},
|
|
76
|
-
}, handlerFor('task_set_result'), { description: 'Transition a task to its terminal state
|
|
76
|
+
}, handlerFor('task_set_result'), { description: 'Transition a task to its terminal state, completed or failed, and record the result. To cancel a task use task_cancel. Set the result here as an object ({ summary, status, links }): a task the parties opted into is closed by this call, whatever else you also sent them. Pass state (not status). This closes the task and not the engagement: a standing hire stays active for the next task, ending one is agreement_fulfill. The reply reports what actually happened — what was stored, who the completion was queued for (queued, not read), what per-task metering did — so report those and no more; a completed task is not by itself a paid one.' });
|
|
77
77
|
export const agreementCounterTool = defineTool('agreement_counter', { agreementId: { type: 'string', required: true }, price: { type: 'number', description: 'Price in POINTS, as an integer of hundredths — 500 means ϟ5.00. Convert both ways: a human asking for "ϟ2 per task" means 200, not 2. ϟ is the points symbol, not a hundredth.' }, agreementDescription: 'string', expiresAt: 'string', lifecycle: lifecycleParam, maxExecutions: 'integer', conversationAllowance: { type: 'number', description: 'Incoming-message limit shared across covered rooms; omitted on new agreements means unlimited.' }, description: 'string' }, handlerFor('agreement_counter'), { description: 'Counter a pending proposal with revised terms instead of approving or rejecting. Check status first with agreement_get.' });
|
|
78
78
|
export const agreementGetTool = defineTool('agreement_get', { agreementId: { type: 'string', required: true } }, handlerFor('agreement_get'), { description: 'Read the current proposal status for an agreement (pending, approved, rejected, countered, expired).' });
|
|
79
79
|
export const agreementRevokeTool = defineTool('agreement_revoke', { agreementId: { type: 'string', required: true } }, handlerFor('agreement_revoke'), { description: 'Revoke a pending proposal you created so it can no longer be approved. Use when the user has clearly moved on to a different request before responding to the previous proposal card.' });
|
|
@@ -3,7 +3,6 @@ export declare const contextReadTool: ToolDefinition;
|
|
|
3
3
|
export declare const openTool: ToolDefinition;
|
|
4
4
|
export declare const accessExplainTool: ToolDefinition;
|
|
5
5
|
export declare const accessRequestTool: ToolDefinition;
|
|
6
|
-
export declare const contextRequestTool: ToolDefinition;
|
|
7
6
|
export declare const contextDelegateTool: ToolDefinition;
|
|
8
7
|
export declare const contextExpandReachTool: ToolDefinition;
|
|
9
8
|
export declare const contextDiscoverGrantableTool: ToolDefinition;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CONTEXT_CAPABILITIES, contextReadCapability, openCapability, accessExplainCapability, contextDelegateCapability, contextExpandReachCapability, contextDiscoverGrantableCapability,
|
|
1
|
+
import { CONTEXT_CAPABILITIES, contextReadCapability, openCapability, accessExplainCapability, contextDelegateCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, accessRequestCapability, } from '@ziggs-ai/api-client';
|
|
2
2
|
import { toolFromCapability, toolsFromCapabilities } from '../../../tools/capabilityAdapter.js';
|
|
3
3
|
// context grant tools ride the same api-client
|
|
4
4
|
// clients as ziggs-mcp; the whole definition (schema + handler) is now shared
|
|
@@ -7,7 +7,6 @@ export const contextReadTool = toolFromCapability(contextReadCapability);
|
|
|
7
7
|
export const openTool = toolFromCapability(openCapability);
|
|
8
8
|
export const accessExplainTool = toolFromCapability(accessExplainCapability);
|
|
9
9
|
export const accessRequestTool = toolFromCapability(accessRequestCapability);
|
|
10
|
-
export const contextRequestTool = toolFromCapability(contextRequestCapability);
|
|
11
10
|
export const contextDelegateTool = toolFromCapability(contextDelegateCapability);
|
|
12
11
|
export const contextExpandReachTool = toolFromCapability(contextExpandReachCapability);
|
|
13
12
|
export const contextDiscoverGrantableTool = toolFromCapability(contextDiscoverGrantableCapability);
|
package/dist/probeChat.d.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* One classifier for store-publish probe rooms.
|
|
3
3
|
*
|
|
4
|
-
* The runner mints `probe_<agentId>_<ts>`.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The runner mints `probe_<agentId>_<ts>`. That prefix skips the wake gate
|
|
5
|
+
* and the wake-rate breaker, so a store publish still hears a model reply.
|
|
6
|
+
* It does not write a canned answer, and it is not capability evidence.
|
|
7
|
+
* Capability versus liveness is decided by the store sync.
|
|
8
8
|
*/
|
|
9
9
|
export declare const PROBE_CHAT_ID_PREFIX = "probe_";
|
|
10
10
|
export declare function isPublishProbeChat(chatId: string | undefined | null): boolean;
|
|
11
|
-
/** @deprecated use {@link isPublishProbeChat} — same classifier, old name. */
|
|
12
|
-
export declare const isCapabilityProbeSession: typeof isPublishProbeChat;
|
package/dist/probeChat.js
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* One classifier for store-publish probe rooms.
|
|
3
3
|
*
|
|
4
|
-
* The runner mints `probe_<agentId>_<ts>`.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The runner mints `probe_<agentId>_<ts>`. That prefix skips the wake gate
|
|
5
|
+
* and the wake-rate breaker, so a store publish still hears a model reply.
|
|
6
|
+
* It does not write a canned answer, and it is not capability evidence.
|
|
7
|
+
* Capability versus liveness is decided by the store sync.
|
|
8
8
|
*/
|
|
9
9
|
export const PROBE_CHAT_ID_PREFIX = 'probe_';
|
|
10
10
|
export function isPublishProbeChat(chatId) {
|
|
11
11
|
return typeof chatId === 'string' && chatId.startsWith(PROBE_CHAT_ID_PREFIX);
|
|
12
12
|
}
|
|
13
|
-
/** @deprecated use {@link isPublishProbeChat} — same classifier, old name. */
|
|
14
|
-
export const isCapabilityProbeSession = isPublishProbeChat;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ziggs-ai/agent-sdk",
|
|
3
|
-
"version": "0.38.
|
|
3
|
+
"version": "0.38.3",
|
|
4
4
|
"description": "Agent framework SDK for building autonomous agents on the Ziggs platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"@anthropic-ai/claude-agent-sdk": "^0.3.284",
|
|
27
27
|
"@anthropic-ai/sdk": "^0.110.0",
|
|
28
28
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
29
|
-
"@ziggs-ai/api-client": "0.37.
|
|
29
|
+
"@ziggs-ai/api-client": "0.37.5",
|
|
30
30
|
"@ziggs-ai/contracts": "^0.23.3",
|
|
31
31
|
"ajv": "^8.17.1",
|
|
32
32
|
"dotenv": "^17.2.3",
|