dsh-bettertaskgraphs 0.2.2 → 0.2.3
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 +2 -2
- package/client.js +2 -6
- package/client.source.mjs +1 -5
- package/package.json +1 -1
- package/projection.mjs +27 -7
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ npm test
|
|
|
18
18
|
npm run pack:bundle
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
`build` 从前端源码生成可由 DSH Web 加载的 `client.js`。`check` 校验生成物与源码一致,`pack:bundle` 会重新构建前端并生成 `dist/dsh-bettertaskgraphs-0.2.
|
|
21
|
+
`build` 从前端源码生成可由 DSH Web 加载的 `client.js`。`check` 校验生成物与源码一致,`pack:bundle` 会重新构建前端并生成 `dist/dsh-bettertaskgraphs-0.2.3.tgz`。包内包含服务端模块、Web 构建产物、对应源码和构建脚本,以及 Bundle 配置补丁。
|
|
22
22
|
|
|
23
23
|
## 安装到独立 Web profile
|
|
24
24
|
|
|
@@ -26,7 +26,7 @@ npm run pack:bundle
|
|
|
26
26
|
|
|
27
27
|
```sh
|
|
28
28
|
dsh --profile bettertaskgraphs --from-default-profile web --dump-config
|
|
29
|
-
dsh plugin --profile bettertaskgraphs add "$PWD/dist/dsh-bettertaskgraphs-0.2.
|
|
29
|
+
dsh plugin --profile bettertaskgraphs add "$PWD/dist/dsh-bettertaskgraphs-0.2.3.tgz"
|
|
30
30
|
dsh --profile bettertaskgraphs --dump-config
|
|
31
31
|
dsh --profile bettertaskgraphs
|
|
32
32
|
```
|
package/client.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
-
// Corresponding source: https://github.com/oldcitynight/dsh-bettertaskgraphs/tree/v0.2.
|
|
2
|
+
// Corresponding source: https://github.com/oldcitynight/dsh-bettertaskgraphs/tree/v0.2.3
|
|
3
3
|
// Generated by scripts/build-client.mjs.
|
|
4
4
|
window.__ModuleLoader__.load({id:"dsh-bettertaskgraphs",factory(require){
|
|
5
5
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
@@ -289,7 +289,6 @@ function createClientPlugin(React, z, createRemoteContribution, graphPresentatio
|
|
|
289
289
|
.tg-badge[data-state=failed]{background:#d452522f}
|
|
290
290
|
.tg-current,.tg-conditions{font-size:12px;line-height:1.4;overflow-wrap:anywhere}.tg-current{margin-bottom:7px}.tg-current .tg-badge{margin-inline-start:6px}
|
|
291
291
|
.tg-conditions{list-style:decimal;margin:4px 0 0;padding-inline-start:1.7em}.tg-conditions li+li{margin-top:4px}.tg-no-conditions{margin:4px 0 0;color:var(--tg-muted);font-size:12px}
|
|
292
|
-
.tg-error-only{padding:6px 9px;font-size:12px}
|
|
293
292
|
|
|
294
293
|
.tg-viewer{
|
|
295
294
|
--tg-panel:var(--dsw-alias-bg-base,Canvas);
|
|
@@ -511,10 +510,7 @@ function createClientPlugin(React, z, createRemoteContribution, graphPresentatio
|
|
|
511
510
|
void refreshStatus()
|
|
512
511
|
return () => { controller.abort(); clearTimeout(timer) }
|
|
513
512
|
}, [canRead, opened, running, sessionId, refreshCount])
|
|
514
|
-
if (!canRead || !opened || result?.sessionId !== sessionId) return null
|
|
515
|
-
if (!result.graph) return result.failed ? h('section', { className: 'tg-dock tg-error-only', 'aria-label': t('title') },
|
|
516
|
-
h('style', null, styles), h('span', { role: 'status' }, t('unavailable')), ' ',
|
|
517
|
-
h('button', { type: 'button', onClick: refresh }, t('retry'))) : null
|
|
513
|
+
if (!canRead || !opened || result?.sessionId !== sessionId || !result.graph) return null
|
|
518
514
|
return h(GraphPanel, { key: sessionId, graph: result.graph, t, busy: result.busy, failed: result.failed, refresh })
|
|
519
515
|
}
|
|
520
516
|
}
|
package/client.source.mjs
CHANGED
|
@@ -75,7 +75,6 @@ export function createClientPlugin(React, z, createRemoteContribution, graphPres
|
|
|
75
75
|
.tg-badge[data-state=failed]{background:#d452522f}
|
|
76
76
|
.tg-current,.tg-conditions{font-size:12px;line-height:1.4;overflow-wrap:anywhere}.tg-current{margin-bottom:7px}.tg-current .tg-badge{margin-inline-start:6px}
|
|
77
77
|
.tg-conditions{list-style:decimal;margin:4px 0 0;padding-inline-start:1.7em}.tg-conditions li+li{margin-top:4px}.tg-no-conditions{margin:4px 0 0;color:var(--tg-muted);font-size:12px}
|
|
78
|
-
.tg-error-only{padding:6px 9px;font-size:12px}
|
|
79
78
|
|
|
80
79
|
.tg-viewer{
|
|
81
80
|
--tg-panel:var(--dsw-alias-bg-base,Canvas);
|
|
@@ -297,10 +296,7 @@ export function createClientPlugin(React, z, createRemoteContribution, graphPres
|
|
|
297
296
|
void refreshStatus()
|
|
298
297
|
return () => { controller.abort(); clearTimeout(timer) }
|
|
299
298
|
}, [canRead, opened, running, sessionId, refreshCount])
|
|
300
|
-
if (!canRead || !opened || result?.sessionId !== sessionId) return null
|
|
301
|
-
if (!result.graph) return result.failed ? h('section', { className: 'tg-dock tg-error-only', 'aria-label': t('title') },
|
|
302
|
-
h('style', null, styles), h('span', { role: 'status' }, t('unavailable')), ' ',
|
|
303
|
-
h('button', { type: 'button', onClick: refresh }, t('retry'))) : null
|
|
299
|
+
if (!canRead || !opened || result?.sessionId !== sessionId || !result.graph) return null
|
|
304
300
|
return h(GraphPanel, { key: sessionId, graph: result.graph, t, busy: result.busy, failed: result.failed, refresh })
|
|
305
301
|
}
|
|
306
302
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-bettertaskgraphs",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Transactional task graphs with a conversation panel for DeepSeek Harness",
|
|
5
5
|
"license": "GPL-3.0-only",
|
|
6
6
|
"keywords": ["dsh", "deepseek-harness", "cordis", "task-graph"],
|
package/projection.mjs
CHANGED
|
@@ -12,6 +12,7 @@ const bounded = n => z.string().min(1).max(n)
|
|
|
12
12
|
const hex = z.string().regex(/^[a-f0-9]{64}$/)
|
|
13
13
|
const seq = z.number().int().nonnegative()
|
|
14
14
|
const workSchema = z.object({ rootCallId: bounded(512), callSeq: seq, rootResultSeq: seq.nullable(), eventDigest: hex }).strict()
|
|
15
|
+
const bashFailureSchema = z.object({ rootCallId: bounded(512), callId: bounded(512) }).strict()
|
|
15
16
|
const factSchema = z.object({ seq, callId: bounded(512), tool: bounded(300), isError: z.boolean(),
|
|
16
17
|
outcome: z.enum(['UNVERIFIED', 'FAIL', 'NOT_RUN', 'UNKNOWN']) }).strict()
|
|
17
18
|
const intentSchema = z.object({ version: z.literal(4), operationId: bounded(80), callId: bounded(512),
|
|
@@ -30,13 +31,14 @@ const pendingSchema = z.object({ intentSeq: seq, data: intentSchema,
|
|
|
30
31
|
export const stateSchema = z.object({ sessionId: bounded(512), ownFrom: seq, turn: seq, lastSeq: z.number().int().min(-1),
|
|
31
32
|
workStarted: seq, workExecuted: seq, uncertainties: z.array(workSchema).max(1024), acknowledgedWork: z.array(workSchema).max(1024),
|
|
32
33
|
calls: z.record(z.string(), z.object({ tool: bounded(300), turn: seq, seq, eventDigest: hex }).strict()),
|
|
34
|
+
bashFailures: z.array(bashFailureSchema).max(256),
|
|
33
35
|
dispatches: z.array(dispatchSchema).max(256), intents: z.array(pendingSchema).max(1024),
|
|
34
36
|
proofs: z.array(proofSchema).max(MAX_PROOFS), receipts: z.array(z.object({ operationId: bounded(80), seq,
|
|
35
37
|
status: z.enum(['COMMITTED', 'ABORTED']), graphRevision: seq, graphChecksum: hex.nullable(), eventDigest: hex }).strict()).max(1024), failure: z.string().nullable() }).strict()
|
|
36
38
|
export function initialState(header, inheritedEventCount = 0) {
|
|
37
39
|
return { sessionId: header.id, ownFrom: inheritedEventCount, turn: 0, lastSeq: -1, workStarted: 0, workExecuted: 0,
|
|
38
40
|
uncertainties: [], acknowledgedWork: [],
|
|
39
|
-
calls: {}, dispatches: [], intents: [], proofs: [], receipts: [], failure: null }
|
|
41
|
+
calls: {}, bashFailures: [], dispatches: [], intents: [], proofs: [], receipts: [], failure: null }
|
|
40
42
|
}
|
|
41
43
|
export function outcomeOfContent(content = []) {
|
|
42
44
|
const outcomes = new Set()
|
|
@@ -55,6 +57,11 @@ export function outcomeOfContent(content = []) {
|
|
|
55
57
|
// A first FAIL/NOT_RUN block may not hide a later UNKNOWN/contradiction.
|
|
56
58
|
return outcomes.has('UNKNOWN') || outcomes.size > 1 ? 'UNKNOWN' : [...outcomes][0] ?? 'UNVERIFIED'
|
|
57
59
|
}
|
|
60
|
+
function toolOutcome(state, tool, rootCallId, callId, content) {
|
|
61
|
+
const outcome = outcomeOfContent(content)
|
|
62
|
+
if (tool !== 'bash' || !state.bashFailures.some(row => row.rootCallId === rootCallId && row.callId === callId)) return outcome
|
|
63
|
+
return outcome === 'UNKNOWN' || outcome === 'NOT_RUN' ? 'UNKNOWN' : 'FAIL'
|
|
64
|
+
}
|
|
58
65
|
export function chainQualification(start, root, dispatches) {
|
|
59
66
|
let child = start
|
|
60
67
|
const visited = new Set()
|
|
@@ -94,7 +101,7 @@ export function applyEvent(state, event) {
|
|
|
94
101
|
if (event.seq < state.ownFrom) return state // Fork parents cannot authorize a child's graph.
|
|
95
102
|
if (event.type.startsWith('plugin:project-transaction-graph/') && ![INTENT, RECEIPT, RECOVERY].includes(event.type))
|
|
96
103
|
throw new GraphError('LEGACY_GRAPH_REQUIRES_EXPLICIT_MIGRATION')
|
|
97
|
-
if (!['turn/start', 'turn/end', 'tool/call', 'tool/ptc-dispatch', 'tool/result', INTENT, RECEIPT, RECOVERY].includes(event.type)) return state
|
|
104
|
+
if (!['turn/start', 'turn/end', 'tool/call', 'tool/ptc-dispatch', 'tool/result', 'job/settled', INTENT, RECEIPT, RECOVERY].includes(event.type)) return state
|
|
98
105
|
const d = event.data
|
|
99
106
|
let next = { ...state, lastSeq: event.seq }
|
|
100
107
|
if (event.type === 'turn/start') return { ...next, turn: d.turn }
|
|
@@ -126,6 +133,18 @@ export function applyEvent(state, event) {
|
|
|
126
133
|
rootResultSeq: event.type === RECOVERY ? event.seq : null, rootResultDigest: event.type === RECOVERY ? digest(event) : null,
|
|
127
134
|
workStartedAtRoot: event.type === RECOVERY ? state.workStarted : null, workExecutedAtRoot: event.type === RECOVERY ? state.workExecuted : null }] }
|
|
128
135
|
}
|
|
136
|
+
if (event.type === 'job/settled') {
|
|
137
|
+
const origin = d.toolCall
|
|
138
|
+
if (d.kind !== 'bash' || d.owner !== state.sessionId || !origin || !Object.hasOwn(state.calls, origin.rootCallId)
|
|
139
|
+
|| d.status !== 'completed' || d.producerReported !== true) return next
|
|
140
|
+
// Native Bash reports a nonzero exit in the owned job log even when its tool message is not an error.
|
|
141
|
+
const exit = /^exit code: (-?\d+)(?:;|$)/.exec(d.detail ?? '')
|
|
142
|
+
if (!exit || Number(exit[1]) === 0) return next
|
|
143
|
+
const failure = bashFailureSchema.parse({ rootCallId: origin.rootCallId, callId: origin.callId })
|
|
144
|
+
if (state.bashFailures.some(row => row.rootCallId === failure.rootCallId && row.callId === failure.callId)) return next
|
|
145
|
+
if (state.bashFailures.length >= 256) throw new GraphError('BASH_FAILURE_RECORD_LIMIT')
|
|
146
|
+
return { ...next, bashFailures: [...state.bashFailures, failure] }
|
|
147
|
+
}
|
|
129
148
|
if (event.type === 'tool/ptc-dispatch') {
|
|
130
149
|
const rootCallId = d.rootCallId ?? d.parentCallId
|
|
131
150
|
if (!Object.hasOwn(state.calls, rootCallId)) {
|
|
@@ -136,7 +155,7 @@ export function applyEvent(state, event) {
|
|
|
136
155
|
}
|
|
137
156
|
const dispatch = dispatchSchema.parse({ seq: event.seq, subCallId: d.subCallId, parentCallId: d.parentCallId,
|
|
138
157
|
rootCallId, tool: d.name, isError: d.isError,
|
|
139
|
-
outcome:
|
|
158
|
+
outcome: toolOutcome(state, d.name, rootCallId, d.subCallId, d.content), eventDigest: digest(event), turn: state.turn })
|
|
140
159
|
if (state.dispatches.some(row => row.subCallId === dispatch.subCallId && row.rootCallId === dispatch.rootCallId)
|
|
141
160
|
|| state.dispatches.length >= 256) throw new GraphError('PTC_DISPATCH_REPLAY_OR_LIMIT')
|
|
142
161
|
return { ...next, workStarted: state.workStarted + (!internal(d.name) && d.name !== 'run_code' ? 1 : 0),
|
|
@@ -145,9 +164,6 @@ export function applyEvent(state, event) {
|
|
|
145
164
|
if (event.type === 'tool/result') {
|
|
146
165
|
const root = d.message.source.callId, success = d.message.isError !== true // Optional false in the published ToolResultMessage API.
|
|
147
166
|
const syntheticUnknown = [TOOL_OUTCOME_UNKNOWN, TOOL_NOT_STARTED].includes(d.error?.code)
|
|
148
|
-
const rootOutcome = outcomeOfContent(d.message.content)
|
|
149
|
-
const rootQualification = syntheticUnknown || rootOutcome === 'UNKNOWN' ? 'UNKNOWN'
|
|
150
|
-
: !success || ['FAIL', 'NOT_RUN'].includes(rootOutcome) ? 'FAILED' : 'SUCCESS'
|
|
151
167
|
const sources = state.dispatches.filter(row => row.rootCallId === root)
|
|
152
168
|
const call = state.calls[root]
|
|
153
169
|
if (!call) {
|
|
@@ -158,6 +174,9 @@ export function applyEvent(state, event) {
|
|
|
158
174
|
return next
|
|
159
175
|
}
|
|
160
176
|
const standaloneCode = call.tool === 'run_code' && !sources.length
|
|
177
|
+
const rootOutcome = toolOutcome(state, call.tool, root, root, d.message.content)
|
|
178
|
+
const rootQualification = syntheticUnknown || rootOutcome === 'UNKNOWN' ? 'UNKNOWN'
|
|
179
|
+
: !success || ['FAIL', 'NOT_RUN'].includes(rootOutcome) ? 'FAILED' : 'SUCCESS'
|
|
161
180
|
const leafCode = sources.filter(row => row.tool === 'run_code' && !sources.some(child => child.parentCallId === row.subCallId))
|
|
162
181
|
const workStarted = state.workStarted + (standaloneCode ? 1 : 0) + leafCode.length
|
|
163
182
|
const executed = sources.filter(row => !internal(row.tool) && (row.tool !== 'run_code' || leafCode.includes(row)) && !['NOT_RUN', 'UNKNOWN'].includes(row.outcome)).length
|
|
@@ -189,6 +208,7 @@ export function applyEvent(state, event) {
|
|
|
189
208
|
if (uncertainties.length > 1024) throw new GraphError('UNRESOLVED_WORK_LIMIT')
|
|
190
209
|
}
|
|
191
210
|
return { ...next, calls, intents, workStarted, workExecuted, uncertainties, dispatches: state.dispatches.filter(row => row.rootCallId !== root),
|
|
211
|
+
bashFailures: state.bashFailures.filter(row => row.rootCallId !== root),
|
|
192
212
|
proofs: [...state.proofs, ...proofs].slice(-MAX_PROOFS) }
|
|
193
213
|
}
|
|
194
214
|
if (event.type === RECEIPT) {
|
|
@@ -202,4 +222,4 @@ export function applyEvent(state, event) {
|
|
|
202
222
|
return next
|
|
203
223
|
} catch (error) { return { ...state, failure: `Transaction projection failed at ${event.seq}: ${error.message}` } }
|
|
204
224
|
}
|
|
205
|
-
export const projection = { key: KEY, stateVersion:
|
|
225
|
+
export const projection = { key: KEY, stateVersion: 6, stateSchema, init: initialState, apply: applyEvent }
|