deepclause-sdk 0.0.89 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +481 -941
- package/dist/agent.d.ts +19 -2
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +75 -21
- package/dist/agent.js.map +1 -1
- package/dist/cli/config.d.ts +385 -178
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +161 -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 +85 -411
- 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 +216 -0
- package/dist/cli/repl.d.ts.map +1 -0
- package/dist/cli/repl.js +1392 -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 +202 -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 +42 -0
- package/dist/cli/visualize.d.ts.map +1 -0
- package/dist/cli/visualize.js +150 -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 +5 -1
- package/dist/prolog/bridge.d.ts.map +1 -1
- package/dist/prolog/bridge.js +10 -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 +2440 -49
- package/dist/repl/agent.d.ts +90 -0
- package/dist/repl/agent.d.ts.map +1 -0
- package/dist/repl/agent.js +410 -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 +321 -0
- package/dist/repl/backend.d.ts.map +1 -0
- package/dist/repl/backend.js +415 -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 +99 -0
- package/dist/repl/harness-tools.d.ts.map +1 -0
- package/dist/repl/harness-tools.js +192 -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/imagegen.d.ts +29 -0
- package/dist/repl/imagegen.d.ts.map +1 -0
- package/dist/repl/imagegen.js +84 -0
- package/dist/repl/imagegen.js.map +1 -0
- package/dist/repl/index.d.ts +130 -0
- package/dist/repl/index.d.ts.map +1 -0
- package/dist/repl/index.js +112 -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/model-resolver.d.ts +59 -0
- package/dist/repl/model-resolver.d.ts.map +1 -0
- package/dist/repl/model-resolver.js +156 -0
- package/dist/repl/model-resolver.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 +187 -0
- package/dist/repl/render.d.ts.map +1 -0
- package/dist/repl/render.js +808 -0
- package/dist/repl/render.js.map +1 -0
- package/dist/repl/session.d.ts +424 -0
- package/dist/repl/session.d.ts.map +1 -0
- package/dist/repl/session.js +1473 -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 +41 -0
- package/dist/repl/tui/transcript.d.ts.map +1 -0
- package/dist/repl/tui/transcript.js +139 -0
- package/dist/repl/tui/transcript.js.map +1 -0
- package/dist/runner.d.ts +143 -13
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +532 -290
- 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 +1447 -0
- package/dist/system/assets/docs/DML_AUTHORING.md +195 -0
- package/dist/system/assets/docs/DML_REFERENCE.md +344 -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 +136 -119
- 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/ascii.d.ts +55 -0
- package/dist/visualize/ascii.d.ts.map +1 -0
- package/dist/visualize/ascii.js +242 -0
- package/dist/visualize/ascii.js.map +1 -0
- package/dist/visualize/display.d.ts +24 -0
- package/dist/visualize/display.d.ts.map +1 -0
- package/dist/visualize/display.js +29 -0
- package/dist/visualize/display.js.map +1 -0
- package/dist/visualize/graphics.d.ts +35 -0
- package/dist/visualize/graphics.d.ts.map +1 -0
- package/dist/visualize/graphics.js +96 -0
- package/dist/visualize/graphics.js.map +1 -0
- package/dist/visualize/image.d.ts +38 -0
- package/dist/visualize/image.d.ts.map +1 -0
- package/dist/visualize/image.js +95 -0
- package/dist/visualize/image.js.map +1 -0
- package/dist/visualize/index.d.ts +59 -0
- package/dist/visualize/index.d.ts.map +1 -0
- package/dist/visualize/index.js +198 -0
- package/dist/visualize/index.js.map +1 -0
- package/dist/visualize/raster.d.ts +23 -0
- package/dist/visualize/raster.d.ts.map +1 -0
- package/dist/visualize/raster.js +47 -0
- package/dist/visualize/raster.js.map +1 -0
- package/dist/visualize/svg.d.ts +39 -0
- package/dist/visualize/svg.d.ts.map +1 -0
- package/dist/visualize/svg.js +148 -0
- package/dist/visualize/svg.js.map +1 -0
- package/dist/visualize/transcode.d.ts +13 -0
- package/dist/visualize/transcode.d.ts.map +1 -0
- package/dist/visualize/transcode.js +40 -0
- package/dist/visualize/transcode.js.map +1 -0
- package/docs/images/repl.png +0 -0
- package/package.json +19 -15
- package/src/prolog-src/deepclause_analysis.pl +10 -2
- package/src/prolog-src/deepclause_mi.pl +2440 -49
- 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,21 @@
|
|
|
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';
|
|
12
|
+
import { resolveMedia } from './visualize/image.js';
|
|
13
|
+
import { parseModelSpec } from './repl/model-resolver.js';
|
|
15
14
|
/**
|
|
16
15
|
* Convert swipl-wasm value to plain JavaScript value
|
|
17
16
|
* Handles PrologString, PrologAtom, PrologList, etc.
|
|
18
17
|
*/
|
|
19
|
-
function toJsValue(value) {
|
|
18
|
+
export function toJsValue(value) {
|
|
20
19
|
if (value === null || value === undefined) {
|
|
21
20
|
return value;
|
|
22
21
|
}
|
|
@@ -60,45 +59,45 @@ function toJsValue(value) {
|
|
|
60
59
|
}
|
|
61
60
|
return value;
|
|
62
61
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Decode the `bindings` payload of a goal-mode `solution` event.
|
|
64
|
+
*
|
|
65
|
+
* `collect_bindings/2` yields a list of `Name-Text` pairs. swipl-wasm encodes
|
|
66
|
+
* that compound as `{ '$t': 't', '-': [[Name, Text]] }`; a dict/array form is
|
|
67
|
+
* also accepted for forward compatibility.
|
|
68
|
+
*/
|
|
69
|
+
export function decodeSolutionBindings(raw) {
|
|
70
|
+
const normalized = toJsValue(raw);
|
|
71
|
+
if (!Array.isArray(normalized)) {
|
|
72
|
+
return [];
|
|
73
73
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
74
|
+
const bindings = [];
|
|
75
|
+
for (const item of normalized) {
|
|
76
|
+
const decoded = toJsValue(item);
|
|
77
|
+
if (decoded && typeof decoded === 'object') {
|
|
78
|
+
const compound = decoded;
|
|
79
|
+
const args = compound['-'];
|
|
80
|
+
if (Array.isArray(args) && Array.isArray(args[0]) && args[0].length >= 2) {
|
|
81
|
+
const [name, text] = args[0];
|
|
82
|
+
bindings.push({ name: String(toJsValue(name) ?? ''), value: String(toJsValue(text) ?? '') });
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if ('name' in compound || 'value' in compound) {
|
|
86
|
+
bindings.push({
|
|
87
|
+
name: String(toJsValue(compound.name) ?? ''),
|
|
88
|
+
value: String(toJsValue(compound.value) ?? ''),
|
|
89
|
+
});
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
82
92
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
93
|
+
if (Array.isArray(decoded) && decoded.length >= 2) {
|
|
94
|
+
bindings.push({
|
|
95
|
+
name: String(toJsValue(decoded[0]) ?? ''),
|
|
96
|
+
value: String(toJsValue(decoded[1]) ?? ''),
|
|
97
|
+
});
|
|
87
98
|
}
|
|
88
99
|
}
|
|
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
|
-
};
|
|
100
|
+
return bindings;
|
|
102
101
|
}
|
|
103
102
|
export function decodeAgentOutputVars(rawOutputVars) {
|
|
104
103
|
const normalized = toJsValue(rawOutputVars);
|
|
@@ -142,11 +141,18 @@ function prologTypeToJsonType(prologType) {
|
|
|
142
141
|
export class DMLRunner {
|
|
143
142
|
swipl;
|
|
144
143
|
options;
|
|
145
|
-
engine = null;
|
|
146
144
|
sessionId = '';
|
|
147
145
|
currentMemory = [];
|
|
148
146
|
stepCounter = 0;
|
|
149
147
|
judgeCache = new Map();
|
|
148
|
+
/**
|
|
149
|
+
* Runner-owned frame table: FrameId -> { frame, engine }.
|
|
150
|
+
* The Prolog session owns the authoritative engine handles
|
|
151
|
+
* (session_frame_engine/2); this map mirrors them for the driver.
|
|
152
|
+
*/
|
|
153
|
+
frames = new Map();
|
|
154
|
+
/** FrameId of the session's default query frame (created by createEngine). */
|
|
155
|
+
defaultFrameId = null;
|
|
150
156
|
constructor(swipl, options) {
|
|
151
157
|
this.swipl = swipl;
|
|
152
158
|
this.options = options;
|
|
@@ -191,94 +197,18 @@ export class DMLRunner {
|
|
|
191
197
|
yield { type: 'error', content: parseResult.error };
|
|
192
198
|
return;
|
|
193
199
|
}
|
|
194
|
-
// Create cooperative engine with args
|
|
200
|
+
// Create cooperative engine with args (registers the default query frame)
|
|
195
201
|
const engineResult = this.createEngine(memoryId, args, params);
|
|
196
202
|
if (engineResult.error) {
|
|
197
203
|
yield { type: 'error', content: engineResult.error };
|
|
198
204
|
return;
|
|
199
205
|
}
|
|
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
|
-
}
|
|
206
|
+
const frame = this.defaultFrameId ? this.frames.get(this.defaultFrameId)?.frame : undefined;
|
|
207
|
+
if (!frame) {
|
|
208
|
+
yield { type: 'error', content: 'Failed to register default query frame' };
|
|
209
|
+
return;
|
|
281
210
|
}
|
|
211
|
+
yield* this.stepLoop(frame, options);
|
|
282
212
|
}
|
|
283
213
|
catch (error) {
|
|
284
214
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -289,6 +219,185 @@ export class DMLRunner {
|
|
|
289
219
|
this.cleanup();
|
|
290
220
|
}
|
|
291
221
|
}
|
|
222
|
+
/**
|
|
223
|
+
* Drive a single frame until it finishes or errors, yielding its events.
|
|
224
|
+
*
|
|
225
|
+
* This is the extracted, reentrant form of the original `while(true)` loop.
|
|
226
|
+
* `run()` calls it for the default query frame; `runFrame` calls it for
|
|
227
|
+
* nested frames. Because frames are read from the Prolog frame table via
|
|
228
|
+
* `step_frame/4`, stepping a different frame never disturbs a suspended
|
|
229
|
+
* parent's engine.
|
|
230
|
+
*/
|
|
231
|
+
async *stepLoop(frame, options) {
|
|
232
|
+
for await (const event of this.stepLoopInner(frame, options)) {
|
|
233
|
+
// Preserve frame metadata already attached by a nested frame; otherwise
|
|
234
|
+
// stamp this frame so the renderer can attribute output correctly.
|
|
235
|
+
yield {
|
|
236
|
+
...event,
|
|
237
|
+
frameId: event.frameId ?? frame.id,
|
|
238
|
+
depth: event.depth ?? frame.depth,
|
|
239
|
+
frameKind: event.frameKind ?? frame.kind,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
async *stepLoopInner(frame, options) {
|
|
244
|
+
// Cooperative execution loop
|
|
245
|
+
while (true) {
|
|
246
|
+
// Check for abort
|
|
247
|
+
if (options.signal?.aborted) {
|
|
248
|
+
const reason = options.signal.reason;
|
|
249
|
+
const abortMessage = reason instanceof Error
|
|
250
|
+
? reason.message
|
|
251
|
+
: typeof reason === 'string' && reason.trim().length > 0
|
|
252
|
+
? reason
|
|
253
|
+
: 'Execution aborted';
|
|
254
|
+
yield { type: 'error', content: abortMessage };
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
// Step the frame
|
|
258
|
+
const step = this.stepFrame(frame.id);
|
|
259
|
+
if (process.env.DEBUG_STEPS) {
|
|
260
|
+
console.log('[MAIN_LOOP] step:', step.status, step.content?.substring(0, 50));
|
|
261
|
+
}
|
|
262
|
+
switch (step.status) {
|
|
263
|
+
case 'output':
|
|
264
|
+
if (step.content) {
|
|
265
|
+
if (process.env.DEBUG_STEPS) {
|
|
266
|
+
console.log('[MAIN_LOOP] yielding output:', step.content.substring(0, 50));
|
|
267
|
+
}
|
|
268
|
+
yield { type: 'output', content: step.content };
|
|
269
|
+
}
|
|
270
|
+
break;
|
|
271
|
+
case 'media': {
|
|
272
|
+
// `output(media(...))` — resolve to a base64 PNG for inline display.
|
|
273
|
+
const payload = (step.payload ?? {});
|
|
274
|
+
const media = {
|
|
275
|
+
path: typeof payload.path === 'string' && payload.path ? payload.path : undefined,
|
|
276
|
+
mimeType: typeof payload.mimeType === 'string' ? payload.mimeType : 'image/png',
|
|
277
|
+
kind: typeof payload.kind === 'string' ? payload.kind : undefined,
|
|
278
|
+
data: typeof payload.data === 'string' && payload.data ? payload.data : undefined,
|
|
279
|
+
};
|
|
280
|
+
const resolved = await resolveMedia(options.workspacePath ?? process.cwd(), media);
|
|
281
|
+
yield {
|
|
282
|
+
type: 'media',
|
|
283
|
+
media: {
|
|
284
|
+
path: media.path,
|
|
285
|
+
mimeType: media.mimeType,
|
|
286
|
+
kind: media.kind,
|
|
287
|
+
data: resolved?.pngBase64,
|
|
288
|
+
},
|
|
289
|
+
};
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
case 'log':
|
|
293
|
+
if (step.content) {
|
|
294
|
+
yield { type: 'log', content: step.content };
|
|
295
|
+
}
|
|
296
|
+
break;
|
|
297
|
+
case 'answer':
|
|
298
|
+
yield { type: 'answer', content: step.content };
|
|
299
|
+
break;
|
|
300
|
+
case 'solution': {
|
|
301
|
+
const payload = (step.payload ?? {});
|
|
302
|
+
yield {
|
|
303
|
+
type: 'solution',
|
|
304
|
+
solution: {
|
|
305
|
+
bindings: decodeSolutionBindings(payload.bindings),
|
|
306
|
+
deterministic: String(toJsValue(payload.deterministic) ?? 'false') === 'true',
|
|
307
|
+
},
|
|
308
|
+
};
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
case 'request_agent_loop':
|
|
312
|
+
// Run agent loop for task() predicate. The frame kind decides the
|
|
313
|
+
// tool set: `meta` frames get the host meta tools and hide the
|
|
314
|
+
// harness's own DML tools (proposal §9.2). Budget exhaustion (and any
|
|
315
|
+
// other agent-loop error) is surfaced as an error event.
|
|
316
|
+
try {
|
|
317
|
+
yield* this.handleAgentLoop(step.payload, '', options, frame);
|
|
318
|
+
}
|
|
319
|
+
catch (error) {
|
|
320
|
+
yield { type: 'error', content: error instanceof Error ? error.message : String(error) };
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
break;
|
|
324
|
+
case 'meta_result': {
|
|
325
|
+
const payload = (step.payload ?? {});
|
|
326
|
+
yield {
|
|
327
|
+
type: 'meta_result',
|
|
328
|
+
metaResult: {
|
|
329
|
+
code: String(toJsValue(payload.code) ?? ''),
|
|
330
|
+
answer: String(toJsValue(payload.answer) ?? ''),
|
|
331
|
+
},
|
|
332
|
+
};
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
case 'request_exec':
|
|
336
|
+
// Execute external tool
|
|
337
|
+
yield* this.handleExec(step.payload, options);
|
|
338
|
+
break;
|
|
339
|
+
case 'request_sample_token':
|
|
340
|
+
yield* this.handleSampleToken(step.payload, options);
|
|
341
|
+
break;
|
|
342
|
+
case 'request_llm':
|
|
343
|
+
yield* this.handleLlm(step.payload, options);
|
|
344
|
+
break;
|
|
345
|
+
case 'request_judgment':
|
|
346
|
+
yield* this.handleJudgment(step.payload, options);
|
|
347
|
+
break;
|
|
348
|
+
case 'wait_input': {
|
|
349
|
+
// Request user input
|
|
350
|
+
yield { type: 'input_required', prompt: step.prompt };
|
|
351
|
+
const input = await options.onInputRequired(step.prompt ?? '');
|
|
352
|
+
if (input === undefined || input === null) {
|
|
353
|
+
this.cancelInput();
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
this.provideInput(input);
|
|
357
|
+
}
|
|
358
|
+
break;
|
|
359
|
+
}
|
|
360
|
+
case 'finished':
|
|
361
|
+
// Include trace data if trace mode was enabled
|
|
362
|
+
if (this.options.trace && step.payload) {
|
|
363
|
+
const traceData = toJsValue(step.payload.trace);
|
|
364
|
+
yield { type: 'finished', trace: Array.isArray(traceData) ? traceData : undefined };
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
yield { type: 'finished' };
|
|
368
|
+
}
|
|
369
|
+
return;
|
|
370
|
+
case 'error':
|
|
371
|
+
if (this.options.trace && step.payload) {
|
|
372
|
+
const traceData = toJsValue(step.payload.trace);
|
|
373
|
+
yield { type: 'error', content: step.content, trace: Array.isArray(traceData) ? traceData : undefined };
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
yield { type: 'error', content: step.content };
|
|
377
|
+
}
|
|
378
|
+
return;
|
|
379
|
+
default:
|
|
380
|
+
yield { type: 'error', content: `Unknown step status: ${step.status}` };
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Run a specific frame to completion, yielding its events.
|
|
387
|
+
*
|
|
388
|
+
* This is the reentrant entry point used by nested execution (the REPL's
|
|
389
|
+
* `execute` meta tool): the caller builds a child frame with
|
|
390
|
+
* `createNestedFrame`, runs it with this method while the parent frame is
|
|
391
|
+
* suspended, then posts the result back to the parent.
|
|
392
|
+
*/
|
|
393
|
+
async *runFrame(frameId, options) {
|
|
394
|
+
const frame = this.frames.get(frameId)?.frame;
|
|
395
|
+
if (!frame) {
|
|
396
|
+
yield { type: 'error', content: `Unknown frame: ${frameId}` };
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
yield* this.stepLoop(frame, options);
|
|
400
|
+
}
|
|
292
401
|
/**
|
|
293
402
|
* Parse DML code into Prolog clauses
|
|
294
403
|
*/
|
|
@@ -381,10 +490,15 @@ export class DMLRunner {
|
|
|
381
490
|
createEngine(memoryId, args, params) {
|
|
382
491
|
try {
|
|
383
492
|
const initialMemory = this.serializeMemoryMessages(this.currentMemory);
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
493
|
+
// create_engine/6 registers the default `query` frame; default_frame/2
|
|
494
|
+
// returns its FrameId so the runner can address it explicitly.
|
|
495
|
+
const result = this.query(`deepclause_mi:create_engine('${this.sessionId}', '${memoryId}', ${args}, ${params}, [${initialMemory}], Engine), deepclause_mi:default_frame('${this.sessionId}', FrameId)`);
|
|
496
|
+
if (result && result.Engine && result.FrameId) {
|
|
497
|
+
const frameId = String(toJsValue(result.FrameId));
|
|
498
|
+
const frame = { id: frameId, kind: 'query', depth: 0 };
|
|
499
|
+
this.frames.set(frameId, { frame, engine: result.Engine });
|
|
500
|
+
this.defaultFrameId = frameId;
|
|
501
|
+
return { frameId };
|
|
388
502
|
}
|
|
389
503
|
return { error: 'Failed to create engine' };
|
|
390
504
|
}
|
|
@@ -394,11 +508,93 @@ export class DMLRunner {
|
|
|
394
508
|
}
|
|
395
509
|
}
|
|
396
510
|
/**
|
|
397
|
-
*
|
|
511
|
+
* Create a nested frame that runs a Prolog goal in this session.
|
|
512
|
+
*
|
|
513
|
+
* `goalTerm` is inserted verbatim into create_frame/6, so callers MUST pass
|
|
514
|
+
* a term built with the runner's `toPrologTerm` escaping or parsed from DML
|
|
515
|
+
* via `read_term` — never raw user text (foot gun #18). `stateTerm` is an
|
|
516
|
+
* optional Prolog `state{...}` term; the default is an empty state with no
|
|
517
|
+
* gas limit.
|
|
518
|
+
*
|
|
519
|
+
* Returns the registered frame, or null on failure. The caller is
|
|
520
|
+
* responsible for running (`runFrame`) and ending (`endFrame`) it.
|
|
398
521
|
*/
|
|
399
|
-
|
|
522
|
+
createNestedFrame(params) {
|
|
523
|
+
const depth = params.depth ?? 1;
|
|
524
|
+
const stateTerm = params.stateTerm
|
|
525
|
+
?? 'state{memory: [], params: params{}, context_stack: [], depth: 0}';
|
|
526
|
+
const result = this.query(`deepclause_mi:create_frame('${this.sessionId}', ${params.kind}, ${depth}, ${params.goalTerm}, ${stateTerm}, FrameId)`);
|
|
527
|
+
if (!result || !result.FrameId) {
|
|
528
|
+
return null;
|
|
529
|
+
}
|
|
530
|
+
const id = String(toJsValue(result.FrameId));
|
|
531
|
+
const frame = { id, kind: params.kind, depth };
|
|
532
|
+
this.frames.set(id, { frame, engine: null });
|
|
533
|
+
return frame;
|
|
534
|
+
}
|
|
535
|
+
/** End a frame, destroying its engine in the Prolog session. */
|
|
536
|
+
endFrame(frameId) {
|
|
537
|
+
this.query(`deepclause_mi:end_frame('${frameId}')`);
|
|
538
|
+
this.frames.delete(frameId);
|
|
539
|
+
if (this.defaultFrameId === frameId) {
|
|
540
|
+
this.defaultFrameId = null;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Bind this runner to an existing session id. Used by the REPL's
|
|
545
|
+
* DMLSession, whose session outlives a single `run()`.
|
|
546
|
+
*/
|
|
547
|
+
bindSession(sessionId) {
|
|
548
|
+
this.sessionId = sessionId;
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Adopt the session's default frame (for example one created by the REPL's
|
|
552
|
+
* `query_start/5`) into the runner's frame table so `runFrame` can drive it.
|
|
553
|
+
*/
|
|
554
|
+
registerDefaultFrame(kind, depth = 0) {
|
|
555
|
+
const result = this.query(`deepclause_mi:default_frame('${this.sessionId}', FrameId)`);
|
|
556
|
+
if (!result || !result.FrameId) {
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
const id = String(toJsValue(result.FrameId));
|
|
560
|
+
const frame = { id, kind, depth };
|
|
561
|
+
this.frames.set(id, { frame, engine: null });
|
|
562
|
+
this.defaultFrameId = id;
|
|
563
|
+
return frame;
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Adopt a Prolog-created frame into the runner's frame table. Used by the
|
|
567
|
+
* REPL for `execute` child frames (`repl_execute_frame/4`).
|
|
568
|
+
*/
|
|
569
|
+
registerFrame(frameId, kind, depth = 0) {
|
|
570
|
+
const frame = { id: frameId, kind, depth };
|
|
571
|
+
this.frames.set(frameId, { frame, engine: null });
|
|
572
|
+
return frame;
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Drop all runner-side frame bookkeeping without touching Prolog. Used after
|
|
576
|
+
* the REPL clears a session (the Prolog side already ended every frame).
|
|
577
|
+
*/
|
|
578
|
+
clearFrames() {
|
|
579
|
+
this.frames.clear();
|
|
580
|
+
this.defaultFrameId = null;
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Step a specific frame exactly once and return its raw step result.
|
|
584
|
+
* Used by the reentrant driver and nested-execution tests.
|
|
585
|
+
*/
|
|
586
|
+
stepFrameOnce(frameId) {
|
|
587
|
+
if (!this.frames.has(frameId)) {
|
|
588
|
+
return null;
|
|
589
|
+
}
|
|
590
|
+
return this.stepFrame(frameId);
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Step the cooperative engine for a frame
|
|
594
|
+
*/
|
|
595
|
+
stepFrame(frameId) {
|
|
400
596
|
try {
|
|
401
|
-
const result = this.query(`deepclause_mi:
|
|
597
|
+
const result = this.query(`deepclause_mi:step_frame('${frameId}', Status, Content, Payload)`);
|
|
402
598
|
// Convert swipl-wasm values to plain JS
|
|
403
599
|
const status = toJsValue(result?.Status);
|
|
404
600
|
const content = toJsValue(result?.Content);
|
|
@@ -419,13 +615,15 @@ export class DMLRunner {
|
|
|
419
615
|
* Handle agent loop request (task() predicate)
|
|
420
616
|
*/
|
|
421
617
|
async *handleAgentLoop(payload, _memoryId, // No longer used - memory comes from payload
|
|
422
|
-
options) {
|
|
618
|
+
options, _frame) {
|
|
423
619
|
const rawPayload = payload;
|
|
424
620
|
const taskDescription = String(toJsValue(rawPayload.taskDescription) ?? '');
|
|
425
621
|
const stepId = `step_${this.stepCounter++}`;
|
|
426
622
|
yield { type: 'task_activity', taskState: 'started', taskDescription, taskId: stepId };
|
|
427
623
|
let stepSucceeded = null;
|
|
428
624
|
try {
|
|
625
|
+
// The per-model-call budget is enforced inside the agent loop (before each
|
|
626
|
+
// model call) so it also covers multi-iteration turns.
|
|
429
627
|
// Ensure outputVars is an array of either strings or TypedVar objects
|
|
430
628
|
const outputVars = decodeAgentOutputVars(rawPayload.outputVars);
|
|
431
629
|
// Parse userTools - now contains schema info as array of tool_info dicts
|
|
@@ -455,14 +653,48 @@ export class DMLRunner {
|
|
|
455
653
|
};
|
|
456
654
|
// Extract memory from payload (now passed via state threading)
|
|
457
655
|
const preparedMemory = this.extractMemoryFromPayload(rawPayload);
|
|
656
|
+
// A task with a `media(X)` typed output generates an image instead of running
|
|
657
|
+
// a text agent turn; the harness displays it only when it calls output(media(X)).
|
|
658
|
+
const mediaOutputs = outputVars.filter((v) => typeof v !== 'string' && v.type === 'media');
|
|
659
|
+
if (mediaOutputs.length > 0) {
|
|
660
|
+
const imageBackend = this.options.imageBackend;
|
|
661
|
+
if (!imageBackend) {
|
|
662
|
+
yield { type: 'error', content: 'image generation is unavailable: no image backend is configured' };
|
|
663
|
+
this.postAgentResult({ success: false, outputs: [], variables: {}, messages: preparedMemory }, preparedMemory);
|
|
664
|
+
stepSucceeded = false;
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
const refs = [];
|
|
668
|
+
for (const message of preparedMemory) {
|
|
669
|
+
for (const ref of message.media ?? []) {
|
|
670
|
+
refs.push(ref);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
const generated = await imageBackend.generate({
|
|
674
|
+
prompt: taskDescription,
|
|
675
|
+
refs: refs.length > 0 ? refs : undefined,
|
|
676
|
+
signal: options.signal,
|
|
677
|
+
});
|
|
678
|
+
const variables = {};
|
|
679
|
+
for (const output of mediaOutputs) {
|
|
680
|
+
variables[output.name] = generated.path;
|
|
681
|
+
}
|
|
682
|
+
this.postAgentResult({ success: true, outputs: [], variables, messages: preparedMemory }, preparedMemory);
|
|
683
|
+
stepSucceeded = true;
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
458
686
|
// Extract reasoning effort and recipe context (from with_reasoning/with_recipe scoping)
|
|
459
687
|
const reasoningEffort = String(toJsValue(rawPayload.reasoningEffort) ?? 'none');
|
|
460
688
|
const recipeContext = String(toJsValue(rawPayload.recipeContext) ?? '');
|
|
689
|
+
// Per-call model override from with_model/2 (undefined = the session model).
|
|
690
|
+
const scopedModel = await this.resolveScope(rawPayload.modelSpec);
|
|
461
691
|
// Build provider options — merge reasoning effort if set
|
|
462
692
|
const baseProviderOptions = this.options.providerOptions ?? {};
|
|
463
693
|
let effectiveProviderOptions = baseProviderOptions;
|
|
464
|
-
|
|
465
|
-
|
|
694
|
+
const reasoningType = scopedModel?.reasoningType ?? this.options.reasoningType;
|
|
695
|
+
const reasoningBudgetMap = scopedModel?.reasoningBudgetMap ?? this.options.reasoningBudgetMap;
|
|
696
|
+
if (reasoningEffort !== 'none' && reasoningType && reasoningType !== 'none') {
|
|
697
|
+
const reasoningOpts = buildReasoningProviderOptions(reasoningEffort, reasoningType, reasoningBudgetMap);
|
|
466
698
|
effectiveProviderOptions = { ...baseProviderOptions, ...reasoningOpts };
|
|
467
699
|
}
|
|
468
700
|
// Prepend recipe context as a system message if present
|
|
@@ -503,6 +735,9 @@ export class DMLRunner {
|
|
|
503
735
|
try {
|
|
504
736
|
emitToolEvent({ type: 'input_required', prompt });
|
|
505
737
|
const response = await options.onInputRequired(prompt);
|
|
738
|
+
if (response === undefined || response === null) {
|
|
739
|
+
return { cancelled: true };
|
|
740
|
+
}
|
|
506
741
|
return { user_response: response };
|
|
507
742
|
}
|
|
508
743
|
catch (err) {
|
|
@@ -515,14 +750,15 @@ export class DMLRunner {
|
|
|
515
750
|
const emitToolCall = (toolName, args) => {
|
|
516
751
|
emitToolEvent({ type: 'tool_call', toolName, toolArgs: args });
|
|
517
752
|
};
|
|
518
|
-
// Build available tools for agent
|
|
519
|
-
//
|
|
753
|
+
// Build available tools for the agent: the harness's DML tool/2 predicates
|
|
754
|
+
// plus registered TS tools (reachable via exec/2).
|
|
755
|
+
const selfReportingTools = undefined;
|
|
520
756
|
const availableTools = this.buildAgentTools(userTools, options.toolPolicy, async (toolName, args) => {
|
|
521
757
|
return this.executeToolIsolated(toolName, args, augmentedTools, onToolOutput, {
|
|
522
758
|
workspacePath: options.workspacePath,
|
|
523
759
|
signal: options.signal,
|
|
524
|
-
compaction: options.compaction,
|
|
525
760
|
toolPolicy: options.toolPolicy,
|
|
761
|
+
onModelCall: options.onModelCall,
|
|
526
762
|
}, [], emitToolCall, emitToolEvent);
|
|
527
763
|
});
|
|
528
764
|
// Run agent loop with streaming support
|
|
@@ -532,13 +768,15 @@ export class DMLRunner {
|
|
|
532
768
|
memory: effectiveMemory,
|
|
533
769
|
tools: availableTools,
|
|
534
770
|
workspacePath: options.workspacePath,
|
|
771
|
+
resolveMedia: (refs) => this.readMediaRefs(options.workspacePath, refs),
|
|
535
772
|
modelOptions: {
|
|
536
|
-
model: this.options.model,
|
|
537
|
-
provider: this.options.provider,
|
|
538
|
-
temperature: this.options.temperature,
|
|
539
|
-
maxOutputTokens: this.options.maxTokens,
|
|
773
|
+
model: scopedModel?.id ?? this.options.model,
|
|
774
|
+
provider: scopedModel?.provider ?? this.options.provider,
|
|
775
|
+
temperature: scopedModel?.temperature ?? this.options.temperature,
|
|
776
|
+
maxOutputTokens: scopedModel?.maxTokens ?? this.options.maxTokens,
|
|
540
777
|
baseUrl: this.options.baseUrl,
|
|
541
778
|
providerOptions: effectiveProviderOptions,
|
|
779
|
+
target: scopedModel,
|
|
542
780
|
},
|
|
543
781
|
streaming: this.options.streaming,
|
|
544
782
|
debug: this.options.debug,
|
|
@@ -550,6 +788,13 @@ export class DMLRunner {
|
|
|
550
788
|
streamResolve = null;
|
|
551
789
|
}
|
|
552
790
|
},
|
|
791
|
+
onReasoning: (chunk, done) => {
|
|
792
|
+
streamQueue.push({ type: 'reasoning', content: chunk, done });
|
|
793
|
+
if (streamResolve) {
|
|
794
|
+
streamResolve();
|
|
795
|
+
streamResolve = null;
|
|
796
|
+
}
|
|
797
|
+
},
|
|
553
798
|
onToolCall: (toolName, args) => {
|
|
554
799
|
streamQueue.push({ type: 'tool_call', toolName, toolArgs: args });
|
|
555
800
|
if (streamResolve) {
|
|
@@ -564,22 +809,27 @@ export class DMLRunner {
|
|
|
564
809
|
streamResolve = null;
|
|
565
810
|
}
|
|
566
811
|
},
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
}
|
|
812
|
+
onModelCallPhase: (phase) => {
|
|
813
|
+
streamQueue.push({ type: 'model_call', modelCallState: phase, modelId: this.options.model });
|
|
814
|
+
if (streamResolve) {
|
|
815
|
+
streamResolve();
|
|
816
|
+
streamResolve = null;
|
|
817
|
+
}
|
|
818
|
+
},
|
|
819
|
+
selfReportingTools,
|
|
820
|
+
onToolResult: (toolName, toolArgs, toolResult) => {
|
|
821
|
+
streamQueue.push(buildToolCompletionEvent(toolName, toolArgs, toolResult));
|
|
822
|
+
if (streamResolve) {
|
|
823
|
+
streamResolve();
|
|
824
|
+
streamResolve = null;
|
|
825
|
+
}
|
|
826
|
+
},
|
|
827
|
+
onBeforeModelCall: async (messages, lastInputTokens) => {
|
|
828
|
+
const hostCompacted = await this.applyHostCompaction(messages, lastInputTokens, emitRunnerEvent, scopedModel?.contextWindow);
|
|
829
|
+
return hostCompacted ?? messages;
|
|
830
|
+
},
|
|
582
831
|
onAskUser: options.onInputRequired,
|
|
832
|
+
onModelCall: options.onModelCall,
|
|
583
833
|
signal: options.signal,
|
|
584
834
|
llmBackend: this.options.llmBackend,
|
|
585
835
|
});
|
|
@@ -678,15 +928,17 @@ export class DMLRunner {
|
|
|
678
928
|
yield { type: 'task_activity', taskState: 'started', taskDescription, taskId: stepId };
|
|
679
929
|
let succeeded = false;
|
|
680
930
|
try {
|
|
931
|
+
const scopedModel = await this.resolveScope(data.modelSpec);
|
|
681
932
|
const { token, usage } = await sampleSingleToken({
|
|
682
933
|
prompt,
|
|
683
934
|
allowedTokens,
|
|
684
935
|
modelOptions: {
|
|
685
|
-
provider: this.options.provider,
|
|
686
|
-
model: this.options.model,
|
|
687
|
-
temperature: this.options.temperature,
|
|
936
|
+
provider: scopedModel?.provider ?? this.options.provider,
|
|
937
|
+
model: scopedModel?.id ?? this.options.model,
|
|
938
|
+
temperature: scopedModel?.temperature ?? this.options.temperature,
|
|
688
939
|
baseUrl: this.options.baseUrl,
|
|
689
940
|
providerOptions: this.options.providerOptions,
|
|
941
|
+
target: scopedModel,
|
|
690
942
|
},
|
|
691
943
|
signal: options.signal,
|
|
692
944
|
llmBackend: this.options.llmBackend,
|
|
@@ -717,18 +969,21 @@ export class DMLRunner {
|
|
|
717
969
|
yield { type: 'task_activity', taskState: 'started', taskDescription, taskId: stepId };
|
|
718
970
|
let succeeded = false;
|
|
719
971
|
try {
|
|
972
|
+
const scopedModel = await this.resolveScope(data.modelSpec);
|
|
720
973
|
const { text, usage } = await generateLlmReply({
|
|
721
974
|
messages,
|
|
722
975
|
modelOptions: {
|
|
723
|
-
provider: this.options.provider,
|
|
724
|
-
model: this.options.model,
|
|
725
|
-
temperature: this.options.temperature,
|
|
726
|
-
maxOutputTokens: this.options.maxTokens,
|
|
976
|
+
provider: scopedModel?.provider ?? this.options.provider,
|
|
977
|
+
model: scopedModel?.id ?? this.options.model,
|
|
978
|
+
temperature: scopedModel?.temperature ?? this.options.temperature,
|
|
979
|
+
maxOutputTokens: scopedModel?.maxTokens ?? this.options.maxTokens,
|
|
727
980
|
baseUrl: this.options.baseUrl,
|
|
728
981
|
providerOptions: this.options.providerOptions,
|
|
982
|
+
target: scopedModel,
|
|
729
983
|
},
|
|
730
984
|
signal: options.signal,
|
|
731
985
|
llmBackend: this.options.llmBackend,
|
|
986
|
+
resolveMedia: (refs) => this.readMediaRefs(options.workspacePath, refs),
|
|
732
987
|
});
|
|
733
988
|
succeeded = true;
|
|
734
989
|
this.postLlmResult({ success: true, text });
|
|
@@ -859,15 +1114,18 @@ export class DMLRunner {
|
|
|
859
1114
|
*/
|
|
860
1115
|
async *handleExec(payload, options) {
|
|
861
1116
|
const { toolName, args } = payload;
|
|
862
|
-
// Check tool policy
|
|
1117
|
+
// Check tool policy (ask_user is the human channel and is exempt).
|
|
863
1118
|
const policyCheck = checkToolPolicy(toolName, options.toolPolicy);
|
|
864
1119
|
if (!policyCheck.allowed) {
|
|
865
1120
|
yield { type: 'log', content: `Tool '${toolName}' blocked by policy: ${policyCheck.reason}` };
|
|
866
1121
|
this.postExecResult({ success: false, error: policyCheck.reason });
|
|
867
1122
|
return;
|
|
868
1123
|
}
|
|
869
|
-
// Get tool definition
|
|
870
|
-
|
|
1124
|
+
// Get tool definition. `ask_user` is SDK-internal so exec(ask_user(...))
|
|
1125
|
+
// works in DML even when no capability tools are registered.
|
|
1126
|
+
const tool = toolName === 'ask_user'
|
|
1127
|
+
? this.internalAskUserTool(options)
|
|
1128
|
+
: options.tools.get(toolName);
|
|
871
1129
|
if (!tool) {
|
|
872
1130
|
yield { type: 'log', content: `Tool '${toolName}' not found` };
|
|
873
1131
|
this.postExecResult({ success: false, error: `Tool not found: ${toolName}` });
|
|
@@ -879,7 +1137,7 @@ export class DMLRunner {
|
|
|
879
1137
|
argsObj = this.argsToObject(args, tool);
|
|
880
1138
|
// Emit tool_call event before execution
|
|
881
1139
|
yield buildToolStartEvent(toolName, argsObj);
|
|
882
|
-
const result = await tool.execute(argsObj);
|
|
1140
|
+
const result = await tool.execute(argsObj, { signal: options.signal });
|
|
883
1141
|
yield buildToolCompletionEvent(toolName, argsObj, result);
|
|
884
1142
|
// Post result back to Prolog
|
|
885
1143
|
this.postExecResult({ success: true, result });
|
|
@@ -1172,7 +1430,7 @@ export class DMLRunner {
|
|
|
1172
1430
|
try {
|
|
1173
1431
|
argsObj = this.argsToObject(execArgs, tool);
|
|
1174
1432
|
onToolEvent?.(buildToolStartEvent(execToolName, argsObj));
|
|
1175
|
-
const execResult = await tool.execute(argsObj);
|
|
1433
|
+
const execResult = await tool.execute(argsObj, { signal: runContext.signal });
|
|
1176
1434
|
onToolEvent?.(buildToolCompletionEvent(execToolName, argsObj, execResult));
|
|
1177
1435
|
const postGoal = `deepclause_mi:post_exec_result('${this.sessionId}', success, ${this.toPrologTerm(execResult)})`;
|
|
1178
1436
|
this.query(postGoal);
|
|
@@ -1243,6 +1501,7 @@ export class DMLRunner {
|
|
|
1243
1501
|
},
|
|
1244
1502
|
signal: runContext.signal,
|
|
1245
1503
|
llmBackend: this.options.llmBackend,
|
|
1504
|
+
resolveMedia: (refs) => this.readMediaRefs(runContext.workspacePath, refs),
|
|
1246
1505
|
});
|
|
1247
1506
|
this.postLlmResult({ success: true, text });
|
|
1248
1507
|
if (usage) {
|
|
@@ -1436,6 +1695,7 @@ export class DMLRunner {
|
|
|
1436
1695
|
streaming: this.options.streaming,
|
|
1437
1696
|
debug: this.options.debug,
|
|
1438
1697
|
llmBackend: this.options.llmBackend,
|
|
1698
|
+
resolveMedia: (refs) => this.readMediaRefs(runContext.workspacePath, refs),
|
|
1439
1699
|
onOutput: (text) => {
|
|
1440
1700
|
outputs.push(text);
|
|
1441
1701
|
onOutput(text);
|
|
@@ -1443,26 +1703,26 @@ export class DMLRunner {
|
|
|
1443
1703
|
onStream: (chunk, done) => {
|
|
1444
1704
|
onToolEvent?.({ type: 'stream', content: chunk, done });
|
|
1445
1705
|
},
|
|
1706
|
+
onReasoning: (chunk, done) => {
|
|
1707
|
+
onToolEvent?.({ type: 'reasoning', content: chunk, done });
|
|
1708
|
+
},
|
|
1446
1709
|
onUsage: (usage) => {
|
|
1447
1710
|
onToolEvent?.({ type: 'usage', usage });
|
|
1448
1711
|
},
|
|
1712
|
+
onModelCallPhase: (phase) => {
|
|
1713
|
+
onToolEvent?.({ type: 'model_call', modelCallState: phase, modelId: this.options.model });
|
|
1714
|
+
},
|
|
1449
1715
|
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
|
-
}),
|
|
1716
|
+
onBeforeModelCall: async (messages, lastInputTokens) => {
|
|
1717
|
+
const hostCompacted = await this.applyHostCompaction(messages, lastInputTokens, onToolEvent);
|
|
1718
|
+
return hostCompacted ?? messages;
|
|
1719
|
+
},
|
|
1461
1720
|
onAskUser: async () => {
|
|
1462
1721
|
// Nested agent loops don't support ask_user directly
|
|
1463
1722
|
// If needed, the tool should use exec(ask_user, ...) instead
|
|
1464
1723
|
return '';
|
|
1465
1724
|
},
|
|
1725
|
+
onModelCall: runContext.onModelCall,
|
|
1466
1726
|
signal: runContext.signal,
|
|
1467
1727
|
});
|
|
1468
1728
|
if (process.env.DEBUG_RUNNER) {
|
|
@@ -1524,116 +1784,17 @@ export class DMLRunner {
|
|
|
1524
1784
|
}
|
|
1525
1785
|
}
|
|
1526
1786
|
}
|
|
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');
|
|
1787
|
+
async applyHostCompaction(messages, lastInputTokens, emit, contextWindow) {
|
|
1788
|
+
const host = this.options.compactMessages;
|
|
1789
|
+
if (!host) {
|
|
1790
|
+
return null;
|
|
1626
1791
|
}
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
}
|
|
1631
|
-
return source;
|
|
1792
|
+
const result = await host(messages, lastInputTokens, contextWindow);
|
|
1793
|
+
if (result.event) {
|
|
1794
|
+
emit?.(result.event);
|
|
1632
1795
|
}
|
|
1796
|
+
return result.messages;
|
|
1633
1797
|
}
|
|
1634
|
-
/**
|
|
1635
|
-
* Extract memory from payload (now passed via state threading)
|
|
1636
|
-
*/
|
|
1637
1798
|
extractMessagesFromValue(rawMessages) {
|
|
1638
1799
|
const normalized = toJsValue(rawMessages);
|
|
1639
1800
|
if (!Array.isArray(normalized)) {
|
|
@@ -1643,19 +1804,46 @@ export class DMLRunner {
|
|
|
1643
1804
|
const msg = toJsValue(messageValue);
|
|
1644
1805
|
const role = String(toJsValue(msg.role) ?? 'user');
|
|
1645
1806
|
const content = String(toJsValue(msg.content) ?? '');
|
|
1807
|
+
const media = this.extractMediaRefs(msg.media);
|
|
1646
1808
|
if (role !== 'system' && role !== 'user' && role !== 'assistant') {
|
|
1647
|
-
return { role: 'user', content };
|
|
1809
|
+
return { role: 'user', content, media: media.length ? media : undefined };
|
|
1648
1810
|
}
|
|
1649
|
-
return { role: role, content };
|
|
1811
|
+
return { role: role, content, media: media.length ? media : undefined };
|
|
1650
1812
|
});
|
|
1651
1813
|
}
|
|
1814
|
+
/** Decode `media{path:..., mime:...}` dicts into reference-only refs. */
|
|
1815
|
+
extractMediaRefs(raw) {
|
|
1816
|
+
const value = toJsValue(raw);
|
|
1817
|
+
if (!Array.isArray(value))
|
|
1818
|
+
return [];
|
|
1819
|
+
const refs = [];
|
|
1820
|
+
for (const entry of value) {
|
|
1821
|
+
const obj = toJsValue(entry);
|
|
1822
|
+
const mediaPath = String(obj?.path ?? '');
|
|
1823
|
+
const mimeType = String(obj?.mime ?? obj?.mimeType ?? '');
|
|
1824
|
+
if (mediaPath && mimeType)
|
|
1825
|
+
refs.push({ path: mediaPath, mimeType });
|
|
1826
|
+
}
|
|
1827
|
+
return refs;
|
|
1828
|
+
}
|
|
1829
|
+
/** Resolve a message's reference-only media to base64 bytes (workspace files). */
|
|
1830
|
+
async readMediaRefs(workspacePath, refs) {
|
|
1831
|
+
return resolveMediaRefs(workspacePath ?? process.cwd(), refs);
|
|
1832
|
+
}
|
|
1652
1833
|
extractMemoryFromPayload(rawPayload) {
|
|
1653
1834
|
const rawMemory = toJsValue(rawPayload.memory);
|
|
1654
1835
|
return this.extractMessagesFromValue(rawMemory);
|
|
1655
1836
|
}
|
|
1656
1837
|
serializeMemoryMessages(messages) {
|
|
1657
1838
|
return messages
|
|
1658
|
-
.map((message) =>
|
|
1839
|
+
.map((message) => {
|
|
1840
|
+
const media = message.media && message.media.length > 0
|
|
1841
|
+
? `, media: [${message.media
|
|
1842
|
+
.map((ref) => `media{path: ${this.toPrologTerm(ref.path)}, mime: ${this.toPrologTerm(ref.mimeType)}}`)
|
|
1843
|
+
.join(', ')}]`
|
|
1844
|
+
: '';
|
|
1845
|
+
return `message{role: ${message.role}, content: ${this.toPrologTerm(message.content)}${media}}`;
|
|
1846
|
+
})
|
|
1659
1847
|
.join(', ');
|
|
1660
1848
|
}
|
|
1661
1849
|
/**
|
|
@@ -1723,6 +1911,30 @@ export class DMLRunner {
|
|
|
1723
1911
|
const fullMemoryStr = this.serializeMemoryMessages(fullMemory ?? []);
|
|
1724
1912
|
this.query(`deepclause_mi:post_tool_agent_result('${this.sessionId}', '${toolEngineId}', ${result.success}, vars{${varsStr}}, [${messagesStr}], [${fullMemoryStr}])`);
|
|
1725
1913
|
}
|
|
1914
|
+
/** SDK-internal ask_user tool used by exec(ask_user(...)) in DML. */
|
|
1915
|
+
internalAskUserTool(options) {
|
|
1916
|
+
return {
|
|
1917
|
+
description: 'Ask the user for input or clarification',
|
|
1918
|
+
parameters: {
|
|
1919
|
+
type: 'object',
|
|
1920
|
+
properties: {
|
|
1921
|
+
prompt: { type: 'string', description: 'The question or prompt to show the user' },
|
|
1922
|
+
},
|
|
1923
|
+
required: ['prompt'],
|
|
1924
|
+
},
|
|
1925
|
+
execute: async (args) => {
|
|
1926
|
+
const prompt = String(args.prompt ?? args.arg1 ?? '');
|
|
1927
|
+
if (!prompt) {
|
|
1928
|
+
return { error: 'No prompt provided to ask_user' };
|
|
1929
|
+
}
|
|
1930
|
+
const response = await options.onInputRequired(prompt);
|
|
1931
|
+
if (response === undefined || response === null) {
|
|
1932
|
+
return { cancelled: true };
|
|
1933
|
+
}
|
|
1934
|
+
return { user_response: response };
|
|
1935
|
+
},
|
|
1936
|
+
};
|
|
1937
|
+
}
|
|
1726
1938
|
/**
|
|
1727
1939
|
* Provide user input to waiting Prolog
|
|
1728
1940
|
*/
|
|
@@ -1730,9 +1942,36 @@ export class DMLRunner {
|
|
|
1730
1942
|
const escaped = input.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
1731
1943
|
this.query(`deepclause_mi:provide_input('${this.sessionId}', "${escaped}")`);
|
|
1732
1944
|
}
|
|
1945
|
+
/** Signal a waiting input/2 that no human is available. */
|
|
1946
|
+
cancelInput() {
|
|
1947
|
+
this.query(`deepclause_mi:provide_input_cancelled('${this.sessionId}')`);
|
|
1948
|
+
}
|
|
1733
1949
|
/**
|
|
1734
1950
|
* Execute a Prolog query
|
|
1735
1951
|
*/
|
|
1952
|
+
scopedModels = new Map();
|
|
1953
|
+
/**
|
|
1954
|
+
* Resolve a `with_model/2` spec to a model profile (cached). Returns undefined
|
|
1955
|
+
* for a `default`/absent scope (use the session model).
|
|
1956
|
+
*/
|
|
1957
|
+
async resolveScope(rawSpec) {
|
|
1958
|
+
const spec = parseModelSpec(rawSpec);
|
|
1959
|
+
if (spec === 'default') {
|
|
1960
|
+
return undefined;
|
|
1961
|
+
}
|
|
1962
|
+
const key = JSON.stringify(spec);
|
|
1963
|
+
const cached = this.scopedModels.get(key);
|
|
1964
|
+
if (cached) {
|
|
1965
|
+
return cached;
|
|
1966
|
+
}
|
|
1967
|
+
const resolver = this.options.modelResolver;
|
|
1968
|
+
if (!resolver) {
|
|
1969
|
+
throw new Error('with_model is unavailable: no model resolver is configured');
|
|
1970
|
+
}
|
|
1971
|
+
const resolved = await resolver.resolve(spec);
|
|
1972
|
+
this.scopedModels.set(key, resolved);
|
|
1973
|
+
return resolved;
|
|
1974
|
+
}
|
|
1736
1975
|
query(goal) {
|
|
1737
1976
|
try {
|
|
1738
1977
|
const q = this.swipl.prolog.query(goal);
|
|
@@ -1748,14 +1987,17 @@ export class DMLRunner {
|
|
|
1748
1987
|
* Cleanup resources
|
|
1749
1988
|
*/
|
|
1750
1989
|
cleanup() {
|
|
1751
|
-
if (this.
|
|
1990
|
+
if (this.frames.size > 0) {
|
|
1752
1991
|
try {
|
|
1992
|
+
// destroy_engine/1 ends every frame registered for the session and
|
|
1993
|
+
// retracts the session's dynamic state.
|
|
1753
1994
|
this.query(`deepclause_mi:destroy_engine('${this.sessionId}')`);
|
|
1754
1995
|
}
|
|
1755
1996
|
catch {
|
|
1756
1997
|
// Ignore cleanup errors
|
|
1757
1998
|
}
|
|
1758
|
-
this.
|
|
1999
|
+
this.frames.clear();
|
|
2000
|
+
this.defaultFrameId = null;
|
|
1759
2001
|
}
|
|
1760
2002
|
}
|
|
1761
2003
|
/**
|