dsh-team-rooms 1.0.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/ARCHITECTURE.md +127 -0
- package/LICENSE +201 -0
- package/README-es.md +248 -0
- package/README-hi.md +248 -0
- package/README-pt.md +248 -0
- package/README-zh.md +248 -0
- package/README.md +248 -0
- package/THIRD_PARTY_NOTICES.md +41 -0
- package/cordis.patch.yml +34 -0
- package/lib/client.js +4713 -0
- package/lib/index.js +6580 -0
- package/lib/types/audit.d.ts +80 -0
- package/lib/types/client/TeamRoomsSection.d.ts +33 -0
- package/lib/types/client/index.d.ts +41 -0
- package/lib/types/client/room-locales.d.ts +61 -0
- package/lib/types/client/room-presenter.d.ts +67 -0
- package/lib/types/facts.d.ts +66 -0
- package/lib/types/inbound.d.ts +282 -0
- package/lib/types/index.d.ts +125 -0
- package/lib/types/room/commands.d.ts +23 -0
- package/lib/types/room/domain.d.ts +72 -0
- package/lib/types/room/events.d.ts +105 -0
- package/lib/types/room/hub.d.ts +243 -0
- package/lib/types/room/projection.d.ts +388 -0
- package/lib/types/room/schema.d.ts +550 -0
- package/lib/types/room/tools.d.ts +34 -0
- package/lib/types/vocabulary.d.ts +20 -0
- package/package.json +170 -0
- package/src/audit.ts +113 -0
- package/src/client/TeamRoomsSection.module.css +272 -0
- package/src/client/TeamRoomsSection.tsx +369 -0
- package/src/client/css-modules.d.ts +5 -0
- package/src/client/index.ts +215 -0
- package/src/client/room-locales.ts +115 -0
- package/src/client/room-presenter.ts +126 -0
- package/src/facts.ts +127 -0
- package/src/inbound.ts +440 -0
- package/src/index.ts +309 -0
- package/src/room/commands.ts +278 -0
- package/src/room/domain.ts +38 -0
- package/src/room/events.ts +116 -0
- package/src/room/hub.ts +1022 -0
- package/src/room/projection.ts +267 -0
- package/src/room/schema.ts +153 -0
- package/src/room/tools.ts +603 -0
- package/src/vocabulary.ts +20 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-capability detection for the `ignorable` envelope-marker surface
|
|
3
|
+
* that every log-only plugin fact event depends on.
|
|
4
|
+
*
|
|
5
|
+
* `Session.append(type, data, { ignorable: true })` stamps the envelope
|
|
6
|
+
* marker on host builds that expose the surface (harness master
|
|
7
|
+
* `@deepseek-ai/dsh-session`); every released rc line through `0.1.0-rc.8`
|
|
8
|
+
* and the `0.1.1-rc` line silently drop the options bag, and the
|
|
9
|
+
* `0.1.2-alpha`/`0.1.2-rc` lines keep the envelope field for read
|
|
10
|
+
* compatibility but never accept an options bag either (the third append
|
|
11
|
+
* parameter is `SurfaceIntent` for surface event types only), so fact
|
|
12
|
+
* events land unmarked on all of them and stricter hosts refuse to resume
|
|
13
|
+
* those sessions (`SessionFormatUnsupportedError`).
|
|
14
|
+
* The fact appender detects the host before polluting a log: the installed
|
|
15
|
+
* peer version is checked against the known-unmarked lines first, and an
|
|
16
|
+
* unknown (unresolvable) version is verified by probing the FIRST
|
|
17
|
+
* appended event's returned envelope. The same discipline lives in
|
|
18
|
+
* `dsh-permission-rules` and `dsh-auto-review`.
|
|
19
|
+
* @module dsh-team-rooms/audit
|
|
20
|
+
*/
|
|
21
|
+
/** Host envelope capability: unknown until the first append (or the peer-version pre-check). */
|
|
22
|
+
export type AuditSupport = 'unknown' | 'supported' | 'unsupported';
|
|
23
|
+
/**
|
|
24
|
+
* Whether an `append` call actually honored the `ignorable` marker: the
|
|
25
|
+
* logged event returned by the host carries `ignorable === true` on
|
|
26
|
+
* marker-aware builds and nothing on pre-marker builds. `false` (or any
|
|
27
|
+
* non-event return) means the host dropped the marker and the event landed
|
|
28
|
+
* unmarked — the appender then degrades instead of polluting further logs.
|
|
29
|
+
* @param result - the return value of the fact append.
|
|
30
|
+
* @returns true only when the marker is present on the returned envelope.
|
|
31
|
+
*/
|
|
32
|
+
export declare function isMarkedAuditEvent(result: unknown): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Whether a `@deepseek-ai/dsh-session` version line predates the
|
|
35
|
+
* `ignorable` envelope-marker surface: every released rc line through
|
|
36
|
+
* `0.1.0-rc.8` silently drops the marker from `Session.append` options
|
|
37
|
+
* (the stamping fix exists on harness master only — no release carries it
|
|
38
|
+
* yet), and the `0.1.1-rc` line regressed the same way (verified on
|
|
39
|
+
* `0.1.1-rc.2`), so fact events written by those builds land unmarked and
|
|
40
|
+
* break resume on stricter hosts. The `0.1.2` line keeps the `ignorable`
|
|
41
|
+
* field on the event envelope but has no append option that writes it
|
|
42
|
+
* (the third parameter is `SurfaceIntent` for surface event types only),
|
|
43
|
+
* so every `0.1.2-rc` build is known-unmarked too (verified on
|
|
44
|
+
* `0.1.2-rc.1`). The gate therefore treats
|
|
45
|
+
* `0.1.1-rc.1`–`rc.8` as known-unmarked as well; over-refusal is harmless
|
|
46
|
+
* because `allowUnmarkedFacts: true` opts back in. Extend the bound when a
|
|
47
|
+
* new rc line ships that still drops the marker. Non-matching (later rc,
|
|
48
|
+
* stable, or unresolvable) versions are treated as possibly-marker-aware
|
|
49
|
+
* and verified by the append probe.
|
|
50
|
+
* @param version - the installed peer version string.
|
|
51
|
+
* @returns true for the known-unmarked rc.1–rc.8 lines of `0.1.0` and
|
|
52
|
+
* `0.1.1`, and every `0.1.2-rc` build.
|
|
53
|
+
*/
|
|
54
|
+
export declare function isUnmarkedHostVersion(version: string): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* The installed `@deepseek-ai/dsh-session` version, or `null` when
|
|
57
|
+
* unresolvable (falls back to the append probe).
|
|
58
|
+
* @returns the version string, or null when the peer cannot be resolved.
|
|
59
|
+
*/
|
|
60
|
+
export declare function peerSessionVersion(): string | null;
|
|
61
|
+
/**
|
|
62
|
+
* Host policy for log-only fact events.
|
|
63
|
+
* `append` = the rc lines through `0.1.1-rc.2`: fact events may be
|
|
64
|
+
* appended when the `ignorable` marker is honored; `forbidden` = hosts at
|
|
65
|
+
* `0.1.2-alpha.1` and later (including every `0.1.2-rc` build), which fail
|
|
66
|
+
* closed on the session event
|
|
67
|
+
* vocabulary (`background-agents/fact` and `team-room/fact` are not in
|
|
68
|
+
* KNOWN_SESSION_EVENT_TYPES, so a written fact makes the session
|
|
69
|
+
* unreadable there) — never append fact events on those hosts.
|
|
70
|
+
*/
|
|
71
|
+
export type FactEventPolicy = 'append' | 'forbidden' | 'unknown';
|
|
72
|
+
/**
|
|
73
|
+
* Classify an installed `@deepseek-ai/dsh-session` version for log-only
|
|
74
|
+
* fact events: `forbidden` at `0.1.2-alpha.1` and later, `append` on the
|
|
75
|
+
* earlier rc lines, `unknown` for unresolvable versions.
|
|
76
|
+
* @param version - the installed peer version string.
|
|
77
|
+
* @returns the fact-event policy for that host line.
|
|
78
|
+
*/
|
|
79
|
+
export declare function factEventPolicyForVersion(version: string): FactEventPolicy;
|
|
80
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { PropsLocale, PropsRuntime, InjectFace } from '@deepseek-ai/dsh-client-ui-slots';
|
|
2
|
+
/** Minimal structural snapshot contract (the owner package no longer re-exports the generic). */
|
|
3
|
+
interface ObservableSnapshot<T> {
|
|
4
|
+
getSnapshot(): T;
|
|
5
|
+
subscribe(listener: () => void): () => void;
|
|
6
|
+
}
|
|
7
|
+
import type { TeamRoomsState } from './room-presenter.js';
|
|
8
|
+
import { ROOM_NS } from './room-locales.js';
|
|
9
|
+
/** Business actions supplied by the slot registration (all via /room command execution). */
|
|
10
|
+
export interface TeamRoomsInjected {
|
|
11
|
+
hooks: {
|
|
12
|
+
/** The live controller state: current session id + derived room panels. */
|
|
13
|
+
teamRooms: ObservableSnapshot<TeamRoomsState>;
|
|
14
|
+
};
|
|
15
|
+
/** The current session id (commands execute against it), or undefined. */
|
|
16
|
+
getSessionId(): string | undefined;
|
|
17
|
+
create(name: string): Promise<string | undefined>;
|
|
18
|
+
join(roomId: string): Promise<string | undefined>;
|
|
19
|
+
leave(roomId: string): Promise<string | undefined>;
|
|
20
|
+
post(roomId: string, text: string): Promise<string | undefined>;
|
|
21
|
+
addTask(roomId: string, title: string): Promise<string | undefined>;
|
|
22
|
+
claimTask(roomId: string, taskId: string): Promise<string | undefined>;
|
|
23
|
+
completeTask(roomId: string, taskId: string): Promise<string | undefined>;
|
|
24
|
+
assignTask(roomId: string, taskId: string, member: string): Promise<string | undefined>;
|
|
25
|
+
}
|
|
26
|
+
/** The action share one room card needs (everything except the bound hooks). */
|
|
27
|
+
export type RoomActions = Omit<TeamRoomsInjected, 'hooks'>;
|
|
28
|
+
/** Full component props: the settings-section owner share, locale, and the bound inject face. */
|
|
29
|
+
export type TeamRoomsSectionProps = PropsRuntime<'settings.section'> & PropsLocale<typeof ROOM_NS> & InjectFace<TeamRoomsInjected>;
|
|
30
|
+
/** The settings section: room management over the live session's projection. */
|
|
31
|
+
export declare function TeamRoomsSection(props: TeamRoomsSectionProps): import("react").JSX.Element;
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=TeamRoomsSection.d.ts.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-team-rooms, browser half: the Team Rooms `settings.section` page.
|
|
3
|
+
* Member status, the shared task board, and the timeline of every room the
|
|
4
|
+
* current session belongs to, read from the current session's `teamRoom`
|
|
5
|
+
* projection (live `faceOf` snapshots) and written back through the HOST
|
|
6
|
+
* `/room` command (`remote.commands.execute`) so every action keeps the
|
|
7
|
+
* durable command lifecycle. Zero custom RPC: the page rides the sanctioned
|
|
8
|
+
* plugin channels only.
|
|
9
|
+
*
|
|
10
|
+
* Extracted from the single client entry of `dsh-background-agents` 0.9.6,
|
|
11
|
+
* which served two slots from one bundle (`sidebar.footer.action`
|
|
12
|
+
* id `background-agents` for the background-agent panel, and this
|
|
13
|
+
* `settings.section` id `team-rooms`). Only the room half is shipped here;
|
|
14
|
+
* the slot id stays byte-identical so an installed settings page keeps
|
|
15
|
+
* resolving to this plugin.
|
|
16
|
+
*
|
|
17
|
+
* The client half reads no subagent surface at all: it needs `sessions`
|
|
18
|
+
* (list + bindings), `slots`, `locale`, and the remote command executor.
|
|
19
|
+
*/
|
|
20
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
21
|
+
import { type TeamRoomsKey } from './room-locales.js';
|
|
22
|
+
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
23
|
+
interface LocaleNamespaceMap {
|
|
24
|
+
/** Team-room settings panel copy. */
|
|
25
|
+
teamRooms: TeamRoomsKey;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export type { TeamRoomsInjected, TeamRoomsSectionProps, } from './TeamRoomsSection.js';
|
|
29
|
+
/**
|
|
30
|
+
* Required services: sessions (list + bindings), slots, locale, the wire
|
|
31
|
+
* client, and the remote command executor (`remote.commands`, which carries
|
|
32
|
+
* every `/room` line this page issues). `connection` is deliberately absent —
|
|
33
|
+
* the room page never calls a remote namespace directly.
|
|
34
|
+
*/
|
|
35
|
+
export declare const inject: string[];
|
|
36
|
+
/**
|
|
37
|
+
* Register the Team Rooms settings page.
|
|
38
|
+
* @param ctx - client root context.
|
|
39
|
+
*/
|
|
40
|
+
export declare function apply(ctx: Context): void;
|
|
41
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/** `teamRooms` namespace dictionaries (the settings page panel copy). */
|
|
2
|
+
/** Dictionary namespace owned by the room panel. */
|
|
3
|
+
export declare const ROOM_NS = "teamRooms";
|
|
4
|
+
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
5
|
+
export declare const zh: {
|
|
6
|
+
readonly nav: "团队房间";
|
|
7
|
+
readonly title: "团队房间";
|
|
8
|
+
readonly intro: "跨会话的持久多 agent 协作:成员注册、消息总线、共享任务板与时间线。状态存在本地存储层(SQLite/JSONL),DSH 重启后自动恢复。";
|
|
9
|
+
readonly noSession: "暂无活动会话。打开一个会话后再管理团队房间。";
|
|
10
|
+
readonly empty: "本会话尚未加入任何团队房间。创建一个房间,或粘贴其它会话分享的房间 id 加入。";
|
|
11
|
+
readonly create: "创建房间";
|
|
12
|
+
readonly createPlaceholder: "房间名称…";
|
|
13
|
+
readonly createAction: "创建";
|
|
14
|
+
readonly join: "加入房间";
|
|
15
|
+
readonly joinPlaceholder: "房间 id…";
|
|
16
|
+
readonly joinAction: "加入";
|
|
17
|
+
readonly roomId: "房间 id";
|
|
18
|
+
readonly copy: "复制";
|
|
19
|
+
readonly copied: "已复制";
|
|
20
|
+
readonly members: "成员";
|
|
21
|
+
readonly owner: "房主";
|
|
22
|
+
readonly member: "成员";
|
|
23
|
+
readonly live: "在线";
|
|
24
|
+
readonly offline: "离线";
|
|
25
|
+
readonly tasks: "任务板";
|
|
26
|
+
readonly tasksEmpty: "暂无任务 —— 用 /room task add 或 room_create_task 添加。";
|
|
27
|
+
readonly taskAddPlaceholder: "新任务标题…";
|
|
28
|
+
readonly taskAdd: "添加";
|
|
29
|
+
readonly claim: "认领";
|
|
30
|
+
readonly assign: "交接";
|
|
31
|
+
readonly assignTo: "成员 id 或 me";
|
|
32
|
+
readonly assignAction: "确定";
|
|
33
|
+
readonly done: "完成";
|
|
34
|
+
readonly unassigned: "未分配";
|
|
35
|
+
readonly statusTodo: "待办";
|
|
36
|
+
readonly statusInProgress: "进行中";
|
|
37
|
+
readonly statusDone: "完成";
|
|
38
|
+
readonly timeline: "时间线";
|
|
39
|
+
readonly timelineEmpty: "暂无事件。";
|
|
40
|
+
readonly timelineMessage: "{sender} 广播:{text}";
|
|
41
|
+
readonly timelineDirected: "{sender} → {to}:{text}";
|
|
42
|
+
readonly timelineMemberJoined: "{member} 加入了房间";
|
|
43
|
+
readonly timelineMemberLeft: "{member} 离开了房间";
|
|
44
|
+
readonly timelineTaskCreated: "创建任务「{title}」";
|
|
45
|
+
readonly timelineTaskClaimed: "{member} 认领了任务";
|
|
46
|
+
readonly timelineTaskAssigned: "任务交接给 {member}";
|
|
47
|
+
readonly timelineTaskCompleted: "任务完成";
|
|
48
|
+
readonly timelineRoomCreated: "{member} 创建了房间";
|
|
49
|
+
readonly timelineUnknown: "事件 #{seq}";
|
|
50
|
+
readonly message: "发消息";
|
|
51
|
+
readonly messagePlaceholder: "广播给所有成员…";
|
|
52
|
+
readonly send: "发送";
|
|
53
|
+
readonly error: "操作失败";
|
|
54
|
+
readonly busy: "处理中…";
|
|
55
|
+
readonly leave: "离开房间";
|
|
56
|
+
};
|
|
57
|
+
/** English dictionary, key-identical to the Chinese source of truth. */
|
|
58
|
+
export declare const en: Record<TeamRoomsKey, string>;
|
|
59
|
+
/** The `teamRooms` namespace key union. */
|
|
60
|
+
export type TeamRoomsKey = keyof typeof zh;
|
|
61
|
+
//# sourceMappingURL=room-locales.d.ts.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure presentation of the team-room settings panel: derives the display
|
|
3
|
+
* state from the `teamRoom` projection value plus the session-list snapshot
|
|
4
|
+
* (the live overlay for member status). No I/O, clock, or randomness — the
|
|
5
|
+
* component stays a thin binder and the rows stay testable.
|
|
6
|
+
*
|
|
7
|
+
* @module dsh-team-rooms/room-presenter
|
|
8
|
+
*/
|
|
9
|
+
import { type TimelineEvent } from '../room/schema.js';
|
|
10
|
+
/** The session-list face the presenter reads (host-sampled `running` per session). */
|
|
11
|
+
export interface SessionListLike {
|
|
12
|
+
byId: Record<string, {
|
|
13
|
+
id: string;
|
|
14
|
+
running?: boolean;
|
|
15
|
+
displayTitle?: string;
|
|
16
|
+
}>;
|
|
17
|
+
}
|
|
18
|
+
/** One member row with the live overlay applied. */
|
|
19
|
+
export interface RoomMemberRow {
|
|
20
|
+
readonly sessionId: string;
|
|
21
|
+
readonly role: 'owner' | 'member';
|
|
22
|
+
readonly joinedAt: number;
|
|
23
|
+
/** Live bit from the session list (this browser's own view of the process). */
|
|
24
|
+
readonly live: boolean;
|
|
25
|
+
readonly title?: string;
|
|
26
|
+
}
|
|
27
|
+
/** One task-board row. */
|
|
28
|
+
export interface RoomTaskRow {
|
|
29
|
+
readonly taskId: string;
|
|
30
|
+
readonly title: string;
|
|
31
|
+
readonly description: string;
|
|
32
|
+
readonly status: 'todo' | 'in-progress' | 'done';
|
|
33
|
+
readonly assigneeSessionId: string | null;
|
|
34
|
+
readonly createdBy: string;
|
|
35
|
+
}
|
|
36
|
+
/** One rendered timeline entry (the raw event plus its localized label inputs). */
|
|
37
|
+
export interface RoomTimelineRow {
|
|
38
|
+
readonly seq: number;
|
|
39
|
+
readonly kind: TimelineEvent['kind'];
|
|
40
|
+
readonly data: TimelineEvent['data'];
|
|
41
|
+
}
|
|
42
|
+
/** One rendered room panel. */
|
|
43
|
+
export interface RoomPanel {
|
|
44
|
+
readonly roomId: string;
|
|
45
|
+
readonly name: string;
|
|
46
|
+
readonly createdAt: number;
|
|
47
|
+
readonly members: RoomMemberRow[];
|
|
48
|
+
readonly tasks: RoomTaskRow[];
|
|
49
|
+
readonly timeline: RoomTimelineRow[];
|
|
50
|
+
}
|
|
51
|
+
/** The controller state the section component selects from. */
|
|
52
|
+
export interface TeamRoomsState {
|
|
53
|
+
readonly status: 'ready' | 'unavailable';
|
|
54
|
+
/** The current session id, when a conversation is open. */
|
|
55
|
+
readonly sessionId?: string;
|
|
56
|
+
readonly rooms: RoomPanel[];
|
|
57
|
+
}
|
|
58
|
+
/** Empty ready state (no session open). */
|
|
59
|
+
export declare function emptyTeamRoomsState(): TeamRoomsState;
|
|
60
|
+
/**
|
|
61
|
+
* Derive the room panels from one projection value and the session list.
|
|
62
|
+
* @param value - the opaque `teamRoom` projection cell.
|
|
63
|
+
* @param list - the session-list snapshot (live overlay source).
|
|
64
|
+
* @returns the panels, or undefined when the cell is absent or invalid.
|
|
65
|
+
*/
|
|
66
|
+
export declare function buildRoomPanels(value: unknown, list: SessionListLike): RoomPanel[] | undefined;
|
|
67
|
+
//# sourceMappingURL=room-presenter.d.ts.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single append seam for the log-only `team-room/fact` event that carries
|
|
3
|
+
* the shared room timeline into every member session.
|
|
4
|
+
*
|
|
5
|
+
* Two host lines gate the appender:
|
|
6
|
+
* - rc lines through `0.1.1-rc.2`: `Session.append` predates the
|
|
7
|
+
* `ignorable` envelope marker, so a fact event lands unmarked and
|
|
8
|
+
* stricter harness builds refuse to resume the session. The appender
|
|
9
|
+
* detects such hosts before the first append and disables fact appends
|
|
10
|
+
* with a one-time warning.
|
|
11
|
+
* - `0.1.2-alpha.1` and later: the host fails closed on the session event
|
|
12
|
+
* vocabulary - `team-room/fact` is not in KNOWN_SESSION_EVENT_TYPES, so
|
|
13
|
+
* writing one makes the session unreadable there. On those hosts fact
|
|
14
|
+
* events are never appended; each fact is routed to the injected fallback
|
|
15
|
+
* sink (logger/panel channel) instead.
|
|
16
|
+
* `allowUnmarkedFacts: true` opts back into unmarked appends - deliberately
|
|
17
|
+
* dangerous - and already-polluted logs can be repaired with
|
|
18
|
+
* `scripts/repair-session-logs.mjs` from `dsh-permission-rules`.
|
|
19
|
+
*
|
|
20
|
+
* Copied from `dsh-background-agents` 0.9.6 with exactly two changes: the
|
|
21
|
+
* header docstring names only the room fact channel, and {@link FactEventType}
|
|
22
|
+
* is narrowed to `'team-room/fact'`. The host gating itself is unchanged, so a
|
|
23
|
+
* profile holding `team_rooms` facts written by 0.9.6 replays them here
|
|
24
|
+
* identically. (The source carried five mojibake sequences - U+9205 where an
|
|
25
|
+
* em dash was intended - which are written as plain hyphens here.)
|
|
26
|
+
* @module dsh-team-rooms/facts
|
|
27
|
+
*/
|
|
28
|
+
import type { Session } from '@deepseek-ai/dsh-session';
|
|
29
|
+
/** The log-only fact event type this appender owns. */
|
|
30
|
+
export type FactEventType = 'team-room/fact';
|
|
31
|
+
/**
|
|
32
|
+
* Host-gated fact appender. One instance per plugin mount, shared by the
|
|
33
|
+
* room hub and the room tools.
|
|
34
|
+
*/
|
|
35
|
+
export declare class FactAppender {
|
|
36
|
+
private readonly allowUnmarked;
|
|
37
|
+
private readonly warn;
|
|
38
|
+
private readonly fallback?;
|
|
39
|
+
private support;
|
|
40
|
+
private policy;
|
|
41
|
+
private warned;
|
|
42
|
+
private warnedForbidden;
|
|
43
|
+
constructor(allowUnmarked: boolean, warn: (message: string) => void, fallback?: ((type: FactEventType, data: unknown) => void) | undefined);
|
|
44
|
+
/**
|
|
45
|
+
* Append one log-only fact, requesting the envelope's `ignorable: true`
|
|
46
|
+
* marker. On hosts whose event vocabulary forbids the fact events
|
|
47
|
+
* (`0.1.2-alpha.1+`) the record is routed to the fallback sink instead
|
|
48
|
+
* of the session log; on pre-marker rc hosts (and after a failed probe)
|
|
49
|
+
* the append is skipped so the session log stays loadable everywhere. On 0.1.2-alpha.3 the envelope field is retained for stored-log read compatibility only - its Session.append still cannot stamp the marker, so the gate behavior is unchanged.
|
|
50
|
+
* Append failures are contained: a fact hiccup never disturbs the
|
|
51
|
+
* caller's operation.
|
|
52
|
+
* @param session - the session whose log carries the fact.
|
|
53
|
+
* @param type - the fact event type.
|
|
54
|
+
* @param data - the fact payload.
|
|
55
|
+
*/
|
|
56
|
+
append(session: Session, type: FactEventType, data: unknown): void;
|
|
57
|
+
/** Whether the host's event vocabulary still accepts the log-only fact events; the pre-check runs once. */
|
|
58
|
+
private factEventsAllowed;
|
|
59
|
+
/** Whether the host stamps the marker (or the dangerous opt-in is set); the pre-check runs once. */
|
|
60
|
+
private mayAppend;
|
|
61
|
+
/** After the first append on an unversioned host, probe the returned envelope for the marker. */
|
|
62
|
+
private probe;
|
|
63
|
+
/** One-time warning that fact appends were disabled to keep session logs loadable. */
|
|
64
|
+
private warnOnce;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=facts.d.ts.map
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-ecosystem inbound (P2): a minimal newline-delimited JSON-RPC 2.0
|
|
3
|
+
* bridge over stdio that lets external agent runtimes — OpenAI Agents SDK,
|
|
4
|
+
* CrewAI, and similar — publish into a team room.
|
|
5
|
+
*
|
|
6
|
+
* This is a JSON-RPC direct-connect minimal set, not the official ACP wire
|
|
7
|
+
* protocol: full ACP compatibility waits for the upstream seam. The bridge
|
|
8
|
+
* exposes one seam ({@link InboundCoordinator.registerInboundAdapter}) that
|
|
9
|
+
* returns a disposer, and one concrete adapter ({@link StdioJsonRpcInbound})
|
|
10
|
+
* that spawns a runtime command and listens on its stdout.
|
|
11
|
+
*
|
|
12
|
+
* Wire shape (one JSON notification per line on the child's stdout):
|
|
13
|
+
*
|
|
14
|
+
* - `jsonrpc` is always `"2.0"`; `method` is the event name
|
|
15
|
+
* (`agent_started` | `agent_message` | `agent_finished`).
|
|
16
|
+
* - `params` carries the payload: `name` (the external agent's display
|
|
17
|
+
* name), `room` (the team-room id), `traceId` (the correlation id),
|
|
18
|
+
* `status` (`ok` | `error`, only meaningful on `agent_finished`),
|
|
19
|
+
* `message` (the text), and optional `usage` token accounting.
|
|
20
|
+
*
|
|
21
|
+
* Mapping onto the team room's existing surfaces (see {@link deliveriesFor}):
|
|
22
|
+
* `agent_started` opens a task-board card, `agent_message` posts to the
|
|
23
|
+
* message bus, and `agent_finished` closes the card and posts the outcome.
|
|
24
|
+
* Every invalid message fails closed: it is dropped and a JSON-RPC error
|
|
25
|
+
* response is written back to the child's stdin. The owning fiber disposes
|
|
26
|
+
* the adapter (kills the child, removes listeners) through the returned
|
|
27
|
+
* disposer; an unspawnable command degrades to a logged warning and a
|
|
28
|
+
* dormant bridge.
|
|
29
|
+
*
|
|
30
|
+
* @module dsh-team-rooms/inbound
|
|
31
|
+
*/
|
|
32
|
+
import { type ChildProcess, type SpawnOptions } from 'node:child_process';
|
|
33
|
+
import { z } from 'zod';
|
|
34
|
+
/** A JSON-RPC 2.0 id: a string, a number, or null (notifications carry none). */
|
|
35
|
+
export type JsonRpcId = string | number | null;
|
|
36
|
+
/** The three recognized inbound event names (the JSON-RPC `method`). */
|
|
37
|
+
export declare const INBOUND_METHODS: readonly ["agent_started", "agent_message", "agent_finished"];
|
|
38
|
+
/** One recognized inbound event name. */
|
|
39
|
+
export type InboundMethod = (typeof INBOUND_METHODS)[number];
|
|
40
|
+
/** Optional token accounting the runtime may report on `agent_finished`. */
|
|
41
|
+
export declare const inboundUsageSchema: z.ZodObject<{
|
|
42
|
+
/** Un-cached input tokens of the finished run. */
|
|
43
|
+
inputTokens: z.ZodNumber;
|
|
44
|
+
/** Output tokens of the finished run. */
|
|
45
|
+
outputTokens: z.ZodNumber;
|
|
46
|
+
}, "strict", z.ZodTypeAny, {
|
|
47
|
+
inputTokens: number;
|
|
48
|
+
outputTokens: number;
|
|
49
|
+
}, {
|
|
50
|
+
inputTokens: number;
|
|
51
|
+
outputTokens: number;
|
|
52
|
+
}>;
|
|
53
|
+
/** Token accounting the runtime may report on `agent_finished`. */
|
|
54
|
+
export type InboundUsage = z.infer<typeof inboundUsageSchema>;
|
|
55
|
+
/**
|
|
56
|
+
* The `params` payload every inbound notification carries. `name` is the
|
|
57
|
+
* external agent's display name; `room` the team-room id; `traceId` the
|
|
58
|
+
* correlation id; `status` the ok/error outcome (only `agent_finished`);
|
|
59
|
+
* `message` the text (required for `agent_message`); `usage` optional token
|
|
60
|
+
* accounting. `.strict()` rejects unknown fields — fail-closed by default.
|
|
61
|
+
*/
|
|
62
|
+
export declare const inboundParamsSchema: z.ZodObject<{
|
|
63
|
+
name: z.ZodString;
|
|
64
|
+
room: z.ZodString;
|
|
65
|
+
traceId: z.ZodString;
|
|
66
|
+
status: z.ZodOptional<z.ZodEnum<["ok", "error"]>>;
|
|
67
|
+
message: z.ZodOptional<z.ZodString>;
|
|
68
|
+
usage: z.ZodOptional<z.ZodObject<{
|
|
69
|
+
/** Un-cached input tokens of the finished run. */
|
|
70
|
+
inputTokens: z.ZodNumber;
|
|
71
|
+
/** Output tokens of the finished run. */
|
|
72
|
+
outputTokens: z.ZodNumber;
|
|
73
|
+
}, "strict", z.ZodTypeAny, {
|
|
74
|
+
inputTokens: number;
|
|
75
|
+
outputTokens: number;
|
|
76
|
+
}, {
|
|
77
|
+
inputTokens: number;
|
|
78
|
+
outputTokens: number;
|
|
79
|
+
}>>;
|
|
80
|
+
}, "strict", z.ZodTypeAny, {
|
|
81
|
+
name: string;
|
|
82
|
+
room: string;
|
|
83
|
+
traceId: string;
|
|
84
|
+
message?: string | undefined;
|
|
85
|
+
status?: "ok" | "error" | undefined;
|
|
86
|
+
usage?: {
|
|
87
|
+
inputTokens: number;
|
|
88
|
+
outputTokens: number;
|
|
89
|
+
} | undefined;
|
|
90
|
+
}, {
|
|
91
|
+
name: string;
|
|
92
|
+
room: string;
|
|
93
|
+
traceId: string;
|
|
94
|
+
message?: string | undefined;
|
|
95
|
+
status?: "ok" | "error" | undefined;
|
|
96
|
+
usage?: {
|
|
97
|
+
inputTokens: number;
|
|
98
|
+
outputTokens: number;
|
|
99
|
+
} | undefined;
|
|
100
|
+
}>;
|
|
101
|
+
/** One decoded JSON-RPC request line (params left unknown until mapped). */
|
|
102
|
+
export interface InboundRequest {
|
|
103
|
+
/** The JSON-RPC `method`, expected to be one of {@link INBOUND_METHODS}. */
|
|
104
|
+
readonly method: string;
|
|
105
|
+
/** The raw `params` value; validated by {@link mapInboundEvent}. */
|
|
106
|
+
readonly params: unknown;
|
|
107
|
+
/** The request id, or null when the line was a notification (no id). */
|
|
108
|
+
readonly id: JsonRpcId;
|
|
109
|
+
}
|
|
110
|
+
/** A parsed stdin line: either a valid request or a protocol error to report back. */
|
|
111
|
+
export type InboundParse = {
|
|
112
|
+
readonly ok: true;
|
|
113
|
+
readonly request: InboundRequest;
|
|
114
|
+
} | {
|
|
115
|
+
readonly ok: false;
|
|
116
|
+
readonly id: JsonRpcId;
|
|
117
|
+
readonly code: number;
|
|
118
|
+
readonly message: string;
|
|
119
|
+
};
|
|
120
|
+
/** A mapped event: either a normalized event or an invalid-params error. */
|
|
121
|
+
export type InboundDecode = {
|
|
122
|
+
readonly ok: true;
|
|
123
|
+
readonly event: InboundEvent;
|
|
124
|
+
} | {
|
|
125
|
+
readonly ok: false;
|
|
126
|
+
readonly code: number;
|
|
127
|
+
readonly message: string;
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* One normalized inbound event, discriminated on the JSON-RPC `method`.
|
|
131
|
+
* `name` is the external agent's display name; `roomId`/`traceId` come from
|
|
132
|
+
* `params.room`/`params.traceId`.
|
|
133
|
+
*/
|
|
134
|
+
export type InboundEvent = {
|
|
135
|
+
readonly method: 'agent_started';
|
|
136
|
+
readonly name: string;
|
|
137
|
+
readonly roomId: string;
|
|
138
|
+
readonly traceId: string;
|
|
139
|
+
readonly message?: string;
|
|
140
|
+
} | {
|
|
141
|
+
readonly method: 'agent_message';
|
|
142
|
+
readonly name: string;
|
|
143
|
+
readonly roomId: string;
|
|
144
|
+
readonly traceId: string;
|
|
145
|
+
readonly message: string;
|
|
146
|
+
} | {
|
|
147
|
+
readonly method: 'agent_finished';
|
|
148
|
+
readonly name: string;
|
|
149
|
+
readonly roomId: string;
|
|
150
|
+
readonly traceId: string;
|
|
151
|
+
readonly status: 'ok' | 'error';
|
|
152
|
+
readonly message?: string;
|
|
153
|
+
readonly usage?: InboundUsage;
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* One room write the bridge performs for a normalized event: a task-board
|
|
157
|
+
* card open/close or a message-bus post. `traceId` correlates the card with
|
|
158
|
+
* the external run so `agent_finished` can close the card `agent_started`
|
|
159
|
+
* opened.
|
|
160
|
+
*/
|
|
161
|
+
export type InboundDelivery = {
|
|
162
|
+
readonly kind: 'task-open';
|
|
163
|
+
readonly roomId: string;
|
|
164
|
+
readonly traceId: string;
|
|
165
|
+
readonly title: string;
|
|
166
|
+
} | {
|
|
167
|
+
readonly kind: 'bus-post';
|
|
168
|
+
readonly roomId: string;
|
|
169
|
+
readonly traceId: string;
|
|
170
|
+
readonly text: string;
|
|
171
|
+
} | {
|
|
172
|
+
readonly kind: 'task-close';
|
|
173
|
+
readonly roomId: string;
|
|
174
|
+
readonly traceId: string;
|
|
175
|
+
readonly status: 'ok' | 'error';
|
|
176
|
+
};
|
|
177
|
+
/** The host-side delivery target: receives normalized events, returns nothing (or a promise). */
|
|
178
|
+
export type InboundSink = (event: InboundEvent) => Promise<void> | void;
|
|
179
|
+
/** A minimal logger face; `console` and the Cordis logger both satisfy it. */
|
|
180
|
+
export interface InboundLogger {
|
|
181
|
+
info(message: string): void;
|
|
182
|
+
warn(message: string): void;
|
|
183
|
+
error(message: string): void;
|
|
184
|
+
debug?(message: string): void;
|
|
185
|
+
}
|
|
186
|
+
/** An adapter that listens for external events and forwards them to a sink. */
|
|
187
|
+
export interface InboundAdapter {
|
|
188
|
+
/**
|
|
189
|
+
* Begin listening; every mapped event goes to `sink`. Returns a disposer
|
|
190
|
+
* that stops the adapter and releases every side effect it owns.
|
|
191
|
+
* @param sink - the delivery target for normalized events.
|
|
192
|
+
* @returns the stop disposer.
|
|
193
|
+
*/
|
|
194
|
+
start(sink: InboundSink): () => void;
|
|
195
|
+
}
|
|
196
|
+
/** Spawn signature the stdio adapter uses; injectable for protocol tests. */
|
|
197
|
+
export type SpawnFn = (command: string, options: SpawnOptions) => ChildProcess;
|
|
198
|
+
/**
|
|
199
|
+
* Parse one newline-delimited stdin line as a JSON-RPC 2.0 request or
|
|
200
|
+
* notification. Malformed JSON and non-conforming envelopes fail closed with
|
|
201
|
+
* a stable JSON-RPC error code; the caller reports the error back.
|
|
202
|
+
* @param line - one raw line (whitespace tolerated).
|
|
203
|
+
* @returns the parsed request, or a protocol error with its response id.
|
|
204
|
+
*/
|
|
205
|
+
export declare function parseInboundLine(line: string): InboundParse;
|
|
206
|
+
/**
|
|
207
|
+
* Validate a parsed request's method and params and map them to a normalized
|
|
208
|
+
* {@link InboundEvent}. Unknown methods and invalid params fail closed with a
|
|
209
|
+
* stable JSON-RPC error code.
|
|
210
|
+
* @param request - the parsed request from {@link parseInboundLine}.
|
|
211
|
+
* @returns the normalized event, or an error to report back.
|
|
212
|
+
*/
|
|
213
|
+
export declare function mapInboundEvent(request: InboundRequest): InboundDecode;
|
|
214
|
+
/**
|
|
215
|
+
* Map one normalized event to the team room's existing surfaces. The result
|
|
216
|
+
* is a list of room writes the host executes in order against the `RoomHub`.
|
|
217
|
+
* @param event - the normalized inbound event.
|
|
218
|
+
* @returns the ordered room deliveries for the event.
|
|
219
|
+
*/
|
|
220
|
+
export declare function deliveriesFor(event: InboundEvent): InboundDelivery[];
|
|
221
|
+
/**
|
|
222
|
+
* Serialize one JSON-RPC 2.0 error response. `id` is echoed when the request
|
|
223
|
+
* carried one; null otherwise (best-effort observability for a notification,
|
|
224
|
+
* which the JSON-RPC spec would normally not answer).
|
|
225
|
+
* @param id - the request id to echo (or null).
|
|
226
|
+
* @param code - the JSON-RPC error code.
|
|
227
|
+
* @param message - the human-readable error text.
|
|
228
|
+
* @returns one serialized error response line (no trailing newline).
|
|
229
|
+
*/
|
|
230
|
+
export declare function jsonRpcErrorResponse(id: JsonRpcId, code: number, message: string): string;
|
|
231
|
+
/**
|
|
232
|
+
* The stdio JSON-RPC inbound adapter: spawns a runtime command and listens
|
|
233
|
+
* for newline-delimited JSON-RPC notifications on its stdout. Invalid lines
|
|
234
|
+
* are dropped (fail-closed) and answered with a JSON-RPC error on the child's
|
|
235
|
+
* stdin. Start/stop are owned entirely by the returned disposer.
|
|
236
|
+
*/
|
|
237
|
+
export declare class StdioJsonRpcInbound implements InboundAdapter {
|
|
238
|
+
private readonly command;
|
|
239
|
+
private readonly logger;
|
|
240
|
+
private readonly spawnFn;
|
|
241
|
+
private child;
|
|
242
|
+
private attempted;
|
|
243
|
+
private disposed;
|
|
244
|
+
/**
|
|
245
|
+
* @param command - the runtime launch command (spawned with a shell).
|
|
246
|
+
* @param logger - where lifecycle and rejection lines are logged.
|
|
247
|
+
* @param spawnFn - injectable spawn for tests; defaults to `node:child_process` spawn.
|
|
248
|
+
*/
|
|
249
|
+
constructor(command: string, logger?: InboundLogger, spawnFn?: SpawnFn);
|
|
250
|
+
/**
|
|
251
|
+
* Spawn the runtime and begin listening. A spawn failure degrades to a
|
|
252
|
+
* logged warning and a no-op disposer (the bridge stays dormant).
|
|
253
|
+
* @param sink - the delivery target for mapped events.
|
|
254
|
+
* @returns the stop disposer (kills the child; further output is ignored).
|
|
255
|
+
*/
|
|
256
|
+
start(sink: InboundSink): () => void;
|
|
257
|
+
/** Stop the adapter: kill the child if still running; further output is ignored. Idempotent. */
|
|
258
|
+
stop(): void;
|
|
259
|
+
/** Decode one line, emit a mapped event, or report the failure back (fail-closed). */
|
|
260
|
+
private handleLine;
|
|
261
|
+
/** Best-effort write of one JSON-RPC error response to the child's stdin. */
|
|
262
|
+
private writeResponse;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* The inbound adapter provider seam: registers adapters and owns their
|
|
266
|
+
* disposers. Every registration returns a disposer that stops and unregisters
|
|
267
|
+
* exactly that adapter; {@link stopAll} stops every registered adapter.
|
|
268
|
+
*/
|
|
269
|
+
export declare class InboundCoordinator {
|
|
270
|
+
private readonly stops;
|
|
271
|
+
/**
|
|
272
|
+
* Register one adapter for a sink and start it. Returns a disposer that
|
|
273
|
+
* stops the adapter and drops it from the registry (idempotent).
|
|
274
|
+
* @param adapter - the adapter to start.
|
|
275
|
+
* @param sink - the delivery target the adapter emits into.
|
|
276
|
+
* @returns the stop-and-unregister disposer.
|
|
277
|
+
*/
|
|
278
|
+
registerInboundAdapter(adapter: InboundAdapter, sink: InboundSink): () => void;
|
|
279
|
+
/** Stop every registered adapter (idempotent). */
|
|
280
|
+
stopAll(): void;
|
|
281
|
+
}
|
|
282
|
+
//# sourceMappingURL=inbound.d.ts.map
|