dsh-bridge 0.1.0-rc.10
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 +68 -0
- package/cordis.patch.yml +3 -0
- package/lib/index.js +240 -0
- package/lib/types/index.d.ts +60 -0
- package/lib/types/index.js +114 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# dsh-bridge
|
|
2
|
+
|
|
3
|
+
> Local, cross-session messaging for DeepSeek Harness.
|
|
4
|
+
|
|
5
|
+
**DSH Bridge** is the local messaging foundation of the DSH family. It lets live
|
|
6
|
+
sessions in one DSH host discover one another and exchange messages. It has no
|
|
7
|
+
Web UI and no network transport.
|
|
8
|
+
|
|
9
|
+
`dsh-weave` can extend Bridge across machines; `dsh-chat` can present its
|
|
10
|
+
messages as a human-facing group chat. Neither is required for local use.
|
|
11
|
+
|
|
12
|
+
The plugin exposes the `ctx.dshBridge` service and registers `session_list`,
|
|
13
|
+
`session_send`, and `session_messages` for agents. The old `ctx.sessionMessaging`
|
|
14
|
+
accessor remains as a temporary compatibility alias.
|
|
15
|
+
|
|
16
|
+
`ctx.dshBridge.deliverExternal()` is the controlled inbound seam for a trusted
|
|
17
|
+
transport such as Weave: it emits the same session follow-up and audit record
|
|
18
|
+
as local delivery, rather than letting a transport manipulate agents directly.
|
|
19
|
+
Delivery uses the public `ctx.agents` registry and `Agent.followup()`. An idle
|
|
20
|
+
target is woken, a running target receives ordinary queued work, and a persisted
|
|
21
|
+
offline target is resumed through DSH's configured Host agent resolver before
|
|
22
|
+
delivery. Concurrent messages to the same cold session share one resume operation.
|
|
23
|
+
The resolver reconstructs the recorded agent preset and model selection exactly
|
|
24
|
+
as the Web host does. A bounded
|
|
25
|
+
in-memory recent log (the latest 1,000 delivered messages) is kept only for
|
|
26
|
+
`session_messages` replay and diagnostics; it is not a second delivery queue.
|
|
27
|
+
Messages carry sender, target, UUID, and timestamp metadata.
|
|
28
|
+
|
|
29
|
+
`ctx.dshBridge.status(sessionId)` reports a session's presentation state:
|
|
30
|
+
`waking` while a cold resume is in flight, `archived` when the id is in the
|
|
31
|
+
workspace registry's archive set, `idle`/`running` for live agents, `offline`
|
|
32
|
+
for persisted sessions, and `missing` otherwise. Archive takes precedence over
|
|
33
|
+
live presence: archiving a session hides it from live surfaces (such as
|
|
34
|
+
dsh-chat room member indicators) without stopping its agent. Delivery to an
|
|
35
|
+
archived session is rejected: `session_send` and `deliverExternal` refuse to
|
|
36
|
+
wake or reach an archived target, so an archived agent stops receiving messages
|
|
37
|
+
entirely rather than merely dropping off live surfaces.
|
|
38
|
+
|
|
39
|
+
This package intentionally does not implement cross-host transport. `dsh-weave`
|
|
40
|
+
will provide the authenticated network backend while preserving the local
|
|
41
|
+
message semantics.
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
dsh plugin --profile web add dsh-bridge@next
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Known Limitations and Deferred Work
|
|
50
|
+
|
|
51
|
+
- **Process boundary** — sessions in another process or host are not visible;
|
|
52
|
+
add an authenticated relay/backend before advertising cross-host delivery.
|
|
53
|
+
- **In-memory retention** — messages are lost when the plugin process exits and
|
|
54
|
+
older than the latest 1,000 are evicted; durable inbox/outbox persistence is
|
|
55
|
+
still deferred until a cross-process relay needs it.
|
|
56
|
+
- **Delivery acknowledgement** — the current result means the target was live
|
|
57
|
+
(or successfully resumed) and accepted the follow-up call, not that the target
|
|
58
|
+
model processed it.
|
|
59
|
+
|
|
60
|
+
## Model Experience
|
|
61
|
+
|
|
62
|
+
None, as `session_list`, `session_send`, and `session_messages` expose their
|
|
63
|
+
schemas directly through the tool registry.
|
|
64
|
+
|
|
65
|
+
### KV Cache effect
|
|
66
|
+
|
|
67
|
+
Independent tool schemas; sending a message changes only the target session's
|
|
68
|
+
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,240 @@
|
|
|
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 = "dsh-bridge";
|
|
6
|
+
const inject = ["agents", "tools", "sessionPersistence", "workspaceRegistry", "typert"];
|
|
7
|
+
const MAX_RECENT_MESSAGES = 1000;
|
|
8
|
+
class LocalSessionMessagingImpl {
|
|
9
|
+
ctx;
|
|
10
|
+
messages = [];
|
|
11
|
+
listeners = /* @__PURE__ */ new Set();
|
|
12
|
+
resuming = /* @__PURE__ */ new Map();
|
|
13
|
+
constructor(ctx) {
|
|
14
|
+
this.ctx = ctx;
|
|
15
|
+
}
|
|
16
|
+
list() {
|
|
17
|
+
return [...this.ctx.agents.list()].map((agent) => agent.session.id);
|
|
18
|
+
}
|
|
19
|
+
async status(sessionId) {
|
|
20
|
+
const id = String(sessionId);
|
|
21
|
+
if (this.resuming.has(id)) return { sessionId: id, state: "waking", live: false };
|
|
22
|
+
// Archive is a durable hide flag: it masks live presence so an archived
|
|
23
|
+
// session never advertises as running/idle, even while its agent keeps
|
|
24
|
+
// working in the background.
|
|
25
|
+
if (new Set(this.ctx.workspaceRegistry?.archivedSessionIds ?? []).has(id)) return { sessionId: id, state: "archived", live: false };
|
|
26
|
+
const live = this.ctx.agents.get(sessionId);
|
|
27
|
+
if (live) return { sessionId: id, state: live.status, live: true };
|
|
28
|
+
const headers = await this.ctx.sessionPersistence.list();
|
|
29
|
+
return { sessionId: id, state: headers.some((header) => String(header.id) === id) ? "offline" : "missing", live: false };
|
|
30
|
+
}
|
|
31
|
+
async target(to) {
|
|
32
|
+
const id = String(to);
|
|
33
|
+
// Archive is a durable hide flag: an archived session never receives
|
|
34
|
+
// delivery, whether its agent is still live or only persisted on disk.
|
|
35
|
+
if (new Set(this.ctx.workspaceRegistry?.archivedSessionIds ?? []).has(id)) {
|
|
36
|
+
throw new Error(`session "${id}" is archived and cannot receive messages`);
|
|
37
|
+
}
|
|
38
|
+
const live = this.ctx.agents.get(to);
|
|
39
|
+
if (live) return live;
|
|
40
|
+
let pending = this.resuming.get(id);
|
|
41
|
+
if (!pending) {
|
|
42
|
+
pending = (async () => {
|
|
43
|
+
const provider = this.ctx.typert.lookups.get("agent");
|
|
44
|
+
if (!provider) throw new Error("DSH host agent resolver is unavailable");
|
|
45
|
+
const resolved = await provider.resolve(to);
|
|
46
|
+
if (!resolved) throw new Error(`session "${id}" could not be resumed`);
|
|
47
|
+
return resolved;
|
|
48
|
+
})().then((agent) => {
|
|
49
|
+
return agent;
|
|
50
|
+
}).catch((error) => {
|
|
51
|
+
const raced = this.ctx.agents.get(to);
|
|
52
|
+
if (raced) return raced;
|
|
53
|
+
throw error;
|
|
54
|
+
}).finally(() => this.resuming.delete(id));
|
|
55
|
+
this.resuming.set(id, pending);
|
|
56
|
+
}
|
|
57
|
+
return pending;
|
|
58
|
+
}
|
|
59
|
+
async deliver({ id = crypto.randomUUID(), from, to, text, transport = "local" }) {
|
|
60
|
+
const target = await this.target(to);
|
|
61
|
+
const message = {
|
|
62
|
+
id,
|
|
63
|
+
from: String(from),
|
|
64
|
+
to,
|
|
65
|
+
text,
|
|
66
|
+
createdAt: Date.now(),
|
|
67
|
+
delivered: true,
|
|
68
|
+
transport
|
|
69
|
+
};
|
|
70
|
+
target.followup(createUserMessage({
|
|
71
|
+
content: [{
|
|
72
|
+
type: "text",
|
|
73
|
+
text: `[dsh-bridge ${transport} message ${id} from ${message.from}]\n${text}`
|
|
74
|
+
}],
|
|
75
|
+
source: {
|
|
76
|
+
kind: "plugin",
|
|
77
|
+
plugin: name,
|
|
78
|
+
form: transport
|
|
79
|
+
}
|
|
80
|
+
}));
|
|
81
|
+
this.messages.push(message);
|
|
82
|
+
if (this.messages.length > MAX_RECENT_MESSAGES) {
|
|
83
|
+
this.messages.splice(0, this.messages.length - MAX_RECENT_MESSAGES);
|
|
84
|
+
}
|
|
85
|
+
for (const listener of this.listeners) listener(message);
|
|
86
|
+
return {
|
|
87
|
+
messageId: id,
|
|
88
|
+
from: message.from,
|
|
89
|
+
to: String(to),
|
|
90
|
+
delivered: true
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
async send(from, to, text) {
|
|
94
|
+
return this.deliver({ from: from.session.id, to, text });
|
|
95
|
+
}
|
|
96
|
+
async deliverExternal(from, to, text, options = {}) {
|
|
97
|
+
return this.deliver({
|
|
98
|
+
id: options.id,
|
|
99
|
+
from,
|
|
100
|
+
to,
|
|
101
|
+
text,
|
|
102
|
+
transport: options.transport ?? "external"
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
subscribe(listener) {
|
|
106
|
+
this.listeners.add(listener);
|
|
107
|
+
return () => this.listeners.delete(listener);
|
|
108
|
+
}
|
|
109
|
+
receive(sessionId, limit) {
|
|
110
|
+
return this.messages.filter((message) => message.to === sessionId).slice(-limit);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
function apply(ctx) {
|
|
114
|
+
const messaging = new LocalSessionMessagingImpl(ctx);
|
|
115
|
+
// `dshBridge` is the public service name. Keep the old accessor for one
|
|
116
|
+
// release so an early local installation does not break on upgrade.
|
|
117
|
+
ctx.accessor("dshBridge", { get: () => messaging });
|
|
118
|
+
ctx.accessor("sessionMessaging", { get: () => messaging });
|
|
119
|
+
ctx.tools.register(defineTool({
|
|
120
|
+
name: "session_list",
|
|
121
|
+
description: "List live DeepSeek Harness sessions in this process. Each returned session is idle or running and can be messaged immediately.",
|
|
122
|
+
parameters: {},
|
|
123
|
+
output: {
|
|
124
|
+
schema: {
|
|
125
|
+
type: "array",
|
|
126
|
+
items: { type: "string" }
|
|
127
|
+
},
|
|
128
|
+
render: (_args, value) => [{
|
|
129
|
+
type: "text",
|
|
130
|
+
text: value.join("\n") || "No live sessions."
|
|
131
|
+
}]
|
|
132
|
+
},
|
|
133
|
+
async execute() {
|
|
134
|
+
return messaging.list().map(String);
|
|
135
|
+
}
|
|
136
|
+
}));
|
|
137
|
+
ctx.tools.register(defineTool({
|
|
138
|
+
name: "session_send",
|
|
139
|
+
description: "Send a message to another DeepSeek Harness session in this host. A persisted offline session is resumed before delivery.",
|
|
140
|
+
parameters: {
|
|
141
|
+
to: {
|
|
142
|
+
type: "string",
|
|
143
|
+
required: true,
|
|
144
|
+
description: "Target session id from session_list."
|
|
145
|
+
},
|
|
146
|
+
text: {
|
|
147
|
+
type: "string",
|
|
148
|
+
required: true,
|
|
149
|
+
description: "Message text."
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
output: {
|
|
153
|
+
schema: {
|
|
154
|
+
type: "object",
|
|
155
|
+
additionalProperties: false,
|
|
156
|
+
properties: {
|
|
157
|
+
messageId: {
|
|
158
|
+
type: "string",
|
|
159
|
+
required: true
|
|
160
|
+
},
|
|
161
|
+
from: {
|
|
162
|
+
type: "string",
|
|
163
|
+
required: true
|
|
164
|
+
},
|
|
165
|
+
to: {
|
|
166
|
+
type: "string",
|
|
167
|
+
required: true
|
|
168
|
+
},
|
|
169
|
+
delivered: {
|
|
170
|
+
type: "boolean",
|
|
171
|
+
required: true
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
render: (_args, value) => [{
|
|
176
|
+
type: "text",
|
|
177
|
+
text: `Delivered ${value.messageId} to ${value.to}.`
|
|
178
|
+
}]
|
|
179
|
+
},
|
|
180
|
+
async execute(args, exec) {
|
|
181
|
+
if (!exec.agent) throw new Error("session_send requires an owning agent");
|
|
182
|
+
if (args.text.trim() === "") throw new Error("text must not be empty");
|
|
183
|
+
return await messaging.send(exec.agent, SessionId(args.to), args.text);
|
|
184
|
+
}
|
|
185
|
+
}));
|
|
186
|
+
ctx.tools.register(defineTool({
|
|
187
|
+
name: "session_messages",
|
|
188
|
+
description: "Read messages delivered to the current session in this process.",
|
|
189
|
+
parameters: { limit: {
|
|
190
|
+
type: "number",
|
|
191
|
+
description: "Maximum number of messages, default 20."
|
|
192
|
+
} },
|
|
193
|
+
output: {
|
|
194
|
+
schema: {
|
|
195
|
+
type: "array",
|
|
196
|
+
items: {
|
|
197
|
+
type: "object",
|
|
198
|
+
additionalProperties: false,
|
|
199
|
+
properties: {
|
|
200
|
+
id: {
|
|
201
|
+
type: "string",
|
|
202
|
+
required: true
|
|
203
|
+
},
|
|
204
|
+
from: {
|
|
205
|
+
type: "string",
|
|
206
|
+
required: true
|
|
207
|
+
},
|
|
208
|
+
to: {
|
|
209
|
+
type: "string",
|
|
210
|
+
required: true
|
|
211
|
+
},
|
|
212
|
+
text: {
|
|
213
|
+
type: "string",
|
|
214
|
+
required: true
|
|
215
|
+
},
|
|
216
|
+
createdAt: {
|
|
217
|
+
type: "number",
|
|
218
|
+
required: true
|
|
219
|
+
},
|
|
220
|
+
delivered: {
|
|
221
|
+
type: "boolean",
|
|
222
|
+
required: true
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
render: (_args, value) => [{
|
|
228
|
+
type: "text",
|
|
229
|
+
text: value.map((message) => `[${message.from}] ${message.text}`).join("\n") || "No messages."
|
|
230
|
+
}]
|
|
231
|
+
},
|
|
232
|
+
async execute(args, exec) {
|
|
233
|
+
if (!exec.agent) throw new Error("session_messages requires an owning agent");
|
|
234
|
+
const limit = args.limit === void 0 ? 20 : Math.max(1, Math.min(100, Math.floor(args.limit)));
|
|
235
|
+
return [...messaging.receive(exec.agent.session.id, limit)];
|
|
236
|
+
}
|
|
237
|
+
}));
|
|
238
|
+
}
|
|
239
|
+
//#endregion
|
|
240
|
+
export { LocalSessionMessagingImpl, apply, inject, name };
|
|
@@ -0,0 +1,60 @@
|
|
|
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 = "dsh-bridge";
|
|
11
|
+
export declare const inject: string[];
|
|
12
|
+
export interface LocalMessage {
|
|
13
|
+
readonly id: string;
|
|
14
|
+
readonly from: string;
|
|
15
|
+
readonly to: SessionIdValue;
|
|
16
|
+
readonly text: string;
|
|
17
|
+
readonly createdAt: number;
|
|
18
|
+
readonly delivered: boolean;
|
|
19
|
+
readonly transport: string;
|
|
20
|
+
}
|
|
21
|
+
export interface SendMessageResult {
|
|
22
|
+
readonly messageId: string;
|
|
23
|
+
readonly from: string;
|
|
24
|
+
readonly to: string;
|
|
25
|
+
readonly delivered: boolean;
|
|
26
|
+
}
|
|
27
|
+
export type SessionRuntimeState = 'idle' | 'running' | 'waking' | 'offline' | 'archived' | 'missing';
|
|
28
|
+
export interface SessionRuntimeStatus {
|
|
29
|
+
readonly sessionId: string;
|
|
30
|
+
readonly state: SessionRuntimeState;
|
|
31
|
+
readonly live: boolean;
|
|
32
|
+
}
|
|
33
|
+
export interface LocalSessionMessaging {
|
|
34
|
+
list(): readonly SessionIdValue[];
|
|
35
|
+
status(sessionId: SessionIdValue): Promise<SessionRuntimeStatus>;
|
|
36
|
+
send(from: Agent, to: SessionIdValue, text: string): Promise<SendMessageResult>;
|
|
37
|
+
deliverExternal(from: string, to: SessionIdValue, text: string, options?: {
|
|
38
|
+
id?: string;
|
|
39
|
+
transport?: string;
|
|
40
|
+
}): Promise<SendMessageResult>;
|
|
41
|
+
subscribe(listener: (message: LocalMessage) => void): () => void;
|
|
42
|
+
receive(sessionId: SessionIdValue, limit: number): readonly LocalMessage[];
|
|
43
|
+
}
|
|
44
|
+
export declare class LocalSessionMessagingImpl implements LocalSessionMessaging {
|
|
45
|
+
constructor(ctx: Context);
|
|
46
|
+
list(): readonly SessionIdValue[];
|
|
47
|
+
status(sessionId: SessionIdValue): Promise<SessionRuntimeStatus>;
|
|
48
|
+
send(from: Agent, to: SessionIdValue, text: string): Promise<SendMessageResult>;
|
|
49
|
+
deliverExternal(from: string, to: SessionIdValue, text: string, options?: { id?: string; transport?: string }): Promise<SendMessageResult>;
|
|
50
|
+
subscribe(listener: (message: LocalMessage) => void): () => void;
|
|
51
|
+
receive(sessionId: SessionIdValue, limit: number): readonly LocalMessage[];
|
|
52
|
+
}
|
|
53
|
+
declare module '@deepseek-ai/cordis' {
|
|
54
|
+
interface Context {
|
|
55
|
+
dshBridge: LocalSessionMessaging;
|
|
56
|
+
sessionMessaging: LocalSessionMessaging;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export declare function apply(ctx: Context): void;
|
|
60
|
+
//# 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,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-bridge",
|
|
3
|
+
"description": "Local session messaging and event bridge for DeepSeek Harness",
|
|
4
|
+
"version": "0.1.0-rc.10",
|
|
5
|
+
"publishConfig": { "access": "public", "tag": "next" },
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/baixianger/dsh-bridge.git"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"check": "node --check lib/index.js && npm pack --dry-run",
|
|
13
|
+
"test": "node --test test/**/*.test.mjs"
|
|
14
|
+
},
|
|
15
|
+
"main": "lib/index.js",
|
|
16
|
+
"types": "lib/types/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./lib/types/index.d.ts",
|
|
20
|
+
"default": "./lib/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"dsh": {
|
|
24
|
+
"bundle": {
|
|
25
|
+
"patch": "./cordis.patch.yml"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"files": ["lib/index.js", "lib/types/**/*.js", "lib/types/**/*.d.ts", "cordis.patch.yml", "README.md"],
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"@deepseek-ai/cordis": ">=0.1.0-rc.5",
|
|
32
|
+
"@deepseek-ai/dsh-agent": ">=0.1.0-rc.5",
|
|
33
|
+
"@deepseek-ai/dsh-llm": ">=0.1.0-rc.5",
|
|
34
|
+
"@deepseek-ai/dsh-session": ">=0.1.0-rc.5",
|
|
35
|
+
"@deepseek-ai/dsh-tools": ">=0.1.0-rc.5"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@deepseek-ai/cordis": ">=0.1.0-rc.5",
|
|
39
|
+
"@deepseek-ai/dsh-agent": ">=0.1.0-rc.5",
|
|
40
|
+
"@deepseek-ai/dsh-llm": ">=0.1.0-rc.5",
|
|
41
|
+
"@deepseek-ai/dsh-session": ">=0.1.0-rc.5",
|
|
42
|
+
"@deepseek-ai/dsh-tools": ">=0.1.0-rc.5"
|
|
43
|
+
}
|
|
44
|
+
}
|