@duckmind/dm-windows-x64 0.60.4 → 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 +1,68 @@
|
|
|
1
|
-
|
|
1
|
+
export function trySignalChild(child, signal) {
|
|
2
|
+
try {
|
|
3
|
+
return child.kill(signal);
|
|
4
|
+
} catch {
|
|
5
|
+
return false;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export function attachPostExitStdioGuard(child, options) {
|
|
9
|
+
const { idleMs, hardMs } = options;
|
|
10
|
+
let exited = false;
|
|
11
|
+
let stdoutEnded = false;
|
|
12
|
+
let stderrEnded = false;
|
|
13
|
+
let idleTimer;
|
|
14
|
+
let hardTimer;
|
|
15
|
+
const destroyUnendedStdio = () => {
|
|
16
|
+
if (!stdoutEnded) {
|
|
17
|
+
try {
|
|
18
|
+
child.stdout?.destroy();
|
|
19
|
+
} catch {}
|
|
20
|
+
}
|
|
21
|
+
if (!stderrEnded) {
|
|
22
|
+
try {
|
|
23
|
+
child.stderr?.destroy();
|
|
24
|
+
} catch {}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const clearTimers = () => {
|
|
28
|
+
if (idleTimer) {
|
|
29
|
+
clearTimeout(idleTimer);
|
|
30
|
+
idleTimer = undefined;
|
|
31
|
+
}
|
|
32
|
+
if (hardTimer) {
|
|
33
|
+
clearTimeout(hardTimer);
|
|
34
|
+
hardTimer = undefined;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const armIdleTimer = () => {
|
|
38
|
+
if (!exited)
|
|
39
|
+
return;
|
|
40
|
+
if (idleTimer)
|
|
41
|
+
clearTimeout(idleTimer);
|
|
42
|
+
idleTimer = setTimeout(destroyUnendedStdio, idleMs);
|
|
43
|
+
idleTimer.unref?.();
|
|
44
|
+
};
|
|
45
|
+
child.stdout?.on("data", armIdleTimer);
|
|
46
|
+
child.stderr?.on("data", armIdleTimer);
|
|
47
|
+
child.stdout?.on("end", () => {
|
|
48
|
+
stdoutEnded = true;
|
|
49
|
+
if (stdoutEnded && stderrEnded)
|
|
50
|
+
clearTimers();
|
|
51
|
+
});
|
|
52
|
+
child.stderr?.on("end", () => {
|
|
53
|
+
stderrEnded = true;
|
|
54
|
+
if (stdoutEnded && stderrEnded)
|
|
55
|
+
clearTimers();
|
|
56
|
+
});
|
|
57
|
+
child.on("exit", () => {
|
|
58
|
+
exited = true;
|
|
59
|
+
armIdleTimer();
|
|
60
|
+
if (hardTimer)
|
|
61
|
+
return;
|
|
62
|
+
hardTimer = setTimeout(destroyUnendedStdio, hardMs);
|
|
63
|
+
hardTimer.unref?.();
|
|
64
|
+
});
|
|
65
|
+
child.on("close", clearTimers);
|
|
66
|
+
child.on("error", clearTimers);
|
|
67
|
+
return clearTimers;
|
|
68
|
+
}
|
|
@@ -1 +1,6 @@
|
|
|
1
|
-
function
|
|
1
|
+
export function resolveCurrentSessionId(sessionManager) {
|
|
2
|
+
const sessionId = sessionManager.getSessionFile() ?? sessionManager.getSessionId();
|
|
3
|
+
if (!sessionId)
|
|
4
|
+
throw new Error("Current session identity is unavailable.");
|
|
5
|
+
return sessionId;
|
|
6
|
+
}
|
|
@@ -1,2 +1,39 @@
|
|
|
1
|
-
import*as
|
|
2
|
-
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
function findLatestSessionFile(sessionDir) {
|
|
4
|
+
try {
|
|
5
|
+
const files = fs.readdirSync(sessionDir).filter((f) => f.endsWith(".jsonl")).map((f) => path.join(sessionDir, f));
|
|
6
|
+
if (files.length === 0)
|
|
7
|
+
return null;
|
|
8
|
+
files.sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs);
|
|
9
|
+
return files[0] ?? null;
|
|
10
|
+
} catch {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function parseSessionTokens(sessionDir) {
|
|
15
|
+
const sessionFile = findLatestSessionFile(sessionDir);
|
|
16
|
+
if (!sessionFile)
|
|
17
|
+
return null;
|
|
18
|
+
try {
|
|
19
|
+
const content = fs.readFileSync(sessionFile, "utf-8");
|
|
20
|
+
let input = 0;
|
|
21
|
+
let output = 0;
|
|
22
|
+
for (const line of content.split(`
|
|
23
|
+
`)) {
|
|
24
|
+
if (!line.trim())
|
|
25
|
+
continue;
|
|
26
|
+
try {
|
|
27
|
+
const entry = JSON.parse(line);
|
|
28
|
+
const usage = entry.usage ?? entry.message?.usage;
|
|
29
|
+
if (usage) {
|
|
30
|
+
input += usage.inputTokens ?? usage.input ?? 0;
|
|
31
|
+
output += usage.outputTokens ?? usage.output ?? 0;
|
|
32
|
+
}
|
|
33
|
+
} catch {}
|
|
34
|
+
}
|
|
35
|
+
return { input, output, total: input + output };
|
|
36
|
+
} catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import*as
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { normalizeSkillInput } from "../agents/skills.js";
|
|
4
|
+
import { CHAIN_RUNS_DIR } from "./types.js";
|
|
5
|
+
const CHAIN_DIR_MAX_AGE_MS = 24 * 60 * 60 * 1000;
|
|
6
|
+
const INITIAL_PROGRESS_CONTENT = `# Progress
|
|
6
7
|
|
|
7
8
|
## Status
|
|
8
9
|
In Progress
|
|
@@ -12,12 +13,198 @@ In Progress
|
|
|
12
13
|
## Files Changed
|
|
13
14
|
|
|
14
15
|
## Notes
|
|
15
|
-
`;
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
`;
|
|
17
|
+
function normalizeOutputOverride(output) {
|
|
18
|
+
return output === "false" ? false : output;
|
|
19
|
+
}
|
|
20
|
+
export function isParallelStep(step) {
|
|
21
|
+
return "parallel" in step && Array.isArray(step.parallel);
|
|
22
|
+
}
|
|
23
|
+
export function isDynamicParallelStep(step) {
|
|
24
|
+
return "expand" in step && "collect" in step && "parallel" in step && !Array.isArray(step.parallel);
|
|
25
|
+
}
|
|
26
|
+
export function getStepAgents(step) {
|
|
27
|
+
if (isParallelStep(step)) {
|
|
28
|
+
return step.parallel.map((t) => t.agent);
|
|
29
|
+
}
|
|
30
|
+
if (isDynamicParallelStep(step)) {
|
|
31
|
+
return [step.parallel.agent];
|
|
32
|
+
}
|
|
33
|
+
return [step.agent];
|
|
34
|
+
}
|
|
35
|
+
export function createChainDir(runId, baseDir) {
|
|
36
|
+
const chainDir = path.join(baseDir ? path.resolve(baseDir) : CHAIN_RUNS_DIR, runId);
|
|
37
|
+
fs.mkdirSync(chainDir, { recursive: true });
|
|
38
|
+
return chainDir;
|
|
39
|
+
}
|
|
40
|
+
export function removeChainDir(chainDir) {
|
|
41
|
+
try {
|
|
42
|
+
fs.rmSync(chainDir, { recursive: true });
|
|
43
|
+
} catch {}
|
|
44
|
+
}
|
|
45
|
+
export function cleanupOldChainDirs() {
|
|
46
|
+
if (!fs.existsSync(CHAIN_RUNS_DIR))
|
|
47
|
+
return;
|
|
48
|
+
const now = Date.now();
|
|
49
|
+
let dirs;
|
|
50
|
+
try {
|
|
51
|
+
dirs = fs.readdirSync(CHAIN_RUNS_DIR);
|
|
52
|
+
} catch {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
for (const dir of dirs) {
|
|
56
|
+
try {
|
|
57
|
+
const dirPath = path.join(CHAIN_RUNS_DIR, dir);
|
|
58
|
+
const stat = fs.statSync(dirPath);
|
|
59
|
+
if (stat.isDirectory() && now - stat.mtimeMs > CHAIN_DIR_MAX_AGE_MS) {
|
|
60
|
+
fs.rmSync(dirPath, { recursive: true });
|
|
61
|
+
}
|
|
62
|
+
} catch {}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export function resolveChainTemplates(steps) {
|
|
66
|
+
return steps.map((step, i) => {
|
|
67
|
+
if (isParallelStep(step)) {
|
|
68
|
+
return step.parallel.map((task) => {
|
|
69
|
+
if (task.task)
|
|
70
|
+
return task.task;
|
|
71
|
+
return "{previous}";
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
if (isDynamicParallelStep(step)) {
|
|
75
|
+
return step.parallel.task ?? "{previous}";
|
|
76
|
+
}
|
|
77
|
+
const seq = step;
|
|
78
|
+
if (seq.task)
|
|
79
|
+
return seq.task;
|
|
80
|
+
return i === 0 ? "{task}" : "{previous}";
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
export function resolveStepBehavior(agentConfig, stepOverrides, chainSkills) {
|
|
84
|
+
const stepOutput = normalizeOutputOverride(stepOverrides.output);
|
|
85
|
+
const output = stepOutput !== undefined ? stepOutput : normalizeOutputOverride(agentConfig.output) ?? false;
|
|
86
|
+
const reads = stepOverrides.reads !== undefined ? stepOverrides.reads : agentConfig.defaultReads ?? false;
|
|
87
|
+
const progress = stepOverrides.progress !== undefined ? stepOverrides.progress : agentConfig.defaultProgress ?? false;
|
|
88
|
+
let skills;
|
|
89
|
+
if (stepOverrides.skills === false) {
|
|
90
|
+
skills = false;
|
|
91
|
+
} else if (stepOverrides.skills !== undefined) {
|
|
92
|
+
skills = [...stepOverrides.skills];
|
|
93
|
+
if (chainSkills && chainSkills.length > 0) {
|
|
94
|
+
skills = [...new Set([...skills, ...chainSkills])];
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
skills = agentConfig.skills ? [...agentConfig.skills] : [];
|
|
98
|
+
if (chainSkills && chainSkills.length > 0) {
|
|
99
|
+
skills = [...new Set([...skills, ...chainSkills])];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const outputMode = stepOverrides.outputMode ?? "inline";
|
|
103
|
+
const model = stepOverrides.model ?? agentConfig.model;
|
|
104
|
+
return { output, outputMode, reads, progress, skills, model };
|
|
105
|
+
}
|
|
106
|
+
export function resolveTaskTextForFileUpdatePolicy(task, originalTask) {
|
|
107
|
+
if (!task)
|
|
108
|
+
return originalTask;
|
|
109
|
+
return originalTask ? task.replaceAll("{task}", originalTask) : task;
|
|
110
|
+
}
|
|
111
|
+
export function taskDisallowsFileUpdates(task) {
|
|
112
|
+
if (!task)
|
|
113
|
+
return false;
|
|
114
|
+
return /\breview[- ]only\b/i.test(task) || /\bread[- ]only\s+(?:review|audit|inspection|pass)\b/i.test(task) || /\b(?:no|without)\s+(?:file\s+)?edits?\b/i.test(task) || /\b(?:do not|don't|must not)\s+(?:edit|modify|write|touch)\b/i.test(task) || /\bleave\s+files?\s+unchanged\b/i.test(task);
|
|
115
|
+
}
|
|
116
|
+
export function suppressProgressForReadOnlyTask(behavior, task, originalTask) {
|
|
117
|
+
const policyTask = resolveTaskTextForFileUpdatePolicy(task, originalTask);
|
|
118
|
+
return behavior.progress && taskDisallowsFileUpdates(policyTask) ? { ...behavior, progress: false } : behavior;
|
|
119
|
+
}
|
|
120
|
+
function resolveChainPath(filePath, chainDir) {
|
|
121
|
+
return path.isAbsolute(filePath) ? filePath : path.join(chainDir, filePath);
|
|
122
|
+
}
|
|
123
|
+
export function writeInitialProgressFile(progressDir) {
|
|
124
|
+
fs.mkdirSync(progressDir, { recursive: true });
|
|
125
|
+
fs.writeFileSync(path.join(progressDir, "progress.md"), INITIAL_PROGRESS_CONTENT);
|
|
126
|
+
}
|
|
127
|
+
export function buildChainInstructions(behavior, chainDir, isFirstProgressAgent, previousSummary) {
|
|
128
|
+
const prefixParts = [];
|
|
129
|
+
const suffixParts = [];
|
|
130
|
+
if (behavior.reads && behavior.reads.length > 0) {
|
|
131
|
+
const files = behavior.reads.map((f) => resolveChainPath(f, chainDir));
|
|
132
|
+
prefixParts.push(`[Read from: ${files.join(", ")}]`);
|
|
133
|
+
}
|
|
134
|
+
if (behavior.output) {
|
|
135
|
+
const outputPath = resolveChainPath(behavior.output, chainDir);
|
|
136
|
+
prefixParts.push(`[Write to: ${outputPath}]`);
|
|
137
|
+
}
|
|
138
|
+
if (behavior.progress) {
|
|
139
|
+
const progressPath = path.join(chainDir, "progress.md");
|
|
140
|
+
if (isFirstProgressAgent) {
|
|
141
|
+
suffixParts.push(`Create and maintain progress at: ${progressPath}`);
|
|
142
|
+
} else {
|
|
143
|
+
suffixParts.push(`Update progress at: ${progressPath}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (previousSummary && previousSummary.trim()) {
|
|
147
|
+
suffixParts.push(`Previous step output:
|
|
148
|
+
${previousSummary.trim()}`);
|
|
149
|
+
}
|
|
150
|
+
const prefix = prefixParts.length > 0 ? prefixParts.join(`
|
|
151
|
+
`) + `
|
|
18
152
|
|
|
19
|
-
|
|
153
|
+
` : "";
|
|
154
|
+
const suffix = suffixParts.length > 0 ? `
|
|
20
155
|
|
|
21
156
|
---
|
|
22
|
-
|
|
23
|
-
`)
|
|
157
|
+
` + suffixParts.join(`
|
|
158
|
+
`) : "";
|
|
159
|
+
return { prefix, suffix };
|
|
160
|
+
}
|
|
161
|
+
export function resolveParallelBehaviors(tasks, agentConfigs, stepIndex, chainSkills) {
|
|
162
|
+
return tasks.map((task, taskIndex) => {
|
|
163
|
+
const config = agentConfigs.find((a) => a.name === task.agent);
|
|
164
|
+
if (!config) {
|
|
165
|
+
throw new Error(`Unknown agent: ${task.agent}`);
|
|
166
|
+
}
|
|
167
|
+
const subdir = path.join(`parallel-${stepIndex}`, `${taskIndex}-${task.agent}`);
|
|
168
|
+
let output = false;
|
|
169
|
+
const taskOutput = normalizeOutputOverride(task.output);
|
|
170
|
+
const configOutput = normalizeOutputOverride(config.output);
|
|
171
|
+
if (taskOutput !== undefined) {
|
|
172
|
+
if (taskOutput === false) {
|
|
173
|
+
output = false;
|
|
174
|
+
} else if (path.isAbsolute(taskOutput)) {
|
|
175
|
+
output = taskOutput;
|
|
176
|
+
} else {
|
|
177
|
+
output = path.join(subdir, taskOutput);
|
|
178
|
+
}
|
|
179
|
+
} else if (configOutput) {
|
|
180
|
+
output = path.join(subdir, configOutput);
|
|
181
|
+
}
|
|
182
|
+
const reads = task.reads !== undefined ? task.reads : config.defaultReads ?? false;
|
|
183
|
+
const progress = task.progress !== undefined ? task.progress : config.defaultProgress ?? false;
|
|
184
|
+
const taskSkillInput = normalizeSkillInput(task.skill);
|
|
185
|
+
let skills;
|
|
186
|
+
if (taskSkillInput === false) {
|
|
187
|
+
skills = false;
|
|
188
|
+
} else if (taskSkillInput !== undefined) {
|
|
189
|
+
skills = [...taskSkillInput];
|
|
190
|
+
if (chainSkills && chainSkills.length > 0) {
|
|
191
|
+
skills = [...new Set([...skills, ...chainSkills])];
|
|
192
|
+
}
|
|
193
|
+
} else {
|
|
194
|
+
skills = config.skills ? [...config.skills] : [];
|
|
195
|
+
if (chainSkills && chainSkills.length > 0) {
|
|
196
|
+
skills = [...new Set([...skills, ...chainSkills])];
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const outputMode = task.outputMode ?? "inline";
|
|
200
|
+
const model = task.model ?? config.model;
|
|
201
|
+
return { output, outputMode, reads, progress, skills, model };
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
export function createParallelDirs(chainDir, stepIndex, taskCount, agentNames) {
|
|
205
|
+
for (let i = 0;i < taskCount; i++) {
|
|
206
|
+
const subdir = path.join(chainDir, `parallel-${stepIndex}`, `${i}-${agentNames[i]}`);
|
|
207
|
+
fs.mkdirSync(subdir, { recursive: true });
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
export { aggregateParallelOutputs } from "../runs/shared/parallel-utils.js";
|
|
@@ -1 +1,53 @@
|
|
|
1
|
-
function
|
|
1
|
+
function formatActivityAge(ms) {
|
|
2
|
+
if (ms < 1000)
|
|
3
|
+
return "now";
|
|
4
|
+
if (ms < 60000)
|
|
5
|
+
return `${Math.floor(ms / 1000)}s`;
|
|
6
|
+
return `${Math.floor(ms / 60000)}m`;
|
|
7
|
+
}
|
|
8
|
+
export function formatActivityLabel(lastActivityAt, activityState, now = Date.now()) {
|
|
9
|
+
if (lastActivityAt === undefined) {
|
|
10
|
+
if (activityState === "needs_attention")
|
|
11
|
+
return "needs attention";
|
|
12
|
+
if (activityState === "active_long_running")
|
|
13
|
+
return "active but long-running";
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const age = formatActivityAge(Math.max(0, now - lastActivityAt));
|
|
17
|
+
if (activityState === "needs_attention")
|
|
18
|
+
return `no activity for ${age}`;
|
|
19
|
+
if (activityState === "active_long_running")
|
|
20
|
+
return `active but long-running · last activity ${age} ago`;
|
|
21
|
+
return age === "now" ? "active now" : `active ${age} ago`;
|
|
22
|
+
}
|
|
23
|
+
function isCompletedStepStatus(status) {
|
|
24
|
+
return status === "complete" || status === "completed";
|
|
25
|
+
}
|
|
26
|
+
export function aggregateStepStatus(steps) {
|
|
27
|
+
if (steps.some((step) => step.status === "running"))
|
|
28
|
+
return "running";
|
|
29
|
+
if (steps.some((step) => step.status === "failed"))
|
|
30
|
+
return "failed";
|
|
31
|
+
if (steps.some((step) => step.status === "paused"))
|
|
32
|
+
return "paused";
|
|
33
|
+
if (steps.length > 0 && steps.every((step) => isCompletedStepStatus(step.status)))
|
|
34
|
+
return "complete";
|
|
35
|
+
return "pending";
|
|
36
|
+
}
|
|
37
|
+
export function formatAgentRunningLabel(count) {
|
|
38
|
+
return count === 1 ? "1 agent running" : `${count} agents running`;
|
|
39
|
+
}
|
|
40
|
+
export function formatParallelOutcome(steps, total, options = {}) {
|
|
41
|
+
const running = steps.filter((step) => step.status === "running").length;
|
|
42
|
+
const done = steps.filter((step) => isCompletedStepStatus(step.status)).length;
|
|
43
|
+
const failed = steps.filter((step) => step.status === "failed").length;
|
|
44
|
+
const paused = steps.filter((step) => step.status === "paused").length;
|
|
45
|
+
const parts = [`${done}/${total} done`];
|
|
46
|
+
if (options.showRunning !== false && running > 0)
|
|
47
|
+
parts.unshift(formatAgentRunningLabel(running));
|
|
48
|
+
if (failed > 0)
|
|
49
|
+
parts.push(`${failed} failed`);
|
|
50
|
+
if (paused > 0)
|
|
51
|
+
parts.push(`${paused} paused`);
|
|
52
|
+
return parts.join(" · ");
|
|
53
|
+
}
|
|
@@ -1,8 +1,186 @@
|
|
|
1
|
-
import*as
|
|
1
|
+
import * as os from "node:os";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
export const SUBAGENT_LIFECYCLE_ARTIFACT_VERSION = 1;
|
|
4
|
+
export const INTERCOM_DETACH_REQUEST_EVENT = "pi-intercom:detach-request";
|
|
5
|
+
export const INTERCOM_DETACH_RESPONSE_EVENT = "pi-intercom:detach-response";
|
|
6
|
+
export const SUBAGENT_ASYNC_STARTED_EVENT = "subagent:async-started";
|
|
7
|
+
export const SUBAGENT_ASYNC_COMPLETE_EVENT = "subagent:async-complete";
|
|
8
|
+
export const SUBAGENT_CONTROL_EVENT = "subagent:control-event";
|
|
9
|
+
export const SUBAGENT_CONTROL_INTERCOM_EVENT = "subagent:control-intercom";
|
|
10
|
+
export const SUBAGENT_RESULT_INTERCOM_EVENT = "subagent:result-intercom";
|
|
11
|
+
export const SUBAGENT_RESULT_INTERCOM_DELIVERY_EVENT = "subagent:result-intercom-delivery";
|
|
12
|
+
export const DEFAULT_MAX_OUTPUT = {
|
|
13
|
+
bytes: 200 * 1024,
|
|
14
|
+
lines: 5000
|
|
15
|
+
};
|
|
16
|
+
export const DEFAULT_ARTIFACT_CONFIG = {
|
|
17
|
+
enabled: true,
|
|
18
|
+
includeInput: true,
|
|
19
|
+
includeOutput: true,
|
|
20
|
+
includeJsonl: false,
|
|
21
|
+
includeTranscript: true,
|
|
22
|
+
includeMetadata: true,
|
|
23
|
+
cleanupDays: 7
|
|
24
|
+
};
|
|
25
|
+
function sanitizeTempScopeSegment(value) {
|
|
26
|
+
const sanitized = value.trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
27
|
+
return sanitized || "unknown";
|
|
28
|
+
}
|
|
29
|
+
export function resolveTempScopeId(options) {
|
|
30
|
+
const env = options?.env ?? process.env;
|
|
31
|
+
const getuid = options && Object.hasOwn(options, "getuid") ? options.getuid : process.getuid?.bind(process);
|
|
32
|
+
if (typeof getuid === "function") {
|
|
33
|
+
return `uid-${getuid()}`;
|
|
34
|
+
}
|
|
35
|
+
for (const key of ["USERNAME", "USER", "LOGNAME"]) {
|
|
36
|
+
const value = env[key];
|
|
37
|
+
if (value)
|
|
38
|
+
return `user-${sanitizeTempScopeSegment(value)}`;
|
|
39
|
+
}
|
|
40
|
+
const userInfo = options && Object.hasOwn(options, "userInfo") ? options.userInfo : os.userInfo;
|
|
41
|
+
try {
|
|
42
|
+
const username = userInfo?.().username;
|
|
43
|
+
if (username)
|
|
44
|
+
return `user-${sanitizeTempScopeSegment(username)}`;
|
|
45
|
+
} catch {}
|
|
46
|
+
const homedir = env.USERPROFILE ?? env.HOME;
|
|
47
|
+
if (homedir)
|
|
48
|
+
return `home-${sanitizeTempScopeSegment(homedir)}`;
|
|
49
|
+
const resolveHomedir = options && Object.hasOwn(options, "homedir") ? options.homedir : os.homedir;
|
|
50
|
+
try {
|
|
51
|
+
const fallbackHomedir = resolveHomedir?.();
|
|
52
|
+
if (fallbackHomedir)
|
|
53
|
+
return `home-${sanitizeTempScopeSegment(fallbackHomedir)}`;
|
|
54
|
+
} catch {}
|
|
55
|
+
return "shared";
|
|
56
|
+
}
|
|
57
|
+
const MAX_PARALLEL = 8;
|
|
58
|
+
export const MAX_CONCURRENCY = 4;
|
|
59
|
+
export const TEMP_ROOT_DIR = path.join(os.tmpdir(), `dm-subagents-${resolveTempScopeId()}`);
|
|
60
|
+
export const RESULTS_DIR = path.join(TEMP_ROOT_DIR, "async-subagent-results");
|
|
61
|
+
export const ASYNC_DIR = path.join(TEMP_ROOT_DIR, "async-subagent-runs");
|
|
62
|
+
export const CHAIN_RUNS_DIR = path.join(TEMP_ROOT_DIR, "chain-runs");
|
|
63
|
+
export const TEMP_ARTIFACTS_DIR = path.join(TEMP_ROOT_DIR, "artifacts");
|
|
64
|
+
export const WIDGET_KEY = "subagent-async";
|
|
65
|
+
export const SLASH_RESULT_TYPE = "subagent-slash-result";
|
|
66
|
+
export const SLASH_TEXT_RESULT_TYPE = "subagent-slash-text-result";
|
|
67
|
+
export const SLASH_SUBAGENT_REQUEST_EVENT = "subagent:slash:request";
|
|
68
|
+
export const SLASH_SUBAGENT_STARTED_EVENT = "subagent:slash:started";
|
|
69
|
+
export const SLASH_SUBAGENT_RESPONSE_EVENT = "subagent:slash:response";
|
|
70
|
+
export const SLASH_SUBAGENT_UPDATE_EVENT = "subagent:slash:update";
|
|
71
|
+
export const SLASH_SUBAGENT_CANCEL_EVENT = "subagent:slash:cancel";
|
|
72
|
+
export const POLL_INTERVAL_MS = 250;
|
|
73
|
+
export const MAX_WIDGET_JOBS = 4;
|
|
74
|
+
export const DEFAULT_SUBAGENT_MAX_DEPTH = 2;
|
|
75
|
+
export const DEFAULT_MAX_SUBAGENT_SPAWNS_PER_SESSION = 40;
|
|
76
|
+
export const SUBAGENT_ACTIONS = ["list", "get", "models", "create", "update", "delete", "eject", "disable", "enable", "reset", "status", "interrupt", "resume", "steer", "append-step", "doctor", "schedule", "schedule-list", "schedule-status", "schedule-cancel"];
|
|
77
|
+
export const DEFAULT_FORK_PREAMBLE = "You are a delegated subagent running from a fork of the parent session. " + "Treat the inherited conversation as reference-only context, not a live thread to continue. " + "Do not continue or answer prior messages as if they are waiting for a reply. " + "Your sole job is to execute the task below and return a focused result for that task using your tools.";
|
|
78
|
+
function normalizeTopLevelParallelValue(value) {
|
|
79
|
+
const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
|
|
80
|
+
if (!Number.isInteger(parsed) || parsed < 1)
|
|
81
|
+
return;
|
|
82
|
+
return parsed;
|
|
83
|
+
}
|
|
84
|
+
export function resolveTopLevelParallelMaxTasks(value) {
|
|
85
|
+
return normalizeTopLevelParallelValue(value) ?? MAX_PARALLEL;
|
|
86
|
+
}
|
|
87
|
+
export function resolveTopLevelParallelConcurrency(override, configValue) {
|
|
88
|
+
return normalizeTopLevelParallelValue(override) ?? normalizeTopLevelParallelValue(configValue) ?? MAX_CONCURRENCY;
|
|
89
|
+
}
|
|
90
|
+
export function getAsyncConfigPath(suffix) {
|
|
91
|
+
return path.join(TEMP_ROOT_DIR, `async-cfg-${suffix}.json`);
|
|
92
|
+
}
|
|
93
|
+
export function wrapForkTask(task, preamble) {
|
|
94
|
+
if (preamble === false)
|
|
95
|
+
return task;
|
|
96
|
+
const effectivePreamble = preamble ?? DEFAULT_FORK_PREAMBLE;
|
|
97
|
+
const wrappedPrefix = `${effectivePreamble}
|
|
2
98
|
|
|
3
99
|
Task:
|
|
4
|
-
`;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
100
|
+
`;
|
|
101
|
+
if (task.startsWith(wrappedPrefix))
|
|
102
|
+
return task;
|
|
103
|
+
return `${wrappedPrefix}${task}`;
|
|
104
|
+
}
|
|
105
|
+
function normalizeNonNegativeInteger(value) {
|
|
106
|
+
const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
|
|
107
|
+
if (!Number.isInteger(parsed) || parsed < 0)
|
|
108
|
+
return;
|
|
109
|
+
return parsed;
|
|
110
|
+
}
|
|
111
|
+
export function normalizeMaxSubagentDepth(value) {
|
|
112
|
+
return normalizeNonNegativeInteger(value);
|
|
113
|
+
}
|
|
114
|
+
export function resolveCurrentMaxSubagentDepth(configMaxDepth) {
|
|
115
|
+
return normalizeMaxSubagentDepth(process.env.DM_SUBAGENT_MAX_DEPTH) ?? normalizeMaxSubagentDepth(configMaxDepth) ?? DEFAULT_SUBAGENT_MAX_DEPTH;
|
|
116
|
+
}
|
|
117
|
+
export function resolveChildMaxSubagentDepth(parentMaxDepth, agentMaxDepth) {
|
|
118
|
+
const normalizedParent = normalizeMaxSubagentDepth(parentMaxDepth) ?? DEFAULT_SUBAGENT_MAX_DEPTH;
|
|
119
|
+
const normalizedAgent = normalizeMaxSubagentDepth(agentMaxDepth);
|
|
120
|
+
return normalizedAgent === undefined ? normalizedParent : Math.min(normalizedParent, normalizedAgent);
|
|
121
|
+
}
|
|
122
|
+
export function checkSubagentDepth(configMaxDepth) {
|
|
123
|
+
const depth = Number(process.env.DM_SUBAGENT_DEPTH ?? "0");
|
|
124
|
+
const maxDepth = resolveCurrentMaxSubagentDepth(configMaxDepth);
|
|
125
|
+
const blocked = Number.isFinite(depth) && depth >= maxDepth;
|
|
126
|
+
return { blocked, depth, maxDepth };
|
|
127
|
+
}
|
|
128
|
+
export function getSubagentDepthEnv(maxDepth) {
|
|
129
|
+
const parentDepth = Number(process.env.DM_SUBAGENT_DEPTH ?? "0");
|
|
130
|
+
const nextDepth = Number.isFinite(parentDepth) ? parentDepth + 1 : 1;
|
|
131
|
+
return {
|
|
132
|
+
DM_SUBAGENT_DEPTH: String(nextDepth),
|
|
133
|
+
DM_SUBAGENT_MAX_DEPTH: String(normalizeMaxSubagentDepth(maxDepth) ?? resolveCurrentMaxSubagentDepth())
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
export function normalizeMaxSubagentSpawnsPerSession(value) {
|
|
137
|
+
return normalizeNonNegativeInteger(value);
|
|
138
|
+
}
|
|
139
|
+
export function resolveMaxSubagentSpawnsPerSession(configMaxSpawns) {
|
|
140
|
+
return normalizeMaxSubagentSpawnsPerSession(process.env.DM_SUBAGENT_MAX_SPAWNS_PER_SESSION) ?? normalizeMaxSubagentSpawnsPerSession(configMaxSpawns) ?? DEFAULT_MAX_SUBAGENT_SPAWNS_PER_SESSION;
|
|
141
|
+
}
|
|
142
|
+
function formatBytes(bytes) {
|
|
143
|
+
if (bytes < 1024)
|
|
144
|
+
return `${bytes}B`;
|
|
145
|
+
if (bytes < 1024 * 1024)
|
|
146
|
+
return `${(bytes / 1024).toFixed(1)}KB`;
|
|
147
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
148
|
+
}
|
|
149
|
+
export function truncateOutput(output, config, artifactPath) {
|
|
150
|
+
const lines = output.split(`
|
|
151
|
+
`);
|
|
152
|
+
const bytes = Buffer.byteLength(output, "utf-8");
|
|
153
|
+
if (bytes <= config.bytes && lines.length <= config.lines) {
|
|
154
|
+
return { text: output, truncated: false };
|
|
155
|
+
}
|
|
156
|
+
let truncatedLines = lines;
|
|
157
|
+
if (lines.length > config.lines) {
|
|
158
|
+
truncatedLines = lines.slice(0, config.lines);
|
|
159
|
+
}
|
|
160
|
+
let result = truncatedLines.join(`
|
|
161
|
+
`);
|
|
162
|
+
if (Buffer.byteLength(result, "utf-8") > config.bytes) {
|
|
163
|
+
let low = 0;
|
|
164
|
+
let high = result.length;
|
|
165
|
+
while (low < high) {
|
|
166
|
+
const mid = Math.floor((low + high + 1) / 2);
|
|
167
|
+
if (Buffer.byteLength(result.slice(0, mid), "utf-8") <= config.bytes) {
|
|
168
|
+
low = mid;
|
|
169
|
+
} else {
|
|
170
|
+
high = mid - 1;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
result = result.slice(0, low);
|
|
174
|
+
}
|
|
175
|
+
const keptLines = result.split(`
|
|
176
|
+
`).length;
|
|
177
|
+
const marker = `[TRUNCATED: showing first ${keptLines} of ${lines.length} lines, ${formatBytes(Buffer.byteLength(result))} of ${formatBytes(bytes)}${artifactPath ? ` - full output at ${artifactPath}` : ""}]
|
|
178
|
+
`;
|
|
179
|
+
return {
|
|
180
|
+
text: marker + result,
|
|
181
|
+
truncated: true,
|
|
182
|
+
originalBytes: bytes,
|
|
183
|
+
originalLines: lines.length,
|
|
184
|
+
artifactPath
|
|
185
|
+
};
|
|
186
|
+
}
|