@agentfield/sdk 0.1.137-rc.4 → 0.1.137-rc.6
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 -0
- package/dist/index.d.ts +12 -1
- package/dist/index.js +99 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# AgentField TypeScript SDK
|
|
2
2
|
|
|
3
|
+
## Graceful shutdown
|
|
4
|
+
|
|
5
|
+
`serve()` installs SIGTERM and SIGINT handlers that notify the control plane and drain detached executions. Host processes that own signal handling can use `serve({ handleSignals: false })` and call the idempotent `shutdown()` method themselves. `AGENTFIELD_SHUTDOWN_TIMEOUT` accepts bare seconds (`30`) or durations (`30s`, `5m`) and defaults to 30 seconds. In Kubernetes, set `terminationGracePeriodSeconds` higher than this timeout.
|
|
6
|
+
|
|
3
7
|
The TypeScript SDK provides an idiomatic Node.js interface for building and running AgentField agents. It mirrors the Python SDK APIs, including AI, memory, discovery, and MCP tooling.
|
|
4
8
|
|
|
5
9
|
## Installing
|
package/dist/index.d.ts
CHANGED
|
@@ -792,6 +792,7 @@ declare class AgentFieldClient {
|
|
|
792
792
|
constructor(config: AgentConfig);
|
|
793
793
|
register(payload: any): Promise<any>;
|
|
794
794
|
getNode(nodeId: string): Promise<any>;
|
|
795
|
+
shutdown(nodeId: string): Promise<any>;
|
|
795
796
|
heartbeat(status?: 'starting' | 'ready' | 'degraded' | 'offline'): Promise<HealthStatus>;
|
|
796
797
|
execute<T = any>(target: string, input: any, metadata?: {
|
|
797
798
|
runId?: string;
|
|
@@ -1949,6 +1950,10 @@ declare class RealtimeSession {
|
|
|
1949
1950
|
}
|
|
1950
1951
|
declare function buildSessionDefinition(name: string, options: SessionOptions): SessionDefinition;
|
|
1951
1952
|
|
|
1953
|
+
interface ServeOptions {
|
|
1954
|
+
handleSignals?: boolean;
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1952
1957
|
declare class Agent {
|
|
1953
1958
|
readonly config: AgentConfig;
|
|
1954
1959
|
readonly app: express.Express;
|
|
@@ -1956,6 +1961,10 @@ declare class Agent {
|
|
|
1956
1961
|
readonly skills: SkillRegistry;
|
|
1957
1962
|
private server?;
|
|
1958
1963
|
private heartbeatTimer?;
|
|
1964
|
+
private shutdownPromise?;
|
|
1965
|
+
private readonly inFlightExecutions;
|
|
1966
|
+
private shuttingDown;
|
|
1967
|
+
private signalHandlers?;
|
|
1959
1968
|
private readonly aiClient;
|
|
1960
1969
|
private readonly agentFieldClient;
|
|
1961
1970
|
private readonly memoryClient;
|
|
@@ -2074,8 +2083,10 @@ declare class Agent {
|
|
|
2074
2083
|
executionId?: string;
|
|
2075
2084
|
}): Promise<ApprovalResult>;
|
|
2076
2085
|
private buildExecutionLogContext;
|
|
2077
|
-
serve(): Promise<void>;
|
|
2086
|
+
serve(options?: ServeOptions): Promise<void>;
|
|
2078
2087
|
shutdown(): Promise<void>;
|
|
2088
|
+
private performShutdown;
|
|
2089
|
+
private installSignalHandlers;
|
|
2079
2090
|
call(target: string, input: any): Promise<any>;
|
|
2080
2091
|
/**
|
|
2081
2092
|
* Remote call variant that submits the execution asynchronously and polls for
|
package/dist/index.js
CHANGED
|
@@ -4161,6 +4161,14 @@ var AgentFieldClient = class {
|
|
|
4161
4161
|
});
|
|
4162
4162
|
return res.data;
|
|
4163
4163
|
}
|
|
4164
|
+
async shutdown(nodeId) {
|
|
4165
|
+
const bodyStr = JSON.stringify({ reason: "shutdown", version: this.config.version ?? "" });
|
|
4166
|
+
const authHeaders = this.didAuthenticator.signRequest(Buffer.from(bodyStr));
|
|
4167
|
+
const res = await this.http.post(`/api/v1/nodes/${encodeURIComponent(nodeId)}/shutdown`, bodyStr, {
|
|
4168
|
+
headers: this.mergeHeaders({ "Content-Type": "application/json", ...authHeaders })
|
|
4169
|
+
});
|
|
4170
|
+
return res.data;
|
|
4171
|
+
}
|
|
4164
4172
|
async heartbeat(status = "ready") {
|
|
4165
4173
|
const nodeId = this.config.nodeId;
|
|
4166
4174
|
const bodyStr = JSON.stringify({ status, version: this.config.version ?? "", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
@@ -6041,10 +6049,26 @@ function buildSessionDefinition(name, options) {
|
|
|
6041
6049
|
};
|
|
6042
6050
|
}
|
|
6043
6051
|
|
|
6052
|
+
// src/agent/signals.ts
|
|
6053
|
+
var DEFAULT_SHUTDOWN_TIMEOUT_MS = 3e4;
|
|
6054
|
+
function parseShutdownTimeout(value, warn = console.warn) {
|
|
6055
|
+
if (!value?.trim()) return DEFAULT_SHUTDOWN_TIMEOUT_MS;
|
|
6056
|
+
const text2 = value.trim();
|
|
6057
|
+
if (/^\d+$/.test(text2)) return Number(text2) * 1e3;
|
|
6058
|
+
const match = text2.match(/^(\d+(?:\.\d+)?)(ms|s|m|h)$/);
|
|
6059
|
+
if (match) {
|
|
6060
|
+
const factors = { ms: 1, s: 1e3, m: 6e4, h: 36e5 };
|
|
6061
|
+
return Number(match[1]) * factors[match[2]];
|
|
6062
|
+
}
|
|
6063
|
+
warn(`invalid AGENTFIELD_SHUTDOWN_TIMEOUT ${JSON.stringify(value)}; using 30s`);
|
|
6064
|
+
return DEFAULT_SHUTDOWN_TIMEOUT_MS;
|
|
6065
|
+
}
|
|
6066
|
+
|
|
6044
6067
|
// src/agent/Agent.ts
|
|
6045
6068
|
var TargetNotFoundError = class extends Error {
|
|
6046
6069
|
};
|
|
6047
6070
|
var AGENTFIELD_TS_SDK_VERSION = "0.1.82";
|
|
6071
|
+
var POST_CANCEL_SETTLEMENT_MS = 5e3;
|
|
6048
6072
|
var harnessRunners = /* @__PURE__ */ new WeakMap();
|
|
6049
6073
|
function normalizeAcceptsWebhook(value) {
|
|
6050
6074
|
if (value === true) return "true";
|
|
@@ -6074,6 +6098,10 @@ var Agent = class {
|
|
|
6074
6098
|
skills = new SkillRegistry();
|
|
6075
6099
|
server;
|
|
6076
6100
|
heartbeatTimer;
|
|
6101
|
+
shutdownPromise;
|
|
6102
|
+
inFlightExecutions = /* @__PURE__ */ new Map();
|
|
6103
|
+
shuttingDown = false;
|
|
6104
|
+
signalHandlers;
|
|
6077
6105
|
aiClient;
|
|
6078
6106
|
agentFieldClient;
|
|
6079
6107
|
memoryClient;
|
|
@@ -6473,7 +6501,7 @@ var Agent = class {
|
|
|
6473
6501
|
agentNodeDid: current.agentNodeDid
|
|
6474
6502
|
};
|
|
6475
6503
|
}
|
|
6476
|
-
async serve() {
|
|
6504
|
+
async serve(options = {}) {
|
|
6477
6505
|
await this.registerWithControlPlane();
|
|
6478
6506
|
if (this.localVerifier) {
|
|
6479
6507
|
try {
|
|
@@ -6493,19 +6521,70 @@ var Agent = class {
|
|
|
6493
6521
|
});
|
|
6494
6522
|
this.memoryEventClient.start();
|
|
6495
6523
|
this.startHeartbeat();
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
this.
|
|
6502
|
-
|
|
6503
|
-
|
|
6524
|
+
if (options.handleSignals !== false) this.installSignalHandlers();
|
|
6525
|
+
}
|
|
6526
|
+
shutdown() {
|
|
6527
|
+
if (this.shutdownPromise) return this.shutdownPromise;
|
|
6528
|
+
this.shuttingDown = true;
|
|
6529
|
+
this.shutdownPromise = this.performShutdown();
|
|
6530
|
+
return this.shutdownPromise;
|
|
6531
|
+
}
|
|
6532
|
+
async performShutdown() {
|
|
6533
|
+
const listenerClosed = new Promise((resolve3, reject) => {
|
|
6534
|
+
if (!this.server) return resolve3();
|
|
6535
|
+
this.server.close((err) => {
|
|
6504
6536
|
if (err) reject(err);
|
|
6505
6537
|
else resolve3();
|
|
6506
6538
|
});
|
|
6507
6539
|
});
|
|
6540
|
+
try {
|
|
6541
|
+
await this.agentFieldClient.shutdown(this.config.nodeId);
|
|
6542
|
+
} catch (err) {
|
|
6543
|
+
console.warn("[Agent] Failed to notify control plane of shutdown:", err);
|
|
6544
|
+
}
|
|
6545
|
+
if (this.heartbeatTimer) {
|
|
6546
|
+
clearInterval(this.heartbeatTimer);
|
|
6547
|
+
}
|
|
6548
|
+
await listenerClosed;
|
|
6549
|
+
const timeoutMs = parseShutdownTimeout(process.env.AGENTFIELD_SHUTDOWN_TIMEOUT);
|
|
6550
|
+
let timer;
|
|
6551
|
+
const timeout = new Promise((resolve3) => {
|
|
6552
|
+
timer = setTimeout(() => resolve3("timeout"), timeoutMs);
|
|
6553
|
+
});
|
|
6554
|
+
const drained = Promise.allSettled([...this.inFlightExecutions.values()]).then(() => "drained");
|
|
6555
|
+
if (await Promise.race([drained, timeout]) === "timeout") {
|
|
6556
|
+
for (const executionId of this.inFlightExecutions.keys()) {
|
|
6557
|
+
this.cancelRegistry.cancel(executionId, "shutdown_timeout");
|
|
6558
|
+
}
|
|
6559
|
+
this.pauseManager.cancelAll();
|
|
6560
|
+
let settlementTimer;
|
|
6561
|
+
const settlementTimeout = new Promise((resolve3) => {
|
|
6562
|
+
settlementTimer = setTimeout(resolve3, POST_CANCEL_SETTLEMENT_MS);
|
|
6563
|
+
});
|
|
6564
|
+
await Promise.race([
|
|
6565
|
+
Promise.allSettled([...this.inFlightExecutions.values()]),
|
|
6566
|
+
settlementTimeout
|
|
6567
|
+
]);
|
|
6568
|
+
if (settlementTimer) clearTimeout(settlementTimer);
|
|
6569
|
+
}
|
|
6570
|
+
if (timer) clearTimeout(timer);
|
|
6508
6571
|
this.memoryEventClient.stop();
|
|
6572
|
+
if (this.signalHandlers) {
|
|
6573
|
+
process.off("SIGTERM", this.signalHandlers.SIGTERM);
|
|
6574
|
+
process.off("SIGINT", this.signalHandlers.SIGINT);
|
|
6575
|
+
this.signalHandlers = void 0;
|
|
6576
|
+
}
|
|
6577
|
+
}
|
|
6578
|
+
installSignalHandlers() {
|
|
6579
|
+
if (this.signalHandlers) return;
|
|
6580
|
+
const handle = (signal) => () => {
|
|
6581
|
+
void this.shutdown().finally(() => {
|
|
6582
|
+
process.exit(signal === "SIGTERM" ? 143 : 130);
|
|
6583
|
+
});
|
|
6584
|
+
};
|
|
6585
|
+
this.signalHandlers = { SIGTERM: handle("SIGTERM"), SIGINT: handle("SIGINT") };
|
|
6586
|
+
process.on("SIGTERM", this.signalHandlers.SIGTERM);
|
|
6587
|
+
process.on("SIGINT", this.signalHandlers.SIGINT);
|
|
6509
6588
|
}
|
|
6510
6589
|
async call(target, input) {
|
|
6511
6590
|
const { agentId, name } = this.parseTarget(target);
|
|
@@ -6927,11 +7006,17 @@ var Agent = class {
|
|
|
6927
7006
|
this.app.post("/execute/:name", (req, res) => this.executeServerlessHttp(req, res, req.params.name));
|
|
6928
7007
|
}
|
|
6929
7008
|
async executeReasoner(req, res, name) {
|
|
7009
|
+
if (this.shuttingDown) {
|
|
7010
|
+
res.status(503).json({ error: "agent shutting down" });
|
|
7011
|
+
return;
|
|
7012
|
+
}
|
|
6930
7013
|
const metadata = this.buildMetadata(req);
|
|
6931
7014
|
const reasoner = this.reasoners.get(name);
|
|
6932
7015
|
if (reasoner && this.shouldRunAsync(req)) {
|
|
6933
7016
|
res.status(202).json({ status: "processing", execution_id: metadata.executionId });
|
|
6934
|
-
|
|
7017
|
+
const execution = this.runReasonerAsync(reasoner, { targetName: name, input: req.body, metadata });
|
|
7018
|
+
this.inFlightExecutions.set(metadata.executionId, execution);
|
|
7019
|
+
void execution.finally(() => this.inFlightExecutions.delete(metadata.executionId));
|
|
6935
7020
|
return;
|
|
6936
7021
|
}
|
|
6937
7022
|
try {
|
|
@@ -6981,6 +7066,10 @@ var Agent = class {
|
|
|
6981
7066
|
return this.buildMetadataFromHeaders(req.headers);
|
|
6982
7067
|
}
|
|
6983
7068
|
async executeServerlessHttp(req, res, explicitName) {
|
|
7069
|
+
if (this.shuttingDown) {
|
|
7070
|
+
res.status(503).json({ error: "agent shutting down" });
|
|
7071
|
+
return;
|
|
7072
|
+
}
|
|
6984
7073
|
const invocation = this.extractInvocationDetails({
|
|
6985
7074
|
path: req.path,
|
|
6986
7075
|
explicitTarget: explicitName,
|