@duckmind/dm-windows-x64 0.60.6 → 0.60.8
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/dm.exe +0 -0
- package/extensions/.dm-extensions.json +70 -123
- package/extensions/dm-9router-ext/src/index.js +410 -5
- package/extensions/dm-caveman/extensions/caveman.js +283 -12
- package/extensions/dm-cliproxy/index.js +182 -2
- package/extensions/dm-cliproxy/scripts/check-config-migration.js +66 -8
- package/extensions/dm-cliproxy/src/apply.js +228 -1
- package/extensions/dm-cliproxy/src/cache.js +42 -1
- package/extensions/dm-cliproxy/src/commands.js +50 -2
- package/extensions/dm-cliproxy/src/compat.js +81 -1
- package/extensions/dm-cliproxy/src/config.js +192 -2
- package/extensions/dm-cliproxy/src/conflicts.js +46 -1
- package/extensions/dm-cliproxy/src/fetch-models.js +190 -1
- package/extensions/dm-cliproxy/src/fetch-usage.js +41 -1
- package/extensions/dm-cliproxy/src/log.js +23 -1
- package/extensions/dm-cliproxy/src/status-quota.js +77 -1
- package/extensions/dm-cliproxy/src/ui-frame.js +50 -1
- package/extensions/dm-cliproxy/src/ui-hub/hub.js +199 -2
- package/extensions/dm-cliproxy/src/ui-hub/index.js +26 -2
- package/extensions/dm-cliproxy/src/ui-hub/shell.js +46 -1
- package/extensions/dm-cliproxy/src/ui-hub/view-diagnostics.js +69 -1
- package/extensions/dm-cliproxy/src/ui-hub/view-models.js +379 -1
- package/extensions/dm-cliproxy/src/ui-hub/view-usage.js +106 -1
- package/extensions/dm-cliproxy/src/ui-picker/catalog.js +41 -1
- package/extensions/dm-cliproxy/src/ui-picker/mutate.js +115 -1
- package/extensions/dm-cliproxy/src/ui-picker/prompt-confirm.js +35 -1
- package/extensions/dm-cliproxy/src/ui-picker/prompt-name.js +62 -1
- package/extensions/dm-cliproxy/src/ui-picker/providers.js +45 -1
- package/extensions/dm-cliproxy/src/ui-picker/render-text.js +34 -1
- package/extensions/dm-cliproxy/src/ui-picker/rows.js +57 -1
- package/extensions/dm-cliproxy/src/ui-setup.js +260 -2
- package/extensions/dm-cliproxy/src/ui-usage.js +151 -1
- package/extensions/dm-cliproxy/src/usage-shared-cache.js +97 -1
- package/extensions/dm-context/src/context.js +144 -1
- package/extensions/dm-context/src/index.js +339 -7
- package/extensions/dm-context/src/utils.js +9 -1
- package/extensions/dm-cua/bin/browser-cua.mjs +73 -8
- package/extensions/dm-cua/index.js +75 -6
- package/extensions/dm-cua/src/browser-cua-lib.mjs +490 -6
- package/extensions/dm-cua/src/browser-install.mjs +331 -2
- package/extensions/dm-fff/src/index.js +688 -12
- package/extensions/dm-fff/src/query.js +60 -1
- package/extensions/dm-goal/src/goal.js +894 -23
- package/extensions/dm-image2/index.js +103 -9
- package/extensions/dm-image2/src/image-lib.mjs +1275 -8
- package/extensions/dm-subagents/install.mjs +62 -8
- package/extensions/dm-subagents/src/agents/agent-management.js +1190 -36
- package/extensions/dm-subagents/src/agents/agent-memory.js +216 -6
- package/extensions/dm-subagents/src/agents/agent-scope.js +5 -1
- package/extensions/dm-subagents/src/agents/agent-selection.js +20 -1
- package/extensions/dm-subagents/src/agents/agent-serializer.js +120 -5
- package/extensions/dm-subagents/src/agents/agents.js +1139 -11
- package/extensions/dm-subagents/src/agents/chain-serializer.js +299 -11
- package/extensions/dm-subagents/src/agents/frontmatter.js +65 -6
- package/extensions/dm-subagents/src/agents/identity.js +29 -1
- package/extensions/dm-subagents/src/agents/proactive-skills.js +141 -1
- package/extensions/dm-subagents/src/agents/skills.js +614 -6
- package/extensions/dm-subagents/src/extension/config.js +35 -2
- package/extensions/dm-subagents/src/extension/control-notices.js +69 -4
- package/extensions/dm-subagents/src/extension/doctor.js +172 -15
- package/extensions/dm-subagents/src/extension/fanout-child.js +158 -231
- package/extensions/dm-subagents/src/extension/index.js +521 -359
- package/extensions/dm-subagents/src/extension/rpc.js +266 -7
- package/extensions/dm-subagents/src/extension/schemas.js +275 -1
- package/extensions/dm-subagents/src/extension/tool-description.js +111 -6
- package/extensions/dm-subagents/src/intercom/intercom-bridge.js +126 -4
- package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +452 -5
- package/extensions/dm-subagents/src/intercom/result-intercom.js +319 -3
- package/extensions/dm-subagents/src/profiles/profiles.js +458 -3
- package/extensions/dm-subagents/src/runs/background/async-execution.js +834 -40
- package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +435 -14
- package/extensions/dm-subagents/src/runs/background/async-resume.js +334 -8
- package/extensions/dm-subagents/src/runs/background/async-status.js +313 -12
- package/extensions/dm-subagents/src/runs/background/chain-append.js +245 -2
- package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +136 -1
- package/extensions/dm-subagents/src/runs/background/completion-batcher.js +94 -1
- package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +54 -1
- package/extensions/dm-subagents/src/runs/background/control-channel.js +190 -1
- package/extensions/dm-subagents/src/runs/background/fleet-view.js +483 -17
- package/extensions/dm-subagents/src/runs/background/notify.js +129 -3
- package/extensions/dm-subagents/src/runs/background/parallel-groups.js +34 -1
- package/extensions/dm-subagents/src/runs/background/result-watcher.js +236 -6
- package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +76 -4
- package/extensions/dm-subagents/src/runs/background/run-status.js +427 -23
- package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +487 -4
- package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +306 -9
- package/extensions/dm-subagents/src/runs/background/subagent-runner.js +2849 -73
- package/extensions/dm-subagents/src/runs/background/top-level-async.js +5 -1
- package/extensions/dm-subagents/src/runs/background/wait.js +206 -11
- package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +1013 -12
- package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +980 -101
- package/extensions/dm-subagents/src/runs/foreground/execution.js +1165 -45
- package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +3157 -222
- package/extensions/dm-subagents/src/runs/shared/acceptance.js +835 -3
- package/extensions/dm-subagents/src/runs/shared/chain-outputs.js +104 -1
- package/extensions/dm-subagents/src/runs/shared/completion-guard.js +116 -3
- package/extensions/dm-subagents/src/runs/shared/dm-args.js +208 -1
- package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +90 -1
- package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +282 -1
- package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +148 -1
- package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +305 -1
- package/extensions/dm-subagents/src/runs/shared/model-fallback.js +194 -1
- package/extensions/dm-subagents/src/runs/shared/model-scope.js +65 -1
- package/extensions/dm-subagents/src/runs/shared/nested-events.js +851 -8
- package/extensions/dm-subagents/src/runs/shared/nested-path.js +41 -1
- package/extensions/dm-subagents/src/runs/shared/nested-render.js +105 -1
- package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +81 -4
- package/extensions/dm-subagents/src/runs/shared/run-history.js +51 -4
- package/extensions/dm-subagents/src/runs/shared/single-output.js +149 -8
- package/extensions/dm-subagents/src/runs/shared/structured-output.js +58 -1
- package/extensions/dm-subagents/src/runs/shared/subagent-control.js +166 -5
- package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +329 -13
- package/extensions/dm-subagents/src/runs/shared/tool-budget.js +73 -1
- package/extensions/dm-subagents/src/runs/shared/turn-budget.js +47 -4
- package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +196 -1
- package/extensions/dm-subagents/src/runs/shared/worktree.js +435 -3
- package/extensions/dm-subagents/src/shared/artifacts.js +92 -2
- package/extensions/dm-subagents/src/shared/atomic-json.js +55 -1
- package/extensions/dm-subagents/src/shared/child-transcript.js +167 -5
- package/extensions/dm-subagents/src/shared/file-coalescer.js +25 -1
- package/extensions/dm-subagents/src/shared/fork-context.js +147 -4
- package/extensions/dm-subagents/src/shared/formatters.js +98 -7
- package/extensions/dm-subagents/src/shared/jsonl-writer.js +56 -2
- package/extensions/dm-subagents/src/shared/model-info.js +62 -1
- package/extensions/dm-subagents/src/shared/post-exit-stdio-guard.js +68 -1
- package/extensions/dm-subagents/src/shared/session-identity.js +6 -1
- package/extensions/dm-subagents/src/shared/session-tokens.js +39 -2
- package/extensions/dm-subagents/src/shared/settings.js +198 -11
- package/extensions/dm-subagents/src/shared/status-format.js +53 -1
- package/extensions/dm-subagents/src/shared/types.js +184 -6
- package/extensions/dm-subagents/src/shared/utils.js +462 -2
- package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +288 -1
- package/extensions/dm-subagents/src/slash/prompt-workflows.js +297 -7
- package/extensions/dm-subagents/src/slash/slash-bridge.js +118 -1
- package/extensions/dm-subagents/src/slash/slash-commands.js +1287 -31
- package/extensions/dm-subagents/src/slash/slash-live-state.js +240 -4
- package/extensions/dm-subagents/src/tui/render-helpers.js +64 -1
- package/extensions/dm-subagents/src/tui/render.js +1542 -4
- package/extensions/dm-usage/index.js +1294 -9
- package/extensions/greedysearch-dm/bin/cdp-greedy.mjs +40 -9
- package/extensions/greedysearch-dm/bin/cdp-headless.mjs +5 -2
- package/extensions/greedysearch-dm/bin/cdp-visible.mjs +5 -2
- package/extensions/greedysearch-dm/bin/cdp.mjs +896 -30
- package/extensions/greedysearch-dm/bin/gschrome.mjs +30 -2
- package/extensions/greedysearch-dm/bin/kill-visible.mjs +7 -2
- package/extensions/greedysearch-dm/bin/launch-visible.mjs +13 -2
- package/extensions/greedysearch-dm/bin/launch.mjs +282 -10
- package/extensions/greedysearch-dm/bin/mcp.mjs +386 -361
- package/extensions/greedysearch-dm/bin/search.mjs +620 -540
- package/extensions/greedysearch-dm/bin/visible.mjs +22 -2
- package/extensions/greedysearch-dm/extractors/bing-copilot.mjs +329 -579
- package/extensions/greedysearch-dm/extractors/chatgpt.mjs +301 -583
- package/extensions/greedysearch-dm/extractors/common.mjs +408 -32
- package/extensions/greedysearch-dm/extractors/consensus.mjs +376 -365
- package/extensions/greedysearch-dm/extractors/consent.mjs +303 -14
- package/extensions/greedysearch-dm/extractors/gemini.mjs +228 -592
- package/extensions/greedysearch-dm/extractors/google-ai.mjs +78 -499
- package/extensions/greedysearch-dm/extractors/logically.mjs +270 -347
- package/extensions/greedysearch-dm/extractors/perplexity.mjs +243 -581
- package/extensions/greedysearch-dm/extractors/selectors.mjs +32 -1
- package/extensions/greedysearch-dm/extractors/semantic-scholar.mjs +130 -317
- package/extensions/greedysearch-dm/index.js +123 -23
- package/extensions/greedysearch-dm/src/fetcher.mjs +576 -2
- package/extensions/greedysearch-dm/src/formatters/results.js +95 -10
- package/extensions/greedysearch-dm/src/formatters/sources.js +57 -1
- package/extensions/greedysearch-dm/src/formatters/synthesis.js +49 -1
- package/extensions/greedysearch-dm/src/github.mjs +222 -7
- package/extensions/greedysearch-dm/src/reddit.mjs +145 -14
- package/extensions/greedysearch-dm/src/search/browser-lifecycle.mjs +340 -9
- package/extensions/greedysearch-dm/src/search/challenge-detect.mjs +112 -4
- package/extensions/greedysearch-dm/src/search/chrome.mjs +486 -285
- package/extensions/greedysearch-dm/src/search/constants.mjs +109 -8
- package/extensions/greedysearch-dm/src/search/defaults.mjs +10 -1
- package/extensions/greedysearch-dm/src/search/engines.mjs +79 -9
- package/extensions/greedysearch-dm/src/search/fetch-source.mjs +441 -349
- package/extensions/greedysearch-dm/src/search/file-sources.mjs +29 -7
- package/extensions/greedysearch-dm/src/search/minimize.mjs +86 -1
- package/extensions/greedysearch-dm/src/search/output.mjs +51 -5
- package/extensions/greedysearch-dm/src/search/paths.mjs +48 -1
- package/extensions/greedysearch-dm/src/search/pdf.mjs +63 -2
- package/extensions/greedysearch-dm/src/search/port-pid.mjs +69 -1
- package/extensions/greedysearch-dm/src/search/progress.mjs +109 -2
- package/extensions/greedysearch-dm/src/search/query.mjs +21 -1
- package/extensions/greedysearch-dm/src/search/recovery.mjs +49 -1
- package/extensions/greedysearch-dm/src/search/research.mjs +2227 -458
- package/extensions/greedysearch-dm/src/search/scale-aware.mjs +61 -11
- package/extensions/greedysearch-dm/src/search/simple-research.mjs +396 -805
- package/extensions/greedysearch-dm/src/search/sources.mjs +412 -1
- package/extensions/greedysearch-dm/src/search/synthesis-runner.mjs +127 -12
- package/extensions/greedysearch-dm/src/search/synthesis.mjs +202 -12
- package/extensions/greedysearch-dm/src/tools/greedy-search-handler.js +209 -23
- package/extensions/greedysearch-dm/src/tools/shared.js +226 -10
- package/extensions/greedysearch-dm/src/utils/content.mjs +35 -4
- package/extensions/greedysearch-dm/src/utils/helpers.js +22 -1
- package/extensions/greedysearch-dm/src/utils/node-runtime.mjs +10 -1
- package/extensions/greedysearch-dm/src/utils/system-cmds.mjs +61 -1
- package/package.json +1 -1
|
@@ -1,5 +1,452 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { Type } from "typebox";
|
|
5
|
+
import {
|
|
6
|
+
SUBAGENT_CHILD_AGENT_ENV,
|
|
7
|
+
SUBAGENT_CHILD_INDEX_ENV,
|
|
8
|
+
SUBAGENT_ORCHESTRATOR_SESSION_ID_ENV,
|
|
9
|
+
SUBAGENT_ORCHESTRATOR_TARGET_ENV,
|
|
10
|
+
SUBAGENT_RUN_ID_ENV,
|
|
11
|
+
SUBAGENT_SUPERVISOR_CHANNEL_DIR_ENV
|
|
12
|
+
} from "../runs/shared/dm-args.js";
|
|
13
|
+
import { POLL_INTERVAL_MS, TEMP_ROOT_DIR } from "../shared/types.js";
|
|
14
|
+
import { writeAtomicJson } from "../shared/atomic-json.js";
|
|
15
|
+
const SUPERVISOR_CHANNEL_ROOT = path.join(TEMP_ROOT_DIR, "supervisor-channels");
|
|
16
|
+
const REQUESTS_DIR = "requests";
|
|
17
|
+
const REPLIES_DIR = "replies";
|
|
18
|
+
export const NATIVE_SUPERVISOR_TOOL_NAME = "subagent_supervisor";
|
|
19
|
+
const MAX_MESSAGE_BYTES = 64 * 1024;
|
|
20
|
+
const DEFAULT_ASK_TIMEOUT_MS = 10 * 60 * 1000;
|
|
21
|
+
const CHANNEL_POLL_MS = Math.min(POLL_INTERVAL_MS, 500);
|
|
22
|
+
const ContactSupervisorParamsSchema = Type.Object({
|
|
23
|
+
reason: Type.String({ enum: ["need_decision", "interview_request", "progress_update"] }),
|
|
24
|
+
message: Type.Optional(Type.String()),
|
|
25
|
+
interview: Type.Optional(Type.Unsafe({ type: "object", additionalProperties: true }))
|
|
26
|
+
}, { additionalProperties: false });
|
|
27
|
+
const IntercomParamsSchema = Type.Object({
|
|
28
|
+
action: Type.String({ enum: ["list", "send", "ask", "reply", "pending", "status"] }),
|
|
29
|
+
to: Type.Optional(Type.String()),
|
|
30
|
+
message: Type.Optional(Type.String()),
|
|
31
|
+
replyTo: Type.Optional(Type.String())
|
|
32
|
+
}, { additionalProperties: false });
|
|
33
|
+
function safeSegment(value) {
|
|
34
|
+
return value.trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
|
|
35
|
+
}
|
|
36
|
+
export function resolveSupervisorChannelDir(runId, agent, childIndex) {
|
|
37
|
+
return path.join(SUPERVISOR_CHANNEL_ROOT, `${safeSegment(runId)}-${safeSegment(agent)}-${childIndex}`);
|
|
38
|
+
}
|
|
39
|
+
export function ensureSupervisorChannelDir(channelDir) {
|
|
40
|
+
fs.mkdirSync(path.join(channelDir, REQUESTS_DIR), { recursive: true, mode: 448 });
|
|
41
|
+
fs.mkdirSync(path.join(channelDir, REPLIES_DIR), { recursive: true, mode: 448 });
|
|
42
|
+
}
|
|
43
|
+
function requestPath(channelDir, requestId) {
|
|
44
|
+
return path.join(channelDir, REQUESTS_DIR, `${safeSegment(requestId)}.json`);
|
|
45
|
+
}
|
|
46
|
+
function replyPath(channelDir, requestId) {
|
|
47
|
+
return path.join(channelDir, REPLIES_DIR, `${safeSegment(requestId)}.json`);
|
|
48
|
+
}
|
|
49
|
+
function readTextEnv(name) {
|
|
50
|
+
const value = process.env[name]?.trim();
|
|
51
|
+
return value ? value : undefined;
|
|
52
|
+
}
|
|
53
|
+
function readChildMetadata() {
|
|
54
|
+
const channelDir = readTextEnv(SUBAGENT_SUPERVISOR_CHANNEL_DIR_ENV);
|
|
55
|
+
const runId = readTextEnv(SUBAGENT_RUN_ID_ENV);
|
|
56
|
+
const agent = readTextEnv(SUBAGENT_CHILD_AGENT_ENV);
|
|
57
|
+
const rawIndex = readTextEnv(SUBAGENT_CHILD_INDEX_ENV);
|
|
58
|
+
const orchestratorSessionId = readTextEnv(SUBAGENT_ORCHESTRATOR_SESSION_ID_ENV);
|
|
59
|
+
if (!channelDir || !runId || !agent || !orchestratorSessionId || rawIndex === undefined || !/^\d+$/.test(rawIndex))
|
|
60
|
+
return;
|
|
61
|
+
return {
|
|
62
|
+
channelDir,
|
|
63
|
+
runId,
|
|
64
|
+
agent,
|
|
65
|
+
childIndex: Number(rawIndex),
|
|
66
|
+
orchestratorTarget: readTextEnv(SUBAGENT_ORCHESTRATOR_TARGET_ENV),
|
|
67
|
+
orchestratorSessionId,
|
|
68
|
+
childTarget: readTextEnv("DM_SUBAGENT_INTERCOM_SESSION_NAME")
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function reasonHeading(reason) {
|
|
72
|
+
if (reason === "interview_request")
|
|
73
|
+
return "Subagent requests a structured supervisor interview.";
|
|
74
|
+
if (reason === "progress_update")
|
|
75
|
+
return "Subagent progress update.";
|
|
76
|
+
return "Subagent needs a supervisor decision.";
|
|
77
|
+
}
|
|
78
|
+
function formatChildMessage(input) {
|
|
79
|
+
const lines = [
|
|
80
|
+
reasonHeading(input.reason),
|
|
81
|
+
`Run: ${input.runId}`,
|
|
82
|
+
`Agent: ${input.agent}`,
|
|
83
|
+
`Child index: ${input.childIndex}`
|
|
84
|
+
];
|
|
85
|
+
if (input.childTarget)
|
|
86
|
+
lines.push(`Child intercom target: ${input.childTarget}`);
|
|
87
|
+
lines.push("");
|
|
88
|
+
if (input.message?.trim())
|
|
89
|
+
lines.push(input.message.trim());
|
|
90
|
+
if (input.reason === "interview_request") {
|
|
91
|
+
lines.push("", "Structured response requested. Reply with JSON, optionally fenced in ```json, matching the requested interview shape.");
|
|
92
|
+
if (input.interview !== undefined)
|
|
93
|
+
lines.push(JSON.stringify(input.interview, null, "\t"));
|
|
94
|
+
}
|
|
95
|
+
return lines.join(`
|
|
96
|
+
`).trimEnd();
|
|
97
|
+
}
|
|
98
|
+
function parseStructuredReply(message) {
|
|
99
|
+
const trimmed = message.trim();
|
|
100
|
+
const fenced = trimmed.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/i)?.[1]?.trim();
|
|
101
|
+
try {
|
|
102
|
+
return { value: JSON.parse(fenced ?? trimmed) };
|
|
103
|
+
} catch (error) {
|
|
104
|
+
return { error: error instanceof Error ? `${error.name}: ${error.message}` : String(error) };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function askTimeoutMs() {
|
|
108
|
+
const parsed = Number(process.env.PI_INTERCOM_ASK_TIMEOUT_MS);
|
|
109
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_ASK_TIMEOUT_MS;
|
|
110
|
+
}
|
|
111
|
+
function delay(ms, signal) {
|
|
112
|
+
return new Promise((resolve, reject) => {
|
|
113
|
+
if (signal?.aborted) {
|
|
114
|
+
reject(new Error("Supervisor request cancelled."));
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
let timer;
|
|
118
|
+
const cleanup = () => {
|
|
119
|
+
if (timer)
|
|
120
|
+
clearTimeout(timer);
|
|
121
|
+
signal?.removeEventListener("abort", onAbort);
|
|
122
|
+
};
|
|
123
|
+
const onAbort = () => {
|
|
124
|
+
cleanup();
|
|
125
|
+
reject(new Error("Supervisor request cancelled."));
|
|
126
|
+
};
|
|
127
|
+
timer = setTimeout(() => {
|
|
128
|
+
cleanup();
|
|
129
|
+
resolve();
|
|
130
|
+
}, ms);
|
|
131
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
async function waitForReply(channelDir, requestId, signal) {
|
|
135
|
+
const file = replyPath(channelDir, requestId);
|
|
136
|
+
const deadline = Date.now() + askTimeoutMs();
|
|
137
|
+
while (Date.now() <= deadline) {
|
|
138
|
+
if (signal?.aborted)
|
|
139
|
+
throw new Error("Supervisor request cancelled.");
|
|
140
|
+
if (fs.existsSync(file)) {
|
|
141
|
+
const parsed = JSON.parse(fs.readFileSync(file, "utf-8"));
|
|
142
|
+
if (parsed.type === "subagent.supervisor.reply" && parsed.requestId === requestId && typeof parsed.message === "string") {
|
|
143
|
+
return parsed;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
await delay(250, signal);
|
|
147
|
+
}
|
|
148
|
+
throw new Error("Timed out waiting for supervisor reply.");
|
|
149
|
+
}
|
|
150
|
+
async function sendSupervisorRequest(params, signal) {
|
|
151
|
+
const metadata = readChildMetadata();
|
|
152
|
+
if (!metadata)
|
|
153
|
+
throw new Error("Native supervisor channel is not available for this subagent.");
|
|
154
|
+
if (params.reason !== "progress_update" && !params.message?.trim() && params.reason !== "interview_request") {
|
|
155
|
+
throw new Error("message is required for supervisor decisions.");
|
|
156
|
+
}
|
|
157
|
+
ensureSupervisorChannelDir(metadata.channelDir);
|
|
158
|
+
const requestId = randomUUID();
|
|
159
|
+
const expectsReply = params.reason !== "progress_update";
|
|
160
|
+
const message = formatChildMessage({ ...metadata, reason: params.reason, message: params.message, interview: params.interview });
|
|
161
|
+
const request = {
|
|
162
|
+
type: "subagent.supervisor.request",
|
|
163
|
+
id: requestId,
|
|
164
|
+
createdAt: Date.now(),
|
|
165
|
+
reason: params.reason,
|
|
166
|
+
message,
|
|
167
|
+
expectsReply,
|
|
168
|
+
...metadata.orchestratorTarget ? { orchestratorTarget: metadata.orchestratorTarget } : {},
|
|
169
|
+
...metadata.orchestratorSessionId ? { orchestratorSessionId: metadata.orchestratorSessionId } : {},
|
|
170
|
+
runId: metadata.runId,
|
|
171
|
+
agent: metadata.agent,
|
|
172
|
+
childIndex: metadata.childIndex,
|
|
173
|
+
...metadata.childTarget ? { childTarget: metadata.childTarget } : {},
|
|
174
|
+
...params.interview !== undefined ? { interview: params.interview } : {}
|
|
175
|
+
};
|
|
176
|
+
const serialized = JSON.stringify(request, null, "\t");
|
|
177
|
+
if (Buffer.byteLength(serialized, "utf-8") > MAX_MESSAGE_BYTES)
|
|
178
|
+
throw new Error("Supervisor request is too large.");
|
|
179
|
+
writeAtomicJson(requestPath(metadata.channelDir, requestId), request);
|
|
180
|
+
if (!expectsReply) {
|
|
181
|
+
return {
|
|
182
|
+
content: [{ type: "text", text: "Supervisor progress update queued." }],
|
|
183
|
+
details: { delivered: true, requestId, reason: params.reason }
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
const reply = await waitForReply(metadata.channelDir, requestId, signal);
|
|
187
|
+
const details = { requestId, reason: params.reason };
|
|
188
|
+
if (params.reason === "interview_request") {
|
|
189
|
+
const structured = parseStructuredReply(reply.message);
|
|
190
|
+
if (structured.error)
|
|
191
|
+
details.structuredReplyParseError = structured.error;
|
|
192
|
+
else
|
|
193
|
+
details.structuredReply = structured.value;
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
content: [{ type: "text", text: `**Reply from supervisor:**
|
|
197
|
+
${reply.message}` }],
|
|
198
|
+
details
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function hasTool(pi, name) {
|
|
202
|
+
try {
|
|
203
|
+
return pi.getAllTools?.().some((tool) => tool.name === name) === true;
|
|
204
|
+
} catch {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
export function registerNativeSupervisorClient(pi, options = {}) {
|
|
209
|
+
if (!readChildMetadata())
|
|
210
|
+
return;
|
|
211
|
+
const includeIntercomFallback = options.includeIntercomFallback !== false;
|
|
212
|
+
if (!hasTool(pi, "contact_supervisor")) {
|
|
213
|
+
const tool = {
|
|
214
|
+
name: "contact_supervisor",
|
|
215
|
+
label: "Contact Supervisor",
|
|
216
|
+
description: "Contact the parent/supervisor session for a blocking decision, structured interview, or progress update.",
|
|
217
|
+
parameters: ContactSupervisorParamsSchema,
|
|
218
|
+
execute(_id, params, signal) {
|
|
219
|
+
return sendSupervisorRequest(params, signal);
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
pi.registerTool(tool);
|
|
223
|
+
}
|
|
224
|
+
if (includeIntercomFallback && !hasTool(pi, "intercom")) {
|
|
225
|
+
const tool = {
|
|
226
|
+
name: "intercom",
|
|
227
|
+
label: "Intercom",
|
|
228
|
+
description: "Native supervisor-channel intercom fallback for subagents. Prefer contact_supervisor when available.",
|
|
229
|
+
parameters: IntercomParamsSchema,
|
|
230
|
+
async execute(_id, params, signal) {
|
|
231
|
+
const action = params.action;
|
|
232
|
+
if (action === "status")
|
|
233
|
+
return { content: [{ type: "text", text: "Native supervisor channel is active." }], details: { active: true } };
|
|
234
|
+
if (action === "list")
|
|
235
|
+
return { content: [{ type: "text", text: "Supervisor session available through contact_supervisor." }], details: { sessions: [] } };
|
|
236
|
+
if (action === "send")
|
|
237
|
+
return sendSupervisorRequest({ reason: "progress_update", message: params.message ?? "" }, signal);
|
|
238
|
+
if (action === "ask")
|
|
239
|
+
return sendSupervisorRequest({ reason: "need_decision", message: params.message ?? "" }, signal);
|
|
240
|
+
throw new Error("Native child intercom supports status, list, send, and ask. Use parent intercom reply from the supervisor session.");
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
pi.registerTool(tool);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function parseRequestFile(file, channelDir) {
|
|
247
|
+
try {
|
|
248
|
+
const parsed = JSON.parse(fs.readFileSync(file, "utf-8"));
|
|
249
|
+
if (parsed.type !== "subagent.supervisor.request")
|
|
250
|
+
return;
|
|
251
|
+
if (typeof parsed.id !== "string" || !parsed.id)
|
|
252
|
+
return;
|
|
253
|
+
if (parsed.reason !== "need_decision" && parsed.reason !== "interview_request" && parsed.reason !== "progress_update")
|
|
254
|
+
return;
|
|
255
|
+
if (typeof parsed.message !== "string" || !parsed.message)
|
|
256
|
+
return;
|
|
257
|
+
if (typeof parsed.runId !== "string" || typeof parsed.agent !== "string" || typeof parsed.childIndex !== "number")
|
|
258
|
+
return;
|
|
259
|
+
return { ...parsed, channelDir, requestFile: file };
|
|
260
|
+
} catch {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
function listRequestFiles() {
|
|
265
|
+
let channelEntries;
|
|
266
|
+
try {
|
|
267
|
+
channelEntries = fs.readdirSync(SUPERVISOR_CHANNEL_ROOT, { withFileTypes: true });
|
|
268
|
+
} catch (error) {
|
|
269
|
+
if (error.code === "ENOENT")
|
|
270
|
+
return [];
|
|
271
|
+
throw error;
|
|
272
|
+
}
|
|
273
|
+
const files = [];
|
|
274
|
+
for (const entry of channelEntries) {
|
|
275
|
+
if (!entry.isDirectory())
|
|
276
|
+
continue;
|
|
277
|
+
const channelDir = path.join(SUPERVISOR_CHANNEL_ROOT, entry.name);
|
|
278
|
+
const requestsDir = path.join(channelDir, REQUESTS_DIR);
|
|
279
|
+
let requestEntries;
|
|
280
|
+
try {
|
|
281
|
+
requestEntries = fs.readdirSync(requestsDir, { withFileTypes: true });
|
|
282
|
+
} catch {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
for (const requestEntry of requestEntries) {
|
|
286
|
+
if (requestEntry.isFile() && requestEntry.name.endsWith(".json"))
|
|
287
|
+
files.push({ channelDir, file: path.join(requestsDir, requestEntry.name) });
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return files;
|
|
291
|
+
}
|
|
292
|
+
function currentContextSessionId(state, ctx) {
|
|
293
|
+
try {
|
|
294
|
+
const sessionId = ctx.sessionManager.getSessionId();
|
|
295
|
+
if (sessionId)
|
|
296
|
+
return sessionId;
|
|
297
|
+
} catch {}
|
|
298
|
+
return state.currentSessionId ?? undefined;
|
|
299
|
+
}
|
|
300
|
+
function requestMatchesContext(request, state, ctx) {
|
|
301
|
+
const currentSessionId = currentContextSessionId(state, ctx);
|
|
302
|
+
return Boolean(currentSessionId && request.orchestratorSessionId === currentSessionId);
|
|
303
|
+
}
|
|
304
|
+
function formatPendingLine(request) {
|
|
305
|
+
const replyHint = request.expectsReply ? ` Reply: ${NATIVE_SUPERVISOR_TOOL_NAME}({ action: "reply", replyTo: "${request.id}", message: "..." })` : "";
|
|
306
|
+
return `- ${request.id}: ${request.agent} [${request.runId}#${request.childIndex}] ${request.reason}.${replyHint}`;
|
|
307
|
+
}
|
|
308
|
+
function requestVisibleText(request) {
|
|
309
|
+
const lines = [request.message];
|
|
310
|
+
if (request.expectsReply) {
|
|
311
|
+
lines.push("", `Reply with: ${NATIVE_SUPERVISOR_TOOL_NAME}({ action: "reply", replyTo: "${request.id}", message: "..." })`);
|
|
312
|
+
}
|
|
313
|
+
return lines.join(`
|
|
314
|
+
`);
|
|
315
|
+
}
|
|
316
|
+
function writeReply(request, message) {
|
|
317
|
+
if (!message.trim())
|
|
318
|
+
throw new Error("message is required for supervisor replies.");
|
|
319
|
+
const reply = {
|
|
320
|
+
type: "subagent.supervisor.reply",
|
|
321
|
+
requestId: request.id,
|
|
322
|
+
createdAt: Date.now(),
|
|
323
|
+
message: message.trim()
|
|
324
|
+
};
|
|
325
|
+
writeAtomicJson(replyPath(request.channelDir, request.id), reply);
|
|
326
|
+
try {
|
|
327
|
+
fs.rmSync(request.requestFile, { force: true });
|
|
328
|
+
} catch {}
|
|
329
|
+
}
|
|
330
|
+
function resolvePendingRequest(pending, params) {
|
|
331
|
+
if (params.replyTo) {
|
|
332
|
+
const request = pending.get(params.replyTo);
|
|
333
|
+
if (!request)
|
|
334
|
+
throw new Error(`No pending supervisor request found for replyTo '${params.replyTo}'.`);
|
|
335
|
+
return request;
|
|
336
|
+
}
|
|
337
|
+
const requests = [...pending.values()].filter((request) => request.expectsReply);
|
|
338
|
+
if (params.to) {
|
|
339
|
+
const normalizedTo = params.to.toLowerCase();
|
|
340
|
+
const matches = requests.filter((request) => request.id.toLowerCase().startsWith(normalizedTo) || request.agent.toLowerCase() === normalizedTo || request.childTarget?.toLowerCase() === normalizedTo);
|
|
341
|
+
if (matches.length === 1)
|
|
342
|
+
return matches[0];
|
|
343
|
+
if (matches.length > 1)
|
|
344
|
+
throw new Error(`Multiple pending supervisor requests match '${params.to}'. Use replyTo.`);
|
|
345
|
+
}
|
|
346
|
+
if (requests.length === 1)
|
|
347
|
+
return requests[0];
|
|
348
|
+
if (requests.length === 0)
|
|
349
|
+
throw new Error("No pending supervisor requests need a reply.");
|
|
350
|
+
throw new Error("Multiple pending supervisor requests need replies. Use replyTo.");
|
|
351
|
+
}
|
|
352
|
+
function publicPendingRequests(pending) {
|
|
353
|
+
return [...pending.values()].map((request) => ({
|
|
354
|
+
id: request.id,
|
|
355
|
+
runId: request.runId,
|
|
356
|
+
agent: request.agent,
|
|
357
|
+
childIndex: request.childIndex,
|
|
358
|
+
reason: request.reason,
|
|
359
|
+
expectsReply: request.expectsReply
|
|
360
|
+
}));
|
|
361
|
+
}
|
|
362
|
+
function buildParentIntercomTool(pending, name = "intercom") {
|
|
363
|
+
return {
|
|
364
|
+
name,
|
|
365
|
+
label: name === "intercom" ? "Intercom" : "Subagent Supervisor",
|
|
366
|
+
description: name === "intercom" ? "Native dm-subagents supervisor channel. Use reply/pending/status to answer child subagent requests." : "Native dm-subagents supervisor channel. Use reply/pending/status to answer child subagent requests without overriding the optional intercom compatibility extension.",
|
|
367
|
+
parameters: IntercomParamsSchema,
|
|
368
|
+
async execute(_id, params) {
|
|
369
|
+
const input = params;
|
|
370
|
+
if (input.action === "status") {
|
|
371
|
+
return { content: [{ type: "text", text: `Native supervisor channel active. Pending replies: ${pending.size}.` }], details: { active: true, pending: pending.size, root: SUPERVISOR_CHANNEL_ROOT } };
|
|
372
|
+
}
|
|
373
|
+
if (input.action === "pending" || input.action === "list") {
|
|
374
|
+
const lines = [...pending.values()].filter((request) => request.expectsReply).map(formatPendingLine);
|
|
375
|
+
return { content: [{ type: "text", text: lines.length ? lines.join(`
|
|
376
|
+
`) : "No pending supervisor requests." }], details: { pending: publicPendingRequests(pending) } };
|
|
377
|
+
}
|
|
378
|
+
if (input.action === "reply") {
|
|
379
|
+
const request = resolvePendingRequest(pending, input);
|
|
380
|
+
writeReply(request, input.message ?? "");
|
|
381
|
+
pending.delete(request.id);
|
|
382
|
+
return { content: [{ type: "text", text: `Replied to supervisor request ${request.id}.` }], details: { replyTo: request.id, runId: request.runId, agent: request.agent } };
|
|
383
|
+
}
|
|
384
|
+
if (input.action === "send" || input.action === "ask") {
|
|
385
|
+
throw new Error("Native dm-subagents intercom currently handles supervisor replies. Child agents initiate asks with contact_supervisor.");
|
|
386
|
+
}
|
|
387
|
+
throw new Error(`Unsupported intercom action: ${input.action}`);
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
export function createNativeSupervisorChannel(pi, state) {
|
|
392
|
+
const pending = new Map;
|
|
393
|
+
const seenFiles = new Set;
|
|
394
|
+
let poller;
|
|
395
|
+
const registerParentTools = () => {
|
|
396
|
+
if (!hasTool(pi, NATIVE_SUPERVISOR_TOOL_NAME))
|
|
397
|
+
pi.registerTool(buildParentIntercomTool(pending, NATIVE_SUPERVISOR_TOOL_NAME));
|
|
398
|
+
if (!hasTool(pi, "intercom"))
|
|
399
|
+
pi.registerTool(buildParentIntercomTool(pending));
|
|
400
|
+
};
|
|
401
|
+
const poll = () => {
|
|
402
|
+
const ctx = state.lastUiContext;
|
|
403
|
+
if (!ctx)
|
|
404
|
+
return;
|
|
405
|
+
for (const { channelDir, file } of listRequestFiles()) {
|
|
406
|
+
if (seenFiles.has(file))
|
|
407
|
+
continue;
|
|
408
|
+
const request = parseRequestFile(file, channelDir);
|
|
409
|
+
if (!request || !requestMatchesContext(request, state, ctx))
|
|
410
|
+
continue;
|
|
411
|
+
seenFiles.add(file);
|
|
412
|
+
if (request.expectsReply)
|
|
413
|
+
pending.set(request.id, request);
|
|
414
|
+
else {
|
|
415
|
+
try {
|
|
416
|
+
fs.rmSync(request.requestFile, { force: true });
|
|
417
|
+
} catch {}
|
|
418
|
+
}
|
|
419
|
+
pi.sendMessage({
|
|
420
|
+
customType: "subagent_supervisor_request",
|
|
421
|
+
content: requestVisibleText(request),
|
|
422
|
+
display: true,
|
|
423
|
+
details: {
|
|
424
|
+
id: request.id,
|
|
425
|
+
reason: request.reason,
|
|
426
|
+
expectsReply: request.expectsReply,
|
|
427
|
+
runId: request.runId,
|
|
428
|
+
agent: request.agent,
|
|
429
|
+
childIndex: request.childIndex
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
return {
|
|
435
|
+
start: () => {
|
|
436
|
+
if (poller)
|
|
437
|
+
return;
|
|
438
|
+
registerParentTools();
|
|
439
|
+
poll();
|
|
440
|
+
poller = setInterval(poll, CHANNEL_POLL_MS);
|
|
441
|
+
poller.unref?.();
|
|
442
|
+
},
|
|
443
|
+
dispose: () => {
|
|
444
|
+
if (poller)
|
|
445
|
+
clearInterval(poller);
|
|
446
|
+
poller = undefined;
|
|
447
|
+
pending.clear();
|
|
448
|
+
seenFiles.clear();
|
|
449
|
+
},
|
|
450
|
+
pending
|
|
451
|
+
};
|
|
452
|
+
}
|