deepclause-pi 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DeepClause
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,255 @@
1
+ # DeepClause for pi
2
+
3
+ Run [DeepClause](https://github.com/deepclause/deepclause-sdk) DML programs inside [pi](https://github.com/badlogic/pi-mono).
4
+
5
+ DeepClause for pi is a runtime-only integration. Pi supplies the selected model, existing credentials, active session context, terminal UI, cancellation, and usage accounting. DeepClause supplies deterministic DML execution, Prolog constraints, task orchestration, backtracking, and a deliberately small runtime-tool boundary.
6
+
7
+ ## Requirements
8
+
9
+ - Node.js 22 or newer
10
+ - pi 0.84 or newer
11
+ - A model configured and selected in pi
12
+ - `curl` for the bundled deep-research example
13
+
14
+ The extension does not request API keys or modify provider environment variables.
15
+
16
+ ## Install
17
+
18
+ Install directly from GitHub:
19
+
20
+ ```sh
21
+ pi install git:github.com/deepclause/deepclause-pi
22
+ ```
23
+
24
+ For a project-local installation:
25
+
26
+ ```sh
27
+ pi install git:github.com/deepclause/deepclause-pi -l
28
+ ```
29
+
30
+ Restart pi after installation. Run `/dc` to initialize the current workspace and verify the active model and runtime status.
31
+
32
+ For a project-local (`-l`) installation, start pi from the directory containing `.pi/settings.json`. Pi does not discover a project package from a parent directory when launched inside a nested subdirectory. The startup screen should list DeepClause under **Extensions** and `/dc-run` should appear in slash-command completion. If the extension is absent, an input beginning with `/dc-run` is forwarded to the model as ordinary text instead of executing the command.
33
+
34
+ To try an unpublished checkout during development:
35
+
36
+ ```sh
37
+ pi -e ./deepclause-pi/src/index.ts
38
+ ```
39
+
40
+ ## Commands
41
+
42
+ | Command | Description |
43
+ | --- | --- |
44
+ | `/dc` | Initialize the workspace non-destructively and show help, model, paths, and status. |
45
+ | `/dc-list` | List authored skills and generated plans. |
46
+ | `/dc-plan <request> [--name=slug]` | Create a validated executable DML plan using pi's current context, skills, and active tools. |
47
+ | `/dc-run <skill> [args]` | Run a named skill such as `example` or `deep_research`. |
48
+ | `/dc-run <path> [args]` | Run a DML file below `.pi/deepclause/`. |
49
+ | `/dc-tool enable\|disable\|status` | Control the default-off `dc_run` tool callable by pi's model. |
50
+ | `/dc-cancel` | Cancel the active DeepClause execution. |
51
+
52
+ Run options:
53
+
54
+ - `--context=turn|branch|isolated` overrides session-context import for one run.
55
+ - `--verbose` or `-v` displays lifecycle events.
56
+ - `--debug` or `-d` displays complete event payloads and SDK model diagnostics.
57
+
58
+ Examples:
59
+
60
+ ```text
61
+ /dc-run example --debug
62
+ /dc-run deep_research "What are the practical impacts of small language models?" --verbose
63
+ /dc-run skills/my_skill.dml "first argument" --context=isolated
64
+ /dc-plan inspect this repository and propose a safe ESM migration --name=esm-migration
65
+ /dc-run plans/esm_migration.dml
66
+ ```
67
+
68
+ ## Workspace layout
69
+
70
+ The first `/dc` or `/dc-run` creates missing files under the active workspace:
71
+
72
+ ```text
73
+ .pi/deepclause/
74
+ ├── config.json
75
+ ├── AGENTS.md
76
+ ├── DML_REFERENCE.md
77
+ ├── skills/
78
+ │ ├── example.dml
79
+ │ └── deep_research.dml
80
+ └── plans/
81
+ ```
82
+
83
+ Existing files are never overwritten silently. The extension neither creates nor reads `.deepclause/`.
84
+
85
+ `AGENTS.md` teaches pi how to author and conservatively edit DML. `DML_REFERENCE.md` is the bundled language/runtime reference. Add user-maintained programs to `skills/`; `/dc-plan` writes generated executable programs to `plans/`. Pi can edit either with its normal coding tools.
86
+
87
+ ## Session context
88
+
89
+ Configure the default mode in `.pi/deepclause/config.json`:
90
+
91
+ ```json
92
+ {
93
+ "version": 1,
94
+ "contextMode": "turn",
95
+ "branchMessageLimit": 20,
96
+ "gasLimit": 100000,
97
+ "maxTokens": 16384,
98
+ "verbose": false,
99
+ "modelToolEnabled": false
100
+ }
101
+ ```
102
+
103
+ - `turn` imports the current request and relevant immediate context. This is the default.
104
+ - `branch` imports a bounded set of messages from the active pi branch, including compacted history.
105
+ - `isolated` imports no pi conversation.
106
+
107
+ Pi remains the sole persistent session owner. Executions stop when their pi session closes or changes, and results are rendered into the current session.
108
+
109
+ ## Opt-in model tool
110
+
111
+ The model-callable `dc_run` tool is disabled by default. Enable it explicitly for the current workspace:
112
+
113
+ ```text
114
+ /dc-tool enable
115
+ ```
116
+
117
+ The change takes effect immediately and persists in `.pi/deepclause/config.json`; no reload is required. Use `/dc-tool status` to inspect it and `/dc-tool disable` to remove it from pi's active tools.
118
+
119
+ When active, pi can call `dc_run` with an existing `skill`, optional positional `args`, and an optional `turn`, `branch`, or `isolated` context override. The tool reuses the same path isolation, active model, cancellation, session context, events, and runtime policy as `/dc-run`. It rejects concurrent execution, cannot compile natural language into DML, and cannot escape `.pi/deepclause/`. Any DML request for `pi_bash` still requires explicit user approval.
120
+
121
+ Contextual plans containing `pi_agent_step` cannot be invoked through `dc_run`. They must be started explicitly by the user with `/dc-run`, which displays a confirmation first.
122
+
123
+ ## Contextual executable plans
124
+
125
+ `/dc-plan` starts a normal pi agent turn. The planner can inspect the workspace and account for project instructions, loaded skills, the selected model, and currently active built-in or extension tools. It does not ask the model to emit raw DML. Instead, a transaction-scoped `dc_plan_commit` tool accepts a typed plan specification; the extension validates it, deterministically assembles DML, validates the generated program with the SDK parser, previews it for confirmation, and writes it without overwriting an existing plan.
126
+
127
+ The resulting `.dml` file is the plan. Steps use one of two executors:
128
+
129
+ - `dml` — contained reasoning through ordinary typed DML tasks.
130
+ - `pi` — a bounded `pi_agent_step` that runs as a normal pi turn with current session context and loaded skills.
131
+
132
+ For each pi step, only the exact tools named in the committed plan are temporarily active. They must still be installed and active when execution begins; existing tool policies, UI, and approvals remain authoritative. DeepClause control tools cannot be requested recursively. The prior active-tool set is restored after success, failure, or cancellation.
133
+
134
+ ## Runtime tools and approval
135
+
136
+ The extension never exposes pi's general tool registry directly to ordinary DML. The optional `dc_run` tool runs an existing DML program; inside that runtime, only these host operations are registered:
137
+
138
+ - `pi_workspace_list(RelativePath)` — read-only, one-level workspace listing. Absolute paths, traversal, and resolved symlink escapes are rejected.
139
+ - `pi_bash(Command)` — runs an explicitly approved shell command in the active workspace.
140
+ - `pi_bash(Executable, Args)` — runs an explicitly approved executable with a separate argv list, avoiding shell interpolation.
141
+
142
+ Every `pi_bash` call has a 60-second timeout, inherits cancellation, and is denied when interactive approval is unavailable.
143
+
144
+ User-approved contextual plans additionally receive the internal `pi_agent_step` bridge. That bridge delegates a bounded instruction to a normal pi turn rather than invoking arbitrary tools itself, preserving policies from pi and other extensions.
145
+
146
+ DML can wrap these runtime operations in higher-level tool predicates. It can also wrap the SDK's internal `ask_user` operation. During `/dc-run`, `ask_user` opens pi's native, cancellable input UI and returns the response to the DML task loop.
147
+
148
+ ```prolog
149
+ tool(user_feedback(Prompt, Response), "Ask the user for feedback") :-
150
+ exec(ask_user(prompt: Prompt), Result),
151
+ get_dict(user_response, Result, Response).
152
+
153
+ tool(bing_search(Query, Results), "Search Bing RSS with curl") :-
154
+ format(string(QueryArg), "q=~w", [Query]),
155
+ exec(pi_bash("curl", [
156
+ "--fail", "--silent", "--show-error", "--location", "--get",
157
+ "--data-urlencode", QueryArg,
158
+ "https://www.bing.com/search?format=rss&count=8"
159
+ ]), Result),
160
+ get_dict(stdout, Result, Results).
161
+ ```
162
+
163
+ DML predicates remain visible to `task/N` agent loops, while their nested `exec/2` calls are still checked against the host runtime whitelist.
164
+
165
+ ## Bundled examples
166
+
167
+ ### `example.dml`
168
+
169
+ Demonstrates:
170
+
171
+ - CLP(FD) constraint solving
172
+ - Read-only workspace listing
173
+ - Approval-gated bash execution
174
+ - Typed task output through pi's active model
175
+ - Runtime progress, tool, usage, and answer events
176
+
177
+ Run `/dc-run example --debug` and approve the harmless displayed `printf` command.
178
+
179
+ ### `deep_research.dml`
180
+
181
+ Demonstrates model-callable DML tool predicates:
182
+
183
+ 1. The model creates three focused research queries.
184
+ 2. `user_feedback/2` presents the plan through pi's input UI.
185
+ 3. The model revises or accepts the plan.
186
+ 4. `bing_search/2` invokes approved `curl` requests against Bing RSS.
187
+ 5. The model synthesizes a cited Markdown report from the returned result snippets.
188
+
189
+ The example does not use SDK web search, URL fetch, file writing, or unrestricted pi tools. Each curl request requires explicit approval.
190
+
191
+ ## Event presentation
192
+
193
+ Every run displays a live panel containing the skill, active model, context mode, elapsed time, phase, output, recent events, and token usage. Runtime events map into pi as follows:
194
+
195
+ - `task_activity` → progress
196
+ - `stream` → model text
197
+ - `tool_call` → tool activity
198
+ - `input_required` → native pi input prompt
199
+ - `usage` → usage totals
200
+ - `answer` → command result
201
+ - `error` → concise error notification
202
+
203
+ The SDK supports incremental text callbacks inside `task/N`. The current pi adapter uses pi's completion API, so model text presently arrives as one completed stream chunk; task, tool, input, and usage events remain live.
204
+
205
+ ## Authoring a skill
206
+
207
+ New workspaces receive a comprehensive `.pi/deepclause/AGENTS.md` authoring guide distilled from the SDK language reference, runtime implementation, examples, compiler prompts, and planning benchmarks. It teaches pi to design DML as deterministic Prolog orchestration around typed model tasks, narrow tools, explicit progress, constraints, and safe fallback.
208
+
209
+ The evidence and design decisions behind it are recorded in [docs/AUTHORING_GUIDE_ANALYSIS.md](docs/AUTHORING_GUIDE_ANALYSIS.md).
210
+
211
+ The design and security rationale for `/dc-plan` are recorded in [docs/DC_PLAN_PROPOSAL.md](docs/DC_PLAN_PROPOSAL.md).
212
+
213
+ The guide covers:
214
+
215
+ - `agent_main/0` through `agent_main/3`, typed `task/N` and isolated `prompt/N`
216
+ - memory, interpolation, dicts, model-callable DML tools, and pi's restricted host tools
217
+ - backtracking, CLP constraints, failure handling, command approval, and validation workflow
218
+ - architecture patterns for research, constrained planning, workspace engineering, compliance gates, interactive expert systems, data pipelines, and generate-review-repair workflows
219
+
220
+ A minimal skill accepts one slash-command argument:
221
+
222
+ ```prolog
223
+ agent_main(Topic) :-
224
+ system("You are a concise analyst."),
225
+ format(string(Request), "Explain ~w and store the final text in Summary.", [Topic]),
226
+ task(Request, string(Summary)),
227
+ answer(Summary).
228
+ ```
229
+
230
+ Before creating or modifying DML, consult `.pi/deepclause/AGENTS.md` and `.pi/deepclause/DML_REFERENCE.md`. DeepClause compilation is intentionally unavailable in this integration; authored content must already be valid DML.
231
+
232
+ ## Development
233
+
234
+ ```sh
235
+ git clone https://github.com/deepclause/deepclause-pi.git
236
+ cd deepclause-pi
237
+ npm install
238
+ npm run check
239
+ ```
240
+
241
+ The package depends on `deepclause-sdk` 0.0.87 and uses pi packages as peer dependencies. The source extension entry point is declared in the `pi.extensions` package field, matching pi's TypeScript extension-loading convention.
242
+
243
+ ## Scope
244
+
245
+ - No Markdown-to-DML compiler
246
+ - No dynamic per-skill slash commands
247
+ - No independent DeepClause session or execution-log store
248
+ - No direct DML access to pi's full tool registry
249
+ - No model-callable execution unless the user enables `dc_run` for the workspace
250
+ - No silent mutation of user files
251
+ - No workspace path escape
252
+
253
+ ## License
254
+
255
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,13 @@
1
+ export type ContextMode = "turn" | "branch" | "isolated";
2
+ export interface DeepClauseConfig {
3
+ version: 1;
4
+ contextMode: ContextMode;
5
+ branchMessageLimit: number;
6
+ gasLimit: number;
7
+ maxTokens: number;
8
+ verbose: boolean;
9
+ modelToolEnabled: boolean;
10
+ }
11
+ export declare const DEFAULT_CONFIG: DeepClauseConfig;
12
+ export declare function loadConfig(path: string): Promise<DeepClauseConfig>;
13
+ export declare function setModelToolEnabled(configPath: string, enabled: boolean): Promise<DeepClauseConfig>;
package/dist/config.js ADDED
@@ -0,0 +1,59 @@
1
+ import { readFile, writeFile } from "node:fs/promises";
2
+ export const DEFAULT_CONFIG = {
3
+ version: 1,
4
+ contextMode: "turn",
5
+ branchMessageLimit: 20,
6
+ gasLimit: 100_000,
7
+ maxTokens: 16_384,
8
+ verbose: false,
9
+ modelToolEnabled: false,
10
+ };
11
+ const isContextMode = (value) => value === "turn" || value === "branch" || value === "isolated";
12
+ export async function loadConfig(path) {
13
+ let value;
14
+ try {
15
+ value = JSON.parse(await readFile(path, "utf8"));
16
+ }
17
+ catch (error) {
18
+ if (error.code === "ENOENT")
19
+ return DEFAULT_CONFIG;
20
+ throw new Error(`Invalid DeepClause config: ${error instanceof Error ? error.message : String(error)}`);
21
+ }
22
+ if (!value || typeof value !== "object")
23
+ throw new Error("DeepClause config must be a JSON object");
24
+ const config = value;
25
+ const contextMode = config.contextMode ?? DEFAULT_CONFIG.contextMode;
26
+ if (!isContextMode(contextMode))
27
+ throw new Error("contextMode must be turn, branch, or isolated");
28
+ const positiveInteger = (key, fallback) => {
29
+ const candidate = config[key] ?? fallback;
30
+ if (!Number.isInteger(candidate) || Number(candidate) <= 0) {
31
+ throw new Error(`${key} must be a positive integer`);
32
+ }
33
+ return Number(candidate);
34
+ };
35
+ return {
36
+ version: 1,
37
+ contextMode,
38
+ branchMessageLimit: positiveInteger("branchMessageLimit", DEFAULT_CONFIG.branchMessageLimit),
39
+ gasLimit: positiveInteger("gasLimit", DEFAULT_CONFIG.gasLimit),
40
+ maxTokens: positiveInteger("maxTokens", DEFAULT_CONFIG.maxTokens),
41
+ verbose: config.verbose === true,
42
+ modelToolEnabled: config.modelToolEnabled === true,
43
+ };
44
+ }
45
+ export async function setModelToolEnabled(configPath, enabled) {
46
+ let existing = {};
47
+ try {
48
+ const parsed = JSON.parse(await readFile(configPath, "utf8"));
49
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
50
+ existing = parsed;
51
+ }
52
+ catch (error) {
53
+ if (error.code !== "ENOENT") {
54
+ throw new Error(`Invalid DeepClause config: ${error instanceof Error ? error.message : String(error)}`);
55
+ }
56
+ }
57
+ await writeFile(configPath, `${JSON.stringify({ ...existing, modelToolEnabled: enabled }, null, 2)}\n`, "utf8");
58
+ return loadConfig(configPath);
59
+ }
@@ -0,0 +1,3 @@
1
+ import type { MemoryMessage } from "deepclause-sdk";
2
+ import type { ContextMode } from "./config.js";
3
+ export declare function buildInitialMessages(entries: readonly unknown[], mode: ContextMode, branchMessageLimit: number): MemoryMessage[];
@@ -0,0 +1,38 @@
1
+ function textContent(content) {
2
+ if (typeof content === "string")
3
+ return content;
4
+ if (!Array.isArray(content))
5
+ return "";
6
+ return content
7
+ .filter((block) => Boolean(block) && typeof block === "object")
8
+ .filter((block) => block.type === "text" && typeof block.text === "string")
9
+ .map((block) => block.text)
10
+ .join("\n");
11
+ }
12
+ function entryToMessage(entry) {
13
+ if (entry.type === "compaction" && typeof entry.summary === "string") {
14
+ return { role: "system", content: `Compacted pi session context:\n${entry.summary}` };
15
+ }
16
+ if (entry.type !== "message" || !entry.message)
17
+ return undefined;
18
+ if (entry.message.role !== "user" && entry.message.role !== "assistant")
19
+ return undefined;
20
+ const content = textContent(entry.message.content).trim();
21
+ return content ? { role: entry.message.role, content } : undefined;
22
+ }
23
+ export function buildInitialMessages(entries, mode, branchMessageLimit) {
24
+ if (mode === "isolated")
25
+ return [];
26
+ const messages = entries
27
+ .map(entryToMessage)
28
+ .filter((message) => message !== undefined);
29
+ if (mode === "branch")
30
+ return messages.slice(-branchMessageLimit);
31
+ const immediate = [];
32
+ for (let index = messages.length - 1; index >= 0 && immediate.length < 2; index--) {
33
+ const message = messages[index];
34
+ if (message)
35
+ immediate.unshift(message);
36
+ }
37
+ return immediate;
38
+ }
@@ -0,0 +1,19 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { type ContextMode } from "./config.js";
3
+ export interface ParsedRun {
4
+ target: string;
5
+ args: string[];
6
+ contextMode?: ContextMode;
7
+ verbose: boolean;
8
+ debug: boolean;
9
+ }
10
+ interface ParsedPlan {
11
+ request: string;
12
+ name?: string;
13
+ debug: boolean;
14
+ }
15
+ export declare function splitArguments(input: string): string[];
16
+ export declare function parseRun(input: string): ParsedRun;
17
+ export declare function parsePlan(input: string): ParsedPlan;
18
+ export default function deepClauseExtension(pi: ExtensionAPI): void;
19
+ export {};