@ziggs-ai/api-client 0.30.2 → 0.31.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/README.md +1 -1
- package/dist/capabilities/context.js +18 -12
- package/dist/http/ContextGrantsClient.d.ts +6 -1
- package/dist/http/ContextGrantsClient.js +1 -1
- package/dist/http/ContextReadClient.d.ts +0 -1
- package/dist/http/ContextReadClient.js +0 -2
- package/dist/http/ToolPickClient.d.ts +25 -0
- package/dist/http/ToolPickClient.js +35 -0
- package/dist/http/index.d.ts +1 -0
- package/dist/http/index.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/shared/contextAccessRequest.d.ts +12 -0
- package/dist/shared/contextAccessRequest.js +6 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -106,7 +106,7 @@ Cross-org inbox and chat payloads mask counterparties behind presentation faces:
|
|
|
106
106
|
|
|
107
107
|
- Inbox connection requests expose `requesterRef` (`psn_*`) plus display name/org — not `requesterAgentId`.
|
|
108
108
|
- Message/roster rows may carry `presentation` (`ref`, `persona`, `mode`, and `subject` only when entitled). Masked senders omit `underAgreementId` / `presentedAs`.
|
|
109
|
-
- Opaque `psn_*` / `rpb_*` refs are **not** account ids. Do not use them for agent lookup, wake, or payment parties. Chat sends may
|
|
109
|
+
- Opaque `psn_*` / `rpb_*` refs are **not** account ids. Do not use them for agent lookup, wake, or payment parties. Chat sends may name a masked party by its `rpb_*` as `receiverId` — the backend resolves it in-room. Leave the receiver out to write back in the room.
|
|
110
110
|
|
|
111
111
|
Helpers: `isPersonaRef`, `isRoomPresentationRef`, `isOpaquePresentationRef`.
|
|
112
112
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isContextAccessRequest } from '../shared/contextAccessRequest.js';
|
|
1
2
|
import { presentResourceActions } from './resourceActions.js';
|
|
2
3
|
import { ContextReadClient, CONTEXT_READ_TYPES, CONTEXT_READ_VIA, parseVia, viaHint, } from '../http/ContextReadClient.js';
|
|
3
4
|
import { ContextOpenClient } from '../http/ContextOpenClient.js';
|
|
@@ -65,7 +66,7 @@ export const contextReadCapability = {
|
|
|
65
66
|
title: 'Read context you hold',
|
|
66
67
|
descriptions: {
|
|
67
68
|
sdk: `Read the contents of a scope you already hold a grant for: ${CONTEXT_READ_TYPES.join(' | ')}. Each type reads through its own entries — ${VIA_BY_TYPE}. Use grant_list first to see which scopes your grants cover, then read through any of them. ${ARTIFACT_VIA_NOTE}. Cursored; all access is grant-fenced server-side.`,
|
|
68
|
-
mcp: `Read the contents of a scope you already hold: ${CONTEXT_READ_TYPES.join(' | ')} (the type param). Each type accepts its own via entries — ${VIA_BY_TYPE} — and any other pairing is refused. ${ARTIFACT_VIA_NOTE} (e.g. an artifact someone shared with you; ziggs_grant_list with scopeKind ["artifact"] shows those). Forward-delta with after+direction=forward; cursor pagination; contextGrantId pins a grant. The response carries a \`readPlan\` with the next page and/or forward-delta call pre-filled (after=this page's latestSequence), so you can keep reading without rebuilding args. This is the single read path for all
|
|
69
|
+
mcp: `Read the contents of a scope you already hold: ${CONTEXT_READ_TYPES.join(' | ')} (the type param). Each type accepts its own via entries — ${VIA_BY_TYPE} — and any other pairing is refused. ${ARTIFACT_VIA_NOTE} (e.g. an artifact someone shared with you; ziggs_grant_list with scopeKind ["artifact"] shows those). Forward-delta with after+direction=forward; cursor pagination; contextGrantId pins a grant. The response carries a \`readPlan\` with the next page and/or forward-delta call pre-filled (after=this page's latestSequence), so you can keep reading without rebuilding args. This is the single read path for all context types — to discover which scopes exist, use the listers: ziggs_chat_list, ziggs_task_list, ziggs_agreement_list, ziggs_grant_list, ziggs_link_list.`,
|
|
69
70
|
},
|
|
70
71
|
annotation: 'read-only',
|
|
71
72
|
params: {
|
|
@@ -301,7 +302,7 @@ function presentOpenResult(result, env) {
|
|
|
301
302
|
return [];
|
|
302
303
|
const row = item;
|
|
303
304
|
const ask = row.grantRequest;
|
|
304
|
-
if (row.status !== 'active' || ask
|
|
305
|
+
if (row.status !== 'active' || !isContextAccessRequest(ask) || ask.holderId !== fullCreds(env).agentId || !ask.scope?.id || !['artifact', 'chat', 'agreement', 'task'].includes(ask.scope.kind ?? ''))
|
|
305
306
|
return [];
|
|
306
307
|
return [{ kind: 'resume', tool: env.surface === 'mcp' ? 'ziggs_open' : 'open', args: { [`${ask.scope.kind}Id`]: ask.scope.id }, effect: 'read_only', note: 'Approval recorded. Open the original scope; the server rechecks expiry and revocation.' }];
|
|
307
308
|
});
|
|
@@ -406,30 +407,35 @@ export const accessRequestCapability = {
|
|
|
406
407
|
key: 'access_request',
|
|
407
408
|
names: { sdk: 'access_request', mcp: 'ziggs_access_request' },
|
|
408
409
|
title: 'Request access for yourself',
|
|
410
|
+
annotation: 'write', needsAgentId: true,
|
|
409
411
|
descriptions: {
|
|
410
|
-
sdk: '
|
|
411
|
-
mcp: '
|
|
412
|
+
sdk: 'Request optional bounded access to one artifact, chat, agreement context or task. Creates only a pending approval; never grants access or approves. Artifact requests need chatId and ownerId of the person authorized to share it. 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. Artifact requests require chatId and ownerId of the authorized person in that conversation. 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.',
|
|
412
414
|
},
|
|
413
|
-
annotation: 'write', needsAgentId: true,
|
|
414
415
|
params: {
|
|
415
416
|
scopeKind: { type: 'string', required: true, enum: ['artifact', 'chat', 'agreement', 'task'] },
|
|
416
|
-
scopeId: { type: 'string',
|
|
417
|
+
scopeId: { type: 'string', description: 'Original resource id. Omit when using a hire agreementId to request its workspace summary.' },
|
|
418
|
+
agreementId: { type: 'string', description: 'Artifact scope only: current hire id, used instead of scopeId to request the system-maintained workspace summary.' },
|
|
417
419
|
reason: { type: 'string', required: true, description: 'Why access is needed and who will receive the reply or summary' },
|
|
418
420
|
permission: { type: 'string', enum: ['read', 'reply'], description: 'read by default; reply is read and reply and is only valid for a chat' },
|
|
419
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.' },
|
|
420
422
|
durationHours: { type: 'number', description: 'Integer 1–168 hours after approval; default 24' },
|
|
421
|
-
chatId: { type: 'string', description: 'Artifact requests only: existing conversation with the owner' },
|
|
422
|
-
ownerId: { type: 'string', description: 'Artifact requests only: human account or participant reference in that conversation' },
|
|
423
|
+
chatId: { type: 'string', description: 'Artifact or workspace requests only: existing conversation with the owner' },
|
|
424
|
+
ownerId: { type: 'string', description: 'Artifact or workspace requests only: human account or participant reference in that conversation' },
|
|
423
425
|
idempotencyKey: { type: 'string', description: 'Reuse for the same logical request' },
|
|
424
426
|
},
|
|
425
427
|
handler: async (args, env) => {
|
|
426
|
-
for (const key of ['scopeKind', '
|
|
428
|
+
for (const key of ['scopeKind', 'reason']) {
|
|
427
429
|
if (typeof args[key] !== 'string' || !String(args[key]).trim())
|
|
428
430
|
throw new Error(`${key} is required`);
|
|
429
431
|
}
|
|
430
432
|
const scopeKind = args.scopeKind;
|
|
431
433
|
if (!['artifact', 'chat', 'agreement', 'task'].includes(scopeKind))
|
|
432
434
|
throw new Error('Unsupported scopeKind');
|
|
435
|
+
const agreementId = typeof args.agreementId === 'string' ? args.agreementId.trim() : undefined;
|
|
436
|
+
const scopeId = typeof args.scopeId === 'string' ? args.scopeId.trim() : undefined;
|
|
437
|
+
if (Boolean(agreementId) === Boolean(scopeId) || (agreementId && scopeKind !== 'artifact'))
|
|
438
|
+
throw new Error('Provide scopeId, or agreementId for its workspace summary artifact');
|
|
433
439
|
const permission = args.permission ?? 'read';
|
|
434
440
|
if (!['read', 'reply'].includes(String(permission)) || (permission === 'reply' && scopeKind !== 'chat'))
|
|
435
441
|
throw new Error('Only chat access may request reply permission');
|
|
@@ -440,15 +446,15 @@ export const accessRequestCapability = {
|
|
|
440
446
|
throw new Error('durationHours must be an integer between 1 and 168');
|
|
441
447
|
const creds = fullCreds(env);
|
|
442
448
|
const result = await new ContextGrantsClient(creds.operatorKey, creds.agentId, env.baseUrl).requestAccess({
|
|
443
|
-
scopeKind, scopeId
|
|
449
|
+
scopeKind, scopeId, agreementId, reason: String(args.reason).trim(),
|
|
444
450
|
permission: permission, temporal: args.temporal,
|
|
445
451
|
durationHours, chatId: args.chatId, ownerId: args.ownerId,
|
|
446
452
|
}, { idempotencyKey: args.idempotencyKey, laneId: creds.laneId });
|
|
447
453
|
return {
|
|
448
454
|
...result, outcome: 'pending', state: 'requested', actingAgentId: creds.agentId,
|
|
449
|
-
scope: { kind: scopeKind, id:
|
|
455
|
+
scope: result.scope ?? { kind: scopeKind, id: scopeId },
|
|
450
456
|
appUrl: `${(env.webUrl ?? 'https://ziggsai.com').replace(/\/$/, '')}/app/agreements/${encodeURIComponent(result.agreementId)}`,
|
|
451
|
-
resumeAfterApproval: { tool: env.surface === 'mcp' ? 'ziggs_open' : 'open', args: { [`${scopeKind}Id`]:
|
|
457
|
+
resumeAfterApproval: { tool: env.surface === 'mcp' ? 'ziggs_open' : 'open', args: result.scope ? { [`${result.scope.kind}Id`]: result.scope.id } : { [`${scopeKind}Id`]: scopeId } },
|
|
452
458
|
note: 'No access granted. The authorized person must approve; then open the original resource using the same agent identity.',
|
|
453
459
|
};
|
|
454
460
|
},
|
|
@@ -99,7 +99,8 @@ export declare class ContextGrantsClient {
|
|
|
99
99
|
/** Request-only endpoint: a successful call must still have granted no access. */
|
|
100
100
|
requestAccess(input: {
|
|
101
101
|
scopeKind: 'artifact' | 'chat' | 'agreement' | 'task';
|
|
102
|
-
scopeId
|
|
102
|
+
scopeId?: string;
|
|
103
|
+
agreementId?: string;
|
|
103
104
|
reason: string;
|
|
104
105
|
permission?: 'read' | 'reply';
|
|
105
106
|
temporal?: 'from-now' | 'from-start';
|
|
@@ -113,6 +114,10 @@ export declare class ContextGrantsClient {
|
|
|
113
114
|
status: 'pending_approval';
|
|
114
115
|
agreementId: string;
|
|
115
116
|
accessGranted: false;
|
|
117
|
+
scope?: {
|
|
118
|
+
kind: string;
|
|
119
|
+
id: string;
|
|
120
|
+
};
|
|
116
121
|
}>;
|
|
117
122
|
requestArtifactAccess(artifactId: string, input: {
|
|
118
123
|
chatId: string;
|
|
@@ -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 };
|
|
83
|
+
return { status: 'pending_approval', agreementId: result.agreementId, accessGranted: false, ...(result.scope ? { scope: result.scope } : {}) };
|
|
84
84
|
}
|
|
85
85
|
async requestArtifactAccess(artifactId, input, opts) {
|
|
86
86
|
const res = await fetch(`${this.baseUrl}/context/artifacts/${encodeURIComponent(artifactId)}/access-requests`, {
|
|
@@ -124,8 +124,6 @@ export class ContextReadClient {
|
|
|
124
124
|
url.searchParams.set('via', opts.taskId ? `task:${opts.taskId}` : opts.via ?? (chatId.startsWith('agrn-') ? `agreement:${opts.agreementId ?? chatId.slice(5)}` : `chat:${chatId}`));
|
|
125
125
|
if (opts.agreementId)
|
|
126
126
|
url.searchParams.set('agreementId', opts.agreementId);
|
|
127
|
-
if (opts.messageId)
|
|
128
|
-
url.searchParams.set('messageId', opts.messageId);
|
|
129
127
|
if (opts.artifactId)
|
|
130
128
|
url.searchParams.set('artifactId', opts.artifactId);
|
|
131
129
|
if (opts.maxMessages != null) {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Creds } from '../types.js';
|
|
2
|
+
/** One catalog row as the picker reads it. */
|
|
3
|
+
export interface ToolPickOption {
|
|
4
|
+
name: string;
|
|
5
|
+
line: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* - `pick` one tool does the need: `tool` names it.
|
|
9
|
+
* - `nothing_fits` no tool does it.
|
|
10
|
+
* - `unsure` / `off` no answer worth giving: show the list.
|
|
11
|
+
*/
|
|
12
|
+
export interface ToolPickAnswer {
|
|
13
|
+
how: 'pick' | 'nothing_fits' | 'unsure' | 'off';
|
|
14
|
+
tool?: string;
|
|
15
|
+
confidence?: number;
|
|
16
|
+
}
|
|
17
|
+
/** Null means "no answer": the surface shows the list it always has. */
|
|
18
|
+
export type PickTool = (need: string, options: ToolPickOption[]) => Promise<ToolPickAnswer | null>;
|
|
19
|
+
/**
|
|
20
|
+
* The backend picker: `POST /internal/tool-pick` with this connection's key.
|
|
21
|
+
* The model call and its key live on the backend, since this server also runs
|
|
22
|
+
* on a caller's own machine. Any failure is null, including the 404 from a
|
|
23
|
+
* backend that predates the route.
|
|
24
|
+
*/
|
|
25
|
+
export declare function backendToolPick(creds: Creds): PickTool;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { buildCredsHeaders } from './operatorHeaders.js';
|
|
2
|
+
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
|
+
/** A listing is always the fallback, so waiting long for a pick buys nothing. */
|
|
4
|
+
const TIMEOUT_MS = 6000;
|
|
5
|
+
/**
|
|
6
|
+
* The backend picker: `POST /internal/tool-pick` with this connection's key.
|
|
7
|
+
* The model call and its key live on the backend, since this server also runs
|
|
8
|
+
* on a caller's own machine. Any failure is null, including the 404 from a
|
|
9
|
+
* backend that predates the route.
|
|
10
|
+
*/
|
|
11
|
+
export function backendToolPick(creds) {
|
|
12
|
+
return async (need, options) => {
|
|
13
|
+
if (!creds.operatorKey)
|
|
14
|
+
return null;
|
|
15
|
+
try {
|
|
16
|
+
const res = await fetch(`${getBackendUrl()}/internal/tool-pick`, {
|
|
17
|
+
method: 'POST',
|
|
18
|
+
headers: buildCredsHeaders(creds),
|
|
19
|
+
body: JSON.stringify({ need, options }),
|
|
20
|
+
signal: AbortSignal.timeout(TIMEOUT_MS),
|
|
21
|
+
});
|
|
22
|
+
if (!res.ok) {
|
|
23
|
+
await res.body?.cancel().catch(() => undefined);
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const data = (await res.json().catch(() => null));
|
|
27
|
+
if (!data || typeof data.how !== 'string')
|
|
28
|
+
return null;
|
|
29
|
+
return data;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
package/dist/http/index.d.ts
CHANGED
|
@@ -33,3 +33,4 @@ export { InboxClient } from './InboxClient.js';
|
|
|
33
33
|
export { planInboxAck, planPartialInboxAck, inboxAckAllowed, inboxAckHandledIds, } from '../inboxAck.js';
|
|
34
34
|
export type { InboxAckDecision, InboxAckStep, InboxAckEnvelope, PlanInboxAckOptions, } from '../inboxAck.js';
|
|
35
35
|
export { WAKE_LANE_HEADER, laneHeader, buildOperatorHeaders, buildCredsHeaders, } from './operatorHeaders.js';
|
|
36
|
+
export { backendToolPick, type ToolPickOption, type ToolPickAnswer, type PickTool } from './ToolPickClient.js';
|
package/dist/http/index.js
CHANGED
|
@@ -25,3 +25,4 @@ export { planInboxAck, planPartialInboxAck, inboxAckAllowed, inboxAckHandledIds,
|
|
|
25
25
|
// because the MCP transports in agent-sdk and ziggs-mcp send it too, and a
|
|
26
26
|
// copy of a header nobody notices missing goes stale unseen.
|
|
27
27
|
export { WAKE_LANE_HEADER, laneHeader, buildOperatorHeaders, buildCredsHeaders, } from './operatorHeaders.js';
|
|
28
|
+
export { backendToolPick } from './ToolPickClient.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -24,3 +24,4 @@ export { sessionOrientation, } from './sessionOrientation.js';
|
|
|
24
24
|
export type { ContinuationKind, InboxAckDriver, InboxHandling, RepresentedPerson, SessionContinuation, SessionOrientation, SessionOrientationInput, } from './sessionOrientation.js';
|
|
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
|
+
export { isContextAccessRequest } from './shared/contextAccessRequest.js';
|
package/dist/index.js
CHANGED
|
@@ -23,3 +23,4 @@ export { INBOX_HOST_CLAIMANT_HEADER, INSTANCE_IDENTITY_MAX_LENGTH, cliHostIdenti
|
|
|
23
23
|
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
|
+
export { isContextAccessRequest } from './shared/contextAccessRequest.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** The common marker carried by access-request approvals and notifications. */
|
|
2
|
+
export interface ContextAccessRequest {
|
|
3
|
+
primitive: 'context';
|
|
4
|
+
kind: 'access-request';
|
|
5
|
+
holderId?: string;
|
|
6
|
+
scope?: {
|
|
7
|
+
kind?: string;
|
|
8
|
+
id?: string;
|
|
9
|
+
};
|
|
10
|
+
resumeAgreementId?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function isContextAccessRequest(value: unknown): value is ContextAccessRequest;
|