@try-works/dsh-recursive-mode 0.1.5 → 0.1.7
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/bootstrap.d.ts +6 -3
- package/lib/client/board.d.ts +4 -1
- package/lib/client/contract.d.ts +16 -13
- package/lib/client/index.d.ts +1 -1
- package/lib/client/open-state.d.ts +20 -0
- package/lib/client/slots.d.ts +13 -1
- package/lib/client.js +160 -71
- package/lib/index.js +3391 -906
- package/lib/runtime.d.ts +12 -5
- package/lib/ts-lint.d.ts +205 -0
- package/package.json +1 -1
- package/src/bootstrap.ts +338 -331
- package/src/client/board.tsx +0 -0
- package/src/client/contract.ts +19 -22
- package/src/client/index.ts +1 -1
- package/src/client/open-state.ts +46 -0
- package/src/client/slots.ts +67 -33
- package/src/commands.ts +11 -0
- package/src/index.ts +238 -228
- package/src/runtime.ts +19 -23
- package/src/ts-lint.ts +2110 -0
- package/references/scripts/__pycache__/recursive_phase_rules.cpython-314.pyc +0 -0
- package/references/scripts/lint-recursive-run.ps1 +0 -25
- package/references/scripts/lint-recursive-run.py +0 -2870
- package/references/scripts/recursive-closeout.ps1 +0 -38
- package/references/scripts/recursive-closeout.py +0 -541
- package/references/scripts/recursive-init.ps1 +0 -515
- package/references/scripts/recursive-init.py +0 -356
- package/references/scripts/recursive-lock.ps1 +0 -26
- package/references/scripts/recursive-lock.py +0 -302
- package/references/scripts/recursive-review-bundle.ps1 +0 -58
- package/references/scripts/recursive-review-bundle.py +0 -503
- package/references/scripts/recursive-router-cli-configure.ps1 +0 -2
- package/references/scripts/recursive-router-cli-configure.py +0 -9
- package/references/scripts/recursive-router-cli-init.ps1 +0 -2
- package/references/scripts/recursive-router-cli-init.py +0 -9
- package/references/scripts/recursive-router-cli-invoke.ps1 +0 -2
- package/references/scripts/recursive-router-cli-invoke.py +0 -9
- package/references/scripts/recursive-router-cli-probe.ps1 +0 -2
- package/references/scripts/recursive-router-cli-probe.py +0 -9
- package/references/scripts/recursive-router-cli-resolve.ps1 +0 -2
- package/references/scripts/recursive-router-cli-resolve.py +0 -9
- package/references/scripts/recursive-router-cli-validate.ps1 +0 -2
- package/references/scripts/recursive-router-cli-validate.py +0 -9
- package/references/scripts/recursive-router-configure.ps1 +0 -27
- package/references/scripts/recursive-router-configure.py +0 -74
- package/references/scripts/recursive-router-init.ps1 +0 -17
- package/references/scripts/recursive-router-init.py +0 -29
- package/references/scripts/recursive-router-invoke.ps1 +0 -47
- package/references/scripts/recursive-router-invoke.py +0 -103
- package/references/scripts/recursive-router-probe.ps1 +0 -25
- package/references/scripts/recursive-router-probe.py +0 -44
- package/references/scripts/recursive-router-resolve.ps1 +0 -26
- package/references/scripts/recursive-router-resolve.py +0 -46
- package/references/scripts/recursive-router-validate.ps1 +0 -17
- package/references/scripts/recursive-router-validate.py +0 -27
- package/references/scripts/recursive-status.ps1 +0 -23
- package/references/scripts/recursive-status.py +0 -2124
- package/references/scripts/recursive-subagent-action.ps1 +0 -98
- package/references/scripts/recursive-subagent-action.py +0 -197
- package/references/scripts/recursive-training-extract.ps1 +0 -23
- package/references/scripts/recursive-training-extract.py +0 -99
- package/references/scripts/recursive-training-grpo.ps1 +0 -52
- package/references/scripts/recursive-training-grpo.py +0 -1341
- package/references/scripts/recursive-training-loader.ps1 +0 -78
- package/references/scripts/recursive-training-loader.py +0 -558
- package/references/scripts/recursive-training-mcp.ps1 +0 -29
- package/references/scripts/recursive-training-mcp.py +0 -261
- package/references/scripts/recursive-training-phase8-trigger.ps1 +0 -55
- package/references/scripts/recursive-training-phase8-trigger.py +0 -137
- package/references/scripts/recursive-training-sync.ps1 +0 -29
- package/references/scripts/recursive-training-sync.py +0 -183
- package/references/scripts/recursive_phase_rules.py +0 -367
- package/references/scripts/recursive_router_cli_lib.py +0 -2
- package/references/scripts/recursive_router_lib.py +0 -2282
- package/references/scripts/verify-locks.ps1 +0 -25
- package/references/scripts/verify-locks.py +0 -353
- package/scripts/__pycache__/lint-recursive-run.cpython-314.pyc +0 -0
- package/scripts/__pycache__/recursive_phase_rules.cpython-314.pyc +0 -0
- package/scripts/install-recursive-mode.ps1 +0 -956
- package/scripts/install-recursive-mode.py +0 -750
- package/scripts/lint-recursive-run.py +0 -2870
- package/scripts/recursive-closeout.py +0 -541
- package/scripts/recursive-init.py +0 -356
- package/scripts/recursive-lock.py +0 -302
- package/scripts/recursive-status.py +0 -2124
- package/scripts/recursive_phase_rules.py +0 -367
- package/scripts/recursive_router_lib.py +0 -2282
- package/scripts/verify-locks.py +0 -353
package/src/index.ts
CHANGED
|
@@ -1,228 +1,238 @@
|
|
|
1
|
-
import { type Context } from '@deepseek-ai/cordis'
|
|
2
|
-
import { createUserMessage } from '@deepseek-ai/dsh-llm'
|
|
3
|
-
import { existsSync } from 'node:fs'
|
|
4
|
-
import { join } from 'node:path'
|
|
5
|
-
import { RecursiveRuntime } from './runtime.ts'
|
|
6
|
-
import { createRecursiveStatusTool } from './recursive_status.tool.ts'
|
|
7
|
-
import { createRecursiveInitTool } from './recursive_init.tool.ts'
|
|
8
|
-
import { createRecursiveLockTool } from './recursive_lock.tool.ts'
|
|
9
|
-
import { createRecursiveLintTool } from './recursive_lint.tool.ts'
|
|
10
|
-
import { createRecursiveCloseoutTool } from './recursive_closeout.tool.ts'
|
|
11
|
-
import { createRecursiveScratchTool } from './recursive_scratch.tool.ts'
|
|
12
|
-
import { registerRecursiveCommand } from './commands.ts'
|
|
13
|
-
import { evaluateToolGuard } from './enforcement.ts'
|
|
14
|
-
import { renderRecursivePolicy } from './policy.ts'
|
|
15
|
-
import { fsPolicyIntent } from './fs-intent.ts'
|
|
16
|
-
import { snapshotWorkspace } from './snapshot.ts'
|
|
17
|
-
import { mountRecursiveRoutesOnce, makeRecursiveRoutes, type RecursiveRouteHost } from './live-route.ts'
|
|
18
|
-
import { enumerateRuns } from './bootstrap.ts'
|
|
19
|
-
import { getNextLegalPhase, getLockStatus } from './lock.ts'
|
|
20
|
-
import { phaseLintRulesMessage } from './phase-rules.ts'
|
|
21
|
-
|
|
22
|
-
export const name = '@try-works/dsh-recursive-mode'
|
|
23
|
-
|
|
24
|
-
export { RecursiveRuntime } from './runtime.ts'
|
|
25
|
-
export { createRecursiveStatusTool } from './recursive_status.tool.ts'
|
|
26
|
-
export { createRecursiveInitTool } from './recursive_init.tool.ts'
|
|
27
|
-
export { createRecursiveLockTool } from './recursive_lock.tool.ts'
|
|
28
|
-
export { createRecursiveLintTool } from './recursive_lint.tool.ts'
|
|
29
|
-
export { createRecursiveCloseoutTool } from './recursive_closeout.tool.ts'
|
|
30
|
-
export { createRecursiveScratchTool } from './recursive_scratch.tool.ts'
|
|
31
|
-
export * from './status.ts'
|
|
32
|
-
export {
|
|
33
|
-
PHASE_SEQUENCE,
|
|
34
|
-
OPTIONAL_PHASES,
|
|
35
|
-
normalizeForLockHash,
|
|
36
|
-
lockHashFromContent,
|
|
37
|
-
phaseIndex,
|
|
38
|
-
isCoreArtifact,
|
|
39
|
-
getPrerequisites,
|
|
40
|
-
getLockStatus,
|
|
41
|
-
getPrerequisiteBlockers,
|
|
42
|
-
receiptPath,
|
|
43
|
-
readReceipt,
|
|
44
|
-
writeReceipt,
|
|
45
|
-
invalidateReceipt,
|
|
46
|
-
getStaleDownstreamPhases,
|
|
47
|
-
getNextLegalPhase,
|
|
48
|
-
getAllStaleReceipts,
|
|
49
|
-
validateChain,
|
|
50
|
-
} from './lock.ts'
|
|
51
|
-
export type {
|
|
52
|
-
LockReceipt,
|
|
53
|
-
LockStatus,
|
|
54
|
-
PrerequisiteBlocker,
|
|
55
|
-
StaleDownstream,
|
|
56
|
-
ChainPhaseResult,
|
|
57
|
-
LockChainResult,
|
|
58
|
-
} from './lock.ts'
|
|
59
|
-
export * from './run.ts'
|
|
60
|
-
export * from './review.ts'
|
|
61
|
-
export * from './handoff.ts'
|
|
62
|
-
export * from './router.ts'
|
|
63
|
-
export * from './delegation.ts'
|
|
64
|
-
export * from './lifecycle.ts'
|
|
65
|
-
export * from './enforcement.ts'
|
|
66
|
-
export * from './policy.ts'
|
|
67
|
-
export * from './snapshot.ts'
|
|
68
|
-
export * from './live-route.ts'
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Bundle plugin entry. The Loader activates this row once `tools` is available
|
|
72
|
-
* (`inject` below); the RecursiveRuntime service is constructed directly so it
|
|
73
|
-
* is provided on `ctx.recursive` for the lifetime of this fiber, and the
|
|
74
|
-
* read-path tools (status/init/lock/lint) are registered through it (R2/R4).
|
|
75
|
-
*/
|
|
76
|
-
export const inject = ['tools']
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Plugin entry (SP2 R1). Stage A (mount-time, this apply): register the
|
|
80
|
-
* isolated ctx.recursive service + the recursive_* tools + the /recursive
|
|
81
|
-
* command + the recursive:policy prompt section + the LIVE board/strip route
|
|
82
|
-
* (HTTP state + SSE), served per-workspace from the filesystem fold. No
|
|
83
|
-
* repo/run work and NO session-event emission here: zero recursive/* events
|
|
84
|
-
* are ever appended (resume-crash fix), and the board reads the live fs route.
|
|
85
|
-
*/
|
|
86
|
-
export function apply(ctx: Context, config?: { shellOnly?: boolean; repoRoot?: string }) {
|
|
87
|
-
// R4 shell split (02-to-be-plan.addendum-r4-r2-mount-resolution.md): the
|
|
88
|
-
// global bare-name row in cordis.patch.yml mounts with config.shellOnly=true
|
|
89
|
-
// to expose ONLY the client bundle for client discovery. It must register
|
|
90
|
-
// NOTHING on the server root — no tools, no /recursive command, no
|
|
91
|
-
// recursive:policy, no projection (BUG 4 always-on leak). The full server
|
|
92
|
-
// surface is mounted ONLY by the recursive preset's isolated recursive-realm,
|
|
93
|
-
// whose rows carry no config (shellOnly undefined).
|
|
94
|
-
if (config?.shellOnly) return
|
|
95
|
-
ctx.effect(function* () {
|
|
96
|
-
// Workspace registry: optional host service (durable). Access via ctx.get —
|
|
97
|
-
// property access requires inject and would fail boot when undeclared.
|
|
98
|
-
// Resolve the control-plane root strictly from the session agent's cwd.
|
|
99
|
-
const workspaceRegistry = ctx.get('workspaceRegistry') as never
|
|
100
|
-
const recursive = new RecursiveRuntime(ctx, { repoRoot: config?.repoRoot ?? process.cwd(), workspaceRegistry })
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
ctx.tools.register(
|
|
105
|
-
ctx.tools.register(
|
|
106
|
-
ctx.tools.register(
|
|
107
|
-
ctx.tools.register(
|
|
108
|
-
ctx.tools.register(
|
|
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
|
-
//
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
if (decision.kind === '
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
//
|
|
170
|
-
//
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if (!
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
1
|
+
import { type Context } from '@deepseek-ai/cordis'
|
|
2
|
+
import { createUserMessage } from '@deepseek-ai/dsh-llm'
|
|
3
|
+
import { existsSync } from 'node:fs'
|
|
4
|
+
import { join } from 'node:path'
|
|
5
|
+
import { RecursiveRuntime } from './runtime.ts'
|
|
6
|
+
import { createRecursiveStatusTool } from './recursive_status.tool.ts'
|
|
7
|
+
import { createRecursiveInitTool } from './recursive_init.tool.ts'
|
|
8
|
+
import { createRecursiveLockTool } from './recursive_lock.tool.ts'
|
|
9
|
+
import { createRecursiveLintTool } from './recursive_lint.tool.ts'
|
|
10
|
+
import { createRecursiveCloseoutTool } from './recursive_closeout.tool.ts'
|
|
11
|
+
import { createRecursiveScratchTool } from './recursive_scratch.tool.ts'
|
|
12
|
+
import { registerRecursiveCommand } from './commands.ts'
|
|
13
|
+
import { evaluateToolGuard } from './enforcement.ts'
|
|
14
|
+
import { renderRecursivePolicy } from './policy.ts'
|
|
15
|
+
import { fsPolicyIntent } from './fs-intent.ts'
|
|
16
|
+
import { snapshotWorkspace } from './snapshot.ts'
|
|
17
|
+
import { mountRecursiveRoutesOnce, makeRecursiveRoutes, type RecursiveRouteHost } from './live-route.ts'
|
|
18
|
+
import { enumerateRuns, stageBWorkflowInit } from './bootstrap.ts'
|
|
19
|
+
import { getNextLegalPhase, getLockStatus } from './lock.ts'
|
|
20
|
+
import { phaseLintRulesMessage } from './phase-rules.ts'
|
|
21
|
+
|
|
22
|
+
export const name = '@try-works/dsh-recursive-mode'
|
|
23
|
+
|
|
24
|
+
export { RecursiveRuntime } from './runtime.ts'
|
|
25
|
+
export { createRecursiveStatusTool } from './recursive_status.tool.ts'
|
|
26
|
+
export { createRecursiveInitTool } from './recursive_init.tool.ts'
|
|
27
|
+
export { createRecursiveLockTool } from './recursive_lock.tool.ts'
|
|
28
|
+
export { createRecursiveLintTool } from './recursive_lint.tool.ts'
|
|
29
|
+
export { createRecursiveCloseoutTool } from './recursive_closeout.tool.ts'
|
|
30
|
+
export { createRecursiveScratchTool } from './recursive_scratch.tool.ts'
|
|
31
|
+
export * from './status.ts'
|
|
32
|
+
export {
|
|
33
|
+
PHASE_SEQUENCE,
|
|
34
|
+
OPTIONAL_PHASES,
|
|
35
|
+
normalizeForLockHash,
|
|
36
|
+
lockHashFromContent,
|
|
37
|
+
phaseIndex,
|
|
38
|
+
isCoreArtifact,
|
|
39
|
+
getPrerequisites,
|
|
40
|
+
getLockStatus,
|
|
41
|
+
getPrerequisiteBlockers,
|
|
42
|
+
receiptPath,
|
|
43
|
+
readReceipt,
|
|
44
|
+
writeReceipt,
|
|
45
|
+
invalidateReceipt,
|
|
46
|
+
getStaleDownstreamPhases,
|
|
47
|
+
getNextLegalPhase,
|
|
48
|
+
getAllStaleReceipts,
|
|
49
|
+
validateChain,
|
|
50
|
+
} from './lock.ts'
|
|
51
|
+
export type {
|
|
52
|
+
LockReceipt,
|
|
53
|
+
LockStatus,
|
|
54
|
+
PrerequisiteBlocker,
|
|
55
|
+
StaleDownstream,
|
|
56
|
+
ChainPhaseResult,
|
|
57
|
+
LockChainResult,
|
|
58
|
+
} from './lock.ts'
|
|
59
|
+
export * from './run.ts'
|
|
60
|
+
export * from './review.ts'
|
|
61
|
+
export * from './handoff.ts'
|
|
62
|
+
export * from './router.ts'
|
|
63
|
+
export * from './delegation.ts'
|
|
64
|
+
export * from './lifecycle.ts'
|
|
65
|
+
export * from './enforcement.ts'
|
|
66
|
+
export * from './policy.ts'
|
|
67
|
+
export * from './snapshot.ts'
|
|
68
|
+
export * from './live-route.ts'
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Bundle plugin entry. The Loader activates this row once `tools` is available
|
|
72
|
+
* (`inject` below); the RecursiveRuntime service is constructed directly so it
|
|
73
|
+
* is provided on `ctx.recursive` for the lifetime of this fiber, and the
|
|
74
|
+
* read-path tools (status/init/lock/lint) are registered through it (R2/R4).
|
|
75
|
+
*/
|
|
76
|
+
export const inject = ['tools']
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Plugin entry (SP2 R1). Stage A (mount-time, this apply): register the
|
|
80
|
+
* isolated ctx.recursive service + the recursive_* tools + the /recursive
|
|
81
|
+
* command + the recursive:policy prompt section + the LIVE board/strip route
|
|
82
|
+
* (HTTP state + SSE), served per-workspace from the filesystem fold. No
|
|
83
|
+
* repo/run work and NO session-event emission here: zero recursive/* events
|
|
84
|
+
* are ever appended (resume-crash fix), and the board reads the live fs route.
|
|
85
|
+
*/
|
|
86
|
+
export function apply(ctx: Context, config?: { shellOnly?: boolean; repoRoot?: string }) {
|
|
87
|
+
// R4 shell split (02-to-be-plan.addendum-r4-r2-mount-resolution.md): the
|
|
88
|
+
// global bare-name row in cordis.patch.yml mounts with config.shellOnly=true
|
|
89
|
+
// to expose ONLY the client bundle for client discovery. It must register
|
|
90
|
+
// NOTHING on the server root — no tools, no /recursive command, no
|
|
91
|
+
// recursive:policy, no projection (BUG 4 always-on leak). The full server
|
|
92
|
+
// surface is mounted ONLY by the recursive preset's isolated recursive-realm,
|
|
93
|
+
// whose rows carry no config (shellOnly undefined).
|
|
94
|
+
if (config?.shellOnly) return
|
|
95
|
+
ctx.effect(function* () {
|
|
96
|
+
// Workspace registry: optional host service (durable). Access via ctx.get —
|
|
97
|
+
// property access requires inject and would fail boot when undeclared.
|
|
98
|
+
// Resolve the control-plane root strictly from the session agent's cwd.
|
|
99
|
+
const workspaceRegistry = ctx.get('workspaceRegistry') as never
|
|
100
|
+
const recursive = new RecursiveRuntime(ctx, { repoRoot: config?.repoRoot ?? process.cwd(), workspaceRegistry })
|
|
101
|
+
|
|
102
|
+
const repairedRoots = new Set<string>()
|
|
103
|
+
const disposers = [
|
|
104
|
+
ctx.tools.register(createRecursiveStatusTool(recursive)),
|
|
105
|
+
ctx.tools.register(createRecursiveInitTool(recursive)),
|
|
106
|
+
ctx.tools.register(createRecursiveLockTool(recursive)),
|
|
107
|
+
ctx.tools.register(createRecursiveLintTool(recursive)),
|
|
108
|
+
ctx.tools.register(createRecursiveCloseoutTool(recursive)),
|
|
109
|
+
ctx.tools.register(createRecursiveScratchTool(recursive)),
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
// /recursive command (R4): preset-scoped registration, workspace-scoped dispatch.
|
|
113
|
+
const commands = ctx.get('commands') as { register: (def: unknown) => () => void } | undefined
|
|
114
|
+
if (commands) {
|
|
115
|
+
disposers.push(registerRecursiveCommand({ commands } as never, recursive))
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// recursive:policy prompt section (Phase C R5): workspace-scoped behavior +
|
|
119
|
+
// current-phase contract rendered from folded state + enforcement config.
|
|
120
|
+
const systemPrompt = ctx.get('systemPrompt') as { section: (def: unknown) => () => void } | undefined
|
|
121
|
+
if (systemPrompt) {
|
|
122
|
+
disposers.push(systemPrompt.section({
|
|
123
|
+
name: 'recursive:policy',
|
|
124
|
+
order: 55,
|
|
125
|
+
text: (context: unknown) => {
|
|
126
|
+
const agent = (context as { agent?: { session?: { header?: { cwd?: string } } } } | undefined)?.agent
|
|
127
|
+
if (!agent) return ''
|
|
128
|
+
// SP3 R5 policy-render fix: derive intent from the FILESYSTEM, not the
|
|
129
|
+
// retired recursive/phase-intent session event (zero-emission removed
|
|
130
|
+
// the emitter, so detectTransitionIntent was ALWAYS null and this
|
|
131
|
+
// section rendered ''). Pure read-only fs folding; no recursive/*
|
|
132
|
+
// events are appended.
|
|
133
|
+
const intent = fsPolicyIntent(agent, workspaceRegistry as never)
|
|
134
|
+
if (!intent) return ''
|
|
135
|
+
return renderRecursivePolicy({ worktreeRoot: intent.worktreeRoot, runId: intent.runId, config: recursive.enforcementConfig })
|
|
136
|
+
},
|
|
137
|
+
}))
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Phase C R3 (Layer 1, agent/pre-step proactive intent gate) is RETIRED
|
|
141
|
+
// under zero-emission (SP2 R1): its only signal was the recursive/phase-intent
|
|
142
|
+
// session event, whose emitter is now deleted, and it was ADVISORY by default
|
|
143
|
+
// (it never rejected; its sole side effect was the now-removed emission).
|
|
144
|
+
// Enforcement is preserved where it actually bites: Layer 2 (tools/pre-execute
|
|
145
|
+
// inspects the REAL tool call args below) plus the recursive_lock tool's own
|
|
146
|
+
// prerequisite validation in lockArtifact — strictly more reliable than the
|
|
147
|
+
// heuristic intent scan. See 03-implementation-summary.addendum-r1-*.md.
|
|
148
|
+
|
|
149
|
+
// Phase C R4: tools/pre-execute surgical guards (Layer 2, caller of the
|
|
150
|
+
// transition set). Scope-filtered to the active run's worktree.
|
|
151
|
+
const toolRuntime = ctx as unknown as { on?: (event: string, listener: (payload: unknown, next?: unknown) => unknown) => () => void }
|
|
152
|
+
if (toolRuntime.on) {
|
|
153
|
+
disposers.push(toolRuntime.on('tools/pre-execute', (payload, next) => {
|
|
154
|
+
const exec = payload as { name?: string; arguments?: unknown; agent?: { session?: { header?: { cwd?: string } } } | null } | null
|
|
155
|
+
if (!exec?.name) return typeof next === 'function' ? next() : { kind: 'allow' }
|
|
156
|
+
// B3: per-call root is the session cwd (authoritative when the registry is
|
|
157
|
+
// absent), never process.cwd().
|
|
158
|
+
const root = exec?.agent?.session?.header?.cwd ?? ''
|
|
159
|
+
const decision = evaluateToolGuard(exec as never, root, '', recursive.enforcementConfig.toolGuards)
|
|
160
|
+
if (decision.kind === 'allow') return typeof next === 'function' ? next() : { kind: 'allow' }
|
|
161
|
+
if (decision.kind === 'deny') return decision
|
|
162
|
+
// ask -> in advisory we let it through with a warn (the approval seam is Phase D)
|
|
163
|
+
}))
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// SP3 R5: agent/pre-step lint-rules injection (pre-step contract verified: the
|
|
167
|
+
// listener returns { kind: 'enter', messages: [...messages, injected] } and the
|
|
168
|
+
// returned array REPLACES the default [...claimed, context]). When the session's
|
|
169
|
+
// control-plane root has an active recursive run whose current phase doc is
|
|
170
|
+
// DRAFT, prepend a compact system-reminder with THAT phase's required sections +
|
|
171
|
+
// gates. Pure fs read (zero-emission): never appends recursive/* events.
|
|
172
|
+
const agentRuntime = ctx as unknown as { on?: (event: string, listener: (payload: unknown, next?: unknown) => unknown) => () => void }
|
|
173
|
+
if (agentRuntime.on) {
|
|
174
|
+
disposers.push(agentRuntime.on('agent/pre-step', async (payload, next) => {
|
|
175
|
+
const p = payload as {
|
|
176
|
+
messages?: Array<{ content: Array<{ type: string; text?: string }> }>,
|
|
177
|
+
agent?: { session?: { header?: { cwd?: string } } } | null,
|
|
178
|
+
} | null
|
|
179
|
+
const messages = p?.messages ?? []
|
|
180
|
+
const agent = p?.agent ?? null
|
|
181
|
+
const cwd = agent?.session?.header?.cwd ?? ''
|
|
182
|
+
// delegate first so later listeners keep veto power, then fold ours on
|
|
183
|
+
if (typeof next === 'function') await next()
|
|
184
|
+
if (!cwd) return { kind: 'enter', messages } as const
|
|
185
|
+
const root = await recursive.resolveRootForRoute(undefined, cwd, undefined)
|
|
186
|
+
if (!root) return { kind: 'enter', messages } as const
|
|
187
|
+
// R3/R6 (run 09): idempotent scaffold REPAIR on session-start (new AND
|
|
188
|
+
// resume). bootstrapScaffold is upsert-only: it adds missing control-plane
|
|
189
|
+
// files/dirs + re-upserts marked blocks, NEVER overwriting user content or
|
|
190
|
+
// touching run/ artifacts (in-flight 02-to-be-plan etc. are preserved).
|
|
191
|
+
// Guarded once per root so repeated pre-steps are cheap no-ops.
|
|
192
|
+
if (!repairedRoots.has(root)) {
|
|
193
|
+
repairedRoots.add(root)
|
|
194
|
+
stageBWorkflowInit({ root, source: 'resume' })
|
|
195
|
+
}
|
|
196
|
+
const runs = enumerateRuns(root)
|
|
197
|
+
if (runs.length === 0) return { kind: 'enter', messages } as const
|
|
198
|
+
const runId = runs[runs.length - 1]
|
|
199
|
+
const runDir = join(root, '.recursive', 'run', runId)
|
|
200
|
+
if (!existsSync(runDir)) return { kind: 'enter', messages } as const
|
|
201
|
+
const phase = getNextLegalPhase(runDir)
|
|
202
|
+
if (!phase) return { kind: 'enter', messages } as const
|
|
203
|
+
// only inject when the phase doc is DRAFT (not locked/missing).
|
|
204
|
+
const phasePath = join(runDir, phase)
|
|
205
|
+
const status = existsSync(phasePath) ? getLockStatus(phasePath) : null
|
|
206
|
+
if (status !== 'DRAFT') return { kind: 'enter', messages } as const
|
|
207
|
+
const reminder = phaseLintRulesMessage(phase)
|
|
208
|
+
return {
|
|
209
|
+
kind: 'enter',
|
|
210
|
+
messages: [...messages, createUserMessage({ content: [{ type: 'text', text: reminder }], source: { kind: 'plugin', plugin: '@try-works/dsh-recursive-mode' } })],
|
|
211
|
+
} as const
|
|
212
|
+
}))
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Phase C R8: fs/observed lock-tamper WARNINGS are served to the board via the
|
|
216
|
+
// mountOnce-global: apply() runs per-session, but the route must register
|
|
217
|
+
// exactly once (WebServer.register throws on duplicate kind+path) and serve
|
|
218
|
+
// PER-WORKSPACE state. No-op when the host composes no webServer (headless).
|
|
219
|
+
const webServer = ctx.get('webServer') as
|
|
220
|
+
| { register: (route: { kind: string; path: string; handler: unknown }) => () => void }
|
|
221
|
+
| undefined
|
|
222
|
+
const sessionsStore = ctx.get('sessions') as
|
|
223
|
+
| { get?: (id: string) => { header?: { cwd?: string } } | undefined }
|
|
224
|
+
| undefined
|
|
225
|
+
if (webServer) {
|
|
226
|
+
const host: RecursiveRouteHost = {
|
|
227
|
+
// sessionId PRIMARY: the host resolves cwd from the attached session header;
|
|
228
|
+
// the client-passed cwd is a fallback hint (hydration / headless).
|
|
229
|
+
resolveRoot: async (sessionId: string | undefined, cwd: string) => recursive.resolveRootForRoute(sessionId, cwd, sessionsStore),
|
|
230
|
+
snapshot: async (root: string) => snapshotWorkspace(root),
|
|
231
|
+
revision: () => 1,
|
|
232
|
+
}
|
|
233
|
+
disposers.push(mountRecursiveRoutesOnce('@try-works/dsh-recursive-mode', () => makeRecursiveRoutes(host), webServer))
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
yield () => { for (const d of disposers) d() }
|
|
237
|
+
})
|
|
238
|
+
}
|
package/src/runtime.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Service, type Context } from '@deepseek-ai/cordis'
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
3
|
-
import { execFileSync } from 'node:child_process'
|
|
4
3
|
import { join } from 'node:path'
|
|
5
|
-
import {
|
|
4
|
+
import { lintRun } from './ts-lint.ts'
|
|
6
5
|
import { requirementsContent, worktreeContent, laterPhaseContent, detectGitContext, RUN_SCAFFOLD_DIRS, type GitContext } from './init-templates.ts'
|
|
7
6
|
import { foldRun, resolveRunDir } from './status.ts'
|
|
8
7
|
import {
|
|
@@ -464,12 +463,11 @@ export class RecursiveRuntime extends Service {
|
|
|
464
463
|
}
|
|
465
464
|
|
|
466
465
|
/**
|
|
467
|
-
* Lint an artifact with FULL canonical parity:
|
|
468
|
-
*
|
|
469
|
-
*
|
|
466
|
+
* Lint an artifact with FULL canonical parity: runs the in-process ts-lint
|
|
467
|
+
* port (ts-lint.ts `lintRun`) against the whole run and filters the verdict
|
|
468
|
+
* lines to the requested artifact. `errors` = FAIL lines, `warnings` =
|
|
470
469
|
* WARN lines, `passed` = no FAIL (WARN-only passes, matching the canonical
|
|
471
|
-
* non-strict verdict).
|
|
472
|
-
* script is unavailable (e.g. bundled lib without references/).
|
|
470
|
+
* non-strict verdict).
|
|
473
471
|
*/
|
|
474
472
|
async lintArtifact(runId: string, artifact?: string, agent?: { session?: { header?: { cwd?: string } } } | null): Promise<LintArtifactResult> {
|
|
475
473
|
const root = await this.resolveRootFor(agent)
|
|
@@ -480,22 +478,20 @@ export class RecursiveRuntime extends Service {
|
|
|
480
478
|
if (!existsSync(artifactPath)) {
|
|
481
479
|
return { artifact: target, runId, errors: ['Artifact not found'], warnings: [], passed: false }
|
|
482
480
|
}
|
|
483
|
-
const
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
// script failed to run entirely — fall through to structural check
|
|
498
|
-
}
|
|
481
|
+
const result = lintRun(root, runId)
|
|
482
|
+
const stdout = [...result.errors, ...result.warnings].join('\n')
|
|
483
|
+
return parseLintOutput(stdout, target, runId)
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/** Legacy structural fallback (kept for type reference; lintArtifact uses lintRun). */
|
|
487
|
+
async lintArtifactStructuralFallback(runId: string, artifact: string, agent?: { session?: { header?: { cwd?: string } } } | null): Promise<LintArtifactResult> {
|
|
488
|
+
const root = await this.resolveRootFor(agent)
|
|
489
|
+
if (!root) throw new Error('cannot resolve workspace control-plane root for this session')
|
|
490
|
+
const runDir = join(root, '.recursive', 'run', runId)
|
|
491
|
+
const target = artifact ?? getNextLegalPhase(runDir) ?? '00-requirements.md'
|
|
492
|
+
const artifactPath = join(runDir, target)
|
|
493
|
+
if (!existsSync(artifactPath)) {
|
|
494
|
+
return { artifact: target, runId, errors: ['Artifact not found'], warnings: [], passed: false }
|
|
499
495
|
}
|
|
500
496
|
// Structural fallback (no vendored script available).
|
|
501
497
|
const content = readFileSync(artifactPath, 'utf8')
|