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/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.stopReason);
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
- /** Register the Trellis-only, event-driven subagent wait tool. */
56
- export function registerWaitTool(ctx) {
57
- return ctx.tools.register(
58
- defineTool({
59
- name: "trellis_wait",
60
- description:
61
- "Wait efficiently for one direct background Trellis subagent after independent work is exhausted. This is event-driven: it returns only after DeepSeek Harness has queued the child's native settlement notice. Use the subagent id returned by `subagent`; never replace this with shell sleep, polling, or job_output.",
62
- parameters: {
63
- subagent_id: {
64
- type: "string",
65
- required: true,
66
- description:
67
- "Direct continuable subagent id returned by the `subagent` tool.",
68
- },
69
- },
70
- output: {
71
- schema: {
72
- type: "object",
73
- additionalProperties: false,
74
- properties: {
75
- subagentId: { type: "string", required: true },
76
- state: { type: "string", required: true },
77
- stopReason: { type: "string", required: true },
78
- },
79
- },
80
- render(_args, value) {
81
- if (value.state === "already-inactive") {
82
- return [
83
- {
84
- type: "text",
85
- text: `subagent ${value.subagentId} is already inactive; consume its queued settlement notice or inspect its transcript`,
86
- },
87
- ];
88
- }
89
- return [
90
- {
91
- type: "text",
92
- text: `subagent ${value.subagentId} settled (${value.stopReason}); its native settlement notice is queued`,
93
- },
94
- ];
95
- },
96
- },
97
- async execute(args, exec) {
98
- const parent = exec.agent;
99
- if (!parent)
100
- throw new Error(
101
- "trellis_wait requires a calling agent (exec.agent was undefined)",
102
- );
103
-
104
- // Install the listener before reading the durable catalog so settlement
105
- // cannot slip through the validation/read-to-wait window.
106
- const waiter = createSettlementWaiter(
107
- parent.ctx,
108
- args.subagent_id,
109
- exec.signal,
110
- );
111
- try {
112
- const entries = await ctx.subagents.listChildren(
113
- parent.id,
114
- exec.signal,
115
- );
116
- const child = entries.find(
117
- (entry) =>
118
- entry.kind === "child" &&
119
- entry.mode === "continuable" &&
120
- entry.id === args.subagent_id,
121
- );
122
- if (child === undefined) {
123
- throw new Error(
124
- `trellis_wait can only wait for a direct continuable subagent of this session: ${args.subagent_id}`,
125
- );
126
- }
127
- if (child.activity === "inactive") {
128
- waiter.cancel();
129
- return {
130
- subagentId: args.subagent_id,
131
- state: "already-inactive",
132
- stopReason: "unknown",
133
- };
134
- }
135
-
136
- const stopReason = await waiter.promise;
137
- return {
138
- subagentId: args.subagent_id,
139
- state: "settled",
140
- stopReason,
141
- };
142
- } catch (error) {
143
- waiter.cancel();
144
- throw error;
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.0",
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
- "files": [
11
- "lib",
12
- "cordis.patch.yml"
13
- ],
14
- "author": "SajoLuo",
15
- "license": "MIT",
16
- "repository": {
17
- "type": "git",
18
- "url": "git+https://github.com/SajoLuo/dsh-trellis.git"
19
- },
20
- "homepage": "https://github.com/SajoLuo/dsh-trellis#readme",
21
- "bugs": {
22
- "url": "https://github.com/SajoLuo/dsh-trellis/issues"
23
- },
24
- "keywords": [
25
- "deepseek-harness",
26
- "dsh",
27
- "trellis",
28
- "workflow",
29
- "ai-agent"
30
- ],
31
- "publishConfig": {
32
- "access": "public"
33
- },
34
- "packageManager": "pnpm@10.33.0",
35
- "dsh": {
36
- "bundle": {
37
- "patch": "cordis.patch.yml"
38
- }
39
- },
40
- "scripts": {
41
- "test": "node --test \"test/*.test.js\""
42
- },
43
- "peerDependencies": {
44
- "@deepseek-ai/cordis": "^4.0.1",
45
- "@deepseek-ai/dsh-commands": "^0.1.0-rc.6",
46
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
47
- "@deepseek-ai/dsh-shell-env": "^0.1.0-rc.6",
48
- "@deepseek-ai/dsh-tools": "^0.1.0-rc.6",
49
- "@deepseek-ai/schemastery": "^3.18.1"
50
- },
51
- "peerDependenciesMeta": {
52
- "@deepseek-ai/cordis": {
53
- "optional": true
54
- },
55
- "@deepseek-ai/dsh-commands": {
56
- "optional": true
57
- },
58
- "@deepseek-ai/dsh-llm": {
59
- "optional": true
60
- },
61
- "@deepseek-ai/dsh-shell-env": {
62
- "optional": true
63
- },
64
- "@deepseek-ai/dsh-tools": {
65
- "optional": true
66
- },
67
- "@deepseek-ai/schemastery": {
68
- "optional": true
69
- }
70
- },
71
- "devDependencies": {
72
- "@deepseek-ai/cordis": "^4.0.1",
73
- "@deepseek-ai/dsh-commands": "^0.1.0-rc.6",
74
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
75
- "@deepseek-ai/dsh-shell-env": "^0.1.0-rc.6",
76
- "@deepseek-ai/dsh-tools": "^0.1.0-rc.6",
77
- "@deepseek-ai/schemastery": "^3.18.1"
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
+ }