@ziggs-ai/api-client 0.19.0 → 0.21.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 +7 -0
- package/dist/capabilities/agreementVerbs.js +84 -1
- package/dist/capabilities/agreements.d.ts +3 -0
- package/dist/capabilities/agreements.js +9 -1
- package/dist/capabilities/artifacts.d.ts +38 -0
- package/dist/capabilities/artifacts.js +148 -7
- package/dist/capabilities/chat.d.ts +31 -1
- package/dist/capabilities/chat.js +39 -0
- package/dist/capabilities/context.d.ts +3 -0
- package/dist/capabilities/context.js +125 -1
- package/dist/capabilities/index.d.ts +6 -6
- package/dist/capabilities/index.js +6 -6
- package/dist/capabilities/introductions.js +5 -10
- package/dist/capabilities/links.js +1 -0
- package/dist/capabilities/marketplace.js +15 -6
- package/dist/capabilities/nextCall.d.ts +72 -14
- package/dist/capabilities/nextCall.js +279 -4
- package/dist/capabilities/tasks.d.ts +68 -1
- package/dist/capabilities/tasks.js +112 -3
- package/dist/engagementGuide.d.ts +148 -0
- package/dist/engagementGuide.js +349 -0
- package/dist/http/AgreementClient.d.ts +2 -1
- package/dist/http/AgreementClient.js +4 -0
- package/dist/http/ArtifactsClient.d.ts +47 -0
- package/dist/http/ArtifactsClient.js +33 -0
- package/dist/http/ChatClient.d.ts +28 -2
- package/dist/http/ChatClient.js +7 -0
- package/dist/http/ContextGrantsClient.d.ts +17 -1
- package/dist/http/ContextGrantsClient.js +26 -2
- package/dist/http/ContextOpenClient.d.ts +70 -0
- package/dist/http/ContextOpenClient.js +52 -0
- package/dist/http/InboxClient.d.ts +8 -0
- package/dist/http/InboxClient.js +18 -0
- package/dist/http/TaskClient.d.ts +2 -0
- package/dist/http/TaskClient.js +1 -0
- package/dist/http/index.d.ts +2 -0
- package/dist/http/index.js +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -0
- package/dist/instanceIdentity.d.ts +1 -1
- package/dist/instanceIdentity.js +2 -2
- package/dist/sessionOrientation.d.ts +50 -0
- package/dist/sessionOrientation.js +43 -0
- package/dist/types.d.ts +11 -0
- package/package.json +2 -2
|
@@ -22,3 +22,10 @@ export declare const agreementRequestCapability: CapabilityDefinition;
|
|
|
22
22
|
export declare const agreementOfferCapability: CapabilityDefinition;
|
|
23
23
|
export declare const agreementHandoffCapability: CapabilityDefinition;
|
|
24
24
|
export declare const AGREEMENT_VERB_CAPABILITIES: CapabilityDefinition[];
|
|
25
|
+
/**
|
|
26
|
+
* Subcontract sits next to the propose verbs, not inside them: the parent
|
|
27
|
+
* already exists, and the conversation is optional. The server uses the
|
|
28
|
+
* parent's own chat when none is named. That list of required arguments is
|
|
29
|
+
* this definition — nextCall, MCP, and the SDK tool all read it.
|
|
30
|
+
*/
|
|
31
|
+
export declare const agreementSubcontractCapability: CapabilityDefinition;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getAgreement, proposeBroadcast, proposeDirectTo, } from '../http/AgreementClient.js';
|
|
1
|
+
import { delegateAgreement, getAgreement, proposeBroadcast, proposeDirectTo, } from '../http/AgreementClient.js';
|
|
2
2
|
import { publishOffer } from '../http/MarketplaceClient.js';
|
|
3
3
|
import { fullCreds } from './types.js';
|
|
4
4
|
import { nextCall } from './nextCall.js';
|
|
@@ -382,3 +382,86 @@ export const AGREEMENT_VERB_CAPABILITIES = [
|
|
|
382
382
|
agreementOfferCapability,
|
|
383
383
|
agreementHandoffCapability,
|
|
384
384
|
];
|
|
385
|
+
/**
|
|
386
|
+
* Subcontract sits next to the propose verbs, not inside them: the parent
|
|
387
|
+
* already exists, and the conversation is optional. The server uses the
|
|
388
|
+
* parent's own chat when none is named. That list of required arguments is
|
|
389
|
+
* this definition — nextCall, MCP, and the SDK tool all read it.
|
|
390
|
+
*/
|
|
391
|
+
export const agreementSubcontractCapability = {
|
|
392
|
+
key: 'agreement_subcontract',
|
|
393
|
+
names: { sdk: 'agreement_subcontract', mcp: 'ziggs_agreement_subcontract' },
|
|
394
|
+
title: 'Subcontract part of your work',
|
|
395
|
+
descriptions: {
|
|
396
|
+
sdk: 'Delegate a slice of an active parent agreement to another agent. Name the parent, the worker, and the slice. The worker must approve — never impersonated. A chat is optional: omit it and the slice lands in the parent agreement\'s own room.',
|
|
397
|
+
mcp: 'Delegate a slice of an active parent agreement to another agent. Name the parent, the worker, and the slice. The worker must approve — never impersonated. A chat is optional: omit it and the slice lands in the parent agreement\'s own room. Spawn tasks for the worker under the sub-agreement once it is active.',
|
|
398
|
+
},
|
|
399
|
+
annotation: 'write',
|
|
400
|
+
params: {
|
|
401
|
+
parentAgreementId: {
|
|
402
|
+
type: 'string',
|
|
403
|
+
required: true,
|
|
404
|
+
description: 'The active agreement you are delegating under',
|
|
405
|
+
},
|
|
406
|
+
executorId: {
|
|
407
|
+
type: 'string',
|
|
408
|
+
required: true,
|
|
409
|
+
description: 'Agent doing the delegated work',
|
|
410
|
+
},
|
|
411
|
+
description: {
|
|
412
|
+
type: 'string',
|
|
413
|
+
required: true,
|
|
414
|
+
description: 'What the sub-agreement covers',
|
|
415
|
+
},
|
|
416
|
+
chatId: {
|
|
417
|
+
type: 'string',
|
|
418
|
+
description: "Chat the subcontract is coordinated in. Omit it and the server uses the parent agreement's own chat — which is where a slice with no conversation of its own belongs.",
|
|
419
|
+
},
|
|
420
|
+
price: {
|
|
421
|
+
type: 'number',
|
|
422
|
+
description: 'Price in POINTS, as an integer of hundredths — 500 means ϟ5.00. Recording a price here does not itself move any.',
|
|
423
|
+
},
|
|
424
|
+
lifecycle: {
|
|
425
|
+
type: 'string',
|
|
426
|
+
enum: ['open', 'time-bound', 'count-bound'],
|
|
427
|
+
description: "Usually inferred: expiresAt → 'time-bound', maxExecutions → 'count-bound', neither → 'open' (standing).",
|
|
428
|
+
},
|
|
429
|
+
expiresAt: { type: 'string' },
|
|
430
|
+
maxExecutions: { type: 'number' },
|
|
431
|
+
agreementDescription: { type: 'string' },
|
|
432
|
+
payerId: {
|
|
433
|
+
type: 'string',
|
|
434
|
+
description: 'Who pays for the subcontracted work. Defaults to the delegating side.',
|
|
435
|
+
},
|
|
436
|
+
},
|
|
437
|
+
needsAgentId: true,
|
|
438
|
+
handler: async (args, env) => {
|
|
439
|
+
const chatId = args['chatId'];
|
|
440
|
+
const agreement = await delegateAgreement({
|
|
441
|
+
parentAgreementId: args['parentAgreementId'],
|
|
442
|
+
executorId: args['executorId'],
|
|
443
|
+
description: args['description'],
|
|
444
|
+
...(typeof chatId === 'string' && chatId.trim()
|
|
445
|
+
? { chatId: chatId.trim() }
|
|
446
|
+
: {}),
|
|
447
|
+
...(args['price'] === undefined ? {} : { price: args['price'] }),
|
|
448
|
+
...(args['lifecycle'] === undefined
|
|
449
|
+
? {}
|
|
450
|
+
: { lifecycle: args['lifecycle'] }),
|
|
451
|
+
...(args['expiresAt'] === undefined
|
|
452
|
+
? {}
|
|
453
|
+
: { expiresAt: args['expiresAt'] }),
|
|
454
|
+
...(args['maxExecutions'] === undefined
|
|
455
|
+
? {}
|
|
456
|
+
: { maxExecutions: args['maxExecutions'] }),
|
|
457
|
+
...(args['agreementDescription'] === undefined
|
|
458
|
+
? {}
|
|
459
|
+
: { agreementDescription: args['agreementDescription'] }),
|
|
460
|
+
...(args['payerId'] === undefined
|
|
461
|
+
? {}
|
|
462
|
+
: { payerId: args['payerId'] }),
|
|
463
|
+
}, fullCreds(env));
|
|
464
|
+
return { agreement };
|
|
465
|
+
},
|
|
466
|
+
sdkOptions: { isAgreementCreation: true },
|
|
467
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ClaimedKind } from '../http/AgreementClient.js';
|
|
2
2
|
import type { Agreement } from '../types.js';
|
|
3
|
+
import { type NextCallOutcome, type WorkContext } from './nextCall.js';
|
|
3
4
|
import { type CapabilityDefinition, type CapabilityEnv } from './types.js';
|
|
4
5
|
/**
|
|
5
6
|
* One claim result shape for both surfaces. The SDK protocol runner used to
|
|
@@ -8,9 +9,11 @@ import { type CapabilityDefinition, type CapabilityEnv } from './types.js';
|
|
|
8
9
|
*/
|
|
9
10
|
export declare function presentClaimResult(agreement: Agreement, kind: ClaimedKind, env: CapabilityEnv): {
|
|
10
11
|
status: string;
|
|
12
|
+
outcome: NextCallOutcome;
|
|
11
13
|
kind: ClaimedKind;
|
|
12
14
|
message: string;
|
|
13
15
|
agreement: Agreement;
|
|
16
|
+
workContext?: WorkContext;
|
|
14
17
|
};
|
|
15
18
|
/**
|
|
16
19
|
* the one claim verb. Requests, standing offers, and link invites
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { agreementAppUrl } from '../utils/appUrls.js';
|
|
2
2
|
import { claimOpenAgreement } from '../http/agreementFlows.js';
|
|
3
3
|
import { linkIsReachOnly, webAppOrigin } from './links.js';
|
|
4
|
+
import { workContextFromEnv } from './nextCall.js';
|
|
4
5
|
import { fullCreds } from './types.js';
|
|
5
6
|
/** Which side the claimer just took, for a claim that has not activated yet. */
|
|
6
7
|
function claimedWhat(kind) {
|
|
@@ -16,12 +17,15 @@ function claimedWhat(kind) {
|
|
|
16
17
|
* agreement}` — same HTTP call, two answers.
|
|
17
18
|
*/
|
|
18
19
|
export function presentClaimResult(agreement, kind, env) {
|
|
20
|
+
const workContext = workContextFromEnv(env);
|
|
19
21
|
if (kind === 'link') {
|
|
20
22
|
return {
|
|
21
23
|
status: 'linked',
|
|
24
|
+
outcome: 'ok',
|
|
22
25
|
kind,
|
|
23
26
|
message: `Link invite claimed — you are now linked. ${linkIsReachOnly(env)}`,
|
|
24
27
|
agreement,
|
|
28
|
+
...(workContext ? { workContext } : {}),
|
|
25
29
|
};
|
|
26
30
|
}
|
|
27
31
|
if (agreement?.status !== 'active') {
|
|
@@ -31,16 +35,19 @@ export function presentClaimResult(agreement, kind, env) {
|
|
|
31
35
|
? 'This is a first engagement with that counterparty — your human approves once; a standing link covers it after that.'
|
|
32
36
|
: 'Claiming for a job your human already approved activates at once — name the job with mandateAgreementId.';
|
|
33
37
|
return {
|
|
34
|
-
status: '
|
|
38
|
+
status: 'pending_approval',
|
|
39
|
+
outcome: 'pending',
|
|
35
40
|
kind,
|
|
36
41
|
message: `${claimedWhat(kind)} It is not active yet: the formation is waiting on ` +
|
|
37
42
|
'human approval, so nothing can be created under it until that lands. ' +
|
|
38
43
|
`Your human can approve it here: ${approveUrl} — ${remedy}`,
|
|
39
44
|
agreement,
|
|
45
|
+
...(workContext ? { workContext } : {}),
|
|
40
46
|
};
|
|
41
47
|
}
|
|
42
48
|
return {
|
|
43
49
|
status: 'claimed',
|
|
50
|
+
outcome: 'ok',
|
|
44
51
|
kind,
|
|
45
52
|
message: kind === 'offer'
|
|
46
53
|
? 'Standing offer claimed — the publisher provides, your side pays. Spawn work under it with the task-create tool.'
|
|
@@ -48,6 +55,7 @@ export function presentClaimResult(agreement, kind, env) {
|
|
|
48
55
|
? 'Hand-off claimed — the pinned agent works FOR you: you are the customer (and the payer when priced), never the worker. Spawn work under it with the task-create tool.'
|
|
49
56
|
: 'Request claimed — you provide the work. Read the terms, then post progress and set the task result under this agreement.',
|
|
50
57
|
agreement,
|
|
58
|
+
...(workContext ? { workContext } : {}),
|
|
51
59
|
};
|
|
52
60
|
}
|
|
53
61
|
/**
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { type DiscoverableItem } from '../http/ContextDiscoveryClient.js';
|
|
1
2
|
import { type CapabilityDefinition } from './types.js';
|
|
3
|
+
import { type NextCall } from './nextCall.js';
|
|
2
4
|
export declare const recordArtifactCapability: CapabilityDefinition;
|
|
3
5
|
/**
|
|
4
6
|
* the artifacts you wrote, across every scope and none.
|
|
@@ -8,6 +10,42 @@ export declare const recordArtifactCapability: CapabilityDefinition;
|
|
|
8
10
|
* agent finds what it recorded without naming a container.
|
|
9
11
|
*/
|
|
10
12
|
export declare const listArtifactsCapability: CapabilityDefinition;
|
|
13
|
+
/**
|
|
14
|
+
* What the agent could not read, said as metadata and nothing else.
|
|
15
|
+
*
|
|
16
|
+
* The one thing this block must never become is a second results list. A row
|
|
17
|
+
* here is a LABEL — `context_discover_grantable` re-shapes every row to
|
|
18
|
+
* type/label/scopeRef/orgId on the way through, so no message, no member name
|
|
19
|
+
* and no artifact body can travel in it, and none of these scopes were searched
|
|
20
|
+
* at all. Keeping them in a separate field with its own wording is the whole
|
|
21
|
+
* point: an agent that cannot tell "no match in what I can read" from "there is
|
|
22
|
+
* a locked room over there" will confidently report the first while the answer
|
|
23
|
+
* sits in the second.
|
|
24
|
+
*/
|
|
25
|
+
export interface UnsearchedContext {
|
|
26
|
+
/** Locked scopes, labels only — never content, never counts of content. */
|
|
27
|
+
items: (DiscoverableItem & {
|
|
28
|
+
requestAccess: NextCall;
|
|
29
|
+
})[];
|
|
30
|
+
count: number;
|
|
31
|
+
note: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* search the work you hold, and say what you searched.
|
|
35
|
+
*
|
|
36
|
+
* `artifact_list` answers "what did I write" and the scope reads answer "what
|
|
37
|
+
* is in this container". Neither answers "where is the thing about X", which is
|
|
38
|
+
* the question an agent actually has when it has been handed work that refers
|
|
39
|
+
* to something it did not produce. The server bounds this by the agent's own
|
|
40
|
+
* reach — authorship, readable containers, artifact grants it holds — under the
|
|
41
|
+
* active lane and shared-room containment, so there is no `reach` arm to ask
|
|
42
|
+
* for and the narrowing filters are refused rather than ignored.
|
|
43
|
+
*
|
|
44
|
+
* The response is deliberately two things that never merge: `artifacts`, which
|
|
45
|
+
* is readable content that matched, and `notSearched`, which is labels of
|
|
46
|
+
* scopes that were never opened. `coverage` sits between them naming what ran.
|
|
47
|
+
*/
|
|
48
|
+
export declare const findArtifactsCapability: CapabilityDefinition;
|
|
11
49
|
/**
|
|
12
50
|
* hand one artifact you authored to one other agent.
|
|
13
51
|
*
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { ArtifactsClient } from '../http/ArtifactsClient.js';
|
|
2
2
|
import { ContextGrantsClient } from '../http/ContextGrantsClient.js';
|
|
3
|
+
import { ContextDiscoveryClient, } from '../http/ContextDiscoveryClient.js';
|
|
3
4
|
import { fullCreds } from './types.js';
|
|
4
5
|
import { LIST_FIELDS_PARAM, parseListFields, pickListedRows } from './listedFields.js';
|
|
6
|
+
import { nextCall } from './nextCall.js';
|
|
5
7
|
/**
|
|
6
8
|
* teaching: name the result slot on the success path so an agent finds
|
|
7
9
|
* the right move unaided — worded in each surface's task grammar (SDK
|
|
@@ -16,7 +18,10 @@ function reportingHint(env, contentType, taskId) {
|
|
|
16
18
|
? `Recorded as a task-bound result artifact. Close the task by setting its terminal result with ${close}.`
|
|
17
19
|
: `Recorded as a result artifact, but not bound to a task — pass taskId to bind it, then close the task with ${close}.`;
|
|
18
20
|
}
|
|
19
|
-
|
|
21
|
+
// The record and the delivery are different acts. A chat message
|
|
22
|
+
// is not where the next agent collects the work, and saying that as "chat is
|
|
23
|
+
// conversation only" read as a ban on telling the person who asked.
|
|
24
|
+
return `Reporting finished work? Record it with contentType=result bound to the task (taskId), then close the task with ${close} — that record is what the next agent reads. Telling the person who asked is a separate chat message, and still worth sending.`;
|
|
20
25
|
}
|
|
21
26
|
/** Inline body cap + escape hatch, shared by SDK/MCP descriptions. */
|
|
22
27
|
const ARTIFACT_RECORD_INLINE_CAP = 'Inline text max 50000 characters. Over that: pass the body as a file ' +
|
|
@@ -267,8 +272,8 @@ export const listArtifactsCapability = {
|
|
|
267
272
|
names: { sdk: 'artifact_list', mcp: 'ziggs_artifact_list' },
|
|
268
273
|
title: 'List artifacts you wrote',
|
|
269
274
|
descriptions: {
|
|
270
|
-
sdk: 'List artifacts you authored, in any scope or none — including free-standing ones no scope read can reach. Forward-delta with `after`.',
|
|
271
|
-
mcp: 'List artifacts YOU authored, across every scope and none. Use this to find something you ' +
|
|
275
|
+
sdk: 'List business artifacts you authored, in any scope or none — including free-standing ones no scope read can reach. Tool-operation and thought traces are excluded. Forward-delta with `after`.',
|
|
276
|
+
mcp: 'List business artifacts YOU authored, across every scope and none; tool-operation and thought traces are excluded. Use this to find something you ' +
|
|
272
277
|
'recorded free-standing (no chat/agreement/task), which the scope reads cannot return. ' +
|
|
273
278
|
'To read an artifact someone shared WITH you, use ziggs_context_read with via=artifact:<id>; ' +
|
|
274
279
|
'to see what has been shared with you, use ziggs_grant_list with scopeKind=artifact.',
|
|
@@ -295,6 +300,133 @@ export const listArtifactsCapability = {
|
|
|
295
300
|
: listed;
|
|
296
301
|
},
|
|
297
302
|
};
|
|
303
|
+
const UNSEARCHED_NOTE = 'These were NOT searched. You hold no grant on them, so only their label is ' +
|
|
304
|
+
'visible here — no contents, no message counts, nothing that would leak what ' +
|
|
305
|
+
'is inside. Each row carries requestAccess: an access explanation, not a ' +
|
|
306
|
+
'root grant. A human issues the grant; this never widens the scope to the ' +
|
|
307
|
+
'chat, agreement, or org around one artifact.';
|
|
308
|
+
const OPEN_ID_FIELD = {
|
|
309
|
+
chat: 'chatId',
|
|
310
|
+
agreement: 'agreementId',
|
|
311
|
+
artifact: 'artifactId',
|
|
312
|
+
task: 'taskId',
|
|
313
|
+
};
|
|
314
|
+
function pendingAccessReceipt(result, extra) {
|
|
315
|
+
return {
|
|
316
|
+
...extra,
|
|
317
|
+
ok: false,
|
|
318
|
+
outcome: 'pending',
|
|
319
|
+
status: 'pending_approval',
|
|
320
|
+
state: 'requested',
|
|
321
|
+
recoverable: true,
|
|
322
|
+
agreementId: result.agreementId,
|
|
323
|
+
ownerId: result.ownerId,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* The locked scopes near a search that found nothing.
|
|
328
|
+
*
|
|
329
|
+
* Fetched only when it changes what a caller should conclude, which is why it
|
|
330
|
+
* is not on every response: an agent holding a page of matches does not need
|
|
331
|
+
* the list of doors it has not opened, and an extra round trip on every find
|
|
332
|
+
* would buy nothing. A search that came back empty is exactly where the wrong
|
|
333
|
+
* conclusion gets drawn, so that is where this arrives unasked.
|
|
334
|
+
*
|
|
335
|
+
* Discovery failing is not find failing. It is bounded to orgs the agent holds
|
|
336
|
+
* an active work agreement in and can legitimately return nothing, or refuse;
|
|
337
|
+
* either way the matches already computed are the answer, and the miss is
|
|
338
|
+
* reported as a limitation rather than thrown over the top of them.
|
|
339
|
+
*/
|
|
340
|
+
async function unsearchedContext(env) {
|
|
341
|
+
const creds = fullCreds(env);
|
|
342
|
+
let items;
|
|
343
|
+
try {
|
|
344
|
+
items = await new ContextDiscoveryClient(creds.operatorKey, creds.agentId).discoverGrantable();
|
|
345
|
+
}
|
|
346
|
+
catch (err) {
|
|
347
|
+
return {
|
|
348
|
+
note: 'Could not check for context you cannot read yet: ' +
|
|
349
|
+
`${err instanceof Error ? err.message : String(err)}. ` +
|
|
350
|
+
'Treat this answer as covering only what was searched.',
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
return {
|
|
354
|
+
items: items.map((item) => {
|
|
355
|
+
const idField = OPEN_ID_FIELD[item.scopeRef.kind];
|
|
356
|
+
return {
|
|
357
|
+
...item,
|
|
358
|
+
requestAccess: nextCall(env, 'access_explain', idField ? { [idField]: item.scopeRef.id } : undefined, idField
|
|
359
|
+
? `explain access to locked ${item.type} "${item.label}" — a human issues a root grant; this call does not request or widen`
|
|
360
|
+
: `locked ${item.type} "${item.label}" cannot be requested from this surface — a human issues a root grant; this never widens the scope`, {
|
|
361
|
+
hold: 'human_approval',
|
|
362
|
+
...(idField ? {} : { outcome: 'unavailable' }),
|
|
363
|
+
}),
|
|
364
|
+
};
|
|
365
|
+
}),
|
|
366
|
+
count: items.length,
|
|
367
|
+
note: UNSEARCHED_NOTE,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* search the work you hold, and say what you searched.
|
|
372
|
+
*
|
|
373
|
+
* `artifact_list` answers "what did I write" and the scope reads answer "what
|
|
374
|
+
* is in this container". Neither answers "where is the thing about X", which is
|
|
375
|
+
* the question an agent actually has when it has been handed work that refers
|
|
376
|
+
* to something it did not produce. The server bounds this by the agent's own
|
|
377
|
+
* reach — authorship, readable containers, artifact grants it holds — under the
|
|
378
|
+
* active lane and shared-room containment, so there is no `reach` arm to ask
|
|
379
|
+
* for and the narrowing filters are refused rather than ignored.
|
|
380
|
+
*
|
|
381
|
+
* The response is deliberately two things that never merge: `artifacts`, which
|
|
382
|
+
* is readable content that matched, and `notSearched`, which is labels of
|
|
383
|
+
* scopes that were never opened. `coverage` sits between them naming what ran.
|
|
384
|
+
*/
|
|
385
|
+
export const findArtifactsCapability = {
|
|
386
|
+
key: 'artifact_find',
|
|
387
|
+
names: { sdk: 'artifact_find', mcp: 'ziggs_artifact_find' },
|
|
388
|
+
title: 'Find artifacts you can read',
|
|
389
|
+
descriptions: {
|
|
390
|
+
sdk: 'Search business artifacts you can already reach (excluding tool-operation and thought traces) — what you authored, what your readable chats/agreements/tasks hold, and what artifact grants you hold — by words in the name, filename or body. Bounded by your active lane and the rooms this wake is shared into, the same fences a point read obeys; there is no cross-customer arm and narrowing filters are refused, not ignored. Every answer carries coverage (fields searched, reach arms and their sizes, any that hit a cap), so an empty result means no match in what was searched, never that the thing does not exist. When nothing matched you also get notSearched: labels of context you hold no grant on, each with an access explanation — a human issues a root grant; this never widens. Metadata only, never contents.',
|
|
391
|
+
mcp: 'Search the business artifacts YOU can read for words in their name, filename or body; tool-operation and thought traces are excluded. Covers what you authored, what the chats/agreements/tasks you can read contain, and artifacts shared with you — bounded by your active lane and shared-room containment, exactly like a point read. Open a hit with ziggs_context_read (via=artifact:<id>). ' +
|
|
392
|
+
'Read the coverage block before concluding anything: it names the fields searched, the arms that produced candidates and any that hit a cap. No match means no match in THOSE sources — it is never a statement that the artifact does not exist. ' +
|
|
393
|
+
'If nothing matched, notSearched lists context you cannot read yet (same rows as ziggs_context_discover_grantable): labels only, never contents, each with an access explanation. A human issues a root grant; do not treat that as a share or a wider container grant. Do not report "not found" while notSearched is non-empty — say what you searched and what is still locked.',
|
|
394
|
+
},
|
|
395
|
+
annotation: 'read-only',
|
|
396
|
+
params: {
|
|
397
|
+
q: {
|
|
398
|
+
type: 'string',
|
|
399
|
+
required: true,
|
|
400
|
+
description: 'Words to match; every term must appear. This is a lexical match on name, filename and extracted text — not a semantic search.',
|
|
401
|
+
},
|
|
402
|
+
limit: { type: 'number', description: 'Page size (server default when omitted)' },
|
|
403
|
+
before: {
|
|
404
|
+
type: 'string',
|
|
405
|
+
description: 'Older-rows cursor: pass the previous page’s coverage.nextCursor back verbatim (opaque).',
|
|
406
|
+
},
|
|
407
|
+
includeUnreadable: {
|
|
408
|
+
type: 'boolean',
|
|
409
|
+
description: 'Also list context you hold no grant on (labels only, never contents). Defaults on when nothing matched, since that is when an empty answer is most likely to be misread; pass true to ask for it alongside matches.',
|
|
410
|
+
},
|
|
411
|
+
fields: LIST_FIELDS_PARAM,
|
|
412
|
+
},
|
|
413
|
+
needsAgentId: true,
|
|
414
|
+
handler: async (args, env) => {
|
|
415
|
+
const creds = fullCreds(env);
|
|
416
|
+
const found = await new ArtifactsClient(creds.operatorKey, creds.agentId, creds.laneId).find(args['q'], {
|
|
417
|
+
limit: typeof args['limit'] === 'number' ? args['limit'] : undefined,
|
|
418
|
+
before: args['before'],
|
|
419
|
+
});
|
|
420
|
+
const fields = parseListFields(args['fields']);
|
|
421
|
+
const shaped = fields
|
|
422
|
+
? { ...found, artifacts: pickListedRows(found.artifacts, fields) }
|
|
423
|
+
: found;
|
|
424
|
+
const asked = args['includeUnreadable'] === true;
|
|
425
|
+
if (!asked && found.artifacts.length > 0)
|
|
426
|
+
return shaped;
|
|
427
|
+
return { ...shaped, notSearched: await unsearchedContext(env) };
|
|
428
|
+
},
|
|
429
|
+
};
|
|
298
430
|
/**
|
|
299
431
|
* hand one artifact you authored to one other agent.
|
|
300
432
|
*
|
|
@@ -335,6 +467,10 @@ export const shareArtifactCapability = {
|
|
|
335
467
|
type: 'string',
|
|
336
468
|
description: 'Chat to surface the approval request in, when the receiver’s owner has to approve',
|
|
337
469
|
},
|
|
470
|
+
idempotencyKey: {
|
|
471
|
+
type: 'string',
|
|
472
|
+
description: 'Replay the same share. Same key and payload recover the original request; the same key with a different payload is a conflict, not a second share.',
|
|
473
|
+
},
|
|
338
474
|
},
|
|
339
475
|
needsAgentId: true,
|
|
340
476
|
handler: async (args, env) => {
|
|
@@ -349,16 +485,20 @@ export const shareArtifactCapability = {
|
|
|
349
485
|
holderId,
|
|
350
486
|
expiresAt: args['expiresAt'],
|
|
351
487
|
chatId: args['chatId'],
|
|
488
|
+
}, {
|
|
489
|
+
idempotencyKey: typeof args['idempotencyKey'] === 'string'
|
|
490
|
+
? args['idempotencyKey']
|
|
491
|
+
: undefined,
|
|
352
492
|
});
|
|
353
493
|
if (result.status === 'pending_approval') {
|
|
354
|
-
return {
|
|
355
|
-
ok: true,
|
|
494
|
+
return pendingAccessReceipt(result, {
|
|
356
495
|
...result,
|
|
357
|
-
note: `
|
|
358
|
-
};
|
|
496
|
+
note: `Requested, not granted — ${result.ownerId ?? 'the owner'} must approve agreement ${result.agreementId}. Recover that id; do not open a second card.`,
|
|
497
|
+
});
|
|
359
498
|
}
|
|
360
499
|
return {
|
|
361
500
|
ok: true,
|
|
501
|
+
outcome: 'ok',
|
|
362
502
|
...result,
|
|
363
503
|
note: `${holderId} can now read artifact ${artifactId} with via=artifact:${artifactId}.`,
|
|
364
504
|
};
|
|
@@ -644,6 +784,7 @@ export const reextractArtifactCapability = {
|
|
|
644
784
|
export const ARTIFACT_CAPABILITIES = [
|
|
645
785
|
recordArtifactCapability,
|
|
646
786
|
listArtifactsCapability,
|
|
787
|
+
findArtifactsCapability,
|
|
647
788
|
shareArtifactCapability,
|
|
648
789
|
attachArtifactCapability,
|
|
649
790
|
uploadArtifactUrlCapability,
|
|
@@ -1,4 +1,34 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type SendChatMessageResult } from '../http/ChatClient.js';
|
|
2
|
+
import { type CapabilityDefinition, type CapabilityEnv } from './types.js';
|
|
3
|
+
import { type WorkContext } from './nextCall.js';
|
|
4
|
+
export interface SendPresentation {
|
|
5
|
+
ok: true;
|
|
6
|
+
chatId: string;
|
|
7
|
+
messageId: string;
|
|
8
|
+
/** What the server did with this send, stated so it cannot be over-read. */
|
|
9
|
+
delivered: string;
|
|
10
|
+
/** Who it was addressed to, exactly as the server resolved it. */
|
|
11
|
+
to?: {
|
|
12
|
+
id: string;
|
|
13
|
+
type: string;
|
|
14
|
+
};
|
|
15
|
+
workContext?: WorkContext;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Present a send as what it is: a message that now exists, addressed to
|
|
19
|
+
* somebody, which may or may not wake anyone.
|
|
20
|
+
*
|
|
21
|
+
* An agent that sent an answer and read `success: true` had every reason to
|
|
22
|
+
* report the answer delivered. Three different things hide behind that word.
|
|
23
|
+
* The message exists — that much is certain. Who it reached is the server's
|
|
24
|
+
* decision, not the caller's, because an omitted recipient may be inferred.
|
|
25
|
+
* Mailbox routing is a separate asynchronous decision, including coverage by
|
|
26
|
+
* an assistant when the addressee is a person.
|
|
27
|
+
*
|
|
28
|
+
* A server that does not report the destination gets one honest line about the
|
|
29
|
+
* message existing, and nothing invented about where it went.
|
|
30
|
+
*/
|
|
31
|
+
export declare function presentSendResult(result: SendChatMessageResult, env: CapabilityEnv): SendPresentation;
|
|
2
32
|
/**
|
|
3
33
|
* the decided chat surface for agents: chat_open only.
|
|
4
34
|
* There is deliberately NO chat-listing tool on the SDK: taking part in a room
|
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
import { openConversation } from '../http/ChatClient.js';
|
|
2
2
|
import { fullCreds } from './types.js';
|
|
3
|
+
import { workContextFromEnv } from './nextCall.js';
|
|
4
|
+
/**
|
|
5
|
+
* Present a send as what it is: a message that now exists, addressed to
|
|
6
|
+
* somebody, which may or may not wake anyone.
|
|
7
|
+
*
|
|
8
|
+
* An agent that sent an answer and read `success: true` had every reason to
|
|
9
|
+
* report the answer delivered. Three different things hide behind that word.
|
|
10
|
+
* The message exists — that much is certain. Who it reached is the server's
|
|
11
|
+
* decision, not the caller's, because an omitted recipient may be inferred.
|
|
12
|
+
* Mailbox routing is a separate asynchronous decision, including coverage by
|
|
13
|
+
* an assistant when the addressee is a person.
|
|
14
|
+
*
|
|
15
|
+
* A server that does not report the destination gets one honest line about the
|
|
16
|
+
* message existing, and nothing invented about where it went.
|
|
17
|
+
*/
|
|
18
|
+
export function presentSendResult(result, env) {
|
|
19
|
+
const workContext = workContextFromEnv(env);
|
|
20
|
+
const delivery = result.delivery;
|
|
21
|
+
const base = {
|
|
22
|
+
ok: true,
|
|
23
|
+
chatId: result.chatId,
|
|
24
|
+
messageId: result.messageId,
|
|
25
|
+
...(workContext ? { workContext } : {}),
|
|
26
|
+
};
|
|
27
|
+
if (!delivery) {
|
|
28
|
+
return {
|
|
29
|
+
...base,
|
|
30
|
+
delivered: `Message ${result.messageId} was accepted into chat ${result.chatId}. Accepted is not read: nothing here says anyone has seen it.`,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const destination = delivery.broadcast
|
|
34
|
+
? 'the humans in the room, not one addressee'
|
|
35
|
+
: `${delivery.to.id} (${delivery.to.type})`;
|
|
36
|
+
return {
|
|
37
|
+
...base,
|
|
38
|
+
delivered: `Message ${result.messageId} was accepted into chat ${result.chatId}, addressed to ${destination}. Mailbox routing is unconfirmed; a person may be covered by an assistant. Accepted is not read and does not confirm a wake or reply.`,
|
|
39
|
+
to: delivery.to,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
3
42
|
/**
|
|
4
43
|
* the decided chat surface for agents: chat_open only.
|
|
5
44
|
* There is deliberately NO chat-listing tool on the SDK: taking part in a room
|
|
@@ -20,4 +20,7 @@ export declare const contextReadCapability: CapabilityDefinition;
|
|
|
20
20
|
export declare const contextExpandReachCapability: CapabilityDefinition;
|
|
21
21
|
export declare const contextDiscoverGrantableCapability: CapabilityDefinition;
|
|
22
22
|
export declare const contextDelegateCapability: CapabilityDefinition;
|
|
23
|
+
export declare const openCapability: CapabilityDefinition;
|
|
24
|
+
export declare const accessExplainCapability: CapabilityDefinition;
|
|
23
25
|
export declare const CONTEXT_CAPABILITIES: CapabilityDefinition[];
|
|
26
|
+
export declare const contextRequestCapability: CapabilityDefinition;
|