dsh-trellis 0.1.0 → 0.1.2
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 +92 -80
- package/lib/client.js +475 -0
- package/lib/client.js.map +1 -0
- package/lib/commands.js +215 -195
- package/lib/config.js +77 -45
- package/lib/index.js +186 -146
- package/lib/wait-tool.js +192 -149
- package/package.json +114 -79
package/lib/wait-tool.js
CHANGED
|
@@ -1,149 +1,192 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Event-driven waiting for DeepSeek Harness continuable subagents.
|
|
3
|
-
*
|
|
4
|
-
* Trellis dispatches role agents in DSH's default background mode so the main
|
|
5
|
-
* agent can keep doing independent work. When that work is exhausted, this
|
|
6
|
-
* tool provides a native synchronization point: it waits for `subagent/end`,
|
|
7
|
-
* which DSH emits only after the child's settlement notice has been queued for
|
|
8
|
-
* the parent. It deliberately replaces shell sleeps and polling loops.
|
|
9
|
-
*
|
|
10
|
-
* @module dsh-trellis/wait-tool
|
|
11
|
-
*/
|
|
12
|
-
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
13
|
-
|
|
14
|
-
function createSettlementWaiter(parentCtx, subagentId, signal) {
|
|
15
|
-
let disposeListener = () => {};
|
|
16
|
-
let settled = false;
|
|
17
|
-
let resolvePromise;
|
|
18
|
-
let rejectPromise;
|
|
19
|
-
|
|
20
|
-
const cleanup = () => {
|
|
21
|
-
disposeListener?.();
|
|
22
|
-
signal?.removeEventListener("abort", onAbort);
|
|
23
|
-
};
|
|
24
|
-
const onAbort = () => {
|
|
25
|
-
if (settled) return;
|
|
26
|
-
settled = true;
|
|
27
|
-
cleanup();
|
|
28
|
-
rejectPromise(signal.reason ?? new Error("Trellis subagent wait cancelled."));
|
|
29
|
-
};
|
|
30
|
-
const promise = new Promise((resolve, reject) => {
|
|
31
|
-
resolvePromise = resolve;
|
|
32
|
-
rejectPromise = reject;
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
disposeListener =
|
|
36
|
-
parentCtx.on("subagent/end", (info) => {
|
|
37
|
-
if (settled || info.id !== subagentId) return;
|
|
38
|
-
settled = true;
|
|
39
|
-
cleanup();
|
|
40
|
-
resolvePromise(info
|
|
41
|
-
}) ?? (() => {});
|
|
42
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
43
|
-
if (signal?.aborted) onAbort();
|
|
44
|
-
|
|
45
|
-
return {
|
|
46
|
-
promise,
|
|
47
|
-
cancel() {
|
|
48
|
-
if (settled) return;
|
|
49
|
-
settled = true;
|
|
50
|
-
cleanup();
|
|
51
|
-
},
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
export function
|
|
57
|
-
return
|
|
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
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Event-driven waiting for DeepSeek Harness continuable subagents.
|
|
3
|
+
*
|
|
4
|
+
* Trellis dispatches role agents in DSH's default background mode so the main
|
|
5
|
+
* agent can keep doing independent work. When that work is exhausted, this
|
|
6
|
+
* tool provides a native synchronization point: it waits for `subagent/end`,
|
|
7
|
+
* which DSH emits only after the child's settlement notice has been queued for
|
|
8
|
+
* the parent. It deliberately replaces shell sleeps and polling loops.
|
|
9
|
+
*
|
|
10
|
+
* @module dsh-trellis/wait-tool
|
|
11
|
+
*/
|
|
12
|
+
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
13
|
+
|
|
14
|
+
function createSettlementWaiter(parentCtx, subagentId, signal) {
|
|
15
|
+
let disposeListener = () => {};
|
|
16
|
+
let settled = false;
|
|
17
|
+
let resolvePromise;
|
|
18
|
+
let rejectPromise;
|
|
19
|
+
|
|
20
|
+
const cleanup = () => {
|
|
21
|
+
disposeListener?.();
|
|
22
|
+
signal?.removeEventListener("abort", onAbort);
|
|
23
|
+
};
|
|
24
|
+
const onAbort = () => {
|
|
25
|
+
if (settled) return;
|
|
26
|
+
settled = true;
|
|
27
|
+
cleanup();
|
|
28
|
+
rejectPromise(signal.reason ?? new Error("Trellis subagent wait cancelled."));
|
|
29
|
+
};
|
|
30
|
+
const promise = new Promise((resolve, reject) => {
|
|
31
|
+
resolvePromise = resolve;
|
|
32
|
+
rejectPromise = reject;
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
disposeListener =
|
|
36
|
+
parentCtx.on("subagent/end", (info) => {
|
|
37
|
+
if (settled || info.id !== subagentId) return;
|
|
38
|
+
settled = true;
|
|
39
|
+
cleanup();
|
|
40
|
+
resolvePromise(info);
|
|
41
|
+
}) ?? (() => {});
|
|
42
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
43
|
+
if (signal?.aborted) onAbort();
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
promise,
|
|
47
|
+
cancel() {
|
|
48
|
+
if (settled) return;
|
|
49
|
+
settled = true;
|
|
50
|
+
cleanup();
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Map a DSH stop reason to a gate-safe Trellis outcome. */
|
|
56
|
+
export function settlementOutcome(stopReason) {
|
|
57
|
+
if (stopReason === "completed") return "completed";
|
|
58
|
+
if (stopReason === "aborted") return "aborted";
|
|
59
|
+
if (stopReason === "unknown") return "unknown";
|
|
60
|
+
return "failed";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function settledResult(subagentId, info) {
|
|
64
|
+
const stopReason =
|
|
65
|
+
typeof info?.stopReason === "string" ? info.stopReason : "unknown";
|
|
66
|
+
return {
|
|
67
|
+
subagentId,
|
|
68
|
+
state: "settled",
|
|
69
|
+
outcome: settlementOutcome(stopReason),
|
|
70
|
+
stopReason,
|
|
71
|
+
runId: typeof info?.runId === "string" ? info.runId : "",
|
|
72
|
+
provider: typeof info?.provider === "string" ? info.provider : "",
|
|
73
|
+
assistantOutputBlocks: Array.isArray(info?.lastAssistantMessage)
|
|
74
|
+
? info.lastAssistantMessage.length
|
|
75
|
+
: 0,
|
|
76
|
+
settlementNoticeQueued: true,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function inactiveResult(subagentId) {
|
|
81
|
+
return {
|
|
82
|
+
subagentId,
|
|
83
|
+
state: "already-inactive",
|
|
84
|
+
outcome: "unknown",
|
|
85
|
+
stopReason: "unknown",
|
|
86
|
+
runId: "",
|
|
87
|
+
provider: "",
|
|
88
|
+
assistantOutputBlocks: 0,
|
|
89
|
+
settlementNoticeQueued: false,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Register the Trellis-only, event-driven subagent wait tool. */
|
|
94
|
+
export function registerWaitTool(ctx) {
|
|
95
|
+
return ctx.tools.register(
|
|
96
|
+
defineTool({
|
|
97
|
+
name: "trellis_wait",
|
|
98
|
+
description:
|
|
99
|
+
"Wait efficiently for one direct background Trellis subagent after independent work is exhausted. This is an event-driven synchronization barrier over DSH's native settlement lifecycle. Use the subagent id returned by `subagent`; never replace this with shell sleep, polling, or job_output. On DSH rc.8+, an idle parent can instead yield and be woken by the native settlement notice.",
|
|
100
|
+
parameters: {
|
|
101
|
+
subagent_id: {
|
|
102
|
+
type: "string",
|
|
103
|
+
required: true,
|
|
104
|
+
description:
|
|
105
|
+
"Direct continuable subagent id returned by the `subagent` tool.",
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
output: {
|
|
109
|
+
schema: {
|
|
110
|
+
type: "object",
|
|
111
|
+
additionalProperties: false,
|
|
112
|
+
properties: {
|
|
113
|
+
subagentId: { type: "string", required: true },
|
|
114
|
+
state: { type: "string", required: true },
|
|
115
|
+
outcome: { type: "string", required: true },
|
|
116
|
+
stopReason: { type: "string", required: true },
|
|
117
|
+
runId: { type: "string", required: true },
|
|
118
|
+
provider: { type: "string", required: true },
|
|
119
|
+
assistantOutputBlocks: { type: "number", required: true },
|
|
120
|
+
settlementNoticeQueued: { type: "boolean", required: true },
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
render(_args, value) {
|
|
124
|
+
if (value.state === "already-inactive") {
|
|
125
|
+
return [
|
|
126
|
+
{
|
|
127
|
+
type: "text",
|
|
128
|
+
text: `subagent ${value.subagentId} is already inactive; no stop reason can be proven from the catalog snapshot, so inspect its transcript or an already-visible settlement notice before passing a gate`,
|
|
129
|
+
},
|
|
130
|
+
];
|
|
131
|
+
}
|
|
132
|
+
if (value.outcome !== "completed") {
|
|
133
|
+
return [
|
|
134
|
+
{
|
|
135
|
+
type: "text",
|
|
136
|
+
text: `subagent ${value.subagentId} settled with ${value.outcome} (${value.stopReason}); do not treat this gate as passed; its native settlement notice is queued`,
|
|
137
|
+
},
|
|
138
|
+
];
|
|
139
|
+
}
|
|
140
|
+
return [
|
|
141
|
+
{
|
|
142
|
+
type: "text",
|
|
143
|
+
text: `subagent ${value.subagentId} completed; its native settlement notice is queued`,
|
|
144
|
+
},
|
|
145
|
+
];
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
async execute(args, exec) {
|
|
149
|
+
const parent = exec.agent;
|
|
150
|
+
if (!parent)
|
|
151
|
+
throw new Error(
|
|
152
|
+
"trellis_wait requires a calling agent (exec.agent was undefined)",
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
// Install the listener before reading the durable catalog so settlement
|
|
156
|
+
// cannot slip through the validation/read-to-wait window.
|
|
157
|
+
const waiter = createSettlementWaiter(
|
|
158
|
+
parent.ctx,
|
|
159
|
+
args.subagent_id,
|
|
160
|
+
exec.signal,
|
|
161
|
+
);
|
|
162
|
+
try {
|
|
163
|
+
const entries = await ctx.subagents.listChildren(
|
|
164
|
+
parent.id,
|
|
165
|
+
exec.signal,
|
|
166
|
+
);
|
|
167
|
+
const child = entries.find(
|
|
168
|
+
(entry) =>
|
|
169
|
+
entry.kind === "child" &&
|
|
170
|
+
entry.mode === "continuable" &&
|
|
171
|
+
entry.id === args.subagent_id,
|
|
172
|
+
);
|
|
173
|
+
if (child === undefined) {
|
|
174
|
+
throw new Error(
|
|
175
|
+
`trellis_wait can only wait for a direct continuable subagent of this session: ${args.subagent_id}`,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
if (child.activity === "inactive") {
|
|
179
|
+
waiter.cancel();
|
|
180
|
+
return inactiveResult(args.subagent_id);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const info = await waiter.promise;
|
|
184
|
+
return settledResult(args.subagent_id, info);
|
|
185
|
+
} catch (error) {
|
|
186
|
+
waiter.cancel();
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
}),
|
|
191
|
+
);
|
|
192
|
+
}
|
package/package.json
CHANGED
|
@@ -1,79 +1,114 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "dsh-trellis",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Trellis workflow integration for DeepSeek Harness: per-turn workflow-state breadcrumbs, utility commands, and native session identity.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "lib/index.js",
|
|
7
|
-
"exports": {
|
|
8
|
-
".": "./lib/index.js"
|
|
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
|
-
"@deepseek-ai/dsh-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
"@deepseek-ai/dsh-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
"@deepseek-ai/dsh-tools":
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
"@deepseek-ai/
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
"@deepseek-ai/dsh-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-trellis",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Trellis workflow integration for DeepSeek Harness: per-turn workflow-state breadcrumbs, utility commands, and native session identity.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./lib/index.js",
|
|
9
|
+
"./client": "./lib/client.js",
|
|
10
|
+
"./package.json": "./package.json"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"lib",
|
|
14
|
+
"cordis.patch.yml"
|
|
15
|
+
],
|
|
16
|
+
"author": "SajoLuo",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/SajoLuo/dsh-trellis.git"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/SajoLuo/dsh-trellis#readme",
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/SajoLuo/dsh-trellis/issues"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"deepseek-harness",
|
|
28
|
+
"dsh",
|
|
29
|
+
"trellis",
|
|
30
|
+
"workflow",
|
|
31
|
+
"ai-agent"
|
|
32
|
+
],
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"packageManager": "pnpm@10.33.0",
|
|
37
|
+
"dsh": {
|
|
38
|
+
"bundle": {
|
|
39
|
+
"patch": "cordis.patch.yml"
|
|
40
|
+
},
|
|
41
|
+
"client": {
|
|
42
|
+
"inject": [
|
|
43
|
+
"@deepseek-ai/dsh-client-locale",
|
|
44
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
45
|
+
"@deepseek-ai/dsh-client-ui-settings-plugins"
|
|
46
|
+
],
|
|
47
|
+
"platform": "web"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build:client": "tsdown",
|
|
52
|
+
"test": "node --test \"test/*.test.js\"",
|
|
53
|
+
"prepack": "pnpm run build:client"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
57
|
+
"@deepseek-ai/dsh-commands": "^0.1.0-rc.6",
|
|
58
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.0-rc.8",
|
|
59
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.8",
|
|
60
|
+
"@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.0-rc.8",
|
|
61
|
+
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
|
|
62
|
+
"@deepseek-ai/dsh-shell-env": "^0.1.0-rc.6",
|
|
63
|
+
"@deepseek-ai/dsh-subagent": "^0.1.0-rc.6",
|
|
64
|
+
"@deepseek-ai/dsh-tools": "^0.1.0-rc.6",
|
|
65
|
+
"@deepseek-ai/schemastery": "^3.18.1",
|
|
66
|
+
"react": "^18.2.0"
|
|
67
|
+
},
|
|
68
|
+
"peerDependenciesMeta": {
|
|
69
|
+
"@deepseek-ai/cordis": {
|
|
70
|
+
"optional": true
|
|
71
|
+
},
|
|
72
|
+
"@deepseek-ai/dsh-commands": {
|
|
73
|
+
"optional": true
|
|
74
|
+
},
|
|
75
|
+
"@deepseek-ai/dsh-client-locale": {
|
|
76
|
+
"optional": true
|
|
77
|
+
},
|
|
78
|
+
"@deepseek-ai/dsh-client-ui-settings": {
|
|
79
|
+
"optional": true
|
|
80
|
+
},
|
|
81
|
+
"@deepseek-ai/dsh-client-ui-settings-plugins": {
|
|
82
|
+
"optional": true
|
|
83
|
+
},
|
|
84
|
+
"@deepseek-ai/dsh-llm": {
|
|
85
|
+
"optional": true
|
|
86
|
+
},
|
|
87
|
+
"@deepseek-ai/dsh-shell-env": {
|
|
88
|
+
"optional": true
|
|
89
|
+
},
|
|
90
|
+
"@deepseek-ai/dsh-subagent": {
|
|
91
|
+
"optional": true
|
|
92
|
+
},
|
|
93
|
+
"@deepseek-ai/dsh-tools": {
|
|
94
|
+
"optional": true
|
|
95
|
+
},
|
|
96
|
+
"@deepseek-ai/schemastery": {
|
|
97
|
+
"optional": true
|
|
98
|
+
},
|
|
99
|
+
"react": {
|
|
100
|
+
"optional": true
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
"devDependencies": {
|
|
104
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
105
|
+
"@deepseek-ai/dsh-commands": "0.1.0-rc.8",
|
|
106
|
+
"@deepseek-ai/dsh-llm": "0.1.0-rc.8",
|
|
107
|
+
"@deepseek-ai/dsh-shell-env": "0.1.0-rc.8",
|
|
108
|
+
"@deepseek-ai/dsh-subagent": "0.1.0-rc.8",
|
|
109
|
+
"@deepseek-ai/dsh-tools": "0.1.0-rc.8",
|
|
110
|
+
"@deepseek-ai/schemastery": "^3.18.1",
|
|
111
|
+
"react": "^18.2.0",
|
|
112
|
+
"tsdown": "^0.22.2"
|
|
113
|
+
}
|
|
114
|
+
}
|