Repository navigation
Expand file tree
/
Copy pathdsh-integration.ts
More file actions
128 lines (120 loc) · 4.91 KB
/
Copy pathdsh-integration.ts
File metadata and controls
128 lines (120 loc) · 4.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import type { AgentIntegration } from "../agent-integration.ts"
import type { AgentIntegrationContext } from "../agent-integration.ts"
import type { RuntimeSubtaskHook } from "../agent-integration.ts"
import { DshConnection } from "./connection.ts"
import { DshConversationStore } from "./dsh-conversation-store.ts"
import { DshAgentAdapter } from "./dsh-adapter.ts"
import { DshSubagentLinkRegistry } from "./dsh-subagent-link-registry.ts"
import { DshModelCatalog } from "./dsh-model-catalog.ts"
import { DshSkillCatalog } from "./dsh-skill-catalog.ts"
import { createDshSessionDeleteHook } from "./dsh-session-delete.ts"
/**
* Builds the complete DeepSeek Harness contribution behind the generic
* integration boundary. The shared connection, the conversation cursor store,
* the subagent link registry and the approval policy live in this factory
* closure; OpenCode routes and services never see DSH types.
*/
export function createDshAgentIntegration(context: AgentIntegrationContext): AgentIntegration {
const { config, db, logger } = context
const connection = new DshConnection({
baseUrl: config.dshBaseUrl,
allowRemote: config.dshAllowRemote,
requestTimeoutMs: config.dshRequestTimeoutMs,
approvalPolicy: config.dshApprovalPolicy,
logger,
})
const store = new DshConversationStore(db)
const subagentLinks = new DshSubagentLinkRegistry()
// Mirror the DSH host's live model catalog into OpenCode provider/model
// lists (provider id = the DSH route key, selectable via selectModel).
const modelCatalog = new DshModelCatalog({ connection, logger })
connection.subscribeState((state) => {
if (state === "connected") void modelCatalog.refresh()
})
// Do not contact DSH while the adaptor itself is starting. A DSH host is an
// optional backend, so an unavailable host must stay completely silent
// until a DSH runtime is actually selected. DshRuntime.start() starts the
// shared connection; the state listener above then fills the live catalog.
// Mirror the host's user-invocable skill catalog (`skills.list` is
// session-addressed, so the mirror fills whenever a DSH runtime starts).
const skillCatalog = new DshSkillCatalog({ connection, logger })
// Subagent usage is the DSH host's own decision (the model's native
// `subagent` tool): the adaptor never exposes a client-driven subagent
// runner. When a native subagent call is observed, AgentService mirrors an
// OpenCode child session and this hook records the call → child link so the
// projector can bind the real DSH child session to it. The DSH mapper tags
// every task tool input with `agent`, which is how this hook recognizes
// DSH-originated runtime subtasks among all backends.
const runtimeSubtaskHook: RuntimeSubtaskHook = (info) => {
if (typeof info.input.agent !== "string") return
subagentLinks.setChild(info.parentSessionId, info.callId, info.childSessionId)
}
const adapter = new DshAgentAdapter(config, {
connection,
store,
subagentLinks,
skillCatalog,
// Legacy identifiers for older sessions and DEFAULT_AGENT setups.
aliases: ["dsh", "DeepSeekHarness", "DSHarness"],
})
// Plan variant: same host session, but the host's /plan command flips plan
// mode on for freshly created sessions (plan:policy guidance + the
// exit_plan_mode review tool; plan reviews are answered by the approval
// policy). Mirrors Pi's pi-Plan entry point.
const planAdapter = new DshAgentAdapter(config, {
id: "deepSeek-Harness-Plan",
displayName: "DeepSeek-Harness-Plan",
mode: "primary",
aliases: ["dsh-plan"],
planMode: true,
connection,
store,
subagentLinks,
skillCatalog,
})
return {
adapters: [adapter, planAdapter],
factories: [
{
type: "dsh",
factory: (input) =>
new DshAgentAdapter(config, {
id: input.id,
displayName: input.displayName,
provider: input.provider,
model: input.model,
removable: true,
connection,
store,
subagentLinks,
skillCatalog,
}),
},
],
providers: [
{
id: "dsh",
name: "DeepSeek Harness",
modelID: "default",
modelName: "DeepSeek Harness default model",
reasoning: true,
contextLimit: 1_000_000,
outputLimit: 256_000,
requiresAuth: false,
backend: "dsh",
},
],
providerSources: [modelCatalog],
skillSources: [skillCatalog],
// The host has no session.delete: OpenCode deletes map to DSH archives.
// The second hook drops any in-flight subagent links owned by the deleted
// parent session.
sessionDeleteHooks: [
createDshSessionDeleteHook({ connection, store, logger }),
(sessionId) => subagentLinks.clearSession(sessionId),
],
runtimeSubtaskHooks: [runtimeSubtaskHook],
defaultAdapterType: "dsh",
defaultModel: "dsh/default",
}
}