dsh-logicprobe 0.6.8 → 0.7.1
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.en-US.md +5 -2
- package/README.md +5 -2
- package/lib/client.js +212 -0
- package/lib/engine.js +127 -2
- package/lib/index.js +342 -304
- package/lib/tool.js +1 -1
- package/lib/types/engine.d.ts +22 -0
- package/lib/types/index.d.ts +60 -54
- package/package.json +32 -16
- package/skills/logicprobe/references/__pycache__/logicprobe-engine.cpython-310.pyc +0 -0
- package/skills/logicprobe/references/dsh-model-schema.md +322 -266
- package/skills/logicprobe/references/logicprobe-engine.py +3169 -3028
- package/src/client.js +212 -0
- package/src/engine.ts +2714 -2568
- package/src/index.ts +56 -5
- package/src/tool.ts +61 -61
package/src/index.ts
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
import { fileURLToPath } from 'node:url'
|
|
29
|
-
import type { Context } from '@deepseek-ai/cordis'
|
|
29
|
+
import type { Context, Volatile } from '@deepseek-ai/cordis'
|
|
30
30
|
import z from '@deepseek-ai/schemastery'
|
|
31
31
|
import { createUserMessage } from '@deepseek-ai/dsh-llm'
|
|
32
32
|
import type { ContextFormed } from '@deepseek-ai/dsh-llm'
|
|
@@ -90,18 +90,63 @@ Plugin logicprobe is active. Documents are not truth — code is. Verify every v
|
|
|
90
90
|
**Proactive suggestion**: When a user asks code-level behavioral questions — "could this state machine deadlock", "is this retry limit safe", "check this timing sequence for bugs", "is this migration non-breaking", "does this copy cover all required fields" — suggest logicprobe as an optional verification pass (do not auto-escalate).
|
|
91
91
|
</EXTREMELY_IMPORTANT>`
|
|
92
92
|
|
|
93
|
+
/** A schemastery field that may or may not carry `.volatile()`. */
|
|
94
|
+
interface LiveField {
|
|
95
|
+
volatile?: () => unknown
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Declare a field as live where this host's schemastery can — `.volatile()`
|
|
100
|
+
* arrived in 3.18.3 — and leave it an ordinary field where it cannot.
|
|
101
|
+
*
|
|
102
|
+
* The fallback is load-bearing, not defensive padding. `Config` below is built
|
|
103
|
+
* while this module is still being evaluated, so an unconditional `.volatile()`
|
|
104
|
+
* on a host shipping schemastery 3.18.2 (measured: dsh 0.1.5-rc.2 and
|
|
105
|
+
* 0.1.5-rc.3) throws during import; the loader entry then fails and takes the
|
|
106
|
+
* WHOLE plugin tree — and the host's boot — down with it. Degrading costs only
|
|
107
|
+
* the Plugins-page switch, because the settings service projects nothing but
|
|
108
|
+
* fields under a `.volatile()` node; skills, tools and the gate injection are
|
|
109
|
+
* untouched. The returned schema keeps the plain field's static type; the
|
|
110
|
+
* `Config` interface below carries the union the host actually hands over.
|
|
111
|
+
*/
|
|
112
|
+
function live<T>(field: T): T {
|
|
113
|
+
const probe = field as T & LiveField
|
|
114
|
+
return typeof probe.volatile === 'function' ? (probe.volatile() as T) : field
|
|
115
|
+
}
|
|
116
|
+
|
|
93
117
|
export interface Config {
|
|
94
|
-
|
|
118
|
+
/**
|
|
119
|
+
* The injection switch the Web client's Plugins page edits live: a `Volatile`
|
|
120
|
+
* reference on a host whose schemastery supports one, an ordinary boolean on a
|
|
121
|
+
* host that predates `.volatile()`. Read it through {@link injectionEnabled},
|
|
122
|
+
* which accepts both shapes.
|
|
123
|
+
*/
|
|
124
|
+
enabled: Volatile<boolean> | boolean
|
|
95
125
|
gateContent: string
|
|
96
126
|
interaction: InteractionMode
|
|
97
127
|
}
|
|
98
128
|
|
|
99
129
|
export const Config = z.object({
|
|
100
|
-
|
|
130
|
+
// Live so the Web Plugins page can flip the gate injection inside a running
|
|
131
|
+
// session: dsh's settings service projects ONLY fields under a `.volatile()`
|
|
132
|
+
// node and rejects writes to every other path. The price is that the injection
|
|
133
|
+
// reads the reference per step instead of deciding once at mount, which is also
|
|
134
|
+
// what lets a toggle take effect without remounting the row.
|
|
135
|
+
enabled: live(z.boolean().default(true)),
|
|
101
136
|
gateContent: z.string().default(DEFAULT_GATE_CONTENT),
|
|
102
137
|
interaction: z.union(['ask', 'auto', 'follow-approval']).default('follow-approval'),
|
|
103
138
|
})
|
|
104
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Read the injection switch as a boolean, whichever shape this host produced.
|
|
142
|
+
* @param config - the resolved plugin configuration.
|
|
143
|
+
* @returns whether the gate may be injected.
|
|
144
|
+
*/
|
|
145
|
+
function injectionEnabled(config: Config): boolean {
|
|
146
|
+
const value = config.enabled
|
|
147
|
+
return typeof value === 'boolean' ? value : value.get()
|
|
148
|
+
}
|
|
149
|
+
|
|
105
150
|
function gateMessage(text: string): UserMessage {
|
|
106
151
|
return createUserMessage({
|
|
107
152
|
content: [{ type: 'text', text }],
|
|
@@ -227,7 +272,7 @@ function inspectProvider(config: Config, isToolRegistered: () => boolean, isData
|
|
|
227
272
|
query: async (method) => {
|
|
228
273
|
if (method === 'status') {
|
|
229
274
|
return {
|
|
230
|
-
enabled: config
|
|
275
|
+
enabled: injectionEnabled(config),
|
|
231
276
|
gateContentLength: config.gateContent.length,
|
|
232
277
|
interaction: config.interaction,
|
|
233
278
|
toolRegistered: isToolRegistered(),
|
|
@@ -324,7 +369,12 @@ export function apply(ctx: Context, config: Config): void {
|
|
|
324
369
|
customSkillDirs: [SKILLS_DIR],
|
|
325
370
|
})
|
|
326
371
|
})
|
|
327
|
-
|
|
372
|
+
// Injection listens unconditionally, including while the switch is off: the
|
|
373
|
+
// switch is volatile, so `apply` runs once and the value behind it can turn on
|
|
374
|
+
// later from the Web Plugins page. Returning early on a false value here would
|
|
375
|
+
// freeze that decision for the lifetime of the mount, and turning the switch
|
|
376
|
+
// back on could never take effect without a profile restart.
|
|
377
|
+
//
|
|
328
378
|
// Inject the gate once per session on the FIRST model step that runs,
|
|
329
379
|
// instead of at session-start: session-start injection lands in the agent's
|
|
330
380
|
// inbox, which a blank-session preset switch (agentPreset.select ->
|
|
@@ -340,6 +390,7 @@ export function apply(ctx: Context, config: Config): void {
|
|
|
340
390
|
const decision = await next()
|
|
341
391
|
if (decision.kind === 'reject') return decision
|
|
342
392
|
registerIntegrations()
|
|
393
|
+
if (!injectionEnabled(config)) return decision
|
|
343
394
|
if (gateInHistory(agent.session)) return decision
|
|
344
395
|
return {
|
|
345
396
|
kind: 'enter',
|
package/src/tool.ts
CHANGED
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
import { defineTool, type JsonValue } from '@deepseek-ai/dsh-tools'
|
|
2
|
-
import { runVerification } from './engine.js'
|
|
3
|
-
|
|
4
|
-
export const LOGICPROBE_VERIFY_TOOL_NAME = 'logicprobe_verify'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Model-visible DSH tool wrapping the bundled TypeScript verification engine.
|
|
8
|
-
* The model passes a LogicModelV1 object; the engine validates it and returns
|
|
9
|
-
* the 22-check report (S1-S8 structural, A1-A14), or a 26-check
|
|
10
|
-
* report when beforeModel is supplied (D1 behavioral preservation, D2
|
|
11
|
-
* invariant continuity, D3 regression delta, D4 deadlock/liveness regression).
|
|
12
|
-
* This is the dsh-native replacement for hand-filling the Python template
|
|
13
|
-
* shipped in the skill references.
|
|
14
|
-
*/
|
|
15
|
-
export const logicProbeVerifyTool = defineTool({
|
|
16
|
-
name: LOGICPROBE_VERIFY_TOOL_NAME,
|
|
17
|
-
description:
|
|
18
|
-
'Run executable state-machine verification (logicprobe). Takes a LogicModelV1 object with schemaVersion=1, init, states ({id, terminal?}), transitions ({from, event, to, guard?, updates?, cost?}), variables?, invariants?, concurrentPairs?, boundaryChecks?, resourcePairs?, idempotentEvents?, narrative?. Guards are structured ({variable, op, value} | {all} | {any} | {not}); invariants support never-states, var-in-range, event-before-state, leads-to, sequence, atomicity, budget (transition cost defaults to 1; A12 checks every reachable path stays within the declared budget and reports the shortest over-budget counterexample), and probability (transition weight, default 1, makes the model a DTMC; A13 checks P(hit target) against the bound by value iteration). Variables support monotonic inc/dec. The optional narrative block carries natural-language descriptions of states (narrative.states), events (narrative.events), and (state, event) scenarios (narrative.scenarios: [{from, event, scenario}]); when present it must fully cover the model and is echoed in the report. Returns a report with S1-S8 structural checks and A1-A13 adversarial/probability probes including shortest counterexample paths. If beforeModel is provided, also runs D1-D4 before/after regression checks. See skills/logicprobe/references/dsh-model-schema.md.',
|
|
19
|
-
parameters: {
|
|
20
|
-
model: {
|
|
21
|
-
type: 'json',
|
|
22
|
-
required: true,
|
|
23
|
-
description: 'LogicModelV1 state machine model to verify.',
|
|
24
|
-
},
|
|
25
|
-
maxStates: {
|
|
26
|
-
type: 'integer',
|
|
27
|
-
description: 'Maximum runtime states to explore. Default 10000.',
|
|
28
|
-
},
|
|
29
|
-
maxPermutationEvents: {
|
|
30
|
-
type: 'integer',
|
|
31
|
-
description: 'Maximum event count for A3 order permutation. Default 5.',
|
|
32
|
-
},
|
|
33
|
-
beforeModel: {
|
|
34
|
-
type: 'json',
|
|
35
|
-
description: 'Optional BEFORE LogicModelV1 state machine model for refactoring/migration regression comparison.',
|
|
36
|
-
},
|
|
37
|
-
stateMapping: {
|
|
38
|
-
type: 'json',
|
|
39
|
-
description: 'Optional object mapping BEFORE state ids to AFTER state ids. Omit for identity mapping (same state names).',
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
output: {
|
|
43
|
-
schema: {
|
|
44
|
-
type: 'json',
|
|
45
|
-
description: 'logicprobe verification report with summary and per-check findings.',
|
|
46
|
-
},
|
|
47
|
-
render(_args, value) {
|
|
48
|
-
return [{ type: 'text' as const, text: JSON.stringify(value, null, 2) }]
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
timeoutMs: 10_000,
|
|
52
|
-
isConcurrencySafe: () => true,
|
|
53
|
-
async execute(args) {
|
|
54
|
-
return runVerification(args.model, {
|
|
55
|
-
maxStates: args.maxStates,
|
|
56
|
-
maxPermutationEvents: args.maxPermutationEvents,
|
|
57
|
-
beforeModel: args.beforeModel,
|
|
58
|
-
stateMapping: args.stateMapping as Record<string, string> | undefined,
|
|
59
|
-
}) as unknown as JsonValue
|
|
60
|
-
},
|
|
61
|
-
})
|
|
1
|
+
import { defineTool, type JsonValue } from '@deepseek-ai/dsh-tools'
|
|
2
|
+
import { runVerification } from './engine.js'
|
|
3
|
+
|
|
4
|
+
export const LOGICPROBE_VERIFY_TOOL_NAME = 'logicprobe_verify'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Model-visible DSH tool wrapping the bundled TypeScript verification engine.
|
|
8
|
+
* The model passes a LogicModelV1 object; the engine validates it and returns
|
|
9
|
+
* the 22-check report (S1-S8 structural, A1-A14), or a 26-check
|
|
10
|
+
* report when beforeModel is supplied (D1 behavioral preservation, D2
|
|
11
|
+
* invariant continuity, D3 regression delta, D4 deadlock/liveness regression).
|
|
12
|
+
* This is the dsh-native replacement for hand-filling the Python template
|
|
13
|
+
* shipped in the skill references.
|
|
14
|
+
*/
|
|
15
|
+
export const logicProbeVerifyTool = defineTool({
|
|
16
|
+
name: LOGICPROBE_VERIFY_TOOL_NAME,
|
|
17
|
+
description:
|
|
18
|
+
'Run executable state-machine verification (logicprobe). Takes a LogicModelV1 object with schemaVersion=1, init, states ({id, terminal?}), transitions ({from, event, to, guard?, updates?, cost?}), variables?, invariants?, concurrentPairs?, boundaryChecks?, resourcePairs?, idempotentEvents?, narrative?. Guards are structured ({variable, op, value} | {all} | {any} | {not}); invariants support never-states, var-in-range ({variable, min?, max?, when?} — when scopes the range to a state ({state}) or a guard node, is evaluated on the post-state of every transition, leaves the initial state checked unconditionally, and is rejected on any other invariant kind), event-before-state, leads-to, sequence, atomicity, budget (transition cost defaults to 1; A12 checks every reachable path stays within the declared budget and reports the shortest over-budget counterexample), and probability (transition weight, default 1, makes the model a DTMC; A13 checks P(hit target) against the bound by value iteration). The schema is closed: an undeclared key on any model part (model, state, transition, update, variable, invariant, boundaryCheck, resourcePair, narrative, scenario) is a validation error, never ignored. Variables support monotonic inc/dec. The optional narrative block carries natural-language descriptions of states (narrative.states), events (narrative.events), and (state, event) scenarios (narrative.scenarios: [{from, event, scenario}]); when present it must fully cover the model and is echoed in the report. Returns a report with S1-S8 structural checks and A1-A13 adversarial/probability probes including shortest counterexample paths. If beforeModel is provided, also runs D1-D4 before/after regression checks. See skills/logicprobe/references/dsh-model-schema.md.',
|
|
19
|
+
parameters: {
|
|
20
|
+
model: {
|
|
21
|
+
type: 'json',
|
|
22
|
+
required: true,
|
|
23
|
+
description: 'LogicModelV1 state machine model to verify.',
|
|
24
|
+
},
|
|
25
|
+
maxStates: {
|
|
26
|
+
type: 'integer',
|
|
27
|
+
description: 'Maximum runtime states to explore. Default 10000.',
|
|
28
|
+
},
|
|
29
|
+
maxPermutationEvents: {
|
|
30
|
+
type: 'integer',
|
|
31
|
+
description: 'Maximum event count for A3 order permutation. Default 5.',
|
|
32
|
+
},
|
|
33
|
+
beforeModel: {
|
|
34
|
+
type: 'json',
|
|
35
|
+
description: 'Optional BEFORE LogicModelV1 state machine model for refactoring/migration regression comparison.',
|
|
36
|
+
},
|
|
37
|
+
stateMapping: {
|
|
38
|
+
type: 'json',
|
|
39
|
+
description: 'Optional object mapping BEFORE state ids to AFTER state ids. Omit for identity mapping (same state names).',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
output: {
|
|
43
|
+
schema: {
|
|
44
|
+
type: 'json',
|
|
45
|
+
description: 'logicprobe verification report with summary and per-check findings.',
|
|
46
|
+
},
|
|
47
|
+
render(_args, value) {
|
|
48
|
+
return [{ type: 'text' as const, text: JSON.stringify(value, null, 2) }]
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
timeoutMs: 10_000,
|
|
52
|
+
isConcurrencySafe: () => true,
|
|
53
|
+
async execute(args) {
|
|
54
|
+
return runVerification(args.model, {
|
|
55
|
+
maxStates: args.maxStates,
|
|
56
|
+
maxPermutationEvents: args.maxPermutationEvents,
|
|
57
|
+
beforeModel: args.beforeModel,
|
|
58
|
+
stateMapping: args.stateMapping as Record<string, string> | undefined,
|
|
59
|
+
}) as unknown as JsonValue
|
|
60
|
+
},
|
|
61
|
+
})
|