@khorsheed/dsh-room 0.2.0 → 0.2.2

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,7 +1,7 @@
1
1
  /**
2
2
  * The room's ONLY coupling point to the local-agent family's delegation
3
- * facade (`proposals/closed/2026-08-18-local-agent-delegation-api.md`, M1
4
- * landed as `LocalAgentRegistry.start/resume/cancel`). The TYPES come from a
3
+ * facade (`.agents/notes/implemented/feature/2026-08-18-local-agent-delegation-facade.md`,
4
+ * M1 landed as `LocalAgentRegistry.start/resume/cancel`). The TYPES come from a
5
5
  * type-only import of the family core (drift-checked at compile time); the
6
6
  * RUNTIME stays a probe — `ctx.get('localAgent')` plus method-existence
7
7
  * checks — so a composition without the family degrades the CLI-member
@@ -17,5 +17,5 @@ import type { RoomComposerMatch, RoomComposerProps } from './slots.ts';
17
17
  */
18
18
  export declare function selectRoomComposer(owner: ComposerChainProps, isRoomCached: (sessionId: SessionId) => boolean): RoomComposerMatch | null;
19
19
  /** The room composer takeover component. */
20
- export declare function RoomComposer({ sessionId, roomStore, submit, stop, addTask, closeTask, setGoal, planCommand, openPlanSession, openPlan, stopExecution, backgroundSessions, activeChildren, stopChild, reconcileDelivery, roomCwd, invite, listProviders, listNames, browseDirectory, modelSurface, renderHarnessModelPicker, roomChrome, modelDirectory, renderMemberConfiguration, renderMemberInbox, stopMember, useSession, useProjection, t, }: RoomComposerProps): ReactNode;
20
+ export declare function RoomComposer({ sessionId, roomStore, submit, stop, addTask, closeTask, setGoal, planCommand, openPlanSession, openPlan, stopExecution, backgroundSessions, activeChildren, stopChild, reconcileDelivery, roomCwd, invite, listProviders, listNames, browseDirectory, modelSurface, renderHarnessModelPicker, roomChrome, modelDirectory, renderMemberConfiguration, renderMemberInbox, stopMember, useSession, useProjection, useInput, inputActions, t, }: RoomComposerProps): ReactNode;
21
21
  //# sourceMappingURL=RoomComposer.d.ts.map
@@ -11,14 +11,22 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
11
  * records explicit addressing wherever the `@name` sits in the sentence —
12
12
  * the text is dispatched verbatim, the picks ride the postMessage request's
13
13
  * `targets`). Hand-typed mid-sentence mentions without a menu pick stay
14
- * prose: they address nobody. A bare message (neither channel) is NOT room
15
- * business: it is released to the official submit
16
- * path — the takeover writes the text into the session's official input
17
- * machine (`inputActions.setDraft` + `inputActions.submit()`, the same entry
18
- * the InputBar's Enter key drives), so it becomes an ordinary main-agent
19
- * turn; sent while that turn runs, the machine's busy admission enqueues it
20
- * exactly as the official bar's does. A structured rejection (unknown
21
- * targets) shows as an inline error line.
14
+ * prose: they address nobody. EVERY send — bare text included — routes
15
+ * through room's own dispatch (the postMessage Remote; the host routes bare
16
+ * text to the current coordinator), never through `inputActions.submit()`:
17
+ * the machine's busy admission would enqueue behind a running coordinator
18
+ * and serialize what the dispatch engine deliberately runs in parallel. A
19
+ * structured rejection (unknown targets) shows as an inline error line.
20
+ *
21
+ * The DRAFT, though, lives in the session's official input machine: the
22
+ * chain occupant's standard session kit (`useInput`/`inputActions`) is this
23
+ * component's draft store, so the takeover and the display:none fallback
24
+ * share one draft — text typed during the first-pull election window
25
+ * carries over, drafts persist across navigation, and external draft
26
+ * writers (quote-anything's 引用到当前会话) land here with zero
27
+ * room-specific code. A reference chip in the machine projects to its
28
+ * clipboard text in this plain textarea, and editing flattens chips back
29
+ * to text (`setDraft` semantics).
22
30
  *
23
31
  * Taking over the chain inherits the official bar's environment duties (the
24
32
  * member-channel rule, local-agent 84a2ed0): a pending interaction yields (the
@@ -36,7 +44,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
36
44
  * picker).
37
45
  */
38
46
  import { coordinatorMember } from '../journal.js';
39
- import { useRef, useState, useSyncExternalStore, } from 'react';
47
+ import { useEffect, useRef, useState, useSyncExternalStore, } from 'react';
40
48
  import { memberColor } from './member-color.js';
41
49
  import { RoomStatsLine } from './RoomStatsLine.js';
42
50
  import { RoomDockCapsules } from './RoomDockCapsules.js';
@@ -91,9 +99,12 @@ export function selectRoomComposer(owner, isRoomCached) {
91
99
  return sessionId !== undefined && isRoomCached(sessionId) ? { room: true } : null;
92
100
  }
93
101
  /** The room composer takeover component. */
94
- export function RoomComposer({ sessionId, roomStore, submit, stop, addTask, closeTask, setGoal, planCommand, openPlanSession, openPlan, stopExecution, backgroundSessions, activeChildren, stopChild, reconcileDelivery, roomCwd, invite, listProviders, listNames, browseDirectory, modelSurface, renderHarnessModelPicker, roomChrome, modelDirectory, renderMemberConfiguration, renderMemberInbox, stopMember, useSession, useProjection, t, }) {
102
+ export function RoomComposer({ sessionId, roomStore, submit, stop, addTask, closeTask, setGoal, planCommand, openPlanSession, openPlan, stopExecution, backgroundSessions, activeChildren, stopChild, reconcileDelivery, roomCwd, invite, listProviders, listNames, browseDirectory, modelSurface, renderHarnessModelPicker, roomChrome, modelDirectory, renderMemberConfiguration, renderMemberInbox, stopMember, useSession, useProjection, useInput, inputActions, t, }) {
95
103
  const state = useSyncExternalStore(roomStore.subscribe, () => roomStore.getCached(sessionId));
96
- const [draft, setDraft] = useState('');
104
+ // The draft is the session's official input machine (the module header):
105
+ // shared with the hidden fallback bar, persistent across navigation, and
106
+ // writable by external drafters (quote) with zero room-specific code.
107
+ const draft = useInput(input => input.draft);
97
108
  const [busy, setBusy] = useState(false);
98
109
  const [error, setError] = useState(null);
99
110
  const [mention, setMention] = useState(null);
@@ -128,9 +139,15 @@ export function RoomComposer({ sessionId, roomStore, submit, stop, addTask, clos
128
139
  area.style.height = 'auto';
129
140
  area.style.height = `${area.scrollHeight}px`;
130
141
  };
142
+ // External draft writes (a quote insertion, the election window's
143
+ // carry-over) never fire onChange — keep the height in sync with the draft.
144
+ useEffect(() => {
145
+ if (areaRef.current !== null)
146
+ resize(areaRef.current);
147
+ }, [draft]);
131
148
  const onChange = (event) => {
132
149
  const value = event.target.value;
133
- setDraft(value);
150
+ inputActions.setDraft(value);
134
151
  resize(event.target);
135
152
  const active = detectMention(value, event.target.selectionStart ?? value.length);
136
153
  setMention(active === null ? null : { ...active, index: 0 });
@@ -140,9 +157,13 @@ export function RoomComposer({ sessionId, roomStore, submit, stop, addTask, clos
140
157
  return;
141
158
  const caret = areaRef.current?.selectionStart ?? draft.length;
142
159
  const next = `${draft.slice(0, mention.start)}@${name} ${draft.slice(caret)}`;
143
- setDraft(next);
160
+ inputActions.setDraft(next);
144
161
  setMention(null);
145
162
  setPicked(picked.includes(name) ? picked : [...picked, name]);
163
+ // The machine tracks no caret: restore it behind the spliced pick once
164
+ // the store round-trip has re-rendered the textarea.
165
+ const restored = mention.start + name.length + 2;
166
+ requestAnimationFrame(() => areaRef.current?.setSelectionRange(restored, restored));
146
167
  };
147
168
  const send = async () => {
148
169
  const text = draft.trim();
@@ -160,7 +181,11 @@ export function RoomComposer({ sessionId, roomStore, submit, stop, addTask, clos
160
181
  setError(outcome.message);
161
182
  return;
162
183
  }
163
- setDraft('');
184
+ inputActions.setDraft('');
185
+ // Persist the cleared draft when the host's kit exposes the flush (the
186
+ // send bypasses the machine's own submit, which would do it).
187
+ const flushDraft = inputActions.persistDraft;
188
+ flushDraft?.();
164
189
  setMention(null);
165
190
  setPicked([]);
166
191
  }
@@ -72,20 +72,30 @@ export declare class RoomPresetVisibility implements RoomChromeVisibility {
72
72
  * session and disposes otherwise; the slot's own subscription re-renders the
73
73
  * strip (the same re-registration mechanism the composer takeover's
74
74
  * promotion bump uses).
75
+ *
76
+ * Criterion-driven switches apply on the NEXT frame (see {@link nextFrame}),
77
+ * coalesced — repeated syncs collapse into the one scheduled frame, which
78
+ * reads the LATEST decision. The readiness gate's teardown arm is the
79
+ * exception: setReady(false) cancels a pending frame and unmounts a live
80
+ * registration SYNCHRONOUSLY, so nothing can register after it. (The plan
81
+ * tab-type toggle on sidebarRightTabs shares this class — its registry never
82
+ * fed refreshViews, and the same deferral is harmless there.)
75
83
  */
76
84
  export declare class RegistrationToggle {
77
85
  private readonly register;
78
86
  private readonly show;
79
87
  private dispose;
80
88
  private ready;
89
+ /** The coalesced pending re-evaluation's canceler; undefined when no frame is scheduled. */
90
+ private pending;
81
91
  /**
82
92
  * @param register - mounts the entry and returns its disposer.
83
- * @param show - the current decision (re-read on every sync).
93
+ * @param show - the current decision (re-read when the scheduled frame runs).
84
94
  */
85
95
  constructor(register: () => () => void, show: () => boolean);
86
96
  /** The slot declaration appeared (slots.inject's arm) — decisions may register now. */
87
97
  setReady(ready: boolean): void;
88
- /** Re-evaluate: register while shown and ready, dispose otherwise. */
98
+ /** Re-evaluate on the next frame: register while shown and ready, dispose otherwise. */
89
99
  sync(): void;
90
100
  }
91
101
  //# sourceMappingURL=preset-visibility.d.ts.map
@@ -66,6 +66,27 @@ export class RoomPresetVisibility {
66
66
  listener();
67
67
  }
68
68
  }
69
+ /**
70
+ * The register/unregister beat: the NEXT animation frame (a zero timeout
71
+ * where no rAF exists — headless tests), cancellable.
72
+ *
73
+ * Why not synchronous: every change of a `conversation.view` registration
74
+ * re-triggers ui-conversation's `refreshViews`, which re-reads every mounted
75
+ * session binding and throws UNCAUGHT on one whose session record is already
76
+ * released — the window a session switch opens, and this toggle's sync() rides
77
+ * exactly that flush (upstream proposal
78
+ * docs/upstream-proposals/2026-10-20-refreshviews-stale-binding-robustness.md;
79
+ * the production incident killed the message timeline, which shared the
80
+ * aborted flush). One frame later the flush has settled.
81
+ */
82
+ function nextFrame(callback) {
83
+ if (typeof requestAnimationFrame === 'function') {
84
+ const id = requestAnimationFrame(callback);
85
+ return () => { cancelAnimationFrame(id); };
86
+ }
87
+ const id = setTimeout(callback, 0);
88
+ return () => { clearTimeout(id); };
89
+ }
69
90
  /**
70
91
  * The members tab's registration toggle: the `conversation.view` tab strip's
71
92
  * BUTTONS enumerate the slot's registrations (ui-conversation's `viewTabs()`
@@ -75,15 +96,25 @@ export class RoomPresetVisibility {
75
96
  * session and disposes otherwise; the slot's own subscription re-renders the
76
97
  * strip (the same re-registration mechanism the composer takeover's
77
98
  * promotion bump uses).
99
+ *
100
+ * Criterion-driven switches apply on the NEXT frame (see {@link nextFrame}),
101
+ * coalesced — repeated syncs collapse into the one scheduled frame, which
102
+ * reads the LATEST decision. The readiness gate's teardown arm is the
103
+ * exception: setReady(false) cancels a pending frame and unmounts a live
104
+ * registration SYNCHRONOUSLY, so nothing can register after it. (The plan
105
+ * tab-type toggle on sidebarRightTabs shares this class — its registry never
106
+ * fed refreshViews, and the same deferral is harmless there.)
78
107
  */
79
108
  export class RegistrationToggle {
80
109
  register;
81
110
  show;
82
111
  dispose;
83
112
  ready = false;
113
+ /** The coalesced pending re-evaluation's canceler; undefined when no frame is scheduled. */
114
+ pending;
84
115
  /**
85
116
  * @param register - mounts the entry and returns its disposer.
86
- * @param show - the current decision (re-read on every sync).
117
+ * @param show - the current decision (re-read when the scheduled frame runs).
87
118
  */
88
119
  constructor(register, show) {
89
120
  this.register = register;
@@ -92,22 +123,32 @@ export class RegistrationToggle {
92
123
  /** The slot declaration appeared (slots.inject's arm) — decisions may register now. */
93
124
  setReady(ready) {
94
125
  this.ready = ready;
95
- if (!ready && this.dispose !== undefined) {
96
- this.dispose();
97
- this.dispose = undefined;
126
+ if (!ready) {
127
+ this.pending?.();
128
+ this.pending = undefined;
129
+ if (this.dispose !== undefined) {
130
+ this.dispose();
131
+ this.dispose = undefined;
132
+ }
133
+ return;
98
134
  }
99
135
  this.sync();
100
136
  }
101
- /** Re-evaluate: register while shown and ready, dispose otherwise. */
137
+ /** Re-evaluate on the next frame: register while shown and ready, dispose otherwise. */
102
138
  sync() {
103
- if (!this.ready)
139
+ if (!this.ready || this.pending !== undefined)
104
140
  return;
105
- if (this.show()) {
106
- this.dispose ??= this.register();
107
- }
108
- else if (this.dispose !== undefined) {
109
- this.dispose();
110
- this.dispose = undefined;
111
- }
141
+ this.pending = nextFrame(() => {
142
+ this.pending = undefined;
143
+ if (!this.ready)
144
+ return;
145
+ if (this.show()) {
146
+ this.dispose ??= this.register();
147
+ }
148
+ else if (this.dispose !== undefined) {
149
+ this.dispose();
150
+ this.dispose = undefined;
151
+ }
152
+ });
112
153
  }
113
154
  }
@@ -172,10 +172,13 @@ export interface RoomComposerInjected extends RoomTasksInjected, RoomInviteInjec
172
172
  readonly renderMemberConfiguration?: LocalAgentUi['renderMemberConfiguration'] | undefined;
173
173
  readonly stopMember?: ((name: string, expectedRunId?: string) => void) | undefined;
174
174
  /**
175
- * Dispatch an @-message into the room and refresh the store on success.
176
- * Bare messages never reach here — the composer releases them to the
177
- * official submit path (useInput/inputActions) itself. `targets` carries
178
- * the mention-menu picks (explicit addressing wherever the `@name` sits in
175
+ * Dispatch a message into the room and refresh the store on success. EVERY
176
+ * send routes here, bare text included (the host routes bare text to the
177
+ * current coordinator); the composer's useInput/inputActions kit carries
178
+ * the DRAFT only, never the submission — the official machine's busy
179
+ * admission must stay out of the dispatch path (it would serialize what
180
+ * the dispatch engine runs in parallel). `targets` carries the
181
+ * mention-menu picks (explicit addressing wherever the `@name` sits in
179
182
  * the sentence); the host unions them with the parsed leading tokens.
180
183
  */
181
184
  readonly submit: (sessionId: SessionId, text: string, targets?: readonly string[]) => Promise<RoomMutationOutcome>;
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "cordis",
8
8
  "multi-agent"
9
9
  ],
10
- "version": "0.2.0",
10
+ "version": "0.2.2",
11
11
  "type": "module",
12
12
  "main": "lib/index.js",
13
13
  "types": "lib/types/index.d.ts",
@@ -16,10 +16,6 @@
16
16
  "types": "./lib/types/index.d.ts",
17
17
  "default": "./lib/index.js"
18
18
  },
19
- "./invariant": {
20
- "types": "./lib/types/invariant.d.ts",
21
- "default": "./lib/invariant.js"
22
- },
23
19
  "./client": {
24
20
  "types": "./lib/types/client/index.d.ts",
25
21
  "default": "./lib/client.js"
@@ -42,7 +38,8 @@
42
38
  },
43
39
  "./src/*": "./src/*",
44
40
  "./package.json": "./package.json",
45
- "./locale/*.json": "./locale/*.json"
41
+ "./locale/*.json": "./locale/*.json",
42
+ "./tool/locale/*.json": "./locale/*.json"
46
43
  },
47
44
  "dsh": {
48
45
  "bundle": {
@@ -59,36 +56,38 @@
59
56
  "compat": {
60
57
  "minHost": "0.1.5-rc.1",
61
58
  "notes": "0.1.5-rc.1 full-line boot-verified 2026-09-25 (42 packages including capture) through the three compat layers: preset-registry dual-name probe, dual-shape typert codecs, and typert faces carrying zod@4; 0.1.7-rc.2 is the 3080 production-verified line. External coordinators require the unpublished local-agent family with prepared members, shared model/effort controls and authenticated member tools; without it these operations degrade to unavailable. Scoped cwd and cross-restart mappings are supported. Native DSH session model switching retains host semantics. Room vocabulary is registered through the active loader. DSH/Kimi source-host runtime checks and candidate tarball fresh/upgrade checks on the npm host pass; Codex/Claude auth and full P95 streaming acceptance remain pending. The session-granted room tool row folded back from the standalone `@khorsheed/dsh-room-tool` companion into this package at 0.2.0 as the `./tool` composition entry — the preset row id `room-tool` is unchanged, so a preset's composition state is unaffected once its row name points at `@khorsheed/dsh-room/tool`; the old npm name is deprecated.",
62
- "verifiedHost": "0.1.7-rc.2"
59
+ "verifiedHost": "0.2.1-rc.1"
63
60
  }
64
61
  },
65
62
  "license": "MIT",
63
+ "engines": {
64
+ "dsh": ">=0.1.5-rc.1 <0.3.0-0"
65
+ },
66
66
  "dependencies": {
67
67
  "zod": "^4.4.3"
68
68
  },
69
69
  "peerDependencies": {
70
70
  "@deepseek-ai/cordis": "^4.0.1",
71
- "@deepseek-ai/dsh-agent": "^0.1.0-rc.6",
72
- "@deepseek-ai/dsh-agent-preset-registry": "^0.1.0-rc.6",
73
- "@deepseek-ai/dsh-agent-presets": "^0.1.0-rc.6",
74
- "@deepseek-ai/dsh-api-remotes": "^0.1.0-rc.6",
75
- "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6",
76
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6",
77
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.6",
78
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6",
79
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
80
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
81
- "@deepseek-ai/dsh-session": "^0.1.0-rc.6",
82
- "@deepseek-ai/dsh-session-persistence": "^0.1.0-rc.6",
83
- "@deepseek-ai/dsh-session-stats": "^0.1.0-rc.6",
84
- "@deepseek-ai/dsh-subagent": "^0.1.0-rc.6",
85
- "@deepseek-ai/dsh-token-meter": "^0.1.0-rc.6",
86
- "@deepseek-ai/dsh-tool-todo": "^0.1.0-rc.6",
87
- "@deepseek-ai/dsh-tools": "^0.1.0-rc.6",
88
- "@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.6",
89
- "@khorsheed/dsh-local-agent": "^0.1.0-rc.7",
71
+ "@deepseek-ai/dsh-agent": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
72
+ "@deepseek-ai/dsh-agent-preset-registry": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
73
+ "@deepseek-ai/dsh-agent-presets": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
74
+ "@deepseek-ai/dsh-api-remotes": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
75
+ "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
76
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
77
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
78
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
79
+ "@deepseek-ai/dsh-llm": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
80
+ "@deepseek-ai/dsh-session": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
81
+ "@deepseek-ai/dsh-session-persistence": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
82
+ "@deepseek-ai/dsh-session-stats": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
83
+ "@deepseek-ai/dsh-subagent": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
84
+ "@deepseek-ai/dsh-token-meter": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
85
+ "@deepseek-ai/dsh-tool-todo": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
86
+ "@deepseek-ai/dsh-tools": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
87
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
88
+ "@khorsheed/dsh-local-agent": "^0.2.0-rc.2",
90
89
  "react": "^18.2.0",
91
- "@deepseek-ai/dsh-client-ui-sidebar-right": "^0.1.0-rc.6"
90
+ "@deepseek-ai/dsh-client-ui-sidebar-right": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1"
92
91
  },
93
92
  "peerDependenciesMeta": {
94
93
  "@deepseek-ai/dsh-agent-preset-registry": {
@@ -115,7 +114,6 @@
115
114
  },
116
115
  "files": [
117
116
  "lib/index.js",
118
- "lib/invariant.js",
119
117
  "lib/client.js",
120
118
  "lib/tool.js",
121
119
  "lib/journal-*.js",
package/lib/invariant.js DELETED
@@ -1,26 +0,0 @@
1
- //#region lib/types/invariant.js
2
- /**
3
- * Package-owned invariant companion for `@khorsheed/dsh-room`.
4
- * @module @khorsheed/dsh-room/invariant
5
- */
6
- const PACKAGE_NAME = "@khorsheed/dsh-room";
7
- /** Cordis companion plugin name. */
8
- const name = "room-invariant";
9
- /** Service required before the companion can reserve package ownership. */
10
- const inject = ["invariants"];
11
- /**
12
- * Placeholder (Step 0/1 spike): no runtime invariant yet. The `room/created`
13
- * marker write passes through Session.append, whose validation rejects any
14
- * non-JSON payload, and the client contributions ride the slot/locale
15
- * lifecycles disposed with the plugin fiber. Later steps (roster transition
16
- * validation, dispatch invariants) install real checks here.
17
- */
18
- const install = () => {};
19
- /**
20
- * Register this package's invariant companion.
21
- * @param ctx - Cordis context carrying the invariant service.
22
- * @returns the installed registration's disposer after setup succeeds.
23
- */
24
- const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
25
- //#endregion
26
- export { apply, inject, name };
@@ -1,16 +0,0 @@
1
- /**
2
- * Package-owned invariant companion for `@khorsheed/dsh-room`.
3
- * @module @khorsheed/dsh-room/invariant
4
- */
5
- import type { Context } from '@deepseek-ai/cordis';
6
- /** Cordis companion plugin name. */
7
- export declare const name = "room-invariant";
8
- /** Service required before the companion can reserve package ownership. */
9
- export declare const inject: string[];
10
- /**
11
- * Register this package's invariant companion.
12
- * @param ctx - Cordis context carrying the invariant service.
13
- * @returns the installed registration's disposer after setup succeeds.
14
- */
15
- export declare const apply: (ctx: Context) => Promise<() => void>;
16
- //# sourceMappingURL=invariant.d.ts.map
@@ -1,24 +0,0 @@
1
- /**
2
- * Package-owned invariant companion for `@khorsheed/dsh-room`.
3
- * @module @khorsheed/dsh-room/invariant
4
- */
5
- const PACKAGE_NAME = '@khorsheed/dsh-room';
6
- /** Cordis companion plugin name. */
7
- export const name = 'room-invariant';
8
- /** Service required before the companion can reserve package ownership. */
9
- export const inject = ['invariants'];
10
- /**
11
- * Placeholder (Step 0/1 spike): no runtime invariant yet. The `room/created`
12
- * marker write passes through Session.append, whose validation rejects any
13
- * non-JSON payload, and the client contributions ride the slot/locale
14
- * lifecycles disposed with the plugin fiber. Later steps (roster transition
15
- * validation, dispatch invariants) install real checks here.
16
- */
17
- const install = () => { };
18
- /**
19
- * Register this package's invariant companion.
20
- * @param ctx - Cordis context carrying the invariant service.
21
- * @returns the installed registration's disposer after setup succeeds.
22
- */
23
- export const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
24
- /* jscpd:ignore-end */