@devflow-core/dsh-devflow 0.3.0 → 0.4.0
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.
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
* preset's full DevFlow prompt from agent.cordis.yml) is restored untouched,
|
|
6
6
|
* and the workspace line is only appended when the persona does not already
|
|
7
7
|
* mention a working directory (the DevFlow template resolves {{cwd}} itself).
|
|
8
|
+
*
|
|
9
|
+
* Synced with dsh-liangshen 0.3.14: goal whitelist (#578), presentation
|
|
10
|
+
* broadcasts (#1128), and the snapshotEvents() session-events fallback for
|
|
11
|
+
* newer DSH releases.
|
|
8
12
|
*/
|
|
9
13
|
|
|
10
14
|
/**
|
|
@@ -16,7 +20,8 @@
|
|
|
16
20
|
* - prompt sections: only the persona section (all other sections,
|
|
17
21
|
* including plan-mode's `plan:policy`, return after promotion)
|
|
18
22
|
* - runtime contexts: emptied (no sandbox/approval snapshot)
|
|
19
|
-
* - pre-step messages: only
|
|
23
|
+
* - pre-step messages: only whitelisted source kinds pass (direct user
|
|
24
|
+
* messages and goal auto-rounds by default)
|
|
20
25
|
*
|
|
21
26
|
* Promotion opens the full tool catalog and restores runtime contexts and all
|
|
22
27
|
* prompt sections. With `anchorGate` the promotion after the first tool call
|
|
@@ -82,11 +87,13 @@ const PERSONA_SECTION_NAMES = new Set(['deployment:persona', 'persona'])
|
|
|
82
87
|
*/
|
|
83
88
|
const WORKSPACE_LINE_PREFIX = '\n\nYour working directory is '
|
|
84
89
|
|
|
85
|
-
/**
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Message-source kinds the model may see during phase 1. Goal auto-rounds
|
|
92
|
+
* (source kind `goal`, issue #578) must be here: a filtered-out goal round
|
|
93
|
+
* never produces a response or tool call, so no promotion branch ever fires
|
|
94
|
+
* and the goal resume/pause loop deadlocks.
|
|
95
|
+
*/
|
|
96
|
+
const DEFAULT_MESSAGE_SOURCES = ['user', 'goal']
|
|
90
97
|
|
|
91
98
|
function stringList(value, field, fallback) {
|
|
92
99
|
if (value === undefined) return [...fallback]
|
|
@@ -153,12 +160,15 @@ export function hasAnchoredReasoning(content) {
|
|
|
153
160
|
}
|
|
154
161
|
|
|
155
162
|
/**
|
|
156
|
-
* Whether one pre-step message
|
|
157
|
-
*
|
|
163
|
+
* Whether one pre-step message belongs to a whitelisted source kind. The
|
|
164
|
+
* configured whitelist alone decides; injected kinds and source-less seed
|
|
165
|
+
* messages never pass unless explicitly named. (Before issue #578 this also
|
|
166
|
+
* hardcoded `kind === 'user'`, so no whitelist entry could ever admit a
|
|
167
|
+
* goal auto-round and `/goal` sessions deadlocked in phase 1.)
|
|
158
168
|
*/
|
|
159
169
|
function isAllowedMessage(message, allowedSources) {
|
|
160
170
|
const kind = message.source?.kind
|
|
161
|
-
return kind
|
|
171
|
+
return kind !== undefined && allowedSources.has(kind)
|
|
162
172
|
}
|
|
163
173
|
|
|
164
174
|
/** Whether one pre-step message belongs to a deferred injection kind. */
|
|
@@ -276,7 +286,7 @@ function stateFor(session) {
|
|
|
276
286
|
* Code Mode presentation is disposed so the next assembly sees the native
|
|
277
287
|
* catalog and the phase-1 filter can narrow it again.
|
|
278
288
|
*/
|
|
279
|
-
function resetToControlled(state) {
|
|
289
|
+
function resetToControlled(state, session) {
|
|
280
290
|
if (typeof state.presentationDisposer === 'function') {
|
|
281
291
|
try {
|
|
282
292
|
state.presentationDisposer()
|
|
@@ -285,6 +295,10 @@ function resetToControlled(state) {
|
|
|
285
295
|
// next promotion re-declares Code Mode anyway.
|
|
286
296
|
}
|
|
287
297
|
state.presentationDisposer = undefined
|
|
298
|
+
const agent = session !== undefined ? agentBySession.get(session) : undefined
|
|
299
|
+
if (agent?.ctx && typeof agent.ctx.emit === 'function') {
|
|
300
|
+
agent.ctx.emit('tools/presentation-changed', { mode: 'native', session: session?.id })
|
|
301
|
+
}
|
|
288
302
|
}
|
|
289
303
|
state.promoted = false
|
|
290
304
|
state.toolCalled = false
|
|
@@ -305,13 +319,20 @@ function resetToControlled(state) {
|
|
|
305
319
|
*/
|
|
306
320
|
function applyPresentation(agent, state, policy) {
|
|
307
321
|
if (state.presentationApplied || policy.promotedPresentation !== 'code') return
|
|
308
|
-
|
|
309
|
-
|
|
322
|
+
const tools = agent?.ctx?.tools
|
|
323
|
+
// Latch only after the switch really happened: without a tools view there
|
|
324
|
+
// is nothing to present, and latching early would skip Code Mode forever.
|
|
310
325
|
if (tools === undefined) return
|
|
311
326
|
// The disposer restores the deployment-default (native) presentation; it is
|
|
312
327
|
// kept on the state so a post-compaction reset can release Code Mode and
|
|
313
328
|
// let the phase-1 catalog filter see the native tool list again.
|
|
314
329
|
state.presentationDisposer = tools.presentAs('code')
|
|
330
|
+
state.presentationApplied = true
|
|
331
|
+
// #1128: Broadcast presentation switch so external discipline / analysis
|
|
332
|
+
// plugins decouple presentation mode from tool failure detection.
|
|
333
|
+
if (typeof agent?.ctx?.emit === 'function') {
|
|
334
|
+
agent.ctx.emit('tools/presentation-changed', { mode: 'code', session: agent.session?.id })
|
|
335
|
+
}
|
|
315
336
|
}
|
|
316
337
|
|
|
317
338
|
/**
|
|
@@ -333,7 +354,11 @@ function decidePromotion(state, config) {
|
|
|
333
354
|
|
|
334
355
|
/** Scan newly appended session events and update promotion state. */
|
|
335
356
|
function scanEvents(state, session) {
|
|
336
|
-
const events = session
|
|
357
|
+
const events = Array.isArray(session?.events)
|
|
358
|
+
? session.events
|
|
359
|
+
: typeof session?.snapshotEvents === 'function'
|
|
360
|
+
? session.snapshotEvents()
|
|
361
|
+
: []
|
|
337
362
|
for (; state.next < events.length; state.next += 1) {
|
|
338
363
|
const event = events[state.next]
|
|
339
364
|
if (event === undefined) continue
|
|
@@ -343,7 +368,7 @@ function scanEvents(state, session) {
|
|
|
343
368
|
// past this boundary (the `next` pointer stays, so events before the
|
|
344
369
|
// boundary never re-promote). Handled inside the scan so cold starts
|
|
345
370
|
// reconstruct the same phase from the durable log.
|
|
346
|
-
resetToControlled(state)
|
|
371
|
+
resetToControlled(state, session)
|
|
347
372
|
} else if (event.type === 'tool/call') {
|
|
348
373
|
state.toolCalled = true
|
|
349
374
|
} else if (event.type === 'step/start') {
|
|
@@ -452,7 +477,7 @@ export function apply(ctx, config) {
|
|
|
452
477
|
// start reconstructs the same controlled phase from the durable log.
|
|
453
478
|
ctx.on('session/event', (session, event) => {
|
|
454
479
|
if (event.type === 'compaction/end') {
|
|
455
|
-
resetToControlled(stateFor(session))
|
|
480
|
+
resetToControlled(stateFor(session), session)
|
|
456
481
|
return
|
|
457
482
|
}
|
|
458
483
|
if (event.type !== 'step/end' && event.type !== 'turn/end') return
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devflow-core/dsh-devflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "DevFlow for DeepSeek Harness: devflow-2 agent preset + skills + commands + verification scripts, synced into ~/.dsh on host startup.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|