@khorsheed/dsh-room 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +48 -0
  4. package/README.zh.md +48 -0
  5. package/cordis.patch.yml +6 -0
  6. package/lib/client.js +11481 -0
  7. package/lib/index.js +3231 -0
  8. package/lib/invariant.js +26 -0
  9. package/lib/journal-CuRnWk66.js +435 -0
  10. package/lib/tool.js +350 -0
  11. package/lib/typert.host.d.ts +3 -0
  12. package/lib/typert.host.js +2615 -0
  13. package/lib/typert.remote-client.d.ts +55 -0
  14. package/lib/typert.remote-client.js +1711 -0
  15. package/lib/types/adapter.d.ts +42 -0
  16. package/lib/types/adapter.js +41 -0
  17. package/lib/types/agent-setup.d.ts +141 -0
  18. package/lib/types/agent-setup.js +145 -0
  19. package/lib/types/client/InviteAgentAction.d.ts +13 -0
  20. package/lib/types/client/InviteAgentAction.js +45 -0
  21. package/lib/types/client/InviteDialog.d.ts +90 -0
  22. package/lib/types/client/InviteDialog.js +216 -0
  23. package/lib/types/client/MemberCard.d.ts +33 -0
  24. package/lib/types/client/MemberCard.js +32 -0
  25. package/lib/types/client/MembersView.d.ts +5 -0
  26. package/lib/types/client/MembersView.js +168 -0
  27. package/lib/types/client/RoomComposer.d.ts +21 -0
  28. package/lib/types/client/RoomComposer.js +211 -0
  29. package/lib/types/client/RoomDockCapsules.d.ts +64 -0
  30. package/lib/types/client/RoomDockCapsules.js +277 -0
  31. package/lib/types/client/RoomEventView.d.ts +11 -0
  32. package/lib/types/client/RoomEventView.js +17 -0
  33. package/lib/types/client/RoomModelPicker.d.ts +18 -0
  34. package/lib/types/client/RoomModelPicker.js +134 -0
  35. package/lib/types/client/RoomPlanView.d.ts +17 -0
  36. package/lib/types/client/RoomPlanView.js +88 -0
  37. package/lib/types/client/RoomQueueStrip.d.ts +26 -0
  38. package/lib/types/client/RoomQueueStrip.js +28 -0
  39. package/lib/types/client/RoomRecoveryView.d.ts +11 -0
  40. package/lib/types/client/RoomRecoveryView.js +38 -0
  41. package/lib/types/client/RoomRelayView.d.ts +12 -0
  42. package/lib/types/client/RoomRelayView.js +20 -0
  43. package/lib/types/client/RoomRunView.d.ts +15 -0
  44. package/lib/types/client/RoomRunView.js +50 -0
  45. package/lib/types/client/RoomSpeechView.d.ts +18 -0
  46. package/lib/types/client/RoomSpeechView.js +62 -0
  47. package/lib/types/client/RoomStatsLine.d.ts +61 -0
  48. package/lib/types/client/RoomStatsLine.js +138 -0
  49. package/lib/types/client/RoomTaskLineView.d.ts +13 -0
  50. package/lib/types/client/RoomTaskLineView.js +21 -0
  51. package/lib/types/client/RoomTodoStrip.d.ts +24 -0
  52. package/lib/types/client/RoomTodoStrip.js +64 -0
  53. package/lib/types/client/format.d.ts +31 -0
  54. package/lib/types/client/format.js +48 -0
  55. package/lib/types/client/icons.d.ts +27 -0
  56. package/lib/types/client/icons.js +11 -0
  57. package/lib/types/client/index.d.ts +39 -0
  58. package/lib/types/client/index.js +471 -0
  59. package/lib/types/client/locales.d.ts +422 -0
  60. package/lib/types/client/locales.js +413 -0
  61. package/lib/types/client/member-color.d.ts +13 -0
  62. package/lib/types/client/member-color.js +22 -0
  63. package/lib/types/client/name-pool.d.ts +17 -0
  64. package/lib/types/client/name-pool.js +25 -0
  65. package/lib/types/client/nodes.d.ts +136 -0
  66. package/lib/types/client/nodes.js +212 -0
  67. package/lib/types/client/preset-visibility.d.ts +91 -0
  68. package/lib/types/client/preset-visibility.js +113 -0
  69. package/lib/types/client/request-ids.d.ts +7 -0
  70. package/lib/types/client/request-ids.js +21 -0
  71. package/lib/types/client/room-store.d.ts +137 -0
  72. package/lib/types/client/room-store.js +244 -0
  73. package/lib/types/client/slots.d.ts +319 -0
  74. package/lib/types/client/slots.js +1 -0
  75. package/lib/types/deliveries.d.ts +5 -0
  76. package/lib/types/deliveries.js +20 -0
  77. package/lib/types/dispatch.d.ts +105 -0
  78. package/lib/types/dispatch.js +467 -0
  79. package/lib/types/index.d.ts +301 -0
  80. package/lib/types/index.js +1200 -0
  81. package/lib/types/invariant.d.ts +16 -0
  82. package/lib/types/invariant.js +24 -0
  83. package/lib/types/journal.d.ts +117 -0
  84. package/lib/types/journal.js +434 -0
  85. package/lib/types/plan-service.d.ts +34 -0
  86. package/lib/types/plan-service.js +266 -0
  87. package/lib/types/plan.d.ts +168 -0
  88. package/lib/types/plan.js +494 -0
  89. package/lib/types/tool.d.ts +56 -0
  90. package/lib/types/tool.js +389 -0
  91. package/lib/types/types.d.ts +813 -0
  92. package/lib/types/types.js +1 -0
  93. package/locale/en.json +6 -0
  94. package/locale/zh.json +6 -0
  95. package/package.json +131 -0
@@ -0,0 +1,42 @@
1
+ /**
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
5
+ * type-only import of the family core (drift-checked at compile time); the
6
+ * RUNTIME stays a probe — `ctx.get('localAgent')` plus method-existence
7
+ * checks — so a composition without the family degrades the CLI-member
8
+ * capability (structured `local-agent-unavailable` at invite, journaled
9
+ * `failed` runs at dispatch) while the main-agent member keeps working.
10
+ * @module @khorsheed/dsh-room/adapter
11
+ */
12
+ import type { Context } from '@deepseek-ai/cordis';
13
+ import type { ContentBlock } from '@deepseek-ai/dsh-llm';
14
+ import type { LocalAgentRegistry } from '@khorsheed/dsh-local-agent';
15
+ /** The delegation-facade slice room consumes (the family's public M1 API). */
16
+ export type LocalAgentFacade = Pick<LocalAgentRegistry, 'start' | 'resume' | 'cancel'> & Partial<Pick<LocalAgentRegistry, 'prepareMember' | 'isPreparedMember' | 'supportsMemberConfiguration'>>;
17
+ /** The roster slice of the registry (older than the M1 facade). */
18
+ export type LocalAgentRosterSlice = Pick<LocalAgentRegistry, 'roster' | 'statusOf'>;
19
+ /**
20
+ * Probe the local-agent facade.
21
+ * @param ctx - host context.
22
+ * @returns the facade when `localAgent` is present and carries the full M1
23
+ * method set, undefined otherwise (absent core, or a pre-facade version).
24
+ */
25
+ export declare function probeLocalAgent(ctx: Context): LocalAgentFacade | undefined;
26
+ /**
27
+ * Probe the registry's roster slice (independent of the delegation facade:
28
+ * a core without M1 still serves the roster, and the invite dialog greys
29
+ * undispatchable providers instead of hiding them).
30
+ * @param ctx - host context.
31
+ * @returns the roster slice, or undefined.
32
+ */
33
+ export declare function probeLocalAgentRoster(ctx: Context): LocalAgentRosterSlice | undefined;
34
+ /**
35
+ * Extract a member's reply text from a settled run: the text blocks of its
36
+ * final output, joined. Non-text blocks (images etc.) are dropped — the
37
+ * blackboard is a text layer.
38
+ * @param output - the settled run's output content.
39
+ * @returns the reply text (empty string when the run produced no text).
40
+ */
41
+ export declare function runOutputText(output: readonly ContentBlock[]): string;
42
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1,41 @@
1
+ /** The methods a service must carry to quack like the facade. */
2
+ const FACADE_METHODS = ['start', 'resume', 'cancel'];
3
+ /**
4
+ * Probe the local-agent facade.
5
+ * @param ctx - host context.
6
+ * @returns the facade when `localAgent` is present and carries the full M1
7
+ * method set, undefined otherwise (absent core, or a pre-facade version).
8
+ */
9
+ export function probeLocalAgent(ctx) {
10
+ const service = ctx.get('localAgent');
11
+ if (service === undefined || service === null)
12
+ return undefined;
13
+ return FACADE_METHODS.every(method => typeof service[method] === 'function')
14
+ ? service
15
+ : undefined;
16
+ }
17
+ /**
18
+ * Probe the registry's roster slice (independent of the delegation facade:
19
+ * a core without M1 still serves the roster, and the invite dialog greys
20
+ * undispatchable providers instead of hiding them).
21
+ * @param ctx - host context.
22
+ * @returns the roster slice, or undefined.
23
+ */
24
+ export function probeLocalAgentRoster(ctx) {
25
+ const service = ctx.get('localAgent');
26
+ if (service === undefined || service === null)
27
+ return undefined;
28
+ return typeof service['roster'] === 'function' && typeof service['statusOf'] === 'function'
29
+ ? service
30
+ : undefined;
31
+ }
32
+ /**
33
+ * Extract a member's reply text from a settled run: the text blocks of its
34
+ * final output, joined. Non-text blocks (images etc.) are dropped — the
35
+ * blackboard is a text layer.
36
+ * @param output - the settled run's output content.
37
+ * @returns the reply text (empty string when the run produced no text).
38
+ */
39
+ export function runOutputText(output) {
40
+ return output.flatMap(block => (block.type === 'text' ? [block.text] : [])).join('\n');
41
+ }
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Room agent lifecycle. A room IS its session's agent: the local-agent
3
+ * delegation facade resolves the dispatch parent through `ctx.agents.get`
4
+ * (live agents only — a harness hard constraint), and the main-agent member
5
+ * speaks through the same agent. So a room is created THROUGH the agent
6
+ * factory (the official `session.create` shape: session + live agent + the
7
+ * preset recorded on the header), and a mutating Remote on a cold room
8
+ * (after a host restart nothing republishes it — history reads never resume
9
+ * an agent) cold-resumes the agent first, composing the preset the session's
10
+ * log records (mirroring apiproxy's agentFor). Read-only remotes
11
+ * (isRoom/getState) never resume: a cold room answers from a persistence
12
+ * inspection instead.
13
+ * @module @khorsheed/dsh-room/agent-setup
14
+ */
15
+ import type { Context } from '@deepseek-ai/cordis';
16
+ import type { AgentSetup } from '@deepseek-ai/dsh-agent';
17
+ import type AgentPresets from '@deepseek-ai/dsh-agent-preset-registry';
18
+ import type { Session, SessionId } from '@deepseek-ai/dsh-session';
19
+ import type { SessionInspection, SessionPersistence } from '@deepseek-ai/dsh-session-persistence';
20
+ /**
21
+ * The slice of a session log the preset derivation reads. Structural rather
22
+ * than the rc host's deleted `PresetBearingSession`: the 0.1.2 host removed
23
+ * that type together with `resolveSessionPreset`.
24
+ */
25
+ interface PersistedPresetSource {
26
+ header: {
27
+ agentPreset?: string | null;
28
+ };
29
+ events: readonly {
30
+ type: string;
31
+ data?: unknown;
32
+ }[];
33
+ }
34
+ /**
35
+ * The two preset-derivation surfaces a host may carry: the rc line exports
36
+ * `resolveSessionPreset`; the 0.1.2 line replaced it with the
37
+ * `agentPresetProjectionDefinition` unit. Probed per call, never from a
38
+ * version string (mirrors ankh-guard's PresetDerivationSurface).
39
+ */
40
+ export interface PresetDerivationSurface {
41
+ agentPresetProjectionDefinition?: {
42
+ init(header: {
43
+ agentPreset?: string | null;
44
+ }): string | null;
45
+ apply(state: string | null, event: {
46
+ type: string;
47
+ data?: unknown;
48
+ }): string | null;
49
+ };
50
+ resolveSessionPreset?: (session: PersistedPresetSource) => string | undefined;
51
+ }
52
+ /**
53
+ * Which preset a session actually runs, newest selection winning. The 0.1.2
54
+ * host folds the header plus `agent-preset/selected` events through
55
+ * `agentPresetProjectionDefinition`; the rc host's `resolveSessionPreset` is
56
+ * the same fold. A host with neither yields undefined: the resume falls back
57
+ * to the deployment's default preset, the same outcome a preset-less session
58
+ * had before.
59
+ * @param host - the agent-presets module namespace, structurally probed.
60
+ * @param session - the session's header and event log.
61
+ * @returns the preset id, or `undefined` when the session names none.
62
+ */
63
+ export declare function deriveSessionPreset(host: PresetDerivationSurface, session: PersistedPresetSource): string | undefined;
64
+ /**
65
+ * Resolve the preset-registry module by name. The 0.1.7-rc.1 host renamed
66
+ * `@deepseek-ai/dsh-agent-presets` to `@deepseek-ai/dsh-agent-preset-registry`
67
+ * (the API carried over); 0.1.5/0.1.6 hosts install only the old name, and a
68
+ * host without preset composition installs neither — `null`, and the
69
+ * derivation host stays `{}`, the established no-preset degrade (the resume
70
+ * falls back to the deployment's default preset). A STATIC import of either
71
+ * name is a module-resolution failure at load on hosts installing only the
72
+ * other — pack-dist strips `dependencies`, so the specifier resolves against
73
+ * the host's install tree. Both specifiers stay static string literals so
74
+ * tsdown externalizes them and tsc type-checks them.
75
+ * @returns the module namespace, or null when neither name resolves.
76
+ */
77
+ export declare function loadPresetRegistry(): Promise<unknown>;
78
+ /**
79
+ * The derivation input resolved by the last preload. `{}` before it, which
80
+ * the derivation reads as "no preset surface" — the no-preset degrade.
81
+ */
82
+ export declare function presetDerivationHost(): PresetDerivationSurface;
83
+ /**
84
+ * Populate {@link presetDerivationHost} once, at apply time — every
85
+ * derivation happens on the resume/create paths that run after apply.
86
+ */
87
+ export declare function preloadPresetDerivationHost(): Promise<void>;
88
+ /** Test seam: drop the cached probe and host so a mocked resolution re-runs. */
89
+ export declare function resetPresetRegistryProbeForTest(): void;
90
+ /** The agentPresets face this package consumes (probed, never injected). */
91
+ export type AgentPresetsProbe = Pick<AgentPresets, 'resolve' | 'mount'>;
92
+ /**
93
+ * Probe the agent-presets roster.
94
+ * @param ctx - host context.
95
+ * @returns the roster slice, or undefined when the composition mounts none
96
+ * (the agent then composes plain — the pre-presets host behavior).
97
+ */
98
+ export declare function probeAgentPresets(ctx: Context): AgentPresetsProbe | undefined;
99
+ /** One agent's preset composition: the header-recorded id plus the setup. */
100
+ export interface RoomAgentComposition {
101
+ /** Preset id to record on the creation header (absent without a roster). */
102
+ readonly agentPreset?: string;
103
+ /** Creation/resume setup mounting the preset into the agent's scope. */
104
+ readonly setup?: AgentSetup;
105
+ }
106
+ /**
107
+ * Compose the create/resume setup for a room session's agent, mirroring the
108
+ * official web path: resolve first (creation records the id on the header),
109
+ * then mount into the agent's scope during setup.
110
+ * @param ctx - host context.
111
+ * @param presetId - the preset to compose; undefined resolves the default.
112
+ * @returns the composition (empty when no presets roster is mounted).
113
+ */
114
+ export declare function composeRoomAgent(ctx: Context, presetId: string | undefined): Promise<RoomAgentComposition>;
115
+ /**
116
+ * The preset a room session runs, read from its log (a blank-window switch
117
+ * wins over the creation header — the official reconstruction rule).
118
+ * @param session - the live room session.
119
+ * @returns the preset id, or undefined when none was recorded.
120
+ */
121
+ export declare function roomSessionPreset(session: Session): string | undefined;
122
+ /** The sessionPersistence face this package consumes (probed, never injected). */
123
+ export type SessionPersistenceProbe = Pick<SessionPersistence, 'open'>;
124
+ /**
125
+ * Probe the session-persistence service.
126
+ * @param ctx - host context.
127
+ * @returns the handle-opening slice, or undefined when no backend is mounted.
128
+ */
129
+ export declare function probeSessionPersistence(ctx: Context): SessionPersistenceProbe | undefined;
130
+ /**
131
+ * Read a cold session's log from persistence without attaching or resuming
132
+ * anything (the read-only remotes' cold path). The 0.1.5 handle-based API
133
+ * replaced the one-shot `inspect`: open a read handle (no write ownership),
134
+ * read the full log, close.
135
+ * @param ctx - host context.
136
+ * @param sessionId - the persisted session.
137
+ * @returns the inspection, or undefined when absent/unreadable.
138
+ */
139
+ export declare function inspectCold(ctx: Context, sessionId: SessionId): Promise<SessionInspection | undefined>;
140
+ export {};
141
+ //# sourceMappingURL=agent-setup.d.ts.map
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Which preset a session actually runs, newest selection winning. The 0.1.2
3
+ * host folds the header plus `agent-preset/selected` events through
4
+ * `agentPresetProjectionDefinition`; the rc host's `resolveSessionPreset` is
5
+ * the same fold. A host with neither yields undefined: the resume falls back
6
+ * to the deployment's default preset, the same outcome a preset-less session
7
+ * had before.
8
+ * @param host - the agent-presets module namespace, structurally probed.
9
+ * @param session - the session's header and event log.
10
+ * @returns the preset id, or `undefined` when the session names none.
11
+ */
12
+ export function deriveSessionPreset(host, session) {
13
+ if (host.agentPresetProjectionDefinition !== undefined) {
14
+ const projection = host.agentPresetProjectionDefinition;
15
+ let state = projection.init(session.header);
16
+ for (const event of session.events)
17
+ state = projection.apply(state, event);
18
+ return state ?? undefined;
19
+ }
20
+ return host.resolveSessionPreset?.(session);
21
+ }
22
+ let presetRegistryProbe;
23
+ let probedHost = {};
24
+ /**
25
+ * Resolve the preset-registry module by name. The 0.1.7-rc.1 host renamed
26
+ * `@deepseek-ai/dsh-agent-presets` to `@deepseek-ai/dsh-agent-preset-registry`
27
+ * (the API carried over); 0.1.5/0.1.6 hosts install only the old name, and a
28
+ * host without preset composition installs neither — `null`, and the
29
+ * derivation host stays `{}`, the established no-preset degrade (the resume
30
+ * falls back to the deployment's default preset). A STATIC import of either
31
+ * name is a module-resolution failure at load on hosts installing only the
32
+ * other — pack-dist strips `dependencies`, so the specifier resolves against
33
+ * the host's install tree. Both specifiers stay static string literals so
34
+ * tsdown externalizes them and tsc type-checks them.
35
+ * @returns the module namespace, or null when neither name resolves.
36
+ */
37
+ export function loadPresetRegistry() {
38
+ presetRegistryProbe ??= import('@deepseek-ai/dsh-agent-preset-registry')
39
+ .catch(() => import('@deepseek-ai/dsh-agent-presets'))
40
+ .catch(() => null);
41
+ return presetRegistryProbe;
42
+ }
43
+ /**
44
+ * The derivation input resolved by the last preload. `{}` before it, which
45
+ * the derivation reads as "no preset surface" — the no-preset degrade.
46
+ */
47
+ export function presetDerivationHost() {
48
+ return probedHost;
49
+ }
50
+ /**
51
+ * Populate {@link presetDerivationHost} once, at apply time — every
52
+ * derivation happens on the resume/create paths that run after apply.
53
+ */
54
+ export async function preloadPresetDerivationHost() {
55
+ probedHost = ((await loadPresetRegistry()) ?? {});
56
+ }
57
+ /** Test seam: drop the cached probe and host so a mocked resolution re-runs. */
58
+ export function resetPresetRegistryProbeForTest() {
59
+ presetRegistryProbe = undefined;
60
+ probedHost = {};
61
+ }
62
+ /**
63
+ * Probe the agent-presets roster.
64
+ * @param ctx - host context.
65
+ * @returns the roster slice, or undefined when the composition mounts none
66
+ * (the agent then composes plain — the pre-presets host behavior).
67
+ */
68
+ export function probeAgentPresets(ctx) {
69
+ const service = ctx.get('agentPresets');
70
+ if (service === undefined || service === null)
71
+ return undefined;
72
+ return typeof service.resolve === 'function' && typeof service.mount === 'function'
73
+ ? service
74
+ : undefined;
75
+ }
76
+ /**
77
+ * Compose the create/resume setup for a room session's agent, mirroring the
78
+ * official web path: resolve first (creation records the id on the header),
79
+ * then mount into the agent's scope during setup.
80
+ * @param ctx - host context.
81
+ * @param presetId - the preset to compose; undefined resolves the default.
82
+ * @returns the composition (empty when no presets roster is mounted).
83
+ */
84
+ export async function composeRoomAgent(ctx, presetId) {
85
+ const presets = probeAgentPresets(ctx);
86
+ if (presets === undefined)
87
+ return {};
88
+ const resolved = await presets.resolve(presetId);
89
+ return {
90
+ agentPreset: resolved.id,
91
+ setup: async (agentCtx) => { await presets.mount(agentCtx, resolved.id); },
92
+ };
93
+ }
94
+ /**
95
+ * The preset a room session runs, read from its log (a blank-window switch
96
+ * wins over the creation header — the official reconstruction rule).
97
+ * @param session - the live room session.
98
+ * @returns the preset id, or undefined when none was recorded.
99
+ */
100
+ export function roomSessionPreset(session) {
101
+ return deriveSessionPreset(presetDerivationHost(), { header: session.header, events: session.snapshotEvents() });
102
+ }
103
+ /**
104
+ * Probe the session-persistence service.
105
+ * @param ctx - host context.
106
+ * @returns the handle-opening slice, or undefined when no backend is mounted.
107
+ */
108
+ export function probeSessionPersistence(ctx) {
109
+ const service = ctx.get('sessionPersistence');
110
+ if (service === undefined || service === null)
111
+ return undefined;
112
+ return typeof service.open === 'function' ? service : undefined;
113
+ }
114
+ /**
115
+ * Read a cold session's log from persistence without attaching or resuming
116
+ * anything (the read-only remotes' cold path). The 0.1.5 handle-based API
117
+ * replaced the one-shot `inspect`: open a read handle (no write ownership),
118
+ * read the full log, close.
119
+ * @param ctx - host context.
120
+ * @param sessionId - the persisted session.
121
+ * @returns the inspection, or undefined when absent/unreadable.
122
+ */
123
+ export async function inspectCold(ctx, sessionId) {
124
+ const persistence = probeSessionPersistence(ctx);
125
+ if (persistence === undefined)
126
+ return undefined;
127
+ try {
128
+ const handle = await persistence.open(sessionId, 'read');
129
+ try {
130
+ const cold = await handle.read(0);
131
+ return {
132
+ meta: structuredClone(handle.header),
133
+ inheritedEventCount: handle.inheritedEventCount,
134
+ events: [...cold.events],
135
+ };
136
+ }
137
+ finally {
138
+ await handle.close();
139
+ }
140
+ }
141
+ catch {
142
+ // Unknown id, torn log, refused vocabulary: the probe answers "absent".
143
+ return undefined;
144
+ }
145
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The session-header「邀请 agent」action (the `conversation.session.header.actions`
3
+ * list slot, every session): one quiet chip that opens the invite dialog.
4
+ * Inviting into a plain session PROMOTES it into a room host-side
5
+ * (ensureRoom); the dialog and its submit path are the same ones the members
6
+ * tab uses.
7
+ * @module @khorsheed/dsh-room/client/InviteAgentAction
8
+ */
9
+ import { type ReactNode } from 'react';
10
+ import type { InviteAgentActionProps } from './slots.ts';
11
+ /** The header chip plus its dialog. */
12
+ export declare function InviteAgentAction({ roomCwd, invite, listProviders, listNames, browseDirectory, modelSurface, renderHarnessModelPicker, roomChrome, sessionId, t, }: InviteAgentActionProps): ReactNode;
13
+ //# sourceMappingURL=InviteAgentAction.d.ts.map
@@ -0,0 +1,45 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * The session-header「邀请 agent」action (the `conversation.session.header.actions`
4
+ * list slot, every session): one quiet chip that opens the invite dialog.
5
+ * Inviting into a plain session PROMOTES it into a room host-side
6
+ * (ensureRoom); the dialog and its submit path are the same ones the members
7
+ * tab uses.
8
+ * @module @khorsheed/dsh-room/client/InviteAgentAction
9
+ */
10
+ import { useEffect, useReducer, useState } from 'react';
11
+ import { InviteDialog } from './InviteDialog.js';
12
+ import css from './InviteAgentAction.module.css';
13
+ /** The header chip plus its dialog. */
14
+ export function InviteAgentAction({ roomCwd, invite, listProviders, listNames, browseDirectory, modelSurface, renderHarnessModelPicker, roomChrome, sessionId, t, }) {
15
+ const [open, setOpen] = useState(false);
16
+ const [providers, setProviders] = useState(undefined);
17
+ // Re-render when the criterion's inputs move (the inventory answer lands,
18
+ // or the current session flips the per-session verdict).
19
+ const [, bump] = useReducer((count) => count + 1, 0);
20
+ useEffect(() => roomChrome.subscribe(bump), [roomChrome]);
21
+ const openDialog = () => {
22
+ setProviders(undefined);
23
+ setOpen(true);
24
+ void listProviders().then(setProviders);
25
+ };
26
+ const submit = async (values) => {
27
+ // Blank optional fields are omitted, not sent (the host rejects a
28
+ // present-but-blank role; an absent one simply carries no preset).
29
+ const outcome = await invite({
30
+ provider: values.provider,
31
+ name: values.name,
32
+ ...values.instructions === '' ? {} : { instructions: values.instructions },
33
+ ...values.cwd === '' ? {} : { cwd: values.cwd },
34
+ ...values.firstTask === '' ? {} : { firstTask: values.firstTask },
35
+ ...values.model === '' ? {} : { model: values.model },
36
+ });
37
+ return outcome;
38
+ };
39
+ // M3' self-hide: the chip is the room's dev affordance — it renders only
40
+ // when this session's preset composition grants the room tools (or the
41
+ // session IS a room, or the criterion has no answer — all fail-open).
42
+ if (!roomChrome.show(sessionId))
43
+ return null;
44
+ return (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: css.trigger, "aria-label": t('action.inviteAgent'), onClick: openDialog, children: t('action.inviteAgent') }), open && (_jsx(InviteDialog, { mode: "invite", providers: providers?.providers, localAgentAvailable: providers?.localAgentAvailable ?? true, inheritedCwd: roomCwd, existingNames: listNames(), browseDirectory: browseDirectory, modelSurface: modelSurface, renderHarnessModelPicker: renderHarnessModelPicker, onSubmit: submit, onClose: () => { setOpen(false); }, t: t }))] }));
45
+ }
@@ -0,0 +1,90 @@
1
+ import type { LocalAgentUi } from '@khorsheed/dsh-local-agent/client';
2
+ /**
3
+ * The invite/edit dialog as a character-creation card: a self-drawn
4
+ * lightweight modal (overlay + centered card, Esc/overlay-click closes) with
5
+ * a live member-card preview beside the form — every keystroke re-renders
6
+ * the preview (the preview avatar stays neutral; the name-hash color joins
7
+ * once the member is seated), and a 🎲 dice button rolls a random
8
+ * game-flavored name from the pool (name-pool.ts), skipping names already
9
+ * on the roster. The chrome rides the official design tokens: the near-black
10
+ * button-primary-fill capsule is the one filled action, and no blue accent
11
+ * appears (see InviteDialog.module.css).
12
+ *
13
+ * Field order is primary-first: provider → 默认模型 → 称呼(+🎲) → 首个任务 →
14
+ * ▸ 高级设置 (role instructions + the member-level cwd, collapsed on invite —
15
+ * editing opens it, since editing IS changing those). The model field sits in
16
+ * the main form (promoted out of the drawer): the same control the provider
17
+ * settings cards use. Invites retain a saved provider choice, otherwise the
18
+ * shared picker chooses the first candidate. Edits retain member configuration.
19
+ * Older cores without the shared picker keep the legacy default/input fallback.
20
+ * The rest of the original
21
+ * contract is unchanged: logged-out providers grey with login guidance (an
22
+ * absent facade degrades the whole section to a hint), the name precheck
23
+ * plus the host's structured duplicate/invalid errors ride the inline error
24
+ * line, instructions ride the front of the member's FIRST task message
25
+ * (never a system-prompt channel), the cwd is a read-only display filled by
26
+ * the 浏览… button (the official `uiWorkspace.pickDirectory` call;
27
+ * empty = inherit the room session's cwd, shown as the placeholder), and
28
+ * edit mode diffs name/cwd/instructions/model.
29
+ */
30
+ import { type ReactNode } from 'react';
31
+ import type { MembersViewProps, RoomInviteOutcome, RoomMutationOutcome } from './slots.ts';
32
+ import type { RoomMember, RoomProviderInfo } from '../types.ts';
33
+ import type { LocalAgentModelInfo } from '@khorsheed/dsh-local-agent/types';
34
+ /** The dialog's submit values (edit mode carries name/cwd/instructions/model). */
35
+ export interface InviteDialogSubmit {
36
+ readonly provider: string;
37
+ readonly name: string;
38
+ readonly instructions: string;
39
+ readonly cwd: string;
40
+ readonly firstTask: string;
41
+ /** The delegation's invite-time model ('' = follow the harness default). */
42
+ readonly model: string;
43
+ /**
44
+ * The model value the edit field opened with (the member's effective model
45
+ * when the broker answered, else the journaled intent): the edit-mode diff
46
+ * baseline — a save only touches the broker/journal when the value moved.
47
+ */
48
+ readonly modelBaseline: string;
49
+ }
50
+ export interface InviteDialogProps {
51
+ readonly renderHarnessModelPicker?: LocalAgentUi['renderHarnessModelPicker'] | undefined;
52
+ readonly memberConfiguration?: ReactNode | undefined;
53
+ readonly mode: 'invite' | 'edit';
54
+ /** The member being edited (edit mode). */
55
+ readonly member?: RoomMember | undefined;
56
+ /** The invitable providers; undefined while loading (invite mode). */
57
+ readonly providers?: readonly RoomProviderInfo[] | undefined;
58
+ /** False = the delegation facade is absent (invite mode). */
59
+ readonly localAgentAvailable: boolean;
60
+ /** The room session's cwd (the empty-cwd placeholder, invite mode). */
61
+ readonly inheritedCwd?: string | undefined;
62
+ /** The roster's current names — the name dice never rolls one of these. */
63
+ readonly existingNames: readonly string[];
64
+ /**
65
+ * The 浏览… button's pick call (the official `uiWorkspace.pickDirectory`
66
+ * call): resolves the picked absolute path, null on cancel or when the
67
+ * host serves no native picking capability.
68
+ */
69
+ readonly browseDirectory: () => Promise<string | null>;
70
+ /**
71
+ * The pickable model surface of one harness (the localAgentGateway
72
+ * `harnessModel` read): the model row's menu vocabulary plus the default an
73
+ * unset field displays. Absent, the model field is a plain text input.
74
+ */
75
+ readonly modelSurface?: ((harness: string) => Promise<LocalAgentModelInfo | undefined>) | undefined;
76
+ /**
77
+ * A member's own model surface (the localAgentGateway `memberModel` read):
78
+ * the edit dialog's prefill and menu source — preferred over
79
+ * `harnessModel` for an existing member, since it carries that member's
80
+ * effective/source/choices. null/undefined answers keep the journaled
81
+ * intent as the baseline.
82
+ */
83
+ readonly memberModel?: ((childSessionId: string) => Promise<LocalAgentModelInfo | null | undefined>) | undefined;
84
+ readonly onSubmit: (values: InviteDialogSubmit) => Promise<RoomMutationOutcome | RoomInviteOutcome>;
85
+ readonly onClose: () => void;
86
+ readonly t: MembersViewProps['t'];
87
+ }
88
+ /** The invite/edit modal card. */
89
+ export declare function InviteDialog({ mode, member, providers, localAgentAvailable, inheritedCwd, existingNames, browseDirectory, modelSurface, renderHarnessModelPicker, memberModel, memberConfiguration, onSubmit, onClose, t, }: InviteDialogProps): ReactNode;
90
+ //# sourceMappingURL=InviteDialog.d.ts.map