@ziggs-ai/api-client 0.3.1 → 0.5.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.
Files changed (42) hide show
  1. package/dist/ConnectionManager.d.ts +13 -1
  2. package/dist/ConnectionManager.js +52 -9
  3. package/dist/capabilities/artifacts.d.ts +3 -0
  4. package/dist/capabilities/artifacts.js +94 -0
  5. package/dist/capabilities/chat.d.ts +11 -0
  6. package/dist/capabilities/chat.js +38 -0
  7. package/dist/capabilities/connections.d.ts +4 -0
  8. package/dist/capabilities/connections.js +112 -0
  9. package/dist/capabilities/context.d.ts +23 -0
  10. package/dist/capabilities/context.js +220 -0
  11. package/dist/capabilities/discovery.d.ts +4 -0
  12. package/dist/capabilities/discovery.js +77 -0
  13. package/dist/capabilities/grants.d.ts +9 -0
  14. package/dist/capabilities/grants.js +77 -0
  15. package/dist/capabilities/index.d.ts +9 -0
  16. package/dist/capabilities/index.js +9 -0
  17. package/dist/capabilities/links.d.ts +17 -0
  18. package/dist/capabilities/links.js +219 -0
  19. package/dist/capabilities/payments.d.ts +11 -0
  20. package/dist/capabilities/payments.js +404 -0
  21. package/dist/capabilities/types.d.ts +88 -0
  22. package/dist/capabilities/types.js +24 -0
  23. package/dist/http/AgreementClient.d.ts +6 -0
  24. package/dist/http/ConnectionsClient.d.ts +27 -1
  25. package/dist/http/ConnectionsClient.js +29 -0
  26. package/dist/http/ContextReadClient.js +7 -1
  27. package/dist/http/GrantsClient.d.ts +16 -0
  28. package/dist/http/GrantsClient.js +3 -0
  29. package/dist/http/InboxClient.d.ts +95 -65
  30. package/dist/http/InboxClient.js +42 -14
  31. package/dist/http/MarketplaceClient.d.ts +8 -0
  32. package/dist/http/OrgsClient.d.ts +36 -0
  33. package/dist/http/OrgsClient.js +61 -0
  34. package/dist/http/PaymentsClient.d.ts +75 -10
  35. package/dist/http/PaymentsClient.js +26 -14
  36. package/dist/http/index.d.ts +6 -6
  37. package/dist/http/index.js +1 -1
  38. package/dist/index.d.ts +2 -0
  39. package/dist/index.js +1 -0
  40. package/package.json +1 -1
  41. package/dist/http/grantRails.d.ts +0 -20
  42. package/dist/http/grantRails.js +0 -50
@@ -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;
@@ -16,6 +16,7 @@ export class ConnectionManager {
16
16
  _active;
17
17
  _meta;
18
18
  _starting;
19
+ _pinned;
19
20
  _shouldLazyStart;
20
21
  constructor({ maxActive = 50, idleTimeoutMs = 60_000, control, shouldLazyStart } = {}) {
21
22
  this.maxActive = maxActive;
@@ -28,6 +29,7 @@ export class ConnectionManager {
28
29
  this._active = new Map();
29
30
  this._meta = new Map();
30
31
  this._starting = new Map();
32
+ this._pinned = new Set();
31
33
  }
32
34
  register(id, openFn, closeFn, meta) {
33
35
  if (!id)
@@ -68,7 +70,19 @@ export class ConnectionManager {
68
70
  let consecutiveErrors = 0;
69
71
  while (this._polling) {
70
72
  try {
71
- const env = await inbox.getOperatorInbox({ waitSeconds: OPERATOR_POLL_WAIT_SECONDS });
73
+ // Roster scoping (ZIG-965): only agents this manager can actually
74
+ // start are worth sweeping server-side. Without it a partial launcher
75
+ // (ONLY_AGENTS) paid for a sweep of the owner's whole seeded fleet.
76
+ //
77
+ // Already-running agents are excluded: this loop's only job is
78
+ // lazy-start, and a running host polls its own inbox. Sending them
79
+ // made the server answer for agents whose entry we then discarded
80
+ // below — pure duplicate work, growing with how healthy the fleet is.
81
+ const startable = [...this._entries.keys()].filter((id) => !this._active.has(id));
82
+ const env = await inbox.getOperatorInbox({
83
+ waitSeconds: OPERATOR_POLL_WAIT_SECONDS,
84
+ agents: startable,
85
+ });
72
86
  consecutiveErrors = 0;
73
87
  for (const entry of env.agents) {
74
88
  const id = entry.agentId;
@@ -92,7 +106,11 @@ export class ConnectionManager {
92
106
  }
93
107
  }
94
108
  }
95
- async startAgent(id) {
109
+ async startAgent(id, { pinned = false } = {}) {
110
+ // Recorded before any await so a pin is never lost to a concurrent
111
+ // unpinned start of the same id.
112
+ if (pinned)
113
+ this._pinned.add(id);
96
114
  const existing = this._active.get(id);
97
115
  if (existing) {
98
116
  this._resetTimer(id);
@@ -136,6 +154,7 @@ export class ConnectionManager {
136
154
  touch(id) { this._resetTimer(id); }
137
155
  list() { return [...this._entries.keys()]; }
138
156
  listActive() { return [...this._active.keys()]; }
157
+ listPinned() { return [...this._pinned]; }
139
158
  get size() { return this._entries.size; }
140
159
  query({ domain, expertise, tags } = {}) {
141
160
  const results = [];
@@ -156,6 +175,14 @@ export class ConnectionManager {
156
175
  if (!entry)
157
176
  return;
158
177
  clearTimeout(entry.timer);
178
+ // A pinned host has no idle timer at all. Empty polls don't count as
179
+ // activity (only delivered items call touch()), so a pinned agent nobody
180
+ // talks to would otherwise be swept — and with lazy start unable to keep
181
+ // up at fleet size, nothing brings it back until the process restarts.
182
+ if (this._pinned.has(id)) {
183
+ entry.timer = null;
184
+ return;
185
+ }
159
186
  entry.timer = setTimeout(() => {
160
187
  this.sleep(id).catch(err => runtimeLog.warn('ConnectionManager', `idle sleep("${id}") failed: ${err.message}`));
161
188
  }, this.idleTimeoutMs);
@@ -168,13 +195,29 @@ export class ConnectionManager {
168
195
  this._scheduleIdle(id);
169
196
  }
170
197
  async _evictLRU() {
171
- let oldest = null;
172
- for (const [id, entry] of this._active) {
173
- const oldestEntry = oldest ? this._active.get(oldest) : null;
174
- if (!oldest || !oldestEntry || entry.lastActive < oldestEntry.lastActive)
175
- oldest = id;
198
+ // Pinned hosts are the last to go: pick the LRU unpinned one, and only
199
+ // fall back to a pinned one when every active host is pinned. maxActive
200
+ // stays a hard cap — a pin outranks other agents, not the capacity limit —
201
+ // but say so, because it means more agents were pinned than can run.
202
+ const oldestOf = (ids) => {
203
+ let oldest = null;
204
+ for (const id of ids) {
205
+ const entry = this._active.get(id);
206
+ const oldestEntry = oldest ? this._active.get(oldest) : null;
207
+ if (!entry)
208
+ continue;
209
+ if (!oldest || !oldestEntry || entry.lastActive < oldestEntry.lastActive)
210
+ oldest = id;
211
+ }
212
+ return oldest;
213
+ };
214
+ const active = [...this._active.keys()];
215
+ const victim = oldestOf(active.filter(id => !this._pinned.has(id))) ?? oldestOf(active);
216
+ if (!victim)
217
+ return;
218
+ if (this._pinned.has(victim)) {
219
+ 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
220
  }
177
- if (oldest)
178
- await this.sleep(oldest);
221
+ await this.sleep(victim);
179
222
  }
180
223
  }
@@ -0,0 +1,3 @@
1
+ import { type CapabilityDefinition } from './types.js';
2
+ export declare const recordArtifactCapability: CapabilityDefinition;
3
+ export declare const ARTIFACT_CAPABILITIES: CapabilityDefinition[];
@@ -0,0 +1,94 @@
1
+ import { ArtifactsClient } from '../http/ArtifactsClient.js';
2
+ import { fullCreds } from './types.js';
3
+ /**
4
+ * ZIG-560 teaching: name the result slot on the success path so an agent finds
5
+ * the right move unaided — worded in each surface's task grammar (SDK
6
+ * task_update carries the terminal result; MCP has ziggs_set_task_result).
7
+ */
8
+ function reportingHint(env, contentType, taskId) {
9
+ const close = env.surface === 'mcp'
10
+ ? 'ziggs_set_task_result ({ summary, status, links })'
11
+ : 'task_update';
12
+ if (contentType === 'result') {
13
+ return taskId
14
+ ? `Recorded as a task-bound result artifact. Close the task by setting its terminal result with ${close}.`
15
+ : `Recorded as a result artifact, but not bound to a task — pass taskId to bind it, then close the task with ${close}.`;
16
+ }
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
+ }
19
+ export const recordArtifactCapability = {
20
+ key: 'record_artifact',
21
+ names: { sdk: 'record_artifact', mcp: 'ziggs_record_artifact' },
22
+ descriptions: {
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
+ mcp: 'Write an artifact to a chat or agreement scope. Set visibility explicitly. ' +
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 ziggs_set_task_result; never report finished work as a chat message (chat is conversation only).',
27
+ },
28
+ annotation: 'write',
29
+ params: {
30
+ text: { type: 'string', required: true, description: 'Artifact body' },
31
+ visibility: {
32
+ type: 'string',
33
+ required: true,
34
+ enum: ['chat', 'agent-private'],
35
+ description: 'chat = visible to scope parties; agent-private = your eyes only',
36
+ },
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
+ },
42
+ taskId: {
43
+ type: 'string',
44
+ description: 'Optional task — creates a TaskArtifactLink alongside the primary scope link',
45
+ },
46
+ content_type: {
47
+ type: 'string',
48
+ description: 'Default text; use result for a finished deliverable',
49
+ },
50
+ idempotencyKey: {
51
+ type: 'string',
52
+ description: 'Optional dedup key: a redelivered record with the same key no-ops and returns the original artifact. Derive it deterministically (e.g. from the source event + step) — not a random value — so a crash-replay reproduces it.',
53
+ },
54
+ },
55
+ needsAgentId: true,
56
+ handler: async (args, env) => {
57
+ const agreementId = args['agreementId'];
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');
66
+ }
67
+ const visibility = args['visibility'];
68
+ if (visibility !== 'chat' && visibility !== 'agent-private') {
69
+ throw new Error('visibility must be chat or agent-private');
70
+ }
71
+ const contentType = args['content_type'];
72
+ const taskId = args['taskId'];
73
+ const creds = fullCreds(env);
74
+ const { artifactId } = await new ArtifactsClient(creds.operatorKey, creds.agentId).writeStrict({
75
+ text: args['text'],
76
+ visibility,
77
+ chatId,
78
+ agreementId,
79
+ taskId,
80
+ content_type: contentType,
81
+ idempotencyKey: args['idempotencyKey'],
82
+ });
83
+ return {
84
+ ok: true,
85
+ artifactId,
86
+ visibility,
87
+ chatId,
88
+ agreementId,
89
+ taskId,
90
+ reportingHint: reportingHint(env, contentType, taskId),
91
+ };
92
+ },
93
+ };
94
+ export const ARTIFACT_CAPABILITIES = [recordArtifactCapability];
@@ -0,0 +1,11 @@
1
+ import { type CapabilityDefinition } from './types.js';
2
+ /**
3
+ * ZIG-900 — the decided chat surface for agents: open_conversation only.
4
+ * There is deliberately NO chat-listing tool on the SDK: agents read context
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 list_grants
7
+ * scopeKind=chat (ZIG-626 rule). MCP keeps its rich session-UX lister
8
+ * (ziggs_list_chats) for humans in Cursor/Claude.
9
+ */
10
+ export declare const openConversationCapability: CapabilityDefinition;
11
+ export declare const CHAT_CAPABILITIES: CapabilityDefinition[];
@@ -0,0 +1,38 @@
1
+ import { openConversation } from '../http/ChatClient.js';
2
+ import { fullCreds } from './types.js';
3
+ /**
4
+ * ZIG-900 — the decided chat surface for agents: open_conversation only.
5
+ * There is deliberately NO chat-listing tool on the SDK: agents read context
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 list_grants
8
+ * scopeKind=chat (ZIG-626 rule). MCP keeps its rich session-UX lister
9
+ * (ziggs_list_chats) for humans in Cursor/Claude.
10
+ */
11
+ export const openConversationCapability = {
12
+ key: 'open_conversation',
13
+ names: { sdk: 'open_conversation', mcp: 'ziggs_open_conversation' },
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 — request_link (if you have its agent id) or create_link_invite (if you do not), approved/claimed — otherwise this fails with AGENT_NOT_PUBLISHED. To list chats you can already read, use list_grants 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 — call ziggs_request_link (if you have its agent id) or ziggs_create_link_invite (if you do not) and have it approved/claimed — otherwise this fails with AGENT_NOT_PUBLISHED.',
17
+ },
18
+ annotation: 'write',
19
+ params: {
20
+ participantId: {
21
+ type: 'string',
22
+ required: true,
23
+ description: 'User or agent id to converse with (from agent search — do not guess ids)',
24
+ },
25
+ },
26
+ needsAgentId: true,
27
+ handler: async (args, env) => {
28
+ if (!args['participantId'])
29
+ throw new Error('participantId is required');
30
+ const { chatId } = await openConversation(args['participantId'], fullCreds(env));
31
+ const lister = env.surface === 'mcp' ? 'ziggs_list_grants' : 'list_grants';
32
+ return {
33
+ chatId,
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.`,
35
+ };
36
+ },
37
+ };
38
+ export const CHAT_CAPABILITIES = [openConversationCapability];
@@ -0,0 +1,4 @@
1
+ import { type CapabilityDefinition } from './types.js';
2
+ export declare const connectionProxyCapability: CapabilityDefinition;
3
+ export declare const requestConnectionCapability: CapabilityDefinition;
4
+ export declare const CONNECTION_CAPABILITIES: CapabilityDefinition[];
@@ -0,0 +1,112 @@
1
+ import { ConnectionsClient } from '../http/ConnectionsClient.js';
2
+ import { rethrowWithContext, } from './types.js';
3
+ function client(env) {
4
+ const { operatorKey, agentId } = env.creds;
5
+ if (!operatorKey)
6
+ throw new Error('operatorKey missing from tool context');
7
+ return new ConnectionsClient(operatorKey, agentId, env.baseUrl);
8
+ }
9
+ export const connectionProxyCapability = {
10
+ key: 'connection_proxy',
11
+ names: { sdk: 'connection_proxy', mcp: 'ziggs_connection_proxy' },
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 list_grants (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_list_links for that) without ever seeing the credential. " +
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
+ 'Provide connectionId, grantId, the provider action (e.g. repo:read), and an optional action-specific payload. ' +
17
+ "Don't know connectionId/grantId yet? Call ziggs_list_my_connections first.",
18
+ },
19
+ annotation: 'write',
20
+ params: {
21
+ connectionId: { type: 'string', required: true, description: 'Connection to act on' },
22
+ grantId: {
23
+ type: 'string',
24
+ required: true,
25
+ description: 'Grant the owner issued to this agent for the connection',
26
+ },
27
+ action: { type: 'string', required: true, description: 'Provider action, e.g. repo:read' },
28
+ payload: { type: 'object', description: 'Action-specific arguments (provider-defined)' },
29
+ },
30
+ needsAgentId: true,
31
+ handler: async (args, env) => {
32
+ if (!args['connectionId'])
33
+ throw new Error('connectionId is required');
34
+ if (!args['grantId'])
35
+ throw new Error('grantId is required');
36
+ if (!args['action'])
37
+ throw new Error('action is required');
38
+ try {
39
+ const result = await client(env).proxy({
40
+ connectionId: args['connectionId'],
41
+ grantId: args['grantId'],
42
+ action: args['action'],
43
+ payload: args['payload'],
44
+ });
45
+ return { ok: true, action: args['action'], result };
46
+ }
47
+ catch (e) {
48
+ rethrowWithContext(e, 'Connection proxy failed');
49
+ }
50
+ },
51
+ };
52
+ export const requestConnectionCapability = {
53
+ key: 'request_connection',
54
+ names: { sdk: 'request_connection', mcp: 'ziggs_request_connection' },
55
+ descriptions: {
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
+ mcp: 'Ask your principal (the human) to connect a remote MCP server and grant you the listed tools. ' +
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 ziggs_list_my_connections for use with ziggs_connection_proxy.',
60
+ },
61
+ annotation: 'write',
62
+ params: {
63
+ chatId: {
64
+ type: 'string',
65
+ required: true,
66
+ description: 'The chat you are working in — the consent card is opened there',
67
+ },
68
+ serverUrl: { type: 'string', required: true, description: 'Remote MCP server URL (https)' },
69
+ tools: {
70
+ type: 'array',
71
+ items: { type: 'string' },
72
+ required: true,
73
+ description: "Tool names you want — become the grant's allowed_actions caveats",
74
+ },
75
+ reason: { type: 'string', description: 'Plain-language reason shown to the human deciding' },
76
+ },
77
+ needsAgentId: true,
78
+ handler: async (args, env) => {
79
+ if (!args['chatId'])
80
+ throw new Error('chatId is required');
81
+ if (!args['serverUrl'])
82
+ throw new Error('serverUrl is required');
83
+ const tools = args['tools'];
84
+ if (!Array.isArray(tools) || tools.length === 0) {
85
+ throw new Error('tools must be a non-empty array of tool names');
86
+ }
87
+ try {
88
+ const result = await client(env).requestMcpConnection({
89
+ chatId: args['chatId'],
90
+ serverUrl: args['serverUrl'],
91
+ tools: tools,
92
+ reason: args['reason'],
93
+ });
94
+ return {
95
+ ok: true,
96
+ ...result,
97
+ note: env.surface === 'mcp'
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 ziggs_list_my_connections for ziggs_connection_proxy.'
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 list_grants (scopeKind=connection) / connection_list_grants for connection_proxy.',
102
+ };
103
+ }
104
+ catch (e) {
105
+ rethrowWithContext(e, 'Connection request failed');
106
+ }
107
+ },
108
+ };
109
+ export const CONNECTION_CAPABILITIES = [
110
+ connectionProxyCapability,
111
+ requestConnectionCapability,
112
+ ];
@@ -0,0 +1,23 @@
1
+ import { type GrantView } from '../http/grants.js';
2
+ import { type CapabilityDefinition, type CapabilityEnv } from './types.js';
3
+ /**
4
+ * Human/LLM-readable bounds for a context grant. ZIG-646 folded a context
5
+ * grant's temporal mode + read watermark into the canonical `caveats` array,
6
+ * so pull them back out here to keep the `bounds` summary stable.
7
+ */
8
+ export declare function contextBounds(grant: GrantView): Record<string, unknown>;
9
+ /**
10
+ * ZIG-941 #6 — org-scoped grants may name the org instead of pasting its
11
+ * opaque org_... id: exact id or case-insensitive name against the operator's
12
+ * memberships. Ambiguous names throw with the candidate list rather than
13
+ * guessing; a name that matched nothing throws with a pointer to the org
14
+ * lister. An org_... id that is not a membership passes through unchanged —
15
+ * you may hold a grant on an org you do not belong to, so the server stays
16
+ * the authority on the id.
17
+ */
18
+ export declare function resolveOrgScopeId(env: CapabilityEnv, scopeId: string): Promise<string>;
19
+ export declare const contextReadCapability: CapabilityDefinition;
20
+ export declare const contextExpandReachCapability: CapabilityDefinition;
21
+ export declare const contextDiscoverGrantableCapability: CapabilityDefinition;
22
+ export declare const contextDelegateCapability: CapabilityDefinition;
23
+ export declare const CONTEXT_CAPABILITIES: CapabilityDefinition[];