@carljia/omd-dsh 0.1.4 → 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/README.md +5 -1
- package/cordis.patch.yml +10 -0
- package/lib/boot.d.ts +21 -0
- package/lib/boot.js +45 -0
- package/lib/cli.js +9 -554
- package/lib/index.d.ts +2 -2
- package/lib/index.js +7 -5
- package/lib/plan.d.ts +14 -8
- package/lib/plan.js +61 -8
- package/lib/shared.d.ts +25 -0
- package/lib/shared.js +28 -0
- package/lib/sync.d.ts +62 -0
- package/lib/sync.js +549 -0
- package/lib/task.js +4 -3
- package/lib/vendor/omd-mode.mjs +7 -5
- package/lib/vendor/omd-plan.mjs +61 -8
- package/lib/vendor/omd-task.mjs +4 -3
- package/lib/vendor/shared.js +28 -0
- package/package.json +11 -1
- package/presets/omd-chat/agent.cordis.yml +1 -1
- package/presets/omd-executor/agent.cordis.yml +34 -1
- package/presets/omd-explorer/agent.cordis.yml +1 -1
- package/presets/omd-librarian/agent.cordis.yml +1 -1
- package/presets/omd-planner/agent.cordis.yml +1 -1
- package/presets/omd-reviewer/agent.cordis.yml +1 -1
- package/presets/omd-ultraworker/agent.cordis.yml +1 -1
package/lib/plan.d.ts
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module @carljia/omd-dsh/plan
|
|
3
3
|
*
|
|
4
|
-
* omd-plan: plan persistence
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
4
|
+
* omd-plan: plan persistence + plan-mode activation + write scope for the OMD
|
|
5
|
+
* planner mode. Three jobs, all scoped to the planner preset:
|
|
6
|
+
* 1. auto-activate plan mode for the top-level agent, so the plan:policy
|
|
7
|
+
* section renders and exit_plan_mode works (DSH leaves plan state
|
|
8
|
+
* inactive until /plan or a programmatic set);
|
|
9
|
+
* 2. enforce a .md-only write guard so the planner stays read-only except
|
|
10
|
+
* for markdown files;
|
|
11
|
+
* 3. wrap `tools/post-execute` and intercept a successful `exit_plan_mode`
|
|
12
|
+
* approval: the approved plan text is written into the workspace's plan
|
|
13
|
+
* directory (a fixed, code-level convention -- never mentioned in any
|
|
14
|
+
* persona/prompt text), and the tool result content is enriched with the
|
|
15
|
+
* saved file name so the planner's fixed Start Work final step can hand
|
|
16
|
+
* it to the user.
|
|
11
17
|
*
|
|
12
18
|
* Plan directory convention (hardcoded here and in omd-start-work only):
|
|
13
19
|
* <session cwd>/.omd/plans/<slug>-<timestamp>.md
|
|
@@ -16,7 +22,7 @@
|
|
|
16
22
|
*/
|
|
17
23
|
/** Cordis plugin name. */
|
|
18
24
|
declare const name = "omd-plan";
|
|
19
|
-
/** No service injection: this row only registers
|
|
25
|
+
/** No service injection: this row only registers scoped event listeners. */
|
|
20
26
|
declare const inject: never[];
|
|
21
27
|
declare function apply(ctx: any): void;
|
|
22
28
|
export { apply, inject, name };
|
package/lib/plan.js
CHANGED
|
@@ -4,13 +4,19 @@ import { scopeOf } from "@deepseek-ai/dsh-scope";
|
|
|
4
4
|
/**
|
|
5
5
|
* @module @carljia/omd-dsh/plan
|
|
6
6
|
*
|
|
7
|
-
* omd-plan: plan persistence
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
7
|
+
* omd-plan: plan persistence + plan-mode activation + write scope for the OMD
|
|
8
|
+
* planner mode. Three jobs, all scoped to the planner preset:
|
|
9
|
+
* 1. auto-activate plan mode for the top-level agent, so the plan:policy
|
|
10
|
+
* section renders and exit_plan_mode works (DSH leaves plan state
|
|
11
|
+
* inactive until /plan or a programmatic set);
|
|
12
|
+
* 2. enforce a .md-only write guard so the planner stays read-only except
|
|
13
|
+
* for markdown files;
|
|
14
|
+
* 3. wrap `tools/post-execute` and intercept a successful `exit_plan_mode`
|
|
15
|
+
* approval: the approved plan text is written into the workspace's plan
|
|
16
|
+
* directory (a fixed, code-level convention -- never mentioned in any
|
|
17
|
+
* persona/prompt text), and the tool result content is enriched with the
|
|
18
|
+
* saved file name so the planner's fixed Start Work final step can hand
|
|
19
|
+
* it to the user.
|
|
14
20
|
*
|
|
15
21
|
* Plan directory convention (hardcoded here and in omd-start-work only):
|
|
16
22
|
* <session cwd>/.omd/plans/<slug>-<timestamp>.md
|
|
@@ -19,7 +25,7 @@ import { scopeOf } from "@deepseek-ai/dsh-scope";
|
|
|
19
25
|
*/
|
|
20
26
|
/** Cordis plugin name. */
|
|
21
27
|
const name = "omd-plan";
|
|
22
|
-
/** No service injection: this row only registers
|
|
28
|
+
/** No service injection: this row only registers scoped event listeners. */
|
|
23
29
|
const inject = [];
|
|
24
30
|
/** Plan directory segments relative to the session workspace root (cwd). */
|
|
25
31
|
const PLAN_DIR_SEGMENTS = [".omd", "plans"];
|
|
@@ -79,10 +85,57 @@ function isSubagent(agent) {
|
|
|
79
85
|
typeof agent.options.subagentDepth === "number" &&
|
|
80
86
|
agent.options.subagentDepth > 0);
|
|
81
87
|
}
|
|
88
|
+
/** Fold the session's plan/mode events (last one wins); mirror mode.ts. */
|
|
89
|
+
function planModeActive(events) {
|
|
90
|
+
let active = false;
|
|
91
|
+
for (const event of events ?? []) {
|
|
92
|
+
if (event !== undefined && event.type === "plan/mode") {
|
|
93
|
+
active = event.data !== undefined && event.data !== null && event.data.active === true;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return active;
|
|
97
|
+
}
|
|
82
98
|
function apply(ctx) {
|
|
83
99
|
if (scopeOf(ctx) === undefined) {
|
|
84
100
|
throw new Error("omd-plan: refusing to mount outside a scoped context; mount this row inside an agent preset");
|
|
85
101
|
}
|
|
102
|
+
// Scoped .md-only write guard: the planner may write/edit only markdown
|
|
103
|
+
// files, keeping it read-only for every other path. The plan file itself is
|
|
104
|
+
// written by this row via node:fs (not through the model's write/edit tools),
|
|
105
|
+
// so plan persistence is unaffected by the guard. Implemented on the
|
|
106
|
+
// tools/pre-execute gate (the row's existing ctx.on style) so no service
|
|
107
|
+
// injection is required.
|
|
108
|
+
ctx.on("tools/pre-execute", async (exec, next) => {
|
|
109
|
+
const toolName = exec !== undefined && exec !== null ? exec.name : undefined;
|
|
110
|
+
if (toolName !== "write" && toolName !== "edit")
|
|
111
|
+
return await next();
|
|
112
|
+
const filePath = exec.arguments !== undefined &&
|
|
113
|
+
exec.arguments !== null &&
|
|
114
|
+
typeof exec.arguments.file_path === "string"
|
|
115
|
+
? exec.arguments.file_path
|
|
116
|
+
: undefined;
|
|
117
|
+
if (filePath !== undefined && filePath.toLowerCase().endsWith(".md"))
|
|
118
|
+
return await next();
|
|
119
|
+
return {
|
|
120
|
+
kind: "deny",
|
|
121
|
+
reason: "omd-plan: the planner preset may write or edit only .md files (refusing " +
|
|
122
|
+
toolName +
|
|
123
|
+
" on a non-.md path)",
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
// Auto-activate plan mode for the top-level planner agent. DSH leaves plan
|
|
127
|
+
// state inactive until /plan or a programmatic set, so without this the
|
|
128
|
+
// plan:policy section never renders and exit_plan_mode fails with "only
|
|
129
|
+
// available in plan mode". Mirror mode.ts's direct log append (no narration)
|
|
130
|
+
// so the planner session is in plan mode from its first request onward.
|
|
131
|
+
ctx.on("agent/pre-step", async ({ agent }, next) => {
|
|
132
|
+
if (agent !== undefined && agent !== null && !isSubagent(agent) && agent.session !== undefined && agent.session !== null) {
|
|
133
|
+
if (!planModeActive(agent.session.events)) {
|
|
134
|
+
agent.session.append("plan/mode", { active: true });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return await next();
|
|
138
|
+
});
|
|
86
139
|
ctx.on("tools/post-execute", async (exec, result, next) => {
|
|
87
140
|
const decision = await next();
|
|
88
141
|
if (decision.kind !== "accept" || decision.value !== undefined)
|
package/lib/shared.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @carljia/omd-dsh/shared
|
|
3
|
+
*
|
|
4
|
+
* Per-agent transient state shared between the omd-mode and omd-task rows.
|
|
5
|
+
*
|
|
6
|
+
* Why this module exists: cordis scoped contexts are proxies — assigning an
|
|
7
|
+
* undeclared property throws ("cannot set property ... without provide"), and
|
|
8
|
+
* two rows in one preset are sibling contexts that cannot see each other's
|
|
9
|
+
* declared properties either. Both rows therefore import this module, and the
|
|
10
|
+
* sync ships it next to them (`.omd-vendor/shared.js`), so the two vendored
|
|
11
|
+
* rows resolve the SAME module instance and share one WeakMap. The override is
|
|
12
|
+
* keyed by the top-level agent object (stable across a session's turns; a
|
|
13
|
+
* resumed session mints a new agent and starts clean; subagents are distinct
|
|
14
|
+
* objects and simply miss the map, which is exactly the documented passthrough
|
|
15
|
+
* semantics).
|
|
16
|
+
*/
|
|
17
|
+
/** One user model pick, recorded when omd-mode yields to it. */
|
|
18
|
+
export interface ModeOverride {
|
|
19
|
+
provider: string;
|
|
20
|
+
model: string;
|
|
21
|
+
}
|
|
22
|
+
/** Record (or clear, with `undefined`) the user's model pick for one agent. */
|
|
23
|
+
export declare function setModeOverride(agent: object, override: ModeOverride | undefined): void;
|
|
24
|
+
/** The user's recorded model pick for one agent, or undefined. */
|
|
25
|
+
export declare function modeOverrideFor(agent: object): ModeOverride | undefined;
|
package/lib/shared.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @carljia/omd-dsh/shared
|
|
3
|
+
*
|
|
4
|
+
* Per-agent transient state shared between the omd-mode and omd-task rows.
|
|
5
|
+
*
|
|
6
|
+
* Why this module exists: cordis scoped contexts are proxies — assigning an
|
|
7
|
+
* undeclared property throws ("cannot set property ... without provide"), and
|
|
8
|
+
* two rows in one preset are sibling contexts that cannot see each other's
|
|
9
|
+
* declared properties either. Both rows therefore import this module, and the
|
|
10
|
+
* sync ships it next to them (`.omd-vendor/shared.js`), so the two vendored
|
|
11
|
+
* rows resolve the SAME module instance and share one WeakMap. The override is
|
|
12
|
+
* keyed by the top-level agent object (stable across a session's turns; a
|
|
13
|
+
* resumed session mints a new agent and starts clean; subagents are distinct
|
|
14
|
+
* objects and simply miss the map, which is exactly the documented passthrough
|
|
15
|
+
* semantics).
|
|
16
|
+
*/
|
|
17
|
+
const modeOverrides = new WeakMap();
|
|
18
|
+
/** Record (or clear, with `undefined`) the user's model pick for one agent. */
|
|
19
|
+
export function setModeOverride(agent, override) {
|
|
20
|
+
if (override === undefined)
|
|
21
|
+
modeOverrides.delete(agent);
|
|
22
|
+
else
|
|
23
|
+
modeOverrides.set(agent, override);
|
|
24
|
+
}
|
|
25
|
+
/** The user's recorded model pick for one agent, or undefined. */
|
|
26
|
+
export function modeOverrideFor(agent) {
|
|
27
|
+
return modeOverrides.get(agent);
|
|
28
|
+
}
|
package/lib/sync.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* omd-dsh sync core — materialize the OMD presets into <DSH_HOME>/.agent-presets.
|
|
3
|
+
*
|
|
4
|
+
* Shared by the CLI (\`omd-dsh sync\`) and the bundle boot row (\`dsh plugin add\`
|
|
5
|
+
* + restart): locate the DSH harness node_modules, render each preset's
|
|
6
|
+
* omd-mode / omd-task rows from the user's model matrix, copy the presets and
|
|
7
|
+
* vendored row modules into .agent-presets, and rewrite the vendored modules'
|
|
8
|
+
* bare @deepseek-ai/* imports to absolute file:// URLs into the harness tree so
|
|
9
|
+
* the rows share ONE module instance with the harness (scope symbols etc.).
|
|
10
|
+
*/
|
|
11
|
+
export type SyncFlags = {
|
|
12
|
+
harness?: string;
|
|
13
|
+
dryRun: boolean;
|
|
14
|
+
verbose: boolean;
|
|
15
|
+
};
|
|
16
|
+
export interface TierConfig {
|
|
17
|
+
provider: string;
|
|
18
|
+
model: string;
|
|
19
|
+
hint?: string;
|
|
20
|
+
persona?: string;
|
|
21
|
+
maxTokens?: number;
|
|
22
|
+
toolFilter?: {
|
|
23
|
+
allow?: string[];
|
|
24
|
+
deny?: string[];
|
|
25
|
+
denyShell?: boolean;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export interface ModeConfig {
|
|
29
|
+
provider?: string;
|
|
30
|
+
model?: string;
|
|
31
|
+
reasoningEffort?: string;
|
|
32
|
+
tiers?: Record<string, TierConfig>;
|
|
33
|
+
}
|
|
34
|
+
export interface Matrix {
|
|
35
|
+
version: number;
|
|
36
|
+
defaults?: {
|
|
37
|
+
provider?: string;
|
|
38
|
+
};
|
|
39
|
+
modes: Record<string, ModeConfig>;
|
|
40
|
+
}
|
|
41
|
+
export declare const PACKAGE_ROOT: string;
|
|
42
|
+
export declare const VENDOR_SOURCES: string[];
|
|
43
|
+
/** User-owned model matrix: lives under DSH_HOME, never inside the package or the repo. */
|
|
44
|
+
export declare const MATRIX_PATH: string;
|
|
45
|
+
export declare function dshHome(): string;
|
|
46
|
+
/**
|
|
47
|
+
* Resolve the DSH harness node_modules:
|
|
48
|
+
* 1. --harness flag (and cache it for later);
|
|
49
|
+
* 2. auto-detect via the dsh executable on PATH;
|
|
50
|
+
* 3. fall back to the locally cached value.
|
|
51
|
+
*/
|
|
52
|
+
export declare function resolveHarness(flags: SyncFlags): string | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* Resolve the harness node_modules from THIS module's own location, walking up
|
|
55
|
+
* the Node resolution path for @deepseek-ai/dsh-scope. This is the reliable
|
|
56
|
+
* anchor when the package runs as a bundle inside a DSH profile: the profile's
|
|
57
|
+
* flat module fallback (or its hoisted node_modules) exposes the harness tree.
|
|
58
|
+
*/
|
|
59
|
+
export declare function resolveHarnessFromSelf(): string | undefined;
|
|
60
|
+
export declare function loadMatrix(flags: SyncFlags, log?: (msg: string) => void): Matrix;
|
|
61
|
+
export declare function saveMatrix(m: Matrix): void;
|
|
62
|
+
export declare function runSync(flags: SyncFlags, harnessNodeModules: string, log?: (msg: string) => void): Promise<void>;
|