@ziggs-ai/api-client 0.37.21 → 0.37.23
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/CHANGELOG.md +11 -0
- package/dist/capabilities/artifacts.js +14 -4
- package/dist/capabilities/chat.js +2 -2
- package/dist/capabilities/grants.js +2 -1
- package/dist/capabilities/index.d.ts +1 -1
- package/dist/capabilities/index.js +1 -1
- package/dist/capabilities/listedFields.d.ts +11 -0
- package/dist/capabilities/listedFields.js +54 -3
- package/dist/capabilities/marketplace.js +7 -3
- package/dist/capabilities/mcpBriefs.js +1 -2
- package/dist/capabilities/tasks.js +6 -2
- package/dist/http/AgreementClient.js +5 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.37.23
|
|
6
|
+
|
|
7
|
+
- A pair `chat_open` accepts an idempotency key. Passing `chatId` without `participantIds` names only `chatId`.
|
|
8
|
+
- `includeUnreadable: false` never attaches the unreadable list. Omitting it still attaches that list when nothing matched.
|
|
9
|
+
- `fields` resolves `name`, `title`, `agentId`, `providerId`, `kind`, `scopeKind` and `scopeId` when the row stores them under another key, and lists a name that still matches nothing in `unknownFields`.
|
|
10
|
+
- `respondToAgreement` uses the same refusal statuses as counter: 404 when the agreement is missing, 403 when this caller cannot approve it.
|
|
11
|
+
|
|
12
|
+
## 0.37.22
|
|
13
|
+
|
|
14
|
+
- A full inbox read says it takes or renews this host's lease and that waitSeconds long-polls. The read stays read-only. Acknowledgement stays on its own call.
|
|
15
|
+
|
|
5
16
|
## 0.37.21
|
|
6
17
|
|
|
7
18
|
- A room lane can name the agreement the wake works under: `laneUnderAgreement(chatId, agreementId)` builds it, and the lane header sends it as `X-Ziggs-Agreement` beside `X-Ziggs-Lane`. The backend takes it only for an agreement the agent has mail under in that room.
|
|
@@ -3,7 +3,7 @@ import { ARTIFACT_FILE_FORMATS, ArtifactsClient, } from '../http/ArtifactsClient
|
|
|
3
3
|
import { ContextGrantsClient } from '../http/ContextGrantsClient.js';
|
|
4
4
|
import { ContextDiscoveryClient, } from '../http/ContextDiscoveryClient.js';
|
|
5
5
|
import { fullCreds } from './types.js';
|
|
6
|
-
import { LIST_FIELDS_PARAM, parseListFields, pickListedRows } from './listedFields.js';
|
|
6
|
+
import { LIST_FIELDS_PARAM, listedFieldNote, parseListFields, pickListedRows } from './listedFields.js';
|
|
7
7
|
import { nextCall } from './nextCall.js';
|
|
8
8
|
/**
|
|
9
9
|
* teaching: name the result slot on the success path so an agent finds
|
|
@@ -311,7 +311,11 @@ export const listArtifactsCapability = {
|
|
|
311
311
|
});
|
|
312
312
|
const fields = parseListFields(args['fields']);
|
|
313
313
|
return fields
|
|
314
|
-
? {
|
|
314
|
+
? {
|
|
315
|
+
...listed,
|
|
316
|
+
artifacts: pickListedRows(listed.artifacts, fields),
|
|
317
|
+
...listedFieldNote(listed.artifacts, fields),
|
|
318
|
+
}
|
|
315
319
|
: listed;
|
|
316
320
|
},
|
|
317
321
|
};
|
|
@@ -418,7 +422,7 @@ export const findArtifactsCapability = {
|
|
|
418
422
|
},
|
|
419
423
|
includeUnreadable: {
|
|
420
424
|
type: 'boolean',
|
|
421
|
-
description: 'Also list context you hold no grant on (labels only, never contents).
|
|
425
|
+
description: 'Also list context you hold no grant on (labels only, never contents). When omitted, that list is attached only if nothing matched. Pass true to ask for it alongside matches. Pass false to leave it off.',
|
|
422
426
|
},
|
|
423
427
|
fields: LIST_FIELDS_PARAM,
|
|
424
428
|
},
|
|
@@ -431,8 +435,14 @@ export const findArtifactsCapability = {
|
|
|
431
435
|
});
|
|
432
436
|
const fields = parseListFields(args['fields']);
|
|
433
437
|
const shaped = fields
|
|
434
|
-
? {
|
|
438
|
+
? {
|
|
439
|
+
...found,
|
|
440
|
+
artifacts: pickListedRows(found.artifacts, fields),
|
|
441
|
+
...listedFieldNote(found.artifacts, fields),
|
|
442
|
+
}
|
|
435
443
|
: found;
|
|
444
|
+
if (args['includeUnreadable'] === false)
|
|
445
|
+
return shaped;
|
|
436
446
|
const asked = args['includeUnreadable'] === true;
|
|
437
447
|
if (!asked && found.artifacts.length > 0)
|
|
438
448
|
return shaped;
|
|
@@ -115,8 +115,8 @@ export const openConversationCapability = {
|
|
|
115
115
|
throw new Error('Group creation requires an idempotencyKey; reuse it when retrying');
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
|
-
else if (args.chatId
|
|
119
|
-
throw new Error('chatId
|
|
118
|
+
else if (args.chatId) {
|
|
119
|
+
throw new Error('chatId requires participantIds');
|
|
120
120
|
}
|
|
121
121
|
if (typeof args.name === 'string' && args.name.trim() && !Array.isArray(args.participantIds) && args.newChat !== true) {
|
|
122
122
|
throw new Error('A name is for a new group or a newChat room. Pair rooms take no name; the person is the name.');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { GrantsClient } from '../http/GrantsClient.js';
|
|
2
2
|
import { GRANT_SCOPE_KINDS, } from '../http/grants.js';
|
|
3
3
|
import { fullCreds } from './types.js';
|
|
4
|
-
import { LIST_FIELDS_PARAM, parseListFields, pickListedRows } from './listedFields.js';
|
|
4
|
+
import { LIST_FIELDS_PARAM, listedFieldNote, parseListFields, pickListedRows } from './listedFields.js';
|
|
5
5
|
// BOTH the tool's param enum and its validator, so a missing kind makes the
|
|
6
6
|
// filter the description advertises fail validation. Derived from the canonical
|
|
7
7
|
// list next to the type rather than restated, because that is exactly how
|
|
@@ -97,6 +97,7 @@ export const listGrantsCapability = {
|
|
|
97
97
|
return {
|
|
98
98
|
count: items.length,
|
|
99
99
|
grants: pickListedRows(items, fields),
|
|
100
|
+
...listedFieldNote(items, fields),
|
|
100
101
|
nextCursor,
|
|
101
102
|
...(unreadableRails?.length ? { unreadableRails } : {}),
|
|
102
103
|
};
|
|
@@ -8,7 +8,7 @@ export { PARTY_CAPABILITIES, partyNoteCapability } from './parties.js';
|
|
|
8
8
|
export { LINK_CAPABILITIES, listLinksCapability, proposeLinkCapability, linkIsReachOnly, } from './links.js';
|
|
9
9
|
export { INTRODUCTION_CAPABILITIES, mintIntroductionCapability, redeemIntroductionCapability, listIntroductionsCapability, revokeIntroductionCapability, } from './introductions.js';
|
|
10
10
|
export { AGREEMENT_CAPABILITIES, agreementClaimCapability, presentClaimResult, } from './agreements.js';
|
|
11
|
-
export { LIST_FIELDS_PARAM, parseListFields, pickListedRow, pickListedRows, } from './listedFields.js';
|
|
11
|
+
export { LIST_FIELDS_PARAM, parseListFields, pickListedRow, pickListedRows, listedFieldNote, } from './listedFields.js';
|
|
12
12
|
export { CHAT_LIST_DEFAULT_FIELDS, projectChatListRows, utf8Bytes, approxTokens, } from './chatListFields.js';
|
|
13
13
|
export { TASK_CAPABILITIES, listTasksCapability, cancelTaskCapability, presentTaskOutcome, type TaskOutcomeEffects, type TaskOutcomePresentation, } from './tasks.js';
|
|
14
14
|
export { AGREEMENT_PROPOSE_PROVIDER_ID_DESCRIPTION } from './proposeProviderId.js';
|
|
@@ -8,7 +8,7 @@ export { PARTY_CAPABILITIES, partyNoteCapability } from './parties.js';
|
|
|
8
8
|
export { LINK_CAPABILITIES, listLinksCapability, proposeLinkCapability, linkIsReachOnly, } from './links.js';
|
|
9
9
|
export { INTRODUCTION_CAPABILITIES, mintIntroductionCapability, redeemIntroductionCapability, listIntroductionsCapability, revokeIntroductionCapability, } from './introductions.js';
|
|
10
10
|
export { AGREEMENT_CAPABILITIES, agreementClaimCapability, presentClaimResult, } from './agreements.js';
|
|
11
|
-
export { LIST_FIELDS_PARAM, parseListFields, pickListedRow, pickListedRows, } from './listedFields.js';
|
|
11
|
+
export { LIST_FIELDS_PARAM, parseListFields, pickListedRow, pickListedRows, listedFieldNote, } from './listedFields.js';
|
|
12
12
|
export { CHAT_LIST_DEFAULT_FIELDS, projectChatListRows, utf8Bytes, approxTokens, } from './chatListFields.js';
|
|
13
13
|
export { TASK_CAPABILITIES, listTasksCapability, cancelTaskCapability, presentTaskOutcome, } from './tasks.js';
|
|
14
14
|
export { AGREEMENT_PROPOSE_PROVIDER_ID_DESCRIPTION } from './proposeProviderId.js';
|
|
@@ -13,5 +13,16 @@ export declare const LIST_FIELDS_PARAM: {
|
|
|
13
13
|
description: string;
|
|
14
14
|
};
|
|
15
15
|
export declare function parseListFields(raw: unknown): string[] | undefined;
|
|
16
|
+
/** The value a caller asked for, including the names list rows actually store. */
|
|
17
|
+
export declare function readListedField(row: Record<string, unknown>, key: string): {
|
|
18
|
+
found: true;
|
|
19
|
+
value: unknown;
|
|
20
|
+
} | {
|
|
21
|
+
found: false;
|
|
22
|
+
};
|
|
16
23
|
export declare function pickListedRow(row: Record<string, unknown>, fields: string[] | undefined): Record<string, unknown>;
|
|
24
|
+
/** Names in `fields` that no row could resolve. Empty rows say nothing. */
|
|
25
|
+
export declare function listedFieldNote(rows: unknown[], fields: string[] | undefined): {
|
|
26
|
+
unknownFields?: string[];
|
|
27
|
+
};
|
|
17
28
|
export declare function pickListedRows(rows: unknown[], fields: string[] | undefined): unknown[];
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
export const LIST_FIELDS_PARAM = {
|
|
9
9
|
type: 'array',
|
|
10
10
|
items: { type: 'string' },
|
|
11
|
-
description: 'Return only these keys on each row. Omit for the full row. Keep the id you will use next (agreementId, taskId, artifactId, grantId).',
|
|
11
|
+
description: 'Return only these keys on each row. Omit for the full row. Keep the id you will use next (agreementId, taskId, artifactId, grantId). A name no row has is listed in unknownFields.',
|
|
12
12
|
};
|
|
13
13
|
export function parseListFields(raw) {
|
|
14
14
|
if (raw == null)
|
|
@@ -26,16 +26,67 @@ export function parseListFields(raw) {
|
|
|
26
26
|
}
|
|
27
27
|
return out;
|
|
28
28
|
}
|
|
29
|
+
function asRecord(value) {
|
|
30
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
31
|
+
? value
|
|
32
|
+
: null;
|
|
33
|
+
}
|
|
34
|
+
/** The value a caller asked for, including the names list rows actually store. */
|
|
35
|
+
export function readListedField(row, key) {
|
|
36
|
+
if (key in row)
|
|
37
|
+
return { found: true, value: row[key] };
|
|
38
|
+
const scope = asRecord(row.scope);
|
|
39
|
+
if (key === 'scopeKind' && scope && 'kind' in scope)
|
|
40
|
+
return { found: true, value: scope.kind };
|
|
41
|
+
if (key === 'scopeId' && scope && 'id' in scope)
|
|
42
|
+
return { found: true, value: scope.id };
|
|
43
|
+
const terms = asRecord(row.terms);
|
|
44
|
+
const description = typeof row.description === 'string'
|
|
45
|
+
? row.description
|
|
46
|
+
: typeof terms?.description === 'string'
|
|
47
|
+
? terms.description
|
|
48
|
+
: undefined;
|
|
49
|
+
if ((key === 'name' || key === 'title') && description !== undefined)
|
|
50
|
+
return { found: true, value: description };
|
|
51
|
+
if (key === 'name' && typeof row.title === 'string')
|
|
52
|
+
return { found: true, value: row.title };
|
|
53
|
+
if (key === 'title' && typeof row.name === 'string')
|
|
54
|
+
return { found: true, value: row.name };
|
|
55
|
+
if (key === 'agentId' && 'providerAgent' in row)
|
|
56
|
+
return { found: true, value: row.providerAgent };
|
|
57
|
+
if (key === 'providerId' && 'provider' in row)
|
|
58
|
+
return { found: true, value: row.provider };
|
|
59
|
+
if (key === 'kind' && 'engagementKind' in row)
|
|
60
|
+
return { found: true, value: row.engagementKind };
|
|
61
|
+
const parties = asRecord(row.parties);
|
|
62
|
+
const provider = asRecord(parties?.provider);
|
|
63
|
+
if (key === 'agentId' && provider && 'actor' in provider)
|
|
64
|
+
return { found: true, value: provider.actor };
|
|
65
|
+
if (key === 'providerId' && provider && 'principal' in provider)
|
|
66
|
+
return { found: true, value: provider.principal };
|
|
67
|
+
return { found: false };
|
|
68
|
+
}
|
|
29
69
|
export function pickListedRow(row, fields) {
|
|
30
70
|
if (!fields)
|
|
31
71
|
return row;
|
|
32
72
|
const picked = {};
|
|
33
73
|
for (const key of fields) {
|
|
34
|
-
|
|
35
|
-
|
|
74
|
+
const resolved = readListedField(row, key);
|
|
75
|
+
if (resolved.found)
|
|
76
|
+
picked[key] = resolved.value;
|
|
36
77
|
}
|
|
37
78
|
return picked;
|
|
38
79
|
}
|
|
80
|
+
/** Names in `fields` that no row could resolve. Empty rows say nothing. */
|
|
81
|
+
export function listedFieldNote(rows, fields) {
|
|
82
|
+
if (!fields || rows.length === 0)
|
|
83
|
+
return {};
|
|
84
|
+
const unknown = fields.filter((key) => !rows.some((row) => {
|
|
85
|
+
const record = asRecord(row);
|
|
86
|
+
return record != null && readListedField(record, key).found;
|
|
87
|
+
}));
|
|
88
|
+
return unknown.length > 0 ? { unknownFields: unknown } : {};
|
|
89
|
+
}
|
|
39
90
|
export function pickListedRows(rows, fields) {
|
|
40
91
|
if (!fields)
|
|
41
92
|
return rows;
|
|
@@ -2,7 +2,7 @@ import { marketplaceFormation } from '../engagementGuide.js';
|
|
|
2
2
|
import { pullOffersPage, pullRequests } from '../http/MarketplaceClient.js';
|
|
3
3
|
import { fullCreds } from './types.js';
|
|
4
4
|
import { nextCall } from './nextCall.js';
|
|
5
|
-
import { LIST_FIELDS_PARAM, parseListFields, pickListedRows } from './listedFields.js';
|
|
5
|
+
import { LIST_FIELDS_PARAM, listedFieldNote, parseListFields, pickListedRows } from './listedFields.js';
|
|
6
6
|
const VIEW_KINDS = ['all', 'requests', 'offers'];
|
|
7
7
|
function publishHint(env) {
|
|
8
8
|
const claim = env.surface === 'mcp' ? 'ziggs_agreement_claim' : 'agreement_claim';
|
|
@@ -102,8 +102,11 @@ export const marketplaceViewCapability = {
|
|
|
102
102
|
]);
|
|
103
103
|
const offers = offerPage.offers;
|
|
104
104
|
const fields = parseListFields(args['fields']);
|
|
105
|
-
const
|
|
106
|
-
const
|
|
105
|
+
const requestSource = requests.map((q) => toListingRow(q, 'request'));
|
|
106
|
+
const offerSource = offers.map((o) => toListingRow(o, 'offer'));
|
|
107
|
+
const requestRows = pickListedRows(requestSource, fields);
|
|
108
|
+
const offerRows = pickListedRows(offerSource, fields);
|
|
109
|
+
const fieldNote = listedFieldNote([...requestSource, ...offerSource], fields);
|
|
107
110
|
const listingAgreementIds = [...requests, ...offers]
|
|
108
111
|
.map((row) => row.agreementId)
|
|
109
112
|
.filter((id) => typeof id === 'string' && id.length > 0);
|
|
@@ -128,6 +131,7 @@ export const marketplaceViewCapability = {
|
|
|
128
131
|
: {}),
|
|
129
132
|
}
|
|
130
133
|
: {}),
|
|
134
|
+
...fieldNote,
|
|
131
135
|
engagement,
|
|
132
136
|
// Claiming is the move after browsing, and the id is in the row the
|
|
133
137
|
// caller just received. The prose hint stays for the publish side, which
|
|
@@ -35,8 +35,7 @@ const BRIEFS = {
|
|
|
35
35
|
returns: 'a readPlan of next calls; backlog if more is unread',
|
|
36
36
|
next: 'ziggs_inbox_claim to handle them',
|
|
37
37
|
compact: {
|
|
38
|
-
does: 'Reads your inbox: deliveries by recipient, tasks, decisions, sessionChatCard; waitSeconds
|
|
39
|
-
changes: 'takes or renews this host’s lease',
|
|
38
|
+
does: 'Reads your inbox: deliveries by recipient, tasks, decisions, sessionChatCard; waitSeconds long-polls. It takes or renews this host’s lease.',
|
|
40
39
|
next: 'ziggs_inbox_ack after handling',
|
|
41
40
|
},
|
|
42
41
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cancelTask, listTasks } from '../http/TaskClient.js';
|
|
2
2
|
import { fullCreds, rethrowWithContext, } from './types.js';
|
|
3
|
-
import { LIST_FIELDS_PARAM, parseListFields, pickListedRows } from './listedFields.js';
|
|
3
|
+
import { LIST_FIELDS_PARAM, listedFieldNote, parseListFields, pickListedRows } from './listedFields.js';
|
|
4
4
|
import { nextCall, workContextFromEnv, } from './nextCall.js';
|
|
5
5
|
/**
|
|
6
6
|
* One list-tasks verb. MCP and the hosted SDK both used to re-declare the
|
|
@@ -59,7 +59,11 @@ export const listTasksCapability = {
|
|
|
59
59
|
}, creds);
|
|
60
60
|
const fields = parseListFields(args['fields']);
|
|
61
61
|
return fields
|
|
62
|
-
? {
|
|
62
|
+
? {
|
|
63
|
+
...listed,
|
|
64
|
+
tasks: pickListedRows(listed.tasks, fields),
|
|
65
|
+
...listedFieldNote(listed.tasks, fields),
|
|
66
|
+
}
|
|
63
67
|
: listed;
|
|
64
68
|
},
|
|
65
69
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { decodeOperatorKeyClaims } from '../shared/operatorKey.js';
|
|
2
2
|
import { runtimeLog } from '../shared/runtimeLog.js';
|
|
3
3
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
4
|
-
import { OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, isBroadcastTarget, partySideIds } from '../types.js';
|
|
4
|
+
import { ApiError, OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, isBroadcastTarget, partySideIds } from '../types.js';
|
|
5
5
|
import { throwApiError } from '../shared/apiError.js';
|
|
6
6
|
import { buildCredsHeaders } from './operatorHeaders.js';
|
|
7
7
|
// Lazy: read at call time so a `configureApiClient` call that lands after this
|
|
@@ -222,11 +222,11 @@ export async function respondToAgreement(agreementId, action, creds, opts = {})
|
|
|
222
222
|
// every link approval with "no pending approval entry".
|
|
223
223
|
const agreement = opts.agreement ?? (await getAgreementDocument(agreementId, creds));
|
|
224
224
|
if (!agreement) {
|
|
225
|
-
throw new
|
|
225
|
+
throw new ApiError(`Agreement ${agreementId} not found`, 404);
|
|
226
226
|
}
|
|
227
227
|
const approval = agreement.approvalAction;
|
|
228
228
|
if (approval && (approval.state !== 'available' || !approval.partyId)) {
|
|
229
|
-
throw new
|
|
229
|
+
throw new ApiError(`${approval.explanation}${opts.appUrl ? ` Review: ${opts.appUrl}` : ''}`, 403);
|
|
230
230
|
}
|
|
231
231
|
const partyId = approval?.partyId ?? resolveMyPendingApprovalPartyId(agreement, {
|
|
232
232
|
ownerUserId: opts.ownerUserId,
|
|
@@ -246,11 +246,11 @@ export async function respondToAgreement(agreementId, action, creds, opts = {})
|
|
|
246
246
|
// through ANY surface and the hand-off sat unclaimable forever. The
|
|
247
247
|
// backend keeps a consented broadcast OPEN for claims.
|
|
248
248
|
if (!partyId) {
|
|
249
|
-
throw new
|
|
249
|
+
throw new ApiError(`Agreement ${agreementId} is an open broadcast and has no personal approval slot — respond cannot ${action} it. Claim it with the claim tool (agreement_claim / ziggs_agreement_claim), or ignore it to pass.`, 403);
|
|
250
250
|
}
|
|
251
251
|
}
|
|
252
252
|
else if (!partyId) {
|
|
253
|
-
throw new
|
|
253
|
+
throw new ApiError(`No pending approval entry for this operator on agreement ${agreementId}`, 403);
|
|
254
254
|
}
|
|
255
255
|
// If the slot is the principal's, the server accepts
|
|
256
256
|
// only under a live approval-authority grant (else 403). Do not pre-refuse
|