@ziggs-ai/api-client 0.31.0 → 0.32.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/capabilities/agreementVerbs.js +1 -1
- package/dist/capabilities/chat.js +2 -2
- package/dist/capabilities/connections.js +22 -7
- package/dist/capabilities/context.js +11 -10
- package/dist/capabilities/tasks.js +2 -2
- package/dist/capabilities/types.d.ts +0 -16
- package/dist/capabilities/types.js +19 -2
- package/dist/decisionWords.d.ts +10 -0
- package/dist/decisionWords.js +16 -1
- package/dist/http/AgreementClient.js +2 -1
- package/dist/http/AssistantConnectionClient.d.ts +27 -0
- package/dist/http/AssistantConnectionClient.js +49 -0
- package/dist/http/ChatClient.js +2 -1
- package/dist/http/ConnectionsClient.d.ts +7 -1
- package/dist/http/ConnectionsClient.js +6 -5
- package/dist/http/ContextGrantsClient.d.ts +20 -2
- package/dist/http/ContextGrantsClient.js +9 -2
- package/dist/http/MarketplaceClient.js +2 -1
- package/dist/http/MessagesClient.js +4 -3
- package/dist/http/OrgsClient.js +0 -1
- package/dist/http/TaskClient.js +2 -1
- package/dist/http/operatorHeaders.d.ts +8 -0
- package/dist/http/operatorHeaders.js +15 -0
- package/dist/inboxAck.d.ts +2 -1
- package/dist/inboxAck.js +6 -6
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/shared/operatorKey.d.ts +1 -0
- package/dist/shared/operatorKey.js +1 -0
- package/package.json +1 -1
|
@@ -214,7 +214,7 @@ export const agreementBidCapability = {
|
|
|
214
214
|
proposedTo: args['counterparty'],
|
|
215
215
|
chatId: args['chatId'],
|
|
216
216
|
// You provide, so the counterparty pays.
|
|
217
|
-
providerId: creds.agentId,
|
|
217
|
+
providerId: creds.agentId || undefined,
|
|
218
218
|
engagementKind: engagementKindFrom(args),
|
|
219
219
|
}, creds);
|
|
220
220
|
return { agreement };
|
|
@@ -138,7 +138,7 @@ export const openConversationCapability = {
|
|
|
138
138
|
return {
|
|
139
139
|
chatId,
|
|
140
140
|
appUrl: chatAppUrl(webAppOrigin(env), chatId),
|
|
141
|
-
actingAgentId: agentId,
|
|
141
|
+
...(agentId ? { actingAgentId: agentId } : {}),
|
|
142
142
|
people,
|
|
143
143
|
pending,
|
|
144
144
|
...(typeof reused === 'boolean' ? { reused } : {}),
|
|
@@ -158,7 +158,7 @@ function roomRoster(args, invited, agentId) {
|
|
|
158
158
|
const pending = (invited ?? []).filter((row) => row.status === 'pending').map((row) => row.id);
|
|
159
159
|
const waiting = new Set(pending);
|
|
160
160
|
const here = (invited ?? []).filter((row) => row.status !== 'pending').map((row) => row.id);
|
|
161
|
-
const people = [...new Set([...named.filter((id) => !waiting.has(id)), ...here, agentId])];
|
|
161
|
+
const people = [...new Set([...named.filter((id) => !waiting.has(id)), ...here, ...(agentId ? [agentId] : [])])];
|
|
162
162
|
return { people, pending };
|
|
163
163
|
}
|
|
164
164
|
/** Say what happened to each invited id, so the agent reports it truthfully. */
|
|
@@ -72,13 +72,15 @@ export const connectionProxyCapability = {
|
|
|
72
72
|
export const requestConnectionCapability = {
|
|
73
73
|
key: "connection_request",
|
|
74
74
|
names: { sdk: "connection_request", mcp: "ziggs_connection_request" },
|
|
75
|
-
title: "Ask
|
|
75
|
+
title: "Ask someone for a connection",
|
|
76
76
|
descriptions: {
|
|
77
|
-
sdk: "Ask
|
|
78
|
-
"
|
|
77
|
+
sdk: "Ask a person to connect a remote MCP server and grant you the listed tools. " +
|
|
78
|
+
"Name who to ask in recipientId: your work brief offers them for this wake — workBrief.actingFor.customer.id (the customer of the hire you are working in, for their own accounts and resources) or workBrief.actingFor.operator.id (your operator, for tools you bring). Anyone else is refused, and the refusal lists who you can ask. " +
|
|
79
|
+
"Opens a connection-consent card in the chat you pass; the answer says who got it and where they approve it — tell that person, not whoever happens to be in the room. " +
|
|
79
80
|
"On approval the server is connected (browser OAuth if needed) and you are granted the tools; call them with mcp_tool_call / mcp_tools_list (not connection_proxy).",
|
|
80
|
-
mcp: "Ask
|
|
81
|
-
"
|
|
81
|
+
mcp: "Ask a person to connect a remote MCP server and grant you the listed tools. " +
|
|
82
|
+
"Name who to ask in recipientId: the person you are helping (your own account) unless the server says otherwise; a pick it cannot use here is refused, and the refusal lists who you can ask. " +
|
|
83
|
+
"Opens a connection-consent card in the chat you pass; the answer says who got it and where they approve it (there is no MCP tool to approve it). " +
|
|
82
84
|
"On approval the server is connected (browser OAuth if needed) and you are granted the tools; the result shows up in ziggs_connection_list for use with ziggs_mcp_tools_list / ziggs_mcp_tool_call.",
|
|
83
85
|
},
|
|
84
86
|
annotation: "write",
|
|
@@ -103,6 +105,11 @@ export const requestConnectionCapability = {
|
|
|
103
105
|
type: "string",
|
|
104
106
|
description: "Plain-language reason shown to the human deciding",
|
|
105
107
|
},
|
|
108
|
+
recipientId: {
|
|
109
|
+
type: "string",
|
|
110
|
+
required: true,
|
|
111
|
+
description: "Who to ask: workBrief.actingFor.customer.id (the customer of this hire, for their own accounts) or workBrief.actingFor.operator.id (your operator, for tools you bring)",
|
|
112
|
+
},
|
|
106
113
|
},
|
|
107
114
|
needsAgentId: true,
|
|
108
115
|
handler: async (args, env) => {
|
|
@@ -114,20 +121,28 @@ export const requestConnectionCapability = {
|
|
|
114
121
|
if (!Array.isArray(tools) || tools.length === 0) {
|
|
115
122
|
throw new Error("tools must be a non-empty array of tool names");
|
|
116
123
|
}
|
|
124
|
+
if (typeof args["recipientId"] !== "string" || !args["recipientId"].trim())
|
|
125
|
+
throw new Error("recipientId is required: who to ask, from your work brief");
|
|
117
126
|
try {
|
|
118
127
|
const result = await client(env).requestMcpConnection({
|
|
119
128
|
chatId: args["chatId"],
|
|
120
129
|
serverUrl: args["serverUrl"],
|
|
121
130
|
tools: tools,
|
|
122
131
|
reason: args["reason"],
|
|
132
|
+
recipientId: args["recipientId"].trim(),
|
|
123
133
|
});
|
|
134
|
+
// Who got the card is the server's answer; say that, never "your
|
|
135
|
+
// principal" — the card often goes to someone else.
|
|
136
|
+
const sent = typeof result["message"] === "string"
|
|
137
|
+
? result["message"]
|
|
138
|
+
: "A connection-consent card is open.";
|
|
124
139
|
return {
|
|
125
140
|
ok: true,
|
|
126
141
|
...result,
|
|
127
142
|
note: env.surface === "mcp"
|
|
128
|
-
?
|
|
143
|
+
? `${sent} Tell that person now (pull-only MCP has no push). ` +
|
|
129
144
|
"Once approved, the connection + grant appear in ziggs_connection_list for ziggs_mcp_tools_list / ziggs_mcp_tool_call."
|
|
130
|
-
:
|
|
145
|
+
: `${sent} Tell that person; only they can approve it. ` +
|
|
131
146
|
"Once approved, the connection + grant appear in grant_list (scopeKind [\"connection\"]) / connection_list_grants for mcp_tools_list / mcp_tool_call.",
|
|
132
147
|
};
|
|
133
148
|
}
|
|
@@ -369,13 +369,13 @@ export const contextRequestCapability = {
|
|
|
369
369
|
title: 'Ask an owner for artifact access',
|
|
370
370
|
descriptions: {
|
|
371
371
|
sdk: 'Ask a human owner for temporary read-only access to one artifact you were given a reference for. Use your existing chat with that owner. Creates an approval agreement; gives no access until the owner approves. You are the only recipient. After approval, open the original artifact. Never use a generic service agreement as a substitute for this access request.',
|
|
372
|
-
mcp: 'Request access to a restricted artifact directly from its human owner. Pass the artifact id you were given, your conversation with the owner, and the
|
|
372
|
+
mcp: 'Request access to a restricted artifact directly from its human owner. Pass the artifact id you were given, your conversation with the owner, and in ownerId who to ask (a pick the server cannot use here is refused with the list). This creates a pending approval agreement, NOT a grant. No prior grant is needed. The human approves at the agreement link. After approval, ziggs_open the original artifact. Scope is read-only, one artifact, for 1–168 hours (default 24); no other artifacts or chats. Include the intended summary recipient in reason. Keep polling for the decision when asked to wait. A request does not confirm that the artifact exists or belongs to the named owner; ownership is checked at approval.',
|
|
373
373
|
},
|
|
374
374
|
annotation: 'write', needsAgentId: true,
|
|
375
375
|
params: {
|
|
376
376
|
artifactId: { type: 'string', required: true },
|
|
377
377
|
chatId: { type: 'string', required: true },
|
|
378
|
-
ownerId: { type: 'string', required: true, description: '
|
|
378
|
+
ownerId: { type: 'string', required: true, description: 'Who to ask, from your work brief: workBrief.actingFor.customer.id or workBrief.actingFor.operator.id. Anyone else is refused, and the refusal lists who you can ask.' },
|
|
379
379
|
reason: { type: 'string', required: true, description: 'Purpose, including whom you will summarize the artifact to' },
|
|
380
380
|
durationHours: { type: 'number', description: 'Integer 1–168 hours after approval; default 24' },
|
|
381
381
|
idempotencyKey: { type: 'string', description: 'Reuse for the same request; do not create duplicate approval cards' },
|
|
@@ -391,7 +391,7 @@ export const contextRequestCapability = {
|
|
|
391
391
|
const result = await new ContextGrantsClient(creds.operatorKey, creds.agentId, env.baseUrl).requestArtifactAccess(args.artifactId, {
|
|
392
392
|
chatId: args.chatId, ownerId: args.ownerId, reason: args.reason, durationHours,
|
|
393
393
|
}, { idempotencyKey: args.idempotencyKey, laneId: creds.laneId });
|
|
394
|
-
return { ...result, outcome: 'pending', state: 'requested', appUrl: `${(env.webUrl ?? 'https://ziggsai.com').replace(/\/$/, '')}/app/agreements/${encodeURIComponent(result.agreementId)}`, note: 'Waiting for the artifact owner. No access granted yet; open the original artifact only after approval
|
|
394
|
+
return { ...result, outcome: 'pending', state: 'requested', appUrl: `${(env.webUrl ?? 'https://ziggsai.com').replace(/\/$/, '')}/app/agreements/${encodeURIComponent(result.agreementId)}`, note: `${result.message ?? 'Waiting for the artifact owner.'} No access granted yet; open the original artifact only after approval.` };
|
|
395
395
|
},
|
|
396
396
|
};
|
|
397
397
|
/** A suggested request still needs a purpose; it is never a grant or an approval. */
|
|
@@ -399,7 +399,7 @@ function requestAccessAction(args, env) {
|
|
|
399
399
|
return {
|
|
400
400
|
kind: 'request_access',
|
|
401
401
|
tool: env.surface === 'mcp' ? 'ziggs_access_request' : 'access_request',
|
|
402
|
-
args, requiredInput: args.scopeKind === 'artifact' ? ['reason', 'chatId', '
|
|
402
|
+
args, requiredInput: args.scopeKind === 'artifact' ? ['reason', 'chatId', 'recipientId'] : ['reason', 'recipientId'], effect: 'request_only',
|
|
403
403
|
requiresApproval: true,
|
|
404
404
|
};
|
|
405
405
|
}
|
|
@@ -409,8 +409,8 @@ export const accessRequestCapability = {
|
|
|
409
409
|
title: 'Request access for yourself',
|
|
410
410
|
annotation: 'write', needsAgentId: true,
|
|
411
411
|
descriptions: {
|
|
412
|
-
sdk: 'Request optional bounded access to one artifact, chat, agreement context or task. Creates only a pending approval; never grants access or approves.
|
|
413
|
-
mcp: 'Request access for this agent, never approve or grant it. Use scopeId for an artifact, chat, agreement or task; scopeKind=artifact with agreementId of your active hire resolves its system-maintained workspace summary artifact.
|
|
412
|
+
sdk: 'Request optional bounded access to one artifact, chat, agreement context or task. Creates only a pending approval; never grants access or approves. Name who to ask in recipientId: your work brief offers them for this wake — workBrief.actingFor.customer.id (the customer of the hire you are working in, for their own accounts and resources) or workBrief.actingFor.operator.id (your operator, for tools you bring). Anyone else is refused, and the refusal lists who you can ask. Artifact requests also need chatId, the conversation they approve in. Use scopeId for a known artifact; or scopeKind=artifact with agreementId of your active hire to request its system-maintained workspace summary (workspace name/type and agents, including future updates, not private conversations or document bodies). Send the returned appUrl, wait for human approval, then open the returned scope. Hiring alone grants no source access. Revocation and expiry are checked by the system.',
|
|
413
|
+
mcp: 'Request access for this agent, never approve or grant it. Use scopeId for an artifact, chat, agreement or task; scopeKind=artifact with agreementId of your active hire resolves its system-maintained workspace summary artifact. Name who to ask in recipientId; a pick the server cannot use here is refused, and the refusal lists who you can ask. Artifact requests also require chatId. Explain the scope and lifetime; summary access includes future updates only to that artifact. Surface appUrl and wait for human approval; then open the returned scope. Pending grants no access.',
|
|
414
414
|
},
|
|
415
415
|
params: {
|
|
416
416
|
scopeKind: { type: 'string', required: true, enum: ['artifact', 'chat', 'agreement', 'task'] },
|
|
@@ -421,11 +421,11 @@ export const accessRequestCapability = {
|
|
|
421
421
|
temporal: { type: 'string', enum: ['from-now', 'from-start'], description: 'Chat history requested; default from-now. Earlier messages need from-start. Other resources use from-start.' },
|
|
422
422
|
durationHours: { type: 'number', description: 'Integer 1–168 hours after approval; default 24' },
|
|
423
423
|
chatId: { type: 'string', description: 'Artifact or workspace requests only: existing conversation with the owner' },
|
|
424
|
-
|
|
424
|
+
recipientId: { type: 'string', required: true, description: 'Who to ask: workBrief.actingFor.customer.id (the customer of this hire) or workBrief.actingFor.operator.id (your operator)' },
|
|
425
425
|
idempotencyKey: { type: 'string', description: 'Reuse for the same logical request' },
|
|
426
426
|
},
|
|
427
427
|
handler: async (args, env) => {
|
|
428
|
-
for (const key of ['scopeKind', 'reason']) {
|
|
428
|
+
for (const key of ['scopeKind', 'reason', 'recipientId']) {
|
|
429
429
|
if (typeof args[key] !== 'string' || !String(args[key]).trim())
|
|
430
430
|
throw new Error(`${key} is required`);
|
|
431
431
|
}
|
|
@@ -448,14 +448,15 @@ export const accessRequestCapability = {
|
|
|
448
448
|
const result = await new ContextGrantsClient(creds.operatorKey, creds.agentId, env.baseUrl).requestAccess({
|
|
449
449
|
scopeKind, scopeId, agreementId, reason: String(args.reason).trim(),
|
|
450
450
|
permission: permission, temporal: args.temporal,
|
|
451
|
-
durationHours, chatId: args.chatId,
|
|
451
|
+
durationHours, chatId: args.chatId,
|
|
452
|
+
recipientId: typeof args.recipientId === 'string' ? args.recipientId.trim() : undefined,
|
|
452
453
|
}, { idempotencyKey: args.idempotencyKey, laneId: creds.laneId });
|
|
453
454
|
return {
|
|
454
455
|
...result, outcome: 'pending', state: 'requested', actingAgentId: creds.agentId,
|
|
455
456
|
scope: result.scope ?? { kind: scopeKind, id: scopeId },
|
|
456
457
|
appUrl: `${(env.webUrl ?? 'https://ziggsai.com').replace(/\/$/, '')}/app/agreements/${encodeURIComponent(result.agreementId)}`,
|
|
457
458
|
resumeAfterApproval: { tool: env.surface === 'mcp' ? 'ziggs_open' : 'open', args: result.scope ? { [`${result.scope.kind}Id`]: result.scope.id } : { [`${scopeKind}Id`]: scopeId } },
|
|
458
|
-
note: 'No access granted
|
|
459
|
+
note: `${result.message ?? 'A request card is open.'} No access granted until they approve; then open the original resource using the same agent identity.`,
|
|
459
460
|
};
|
|
460
461
|
},
|
|
461
462
|
};
|
|
@@ -48,9 +48,9 @@ export const listTasksCapability = {
|
|
|
48
48
|
handler: async (args, env) => {
|
|
49
49
|
const creds = fullCreds(env);
|
|
50
50
|
const assignedTo = args['assignedToMe']
|
|
51
|
-
?
|
|
51
|
+
? 'self'
|
|
52
52
|
: args['assignedTo'];
|
|
53
|
-
const createdBy = args['createdByMe'] ?
|
|
53
|
+
const createdBy = args['createdByMe'] ? 'self' : undefined;
|
|
54
54
|
const listed = await listTasks({
|
|
55
55
|
state: args['state'],
|
|
56
56
|
cursor: args['cursor'],
|
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* one shared definition per SDK/MCP tool capability.
|
|
3
|
-
*
|
|
4
|
-
* The tool-surface-parity epic (→903) unified the HTTP layer here in
|
|
5
|
-
* api-client but left ~25 shared capabilities hand-written twice: once in the
|
|
6
|
-
* agent-sdk's defineTool DSL, once in ziggs-mcp zod. Each capability below is
|
|
7
|
-
* the single source for the tool's schema, validation, response shaping, and
|
|
8
|
-
* guidance; the two surfaces register it through thin adapters
|
|
9
|
-
* (agent-sdk `toolFromCapability`, ziggs-mcp `registerCapability`).
|
|
10
|
-
*
|
|
11
|
-
* Params are a neutral JSON-schema-flavoured DSL rather than zod because the
|
|
12
|
-
* two surfaces cannot share one zod: agent-sdk is on zod v4, ziggs-mcp on
|
|
13
|
-
* zod v3 (pinned by the MCP SDK's ZodRawShape), and api-client deliberately
|
|
14
|
-
* has no zod dependency. The SDK adapter feeds params straight into
|
|
15
|
-
* defineTool's converter; the MCP adapter lowers them to zod v3.
|
|
16
|
-
*/
|
|
17
1
|
import type { Creds } from '../types.js';
|
|
18
2
|
export type CapabilitySurface = 'sdk' | 'mcp';
|
|
19
3
|
/** read-only / write / destructive — lowered to MCP tool annotations. */
|
|
@@ -1,13 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* one shared definition per SDK/MCP tool capability.
|
|
3
|
+
*
|
|
4
|
+
* The tool-surface-parity epic (→903) unified the HTTP layer here in
|
|
5
|
+
* api-client but left ~25 shared capabilities hand-written twice: once in the
|
|
6
|
+
* agent-sdk's defineTool DSL, once in ziggs-mcp zod. Each capability below is
|
|
7
|
+
* the single source for the tool's schema, validation, response shaping, and
|
|
8
|
+
* guidance; the two surfaces register it through thin adapters
|
|
9
|
+
* (agent-sdk `toolFromCapability`, ziggs-mcp `registerCapability`).
|
|
10
|
+
*
|
|
11
|
+
* Params are a neutral JSON-schema-flavoured DSL rather than zod because the
|
|
12
|
+
* two surfaces cannot share one zod: agent-sdk is on zod v4, ziggs-mcp on
|
|
13
|
+
* zod v3 (pinned by the MCP SDK's ZodRawShape), and api-client deliberately
|
|
14
|
+
* has no zod dependency. The SDK adapter feeds params straight into
|
|
15
|
+
* defineTool's converter; the MCP adapter lowers them to zod v3.
|
|
16
|
+
*/
|
|
17
|
+
import { decodeOperatorKeyClaims } from '../shared/operatorKey.js';
|
|
1
18
|
/** Creds for impersonated calls; adapters guarantee agentId when needsAgentId. */
|
|
2
19
|
export function fullCreds(env) {
|
|
3
20
|
const { operatorKey, agentId, laneId } = env.creds;
|
|
4
21
|
if (!operatorKey)
|
|
5
22
|
throw new Error('operatorKey missing from tool context');
|
|
6
|
-
if (!agentId)
|
|
23
|
+
if (!agentId && !decodeOperatorKeyClaims(operatorKey)?.connectionClientId)
|
|
7
24
|
throw new Error('agentId missing from tool context');
|
|
8
25
|
// the lane rides along so every Creds-based client sends
|
|
9
26
|
// X-Ziggs-Lane without each capability having to remember to.
|
|
10
|
-
return { operatorKey, agentId, ...(laneId ? { laneId } : {}) };
|
|
27
|
+
return { operatorKey, agentId: agentId ?? '', ...(laneId ? { laneId } : {}) };
|
|
11
28
|
}
|
|
12
29
|
/**
|
|
13
30
|
* Re-throw a client error with a capability-level prefix, preserving the HTTP
|
package/dist/decisionWords.d.ts
CHANGED
|
@@ -13,6 +13,16 @@
|
|
|
13
13
|
* counterparty wrote: no titles, no messages, no descriptions. Ids, an actor id
|
|
14
14
|
* and an instant are all it carries.
|
|
15
15
|
*/
|
|
16
|
+
/**
|
|
17
|
+
* The actor as it may appear in words, or null.
|
|
18
|
+
*
|
|
19
|
+
* A person's account id is `username:::email`. The server shows a reader
|
|
20
|
+
* outside that person's org their `psn_` face instead, but an older
|
|
21
|
+
* server, or a row written before that, still hands the raw id over, and the
|
|
22
|
+
* model repeats whatever it is told into the room. So an id shaped like a
|
|
23
|
+
* person's account is left out rather than said.
|
|
24
|
+
*/
|
|
25
|
+
export declare function speakableActor(actorId: string | null | undefined): string | null;
|
|
16
26
|
export type DecisionOutcome = 'approved' | 'rejected' | 'fulfilled' | 'held';
|
|
17
27
|
export interface DecisionWordsInput {
|
|
18
28
|
/** The delivery row's `reason`. Anything that is not a decision maps to null. */
|
package/dist/decisionWords.js
CHANGED
|
@@ -13,6 +13,20 @@
|
|
|
13
13
|
* counterparty wrote: no titles, no messages, no descriptions. Ids, an actor id
|
|
14
14
|
* and an instant are all it carries.
|
|
15
15
|
*/
|
|
16
|
+
/**
|
|
17
|
+
* The actor as it may appear in words, or null.
|
|
18
|
+
*
|
|
19
|
+
* A person's account id is `username:::email`. The server shows a reader
|
|
20
|
+
* outside that person's org their `psn_` face instead, but an older
|
|
21
|
+
* server, or a row written before that, still hands the raw id over, and the
|
|
22
|
+
* model repeats whatever it is told into the room. So an id shaped like a
|
|
23
|
+
* person's account is left out rather than said.
|
|
24
|
+
*/
|
|
25
|
+
export function speakableActor(actorId) {
|
|
26
|
+
if (typeof actorId !== 'string' || !actorId)
|
|
27
|
+
return null;
|
|
28
|
+
return actorId.includes(':::') || actorId.includes('@') ? null : actorId;
|
|
29
|
+
}
|
|
16
30
|
const REFUSED_REASONS = new Set([
|
|
17
31
|
'approval:rejected',
|
|
18
32
|
'context_request_rejected',
|
|
@@ -38,7 +52,8 @@ export function decisionWords(input) {
|
|
|
38
52
|
const on = input.agreementId
|
|
39
53
|
? `agreement ${input.agreementId}`
|
|
40
54
|
: 'this agreement';
|
|
41
|
-
const
|
|
55
|
+
const actor = speakableActor(input.actorId);
|
|
56
|
+
const by = actor ? ` by ${actor}` : '';
|
|
42
57
|
const at = input.ts ? ` at ${input.ts}` : '';
|
|
43
58
|
const artifactId = input.scope?.kind === 'artifact' && input.scope.id ? input.scope.id : null;
|
|
44
59
|
if (REFUSED_REASONS.has(reason)) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { decodeOperatorKeyClaims } from '../shared/operatorKey.js';
|
|
1
2
|
import { runtimeLog } from '../shared/runtimeLog.js';
|
|
2
3
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
4
|
import { OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, isBroadcastTarget, partySideIds } from '../types.js';
|
|
@@ -14,7 +15,7 @@ const buildHeaders = buildCredsHeaders;
|
|
|
14
15
|
function assertCreds(creds, op) {
|
|
15
16
|
if (!creds?.operatorKey)
|
|
16
17
|
throw new Error(`operatorKey is required for ${op}`);
|
|
17
|
-
if (!creds?.agentId)
|
|
18
|
+
if (!creds?.agentId && !decodeOperatorKeyClaims(creds.operatorKey)?.connectionClientId)
|
|
18
19
|
throw new Error(`agentId is required for ${op}`);
|
|
19
20
|
}
|
|
20
21
|
/**
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type Creds } from '../types.js';
|
|
2
|
+
export interface AssistantWorkspace {
|
|
3
|
+
orgId: string;
|
|
4
|
+
name: string;
|
|
5
|
+
agentId: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* The reads a connection makes about itself: which workspaces it works in,
|
|
9
|
+
* and its one inbox across all of them. It never remembers a
|
|
10
|
+
* current workspace and never changes shared credentials; the per-call pick
|
|
11
|
+
* is the server's (see `routing.ts`).
|
|
12
|
+
*/
|
|
13
|
+
export declare class AssistantConnectionClient {
|
|
14
|
+
private readonly creds;
|
|
15
|
+
private readonly baseUrl;
|
|
16
|
+
constructor(creds: Creds, baseUrl?: string);
|
|
17
|
+
request<T>(path: string, body?: unknown): Promise<T>;
|
|
18
|
+
/** Live: a workspace joined since connecting shows up here with its assistant. */
|
|
19
|
+
workspaces(): Promise<AssistantWorkspace[]>;
|
|
20
|
+
/** Which of the connection's assistants is on an existing thing. */
|
|
21
|
+
resolve(kind: string, id: string): Promise<{
|
|
22
|
+
workspace: AssistantWorkspace | null;
|
|
23
|
+
choices: AssistantWorkspace[];
|
|
24
|
+
}>;
|
|
25
|
+
inbox(waitSeconds?: number): Promise<Record<string, unknown>>;
|
|
26
|
+
ack(upTo: string, handledResourceIds?: string[]): Promise<unknown>;
|
|
27
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
2
|
+
import { hostIdentity } from '../instanceIdentity.js';
|
|
3
|
+
import { ApiError } from '../types.js';
|
|
4
|
+
import { parseErrorCode, parseErrorMessage } from '../shared/apiError.js';
|
|
5
|
+
/**
|
|
6
|
+
* The reads a connection makes about itself: which workspaces it works in,
|
|
7
|
+
* and its one inbox across all of them. It never remembers a
|
|
8
|
+
* current workspace and never changes shared credentials; the per-call pick
|
|
9
|
+
* is the server's (see `routing.ts`).
|
|
10
|
+
*/
|
|
11
|
+
export class AssistantConnectionClient {
|
|
12
|
+
creds;
|
|
13
|
+
baseUrl;
|
|
14
|
+
constructor(creds, baseUrl = getBackendUrl()) {
|
|
15
|
+
this.creds = creds;
|
|
16
|
+
this.baseUrl = baseUrl;
|
|
17
|
+
}
|
|
18
|
+
async request(path, body) {
|
|
19
|
+
const response = await fetch(`${this.baseUrl}/oauth/${path}`, {
|
|
20
|
+
method: body === undefined ? 'GET' : 'POST',
|
|
21
|
+
headers: {
|
|
22
|
+
Authorization: `Bearer ${this.creds.operatorKey}`,
|
|
23
|
+
'Content-Type': 'application/json',
|
|
24
|
+
'X-Ziggs-Instance': hostIdentity(this.creds.instanceId),
|
|
25
|
+
},
|
|
26
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
27
|
+
});
|
|
28
|
+
const text = await response.text();
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
throw new ApiError(parseErrorMessage(text, `${path} failed: ${response.status}`), response.status, text, parseErrorCode(text));
|
|
31
|
+
}
|
|
32
|
+
return JSON.parse(text);
|
|
33
|
+
}
|
|
34
|
+
/** Live: a workspace joined since connecting shows up here with its assistant. */
|
|
35
|
+
async workspaces() {
|
|
36
|
+
return (await this.request('workspaces'))
|
|
37
|
+
.workspaces;
|
|
38
|
+
}
|
|
39
|
+
/** Which of the connection's assistants is on an existing thing. */
|
|
40
|
+
resolve(kind, id) {
|
|
41
|
+
return this.request(`resolve?${new URLSearchParams({ kind, id })}`);
|
|
42
|
+
}
|
|
43
|
+
inbox(waitSeconds = 0) {
|
|
44
|
+
return this.request(`inbox?wait=${waitSeconds}`);
|
|
45
|
+
}
|
|
46
|
+
ack(upTo, handledResourceIds = []) {
|
|
47
|
+
return this.request('inbox/ack', { upTo, handledResourceIds });
|
|
48
|
+
}
|
|
49
|
+
}
|
package/dist/http/ChatClient.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { decodeOperatorKeyClaims } from '../shared/operatorKey.js';
|
|
1
2
|
import { runtimeLog } from '../shared/runtimeLog.js';
|
|
2
3
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
4
|
import { throwApiError } from '../shared/apiError.js';
|
|
@@ -7,7 +8,7 @@ const buildHeaders = buildCredsHeaders;
|
|
|
7
8
|
function assertCreds(creds, op) {
|
|
8
9
|
if (!creds?.operatorKey)
|
|
9
10
|
throw new Error(`operatorKey is required for ${op}`);
|
|
10
|
-
if (!creds?.agentId)
|
|
11
|
+
if (!creds?.agentId && !decodeOperatorKeyClaims(creds.operatorKey)?.connectionClientId)
|
|
11
12
|
throw new Error(`agentId is required for ${op}`);
|
|
12
13
|
}
|
|
13
14
|
export async function openConversation(participant, creds, { newChat = false, agreementId, createIfMissing } = {}) {
|
|
@@ -32,6 +32,12 @@ export interface McpConnectionRequestParams {
|
|
|
32
32
|
serverUrl: string;
|
|
33
33
|
tools: string[];
|
|
34
34
|
reason?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Who to ask, from the people the server offers this wake (the work brief's
|
|
37
|
+
* `actingFor.customer.id` / `.operator.id`). Required by the server; the
|
|
38
|
+
* per-user relay may send it as `onBehalfOfUserId` instead.
|
|
39
|
+
*/
|
|
40
|
+
recipientId?: string;
|
|
35
41
|
/**
|
|
36
42
|
* Working chat the consent card is opened into. Required by the backend for
|
|
37
43
|
* the agent-tool path; the per-user gateway trigger omits it.
|
|
@@ -119,7 +125,7 @@ export declare class ConnectionsClient {
|
|
|
119
125
|
* connection-consent agreement in the working chat; on approval the
|
|
120
126
|
* server is connected (if needed) and the agent is granted the tools.
|
|
121
127
|
*/
|
|
122
|
-
requestMcpConnection({ serverUrl, tools, reason, chatId, onBehalfOfUserId, }: McpConnectionRequestParams): Promise<Record<string, unknown>>;
|
|
128
|
+
requestMcpConnection({ serverUrl, tools, reason, chatId, recipientId, onBehalfOfUserId, }: McpConnectionRequestParams): Promise<Record<string, unknown>>;
|
|
123
129
|
private _requestRaw;
|
|
124
130
|
private _request;
|
|
125
131
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { decodeOperatorKeyClaims } from '../shared/operatorKey.js';
|
|
1
2
|
import { getBackendUrl } from "../utils/urlUtils.js";
|
|
2
3
|
import { throwApiError } from "../shared/apiError.js";
|
|
3
4
|
import { buildOperatorHeaders } from "./operatorHeaders.js";
|
|
@@ -65,7 +66,7 @@ export class ConnectionsClient {
|
|
|
65
66
|
throw new Error("proxy: grantId is required");
|
|
66
67
|
if (!action)
|
|
67
68
|
throw new Error("proxy: action is required");
|
|
68
|
-
if (!this.agentId) {
|
|
69
|
+
if (!this.agentId && !decodeOperatorKeyClaims(this.operatorKey)?.connectionClientId) {
|
|
69
70
|
throw new Error("proxy: agentId is required — connection broker calls must impersonate the grant holder agent");
|
|
70
71
|
}
|
|
71
72
|
const text = await this._requestRaw("POST", `/connections/${encodeURIComponent(connectionId)}/proxy`, { grantId, action, payload: payload ?? {} });
|
|
@@ -88,12 +89,12 @@ export class ConnectionsClient {
|
|
|
88
89
|
async listGrants({ connectionId, }) {
|
|
89
90
|
if (!connectionId)
|
|
90
91
|
throw new Error("listGrants: connectionId is required");
|
|
91
|
-
if (!this.agentId) {
|
|
92
|
+
if (!this.agentId && !decodeOperatorKeyClaims(this.operatorKey)?.connectionClientId) {
|
|
92
93
|
throw new Error("listGrants: agentId is required — grants are scoped to the impersonated agent");
|
|
93
94
|
}
|
|
94
95
|
const res = (await this._request("GET", `/connections/${encodeURIComponent(connectionId)}/grants`, undefined));
|
|
95
96
|
const grants = res["grants"] || [];
|
|
96
|
-
return grants.filter((g) => g["holderId"] === this.agentId);
|
|
97
|
+
return this.agentId ? grants.filter((g) => g["holderId"] === this.agentId) : grants;
|
|
97
98
|
}
|
|
98
99
|
/**
|
|
99
100
|
* cross-connection discovery over the unified GET /grants:
|
|
@@ -174,10 +175,10 @@ export class ConnectionsClient {
|
|
|
174
175
|
* connection-consent agreement in the working chat; on approval the
|
|
175
176
|
* server is connected (if needed) and the agent is granted the tools.
|
|
176
177
|
*/
|
|
177
|
-
async requestMcpConnection({ serverUrl, tools, reason, chatId, onBehalfOfUserId, }) {
|
|
178
|
+
async requestMcpConnection({ serverUrl, tools, reason, chatId, recipientId, onBehalfOfUserId, }) {
|
|
178
179
|
if (!serverUrl)
|
|
179
180
|
throw new Error("requestMcpConnection: serverUrl is required");
|
|
180
|
-
return (await this._request("POST", "/connections/mcp/requests", { serverUrl, tools, reason, chatId }, onBehalfOfUserId
|
|
181
|
+
return (await this._request("POST", "/connections/mcp/requests", { serverUrl, tools, reason, chatId, ...(recipientId ? { recipientId } : {}) }, onBehalfOfUserId
|
|
181
182
|
? { "X-On-Behalf-Of-User": onBehalfOfUserId }
|
|
182
183
|
: undefined));
|
|
183
184
|
}
|
|
@@ -107,6 +107,8 @@ export declare class ContextGrantsClient {
|
|
|
107
107
|
durationHours?: number;
|
|
108
108
|
chatId?: string;
|
|
109
109
|
ownerId?: string;
|
|
110
|
+
/** Who to ask, from the people the server offers this wake. */
|
|
111
|
+
recipientId?: string;
|
|
110
112
|
}, opts?: {
|
|
111
113
|
idempotencyKey?: string;
|
|
112
114
|
laneId?: string;
|
|
@@ -118,7 +120,7 @@ export declare class ContextGrantsClient {
|
|
|
118
120
|
kind: string;
|
|
119
121
|
id: string;
|
|
120
122
|
};
|
|
121
|
-
}>;
|
|
123
|
+
} & RequestSentTo>;
|
|
122
124
|
requestArtifactAccess(artifactId: string, input: {
|
|
123
125
|
chatId: string;
|
|
124
126
|
ownerId: string;
|
|
@@ -130,7 +132,7 @@ export declare class ContextGrantsClient {
|
|
|
130
132
|
}): Promise<{
|
|
131
133
|
status: 'pending_approval';
|
|
132
134
|
agreementId: string;
|
|
133
|
-
}>;
|
|
135
|
+
} & RequestSentTo>;
|
|
134
136
|
delegateGrant(parentGrantId: string, input: DelegateContextGrantInput, opts?: {
|
|
135
137
|
idempotencyKey?: string;
|
|
136
138
|
}): Promise<DelegateContextGrantResult>;
|
|
@@ -163,3 +165,19 @@ export declare class ContextGrantsClient {
|
|
|
163
165
|
revokedCount?: number;
|
|
164
166
|
}>;
|
|
165
167
|
}
|
|
168
|
+
/** Who got a request card and where they answer it, as the server says. */
|
|
169
|
+
export interface RequestSentTo {
|
|
170
|
+
sentTo?: {
|
|
171
|
+
id: string;
|
|
172
|
+
name: string | null;
|
|
173
|
+
role: 'customer' | 'operator';
|
|
174
|
+
};
|
|
175
|
+
approveIn?: {
|
|
176
|
+
kind: 'chat';
|
|
177
|
+
chatId: string;
|
|
178
|
+
} | {
|
|
179
|
+
kind: 'inbox';
|
|
180
|
+
};
|
|
181
|
+
/** e.g. "Sent to Ido. Ido approves it in this room." */
|
|
182
|
+
message?: string;
|
|
183
|
+
}
|
|
@@ -80,7 +80,7 @@ export class ContextGrantsClient {
|
|
|
80
80
|
if (result.status !== 'pending_approval' || !result.agreementId || result.accessGranted !== false) {
|
|
81
81
|
throw new Error('Expected a pending access request with accessGranted=false');
|
|
82
82
|
}
|
|
83
|
-
return { status: 'pending_approval', agreementId: result.agreementId, accessGranted: false, ...(result.scope ? { scope: result.scope } : {}) };
|
|
83
|
+
return { status: 'pending_approval', agreementId: result.agreementId, accessGranted: false, ...(result.scope ? { scope: result.scope } : {}), ...sentToOf(result) };
|
|
84
84
|
}
|
|
85
85
|
async requestArtifactAccess(artifactId, input, opts) {
|
|
86
86
|
const res = await fetch(`${this.baseUrl}/context/artifacts/${encodeURIComponent(artifactId)}/access-requests`, {
|
|
@@ -98,7 +98,7 @@ export class ContextGrantsClient {
|
|
|
98
98
|
const result = JSON.parse(body);
|
|
99
99
|
if (result.status !== 'pending_approval' || !result.agreementId)
|
|
100
100
|
throw new Error('Expected a pending artifact access request');
|
|
101
|
-
return { status: 'pending_approval', agreementId: result.agreementId };
|
|
101
|
+
return { status: 'pending_approval', agreementId: result.agreementId, ...sentToOf(result) };
|
|
102
102
|
}
|
|
103
103
|
async delegateGrant(parentGrantId, input, opts) {
|
|
104
104
|
const res = await fetch(`${this.baseUrl}/context/grants/${encodeURIComponent(parentGrantId)}/delegate`, {
|
|
@@ -185,3 +185,10 @@ export class ContextGrantsClient {
|
|
|
185
185
|
return JSON.parse(body);
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
|
+
function sentToOf(result) {
|
|
189
|
+
return {
|
|
190
|
+
...(result.sentTo && typeof result.sentTo === 'object' ? { sentTo: result.sentTo } : {}),
|
|
191
|
+
...(result.approveIn && typeof result.approveIn === 'object' ? { approveIn: result.approveIn } : {}),
|
|
192
|
+
...(typeof result.message === 'string' ? { message: result.message } : {}),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { decodeOperatorKeyClaims } from '../shared/operatorKey.js';
|
|
1
2
|
// one shaping rule for every agreement this package parses.
|
|
2
3
|
import { shapeAgreement } from './AgreementClient.js';
|
|
3
4
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
@@ -9,7 +10,7 @@ const buildHeaders = buildCredsHeaders;
|
|
|
9
10
|
function assertCreds(creds, op) {
|
|
10
11
|
if (!creds?.operatorKey)
|
|
11
12
|
throw new Error(`operatorKey is required for ${op}`);
|
|
12
|
-
if (!creds?.agentId)
|
|
13
|
+
if (!creds?.agentId && !decodeOperatorKeyClaims(creds.operatorKey)?.connectionClientId)
|
|
13
14
|
throw new Error(`agentId is required for ${op}`);
|
|
14
15
|
}
|
|
15
16
|
export async function publishOffer(payload, creds) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { decodeOperatorKeyClaims } from '../shared/operatorKey.js';
|
|
2
|
+
import { buildOperatorHeaders } from './operatorHeaders.js';
|
|
1
3
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
2
4
|
import { throwApiError } from '../shared/apiError.js';
|
|
3
5
|
/**
|
|
@@ -14,7 +16,7 @@ export class MessagesClient {
|
|
|
14
16
|
constructor(operatorKey, agentId) {
|
|
15
17
|
if (!operatorKey)
|
|
16
18
|
throw new Error('MessagesClient: operatorKey is required');
|
|
17
|
-
if (!agentId)
|
|
19
|
+
if (!agentId && !decodeOperatorKeyClaims(operatorKey)?.connectionClientId)
|
|
18
20
|
throw new Error('MessagesClient: agentId is required (operator-token impersonation)');
|
|
19
21
|
this.operatorKey = operatorKey;
|
|
20
22
|
this.agentId = agentId;
|
|
@@ -43,8 +45,7 @@ export class MessagesClient {
|
|
|
43
45
|
_headers() {
|
|
44
46
|
return {
|
|
45
47
|
'content-type': 'application/json',
|
|
46
|
-
|
|
47
|
-
'X-Agent-Id': this.agentId,
|
|
48
|
+
...buildOperatorHeaders(this.operatorKey, this.agentId),
|
|
48
49
|
};
|
|
49
50
|
}
|
|
50
51
|
}
|
package/dist/http/OrgsClient.js
CHANGED
package/dist/http/TaskClient.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { decodeOperatorKeyClaims } from '../shared/operatorKey.js';
|
|
1
2
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
2
3
|
import { partyActorIds } from '../types.js';
|
|
3
4
|
import { throwApiError } from '../shared/apiError.js';
|
|
@@ -8,7 +9,7 @@ const buildHeaders = buildCredsHeaders;
|
|
|
8
9
|
function assertCreds(creds, op) {
|
|
9
10
|
if (!creds?.operatorKey)
|
|
10
11
|
throw new Error(`operatorKey is required for ${op}`);
|
|
11
|
-
if (!creds?.agentId)
|
|
12
|
+
if (!creds?.agentId && !decodeOperatorKeyClaims(creds.operatorKey)?.connectionClientId)
|
|
12
13
|
throw new Error(`agentId is required for ${op}`);
|
|
13
14
|
}
|
|
14
15
|
function extractTask(data) {
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
type AssistantHints = {
|
|
2
|
+
orgId?: string;
|
|
3
|
+
resource?: string;
|
|
4
|
+
};
|
|
5
|
+
/** Node-only, opt-in request context. Ordinary HTTP clients remain browser-safe. */
|
|
6
|
+
export declare function withAssistantRequest<T>(hints: AssistantHints, run: () => T): Promise<Awaited<T>>;
|
|
7
|
+
export declare function assistantRequestHeaders(): Record<string, string>;
|
|
1
8
|
/**
|
|
2
9
|
* Auth headers for operator-key HTTP clients.
|
|
3
10
|
*
|
|
@@ -29,3 +36,4 @@ export declare function buildCredsHeaders(creds: {
|
|
|
29
36
|
agentId: string;
|
|
30
37
|
laneId?: string;
|
|
31
38
|
}): Record<string, string>;
|
|
39
|
+
export {};
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
let assistantRequest;
|
|
2
|
+
let assistantContextLoading;
|
|
3
|
+
/** Node-only, opt-in request context. Ordinary HTTP clients remain browser-safe. */
|
|
4
|
+
export async function withAssistantRequest(hints, run) {
|
|
5
|
+
assistantContextLoading ??= import('node:async_hooks').then(({ AsyncLocalStorage }) => {
|
|
6
|
+
assistantRequest = new AsyncLocalStorage();
|
|
7
|
+
return assistantRequest;
|
|
8
|
+
});
|
|
9
|
+
return await (await assistantContextLoading).run(hints, run);
|
|
10
|
+
}
|
|
11
|
+
export function assistantRequestHeaders() {
|
|
12
|
+
const hints = assistantRequest?.getStore();
|
|
13
|
+
return { ...(hints?.orgId ? { 'X-Workspace-Id': hints.orgId } : {}), ...(hints?.resource ? { 'X-Ziggs-Resource': hints.resource } : {}) };
|
|
14
|
+
}
|
|
1
15
|
/**
|
|
2
16
|
* Auth headers for operator-key HTTP clients.
|
|
3
17
|
*
|
|
@@ -27,6 +41,7 @@ export function buildOperatorHeaders(operatorKey, agentId, extra,
|
|
|
27
41
|
laneId) {
|
|
28
42
|
return {
|
|
29
43
|
Authorization: `Bearer ${operatorKey}`,
|
|
44
|
+
...assistantRequestHeaders(),
|
|
30
45
|
// `X-Agent-Id` is only sent when an agentId is present — agent-scoped keys
|
|
31
46
|
// identify the agent themselves; fleet keys must pass one.
|
|
32
47
|
...(agentId ? { 'X-Agent-Id': agentId } : {}),
|
package/dist/inboxAck.d.ts
CHANGED
|
@@ -34,12 +34,13 @@ export interface PlanInboxAckOptions {
|
|
|
34
34
|
* delivery counts (the MCP tool with no configured self id).
|
|
35
35
|
*/
|
|
36
36
|
ownAgentId?: string;
|
|
37
|
+
ownAgentIds?: readonly string[];
|
|
37
38
|
/** Failed handovers hold the watermark unless `forceAck`. */
|
|
38
39
|
failures?: number;
|
|
39
40
|
forceAck?: boolean;
|
|
40
41
|
}
|
|
41
42
|
/** Resource ids this envelope's ack must list. */
|
|
42
|
-
export declare function inboxAckHandledIds(envelope: InboxAckEnvelope, ownAgentId?: string): string[];
|
|
43
|
+
export declare function inboxAckHandledIds(envelope: InboxAckEnvelope, ownAgentId?: string, ownAgentIds?: readonly string[]): string[];
|
|
43
44
|
/** Whether an ack is allowed for this envelope, given handover state. */
|
|
44
45
|
export declare function inboxAckAllowed(envelope: InboxAckEnvelope, opts?: PlanInboxAckOptions): boolean;
|
|
45
46
|
export declare function planInboxAck(envelope: InboxAckEnvelope, opts?: PlanInboxAckOptions): InboxAckDecision;
|
package/dist/inboxAck.js
CHANGED
|
@@ -15,8 +15,8 @@ function isOutOfReach(note) {
|
|
|
15
15
|
typeof n.remedy === 'string');
|
|
16
16
|
}
|
|
17
17
|
/** Resource ids this envelope's ack must list. */
|
|
18
|
-
export function inboxAckHandledIds(envelope, ownAgentId) {
|
|
19
|
-
const mine = (assigneeId) => !ownAgentId || assigneeId === ownAgentId;
|
|
18
|
+
export function inboxAckHandledIds(envelope, ownAgentId, ownAgentIds) {
|
|
19
|
+
const mine = (assigneeId) => ownAgentIds ? ownAgentIds.includes(assigneeId ?? '') : !ownAgentId || assigneeId === ownAgentId;
|
|
20
20
|
return [
|
|
21
21
|
...new Set([
|
|
22
22
|
...(envelope.deliveries ?? [])
|
|
@@ -41,12 +41,12 @@ export function inboxAckAllowed(envelope, opts = {}) {
|
|
|
41
41
|
export function planInboxAck(envelope, opts = {}) {
|
|
42
42
|
return {
|
|
43
43
|
allowed: inboxAckAllowed(envelope, opts),
|
|
44
|
-
handledResourceIds: inboxAckHandledIds(envelope, opts.ownAgentId),
|
|
44
|
+
handledResourceIds: inboxAckHandledIds(envelope, opts.ownAgentId, opts.ownAgentIds),
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
47
|
/** Every row whose instant the server's bury-guard will ask about. */
|
|
48
|
-
function gatingRows(envelope, ownAgentId) {
|
|
49
|
-
const mine = (assigneeId) => !ownAgentId || assigneeId === ownAgentId;
|
|
48
|
+
function gatingRows(envelope, ownAgentId, ownAgentIds) {
|
|
49
|
+
const mine = (assigneeId) => ownAgentIds ? ownAgentIds.includes(assigneeId ?? '') : !ownAgentId || assigneeId === ownAgentId;
|
|
50
50
|
return [
|
|
51
51
|
...(envelope.deliveries ?? [])
|
|
52
52
|
.filter((d) => mine(d.assigneeId))
|
|
@@ -92,7 +92,7 @@ export function planPartialInboxAck(envelope, handled, opts = {}) {
|
|
|
92
92
|
}
|
|
93
93
|
if (tokenByInstant.size === 0)
|
|
94
94
|
return null;
|
|
95
|
-
const gating = gatingRows(envelope, opts.ownAgentId);
|
|
95
|
+
const gating = gatingRows(envelope, opts.ownAgentId, opts.ownAgentIds);
|
|
96
96
|
// ISO-8601 sorts lexicographically in time order, which is what every other
|
|
97
97
|
// ts comparison on this rail already relies on.
|
|
98
98
|
const instants = [...tokenByInstant.keys()].sort();
|
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export { parseErrorMessage, parseErrorCode, throwApiError, } from './shared/apiE
|
|
|
12
12
|
export { ApiError } from './types.js';
|
|
13
13
|
export type { Creds, Task, TaskState, PlanStep, PlanStepStatus, Agreement, AgreementParties, AgreementPartySide, EngagementKind, BroadcastAudience, EntryType, ContentType, MessageMetadata, MessageHandler, InboxDeliveryKind, InboxDeliveryRef, InboxChatNews, InboxProposalRef, InboxTaskRef, InboxRequestRef, InboxConnectionRequestRef, InboxHumanAttention, InboxEnvelope, InboxAckResult, InboxPeek, InboxReadOptions, } from './types.js';
|
|
14
14
|
export type { ProposeTerms, ProposeDirectInput, ProposeBroadcastInput, ProposeAgreementData, ClaimOptions, ClaimRoom, } from './http/AgreementClient.js';
|
|
15
|
-
export { decisionWords } from './decisionWords.js';
|
|
15
|
+
export { decisionWords, speakableActor } from './decisionWords.js';
|
|
16
16
|
export type { DecisionOutcome, DecisionWords, DecisionWordsInput, } from './decisionWords.js';
|
|
17
17
|
export { planInboxAck, planPartialInboxAck, inboxAckAllowed, inboxAckHandledIds, } from './inboxAck.js';
|
|
18
18
|
export type { InboxAckDecision, InboxAckStep, InboxAckEnvelope, PlanInboxAckOptions, } from './inboxAck.js';
|
|
@@ -25,3 +25,5 @@ export type { ContinuationKind, InboxAckDriver, InboxHandling, RepresentedPerson
|
|
|
25
25
|
export { inboxEngagement, isEchoOrDuplicate, isHireWorkOrder, contactPath, formationGuide, marketplaceFormation, liveHireChangedTerms, liveHireConflictFromStatus, withdrawHeldGraph, reminderHonesty, graphReleaseHonesty, hintsFromTasks, } from './engagementGuide.js';
|
|
26
26
|
export type { EngagementGuide, EngagementGuideKind, EngagementOutcome, EngagementTaskHint, ContactPathInput, FormationInput, GraphHonesty, HireTermsHonesty, WaitingOn, } from './engagementGuide.js';
|
|
27
27
|
export { isContextAccessRequest } from './shared/contextAccessRequest.js';
|
|
28
|
+
export { AssistantConnectionClient, type AssistantWorkspace, } from './http/AssistantConnectionClient.js';
|
|
29
|
+
export { withAssistantRequest, assistantRequestHeaders } from './http/operatorHeaders.js';
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ export { parseErrorMessage, parseErrorCode, throwApiError, } from './shared/apiE
|
|
|
15
15
|
export { ApiError } from './types.js';
|
|
16
16
|
// One decision, one sentence, on every rail: the reason a delivery row carries
|
|
17
17
|
// when somebody answered a request this agent made, rendered as words.
|
|
18
|
-
export { decisionWords } from './decisionWords.js';
|
|
18
|
+
export { decisionWords, speakableActor } from './decisionWords.js';
|
|
19
19
|
export { planInboxAck, planPartialInboxAck, inboxAckAllowed, inboxAckHandledIds, } from './inboxAck.js';
|
|
20
20
|
export { agreementLaneId, agreementIdFromLane, isAgreementLaneId, isAgreementId } from '@ziggs-ai/contracts';
|
|
21
21
|
export * from './shared/operatorKey.js';
|
|
@@ -24,3 +24,5 @@ export * from './utils/appUrls.js';
|
|
|
24
24
|
export { sessionOrientation, } from './sessionOrientation.js';
|
|
25
25
|
export { inboxEngagement, isEchoOrDuplicate, isHireWorkOrder, contactPath, formationGuide, marketplaceFormation, liveHireChangedTerms, liveHireConflictFromStatus, withdrawHeldGraph, reminderHonesty, graphReleaseHonesty, hintsFromTasks, } from './engagementGuide.js';
|
|
26
26
|
export { isContextAccessRequest } from './shared/contextAccessRequest.js';
|
|
27
|
+
export { AssistantConnectionClient, } from './http/AssistantConnectionClient.js';
|
|
28
|
+
export { withAssistantRequest, assistantRequestHeaders } from './http/operatorHeaders.js';
|
|
@@ -5,6 +5,7 @@ export interface OperatorKeyClaims {
|
|
|
5
5
|
keyId?: string;
|
|
6
6
|
ownerId?: string;
|
|
7
7
|
boundAgentId?: string | null;
|
|
8
|
+
connectionClientId?: string | null;
|
|
8
9
|
/**
|
|
9
10
|
* How the key was authorized. MCP consent uses `mcp_oauth` or `device_code`;
|
|
10
11
|
* provisioning and other mint paths carry their own provenance.
|
|
@@ -14,6 +14,7 @@ export function decodeOperatorKeyClaims(token) {
|
|
|
14
14
|
keyId: payload.keyId,
|
|
15
15
|
ownerId: payload.ownerId,
|
|
16
16
|
boundAgentId: typeof payload.boundAgentId === 'string' ? payload.boundAgentId : null,
|
|
17
|
+
connectionClientId: typeof payload.connectionClientId === 'string' ? payload.connectionClientId : null,
|
|
17
18
|
issuedVia: typeof payload.issuedVia === 'string' ? payload.issuedVia : undefined,
|
|
18
19
|
exp: payload.exp,
|
|
19
20
|
};
|