deepclause-sdk 0.0.88 → 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/LICENSE +21 -0
- package/README.md +418 -914
- package/dist/agent.d.ts +19 -2
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +221 -4
- 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 +29 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -2
- package/dist/index.js.map +1 -1
- package/dist/judge/index.d.ts +9 -0
- package/dist/judge/index.d.ts.map +1 -0
- package/dist/judge/index.js +6 -0
- package/dist/judge/index.js.map +1 -0
- package/dist/judge/jev.d.ts +21 -0
- package/dist/judge/jev.d.ts.map +1 -0
- package/dist/judge/jev.js +218 -0
- package/dist/judge/jev.js.map +1 -0
- package/dist/judge/llm.d.ts +23 -0
- package/dist/judge/llm.d.ts.map +1 -0
- package/dist/judge/llm.js +120 -0
- package/dist/judge/llm.js.map +1 -0
- package/dist/judge/marshal.d.ts +26 -0
- package/dist/judge/marshal.d.ts.map +1 -0
- package/dist/judge/marshal.js +266 -0
- package/dist/judge/marshal.js.map +1 -0
- package/dist/judge/mock.d.ts +28 -0
- package/dist/judge/mock.d.ts.map +1 -0
- package/dist/judge/mock.js +98 -0
- package/dist/judge/mock.js.map +1 -0
- package/dist/judge/types.d.ts +95 -0
- package/dist/judge/types.d.ts.map +1 -0
- package/dist/judge/types.js +23 -0
- package/dist/judge/types.js.map +1 -0
- package/dist/prolog/bridge.d.ts +5 -1
- package/dist/prolog/bridge.d.ts.map +1 -1
- package/dist/prolog/bridge.js +29 -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 +2522 -118
- 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 +145 -13
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +600 -282
- package/dist/runner.js.map +1 -1
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +56 -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 +393 -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 +149 -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 +2522 -118
- 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/compactors/default-memory-compactor.dml +0 -6
- 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,21 +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
|
-
import {
|
|
6
|
+
import { judgeAnswersToValues, judgeCacheKey, normalizeJudgeState, parseJudgeQuestions, validateJudgeAnswers, valuesToPrologList } from './judge/marshal.js';
|
|
9
7
|
import { buildToolCompletionEvent, buildToolFailureEvent, buildToolStartEvent, } from './system/runtime/shell-tool-events.js';
|
|
10
8
|
import { checkToolPolicy } from './tools.js';
|
|
11
9
|
import { generateLlmReply, sampleSingleToken } from './prolog/bridge.js';
|
|
12
|
-
import { recordTokenUsage } from './system/runtime/token-usage.js';
|
|
13
10
|
import { buildReasoningProviderOptions } from './system/config/model-database.js';
|
|
11
|
+
import { resolveMediaRefs } from './system/runtime/media.js';
|
|
14
12
|
/**
|
|
15
13
|
* Convert swipl-wasm value to plain JavaScript value
|
|
16
14
|
* Handles PrologString, PrologAtom, PrologList, etc.
|
|
17
15
|
*/
|
|
18
|
-
function toJsValue(value) {
|
|
16
|
+
export function toJsValue(value) {
|
|
19
17
|
if (value === null || value === undefined) {
|
|
20
18
|
return value;
|
|
21
19
|
}
|
|
@@ -59,45 +57,45 @@ function toJsValue(value) {
|
|
|
59
57
|
}
|
|
60
58
|
return value;
|
|
61
59
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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 [];
|
|
72
71
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
+
}
|
|
81
90
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
91
|
+
if (Array.isArray(decoded) && decoded.length >= 2) {
|
|
92
|
+
bindings.push({
|
|
93
|
+
name: String(toJsValue(decoded[0]) ?? ''),
|
|
94
|
+
value: String(toJsValue(decoded[1]) ?? ''),
|
|
95
|
+
});
|
|
86
96
|
}
|
|
87
97
|
}
|
|
88
|
-
|
|
89
|
-
if (timeoutMs > 0) {
|
|
90
|
-
timeout = setTimeout(() => controller.abort(new Error(`Compactor timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
91
|
-
disposers.push(() => clearTimeout(timeout));
|
|
92
|
-
}
|
|
93
|
-
return {
|
|
94
|
-
signal: controller.signal,
|
|
95
|
-
dispose: () => {
|
|
96
|
-
for (const dispose of disposers) {
|
|
97
|
-
dispose();
|
|
98
|
-
}
|
|
99
|
-
},
|
|
100
|
-
};
|
|
98
|
+
return bindings;
|
|
101
99
|
}
|
|
102
100
|
export function decodeAgentOutputVars(rawOutputVars) {
|
|
103
101
|
const normalized = toJsValue(rawOutputVars);
|
|
@@ -141,10 +139,18 @@ function prologTypeToJsonType(prologType) {
|
|
|
141
139
|
export class DMLRunner {
|
|
142
140
|
swipl;
|
|
143
141
|
options;
|
|
144
|
-
engine = null;
|
|
145
142
|
sessionId = '';
|
|
146
143
|
currentMemory = [];
|
|
147
144
|
stepCounter = 0;
|
|
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;
|
|
148
154
|
constructor(swipl, options) {
|
|
149
155
|
this.swipl = swipl;
|
|
150
156
|
this.options = options;
|
|
@@ -162,6 +168,7 @@ export class DMLRunner {
|
|
|
162
168
|
// Generate unique session ID
|
|
163
169
|
this.sessionId = `sess_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
164
170
|
const memoryId = `mem_${this.sessionId}`; // Kept for API compatibility
|
|
171
|
+
this.judgeCache.clear();
|
|
165
172
|
try {
|
|
166
173
|
// Mount workspace if path provided
|
|
167
174
|
if (options.workspacePath) {
|
|
@@ -188,91 +195,18 @@ export class DMLRunner {
|
|
|
188
195
|
yield { type: 'error', content: parseResult.error };
|
|
189
196
|
return;
|
|
190
197
|
}
|
|
191
|
-
// Create cooperative engine with args
|
|
198
|
+
// Create cooperative engine with args (registers the default query frame)
|
|
192
199
|
const engineResult = this.createEngine(memoryId, args, params);
|
|
193
200
|
if (engineResult.error) {
|
|
194
201
|
yield { type: 'error', content: engineResult.error };
|
|
195
202
|
return;
|
|
196
203
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const reason = options.signal.reason;
|
|
202
|
-
const abortMessage = reason instanceof Error
|
|
203
|
-
? reason.message
|
|
204
|
-
: typeof reason === 'string' && reason.trim().length > 0
|
|
205
|
-
? reason
|
|
206
|
-
: 'Execution aborted';
|
|
207
|
-
yield { type: 'error', content: abortMessage };
|
|
208
|
-
break;
|
|
209
|
-
}
|
|
210
|
-
// Step the engine
|
|
211
|
-
const step = this.stepEngine();
|
|
212
|
-
if (process.env.DEBUG_STEPS) {
|
|
213
|
-
console.log('[MAIN_LOOP] step:', step.status, step.content?.substring(0, 50));
|
|
214
|
-
}
|
|
215
|
-
switch (step.status) {
|
|
216
|
-
case 'output':
|
|
217
|
-
if (step.content) {
|
|
218
|
-
if (process.env.DEBUG_STEPS) {
|
|
219
|
-
console.log('[MAIN_LOOP] yielding output:', step.content.substring(0, 50));
|
|
220
|
-
}
|
|
221
|
-
yield { type: 'output', content: step.content };
|
|
222
|
-
}
|
|
223
|
-
break;
|
|
224
|
-
case 'log':
|
|
225
|
-
if (step.content) {
|
|
226
|
-
yield { type: 'log', content: step.content };
|
|
227
|
-
}
|
|
228
|
-
break;
|
|
229
|
-
case 'answer':
|
|
230
|
-
yield { type: 'answer', content: step.content };
|
|
231
|
-
break;
|
|
232
|
-
case 'request_agent_loop':
|
|
233
|
-
// Run agent loop for task() predicate
|
|
234
|
-
yield* this.handleAgentLoop(step.payload, memoryId, options);
|
|
235
|
-
break;
|
|
236
|
-
case 'request_exec':
|
|
237
|
-
// Execute external tool
|
|
238
|
-
yield* this.handleExec(step.payload, options);
|
|
239
|
-
break;
|
|
240
|
-
case 'request_sample_token':
|
|
241
|
-
yield* this.handleSampleToken(step.payload, options);
|
|
242
|
-
break;
|
|
243
|
-
case 'request_llm':
|
|
244
|
-
yield* this.handleLlm(step.payload, options);
|
|
245
|
-
break;
|
|
246
|
-
case 'wait_input':
|
|
247
|
-
// Request user input
|
|
248
|
-
yield { type: 'input_required', prompt: step.prompt };
|
|
249
|
-
const input = await options.onInputRequired(step.prompt ?? '');
|
|
250
|
-
this.provideInput(input);
|
|
251
|
-
break;
|
|
252
|
-
case 'finished':
|
|
253
|
-
// Include trace data if trace mode was enabled
|
|
254
|
-
if (this.options.trace && step.payload) {
|
|
255
|
-
const traceData = toJsValue(step.payload.trace);
|
|
256
|
-
yield { type: 'finished', trace: Array.isArray(traceData) ? traceData : undefined };
|
|
257
|
-
}
|
|
258
|
-
else {
|
|
259
|
-
yield { type: 'finished' };
|
|
260
|
-
}
|
|
261
|
-
return;
|
|
262
|
-
case 'error':
|
|
263
|
-
if (this.options.trace && step.payload) {
|
|
264
|
-
const traceData = toJsValue(step.payload.trace);
|
|
265
|
-
yield { type: 'error', content: step.content, trace: Array.isArray(traceData) ? traceData : undefined };
|
|
266
|
-
}
|
|
267
|
-
else {
|
|
268
|
-
yield { type: 'error', content: step.content };
|
|
269
|
-
}
|
|
270
|
-
return;
|
|
271
|
-
default:
|
|
272
|
-
yield { type: 'error', content: `Unknown step status: ${step.status}` };
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
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;
|
|
275
208
|
}
|
|
209
|
+
yield* this.stepLoop(frame, options);
|
|
276
210
|
}
|
|
277
211
|
catch (error) {
|
|
278
212
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -283,6 +217,164 @@ export class DMLRunner {
|
|
|
283
217
|
this.cleanup();
|
|
284
218
|
}
|
|
285
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
|
+
}
|
|
286
378
|
/**
|
|
287
379
|
* Parse DML code into Prolog clauses
|
|
288
380
|
*/
|
|
@@ -375,10 +467,15 @@ export class DMLRunner {
|
|
|
375
467
|
createEngine(memoryId, args, params) {
|
|
376
468
|
try {
|
|
377
469
|
const initialMemory = this.serializeMemoryMessages(this.currentMemory);
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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 };
|
|
382
479
|
}
|
|
383
480
|
return { error: 'Failed to create engine' };
|
|
384
481
|
}
|
|
@@ -388,11 +485,93 @@ export class DMLRunner {
|
|
|
388
485
|
}
|
|
389
486
|
}
|
|
390
487
|
/**
|
|
391
|
-
*
|
|
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.
|
|
392
498
|
*/
|
|
393
|
-
|
|
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) {
|
|
394
573
|
try {
|
|
395
|
-
const result = this.query(`deepclause_mi:
|
|
574
|
+
const result = this.query(`deepclause_mi:step_frame('${frameId}', Status, Content, Payload)`);
|
|
396
575
|
// Convert swipl-wasm values to plain JS
|
|
397
576
|
const status = toJsValue(result?.Status);
|
|
398
577
|
const content = toJsValue(result?.Content);
|
|
@@ -413,13 +592,15 @@ export class DMLRunner {
|
|
|
413
592
|
* Handle agent loop request (task() predicate)
|
|
414
593
|
*/
|
|
415
594
|
async *handleAgentLoop(payload, _memoryId, // No longer used - memory comes from payload
|
|
416
|
-
options) {
|
|
595
|
+
options, _frame) {
|
|
417
596
|
const rawPayload = payload;
|
|
418
597
|
const taskDescription = String(toJsValue(rawPayload.taskDescription) ?? '');
|
|
419
598
|
const stepId = `step_${this.stepCounter++}`;
|
|
420
599
|
yield { type: 'task_activity', taskState: 'started', taskDescription, taskId: stepId };
|
|
421
600
|
let stepSucceeded = null;
|
|
422
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.
|
|
423
604
|
// Ensure outputVars is an array of either strings or TypedVar objects
|
|
424
605
|
const outputVars = decodeAgentOutputVars(rawPayload.outputVars);
|
|
425
606
|
// Parse userTools - now contains schema info as array of tool_info dicts
|
|
@@ -497,6 +678,9 @@ export class DMLRunner {
|
|
|
497
678
|
try {
|
|
498
679
|
emitToolEvent({ type: 'input_required', prompt });
|
|
499
680
|
const response = await options.onInputRequired(prompt);
|
|
681
|
+
if (response === undefined || response === null) {
|
|
682
|
+
return { cancelled: true };
|
|
683
|
+
}
|
|
500
684
|
return { user_response: response };
|
|
501
685
|
}
|
|
502
686
|
catch (err) {
|
|
@@ -509,15 +693,15 @@ export class DMLRunner {
|
|
|
509
693
|
const emitToolCall = (toolName, args) => {
|
|
510
694
|
emitToolEvent({ type: 'tool_call', toolName, toolArgs: args });
|
|
511
695
|
};
|
|
512
|
-
// Build available tools for agent
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
async (toolName, args) => {
|
|
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;
|
|
699
|
+
const availableTools = this.buildAgentTools(userTools, options.toolPolicy, async (toolName, args) => {
|
|
516
700
|
return this.executeToolIsolated(toolName, args, augmentedTools, onToolOutput, {
|
|
517
701
|
workspacePath: options.workspacePath,
|
|
518
702
|
signal: options.signal,
|
|
519
|
-
compaction: options.compaction,
|
|
520
703
|
toolPolicy: options.toolPolicy,
|
|
704
|
+
onModelCall: options.onModelCall,
|
|
521
705
|
}, [], emitToolCall, emitToolEvent);
|
|
522
706
|
});
|
|
523
707
|
// Run agent loop with streaming support
|
|
@@ -527,6 +711,7 @@ export class DMLRunner {
|
|
|
527
711
|
memory: effectiveMemory,
|
|
528
712
|
tools: availableTools,
|
|
529
713
|
workspacePath: options.workspacePath,
|
|
714
|
+
resolveMedia: (refs) => this.readMediaRefs(options.workspacePath, refs),
|
|
530
715
|
modelOptions: {
|
|
531
716
|
model: this.options.model,
|
|
532
717
|
provider: this.options.provider,
|
|
@@ -545,6 +730,13 @@ export class DMLRunner {
|
|
|
545
730
|
streamResolve = null;
|
|
546
731
|
}
|
|
547
732
|
},
|
|
733
|
+
onReasoning: (chunk, done) => {
|
|
734
|
+
streamQueue.push({ type: 'reasoning', content: chunk, done });
|
|
735
|
+
if (streamResolve) {
|
|
736
|
+
streamResolve();
|
|
737
|
+
streamResolve = null;
|
|
738
|
+
}
|
|
739
|
+
},
|
|
548
740
|
onToolCall: (toolName, args) => {
|
|
549
741
|
streamQueue.push({ type: 'tool_call', toolName, toolArgs: args });
|
|
550
742
|
if (streamResolve) {
|
|
@@ -559,23 +751,29 @@ export class DMLRunner {
|
|
|
559
751
|
streamResolve = null;
|
|
560
752
|
}
|
|
561
753
|
},
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
}
|
|
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
|
+
},
|
|
577
773
|
onAskUser: options.onInputRequired,
|
|
774
|
+
onModelCall: options.onModelCall,
|
|
578
775
|
signal: options.signal,
|
|
776
|
+
llmBackend: this.options.llmBackend,
|
|
579
777
|
});
|
|
580
778
|
// Yield streaming events as they arrive
|
|
581
779
|
if (this.options.streaming) {
|
|
@@ -683,6 +881,7 @@ export class DMLRunner {
|
|
|
683
881
|
providerOptions: this.options.providerOptions,
|
|
684
882
|
},
|
|
685
883
|
signal: options.signal,
|
|
884
|
+
llmBackend: this.options.llmBackend,
|
|
686
885
|
});
|
|
687
886
|
succeeded = true;
|
|
688
887
|
this.postSampleTokenResult({ success: true, token });
|
|
@@ -721,6 +920,8 @@ export class DMLRunner {
|
|
|
721
920
|
providerOptions: this.options.providerOptions,
|
|
722
921
|
},
|
|
723
922
|
signal: options.signal,
|
|
923
|
+
llmBackend: this.options.llmBackend,
|
|
924
|
+
resolveMedia: (refs) => this.readMediaRefs(options.workspacePath, refs),
|
|
724
925
|
});
|
|
725
926
|
succeeded = true;
|
|
726
927
|
this.postLlmResult({ success: true, text });
|
|
@@ -737,20 +938,132 @@ export class DMLRunner {
|
|
|
737
938
|
yield { type: 'task_activity', taskState: succeeded ? 'completed' : 'failed', taskId: stepId };
|
|
738
939
|
}
|
|
739
940
|
}
|
|
941
|
+
/**
|
|
942
|
+
* Handle a semantic judgment request (judge/2 and the one-off predicates).
|
|
943
|
+
*/
|
|
944
|
+
async *handleJudgment(payload, options) {
|
|
945
|
+
const parsed = this.parseJudgmentPayload(payload);
|
|
946
|
+
const ids = parsed.questions.map((question) => question.id);
|
|
947
|
+
const backendName = parsed.requestedName === 'default'
|
|
948
|
+
? options.judgeBackend ?? this.options.judgeSelection?.defaultName ?? 'llm'
|
|
949
|
+
: parsed.requestedName;
|
|
950
|
+
const stepId = `judge_${this.stepCounter++}`;
|
|
951
|
+
yield {
|
|
952
|
+
type: 'task_activity',
|
|
953
|
+
taskState: 'started',
|
|
954
|
+
taskDescription: `judge[${backendName}]: ${ids.join(', ')}`,
|
|
955
|
+
taskId: stepId,
|
|
956
|
+
judgeBackend: backendName,
|
|
957
|
+
judgeQuestionIds: ids,
|
|
958
|
+
judgeState: 'started',
|
|
959
|
+
};
|
|
960
|
+
const result = await this.completeJudgment(parsed, { signal: options.signal, judgeBackend: options.judgeBackend });
|
|
961
|
+
if (result.error) {
|
|
962
|
+
this.postJudgmentResult({ success: false, error: result.error });
|
|
963
|
+
yield { type: 'log', content: `judgment failed: ${result.error}` };
|
|
964
|
+
}
|
|
965
|
+
else {
|
|
966
|
+
this.postJudgmentResult({ success: true, values: result.values ?? [] });
|
|
967
|
+
if (result.usage) {
|
|
968
|
+
yield {
|
|
969
|
+
type: 'usage',
|
|
970
|
+
usage: {
|
|
971
|
+
inputTokens: result.usage.inputTokens,
|
|
972
|
+
outputTokens: result.usage.outputTokens,
|
|
973
|
+
totalTokens: result.usage.totalTokens ?? result.usage.inputTokens + result.usage.outputTokens,
|
|
974
|
+
},
|
|
975
|
+
usageSource: 'judge',
|
|
976
|
+
usageModel: result.usageModel,
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
yield {
|
|
981
|
+
type: 'task_activity',
|
|
982
|
+
taskState: result.error ? 'failed' : 'completed',
|
|
983
|
+
taskId: stepId,
|
|
984
|
+
judgeBackend: result.backendName,
|
|
985
|
+
judgeQuestionIds: result.ids,
|
|
986
|
+
judgeState: result.error ? 'failed' : 'completed',
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
parseJudgmentPayload(payload) {
|
|
990
|
+
const data = (payload ?? {});
|
|
991
|
+
return {
|
|
992
|
+
questions: parseJudgeQuestions(data.questions),
|
|
993
|
+
state: normalizeJudgeState(data.state),
|
|
994
|
+
requiredCapabilities: Array.isArray(data.requires) ? data.requires.map(String) : [],
|
|
995
|
+
requestedName: typeof data.backend === 'string' ? data.backend : 'default',
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
async completeJudgment(parsed, context) {
|
|
999
|
+
const selection = this.options.judgeSelection;
|
|
1000
|
+
const defaultName = context.judgeBackend ?? selection?.defaultName ?? 'llm';
|
|
1001
|
+
const backendName = parsed.requestedName === 'default' ? defaultName : parsed.requestedName;
|
|
1002
|
+
const ids = parsed.questions.map((question) => question.id);
|
|
1003
|
+
try {
|
|
1004
|
+
const backend = selection?.backends.get(backendName);
|
|
1005
|
+
if (!backend)
|
|
1006
|
+
throw new Error(`Unknown judgment backend: ${backendName}`);
|
|
1007
|
+
const missing = parsed.requiredCapabilities.filter((capability) => !backend.capabilities[capability]);
|
|
1008
|
+
if (missing.length > 0) {
|
|
1009
|
+
throw new Error(`Judgment backend '${backendName}' lacks required capabilities: ${missing.join(', ')}`);
|
|
1010
|
+
}
|
|
1011
|
+
const model = this.options.model;
|
|
1012
|
+
const key = judgeCacheKey(backend.id, model, parsed.state, parsed.questions);
|
|
1013
|
+
const cached = this.judgeCache.get(key);
|
|
1014
|
+
if (cached) {
|
|
1015
|
+
return { backendName, ids, values: judgeAnswersToValues(parsed.questions, cached) };
|
|
1016
|
+
}
|
|
1017
|
+
const response = await backend.complete({
|
|
1018
|
+
state: parsed.state,
|
|
1019
|
+
questions: parsed.questions,
|
|
1020
|
+
signal: context.signal,
|
|
1021
|
+
model,
|
|
1022
|
+
requiredCapabilities: parsed.requiredCapabilities,
|
|
1023
|
+
});
|
|
1024
|
+
const answers = validateJudgeAnswers(parsed.questions, response.answers);
|
|
1025
|
+
this.judgeCache.set(key, answers);
|
|
1026
|
+
return {
|
|
1027
|
+
backendName,
|
|
1028
|
+
ids,
|
|
1029
|
+
values: judgeAnswersToValues(parsed.questions, answers),
|
|
1030
|
+
usage: response.usage,
|
|
1031
|
+
usageModel: response.model ?? model,
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
catch (error) {
|
|
1035
|
+
return {
|
|
1036
|
+
backendName,
|
|
1037
|
+
ids,
|
|
1038
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
postJudgmentResult(result) {
|
|
1043
|
+
if (result.success) {
|
|
1044
|
+
this.query(`deepclause_mi:post_judgment_result('${this.sessionId}', success, ${valuesToPrologList(result.values ?? [])})`);
|
|
1045
|
+
}
|
|
1046
|
+
else {
|
|
1047
|
+
this.query(`deepclause_mi:post_judgment_result('${this.sessionId}', failure, ${this.toPrologTerm(result.error ?? 'Unknown error')})`);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
740
1050
|
/**
|
|
741
1051
|
* Handle exec request (exec() predicate)
|
|
742
1052
|
*/
|
|
743
1053
|
async *handleExec(payload, options) {
|
|
744
1054
|
const { toolName, args } = payload;
|
|
745
|
-
// Check tool policy
|
|
1055
|
+
// Check tool policy (ask_user is the human channel and is exempt).
|
|
746
1056
|
const policyCheck = checkToolPolicy(toolName, options.toolPolicy);
|
|
747
1057
|
if (!policyCheck.allowed) {
|
|
748
1058
|
yield { type: 'log', content: `Tool '${toolName}' blocked by policy: ${policyCheck.reason}` };
|
|
749
1059
|
this.postExecResult({ success: false, error: policyCheck.reason });
|
|
750
1060
|
return;
|
|
751
1061
|
}
|
|
752
|
-
// Get tool definition
|
|
753
|
-
|
|
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);
|
|
754
1067
|
if (!tool) {
|
|
755
1068
|
yield { type: 'log', content: `Tool '${toolName}' not found` };
|
|
756
1069
|
this.postExecResult({ success: false, error: `Tool not found: ${toolName}` });
|
|
@@ -762,7 +1075,7 @@ export class DMLRunner {
|
|
|
762
1075
|
argsObj = this.argsToObject(args, tool);
|
|
763
1076
|
// Emit tool_call event before execution
|
|
764
1077
|
yield buildToolStartEvent(toolName, argsObj);
|
|
765
|
-
const result = await tool.execute(argsObj);
|
|
1078
|
+
const result = await tool.execute(argsObj, { signal: options.signal });
|
|
766
1079
|
yield buildToolCompletionEvent(toolName, argsObj, result);
|
|
767
1080
|
// Post result back to Prolog
|
|
768
1081
|
this.postExecResult({ success: true, result });
|
|
@@ -934,16 +1247,14 @@ export class DMLRunner {
|
|
|
934
1247
|
* Only includes tools defined in DML - registered TypeScript tools are NOT exposed to the LLM.
|
|
935
1248
|
* DML tools can internally call exec/2 to use registered tools.
|
|
936
1249
|
*/
|
|
937
|
-
buildAgentTools(userTools,
|
|
1250
|
+
buildAgentTools(userTools, _policy, executeToolInline) {
|
|
938
1251
|
const result = new Map();
|
|
939
1252
|
// Add user-defined tools from DML file with schema info
|
|
940
1253
|
// These are the ONLY tools the LLM can call during task()
|
|
1254
|
+
// Do not apply the host runtime-tool policy to these DML predicates. Their
|
|
1255
|
+
// nested exec/2 calls pass through handleExec/executeToolIsolated, where
|
|
1256
|
+
// the policy still restricts actual host capabilities.
|
|
941
1257
|
for (const tool of userTools) {
|
|
942
|
-
// Check policy for DML tools too
|
|
943
|
-
const check = checkToolPolicy(tool.name, policy);
|
|
944
|
-
if (!check.allowed) {
|
|
945
|
-
continue;
|
|
946
|
-
}
|
|
947
1258
|
// Build JSON Schema properties from inputs
|
|
948
1259
|
const properties = {};
|
|
949
1260
|
const required = [];
|
|
@@ -1057,7 +1368,7 @@ export class DMLRunner {
|
|
|
1057
1368
|
try {
|
|
1058
1369
|
argsObj = this.argsToObject(execArgs, tool);
|
|
1059
1370
|
onToolEvent?.(buildToolStartEvent(execToolName, argsObj));
|
|
1060
|
-
const execResult = await tool.execute(argsObj);
|
|
1371
|
+
const execResult = await tool.execute(argsObj, { signal: runContext.signal });
|
|
1061
1372
|
onToolEvent?.(buildToolCompletionEvent(execToolName, argsObj, execResult));
|
|
1062
1373
|
const postGoal = `deepclause_mi:post_exec_result('${this.sessionId}', success, ${this.toPrologTerm(execResult)})`;
|
|
1063
1374
|
this.query(postGoal);
|
|
@@ -1093,6 +1404,7 @@ export class DMLRunner {
|
|
|
1093
1404
|
providerOptions: this.options.providerOptions,
|
|
1094
1405
|
},
|
|
1095
1406
|
signal: runContext.signal,
|
|
1407
|
+
llmBackend: this.options.llmBackend,
|
|
1096
1408
|
});
|
|
1097
1409
|
this.postSampleTokenResult({ success: true, token });
|
|
1098
1410
|
if (usage) {
|
|
@@ -1126,6 +1438,8 @@ export class DMLRunner {
|
|
|
1126
1438
|
providerOptions: this.options.providerOptions,
|
|
1127
1439
|
},
|
|
1128
1440
|
signal: runContext.signal,
|
|
1441
|
+
llmBackend: this.options.llmBackend,
|
|
1442
|
+
resolveMedia: (refs) => this.readMediaRefs(runContext.workspacePath, refs),
|
|
1129
1443
|
});
|
|
1130
1444
|
this.postLlmResult({ success: true, text });
|
|
1131
1445
|
if (usage) {
|
|
@@ -1143,6 +1457,49 @@ export class DMLRunner {
|
|
|
1143
1457
|
}
|
|
1144
1458
|
break;
|
|
1145
1459
|
}
|
|
1460
|
+
case 'request_judgment': {
|
|
1461
|
+
const parsed = this.parseJudgmentPayload(payload);
|
|
1462
|
+
const nestedStepId = `judge_${this.stepCounter++}`;
|
|
1463
|
+
const nestedIds = parsed.questions.map((question) => question.id);
|
|
1464
|
+
onToolEvent?.({
|
|
1465
|
+
type: 'task_activity',
|
|
1466
|
+
taskState: 'started',
|
|
1467
|
+
taskDescription: `judge[${parsed.requestedName}]: ${nestedIds.join(', ')}`,
|
|
1468
|
+
taskId: nestedStepId,
|
|
1469
|
+
judgeBackend: parsed.requestedName,
|
|
1470
|
+
judgeQuestionIds: nestedIds,
|
|
1471
|
+
judgeState: 'started',
|
|
1472
|
+
});
|
|
1473
|
+
const judgeResult = await this.completeJudgment(parsed, { signal: runContext.signal, judgeBackend: undefined });
|
|
1474
|
+
if (judgeResult.error) {
|
|
1475
|
+
this.postJudgmentResult({ success: false, error: judgeResult.error });
|
|
1476
|
+
}
|
|
1477
|
+
else {
|
|
1478
|
+
this.postJudgmentResult({ success: true, values: judgeResult.values ?? [] });
|
|
1479
|
+
if (judgeResult.usage) {
|
|
1480
|
+
onToolEvent?.({
|
|
1481
|
+
type: 'usage',
|
|
1482
|
+
usage: {
|
|
1483
|
+
inputTokens: judgeResult.usage.inputTokens,
|
|
1484
|
+
outputTokens: judgeResult.usage.outputTokens,
|
|
1485
|
+
totalTokens: judgeResult.usage.totalTokens ??
|
|
1486
|
+
judgeResult.usage.inputTokens + judgeResult.usage.outputTokens,
|
|
1487
|
+
},
|
|
1488
|
+
usageSource: 'judge',
|
|
1489
|
+
usageModel: judgeResult.usageModel,
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
onToolEvent?.({
|
|
1494
|
+
type: 'task_activity',
|
|
1495
|
+
taskState: judgeResult.error ? 'failed' : 'completed',
|
|
1496
|
+
taskId: nestedStepId,
|
|
1497
|
+
judgeBackend: judgeResult.backendName,
|
|
1498
|
+
judgeQuestionIds: judgeResult.ids,
|
|
1499
|
+
judgeState: judgeResult.error ? 'failed' : 'completed',
|
|
1500
|
+
});
|
|
1501
|
+
break;
|
|
1502
|
+
}
|
|
1146
1503
|
case 'request_agent_loop': {
|
|
1147
1504
|
// Tool is calling task() or prompt() - run nested agent loop
|
|
1148
1505
|
const taskDescription = String(payload?.taskDescription ?? '');
|
|
@@ -1275,6 +1632,8 @@ export class DMLRunner {
|
|
|
1275
1632
|
},
|
|
1276
1633
|
streaming: this.options.streaming,
|
|
1277
1634
|
debug: this.options.debug,
|
|
1635
|
+
llmBackend: this.options.llmBackend,
|
|
1636
|
+
resolveMedia: (refs) => this.readMediaRefs(runContext.workspacePath, refs),
|
|
1278
1637
|
onOutput: (text) => {
|
|
1279
1638
|
outputs.push(text);
|
|
1280
1639
|
onOutput(text);
|
|
@@ -1282,26 +1641,26 @@ export class DMLRunner {
|
|
|
1282
1641
|
onStream: (chunk, done) => {
|
|
1283
1642
|
onToolEvent?.({ type: 'stream', content: chunk, done });
|
|
1284
1643
|
},
|
|
1644
|
+
onReasoning: (chunk, done) => {
|
|
1645
|
+
onToolEvent?.({ type: 'reasoning', content: chunk, done });
|
|
1646
|
+
},
|
|
1285
1647
|
onUsage: (usage) => {
|
|
1286
1648
|
onToolEvent?.({ type: 'usage', usage });
|
|
1287
1649
|
},
|
|
1650
|
+
onModelCallPhase: (phase) => {
|
|
1651
|
+
onToolEvent?.({ type: 'model_call', modelCallState: phase, modelId: this.options.model });
|
|
1652
|
+
},
|
|
1288
1653
|
onToolCall: onToolCall ?? (() => { }), // Forward tool call events to parent
|
|
1289
|
-
onBeforeModelCall: async (messages, lastInputTokens) =>
|
|
1290
|
-
messages,
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
trigger: 'before_model_call',
|
|
1294
|
-
options: { compaction: runContext.compaction },
|
|
1295
|
-
executionContext: runContext,
|
|
1296
|
-
registeredTools,
|
|
1297
|
-
toolPolicy: runContext.toolPolicy ?? null,
|
|
1298
|
-
emitEvent: onToolEvent,
|
|
1299
|
-
}),
|
|
1654
|
+
onBeforeModelCall: async (messages, lastInputTokens) => {
|
|
1655
|
+
const hostCompacted = await this.applyHostCompaction(messages, lastInputTokens, onToolEvent);
|
|
1656
|
+
return hostCompacted ?? messages;
|
|
1657
|
+
},
|
|
1300
1658
|
onAskUser: async () => {
|
|
1301
1659
|
// Nested agent loops don't support ask_user directly
|
|
1302
1660
|
// If needed, the tool should use exec(ask_user, ...) instead
|
|
1303
1661
|
return '';
|
|
1304
1662
|
},
|
|
1663
|
+
onModelCall: runContext.onModelCall,
|
|
1305
1664
|
signal: runContext.signal,
|
|
1306
1665
|
});
|
|
1307
1666
|
if (process.env.DEBUG_RUNNER) {
|
|
@@ -1363,116 +1722,17 @@ export class DMLRunner {
|
|
|
1363
1722
|
}
|
|
1364
1723
|
}
|
|
1365
1724
|
}
|
|
1366
|
-
async
|
|
1367
|
-
const
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
return params.messages;
|
|
1371
|
-
}
|
|
1372
|
-
let messages = params.messages;
|
|
1373
|
-
for (const binding of bindings) {
|
|
1374
|
-
const result = await executeCompactor({
|
|
1375
|
-
binding,
|
|
1376
|
-
messages,
|
|
1377
|
-
knownInputTokens: params.lastInputTokens,
|
|
1378
|
-
maxContextTokens: this.options.contextWindow,
|
|
1379
|
-
emitEvent: params.emitEvent,
|
|
1380
|
-
execute: (request) => this.runBoundCompactor({
|
|
1381
|
-
request,
|
|
1382
|
-
executionContext: params.executionContext,
|
|
1383
|
-
registeredTools: params.registeredTools,
|
|
1384
|
-
toolPolicy: params.toolPolicy,
|
|
1385
|
-
}),
|
|
1386
|
-
});
|
|
1387
|
-
params.emitEvent?.(result.event);
|
|
1388
|
-
if (result.usageByModel) {
|
|
1389
|
-
for (const totals of Object.values(result.usageByModel)) {
|
|
1390
|
-
params.emitEvent?.({ type: 'usage', usage: {
|
|
1391
|
-
inputTokens: totals.inputTokens,
|
|
1392
|
-
outputTokens: totals.outputTokens,
|
|
1393
|
-
totalTokens: totals.totalTokens,
|
|
1394
|
-
cacheReadTokens: totals.cacheReadTokens || undefined,
|
|
1395
|
-
cacheWriteTokens: totals.cacheWriteTokens || undefined,
|
|
1396
|
-
reasoningTokens: totals.reasoningTokens || undefined,
|
|
1397
|
-
} });
|
|
1398
|
-
}
|
|
1399
|
-
}
|
|
1400
|
-
messages = result.messages;
|
|
1401
|
-
}
|
|
1402
|
-
return messages;
|
|
1403
|
-
}
|
|
1404
|
-
async runBoundCompactor(params) {
|
|
1405
|
-
const { binding, params: compactorParams } = params.request;
|
|
1406
|
-
const code = await this.resolveCompactorSource(binding.compactor.source, binding.compactor.sourceType, params.executionContext.workspacePath);
|
|
1407
|
-
const model = binding.compactor.model ?? this.options.model;
|
|
1408
|
-
const provider = binding.compactor.provider
|
|
1409
|
-
?? (binding.compactor.model ? detectProvider(model) : this.options.provider);
|
|
1410
|
-
const compactorRunner = new DMLRunner(this.swipl, {
|
|
1411
|
-
...this.options,
|
|
1412
|
-
model,
|
|
1413
|
-
provider,
|
|
1414
|
-
streaming: false,
|
|
1415
|
-
compaction: { enabled: false },
|
|
1416
|
-
});
|
|
1417
|
-
const { signal, dispose } = createChildAbortController(params.executionContext.signal, binding.compactor.timeoutMs);
|
|
1418
|
-
try {
|
|
1419
|
-
const tools = binding.compactor.inheritTools ? params.registeredTools : new Map();
|
|
1420
|
-
let answer = '';
|
|
1421
|
-
let errorContent = '';
|
|
1422
|
-
const usageByModel = {};
|
|
1423
|
-
for await (const event of compactorRunner.run(code, {
|
|
1424
|
-
workspacePath: params.executionContext.workspacePath,
|
|
1425
|
-
gasLimit: binding.compactor.gasLimit,
|
|
1426
|
-
params: compactorParams,
|
|
1427
|
-
initialMessages: params.request.messages,
|
|
1428
|
-
tools,
|
|
1429
|
-
toolPolicy: binding.compactor.toolPolicy ?? (binding.compactor.inheritTools ? params.toolPolicy : null) ?? null,
|
|
1430
|
-
onInputRequired: async (prompt) => {
|
|
1431
|
-
throw new Error(`Compactor requested unexpected input: ${prompt}`);
|
|
1432
|
-
},
|
|
1433
|
-
signal,
|
|
1434
|
-
compaction: { enabled: false },
|
|
1435
|
-
})) {
|
|
1436
|
-
if (event.type === 'answer' && event.content) {
|
|
1437
|
-
answer = event.content;
|
|
1438
|
-
}
|
|
1439
|
-
else if (event.type === 'error' && event.content) {
|
|
1440
|
-
errorContent = event.content;
|
|
1441
|
-
}
|
|
1442
|
-
else if (event.type === 'usage' && event.usage) {
|
|
1443
|
-
recordTokenUsage(usageByModel, this.options.model, event.usage);
|
|
1444
|
-
}
|
|
1445
|
-
}
|
|
1446
|
-
return {
|
|
1447
|
-
answer,
|
|
1448
|
-
error: errorContent || undefined,
|
|
1449
|
-
usageByModel: Object.keys(usageByModel).length > 0 ? usageByModel : undefined,
|
|
1450
|
-
};
|
|
1451
|
-
}
|
|
1452
|
-
finally {
|
|
1453
|
-
dispose();
|
|
1454
|
-
await compactorRunner.dispose();
|
|
1455
|
-
}
|
|
1456
|
-
}
|
|
1457
|
-
async resolveCompactorSource(source, sourceType, workspacePath) {
|
|
1458
|
-
if (sourceType === 'inline') {
|
|
1459
|
-
return source;
|
|
1725
|
+
async applyHostCompaction(messages, lastInputTokens, emit) {
|
|
1726
|
+
const host = this.options.compactMessages;
|
|
1727
|
+
if (!host) {
|
|
1728
|
+
return null;
|
|
1460
1729
|
}
|
|
1461
|
-
const
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
return await fs.readFile(candidatePath, 'utf8');
|
|
1465
|
-
}
|
|
1466
|
-
catch (error) {
|
|
1467
|
-
if (sourceType === 'file') {
|
|
1468
|
-
throw error;
|
|
1469
|
-
}
|
|
1470
|
-
return source;
|
|
1730
|
+
const result = await host(messages, lastInputTokens);
|
|
1731
|
+
if (result.event) {
|
|
1732
|
+
emit?.(result.event);
|
|
1471
1733
|
}
|
|
1734
|
+
return result.messages;
|
|
1472
1735
|
}
|
|
1473
|
-
/**
|
|
1474
|
-
* Extract memory from payload (now passed via state threading)
|
|
1475
|
-
*/
|
|
1476
1736
|
extractMessagesFromValue(rawMessages) {
|
|
1477
1737
|
const normalized = toJsValue(rawMessages);
|
|
1478
1738
|
if (!Array.isArray(normalized)) {
|
|
@@ -1482,19 +1742,46 @@ export class DMLRunner {
|
|
|
1482
1742
|
const msg = toJsValue(messageValue);
|
|
1483
1743
|
const role = String(toJsValue(msg.role) ?? 'user');
|
|
1484
1744
|
const content = String(toJsValue(msg.content) ?? '');
|
|
1745
|
+
const media = this.extractMediaRefs(msg.media);
|
|
1485
1746
|
if (role !== 'system' && role !== 'user' && role !== 'assistant') {
|
|
1486
|
-
return { role: 'user', content };
|
|
1747
|
+
return { role: 'user', content, media: media.length ? media : undefined };
|
|
1487
1748
|
}
|
|
1488
|
-
return { role: role, content };
|
|
1749
|
+
return { role: role, content, media: media.length ? media : undefined };
|
|
1489
1750
|
});
|
|
1490
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
|
+
}
|
|
1491
1771
|
extractMemoryFromPayload(rawPayload) {
|
|
1492
1772
|
const rawMemory = toJsValue(rawPayload.memory);
|
|
1493
1773
|
return this.extractMessagesFromValue(rawMemory);
|
|
1494
1774
|
}
|
|
1495
1775
|
serializeMemoryMessages(messages) {
|
|
1496
1776
|
return messages
|
|
1497
|
-
.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
|
+
})
|
|
1498
1785
|
.join(', ');
|
|
1499
1786
|
}
|
|
1500
1787
|
/**
|
|
@@ -1562,6 +1849,30 @@ export class DMLRunner {
|
|
|
1562
1849
|
const fullMemoryStr = this.serializeMemoryMessages(fullMemory ?? []);
|
|
1563
1850
|
this.query(`deepclause_mi:post_tool_agent_result('${this.sessionId}', '${toolEngineId}', ${result.success}, vars{${varsStr}}, [${messagesStr}], [${fullMemoryStr}])`);
|
|
1564
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
|
+
}
|
|
1565
1876
|
/**
|
|
1566
1877
|
* Provide user input to waiting Prolog
|
|
1567
1878
|
*/
|
|
@@ -1569,6 +1880,10 @@ export class DMLRunner {
|
|
|
1569
1880
|
const escaped = input.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
1570
1881
|
this.query(`deepclause_mi:provide_input('${this.sessionId}', "${escaped}")`);
|
|
1571
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
|
+
}
|
|
1572
1887
|
/**
|
|
1573
1888
|
* Execute a Prolog query
|
|
1574
1889
|
*/
|
|
@@ -1587,14 +1902,17 @@ export class DMLRunner {
|
|
|
1587
1902
|
* Cleanup resources
|
|
1588
1903
|
*/
|
|
1589
1904
|
cleanup() {
|
|
1590
|
-
if (this.
|
|
1905
|
+
if (this.frames.size > 0) {
|
|
1591
1906
|
try {
|
|
1907
|
+
// destroy_engine/1 ends every frame registered for the session and
|
|
1908
|
+
// retracts the session's dynamic state.
|
|
1592
1909
|
this.query(`deepclause_mi:destroy_engine('${this.sessionId}')`);
|
|
1593
1910
|
}
|
|
1594
1911
|
catch {
|
|
1595
1912
|
// Ignore cleanup errors
|
|
1596
1913
|
}
|
|
1597
|
-
this.
|
|
1914
|
+
this.frames.clear();
|
|
1915
|
+
this.defaultFrameId = null;
|
|
1598
1916
|
}
|
|
1599
1917
|
}
|
|
1600
1918
|
/**
|