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 +41 -0
- package/cordis.patch.yml +3 -0
- package/lib/index.js +179 -0
- package/lib/types/index.d.ts +37 -0
- package/lib/types/index.js +114 -0
- package/package.json +40 -0
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.
|
package/cordis.patch.yml
ADDED
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
|
+
}
|