agent-sh 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -43
- package/dist/agent/agent-loop.d.ts +69 -6
- package/dist/agent/agent-loop.js +954 -153
- package/dist/agent/conversation-state.d.ts +74 -21
- package/dist/agent/conversation-state.js +361 -150
- package/dist/agent/history-file.d.ts +13 -4
- package/dist/agent/history-file.js +110 -36
- package/dist/agent/nuclear-form.d.ts +28 -3
- package/dist/agent/nuclear-form.js +88 -6
- package/dist/agent/skills.d.ts +2 -4
- package/dist/agent/skills.js +10 -4
- package/dist/agent/subagent.d.ts +23 -0
- package/dist/agent/subagent.js +53 -11
- package/dist/agent/system-prompt.d.ts +37 -5
- package/dist/agent/system-prompt.js +100 -67
- package/dist/{token-budget.d.ts → agent/token-budget.d.ts} +5 -4
- package/dist/{token-budget.js → agent/token-budget.js} +15 -20
- package/dist/agent/tool-protocol.d.ts +105 -0
- package/dist/agent/tool-protocol.js +551 -0
- package/dist/agent/tools/bash.js +3 -3
- package/dist/agent/tools/edit-file.js +9 -6
- package/dist/agent/tools/glob.js +4 -2
- package/dist/agent/tools/grep.js +27 -3
- package/dist/agent/tools/ls.js +5 -6
- package/dist/agent/types.d.ts +22 -2
- package/dist/context-manager.d.ts +17 -0
- package/dist/context-manager.js +37 -4
- package/dist/core.d.ts +7 -7
- package/dist/core.js +99 -196
- package/dist/event-bus.d.ts +85 -2
- package/dist/event-bus.js +20 -1
- package/dist/executor.d.ts +4 -3
- package/dist/executor.js +18 -15
- package/dist/extension-loader.d.ts +5 -0
- package/dist/extension-loader.js +143 -19
- package/dist/extensions/agent-backend.d.ts +14 -0
- package/dist/extensions/agent-backend.js +188 -0
- package/dist/extensions/command-suggest.d.ts +3 -3
- package/dist/extensions/command-suggest.js +4 -3
- package/dist/extensions/index.d.ts +19 -0
- package/dist/extensions/index.js +24 -0
- package/dist/extensions/slash-commands.d.ts +1 -1
- package/dist/extensions/slash-commands.js +30 -10
- package/dist/extensions/tui-renderer.js +117 -113
- package/dist/index.js +39 -26
- package/dist/settings.d.ts +40 -3
- package/dist/settings.js +57 -10
- package/dist/{input-handler.d.ts → shell/input-handler.d.ts} +3 -2
- package/dist/{input-handler.js → shell/input-handler.js} +111 -85
- package/dist/{output-parser.d.ts → shell/output-parser.d.ts} +1 -1
- package/dist/{output-parser.js → shell/output-parser.js} +1 -1
- package/dist/{shell.d.ts → shell/shell.d.ts} +8 -2
- package/dist/{shell.js → shell/shell.js} +39 -8
- package/dist/types.d.ts +61 -10
- package/dist/utils/ansi.d.ts +5 -0
- package/dist/utils/ansi.js +1 -1
- package/dist/utils/compositor.d.ts +67 -0
- package/dist/utils/compositor.js +116 -0
- package/dist/utils/diff-renderer.d.ts +9 -0
- package/dist/utils/diff-renderer.js +312 -146
- package/dist/utils/diff.d.ts +21 -2
- package/dist/utils/diff.js +165 -89
- package/dist/utils/floating-panel.d.ts +2 -0
- package/dist/utils/floating-panel.js +30 -14
- package/dist/utils/handler-registry.d.ts +31 -10
- package/dist/utils/handler-registry.js +58 -16
- package/dist/utils/line-editor.d.ts +33 -3
- package/dist/utils/line-editor.js +221 -44
- package/dist/utils/markdown.d.ts +1 -0
- package/dist/utils/markdown.js +1 -1
- package/dist/utils/message-utils.d.ts +35 -0
- package/dist/utils/message-utils.js +75 -0
- package/dist/utils/terminal-buffer.d.ts +5 -1
- package/dist/utils/terminal-buffer.js +18 -2
- package/dist/utils/tool-display.d.ts +1 -1
- package/dist/utils/tool-display.js +4 -4
- package/dist/utils/tool-interactive.d.ts +12 -0
- package/dist/utils/tool-interactive.js +53 -0
- package/examples/extensions/ash-acp-bridge/README.md +39 -0
- package/examples/extensions/ash-acp-bridge/package.json +23 -0
- package/examples/extensions/ash-acp-bridge/src/index.ts +574 -0
- package/examples/extensions/ash-acp-bridge/tsconfig.json +14 -0
- package/examples/extensions/ash-mcp-bridge/README.md +72 -0
- package/examples/extensions/ash-mcp-bridge/index.ts +164 -0
- package/examples/extensions/ash-mcp-bridge/package.json +9 -0
- package/examples/extensions/claude-code-bridge/index.ts +198 -51
- package/examples/extensions/claude-code-bridge/package.json +1 -0
- package/examples/extensions/interactive-prompts.ts +98 -112
- package/examples/extensions/overlay-agent.ts +84 -38
- package/examples/extensions/peer-mesh.ts +565 -0
- package/examples/extensions/pi-bridge/index.ts +2 -2
- package/examples/extensions/questionnaire.ts +260 -0
- package/examples/extensions/subagents.ts +19 -4
- package/examples/extensions/terminal-buffer.ts +32 -53
- package/examples/extensions/tmux-pane.ts +307 -0
- package/examples/extensions/user-shell.ts +136 -0
- package/examples/extensions/web-access.ts +335 -0
- package/package.json +44 -2
- package/dist/agent/tools/display.d.ts +0 -13
- package/dist/agent/tools/display.js +0 -70
- package/dist/agent/tools/user-shell.d.ts +0 -13
- package/dist/agent/tools/user-shell.js +0 -87
- package/dist/extensions/overlay-agent.d.ts +0 -14
- package/dist/extensions/overlay-agent.js +0 -147
- package/dist/extensions/terminal-buffer.d.ts +0 -14
- package/dist/extensions/terminal-buffer.js +0 -125
|
@@ -4,34 +4,71 @@
|
|
|
4
4
|
* Adds permission gates for tool calls and file writes.
|
|
5
5
|
* Without this extension, agent-sh runs in yolo mode (auto-approve).
|
|
6
6
|
*
|
|
7
|
+
* Uses the interactive UI primitive for compositor-aware, themed rendering.
|
|
8
|
+
*
|
|
7
9
|
* Usage:
|
|
8
|
-
*
|
|
9
|
-
* agent-sh --extensions interactive-prompts
|
|
10
|
+
* agent-sh -e ./examples/extensions/interactive-prompts.ts
|
|
10
11
|
*
|
|
11
12
|
* # Or copy to ~/.agent-sh/extensions/ for permanent use:
|
|
12
13
|
* cp examples/extensions/interactive-prompts.ts ~/.agent-sh/extensions/
|
|
13
|
-
*
|
|
14
|
-
* # Or install as an npm package and load by name:
|
|
15
|
-
* agent-sh --extensions my-prompts-package
|
|
16
14
|
*/
|
|
17
15
|
import { renderDiff } from "agent-sh/utils/diff-renderer.js";
|
|
18
16
|
import { renderBoxFrame } from "agent-sh/utils/box-frame.js";
|
|
19
17
|
import { palette as p } from "agent-sh/utils/palette.js";
|
|
20
18
|
import type { ExtensionContext } from "agent-sh/types";
|
|
19
|
+
import type { ToolUI } from "agent-sh/agent/types.js";
|
|
21
20
|
|
|
22
|
-
export default function activate(
|
|
21
|
+
export default function activate(ctx: ExtensionContext) {
|
|
23
22
|
let autoApproveWrites = false;
|
|
24
23
|
|
|
24
|
+
// Advise the TUI diff renderer to add permission prompt framing.
|
|
25
|
+
// This replaces the default plain diff box with one that has a warning
|
|
26
|
+
// border and key hints, so only one diff box is shown (not two).
|
|
27
|
+
ctx.advise("tui:render-diff", (next, filePath: string, diff: any, width: number) => {
|
|
28
|
+
const boxW = Math.min(84, width);
|
|
29
|
+
const contentW = boxW - 4;
|
|
30
|
+
const MAX_DISPLAY = 25;
|
|
31
|
+
|
|
32
|
+
const stats = diff.isNewFile
|
|
33
|
+
? `(+${diff.added} lines)`
|
|
34
|
+
: `(+${diff.added} / -${diff.removed})`;
|
|
35
|
+
const title = diff.isNewFile
|
|
36
|
+
? `new: ${filePath} ${stats}`
|
|
37
|
+
: `${filePath} ${stats}`;
|
|
38
|
+
|
|
39
|
+
const diffLines = renderDiff(diff, {
|
|
40
|
+
width: contentW,
|
|
41
|
+
filePath,
|
|
42
|
+
maxLines: MAX_DISPLAY,
|
|
43
|
+
trueColor: true,
|
|
44
|
+
mode: "unified",
|
|
45
|
+
});
|
|
46
|
+
const content = ["", ...diffLines.slice(1), ""];
|
|
47
|
+
|
|
48
|
+
return renderBoxFrame(content, {
|
|
49
|
+
width: boxW,
|
|
50
|
+
style: "rounded",
|
|
51
|
+
borderColor: p.warning,
|
|
52
|
+
title,
|
|
53
|
+
footer: [` ${p.bold}[y] Apply [n] Skip [a] Don't ask again${p.reset}`],
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const { bus } = ctx;
|
|
58
|
+
|
|
25
59
|
bus.onPipeAsync("permission:request", async (payload) => {
|
|
60
|
+
const ui = payload.ui as ToolUI | undefined;
|
|
61
|
+
if (!ui) return payload;
|
|
62
|
+
|
|
26
63
|
switch (payload.kind) {
|
|
27
64
|
case "tool-call":
|
|
28
|
-
return
|
|
65
|
+
return handleToolCall(payload, ui);
|
|
29
66
|
case "file-write": {
|
|
30
67
|
if (autoApproveWrites) {
|
|
31
|
-
return { ...payload, decision: {
|
|
68
|
+
return { ...payload, decision: { outcome: "approved" } };
|
|
32
69
|
}
|
|
33
|
-
const result = await
|
|
34
|
-
if (result.decision.autoApprove) {
|
|
70
|
+
const result = await handleFileWrite(payload, ui);
|
|
71
|
+
if ((result.decision as any).autoApprove) {
|
|
35
72
|
autoApproveWrites = true;
|
|
36
73
|
}
|
|
37
74
|
return result;
|
|
@@ -42,120 +79,69 @@ export default function activate({ bus }: ExtensionContext) {
|
|
|
42
79
|
});
|
|
43
80
|
}
|
|
44
81
|
|
|
45
|
-
async function
|
|
82
|
+
async function handleToolCall(payload: any, ui: ToolUI) {
|
|
46
83
|
const options = payload.metadata.options;
|
|
47
|
-
|
|
84
|
+
|
|
85
|
+
const answer = await ui.custom<"approve" | "approve_all" | "deny">({
|
|
86
|
+
render(width) {
|
|
87
|
+
const boxW = Math.min(84, width);
|
|
88
|
+
return renderBoxFrame(
|
|
89
|
+
[`${p.bold}⚠ ${payload.title}${p.reset}`],
|
|
90
|
+
{
|
|
91
|
+
width: boxW,
|
|
92
|
+
style: "rounded",
|
|
93
|
+
borderColor: p.warning,
|
|
94
|
+
title: "Permission required",
|
|
95
|
+
footer: [` ${p.dim}[y]es / [n]o / [a]llow all${p.reset}`],
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
},
|
|
99
|
+
handleInput(data, done) {
|
|
100
|
+
const ch = data.toLowerCase();
|
|
101
|
+
if (ch === "y") done("approve");
|
|
102
|
+
else if (ch === "a") done("approve_all");
|
|
103
|
+
else if (ch === "n" || ch === "\x1b") done("deny");
|
|
104
|
+
},
|
|
105
|
+
});
|
|
48
106
|
|
|
49
107
|
if (answer === "approve" || answer === "approve_all") {
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
108
|
+
const kind = answer === "approve_all" ? "allow_always" : "allow_once";
|
|
109
|
+
const option = options?.find((o: any) => o.kind === kind)
|
|
110
|
+
?? options?.find((o: any) => o.kind === "allow_once" || o.kind === "allow_always");
|
|
53
111
|
if (option) {
|
|
54
112
|
return { ...payload, decision: { outcome: "selected", optionId: option.optionId } };
|
|
55
113
|
}
|
|
114
|
+
return { ...payload, decision: { outcome: "approved" } };
|
|
56
115
|
}
|
|
57
116
|
return { ...payload, decision: { outcome: "cancelled" } };
|
|
58
117
|
}
|
|
59
118
|
|
|
60
|
-
async function
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
[`${p.bold}⚠ ${title}${p.reset}`],
|
|
79
|
-
{
|
|
80
|
-
width: boxW,
|
|
81
|
-
style: "rounded",
|
|
82
|
-
borderColor: p.warning,
|
|
83
|
-
title: "Permission required",
|
|
84
|
-
footer: [` ${p.dim}[y]es / [n]o / [a]llow all${p.reset}`],
|
|
119
|
+
async function handleFileWrite(payload: any, ui: ToolUI) {
|
|
120
|
+
const answer = await ui.custom<"approve" | "approve_all" | "reject">({
|
|
121
|
+
render(width) {
|
|
122
|
+
const boxW = Math.min(84, width);
|
|
123
|
+
// Just show the prompt actions — the diff itself was already rendered
|
|
124
|
+
// by our advise on "tui:render-diff".
|
|
125
|
+
return renderBoxFrame([], {
|
|
126
|
+
width: boxW,
|
|
127
|
+
style: "rounded",
|
|
128
|
+
borderColor: p.warning,
|
|
129
|
+
footer: [` ${p.bold}[y] Apply [n] Skip [a] Don't ask again${p.reset}`],
|
|
130
|
+
});
|
|
131
|
+
},
|
|
132
|
+
handleInput(data, done) {
|
|
133
|
+
const ch = data.toLowerCase();
|
|
134
|
+
if (ch === "y") done("approve");
|
|
135
|
+
else if (ch === "a") done("approve_all");
|
|
136
|
+
else if (ch === "n" || ch === "\x1b") done("reject");
|
|
85
137
|
},
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
process.stdout.write("\n");
|
|
89
|
-
for (const line of framed) {
|
|
90
|
-
process.stdout.write(line + "\n");
|
|
91
|
-
}
|
|
92
|
-
process.stdout.write(" ");
|
|
93
|
-
|
|
94
|
-
return new Promise((resolve) => {
|
|
95
|
-
const handler = (data) => {
|
|
96
|
-
const ch = data.toString("utf-8").toLowerCase();
|
|
97
|
-
process.stdin.removeListener("data", handler);
|
|
98
|
-
process.stdout.write("\n");
|
|
99
|
-
|
|
100
|
-
if (ch === "y") resolve("approve");
|
|
101
|
-
else if (ch === "a") resolve("approve_all");
|
|
102
|
-
else resolve(null);
|
|
103
|
-
};
|
|
104
|
-
process.stdin.on("data", handler);
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
async function previewDiff(opts) {
|
|
109
|
-
const termW = process.stdout.columns || 80;
|
|
110
|
-
const boxW = Math.min(84, termW);
|
|
111
|
-
const contentW = boxW - 4;
|
|
112
|
-
const MAX_DISPLAY = 25;
|
|
113
|
-
|
|
114
|
-
const stats = opts.diff.isNewFile
|
|
115
|
-
? `(+${opts.diff.added} lines)`
|
|
116
|
-
: `(+${opts.diff.added} / -${opts.diff.removed})`;
|
|
117
|
-
const title = opts.diff.isNewFile
|
|
118
|
-
? `new: ${opts.path} ${stats}`
|
|
119
|
-
: `${opts.path} ${stats}`;
|
|
120
|
-
|
|
121
|
-
const diffLines = renderDiff(opts.diff, {
|
|
122
|
-
width: contentW,
|
|
123
|
-
filePath: opts.path,
|
|
124
|
-
maxLines: MAX_DISPLAY,
|
|
125
|
-
trueColor: true,
|
|
126
|
-
mode: "unified",
|
|
127
|
-
});
|
|
128
|
-
const content = ["", ...diffLines.slice(1), ""];
|
|
129
|
-
|
|
130
|
-
const framed = renderBoxFrame(content, {
|
|
131
|
-
width: boxW,
|
|
132
|
-
style: "rounded",
|
|
133
|
-
borderColor: p.warning,
|
|
134
|
-
title,
|
|
135
|
-
footer: [` ${p.bold}[y] Apply [n] Skip [a] Don't ask again${p.reset}`],
|
|
136
138
|
});
|
|
137
139
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
process.stdout.write(line + "\n");
|
|
140
|
+
if (answer === "approve") {
|
|
141
|
+
return { ...payload, decision: { outcome: "approved" } };
|
|
141
142
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
process.stdin.removeListener("data", handler);
|
|
147
|
-
|
|
148
|
-
if (ch === "y") {
|
|
149
|
-
process.stdout.write(` ${p.success}✓ Applied${p.reset}\n`);
|
|
150
|
-
resolve("approve");
|
|
151
|
-
} else if (ch === "a") {
|
|
152
|
-
process.stdout.write(` ${p.success}✓ Applied (auto-approve on)${p.reset}\n`);
|
|
153
|
-
resolve("approve_all");
|
|
154
|
-
} else {
|
|
155
|
-
process.stdout.write(` ${p.error}✗ Skipped${p.reset}\n`);
|
|
156
|
-
resolve("reject");
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
process.stdin.on("data", handler);
|
|
160
|
-
});
|
|
143
|
+
if (answer === "approve_all") {
|
|
144
|
+
return { ...payload, decision: { outcome: "approved", autoApprove: true } };
|
|
145
|
+
}
|
|
146
|
+
return { ...payload, decision: { outcome: "cancelled" } };
|
|
161
147
|
}
|
|
@@ -5,66 +5,112 @@
|
|
|
5
5
|
* inside vim, htop, or ssh. Composites a floating response box on top
|
|
6
6
|
* of the current terminal content.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
8
|
+
* Uses createRemoteSession() to route the full tui-renderer pipeline
|
|
9
|
+
* (markdown, tool grouping, spinner, diffs) into the floating panel.
|
|
10
|
+
*
|
|
11
|
+
* Install:
|
|
12
|
+
* cp examples/extensions/overlay-agent.ts ~/.agent-sh/extensions/
|
|
9
13
|
*
|
|
10
|
-
*
|
|
14
|
+
* Or load directly:
|
|
11
15
|
* agent-sh -e ./examples/extensions/overlay-agent.ts
|
|
12
16
|
*
|
|
13
|
-
*
|
|
14
|
-
* cp examples/extensions/overlay-agent.ts ~/.agent-sh/extensions/
|
|
17
|
+
* Requires: npm install @xterm/headless@5.5.0 @xterm/addon-serialize@0.13.0
|
|
15
18
|
*/
|
|
16
|
-
import type { ExtensionContext } from "agent-sh/types";
|
|
17
|
-
import {
|
|
19
|
+
import type { ExtensionContext, RemoteSession } from "agent-sh/types";
|
|
20
|
+
import type { RenderSurface } from "agent-sh/utils/compositor";
|
|
21
|
+
import { FloatingPanel } from "agent-sh/utils/floating-panel";
|
|
18
22
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
/** Adapt a FloatingPanel to the RenderSurface interface. */
|
|
24
|
+
function createPanelSurface(panel: FloatingPanel): RenderSurface {
|
|
25
|
+
return {
|
|
26
|
+
write(text: string): void {
|
|
27
|
+
// Handle \r (carriage return) — overwrite the current line.
|
|
28
|
+
// The spinner uses "\r <content>\x1b[K" to update in-place.
|
|
29
|
+
if (text.startsWith("\r")) {
|
|
30
|
+
// Strip \r and any erase-line sequences
|
|
31
|
+
const cleaned = text.replace(/^\r/, "").replace(/\x1b\[\d*K/g, "");
|
|
32
|
+
if (cleaned.trim()) {
|
|
33
|
+
panel.updateLastLine(() => cleaned);
|
|
34
|
+
}
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Regular text — may contain newlines
|
|
39
|
+
panel.appendText(text);
|
|
40
|
+
},
|
|
41
|
+
writeLine(line: string): void {
|
|
42
|
+
panel.appendLine(line);
|
|
43
|
+
},
|
|
44
|
+
get columns(): number {
|
|
45
|
+
return panel.computeGeometry().contentW;
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
22
49
|
|
|
23
|
-
export default function activate(
|
|
24
|
-
const
|
|
50
|
+
export default function activate(ctx: ExtensionContext): void {
|
|
51
|
+
const { bus, registerInstruction, createRemoteSession, terminalBuffer } = ctx;
|
|
52
|
+
|
|
53
|
+
const panel = new FloatingPanel(bus, {
|
|
25
54
|
trigger: "\x1c", // Ctrl+\
|
|
26
55
|
dimBackground: true,
|
|
27
|
-
|
|
56
|
+
terminalBuffer: terminalBuffer ?? undefined,
|
|
28
57
|
});
|
|
29
58
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
59
|
+
const panelSurface = createPanelSurface(panel);
|
|
60
|
+
let session: RemoteSession | null = null;
|
|
61
|
+
|
|
62
|
+
registerInstruction("Interactive Overlay Sessions", [
|
|
63
|
+
"When the dynamic context includes `interactive-session: true`, the user has summoned you",
|
|
64
|
+
"via a hotkey overlay from inside their live terminal. They may be in the middle of using",
|
|
65
|
+
"a program (vim, ssh, a REPL, etc.) or at a shell prompt. In this mode:",
|
|
66
|
+
"- Start with terminal_read if you need to understand what's on screen.",
|
|
67
|
+
"- Prefer terminal_keys to interact with whatever is currently running.",
|
|
68
|
+
"- Use user_shell only for running new, standalone commands — not for interacting with",
|
|
69
|
+
" what's already on screen.",
|
|
70
|
+
"- Keep responses concise — the user is in the middle of a workflow.",
|
|
71
|
+
].join("\n"));
|
|
72
|
+
|
|
73
|
+
// ── Panel lifecycle ────────────────────────────────────────────
|
|
36
74
|
|
|
37
|
-
// ── Panel lifecycle ────────────────────────────────────────
|
|
38
75
|
panel.handlers.advise("panel:submit", (_next, query: string) => {
|
|
76
|
+
if (!session) {
|
|
77
|
+
session = createRemoteSession({
|
|
78
|
+
surface: panelSurface,
|
|
79
|
+
suppressQueryBox: true,
|
|
80
|
+
interactive: true,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
39
83
|
panel.setActive();
|
|
40
|
-
|
|
41
|
-
panel.appendLine("");
|
|
42
|
-
bus.emit("agent:submit", { query });
|
|
84
|
+
session.submit(query);
|
|
43
85
|
});
|
|
44
86
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
87
|
+
panel.handlers.advise("panel:show", (_next) => {
|
|
88
|
+
// Re-establish session if panel is shown while agent is still working
|
|
89
|
+
if (panel.active && !session) {
|
|
90
|
+
session = createRemoteSession({
|
|
91
|
+
surface: panelSurface,
|
|
92
|
+
suppressQueryBox: true,
|
|
93
|
+
interactive: true,
|
|
94
|
+
});
|
|
52
95
|
}
|
|
53
96
|
});
|
|
54
97
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
98
|
+
// On dismiss: close session only if agent is not actively processing.
|
|
99
|
+
// If agent is still working (phase="active"), keep session alive so
|
|
100
|
+
// output buffers in the panel and agent can keep executing tools.
|
|
101
|
+
panel.handlers.advise("panel:dismiss", (next) => {
|
|
102
|
+
next();
|
|
103
|
+
if (session && !panel.processing) {
|
|
104
|
+
session.close();
|
|
105
|
+
session = null;
|
|
106
|
+
}
|
|
64
107
|
});
|
|
65
108
|
|
|
66
109
|
bus.on("agent:processing-done", () => {
|
|
67
110
|
if (!panel.active) return;
|
|
68
111
|
panel.setDone();
|
|
112
|
+
// If panel was hidden while processing (passthrough), setDone()
|
|
113
|
+
// triggers dismiss() which closes the session above.
|
|
114
|
+
// If panel is still visible, session stays for the follow-up prompt.
|
|
69
115
|
});
|
|
70
116
|
}
|