@corenel/harness 0.8.0 → 0.10.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/dist/core/compactHistory.d.ts.map +1 -1
- package/dist/core/compactHistory.js +17 -2
- package/dist/core/compactHistory.js.map +1 -1
- package/dist/core/contextPressure.d.ts +45 -0
- package/dist/core/contextPressure.d.ts.map +1 -0
- package/dist/core/contextPressure.js +69 -0
- package/dist/core/contextPressure.js.map +1 -0
- package/dist/core/loop.d.ts.map +1 -1
- package/dist/core/loop.js +77 -12
- package/dist/core/loop.js.map +1 -1
- package/dist/core/pricing.d.ts +30 -1
- package/dist/core/pricing.d.ts.map +1 -1
- package/dist/core/pricing.js +50 -21
- package/dist/core/pricing.js.map +1 -1
- package/dist/guardrail/builtins.d.ts.map +1 -1
- package/dist/guardrail/builtins.js +9 -3
- package/dist/guardrail/builtins.js.map +1 -1
- package/dist/guardrail/validate.d.ts.map +1 -1
- package/dist/guardrail/validate.js +6 -2
- package/dist/guardrail/validate.js.map +1 -1
- package/dist/prompts/host.d.ts +23 -0
- package/dist/prompts/host.d.ts.map +1 -1
- package/dist/prompts/host.js.map +1 -1
- package/dist/prompts/index.d.ts +14 -0
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +43 -2
- package/dist/prompts/index.js.map +1 -1
- package/dist/prompts/library/defaults.generated.js +1 -1
- package/dist/prompts/library/defaults.generated.js.map +1 -1
- package/dist/providers/contextWindow.d.ts +23 -4
- package/dist/providers/contextWindow.d.ts.map +1 -1
- package/dist/providers/contextWindow.js +25 -0
- package/dist/providers/contextWindow.js.map +1 -1
- package/dist/sessions/fileSessionStore.d.ts +2 -1
- package/dist/sessions/fileSessionStore.d.ts.map +1 -1
- package/dist/sessions/fileSessionStore.js +28 -3
- package/dist/sessions/fileSessionStore.js.map +1 -1
- package/dist/sessions/jsonl.d.ts +5 -0
- package/dist/sessions/jsonl.d.ts.map +1 -1
- package/dist/sessions/jsonl.js +10 -0
- package/dist/sessions/jsonl.js.map +1 -1
- package/dist/sidecar/server.d.ts +28 -4
- package/dist/sidecar/server.d.ts.map +1 -1
- package/dist/sidecar/server.js +191 -10
- package/dist/sidecar/server.js.map +1 -1
- package/dist/skillSource.d.ts +36 -0
- package/dist/skillSource.d.ts.map +1 -0
- package/dist/skillSource.js +105 -0
- package/dist/skillSource.js.map +1 -0
- package/dist/skills.d.ts +38 -2
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +64 -9
- package/dist/skills.js.map +1 -1
- package/dist/tools/builtins.d.ts.map +1 -1
- package/dist/tools/builtins.js +48 -2
- package/dist/tools/builtins.js.map +1 -1
- package/package.json +4 -3
package/dist/sessions/jsonl.js
CHANGED
|
@@ -8,6 +8,7 @@ export function foldSession(text) {
|
|
|
8
8
|
const turns = [];
|
|
9
9
|
let title = '';
|
|
10
10
|
let titleLocked;
|
|
11
|
+
let movedTo;
|
|
11
12
|
let updatedAt = 0;
|
|
12
13
|
for (const raw of text.split('\n')) {
|
|
13
14
|
const s = raw.trim();
|
|
@@ -36,6 +37,10 @@ export function foldSession(text) {
|
|
|
36
37
|
turns[line.i] = line.m;
|
|
37
38
|
updatedAt = Math.max(updatedAt, line.at);
|
|
38
39
|
}
|
|
40
|
+
else if (line.k === 'moved' && typeof line.roomId === 'string' && typeof line.at === 'number') {
|
|
41
|
+
movedTo = { movedToRoom: line.roomId, at: line.at };
|
|
42
|
+
updatedAt = Math.max(updatedAt, line.at);
|
|
43
|
+
}
|
|
39
44
|
}
|
|
40
45
|
if (!meta)
|
|
41
46
|
return null;
|
|
@@ -46,7 +51,12 @@ export function foldSession(text) {
|
|
|
46
51
|
updatedAt: updatedAt || meta.createdAt,
|
|
47
52
|
messages: turns.filter((t) => t !== undefined),
|
|
48
53
|
...(meta.surface ? { surface: meta.surface } : {}),
|
|
54
|
+
// Only a STRING owner survives the fold: the line came from disk (or a
|
|
55
|
+
// sidecar), so its declared type guarantees nothing, and a non-string here
|
|
56
|
+
// would reach a comparison that decides how a row is drawn.
|
|
57
|
+
...(typeof meta.owner === 'string' && meta.owner ? { owner: meta.owner } : {}),
|
|
49
58
|
...(titleLocked !== undefined ? { titleLocked } : {}),
|
|
59
|
+
...(movedTo !== undefined ? { movedTo } : {}),
|
|
50
60
|
};
|
|
51
61
|
}
|
|
52
62
|
//# sourceMappingURL=jsonl.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsonl.js","sourceRoot":"","sources":["../../sessions/jsonl.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"jsonl.js","sourceRoot":"","sources":["../../sessions/jsonl.ts"],"names":[],"mappings":"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"}
|
package/dist/sidecar/server.d.ts
CHANGED
|
@@ -13,19 +13,29 @@ export type PermissionAsk = Omit<Extract<SidecarMsg, {
|
|
|
13
13
|
* MORE members than this (isAttended, outstanding, broadcastAsk, settle, nextId)
|
|
14
14
|
* and is structurally assignable here without either package importing the other. */
|
|
15
15
|
export interface SidecarAttendanceRegistry {
|
|
16
|
-
/** Attach a client to receive asks/resolutions; returns a detach function.
|
|
16
|
+
/** Attach a client to receive asks/resolutions; returns a detach function.
|
|
17
|
+
* `identity` is OPTIONAL and additive: a caller that supplies none gets
|
|
18
|
+
* today's behaviour. Declared structurally rather than imported, for the
|
|
19
|
+
* dependency-direction reason in this interface's own header. */
|
|
17
20
|
attach(client: {
|
|
18
21
|
send(ask: PermissionAsk): void;
|
|
19
|
-
resolved(id: number, decision: PermissionDecision, by: 'client' | 'timeout' | 'unattended'): void;
|
|
22
|
+
resolved(id: number, decision: PermissionDecision, by: 'client' | 'timeout' | 'unattended', byClientId?: string): void;
|
|
20
23
|
/** Optional: a daemon-side run started or settled. Optional here for the
|
|
21
24
|
* same reason it is optional on the registry - a sidecar build (or a test
|
|
22
25
|
* double) that only ever forwarded permission asks stays valid. */
|
|
23
26
|
runActivity?(activity: DaemonRunActivity): void;
|
|
24
27
|
registrations?(list: DaemonRegistration[]): void;
|
|
28
|
+
}, identity?: {
|
|
29
|
+
clientId: string;
|
|
30
|
+
accounts: readonly string[];
|
|
25
31
|
}): () => void;
|
|
26
32
|
/** Record this connection's answer to an outstanding ask. Ignored for an
|
|
27
|
-
* unknown/already-resolved id.
|
|
28
|
-
|
|
33
|
+
* unknown/already-resolved id. `by` is the attached-client object this
|
|
34
|
+
* connection registered, so the registry can name who answered -- typed
|
|
35
|
+
* `unknown` deliberately: this port must not learn `packages/sidecar`'s
|
|
36
|
+
* types, and the registry looks the object up in its own map, so an
|
|
37
|
+
* unrecognised value simply yields no name. */
|
|
38
|
+
answer(id: number, decision: PermissionDecision, by?: unknown): void;
|
|
29
39
|
}
|
|
30
40
|
/** The minimal shape SidecarServer needs from a live-run hub, so a second client
|
|
31
41
|
* can watch a run it did not start. Declared HERE (harness-side), not imported
|
|
@@ -267,6 +277,14 @@ export interface SidecarServerOptions {
|
|
|
267
277
|
* resolved). Absent means no room can be opened/listed/read/answered here,
|
|
268
278
|
* and `welcome.features` omits `'rooms'` -- mirrors `roots`/`procs`. */
|
|
269
279
|
rooms?: SidecarRooms;
|
|
280
|
+
/** Whether this daemon was started `--mode team`. Off (the default) changes
|
|
281
|
+
* nothing about rooms: `room-list` returns every room, exactly as it always
|
|
282
|
+
* has. On, `room-list` narrows to rooms the connection's OWN authenticated
|
|
283
|
+
* `clientId` is a member of -- see `onRoomList`. Named the same as
|
|
284
|
+
* `attendance.ts`'s `teamMode` because it is the same operator choice,
|
|
285
|
+
* threaded here for the same reason: private-by-default only once an
|
|
286
|
+
* operator has opted in. */
|
|
287
|
+
teamMode?: boolean;
|
|
270
288
|
/** The daemon's process table, behind `proc-*`. Present ONLY when the operator
|
|
271
289
|
* allowed shell: a streaming spawn is the same capability as the one-shot
|
|
272
290
|
* `sidecar_shell_exec`, held open, so it must not appear on a daemon whose
|
|
@@ -405,6 +423,11 @@ export declare class SidecarServer {
|
|
|
405
423
|
/** The friendly label from the same `hello`, kept beside `clientId` so the
|
|
406
424
|
* dirName resolver is handed the identity the manifest keys on. */
|
|
407
425
|
private clientName?;
|
|
426
|
+
/** This connection's attended-client object, retained so an answer it sends
|
|
427
|
+
* can be attributed to it. Typed `unknown` because its only consumer is the
|
|
428
|
+
* registry's own identity lookup, which takes `unknown` for the
|
|
429
|
+
* dependency-direction reason on `SidecarAttendanceRegistry.answer`. */
|
|
430
|
+
private attended?;
|
|
408
431
|
/** Detach function for this connection's attendance attachment, set on a
|
|
409
432
|
* successful `hello` when `opts.attendance` is wired. Called once, on channel
|
|
410
433
|
* close, so a dead connection stops receiving/counting toward asks. */
|
|
@@ -540,6 +563,7 @@ export declare class SidecarServer {
|
|
|
540
563
|
private onRoomList;
|
|
541
564
|
private onRoomRead;
|
|
542
565
|
private onRoomResume;
|
|
566
|
+
private actingId;
|
|
543
567
|
private onRoomAnswer;
|
|
544
568
|
private onRoomInterject;
|
|
545
569
|
private onRoomClose;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../sidecar/server.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,SAAS,EAEmC,UAAU,EAAE,kBAAkB,EAAE,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,UAAU,EAAE,WAAW,EACxJ,YAAY,EAAE,eAAe,EAAe,eAAe,EAAE,WAAW,EACxE,WAAW,EAAE,eAAe,EAC7B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAEL,KAAK,cAAc,EAAE,KAAK,UAAU,EAAoC,KAAK,WAAW,EACxF,KAAK,aAAa,EAAE,KAAK,eAAe,EACxC,KAAK,aAAa,EAAE,KAAK,UAAU,EACpC,MAAM,mBAAmB,CAAC;AAE3B;6EAC6E;AAC7E,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE;IAAE,CAAC,EAAE,gBAAgB,CAAA;CAAE,CAAC,EAAE,GAAG,CAAC,CAAC;AAEpF;;;;;;qFAMqF;AACrF,MAAM,WAAW,yBAAyB;IACxC
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../sidecar/server.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,SAAS,EAEmC,UAAU,EAAE,kBAAkB,EAAE,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,UAAU,EAAE,WAAW,EACxJ,YAAY,EAAE,eAAe,EAAe,eAAe,EAAE,WAAW,EACxE,WAAW,EAAE,eAAe,EAC7B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAEL,KAAK,cAAc,EAAE,KAAK,UAAU,EAAoC,KAAK,WAAW,EACxF,KAAK,aAAa,EAAE,KAAK,eAAe,EACxC,KAAK,aAAa,EAAE,KAAK,UAAU,EACpC,MAAM,mBAAmB,CAAC;AAE3B;6EAC6E;AAC7E,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE;IAAE,CAAC,EAAE,gBAAgB,CAAA;CAAE,CAAC,EAAE,GAAG,CAAC,CAAC;AAEpF;;;;;;qFAMqF;AACrF,MAAM,WAAW,yBAAyB;IACxC;;;qEAGiE;IACjE,MAAM,CAAC,MAAM,EAAE;QACb,IAAI,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAAC;QAC/B,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,kBAAkB,EAAE,EAAE,EAAE,QAAQ,GAAG,SAAS,GAAG,YAAY,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACvH;;2EAEmE;QACnE,WAAW,CAAC,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;QAChD,aAAa,CAAC,CAAC,IAAI,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAAC;KAClD,EAAE,QAAQ,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,GAAG,MAAM,IAAI,CAAC;IAC7E;;;;;mDAK+C;IAC/C,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,kBAAkB,EAAE,EAAE,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CACtE;AAED;;;;;kFAKkF;AAClF,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IAChD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC1E,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE;QAC5B,KAAK,CAAC,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;QAC3B,IAAI,CAAC,MAAM,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAC;QACzD,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;KAC9B,GAAG;QAAE,MAAM,EAAE;YAAE,MAAM,EAAE,UAAU,EAAE,CAAC;YAAC,OAAO,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,UAAU,EAAE,MAAM,CAAA;aAAE,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,MAAM,EAAE,MAAM,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;CACnJ;AAED;;;;;;;;;;;;qBAYqB;AACrB,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,WAAW,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACzG,IAAI,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC/B,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,QAAQ,EAAE,OAAO,EAAE,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACtG;mDAC+C;IAC/C,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACtG;;gDAE4C;IAC5C,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAC7C;6EACyE;IACzE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACrF;mFAC+E;IAC/E,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACnF;8EAC0E;IAC1E,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IACjD;;;oCAGgC;IAChC,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAClG,SAAS,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACxD;AAED;;;;;;;;;;;;qBAYqB;AACrB,MAAM,WAAW,aAAa;IAC5B,IAAI,IAAI,UAAU,EAAE,CAAC;IACrB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IAC1E,0DAA0D;IAC1D,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;gFAkBgF;AAChF,MAAM,WAAW,YAAY;IAC3B;0DACsD;IACtD,IAAI,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC/B;;wCAEoC;IACpC,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/E,2EAA2E;IAC3E,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,IAAI,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AAiBH,MAAM,WAAW,eAAe;IAC9B,WAAW,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC,CAAC;IACjG,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxF,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAC/E;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CACxF,CAAC;IACF,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3E;AAED,MAAM,WAAW,UAAU;IACzB;;2DAEuD;IACvD,KAAK,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IAC3E;;;gFAG4E;IAC5E,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3E;;0EAEsE;IACtE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACvF;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,cAAc,CAAC;IACxB,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB;gFAC4E;IAC5E,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB;4FACwF;IACxF,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B;iFAC6E;IAC7E,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB;;;+CAG2C;IAC3C,UAAU,CAAC,EAAE,yBAAyB,CAAC;IACvC;;kEAE8D;IAC9D,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB;;0CAEsC;IACtC,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB;;kDAE8C;IAC9C,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB;;;4EAGwE;IACxE,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB;;;;;;gCAM4B;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;2EAIuE;IACvE,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB;;wEAEoE;IACpE,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B;;;;;;;;;;4BAUwB;IACxB,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAClE;;;;uEAImE;IACnE,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B;;;;;4DAKwD;IACxD,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB;;;6BAGyB;IACzB,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB;;;yDAGqD;IACrD,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB;;;yEAGqE;IACrE,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B;;;;;;;;;;;;;;;;;;;OAmBG;IACH,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,KAAK,MAAM,CAAC;IACzF;;;;;;;;;;;;;;;OAeG;IACH,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC;IAChC;;;;;;;;;;;;;;OAcG;IACH,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC1F;AAoDD,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAiB;IACpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAuB;IAC5C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgC;IACtD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA8B;IAC1D;;0DAEsD;IACtD,OAAO,CAAC,YAAY,CAAc;IAClC;;;;;;gFAM4E;IAC5E,OAAO,CAAC,QAAQ,CAAC,CAAS;IAC1B;uEACmE;IACnE,OAAO,CAAC,UAAU,CAAC,CAAS;IAC5B;;;4EAGwE;IACxE,OAAO,CAAC,QAAQ,CAAC,CAAU;IAC3B;;2EAEuE;IACvE,OAAO,CAAC,cAAc,CAAC,CAAa;IACpC;;;8EAG0E;IAC1E,OAAO,CAAC,OAAO,CAAC,CAAS;IACzB,uEAAuE;IACvE,OAAO,CAAC,WAAW,CAAC,CAAa;IACjC;wDACoD;IACpD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAyB;IAEtD;;;;iEAI6D;IAC7D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,SAAS,CAA2B;gBAEhC,IAAI,EAAE,oBAAoB;IAsDtC,OAAO,CAAC,SAAS;IA4OjB,OAAO,CAAC,OAAO;IAoJf;gEAC4D;YAC9C,cAAc;IAc5B;;;eAGW;IACX,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,WAAW;IAiFnB;;sEAEkE;YACpD,SAAS;IA0BvB;;;;gEAI4D;IAC5D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqC;IAElE,OAAO,CAAC,eAAe;YAMT,SAAS;IAkGvB,OAAO,CAAC,YAAY;IASpB;;wBAEoB;IACpB,OAAO,CAAC,YAAY;IAMpB;;mDAE+C;IAC/C,OAAO,CAAC,gBAAgB;YAoBV,WAAW;IA6BzB;4EACwE;IACxE,OAAO,CAAC,YAAY;IAoBpB,mEAAmE;IACnE,OAAO,CAAC,YAAY;YASN,QAAQ;IAqCtB;;;;;;;;;uCASmC;YACrB,UAAU;YAwBV,YAAY;YAcZ,WAAW;IAczB;;;;;;;OAOG;IACH,OAAO,CAAC,WAAW;IA+CnB;;;;;;;;;OASG;YACW,YAAY;IAa1B;+EAC2E;YAC7D,eAAe;IAW7B;;;;iBAIa;YACC,YAAY;YAcZ,KAAK;IAmEnB;;;;;4EAKwE;IACxE,OAAO,CAAC,uBAAuB;YAQjB,aAAa;YAYb,UAAU;YAeV,aAAa;IAgB3B;wEACoE;YACtD,WAAW;IAYzB,OAAO,CAAC,SAAS;IAIjB;;;;;;;;;OASG;YACW,aAAa;IA4B3B,OAAO,CAAC,gBAAgB;YAeV,eAAe;YA2Bf,UAAU;YAyEV,UAAU;YAsCV,UAAU;YA+CV,YAAY;IAmC1B,OAAO,CAAC,QAAQ;YAQF,YAAY;YAoBZ,eAAe;YAoBf,WAAW;IAkBzB;;;;;;;;;;;;;;;;;OAiBG;YAYW,UAAU;YAmCV,SAAS;YAuBT,cAAc;YAed,iBAAiB;IAoB/B;;2DAEuD;YACzC,QAAQ;YAyBR,aAAa;YAkBb,gBAAgB;IAkB9B;;;;oEAIgE;IAChE,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,QAAQ;IAQhB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAOrB;IAEF,OAAO,CAAC,QAAQ,CAAC,EAAE,CAKjB;IAEF,OAAO,CAAC,QAAQ,CAAC,MAAM,CAErB;IAEF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAOvB;IAEF;mFAC+E;IAC/E,OAAO,CAAC,QAAQ,CAAC,KAAK,CAcpB;IAEF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAC2D;IAEnF,OAAO,CAAC,QAAQ,CAAC,GAAG,CACuE;CAC5F"}
|
package/dist/sidecar/server.js
CHANGED
|
@@ -112,6 +112,16 @@ export class SidecarServer {
|
|
|
112
112
|
writable: true,
|
|
113
113
|
value: void 0
|
|
114
114
|
});
|
|
115
|
+
/** This connection's attended-client object, retained so an answer it sends
|
|
116
|
+
* can be attributed to it. Typed `unknown` because its only consumer is the
|
|
117
|
+
* registry's own identity lookup, which takes `unknown` for the
|
|
118
|
+
* dependency-direction reason on `SidecarAttendanceRegistry.answer`. */
|
|
119
|
+
Object.defineProperty(this, "attended", {
|
|
120
|
+
enumerable: true,
|
|
121
|
+
configurable: true,
|
|
122
|
+
writable: true,
|
|
123
|
+
value: void 0
|
|
124
|
+
});
|
|
115
125
|
/** Detach function for this connection's attendance attachment, set on a
|
|
116
126
|
* successful `hello` when `opts.attendance` is wired. Called once, on channel
|
|
117
127
|
* close, so a dead connection stops receiving/counting toward asks. */
|
|
@@ -387,7 +397,7 @@ export class SidecarServer {
|
|
|
387
397
|
// answer is dropped silently rather than forwarded into the registry.
|
|
388
398
|
if (!this.authed)
|
|
389
399
|
return;
|
|
390
|
-
this.opts.attendance?.answer(m.id, m.decision);
|
|
400
|
+
this.opts.attendance?.answer(m.id, m.decision, this.attended);
|
|
391
401
|
return;
|
|
392
402
|
case 'roots-browse':
|
|
393
403
|
// Auth-gated like every other request: directory enumeration must never
|
|
@@ -676,6 +686,13 @@ export class SidecarServer {
|
|
|
676
686
|
// or object) must never reach `onClient`: downstream consumers use it as an
|
|
677
687
|
// object key (a daemon's hosts manifest) or feed it to a directory-name
|
|
678
688
|
// sanitizer that assumes a string.
|
|
689
|
+
/* Declared OUT here, not inside the identity block that fills it, because
|
|
690
|
+
* the attendance attach below needs the same bounded list -- and must get
|
|
691
|
+
* the ALREADY-BOUNDED one rather than re-reading `m.accounts`, which would
|
|
692
|
+
* reintroduce the unbounded walk the block below exists to prevent. A
|
|
693
|
+
* connection that carried no usable clientId leaves this empty, which is
|
|
694
|
+
* the right claim for a peer that identified itself as nobody. */
|
|
695
|
+
let accounts = [];
|
|
679
696
|
if (typeof m.clientId === 'string' && m.clientId) {
|
|
680
697
|
const wireId = m.clientId.slice(0, MAX_IDENTITY_FIELD_LEN);
|
|
681
698
|
/* THE ONE PLACE IDENTITY IS DECIDED, and it has to be here.
|
|
@@ -709,7 +726,7 @@ export class SidecarServer {
|
|
|
709
726
|
* `<account><spaces>` behind, which a normalizing comparison then trims
|
|
710
727
|
* into a match nobody claimed. */
|
|
711
728
|
const claimed = Array.isArray(m.accounts) ? m.accounts : [];
|
|
712
|
-
|
|
729
|
+
accounts = claimed.length > MAX_CLAIMED_ACCOUNTS
|
|
713
730
|
? []
|
|
714
731
|
: claimed.filter((a) => typeof a === 'string' && a.length <= MAX_IDENTITY_FIELD_LEN);
|
|
715
732
|
let clientId = wireId;
|
|
@@ -757,11 +774,25 @@ export class SidecarServer {
|
|
|
757
774
|
}
|
|
758
775
|
}
|
|
759
776
|
if (this.opts.attendance) {
|
|
760
|
-
|
|
777
|
+
const attended = {
|
|
761
778
|
send: (ask) => this.ch.send({ t: 'permission-ask', ...ask }),
|
|
762
|
-
resolved: (id, decision, by) => this.ch.send({ t: 'permission-resolved', id, decision, by }),
|
|
779
|
+
resolved: (id, decision, by, byClientId) => this.ch.send({ t: 'permission-resolved', id, decision, by, ...(byClientId ? { byClientId } : {}) }),
|
|
763
780
|
runActivity: (activity) => this.ch.send({ t: 'run-activity', activity }),
|
|
764
781
|
registrations: (registrations) => this.ch.send({ t: 'registrations', registrations }),
|
|
782
|
+
};
|
|
783
|
+
// Retained so `permission-answer` can name THIS connection as the
|
|
784
|
+
// answerer -- the registry identifies an answerer by the object it
|
|
785
|
+
// attached, not by anything the answer message claims.
|
|
786
|
+
this.attended = attended;
|
|
787
|
+
/* The identity is what routes an ask under `--mode team`. `clientId` is
|
|
788
|
+
* the key this connection was FILED under (mapped through `clientKey`
|
|
789
|
+
* where the daemon supplies one), i.e. the same value its RunIdentity
|
|
790
|
+
* carries -- which is what makes a run's owner and its ask's recipient
|
|
791
|
+
* the same connection. `accounts` are CLAIMED and only ever widen a
|
|
792
|
+
* delivery; see the registry's `recipients`. */
|
|
793
|
+
this.attendedDetach = this.opts.attendance.attach(attended, {
|
|
794
|
+
clientId: this.clientId ?? '',
|
|
795
|
+
accounts,
|
|
765
796
|
});
|
|
766
797
|
}
|
|
767
798
|
this.opts.log?.(`paired — ${this.tools.size} ${this.tools.size === 1 ? 'tool' : 'tools'} advertised (host dir: ${this.hostStateDir})`);
|
|
@@ -1588,8 +1619,64 @@ export class SidecarServer {
|
|
|
1588
1619
|
this.ch.send({ t: 'room-open-result', id: m.id, ok: false, problems: ['this sidecar does not host rooms'] });
|
|
1589
1620
|
return;
|
|
1590
1621
|
}
|
|
1622
|
+
/* Ownership must be OBSERVED, not claimed. `m.req.participants` crossed
|
|
1623
|
+
* the wire from a caller this connection has not otherwise proven
|
|
1624
|
+
* anything about beyond its `hello` token -- every id in it, including
|
|
1625
|
+
* the id the sender picked to represent THEMSELVES, is whatever they
|
|
1626
|
+
* typed. Without this, `room-list`'s membership filter and `mayAct`'s
|
|
1627
|
+
* role gate both key off a string the caller chose for itself, which
|
|
1628
|
+
* makes the whole boundary decorative: connect, claim to be the id
|
|
1629
|
+
* already recorded as owner, done.
|
|
1630
|
+
*
|
|
1631
|
+
* So a room cannot be opened at all without a connection that proved an
|
|
1632
|
+
* identity at `hello` (`this.clientId`) -- an unauthenticated-by-identity
|
|
1633
|
+
* opener could otherwise claim to be anyone, including someone already
|
|
1634
|
+
* seated in an existing room. And whichever participant in the request is
|
|
1635
|
+
* marked `role: 'owner'` has its id OVERWRITTEN with that proven
|
|
1636
|
+
* identity, never trusted as sent.
|
|
1637
|
+
*
|
|
1638
|
+
* The overwrite below is `.find()` -- it stamps the FIRST human carrying
|
|
1639
|
+
* `role: 'owner'`. Nothing here, or upstream of this call, guarantees a
|
|
1640
|
+
* request names only one: `.find()` picks the first regardless of how
|
|
1641
|
+
* many there are. A request naming more than one owner is separately
|
|
1642
|
+
* refused, wholesale, by `validateRoomRequest`'s own one-owner check
|
|
1643
|
+
* (packages/crew/room/open.ts) -- but that runs INSIDE `rooms.open`,
|
|
1644
|
+
* after this overwrite has already happened, and this code neither
|
|
1645
|
+
* depends on nor is ordered by it. If that downstream rule were ever
|
|
1646
|
+
* relaxed, this line would silently start choosing arbitrarily among
|
|
1647
|
+
* several claimed owners rather than refusing anything itself.
|
|
1648
|
+
*
|
|
1649
|
+
* Every OTHER human in the request keeps the id it was sent with: those
|
|
1650
|
+
* are clientIds the owner picked from their own attached connections, and
|
|
1651
|
+
* this connection has no way to verify them. A wrong one there grants
|
|
1652
|
+
* nobody rather than displacing the person it should have named -- widen
|
|
1653
|
+
* never redirect.
|
|
1654
|
+
*
|
|
1655
|
+
* ALL OF THAT IS GATED ON `--mode team`. With team mode off this handler
|
|
1656
|
+
* must be exactly what it was before rooms grew roles: a CLI, an ACP
|
|
1657
|
+
* client or an older host that sends no `clientId` at `hello` opens rooms
|
|
1658
|
+
* as it always did, and the id it sent for itself stands. Refusing them
|
|
1659
|
+
* unconditionally would make the opt-in flag not an opt-in -- see the
|
|
1660
|
+
* team-mode-off test in host.rooms.test.ts, which is the merge-safety
|
|
1661
|
+
* claim for this whole branch. */
|
|
1662
|
+
if (this.opts.teamMode && this.clientId === undefined) {
|
|
1663
|
+
this.ch.send({
|
|
1664
|
+
t: 'room-open-result',
|
|
1665
|
+
id: m.id,
|
|
1666
|
+
ok: false,
|
|
1667
|
+
problems: ['This connection has no verified identity (no clientId from hello), so it cannot open a room.'],
|
|
1668
|
+
});
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
const clientId = this.clientId;
|
|
1672
|
+
const owner = clientId === undefined
|
|
1673
|
+
? undefined
|
|
1674
|
+
: m.req.participants.find((p) => p.kind === 'human' && p.role === 'owner');
|
|
1675
|
+
const req = owner && clientId !== undefined
|
|
1676
|
+
? { ...m.req, participants: m.req.participants.map((p) => (p === owner ? { ...p, id: clientId } : p)) }
|
|
1677
|
+
: m.req;
|
|
1591
1678
|
try {
|
|
1592
|
-
const r = await this.opts.rooms.open(
|
|
1679
|
+
const r = await this.opts.rooms.open(req);
|
|
1593
1680
|
this.ch.send(r.ok
|
|
1594
1681
|
? { t: 'room-open-result', id: m.id, ok: true, room: r.room }
|
|
1595
1682
|
: { t: 'room-open-result', id: m.id, ok: false, problems: r.problems });
|
|
@@ -1609,7 +1696,27 @@ export class SidecarServer {
|
|
|
1609
1696
|
return;
|
|
1610
1697
|
}
|
|
1611
1698
|
try {
|
|
1612
|
-
|
|
1699
|
+
const rooms = await this.opts.rooms.list();
|
|
1700
|
+
/* Private by default, but ONLY once an operator opted into `--mode
|
|
1701
|
+
* team` -- off, this is the same list every daemon has always
|
|
1702
|
+
* returned, unnarrowed, for every existing room test and every daemon
|
|
1703
|
+
* nobody has reconfigured.
|
|
1704
|
+
*
|
|
1705
|
+
* Team mode on: membership is judged against `this.clientId`, the
|
|
1706
|
+
* connection's OWN proven identity, never a participant id read out of
|
|
1707
|
+
* the request -- that is what `onRoomOpen` just finished making
|
|
1708
|
+
* trustworthy. A caller with NO clientId is not "an older client that
|
|
1709
|
+
* predates identity" here the way it is at `hello` -- membership
|
|
1710
|
+
* cannot be proven without an identity, and under private-by-default
|
|
1711
|
+
* the honest answer to "what can you not prove you're in" is nothing,
|
|
1712
|
+
* not everything. Answering with every room would make the boundary
|
|
1713
|
+
* opt-out: connect without a clientId and see it all anyway. */
|
|
1714
|
+
const mine = !this.opts.teamMode
|
|
1715
|
+
? rooms
|
|
1716
|
+
: this.clientId === undefined
|
|
1717
|
+
? []
|
|
1718
|
+
: rooms.filter((r) => r.participants.some((p) => p.id === this.clientId));
|
|
1719
|
+
this.ch.send({ t: 'room-list-result', id: m.id, ok: true, rooms: mine });
|
|
1613
1720
|
}
|
|
1614
1721
|
catch (err) {
|
|
1615
1722
|
// A room failure must never kill the connection, and it must not read
|
|
@@ -1625,6 +1732,32 @@ export class SidecarServer {
|
|
|
1625
1732
|
}
|
|
1626
1733
|
try {
|
|
1627
1734
|
const found = await this.opts.rooms.read(m.roomId);
|
|
1735
|
+
/* Private by default extends past `room-list` (R7) to the read itself:
|
|
1736
|
+
* without this, an authenticated connection that merely knows or
|
|
1737
|
+
* guesses a roomId got the full transcript regardless of membership --
|
|
1738
|
+
* the list filter alone was obscurity, not a boundary. Gated under
|
|
1739
|
+
* `teamMode` exactly like `room-list`, and judged against the same
|
|
1740
|
+
* proven `this.clientId`, never anything claimed in the message.
|
|
1741
|
+
*
|
|
1742
|
+
* A refusal here says SO -- "not a member" -- rather than answering
|
|
1743
|
+
* `room: null`, which is the honest "no such room" answer and would be
|
|
1744
|
+
* a lie about a room that does in fact exist. */
|
|
1745
|
+
if (found && this.opts.teamMode) {
|
|
1746
|
+
if (this.clientId === undefined) {
|
|
1747
|
+
this.ch.send({
|
|
1748
|
+
t: 'room-read-result', id: m.id, ok: false,
|
|
1749
|
+
error: 'This connection has no verified identity, so membership in this room cannot be proven.',
|
|
1750
|
+
});
|
|
1751
|
+
return;
|
|
1752
|
+
}
|
|
1753
|
+
if (!found.room.participants.some((p) => p.id === this.clientId)) {
|
|
1754
|
+
this.ch.send({
|
|
1755
|
+
t: 'room-read-result', id: m.id, ok: false,
|
|
1756
|
+
error: `"${this.clientId}" is not a member of room "${m.roomId}".`,
|
|
1757
|
+
});
|
|
1758
|
+
return;
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1628
1761
|
// `found === null` here is Task 5's considered "no such room" -- keep
|
|
1629
1762
|
// it a successful `ok: true, room: null`, never conflated with the
|
|
1630
1763
|
// catch below's "could not read it" failure.
|
|
@@ -1654,13 +1787,46 @@ export class SidecarServer {
|
|
|
1654
1787
|
this.ch.send({ t: 'room-resume-result', id: m.id, ok: false, error: errMessage(err) });
|
|
1655
1788
|
}
|
|
1656
1789
|
}
|
|
1790
|
+
/* ONE identity rule for every room verb.
|
|
1791
|
+
*
|
|
1792
|
+
* `room-read` judges membership on `this.clientId` -- the identity this
|
|
1793
|
+
* connection PROVED at `hello`. The mutating verbs used to hand
|
|
1794
|
+
* `m.participantId` straight off the wire to `requireRole`/`mayAct`, so one
|
|
1795
|
+
* branch enforced while another trusted the caller: a viewer could send the
|
|
1796
|
+
* owner's participantId and close the room. Under team mode every verb now
|
|
1797
|
+
* judges the caller the same way `room-read` does, and a connection with no
|
|
1798
|
+
* proven identity is refused rather than attributed to whatever it claimed.
|
|
1799
|
+
*
|
|
1800
|
+
* KNOWN, DELIBERATE CONSEQUENCE (R9): the observed `clientId` and the
|
|
1801
|
+
* participant ids a room is opened with are currently DIFFERENT NAMESPACES
|
|
1802
|
+
* for everyone but the owner (whose id `onRoomOpen` overwrites with the
|
|
1803
|
+
* observed one). Until those namespaces are reconciled, these verbs will
|
|
1804
|
+
* REFUSE non-owner participants under `--mode team`. That refusal is the
|
|
1805
|
+
* intended outcome -- fail closed, consistently -- and must NOT be "fixed"
|
|
1806
|
+
* by falling back to the wire id, which is the hole this closes.
|
|
1807
|
+
*
|
|
1808
|
+
* With `--mode team` OFF this is the pre-existing behaviour, untouched: the
|
|
1809
|
+
* wire id is attribution only, exactly as the spec knowingly accepted. */
|
|
1810
|
+
actingId(claimed) {
|
|
1811
|
+
if (!this.opts.teamMode)
|
|
1812
|
+
return { ok: true, id: claimed };
|
|
1813
|
+
if (this.clientId === undefined) {
|
|
1814
|
+
return { ok: false, error: 'This connection has no verified identity (no clientId from hello), so it cannot act on a room.' };
|
|
1815
|
+
}
|
|
1816
|
+
return { ok: true, id: this.clientId };
|
|
1817
|
+
}
|
|
1657
1818
|
async onRoomAnswer(m) {
|
|
1658
1819
|
if (!this.opts.rooms) {
|
|
1659
1820
|
this.ch.send({ t: 'room-answer-result', id: m.id, ok: false, error: 'this sidecar does not host rooms' });
|
|
1660
1821
|
return;
|
|
1661
1822
|
}
|
|
1662
1823
|
try {
|
|
1663
|
-
const
|
|
1824
|
+
const actor = this.actingId(m.participantId);
|
|
1825
|
+
if (!actor.ok) {
|
|
1826
|
+
this.ch.send({ t: 'room-answer-result', id: m.id, ok: false, error: actor.error });
|
|
1827
|
+
return;
|
|
1828
|
+
}
|
|
1829
|
+
const room = await this.opts.rooms.answer(m.roomId, actor.id, m.text, m.addressed);
|
|
1664
1830
|
this.ch.send({ t: 'room-answer-result', id: m.id, ok: true, room });
|
|
1665
1831
|
}
|
|
1666
1832
|
catch (err) {
|
|
@@ -1675,7 +1841,12 @@ export class SidecarServer {
|
|
|
1675
1841
|
return;
|
|
1676
1842
|
}
|
|
1677
1843
|
try {
|
|
1678
|
-
const
|
|
1844
|
+
const actor = this.actingId(m.participantId);
|
|
1845
|
+
if (!actor.ok) {
|
|
1846
|
+
this.ch.send({ t: 'room-interject-result', id: m.id, ok: false, error: actor.error });
|
|
1847
|
+
return;
|
|
1848
|
+
}
|
|
1849
|
+
const room = await this.opts.rooms.interject(m.roomId, actor.id, m.text);
|
|
1679
1850
|
this.ch.send({ t: 'room-interject-result', id: m.id, ok: true, room });
|
|
1680
1851
|
}
|
|
1681
1852
|
catch (err) {
|
|
@@ -1690,7 +1861,12 @@ export class SidecarServer {
|
|
|
1690
1861
|
return;
|
|
1691
1862
|
}
|
|
1692
1863
|
try {
|
|
1693
|
-
const
|
|
1864
|
+
const actor = this.actingId(m.participantId);
|
|
1865
|
+
if (!actor.ok) {
|
|
1866
|
+
this.ch.send({ t: 'room-close-result', id: m.id, ok: false, error: actor.error });
|
|
1867
|
+
return;
|
|
1868
|
+
}
|
|
1869
|
+
const room = await this.opts.rooms.close(m.roomId, actor.id, m.reason);
|
|
1694
1870
|
this.ch.send({ t: 'room-close-result', id: m.id, ok: true, room });
|
|
1695
1871
|
}
|
|
1696
1872
|
catch (err) {
|
|
@@ -1809,7 +1985,12 @@ export class SidecarServer {
|
|
|
1809
1985
|
return;
|
|
1810
1986
|
}
|
|
1811
1987
|
try {
|
|
1812
|
-
const
|
|
1988
|
+
const actor = this.actingId(m.participantId);
|
|
1989
|
+
if (!actor.ok) {
|
|
1990
|
+
this.ch.send({ t: 'room-finding-edit-result', id: m.id, ok: false, error: actor.error });
|
|
1991
|
+
return;
|
|
1992
|
+
}
|
|
1993
|
+
const findings = await this.opts.rooms.editFinding(m.roomId, actor.id, m.edit);
|
|
1813
1994
|
this.ch.send({ t: 'room-finding-edit-result', id: m.id, ok: true, findings });
|
|
1814
1995
|
}
|
|
1815
1996
|
catch (err) {
|