@ziggs-ai/agent-sdk 0.2.1 → 0.3.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/README.md +1 -1
- package/dist/AgentHost.d.ts +21 -20
- package/dist/AgentHost.js +60 -136
- package/dist/adapters/McpAdapter.js +14 -13
- package/dist/agent/Agent.js +4 -0
- package/dist/agent/ClaudeSdkAgent.d.ts +5 -0
- package/dist/agent/ClaudeSdkAgent.js +25 -6
- package/dist/agent/claudeCognition.d.ts +7 -0
- package/dist/agent/claudeCognition.js +38 -7
- package/dist/agent/claudeSdkProtocol.d.ts +8 -7
- package/dist/agent/claudeSdkProtocol.js +8 -7
- package/dist/agent/claudeSdkTools.d.ts +4 -2
- package/dist/agent/claudeSdkTools.js +26 -6
- package/dist/agent/toolManagerToSdkMcp.js +1 -0
- package/dist/index.d.ts +5 -9
- package/dist/index.js +12 -6
- package/dist/ingress/normalizeIncoming.d.ts +17 -3
- package/dist/ingress/normalizeIncoming.js +11 -26
- package/dist/runtime/AgentMachine.d.ts +5 -2
- package/dist/runtime/AgentMachine.js +8 -2
- package/dist/runtime/runMechanical.d.ts +3 -1
- package/dist/runtime/runMechanical.js +2 -2
- package/dist/runtime/runTurn.d.ts +3 -1
- package/dist/runtime/runTurn.js +25 -7
- package/dist/runtime/wakeWriteKeys.d.ts +25 -0
- package/dist/runtime/wakeWriteKeys.js +134 -0
- package/dist/server/ConnectionPool.d.ts +7 -4
- package/dist/server/ConnectionPool.js +11 -14
- package/dist/server/ZiggsEffectHandler.js +17 -12
- package/dist/server/inboxLoop.d.ts +50 -0
- package/dist/server/{InboxCatchUp.js → inboxLoop.js} +63 -52
- package/dist/server/runLauncher.js +1 -1
- package/dist/server/tasks/TaskService.d.ts +2 -1
- package/dist/server/tasks/TaskService.js +5 -2
- package/dist/server/tools/index.d.ts +7 -3
- package/dist/server/tools/index.js +13 -3
- package/dist/server/tools/tier1/protocolRunner.d.ts +2 -1
- package/dist/server/tools/tier1/protocolRunner.js +43 -9
- package/dist/server/tools/tier1/protocolTools.js +1 -1
- package/dist/server/tools/tier2/artifactTools.d.ts +3 -0
- package/dist/server/tools/tier2/artifactTools.js +82 -0
- package/dist/server/tools/tier2/chatTools.d.ts +3 -0
- package/dist/server/tools/tier2/chatTools.js +37 -0
- package/dist/server/tools/tier2/connectionTools.d.ts +1 -0
- package/dist/server/tools/tier2/connectionTools.js +52 -5
- package/dist/server/tools/tier2/contextTools.d.ts +4 -2
- package/dist/server/tools/tier2/contextTools.js +105 -12
- package/dist/server/tools/tier2/grantsTools.d.ts +3 -0
- package/dist/server/tools/tier2/grantsTools.js +81 -0
- package/dist/server/tools/tier2/linkTools.d.ts +7 -0
- package/dist/server/tools/tier2/linkTools.js +156 -0
- package/dist/server/tools/tier2/paymentTools.d.ts +0 -1
- package/dist/server/tools/tier2/paymentTools.js +9 -13
- package/dist/types.d.ts +40 -0
- package/package.json +2 -2
- package/dist/server/InboxCatchUp.d.ts +0 -24
- package/dist/server/ziggsconnect/ZiggsConnectClient.d.ts +0 -35
- package/dist/server/ziggsconnect/ZiggsConnectClient.js +0 -95
- package/dist/server/ziggscontext/ZiggsContextClient.d.ts +0 -53
- package/dist/server/ziggscontext/ZiggsContextClient.js +0 -124
- package/dist/server/ziggspay/ZiggsPayClient.d.ts +0 -82
- package/dist/server/ziggspay/ZiggsPayClient.js +0 -203
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { defineTool } from '../../../tools/defineTool.js';
|
|
2
|
+
import { createAgreement, claimAgreement, listAgreements, revokeAgreement, } from '@ziggs-ai/api-client';
|
|
3
|
+
/**
|
|
4
|
+
* ZIG-898 — link tools on the SDK surface, same base names as ziggs-mcp's
|
|
5
|
+
* trustTools (ziggs_request_link → request_link, …). Links are agreements with
|
|
6
|
+
* engagementKind 'link' — bilateral agent-to-agent trust that gives unpublished
|
|
7
|
+
* delegates cross-org reach. No new endpoints: everything rides the agreements
|
|
8
|
+
* rail exactly as the MCP tools do.
|
|
9
|
+
*
|
|
10
|
+
* A link is reach-only: it lets the peers find and message each other, never
|
|
11
|
+
* read context — grants stay separate (context_delegate / the owner's grant
|
|
12
|
+
* tools).
|
|
13
|
+
*/
|
|
14
|
+
function creds(ctx) {
|
|
15
|
+
const operatorKey = ctx?.['operatorKey'];
|
|
16
|
+
const agentId = ctx?.['agentId'];
|
|
17
|
+
if (!operatorKey)
|
|
18
|
+
throw new Error('operatorKey missing from tool context');
|
|
19
|
+
if (!agentId)
|
|
20
|
+
throw new Error('agentId missing from tool context');
|
|
21
|
+
return { operatorKey, agentId };
|
|
22
|
+
}
|
|
23
|
+
function webAppOrigin() {
|
|
24
|
+
return (process.env.ZIGGS_WEB_URL?.trim() || 'https://ziggsai.com').replace(/\/$/, '');
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* ZIG-670 rule carried over from the MCP tools: link-shaped summaries, not raw
|
|
28
|
+
* agreement documents — the money block, approvals array, and Mongo internals
|
|
29
|
+
* are noise on a trust relationship.
|
|
30
|
+
*/
|
|
31
|
+
function linkSummary(a) {
|
|
32
|
+
return {
|
|
33
|
+
agreementId: a.agreementId,
|
|
34
|
+
status: a.status,
|
|
35
|
+
proposalStatus: a.proposalStatus,
|
|
36
|
+
parties: {
|
|
37
|
+
creatorAgent: a.parties?.creatorAgent ?? null,
|
|
38
|
+
providerAgent: a.parties?.providerAgent ?? null,
|
|
39
|
+
creator: a.parties?.creator ?? null,
|
|
40
|
+
proposedTo: a.parties?.proposedTo ?? null,
|
|
41
|
+
},
|
|
42
|
+
...(a.description ? { description: a.description } : {}),
|
|
43
|
+
createdAt: a.createdAt,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const LINK_IS_REACH_ONLY = 'A link is reach-only: it does not grant context. Share a slice of a grant you hold with context_delegate, or ask the peer owner to issue one, before reading.';
|
|
47
|
+
export const requestLinkTool = defineTool('request_link', {
|
|
48
|
+
providerId: {
|
|
49
|
+
type: 'string',
|
|
50
|
+
required: true,
|
|
51
|
+
description: 'Bare agent id to link with (the target delegate). Use agent_search or a known delegate id — do not guess.',
|
|
52
|
+
},
|
|
53
|
+
message: {
|
|
54
|
+
type: 'string',
|
|
55
|
+
description: 'Optional note shown to the counterparty human on approval (agreement description)',
|
|
56
|
+
},
|
|
57
|
+
}, async (args, ctx) => {
|
|
58
|
+
const { agreement } = await createAgreement({
|
|
59
|
+
engagementKind: 'link',
|
|
60
|
+
providerId: args['providerId'],
|
|
61
|
+
description: args['message'],
|
|
62
|
+
}, creds(ctx));
|
|
63
|
+
return {
|
|
64
|
+
status: 'pending',
|
|
65
|
+
message: 'Link agreement created — the counterparty owner must approve (agreement_respond on their side) before cross-org reach. Surface pending state to your principal.',
|
|
66
|
+
agreement: linkSummary(agreement),
|
|
67
|
+
};
|
|
68
|
+
}, {
|
|
69
|
+
description: 'Request a bilateral trust link with another agent before cross-org reach (party-to-party, NOT a third-party service connection — that is the connection rail). A link is just an agreement (engagementKind "link"). The target OWNER must approve it before unpublished delegates can message each other.',
|
|
70
|
+
isAgreementCreation: true,
|
|
71
|
+
});
|
|
72
|
+
export const createLinkInviteTool = defineTool('create_link_invite', {
|
|
73
|
+
message: {
|
|
74
|
+
type: 'string',
|
|
75
|
+
description: 'Optional note shown to whoever opens the invite (agreement description)',
|
|
76
|
+
},
|
|
77
|
+
}, async (args, ctx) => {
|
|
78
|
+
const { agreement } = await createAgreement({ engagementKind: 'link', description: args['message'] }, creds(ctx));
|
|
79
|
+
return {
|
|
80
|
+
status: 'open',
|
|
81
|
+
inviteId: agreement.agreementId,
|
|
82
|
+
claimUrl: `${webAppOrigin()}/app/link-invites/${agreement.agreementId}`,
|
|
83
|
+
message: 'Open link invite created. Share claimUrl (or the inviteId) with the counterparty — they claim it in the Ziggs web app or via their claim_link_invite tool. No agent id needed on either side. Revoke with revoke_link to disable.',
|
|
84
|
+
agreement: linkSummary(agreement),
|
|
85
|
+
};
|
|
86
|
+
}, {
|
|
87
|
+
description: "Create a shareable OPEN link invite (bilateral agent-to-agent trust) when you do NOT have the counterparty's agent id (e.g. connecting across orgs). Creates an open link agreement proposed to everyone; the recipient forms the link by claiming the returned inviteId.",
|
|
88
|
+
isAgreementCreation: true,
|
|
89
|
+
});
|
|
90
|
+
export const claimLinkInviteTool = defineTool('claim_link_invite', {
|
|
91
|
+
agreementId: {
|
|
92
|
+
type: 'string',
|
|
93
|
+
required: true,
|
|
94
|
+
description: 'The invite id (agreementId) shared by the issuer',
|
|
95
|
+
},
|
|
96
|
+
}, async (args, ctx) => {
|
|
97
|
+
const { agreement } = await claimAgreement(args['agreementId'], creds(ctx));
|
|
98
|
+
return {
|
|
99
|
+
status: 'linked',
|
|
100
|
+
message: `Link invite claimed — you are now linked. ${LINK_IS_REACH_ONLY}`,
|
|
101
|
+
agreement: linkSummary(agreement),
|
|
102
|
+
};
|
|
103
|
+
}, {
|
|
104
|
+
description: 'Claim an open link invite by its id to form a bilateral link. You become the counterparty and the link activates immediately (cross-org reach). You cannot claim your own invite.',
|
|
105
|
+
});
|
|
106
|
+
export const listLinksTool = defineTool('list_links', {
|
|
107
|
+
status: {
|
|
108
|
+
type: 'string',
|
|
109
|
+
enum: ['active', 'open', 'cancelled', 'all'],
|
|
110
|
+
description: 'active (default) = established links; open = pending proposals/invites awaiting approval or claim; cancelled = revoked; all = every link regardless of status',
|
|
111
|
+
},
|
|
112
|
+
}, async (args, ctx) => {
|
|
113
|
+
const status = args['status'] ?? 'active';
|
|
114
|
+
if (!['active', 'open', 'cancelled', 'all'].includes(status)) {
|
|
115
|
+
throw new Error('status must be one of active, open, cancelled, all');
|
|
116
|
+
}
|
|
117
|
+
const links = await listAgreements({
|
|
118
|
+
engagementKind: 'link',
|
|
119
|
+
...(status === 'all' ? {} : { status }),
|
|
120
|
+
}, creds(ctx));
|
|
121
|
+
const summaries = links.map(linkSummary);
|
|
122
|
+
const hasActive = links.some((a) => a.status === 'active');
|
|
123
|
+
return {
|
|
124
|
+
count: summaries.length,
|
|
125
|
+
status,
|
|
126
|
+
links: summaries,
|
|
127
|
+
...(hasActive ? { nextSteps: LINK_IS_REACH_ONLY } : {}),
|
|
128
|
+
};
|
|
129
|
+
}, {
|
|
130
|
+
description: 'List link agreements for this agent — bilateral agent-to-agent trust relationships (GET /agreements?engagementKind=link). Defaults to ACTIVE links only; pass status to see pending proposals ("open") or revoked ones ("cancelled"). Each item is a link summary: agreementId, status, proposalStatus, parties.',
|
|
131
|
+
isGenericFallback: true,
|
|
132
|
+
});
|
|
133
|
+
export const revokeLinkTool = defineTool('revoke_link', {
|
|
134
|
+
agreementId: {
|
|
135
|
+
type: 'string',
|
|
136
|
+
required: true,
|
|
137
|
+
description: 'agreementId of the link agreement (from list_links)',
|
|
138
|
+
},
|
|
139
|
+
}, async (args, ctx) => {
|
|
140
|
+
const result = await revokeAgreement(args['agreementId'], creds(ctx));
|
|
141
|
+
return {
|
|
142
|
+
status: 'revoked',
|
|
143
|
+
message: 'Link revoked — unpublished cross-org reach to this peer is blocked again.',
|
|
144
|
+
agreementId: args['agreementId'],
|
|
145
|
+
agreement: result.agreement ? linkSummary(result.agreement) : null,
|
|
146
|
+
};
|
|
147
|
+
}, {
|
|
148
|
+
description: 'Revoke a bilateral link agreement (DELETE /agreements/:agreementId). Either party may revoke; cross-org reach ends immediately. For non-link agreements (hire/service/quest), use agreement_revoke — same endpoint, different messaging.',
|
|
149
|
+
});
|
|
150
|
+
export const LINK_TOOLS = [
|
|
151
|
+
requestLinkTool,
|
|
152
|
+
createLinkInviteTool,
|
|
153
|
+
claimLinkInviteTool,
|
|
154
|
+
listLinksTool,
|
|
155
|
+
revokeLinkTool,
|
|
156
|
+
];
|
|
@@ -8,5 +8,4 @@ export declare const paymentReleaseTool: ToolDefinition;
|
|
|
8
8
|
export declare const paymentIssueGrantTool: ToolDefinition;
|
|
9
9
|
export declare const paymentAttenuateGrantTool: ToolDefinition;
|
|
10
10
|
export declare const paymentRevokeGrantTool: ToolDefinition;
|
|
11
|
-
export declare const paymentListGrantsTool: ToolDefinition;
|
|
12
11
|
export declare const PAYMENT_TOOLS: ToolDefinition[];
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { defineTool } from '../../../tools/defineTool.js';
|
|
2
|
-
import {
|
|
2
|
+
import { PaymentsClient } from '@ziggs-ai/api-client';
|
|
3
3
|
function clientFor(ctx) {
|
|
4
4
|
const operatorKey = ctx?.['operatorKey'];
|
|
5
5
|
if (!operatorKey)
|
|
6
6
|
throw new Error('operatorKey missing from tool context');
|
|
7
|
-
|
|
7
|
+
// BACKEND_URL / ZIGGS_BACKEND_URL are the SDK runner's overrides; the shared
|
|
8
|
+
// client defaults to HTTP_URL (api-client convention) when neither is set.
|
|
9
|
+
const baseUrl = process.env.BACKEND_URL || process.env.ZIGGS_BACKEND_URL || undefined;
|
|
10
|
+
return new PaymentsClient(operatorKey, ctx?.['agentId'] || undefined, baseUrl);
|
|
8
11
|
}
|
|
9
12
|
function rethrowWithContext(error, prefix) {
|
|
10
13
|
const e = error;
|
|
@@ -148,18 +151,11 @@ const revokeGrantHandler = async (args, ctx) => {
|
|
|
148
151
|
}
|
|
149
152
|
};
|
|
150
153
|
export const paymentRevokeGrantTool = defineTool('payment_revoke_grant', { grantId: { type: 'string', required: true } }, revokeGrantHandler);
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
return { grants: result || [] };
|
|
155
|
-
}
|
|
156
|
-
catch (e) {
|
|
157
|
-
rethrowWithContext(e, 'Failed to list payment grants');
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
export const paymentListGrantsTool = defineTool('payment_list_grants', {}, listGrantsHandler);
|
|
154
|
+
// ZIG-893 — payment_list_grants retired; the wallet rail is now part of the
|
|
155
|
+
// unified list_grants tool (scopeKind: 'wallet'). Grant *mutations* stay on the
|
|
156
|
+
// payment rail (issue/attenuate/revoke below).
|
|
161
157
|
export const PAYMENT_TOOLS = [
|
|
162
158
|
paymentBalanceTool, paymentTransferTool, paymentWaitForApprovalTool,
|
|
163
159
|
paymentHoldTool, paymentReleaseTool, paymentResolveWalletTool,
|
|
164
|
-
paymentIssueGrantTool, paymentAttenuateGrantTool, paymentRevokeGrantTool,
|
|
160
|
+
paymentIssueGrantTool, paymentAttenuateGrantTool, paymentRevokeGrantTool,
|
|
165
161
|
];
|
package/dist/types.d.ts
CHANGED
|
@@ -346,6 +346,43 @@ export interface RecordedEntry {
|
|
|
346
346
|
text?: string;
|
|
347
347
|
[k: string]: unknown;
|
|
348
348
|
}
|
|
349
|
+
/**
|
|
350
|
+
* Deterministic idempotency keys for one wake's writes (delivery law, item 2).
|
|
351
|
+
* Created once per tick from (laneKey, triggering event) — see
|
|
352
|
+
* `createWakeWriteKeys` — and threaded to every artifact / task-state write the
|
|
353
|
+
* wake performs, so a crash-replay of the same wake reproduces the same keys
|
|
354
|
+
* and the backend drops the duplicates. Never LLM-supplied, never random.
|
|
355
|
+
*/
|
|
356
|
+
export interface WakeWriteKeys {
|
|
357
|
+
/** Stable id of this wake, or null when its trigger has no durable delivery identity (timer/synthetic frames) — null means writes go keyless, as before. */
|
|
358
|
+
readonly wakeKey: string | null;
|
|
359
|
+
/** Key for the next artifact write of this wake. Draws a per-wake ordinal — call exactly once per write, at the call site. */
|
|
360
|
+
artifactKey(): string | undefined;
|
|
361
|
+
/** Key for a task state transition. Derived from (taskId, state), so it is stable regardless of tool-call order within the wake. */
|
|
362
|
+
taskStateKey(taskId: string, state: string): string | undefined;
|
|
363
|
+
/** Default messageId for the next chat-message send of this wake — the backend dedups on (chatId, messageId). Draws the same per-wake ordinal as artifactKey — call exactly once per send, at the call site. */
|
|
364
|
+
messageKey(): string | undefined;
|
|
365
|
+
/** Key for a task spawn. Derived from what the spawn writes (agreement, description, assignee, parent), so it is stable regardless of tool-call order within the wake. */
|
|
366
|
+
taskSpawnKey(fields: {
|
|
367
|
+
agreementId: string;
|
|
368
|
+
description: string;
|
|
369
|
+
assigneeId?: string;
|
|
370
|
+
parentTaskId?: string;
|
|
371
|
+
}): string | undefined;
|
|
372
|
+
/** Key for an agreement proposal (`agreement_propose`). Derived from what the proposal writes (proposedTo, provider, description, parent), so a crash-replay dedups the row instead of minting a duplicate proposal — and multiple proposals in one wake stay distinct. */
|
|
373
|
+
agreementProposeKey(fields: {
|
|
374
|
+
proposedTo: string;
|
|
375
|
+
providerId?: string;
|
|
376
|
+
description: string;
|
|
377
|
+
parentAgreementId?: string;
|
|
378
|
+
}): string | undefined;
|
|
379
|
+
/** Key for a sub-agreement (`agreement_subcontract`). Derived from what the subcontract writes (executor, description, parent), so a replay dedups the row. */
|
|
380
|
+
agreementSubcontractKey(fields: {
|
|
381
|
+
executorId: string;
|
|
382
|
+
description: string;
|
|
383
|
+
parentAgreementId: string;
|
|
384
|
+
}): string | undefined;
|
|
385
|
+
}
|
|
349
386
|
import type { MemoryStore } from './memory/MemoryStore.js';
|
|
350
387
|
export type EffectKind = 'read-context' | 'llm-call' | 'stream-text' | 'tool-call' | 'send-message' | 'record-event';
|
|
351
388
|
export type Effect = {
|
|
@@ -388,6 +425,8 @@ export type Effect = {
|
|
|
388
425
|
runId?: string;
|
|
389
426
|
/** FSM state name (ZIG-440). */
|
|
390
427
|
stateId?: string;
|
|
428
|
+
/** Replay-stable write keys for this wake — write-capable tools derive their idempotency key from here, never from the LLM. */
|
|
429
|
+
wakeKeys?: WakeWriteKeys;
|
|
391
430
|
} | {
|
|
392
431
|
kind: 'send-message';
|
|
393
432
|
sessionId: string;
|
|
@@ -398,6 +437,7 @@ export type Effect = {
|
|
|
398
437
|
kind: 'record-event';
|
|
399
438
|
sessionId: string;
|
|
400
439
|
entry: RecordedEntry;
|
|
440
|
+
idempotencyKey?: string;
|
|
401
441
|
};
|
|
402
442
|
/**
|
|
403
443
|
* Typed result map. With this shape, `await eff({ kind: 'read-context', ... })`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ziggs-ai/agent-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Agent framework SDK for building autonomous agents on the Ziggs platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"@anthropic-ai/claude-agent-sdk": "^0.3.204",
|
|
27
27
|
"@anthropic-ai/sdk": "^0.110.0",
|
|
28
28
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
29
|
-
"@ziggs-ai/api-client": "^0.
|
|
29
|
+
"@ziggs-ai/api-client": "^0.2.0",
|
|
30
30
|
"ajv": "^8.17.1",
|
|
31
31
|
"dotenv": "^17.2.3",
|
|
32
32
|
"openai": "^4.0.0",
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { type Creds } from '@ziggs-ai/api-client';
|
|
2
|
-
import { SeenMessages } from './SeenMessages.js';
|
|
3
|
-
export interface InboxCatchUpDeps {
|
|
4
|
-
creds: Creds;
|
|
5
|
-
ownAgentId: string;
|
|
6
|
-
baseUrl?: string;
|
|
7
|
-
seen: SeenMessages;
|
|
8
|
-
onMessage: (text: string, metadata: Record<string, unknown>) => Promise<void>;
|
|
9
|
-
onResourceEvent: (event: Record<string, unknown>) => Promise<void>;
|
|
10
|
-
label?: string;
|
|
11
|
-
}
|
|
12
|
-
export interface InboxCatchUpResult {
|
|
13
|
-
scopes: number;
|
|
14
|
-
messagesDelivered: number;
|
|
15
|
-
resourceEvents: number;
|
|
16
|
-
acked: number;
|
|
17
|
-
truncated: boolean;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Pull-based recovery after reconnect (ZIG-434 / ZIG-446 / ZIG-454).
|
|
21
|
-
* Inbox → read message deltas → deliver → ack. Push remains a hint;
|
|
22
|
-
* this path survives backend restarts that drop in-memory pending queues.
|
|
23
|
-
*/
|
|
24
|
-
export declare function runInboxCatchUp(deps: InboxCatchUpDeps): Promise<InboxCatchUpResult>;
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import 'dotenv/config';
|
|
2
|
-
export interface ZiggsConnectClientOptions {
|
|
3
|
-
operatorKey: string;
|
|
4
|
-
actAsAgentId?: string;
|
|
5
|
-
baseUrl?: string;
|
|
6
|
-
}
|
|
7
|
-
export interface ProxyParams {
|
|
8
|
-
connectionId: string;
|
|
9
|
-
grantId: string;
|
|
10
|
-
action: string;
|
|
11
|
-
payload?: unknown;
|
|
12
|
-
}
|
|
13
|
-
export declare class ZiggsConnectClient {
|
|
14
|
-
private operatorKey;
|
|
15
|
-
private actAsAgentId;
|
|
16
|
-
private baseUrl;
|
|
17
|
-
constructor({ operatorKey, actAsAgentId, baseUrl }: ZiggsConnectClientOptions);
|
|
18
|
-
/**
|
|
19
|
-
* Present a ConnectionGrant to the broker and perform a provider action.
|
|
20
|
-
* Returns the provider result — never the raw OAuth token.
|
|
21
|
-
*/
|
|
22
|
-
proxy({ connectionId, grantId, action, payload }: ProxyParams): Promise<unknown>;
|
|
23
|
-
/**
|
|
24
|
-
* List ConnectionGrants held by this agent for a connection.
|
|
25
|
-
* Requires actAsAgentId; filters server rows to holderId === agent.
|
|
26
|
-
*/
|
|
27
|
-
listGrants({ connectionId }: {
|
|
28
|
-
connectionId: string;
|
|
29
|
-
}): Promise<unknown[]>;
|
|
30
|
-
private _buildHeaders;
|
|
31
|
-
private _request;
|
|
32
|
-
private _get;
|
|
33
|
-
private _post;
|
|
34
|
-
}
|
|
35
|
-
export declare function createZiggsConnectClient(opts: ZiggsConnectClientOptions): ZiggsConnectClient;
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import 'dotenv/config';
|
|
2
|
-
function getDefaultBaseUrl() {
|
|
3
|
-
return process.env.BACKEND_URL || process.env.ZIGGS_BACKEND_URL || 'http://localhost:3000';
|
|
4
|
-
}
|
|
5
|
-
function parseError(text, fallback) {
|
|
6
|
-
if (!text)
|
|
7
|
-
return fallback;
|
|
8
|
-
try {
|
|
9
|
-
const parsed = JSON.parse(text);
|
|
10
|
-
return parsed['message'] || parsed['error'] || text;
|
|
11
|
-
}
|
|
12
|
-
catch {
|
|
13
|
-
return text;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
export class ZiggsConnectClient {
|
|
17
|
-
operatorKey;
|
|
18
|
-
actAsAgentId;
|
|
19
|
-
baseUrl;
|
|
20
|
-
constructor({ operatorKey, actAsAgentId, baseUrl }) {
|
|
21
|
-
if (!operatorKey || typeof operatorKey !== 'string') {
|
|
22
|
-
throw new Error('ZiggsConnectClient requires an operatorKey');
|
|
23
|
-
}
|
|
24
|
-
this.operatorKey = operatorKey;
|
|
25
|
-
this.actAsAgentId = actAsAgentId || null;
|
|
26
|
-
this.baseUrl = baseUrl || getDefaultBaseUrl();
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Present a ConnectionGrant to the broker and perform a provider action.
|
|
30
|
-
* Returns the provider result — never the raw OAuth token.
|
|
31
|
-
*/
|
|
32
|
-
async proxy({ connectionId, grantId, action, payload }) {
|
|
33
|
-
if (!connectionId)
|
|
34
|
-
throw new Error('proxy: connectionId is required');
|
|
35
|
-
if (!grantId)
|
|
36
|
-
throw new Error('proxy: grantId is required');
|
|
37
|
-
if (!action)
|
|
38
|
-
throw new Error('proxy: action is required');
|
|
39
|
-
if (!this.actAsAgentId) {
|
|
40
|
-
throw new Error('proxy: actAsAgentId is required — connection broker calls must impersonate the grant holder agent');
|
|
41
|
-
}
|
|
42
|
-
const res = (await this._post(`/connections/${encodeURIComponent(connectionId)}/proxy`, {
|
|
43
|
-
grantId,
|
|
44
|
-
action,
|
|
45
|
-
payload: payload ?? {},
|
|
46
|
-
}));
|
|
47
|
-
return res['result'] ?? res;
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* List ConnectionGrants held by this agent for a connection.
|
|
51
|
-
* Requires actAsAgentId; filters server rows to holderId === agent.
|
|
52
|
-
*/
|
|
53
|
-
async listGrants({ connectionId }) {
|
|
54
|
-
if (!connectionId)
|
|
55
|
-
throw new Error('listGrants: connectionId is required');
|
|
56
|
-
if (!this.actAsAgentId) {
|
|
57
|
-
throw new Error('listGrants: actAsAgentId is required — grants are scoped to the impersonated agent');
|
|
58
|
-
}
|
|
59
|
-
const res = (await this._get(`/connections/${encodeURIComponent(connectionId)}/grants`));
|
|
60
|
-
const grants = res['grants'] || [];
|
|
61
|
-
return grants.filter((g) => g['holderId'] === this.actAsAgentId);
|
|
62
|
-
}
|
|
63
|
-
_buildHeaders(extra = {}) {
|
|
64
|
-
const h = { Authorization: `Bearer ${this.operatorKey}`, ...extra };
|
|
65
|
-
if (this.actAsAgentId)
|
|
66
|
-
h['X-Agent-Id'] = this.actAsAgentId;
|
|
67
|
-
return h;
|
|
68
|
-
}
|
|
69
|
-
async _request(method, path, body) {
|
|
70
|
-
const init = {
|
|
71
|
-
method,
|
|
72
|
-
headers: this._buildHeaders(body !== undefined ? { 'content-type': 'application/json' } : {}),
|
|
73
|
-
};
|
|
74
|
-
if (body !== undefined)
|
|
75
|
-
init.body = JSON.stringify(body);
|
|
76
|
-
const response = await fetch(`${this.baseUrl}${path}`, init);
|
|
77
|
-
const text = await response.text();
|
|
78
|
-
if (!response.ok) {
|
|
79
|
-
const err = new Error(parseError(text, `HTTP ${response.status}`));
|
|
80
|
-
err.status = response.status;
|
|
81
|
-
err.body = text;
|
|
82
|
-
throw err;
|
|
83
|
-
}
|
|
84
|
-
return text ? JSON.parse(text) : null;
|
|
85
|
-
}
|
|
86
|
-
_get(path) {
|
|
87
|
-
return this._request('GET', path, undefined);
|
|
88
|
-
}
|
|
89
|
-
_post(path, body) {
|
|
90
|
-
return this._request('POST', path, body ?? {});
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
export function createZiggsConnectClient(opts) {
|
|
94
|
-
return new ZiggsConnectClient(opts);
|
|
95
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import 'dotenv/config';
|
|
2
|
-
export interface ZiggsContextClientOptions {
|
|
3
|
-
operatorKey: string;
|
|
4
|
-
actAsAgentId?: string;
|
|
5
|
-
baseUrl?: string;
|
|
6
|
-
}
|
|
7
|
-
export interface ContextReachDescriptor {
|
|
8
|
-
grantId: string;
|
|
9
|
-
scope: {
|
|
10
|
-
kind: 'chat' | 'agreement' | 'org';
|
|
11
|
-
id: string;
|
|
12
|
-
};
|
|
13
|
-
temporal: 'from-now' | 'from-start';
|
|
14
|
-
watermarkAt: string;
|
|
15
|
-
expiresAt: string | null;
|
|
16
|
-
parentGrantId: string | null;
|
|
17
|
-
createdAt: string;
|
|
18
|
-
}
|
|
19
|
-
export interface DelegateContextGrantParams {
|
|
20
|
-
parentGrantId: string;
|
|
21
|
-
holderId: string;
|
|
22
|
-
scope: {
|
|
23
|
-
kind: 'chat' | 'agreement' | 'org';
|
|
24
|
-
id: string;
|
|
25
|
-
};
|
|
26
|
-
temporal: 'from-now' | 'from-start';
|
|
27
|
-
expiresAt?: string | null;
|
|
28
|
-
watermarkAt?: string;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* HTTP client for context grants (ZIG-413).
|
|
32
|
-
* Presentation model (signed bytes vs grant id) is intentionally not baked in here.
|
|
33
|
-
*/
|
|
34
|
-
export declare class ZiggsContextClient {
|
|
35
|
-
private operatorKey;
|
|
36
|
-
private actAsAgentId;
|
|
37
|
-
private baseUrl;
|
|
38
|
-
constructor({ operatorKey, actAsAgentId, baseUrl }: ZiggsContextClientOptions);
|
|
39
|
-
/**
|
|
40
|
-
* Scope descriptors for what this agent can reach (no content). GET
|
|
41
|
-
* /context/discovery was retired (ZIG-648); reach is now the agent's live
|
|
42
|
-
* context grants from the unified GET /grants, mapped back to the reach shape
|
|
43
|
-
* (temporal/watermark ride in caveats). Follows the cursor for completeness.
|
|
44
|
-
*/
|
|
45
|
-
discover(): Promise<ContextReachDescriptor[]>;
|
|
46
|
-
/** Holder delegates a narrower child grant. */
|
|
47
|
-
delegate(params: DelegateContextGrantParams): Promise<unknown>;
|
|
48
|
-
private requireActAsAgent;
|
|
49
|
-
private _buildHeaders;
|
|
50
|
-
private _request;
|
|
51
|
-
private _get;
|
|
52
|
-
private _post;
|
|
53
|
-
}
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import 'dotenv/config';
|
|
2
|
-
function getDefaultBaseUrl() {
|
|
3
|
-
return process.env.BACKEND_URL || process.env.ZIGGS_BACKEND_URL || 'http://localhost:3000';
|
|
4
|
-
}
|
|
5
|
-
function parseError(text, fallback) {
|
|
6
|
-
if (!text)
|
|
7
|
-
return fallback;
|
|
8
|
-
try {
|
|
9
|
-
const parsed = JSON.parse(text);
|
|
10
|
-
return parsed['message'] || parsed['error'] || text;
|
|
11
|
-
}
|
|
12
|
-
catch {
|
|
13
|
-
return text;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* HTTP client for context grants (ZIG-413).
|
|
18
|
-
* Presentation model (signed bytes vs grant id) is intentionally not baked in here.
|
|
19
|
-
*/
|
|
20
|
-
export class ZiggsContextClient {
|
|
21
|
-
operatorKey;
|
|
22
|
-
actAsAgentId;
|
|
23
|
-
baseUrl;
|
|
24
|
-
constructor({ operatorKey, actAsAgentId, baseUrl }) {
|
|
25
|
-
if (!operatorKey || typeof operatorKey !== 'string') {
|
|
26
|
-
throw new Error('ZiggsContextClient requires an operatorKey');
|
|
27
|
-
}
|
|
28
|
-
this.operatorKey = operatorKey;
|
|
29
|
-
this.actAsAgentId = actAsAgentId || null;
|
|
30
|
-
this.baseUrl = baseUrl || getDefaultBaseUrl();
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Scope descriptors for what this agent can reach (no content). GET
|
|
34
|
-
* /context/discovery was retired (ZIG-648); reach is now the agent's live
|
|
35
|
-
* context grants from the unified GET /grants, mapped back to the reach shape
|
|
36
|
-
* (temporal/watermark ride in caveats). Follows the cursor for completeness.
|
|
37
|
-
*/
|
|
38
|
-
async discover() {
|
|
39
|
-
this.requireActAsAgent('discover');
|
|
40
|
-
const out = [];
|
|
41
|
-
let cursor;
|
|
42
|
-
for (let page = 0; page < 50; page++) {
|
|
43
|
-
const q = new URLSearchParams();
|
|
44
|
-
for (const k of ['chat', 'agreement', 'org'])
|
|
45
|
-
q.append('scopeKind', k);
|
|
46
|
-
q.set('health', 'active');
|
|
47
|
-
if (cursor)
|
|
48
|
-
q.set('cursor', cursor);
|
|
49
|
-
const res = (await this._get(`/grants?${q.toString()}`));
|
|
50
|
-
const items = res['items'] || [];
|
|
51
|
-
for (const g of items) {
|
|
52
|
-
const caveats = g['caveats'] || [];
|
|
53
|
-
const caveat = (t) => caveats.find((c) => c.type === t)?.value;
|
|
54
|
-
const scope = g['scope'];
|
|
55
|
-
out.push({
|
|
56
|
-
grantId: g['grantId'],
|
|
57
|
-
scope: { kind: scope.kind, id: scope.id },
|
|
58
|
-
temporal: caveat('temporal') ?? 'from-now',
|
|
59
|
-
watermarkAt: caveat('watermark_at') ?? '',
|
|
60
|
-
expiresAt: g['expiresAt'] ?? null,
|
|
61
|
-
parentGrantId: g['parentGrantId'] ?? null,
|
|
62
|
-
createdAt: g['createdAt'],
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
const next = res['nextCursor'];
|
|
66
|
-
if (!next)
|
|
67
|
-
break;
|
|
68
|
-
cursor = next;
|
|
69
|
-
}
|
|
70
|
-
return out;
|
|
71
|
-
}
|
|
72
|
-
/** Holder delegates a narrower child grant. */
|
|
73
|
-
async delegate(params) {
|
|
74
|
-
this.requireActAsAgent('delegate');
|
|
75
|
-
if (!params.parentGrantId)
|
|
76
|
-
throw new Error('delegate: parentGrantId is required');
|
|
77
|
-
if (!params.holderId)
|
|
78
|
-
throw new Error('delegate: holderId is required');
|
|
79
|
-
if (!params.scope?.kind || !params.scope?.id) {
|
|
80
|
-
throw new Error('delegate: scope { kind, id } is required');
|
|
81
|
-
}
|
|
82
|
-
return this._post(`/context/grants/${encodeURIComponent(params.parentGrantId)}/delegate`, {
|
|
83
|
-
holderId: params.holderId,
|
|
84
|
-
scope: params.scope,
|
|
85
|
-
temporal: params.temporal,
|
|
86
|
-
expiresAt: params.expiresAt,
|
|
87
|
-
watermarkAt: params.watermarkAt,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
requireActAsAgent(method) {
|
|
91
|
-
if (!this.actAsAgentId) {
|
|
92
|
-
throw new Error(`${method}: actAsAgentId is required — context grant calls must impersonate the grant holder agent`);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
_buildHeaders(extra = {}) {
|
|
96
|
-
const h = { Authorization: `Bearer ${this.operatorKey}`, ...extra };
|
|
97
|
-
if (this.actAsAgentId)
|
|
98
|
-
h['X-Agent-Id'] = this.actAsAgentId;
|
|
99
|
-
return h;
|
|
100
|
-
}
|
|
101
|
-
async _request(method, path, body) {
|
|
102
|
-
const init = {
|
|
103
|
-
method,
|
|
104
|
-
headers: this._buildHeaders(body !== undefined ? { 'content-type': 'application/json' } : {}),
|
|
105
|
-
};
|
|
106
|
-
if (body !== undefined)
|
|
107
|
-
init.body = JSON.stringify(body);
|
|
108
|
-
const res = await fetch(`${this.baseUrl}${path}`, init);
|
|
109
|
-
const text = await res.text();
|
|
110
|
-
if (!res.ok) {
|
|
111
|
-
const err = new Error(parseError(text, `${method} ${path} failed (${res.status})`));
|
|
112
|
-
err.status = res.status;
|
|
113
|
-
err.body = text;
|
|
114
|
-
throw err;
|
|
115
|
-
}
|
|
116
|
-
return text ? JSON.parse(text) : {};
|
|
117
|
-
}
|
|
118
|
-
_get(path) {
|
|
119
|
-
return this._request('GET', path, undefined);
|
|
120
|
-
}
|
|
121
|
-
_post(path, body) {
|
|
122
|
-
return this._request('POST', path, body);
|
|
123
|
-
}
|
|
124
|
-
}
|