@ziggs-ai/contracts 0.1.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 +31 -0
- package/dist/chat.d.ts +258 -0
- package/dist/chat.js +5 -0
- package/dist/error.d.ts +11 -0
- package/dist/error.js +9 -0
- package/dist/inbox.d.ts +156 -0
- package/dist/inbox.js +5 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +29 -0
- package/dist/socket.d.ts +89 -0
- package/dist/socket.js +13 -0
- package/dist/vocabulary.d.ts +42 -0
- package/dist/vocabulary.js +41 -0
- package/package.json +29 -0
package/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# @ziggs-ai/contracts
|
|
2
|
+
|
|
3
|
+
Wire contracts for the Ziggs API — the response shapes, error body, and socket
|
|
4
|
+
frames the server actually sends, shared by every client.
|
|
5
|
+
|
|
6
|
+
Types plus the closed vocabularies they are built from. No network, no
|
|
7
|
+
environment reads, no dependencies, so any runtime can import it.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import type { ChatListResponse, AttentionFeed } from '@ziggs-ai/contracts';
|
|
11
|
+
import { SERVER_EVENTS, isApiErrorBody } from '@ziggs-ai/contracts';
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## What is here
|
|
15
|
+
|
|
16
|
+
| Module | Carries |
|
|
17
|
+
| --- | --- |
|
|
18
|
+
| `chat` | `GET /chats`, `GET /chats/:chatId/messages`, the message write paths |
|
|
19
|
+
| `inbox` | `GET /inbox/attention`, `POST /inbox/seen` |
|
|
20
|
+
| `error` | the one error body every handler answers in |
|
|
21
|
+
| `socket` | frame names and payloads, both directions |
|
|
22
|
+
| `vocabulary` | the closed value lists the above are built from |
|
|
23
|
+
|
|
24
|
+
## The server is authoritative
|
|
25
|
+
|
|
26
|
+
These are the server's own definitions, not a copy of them. A shape changes
|
|
27
|
+
here and in the server in the same commit, so a client that pins a version
|
|
28
|
+
knows exactly which wire it is talking to.
|
|
29
|
+
|
|
30
|
+
The socket is a receive-only accelerator: every frame has an HTTP read that is
|
|
31
|
+
the source of truth. A client that misses a frame catches up on its next read.
|
package/dist/chat.d.ts
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat wire contracts: what `GET /chats`, `GET /chats/:chatId/messages` and
|
|
3
|
+
* the message write paths carry.
|
|
4
|
+
*
|
|
5
|
+
* Every shape here is what the server actually sends. A client that needs less
|
|
6
|
+
* narrows locally rather than redeclaring the shape.
|
|
7
|
+
*/
|
|
8
|
+
import type { ChatAddressKind, GrantAccessKind, MessageSenderType, PartyRole } from './vocabulary';
|
|
9
|
+
/** A persona's face, as any surface renders it. */
|
|
10
|
+
export interface PersonaDisplay {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
image: string | null;
|
|
14
|
+
revision?: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* How a principal is shown. Render this — never infer identity from an id.
|
|
18
|
+
*
|
|
19
|
+
* `subject` exists only own-side: across an org boundary the masked id is the
|
|
20
|
+
* whole point, so correlating it back to a principal must not be possible.
|
|
21
|
+
*/
|
|
22
|
+
export interface PrincipalPresentation {
|
|
23
|
+
/** Public, non-addressable reference. */
|
|
24
|
+
ref?: string;
|
|
25
|
+
persona: PersonaDisplay;
|
|
26
|
+
mode: 'persona' | 'chain';
|
|
27
|
+
subject?: {
|
|
28
|
+
id: string;
|
|
29
|
+
type: 'user' | 'agent';
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Why a principal appears on the derived chat roster.
|
|
34
|
+
*
|
|
35
|
+
* There is no `member` arm: taking part is `grant`. The other kind is an
|
|
36
|
+
* overlay row that explains why someone is visible without putting them in the
|
|
37
|
+
* room, so a client branching on it must not treat `agreement` as absence.
|
|
38
|
+
*/
|
|
39
|
+
export type ChatRosterVia = {
|
|
40
|
+
kind: 'agreement';
|
|
41
|
+
/**
|
|
42
|
+
* Present on authority-derived rows. Dropped on persona-masked
|
|
43
|
+
* presentation so a masked member id cannot be correlated through the
|
|
44
|
+
* agreement.
|
|
45
|
+
*/
|
|
46
|
+
agreementId?: string;
|
|
47
|
+
role: PartyRole;
|
|
48
|
+
} | {
|
|
49
|
+
kind: 'grant';
|
|
50
|
+
};
|
|
51
|
+
/** One row of the complete display roster for a room. */
|
|
52
|
+
export interface ChatRosterRow {
|
|
53
|
+
id: string;
|
|
54
|
+
type: 'user' | 'agent' | 'service' | 'org';
|
|
55
|
+
via: ChatRosterVia[];
|
|
56
|
+
/**
|
|
57
|
+
* What this holder's grant on the room lets them do, declared rather than
|
|
58
|
+
* guessed. Absent on overlay rows (an agreement party or a service): those
|
|
59
|
+
* are on the roster without holding anything, which is precisely the
|
|
60
|
+
* difference a reader needs. `write` or `admit` means they take part;
|
|
61
|
+
* `read` means they watch.
|
|
62
|
+
*/
|
|
63
|
+
access?: GrantAccessKind;
|
|
64
|
+
}
|
|
65
|
+
/** One principal who takes part in the room. */
|
|
66
|
+
export interface ChatParticipantRow {
|
|
67
|
+
id: string;
|
|
68
|
+
type: 'user' | 'agent' | 'org';
|
|
69
|
+
via: Array<Extract<ChatRosterVia, {
|
|
70
|
+
kind: 'grant';
|
|
71
|
+
}>>;
|
|
72
|
+
/** Always present here: a participant is a holder of `write` or stronger. */
|
|
73
|
+
access?: GrantAccessKind;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Flat projection of the participant list.
|
|
77
|
+
*
|
|
78
|
+
* An agent participates in its own name, so it appears as both ids: an agent's
|
|
79
|
+
* owner is not automatically in the room with it.
|
|
80
|
+
*/
|
|
81
|
+
export interface ChatMemberView {
|
|
82
|
+
principalId: string;
|
|
83
|
+
agentId: string | null;
|
|
84
|
+
/**
|
|
85
|
+
* Present when the room shows this member through a persona. The ids above
|
|
86
|
+
* are then the exposed ones, not the underlying principal.
|
|
87
|
+
*/
|
|
88
|
+
presentation?: PrincipalPresentation;
|
|
89
|
+
/** The viewer cannot tell which of several representations is speaking. */
|
|
90
|
+
representationAmbiguous?: boolean;
|
|
91
|
+
}
|
|
92
|
+
/** Max characters of a last-message snippet. */
|
|
93
|
+
export declare const CHAT_SNIPPET_CAP = 280;
|
|
94
|
+
/** Newest message in a room, enough to tell entries apart at a glance. */
|
|
95
|
+
export interface ChatLastMessage {
|
|
96
|
+
/** Sender id (user, agent, service, or system). */
|
|
97
|
+
sender: string;
|
|
98
|
+
/** ISO timestamp of the message. */
|
|
99
|
+
at: string;
|
|
100
|
+
/** Message text truncated to {@link CHAT_SNIPPET_CAP}. */
|
|
101
|
+
snippet: string;
|
|
102
|
+
}
|
|
103
|
+
/** One room as an external read returns it. Never a raw stored document. */
|
|
104
|
+
export interface ChatReadDto {
|
|
105
|
+
chatId: string;
|
|
106
|
+
/** Flat projection of `participantSummary`, for clients that read it. */
|
|
107
|
+
members: ChatMemberView[];
|
|
108
|
+
/**
|
|
109
|
+
* Complete display roster derived from members, agreement links, grants,
|
|
110
|
+
* spaces, and services. It is never an authorization input.
|
|
111
|
+
*/
|
|
112
|
+
rosterSummary: ChatRosterRow[];
|
|
113
|
+
/** Same canonical participant answer used by server-side delivery. */
|
|
114
|
+
participantSummary: ChatParticipantRow[];
|
|
115
|
+
/** Next grant-expiry transition for `participantSummary`, if any. */
|
|
116
|
+
participantsRefreshAt: string | null;
|
|
117
|
+
/**
|
|
118
|
+
* What kind of name this room is reached by, when it has one. The kind only:
|
|
119
|
+
* a `pair` key names both principals, and principal ids are what the persona
|
|
120
|
+
* border masks.
|
|
121
|
+
*/
|
|
122
|
+
addressKind: ChatAddressKind | null;
|
|
123
|
+
/** Human-readable name so entries can be told apart at a glance. */
|
|
124
|
+
name: string;
|
|
125
|
+
/** Recency for sorting and triage. Null when the chat has no messages. */
|
|
126
|
+
updatedAt: string | null;
|
|
127
|
+
/** Newest-message snippet. Absent when the chat has no messages. */
|
|
128
|
+
lastMessage?: ChatLastMessage;
|
|
129
|
+
/**
|
|
130
|
+
* Unread messages for the reader, capped at 99 so a badge can render "99+".
|
|
131
|
+
* Attached by the list read; absent on reads that do not resolve it.
|
|
132
|
+
*/
|
|
133
|
+
unreadCount?: number;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* The sender of a message as the wire carries it.
|
|
137
|
+
*
|
|
138
|
+
* The own-side fields are deliberately absent across an org boundary — read
|
|
139
|
+
* them as "not disclosed here", never as "no value".
|
|
140
|
+
*/
|
|
141
|
+
export interface MessageSenderWire {
|
|
142
|
+
id: string;
|
|
143
|
+
type: MessageSenderType;
|
|
144
|
+
/** Own-side/audit only. Deliberately absent across the org boundary. */
|
|
145
|
+
underAgreementId?: string | null;
|
|
146
|
+
/** Own-side/audit only. Deliberately absent across the org boundary. */
|
|
147
|
+
presentedAs?: string | null;
|
|
148
|
+
/** Own-side/audit only. Deliberately absent across the org boundary. */
|
|
149
|
+
actedBy?: string | null;
|
|
150
|
+
persona?: {
|
|
151
|
+
id: string;
|
|
152
|
+
name: string;
|
|
153
|
+
image: string | null;
|
|
154
|
+
} | null;
|
|
155
|
+
/** Canonical presentation contract. Subject/actor exist only own-side. */
|
|
156
|
+
presentation?: {
|
|
157
|
+
ref: string;
|
|
158
|
+
personaId: string;
|
|
159
|
+
revision: number;
|
|
160
|
+
name: string;
|
|
161
|
+
image: string | null;
|
|
162
|
+
badge: 'persona';
|
|
163
|
+
mode: 'persona' | 'chain';
|
|
164
|
+
subject?: {
|
|
165
|
+
id: string;
|
|
166
|
+
type: MessageSenderType;
|
|
167
|
+
};
|
|
168
|
+
actor?: {
|
|
169
|
+
id: string;
|
|
170
|
+
type: string;
|
|
171
|
+
mode: string;
|
|
172
|
+
};
|
|
173
|
+
} | null;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Structured payload written alongside a message (task snapshots, service
|
|
177
|
+
* results, agreement-lifecycle stamps). Stored content, passed through as-is.
|
|
178
|
+
*/
|
|
179
|
+
export interface MessageServicePayload {
|
|
180
|
+
task?: unknown;
|
|
181
|
+
id?: string;
|
|
182
|
+
name?: string;
|
|
183
|
+
operation?: string;
|
|
184
|
+
result?: unknown;
|
|
185
|
+
error?: string;
|
|
186
|
+
/**
|
|
187
|
+
* Agreement-lifecycle notifications persist the acting agreement here so a
|
|
188
|
+
* reader can resolve the lifecycle transition without a second read.
|
|
189
|
+
*/
|
|
190
|
+
agreement?: unknown;
|
|
191
|
+
agreementId?: string;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* One message as the wire carries it.
|
|
195
|
+
*
|
|
196
|
+
* Timestamps are ISO strings here. A server-side read model may hold them as
|
|
197
|
+
* dates; nothing outside the server ever sees that.
|
|
198
|
+
*/
|
|
199
|
+
export interface MessageWireDto {
|
|
200
|
+
messageId: string | null;
|
|
201
|
+
chatId: string;
|
|
202
|
+
text: string;
|
|
203
|
+
sender: MessageSenderWire;
|
|
204
|
+
receiver: {
|
|
205
|
+
id: string;
|
|
206
|
+
type: MessageSenderType;
|
|
207
|
+
};
|
|
208
|
+
entryType: string;
|
|
209
|
+
contentType: string;
|
|
210
|
+
timestamp: string;
|
|
211
|
+
sentTimestamp?: string;
|
|
212
|
+
service?: MessageServicePayload;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* `GET /chats?withMessages=true`.
|
|
216
|
+
*
|
|
217
|
+
* `messages` is flat across rooms — the last page per room — and `hasMore` and
|
|
218
|
+
* `nextCursor` are keyed by chat id. Without the flag the response still has
|
|
219
|
+
* this shape, with `messages: []` and empty maps.
|
|
220
|
+
*/
|
|
221
|
+
export interface ChatListResponse {
|
|
222
|
+
chats: ChatReadDto[];
|
|
223
|
+
messages: MessageWireDto[];
|
|
224
|
+
hasMore: Record<string, boolean>;
|
|
225
|
+
/** Where a backward read continues from, per chat. */
|
|
226
|
+
nextCursor: Record<string, string | null>;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* `GET /chats/:chatId/messages`.
|
|
230
|
+
*
|
|
231
|
+
* One backward page, oldest-to-newest within the page; `nextCursor` reads
|
|
232
|
+
* older.
|
|
233
|
+
*/
|
|
234
|
+
export interface MessagesPage {
|
|
235
|
+
messages: MessageWireDto[];
|
|
236
|
+
hasMore: boolean;
|
|
237
|
+
nextCursor: string | null;
|
|
238
|
+
}
|
|
239
|
+
/** `POST /chats/:chatId/messages`. */
|
|
240
|
+
export interface SendChatMessageBody {
|
|
241
|
+
/**
|
|
242
|
+
* Client-generated idempotency key. The server dedupes on
|
|
243
|
+
* `(chatId, messageId)`, so a retry that re-sends the same key collapses to
|
|
244
|
+
* a single stored row.
|
|
245
|
+
*/
|
|
246
|
+
messageId: string;
|
|
247
|
+
text: string;
|
|
248
|
+
entryType: 'message';
|
|
249
|
+
contentType: 'text';
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* `POST /chats/:chatId/read` — advance the person's read watermark.
|
|
253
|
+
*
|
|
254
|
+
* An empty body marks to the latest message.
|
|
255
|
+
*/
|
|
256
|
+
export interface MarkChatReadBody {
|
|
257
|
+
messageId?: string;
|
|
258
|
+
}
|
package/dist/chat.js
ADDED
package/dist/error.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one error shape the API answers in.
|
|
3
|
+
*
|
|
4
|
+
* Every handler error is rendered as this body with an HTTP status, so a
|
|
5
|
+
* client can render the real message instead of a boolean.
|
|
6
|
+
*/
|
|
7
|
+
export interface ApiErrorBody {
|
|
8
|
+
error: string;
|
|
9
|
+
}
|
|
10
|
+
/** True when a response body is the API's error shape. */
|
|
11
|
+
export declare function isApiErrorBody(body: unknown): body is ApiErrorBody;
|
package/dist/error.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isApiErrorBody = isApiErrorBody;
|
|
4
|
+
/** True when a response body is the API's error shape. */
|
|
5
|
+
function isApiErrorBody(body) {
|
|
6
|
+
return (typeof body === 'object' &&
|
|
7
|
+
body !== null &&
|
|
8
|
+
typeof body.error === 'string');
|
|
9
|
+
}
|
package/dist/inbox.d.ts
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The human inbox: what is waiting on the signed-in person, and how a surface
|
|
3
|
+
* marks it seen.
|
|
4
|
+
*
|
|
5
|
+
* This is a different surface from the agent work protocol. An agent reads its
|
|
6
|
+
* assigned work through `GET /inbox` with per-reader watermarks and ack
|
|
7
|
+
* windows; a person's mark is `seen`, not `handled`.
|
|
8
|
+
*/
|
|
9
|
+
import type { InboxPartyKind, DecisionKind, ResourceKind } from './vocabulary';
|
|
10
|
+
/**
|
|
11
|
+
* How to act on a decision.
|
|
12
|
+
*
|
|
13
|
+
* Carried per row, fully resolved, so a client never hardcodes the mapping
|
|
14
|
+
* from kind to write path and never has to fill in a party id itself. The
|
|
15
|
+
* attention read adds no write endpoints of its own — deciding still happens
|
|
16
|
+
* on the surfaces that already own each object, and this field is how the
|
|
17
|
+
* caller finds them.
|
|
18
|
+
*/
|
|
19
|
+
export interface DecideVia {
|
|
20
|
+
method: 'PUT' | 'POST';
|
|
21
|
+
path: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* One thing waiting on the signed-in person, in the shape a card needs.
|
|
25
|
+
*
|
|
26
|
+
* Deliberately one merged type rather than three arrays: the caller's job is
|
|
27
|
+
* to render "what needs me" as a single list, and merging on the client would
|
|
28
|
+
* mean every client reimplements the same sort.
|
|
29
|
+
*/
|
|
30
|
+
export interface DecisionRef {
|
|
31
|
+
kind: DecisionKind;
|
|
32
|
+
/** agreementId, or approvalId for a spend. Unique within a kind. */
|
|
33
|
+
id: string;
|
|
34
|
+
/** What it is, in the counterparty's own words where there are any. */
|
|
35
|
+
title: string;
|
|
36
|
+
/** Who is asking. Labels, never account or agent ids. */
|
|
37
|
+
requestedByDisplayName: string | null;
|
|
38
|
+
requestedByOrgName: string | null;
|
|
39
|
+
requestedAt: string | null;
|
|
40
|
+
/**
|
|
41
|
+
* Money at stake in cents: an agreement's price, or the amount a paused
|
|
42
|
+
* transfer would move. Null when nothing is owed.
|
|
43
|
+
*/
|
|
44
|
+
amountCents: number | null;
|
|
45
|
+
/** Present where the object expires on its own, e.g. a spend approval. */
|
|
46
|
+
expiresAt: string | null;
|
|
47
|
+
decideVia: DecideVia;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* One row in the reader's merged view. A reference, never content — following
|
|
51
|
+
* it (a chat read, a task read) is where the caller's grants are enforced.
|
|
52
|
+
*
|
|
53
|
+
* "Is this mine to act on" is `assigneeId === my agent id`, nothing else. A
|
|
54
|
+
* row without that stamp is context the reader may open, never a wake.
|
|
55
|
+
*/
|
|
56
|
+
export interface InboxDeliveryRef {
|
|
57
|
+
/**
|
|
58
|
+
* Which vocabulary this row speaks — the same list the emitting event and
|
|
59
|
+
* the stored delivery enum use. Typed rather than `string` so a consumer's
|
|
60
|
+
* switch over it can be exhaustive: a delivery kind nobody handles is how a
|
|
61
|
+
* deliverable gets acked unread.
|
|
62
|
+
*/
|
|
63
|
+
kind: ResourceKind;
|
|
64
|
+
resourceId: string;
|
|
65
|
+
/** One emit, one id — collapse copies of the same event on this. */
|
|
66
|
+
eventId: string;
|
|
67
|
+
/** The mailbox this copy lives in. */
|
|
68
|
+
partyId: string;
|
|
69
|
+
partyKind: InboxPartyKind;
|
|
70
|
+
/** The ONE agent stamped to act; null when nothing has to. */
|
|
71
|
+
assigneeId: string | null;
|
|
72
|
+
/** The owner's human should see this. */
|
|
73
|
+
needsHuman: boolean;
|
|
74
|
+
chatId: string | null;
|
|
75
|
+
agreementId: string | null;
|
|
76
|
+
taskId: string | null;
|
|
77
|
+
/** ISO instant of the event; the axis `ackTo` moves along. */
|
|
78
|
+
ts: string;
|
|
79
|
+
/**
|
|
80
|
+
* Who wrote the event (claimer or buyer on store claims, responder on
|
|
81
|
+
* approve). Null on older rows and system emits.
|
|
82
|
+
*/
|
|
83
|
+
actorId: string | null;
|
|
84
|
+
/**
|
|
85
|
+
* Emitter lifecycle hint when present (e.g. a fulfilled or rejected
|
|
86
|
+
* connection request). Older rows omit it; treat absent as an unspecified
|
|
87
|
+
* doorbell.
|
|
88
|
+
*/
|
|
89
|
+
reason?: string | null;
|
|
90
|
+
/** Connection to use after a fulfilled first-hop request. */
|
|
91
|
+
connectionId?: string | null;
|
|
92
|
+
/** Grant minted for the requesting agent on fulfill. */
|
|
93
|
+
grantId?: string | null;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* One thing on the person, from whichever writer owns it.
|
|
97
|
+
*
|
|
98
|
+
* Two writers, one list: the decisions read says what is waiting and how to
|
|
99
|
+
* answer it, the party mailboxes say what landed. Both are "this is on you",
|
|
100
|
+
* so the merge happens once on the server rather than in every client — two
|
|
101
|
+
* clients merging the same two reads is two totals that can disagree, with
|
|
102
|
+
* nobody to arbitrate.
|
|
103
|
+
*/
|
|
104
|
+
export type AttentionEntry = {
|
|
105
|
+
/** Stable per row, so a list can key on it without inventing one. */
|
|
106
|
+
key: string;
|
|
107
|
+
/** When it arrived; null on a decision the source never timestamped. */
|
|
108
|
+
at: string | null;
|
|
109
|
+
source: 'decision';
|
|
110
|
+
decision: DecisionRef;
|
|
111
|
+
} | {
|
|
112
|
+
key: string;
|
|
113
|
+
at: string;
|
|
114
|
+
source: 'notice';
|
|
115
|
+
notice: InboxDeliveryRef;
|
|
116
|
+
};
|
|
117
|
+
/** How many notice rows one attention window can carry. */
|
|
118
|
+
export declare const INBOX_ATTENTION_NOTICE_CAP = 50;
|
|
119
|
+
/**
|
|
120
|
+
* `GET /inbox/attention` — what needs an answer, then what needs eyes.
|
|
121
|
+
*
|
|
122
|
+
* A window onto the one party-owned log, not a log of its own. Three rules
|
|
123
|
+
* hold it to that:
|
|
124
|
+
* • `message` rows never appear — the room renders conversation and counts
|
|
125
|
+
* its own unread, and a second count here is the disagreement this merge
|
|
126
|
+
* exists to prevent. They are excluded server-side, so they are not
|
|
127
|
+
* counted here either.
|
|
128
|
+
* • Workspace scoped. A lane fences a window; the unfiltered human log at
|
|
129
|
+
* `GET /inbox/feed` stays cross-org and is not what an app reads.
|
|
130
|
+
* • `seenTo` covers ONLY the notice rows this response actually carried, so
|
|
131
|
+
* marking seen can never advance past mail no surface displayed.
|
|
132
|
+
*/
|
|
133
|
+
export interface AttentionFeed {
|
|
134
|
+
asOf: string;
|
|
135
|
+
/** Decisions first — somebody is blocked on those — then notices, newest first. */
|
|
136
|
+
items: AttentionEntry[];
|
|
137
|
+
/** Decisions the server capped. Nothing local can expand into them. */
|
|
138
|
+
truncated: number;
|
|
139
|
+
/** Older unseen notice rows exist past the cap; they stay unseen. */
|
|
140
|
+
hasMore: boolean;
|
|
141
|
+
/**
|
|
142
|
+
* Opaque token for `POST /inbox/seen`, covering the notice rows above and
|
|
143
|
+
* nothing else. Null when the block carried no notices. Never constructed
|
|
144
|
+
* or parsed by a client.
|
|
145
|
+
*/
|
|
146
|
+
seenTo: string | null;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* `POST /inbox/seen`.
|
|
150
|
+
*
|
|
151
|
+
* `upTo` is the `seenTo` token from the attention window being marked — pass
|
|
152
|
+
* it back verbatim.
|
|
153
|
+
*/
|
|
154
|
+
export interface MarkInboxSeenBody {
|
|
155
|
+
upTo: string;
|
|
156
|
+
}
|
package/dist/inbox.js
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire contracts for the Ziggs API.
|
|
3
|
+
*
|
|
4
|
+
* The server is authoritative: every shape here is what it actually sends or
|
|
5
|
+
* accepts. Nothing in this package reaches the network or reads an
|
|
6
|
+
* environment — it is types plus the closed vocabularies they are built from,
|
|
7
|
+
* so any runtime can import it.
|
|
8
|
+
*/
|
|
9
|
+
export * from './vocabulary';
|
|
10
|
+
export * from './chat';
|
|
11
|
+
export * from './inbox';
|
|
12
|
+
export * from './error';
|
|
13
|
+
export * from './socket';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
/**
|
|
18
|
+
* Wire contracts for the Ziggs API.
|
|
19
|
+
*
|
|
20
|
+
* The server is authoritative: every shape here is what it actually sends or
|
|
21
|
+
* accepts. Nothing in this package reaches the network or reads an
|
|
22
|
+
* environment — it is types plus the closed vocabularies they are built from,
|
|
23
|
+
* so any runtime can import it.
|
|
24
|
+
*/
|
|
25
|
+
__exportStar(require("./vocabulary"), exports);
|
|
26
|
+
__exportStar(require("./chat"), exports);
|
|
27
|
+
__exportStar(require("./inbox"), exports);
|
|
28
|
+
__exportStar(require("./error"), exports);
|
|
29
|
+
__exportStar(require("./socket"), exports);
|
package/dist/socket.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Socket contracts.
|
|
3
|
+
*
|
|
4
|
+
* The socket is a receive-only accelerator: every frame here has an HTTP read
|
|
5
|
+
* that is the source of truth. A client that misses a frame catches up on its
|
|
6
|
+
* next read; a client that trusts a frame over the read will disagree with the
|
|
7
|
+
* server.
|
|
8
|
+
*/
|
|
9
|
+
import type { MessageWireDto } from './chat';
|
|
10
|
+
import type { ResourceKind } from './vocabulary';
|
|
11
|
+
/**
|
|
12
|
+
* A resource changed somewhere the reader can see.
|
|
13
|
+
*
|
|
14
|
+
* Deliberately minimal — no body content — so cross-party visibility questions
|
|
15
|
+
* are decided by the reader's subsequent authorized read, not by what shipped
|
|
16
|
+
* on the wire.
|
|
17
|
+
*/
|
|
18
|
+
export interface ResourceChangedFrame {
|
|
19
|
+
kind: ResourceKind;
|
|
20
|
+
/**
|
|
21
|
+
* One emit, one id. Every party copy of the event shares it, so a reader
|
|
22
|
+
* whose grants reach two parties of the same event can collapse the copies
|
|
23
|
+
* instead of seeing doubles.
|
|
24
|
+
*/
|
|
25
|
+
eventId?: string;
|
|
26
|
+
/** ISO timestamp set by the emitter. */
|
|
27
|
+
ts: string;
|
|
28
|
+
/** Primary id of the resource that changed. */
|
|
29
|
+
resourceId: string;
|
|
30
|
+
chatId?: string;
|
|
31
|
+
agreementId?: string;
|
|
32
|
+
taskId?: string;
|
|
33
|
+
/** Coarse change marker. */
|
|
34
|
+
change?: 'created' | 'updated' | 'state-changed';
|
|
35
|
+
/**
|
|
36
|
+
* Principal whose action produced this event, when the emit site knows it.
|
|
37
|
+
* Agents are not woken by their own writes; people still receive their own
|
|
38
|
+
* events, because a surface re-syncs from them.
|
|
39
|
+
*/
|
|
40
|
+
actorId?: string;
|
|
41
|
+
/** Lifecycle hint for a client (e.g. a proposal that was approved). */
|
|
42
|
+
reason?: string;
|
|
43
|
+
/** Connection to plug into after a fulfilled first-hop request. */
|
|
44
|
+
connectionId?: string;
|
|
45
|
+
/** Grant minted for the requesting agent on fulfill. */
|
|
46
|
+
grantId?: string;
|
|
47
|
+
orgId?: string | null;
|
|
48
|
+
/** Artifact visibility, when the event is about one. */
|
|
49
|
+
visibility?: 'chat' | 'public' | 'agent-private';
|
|
50
|
+
}
|
|
51
|
+
/** A message landed in a room the reader is in. */
|
|
52
|
+
export type ChatMessageNewFrame = MessageWireDto;
|
|
53
|
+
/**
|
|
54
|
+
* The reader advanced their own watermark somewhere else.
|
|
55
|
+
*
|
|
56
|
+
* Fanned out to a person's other live sessions so an unread badge clears on
|
|
57
|
+
* their other tabs and devices. Best-effort — the watermark row is the source
|
|
58
|
+
* of truth.
|
|
59
|
+
*/
|
|
60
|
+
export interface ChatReadFrame {
|
|
61
|
+
chatId: string;
|
|
62
|
+
lastReadAt: string;
|
|
63
|
+
lastReadMessageId: string | null;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Every frame a client receives, keyed by its wire name.
|
|
67
|
+
*
|
|
68
|
+
* Written as a map so a client can type one listener registry over it and be
|
|
69
|
+
* told when a frame it handles changes shape.
|
|
70
|
+
*/
|
|
71
|
+
export interface ServerToClientEvents {
|
|
72
|
+
'chat:message:new': ChatMessageNewFrame;
|
|
73
|
+
'chat:read': ChatReadFrame;
|
|
74
|
+
resource_changed: ResourceChangedFrame;
|
|
75
|
+
}
|
|
76
|
+
/** Every frame a client sends. */
|
|
77
|
+
export interface ClientToServerEvents {
|
|
78
|
+
'chat:join': string;
|
|
79
|
+
}
|
|
80
|
+
/** Wire names of the frames a client receives. */
|
|
81
|
+
export declare const SERVER_EVENTS: {
|
|
82
|
+
readonly chatMessageNew: "chat:message:new";
|
|
83
|
+
readonly chatRead: "chat:read";
|
|
84
|
+
readonly resourceChanged: "resource_changed";
|
|
85
|
+
};
|
|
86
|
+
/** Wire names of the frames a client sends. */
|
|
87
|
+
export declare const CLIENT_EVENTS: {
|
|
88
|
+
readonly chatJoin: "chat:join";
|
|
89
|
+
};
|
package/dist/socket.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CLIENT_EVENTS = exports.SERVER_EVENTS = void 0;
|
|
4
|
+
/** Wire names of the frames a client receives. */
|
|
5
|
+
exports.SERVER_EVENTS = {
|
|
6
|
+
chatMessageNew: 'chat:message:new',
|
|
7
|
+
chatRead: 'chat:read',
|
|
8
|
+
resourceChanged: 'resource_changed',
|
|
9
|
+
};
|
|
10
|
+
/** Wire names of the frames a client sends. */
|
|
11
|
+
exports.CLIENT_EVENTS = {
|
|
12
|
+
chatJoin: 'chat:join',
|
|
13
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The small closed vocabularies every other contract in this package speaks.
|
|
3
|
+
*
|
|
4
|
+
* These are value lists rather than bare unions because each one crosses a
|
|
5
|
+
* boundary where the values must exist at runtime: a socket frame, a stored
|
|
6
|
+
* enum, a switch a consumer needs to make exhaustive.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* What a grant lets its holder do with the scope.
|
|
10
|
+
*
|
|
11
|
+
* Ordered weakest to strongest, and the order is load-bearing: `write` implies
|
|
12
|
+
* `read`, `admit` implies both. A required kind is satisfied by any kind at
|
|
13
|
+
* least that strong.
|
|
14
|
+
*
|
|
15
|
+
* - `read` — see the scope and its contents. Observing, not participating.
|
|
16
|
+
* - `write` — take part: post entries, be a delivery participant.
|
|
17
|
+
* - `admit` — bring others in: issue grants on this scope to new holders.
|
|
18
|
+
*/
|
|
19
|
+
export declare const GRANT_ACCESS_KINDS: readonly ["read", "write", "admit"];
|
|
20
|
+
export type GrantAccessKind = (typeof GRANT_ACCESS_KINDS)[number];
|
|
21
|
+
/**
|
|
22
|
+
* The name a room can be reached by, when it has one.
|
|
23
|
+
*
|
|
24
|
+
* Most rooms have no address. What makes two opens land in the same room is
|
|
25
|
+
* that they name the same thing — a pair of principals, or an org.
|
|
26
|
+
*/
|
|
27
|
+
export declare const CHAT_ADDRESS_KINDS: readonly ["pair", "org"];
|
|
28
|
+
export type ChatAddressKind = (typeof CHAT_ADDRESS_KINDS)[number];
|
|
29
|
+
/**
|
|
30
|
+
* The kinds a resource-change event — and therefore a durable delivery row —
|
|
31
|
+
* can be. A consumer that switches on this can be made exhaustive.
|
|
32
|
+
*/
|
|
33
|
+
export declare const RESOURCE_KINDS: readonly ["message", "artifact", "task-state", "agreement", "request"];
|
|
34
|
+
export type ResourceKind = (typeof RESOURCE_KINDS)[number];
|
|
35
|
+
/** What kind of thing is waiting on a person. */
|
|
36
|
+
export type DecisionKind = 'proposal' | 'connection_request' | 'spend_approval';
|
|
37
|
+
/** Which mailbox a delivery copy lives in. */
|
|
38
|
+
export type InboxPartyKind = 'human' | 'org';
|
|
39
|
+
/** How a principal is party to an agreement. */
|
|
40
|
+
export type PartyRole = 'payer' | 'provider' | 'creator' | 'proposedTo' | 'providerPrincipal';
|
|
41
|
+
/** What kind of principal sent a message. */
|
|
42
|
+
export type MessageSenderType = 'user' | 'agent' | 'service' | 'system';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The small closed vocabularies every other contract in this package speaks.
|
|
4
|
+
*
|
|
5
|
+
* These are value lists rather than bare unions because each one crosses a
|
|
6
|
+
* boundary where the values must exist at runtime: a socket frame, a stored
|
|
7
|
+
* enum, a switch a consumer needs to make exhaustive.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.RESOURCE_KINDS = exports.CHAT_ADDRESS_KINDS = exports.GRANT_ACCESS_KINDS = void 0;
|
|
11
|
+
/**
|
|
12
|
+
* What a grant lets its holder do with the scope.
|
|
13
|
+
*
|
|
14
|
+
* Ordered weakest to strongest, and the order is load-bearing: `write` implies
|
|
15
|
+
* `read`, `admit` implies both. A required kind is satisfied by any kind at
|
|
16
|
+
* least that strong.
|
|
17
|
+
*
|
|
18
|
+
* - `read` — see the scope and its contents. Observing, not participating.
|
|
19
|
+
* - `write` — take part: post entries, be a delivery participant.
|
|
20
|
+
* - `admit` — bring others in: issue grants on this scope to new holders.
|
|
21
|
+
*/
|
|
22
|
+
exports.GRANT_ACCESS_KINDS = ['read', 'write', 'admit'];
|
|
23
|
+
/**
|
|
24
|
+
* The name a room can be reached by, when it has one.
|
|
25
|
+
*
|
|
26
|
+
* Most rooms have no address. What makes two opens land in the same room is
|
|
27
|
+
* that they name the same thing — a pair of principals, or an org.
|
|
28
|
+
*/
|
|
29
|
+
exports.CHAT_ADDRESS_KINDS = ['pair', 'org'];
|
|
30
|
+
/**
|
|
31
|
+
* The kinds a resource-change event — and therefore a durable delivery row —
|
|
32
|
+
* can be. A consumer that switches on this can be made exhaustive.
|
|
33
|
+
*/
|
|
34
|
+
exports.RESOURCE_KINDS = [
|
|
35
|
+
'message',
|
|
36
|
+
'artifact',
|
|
37
|
+
'task-state',
|
|
38
|
+
'agreement',
|
|
39
|
+
/** Marketplace request doorbell: a typed inbox channel, not a chat. */
|
|
40
|
+
'request',
|
|
41
|
+
];
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ziggs-ai/contracts",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Wire contracts for the Ziggs API — response shapes, error body, and socket events, shared by every client.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc -p tsconfig.json",
|
|
13
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
14
|
+
"prepack": "rm -rf dist && npm run build"
|
|
15
|
+
},
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=18"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"ziggs",
|
|
21
|
+
"contracts",
|
|
22
|
+
"types"
|
|
23
|
+
],
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public",
|
|
27
|
+
"registry": "https://registry.npmjs.org/"
|
|
28
|
+
}
|
|
29
|
+
}
|