@ziggs-ai/api-client 0.15.1 → 0.17.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.d.ts +9 -0
- package/dist/capabilities/agreementVerbs.js +10 -1
- package/dist/capabilities/agreements.js +2 -1
- package/dist/capabilities/artifacts.js +16 -6
- package/dist/capabilities/connections.js +5 -5
- package/dist/capabilities/grants.js +8 -2
- package/dist/capabilities/index.d.ts +1 -0
- package/dist/capabilities/index.js +1 -0
- package/dist/capabilities/links.js +3 -3
- package/dist/capabilities/listedFields.d.ts +17 -0
- package/dist/capabilities/listedFields.js +45 -0
- package/dist/capabilities/marketplace.js +7 -2
- package/dist/capabilities/payments.js +7 -2
- package/dist/capabilities/tasks.js +9 -3
- package/dist/http/AgreementClient.js +3 -10
- package/dist/http/ArtifactsClient.d.ts +5 -16
- package/dist/http/ArtifactsClient.js +8 -5
- package/dist/http/ChatClient.d.ts +2 -6
- package/dist/http/ChatClient.js +3 -10
- package/dist/http/ConnectionsClient.d.ts +6 -4
- package/dist/http/ConnectionsClient.js +7 -5
- package/dist/http/ContextReadClient.js +3 -14
- package/dist/http/GrantsClient.d.ts +10 -1
- package/dist/http/GrantsClient.js +12 -2
- package/dist/http/IntroductionsClient.d.ts +11 -4
- package/dist/http/MarketplaceClient.js +3 -10
- package/dist/http/OrgsClient.d.ts +0 -14
- package/dist/http/OrgsClient.js +2 -17
- package/dist/http/PaymentsClient.d.ts +27 -3
- package/dist/http/PaymentsClient.js +18 -3
- package/dist/http/TaskClient.js +4 -11
- package/dist/http/index.d.ts +2 -1
- package/dist/http/index.js +5 -1
- package/dist/http/operatorHeaders.d.ts +22 -3
- package/dist/http/operatorHeaders.js +28 -4
- package/dist/http/paymentGrantSelection.d.ts +5 -0
- package/dist/http/paymentGrantSelection.js +26 -16
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/shared/operatorKey.d.ts +47 -0
- package/dist/shared/operatorKey.js +64 -0
- package/dist/types.d.ts +6 -26
- package/dist/types.js +1 -19
- package/dist/utils/appUrls.d.ts +7 -0
- package/dist/utils/appUrls.js +38 -0
- package/package.json +3 -2
|
@@ -9,6 +9,15 @@ export declare const agreementRequestCapability: CapabilityDefinition;
|
|
|
9
9
|
* to be doing today, and the publish path says so at its own end
|
|
10
10
|
* (`POST /marketplace/offers/publish`). Hanging one under a job would kill the
|
|
11
11
|
* listing when the job ended.
|
|
12
|
+
*
|
|
13
|
+
* No price default here on purpose: an unstated price is a free hire, and the
|
|
14
|
+
* publish path is the one place that says so. Stating a price publishes a paid
|
|
15
|
+
* listing.
|
|
16
|
+
*
|
|
17
|
+
* No replay key either, and that one has a cost worth knowing: each call
|
|
18
|
+
* publishes a fresh row rather than replaying, and the previous listing is
|
|
19
|
+
* retired as it goes, so an agent that republishes leaves dead rows behind it.
|
|
20
|
+
* One listing per agent still holds; the churn does not.
|
|
12
21
|
*/
|
|
13
22
|
export declare const agreementOfferCapability: CapabilityDefinition;
|
|
14
23
|
export declare const agreementHandoffCapability: CapabilityDefinition;
|
|
@@ -92,7 +92,7 @@ function engagementKindFrom(args) {
|
|
|
92
92
|
*/
|
|
93
93
|
const MANDATE_PARAM = {
|
|
94
94
|
type: 'string',
|
|
95
|
-
description:
|
|
95
|
+
description: "The active agreement whose work this is part of — the job you are doing. It becomes this engagement's parent, so it ends when the job ends, and it is what lets you commit without waiting on your human: work inside a job they already approved needs no second approval. Name only an agreement you are actually a party to; the server checks, and a wrong name simply earns nothing. Leave it out for work that belongs to no job. It never names who you act for — the server reads that off the room or contract that woke you.",
|
|
96
96
|
};
|
|
97
97
|
/** Pull the declared mandate off a validated arg bag, as the wire field. */
|
|
98
98
|
function mandateFrom(args) {
|
|
@@ -286,6 +286,15 @@ export const agreementRequestCapability = {
|
|
|
286
286
|
* to be doing today, and the publish path says so at its own end
|
|
287
287
|
* (`POST /marketplace/offers/publish`). Hanging one under a job would kill the
|
|
288
288
|
* listing when the job ended.
|
|
289
|
+
*
|
|
290
|
+
* No price default here on purpose: an unstated price is a free hire, and the
|
|
291
|
+
* publish path is the one place that says so. Stating a price publishes a paid
|
|
292
|
+
* listing.
|
|
293
|
+
*
|
|
294
|
+
* No replay key either, and that one has a cost worth knowing: each call
|
|
295
|
+
* publishes a fresh row rather than replaying, and the previous listing is
|
|
296
|
+
* retired as it goes, so an agent that republishes leaves dead rows behind it.
|
|
297
|
+
* One listing per agent still holds; the churn does not.
|
|
289
298
|
*/
|
|
290
299
|
export const agreementOfferCapability = {
|
|
291
300
|
key: 'agreement_offer',
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { agreementAppUrl } from '../utils/appUrls.js';
|
|
1
2
|
import { claimOpenAgreement } from '../http/agreementFlows.js';
|
|
2
3
|
import { linkIsReachOnly, webAppOrigin } from './links.js';
|
|
3
4
|
import { fullCreds } from './types.js';
|
|
@@ -25,7 +26,7 @@ export function presentClaimResult(agreement, kind, env) {
|
|
|
25
26
|
}
|
|
26
27
|
if (agreement?.status !== 'active') {
|
|
27
28
|
const held = (agreement?.approvals ?? []).find((a) => a.status === 'pending');
|
|
28
|
-
const approveUrl =
|
|
29
|
+
const approveUrl = agreementAppUrl(webAppOrigin(env), agreement.agreementId);
|
|
29
30
|
const remedy = held?.heldReason === 'contact-basis'
|
|
30
31
|
? 'This is a first engagement with that counterparty — your human approves once; a standing link covers it after that.'
|
|
31
32
|
: 'Claiming for a job your human already approved activates at once — name the job with mandateAgreementId.';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ArtifactsClient } from '../http/ArtifactsClient.js';
|
|
2
2
|
import { ContextGrantsClient } from '../http/ContextGrantsClient.js';
|
|
3
3
|
import { fullCreds } from './types.js';
|
|
4
|
+
import { LIST_FIELDS_PARAM, parseListFields, pickListedRows } from './listedFields.js';
|
|
4
5
|
/**
|
|
5
6
|
* teaching: name the result slot on the success path so an agent finds
|
|
6
7
|
* the right move unaided — worded in each surface's task grammar (SDK
|
|
@@ -90,6 +91,7 @@ async function recordFileArtifact(args, env, visibility) {
|
|
|
90
91
|
const byteSize = bytes.byteLength;
|
|
91
92
|
const uploaded = await client.uploadUrl({
|
|
92
93
|
filename,
|
|
94
|
+
name: args['name']?.trim() || undefined,
|
|
93
95
|
mime,
|
|
94
96
|
// The byte length of what will be PUT, not of the base64 envelope — the
|
|
95
97
|
// presign signs this number and S3 rejects a mismatch.
|
|
@@ -134,9 +136,8 @@ export const recordArtifactCapability = {
|
|
|
134
136
|
'taskId to bind it to the task. A heavy deliverable belongs in an artifact rather than pasted into a message. ' +
|
|
135
137
|
ARTIFACT_RECORD_INLINE_CAP,
|
|
136
138
|
mcp:
|
|
137
|
-
// Canonical for ziggs-mcp (tools.ts must not override).
|
|
138
|
-
//
|
|
139
|
-
// record-artifact-teaching.test.ts gates the live tool against both.
|
|
139
|
+
// Canonical for ziggs-mcp (tools.ts must not override). Shared protocol
|
|
140
|
+
// (where finished work goes) lives on connect instructions.
|
|
140
141
|
'Write an artifact — text (text) or a file (filename + mime + contentBase64; presign, upload ' +
|
|
141
142
|
'and completion all happen inside this one call, so there is no separate upload dance). ' +
|
|
142
143
|
'Scope is optional — pass agreementId or chatId to record it into that ' +
|
|
@@ -144,8 +145,7 @@ export const recordArtifactCapability = {
|
|
|
144
145
|
'free-standing artifact that is yours until you attach or share it. Never guess a scope: ' +
|
|
145
146
|
'recording with none always succeeds. Set visibility explicitly. ' +
|
|
146
147
|
'For a finished deliverable, set contentType=result and pass taskId to bind it to the task. ' +
|
|
147
|
-
ARTIFACT_RECORD_INLINE_CAP
|
|
148
|
-
' Deliver finished work where the parties agreed it goes: in chat, as a task result, or as an artifact. When the work rides a task, close it with ziggs_task_set_result ({ taskId, state, result: { summary, status, links } }) too, because an agent picking the work up from its own inbox reads that result and not the conversation. Record heavy deliverables as artifacts (ziggs_artifact_record, contentType result, taskId to bind it) rather than pasting them into a message.',
|
|
148
|
+
ARTIFACT_RECORD_INLINE_CAP,
|
|
149
149
|
},
|
|
150
150
|
annotation: 'write',
|
|
151
151
|
params: {
|
|
@@ -157,6 +157,10 @@ export const recordArtifactCapability = {
|
|
|
157
157
|
type: 'string',
|
|
158
158
|
description: 'Base64 file bytes for a file artifact — requires filename and mime. This call presigns, uploads and completes; you never compute a checksum. Bytes over ~1MB will not fit in a tool call: use artifact_upload_url for those.',
|
|
159
159
|
},
|
|
160
|
+
name: {
|
|
161
|
+
type: 'string',
|
|
162
|
+
description: 'Short name shown in lists (room, agreement, artifacts page). Pass this for a text deliverable so the other party can tell what it is without opening it. A file defaults to filename when omitted.',
|
|
163
|
+
},
|
|
160
164
|
filename: {
|
|
161
165
|
type: 'string',
|
|
162
166
|
description: 'Original filename — required with contentBase64',
|
|
@@ -232,6 +236,7 @@ export const recordArtifactCapability = {
|
|
|
232
236
|
const creds = fullCreds(env);
|
|
233
237
|
const { artifactId } = await new ArtifactsClient(creds.operatorKey, creds.agentId, creds.laneId).writeStrict({
|
|
234
238
|
text,
|
|
239
|
+
name: args['name']?.trim() || undefined,
|
|
235
240
|
visibility,
|
|
236
241
|
chatId,
|
|
237
242
|
agreementId,
|
|
@@ -275,14 +280,19 @@ export const listArtifactsCapability = {
|
|
|
275
280
|
description: 'ISO timestamp — return only artifacts written strictly after this',
|
|
276
281
|
},
|
|
277
282
|
limit: { type: 'number', description: 'Page size (server default when omitted)' },
|
|
283
|
+
fields: LIST_FIELDS_PARAM,
|
|
278
284
|
},
|
|
279
285
|
needsAgentId: true,
|
|
280
286
|
handler: async (args, env) => {
|
|
281
287
|
const creds = fullCreds(env);
|
|
282
|
-
|
|
288
|
+
const listed = await new ArtifactsClient(creds.operatorKey, creds.agentId, creds.laneId).list({ authoredBy: 'me' }, {
|
|
283
289
|
after: args['after'],
|
|
284
290
|
limit: typeof args['limit'] === 'number' ? args['limit'] : undefined,
|
|
285
291
|
});
|
|
292
|
+
const fields = parseListFields(args['fields']);
|
|
293
|
+
return fields
|
|
294
|
+
? { ...listed, artifacts: pickListedRows(listed.artifacts, fields) }
|
|
295
|
+
: listed;
|
|
286
296
|
},
|
|
287
297
|
};
|
|
288
298
|
/**
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { ConnectionsClient } from "../http/ConnectionsClient.js";
|
|
2
2
|
import { rethrowWithContext, } from "./types.js";
|
|
3
3
|
function client(env) {
|
|
4
|
-
// laneId, not just the key and the agent. The lane is which
|
|
5
|
-
//
|
|
6
|
-
// another's: an agent that serves several hirers
|
|
7
|
-
// it has been given, so the holder check alone
|
|
8
|
-
// other Creds-based client already carries it.
|
|
4
|
+
// laneId, not just the key and the agent. The lane is which hire this wake is
|
|
5
|
+
// in, and the broker reads off it who the agent is acting for — which is what
|
|
6
|
+
// tells one hirer's grant from another's: an agent that serves several hirers
|
|
7
|
+
// is the holder of every grant it has been given, so the holder check alone
|
|
8
|
+
// cannot separate them. Every other Creds-based client already carries it.
|
|
9
9
|
const { operatorKey, agentId, laneId } = env.creds;
|
|
10
10
|
if (!operatorKey)
|
|
11
11
|
throw new Error("operatorKey missing from tool context");
|
|
@@ -1,6 +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
5
|
// BOTH the tool's param enum and its validator, so a missing kind makes the
|
|
5
6
|
// filter the description advertises fail validation. Derived from the canonical
|
|
6
7
|
// list next to the type rather than restated, because that is exactly how
|
|
@@ -67,6 +68,7 @@ export const listGrantsCapability = {
|
|
|
67
68
|
},
|
|
68
69
|
cursor: { type: 'string', description: 'Opaque cursor from a prior nextCursor to page' },
|
|
69
70
|
limit: { type: 'number', description: 'Page size (server default when omitted)' },
|
|
71
|
+
fields: LIST_FIELDS_PARAM,
|
|
70
72
|
},
|
|
71
73
|
needsAgentId: true,
|
|
72
74
|
handler: async (args, env) => {
|
|
@@ -80,7 +82,10 @@ export const listGrantsCapability = {
|
|
|
80
82
|
if (roleArg !== undefined && roleArg !== 'holder' && roleArg !== 'issuer') {
|
|
81
83
|
throw new Error('role must be holder or issuer');
|
|
82
84
|
}
|
|
83
|
-
const { items, nextCursor, unreadableRails } = await new GrantsClient(creds.operatorKey, creds.agentId, env.baseUrl
|
|
85
|
+
const { items, nextCursor, unreadableRails } = await new GrantsClient(creds.operatorKey, creds.agentId, env.baseUrl,
|
|
86
|
+
// The lane, so the server answers with the grants THIS wake may spend
|
|
87
|
+
// rather than everything the agent holds across every customer.
|
|
88
|
+
creds.laneId).listGrants({
|
|
84
89
|
scopeKind,
|
|
85
90
|
scopeId: typeof args['scopeId'] === 'string' ? args['scopeId'] : undefined,
|
|
86
91
|
role: roleArg,
|
|
@@ -89,9 +94,10 @@ export const listGrantsCapability = {
|
|
|
89
94
|
cursor: args['cursor'],
|
|
90
95
|
limit: typeof args['limit'] === 'number' ? args['limit'] : undefined,
|
|
91
96
|
});
|
|
97
|
+
const fields = parseListFields(args['fields']);
|
|
92
98
|
return {
|
|
93
99
|
count: items.length,
|
|
94
|
-
grants: items,
|
|
100
|
+
grants: pickListedRows(items, fields),
|
|
95
101
|
nextCursor,
|
|
96
102
|
...(unreadableRails?.length ? { unreadableRails } : {}),
|
|
97
103
|
};
|
|
@@ -5,6 +5,7 @@ export { PAYMENT_CAPABILITIES, paymentBalanceCapability } from './payments.js';
|
|
|
5
5
|
export { LINK_CAPABILITIES, listLinksCapability, proposeLinkCapability, linkIsReachOnly, } from './links.js';
|
|
6
6
|
export { INTRODUCTION_CAPABILITIES, mintIntroductionCapability, redeemIntroductionCapability, listIntroductionsCapability, revokeIntroductionCapability, } from './introductions.js';
|
|
7
7
|
export { AGREEMENT_CAPABILITIES, agreementClaimCapability, presentClaimResult, } from './agreements.js';
|
|
8
|
+
export { LIST_FIELDS_PARAM, parseListFields, pickListedRow, pickListedRows, } from './listedFields.js';
|
|
8
9
|
export { TASK_CAPABILITIES, listTasksCapability } from './tasks.js';
|
|
9
10
|
export { AGREEMENT_PROPOSE_PROVIDER_ID_DESCRIPTION } from './proposeProviderId.js';
|
|
10
11
|
export { MARKETPLACE_CAPABILITIES, marketplaceViewCapability } from './marketplace.js';
|
|
@@ -5,6 +5,7 @@ export { PAYMENT_CAPABILITIES, paymentBalanceCapability } from './payments.js';
|
|
|
5
5
|
export { LINK_CAPABILITIES, listLinksCapability, proposeLinkCapability, linkIsReachOnly, } from './links.js';
|
|
6
6
|
export { INTRODUCTION_CAPABILITIES, mintIntroductionCapability, redeemIntroductionCapability, listIntroductionsCapability, revokeIntroductionCapability, } from './introductions.js';
|
|
7
7
|
export { AGREEMENT_CAPABILITIES, agreementClaimCapability, presentClaimResult, } from './agreements.js';
|
|
8
|
+
export { LIST_FIELDS_PARAM, parseListFields, pickListedRow, pickListedRows, } from './listedFields.js';
|
|
8
9
|
export { TASK_CAPABILITIES, listTasksCapability } from './tasks.js';
|
|
9
10
|
export { AGREEMENT_PROPOSE_PROVIDER_ID_DESCRIPTION } from './proposeProviderId.js';
|
|
10
11
|
export { MARKETPLACE_CAPABILITIES, marketplaceViewCapability } from './marketplace.js';
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { resolveWebAppOrigin, connectInviteAppUrl } from '../utils/appUrls.js';
|
|
1
2
|
import { createLink, listAgreements } from '../http/AgreementClient.js';
|
|
2
3
|
import { nextCall } from './nextCall.js';
|
|
3
4
|
import { fullCreds } from './types.js';
|
|
4
|
-
const DEFAULT_WEB_URL = 'https://ziggsai.com';
|
|
5
5
|
export function webAppOrigin(env) {
|
|
6
|
-
return (env.webUrl
|
|
6
|
+
return resolveWebAppOrigin(env.webUrl);
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
9
|
* The one thing an invite travels as: `/connect/<inviteId>`.
|
|
@@ -17,7 +17,7 @@ export function webAppOrigin(env) {
|
|
|
17
17
|
* across two repos.
|
|
18
18
|
*/
|
|
19
19
|
function inviteShareUrl(env, agreementId) {
|
|
20
|
-
return
|
|
20
|
+
return connectInviteAppUrl(webAppOrigin(env), agreementId);
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
23
|
* A link is reach-only — the follow-up move differs by surface tool names.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opt-in row projection for list tools.
|
|
3
|
+
*
|
|
4
|
+
* List endpoints return the full row by default. Pass `fields` to keep only
|
|
5
|
+
* those keys — the same shape as Linear's `list_issues`. Envelope keys
|
|
6
|
+
* (count, nextCursor, hasMore) are never filtered.
|
|
7
|
+
*/
|
|
8
|
+
export declare const LIST_FIELDS_PARAM: {
|
|
9
|
+
type: "array";
|
|
10
|
+
items: {
|
|
11
|
+
type: "string";
|
|
12
|
+
};
|
|
13
|
+
description: string;
|
|
14
|
+
};
|
|
15
|
+
export declare function parseListFields(raw: unknown): string[] | undefined;
|
|
16
|
+
export declare function pickListedRow(row: Record<string, unknown>, fields: string[] | undefined): Record<string, unknown>;
|
|
17
|
+
export declare function pickListedRows(rows: unknown[], fields: string[] | undefined): unknown[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opt-in row projection for list tools.
|
|
3
|
+
*
|
|
4
|
+
* List endpoints return the full row by default. Pass `fields` to keep only
|
|
5
|
+
* those keys — the same shape as Linear's `list_issues`. Envelope keys
|
|
6
|
+
* (count, nextCursor, hasMore) are never filtered.
|
|
7
|
+
*/
|
|
8
|
+
export const LIST_FIELDS_PARAM = {
|
|
9
|
+
type: 'array',
|
|
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).',
|
|
12
|
+
};
|
|
13
|
+
export function parseListFields(raw) {
|
|
14
|
+
if (raw == null)
|
|
15
|
+
return undefined;
|
|
16
|
+
if (!Array.isArray(raw) || raw.length === 0) {
|
|
17
|
+
throw new Error('fields must be a non-empty array of row key names');
|
|
18
|
+
}
|
|
19
|
+
const out = [];
|
|
20
|
+
for (const item of raw) {
|
|
21
|
+
if (typeof item !== 'string' || !item.trim()) {
|
|
22
|
+
throw new Error('fields entries must be non-empty strings');
|
|
23
|
+
}
|
|
24
|
+
if (!out.includes(item))
|
|
25
|
+
out.push(item);
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
export function pickListedRow(row, fields) {
|
|
30
|
+
if (!fields)
|
|
31
|
+
return row;
|
|
32
|
+
const picked = {};
|
|
33
|
+
for (const key of fields) {
|
|
34
|
+
if (key in row)
|
|
35
|
+
picked[key] = row[key];
|
|
36
|
+
}
|
|
37
|
+
return picked;
|
|
38
|
+
}
|
|
39
|
+
export function pickListedRows(rows, fields) {
|
|
40
|
+
if (!fields)
|
|
41
|
+
return rows;
|
|
42
|
+
return rows.map((row) => row && typeof row === 'object' && !Array.isArray(row)
|
|
43
|
+
? pickListedRow(row, fields)
|
|
44
|
+
: row);
|
|
45
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { pullOffers, pullRequests } from '../http/MarketplaceClient.js';
|
|
2
2
|
import { fullCreds } from './types.js';
|
|
3
3
|
import { nextCall } from './nextCall.js';
|
|
4
|
+
import { LIST_FIELDS_PARAM, parseListFields, pickListedRows } from './listedFields.js';
|
|
4
5
|
const VIEW_KINDS = ['all', 'requests', 'offers'];
|
|
5
6
|
function publishHint(env) {
|
|
6
7
|
const propose = env.surface === 'mcp' ? 'ziggs_agreement_request' : 'agreement_request';
|
|
@@ -77,6 +78,7 @@ export const marketplaceViewCapability = {
|
|
|
77
78
|
},
|
|
78
79
|
limit: { type: 'number', description: 'Max rows per kind (default 20)' },
|
|
79
80
|
since: { type: 'string', description: 'ISO timestamp — only rows published after this' },
|
|
81
|
+
fields: LIST_FIELDS_PARAM,
|
|
80
82
|
},
|
|
81
83
|
needsAgentId: true,
|
|
82
84
|
handler: async (args, env) => {
|
|
@@ -93,12 +95,15 @@ export const marketplaceViewCapability = {
|
|
|
93
95
|
kind === 'offers' ? Promise.resolve([]) : pullRequests(options, creds),
|
|
94
96
|
kind === 'requests' ? Promise.resolve([]) : pullOffers(options, creds),
|
|
95
97
|
]);
|
|
98
|
+
const fields = parseListFields(args['fields']);
|
|
99
|
+
const requestRows = pickListedRows(requests.map((q) => toListingRow(q, 'request')), fields);
|
|
100
|
+
const offerRows = pickListedRows(offers.map((o) => toListingRow(o, 'offer')), fields);
|
|
96
101
|
return {
|
|
97
102
|
...(kind !== 'offers'
|
|
98
|
-
? { requests:
|
|
103
|
+
? { requests: requestRows, requestCount: requests.length }
|
|
99
104
|
: {}),
|
|
100
105
|
...(kind !== 'requests'
|
|
101
|
-
? { offers:
|
|
106
|
+
? { offers: offerRows, offerCount: offers.length }
|
|
102
107
|
: {}),
|
|
103
108
|
// Claiming is the move after browsing, and the id is in the row the
|
|
104
109
|
// caller just received. The prose hint stays for the publish side, which
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { PaymentsClient } from '../http/PaymentsClient.js';
|
|
2
2
|
import { rethrowWithContext, } from './types.js';
|
|
3
3
|
function client(env) {
|
|
4
|
-
|
|
4
|
+
// laneId too, like every other Creds-based client. Only the balance read is
|
|
5
|
+
// on this surface today, and a read does not spend — but the lane is what
|
|
6
|
+
// fences a spend to the job it was authorised in, so it is wired here rather
|
|
7
|
+
// than left for whoever brings a spending verb back to remember. Omitting it
|
|
8
|
+
// can only leave a spend unfenced; it can never widen one.
|
|
9
|
+
const { operatorKey, agentId, laneId } = env.creds;
|
|
5
10
|
if (!operatorKey)
|
|
6
11
|
throw new Error('operatorKey missing from tool context');
|
|
7
|
-
return new PaymentsClient(operatorKey, agentId, env.baseUrl);
|
|
12
|
+
return new PaymentsClient(operatorKey, agentId, env.baseUrl, laneId);
|
|
8
13
|
}
|
|
9
14
|
/**
|
|
10
15
|
* Reading the balance is the whole rail on an agent surface.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { listTasks } from '../http/TaskClient.js';
|
|
2
2
|
import { fullCreds } from './types.js';
|
|
3
|
+
import { LIST_FIELDS_PARAM, parseListFields, pickListedRows } from './listedFields.js';
|
|
3
4
|
/**
|
|
4
5
|
* One list-tasks verb. MCP and the hosted SDK both used to re-declare the
|
|
5
6
|
* same GET /tasks filters; assignedToMe vs assignedTo precedence then
|
|
@@ -11,8 +12,8 @@ export const listTasksCapability = {
|
|
|
11
12
|
names: { sdk: 'task_list', mcp: 'ziggs_task_list' },
|
|
12
13
|
title: 'List tasks',
|
|
13
14
|
descriptions: {
|
|
14
|
-
sdk: 'List tasks reachable by this agent (GET /tasks). Scope follows the operator key — same reach as chats and agreements. Optional state / assignee filters and cursor pagination. Use assignedToMe to list your own open work, and createdByMe to list the work you handed to someone else.',
|
|
15
|
-
mcp: 'List tasks reachable by the acting agent (GET /tasks). Scope is determined by the operator key — same reach as chats and agreements. Optional state / assignee filters and cursor pagination. Use assignedToMe to list your own open work, and createdByMe to list the work you handed to someone else.',
|
|
15
|
+
sdk: 'List tasks reachable by this agent (GET /tasks). Scope follows the operator key — same reach as chats and agreements. Optional state / assignee filters and cursor pagination. Use assignedToMe to list your own open work, and createdByMe to list the work you handed to someone else. Pass fields to keep only those keys on each row.',
|
|
16
|
+
mcp: 'List tasks reachable by the acting agent (GET /tasks). Scope is determined by the operator key — same reach as chats and agreements. Optional state / assignee filters and cursor pagination. Use assignedToMe to list your own open work, and createdByMe to list the work you handed to someone else. Pass fields to keep only those keys on each row.',
|
|
16
17
|
},
|
|
17
18
|
annotation: 'read-only',
|
|
18
19
|
params: {
|
|
@@ -40,6 +41,7 @@ export const listTasksCapability = {
|
|
|
40
41
|
type: 'boolean',
|
|
41
42
|
description: "Shorthand for assignedTo=<this agent's id>. Takes precedence over assignedTo when both are set.",
|
|
42
43
|
},
|
|
44
|
+
fields: LIST_FIELDS_PARAM,
|
|
43
45
|
},
|
|
44
46
|
needsAgentId: true,
|
|
45
47
|
handler: async (args, env) => {
|
|
@@ -48,13 +50,17 @@ export const listTasksCapability = {
|
|
|
48
50
|
? creds.agentId
|
|
49
51
|
: args['assignedTo'];
|
|
50
52
|
const createdBy = args['createdByMe'] ? creds.agentId : undefined;
|
|
51
|
-
|
|
53
|
+
const listed = await listTasks({
|
|
52
54
|
state: args['state'],
|
|
53
55
|
cursor: args['cursor'],
|
|
54
56
|
limit: typeof args['limit'] === 'number' ? args['limit'] : undefined,
|
|
55
57
|
assignedTo,
|
|
56
58
|
...(createdBy ? { createdBy } : {}),
|
|
57
59
|
}, creds);
|
|
60
|
+
const fields = parseListFields(args['fields']);
|
|
61
|
+
return fields
|
|
62
|
+
? { ...listed, tasks: pickListedRows(listed.tasks, fields) }
|
|
63
|
+
: listed;
|
|
58
64
|
},
|
|
59
65
|
};
|
|
60
66
|
export const TASK_CAPABILITIES = [listTasksCapability];
|
|
@@ -2,22 +2,15 @@ import { runtimeLog } from '../shared/runtimeLog.js';
|
|
|
2
2
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
3
|
import { OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, isBroadcastTarget, partySideIds } from '../types.js';
|
|
4
4
|
import { throwApiError } from '../shared/apiError.js';
|
|
5
|
+
import { buildCredsHeaders } from './operatorHeaders.js';
|
|
5
6
|
// Lazy: read at call time so a `configureApiClient` call that lands after this
|
|
6
7
|
// module is imported still takes effect. Baking it at module-load time would
|
|
7
8
|
// freeze the URL before the host has configured one.
|
|
8
9
|
function getAgreementBaseUrl() {
|
|
9
10
|
return `${getBackendUrl()}/agreements`;
|
|
10
11
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
'content-type': 'application/json',
|
|
14
|
-
Authorization: `Bearer ${creds.operatorKey}`,
|
|
15
|
-
'X-Agent-Id': creds.agentId,
|
|
16
|
-
// the wake's lane, so the backend can fence this call to the
|
|
17
|
-
// engagement it belongs to rather than the agent's whole authority.
|
|
18
|
-
...(creds.laneId ? { 'X-Ziggs-Lane': creds.laneId } : {}),
|
|
19
|
-
};
|
|
20
|
-
}
|
|
12
|
+
// Auth, agent and the wake's lane, from the one helper — see operatorHeaders.
|
|
13
|
+
const buildHeaders = buildCredsHeaders;
|
|
21
14
|
function assertCreds(creds, op) {
|
|
22
15
|
if (!creds?.operatorKey)
|
|
23
16
|
throw new Error(`operatorKey is required for ${op}`);
|
|
@@ -28,6 +28,8 @@ export interface ListArtifactsResult {
|
|
|
28
28
|
}
|
|
29
29
|
export interface WriteArtifactInput {
|
|
30
30
|
text: string;
|
|
31
|
+
/** Short list name. An upload defaults to filename when omitted. */
|
|
32
|
+
name?: string;
|
|
31
33
|
contentType?: string;
|
|
32
34
|
visibility?: ArtifactVisibility;
|
|
33
35
|
/** When the artifact is associated with a chat. */
|
|
@@ -47,6 +49,8 @@ export interface WriteArtifactInput {
|
|
|
47
49
|
}
|
|
48
50
|
export type ArtifactFileFormat = 'pdf' | 'docx' | 'hwpx' | 'md' | 'txt' | 'html';
|
|
49
51
|
export interface ArtifactUploadUrlInput {
|
|
52
|
+
/** Short list name. Defaults to filename on the server when omitted. */
|
|
53
|
+
name?: string;
|
|
50
54
|
filename: string;
|
|
51
55
|
mime: string;
|
|
52
56
|
/** Exact UTF-8 / file byte length of the object that will be PUT. */
|
|
@@ -82,22 +86,7 @@ export type ArtifactFileView = Record<string, unknown> & {
|
|
|
82
86
|
extractionError?: string | null;
|
|
83
87
|
filename?: string | null;
|
|
84
88
|
};
|
|
85
|
-
|
|
86
|
-
* Replaces `ContextReader`'s artifact reads + `ContextWriter`'s breadcrumb
|
|
87
|
-
* writes. `visibility: 'agent-private'` is the new home for agent thoughts —
|
|
88
|
-
* they persist, are searchable, but are not visible to other chat parties.
|
|
89
|
-
*/
|
|
90
|
-
/**
|
|
91
|
-
* turn a runtime lane id into a scope the backend can accept.
|
|
92
|
-
*
|
|
93
|
-
* A task with no origin chat runs on the lane `agrn-<agreementId>` (see
|
|
94
|
-
* AgentHost.laneSessionIdForTask). That is a routing key, not a chat: no such
|
|
95
|
-
* chat row exists, so passing it as `chatId` made every breadcrumb and recorded
|
|
96
|
-
* thought on an agreement lane come back 403 "not authorized for this scope" —
|
|
97
|
-
* a fictional scope reading like an auth failure. The work is agreement-scoped,
|
|
98
|
-
* so say so; same rule settled for deliverables.
|
|
99
|
-
*/
|
|
100
|
-
export declare const AGREEMENT_LANE_PREFIX = "agrn-";
|
|
89
|
+
export { AGREEMENT_LANE_PREFIX } from '@ziggs-ai/contracts';
|
|
101
90
|
export declare function artifactScopeForSession(sessionId: string): {
|
|
102
91
|
chatId: string;
|
|
103
92
|
} | {
|
|
@@ -33,17 +33,18 @@ function resolveUploadBytes(input) {
|
|
|
33
33
|
* turn a runtime lane id into a scope the backend can accept.
|
|
34
34
|
*
|
|
35
35
|
* A task with no origin chat runs on the lane `agrn-<agreementId>` (see
|
|
36
|
-
*
|
|
36
|
+
* Agent.laneSessionIdForTask). That is a routing key, not a chat: no such
|
|
37
37
|
* chat row exists, so passing it as `chatId` made every breadcrumb and recorded
|
|
38
38
|
* thought on an agreement lane come back 403 "not authorized for this scope" —
|
|
39
39
|
* a fictional scope reading like an auth failure. The work is agreement-scoped,
|
|
40
40
|
* so say so; same rule settled for deliverables.
|
|
41
41
|
*/
|
|
42
|
-
|
|
42
|
+
import { agreementIdFromLane } from '@ziggs-ai/contracts';
|
|
43
|
+
export { AGREEMENT_LANE_PREFIX } from '@ziggs-ai/contracts';
|
|
43
44
|
export function artifactScopeForSession(sessionId) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
const agreementId = agreementIdFromLane(sessionId);
|
|
46
|
+
if (agreementId)
|
|
47
|
+
return { agreementId };
|
|
47
48
|
return { chatId: sessionId };
|
|
48
49
|
}
|
|
49
50
|
export class ArtifactsClient {
|
|
@@ -145,6 +146,7 @@ export class ArtifactsClient {
|
|
|
145
146
|
headers: this._headers(),
|
|
146
147
|
body: JSON.stringify({
|
|
147
148
|
text,
|
|
149
|
+
...(input.name?.trim() ? { name: input.name.trim() } : {}),
|
|
148
150
|
contentType: input.contentType ?? 'text',
|
|
149
151
|
visibility: input.visibility ?? 'chat',
|
|
150
152
|
chatId: input.chatId,
|
|
@@ -214,6 +216,7 @@ export class ArtifactsClient {
|
|
|
214
216
|
}
|
|
215
217
|
const parsed = await this._post('/artifacts/upload-url', {
|
|
216
218
|
filename: input.filename.trim(),
|
|
219
|
+
...(input.name?.trim() ? { name: input.name.trim() } : {}),
|
|
217
220
|
mime: input.mime.trim(),
|
|
218
221
|
byteSize: input.byteSize,
|
|
219
222
|
format: input.format,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SendChatMessageResult } from '@ziggs-ai/contracts';
|
|
1
2
|
import { type Creds } from '../types.js';
|
|
2
3
|
/** Shape of GET /chats/mine items — the backend's ChatReadDto. */
|
|
3
4
|
export interface ChatSummary {
|
|
@@ -41,12 +42,7 @@ export interface SendChatMessageInput {
|
|
|
41
42
|
contentType?: string;
|
|
42
43
|
underAgreementId?: string;
|
|
43
44
|
}
|
|
44
|
-
export
|
|
45
|
-
success: boolean;
|
|
46
|
-
message: string;
|
|
47
|
-
messageId: string;
|
|
48
|
-
chatId: string;
|
|
49
|
-
}
|
|
45
|
+
export type { SendChatMessageResult } from '@ziggs-ai/contracts';
|
|
50
46
|
export type ContextTemporal = 'from-now' | 'from-start';
|
|
51
47
|
export interface AddChatMemberInput {
|
|
52
48
|
chatId: string;
|
package/dist/http/ChatClient.js
CHANGED
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
import { runtimeLog } from '../shared/runtimeLog.js';
|
|
2
2
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
3
|
import { throwApiError } from '../shared/apiError.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Authorization: `Bearer ${creds.operatorKey}`,
|
|
8
|
-
'X-Agent-Id': creds.agentId,
|
|
9
|
-
// the wake's lane, so the backend can fence this call to the
|
|
10
|
-
// engagement it belongs to rather than the agent's whole authority.
|
|
11
|
-
...(creds.laneId ? { 'X-Ziggs-Lane': creds.laneId } : {}),
|
|
12
|
-
};
|
|
13
|
-
}
|
|
4
|
+
import { buildCredsHeaders } from './operatorHeaders.js';
|
|
5
|
+
// Auth, agent and the wake's lane, from the one helper — see operatorHeaders.
|
|
6
|
+
const buildHeaders = buildCredsHeaders;
|
|
14
7
|
function assertCreds(creds, op) {
|
|
15
8
|
if (!creds?.operatorKey)
|
|
16
9
|
throw new Error(`operatorKey is required for ${op}`);
|
|
@@ -59,10 +59,12 @@ export declare class ConnectionsClient {
|
|
|
59
59
|
private readonly laneId?;
|
|
60
60
|
/**
|
|
61
61
|
* @param laneId the wake's lane (a chat id, or `agrn-<agreementId>`), sent as
|
|
62
|
-
* X-Ziggs-Lane. It tells the broker which
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
62
|
+
* X-Ziggs-Lane. It tells the broker which hire this wake is in, and the
|
|
63
|
+
* broker reads off it WHO the agent is acting for: an agent that serves
|
|
64
|
+
* several hirers holds all of their grants at once and is the holder of
|
|
65
|
+
* every one, so without a lane one hirer's grant is indistinguishable from
|
|
66
|
+
* another's, and a grant one customer gave can be spent while working for
|
|
67
|
+
* somebody else.
|
|
66
68
|
* ArtifactsClient sends the same header for the same reason.
|
|
67
69
|
*/
|
|
68
70
|
constructor(operatorKey: string, agentId?: string, baseUrl?: string, laneId?: string);
|
|
@@ -37,10 +37,12 @@ export class ConnectionsClient {
|
|
|
37
37
|
laneId;
|
|
38
38
|
/**
|
|
39
39
|
* @param laneId the wake's lane (a chat id, or `agrn-<agreementId>`), sent as
|
|
40
|
-
* X-Ziggs-Lane. It tells the broker which
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
40
|
+
* X-Ziggs-Lane. It tells the broker which hire this wake is in, and the
|
|
41
|
+
* broker reads off it WHO the agent is acting for: an agent that serves
|
|
42
|
+
* several hirers holds all of their grants at once and is the holder of
|
|
43
|
+
* every one, so without a lane one hirer's grant is indistinguishable from
|
|
44
|
+
* another's, and a grant one customer gave can be spent while working for
|
|
45
|
+
* somebody else.
|
|
44
46
|
* ArtifactsClient sends the same header for the same reason.
|
|
45
47
|
*/
|
|
46
48
|
constructor(operatorKey, agentId, baseUrl, laneId) {
|
|
@@ -101,7 +103,7 @@ export class ConnectionsClient {
|
|
|
101
103
|
* defensively for leaked secrets, as `proxy` does.
|
|
102
104
|
*/
|
|
103
105
|
async listForHolder() {
|
|
104
|
-
const grantsClient = new GrantsClient(this.operatorKey, this.agentId, this.baseUrl);
|
|
106
|
+
const grantsClient = new GrantsClient(this.operatorKey, this.agentId, this.baseUrl, this.laneId);
|
|
105
107
|
// All pages of the agent's live connection grants (not just the first page).
|
|
106
108
|
const { items, unreadableRails } = await grantsClient.listAllGrantsWithRails({
|
|
107
109
|
scopeKind: "connection",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
2
2
|
import { pollSurfaceError } from '../shared/rateLimit.js';
|
|
3
|
+
import { buildOperatorHeaders } from './operatorHeaders.js';
|
|
3
4
|
export const CONTEXT_READ_TYPES = [
|
|
4
5
|
'messages',
|
|
5
6
|
'artifacts',
|
|
@@ -92,13 +93,7 @@ export class ContextReadClient {
|
|
|
92
93
|
if (query.contextGrantId) {
|
|
93
94
|
url.searchParams.set('contextGrantId', query.contextGrantId);
|
|
94
95
|
}
|
|
95
|
-
const headers =
|
|
96
|
-
Authorization: `Bearer ${this.operatorKey}`,
|
|
97
|
-
};
|
|
98
|
-
if (this.agentId)
|
|
99
|
-
headers['X-Agent-Id'] = this.agentId;
|
|
100
|
-
if (this.laneId)
|
|
101
|
-
headers['X-Ziggs-Lane'] = this.laneId;
|
|
96
|
+
const headers = buildOperatorHeaders(this.operatorKey, this.agentId, undefined, this.laneId);
|
|
102
97
|
if (query.contextGrantId) {
|
|
103
98
|
headers['X-Context-Grant-Id'] = query.contextGrantId;
|
|
104
99
|
}
|
|
@@ -131,13 +126,7 @@ export class ContextReadClient {
|
|
|
131
126
|
if (opts.contextGrantId) {
|
|
132
127
|
url.searchParams.set('contextGrantId', opts.contextGrantId);
|
|
133
128
|
}
|
|
134
|
-
const headers =
|
|
135
|
-
Authorization: `Bearer ${this.operatorKey}`,
|
|
136
|
-
};
|
|
137
|
-
if (this.agentId)
|
|
138
|
-
headers['X-Agent-Id'] = this.agentId;
|
|
139
|
-
if (this.laneId)
|
|
140
|
-
headers['X-Ziggs-Lane'] = this.laneId;
|
|
129
|
+
const headers = buildOperatorHeaders(this.operatorKey, this.agentId, undefined, this.laneId);
|
|
141
130
|
if (opts.contextGrantId) {
|
|
142
131
|
headers['X-Context-Grant-Id'] = opts.contextGrantId;
|
|
143
132
|
}
|