@ccmsg/protocol 1.19.0 → 1.21.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 +2 -2
- package/package.json +1 -1
- package/src/attributes.ts +77 -52
- package/src/common/auth.ts +28 -28
- package/src/common/hello.ts +72 -34
- package/src/common/ping.ts +2 -2
- package/src/common/shutdown.ts +4 -4
- package/src/common/topics.ts +36 -21
- package/src/control/agents.ts +23 -4
- package/src/control/dump.ts +73 -66
- package/src/control/files.ts +24 -24
- package/src/control/instances.ts +21 -0
- package/src/control/kv.ts +6 -6
- package/src/control/launcher.ts +4 -4
- package/src/control/llm.ts +8 -8
- package/src/control/peers.ts +52 -68
- package/src/control/sandbox.ts +4 -4
- package/src/control/session-errors.ts +3 -3
- package/src/control/session-status.ts +2 -2
- package/src/control/session.ts +36 -35
- package/src/control/transcript.ts +6 -6
- package/src/control/translate.ts +2 -2
- package/src/envelope.ts +2 -2
- package/src/errors.ts +1 -1
- package/src/fixtures/common.ts +45 -32
- package/src/fixtures/control.ts +67 -67
- package/src/fixtures/index.ts +78 -63
- package/src/fixtures/messaging.ts +9 -9
- package/src/fixtures/topics.ts +40 -19
- package/src/identifiers.ts +8 -6
- package/src/index.ts +1 -0
- package/src/messaging/message.ts +3 -3
- package/src/messaging/notify.ts +3 -3
- package/src/messaging/say.ts +9 -9
- package/src/schemas.ts +81 -67
- package/src/session-meta.ts +2 -1
package/src/control/peers.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { type Static, Type } from "@sinclair/typebox";
|
|
2
|
-
import { InstanceInfo } from "../common/hello.ts";
|
|
3
2
|
import { topicFrame } from "../envelope.ts";
|
|
4
3
|
import { InstanceId, Sid, Timestamp } from "../identifiers.ts";
|
|
5
4
|
import { SessionMetaFields } from "../session-meta.ts";
|
|
@@ -31,8 +30,9 @@ export const SessionState = Type.Union(
|
|
|
31
30
|
);
|
|
32
31
|
export type SessionState = Static<typeof SessionState>;
|
|
33
32
|
|
|
34
|
-
/** How long a session
|
|
35
|
-
*
|
|
33
|
+
/** How long a lost session's row is kept after it was last seen, before the
|
|
34
|
+
* instance forgets it and the row leaves as a removal. The same window the inbox
|
|
35
|
+
* keeps undelivered messages for: what a person comes back to
|
|
36
36
|
* is one thing — the session and what was said to it — so the two cannot expire
|
|
37
37
|
* at different times. */
|
|
38
38
|
export const LAST_LIVE_RETENTION_MS = 7 * 24 * 60 * 60 * 1000;
|
|
@@ -57,11 +57,20 @@ export const StaleClientInfo = Type.Object(
|
|
|
57
57
|
);
|
|
58
58
|
export type StaleClientInfo = Static<typeof StaleClientInfo>;
|
|
59
59
|
|
|
60
|
-
/** One session an instance
|
|
60
|
+
/** One session an instance knows of, connected or lost.
|
|
61
61
|
*
|
|
62
62
|
* The paths are the host's, so the entry names the instance holding them:
|
|
63
63
|
* `peers` carries every instance's sessions in one list, and two hosts' paths
|
|
64
|
-
* would otherwise be indistinguishable.
|
|
64
|
+
* would otherwise be indistinguishable. The pair `instance` and `sid` is also
|
|
65
|
+
* what a later row is matched against, since one session lives on one
|
|
66
|
+
* instance.
|
|
67
|
+
*
|
|
68
|
+
* Connected and lost sessions are one kind of row rather than two lists: a
|
|
69
|
+
* session registering or going quiet moves it between the two, and a row that
|
|
70
|
+
* changed lists while keeping its identity is an update of that row. Which it
|
|
71
|
+
* is now is the `state` below, and the fields that only a lost session has
|
|
72
|
+
* (`last_seen_at`, `stopped_at`, and what it must resume as) are stated
|
|
73
|
+
* alongside the connection fields it kept. */
|
|
65
74
|
export const PeerInfo = Type.Object(
|
|
66
75
|
{
|
|
67
76
|
sid: Sid,
|
|
@@ -81,10 +90,10 @@ export const PeerInfo = Type.Object(
|
|
|
81
90
|
* notification's `sid_label` — so the material for those is on the row that
|
|
82
91
|
* every client already holds. */
|
|
83
92
|
title: Type.Optional(SessionMetaFields.title),
|
|
84
|
-
/** How this session stands
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
93
|
+
/** How this session stands, which is also what separates a connected row
|
|
94
|
+
* from one its instance has lost. Absent from an instance that states no
|
|
95
|
+
* classification, and a client then shows the session without grouping it
|
|
96
|
+
* rather than guessing one. */
|
|
88
97
|
state: Type.Optional(SessionState),
|
|
89
98
|
/** Set while a person has pinned this session. Absent means not pinned. */
|
|
90
99
|
pinned: Type.Optional(Type.Boolean()),
|
|
@@ -129,77 +138,52 @@ export const PeerInfo = Type.Object(
|
|
|
129
138
|
protocol_version: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
130
139
|
/** Set while some client of this session is being refused. */
|
|
131
140
|
stale_client: Type.Optional(StaleClientInfo),
|
|
141
|
+
/** The newest instant this session is known to have been alive. Stated on a
|
|
142
|
+
* row its instance has lost, where it is what the retention window above is
|
|
143
|
+
* measured from; a connected row is alive now and states none. */
|
|
144
|
+
last_seen_at: Type.Optional(Timestamp),
|
|
145
|
+
/** When the session said it was stopping. Its presence is what makes a lost
|
|
146
|
+
* session a pause rather than a disappearance: one that goes without a word
|
|
147
|
+
* leaves nothing to stamp here. */
|
|
148
|
+
stopped_at: Type.Optional(Timestamp),
|
|
149
|
+
/** What its last turn ran as, in the transcript's own spelling, read back
|
|
150
|
+
* from the transcript rather than copied from the connection: what a lost
|
|
151
|
+
* session must resume as is a property of where it actually stopped. A
|
|
152
|
+
* resume must not quietly switch the session to something else. */
|
|
153
|
+
model: Type.Optional(SessionMetaFields.model),
|
|
154
|
+
effort: Type.Optional(SessionMetaFields.effort),
|
|
132
155
|
},
|
|
133
156
|
{ $id: "PeerInfo" },
|
|
134
157
|
);
|
|
135
158
|
export type PeerInfo = Static<typeof PeerInfo>;
|
|
136
159
|
|
|
137
|
-
/**
|
|
138
|
-
*
|
|
160
|
+
/** A row that is gone: the session has been forgotten by the instance that
|
|
161
|
+
* held it, whether its retention window ran out or a person dropped it.
|
|
139
162
|
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
|
|
145
|
-
* written. An entry leaves this list the moment its session registers again, so
|
|
146
|
-
* a fully recovered host shows none. */
|
|
147
|
-
export const LastLiveSession = Type.Object(
|
|
163
|
+
* A removal has to be a marked element rather than an absence, since a frame
|
|
164
|
+
* carries only what changed and an absence in it says nothing. It names the
|
|
165
|
+
* same pair every row is matched by and nothing else — there is no row left to
|
|
166
|
+
* describe. */
|
|
167
|
+
export const PeerRemoved = Type.Object(
|
|
148
168
|
{
|
|
149
169
|
sid: Sid,
|
|
150
170
|
instance: InstanceId,
|
|
151
|
-
|
|
152
|
-
ws: SessionMetaFields.ws,
|
|
153
|
-
cwd: SessionMetaFields.cwd,
|
|
154
|
-
/** Also where the model and effort below were read from. */
|
|
155
|
-
transcript_path: Type.Optional(SessionMetaFields.transcript_path),
|
|
156
|
-
repo_root: Type.Optional(SessionMetaFields.repo_root),
|
|
157
|
-
branch: Type.Optional(SessionMetaFields.branch),
|
|
158
|
-
title: Type.Optional(SessionMetaFields.title),
|
|
159
|
-
/** How this session stands: `paused` or `disappeared`, which the
|
|
160
|
-
* `stopped_at` below is what separates. Absent from an instance that states
|
|
161
|
-
* no classification. */
|
|
162
|
-
state: Type.Optional(SessionState),
|
|
163
|
-
/** Set while a person has pinned this session. Absent means not pinned. */
|
|
164
|
-
pinned: Type.Optional(Type.Boolean()),
|
|
165
|
-
connected_at: Type.Optional(Timestamp),
|
|
166
|
-
/** The newest instant this session is known to have been alive. */
|
|
167
|
-
last_seen_at: Timestamp,
|
|
168
|
-
/** When the session said it was stopping. Its presence is what makes this a
|
|
169
|
-
* pause rather than a disappearance: a session that goes without a word
|
|
170
|
-
* leaves nothing to stamp here. */
|
|
171
|
-
stopped_at: Type.Optional(Timestamp),
|
|
172
|
-
/** What its last turn ran as, in the transcript's own spelling. A resume
|
|
173
|
-
* must not quietly switch the session to something else. */
|
|
174
|
-
model: Type.Optional(SessionMetaFields.model),
|
|
175
|
-
effort: Type.Optional(SessionMetaFields.effort),
|
|
171
|
+
removed: Type.Literal(true),
|
|
176
172
|
},
|
|
177
|
-
{ $id: "
|
|
173
|
+
{ $id: "PeerRemoved" },
|
|
178
174
|
);
|
|
179
|
-
export type
|
|
175
|
+
export type PeerRemoved = Static<typeof PeerRemoved>;
|
|
176
|
+
|
|
177
|
+
export const PeerElement = Type.Union([PeerInfo, PeerRemoved], { $id: "PeerElement" });
|
|
178
|
+
export type PeerElement = Static<typeof PeerElement>;
|
|
180
179
|
|
|
181
180
|
/** The `peers` topic.
|
|
182
181
|
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
182
|
+
* Elements: each frame carries the rows that changed, matched by their
|
|
183
|
+
* `instance` and `sid`, and rows it does not name are left as they were. A row
|
|
184
|
+
* changes on its own — one session becoming busy, another going quiet — and
|
|
185
|
+
* restating every row an instance knows would send a list to report one field.
|
|
186
186
|
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
export const PeersFrame = topicFrame(
|
|
190
|
-
"peers",
|
|
191
|
-
Type.Object({
|
|
192
|
-
peers: Type.Array(PeerInfo),
|
|
193
|
-
last_live: Type.Array(LastLiveSession),
|
|
194
|
-
/** The instances the sending one can see, itself included, as `hello`
|
|
195
|
-
* answers it. Carried here so that a link going down is something a
|
|
196
|
-
* subscriber learns from the topic it is already on, rather than by
|
|
197
|
-
* greeting again to find out. It is the sender's own view like the two
|
|
198
|
-
* lists above — `reachable` says whether that instance can reach the one it
|
|
199
|
-
* names, which two instances may legitimately disagree about.
|
|
200
|
-
*
|
|
201
|
-
* Absent from an instance that states no view; a client then keeps what it
|
|
202
|
-
* last knew rather than reading the omission as nothing being reachable. */
|
|
203
|
-
instances: Type.Optional(Type.Array(InstanceInfo)),
|
|
204
|
-
}),
|
|
205
|
-
);
|
|
187
|
+
* The opening `snapshot: true` frame carries every row the instance holds,
|
|
188
|
+
* connected and lost alike. */
|
|
189
|
+
export const PeersFrame = topicFrame("peers", Type.Object({ peers: Type.Array(PeerElement) }));
|
package/src/control/sandbox.ts
CHANGED
|
@@ -37,8 +37,8 @@ export const SandboxGrantResult = Type.Object({
|
|
|
37
37
|
});
|
|
38
38
|
export type SandboxGrantResult = Static<typeof SandboxGrantResult>;
|
|
39
39
|
|
|
40
|
-
export const SandboxGrantRequest = request("
|
|
41
|
-
export const SandboxGrantResponse = response("
|
|
40
|
+
export const SandboxGrantRequest = request("sandbox.grant", SandboxGrantArgs);
|
|
41
|
+
export const SandboxGrantResponse = response("sandbox.grant", SandboxGrantResult);
|
|
42
42
|
|
|
43
43
|
/** Ends a grant early, as when a preview is closed.
|
|
44
44
|
*
|
|
@@ -54,5 +54,5 @@ export type SandboxRevokeArgs = Static<typeof SandboxRevokeArgs>;
|
|
|
54
54
|
export const SandboxRevokeResult = Type.Object({});
|
|
55
55
|
export type SandboxRevokeResult = Static<typeof SandboxRevokeResult>;
|
|
56
56
|
|
|
57
|
-
export const SandboxRevokeRequest = request("
|
|
58
|
-
export const SandboxRevokeResponse = response("
|
|
57
|
+
export const SandboxRevokeRequest = request("sandbox.revoke", SandboxRevokeArgs);
|
|
58
|
+
export const SandboxRevokeResponse = response("sandbox.revoke", SandboxRevokeResult);
|
|
@@ -10,10 +10,10 @@ export const SessionErrorEntry = Type.Intersect(
|
|
|
10
10
|
);
|
|
11
11
|
export type SessionErrorEntry = Static<typeof SessionErrorEntry>;
|
|
12
12
|
|
|
13
|
-
/** The `
|
|
13
|
+
/** The `session.errors` topic: every connected session currently stopped on a
|
|
14
14
|
* harness error.
|
|
15
15
|
*
|
|
16
|
-
* Not per session, unlike `
|
|
16
|
+
* Not per session, unlike `session.status`. A client showing a list of sessions
|
|
17
17
|
* has to mark the stopped ones, and subscribing to a whole status fold for each
|
|
18
18
|
* visible session is the cost this exists to avoid: the instance folds the one
|
|
19
19
|
* error pattern over every connected session instead.
|
|
@@ -22,6 +22,6 @@ export type SessionErrorEntry = Static<typeof SessionErrorEntry>;
|
|
|
22
22
|
* rather than appearing with an empty error, so a client that missed a frame
|
|
23
23
|
* still converges on the next one. */
|
|
24
24
|
export const SessionErrorsFrame = topicFrame(
|
|
25
|
-
"
|
|
25
|
+
"session.errors",
|
|
26
26
|
Type.Object({ errors: Type.Array(SessionErrorEntry) }),
|
|
27
27
|
);
|
|
@@ -303,9 +303,9 @@ export const SessionStatusSnapshot = Type.Object(
|
|
|
303
303
|
);
|
|
304
304
|
export type SessionStatusSnapshot = Static<typeof SessionStatusSnapshot>;
|
|
305
305
|
|
|
306
|
-
/** The `
|
|
306
|
+
/** The `session.status:<sid>` topic. Whole-value: the fold is recomputed and
|
|
307
307
|
* sent entire whenever something in the transcript changes it. */
|
|
308
308
|
export const SessionStatusFrame = topicFrame(
|
|
309
|
-
"
|
|
309
|
+
"session.status",
|
|
310
310
|
Type.Intersect([Type.Object({ sid: Sid }), SessionStatusSnapshot]),
|
|
311
311
|
);
|
package/src/control/session.ts
CHANGED
|
@@ -25,8 +25,8 @@ export const SessionKillResult = Type.Object({
|
|
|
25
25
|
});
|
|
26
26
|
export type SessionKillResult = Static<typeof SessionKillResult>;
|
|
27
27
|
|
|
28
|
-
export const SessionKillRequest = request("
|
|
29
|
-
export const SessionKillResponse = response("
|
|
28
|
+
export const SessionKillRequest = request("session.kill", SessionKillArgs);
|
|
29
|
+
export const SessionKillResponse = response("session.kill", SessionKillResult);
|
|
30
30
|
|
|
31
31
|
/** How long a title may be. A title is typed into a terminal and shown as a
|
|
32
32
|
* session's first line, where anything longer is unreadable whatever the
|
|
@@ -63,8 +63,8 @@ export const SessionRenameResult = Type.Object({
|
|
|
63
63
|
});
|
|
64
64
|
export type SessionRenameResult = Static<typeof SessionRenameResult>;
|
|
65
65
|
|
|
66
|
-
export const SessionRenameRequest = request("
|
|
67
|
-
export const SessionRenameResponse = response("
|
|
66
|
+
export const SessionRenameRequest = request("session.rename", SessionRenameArgs);
|
|
67
|
+
export const SessionRenameResponse = response("session.rename", SessionRenameResult);
|
|
68
68
|
|
|
69
69
|
/** Reads the environment of a session's own process.
|
|
70
70
|
*
|
|
@@ -84,8 +84,8 @@ export const SessionEnvReadResult = Type.Object({
|
|
|
84
84
|
});
|
|
85
85
|
export type SessionEnvReadResult = Static<typeof SessionEnvReadResult>;
|
|
86
86
|
|
|
87
|
-
export const SessionEnvReadRequest = request("
|
|
88
|
-
export const SessionEnvReadResponse = response("
|
|
87
|
+
export const SessionEnvReadRequest = request("session.env.read", SessionEnvReadArgs);
|
|
88
|
+
export const SessionEnvReadResponse = response("session.env.read", SessionEnvReadResult);
|
|
89
89
|
|
|
90
90
|
/** Searches the transcripts of sessions that have run on this instance,
|
|
91
91
|
* including ones long finished. */
|
|
@@ -162,8 +162,8 @@ export const SessionSearchResult = Type.Object({
|
|
|
162
162
|
});
|
|
163
163
|
export type SessionSearchResult = Static<typeof SessionSearchResult>;
|
|
164
164
|
|
|
165
|
-
export const SessionSearchRequest = request("
|
|
166
|
-
export const SessionSearchResponse = response("
|
|
165
|
+
export const SessionSearchRequest = request("session.search", SessionSearchArgs);
|
|
166
|
+
export const SessionSearchResponse = response("session.search", SessionSearchResult);
|
|
167
167
|
|
|
168
168
|
/** Writes a session's dump to a file on the instance's host and answers with
|
|
169
169
|
* its path.
|
|
@@ -201,7 +201,7 @@ export const SessionDumpWriteArgs = Type.Object({
|
|
|
201
201
|
* thing in the vocabulary the rest of the selection is written in. */
|
|
202
202
|
no_thinking: Type.Optional(Type.Boolean()),
|
|
203
203
|
/** Leave out the machinery of in-process agents, which
|
|
204
|
-
* `["-message
|
|
204
|
+
* `["-message.sub", "-tool.Agent"]` also says. */
|
|
205
205
|
no_agent: Type.Optional(Type.Boolean()),
|
|
206
206
|
});
|
|
207
207
|
export type SessionDumpWriteArgs = Static<typeof SessionDumpWriteArgs>;
|
|
@@ -221,16 +221,16 @@ export const SessionDumpWriteResult = Type.Object({
|
|
|
221
221
|
});
|
|
222
222
|
export type SessionDumpWriteResult = Static<typeof SessionDumpWriteResult>;
|
|
223
223
|
|
|
224
|
-
export const SessionDumpWriteRequest = request("
|
|
225
|
-
export const SessionDumpWriteResponse = response("
|
|
224
|
+
export const SessionDumpWriteRequest = request("session.dump.write", SessionDumpWriteArgs);
|
|
225
|
+
export const SessionDumpWriteResponse = response("session.dump.write", SessionDumpWriteResult);
|
|
226
226
|
|
|
227
227
|
/** Asks where a forked session stopped being a copy of its ancestor.
|
|
228
228
|
*
|
|
229
229
|
* Forking duplicates the ancestor's records keeping each record id, so nothing
|
|
230
230
|
* inside the file marks the seam; finding it means comparing against the
|
|
231
231
|
* sibling transcripts the instance can already enumerate. */
|
|
232
|
-
export const
|
|
233
|
-
export type
|
|
232
|
+
export const SessionForkOriginReadArgs = Type.Object({ sid: Sid });
|
|
233
|
+
export type SessionForkOriginReadArgs = Static<typeof SessionForkOriginReadArgs>;
|
|
234
234
|
|
|
235
235
|
export const ForkOrigin = Type.Object(
|
|
236
236
|
{
|
|
@@ -245,38 +245,39 @@ export const ForkOrigin = Type.Object(
|
|
|
245
245
|
);
|
|
246
246
|
export type ForkOrigin = Static<typeof ForkOrigin>;
|
|
247
247
|
|
|
248
|
-
export const
|
|
248
|
+
export const SessionForkOriginReadResult = Type.Object({
|
|
249
249
|
/** Absent both when the session is no fork and when it is one whose ancestor
|
|
250
250
|
* file is gone. Nothing left on disk tells those two apart, and neither has a
|
|
251
251
|
* seam to place. */
|
|
252
252
|
origin: Type.Optional(ForkOrigin),
|
|
253
253
|
});
|
|
254
|
-
export type
|
|
254
|
+
export type SessionForkOriginReadResult = Static<typeof SessionForkOriginReadResult>;
|
|
255
255
|
|
|
256
|
-
export const
|
|
257
|
-
|
|
256
|
+
export const SessionForkOriginReadRequest = request(
|
|
257
|
+
"session.fork.origin.read",
|
|
258
|
+
SessionForkOriginReadArgs,
|
|
259
|
+
);
|
|
260
|
+
export const SessionForkOriginReadResponse = response(
|
|
261
|
+
"session.fork.origin.read",
|
|
262
|
+
SessionForkOriginReadResult,
|
|
263
|
+
);
|
|
258
264
|
|
|
259
|
-
/**
|
|
260
|
-
*
|
|
265
|
+
/** A person dropping one session an instance still holds a row for, which is
|
|
266
|
+
* the other way a row reaches `peers` as a `PeerRemoved` — the one that does
|
|
267
|
+
* not wait for the retention window.
|
|
261
268
|
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
export const
|
|
267
|
-
export type
|
|
269
|
+
* Forgetting touches the row alone: the session stays resumable by every other
|
|
270
|
+
* route, and an instance that later sees it connected records it again. An
|
|
271
|
+
* unknown session is not an error — two clients pressing the same button is the
|
|
272
|
+
* ordinary case, and the caller's goal holds either way. */
|
|
273
|
+
export const SessionForgetArgs = Type.Object({ sid: Sid });
|
|
274
|
+
export type SessionForgetArgs = Static<typeof SessionForgetArgs>;
|
|
268
275
|
|
|
269
|
-
export const
|
|
276
|
+
export const SessionForgetResult = Type.Object({
|
|
270
277
|
/** Whether the entry was there to remove. */
|
|
271
278
|
removed: Type.Boolean(),
|
|
272
279
|
});
|
|
273
|
-
export type
|
|
280
|
+
export type SessionForgetResult = Static<typeof SessionForgetResult>;
|
|
274
281
|
|
|
275
|
-
export const
|
|
276
|
-
|
|
277
|
-
SessionLastLiveRemoveArgs,
|
|
278
|
-
);
|
|
279
|
-
export const SessionLastLiveRemoveResponse = response(
|
|
280
|
-
"session_last_live_remove",
|
|
281
|
-
SessionLastLiveRemoveResult,
|
|
282
|
-
);
|
|
282
|
+
export const SessionForgetRequest = request("session.forget", SessionForgetArgs);
|
|
283
|
+
export const SessionForgetResponse = response("session.forget", SessionForgetResult);
|
|
@@ -48,8 +48,8 @@ export const TranscriptReadResult = Type.Object({
|
|
|
48
48
|
});
|
|
49
49
|
export type TranscriptReadResult = Static<typeof TranscriptReadResult>;
|
|
50
50
|
|
|
51
|
-
export const TranscriptReadRequest = request("
|
|
52
|
-
export const TranscriptReadResponse = response("
|
|
51
|
+
export const TranscriptReadRequest = request("transcript.read", TranscriptReadArgs);
|
|
52
|
+
export const TranscriptReadResponse = response("transcript.read", TranscriptReadResult);
|
|
53
53
|
|
|
54
54
|
/** Reads a slice of a transcript as the items it was read into.
|
|
55
55
|
*
|
|
@@ -115,13 +115,13 @@ export const TranscriptItemsReadResult = Type.Object({
|
|
|
115
115
|
});
|
|
116
116
|
export type TranscriptItemsReadResult = Static<typeof TranscriptItemsReadResult>;
|
|
117
117
|
|
|
118
|
-
export const TranscriptItemsReadRequest = request("
|
|
118
|
+
export const TranscriptItemsReadRequest = request("transcript.items.read", TranscriptItemsReadArgs);
|
|
119
119
|
export const TranscriptItemsReadResponse = response(
|
|
120
|
-
"
|
|
120
|
+
"transcript.items.read",
|
|
121
121
|
TranscriptItemsReadResult,
|
|
122
122
|
);
|
|
123
123
|
|
|
124
|
-
/** The `
|
|
124
|
+
/** The `transcript.items:<sid>` topic.
|
|
125
125
|
*
|
|
126
126
|
* What `transcript:<sid>` carries as appended bytes, carried as the items those
|
|
127
127
|
* bytes were read as. A subscriber holds a list it only ever appends to, so the
|
|
@@ -133,7 +133,7 @@ export const TranscriptItemsReadResponse = response(
|
|
|
133
133
|
* A record still being written is not classified until its line ends, which is
|
|
134
134
|
* the same rule the raw topic sends whole lines under. */
|
|
135
135
|
export const TranscriptItemsFrame = topicFrame(
|
|
136
|
-
"
|
|
136
|
+
"transcript.items",
|
|
137
137
|
Type.Object({
|
|
138
138
|
sid: Sid,
|
|
139
139
|
items: Type.Array(TranscriptItem),
|
package/src/control/translate.ts
CHANGED
|
@@ -29,5 +29,5 @@ export const TranslateRunResult = Type.Object({
|
|
|
29
29
|
});
|
|
30
30
|
export type TranslateRunResult = Static<typeof TranslateRunResult>;
|
|
31
31
|
|
|
32
|
-
export const TranslateRunRequest = request("
|
|
33
|
-
export const TranslateRunResponse = response("
|
|
32
|
+
export const TranslateRunRequest = request("translate.run", TranslateRunArgs);
|
|
33
|
+
export const TranslateRunResponse = response("translate.run", TranslateRunResult);
|
package/src/envelope.ts
CHANGED
|
@@ -24,7 +24,7 @@ export const MAX_FRAME_BYTES = 1_048_576;
|
|
|
24
24
|
export const CallerIdentity = Type.Object(
|
|
25
25
|
{
|
|
26
26
|
role: Role,
|
|
27
|
-
/** Present exactly when the role is `session`, as in `hello`. */
|
|
27
|
+
/** Present exactly when the role is `session`, as in `hello.session`. */
|
|
28
28
|
sid: Type.Optional(Sid),
|
|
29
29
|
},
|
|
30
30
|
{ $id: "CallerIdentity" },
|
|
@@ -102,7 +102,7 @@ export type ErrorResponse = Static<typeof ErrorResponse>;
|
|
|
102
102
|
|
|
103
103
|
/** A frame pushed on a topic the connection subscribed to.
|
|
104
104
|
*
|
|
105
|
-
* Snapshot and delta share one shape: the first frame after `
|
|
105
|
+
* Snapshot and delta share one shape: the first frame after `topic.subscribe`
|
|
106
106
|
* carries `snapshot: true` and the whole current value, and later frames carry
|
|
107
107
|
* the same payload type as a change. `instance` names where the frame came
|
|
108
108
|
* from, which is what keeps whole-value topics from several instances out of
|
package/src/errors.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { type Static, Type } from "@sinclair/typebox";
|
|
|
5
5
|
export const ERROR_CODES = [
|
|
6
6
|
// --- connection level (no single op owns these) ---
|
|
7
7
|
/** The request could not be dispatched at all: unparseable JSON, no `op`, no
|
|
8
|
-
* `request_id`, or a
|
|
8
|
+
* `request_id`, or a greeting announcing another protocol generation. */
|
|
9
9
|
"bad_request",
|
|
10
10
|
/** The op name is not in the op attribute table. */
|
|
11
11
|
"unknown_op",
|
package/src/fixtures/common.ts
CHANGED
|
@@ -4,10 +4,10 @@ import type {
|
|
|
4
4
|
AuthAssertResponse,
|
|
5
5
|
AuthChallengeRequest,
|
|
6
6
|
AuthChallengeResponse,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
AuthExtendRequest,
|
|
8
|
+
AuthExtendResponse,
|
|
9
|
+
AuthTokenRefreshRequest,
|
|
10
|
+
AuthTokenRefreshResponse,
|
|
11
11
|
AuthRegisterRequest,
|
|
12
12
|
AuthRegisterResponse,
|
|
13
13
|
AuthResolveRequest,
|
|
@@ -15,7 +15,12 @@ import type {
|
|
|
15
15
|
AuthRotateRequest,
|
|
16
16
|
AuthRotateResponse,
|
|
17
17
|
} from "../common/auth.ts";
|
|
18
|
-
import type {
|
|
18
|
+
import type {
|
|
19
|
+
HelloInstanceRequest,
|
|
20
|
+
HelloSessionRequest,
|
|
21
|
+
HelloSessionResponse,
|
|
22
|
+
HelloUserRequest,
|
|
23
|
+
} from "../common/hello.ts";
|
|
19
24
|
import type { InstancePingRequest, InstancePingResponse } from "../common/ping.ts";
|
|
20
25
|
import type {
|
|
21
26
|
InstanceShutdownRequest,
|
|
@@ -33,10 +38,9 @@ import { FIXTURE_IDS, FIXTURE_NOW } from "./ids.ts";
|
|
|
33
38
|
|
|
34
39
|
const { sid, instance, other_instance, endpoint, other_endpoint, request_id } = FIXTURE_IDS;
|
|
35
40
|
|
|
36
|
-
export const
|
|
41
|
+
export const HELLO_SESSION_REQUEST: Static<typeof HelloSessionRequest> = {
|
|
37
42
|
request_id,
|
|
38
|
-
op: "hello",
|
|
39
|
-
role: "session",
|
|
43
|
+
op: "hello.session",
|
|
40
44
|
protocol_version: 3,
|
|
41
45
|
sid,
|
|
42
46
|
client_version: "0.1.0",
|
|
@@ -51,12 +55,19 @@ export const HELLO_REQUEST: Static<typeof HelloRequest> = {
|
|
|
51
55
|
effort: "high",
|
|
52
56
|
};
|
|
53
57
|
|
|
54
|
-
/**
|
|
55
|
-
|
|
56
|
-
|
|
58
|
+
/** A person greeting, which names neither a session nor a mesh claim. */
|
|
59
|
+
export const HELLO_USER_REQUEST: Static<typeof HelloUserRequest> = {
|
|
60
|
+
request_id,
|
|
61
|
+
op: "hello.user",
|
|
62
|
+
protocol_version: 3,
|
|
63
|
+
client_version: "0.1.0",
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/** An instance greeting a peer, which carries the mesh claim in place of a
|
|
67
|
+
* session's meta. */
|
|
68
|
+
export const HELLO_INSTANCE_REQUEST: Static<typeof HelloInstanceRequest> = {
|
|
57
69
|
request_id,
|
|
58
|
-
op: "hello",
|
|
59
|
-
role: "instance",
|
|
70
|
+
op: "hello.instance",
|
|
60
71
|
protocol_version: 3,
|
|
61
72
|
mesh: {
|
|
62
73
|
ver: 1,
|
|
@@ -67,7 +78,9 @@ export const HELLO_MESH_REQUEST: Static<typeof HelloRequest> = {
|
|
|
67
78
|
},
|
|
68
79
|
};
|
|
69
80
|
|
|
70
|
-
|
|
81
|
+
/** One reply for all three greetings: what an instance answers does not turn
|
|
82
|
+
* on which of them asked. */
|
|
83
|
+
export const HELLO_RESPONSE: Static<typeof HelloSessionResponse> = {
|
|
71
84
|
ok: true,
|
|
72
85
|
request_id,
|
|
73
86
|
protocol_version: 3,
|
|
@@ -86,7 +99,7 @@ export const HELLO_RESPONSE: Static<typeof HelloResponse> = {
|
|
|
86
99
|
|
|
87
100
|
export const INSTANCE_PING_REQUEST: Static<typeof InstancePingRequest> = {
|
|
88
101
|
request_id,
|
|
89
|
-
op: "
|
|
102
|
+
op: "instance.ping",
|
|
90
103
|
};
|
|
91
104
|
|
|
92
105
|
export const INSTANCE_PING_RESPONSE: Static<typeof InstancePingResponse> = {
|
|
@@ -103,7 +116,7 @@ export const INSTANCE_PING_RESPONSE: Static<typeof InstancePingResponse> = {
|
|
|
103
116
|
|
|
104
117
|
export const INSTANCE_SHUTDOWN_REQUEST: Static<typeof InstanceShutdownRequest> = {
|
|
105
118
|
request_id,
|
|
106
|
-
op: "
|
|
119
|
+
op: "instance.shutdown",
|
|
107
120
|
};
|
|
108
121
|
|
|
109
122
|
export const INSTANCE_SHUTDOWN_RESPONSE: Static<typeof InstanceShutdownResponse> = {
|
|
@@ -113,7 +126,7 @@ export const INSTANCE_SHUTDOWN_RESPONSE: Static<typeof InstanceShutdownResponse>
|
|
|
113
126
|
|
|
114
127
|
export const SESSION_STOPPING_REQUEST: Static<typeof SessionStoppingRequest> = {
|
|
115
128
|
request_id,
|
|
116
|
-
op: "
|
|
129
|
+
op: "session.stopping",
|
|
117
130
|
reason: "prompt_input_exit",
|
|
118
131
|
};
|
|
119
132
|
|
|
@@ -125,7 +138,7 @@ export const SESSION_STOPPING_RESPONSE: Static<typeof SessionStoppingResponse> =
|
|
|
125
138
|
|
|
126
139
|
export const TOPIC_SUBSCRIBE_REQUEST: Static<typeof TopicSubscribeRequest> = {
|
|
127
140
|
request_id,
|
|
128
|
-
op: "
|
|
141
|
+
op: "topic.subscribe",
|
|
129
142
|
topic: "peers",
|
|
130
143
|
};
|
|
131
144
|
|
|
@@ -139,13 +152,13 @@ export const TOPIC_SUBSCRIBE_RESPONSE: Static<typeof TopicSubscribeResponse> = {
|
|
|
139
152
|
* are spelled. */
|
|
140
153
|
export const TOPIC_SUBSCRIBE_SESSION_REQUEST: Static<typeof TopicSubscribeRequest> = {
|
|
141
154
|
request_id,
|
|
142
|
-
op: "
|
|
155
|
+
op: "topic.subscribe",
|
|
143
156
|
topic: `transcript:${sid}`,
|
|
144
157
|
};
|
|
145
158
|
|
|
146
159
|
export const TOPIC_UNSUBSCRIBE_REQUEST: Static<typeof TopicUnsubscribeRequest> = {
|
|
147
160
|
request_id,
|
|
148
|
-
op: "
|
|
161
|
+
op: "topic.unsubscribe",
|
|
149
162
|
topic: "peers",
|
|
150
163
|
};
|
|
151
164
|
|
|
@@ -169,7 +182,7 @@ const SUBJECT = "personal-1";
|
|
|
169
182
|
|
|
170
183
|
export const AUTH_CHALLENGE_REQUEST: Static<typeof AuthChallengeRequest> = {
|
|
171
184
|
request_id,
|
|
172
|
-
op: "
|
|
185
|
+
op: "auth.challenge",
|
|
173
186
|
};
|
|
174
187
|
|
|
175
188
|
export const AUTH_CHALLENGE_RESPONSE: Static<typeof AuthChallengeResponse> = {
|
|
@@ -180,7 +193,7 @@ export const AUTH_CHALLENGE_RESPONSE: Static<typeof AuthChallengeResponse> = {
|
|
|
180
193
|
|
|
181
194
|
export const AUTH_REGISTER_REQUEST: Static<typeof AuthRegisterRequest> = {
|
|
182
195
|
request_id,
|
|
183
|
-
op: "
|
|
196
|
+
op: "auth.register",
|
|
184
197
|
token: REGISTER_TOKEN,
|
|
185
198
|
code: REGISTER_CODE,
|
|
186
199
|
device_label: "work laptop",
|
|
@@ -202,7 +215,7 @@ export const AUTH_REGISTER_RESPONSE: Static<typeof AuthRegisterResponse> = {
|
|
|
202
215
|
|
|
203
216
|
export const AUTH_ASSERT_REQUEST: Static<typeof AuthAssertRequest> = {
|
|
204
217
|
request_id,
|
|
205
|
-
op: "
|
|
218
|
+
op: "auth.assert",
|
|
206
219
|
challenge: CHALLENGE,
|
|
207
220
|
credential: {
|
|
208
221
|
raw_id: "Y3JlZC1pZA",
|
|
@@ -220,26 +233,26 @@ export const AUTH_ASSERT_RESPONSE: Static<typeof AuthAssertResponse> = {
|
|
|
220
233
|
access: ACCESS,
|
|
221
234
|
};
|
|
222
235
|
|
|
223
|
-
export const
|
|
236
|
+
export const AUTH_TOKEN_REFRESH_REQUEST: Static<typeof AuthTokenRefreshRequest> = {
|
|
224
237
|
request_id,
|
|
225
|
-
op: "
|
|
238
|
+
op: "auth.token.refresh",
|
|
226
239
|
reason: "reload",
|
|
227
240
|
};
|
|
228
241
|
|
|
229
|
-
export const
|
|
242
|
+
export const AUTH_TOKEN_REFRESH_RESPONSE: Static<typeof AuthTokenRefreshResponse> = {
|
|
230
243
|
ok: true,
|
|
231
244
|
request_id,
|
|
232
245
|
sub: SUBJECT,
|
|
233
246
|
access: ACCESS,
|
|
234
247
|
};
|
|
235
248
|
|
|
236
|
-
export const
|
|
249
|
+
export const AUTH_EXTEND_REQUEST: Static<typeof AuthExtendRequest> = {
|
|
237
250
|
request_id,
|
|
238
|
-
op: "
|
|
251
|
+
op: "auth.extend",
|
|
239
252
|
access_token: ACCESS.value,
|
|
240
253
|
};
|
|
241
254
|
|
|
242
|
-
export const
|
|
255
|
+
export const AUTH_EXTEND_RESPONSE: Static<typeof AuthExtendResponse> = {
|
|
243
256
|
ok: true,
|
|
244
257
|
request_id,
|
|
245
258
|
auth_expires_at: FIXTURE_NOW + 20_000_000,
|
|
@@ -247,7 +260,7 @@ export const AUTH_REFRESH_RESPONSE: Static<typeof AuthRefreshResponse> = {
|
|
|
247
260
|
|
|
248
261
|
export const AUTH_RESOLVE_REQUEST = {
|
|
249
262
|
request_id,
|
|
250
|
-
op: "
|
|
263
|
+
op: "auth.resolve",
|
|
251
264
|
to_instance: instance,
|
|
252
265
|
kind: "register",
|
|
253
266
|
token: REGISTER_TOKEN,
|
|
@@ -275,7 +288,7 @@ export const AUTH_RESOLVE_RESPONSE = {
|
|
|
275
288
|
* nothing beyond having spent it. */
|
|
276
289
|
export const AUTH_RESOLVE_CHALLENGE_REQUEST = {
|
|
277
290
|
request_id,
|
|
278
|
-
op: "
|
|
291
|
+
op: "auth.resolve",
|
|
279
292
|
to_instance: instance,
|
|
280
293
|
kind: "challenge",
|
|
281
294
|
challenge: CHALLENGE.challenge,
|
|
@@ -289,7 +302,7 @@ export const AUTH_RESOLVE_CHALLENGE_RESPONSE = {
|
|
|
289
302
|
|
|
290
303
|
export const AUTH_ROTATE_REQUEST: Static<typeof AuthRotateRequest> = {
|
|
291
304
|
request_id,
|
|
292
|
-
op: "
|
|
305
|
+
op: "auth.rotate",
|
|
293
306
|
to_instance: instance,
|
|
294
307
|
refresh_token: REFRESH.value,
|
|
295
308
|
reason: "reconnect",
|