@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 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
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CHAT_SNIPPET_CAP = void 0;
4
+ /** Max characters of a last-message snippet. */
5
+ exports.CHAT_SNIPPET_CAP = 280;
@@ -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
+ }
@@ -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
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.INBOX_ATTENTION_NOTICE_CAP = void 0;
4
+ /** How many notice rows one attention window can carry. */
5
+ exports.INBOX_ATTENTION_NOTICE_CAP = 50;
@@ -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);
@@ -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
+ }