@ziggs-ai/agent-sdk 0.32.0 → 0.33.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/dist/Agent.js +47 -13
- package/dist/mcp/createMcpAgent.js +6 -3
- package/package.json +2 -2
package/dist/Agent.js
CHANGED
|
@@ -463,8 +463,10 @@ export class Agent {
|
|
|
463
463
|
task = await this.taskService.getTask(taskId);
|
|
464
464
|
}
|
|
465
465
|
catch (error) {
|
|
466
|
+
// Unreadable is not a hold. The wake filter still drops a frame whose
|
|
467
|
+
// own task cannot be read; a tool that names one gets an error instead.
|
|
466
468
|
if ([403, 404].includes(error.status ?? 0))
|
|
467
|
-
return { blocked: true };
|
|
469
|
+
return { blocked: true, unread: true };
|
|
468
470
|
throw error;
|
|
469
471
|
}
|
|
470
472
|
// A coordinator sees its children's task events too; those do not pause it.
|
|
@@ -1118,7 +1120,7 @@ export class Agent {
|
|
|
1118
1120
|
release();
|
|
1119
1121
|
taskRuns.clear();
|
|
1120
1122
|
};
|
|
1121
|
-
const checkpoint = async (
|
|
1123
|
+
const checkpoint = async (namedTaskId) => {
|
|
1122
1124
|
if (tickAbort.signal.aborted) {
|
|
1123
1125
|
if (tickAbort.signal.reason?.code === 'TASK_CONTROL')
|
|
1124
1126
|
heldTask = tickAbort.signal.reason;
|
|
@@ -1126,27 +1128,59 @@ export class Agent {
|
|
|
1126
1128
|
}
|
|
1127
1129
|
if (heldTask)
|
|
1128
1130
|
throw new Error('Task execution held');
|
|
1129
|
-
const
|
|
1131
|
+
const wakeId = marks.scope.taskId ?? wakeTaskId;
|
|
1132
|
+
const taskId = namedTaskId ?? wakeId;
|
|
1130
1133
|
if (!taskId)
|
|
1131
1134
|
return;
|
|
1132
|
-
|
|
1135
|
+
// Only this wake's own task shares the tick abort. A tool that names
|
|
1136
|
+
// some other task must not be able to cancel the rest of the wake.
|
|
1137
|
+
const ownsWake = namedTaskId == null || namedTaskId === wakeId;
|
|
1138
|
+
if (ownsWake)
|
|
1139
|
+
registerTaskRun(taskId);
|
|
1133
1140
|
const execution = await readControl(taskId);
|
|
1134
1141
|
if (heldTask)
|
|
1135
1142
|
throw new Error('Task execution held');
|
|
1136
|
-
if (execution?.blocked)
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1143
|
+
if (!execution?.blocked)
|
|
1144
|
+
return;
|
|
1145
|
+
if (!ownsWake || execution.unread) {
|
|
1146
|
+
const error = execution.unread
|
|
1147
|
+
? `Task ${taskId} cannot be read. This call did not run.`
|
|
1148
|
+
: execution.closed
|
|
1149
|
+
? `Task ${taskId} is cancelled. This call did not run.`
|
|
1150
|
+
: `Task ${taskId} is paused. This call did not run.`;
|
|
1151
|
+
return { error };
|
|
1141
1152
|
}
|
|
1153
|
+
heldTask = { taskId, control: execution.control };
|
|
1154
|
+
taskRuns.get(taskId).run.control = execution.control;
|
|
1155
|
+
tickAbort.abort(Object.assign(new Error('Task execution held'), { code: 'TASK_CONTROL', taskId, control: execution.control }));
|
|
1156
|
+
throw new Error('Task execution held');
|
|
1157
|
+
};
|
|
1158
|
+
const taskIdNamedBy = (effect) => {
|
|
1159
|
+
if (effect.kind !== 'tool-call' || !effect.args || typeof effect.args !== 'object' || Array.isArray(effect.args))
|
|
1160
|
+
return undefined;
|
|
1161
|
+
const args = effect.args;
|
|
1162
|
+
if (typeof args.taskId === 'string')
|
|
1163
|
+
return args.taskId;
|
|
1164
|
+
const nested = args.args;
|
|
1165
|
+
if ((effect.name === 'tool_read' || effect.name === 'tool_write')
|
|
1166
|
+
&& nested && typeof nested === 'object' && !Array.isArray(nested)) {
|
|
1167
|
+
const inner = nested.taskId;
|
|
1168
|
+
if (typeof inner === 'string')
|
|
1169
|
+
return inner;
|
|
1170
|
+
}
|
|
1171
|
+
return undefined;
|
|
1142
1172
|
};
|
|
1143
1173
|
const eff = async (effect) => {
|
|
1144
1174
|
// Record-only telemetry can finish after interruption. Every action and
|
|
1145
1175
|
// model call checks the current task before starting another operation.
|
|
1146
|
-
if (effect.kind
|
|
1147
|
-
const
|
|
1148
|
-
|
|
1149
|
-
|
|
1176
|
+
if (effect.kind === 'tool-call') {
|
|
1177
|
+
const refusal = await checkpoint(taskIdNamedBy(effect));
|
|
1178
|
+
// The handler is generic over every effect. This branch is only a tool call.
|
|
1179
|
+
if (refusal)
|
|
1180
|
+
return { ok: false, error: refusal.error };
|
|
1181
|
+
}
|
|
1182
|
+
else if (effect.kind !== 'record-event') {
|
|
1183
|
+
await checkpoint();
|
|
1150
1184
|
}
|
|
1151
1185
|
const agreementId = marks.scope.agreementId;
|
|
1152
1186
|
const sameRoom = 'sessionId' in effect && effect.sessionId === session.sessionId;
|
|
@@ -395,14 +395,17 @@ export async function createMcpAgent(options) {
|
|
|
395
395
|
: 'the external service';
|
|
396
396
|
// Open a connection request for this user; returns its id so the caller can
|
|
397
397
|
// deep-link the user straight to it on the Connections page.
|
|
398
|
-
|
|
398
|
+
// The server holds the invoking user to the people this wake may ask, so
|
|
399
|
+
// the request carries the same lane as the call that missed: in the hire's
|
|
400
|
+
// room that user is its customer.
|
|
401
|
+
const triggerConnectionRequest = async (userId, laneId) => {
|
|
399
402
|
if (!transportConfig.serverUrl)
|
|
400
403
|
return null;
|
|
401
404
|
const operatorKey = transportConfig.operatorKey || process.env.ZIGGS_OPERATOR_KEY;
|
|
402
405
|
if (!operatorKey)
|
|
403
406
|
return null;
|
|
404
407
|
try {
|
|
405
|
-
const body = await new ConnectionsClient(operatorKey, agentId, backendBase).requestMcpConnection({
|
|
408
|
+
const body = await new ConnectionsClient(operatorKey, agentId, backendBase, laneId).requestMcpConnection({
|
|
406
409
|
serverUrl: transportConfig.serverUrl,
|
|
407
410
|
tools: tools.map(t => t.name),
|
|
408
411
|
onBehalfOfUserId: userId,
|
|
@@ -460,7 +463,7 @@ export async function createMcpAgent(options) {
|
|
|
460
463
|
if (lastErr instanceof ZiggsGatewayError &&
|
|
461
464
|
lastErr.code === 'NO_USER_CONNECTION' &&
|
|
462
465
|
transportConfig.serverUrl) {
|
|
463
|
-
const requestId = await triggerConnectionRequest(userId).catch(() => null);
|
|
466
|
+
const requestId = await triggerConnectionRequest(userId, laneId).catch(() => null);
|
|
464
467
|
const link = connectionsSettingsAppUrl(appUrl, requestId);
|
|
465
468
|
return {
|
|
466
469
|
content: [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ziggs-ai/agent-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.0",
|
|
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.
|
|
29
|
+
"@ziggs-ai/api-client": "0.32.0",
|
|
30
30
|
"ajv": "^8.17.1",
|
|
31
31
|
"dotenv": "^17.2.3",
|
|
32
32
|
"openai": "^4.0.0",
|