@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.
@@ -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 stays in `last_live` after it was last seen. The same
35
- * window the inbox keeps undelivered messages for: what a person comes back to
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 can call live (connected or not).
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. One of `waiting`, `live` or `live_unmanaged`:
85
- * a session in this list is connected, so it is by definition not gone.
86
- * Absent from an instance that states no classification, and a client then
87
- * shows the session without grouping it rather than guessing one. */
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
- /** One session that was connected when its instance last saw it, and has not
138
- * come back.
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
- * The connection fields are a frozen copy of what that session looked like at
141
- * the last snapshot, not a live reading by definition it is not connected
142
- * while it appears here. The model and effort are the exception: they are read
143
- * back from the transcript's last turn, because what the session must resume as
144
- * is a property of where it actually stopped, not of when the snapshot was
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
- repo: SessionMetaFields.repo,
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: "LastLiveSession" },
173
+ { $id: "PeerRemoved" },
178
174
  );
179
- export type LastLiveSession = Static<typeof LastLiveSession>;
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
- * Whole-value per instance: a frame replaces everything previously known from
184
- * the instance that sent it and leaves other instances' entries alone, which is
185
- * what lets several instances each state their whole list without colliding.
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
- * Both lists travel together because a session registering is exactly what
188
- * moves it from one to the other. */
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) }));
@@ -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("sandbox_grant", SandboxGrantArgs);
41
- export const SandboxGrantResponse = response("sandbox_grant", SandboxGrantResult);
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("sandbox_revoke", SandboxRevokeArgs);
58
- export const SandboxRevokeResponse = response("sandbox_revoke", SandboxRevokeResult);
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 `session_errors` topic: every connected session currently stopped on a
13
+ /** The `session.errors` topic: every connected session currently stopped on a
14
14
  * harness error.
15
15
  *
16
- * Not per session, unlike `session_status`. A client showing a list of sessions
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
- "session_errors",
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 `session_status:<sid>` topic. Whole-value: the fold is recomputed and
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
- "session_status",
309
+ "session.status",
310
310
  Type.Intersect([Type.Object({ sid: Sid }), SessionStatusSnapshot]),
311
311
  );
@@ -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("session_kill", SessionKillArgs);
29
- export const SessionKillResponse = response("session_kill", SessionKillResult);
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("session_rename", SessionRenameArgs);
67
- export const SessionRenameResponse = response("session_rename", SessionRenameResult);
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("session_env_read", SessionEnvReadArgs);
88
- export const SessionEnvReadResponse = response("session_env_read", SessionEnvReadResult);
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("session_search", SessionSearchArgs);
166
- export const SessionSearchResponse = response("session_search", SessionSearchResult);
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:sub", "-tool:Agent"]` also says. */
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("session_dump_write", SessionDumpWriteArgs);
225
- export const SessionDumpWriteResponse = response("session_dump_write", SessionDumpWriteResult);
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 SessionForkOriginArgs = Type.Object({ sid: Sid });
233
- export type SessionForkOriginArgs = Static<typeof SessionForkOriginArgs>;
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 SessionForkOriginResult = Type.Object({
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 SessionForkOriginResult = Static<typeof SessionForkOriginResult>;
254
+ export type SessionForkOriginReadResult = Static<typeof SessionForkOriginReadResult>;
255
255
 
256
- export const SessionForkOriginRequest = request("session_fork_origin", SessionForkOriginArgs);
257
- export const SessionForkOriginResponse = response("session_fork_origin", SessionForkOriginResult);
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
- /** Drops one entry from the list of sessions that were running when the
260
- * instance last saw them.
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
- * The removal touches that list alone: the session stays resumable by every
263
- * other route, and an instance that later sees it connected records it again.
264
- * An unknown session is not an error — two clients pressing the same button is
265
- * the ordinary case, and the caller's goal holds either way. */
266
- export const SessionLastLiveRemoveArgs = Type.Object({ sid: Sid });
267
- export type SessionLastLiveRemoveArgs = Static<typeof SessionLastLiveRemoveArgs>;
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 SessionLastLiveRemoveResult = Type.Object({
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 SessionLastLiveRemoveResult = Static<typeof SessionLastLiveRemoveResult>;
280
+ export type SessionForgetResult = Static<typeof SessionForgetResult>;
274
281
 
275
- export const SessionLastLiveRemoveRequest = request(
276
- "session_last_live_remove",
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("transcript_read", TranscriptReadArgs);
52
- export const TranscriptReadResponse = response("transcript_read", TranscriptReadResult);
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("transcript_items_read", TranscriptItemsReadArgs);
118
+ export const TranscriptItemsReadRequest = request("transcript.items.read", TranscriptItemsReadArgs);
119
119
  export const TranscriptItemsReadResponse = response(
120
- "transcript_items_read",
120
+ "transcript.items.read",
121
121
  TranscriptItemsReadResult,
122
122
  );
123
123
 
124
- /** The `transcript_items:<sid>` topic.
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
- "transcript_items",
136
+ "transcript.items",
137
137
  Type.Object({
138
138
  sid: Sid,
139
139
  items: Type.Array(TranscriptItem),
@@ -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("translate_run", TranslateRunArgs);
33
- export const TranslateRunResponse = response("translate_run", TranslateRunResult);
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 `topic_subscribe`
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 `hello` announcing another protocol generation. */
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",
@@ -4,10 +4,10 @@ import type {
4
4
  AuthAssertResponse,
5
5
  AuthChallengeRequest,
6
6
  AuthChallengeResponse,
7
- AuthRefreshRequest,
8
- AuthRefreshResponse,
9
- AuthRefreshTokenRequest,
10
- AuthRefreshTokenResponse,
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 { HelloRequest, HelloResponse } from "../common/hello.ts";
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 HELLO_REQUEST: Static<typeof HelloRequest> = {
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
- /** The other side of `hello`: an instance greeting a peer, which carries the
55
- * mesh claim in place of a session's meta. */
56
- export const HELLO_MESH_REQUEST: Static<typeof HelloRequest> = {
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
- export const HELLO_RESPONSE: Static<typeof HelloResponse> = {
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: "instance_ping",
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: "instance_shutdown",
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: "session_stopping",
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: "topic_subscribe",
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: "topic_subscribe",
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: "topic_unsubscribe",
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: "auth_challenge",
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: "auth_register",
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: "auth_assert",
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 AUTH_REFRESH_TOKEN_REQUEST: Static<typeof AuthRefreshTokenRequest> = {
236
+ export const AUTH_TOKEN_REFRESH_REQUEST: Static<typeof AuthTokenRefreshRequest> = {
224
237
  request_id,
225
- op: "auth_refresh_token",
238
+ op: "auth.token.refresh",
226
239
  reason: "reload",
227
240
  };
228
241
 
229
- export const AUTH_REFRESH_TOKEN_RESPONSE: Static<typeof AuthRefreshTokenResponse> = {
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 AUTH_REFRESH_REQUEST: Static<typeof AuthRefreshRequest> = {
249
+ export const AUTH_EXTEND_REQUEST: Static<typeof AuthExtendRequest> = {
237
250
  request_id,
238
- op: "auth_refresh",
251
+ op: "auth.extend",
239
252
  access_token: ACCESS.value,
240
253
  };
241
254
 
242
- export const AUTH_REFRESH_RESPONSE: Static<typeof AuthRefreshResponse> = {
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: "auth_resolve",
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: "auth_resolve",
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: "auth_rotate",
305
+ op: "auth.rotate",
293
306
  to_instance: instance,
294
307
  refresh_token: REFRESH.value,
295
308
  reason: "reconnect",