dsh-bridge 0.1.0-rc.5

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/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # dsh-bridge
2
+
3
+ > The local event and session-messaging bridge for DeepSeek Harness.
4
+
5
+ `dsh-bridge` provides same-process messaging between live DeepSeek Harness sessions. It is the local contract beneath `dsh-weave`: bridge normalizes local session events; weave carries approved work across machines.
6
+
7
+ The plugin registers `session_list`, `session_send`, and `session_messages`.
8
+ Delivery uses the public `ctx.agents` registry and `Agent.followup()`, so an
9
+ idle target is woken and a busy target receives ordinary queued work. A bounded
10
+ in-memory recent log (the latest 1,000 delivered messages) is kept only for
11
+ `session_messages` replay and diagnostics; it is not a second delivery queue.
12
+ Messages carry sender, target, UUID, and timestamp metadata.
13
+
14
+ This package intentionally does not implement cross-host transport. `dsh-weave`
15
+ will provide the authenticated network backend while preserving this tool contract.
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ dsh plugin --profile web add dsh-bridge@next
21
+ ```
22
+
23
+ ## Known Limitations and Deferred Work
24
+
25
+ - **Process boundary** — sessions in another process or host are not visible;
26
+ add an authenticated relay/backend before advertising cross-host delivery.
27
+ - **In-memory retention** — messages are lost when the plugin process exits and
28
+ older than the latest 1,000 are evicted; durable inbox/outbox persistence is
29
+ still deferred until a cross-process relay needs it.
30
+ - **Delivery acknowledgement** — the current result means the target was live
31
+ and accepted the follow-up call, not that the target model processed it.
32
+
33
+ ## Model Experience
34
+
35
+ None, as `session_list`, `session_send`, and `session_messages` expose their
36
+ schemas directly through the tool registry.
37
+
38
+ ### KV Cache effect
39
+
40
+ Independent tool schemas; sending a message changes only the target session's
41
+ queued input and does not alter the sender's cached prompt prefix.
@@ -0,0 +1,3 @@
1
+ - insert:
2
+ - id: dsh-bridge
3
+ name: dsh-bridge
package/lib/index.js ADDED
@@ -0,0 +1,179 @@
1
+ import { createUserMessage } from "@deepseek-ai/dsh-llm";
2
+ import { SessionId } from "@deepseek-ai/dsh-session";
3
+ import { defineTool } from "@deepseek-ai/dsh-tools";
4
+ //#region lib/types/index.js
5
+ const name = "session-messaging";
6
+ const inject = ["agents", "tools"];
7
+ const MAX_RECENT_MESSAGES = 1000;
8
+ var LocalSessionMessagingImpl = class {
9
+ ctx;
10
+ messages = [];
11
+ constructor(ctx) {
12
+ this.ctx = ctx;
13
+ }
14
+ list() {
15
+ return [...this.ctx.agents.list()].map((agent) => agent.session.id);
16
+ }
17
+ send(from, to, text) {
18
+ const target = this.ctx.agents.get(to);
19
+ if (!target) throw new Error(`session "${to}" is not live in this process`);
20
+ const id = crypto.randomUUID();
21
+ const message = {
22
+ id,
23
+ from: from.session.id,
24
+ to,
25
+ text,
26
+ createdAt: Date.now(),
27
+ delivered: true
28
+ };
29
+ target.followup(createUserMessage({
30
+ content: [{
31
+ type: "text",
32
+ text: `[session-message ${id} from ${from.session.id}]\n${text}`
33
+ }],
34
+ source: {
35
+ kind: "plugin",
36
+ plugin: name,
37
+ form: "relay"
38
+ }
39
+ }));
40
+ this.messages.push(message);
41
+ if (this.messages.length > MAX_RECENT_MESSAGES) {
42
+ this.messages.splice(0, this.messages.length - MAX_RECENT_MESSAGES);
43
+ }
44
+ return {
45
+ messageId: id,
46
+ from: String(from.session.id),
47
+ to: String(to),
48
+ delivered: true
49
+ };
50
+ }
51
+ receive(sessionId, limit) {
52
+ return this.messages.filter((message) => message.to === sessionId).slice(-limit);
53
+ }
54
+ };
55
+ function apply(ctx) {
56
+ const messaging = new LocalSessionMessagingImpl(ctx);
57
+ ctx.accessor("sessionMessaging", { get: () => messaging });
58
+ ctx.tools.register(defineTool({
59
+ name: "session_list",
60
+ description: "List live DeepSeek Harness sessions in this process.",
61
+ parameters: {},
62
+ output: {
63
+ schema: {
64
+ type: "array",
65
+ items: { type: "string" }
66
+ },
67
+ render: (_args, value) => [{
68
+ type: "text",
69
+ text: value.join("\n") || "No live sessions."
70
+ }]
71
+ },
72
+ async execute() {
73
+ return messaging.list().map(String);
74
+ }
75
+ }));
76
+ ctx.tools.register(defineTool({
77
+ name: "session_send",
78
+ description: "Send a message to another live DeepSeek Harness session in this process.",
79
+ parameters: {
80
+ to: {
81
+ type: "string",
82
+ required: true,
83
+ description: "Target session id from session_list."
84
+ },
85
+ text: {
86
+ type: "string",
87
+ required: true,
88
+ description: "Message text."
89
+ }
90
+ },
91
+ output: {
92
+ schema: {
93
+ type: "object",
94
+ additionalProperties: false,
95
+ properties: {
96
+ messageId: {
97
+ type: "string",
98
+ required: true
99
+ },
100
+ from: {
101
+ type: "string",
102
+ required: true
103
+ },
104
+ to: {
105
+ type: "string",
106
+ required: true
107
+ },
108
+ delivered: {
109
+ type: "boolean",
110
+ required: true
111
+ }
112
+ }
113
+ },
114
+ render: (_args, value) => [{
115
+ type: "text",
116
+ text: `Delivered ${value.messageId} to ${value.to}.`
117
+ }]
118
+ },
119
+ async execute(args, exec) {
120
+ if (!exec.agent) throw new Error("session_send requires an owning agent");
121
+ if (args.text.trim() === "") throw new Error("text must not be empty");
122
+ return messaging.send(exec.agent, SessionId(args.to), args.text);
123
+ }
124
+ }));
125
+ ctx.tools.register(defineTool({
126
+ name: "session_messages",
127
+ description: "Read messages delivered to the current session in this process.",
128
+ parameters: { limit: {
129
+ type: "number",
130
+ description: "Maximum number of messages, default 20."
131
+ } },
132
+ output: {
133
+ schema: {
134
+ type: "array",
135
+ items: {
136
+ type: "object",
137
+ additionalProperties: false,
138
+ properties: {
139
+ id: {
140
+ type: "string",
141
+ required: true
142
+ },
143
+ from: {
144
+ type: "string",
145
+ required: true
146
+ },
147
+ to: {
148
+ type: "string",
149
+ required: true
150
+ },
151
+ text: {
152
+ type: "string",
153
+ required: true
154
+ },
155
+ createdAt: {
156
+ type: "number",
157
+ required: true
158
+ },
159
+ delivered: {
160
+ type: "boolean",
161
+ required: true
162
+ }
163
+ }
164
+ }
165
+ },
166
+ render: (_args, value) => [{
167
+ type: "text",
168
+ text: value.map((message) => `[${message.from}] ${message.text}`).join("\n") || "No messages."
169
+ }]
170
+ },
171
+ async execute(args, exec) {
172
+ if (!exec.agent) throw new Error("session_messages requires an owning agent");
173
+ const limit = args.limit === void 0 ? 20 : Math.max(1, Math.min(100, Math.floor(args.limit)));
174
+ return [...messaging.receive(exec.agent.session.id, limit)];
175
+ }
176
+ }));
177
+ }
178
+ //#endregion
179
+ export { apply, inject, name };
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Same-process session-to-session messaging.
3
+ *
4
+ * The transport is deliberately process-local. A future relay can implement
5
+ * the same interface without changing the model-facing tools.
6
+ */
7
+ import type { Context } from '@deepseek-ai/cordis';
8
+ import type { Agent } from '@deepseek-ai/dsh-agent';
9
+ import { type SessionId as SessionIdValue } from '@deepseek-ai/dsh-session';
10
+ export declare const name = "session-messaging";
11
+ export declare const inject: string[];
12
+ export interface LocalMessage {
13
+ readonly id: string;
14
+ readonly from: SessionIdValue;
15
+ readonly to: SessionIdValue;
16
+ readonly text: string;
17
+ readonly createdAt: number;
18
+ readonly delivered: boolean;
19
+ }
20
+ export interface SendMessageResult {
21
+ readonly messageId: string;
22
+ readonly from: string;
23
+ readonly to: string;
24
+ readonly delivered: boolean;
25
+ }
26
+ export interface LocalSessionMessaging {
27
+ list(): readonly SessionIdValue[];
28
+ send(from: Agent, to: SessionIdValue, text: string): SendMessageResult;
29
+ receive(sessionId: SessionIdValue, limit: number): readonly LocalMessage[];
30
+ }
31
+ declare module '@deepseek-ai/cordis' {
32
+ interface Context {
33
+ sessionMessaging: LocalSessionMessaging;
34
+ }
35
+ }
36
+ export declare function apply(ctx: Context): void;
37
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,114 @@
1
+ import { createUserMessage } from '@deepseek-ai/dsh-llm';
2
+ import { SessionId } from '@deepseek-ai/dsh-session';
3
+ import { defineTool } from '@deepseek-ai/dsh-tools';
4
+ export const name = 'session-messaging';
5
+ export const inject = ['agents', 'tools'];
6
+ class LocalSessionMessagingImpl {
7
+ ctx;
8
+ messages = [];
9
+ constructor(ctx) {
10
+ this.ctx = ctx;
11
+ }
12
+ list() {
13
+ return [...this.ctx.agents.list()].map(agent => agent.session.id);
14
+ }
15
+ send(from, to, text) {
16
+ const target = this.ctx.agents.get(to);
17
+ if (!target)
18
+ throw new Error(`session "${to}" is not live in this process`);
19
+ const id = crypto.randomUUID();
20
+ const message = {
21
+ id,
22
+ from: from.session.id,
23
+ to,
24
+ text,
25
+ createdAt: Date.now(),
26
+ delivered: true,
27
+ };
28
+ this.messages.push(message);
29
+ target.followup(createUserMessage({
30
+ content: [{ type: 'text', text: `[session-message ${id} from ${from.session.id}]\n${text}` }],
31
+ source: { kind: 'plugin', plugin: name, form: 'relay' },
32
+ }));
33
+ return { messageId: id, from: String(from.session.id), to: String(to), delivered: true };
34
+ }
35
+ receive(sessionId, limit) {
36
+ return this.messages.filter(message => message.to === sessionId).slice(-limit);
37
+ }
38
+ }
39
+ export function apply(ctx) {
40
+ const messaging = new LocalSessionMessagingImpl(ctx);
41
+ ctx.accessor('sessionMessaging', { get: () => messaging });
42
+ ctx.tools.register(defineTool({
43
+ name: 'session_list',
44
+ description: 'List live DeepSeek Harness sessions in this process.',
45
+ parameters: {},
46
+ output: {
47
+ schema: { type: 'array', items: { type: 'string' } },
48
+ render: (_args, value) => [{ type: 'text', text: value.join('\n') || 'No live sessions.' }],
49
+ },
50
+ async execute() {
51
+ return messaging.list().map(String);
52
+ },
53
+ }));
54
+ ctx.tools.register(defineTool({
55
+ name: 'session_send',
56
+ description: 'Send a message to another live DeepSeek Harness session in this process.',
57
+ parameters: {
58
+ to: { type: 'string', required: true, description: 'Target session id from session_list.' },
59
+ text: { type: 'string', required: true, description: 'Message text.' },
60
+ },
61
+ output: {
62
+ schema: {
63
+ type: 'object',
64
+ additionalProperties: false,
65
+ properties: {
66
+ messageId: { type: 'string', required: true },
67
+ from: { type: 'string', required: true },
68
+ to: { type: 'string', required: true },
69
+ delivered: { type: 'boolean', required: true },
70
+ },
71
+ },
72
+ render: (_args, value) => [{ type: 'text', text: `Delivered ${value.messageId} to ${value.to}.` }],
73
+ },
74
+ async execute(args, exec) {
75
+ if (!exec.agent)
76
+ throw new Error('session_send requires an owning agent');
77
+ if (args.text.trim() === '')
78
+ throw new Error('text must not be empty');
79
+ return messaging.send(exec.agent, SessionId(args.to), args.text);
80
+ },
81
+ }));
82
+ ctx.tools.register(defineTool({
83
+ name: 'session_messages',
84
+ description: 'Read messages delivered to the current session in this process.',
85
+ parameters: {
86
+ limit: { type: 'number', description: 'Maximum number of messages, default 20.' },
87
+ },
88
+ output: {
89
+ schema: {
90
+ type: 'array',
91
+ items: {
92
+ type: 'object',
93
+ additionalProperties: false,
94
+ properties: {
95
+ id: { type: 'string', required: true },
96
+ from: { type: 'string', required: true },
97
+ to: { type: 'string', required: true },
98
+ text: { type: 'string', required: true },
99
+ createdAt: { type: 'number', required: true },
100
+ delivered: { type: 'boolean', required: true },
101
+ },
102
+ },
103
+ },
104
+ render: (_args, value) => [{ type: 'text', text: value.map(message => `[${message.from}] ${message.text}`).join('\n') || 'No messages.' }],
105
+ },
106
+ async execute(args, exec) {
107
+ if (!exec.agent)
108
+ throw new Error('session_messages requires an owning agent');
109
+ const limit = args.limit === undefined ? 20 : Math.max(1, Math.min(100, Math.floor(args.limit)));
110
+ return [...messaging.receive(exec.agent.session.id, limit)];
111
+ },
112
+ }));
113
+ }
114
+ //# sourceMappingURL=index.js.map
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "dsh-bridge",
3
+ "description": "Local session messaging and event bridge for DeepSeek Harness",
4
+ "version": "0.1.0-rc.5",
5
+ "publishConfig": { "access": "public" },
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/baixianger/dsh-bridge.git"
9
+ },
10
+ "type": "module",
11
+ "main": "lib/index.js",
12
+ "types": "lib/types/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "types": "./lib/types/index.d.ts",
16
+ "default": "./lib/index.js"
17
+ }
18
+ },
19
+ "dsh": {
20
+ "bundle": {
21
+ "patch": "./cordis.patch.yml"
22
+ }
23
+ },
24
+ "files": ["lib/index.js", "lib/types/**/*.js", "lib/types/**/*.d.ts", "cordis.patch.yml", "README.md"],
25
+ "license": "MIT",
26
+ "peerDependencies": {
27
+ "@deepseek-ai/cordis": ">=0.1.0-rc.5",
28
+ "@deepseek-ai/dsh-agent": ">=0.1.0-rc.5",
29
+ "@deepseek-ai/dsh-llm": ">=0.1.0-rc.5",
30
+ "@deepseek-ai/dsh-session": ">=0.1.0-rc.5",
31
+ "@deepseek-ai/dsh-tools": ">=0.1.0-rc.5"
32
+ },
33
+ "devDependencies": {
34
+ "@deepseek-ai/cordis": ">=0.1.0-rc.5",
35
+ "@deepseek-ai/dsh-agent": ">=0.1.0-rc.5",
36
+ "@deepseek-ai/dsh-llm": ">=0.1.0-rc.5",
37
+ "@deepseek-ai/dsh-session": ">=0.1.0-rc.5",
38
+ "@deepseek-ai/dsh-tools": ">=0.1.0-rc.5"
39
+ }
40
+ }