@gajae-code/coding-agent 0.2.3 → 0.2.5
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/CHANGELOG.md +34 -8600
- package/README.md +1 -1
- package/dist/types/async/job-manager.d.ts +61 -0
- package/dist/types/cli/update-cli.d.ts +3 -0
- package/dist/types/config/settings-schema.d.ts +27 -3
- package/dist/types/config/settings.d.ts +1 -1
- package/dist/types/defaults/gjc-defaults.d.ts +19 -6
- package/dist/types/discovery/helpers.d.ts +1 -0
- package/dist/types/exec/bash-executor.d.ts +8 -1
- package/dist/types/gjc-runtime/restricted-role-agent-bash.d.ts +2 -0
- package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -1
- package/dist/types/modes/components/settings-selector.d.ts +4 -0
- package/dist/types/modes/components/skill-hud/render.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/theme/defaults/index.d.ts +45 -9351
- package/dist/types/modes/theme/theme.d.ts +6 -5
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/session/streaming-output.d.ts +11 -0
- package/dist/types/skill-state/active-state.d.ts +1 -0
- package/dist/types/task/types.d.ts +1 -0
- package/dist/types/tools/bash-allowed-prefixes.d.ts +5 -0
- package/dist/types/tools/bash.d.ts +24 -0
- package/dist/types/tools/cron.d.ts +110 -0
- package/dist/types/tools/index.d.ts +4 -0
- package/dist/types/tools/monitor.d.ts +54 -0
- package/dist/types/web/search/index.d.ts +1 -0
- package/dist/types/web/search/provider.d.ts +11 -4
- package/dist/types/web/search/providers/duckduckgo.d.ts +57 -0
- package/dist/types/web/search/types.d.ts +1 -1
- package/package.json +7 -7
- package/src/async/job-manager.ts +224 -0
- package/src/cli/agents-cli.ts +3 -0
- package/src/cli/update-cli.ts +67 -16
- package/src/config/settings-schema.ts +30 -2
- package/src/config/settings.ts +44 -7
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +48 -6
- package/src/defaults/gjc/skills/deep-interview/auto-answer-uncertain.md +37 -0
- package/src/defaults/gjc/skills/deep-interview/auto-research-greenfield.md +42 -0
- package/src/defaults/gjc/skills/ralplan/SKILL.md +8 -4
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +9 -6
- package/src/defaults/gjc-defaults.ts +68 -16
- package/src/discovery/helpers.ts +5 -0
- package/src/eval/js/shared/rewrite-imports.ts +1 -2
- package/src/exec/bash-executor.ts +20 -9
- package/src/gjc-runtime/deep-interview-runtime.ts +44 -0
- package/src/gjc-runtime/ralplan-runtime.ts +2 -0
- package/src/gjc-runtime/restricted-role-agent-bash.ts +5 -0
- package/src/gjc-runtime/state-runtime.ts +3 -2
- package/src/goals/tools/goal-tool.ts +5 -1
- package/src/hooks/skill-state.ts +1 -1
- package/src/internal-urls/docs-index.generated.ts +8 -4
- package/src/lsp/render.ts +1 -1
- package/src/memories/index.ts +5 -4
- package/src/modes/acp/acp-agent.ts +1 -1
- package/src/modes/acp/acp-client-bridge.ts +1 -1
- package/src/modes/components/agent-dashboard.ts +1 -1
- package/src/modes/components/diff.ts +2 -2
- package/src/modes/components/settings-selector.ts +25 -14
- package/src/modes/components/skill-hud/render.ts +7 -2
- package/src/modes/controllers/command-controller.ts +1 -1
- package/src/modes/controllers/input-controller.ts +10 -2
- package/src/modes/controllers/selector-controller.ts +67 -0
- package/src/modes/interactive-mode.ts +34 -3
- package/src/modes/theme/defaults/blue-crab.json +126 -0
- package/src/modes/theme/defaults/index.ts +2 -196
- package/src/modes/theme/theme.ts +75 -36
- package/src/modes/types.ts +2 -0
- package/src/prompts/agents/architect.md +5 -1
- package/src/prompts/agents/critic.md +5 -1
- package/src/prompts/agents/frontmatter.md +1 -0
- package/src/prompts/agents/planner.md +5 -1
- package/src/prompts/memories/unavailable.md +9 -0
- package/src/prompts/tools/bash.md +9 -0
- package/src/prompts/tools/cron.md +25 -0
- package/src/prompts/tools/monitor.md +30 -0
- package/src/runtime-mcp/oauth-flow.ts +4 -2
- package/src/sdk.ts +7 -0
- package/src/session/agent-session.ts +16 -5
- package/src/session/streaming-output.ts +21 -0
- package/src/skill-state/active-state.ts +163 -12
- package/src/slash-commands/builtin-registry.ts +11 -1
- package/src/task/agents.ts +1 -0
- package/src/task/executor.ts +1 -0
- package/src/task/types.ts +1 -0
- package/src/tools/bash-allowed-prefixes.ts +169 -0
- package/src/tools/bash.ts +190 -29
- package/src/tools/browser/tab-worker.ts +1 -1
- package/src/tools/cron.ts +665 -0
- package/src/tools/index.ts +20 -2
- package/src/tools/monitor.ts +136 -0
- package/src/vim/engine.ts +3 -3
- package/src/web/search/index.ts +31 -18
- package/src/web/search/provider.ts +57 -12
- package/src/web/search/providers/duckduckgo.ts +279 -0
- package/src/web/search/types.ts +2 -0
- package/src/modes/theme/dark.json +0 -95
- package/src/modes/theme/defaults/alabaster.json +0 -93
- package/src/modes/theme/defaults/amethyst.json +0 -96
- package/src/modes/theme/defaults/anthracite.json +0 -93
- package/src/modes/theme/defaults/basalt.json +0 -91
- package/src/modes/theme/defaults/birch.json +0 -95
- package/src/modes/theme/defaults/dark-abyss.json +0 -91
- package/src/modes/theme/defaults/dark-arctic.json +0 -104
- package/src/modes/theme/defaults/dark-aurora.json +0 -95
- package/src/modes/theme/defaults/dark-catppuccin.json +0 -107
- package/src/modes/theme/defaults/dark-cavern.json +0 -91
- package/src/modes/theme/defaults/dark-copper.json +0 -95
- package/src/modes/theme/defaults/dark-cosmos.json +0 -90
- package/src/modes/theme/defaults/dark-cyberpunk.json +0 -102
- package/src/modes/theme/defaults/dark-dracula.json +0 -98
- package/src/modes/theme/defaults/dark-eclipse.json +0 -91
- package/src/modes/theme/defaults/dark-ember.json +0 -95
- package/src/modes/theme/defaults/dark-equinox.json +0 -90
- package/src/modes/theme/defaults/dark-forest.json +0 -96
- package/src/modes/theme/defaults/dark-github.json +0 -105
- package/src/modes/theme/defaults/dark-gruvbox.json +0 -112
- package/src/modes/theme/defaults/dark-lavender.json +0 -95
- package/src/modes/theme/defaults/dark-lunar.json +0 -89
- package/src/modes/theme/defaults/dark-midnight.json +0 -95
- package/src/modes/theme/defaults/dark-monochrome.json +0 -94
- package/src/modes/theme/defaults/dark-monokai.json +0 -98
- package/src/modes/theme/defaults/dark-nebula.json +0 -90
- package/src/modes/theme/defaults/dark-nord.json +0 -97
- package/src/modes/theme/defaults/dark-ocean.json +0 -101
- package/src/modes/theme/defaults/dark-one.json +0 -100
- package/src/modes/theme/defaults/dark-poimandres.json +0 -141
- package/src/modes/theme/defaults/dark-rainforest.json +0 -91
- package/src/modes/theme/defaults/dark-reef.json +0 -91
- package/src/modes/theme/defaults/dark-retro.json +0 -92
- package/src/modes/theme/defaults/dark-rose-pine.json +0 -96
- package/src/modes/theme/defaults/dark-sakura.json +0 -95
- package/src/modes/theme/defaults/dark-slate.json +0 -95
- package/src/modes/theme/defaults/dark-solarized.json +0 -97
- package/src/modes/theme/defaults/dark-solstice.json +0 -90
- package/src/modes/theme/defaults/dark-starfall.json +0 -91
- package/src/modes/theme/defaults/dark-sunset.json +0 -99
- package/src/modes/theme/defaults/dark-swamp.json +0 -90
- package/src/modes/theme/defaults/dark-synthwave.json +0 -103
- package/src/modes/theme/defaults/dark-taiga.json +0 -91
- package/src/modes/theme/defaults/dark-terminal.json +0 -95
- package/src/modes/theme/defaults/dark-tokyo-night.json +0 -101
- package/src/modes/theme/defaults/dark-tundra.json +0 -91
- package/src/modes/theme/defaults/dark-twilight.json +0 -91
- package/src/modes/theme/defaults/dark-volcanic.json +0 -91
- package/src/modes/theme/defaults/graphite.json +0 -92
- package/src/modes/theme/defaults/light-arctic.json +0 -107
- package/src/modes/theme/defaults/light-aurora-day.json +0 -91
- package/src/modes/theme/defaults/light-canyon.json +0 -91
- package/src/modes/theme/defaults/light-catppuccin.json +0 -106
- package/src/modes/theme/defaults/light-cirrus.json +0 -90
- package/src/modes/theme/defaults/light-coral.json +0 -95
- package/src/modes/theme/defaults/light-cyberpunk.json +0 -96
- package/src/modes/theme/defaults/light-dawn.json +0 -90
- package/src/modes/theme/defaults/light-dunes.json +0 -91
- package/src/modes/theme/defaults/light-eucalyptus.json +0 -95
- package/src/modes/theme/defaults/light-forest.json +0 -100
- package/src/modes/theme/defaults/light-frost.json +0 -95
- package/src/modes/theme/defaults/light-github.json +0 -115
- package/src/modes/theme/defaults/light-glacier.json +0 -91
- package/src/modes/theme/defaults/light-gruvbox.json +0 -108
- package/src/modes/theme/defaults/light-haze.json +0 -90
- package/src/modes/theme/defaults/light-honeycomb.json +0 -95
- package/src/modes/theme/defaults/light-lagoon.json +0 -91
- package/src/modes/theme/defaults/light-lavender.json +0 -95
- package/src/modes/theme/defaults/light-meadow.json +0 -91
- package/src/modes/theme/defaults/light-mint.json +0 -95
- package/src/modes/theme/defaults/light-monochrome.json +0 -101
- package/src/modes/theme/defaults/light-ocean.json +0 -99
- package/src/modes/theme/defaults/light-one.json +0 -99
- package/src/modes/theme/defaults/light-opal.json +0 -91
- package/src/modes/theme/defaults/light-orchard.json +0 -91
- package/src/modes/theme/defaults/light-paper.json +0 -95
- package/src/modes/theme/defaults/light-poimandres.json +0 -141
- package/src/modes/theme/defaults/light-prism.json +0 -90
- package/src/modes/theme/defaults/light-retro.json +0 -98
- package/src/modes/theme/defaults/light-sand.json +0 -95
- package/src/modes/theme/defaults/light-savanna.json +0 -91
- package/src/modes/theme/defaults/light-solarized.json +0 -102
- package/src/modes/theme/defaults/light-soleil.json +0 -90
- package/src/modes/theme/defaults/light-sunset.json +0 -99
- package/src/modes/theme/defaults/light-synthwave.json +0 -98
- package/src/modes/theme/defaults/light-tokyo-night.json +0 -111
- package/src/modes/theme/defaults/light-wetland.json +0 -91
- package/src/modes/theme/defaults/light-zenith.json +0 -89
- package/src/modes/theme/defaults/limestone.json +0 -94
- package/src/modes/theme/defaults/mahogany.json +0 -97
- package/src/modes/theme/defaults/marble.json +0 -93
- package/src/modes/theme/defaults/obsidian.json +0 -91
- package/src/modes/theme/defaults/onyx.json +0 -91
- package/src/modes/theme/defaults/pearl.json +0 -93
- package/src/modes/theme/defaults/porcelain.json +0 -91
- package/src/modes/theme/defaults/quartz.json +0 -96
- package/src/modes/theme/defaults/sandstone.json +0 -95
- package/src/modes/theme/defaults/titanium.json +0 -90
- package/src/modes/theme/light.json +0 -93
package/src/tools/bash.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { AsyncJobManager } from "../async";
|
|
|
8
8
|
import { type BashResult, executeBash } from "../exec/bash-executor";
|
|
9
9
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
10
10
|
import { buildGjcRuntimeSessionEnv } from "../gjc-runtime/goal-mode-request";
|
|
11
|
+
import { GJC_RESTRICTED_ROLE_AGENT_BASH_ENV } from "../gjc-runtime/restricted-role-agent-bash";
|
|
11
12
|
import { InternalUrlRouter } from "../internal-urls";
|
|
12
13
|
import { truncateToVisualLines } from "../modes/components/visual-truncate";
|
|
13
14
|
import { highlightCode, type Theme } from "../modes/theme/theme";
|
|
@@ -18,6 +19,7 @@ import { renderStatusLine } from "../tui";
|
|
|
18
19
|
import { CachedOutputBlock } from "../tui/output-block";
|
|
19
20
|
import { getSixelLineMask } from "../utils/sixel";
|
|
20
21
|
import type { ToolSession } from ".";
|
|
22
|
+
import { checkBashAllowedPrefixes } from "./bash-allowed-prefixes";
|
|
21
23
|
import { applyBashFixups } from "./bash-command-fixup";
|
|
22
24
|
import { type BashInteractiveResult, runInteractiveBashPty } from "./bash-interactive";
|
|
23
25
|
import { checkBashInterception } from "./bash-interceptor";
|
|
@@ -253,6 +255,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
253
255
|
hasAstEdit: this.session.settings.get("astEdit.enabled"),
|
|
254
256
|
hasSearch: this.session.settings.get("search.enabled"),
|
|
255
257
|
hasFind: this.session.settings.get("find.enabled"),
|
|
258
|
+
restrictedAllowedPrefixes: this.session.bashAllowedPrefixes,
|
|
256
259
|
});
|
|
257
260
|
}
|
|
258
261
|
|
|
@@ -377,6 +380,13 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
377
380
|
latestText = tailBuffer.text();
|
|
378
381
|
void reportProgress(latestText, { async: { state: "running", jobId, type: "bash" } });
|
|
379
382
|
},
|
|
383
|
+
onRawChunk: chunk => {
|
|
384
|
+
// Forward the unthrottled sanitized chunk to the async-job
|
|
385
|
+
// substrate so the Monitor tool can read the complete process
|
|
386
|
+
// stream by byte offset, independent of the throttled preview
|
|
387
|
+
// path above.
|
|
388
|
+
manager.appendOutput(jobId, chunk);
|
|
389
|
+
},
|
|
380
390
|
onMinimizedSave: originalText => saveBashOriginalArtifact(this.session, originalText),
|
|
381
391
|
});
|
|
382
392
|
const finalResult = this.#buildCompletedResult(result, options.timeoutSec, {
|
|
@@ -454,27 +464,29 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
454
464
|
return Math.max(0, Math.min(this.#autoBackgroundThresholdMs, timeoutMs - timeoutBufferMs));
|
|
455
465
|
}
|
|
456
466
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
pty = false,
|
|
467
|
-
}: BashToolInput,
|
|
468
|
-
signal?: AbortSignal,
|
|
469
|
-
onUpdate?: AgentToolUpdateCallback<BashToolDetails>,
|
|
467
|
+
/**
|
|
468
|
+
* Build the fully-prepared parameters for a `bash`-flavored execution
|
|
469
|
+
* (interceptors, internal URL expansion, env resolution, cwd validation,
|
|
470
|
+
* timeout clamp). Used by both `execute()` and the public `startMonitorJob`
|
|
471
|
+
* helper after `AgentSession` has applied the public-tool permission gate, so
|
|
472
|
+
* Monitor inherits Bash's cwd / env / artifact / interceptor pipeline 1:1.
|
|
473
|
+
*/
|
|
474
|
+
async #prepareBashExecution(
|
|
475
|
+
input: { command: string; env?: Record<string, string>; timeout?: number; cwd?: string },
|
|
470
476
|
ctx?: AgentToolContext,
|
|
471
|
-
): Promise<
|
|
472
|
-
|
|
473
|
-
|
|
477
|
+
): Promise<{
|
|
478
|
+
command: string;
|
|
479
|
+
commandCwd: string;
|
|
480
|
+
resolvedEnv: Record<string, string>;
|
|
481
|
+
requestedTimeoutSec: number;
|
|
482
|
+
timeoutSec: number;
|
|
483
|
+
timeoutMs: number;
|
|
484
|
+
notices: string[];
|
|
485
|
+
}> {
|
|
486
|
+
let command = input.command;
|
|
487
|
+
let cwd = input.cwd;
|
|
488
|
+
const env = normalizeBashEnv(input.env);
|
|
474
489
|
|
|
475
|
-
// Apply conservative bash fixups (strip trailing `| head|tail` and redundant
|
|
476
|
-
// `2>&1`). The helper is single-line only and refuses anything that could
|
|
477
|
-
// change semantics.
|
|
478
490
|
if (this.session.settings.get("bash.stripTrailingHeadTail")) {
|
|
479
491
|
const fixup = applyBashFixups(command);
|
|
480
492
|
if (fixup.stripped.length > 0) {
|
|
@@ -482,9 +494,6 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
482
494
|
}
|
|
483
495
|
}
|
|
484
496
|
|
|
485
|
-
// Extract leading `cd <path> && ...` into cwd when the model ignores the cwd parameter.
|
|
486
|
-
// Constrained to a single line so a `&&` that sits on a later line of a multiline
|
|
487
|
-
// script can't pull the entire script into the "cwd" capture.
|
|
488
497
|
if (!cwd) {
|
|
489
498
|
const cdMatch = command.match(/^cd[ \t]+((?:[^&\\\n\r]|\\.)+?)[ \t]*&&[ \t]*/);
|
|
490
499
|
if (cdMatch) {
|
|
@@ -492,8 +501,20 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
492
501
|
command = command.slice(cdMatch[0].length);
|
|
493
502
|
}
|
|
494
503
|
}
|
|
495
|
-
|
|
496
|
-
|
|
504
|
+
|
|
505
|
+
const rawCommand = input.command;
|
|
506
|
+
const allowedPrefixes = this.session.bashAllowedPrefixes;
|
|
507
|
+
if (allowedPrefixes && allowedPrefixes.length > 0) {
|
|
508
|
+
if (env && Object.keys(env).length > 0) {
|
|
509
|
+
throw new ToolError("Restricted role-agent bash does not allow per-command env overrides.");
|
|
510
|
+
}
|
|
511
|
+
const commandsToCheck = rawCommand === command ? [command] : [rawCommand, command];
|
|
512
|
+
for (const commandToCheck of commandsToCheck) {
|
|
513
|
+
const allowlist = checkBashAllowedPrefixes(commandToCheck, allowedPrefixes);
|
|
514
|
+
if (!allowlist.allowed) {
|
|
515
|
+
throw new ToolError(allowlist.reason ?? "Command blocked by restricted role-agent bash allowlist.");
|
|
516
|
+
}
|
|
517
|
+
}
|
|
497
518
|
}
|
|
498
519
|
|
|
499
520
|
// Check both the original command and the cwd-normalized command so
|
|
@@ -541,9 +562,9 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
541
562
|
cwd: this.session.cwd,
|
|
542
563
|
}),
|
|
543
564
|
...expandedEnv,
|
|
565
|
+
...(allowedPrefixes && allowedPrefixes.length > 0 ? { [GJC_RESTRICTED_ROLE_AGENT_BASH_ENV]: "1" } : {}),
|
|
544
566
|
};
|
|
545
567
|
|
|
546
|
-
// Resolve protocol URLs (agent://, artifact://, etc.) in extracted cwd.
|
|
547
568
|
if (cwd?.includes("://") || cwd?.includes("local:/")) {
|
|
548
569
|
cwd = await expandInternalUrls(cwd, { ...internalUrlOptions, noEscape: true });
|
|
549
570
|
}
|
|
@@ -562,13 +583,153 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
562
583
|
throw new ToolError(`Working directory is not a directory: ${commandCwd}`);
|
|
563
584
|
}
|
|
564
585
|
|
|
565
|
-
|
|
566
|
-
const requestedTimeoutSec = rawTimeout;
|
|
586
|
+
const requestedTimeoutSec = input.timeout ?? 300;
|
|
567
587
|
const timeoutSec = clampTimeout("bash", requestedTimeoutSec);
|
|
568
588
|
const timeoutMs = timeoutSec * 1000;
|
|
569
|
-
const
|
|
589
|
+
const notices: string[] = [];
|
|
570
590
|
const timeoutClampNotice = formatTimeoutClampNotice(requestedTimeoutSec, timeoutSec);
|
|
571
|
-
if (timeoutClampNotice)
|
|
591
|
+
if (timeoutClampNotice) notices.push(timeoutClampNotice);
|
|
592
|
+
|
|
593
|
+
return { command, commandCwd, resolvedEnv, requestedTimeoutSec, timeoutSec, timeoutMs, notices };
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Start a background bash job for the Monitor tool. Reuses the full Bash
|
|
598
|
+
* pipeline (interceptors, internal-URL expansion, env, cwd, timeout); the
|
|
599
|
+
* public `monitor` tool itself is ACP-gated by `AgentSession` before this
|
|
600
|
+
* helper is called. The caller-supplied `onRawLine` callback is invoked once
|
|
601
|
+
* per newline-terminated stdout chunk, between turns, so the upstream Claude
|
|
602
|
+
* Code "Each stdout line is a task-notification event" semantics are preserved
|
|
603
|
+
* through the agent's existing background-task delivery path.
|
|
604
|
+
*/
|
|
605
|
+
async startMonitorJob(
|
|
606
|
+
input: { command: string; cwd?: string; timeout?: number; env?: Record<string, string> },
|
|
607
|
+
opts: {
|
|
608
|
+
ownerId?: string;
|
|
609
|
+
label?: string;
|
|
610
|
+
ctx?: AgentToolContext;
|
|
611
|
+
onRawLine?: (line: string, jobId: string) => void;
|
|
612
|
+
} = {},
|
|
613
|
+
): Promise<{ jobId: string; label: string; commandCwd: string }> {
|
|
614
|
+
const manager = AsyncJobManager.instance();
|
|
615
|
+
if (!manager) {
|
|
616
|
+
throw new ToolError("Async job manager unavailable for this session.");
|
|
617
|
+
}
|
|
618
|
+
const prepared = await this.#prepareBashExecution(input, opts.ctx);
|
|
619
|
+
const label =
|
|
620
|
+
opts.label ?? (prepared.command.length > 120 ? `${prepared.command.slice(0, 117)}...` : prepared.command);
|
|
621
|
+
const monitorTimeoutMs = input.timeout === undefined ? null : prepared.timeoutMs;
|
|
622
|
+
const onRawLine = opts.onRawLine;
|
|
623
|
+
let currentJobId = "";
|
|
624
|
+
let cursorOffset = 0;
|
|
625
|
+
let lineBuffer = "";
|
|
626
|
+
const dispatchLines = (chunk: string) => {
|
|
627
|
+
if (!onRawLine) return;
|
|
628
|
+
lineBuffer += chunk;
|
|
629
|
+
let newlineIndex = lineBuffer.indexOf("\n");
|
|
630
|
+
while (newlineIndex !== -1) {
|
|
631
|
+
const line = lineBuffer.slice(0, newlineIndex);
|
|
632
|
+
lineBuffer = lineBuffer.slice(newlineIndex + 1);
|
|
633
|
+
try {
|
|
634
|
+
onRawLine(line, currentJobId);
|
|
635
|
+
} catch (error) {
|
|
636
|
+
logger.warn("Monitor onRawLine callback failed", {
|
|
637
|
+
error: error instanceof Error ? error.message : String(error),
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
newlineIndex = lineBuffer.indexOf("\n");
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
const flushTrailingLine = () => {
|
|
644
|
+
if (!onRawLine) return;
|
|
645
|
+
if (lineBuffer.length === 0) return;
|
|
646
|
+
const remainder = lineBuffer;
|
|
647
|
+
lineBuffer = "";
|
|
648
|
+
try {
|
|
649
|
+
onRawLine(remainder, currentJobId);
|
|
650
|
+
} catch (error) {
|
|
651
|
+
logger.warn("Monitor onRawLine callback failed (trailing)", {
|
|
652
|
+
error: error instanceof Error ? error.message : String(error),
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
|
|
657
|
+
const ownerId = opts.ownerId ?? this.session.getAgentId?.() ?? undefined;
|
|
658
|
+
const jobId = manager.register(
|
|
659
|
+
"bash",
|
|
660
|
+
label,
|
|
661
|
+
async ({ jobId: id, signal, reportProgress }) => {
|
|
662
|
+
const { path: artifactPath, id: artifactId } = (await this.session.allocateOutputArtifact?.("bash")) ?? {};
|
|
663
|
+
const tailBuffer = new TailBuffer(DEFAULT_MAX_BYTES);
|
|
664
|
+
try {
|
|
665
|
+
const result = await executeBash(prepared.command, {
|
|
666
|
+
cwd: prepared.commandCwd,
|
|
667
|
+
sessionKey: `${this.session.getSessionId?.() ?? ""}:monitor:${id}`,
|
|
668
|
+
timeout: monitorTimeoutMs,
|
|
669
|
+
signal,
|
|
670
|
+
env: prepared.resolvedEnv,
|
|
671
|
+
artifactPath,
|
|
672
|
+
artifactId,
|
|
673
|
+
onChunk: chunk => {
|
|
674
|
+
tailBuffer.append(chunk);
|
|
675
|
+
void reportProgress(tailBuffer.text(), {
|
|
676
|
+
async: { state: "running", jobId: id, type: "bash" },
|
|
677
|
+
});
|
|
678
|
+
},
|
|
679
|
+
onRawChunk: chunk => {
|
|
680
|
+
manager.appendOutput(id, chunk);
|
|
681
|
+
const slice = manager.readOutputSince(id, cursorOffset, ownerId ? { ownerId } : undefined);
|
|
682
|
+
if (!slice) return;
|
|
683
|
+
cursorOffset = slice.nextOffset;
|
|
684
|
+
dispatchLines(slice.text);
|
|
685
|
+
},
|
|
686
|
+
onMinimizedSave: originalText => saveBashOriginalArtifact(this.session, originalText),
|
|
687
|
+
});
|
|
688
|
+
flushTrailingLine();
|
|
689
|
+
this.#buildResultText(result, prepared.timeoutSec, result.output || "(no output)");
|
|
690
|
+
return result.output;
|
|
691
|
+
} catch (error) {
|
|
692
|
+
flushTrailingLine();
|
|
693
|
+
throw error instanceof Error ? error : new Error(String(error));
|
|
694
|
+
}
|
|
695
|
+
},
|
|
696
|
+
{ ownerId },
|
|
697
|
+
);
|
|
698
|
+
currentJobId = jobId;
|
|
699
|
+
return { jobId, label, commandCwd: prepared.commandCwd };
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
async execute(
|
|
703
|
+
_toolCallId: string,
|
|
704
|
+
{
|
|
705
|
+
command: rawCommand,
|
|
706
|
+
env: rawEnv,
|
|
707
|
+
timeout: rawTimeout = 300,
|
|
708
|
+
cwd,
|
|
709
|
+
|
|
710
|
+
async: asyncRequested = false,
|
|
711
|
+
pty = false,
|
|
712
|
+
}: BashToolInput,
|
|
713
|
+
signal?: AbortSignal,
|
|
714
|
+
onUpdate?: AgentToolUpdateCallback<BashToolDetails>,
|
|
715
|
+
ctx?: AgentToolContext,
|
|
716
|
+
): Promise<AgentToolResult<BashToolDetails>> {
|
|
717
|
+
if (asyncRequested && !this.#asyncEnabled) {
|
|
718
|
+
throw new ToolError("Async bash execution is disabled. Enable async.enabled to use async mode.");
|
|
719
|
+
}
|
|
720
|
+
const prepared = await this.#prepareBashExecution(
|
|
721
|
+
{ command: rawCommand, env: rawEnv, timeout: rawTimeout, cwd },
|
|
722
|
+
ctx,
|
|
723
|
+
);
|
|
724
|
+
const {
|
|
725
|
+
command,
|
|
726
|
+
commandCwd,
|
|
727
|
+
resolvedEnv,
|
|
728
|
+
requestedTimeoutSec,
|
|
729
|
+
timeoutSec,
|
|
730
|
+
timeoutMs,
|
|
731
|
+
notices: pendingNotices,
|
|
732
|
+
} = prepared;
|
|
572
733
|
|
|
573
734
|
if (asyncRequested) {
|
|
574
735
|
if (!AsyncJobManager.instance()) {
|
|
@@ -916,7 +916,7 @@ export class WorkerCore {
|
|
|
916
916
|
dispatchEvent: (event: unknown) => boolean;
|
|
917
917
|
}
|
|
918
918
|
const select = el as unknown as SelectLike;
|
|
919
|
-
if (
|
|
919
|
+
if (select?.tagName !== "SELECT") throw new Error("tab.select() requires a <select> element");
|
|
920
920
|
const EventCtor = (
|
|
921
921
|
globalThis as unknown as { Event: new (type: string, init?: { bubbles: boolean }) => unknown }
|
|
922
922
|
).Event;
|