@ziggs-ai/api-client 0.4.0 → 0.6.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/ConnectionManager.d.ts +13 -1
- package/dist/ConnectionManager.js +63 -9
- package/dist/capabilities/agreements.d.ts +8 -0
- package/dist/capabilities/agreements.js +45 -0
- package/dist/capabilities/artifacts.js +19 -11
- package/dist/capabilities/chat.d.ts +3 -3
- package/dist/capabilities/chat.js +8 -8
- package/dist/capabilities/connections.js +8 -8
- package/dist/capabilities/context.js +11 -11
- package/dist/capabilities/discovery.js +4 -4
- package/dist/capabilities/grants.js +3 -3
- package/dist/capabilities/index.d.ts +3 -1
- package/dist/capabilities/index.js +3 -1
- package/dist/capabilities/links.d.ts +0 -3
- package/dist/capabilities/links.js +64 -102
- package/dist/capabilities/marketplace.d.ts +8 -0
- package/dist/capabilities/marketplace.js +55 -0
- package/dist/capabilities/payments.js +2 -2
- package/dist/http/AgreementClient.d.ts +8 -0
- package/dist/http/AgreementClient.js +6 -12
- package/dist/http/ArtifactsClient.d.ts +2 -2
- package/dist/http/ArtifactsClient.js +2 -2
- package/dist/http/ChatClient.js +1 -1
- package/dist/http/ContextReadClient.js +16 -1
- package/dist/http/InboxClient.d.ts +95 -65
- package/dist/http/InboxClient.js +46 -17
- package/dist/http/MarketplaceClient.d.ts +8 -0
- package/dist/http/agreementFlows.d.ts +34 -0
- package/dist/http/agreementFlows.js +80 -0
- package/dist/http/index.d.ts +2 -1
- package/dist/http/index.js +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/shared/rateLimit.d.ts +46 -0
- package/dist/shared/rateLimit.js +73 -0
- package/dist/types.d.ts +12 -1
- package/package.json +1 -1
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { type InboxOperatorAgentEntry } from './http/InboxClient.js';
|
|
2
2
|
type OpenFn = () => Promise<unknown>;
|
|
3
3
|
type CloseFn = (handle: unknown) => Promise<void>;
|
|
4
|
+
export interface StartAgentOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Keep this host running until it is stopped explicitly — exempt from the
|
|
7
|
+
* idle sweep, last choice for LRU eviction. For agents the operator named
|
|
8
|
+
* (launcher `preStart` / `WAKE_AGENTS`): they were started because someone
|
|
9
|
+
* decided they must be up, not because work arrived, so "no work for a
|
|
10
|
+
* while" is not a reason to retire them.
|
|
11
|
+
*/
|
|
12
|
+
pinned?: boolean;
|
|
13
|
+
}
|
|
4
14
|
export interface ConnectionManagerMeta {
|
|
5
15
|
domain?: string;
|
|
6
16
|
expertise?: string[];
|
|
@@ -46,6 +56,7 @@ export declare class ConnectionManager {
|
|
|
46
56
|
private _active;
|
|
47
57
|
private _meta;
|
|
48
58
|
private _starting;
|
|
59
|
+
private _pinned;
|
|
49
60
|
private _shouldLazyStart;
|
|
50
61
|
constructor({ maxActive, idleTimeoutMs, control, shouldLazyStart }?: ConnectionManagerOptions);
|
|
51
62
|
register(id: string, openFn: OpenFn, closeFn: CloseFn, meta?: ConnectionManagerMeta): void;
|
|
@@ -59,12 +70,13 @@ export declare class ConnectionManager {
|
|
|
59
70
|
* the loop never exits until `stop()` (nothing to resynchronize).
|
|
60
71
|
*/
|
|
61
72
|
private _runOperatorPoll;
|
|
62
|
-
startAgent(id: string): Promise<unknown>;
|
|
73
|
+
startAgent(id: string, { pinned }?: StartAgentOptions): Promise<unknown>;
|
|
63
74
|
sleep(id: string): Promise<void>;
|
|
64
75
|
sleepAll(): Promise<void>;
|
|
65
76
|
touch(id: string): void;
|
|
66
77
|
list(): string[];
|
|
67
78
|
listActive(): string[];
|
|
79
|
+
listPinned(): string[];
|
|
68
80
|
get size(): number;
|
|
69
81
|
query({ domain, expertise, tags }?: QueryFilter): string[];
|
|
70
82
|
getMeta(id: string): ConnectionManagerMeta | undefined;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { InboxClient } from './http/InboxClient.js';
|
|
2
2
|
import { runtimeLog } from './shared/runtimeLog.js';
|
|
3
|
+
import { isRateLimited } from './shared/rateLimit.js';
|
|
3
4
|
const OPERATOR_POLL_WAIT_SECONDS = 25;
|
|
4
5
|
const POLL_BACKOFF_BASE_MS = 1_000;
|
|
5
6
|
const POLL_BACKOFF_MAX_MS = 30_000;
|
|
@@ -16,6 +17,7 @@ export class ConnectionManager {
|
|
|
16
17
|
_active;
|
|
17
18
|
_meta;
|
|
18
19
|
_starting;
|
|
20
|
+
_pinned;
|
|
19
21
|
_shouldLazyStart;
|
|
20
22
|
constructor({ maxActive = 50, idleTimeoutMs = 60_000, control, shouldLazyStart } = {}) {
|
|
21
23
|
this.maxActive = maxActive;
|
|
@@ -28,6 +30,7 @@ export class ConnectionManager {
|
|
|
28
30
|
this._active = new Map();
|
|
29
31
|
this._meta = new Map();
|
|
30
32
|
this._starting = new Map();
|
|
33
|
+
this._pinned = new Set();
|
|
31
34
|
}
|
|
32
35
|
register(id, openFn, closeFn, meta) {
|
|
33
36
|
if (!id)
|
|
@@ -68,7 +71,19 @@ export class ConnectionManager {
|
|
|
68
71
|
let consecutiveErrors = 0;
|
|
69
72
|
while (this._polling) {
|
|
70
73
|
try {
|
|
71
|
-
|
|
74
|
+
// Roster scoping (ZIG-965): only agents this manager can actually
|
|
75
|
+
// start are worth sweeping server-side. Without it a partial launcher
|
|
76
|
+
// (ONLY_AGENTS) paid for a sweep of the owner's whole seeded fleet.
|
|
77
|
+
//
|
|
78
|
+
// Already-running agents are excluded: this loop's only job is
|
|
79
|
+
// lazy-start, and a running host polls its own inbox. Sending them
|
|
80
|
+
// made the server answer for agents whose entry we then discarded
|
|
81
|
+
// below — pure duplicate work, growing with how healthy the fleet is.
|
|
82
|
+
const startable = [...this._entries.keys()].filter((id) => !this._active.has(id));
|
|
83
|
+
const env = await inbox.getOperatorInbox({
|
|
84
|
+
waitSeconds: OPERATOR_POLL_WAIT_SECONDS,
|
|
85
|
+
agents: startable,
|
|
86
|
+
});
|
|
72
87
|
consecutiveErrors = 0;
|
|
73
88
|
for (const entry of env.agents) {
|
|
74
89
|
const id = entry.agentId;
|
|
@@ -85,6 +100,16 @@ export class ConnectionManager {
|
|
|
85
100
|
if (!this._polling)
|
|
86
101
|
break;
|
|
87
102
|
consecutiveErrors++;
|
|
103
|
+
// ZIG-1019: same rule as the per-agent loop — when the server throttles
|
|
104
|
+
// us it also says for how long, and retrying sooner just spends more of
|
|
105
|
+
// the budget we were told we are out of.
|
|
106
|
+
const throttleMs = isRateLimited(err) ? (err.retryAfterMs ?? 30_000) : null;
|
|
107
|
+
if (throttleMs !== null) {
|
|
108
|
+
consecutiveErrors = 0;
|
|
109
|
+
runtimeLog.warn('ConnectionManager', `operator poll throttled: ${err.message} — waiting ${Math.round(throttleMs)}ms as instructed`);
|
|
110
|
+
await sleep(throttleMs);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
88
113
|
const backoff = Math.min(POLL_BACKOFF_MAX_MS, POLL_BACKOFF_BASE_MS * 2 ** (consecutiveErrors - 1));
|
|
89
114
|
const jitter = backoff * (0.5 + (consecutiveErrors % 7) / 14);
|
|
90
115
|
runtimeLog.warn('ConnectionManager', `operator poll failed (#${consecutiveErrors}): ${err.message} — retrying in ~${Math.round(jitter)}ms`);
|
|
@@ -92,7 +117,11 @@ export class ConnectionManager {
|
|
|
92
117
|
}
|
|
93
118
|
}
|
|
94
119
|
}
|
|
95
|
-
async startAgent(id) {
|
|
120
|
+
async startAgent(id, { pinned = false } = {}) {
|
|
121
|
+
// Recorded before any await so a pin is never lost to a concurrent
|
|
122
|
+
// unpinned start of the same id.
|
|
123
|
+
if (pinned)
|
|
124
|
+
this._pinned.add(id);
|
|
96
125
|
const existing = this._active.get(id);
|
|
97
126
|
if (existing) {
|
|
98
127
|
this._resetTimer(id);
|
|
@@ -136,6 +165,7 @@ export class ConnectionManager {
|
|
|
136
165
|
touch(id) { this._resetTimer(id); }
|
|
137
166
|
list() { return [...this._entries.keys()]; }
|
|
138
167
|
listActive() { return [...this._active.keys()]; }
|
|
168
|
+
listPinned() { return [...this._pinned]; }
|
|
139
169
|
get size() { return this._entries.size; }
|
|
140
170
|
query({ domain, expertise, tags } = {}) {
|
|
141
171
|
const results = [];
|
|
@@ -156,6 +186,14 @@ export class ConnectionManager {
|
|
|
156
186
|
if (!entry)
|
|
157
187
|
return;
|
|
158
188
|
clearTimeout(entry.timer);
|
|
189
|
+
// A pinned host has no idle timer at all. Empty polls don't count as
|
|
190
|
+
// activity (only delivered items call touch()), so a pinned agent nobody
|
|
191
|
+
// talks to would otherwise be swept — and with lazy start unable to keep
|
|
192
|
+
// up at fleet size, nothing brings it back until the process restarts.
|
|
193
|
+
if (this._pinned.has(id)) {
|
|
194
|
+
entry.timer = null;
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
159
197
|
entry.timer = setTimeout(() => {
|
|
160
198
|
this.sleep(id).catch(err => runtimeLog.warn('ConnectionManager', `idle sleep("${id}") failed: ${err.message}`));
|
|
161
199
|
}, this.idleTimeoutMs);
|
|
@@ -168,13 +206,29 @@ export class ConnectionManager {
|
|
|
168
206
|
this._scheduleIdle(id);
|
|
169
207
|
}
|
|
170
208
|
async _evictLRU() {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
209
|
+
// Pinned hosts are the last to go: pick the LRU unpinned one, and only
|
|
210
|
+
// fall back to a pinned one when every active host is pinned. maxActive
|
|
211
|
+
// stays a hard cap — a pin outranks other agents, not the capacity limit —
|
|
212
|
+
// but say so, because it means more agents were pinned than can run.
|
|
213
|
+
const oldestOf = (ids) => {
|
|
214
|
+
let oldest = null;
|
|
215
|
+
for (const id of ids) {
|
|
216
|
+
const entry = this._active.get(id);
|
|
217
|
+
const oldestEntry = oldest ? this._active.get(oldest) : null;
|
|
218
|
+
if (!entry)
|
|
219
|
+
continue;
|
|
220
|
+
if (!oldest || !oldestEntry || entry.lastActive < oldestEntry.lastActive)
|
|
221
|
+
oldest = id;
|
|
222
|
+
}
|
|
223
|
+
return oldest;
|
|
224
|
+
};
|
|
225
|
+
const active = [...this._active.keys()];
|
|
226
|
+
const victim = oldestOf(active.filter(id => !this._pinned.has(id))) ?? oldestOf(active);
|
|
227
|
+
if (!victim)
|
|
228
|
+
return;
|
|
229
|
+
if (this._pinned.has(victim)) {
|
|
230
|
+
runtimeLog.warn('ConnectionManager', `evicting pinned "${victim}": all ${active.length} active hosts are pinned and maxActive=${this.maxActive} is reached — pin fewer agents or raise maxActive`);
|
|
176
231
|
}
|
|
177
|
-
|
|
178
|
-
await this.sleep(oldest);
|
|
232
|
+
await this.sleep(victim);
|
|
179
233
|
}
|
|
180
234
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type CapabilityDefinition } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* ZIG-1021 — the one claim verb. Quests, standing offers, and link invites
|
|
4
|
+
* are all open broadcasts; claiming any of them is this call. The respond
|
|
5
|
+
* tool no longer claims — it approves/rejects direct proposals only.
|
|
6
|
+
*/
|
|
7
|
+
export declare const agreementClaimCapability: CapabilityDefinition;
|
|
8
|
+
export declare const AGREEMENT_CAPABILITIES: CapabilityDefinition[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { claimOpenAgreement } from '../http/agreementFlows.js';
|
|
2
|
+
import { linkIsReachOnly, linkSummary } from './links.js';
|
|
3
|
+
import { fullCreds } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* ZIG-1021 — the one claim verb. Quests, standing offers, and link invites
|
|
6
|
+
* are all open broadcasts; claiming any of them is this call. The respond
|
|
7
|
+
* tool no longer claims — it approves/rejects direct proposals only.
|
|
8
|
+
*/
|
|
9
|
+
export const agreementClaimCapability = {
|
|
10
|
+
key: 'agreement_claim',
|
|
11
|
+
names: { sdk: 'agreement_claim', mcp: 'ziggs_agreement_claim' },
|
|
12
|
+
descriptions: {
|
|
13
|
+
sdk: 'Claim an open broadcast agreement by id — a quest (you do the work, the publisher pays), a standing offer (you buy, the publisher works), or a link invite (bilateral trust forms immediately). You become the open party and the agreement activates. Find quests/offers with marketplace_view; direct proposals are approved with agreement_respond instead, not claimed.',
|
|
14
|
+
mcp: 'Claim an open broadcast agreement by id — a quest (you do the work, the publisher pays), a standing offer (you buy, the publisher works), or a link invite (bilateral trust forms immediately). You become the open party and the agreement activates. Find quests/offers with ziggs_marketplace_view; direct proposals are approved with ziggs_agreement_respond instead, not claimed. You cannot claim your own broadcast.',
|
|
15
|
+
},
|
|
16
|
+
annotation: 'write',
|
|
17
|
+
params: {
|
|
18
|
+
agreementId: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
required: true,
|
|
21
|
+
description: 'The open agreement to claim (quest / offer / link invite id)',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
needsAgentId: true,
|
|
25
|
+
handler: async (args, env) => {
|
|
26
|
+
const { agreement, kind } = await claimOpenAgreement(args['agreementId'], fullCreds(env));
|
|
27
|
+
if (kind === 'link') {
|
|
28
|
+
return {
|
|
29
|
+
status: 'linked',
|
|
30
|
+
kind,
|
|
31
|
+
message: `Link invite claimed — you are now linked. ${linkIsReachOnly(env)}`,
|
|
32
|
+
agreement: linkSummary(agreement),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
status: 'claimed',
|
|
37
|
+
kind,
|
|
38
|
+
message: kind === 'offer'
|
|
39
|
+
? 'Standing offer claimed — the publisher provides, your side pays. Spawn work under it with the task-create tool.'
|
|
40
|
+
: 'Quest claimed — you provide the work. Read the terms, then post progress and set the task result under this agreement.',
|
|
41
|
+
agreement,
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
export const AGREEMENT_CAPABILITIES = [agreementClaimCapability];
|
|
@@ -3,12 +3,12 @@ import { fullCreds } from './types.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* ZIG-560 teaching: name the result slot on the success path so an agent finds
|
|
5
5
|
* the right move unaided — worded in each surface's task grammar (SDK
|
|
6
|
-
*
|
|
6
|
+
* task_set_result carries the terminal result; MCP has ziggs_task_set_result).
|
|
7
7
|
*/
|
|
8
8
|
function reportingHint(env, contentType, taskId) {
|
|
9
9
|
const close = env.surface === 'mcp'
|
|
10
|
-
? '
|
|
11
|
-
: '
|
|
10
|
+
? 'ziggs_task_set_result ({ summary, status, links })'
|
|
11
|
+
: 'task_set_result';
|
|
12
12
|
if (contentType === 'result') {
|
|
13
13
|
return taskId
|
|
14
14
|
? `Recorded as a task-bound result artifact. Close the task by setting its terminal result with ${close}.`
|
|
@@ -17,13 +17,13 @@ function reportingHint(env, contentType, taskId) {
|
|
|
17
17
|
return `Reporting finished work? Record it with content_type=result bound to the task (taskId), then close the task with ${close} — chat messages are conversation only.`;
|
|
18
18
|
}
|
|
19
19
|
export const recordArtifactCapability = {
|
|
20
|
-
key: '
|
|
21
|
-
names: { sdk: '
|
|
20
|
+
key: 'artifact_record',
|
|
21
|
+
names: { sdk: 'artifact_record', mcp: 'ziggs_artifact_record' },
|
|
22
22
|
descriptions: {
|
|
23
23
|
sdk: 'Write an artifact to a chat or agreement scope. Set visibility explicitly. For a finished deliverable, set content_type=result and pass taskId to bind it to the task — heavy results belong in artifacts, not chat messages.',
|
|
24
24
|
mcp: 'Write an artifact to a chat or agreement scope. Set visibility explicitly. ' +
|
|
25
25
|
'For a finished deliverable, set content_type=result and pass taskId to bind it to the task. ' +
|
|
26
|
-
'Finished work is the task result — set it with
|
|
26
|
+
'Finished work is the task result — set it with ziggs_task_set_result; never report finished work as a chat message (chat is conversation only).',
|
|
27
27
|
},
|
|
28
28
|
annotation: 'write',
|
|
29
29
|
params: {
|
|
@@ -34,8 +34,11 @@ export const recordArtifactCapability = {
|
|
|
34
34
|
enum: ['chat', 'agent-private'],
|
|
35
35
|
description: 'chat = visible to scope parties; agent-private = your eyes only',
|
|
36
36
|
},
|
|
37
|
-
chatId: { type: 'string', description: 'Target chat
|
|
38
|
-
agreementId: {
|
|
37
|
+
chatId: { type: 'string', description: 'Target chat scope' },
|
|
38
|
+
agreementId: {
|
|
39
|
+
type: 'string',
|
|
40
|
+
description: 'Target agreement scope — for a hire deliverable, prefer this. When both chatId and agreementId are passed, agreementId wins.',
|
|
41
|
+
},
|
|
39
42
|
taskId: {
|
|
40
43
|
type: 'string',
|
|
41
44
|
description: 'Optional task — creates a TaskArtifactLink alongside the primary scope link',
|
|
@@ -51,10 +54,15 @@ export const recordArtifactCapability = {
|
|
|
51
54
|
},
|
|
52
55
|
needsAgentId: true,
|
|
53
56
|
handler: async (args, env) => {
|
|
54
|
-
const chatId = args['chatId'];
|
|
55
57
|
const agreementId = args['agreementId'];
|
|
56
|
-
|
|
57
|
-
|
|
58
|
+
// Agreement wins when both scopes arrive. Models naturally pass the chat
|
|
59
|
+
// they are standing in alongside the agreement they deliver under; a hard
|
|
60
|
+
// xor here failed the deliverable at the last step of a finished task
|
|
61
|
+
// (ZIG-924 dogfood). The backend still enforces exactly-one — we resolve
|
|
62
|
+
// the ambiguity at the exposed surface instead of erroring.
|
|
63
|
+
const chatId = agreementId ? undefined : args['chatId'];
|
|
64
|
+
if (!chatId && !agreementId) {
|
|
65
|
+
throw new Error('Pass chatId or agreementId');
|
|
58
66
|
}
|
|
59
67
|
const visibility = args['visibility'];
|
|
60
68
|
if (visibility !== 'chat' && visibility !== 'agent-private') {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { type CapabilityDefinition } from './types.js';
|
|
2
2
|
/**
|
|
3
|
-
* ZIG-900 — the decided chat surface for agents:
|
|
3
|
+
* ZIG-900 — the decided chat surface for agents: chat_open only.
|
|
4
4
|
* There is deliberately NO chat-listing tool on the SDK: agents read context
|
|
5
5
|
* only via held grants (ZIG-425) and chat membership auto-mints a chat grant
|
|
6
|
-
* (ZIG-428/426), so "what chats can I see" is already answered by
|
|
6
|
+
* (ZIG-428/426), so "what chats can I see" is already answered by grant_list
|
|
7
7
|
* scopeKind=chat (ZIG-626 rule). MCP keeps its rich session-UX lister
|
|
8
|
-
* (
|
|
8
|
+
* (ziggs_chat_list) for humans in Cursor/Claude.
|
|
9
9
|
*/
|
|
10
10
|
export declare const openConversationCapability: CapabilityDefinition;
|
|
11
11
|
export declare const CHAT_CAPABILITIES: CapabilityDefinition[];
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { openConversation } from '../http/ChatClient.js';
|
|
2
2
|
import { fullCreds } from './types.js';
|
|
3
3
|
/**
|
|
4
|
-
* ZIG-900 — the decided chat surface for agents:
|
|
4
|
+
* ZIG-900 — the decided chat surface for agents: chat_open only.
|
|
5
5
|
* There is deliberately NO chat-listing tool on the SDK: agents read context
|
|
6
6
|
* only via held grants (ZIG-425) and chat membership auto-mints a chat grant
|
|
7
|
-
* (ZIG-428/426), so "what chats can I see" is already answered by
|
|
7
|
+
* (ZIG-428/426), so "what chats can I see" is already answered by grant_list
|
|
8
8
|
* scopeKind=chat (ZIG-626 rule). MCP keeps its rich session-UX lister
|
|
9
|
-
* (
|
|
9
|
+
* (ziggs_chat_list) for humans in Cursor/Claude.
|
|
10
10
|
*/
|
|
11
11
|
export const openConversationCapability = {
|
|
12
|
-
key: '
|
|
13
|
-
names: { sdk: '
|
|
12
|
+
key: 'chat_open',
|
|
13
|
+
names: { sdk: 'chat_open', mcp: 'ziggs_chat_open' },
|
|
14
14
|
descriptions: {
|
|
15
|
-
sdk: 'Open or reuse a chat with a user or agent participant. To reach an agent in ANOTHER org, establish a link first —
|
|
16
|
-
mcp: 'Open or reuse a chat with a user or agent participant. To reach an agent in ANOTHER org, an unpublished delegate must establish a link first —
|
|
15
|
+
sdk: 'Open or reuse a chat with a user or agent participant. To reach an agent in ANOTHER org, establish a link first — agreement_propose with engagementKind "link" (if you have its agent id) or link_create_invite (if you do not), approved/claimed — otherwise this fails with AGENT_NOT_PUBLISHED. To list chats you can already read, use grant_list scopeKind=chat.',
|
|
16
|
+
mcp: 'Open or reuse a chat with a user or agent participant. To reach an agent in ANOTHER org, an unpublished delegate must establish a link first — ziggs_agreement_propose with engagementKind "link" (if you have its agent id) or ziggs_link_create_invite (if you do not), approved/claimed — otherwise this fails with AGENT_NOT_PUBLISHED.',
|
|
17
17
|
},
|
|
18
18
|
annotation: 'write',
|
|
19
19
|
params: {
|
|
@@ -28,7 +28,7 @@ export const openConversationCapability = {
|
|
|
28
28
|
if (!args['participantId'])
|
|
29
29
|
throw new Error('participantId is required');
|
|
30
30
|
const { chatId } = await openConversation(args['participantId'], fullCreds(env));
|
|
31
|
-
const lister = env.surface === 'mcp' ? '
|
|
31
|
+
const lister = env.surface === 'mcp' ? 'ziggs_grant_list' : 'grant_list';
|
|
32
32
|
return {
|
|
33
33
|
chatId,
|
|
34
34
|
note: `Conversation is open (or reused). Your membership auto-mints a chat grant, so the chat also appears in ${lister} scopeKind=chat for context reads.`,
|
|
@@ -10,11 +10,11 @@ export const connectionProxyCapability = {
|
|
|
10
10
|
key: 'connection_proxy',
|
|
11
11
|
names: { sdk: 'connection_proxy', mcp: 'ziggs_connection_proxy' },
|
|
12
12
|
descriptions: {
|
|
13
|
-
sdk: "Use a stored connection (a third-party credential, e.g. the owner's GitHub/Jira) without ever seeing the credential. Calls the backend connections proxy with a grant the owner issued to this agent: the proxy enforces the grant, decrypts the token server-side, makes the upstream provider call, and returns the result (token-leak guarded on both sides). Provide connectionId, grantId, the provider action (e.g. repo:read), and an optional action-specific payload. Don't know connectionId/grantId yet? Use
|
|
14
|
-
mcp: "Use a stored connection (a third-party credential, e.g. the owner's GitHub/Jira — NOT an agent-to-agent Link, see
|
|
13
|
+
sdk: "Use a stored connection (a third-party credential, e.g. the owner's GitHub/Jira) without ever seeing the credential. Calls the backend connections proxy with a grant the owner issued to this agent: the proxy enforces the grant, decrypts the token server-side, makes the upstream provider call, and returns the result (token-leak guarded on both sides). Provide connectionId, grantId, the provider action (e.g. repo:read), and an optional action-specific payload. Don't know connectionId/grantId yet? Use grant_list (scopeKind=connection) or connection_list_grants first.",
|
|
14
|
+
mcp: "Use a stored connection (a third-party credential, e.g. the owner's GitHub/Jira — NOT an agent-to-agent Link, see ziggs_link_list for that) without ever seeing the credential. " +
|
|
15
15
|
'Calls the backend connections proxy with a grant the owner issued to this agent: the proxy enforces the grant, decrypts the token server-side, makes the upstream provider call, and returns the result (token-leak guarded on both sides). ' +
|
|
16
16
|
'Provide connectionId, grantId, the provider action (e.g. repo:read), and an optional action-specific payload. ' +
|
|
17
|
-
"Don't know connectionId/grantId yet? Call
|
|
17
|
+
"Don't know connectionId/grantId yet? Call ziggs_connection_list first.",
|
|
18
18
|
},
|
|
19
19
|
annotation: 'write',
|
|
20
20
|
params: {
|
|
@@ -50,13 +50,13 @@ export const connectionProxyCapability = {
|
|
|
50
50
|
},
|
|
51
51
|
};
|
|
52
52
|
export const requestConnectionCapability = {
|
|
53
|
-
key: '
|
|
54
|
-
names: { sdk: '
|
|
53
|
+
key: 'connection_request',
|
|
54
|
+
names: { sdk: 'connection_request', mcp: 'ziggs_connection_request' },
|
|
55
55
|
descriptions: {
|
|
56
56
|
sdk: 'Ask your principal (the human) to connect a remote MCP server and grant you the listed tools. Opens a connection-consent agreement as an approvable card in the chat you pass — the human approves it there like any other agreement. On approval the server is connected (browser OAuth if needed) and you are granted the tools; use them via connection_proxy.',
|
|
57
57
|
mcp: 'Ask your principal (the human) to connect a remote MCP server and grant you the listed tools. ' +
|
|
58
58
|
'Opens a connection-consent agreement as an approvable card in the chat you pass — the human approves it there like any other agreement (there is no MCP tool to approve it, so tell them to approve it in the chat). ' +
|
|
59
|
-
'On approval the server is connected (browser OAuth if needed) and you are granted the tools; the result shows up in
|
|
59
|
+
'On approval the server is connected (browser OAuth if needed) and you are granted the tools; the result shows up in ziggs_connection_list for use with ziggs_connection_proxy.',
|
|
60
60
|
},
|
|
61
61
|
annotation: 'write',
|
|
62
62
|
params: {
|
|
@@ -96,9 +96,9 @@ export const requestConnectionCapability = {
|
|
|
96
96
|
...result,
|
|
97
97
|
note: env.surface === 'mcp'
|
|
98
98
|
? 'A connection-consent card is now in the chat awaiting your principal. Tell the human now (pull-only MCP has no push) — they approve it right in the chat. ' +
|
|
99
|
-
'Once approved, the connection + grant appear in
|
|
99
|
+
'Once approved, the connection + grant appear in ziggs_connection_list for ziggs_connection_proxy.'
|
|
100
100
|
: 'A connection-consent card is now in the chat awaiting your principal — they approve it right there (ZIG-798 flow). ' +
|
|
101
|
-
'Once approved, the connection + grant appear in
|
|
101
|
+
'Once approved, the connection + grant appear in grant_list (scopeKind=connection) / connection_list_grants for connection_proxy.',
|
|
102
102
|
};
|
|
103
103
|
}
|
|
104
104
|
catch (e) {
|
|
@@ -43,15 +43,15 @@ export async function resolveOrgScopeId(env, scopeId) {
|
|
|
43
43
|
}
|
|
44
44
|
if (scopeId.startsWith('org_'))
|
|
45
45
|
return scopeId;
|
|
46
|
-
const lister = env.surface === 'mcp' ? '
|
|
46
|
+
const lister = env.surface === 'mcp' ? 'ziggs_org_list' : 'your org list';
|
|
47
47
|
throw new Error(`No org named "${scopeId}" in your memberships — use ${lister} to see them, or pass the org id.`);
|
|
48
48
|
}
|
|
49
49
|
export const contextReadCapability = {
|
|
50
50
|
key: 'context_read',
|
|
51
|
-
names: { sdk: 'context_read', mcp: '
|
|
51
|
+
names: { sdk: 'context_read', mcp: 'ziggs_context_read' },
|
|
52
52
|
descriptions: {
|
|
53
|
-
sdk: 'Read the contents of a scope you already hold a grant for: messages | artifacts | agreements | tasks. Use
|
|
54
|
-
mcp: 'Read the contents of a scope you already hold: messages | artifacts | agreements | tasks (the type param), under via=chat:<id>, agreement:<id>, or task:<id>. 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 four types — to discover which scopes exist
|
|
53
|
+
sdk: 'Read the contents of a scope you already hold a grant for: messages | artifacts | agreements | tasks. Use grant_list first to see which scopes your grants cover, then read through any of them. Cursored; all access is grant-fenced server-side.',
|
|
54
|
+
mcp: 'Read the contents of a scope you already hold: messages | artifacts | agreements | tasks (the type param), under via=chat:<id>, agreement:<id>, or task:<id>. 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 four types — to discover which scopes exist, use the listers: ziggs_chat_list, ziggs_task_list, ziggs_agreement_list, ziggs_grant_list, ziggs_link_list.',
|
|
55
55
|
},
|
|
56
56
|
annotation: 'read-only',
|
|
57
57
|
params: {
|
|
@@ -107,10 +107,10 @@ export const contextReadCapability = {
|
|
|
107
107
|
};
|
|
108
108
|
export const contextExpandReachCapability = {
|
|
109
109
|
key: 'context_expand_reach',
|
|
110
|
-
names: { sdk: 'context_expand_reach', mcp: '
|
|
110
|
+
names: { sdk: 'context_expand_reach', mcp: 'ziggs_context_expand_reach' },
|
|
111
111
|
descriptions: {
|
|
112
|
-
sdk: 'Expand a grant you hold into the chat/agreement ids inside its scope, so you can read through it.
|
|
113
|
-
mcp: 'Expand a grant you hold into the chat/agreement ids inside its scope, so you can actually read through it.
|
|
112
|
+
sdk: 'Expand a grant you hold into the chat/agreement ids inside its scope, so you can read through it. grant_list tells you that you hold e.g. org:acme or agreement:x; this returns the actual { chats, agreements } (ids + labels only, no content) that scope covers — feed an id to context_read (via=chat:<id> / agreement:<id>). Org scope is capped: truncatedChats/truncatedAgreements say how many were left off. Holder-only, grant-fenced.',
|
|
113
|
+
mcp: 'Expand a grant you hold into the chat/agreement ids inside its scope, so you can actually read through it. ziggs_grant_list tells you that you hold e.g. org:acme or agreement:x; this returns the { chats, agreements } (ids + labels only, never content) that scope covers — feed an id to ziggs_context_read (via=chat:<id> / agreement:<id>). Org scope is capped: truncatedChats/truncatedAgreements say how many were left off. Holder-only, grant-fenced.',
|
|
114
114
|
},
|
|
115
115
|
annotation: 'read-only',
|
|
116
116
|
params: {
|
|
@@ -131,10 +131,10 @@ export const contextExpandReachCapability = {
|
|
|
131
131
|
};
|
|
132
132
|
export const contextDiscoverGrantableCapability = {
|
|
133
133
|
key: 'context_discover_grantable',
|
|
134
|
-
names: { sdk: 'context_discover_grantable', mcp: '
|
|
134
|
+
names: { sdk: 'context_discover_grantable', mcp: 'ziggs_context_discover_grantable' },
|
|
135
135
|
descriptions: {
|
|
136
|
-
sdk: 'See what context EXISTS in your orgs that you CANNOT read yet — the inverse of
|
|
137
|
-
mcp: 'See what context EXISTS in your orgs that you CANNOT read yet — so you can ask for it instead of failing blind. Covers chats, agreements, and connections (type is "chat" | "agreement" | "connection"; connection labels are the provider name only). Returns labels only: { type, label, scopeRef, orgId } per item, never content, member names, tokens, or money. Bounded to orgs you have an active agreement in. To act on one, ask your human to grant it, or (if you hold a broader grant of your own) delegate via
|
|
136
|
+
sdk: 'See what context EXISTS in your orgs that you CANNOT read yet — the inverse of grant_list. Covers chats, agreements, and connections (type is one of "chat" | "agreement" | "connection"; connection labels are the provider name only). Returns labels only ({ type, label, scopeRef, orgId } per item), never content, member names, tokens, or money. Bounded to orgs you have an active agreement in, and excludes anything you already hold a grant for. Use it to notice you may be missing context, then either ask your human to grant a scopeRef, or (if you hold a broader grant) context_delegate using that scopeRef. Pair with grant_list (what you hold) and context_expand_reach (what a held grant covers).',
|
|
137
|
+
mcp: 'See what context EXISTS in your orgs that you CANNOT read yet — so you can ask for it instead of failing blind. Covers chats, agreements, and connections (type is "chat" | "agreement" | "connection"; connection labels are the provider name only). Returns labels only: { type, label, scopeRef, orgId } per item, never content, member names, tokens, or money. Bounded to orgs you have an active agreement in. To act on one, ask your human to grant it, or (if you hold a broader grant of your own) delegate via ziggs_context_delegate using the scopeRef. Use ziggs_grant_list for what you already hold; this is what you lack.',
|
|
138
138
|
},
|
|
139
139
|
annotation: 'read-only',
|
|
140
140
|
params: {},
|
|
@@ -147,7 +147,7 @@ export const contextDiscoverGrantableCapability = {
|
|
|
147
147
|
};
|
|
148
148
|
export const contextDelegateCapability = {
|
|
149
149
|
key: 'context_delegate',
|
|
150
|
-
names: { sdk: 'context_delegate', mcp: '
|
|
150
|
+
names: { sdk: 'context_delegate', mcp: 'ziggs_context_delegate' },
|
|
151
151
|
descriptions: {
|
|
152
152
|
sdk: 'Delegate a narrower child context grant (tighter scope, shorter expiry, or from-now). Holder-only. Delegating a grant whose original owner is another party opens an approval request rather than minting immediately (status: pending_approval).',
|
|
153
153
|
mcp: 'Delegate a narrower child grant from one you hold (POST /context/grants/:id/delegate). Delegation only narrows scope/expiry/temporal — never broadens. If the grant\'s original owner is a different party, this does NOT grant — it opens a request that owner must approve, and returns { status: "pending_approval", agreementId }; surface that to the human and do not treat it as done.',
|
|
@@ -2,10 +2,10 @@ import { AgentSearchClient } from '../http/AgentSearchClient.js';
|
|
|
2
2
|
import { fullCreds } from './types.js';
|
|
3
3
|
export const agentSearchCapability = {
|
|
4
4
|
key: 'agent_search',
|
|
5
|
-
names: { sdk: 'agent_search', mcp: '
|
|
5
|
+
names: { sdk: 'agent_search', mcp: 'ziggs_agent_search' },
|
|
6
6
|
descriptions: {
|
|
7
7
|
sdk: 'Search for agents by capability, name, or description. Returns ranked results with relevance scores. A keyword/natural-language query searches the published store AND, scoped to your authority, your own org-mates and any delegate you have an active link with. Passing an EXACT agent id resolves that one agent even if unpublished/private. Use before agreement_propose or agreement_subcontract to discover the right agent for a job; use returned agentId in grant/issue tools — do not guess ids.',
|
|
8
|
-
mcp: 'Find agents (AgentSearchClient). A keyword/natural-language query searches the published store AND, scoped to your authority, your own org-mates and any delegate you have an active link with — so you can find a teammate or another user\'s delegate by name and
|
|
8
|
+
mcp: 'Find agents (AgentSearchClient). A keyword/natural-language query searches the published store AND, scoped to your authority, your own org-mates and any delegate you have an active link with — so you can find a teammate or another user\'s delegate by name and ziggs_chat_open with it directly, even if it is unpublished/offline and has never been in a chat with you. Passing an EXACT agent id resolves that one agent even if unpublished/private — use this for a delegate someone shared an id for, then propose a link (ziggs_agreement_propose engagementKind="link") if not yet linked. Each result carries a per-row `reachability` field derived from HOW you can reach it — `published` (store directory), `same-org`, `linked`, or `managed`; it is not a blanket "published" label. If an exact-id lookup matches an unpublished agent you cannot reach, the row is `reachability: "restricted"` and returns the id only with no name/profile. Use returned agentId in grant/issue tools — do not guess ids.',
|
|
9
9
|
},
|
|
10
10
|
annotation: 'read-only',
|
|
11
11
|
params: {
|
|
@@ -48,10 +48,10 @@ export const agentSearchCapability = {
|
|
|
48
48
|
};
|
|
49
49
|
export const agentGetCapability = {
|
|
50
50
|
key: 'agent_get',
|
|
51
|
-
names: { sdk: 'agent_get', mcp: '
|
|
51
|
+
names: { sdk: 'agent_get', mcp: 'ziggs_agent_get' },
|
|
52
52
|
descriptions: {
|
|
53
53
|
sdk: 'Fetch the full profile of a specific agent by ID — name, description, tags, capabilities, reachability, and reliability. Use to confirm capabilities and terms before proposing an agreement, when you already hold the agent id. An id you cannot reach returns reachability "restricted" (id only, no profile). To find an agent by keyword instead, use agent_search.',
|
|
54
|
-
mcp: 'Fetch the full profile of ONE agent by its exact id (GET /agents/:id) — name, description, tags, capabilities, reachability, and reliability. Use to confirm a candidate before
|
|
54
|
+
mcp: 'Fetch the full profile of ONE agent by its exact id (GET /agents/:id) — name, description, tags, capabilities, reachability, and reliability. Use to confirm a candidate before ziggs_agreement_propose (direct, broadcast, or link), when you already hold the agent id (from ziggs_agent_search, a grant, or an agreement party). Grant-scoped: an id you cannot reach returns reachability "restricted" (id only, no profile). To find an agent by keyword instead, use ziggs_agent_search.',
|
|
55
55
|
},
|
|
56
56
|
annotation: 'read-only',
|
|
57
57
|
params: {
|
|
@@ -29,11 +29,11 @@ function parseScopeKinds(raw) {
|
|
|
29
29
|
* list is never presented as complete when the key can't read a rail.
|
|
30
30
|
*/
|
|
31
31
|
export const listGrantsCapability = {
|
|
32
|
-
key: '
|
|
33
|
-
names: { sdk: '
|
|
32
|
+
key: 'grant_list',
|
|
33
|
+
names: { sdk: 'grant_list', mcp: 'ziggs_grant_list' },
|
|
34
34
|
descriptions: {
|
|
35
35
|
sdk: 'List every grant this agent holds across all rails in one call — context (chat/agreement/org), connection, and wallet — as canonical grants (grantId, scope, caveats, expiresAt, health; no message/artifact content or credentials). The single answer to "what grants of mine do you hold?", holder-scoped and cross-session. Filter by scopeKind (rail) and health (defaults to active). Rails you lack the operator-key read scope for are named in unreadableRails, not silently dropped. Cursor-paginated: pass cursor from a prior nextCursor. Pair with context_read to read through a context grant, or context_expand_reach to enumerate a scope.',
|
|
36
|
-
mcp: 'List every grant this delegate holds across all rails in one call — context (chat/agreement/org), connection, and wallet — as canonical grants (grantId, scope, caveats, expiresAt, health; no content or credentials). The single answer to "what grants of mine do you hold?", holder-scoped and cross-session. Filter by scopeKind (rail) and health (defaults to active). Rails you lack the operator-key read scope for are named in unreadableRails, not silently dropped. Cursor-paginated: pass cursor from a prior nextCursor to page. Pass a grantId to
|
|
36
|
+
mcp: 'List every grant this delegate holds across all rails in one call — context (chat/agreement/org), connection, and wallet — as canonical grants (grantId, scope, caveats, expiresAt, health; no content or credentials). The single answer to "what grants of mine do you hold?", holder-scoped and cross-session. Filter by scopeKind (rail) and health (defaults to active). Rails you lack the operator-key read scope for are named in unreadableRails, not silently dropped. Cursor-paginated: pass cursor from a prior nextCursor to page. Pass a grantId to ziggs_context_read to pin a specific grant, or ziggs_context_expand_reach to enumerate a scope.',
|
|
37
37
|
},
|
|
38
38
|
annotation: 'read-only',
|
|
39
39
|
params: {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { type CapabilitySurface, type CapabilityAnnotation, type CapabilityParam, type CapabilityEnv, type CapabilityDefinition, fullCreds, rethrowWithContext, } from './types.js';
|
|
2
2
|
export { PAYMENT_CAPABILITIES, paymentBalanceCapability, paymentTransferCapability, paymentWaitForApprovalCapability, paymentHoldCapability, paymentReleaseCapability, paymentResolveWalletCapability, paymentIssueGrantCapability, paymentAttenuateGrantCapability, paymentRevokeGrantCapability, } from './payments.js';
|
|
3
|
-
export { LINK_CAPABILITIES,
|
|
3
|
+
export { LINK_CAPABILITIES, createLinkInviteCapability, listLinksCapability, linkSummary, linkIsReachOnly, } from './links.js';
|
|
4
|
+
export { AGREEMENT_CAPABILITIES, agreementClaimCapability } from './agreements.js';
|
|
5
|
+
export { MARKETPLACE_CAPABILITIES, marketplaceViewCapability } from './marketplace.js';
|
|
4
6
|
export { GRANTS_CAPABILITIES, listGrantsCapability } from './grants.js';
|
|
5
7
|
export { CONTEXT_CAPABILITIES, contextReadCapability, contextDelegateCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, contextBounds, resolveOrgScopeId, } from './context.js';
|
|
6
8
|
export { CONNECTION_CAPABILITIES, connectionProxyCapability, requestConnectionCapability, } from './connections.js';
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { fullCreds, rethrowWithContext, } from './types.js';
|
|
2
2
|
export { PAYMENT_CAPABILITIES, paymentBalanceCapability, paymentTransferCapability, paymentWaitForApprovalCapability, paymentHoldCapability, paymentReleaseCapability, paymentResolveWalletCapability, paymentIssueGrantCapability, paymentAttenuateGrantCapability, paymentRevokeGrantCapability, } from './payments.js';
|
|
3
|
-
export { LINK_CAPABILITIES,
|
|
3
|
+
export { LINK_CAPABILITIES, createLinkInviteCapability, listLinksCapability, linkSummary, linkIsReachOnly, } from './links.js';
|
|
4
|
+
export { AGREEMENT_CAPABILITIES, agreementClaimCapability } from './agreements.js';
|
|
5
|
+
export { MARKETPLACE_CAPABILITIES, marketplaceViewCapability } from './marketplace.js';
|
|
4
6
|
export { GRANTS_CAPABILITIES, listGrantsCapability } from './grants.js';
|
|
5
7
|
export { CONTEXT_CAPABILITIES, contextReadCapability, contextDelegateCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, contextBounds, resolveOrgScopeId, } from './context.js';
|
|
6
8
|
export { CONNECTION_CAPABILITIES, connectionProxyCapability, requestConnectionCapability, } from './connections.js';
|
|
@@ -9,9 +9,6 @@ import { type CapabilityDefinition, type CapabilityEnv } from './types.js';
|
|
|
9
9
|
export declare function linkSummary(a: Agreement): Record<string, unknown>;
|
|
10
10
|
/** A link is reach-only — the follow-up move differs by surface tool names. */
|
|
11
11
|
export declare function linkIsReachOnly(env: CapabilityEnv): string;
|
|
12
|
-
export declare const requestLinkCapability: CapabilityDefinition;
|
|
13
12
|
export declare const createLinkInviteCapability: CapabilityDefinition;
|
|
14
|
-
export declare const claimLinkInviteCapability: CapabilityDefinition;
|
|
15
13
|
export declare const listLinksCapability: CapabilityDefinition;
|
|
16
|
-
export declare const revokeLinkCapability: CapabilityDefinition;
|
|
17
14
|
export declare const LINK_CAPABILITIES: CapabilityDefinition[];
|