deepclause-sdk 0.0.89 → 0.1.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.
- package/README.md +417 -948
- package/dist/agent.d.ts +18 -2
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +74 -21
- package/dist/agent.js.map +1 -1
- package/dist/cli/config.d.ts +323 -176
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +146 -188
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/index.d.ts +3 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +61 -425
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/login.d.ts +29 -0
- package/dist/cli/login.d.ts.map +1 -0
- package/dist/cli/login.js +184 -0
- package/dist/cli/login.js.map +1 -0
- package/dist/cli/readline.d.ts +6 -0
- package/dist/cli/readline.d.ts.map +1 -0
- package/dist/cli/readline.js +15 -0
- package/dist/cli/readline.js.map +1 -0
- package/dist/cli/repl-bin.d.ts +9 -0
- package/dist/cli/repl-bin.d.ts.map +1 -0
- package/dist/cli/repl-bin.js +16 -0
- package/dist/cli/repl-bin.js.map +1 -0
- package/dist/cli/repl.d.ts +191 -0
- package/dist/cli/repl.d.ts.map +1 -0
- package/dist/cli/repl.js +1232 -0
- package/dist/cli/repl.js.map +1 -0
- package/dist/cli/run.d.ts +17 -18
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +196 -183
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/set-backend.d.ts +53 -0
- package/dist/cli/set-backend.d.ts.map +1 -0
- package/dist/cli/set-backend.js +259 -0
- package/dist/cli/set-backend.js.map +1 -0
- package/dist/cli/visualize.d.ts +38 -0
- package/dist/cli/visualize.d.ts.map +1 -0
- package/dist/cli/visualize.js +125 -0
- package/dist/cli/visualize.js.map +1 -0
- package/dist/compiler.d.ts +5 -52
- package/dist/compiler.d.ts.map +1 -1
- package/dist/compiler.js +4 -768
- package/dist/compiler.js.map +1 -1
- package/dist/index.d.ts +27 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -2
- package/dist/index.js.map +1 -1
- package/dist/prolog/bridge.d.ts +3 -1
- package/dist/prolog/bridge.d.ts.map +1 -1
- package/dist/prolog/bridge.js +8 -1
- package/dist/prolog/bridge.js.map +1 -1
- package/dist/prolog-src/deepclause_analysis.pl +10 -2
- package/dist/prolog-src/deepclause_mi.pl +2245 -41
- package/dist/repl/agent.d.ts +72 -0
- package/dist/repl/agent.d.ts.map +1 -0
- package/dist/repl/agent.js +366 -0
- package/dist/repl/agent.js.map +1 -0
- package/dist/repl/auth.d.ts +45 -0
- package/dist/repl/auth.d.ts.map +1 -0
- package/dist/repl/auth.js +116 -0
- package/dist/repl/auth.js.map +1 -0
- package/dist/repl/backend.d.ts +290 -0
- package/dist/repl/backend.d.ts.map +1 -0
- package/dist/repl/backend.js +370 -0
- package/dist/repl/backend.js.map +1 -0
- package/dist/repl/compaction.d.ts +122 -0
- package/dist/repl/compaction.d.ts.map +1 -0
- package/dist/repl/compaction.js +318 -0
- package/dist/repl/compaction.js.map +1 -0
- package/dist/repl/export.d.ts +30 -0
- package/dist/repl/export.d.ts.map +1 -0
- package/dist/repl/export.js +23 -0
- package/dist/repl/export.js.map +1 -0
- package/dist/repl/frames.d.ts +21 -0
- package/dist/repl/frames.d.ts.map +1 -0
- package/dist/repl/frames.js +79 -0
- package/dist/repl/frames.js.map +1 -0
- package/dist/repl/harness-tools.d.ts +74 -0
- package/dist/repl/harness-tools.d.ts.map +1 -0
- package/dist/repl/harness-tools.js +121 -0
- package/dist/repl/harness-tools.js.map +1 -0
- package/dist/repl/highlight.d.ts +30 -0
- package/dist/repl/highlight.d.ts.map +1 -0
- package/dist/repl/highlight.js +285 -0
- package/dist/repl/highlight.js.map +1 -0
- package/dist/repl/history.d.ts +234 -0
- package/dist/repl/history.d.ts.map +1 -0
- package/dist/repl/history.js +661 -0
- package/dist/repl/history.js.map +1 -0
- package/dist/repl/index.d.ts +120 -0
- package/dist/repl/index.d.ts.map +1 -0
- package/dist/repl/index.js +107 -0
- package/dist/repl/index.js.map +1 -0
- package/dist/repl/judge.d.ts +51 -0
- package/dist/repl/judge.d.ts.map +1 -0
- package/dist/repl/judge.js +58 -0
- package/dist/repl/judge.js.map +1 -0
- package/dist/repl/markdown.d.ts +25 -0
- package/dist/repl/markdown.d.ts.map +1 -0
- package/dist/repl/markdown.js +103 -0
- package/dist/repl/markdown.js.map +1 -0
- package/dist/repl/picker.d.ts +34 -0
- package/dist/repl/picker.d.ts.map +1 -0
- package/dist/repl/picker.js +81 -0
- package/dist/repl/picker.js.map +1 -0
- package/dist/repl/project-context.d.ts +16 -0
- package/dist/repl/project-context.d.ts.map +1 -0
- package/dist/repl/project-context.js +23 -0
- package/dist/repl/project-context.js.map +1 -0
- package/dist/repl/render.d.ts +160 -0
- package/dist/repl/render.d.ts.map +1 -0
- package/dist/repl/render.js +715 -0
- package/dist/repl/render.js.map +1 -0
- package/dist/repl/session.d.ts +356 -0
- package/dist/repl/session.d.ts.map +1 -0
- package/dist/repl/session.js +1275 -0
- package/dist/repl/session.js.map +1 -0
- package/dist/repl/status.d.ts +100 -0
- package/dist/repl/status.d.ts.map +1 -0
- package/dist/repl/status.js +218 -0
- package/dist/repl/status.js.map +1 -0
- package/dist/repl/tools.d.ts +53 -0
- package/dist/repl/tools.d.ts.map +1 -0
- package/dist/repl/tools.js +307 -0
- package/dist/repl/tools.js.map +1 -0
- package/dist/repl/toolset.d.ts +32 -0
- package/dist/repl/toolset.d.ts.map +1 -0
- package/dist/repl/toolset.js +82 -0
- package/dist/repl/toolset.js.map +1 -0
- package/dist/repl/tui/editor.d.ts +17 -0
- package/dist/repl/tui/editor.d.ts.map +1 -0
- package/dist/repl/tui/editor.js +39 -0
- package/dist/repl/tui/editor.js.map +1 -0
- package/dist/repl/tui/status-bar.d.ts +26 -0
- package/dist/repl/tui/status-bar.d.ts.map +1 -0
- package/dist/repl/tui/status-bar.js +47 -0
- package/dist/repl/tui/status-bar.js.map +1 -0
- package/dist/repl/tui/theme.d.ts +30 -0
- package/dist/repl/tui/theme.d.ts.map +1 -0
- package/dist/repl/tui/theme.js +81 -0
- package/dist/repl/tui/theme.js.map +1 -0
- package/dist/repl/tui/transcript.d.ts +39 -0
- package/dist/repl/tui/transcript.d.ts.map +1 -0
- package/dist/repl/tui/transcript.js +123 -0
- package/dist/repl/tui/transcript.js.map +1 -0
- package/dist/runner.d.ts +132 -13
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +434 -277
- package/dist/runner.js.map +1 -1
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +0 -32
- package/dist/sdk.js.map +1 -1
- package/dist/system/assets/docs/AGENT_PROMPT.md +1373 -0
- package/dist/system/assets/docs/DML_AUTHORING.md +195 -0
- package/dist/system/assets/docs/DML_REFERENCE.md +312 -72
- package/dist/system/assets/docs/HARNESS_EXAMPLES.md +172 -0
- package/dist/system/assets/docs/HARNESS_PATTERNS.md +562 -0
- package/dist/system/assets/docs/META_PROMPT.md +68 -0
- package/dist/system/assets/docs/PROLOG_SNIPPETS.md +117 -0
- package/dist/system/assets/index.d.ts +9 -17
- package/dist/system/assets/index.d.ts.map +1 -1
- package/dist/system/assets/index.js +26 -85
- package/dist/system/assets/index.js.map +1 -1
- package/dist/system/runtime/agentvm-manager.d.ts +9 -2
- package/dist/system/runtime/agentvm-manager.d.ts.map +1 -1
- package/dist/system/runtime/agentvm-manager.js +70 -44
- package/dist/system/runtime/agentvm-manager.js.map +1 -1
- package/dist/system/runtime/media.d.ts +23 -0
- package/dist/system/runtime/media.d.ts.map +1 -0
- package/dist/system/runtime/media.js +55 -0
- package/dist/system/runtime/media.js.map +1 -0
- package/dist/system/runtime/shell-limits.d.ts +21 -0
- package/dist/system/runtime/shell-limits.d.ts.map +1 -0
- package/dist/system/runtime/shell-limits.js +39 -0
- package/dist/system/runtime/shell-limits.js.map +1 -0
- package/dist/system/runtime/shell-manager.d.ts +8 -3
- package/dist/system/runtime/shell-manager.d.ts.map +1 -1
- package/dist/system/runtime/shell-manager.js +56 -39
- package/dist/system/runtime/shell-manager.js.map +1 -1
- package/dist/system/runtime/shell-tool-events.js +5 -0
- package/dist/system/runtime/shell-tool-events.js.map +1 -1
- package/dist/system/runtime/token-usage.d.ts +2 -0
- package/dist/system/runtime/token-usage.d.ts.map +1 -1
- package/dist/system/runtime/token-usage.js +7 -0
- package/dist/system/runtime/token-usage.js.map +1 -1
- package/dist/tools.d.ts +3 -21
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +14 -168
- package/dist/tools.js.map +1 -1
- package/dist/types.d.ts +80 -118
- package/dist/types.d.ts.map +1 -1
- package/dist/validate.d.ts +19 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +338 -0
- package/dist/validate.js.map +1 -0
- package/dist/visualize/index.d.ts +52 -0
- package/dist/visualize/index.d.ts.map +1 -0
- package/dist/visualize/index.js +189 -0
- package/dist/visualize/index.js.map +1 -0
- package/package.json +17 -15
- package/src/prolog-src/deepclause_analysis.pl +10 -2
- package/src/prolog-src/deepclause_mi.pl +2245 -41
- package/dist/cli/commands.d.ts +0 -42
- package/dist/cli/commands.d.ts.map +0 -1
- package/dist/cli/commands.js +0 -246
- package/dist/cli/commands.js.map +0 -1
- package/dist/cli/compile.d.ts +0 -99
- package/dist/cli/compile.d.ts.map +0 -1
- package/dist/cli/compile.js +0 -363
- package/dist/cli/compile.js.map +0 -1
- package/dist/cli/default-skills.d.ts +0 -10
- package/dist/cli/default-skills.d.ts.map +0 -1
- package/dist/cli/default-skills.js +0 -206
- package/dist/cli/default-skills.js.map +0 -1
- package/dist/cli/interactive.d.ts +0 -2
- package/dist/cli/interactive.d.ts.map +0 -1
- package/dist/cli/interactive.js +0 -20
- package/dist/cli/interactive.js.map +0 -1
- package/dist/cli/mcp.d.ts +0 -56
- package/dist/cli/mcp.d.ts.map +0 -1
- package/dist/cli/mcp.js +0 -138
- package/dist/cli/mcp.js.map +0 -1
- package/dist/cli/prompt.d.ts +0 -2
- package/dist/cli/prompt.d.ts.map +0 -1
- package/dist/cli/prompt.js +0 -2
- package/dist/cli/prompt.js.map +0 -1
- package/dist/cli/search.d.ts +0 -19
- package/dist/cli/search.d.ts.map +0 -1
- package/dist/cli/search.js +0 -189
- package/dist/cli/search.js.map +0 -1
- package/dist/cli/tool-args.d.ts +0 -2
- package/dist/cli/tool-args.d.ts.map +0 -1
- package/dist/cli/tool-args.js +0 -39
- package/dist/cli/tool-args.js.map +0 -1
- package/dist/cli/tools.d.ts +0 -36
- package/dist/cli/tools.d.ts.map +0 -1
- package/dist/cli/tools.js +0 -229
- package/dist/cli/tools.js.map +0 -1
- package/dist/cli/tui/index.d.ts +0 -22
- package/dist/cli/tui/index.d.ts.map +0 -1
- package/dist/cli/tui/index.js +0 -29
- package/dist/cli/tui/index.js.map +0 -1
- package/dist/cli/tui.d.ts +0 -167
- package/dist/cli/tui.d.ts.map +0 -1
- package/dist/cli/tui.js +0 -5228
- package/dist/cli/tui.js.map +0 -1
- package/dist/compiler_prompt.d.ts +0 -20
- package/dist/compiler_prompt.d.ts.map +0 -1
- package/dist/compiler_prompt.js +0 -870
- package/dist/compiler_prompt.js.map +0 -1
- package/dist/deepclause-cli.skill +0 -0
- package/dist/prolog-src/deepclause_mi.pl.bak +0 -570
- package/dist/system/assets/docs/CONDUCTOR_PROMPT.md +0 -105
- package/dist/system/assets/docs/DML_COMPILER_PROMPT.md +0 -700
- package/dist/system/assets/docs/TUI.md +0 -150
- package/dist/system/assets/recipes/deepclause-coding-workflow/SKILL.md +0 -100
- package/dist/system/assets/skills/conductor.dml +0 -76
- package/dist/system/assets/skills/deep-planner.dml +0 -412
- package/dist/system/assets/skills/plan.dml +0 -429
- package/dist/system/assets/skills/security-planner.dml +0 -337
- package/dist/system/assets/skills/skill-creator.dml +0 -187
- package/dist/system/runtime/catalog-recipes.d.ts +0 -24
- package/dist/system/runtime/catalog-recipes.d.ts.map +0 -1
- package/dist/system/runtime/catalog-recipes.js +0 -273
- package/dist/system/runtime/catalog-recipes.js.map +0 -1
- package/dist/system/runtime/catalog-skills.d.ts +0 -50
- package/dist/system/runtime/catalog-skills.d.ts.map +0 -1
- package/dist/system/runtime/catalog-skills.js +0 -152
- package/dist/system/runtime/catalog-skills.js.map +0 -1
- package/dist/system/runtime/conductor.d.ts +0 -105
- package/dist/system/runtime/conductor.d.ts.map +0 -1
- package/dist/system/runtime/conductor.js +0 -732
- package/dist/system/runtime/conductor.js.map +0 -1
- package/dist/system/runtime/console-capture.d.ts +0 -9
- package/dist/system/runtime/console-capture.d.ts.map +0 -1
- package/dist/system/runtime/console-capture.js +0 -67
- package/dist/system/runtime/console-capture.js.map +0 -1
- package/dist/system/runtime/dml-executor.d.ts +0 -52
- package/dist/system/runtime/dml-executor.d.ts.map +0 -1
- package/dist/system/runtime/dml-executor.js +0 -203
- package/dist/system/runtime/dml-executor.js.map +0 -1
- package/dist/system/runtime/runtime-tools.d.ts +0 -31
- package/dist/system/runtime/runtime-tools.d.ts.map +0 -1
- package/dist/system/runtime/runtime-tools.js +0 -312
- package/dist/system/runtime/runtime-tools.js.map +0 -1
- package/dist/system/runtime/skill-creator.d.ts +0 -38
- package/dist/system/runtime/skill-creator.d.ts.map +0 -1
- package/dist/system/runtime/skill-creator.js +0 -763
- package/dist/system/runtime/skill-creator.js.map +0 -1
- package/src/prolog-src/deepclause_mi.pl.bak +0 -570
package/dist/runner.js
CHANGED
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* DML Runner - Executes DML code using SWI-Prolog WASM
|
|
3
3
|
*/
|
|
4
|
-
import * as fs from 'fs/promises';
|
|
5
|
-
import * as path from 'path';
|
|
6
4
|
import { mountWorkspace } from './prolog/loader.js';
|
|
7
5
|
import { runAgentLoop } from './agent.js';
|
|
8
6
|
import { judgeAnswersToValues, judgeCacheKey, normalizeJudgeState, parseJudgeQuestions, validateJudgeAnswers, valuesToPrologList } from './judge/marshal.js';
|
|
9
|
-
import { executeCompactor, getCompactionBindings, resolveCompactionOptions, } from './compaction.js';
|
|
10
7
|
import { buildToolCompletionEvent, buildToolFailureEvent, buildToolStartEvent, } from './system/runtime/shell-tool-events.js';
|
|
11
8
|
import { checkToolPolicy } from './tools.js';
|
|
12
9
|
import { generateLlmReply, sampleSingleToken } from './prolog/bridge.js';
|
|
13
|
-
import { recordTokenUsage } from './system/runtime/token-usage.js';
|
|
14
10
|
import { buildReasoningProviderOptions } from './system/config/model-database.js';
|
|
11
|
+
import { resolveMediaRefs } from './system/runtime/media.js';
|
|
15
12
|
/**
|
|
16
13
|
* Convert swipl-wasm value to plain JavaScript value
|
|
17
14
|
* Handles PrologString, PrologAtom, PrologList, etc.
|
|
18
15
|
*/
|
|
19
|
-
function toJsValue(value) {
|
|
16
|
+
export function toJsValue(value) {
|
|
20
17
|
if (value === null || value === undefined) {
|
|
21
18
|
return value;
|
|
22
19
|
}
|
|
@@ -60,45 +57,45 @@ function toJsValue(value) {
|
|
|
60
57
|
}
|
|
61
58
|
return value;
|
|
62
59
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Decode the `bindings` payload of a goal-mode `solution` event.
|
|
62
|
+
*
|
|
63
|
+
* `collect_bindings/2` yields a list of `Name-Text` pairs. swipl-wasm encodes
|
|
64
|
+
* that compound as `{ '$t': 't', '-': [[Name, Text]] }`; a dict/array form is
|
|
65
|
+
* also accepted for forward compatibility.
|
|
66
|
+
*/
|
|
67
|
+
export function decodeSolutionBindings(raw) {
|
|
68
|
+
const normalized = toJsValue(raw);
|
|
69
|
+
if (!Array.isArray(normalized)) {
|
|
70
|
+
return [];
|
|
73
71
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
72
|
+
const bindings = [];
|
|
73
|
+
for (const item of normalized) {
|
|
74
|
+
const decoded = toJsValue(item);
|
|
75
|
+
if (decoded && typeof decoded === 'object') {
|
|
76
|
+
const compound = decoded;
|
|
77
|
+
const args = compound['-'];
|
|
78
|
+
if (Array.isArray(args) && Array.isArray(args[0]) && args[0].length >= 2) {
|
|
79
|
+
const [name, text] = args[0];
|
|
80
|
+
bindings.push({ name: String(toJsValue(name) ?? ''), value: String(toJsValue(text) ?? '') });
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if ('name' in compound || 'value' in compound) {
|
|
84
|
+
bindings.push({
|
|
85
|
+
name: String(toJsValue(compound.name) ?? ''),
|
|
86
|
+
value: String(toJsValue(compound.value) ?? ''),
|
|
87
|
+
});
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
82
90
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
91
|
+
if (Array.isArray(decoded) && decoded.length >= 2) {
|
|
92
|
+
bindings.push({
|
|
93
|
+
name: String(toJsValue(decoded[0]) ?? ''),
|
|
94
|
+
value: String(toJsValue(decoded[1]) ?? ''),
|
|
95
|
+
});
|
|
87
96
|
}
|
|
88
97
|
}
|
|
89
|
-
|
|
90
|
-
if (timeoutMs > 0) {
|
|
91
|
-
timeout = setTimeout(() => controller.abort(new Error(`Compactor timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
92
|
-
disposers.push(() => clearTimeout(timeout));
|
|
93
|
-
}
|
|
94
|
-
return {
|
|
95
|
-
signal: controller.signal,
|
|
96
|
-
dispose: () => {
|
|
97
|
-
for (const dispose of disposers) {
|
|
98
|
-
dispose();
|
|
99
|
-
}
|
|
100
|
-
},
|
|
101
|
-
};
|
|
98
|
+
return bindings;
|
|
102
99
|
}
|
|
103
100
|
export function decodeAgentOutputVars(rawOutputVars) {
|
|
104
101
|
const normalized = toJsValue(rawOutputVars);
|
|
@@ -142,11 +139,18 @@ function prologTypeToJsonType(prologType) {
|
|
|
142
139
|
export class DMLRunner {
|
|
143
140
|
swipl;
|
|
144
141
|
options;
|
|
145
|
-
engine = null;
|
|
146
142
|
sessionId = '';
|
|
147
143
|
currentMemory = [];
|
|
148
144
|
stepCounter = 0;
|
|
149
145
|
judgeCache = new Map();
|
|
146
|
+
/**
|
|
147
|
+
* Runner-owned frame table: FrameId -> { frame, engine }.
|
|
148
|
+
* The Prolog session owns the authoritative engine handles
|
|
149
|
+
* (session_frame_engine/2); this map mirrors them for the driver.
|
|
150
|
+
*/
|
|
151
|
+
frames = new Map();
|
|
152
|
+
/** FrameId of the session's default query frame (created by createEngine). */
|
|
153
|
+
defaultFrameId = null;
|
|
150
154
|
constructor(swipl, options) {
|
|
151
155
|
this.swipl = swipl;
|
|
152
156
|
this.options = options;
|
|
@@ -191,94 +195,18 @@ export class DMLRunner {
|
|
|
191
195
|
yield { type: 'error', content: parseResult.error };
|
|
192
196
|
return;
|
|
193
197
|
}
|
|
194
|
-
// Create cooperative engine with args
|
|
198
|
+
// Create cooperative engine with args (registers the default query frame)
|
|
195
199
|
const engineResult = this.createEngine(memoryId, args, params);
|
|
196
200
|
if (engineResult.error) {
|
|
197
201
|
yield { type: 'error', content: engineResult.error };
|
|
198
202
|
return;
|
|
199
203
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const reason = options.signal.reason;
|
|
205
|
-
const abortMessage = reason instanceof Error
|
|
206
|
-
? reason.message
|
|
207
|
-
: typeof reason === 'string' && reason.trim().length > 0
|
|
208
|
-
? reason
|
|
209
|
-
: 'Execution aborted';
|
|
210
|
-
yield { type: 'error', content: abortMessage };
|
|
211
|
-
break;
|
|
212
|
-
}
|
|
213
|
-
// Step the engine
|
|
214
|
-
const step = this.stepEngine();
|
|
215
|
-
if (process.env.DEBUG_STEPS) {
|
|
216
|
-
console.log('[MAIN_LOOP] step:', step.status, step.content?.substring(0, 50));
|
|
217
|
-
}
|
|
218
|
-
switch (step.status) {
|
|
219
|
-
case 'output':
|
|
220
|
-
if (step.content) {
|
|
221
|
-
if (process.env.DEBUG_STEPS) {
|
|
222
|
-
console.log('[MAIN_LOOP] yielding output:', step.content.substring(0, 50));
|
|
223
|
-
}
|
|
224
|
-
yield { type: 'output', content: step.content };
|
|
225
|
-
}
|
|
226
|
-
break;
|
|
227
|
-
case 'log':
|
|
228
|
-
if (step.content) {
|
|
229
|
-
yield { type: 'log', content: step.content };
|
|
230
|
-
}
|
|
231
|
-
break;
|
|
232
|
-
case 'answer':
|
|
233
|
-
yield { type: 'answer', content: step.content };
|
|
234
|
-
break;
|
|
235
|
-
case 'request_agent_loop':
|
|
236
|
-
// Run agent loop for task() predicate
|
|
237
|
-
yield* this.handleAgentLoop(step.payload, memoryId, options);
|
|
238
|
-
break;
|
|
239
|
-
case 'request_exec':
|
|
240
|
-
// Execute external tool
|
|
241
|
-
yield* this.handleExec(step.payload, options);
|
|
242
|
-
break;
|
|
243
|
-
case 'request_sample_token':
|
|
244
|
-
yield* this.handleSampleToken(step.payload, options);
|
|
245
|
-
break;
|
|
246
|
-
case 'request_llm':
|
|
247
|
-
yield* this.handleLlm(step.payload, options);
|
|
248
|
-
break;
|
|
249
|
-
case 'request_judgment':
|
|
250
|
-
yield* this.handleJudgment(step.payload, options);
|
|
251
|
-
break;
|
|
252
|
-
case 'wait_input':
|
|
253
|
-
// Request user input
|
|
254
|
-
yield { type: 'input_required', prompt: step.prompt };
|
|
255
|
-
const input = await options.onInputRequired(step.prompt ?? '');
|
|
256
|
-
this.provideInput(input);
|
|
257
|
-
break;
|
|
258
|
-
case 'finished':
|
|
259
|
-
// Include trace data if trace mode was enabled
|
|
260
|
-
if (this.options.trace && step.payload) {
|
|
261
|
-
const traceData = toJsValue(step.payload.trace);
|
|
262
|
-
yield { type: 'finished', trace: Array.isArray(traceData) ? traceData : undefined };
|
|
263
|
-
}
|
|
264
|
-
else {
|
|
265
|
-
yield { type: 'finished' };
|
|
266
|
-
}
|
|
267
|
-
return;
|
|
268
|
-
case 'error':
|
|
269
|
-
if (this.options.trace && step.payload) {
|
|
270
|
-
const traceData = toJsValue(step.payload.trace);
|
|
271
|
-
yield { type: 'error', content: step.content, trace: Array.isArray(traceData) ? traceData : undefined };
|
|
272
|
-
}
|
|
273
|
-
else {
|
|
274
|
-
yield { type: 'error', content: step.content };
|
|
275
|
-
}
|
|
276
|
-
return;
|
|
277
|
-
default:
|
|
278
|
-
yield { type: 'error', content: `Unknown step status: ${step.status}` };
|
|
279
|
-
return;
|
|
280
|
-
}
|
|
204
|
+
const frame = this.defaultFrameId ? this.frames.get(this.defaultFrameId)?.frame : undefined;
|
|
205
|
+
if (!frame) {
|
|
206
|
+
yield { type: 'error', content: 'Failed to register default query frame' };
|
|
207
|
+
return;
|
|
281
208
|
}
|
|
209
|
+
yield* this.stepLoop(frame, options);
|
|
282
210
|
}
|
|
283
211
|
catch (error) {
|
|
284
212
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -289,6 +217,164 @@ export class DMLRunner {
|
|
|
289
217
|
this.cleanup();
|
|
290
218
|
}
|
|
291
219
|
}
|
|
220
|
+
/**
|
|
221
|
+
* Drive a single frame until it finishes or errors, yielding its events.
|
|
222
|
+
*
|
|
223
|
+
* This is the extracted, reentrant form of the original `while(true)` loop.
|
|
224
|
+
* `run()` calls it for the default query frame; `runFrame` calls it for
|
|
225
|
+
* nested frames. Because frames are read from the Prolog frame table via
|
|
226
|
+
* `step_frame/4`, stepping a different frame never disturbs a suspended
|
|
227
|
+
* parent's engine.
|
|
228
|
+
*/
|
|
229
|
+
async *stepLoop(frame, options) {
|
|
230
|
+
for await (const event of this.stepLoopInner(frame, options)) {
|
|
231
|
+
// Preserve frame metadata already attached by a nested frame; otherwise
|
|
232
|
+
// stamp this frame so the renderer can attribute output correctly.
|
|
233
|
+
yield {
|
|
234
|
+
...event,
|
|
235
|
+
frameId: event.frameId ?? frame.id,
|
|
236
|
+
depth: event.depth ?? frame.depth,
|
|
237
|
+
frameKind: event.frameKind ?? frame.kind,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
async *stepLoopInner(frame, options) {
|
|
242
|
+
// Cooperative execution loop
|
|
243
|
+
while (true) {
|
|
244
|
+
// Check for abort
|
|
245
|
+
if (options.signal?.aborted) {
|
|
246
|
+
const reason = options.signal.reason;
|
|
247
|
+
const abortMessage = reason instanceof Error
|
|
248
|
+
? reason.message
|
|
249
|
+
: typeof reason === 'string' && reason.trim().length > 0
|
|
250
|
+
? reason
|
|
251
|
+
: 'Execution aborted';
|
|
252
|
+
yield { type: 'error', content: abortMessage };
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
// Step the frame
|
|
256
|
+
const step = this.stepFrame(frame.id);
|
|
257
|
+
if (process.env.DEBUG_STEPS) {
|
|
258
|
+
console.log('[MAIN_LOOP] step:', step.status, step.content?.substring(0, 50));
|
|
259
|
+
}
|
|
260
|
+
switch (step.status) {
|
|
261
|
+
case 'output':
|
|
262
|
+
if (step.content) {
|
|
263
|
+
if (process.env.DEBUG_STEPS) {
|
|
264
|
+
console.log('[MAIN_LOOP] yielding output:', step.content.substring(0, 50));
|
|
265
|
+
}
|
|
266
|
+
yield { type: 'output', content: step.content };
|
|
267
|
+
}
|
|
268
|
+
break;
|
|
269
|
+
case 'log':
|
|
270
|
+
if (step.content) {
|
|
271
|
+
yield { type: 'log', content: step.content };
|
|
272
|
+
}
|
|
273
|
+
break;
|
|
274
|
+
case 'answer':
|
|
275
|
+
yield { type: 'answer', content: step.content };
|
|
276
|
+
break;
|
|
277
|
+
case 'solution': {
|
|
278
|
+
const payload = (step.payload ?? {});
|
|
279
|
+
yield {
|
|
280
|
+
type: 'solution',
|
|
281
|
+
solution: {
|
|
282
|
+
bindings: decodeSolutionBindings(payload.bindings),
|
|
283
|
+
deterministic: String(toJsValue(payload.deterministic) ?? 'false') === 'true',
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
case 'request_agent_loop':
|
|
289
|
+
// Run agent loop for task() predicate. The frame kind decides the
|
|
290
|
+
// tool set: `meta` frames get the host meta tools and hide the
|
|
291
|
+
// harness's own DML tools (proposal §9.2). Budget exhaustion (and any
|
|
292
|
+
// other agent-loop error) is surfaced as an error event.
|
|
293
|
+
try {
|
|
294
|
+
yield* this.handleAgentLoop(step.payload, '', options, frame);
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
yield { type: 'error', content: error instanceof Error ? error.message : String(error) };
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
break;
|
|
301
|
+
case 'meta_result': {
|
|
302
|
+
const payload = (step.payload ?? {});
|
|
303
|
+
yield {
|
|
304
|
+
type: 'meta_result',
|
|
305
|
+
metaResult: {
|
|
306
|
+
code: String(toJsValue(payload.code) ?? ''),
|
|
307
|
+
answer: String(toJsValue(payload.answer) ?? ''),
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
case 'request_exec':
|
|
313
|
+
// Execute external tool
|
|
314
|
+
yield* this.handleExec(step.payload, options);
|
|
315
|
+
break;
|
|
316
|
+
case 'request_sample_token':
|
|
317
|
+
yield* this.handleSampleToken(step.payload, options);
|
|
318
|
+
break;
|
|
319
|
+
case 'request_llm':
|
|
320
|
+
yield* this.handleLlm(step.payload, options);
|
|
321
|
+
break;
|
|
322
|
+
case 'request_judgment':
|
|
323
|
+
yield* this.handleJudgment(step.payload, options);
|
|
324
|
+
break;
|
|
325
|
+
case 'wait_input': {
|
|
326
|
+
// Request user input
|
|
327
|
+
yield { type: 'input_required', prompt: step.prompt };
|
|
328
|
+
const input = await options.onInputRequired(step.prompt ?? '');
|
|
329
|
+
if (input === undefined || input === null) {
|
|
330
|
+
this.cancelInput();
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
this.provideInput(input);
|
|
334
|
+
}
|
|
335
|
+
break;
|
|
336
|
+
}
|
|
337
|
+
case 'finished':
|
|
338
|
+
// Include trace data if trace mode was enabled
|
|
339
|
+
if (this.options.trace && step.payload) {
|
|
340
|
+
const traceData = toJsValue(step.payload.trace);
|
|
341
|
+
yield { type: 'finished', trace: Array.isArray(traceData) ? traceData : undefined };
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
yield { type: 'finished' };
|
|
345
|
+
}
|
|
346
|
+
return;
|
|
347
|
+
case 'error':
|
|
348
|
+
if (this.options.trace && step.payload) {
|
|
349
|
+
const traceData = toJsValue(step.payload.trace);
|
|
350
|
+
yield { type: 'error', content: step.content, trace: Array.isArray(traceData) ? traceData : undefined };
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
yield { type: 'error', content: step.content };
|
|
354
|
+
}
|
|
355
|
+
return;
|
|
356
|
+
default:
|
|
357
|
+
yield { type: 'error', content: `Unknown step status: ${step.status}` };
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Run a specific frame to completion, yielding its events.
|
|
364
|
+
*
|
|
365
|
+
* This is the reentrant entry point used by nested execution (the REPL's
|
|
366
|
+
* `execute` meta tool): the caller builds a child frame with
|
|
367
|
+
* `createNestedFrame`, runs it with this method while the parent frame is
|
|
368
|
+
* suspended, then posts the result back to the parent.
|
|
369
|
+
*/
|
|
370
|
+
async *runFrame(frameId, options) {
|
|
371
|
+
const frame = this.frames.get(frameId)?.frame;
|
|
372
|
+
if (!frame) {
|
|
373
|
+
yield { type: 'error', content: `Unknown frame: ${frameId}` };
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
yield* this.stepLoop(frame, options);
|
|
377
|
+
}
|
|
292
378
|
/**
|
|
293
379
|
* Parse DML code into Prolog clauses
|
|
294
380
|
*/
|
|
@@ -381,10 +467,15 @@ export class DMLRunner {
|
|
|
381
467
|
createEngine(memoryId, args, params) {
|
|
382
468
|
try {
|
|
383
469
|
const initialMemory = this.serializeMemoryMessages(this.currentMemory);
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
470
|
+
// create_engine/6 registers the default `query` frame; default_frame/2
|
|
471
|
+
// returns its FrameId so the runner can address it explicitly.
|
|
472
|
+
const result = this.query(`deepclause_mi:create_engine('${this.sessionId}', '${memoryId}', ${args}, ${params}, [${initialMemory}], Engine), deepclause_mi:default_frame('${this.sessionId}', FrameId)`);
|
|
473
|
+
if (result && result.Engine && result.FrameId) {
|
|
474
|
+
const frameId = String(toJsValue(result.FrameId));
|
|
475
|
+
const frame = { id: frameId, kind: 'query', depth: 0 };
|
|
476
|
+
this.frames.set(frameId, { frame, engine: result.Engine });
|
|
477
|
+
this.defaultFrameId = frameId;
|
|
478
|
+
return { frameId };
|
|
388
479
|
}
|
|
389
480
|
return { error: 'Failed to create engine' };
|
|
390
481
|
}
|
|
@@ -394,11 +485,93 @@ export class DMLRunner {
|
|
|
394
485
|
}
|
|
395
486
|
}
|
|
396
487
|
/**
|
|
397
|
-
*
|
|
488
|
+
* Create a nested frame that runs a Prolog goal in this session.
|
|
489
|
+
*
|
|
490
|
+
* `goalTerm` is inserted verbatim into create_frame/6, so callers MUST pass
|
|
491
|
+
* a term built with the runner's `toPrologTerm` escaping or parsed from DML
|
|
492
|
+
* via `read_term` — never raw user text (foot gun #18). `stateTerm` is an
|
|
493
|
+
* optional Prolog `state{...}` term; the default is an empty state with no
|
|
494
|
+
* gas limit.
|
|
495
|
+
*
|
|
496
|
+
* Returns the registered frame, or null on failure. The caller is
|
|
497
|
+
* responsible for running (`runFrame`) and ending (`endFrame`) it.
|
|
398
498
|
*/
|
|
399
|
-
|
|
499
|
+
createNestedFrame(params) {
|
|
500
|
+
const depth = params.depth ?? 1;
|
|
501
|
+
const stateTerm = params.stateTerm
|
|
502
|
+
?? 'state{memory: [], params: params{}, context_stack: [], depth: 0}';
|
|
503
|
+
const result = this.query(`deepclause_mi:create_frame('${this.sessionId}', ${params.kind}, ${depth}, ${params.goalTerm}, ${stateTerm}, FrameId)`);
|
|
504
|
+
if (!result || !result.FrameId) {
|
|
505
|
+
return null;
|
|
506
|
+
}
|
|
507
|
+
const id = String(toJsValue(result.FrameId));
|
|
508
|
+
const frame = { id, kind: params.kind, depth };
|
|
509
|
+
this.frames.set(id, { frame, engine: null });
|
|
510
|
+
return frame;
|
|
511
|
+
}
|
|
512
|
+
/** End a frame, destroying its engine in the Prolog session. */
|
|
513
|
+
endFrame(frameId) {
|
|
514
|
+
this.query(`deepclause_mi:end_frame('${frameId}')`);
|
|
515
|
+
this.frames.delete(frameId);
|
|
516
|
+
if (this.defaultFrameId === frameId) {
|
|
517
|
+
this.defaultFrameId = null;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Bind this runner to an existing session id. Used by the REPL's
|
|
522
|
+
* DMLSession, whose session outlives a single `run()`.
|
|
523
|
+
*/
|
|
524
|
+
bindSession(sessionId) {
|
|
525
|
+
this.sessionId = sessionId;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Adopt the session's default frame (for example one created by the REPL's
|
|
529
|
+
* `query_start/5`) into the runner's frame table so `runFrame` can drive it.
|
|
530
|
+
*/
|
|
531
|
+
registerDefaultFrame(kind, depth = 0) {
|
|
532
|
+
const result = this.query(`deepclause_mi:default_frame('${this.sessionId}', FrameId)`);
|
|
533
|
+
if (!result || !result.FrameId) {
|
|
534
|
+
return null;
|
|
535
|
+
}
|
|
536
|
+
const id = String(toJsValue(result.FrameId));
|
|
537
|
+
const frame = { id, kind, depth };
|
|
538
|
+
this.frames.set(id, { frame, engine: null });
|
|
539
|
+
this.defaultFrameId = id;
|
|
540
|
+
return frame;
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Adopt a Prolog-created frame into the runner's frame table. Used by the
|
|
544
|
+
* REPL for `execute` child frames (`repl_execute_frame/4`).
|
|
545
|
+
*/
|
|
546
|
+
registerFrame(frameId, kind, depth = 0) {
|
|
547
|
+
const frame = { id: frameId, kind, depth };
|
|
548
|
+
this.frames.set(frameId, { frame, engine: null });
|
|
549
|
+
return frame;
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Drop all runner-side frame bookkeeping without touching Prolog. Used after
|
|
553
|
+
* the REPL clears a session (the Prolog side already ended every frame).
|
|
554
|
+
*/
|
|
555
|
+
clearFrames() {
|
|
556
|
+
this.frames.clear();
|
|
557
|
+
this.defaultFrameId = null;
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Step a specific frame exactly once and return its raw step result.
|
|
561
|
+
* Used by the reentrant driver and nested-execution tests.
|
|
562
|
+
*/
|
|
563
|
+
stepFrameOnce(frameId) {
|
|
564
|
+
if (!this.frames.has(frameId)) {
|
|
565
|
+
return null;
|
|
566
|
+
}
|
|
567
|
+
return this.stepFrame(frameId);
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Step the cooperative engine for a frame
|
|
571
|
+
*/
|
|
572
|
+
stepFrame(frameId) {
|
|
400
573
|
try {
|
|
401
|
-
const result = this.query(`deepclause_mi:
|
|
574
|
+
const result = this.query(`deepclause_mi:step_frame('${frameId}', Status, Content, Payload)`);
|
|
402
575
|
// Convert swipl-wasm values to plain JS
|
|
403
576
|
const status = toJsValue(result?.Status);
|
|
404
577
|
const content = toJsValue(result?.Content);
|
|
@@ -419,13 +592,15 @@ export class DMLRunner {
|
|
|
419
592
|
* Handle agent loop request (task() predicate)
|
|
420
593
|
*/
|
|
421
594
|
async *handleAgentLoop(payload, _memoryId, // No longer used - memory comes from payload
|
|
422
|
-
options) {
|
|
595
|
+
options, _frame) {
|
|
423
596
|
const rawPayload = payload;
|
|
424
597
|
const taskDescription = String(toJsValue(rawPayload.taskDescription) ?? '');
|
|
425
598
|
const stepId = `step_${this.stepCounter++}`;
|
|
426
599
|
yield { type: 'task_activity', taskState: 'started', taskDescription, taskId: stepId };
|
|
427
600
|
let stepSucceeded = null;
|
|
428
601
|
try {
|
|
602
|
+
// The per-model-call budget is enforced inside the agent loop (before each
|
|
603
|
+
// model call) so it also covers multi-iteration turns.
|
|
429
604
|
// Ensure outputVars is an array of either strings or TypedVar objects
|
|
430
605
|
const outputVars = decodeAgentOutputVars(rawPayload.outputVars);
|
|
431
606
|
// Parse userTools - now contains schema info as array of tool_info dicts
|
|
@@ -503,6 +678,9 @@ export class DMLRunner {
|
|
|
503
678
|
try {
|
|
504
679
|
emitToolEvent({ type: 'input_required', prompt });
|
|
505
680
|
const response = await options.onInputRequired(prompt);
|
|
681
|
+
if (response === undefined || response === null) {
|
|
682
|
+
return { cancelled: true };
|
|
683
|
+
}
|
|
506
684
|
return { user_response: response };
|
|
507
685
|
}
|
|
508
686
|
catch (err) {
|
|
@@ -515,14 +693,15 @@ export class DMLRunner {
|
|
|
515
693
|
const emitToolCall = (toolName, args) => {
|
|
516
694
|
emitToolEvent({ type: 'tool_call', toolName, toolArgs: args });
|
|
517
695
|
};
|
|
518
|
-
// Build available tools for agent
|
|
519
|
-
//
|
|
696
|
+
// Build available tools for the agent: the harness's DML tool/2 predicates
|
|
697
|
+
// plus registered TS tools (reachable via exec/2).
|
|
698
|
+
const selfReportingTools = undefined;
|
|
520
699
|
const availableTools = this.buildAgentTools(userTools, options.toolPolicy, async (toolName, args) => {
|
|
521
700
|
return this.executeToolIsolated(toolName, args, augmentedTools, onToolOutput, {
|
|
522
701
|
workspacePath: options.workspacePath,
|
|
523
702
|
signal: options.signal,
|
|
524
|
-
compaction: options.compaction,
|
|
525
703
|
toolPolicy: options.toolPolicy,
|
|
704
|
+
onModelCall: options.onModelCall,
|
|
526
705
|
}, [], emitToolCall, emitToolEvent);
|
|
527
706
|
});
|
|
528
707
|
// Run agent loop with streaming support
|
|
@@ -532,6 +711,7 @@ export class DMLRunner {
|
|
|
532
711
|
memory: effectiveMemory,
|
|
533
712
|
tools: availableTools,
|
|
534
713
|
workspacePath: options.workspacePath,
|
|
714
|
+
resolveMedia: (refs) => this.readMediaRefs(options.workspacePath, refs),
|
|
535
715
|
modelOptions: {
|
|
536
716
|
model: this.options.model,
|
|
537
717
|
provider: this.options.provider,
|
|
@@ -550,6 +730,13 @@ export class DMLRunner {
|
|
|
550
730
|
streamResolve = null;
|
|
551
731
|
}
|
|
552
732
|
},
|
|
733
|
+
onReasoning: (chunk, done) => {
|
|
734
|
+
streamQueue.push({ type: 'reasoning', content: chunk, done });
|
|
735
|
+
if (streamResolve) {
|
|
736
|
+
streamResolve();
|
|
737
|
+
streamResolve = null;
|
|
738
|
+
}
|
|
739
|
+
},
|
|
553
740
|
onToolCall: (toolName, args) => {
|
|
554
741
|
streamQueue.push({ type: 'tool_call', toolName, toolArgs: args });
|
|
555
742
|
if (streamResolve) {
|
|
@@ -564,22 +751,27 @@ export class DMLRunner {
|
|
|
564
751
|
streamResolve = null;
|
|
565
752
|
}
|
|
566
753
|
},
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
}
|
|
754
|
+
onModelCallPhase: (phase) => {
|
|
755
|
+
streamQueue.push({ type: 'model_call', modelCallState: phase, modelId: this.options.model });
|
|
756
|
+
if (streamResolve) {
|
|
757
|
+
streamResolve();
|
|
758
|
+
streamResolve = null;
|
|
759
|
+
}
|
|
760
|
+
},
|
|
761
|
+
selfReportingTools,
|
|
762
|
+
onToolResult: (toolName, toolArgs, toolResult) => {
|
|
763
|
+
streamQueue.push(buildToolCompletionEvent(toolName, toolArgs, toolResult));
|
|
764
|
+
if (streamResolve) {
|
|
765
|
+
streamResolve();
|
|
766
|
+
streamResolve = null;
|
|
767
|
+
}
|
|
768
|
+
},
|
|
769
|
+
onBeforeModelCall: async (messages, lastInputTokens) => {
|
|
770
|
+
const hostCompacted = await this.applyHostCompaction(messages, lastInputTokens, emitRunnerEvent);
|
|
771
|
+
return hostCompacted ?? messages;
|
|
772
|
+
},
|
|
582
773
|
onAskUser: options.onInputRequired,
|
|
774
|
+
onModelCall: options.onModelCall,
|
|
583
775
|
signal: options.signal,
|
|
584
776
|
llmBackend: this.options.llmBackend,
|
|
585
777
|
});
|
|
@@ -729,6 +921,7 @@ export class DMLRunner {
|
|
|
729
921
|
},
|
|
730
922
|
signal: options.signal,
|
|
731
923
|
llmBackend: this.options.llmBackend,
|
|
924
|
+
resolveMedia: (refs) => this.readMediaRefs(options.workspacePath, refs),
|
|
732
925
|
});
|
|
733
926
|
succeeded = true;
|
|
734
927
|
this.postLlmResult({ success: true, text });
|
|
@@ -859,15 +1052,18 @@ export class DMLRunner {
|
|
|
859
1052
|
*/
|
|
860
1053
|
async *handleExec(payload, options) {
|
|
861
1054
|
const { toolName, args } = payload;
|
|
862
|
-
// Check tool policy
|
|
1055
|
+
// Check tool policy (ask_user is the human channel and is exempt).
|
|
863
1056
|
const policyCheck = checkToolPolicy(toolName, options.toolPolicy);
|
|
864
1057
|
if (!policyCheck.allowed) {
|
|
865
1058
|
yield { type: 'log', content: `Tool '${toolName}' blocked by policy: ${policyCheck.reason}` };
|
|
866
1059
|
this.postExecResult({ success: false, error: policyCheck.reason });
|
|
867
1060
|
return;
|
|
868
1061
|
}
|
|
869
|
-
// Get tool definition
|
|
870
|
-
|
|
1062
|
+
// Get tool definition. `ask_user` is SDK-internal so exec(ask_user(...))
|
|
1063
|
+
// works in DML even when no capability tools are registered.
|
|
1064
|
+
const tool = toolName === 'ask_user'
|
|
1065
|
+
? this.internalAskUserTool(options)
|
|
1066
|
+
: options.tools.get(toolName);
|
|
871
1067
|
if (!tool) {
|
|
872
1068
|
yield { type: 'log', content: `Tool '${toolName}' not found` };
|
|
873
1069
|
this.postExecResult({ success: false, error: `Tool not found: ${toolName}` });
|
|
@@ -879,7 +1075,7 @@ export class DMLRunner {
|
|
|
879
1075
|
argsObj = this.argsToObject(args, tool);
|
|
880
1076
|
// Emit tool_call event before execution
|
|
881
1077
|
yield buildToolStartEvent(toolName, argsObj);
|
|
882
|
-
const result = await tool.execute(argsObj);
|
|
1078
|
+
const result = await tool.execute(argsObj, { signal: options.signal });
|
|
883
1079
|
yield buildToolCompletionEvent(toolName, argsObj, result);
|
|
884
1080
|
// Post result back to Prolog
|
|
885
1081
|
this.postExecResult({ success: true, result });
|
|
@@ -1172,7 +1368,7 @@ export class DMLRunner {
|
|
|
1172
1368
|
try {
|
|
1173
1369
|
argsObj = this.argsToObject(execArgs, tool);
|
|
1174
1370
|
onToolEvent?.(buildToolStartEvent(execToolName, argsObj));
|
|
1175
|
-
const execResult = await tool.execute(argsObj);
|
|
1371
|
+
const execResult = await tool.execute(argsObj, { signal: runContext.signal });
|
|
1176
1372
|
onToolEvent?.(buildToolCompletionEvent(execToolName, argsObj, execResult));
|
|
1177
1373
|
const postGoal = `deepclause_mi:post_exec_result('${this.sessionId}', success, ${this.toPrologTerm(execResult)})`;
|
|
1178
1374
|
this.query(postGoal);
|
|
@@ -1243,6 +1439,7 @@ export class DMLRunner {
|
|
|
1243
1439
|
},
|
|
1244
1440
|
signal: runContext.signal,
|
|
1245
1441
|
llmBackend: this.options.llmBackend,
|
|
1442
|
+
resolveMedia: (refs) => this.readMediaRefs(runContext.workspacePath, refs),
|
|
1246
1443
|
});
|
|
1247
1444
|
this.postLlmResult({ success: true, text });
|
|
1248
1445
|
if (usage) {
|
|
@@ -1436,6 +1633,7 @@ export class DMLRunner {
|
|
|
1436
1633
|
streaming: this.options.streaming,
|
|
1437
1634
|
debug: this.options.debug,
|
|
1438
1635
|
llmBackend: this.options.llmBackend,
|
|
1636
|
+
resolveMedia: (refs) => this.readMediaRefs(runContext.workspacePath, refs),
|
|
1439
1637
|
onOutput: (text) => {
|
|
1440
1638
|
outputs.push(text);
|
|
1441
1639
|
onOutput(text);
|
|
@@ -1443,26 +1641,26 @@ export class DMLRunner {
|
|
|
1443
1641
|
onStream: (chunk, done) => {
|
|
1444
1642
|
onToolEvent?.({ type: 'stream', content: chunk, done });
|
|
1445
1643
|
},
|
|
1644
|
+
onReasoning: (chunk, done) => {
|
|
1645
|
+
onToolEvent?.({ type: 'reasoning', content: chunk, done });
|
|
1646
|
+
},
|
|
1446
1647
|
onUsage: (usage) => {
|
|
1447
1648
|
onToolEvent?.({ type: 'usage', usage });
|
|
1448
1649
|
},
|
|
1650
|
+
onModelCallPhase: (phase) => {
|
|
1651
|
+
onToolEvent?.({ type: 'model_call', modelCallState: phase, modelId: this.options.model });
|
|
1652
|
+
},
|
|
1449
1653
|
onToolCall: onToolCall ?? (() => { }), // Forward tool call events to parent
|
|
1450
|
-
onBeforeModelCall: async (messages, lastInputTokens) =>
|
|
1451
|
-
messages,
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
trigger: 'before_model_call',
|
|
1455
|
-
options: { compaction: runContext.compaction },
|
|
1456
|
-
executionContext: runContext,
|
|
1457
|
-
registeredTools,
|
|
1458
|
-
toolPolicy: runContext.toolPolicy ?? null,
|
|
1459
|
-
emitEvent: onToolEvent,
|
|
1460
|
-
}),
|
|
1654
|
+
onBeforeModelCall: async (messages, lastInputTokens) => {
|
|
1655
|
+
const hostCompacted = await this.applyHostCompaction(messages, lastInputTokens, onToolEvent);
|
|
1656
|
+
return hostCompacted ?? messages;
|
|
1657
|
+
},
|
|
1461
1658
|
onAskUser: async () => {
|
|
1462
1659
|
// Nested agent loops don't support ask_user directly
|
|
1463
1660
|
// If needed, the tool should use exec(ask_user, ...) instead
|
|
1464
1661
|
return '';
|
|
1465
1662
|
},
|
|
1663
|
+
onModelCall: runContext.onModelCall,
|
|
1466
1664
|
signal: runContext.signal,
|
|
1467
1665
|
});
|
|
1468
1666
|
if (process.env.DEBUG_RUNNER) {
|
|
@@ -1524,116 +1722,17 @@ export class DMLRunner {
|
|
|
1524
1722
|
}
|
|
1525
1723
|
}
|
|
1526
1724
|
}
|
|
1527
|
-
async
|
|
1528
|
-
const
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
return params.messages;
|
|
1532
|
-
}
|
|
1533
|
-
let messages = params.messages;
|
|
1534
|
-
for (const binding of bindings) {
|
|
1535
|
-
const result = await executeCompactor({
|
|
1536
|
-
binding,
|
|
1537
|
-
messages,
|
|
1538
|
-
knownInputTokens: params.lastInputTokens,
|
|
1539
|
-
maxContextTokens: this.options.contextWindow,
|
|
1540
|
-
emitEvent: params.emitEvent,
|
|
1541
|
-
execute: (request) => this.runBoundCompactor({
|
|
1542
|
-
request,
|
|
1543
|
-
executionContext: params.executionContext,
|
|
1544
|
-
registeredTools: params.registeredTools,
|
|
1545
|
-
toolPolicy: params.toolPolicy,
|
|
1546
|
-
}),
|
|
1547
|
-
});
|
|
1548
|
-
params.emitEvent?.(result.event);
|
|
1549
|
-
if (result.usageByModel) {
|
|
1550
|
-
for (const totals of Object.values(result.usageByModel)) {
|
|
1551
|
-
params.emitEvent?.({ type: 'usage', usage: {
|
|
1552
|
-
inputTokens: totals.inputTokens,
|
|
1553
|
-
outputTokens: totals.outputTokens,
|
|
1554
|
-
totalTokens: totals.totalTokens,
|
|
1555
|
-
cacheReadTokens: totals.cacheReadTokens || undefined,
|
|
1556
|
-
cacheWriteTokens: totals.cacheWriteTokens || undefined,
|
|
1557
|
-
reasoningTokens: totals.reasoningTokens || undefined,
|
|
1558
|
-
} });
|
|
1559
|
-
}
|
|
1560
|
-
}
|
|
1561
|
-
messages = result.messages;
|
|
1562
|
-
}
|
|
1563
|
-
return messages;
|
|
1564
|
-
}
|
|
1565
|
-
async runBoundCompactor(params) {
|
|
1566
|
-
const { binding, params: compactorParams } = params.request;
|
|
1567
|
-
const code = await this.resolveCompactorSource(binding.compactor.source, binding.compactor.sourceType, params.executionContext.workspacePath);
|
|
1568
|
-
const model = binding.compactor.model ?? this.options.model;
|
|
1569
|
-
const provider = binding.compactor.provider
|
|
1570
|
-
?? (binding.compactor.model ? detectProvider(model) : this.options.provider);
|
|
1571
|
-
const compactorRunner = new DMLRunner(this.swipl, {
|
|
1572
|
-
...this.options,
|
|
1573
|
-
model,
|
|
1574
|
-
provider,
|
|
1575
|
-
streaming: false,
|
|
1576
|
-
compaction: { enabled: false },
|
|
1577
|
-
});
|
|
1578
|
-
const { signal, dispose } = createChildAbortController(params.executionContext.signal, binding.compactor.timeoutMs);
|
|
1579
|
-
try {
|
|
1580
|
-
const tools = binding.compactor.inheritTools ? params.registeredTools : new Map();
|
|
1581
|
-
let answer = '';
|
|
1582
|
-
let errorContent = '';
|
|
1583
|
-
const usageByModel = {};
|
|
1584
|
-
for await (const event of compactorRunner.run(code, {
|
|
1585
|
-
workspacePath: params.executionContext.workspacePath,
|
|
1586
|
-
gasLimit: binding.compactor.gasLimit,
|
|
1587
|
-
params: compactorParams,
|
|
1588
|
-
initialMessages: params.request.messages,
|
|
1589
|
-
tools,
|
|
1590
|
-
toolPolicy: binding.compactor.toolPolicy ?? (binding.compactor.inheritTools ? params.toolPolicy : null) ?? null,
|
|
1591
|
-
onInputRequired: async (prompt) => {
|
|
1592
|
-
throw new Error(`Compactor requested unexpected input: ${prompt}`);
|
|
1593
|
-
},
|
|
1594
|
-
signal,
|
|
1595
|
-
compaction: { enabled: false },
|
|
1596
|
-
})) {
|
|
1597
|
-
if (event.type === 'answer' && event.content) {
|
|
1598
|
-
answer = event.content;
|
|
1599
|
-
}
|
|
1600
|
-
else if (event.type === 'error' && event.content) {
|
|
1601
|
-
errorContent = event.content;
|
|
1602
|
-
}
|
|
1603
|
-
else if (event.type === 'usage' && event.usage) {
|
|
1604
|
-
recordTokenUsage(usageByModel, this.options.model, event.usage);
|
|
1605
|
-
}
|
|
1606
|
-
}
|
|
1607
|
-
return {
|
|
1608
|
-
answer,
|
|
1609
|
-
error: errorContent || undefined,
|
|
1610
|
-
usageByModel: Object.keys(usageByModel).length > 0 ? usageByModel : undefined,
|
|
1611
|
-
};
|
|
1612
|
-
}
|
|
1613
|
-
finally {
|
|
1614
|
-
dispose();
|
|
1615
|
-
await compactorRunner.dispose();
|
|
1616
|
-
}
|
|
1617
|
-
}
|
|
1618
|
-
async resolveCompactorSource(source, sourceType, workspacePath) {
|
|
1619
|
-
if (sourceType === 'inline') {
|
|
1620
|
-
return source;
|
|
1621
|
-
}
|
|
1622
|
-
const basePath = workspacePath ?? process.cwd();
|
|
1623
|
-
const candidatePath = path.isAbsolute(source) ? source : path.resolve(basePath, source);
|
|
1624
|
-
try {
|
|
1625
|
-
return await fs.readFile(candidatePath, 'utf8');
|
|
1725
|
+
async applyHostCompaction(messages, lastInputTokens, emit) {
|
|
1726
|
+
const host = this.options.compactMessages;
|
|
1727
|
+
if (!host) {
|
|
1728
|
+
return null;
|
|
1626
1729
|
}
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
}
|
|
1631
|
-
return source;
|
|
1730
|
+
const result = await host(messages, lastInputTokens);
|
|
1731
|
+
if (result.event) {
|
|
1732
|
+
emit?.(result.event);
|
|
1632
1733
|
}
|
|
1734
|
+
return result.messages;
|
|
1633
1735
|
}
|
|
1634
|
-
/**
|
|
1635
|
-
* Extract memory from payload (now passed via state threading)
|
|
1636
|
-
*/
|
|
1637
1736
|
extractMessagesFromValue(rawMessages) {
|
|
1638
1737
|
const normalized = toJsValue(rawMessages);
|
|
1639
1738
|
if (!Array.isArray(normalized)) {
|
|
@@ -1643,19 +1742,46 @@ export class DMLRunner {
|
|
|
1643
1742
|
const msg = toJsValue(messageValue);
|
|
1644
1743
|
const role = String(toJsValue(msg.role) ?? 'user');
|
|
1645
1744
|
const content = String(toJsValue(msg.content) ?? '');
|
|
1745
|
+
const media = this.extractMediaRefs(msg.media);
|
|
1646
1746
|
if (role !== 'system' && role !== 'user' && role !== 'assistant') {
|
|
1647
|
-
return { role: 'user', content };
|
|
1747
|
+
return { role: 'user', content, media: media.length ? media : undefined };
|
|
1648
1748
|
}
|
|
1649
|
-
return { role: role, content };
|
|
1749
|
+
return { role: role, content, media: media.length ? media : undefined };
|
|
1650
1750
|
});
|
|
1651
1751
|
}
|
|
1752
|
+
/** Decode `media{path:..., mime:...}` dicts into reference-only refs. */
|
|
1753
|
+
extractMediaRefs(raw) {
|
|
1754
|
+
const value = toJsValue(raw);
|
|
1755
|
+
if (!Array.isArray(value))
|
|
1756
|
+
return [];
|
|
1757
|
+
const refs = [];
|
|
1758
|
+
for (const entry of value) {
|
|
1759
|
+
const obj = toJsValue(entry);
|
|
1760
|
+
const mediaPath = String(obj?.path ?? '');
|
|
1761
|
+
const mimeType = String(obj?.mime ?? obj?.mimeType ?? '');
|
|
1762
|
+
if (mediaPath && mimeType)
|
|
1763
|
+
refs.push({ path: mediaPath, mimeType });
|
|
1764
|
+
}
|
|
1765
|
+
return refs;
|
|
1766
|
+
}
|
|
1767
|
+
/** Resolve a message's reference-only media to base64 bytes (workspace files). */
|
|
1768
|
+
async readMediaRefs(workspacePath, refs) {
|
|
1769
|
+
return resolveMediaRefs(workspacePath ?? process.cwd(), refs);
|
|
1770
|
+
}
|
|
1652
1771
|
extractMemoryFromPayload(rawPayload) {
|
|
1653
1772
|
const rawMemory = toJsValue(rawPayload.memory);
|
|
1654
1773
|
return this.extractMessagesFromValue(rawMemory);
|
|
1655
1774
|
}
|
|
1656
1775
|
serializeMemoryMessages(messages) {
|
|
1657
1776
|
return messages
|
|
1658
|
-
.map((message) =>
|
|
1777
|
+
.map((message) => {
|
|
1778
|
+
const media = message.media && message.media.length > 0
|
|
1779
|
+
? `, media: [${message.media
|
|
1780
|
+
.map((ref) => `media{path: ${this.toPrologTerm(ref.path)}, mime: ${this.toPrologTerm(ref.mimeType)}}`)
|
|
1781
|
+
.join(', ')}]`
|
|
1782
|
+
: '';
|
|
1783
|
+
return `message{role: ${message.role}, content: ${this.toPrologTerm(message.content)}${media}}`;
|
|
1784
|
+
})
|
|
1659
1785
|
.join(', ');
|
|
1660
1786
|
}
|
|
1661
1787
|
/**
|
|
@@ -1723,6 +1849,30 @@ export class DMLRunner {
|
|
|
1723
1849
|
const fullMemoryStr = this.serializeMemoryMessages(fullMemory ?? []);
|
|
1724
1850
|
this.query(`deepclause_mi:post_tool_agent_result('${this.sessionId}', '${toolEngineId}', ${result.success}, vars{${varsStr}}, [${messagesStr}], [${fullMemoryStr}])`);
|
|
1725
1851
|
}
|
|
1852
|
+
/** SDK-internal ask_user tool used by exec(ask_user(...)) in DML. */
|
|
1853
|
+
internalAskUserTool(options) {
|
|
1854
|
+
return {
|
|
1855
|
+
description: 'Ask the user for input or clarification',
|
|
1856
|
+
parameters: {
|
|
1857
|
+
type: 'object',
|
|
1858
|
+
properties: {
|
|
1859
|
+
prompt: { type: 'string', description: 'The question or prompt to show the user' },
|
|
1860
|
+
},
|
|
1861
|
+
required: ['prompt'],
|
|
1862
|
+
},
|
|
1863
|
+
execute: async (args) => {
|
|
1864
|
+
const prompt = String(args.prompt ?? args.arg1 ?? '');
|
|
1865
|
+
if (!prompt) {
|
|
1866
|
+
return { error: 'No prompt provided to ask_user' };
|
|
1867
|
+
}
|
|
1868
|
+
const response = await options.onInputRequired(prompt);
|
|
1869
|
+
if (response === undefined || response === null) {
|
|
1870
|
+
return { cancelled: true };
|
|
1871
|
+
}
|
|
1872
|
+
return { user_response: response };
|
|
1873
|
+
},
|
|
1874
|
+
};
|
|
1875
|
+
}
|
|
1726
1876
|
/**
|
|
1727
1877
|
* Provide user input to waiting Prolog
|
|
1728
1878
|
*/
|
|
@@ -1730,6 +1880,10 @@ export class DMLRunner {
|
|
|
1730
1880
|
const escaped = input.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
1731
1881
|
this.query(`deepclause_mi:provide_input('${this.sessionId}', "${escaped}")`);
|
|
1732
1882
|
}
|
|
1883
|
+
/** Signal a waiting input/2 that no human is available. */
|
|
1884
|
+
cancelInput() {
|
|
1885
|
+
this.query(`deepclause_mi:provide_input_cancelled('${this.sessionId}')`);
|
|
1886
|
+
}
|
|
1733
1887
|
/**
|
|
1734
1888
|
* Execute a Prolog query
|
|
1735
1889
|
*/
|
|
@@ -1748,14 +1902,17 @@ export class DMLRunner {
|
|
|
1748
1902
|
* Cleanup resources
|
|
1749
1903
|
*/
|
|
1750
1904
|
cleanup() {
|
|
1751
|
-
if (this.
|
|
1905
|
+
if (this.frames.size > 0) {
|
|
1752
1906
|
try {
|
|
1907
|
+
// destroy_engine/1 ends every frame registered for the session and
|
|
1908
|
+
// retracts the session's dynamic state.
|
|
1753
1909
|
this.query(`deepclause_mi:destroy_engine('${this.sessionId}')`);
|
|
1754
1910
|
}
|
|
1755
1911
|
catch {
|
|
1756
1912
|
// Ignore cleanup errors
|
|
1757
1913
|
}
|
|
1758
|
-
this.
|
|
1914
|
+
this.frames.clear();
|
|
1915
|
+
this.defaultFrameId = null;
|
|
1759
1916
|
}
|
|
1760
1917
|
}
|
|
1761
1918
|
/**
|