@ziggs-ai/api-client 0.8.0 → 0.9.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 +10 -0
- package/dist/capabilities/artifacts.js +11 -10
- package/dist/capabilities/context.js +29 -11
- package/dist/capabilities/grants.d.ts +7 -0
- package/dist/capabilities/grants.js +9 -2
- package/dist/capabilities/types.d.ts +1 -0
- package/dist/capabilities/types.js +4 -2
- package/dist/http/AgreementClient.d.ts +35 -16
- package/dist/http/AgreementClient.js +68 -10
- package/dist/http/ArtifactsClient.d.ts +5 -1
- package/dist/http/ArtifactsClient.js +17 -5
- package/dist/http/ChatClient.js +3 -0
- package/dist/http/ContextReadClient.d.ts +30 -3
- package/dist/http/ContextReadClient.js +58 -1
- package/dist/http/InboxClient.d.ts +32 -3
- package/dist/http/MarketplaceClient.d.ts +0 -1
- package/dist/http/MarketplaceClient.js +3 -0
- package/dist/http/TaskClient.d.ts +8 -0
- package/dist/http/TaskClient.js +3 -0
- package/dist/http/index.d.ts +3 -3
- package/dist/http/index.js +1 -1
- package/dist/http/operatorHeaders.d.ts +7 -1
- package/dist/http/operatorHeaders.js +8 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/types.d.ts +55 -0
- package/dist/types.js +19 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -97,6 +97,16 @@ await inbox.ack([{ kind: 'chat', id: '<chatId>', upTo: env.asOf }]);
|
|
|
97
97
|
|
|
98
98
|
Other HTTP clients: `ChatClient`, agreement/marketplace helpers — see `src/http/index.ts`.
|
|
99
99
|
|
|
100
|
+
### Persona wire shapes (ZIG-1137)
|
|
101
|
+
|
|
102
|
+
Cross-org inbox and chat payloads mask counterparties behind presentation faces:
|
|
103
|
+
|
|
104
|
+
- Inbox connection requests expose `requesterRef` (`psn_*`) plus display name/org — not `requesterAgentId`.
|
|
105
|
+
- Message/roster rows may carry `presentation` (`ref`, `persona`, `mode`, and `subject` only when entitled). Masked senders omit `underAgreementId` / `presentedAs`.
|
|
106
|
+
- Opaque `psn_*` / `rpb_*` refs are **not** account ids. Do not use them for agent lookup, wake, or payment parties. Chat sends may echo an `rpb_*` as `receiverId` — the backend resolves it in-room.
|
|
107
|
+
|
|
108
|
+
Helpers: `isPersonaRef`, `isRoomPresentationRef`, `isOpaquePresentationRef`.
|
|
109
|
+
|
|
100
110
|
#### Agent Search Client
|
|
101
111
|
|
|
102
112
|
Search for agents:
|
|
@@ -45,7 +45,7 @@ export const recordArtifactCapability = {
|
|
|
45
45
|
chatId: { type: 'string', description: 'Optional chat scope' },
|
|
46
46
|
agreementId: {
|
|
47
47
|
type: 'string',
|
|
48
|
-
description: 'Optional agreement scope — for a hire deliverable, prefer this.
|
|
48
|
+
description: 'Optional agreement scope — for a hire deliverable, prefer this. Mutually exclusive with chatId: pass one, not both.',
|
|
49
49
|
},
|
|
50
50
|
taskId: {
|
|
51
51
|
type: 'string',
|
|
@@ -63,12 +63,13 @@ export const recordArtifactCapability = {
|
|
|
63
63
|
needsAgentId: true,
|
|
64
64
|
handler: async (args, env) => {
|
|
65
65
|
const agreementId = args['agreementId'];
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
|
|
66
|
+
// Both containers is refused, not resolved. This used to drop chatId and
|
|
67
|
+
// call agreement the winner — two answers to one call, and the silent one
|
|
68
|
+
// was worse: the artifact landed somewhere the caller had just been told it
|
|
69
|
+
// would also appear. The refusal itself lives in ArtifactsClient, the one
|
|
70
|
+
// gate every writer goes through, so this surface cannot drift from the
|
|
71
|
+
// others by wording its own verdict (ZIG-1075).
|
|
72
|
+
const chatId = args['chatId'];
|
|
72
73
|
// ZIG-1037: no scope is legal. The throw that used to live here ("Pass
|
|
73
74
|
// chatId or agreementId") is the exact failure this ticket removed — it cost
|
|
74
75
|
// a live agent a turn mid-delivery for naming no container, when the record
|
|
@@ -80,7 +81,7 @@ export const recordArtifactCapability = {
|
|
|
80
81
|
const contentType = args['content_type'];
|
|
81
82
|
const taskId = args['taskId'];
|
|
82
83
|
const creds = fullCreds(env);
|
|
83
|
-
const { artifactId } = await new ArtifactsClient(creds.operatorKey, creds.agentId).writeStrict({
|
|
84
|
+
const { artifactId } = await new ArtifactsClient(creds.operatorKey, creds.agentId, creds.laneId).writeStrict({
|
|
84
85
|
text: args['text'],
|
|
85
86
|
visibility,
|
|
86
87
|
chatId,
|
|
@@ -128,7 +129,7 @@ export const listArtifactsCapability = {
|
|
|
128
129
|
needsAgentId: true,
|
|
129
130
|
handler: async (args, env) => {
|
|
130
131
|
const creds = fullCreds(env);
|
|
131
|
-
return new ArtifactsClient(creds.operatorKey, creds.agentId).list({ authoredBy: 'me' }, {
|
|
132
|
+
return new ArtifactsClient(creds.operatorKey, creds.agentId, creds.laneId).list({ authoredBy: 'me' }, {
|
|
132
133
|
after: args['after'],
|
|
133
134
|
limit: typeof args['limit'] === 'number' ? args['limit'] : undefined,
|
|
134
135
|
});
|
|
@@ -248,7 +249,7 @@ export const attachArtifactCapability = {
|
|
|
248
249
|
throw new Error('role must be input or output');
|
|
249
250
|
}
|
|
250
251
|
const creds = fullCreds(env);
|
|
251
|
-
const client = new ArtifactsClient(creds.operatorKey, creds.agentId);
|
|
252
|
+
const client = new ArtifactsClient(creds.operatorKey, creds.agentId, creds.laneId);
|
|
252
253
|
if (chatId) {
|
|
253
254
|
await client.attachToChat(artifactId, chatId);
|
|
254
255
|
return {
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
import { ContextReadClient } from '../http/ContextReadClient.js';
|
|
1
|
+
import { ContextReadClient, CONTEXT_READ_TYPES, CONTEXT_READ_VIA, parseVia, viaHint, } from '../http/ContextReadClient.js';
|
|
2
2
|
import { ContextGrantsClient, } from '../http/ContextGrantsClient.js';
|
|
3
3
|
import { ContextDiscoveryClient } from '../http/ContextDiscoveryClient.js';
|
|
4
4
|
import { grantCaveat, CONTEXT_GRANT_SCOPE_KINDS, } from '../http/grants.js';
|
|
5
5
|
import { fetchMyOrgs, resolveOrgSelector } from '../http/OrgsClient.js';
|
|
6
6
|
import { fullCreds } from './types.js';
|
|
7
|
-
const CONTEXT_READ_TYPES = [
|
|
8
|
-
'messages',
|
|
9
|
-
'artifacts',
|
|
10
|
-
'agreements',
|
|
11
|
-
'tasks',
|
|
12
|
-
];
|
|
13
7
|
// ZIG-1037: `artifact` joined the context rail. Delegating an artifact grant
|
|
14
8
|
// onward works (same kind, same id — an exact re-grant); narrowing a container
|
|
15
9
|
// grant DOWN to an artifact inside it is deliberately not supported yet.
|
|
@@ -49,12 +43,26 @@ export async function resolveOrgScopeId(env, scopeId) {
|
|
|
49
43
|
const lister = env.surface === 'mcp' ? 'ziggs_org_list' : 'your org list';
|
|
50
44
|
throw new Error(`No org named "${scopeId}" in your memberships — use ${lister} to see them, or pass the org id.`);
|
|
51
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Which `via` each read type accepts, rendered for the tool text. Generated from
|
|
48
|
+
* CONTEXT_READ_VIA so the pairing an agent is told about and the pairing the
|
|
49
|
+
* call actually accepts are one statement — the prose that used to enumerate
|
|
50
|
+
* these by hand had already drifted from the server's list.
|
|
51
|
+
*/
|
|
52
|
+
const VIA_BY_TYPE = CONTEXT_READ_TYPES.map((t) => `${t} via ${viaHint(t)}`).join('; ');
|
|
53
|
+
/**
|
|
54
|
+
* The one fact about `artifact:<id>` that both surfaces must state: it is how you
|
|
55
|
+
* reach an artifact no container can return. Shared for the same reason the
|
|
56
|
+
* pairings above are generated — hand-copied prose is what drifted.
|
|
57
|
+
*/
|
|
58
|
+
const ARTIFACT_VIA_NOTE = 'via=artifact:<id> is a point read of one named artifact and the ONLY way to ' +
|
|
59
|
+
'read one attached to no chat, agreement or task';
|
|
52
60
|
export const contextReadCapability = {
|
|
53
61
|
key: 'context_read',
|
|
54
62
|
names: { sdk: 'context_read', mcp: 'ziggs_context_read' },
|
|
55
63
|
descriptions: {
|
|
56
|
-
sdk:
|
|
57
|
-
mcp:
|
|
64
|
+
sdk: `Read the contents of a scope you already hold a grant for: ${CONTEXT_READ_TYPES.join(' | ')}. Each type reads through its own entries — ${VIA_BY_TYPE}. Use grant_list first to see which scopes your grants cover, then read through any of them. ${ARTIFACT_VIA_NOTE}. Cursored; all access is grant-fenced server-side.`,
|
|
65
|
+
mcp: `Read the contents of a scope you already hold: ${CONTEXT_READ_TYPES.join(' | ')} (the type param). Each type accepts its own via entries — ${VIA_BY_TYPE} — and any other pairing is refused. ${ARTIFACT_VIA_NOTE} (e.g. an artifact someone shared with you; ziggs_grant_list scopeKind=artifact shows those). 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.`,
|
|
58
66
|
},
|
|
59
67
|
annotation: 'read-only',
|
|
60
68
|
params: {
|
|
@@ -67,7 +75,7 @@ export const contextReadCapability = {
|
|
|
67
75
|
via: {
|
|
68
76
|
type: 'string',
|
|
69
77
|
required: true,
|
|
70
|
-
description:
|
|
78
|
+
description: `Scope entry you hold, as <kind>:<id>. Accepted per type — ${VIA_BY_TYPE}.`,
|
|
71
79
|
},
|
|
72
80
|
cursor: { type: 'string', description: 'Opaque cursor from a prior nextCursor to page' },
|
|
73
81
|
after: { type: 'string', description: 'ISO timestamp for forward-delta (messages/artifacts)' },
|
|
@@ -93,11 +101,21 @@ export const contextReadCapability = {
|
|
|
93
101
|
const via = args['via'];
|
|
94
102
|
if (!via)
|
|
95
103
|
throw new Error('via is required');
|
|
104
|
+
// Same verdict the server gives, given at the call site so the reason
|
|
105
|
+
// arrives with the mistake. Never a softer one: a client that "fixes" an
|
|
106
|
+
// argument the server would reject is a second answer to one call.
|
|
107
|
+
const parsed = parseVia(via);
|
|
108
|
+
if (!parsed) {
|
|
109
|
+
throw new Error(`via must be <kind>:<id> — one of ${viaHint(type)} for type=${type}`);
|
|
110
|
+
}
|
|
111
|
+
if (!CONTEXT_READ_VIA[type].includes(parsed.kind)) {
|
|
112
|
+
throw new Error(`${type} reads accept via=${viaHint(type)} — not ${parsed.kind}:<id>`);
|
|
113
|
+
}
|
|
96
114
|
const direction = args['direction'];
|
|
97
115
|
if (direction !== undefined && direction !== 'forward') {
|
|
98
116
|
throw new Error('direction must be "forward"');
|
|
99
117
|
}
|
|
100
|
-
return new ContextReadClient(creds.operatorKey, creds.agentId).read(type, {
|
|
118
|
+
return new ContextReadClient(creds.operatorKey, creds.agentId, undefined, creds.laneId).read(type, {
|
|
101
119
|
via,
|
|
102
120
|
cursor: args['cursor'],
|
|
103
121
|
after: args['after'],
|
|
@@ -4,6 +4,13 @@ import { type CapabilityDefinition } from './types.js';
|
|
|
4
4
|
* (context chat/agreement/org/artifact, connection, wallet), holder-scoped,
|
|
5
5
|
* cross-session. `unreadableRails` comes from the backend (ZIG-956) so a short
|
|
6
6
|
* list is never presented as complete when the key can't read a rail.
|
|
7
|
+
*
|
|
8
|
+
* ZIG-1088 — HOLD, not reach. `GET /grants` queries three row collections and
|
|
9
|
+
* nothing else; the implicit arms in AccessService (authorship, chat
|
|
10
|
+
* membership, agreement party, org membership) leave no row behind, so a reader
|
|
11
|
+
* can be entitled to something this list will never mention. The description
|
|
12
|
+
* says so, because the old "the single answer" wording was read as completeness
|
|
13
|
+
* and an empty list as "no access".
|
|
7
14
|
*/
|
|
8
15
|
export declare const listGrantsCapability: CapabilityDefinition;
|
|
9
16
|
export declare const GRANTS_CAPABILITIES: CapabilityDefinition[];
|
|
@@ -26,13 +26,20 @@ function parseScopeKinds(raw) {
|
|
|
26
26
|
* (context chat/agreement/org/artifact, connection, wallet), holder-scoped,
|
|
27
27
|
* cross-session. `unreadableRails` comes from the backend (ZIG-956) so a short
|
|
28
28
|
* list is never presented as complete when the key can't read a rail.
|
|
29
|
+
*
|
|
30
|
+
* ZIG-1088 — HOLD, not reach. `GET /grants` queries three row collections and
|
|
31
|
+
* nothing else; the implicit arms in AccessService (authorship, chat
|
|
32
|
+
* membership, agreement party, org membership) leave no row behind, so a reader
|
|
33
|
+
* can be entitled to something this list will never mention. The description
|
|
34
|
+
* says so, because the old "the single answer" wording was read as completeness
|
|
35
|
+
* and an empty list as "no access".
|
|
29
36
|
*/
|
|
30
37
|
export const listGrantsCapability = {
|
|
31
38
|
key: 'grant_list',
|
|
32
39
|
names: { sdk: 'grant_list', mcp: 'ziggs_grant_list' },
|
|
33
40
|
descriptions: {
|
|
34
|
-
sdk: 'List every grant this agent holds across all rails in one call — context (chat/agreement/org/artifact), connection, and wallet — as canonical grants (grantId, scope, caveats, expiresAt, health; no message/artifact content or credentials).
|
|
35
|
-
mcp: 'List every grant this delegate holds across all rails in one call — context (chat/agreement/org/artifact), connection, and wallet — as canonical grants (grantId, scope, caveats, expiresAt, health; no content or credentials).
|
|
41
|
+
sdk: 'List every grant this agent holds across all rails in one call — context (chat/agreement/org/artifact), connection, and wallet — as canonical grants (grantId, scope, caveats, expiresAt, health; no message/artifact content or credentials). Holder-scoped and cross-session. It answers "what grants do I HOLD?", which is narrower than "what can I reach?": reach you have by authoring something, by sitting in a chat, by being a party to an agreement, or through your org is not a grant row and never appears here — so an empty list means "no grants", never "no access". 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 answer "what can I reach?" instead, use context_expand_reach to enumerate a scope, or context_read to read through a grant.',
|
|
42
|
+
mcp: 'List every grant this delegate holds across all rails in one call — context (chat/agreement/org/artifact), connection, and wallet — as canonical grants (grantId, scope, caveats, expiresAt, health; no content or credentials). Holder-scoped and cross-session. It answers "what grants do I HOLD?", which is narrower than "what can I reach?": reach you have by authoring something, by sitting in a chat, by being a party to an agreement, or through your org is not a grant row and never appears here — so an empty list means "no grants", never "no access". 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. To answer "what can I reach?" instead, use ziggs_context_expand_reach to enumerate a scope, or pass a grantId to ziggs_context_read to pin a specific grant.',
|
|
36
43
|
},
|
|
37
44
|
annotation: 'read-only',
|
|
38
45
|
params: {
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/** Creds for impersonated calls; adapters guarantee agentId when needsAgentId. */
|
|
2
2
|
export function fullCreds(env) {
|
|
3
|
-
const { operatorKey, agentId } = env.creds;
|
|
3
|
+
const { operatorKey, agentId, laneId } = env.creds;
|
|
4
4
|
if (!operatorKey)
|
|
5
5
|
throw new Error('operatorKey missing from tool context');
|
|
6
6
|
if (!agentId)
|
|
7
7
|
throw new Error('agentId missing from tool context');
|
|
8
|
-
|
|
8
|
+
// ZIG-1092 — the lane rides along so every Creds-based client sends
|
|
9
|
+
// X-Ziggs-Lane without each capability having to remember to.
|
|
10
|
+
return { operatorKey, agentId, ...(laneId ? { laneId } : {}) };
|
|
9
11
|
}
|
|
10
12
|
/**
|
|
11
13
|
* Re-throw a client error with a capability-level prefix, preserving the HTTP
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
2
|
import { type Creds, type Agreement, type EngagementKind, type BroadcastAudience } from '../types.js';
|
|
3
|
-
export type PlanReviewTiming = 'with_proposal' | 'before_execution';
|
|
4
3
|
/**
|
|
5
4
|
* Shared proposal terms. When `engagementKind` is omitted the server defaults to `service`.
|
|
6
5
|
* For an open buyer-broadcast quest, set `proposedTo` to a broadcast sentinel —
|
|
@@ -22,7 +21,6 @@ export interface ProposeTerms {
|
|
|
22
21
|
billing?: 'total' | 'per_task';
|
|
23
22
|
agreementDescription?: string;
|
|
24
23
|
parentAgreementId?: string;
|
|
25
|
-
parentTaskId?: string;
|
|
26
24
|
/**
|
|
27
25
|
* Who does the work. Required on direct proposals (your own id = you offer;
|
|
28
26
|
* the proposedTo id = you commission the recipient); forbidden on
|
|
@@ -30,9 +28,6 @@ export interface ProposeTerms {
|
|
|
30
28
|
* side — there is no payer input.
|
|
31
29
|
*/
|
|
32
30
|
providerId?: string;
|
|
33
|
-
plan?: unknown;
|
|
34
|
-
planReviewTiming?: PlanReviewTiming;
|
|
35
|
-
requireMidWorkPlanAck?: boolean;
|
|
36
31
|
/** Defaults to `service` on the server when omitted. Set `hire` for representation contracts. */
|
|
37
32
|
engagementKind?: EngagementKind;
|
|
38
33
|
idempotencyKey?: string;
|
|
@@ -67,16 +62,12 @@ export interface DelegateAgreementData {
|
|
|
67
62
|
executorId: string;
|
|
68
63
|
chatId: string;
|
|
69
64
|
parentAgreementId: string;
|
|
70
|
-
parentTaskId?: string;
|
|
71
65
|
price?: number;
|
|
72
66
|
lifecycle?: string;
|
|
73
67
|
expiresAt?: string;
|
|
74
68
|
maxExecutions?: number;
|
|
75
69
|
agreementDescription?: string;
|
|
76
70
|
payerId?: string;
|
|
77
|
-
plan?: unknown;
|
|
78
|
-
planReviewTiming?: PlanReviewTiming;
|
|
79
|
-
requireMidWorkPlanAck?: boolean;
|
|
80
71
|
idempotencyKey?: string;
|
|
81
72
|
}
|
|
82
73
|
export declare function delegateAgreement(proposalData: DelegateAgreementData, creds: Creds): Promise<Agreement>;
|
|
@@ -91,7 +82,29 @@ export declare function delegateAgreement(proposalData: DelegateAgreementData, c
|
|
|
91
82
|
export declare function respondToAgreement(agreementId: string, action: 'approve' | 'reject', creds: Creds, opts?: {
|
|
92
83
|
ownerUserId?: string | null;
|
|
93
84
|
agreement?: Agreement | null;
|
|
85
|
+
/** Where to send the human when the decision is theirs (ZIG-1087). */
|
|
86
|
+
appUrl?: string;
|
|
94
87
|
}): Promise<Agreement>;
|
|
88
|
+
/**
|
|
89
|
+
* The approval facts a "who decides this?" question is answered from: the party
|
|
90
|
+
* ids still owing a decision, plus the named responder slot.
|
|
91
|
+
*
|
|
92
|
+
* Split out (ZIG-1087) so the inbox card and the respond call answer that
|
|
93
|
+
* question with ONE rule. The card had no rule at all — it offered the respond
|
|
94
|
+
* tool for every pending proposal, including the ones only the human can
|
|
95
|
+
* answer, and the agent found out by 403.
|
|
96
|
+
*/
|
|
97
|
+
export interface PendingApprovalFacts {
|
|
98
|
+
/** Party ids with a pending entry on the approvals ledger. */
|
|
99
|
+
pendingPartyIds: readonly string[];
|
|
100
|
+
/** The direct responder slot, when the proposal names one. */
|
|
101
|
+
proposedTo?: string | null;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Which of `candidateIds` holds the pending approval slot, first match wins —
|
|
105
|
+
* so pass them in preference order (agent id before owner principal).
|
|
106
|
+
*/
|
|
107
|
+
export declare function resolvePendingApprovalPartyId(facts: PendingApprovalFacts, candidateIds: ReadonlyArray<string | null | undefined>): string | null;
|
|
95
108
|
/**
|
|
96
109
|
* ZIG-524 — resolve which approvals.partyId the current operator may submit.
|
|
97
110
|
* Checks pending ledger entries against impersonated agent id and owner principal.
|
|
@@ -116,10 +129,6 @@ export interface CounterAgreementData {
|
|
|
116
129
|
lifecycle?: string;
|
|
117
130
|
maxExecutions?: number;
|
|
118
131
|
description?: string;
|
|
119
|
-
/** Override plan `{ steps: [...] }`; omit to copy from original proposal task */
|
|
120
|
-
plan?: unknown;
|
|
121
|
-
planReviewTiming?: PlanReviewTiming;
|
|
122
|
-
requireMidWorkPlanAck?: boolean;
|
|
123
132
|
}
|
|
124
133
|
export declare function counterAgreement(agreementId: string, counter: CounterAgreementData, creds: Creds): Promise<Agreement>;
|
|
125
134
|
export declare function getAgreementStatus(agreementId: string, creds: Creds): Promise<unknown | null>;
|
|
@@ -185,7 +194,15 @@ export declare function claimAgreement(agreementId: string, creds: Creds): Promi
|
|
|
185
194
|
ok: boolean;
|
|
186
195
|
agreement: Agreement;
|
|
187
196
|
}>;
|
|
188
|
-
|
|
197
|
+
/**
|
|
198
|
+
* Link types a caller may ASK for — the server's `CALLER_LINK_TYPES`, not its
|
|
199
|
+
* full `LINK_TYPES`. `space` is deliberately absent: an agreement space gets
|
|
200
|
+
* exactly one system-minted room, and letting a request name that type would
|
|
201
|
+
* burn the root's single slot on an unrelated chat. Shorter than the server
|
|
202
|
+
* union on purpose, which is why this list carries the reason with it.
|
|
203
|
+
*/
|
|
204
|
+
export declare const CHAT_LINK_TYPES: readonly ["origin", "mention", "delegation", "join"];
|
|
205
|
+
export type ChatLinkType = (typeof CHAT_LINK_TYPES)[number];
|
|
189
206
|
export declare function linkAgreementToChat(agreementId: string, chatId: string, linkType: ChatLinkType | undefined, creds: Creds): Promise<unknown | null>;
|
|
190
207
|
export declare function getChatsForAgreement(agreementId: string, creds: Creds): Promise<unknown[]>;
|
|
191
208
|
export declare function joinAgreement(agreementId: string, creds: Creds): Promise<{
|
|
@@ -193,10 +210,12 @@ export declare function joinAgreement(agreementId: string, creds: Creds): Promis
|
|
|
193
210
|
agentId: string | null;
|
|
194
211
|
isNew: boolean;
|
|
195
212
|
}>;
|
|
196
|
-
export
|
|
213
|
+
export declare const ARTIFACT_LINK_TYPES: readonly ["produced", "referenced"];
|
|
214
|
+
export type ArtifactLinkType = (typeof ARTIFACT_LINK_TYPES)[number];
|
|
197
215
|
export declare function linkArtifactToAgreement(agreementId: string, artifactId: string, linkType: ArtifactLinkType | undefined, creds: Creds): Promise<unknown | null>;
|
|
198
216
|
export declare function getArtifactsForAgreement(agreementId: string, creds: Creds): Promise<unknown[]>;
|
|
199
|
-
export
|
|
217
|
+
export declare const AGREEMENT_USER_ROLES: readonly ["payer", "provider", "participant", "observer"];
|
|
218
|
+
export type UserRole = (typeof AGREEMENT_USER_ROLES)[number];
|
|
200
219
|
export declare function linkUserToAgreement(agreementId: string, userId: string, role: UserRole, creds: Creds): Promise<unknown | null>;
|
|
201
220
|
export declare function getUsersForAgreement(agreementId: string, creds: Creds): Promise<unknown[]>;
|
|
202
221
|
export declare class AgreementClient {
|
|
@@ -14,6 +14,9 @@ function buildHeaders(creds) {
|
|
|
14
14
|
'content-type': 'application/json',
|
|
15
15
|
Authorization: `Bearer ${creds.operatorKey}`,
|
|
16
16
|
'X-Agent-Id': creds.agentId,
|
|
17
|
+
// ZIG-1092 — the wake's lane, so the backend can fence this call to the
|
|
18
|
+
// engagement it belongs to rather than the agent's whole authority.
|
|
19
|
+
...(creds.laneId ? { 'X-Ziggs-Lane': creds.laneId } : {}),
|
|
17
20
|
};
|
|
18
21
|
}
|
|
19
22
|
function assertCreds(creds, op) {
|
|
@@ -135,28 +138,54 @@ export async function respondToAgreement(agreementId, action, creds, opts = {})
|
|
|
135
138
|
else if (!partyId) {
|
|
136
139
|
throw new Error(`No pending approval entry for this operator on agreement ${agreementId}`);
|
|
137
140
|
}
|
|
141
|
+
// ZIG-1087 — the slot resolved to the principal, not to us. Every call from
|
|
142
|
+
// this client impersonates `creds.agentId` (X-Agent-Id on every request), and
|
|
143
|
+
// `PUT /approvals/:partyId` takes a decision only from the party itself, so
|
|
144
|
+
// sending this would 403 with "You can only submit your own approval" —
|
|
145
|
+
// a condition, from which the caller cannot tell that no retry will ever
|
|
146
|
+
// work. Withholding consent from delegates is deliberate; being silent about
|
|
147
|
+
// it is not.
|
|
148
|
+
if (partyId && partyId !== creds.agentId) {
|
|
149
|
+
throw new Error(`Agreement ${agreementId} is waiting on ${partyId} — your principal, not you. ` +
|
|
150
|
+
`Consent is the human's to give: a delegate can submit its own approval slot and never its principal's, ` +
|
|
151
|
+
`so there is nothing to retry here and no tool that changes it. ` +
|
|
152
|
+
`Ask your human to approve or reject it${opts.appUrl ? ` at ${opts.appUrl}` : ' in the Ziggs app, under Agreements'}, ` +
|
|
153
|
+
`then read the agreement again to see the outcome.`);
|
|
154
|
+
}
|
|
138
155
|
return approveAgreementAsParty(agreementId, partyId, action === 'approve' ? 'approved' : 'rejected', creds);
|
|
139
156
|
}
|
|
140
157
|
/**
|
|
141
|
-
*
|
|
142
|
-
*
|
|
158
|
+
* Which of `candidateIds` holds the pending approval slot, first match wins —
|
|
159
|
+
* so pass them in preference order (agent id before owner principal).
|
|
143
160
|
*/
|
|
144
|
-
export function
|
|
145
|
-
const actorIds =
|
|
146
|
-
const pending = (agreement.approvals ?? []).filter((a) => a.status === 'pending' || a.status === 'PENDING');
|
|
161
|
+
export function resolvePendingApprovalPartyId(facts, candidateIds) {
|
|
162
|
+
const actorIds = candidateIds.filter((id) => typeof id === 'string' && id.length > 0);
|
|
147
163
|
for (const id of actorIds) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
return hit.partyId;
|
|
164
|
+
if (facts.pendingPartyIds.includes(id))
|
|
165
|
+
return id;
|
|
151
166
|
}
|
|
152
|
-
|
|
167
|
+
// A row with no pending ledger entries is a legacy one (approvals[] predates
|
|
168
|
+
// ZIG-244), and there the named responder slot IS the decision.
|
|
169
|
+
const proposedTo = facts.proposedTo ?? null;
|
|
153
170
|
if (proposedTo &&
|
|
154
171
|
actorIds.includes(proposedTo) &&
|
|
155
|
-
|
|
172
|
+
facts.pendingPartyIds.length === 0) {
|
|
156
173
|
return proposedTo;
|
|
157
174
|
}
|
|
158
175
|
return null;
|
|
159
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* ZIG-524 — resolve which approvals.partyId the current operator may submit.
|
|
179
|
+
* Checks pending ledger entries against impersonated agent id and owner principal.
|
|
180
|
+
*/
|
|
181
|
+
export function resolveMyPendingApprovalPartyId(agreement, opts) {
|
|
182
|
+
return resolvePendingApprovalPartyId({
|
|
183
|
+
pendingPartyIds: (agreement.approvals ?? [])
|
|
184
|
+
.filter((a) => a.status === 'pending' || a.status === 'PENDING')
|
|
185
|
+
.map((a) => a.partyId),
|
|
186
|
+
proposedTo: agreement.parties?.proposedTo ?? null,
|
|
187
|
+
}, [opts.agentId, opts.ownerUserId]);
|
|
188
|
+
}
|
|
160
189
|
/**
|
|
161
190
|
* Record this party's approval decision on an agreement
|
|
162
191
|
* (`PUT /agreements/:agreementId/approvals/:partyId`).
|
|
@@ -406,6 +435,22 @@ export async function claimAgreement(agreementId, creds) {
|
|
|
406
435
|
}
|
|
407
436
|
return data;
|
|
408
437
|
}
|
|
438
|
+
// ---------------------------------------------------------------------------
|
|
439
|
+
// Chat links
|
|
440
|
+
// ---------------------------------------------------------------------------
|
|
441
|
+
/**
|
|
442
|
+
* Link types a caller may ASK for — the server's `CALLER_LINK_TYPES`, not its
|
|
443
|
+
* full `LINK_TYPES`. `space` is deliberately absent: an agreement space gets
|
|
444
|
+
* exactly one system-minted room, and letting a request name that type would
|
|
445
|
+
* burn the root's single slot on an unrelated chat. Shorter than the server
|
|
446
|
+
* union on purpose, which is why this list carries the reason with it.
|
|
447
|
+
*/
|
|
448
|
+
export const CHAT_LINK_TYPES = [
|
|
449
|
+
'origin',
|
|
450
|
+
'mention',
|
|
451
|
+
'delegation',
|
|
452
|
+
'join',
|
|
453
|
+
];
|
|
409
454
|
export async function linkAgreementToChat(agreementId, chatId, linkType = 'mention', creds) {
|
|
410
455
|
if (!agreementId || !chatId)
|
|
411
456
|
return null;
|
|
@@ -468,6 +513,10 @@ export async function joinAgreement(agreementId, creds) {
|
|
|
468
513
|
}
|
|
469
514
|
return data;
|
|
470
515
|
}
|
|
516
|
+
// ---------------------------------------------------------------------------
|
|
517
|
+
// Artifact links
|
|
518
|
+
// ---------------------------------------------------------------------------
|
|
519
|
+
export const ARTIFACT_LINK_TYPES = ['produced', 'referenced'];
|
|
471
520
|
export async function linkArtifactToAgreement(agreementId, artifactId, linkType = 'produced', creds) {
|
|
472
521
|
if (!agreementId || !artifactId)
|
|
473
522
|
return null;
|
|
@@ -512,6 +561,15 @@ export async function getArtifactsForAgreement(agreementId, creds) {
|
|
|
512
561
|
return [];
|
|
513
562
|
}
|
|
514
563
|
}
|
|
564
|
+
// ---------------------------------------------------------------------------
|
|
565
|
+
// User links
|
|
566
|
+
// ---------------------------------------------------------------------------
|
|
567
|
+
export const AGREEMENT_USER_ROLES = [
|
|
568
|
+
'payer',
|
|
569
|
+
'provider',
|
|
570
|
+
'participant',
|
|
571
|
+
'observer',
|
|
572
|
+
];
|
|
515
573
|
export async function linkUserToAgreement(agreementId, userId, role, creds) {
|
|
516
574
|
if (!agreementId || !userId || !role)
|
|
517
575
|
return null;
|
|
@@ -62,11 +62,15 @@ export declare function artifactScopeForSession(sessionId: string): {
|
|
|
62
62
|
export declare class ArtifactsClient {
|
|
63
63
|
private readonly operatorKey;
|
|
64
64
|
private readonly agentId?;
|
|
65
|
+
private readonly laneId?;
|
|
65
66
|
/**
|
|
66
67
|
* @param operatorKey Agent-scoped or fleet operator key.
|
|
67
68
|
* @param agentId Required for fleet keys (sent as X-Agent-Id). Omit for agent-scoped keys.
|
|
69
|
+
* @param laneId ZIG-1092 — the wake's lane (chat id, or `agrn-<agreementId>`),
|
|
70
|
+
* sent as X-Ziggs-Lane so `authoredBy=me` is fenced to this engagement
|
|
71
|
+
* instead of returning every customer's deliverables in one call.
|
|
68
72
|
*/
|
|
69
|
-
constructor(operatorKey: string, agentId?: string);
|
|
73
|
+
constructor(operatorKey: string, agentId?: string, laneId?: string);
|
|
70
74
|
list(q: ListArtifactsQuery, opts?: ListArtifactsOptions): Promise<ListArtifactsResult>;
|
|
71
75
|
/**
|
|
72
76
|
* Record an agent thought as an `agent-private` artifact. Replaces the
|
|
@@ -28,15 +28,20 @@ export function artifactScopeForSession(sessionId) {
|
|
|
28
28
|
export class ArtifactsClient {
|
|
29
29
|
operatorKey;
|
|
30
30
|
agentId;
|
|
31
|
+
laneId;
|
|
31
32
|
/**
|
|
32
33
|
* @param operatorKey Agent-scoped or fleet operator key.
|
|
33
34
|
* @param agentId Required for fleet keys (sent as X-Agent-Id). Omit for agent-scoped keys.
|
|
35
|
+
* @param laneId ZIG-1092 — the wake's lane (chat id, or `agrn-<agreementId>`),
|
|
36
|
+
* sent as X-Ziggs-Lane so `authoredBy=me` is fenced to this engagement
|
|
37
|
+
* instead of returning every customer's deliverables in one call.
|
|
34
38
|
*/
|
|
35
|
-
constructor(operatorKey, agentId) {
|
|
39
|
+
constructor(operatorKey, agentId, laneId) {
|
|
36
40
|
if (!operatorKey)
|
|
37
41
|
throw new Error('ArtifactsClient: operatorKey is required');
|
|
38
42
|
this.operatorKey = operatorKey;
|
|
39
43
|
this.agentId = agentId;
|
|
44
|
+
this.laneId = laneId;
|
|
40
45
|
}
|
|
41
46
|
async list(q, opts = {}) {
|
|
42
47
|
const selectors = [q.chatId, q.agreementId, q.taskId, q.authoredBy].filter(Boolean);
|
|
@@ -178,12 +183,19 @@ export class ArtifactsClient {
|
|
|
178
183
|
*/
|
|
179
184
|
_assertScopeXor(input) {
|
|
180
185
|
if (input.chatId && input.agreementId) {
|
|
181
|
-
|
|
186
|
+
// ZIG-1075: the refusal names the recovery, because this is the one gate
|
|
187
|
+
// for every caller — the exposed tool surfaces inherit it rather than each
|
|
188
|
+
// wording their own, and a caller that used to have chatId silently
|
|
189
|
+
// dropped here now learns what to do instead. Wording matters: a bare
|
|
190
|
+
// "pick one" at the last step of a finished task is what the drop was
|
|
191
|
+
// added to avoid (ZIG-924 dogfood).
|
|
192
|
+
throw new Error('pass at most one of chatId or agreementId — a deliverable under an ' +
|
|
193
|
+
'agreement wants agreementId alone (its parties see it); use chatId ' +
|
|
194
|
+
'only for a chat-scoped note. To put it in both places, record it ' +
|
|
195
|
+
'with agreementId, then attach it to the chat.');
|
|
182
196
|
}
|
|
183
197
|
}
|
|
184
198
|
_headers() {
|
|
185
|
-
return buildOperatorHeaders(this.operatorKey, this.agentId, {
|
|
186
|
-
'content-type': 'application/json',
|
|
187
|
-
});
|
|
199
|
+
return buildOperatorHeaders(this.operatorKey, this.agentId, { 'content-type': 'application/json' }, this.laneId);
|
|
188
200
|
}
|
|
189
201
|
}
|
package/dist/http/ChatClient.js
CHANGED
|
@@ -7,6 +7,9 @@ function buildHeaders(creds) {
|
|
|
7
7
|
'content-type': 'application/json',
|
|
8
8
|
Authorization: `Bearer ${creds.operatorKey}`,
|
|
9
9
|
'X-Agent-Id': creds.agentId,
|
|
10
|
+
// ZIG-1092 — the wake's lane, so the backend can fence this call to the
|
|
11
|
+
// engagement it belongs to rather than the agent's whole authority.
|
|
12
|
+
...(creds.laneId ? { 'X-Ziggs-Lane': creds.laneId } : {}),
|
|
10
13
|
};
|
|
11
14
|
}
|
|
12
15
|
function assertCreds(creds, op) {
|
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
|
-
export
|
|
2
|
+
export declare const CONTEXT_READ_TYPES: readonly ["messages", "artifacts", "agreements", "tasks"];
|
|
3
|
+
export type ContextReadType = (typeof CONTEXT_READ_TYPES)[number];
|
|
4
|
+
/**
|
|
5
|
+
* Entry points a read can name as `via=<kind>:<id>` — the server's `parseVia`
|
|
6
|
+
* grammar. `counterparty` parses but reads nothing: it resolves a scope graph,
|
|
7
|
+
* not content, which is why {@link CONTEXT_READ_VIA} admits it for no type.
|
|
8
|
+
*/
|
|
9
|
+
export declare const VIA_KINDS: readonly ["chat", "agreement", "task", "counterparty", "artifact"];
|
|
10
|
+
export type ViaKind = (typeof VIA_KINDS)[number];
|
|
11
|
+
/**
|
|
12
|
+
* Which entry points each read type actually accepts, mirroring the server's
|
|
13
|
+
* `CONTEXT_READ_VIA`. Stated once here so the tool descriptions, the argument
|
|
14
|
+
* check, and this type all read off the same list instead of three prose
|
|
15
|
+
* copies that drift — and so a wrong pairing is refused with the reason rather
|
|
16
|
+
* than as a bare 400 from a round-trip away.
|
|
17
|
+
*/
|
|
18
|
+
export declare const CONTEXT_READ_VIA: Record<ContextReadType, readonly ViaKind[]>;
|
|
19
|
+
/** `chat:<id>, agreement:<id>` — the accepted entries for one read type, for humans. */
|
|
20
|
+
export declare function viaHint(type: ContextReadType): string;
|
|
21
|
+
/** Split `chat:abc` into its parts, or null when it is not a `via` at all. */
|
|
22
|
+
export declare function parseVia(via: string): {
|
|
23
|
+
kind: ViaKind;
|
|
24
|
+
id: string;
|
|
25
|
+
} | null;
|
|
3
26
|
export interface ContextReadQuery {
|
|
4
27
|
via: string;
|
|
5
28
|
cursor?: string;
|
|
@@ -12,7 +35,7 @@ export interface ContextReadQuery {
|
|
|
12
35
|
export interface ContextReadEnvelope<T = unknown> {
|
|
13
36
|
type: ContextReadType;
|
|
14
37
|
via: {
|
|
15
|
-
kind:
|
|
38
|
+
kind: ViaKind;
|
|
16
39
|
id: string;
|
|
17
40
|
};
|
|
18
41
|
items: T[];
|
|
@@ -44,11 +67,15 @@ export declare class ContextReadClient {
|
|
|
44
67
|
private readonly operatorKey;
|
|
45
68
|
private readonly agentId?;
|
|
46
69
|
private readonly baseUrl;
|
|
70
|
+
private readonly laneId?;
|
|
47
71
|
/**
|
|
48
72
|
* @param operatorKey Agent-scoped or fleet operator key.
|
|
49
73
|
* @param agentId Required for fleet keys (sent as X-Agent-Id). Omit for agent-scoped keys.
|
|
74
|
+
* @param laneId ZIG-1092 — the wake's lane, sent as X-Ziggs-Lane. This is the
|
|
75
|
+
* path the dogfood leak ran through: `via=artifact:<other customer's spec>`
|
|
76
|
+
* was authorised purely because the same agent had authored it.
|
|
50
77
|
*/
|
|
51
|
-
constructor(operatorKey: string, agentId?: string, baseUrl?: string);
|
|
78
|
+
constructor(operatorKey: string, agentId?: string, baseUrl?: string, laneId?: string);
|
|
52
79
|
read<T = unknown>(type: ContextReadType, query: ContextReadQuery): Promise<ContextReadEnvelope<T>>;
|
|
53
80
|
/**
|
|
54
81
|
* Aggregated chat snapshot — `GET /context/snapshot?via=chat:<id>`. The
|
|
@@ -1,6 +1,54 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
2
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
3
|
import { pollSurfaceError } from '../shared/rateLimit.js';
|
|
4
|
+
export const CONTEXT_READ_TYPES = [
|
|
5
|
+
'messages',
|
|
6
|
+
'artifacts',
|
|
7
|
+
'agreements',
|
|
8
|
+
'tasks',
|
|
9
|
+
];
|
|
10
|
+
/**
|
|
11
|
+
* Entry points a read can name as `via=<kind>:<id>` — the server's `parseVia`
|
|
12
|
+
* grammar. `counterparty` parses but reads nothing: it resolves a scope graph,
|
|
13
|
+
* not content, which is why {@link CONTEXT_READ_VIA} admits it for no type.
|
|
14
|
+
*/
|
|
15
|
+
export const VIA_KINDS = [
|
|
16
|
+
'chat',
|
|
17
|
+
'agreement',
|
|
18
|
+
'task',
|
|
19
|
+
'counterparty',
|
|
20
|
+
'artifact',
|
|
21
|
+
];
|
|
22
|
+
/**
|
|
23
|
+
* Which entry points each read type actually accepts, mirroring the server's
|
|
24
|
+
* `CONTEXT_READ_VIA`. Stated once here so the tool descriptions, the argument
|
|
25
|
+
* check, and this type all read off the same list instead of three prose
|
|
26
|
+
* copies that drift — and so a wrong pairing is refused with the reason rather
|
|
27
|
+
* than as a bare 400 from a round-trip away.
|
|
28
|
+
*/
|
|
29
|
+
export const CONTEXT_READ_VIA = {
|
|
30
|
+
messages: ['chat'],
|
|
31
|
+
artifacts: ['chat', 'agreement', 'task', 'artifact'],
|
|
32
|
+
agreements: ['chat', 'agreement'],
|
|
33
|
+
tasks: ['agreement', 'task'],
|
|
34
|
+
};
|
|
35
|
+
/** `chat:<id>, agreement:<id>` — the accepted entries for one read type, for humans. */
|
|
36
|
+
export function viaHint(type) {
|
|
37
|
+
return CONTEXT_READ_VIA[type].map((k) => `${k}:<id>`).join(', ');
|
|
38
|
+
}
|
|
39
|
+
/** Split `chat:abc` into its parts, or null when it is not a `via` at all. */
|
|
40
|
+
export function parseVia(via) {
|
|
41
|
+
const at = via.indexOf(':');
|
|
42
|
+
if (at <= 0)
|
|
43
|
+
return null;
|
|
44
|
+
const kind = via.slice(0, at);
|
|
45
|
+
const id = via.slice(at + 1);
|
|
46
|
+
if (!id)
|
|
47
|
+
return null;
|
|
48
|
+
return VIA_KINDS.includes(kind)
|
|
49
|
+
? { kind: kind, id }
|
|
50
|
+
: null;
|
|
51
|
+
}
|
|
4
52
|
/**
|
|
5
53
|
* Protocol-first uniform context reads (ZIG-427).
|
|
6
54
|
* Wraps `GET /context/read/:type` — one client, one envelope, four types.
|
|
@@ -9,16 +57,21 @@ export class ContextReadClient {
|
|
|
9
57
|
operatorKey;
|
|
10
58
|
agentId;
|
|
11
59
|
baseUrl;
|
|
60
|
+
laneId;
|
|
12
61
|
/**
|
|
13
62
|
* @param operatorKey Agent-scoped or fleet operator key.
|
|
14
63
|
* @param agentId Required for fleet keys (sent as X-Agent-Id). Omit for agent-scoped keys.
|
|
64
|
+
* @param laneId ZIG-1092 — the wake's lane, sent as X-Ziggs-Lane. This is the
|
|
65
|
+
* path the dogfood leak ran through: `via=artifact:<other customer's spec>`
|
|
66
|
+
* was authorised purely because the same agent had authored it.
|
|
15
67
|
*/
|
|
16
|
-
constructor(operatorKey, agentId, baseUrl) {
|
|
68
|
+
constructor(operatorKey, agentId, baseUrl, laneId) {
|
|
17
69
|
if (!operatorKey)
|
|
18
70
|
throw new Error('ContextReadClient: operatorKey is required');
|
|
19
71
|
this.operatorKey = operatorKey;
|
|
20
72
|
this.agentId = agentId;
|
|
21
73
|
this.baseUrl = baseUrl || getBackendUrl();
|
|
74
|
+
this.laneId = laneId;
|
|
22
75
|
}
|
|
23
76
|
async read(type, query) {
|
|
24
77
|
if (!query.via?.trim()) {
|
|
@@ -44,6 +97,8 @@ export class ContextReadClient {
|
|
|
44
97
|
};
|
|
45
98
|
if (this.agentId)
|
|
46
99
|
headers['X-Agent-Id'] = this.agentId;
|
|
100
|
+
if (this.laneId)
|
|
101
|
+
headers['X-Ziggs-Lane'] = this.laneId;
|
|
47
102
|
if (query.contextGrantId) {
|
|
48
103
|
headers['X-Context-Grant-Id'] = query.contextGrantId;
|
|
49
104
|
}
|
|
@@ -88,6 +143,8 @@ export class ContextReadClient {
|
|
|
88
143
|
};
|
|
89
144
|
if (this.agentId)
|
|
90
145
|
headers['X-Agent-Id'] = this.agentId;
|
|
146
|
+
if (this.laneId)
|
|
147
|
+
headers['X-Ziggs-Lane'] = this.laneId;
|
|
91
148
|
if (opts.contextGrantId) {
|
|
92
149
|
headers['X-Context-Grant-Id'] = opts.contextGrantId;
|
|
93
150
|
}
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
|
+
/**
|
|
3
|
+
* What a delivery can be about. A closed union, not a comment: a consumer that
|
|
4
|
+
* dispatches on `kind` (the MCP read plan) is only safe if the compiler can tell
|
|
5
|
+
* it a case is missing. The task-only deliverable that was acked unread got
|
|
6
|
+
* through precisely because this was `string`.
|
|
7
|
+
*
|
|
8
|
+
* A type and not a value list, unlike the backend's `RESOURCE_KINDS` — nothing
|
|
9
|
+
* on this side validates a delivery kind at runtime (the server does that on the
|
|
10
|
+
* way in), and exhaustiveness checking is purely type-level.
|
|
11
|
+
*/
|
|
12
|
+
export type InboxDeliveryKind = 'message' | 'artifact' | 'task-state' | 'agreement';
|
|
2
13
|
/**
|
|
3
14
|
* One thing addressed to this agent. A reference, never content — following it
|
|
4
15
|
* (a chat read, a task read) is where this agent's grants are enforced.
|
|
5
16
|
*/
|
|
6
17
|
export interface InboxDeliveryRef {
|
|
7
|
-
|
|
8
|
-
kind: string;
|
|
18
|
+
kind: InboxDeliveryKind;
|
|
9
19
|
resourceId: string;
|
|
10
20
|
chatId: string | null;
|
|
11
21
|
agreementId: string | null;
|
|
@@ -24,16 +34,35 @@ export interface InboxProposalRef {
|
|
|
24
34
|
agreementId: string;
|
|
25
35
|
title: string;
|
|
26
36
|
proposedAt: string | null;
|
|
37
|
+
/**
|
|
38
|
+
* ZIG-1087 — party ids still owing a decision, and the named responder slot.
|
|
39
|
+
* The inbox lists proposals awaiting the agent OR its human, and only the
|
|
40
|
+
* agent's own slot is one it can submit; these say which is which.
|
|
41
|
+
*
|
|
42
|
+
* Optional because a backend deployed before ZIG-1087 omits them, and this
|
|
43
|
+
* client is installed independently of the server it talks to. Absent reads
|
|
44
|
+
* as "no slot of mine", which routes the decision to the human — the safe
|
|
45
|
+
* direction: it withholds a call, it never invents authority.
|
|
46
|
+
*/
|
|
47
|
+
pendingApprovalPartyIds?: string[];
|
|
48
|
+
proposedTo?: string | null;
|
|
27
49
|
}
|
|
28
50
|
export interface InboxConnectionRequestRef {
|
|
29
51
|
requestId: string;
|
|
30
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Non-addressable persona reference for the requester (`psn_*`).
|
|
54
|
+
* Never use as an account id for lookup / wake / pay (ZIG-1137).
|
|
55
|
+
*/
|
|
56
|
+
requesterRef: string;
|
|
31
57
|
/** ZIG-1039 — human-readable name for consent cards. */
|
|
32
58
|
requesterDisplayName?: string | null;
|
|
33
59
|
/** ZIG-1039 — org label for consent cards. */
|
|
34
60
|
requesterOrgName?: string | null;
|
|
35
61
|
message: string | null;
|
|
36
62
|
requestedAt: string | null;
|
|
63
|
+
/** ZIG-1087 — see InboxProposalRef; a link request uses the same gate. */
|
|
64
|
+
pendingApprovalPartyIds?: string[];
|
|
65
|
+
proposedTo?: string | null;
|
|
37
66
|
}
|
|
38
67
|
/** Pull-only MCP: prompt the human when proposals need a decision (ZIG-482 / ZIG-481). */
|
|
39
68
|
export interface InboxHumanAttention {
|
|
@@ -7,6 +7,9 @@ function buildHeaders(creds) {
|
|
|
7
7
|
'content-type': 'application/json',
|
|
8
8
|
Authorization: `Bearer ${creds.operatorKey}`,
|
|
9
9
|
'X-Agent-Id': creds.agentId,
|
|
10
|
+
// ZIG-1092 — the wake's lane, so the backend can fence this call to the
|
|
11
|
+
// engagement it belongs to rather than the agent's whole authority.
|
|
12
|
+
...(creds.laneId ? { 'X-Ziggs-Lane': creds.laneId } : {}),
|
|
10
13
|
};
|
|
11
14
|
}
|
|
12
15
|
function assertCreds(creds, op) {
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
2
|
import { type Creds, type Task, type TaskState } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* When the buyer reviews a task's plan. Task-rail only — an agreement has no
|
|
5
|
+
* plan to review, which is why ZIG-1095 cut this from the propose/counter/
|
|
6
|
+
* subcontract inputs rather than teaching those routes to keep one.
|
|
7
|
+
*/
|
|
8
|
+
export type PlanReviewTiming = 'with_proposal' | 'before_execution';
|
|
3
9
|
export interface CreateTaskData {
|
|
4
10
|
description: string;
|
|
5
11
|
agreementId: string;
|
|
6
12
|
parentTaskId?: string;
|
|
7
13
|
plan?: unknown;
|
|
14
|
+
planReviewTiming?: PlanReviewTiming;
|
|
15
|
+
requireMidWorkPlanAck?: boolean;
|
|
8
16
|
idempotencyKey?: string;
|
|
9
17
|
/** Explicit delegation target (ZIG-586) — must be a party to the agreement; validated server-side. */
|
|
10
18
|
assigneeId?: string;
|
package/dist/http/TaskClient.js
CHANGED
|
@@ -7,6 +7,9 @@ function buildHeaders(creds) {
|
|
|
7
7
|
'content-type': 'application/json',
|
|
8
8
|
Authorization: `Bearer ${creds.operatorKey}`,
|
|
9
9
|
'X-Agent-Id': creds.agentId,
|
|
10
|
+
// ZIG-1092 — the wake's lane, so the backend can fence this call to the
|
|
11
|
+
// engagement it belongs to rather than the agent's whole authority.
|
|
12
|
+
...(creds.laneId ? { 'X-Ziggs-Lane': creds.laneId } : {}),
|
|
10
13
|
};
|
|
11
14
|
}
|
|
12
15
|
function assertCreds(creds, op) {
|
package/dist/http/index.d.ts
CHANGED
|
@@ -7,8 +7,8 @@ export { MessagesClient } from './MessagesClient.js';
|
|
|
7
7
|
export type { ListMessagesOptions, ListMessagesResult } from './MessagesClient.js';
|
|
8
8
|
export { ArtifactsClient, artifactScopeForSession, AGREEMENT_LANE_PREFIX, } from './ArtifactsClient.js';
|
|
9
9
|
export type { ArtifactVisibility, ListArtifactsOptions, ListArtifactsQuery, ListArtifactsResult, WriteArtifactInput, } from './ArtifactsClient.js';
|
|
10
|
-
export { ContextReadClient } from './ContextReadClient.js';
|
|
11
|
-
export type { ContextReadType, ContextReadQuery, ContextReadEnvelope, ContextSnapshotResult, } from './ContextReadClient.js';
|
|
10
|
+
export { ContextReadClient, CONTEXT_READ_TYPES, CONTEXT_READ_VIA, VIA_KINDS, parseVia, viaHint, } from './ContextReadClient.js';
|
|
11
|
+
export type { ContextReadType, ContextReadQuery, ContextReadEnvelope, ContextSnapshotResult, ViaKind, } from './ContextReadClient.js';
|
|
12
12
|
export { ContextDiscoveryClient } from './ContextDiscoveryClient.js';
|
|
13
13
|
export type { DiscoverableItem } from './ContextDiscoveryClient.js';
|
|
14
14
|
export { GrantsClient } from './GrantsClient.js';
|
|
@@ -26,4 +26,4 @@ export type { MyOrg, OrgResolution } from './OrgsClient.js';
|
|
|
26
26
|
export { AgentSearchClient } from './AgentSearchClient.js';
|
|
27
27
|
export { TelemetryClient } from './TelemetryClient.js';
|
|
28
28
|
export { InboxClient } from './InboxClient.js';
|
|
29
|
-
export type { InboxDeliveryRef, InboxChatNews, InboxProposalRef, InboxTaskRef, InboxConnectionRequestRef, InboxHumanAttention, InboxEnvelope, InboxAckResult, InboxOperatorAgentEntry, InboxOperatorEnvelope, } from './InboxClient.js';
|
|
29
|
+
export type { InboxDeliveryKind, InboxDeliveryRef, InboxChatNews, InboxProposalRef, InboxTaskRef, InboxConnectionRequestRef, InboxHumanAttention, InboxEnvelope, InboxAckResult, InboxOperatorAgentEntry, InboxOperatorEnvelope, } from './InboxClient.js';
|
package/dist/http/index.js
CHANGED
|
@@ -7,7 +7,7 @@ export { MessagesClient } from './MessagesClient.js';
|
|
|
7
7
|
export { ArtifactsClient,
|
|
8
8
|
// ZIG-1032: agreement lanes are not chats — callers scope artifact writes with this.
|
|
9
9
|
artifactScopeForSession, AGREEMENT_LANE_PREFIX, } from './ArtifactsClient.js';
|
|
10
|
-
export { ContextReadClient } from './ContextReadClient.js';
|
|
10
|
+
export { ContextReadClient, CONTEXT_READ_TYPES, CONTEXT_READ_VIA, VIA_KINDS, parseVia, viaHint, } from './ContextReadClient.js';
|
|
11
11
|
export { ContextDiscoveryClient } from './ContextDiscoveryClient.js';
|
|
12
12
|
export { GrantsClient } from './GrantsClient.js';
|
|
13
13
|
export { ContextGrantsClient } from './ContextGrantsClient.js';
|
|
@@ -3,4 +3,10 @@
|
|
|
3
3
|
* an agentId is present — agent-scoped keys identify the agent themselves;
|
|
4
4
|
* fleet keys must pass one (ZIG-642).
|
|
5
5
|
*/
|
|
6
|
-
export declare function buildOperatorHeaders(operatorKey: string, agentId?: string, extra?: Record<string, string
|
|
6
|
+
export declare function buildOperatorHeaders(operatorKey: string, agentId?: string, extra?: Record<string, string>,
|
|
7
|
+
/**
|
|
8
|
+
* ZIG-1092 — the lane this call belongs to, sent as `X-Ziggs-Lane`. The
|
|
9
|
+
* backend narrows the wake's reach to the engagement's orgs; omitting it
|
|
10
|
+
* narrows to the agent's own org, so it can never widen reach.
|
|
11
|
+
*/
|
|
12
|
+
laneId?: string): Record<string, string>;
|
|
@@ -3,10 +3,17 @@
|
|
|
3
3
|
* an agentId is present — agent-scoped keys identify the agent themselves;
|
|
4
4
|
* fleet keys must pass one (ZIG-642).
|
|
5
5
|
*/
|
|
6
|
-
export function buildOperatorHeaders(operatorKey, agentId, extra
|
|
6
|
+
export function buildOperatorHeaders(operatorKey, agentId, extra,
|
|
7
|
+
/**
|
|
8
|
+
* ZIG-1092 — the lane this call belongs to, sent as `X-Ziggs-Lane`. The
|
|
9
|
+
* backend narrows the wake's reach to the engagement's orgs; omitting it
|
|
10
|
+
* narrows to the agent's own org, so it can never widen reach.
|
|
11
|
+
*/
|
|
12
|
+
laneId) {
|
|
7
13
|
return {
|
|
8
14
|
Authorization: `Bearer ${operatorKey}`,
|
|
9
15
|
...(agentId ? { 'X-Agent-Id': agentId } : {}),
|
|
16
|
+
...(laneId ? { 'X-Ziggs-Lane': laneId } : {}),
|
|
10
17
|
...extra,
|
|
11
18
|
};
|
|
12
19
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,8 @@ export * from './capabilities/index.js';
|
|
|
3
3
|
export * from './relay/provisionRelayWorkers.js';
|
|
4
4
|
export { ConnectionManager } from './ConnectionManager.js';
|
|
5
5
|
export type { StartAgentOptions } from './ConnectionManager.js';
|
|
6
|
-
export { EntryTypes, ContentTypes, OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, BROADCAST_TARGETS, isBroadcastTarget, AGREEMENT_ENGAGEMENT_KIND, isValidContentType } from './types.js';
|
|
6
|
+
export { EntryTypes, ContentTypes, OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, BROADCAST_TARGETS, isBroadcastTarget, isPersonaRef, isRoomPresentationRef, isOpaquePresentationRef, AGREEMENT_ENGAGEMENT_KIND, isValidContentType, } from './types.js';
|
|
7
|
+
export type { PrincipalPresentation } from './types.js';
|
|
7
8
|
export { getBackendUrl, getWebSocketUrl } from './utils/urlUtils.js';
|
|
8
9
|
export { runtimeLog, resetRuntimeLogLevelCache } from './shared/runtimeLog.js';
|
|
9
10
|
export { RateLimitedError, isRateLimited, parseRetryAfterMs, pollSurfaceError, } from './shared/rateLimit.js';
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ export * from './http/index.js';
|
|
|
2
2
|
export * from './capabilities/index.js';
|
|
3
3
|
export * from './relay/provisionRelayWorkers.js';
|
|
4
4
|
export { ConnectionManager } from './ConnectionManager.js';
|
|
5
|
-
export { EntryTypes, ContentTypes, OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, BROADCAST_TARGETS, isBroadcastTarget, AGREEMENT_ENGAGEMENT_KIND, isValidContentType } from './types.js';
|
|
5
|
+
export { EntryTypes, ContentTypes, OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, BROADCAST_TARGETS, isBroadcastTarget, isPersonaRef, isRoomPresentationRef, isOpaquePresentationRef, AGREEMENT_ENGAGEMENT_KIND, isValidContentType, } from './types.js';
|
|
6
6
|
export { getBackendUrl, getWebSocketUrl } from './utils/urlUtils.js';
|
|
7
7
|
export { runtimeLog, resetRuntimeLogLevelCache } from './shared/runtimeLog.js';
|
|
8
8
|
// ZIG-1019: retry loops need the server's own wait, not a guess.
|
package/dist/types.d.ts
CHANGED
|
@@ -6,6 +6,21 @@ export declare class ApiError extends Error {
|
|
|
6
6
|
export interface Creds {
|
|
7
7
|
operatorKey: string;
|
|
8
8
|
agentId: string;
|
|
9
|
+
/**
|
|
10
|
+
* ZIG-1092 — the lane (chat id, or `agrn-<agreementId>`) this call is being
|
|
11
|
+
* made from. Sent as `X-Ziggs-Lane` so the backend can fence the wake to the
|
|
12
|
+
* engagement the agent is actually acting inside.
|
|
13
|
+
*
|
|
14
|
+
* The operator key says WHO is calling; this says ON WHOSE BEHALF, RIGHT NOW.
|
|
15
|
+
* Without it an agent serving several customers carries its full authority
|
|
16
|
+
* into every call, and one tool call reaches another customer's work.
|
|
17
|
+
*
|
|
18
|
+
* Optional, and omitting it can only narrow what comes back (the backend
|
|
19
|
+
* falls back to the agent's own org) — never widen it. Nothing here is
|
|
20
|
+
* trusted: the server re-derives the party orgs itself and refuses a lane the
|
|
21
|
+
* agent is not in.
|
|
22
|
+
*/
|
|
23
|
+
laneId?: string;
|
|
9
24
|
}
|
|
10
25
|
export type TaskState = 'active' | 'proposal' | 'completed' | 'failed' | 'cancelled' | 'ledger_open';
|
|
11
26
|
export type PlanStepStatus = 'pending' | 'in_progress' | 'completed' | 'skipped';
|
|
@@ -159,6 +174,36 @@ export type BroadcastAudience = typeof OPEN_AGREEMENT_TARGET | typeof ORG_AGREEM
|
|
|
159
174
|
export declare const BROADCAST_TARGETS: readonly ["everyone", "org"];
|
|
160
175
|
/** True when `id` is a broadcast sentinel ('everyone' | 'org') rather than a concrete principal id. */
|
|
161
176
|
export declare function isBroadcastTarget(id: string | null | undefined): boolean;
|
|
177
|
+
/**
|
|
178
|
+
* Persona face id (`psn_*`). Non-addressable — never use for agent lookup,
|
|
179
|
+
* wake, or payment parties (ZIG-1137).
|
|
180
|
+
*/
|
|
181
|
+
export declare function isPersonaRef(id: string | null | undefined): boolean;
|
|
182
|
+
/**
|
|
183
|
+
* Room presentation binding id (`rpb_*`). Opaque to account lookup / wake /
|
|
184
|
+
* pay. Chat sends may echo it as `receiverId` — the backend resolves it in-room
|
|
185
|
+
* (ZIG-1137).
|
|
186
|
+
*/
|
|
187
|
+
export declare function isRoomPresentationRef(id: string | null | undefined): boolean;
|
|
188
|
+
/** Either opaque presentation ref (`psn_*` | `rpb_*`). */
|
|
189
|
+
export declare function isOpaquePresentationRef(id: string | null | undefined): boolean;
|
|
190
|
+
/** Display + entitlement face for a principal on the message/roster wire. */
|
|
191
|
+
export interface PrincipalPresentation {
|
|
192
|
+
/** Public, non-addressable reference (`psn_*` or `rpb_*`). */
|
|
193
|
+
ref?: string;
|
|
194
|
+
persona: {
|
|
195
|
+
id: string;
|
|
196
|
+
name: string;
|
|
197
|
+
image?: string | null;
|
|
198
|
+
revision?: number;
|
|
199
|
+
};
|
|
200
|
+
mode: 'persona' | 'chain';
|
|
201
|
+
/** Present only when the viewer is entitled to resolve the subject. */
|
|
202
|
+
subject?: {
|
|
203
|
+
id: string;
|
|
204
|
+
type: 'user' | 'agent';
|
|
205
|
+
};
|
|
206
|
+
}
|
|
162
207
|
export interface MessageMetadata {
|
|
163
208
|
chatId: string;
|
|
164
209
|
/** Stable id for dedup across push + inbox catch-up (ZIG-454). */
|
|
@@ -167,13 +212,23 @@ export interface MessageMetadata {
|
|
|
167
212
|
sender: {
|
|
168
213
|
id: string;
|
|
169
214
|
type?: string;
|
|
215
|
+
presentation?: PrincipalPresentation | null;
|
|
216
|
+
/** Absent when the viewer is masked (persona layer). */
|
|
217
|
+
underAgreementId?: string | null;
|
|
218
|
+
presentedAs?: string | null;
|
|
170
219
|
};
|
|
171
220
|
senderId: string;
|
|
172
221
|
senderType?: string;
|
|
173
222
|
receiver?: {
|
|
174
223
|
id: string;
|
|
224
|
+
presentation?: PrincipalPresentation | null;
|
|
175
225
|
} | null;
|
|
176
226
|
receiverId?: string | null;
|
|
227
|
+
/**
|
|
228
|
+
* Message-level presentation stamp when the backend attaches one
|
|
229
|
+
* (same shape as sender.presentation). Prefer sender.presentation.
|
|
230
|
+
*/
|
|
231
|
+
presentation?: PrincipalPresentation | null;
|
|
177
232
|
entryType?: string;
|
|
178
233
|
content_type?: string;
|
|
179
234
|
taskId?: string | null;
|
package/dist/types.js
CHANGED
|
@@ -58,3 +58,22 @@ export const BROADCAST_TARGETS = [OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET];
|
|
|
58
58
|
export function isBroadcastTarget(id) {
|
|
59
59
|
return id === OPEN_AGREEMENT_TARGET || id === ORG_AGREEMENT_TARGET;
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Persona face id (`psn_*`). Non-addressable — never use for agent lookup,
|
|
63
|
+
* wake, or payment parties (ZIG-1137).
|
|
64
|
+
*/
|
|
65
|
+
export function isPersonaRef(id) {
|
|
66
|
+
return typeof id === 'string' && id.startsWith('psn_');
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Room presentation binding id (`rpb_*`). Opaque to account lookup / wake /
|
|
70
|
+
* pay. Chat sends may echo it as `receiverId` — the backend resolves it in-room
|
|
71
|
+
* (ZIG-1137).
|
|
72
|
+
*/
|
|
73
|
+
export function isRoomPresentationRef(id) {
|
|
74
|
+
return typeof id === 'string' && id.startsWith('rpb_');
|
|
75
|
+
}
|
|
76
|
+
/** Either opaque presentation ref (`psn_*` | `rpb_*`). */
|
|
77
|
+
export function isOpaquePresentationRef(id) {
|
|
78
|
+
return isPersonaRef(id) || isRoomPresentationRef(id);
|
|
79
|
+
}
|