@kolisachint/hoocode-agent 0.4.52 → 0.4.54
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 +35 -0
- package/dist/cli/args.d.ts +1 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +4 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/core/keybindings.d.ts +5 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +8 -0
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/team-approvals.d.ts +55 -0
- package/dist/core/team-approvals.d.ts.map +1 -0
- package/dist/core/team-approvals.js +95 -0
- package/dist/core/team-approvals.js.map +1 -0
- package/dist/core/team-auto.d.ts +46 -0
- package/dist/core/team-auto.d.ts.map +1 -0
- package/dist/core/team-auto.js +163 -0
- package/dist/core/team-auto.js.map +1 -0
- package/dist/core/team-view.d.ts +58 -2
- package/dist/core/team-view.d.ts.map +1 -1
- package/dist/core/team-view.js +89 -4
- package/dist/core/team-view.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +28 -7
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/task-panel.d.ts +14 -2
- package/dist/modes/interactive/components/task-panel.d.ts.map +1 -1
- package/dist/modes/interactive/components/task-panel.js +69 -5
- package/dist/modes/interactive/components/task-panel.js.map +1 -1
- package/dist/modes/interactive/components/team-attach-panel.d.ts +60 -0
- package/dist/modes/interactive/components/team-attach-panel.d.ts.map +1 -0
- package/dist/modes/interactive/components/team-attach-panel.js +222 -0
- package/dist/modes/interactive/components/team-attach-panel.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +28 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +147 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
|
@@ -21,6 +21,7 @@ import { formatMissingSessionCwdPrompt, MissingSessionCwdError } from "../../cor
|
|
|
21
21
|
import { SessionManager } from "../../core/session-manager.js";
|
|
22
22
|
import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
|
|
23
23
|
import { taskStore } from "../../core/task-store.js";
|
|
24
|
+
import { TeamApprovalCoordinator } from "../../core/team-approvals.js";
|
|
24
25
|
import { buildCompactWordmark } from "../../core/wordmark.js";
|
|
25
26
|
import { getChangelogPath, getNewEntries, parseChangelog } from "../../utils/changelog.js";
|
|
26
27
|
import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipboard-image.js";
|
|
@@ -53,6 +54,7 @@ import { SessionSelectorComponent } from "./components/session-selector.js";
|
|
|
53
54
|
import { SettingsSelectorComponent } from "./components/settings-selector.js";
|
|
54
55
|
import { SkillInvocationMessageComponent } from "./components/skill-invocation-message.js";
|
|
55
56
|
import { TaskPanelComponent } from "./components/task-panel.js";
|
|
57
|
+
import { TeamAttachPanelComponent } from "./components/team-attach-panel.js";
|
|
56
58
|
import { ToolExecutionComponent } from "./components/tool-execution.js";
|
|
57
59
|
import { TreeSelectorComponent } from "./components/tree-selector.js";
|
|
58
60
|
import { UserMessageComponent } from "./components/user-message.js";
|
|
@@ -184,6 +186,10 @@ export class InteractiveMode {
|
|
|
184
186
|
widgetContainerBelow;
|
|
185
187
|
// Task panel shown just above the editor (active subagent tasks)
|
|
186
188
|
taskPanel;
|
|
189
|
+
// hooteams team client (--team): steering + attach share its single SSE stream
|
|
190
|
+
teamClient = undefined;
|
|
191
|
+
teamAttachPanel = undefined;
|
|
192
|
+
teamAttachHandle = undefined;
|
|
187
193
|
// Custom footer from extension (undefined = use built-in footer)
|
|
188
194
|
customFooter = undefined;
|
|
189
195
|
// Header container that holds the built-in or custom header
|
|
@@ -306,6 +312,9 @@ export class InteractiveMode {
|
|
|
306
312
|
this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
|
|
307
313
|
this.footerDataProvider.setSubagentEnabled(this.session.getActiveToolNames().includes("Task"));
|
|
308
314
|
this.taskPanel = new TaskPanelComponent(this.ui);
|
|
315
|
+
this.taskPanel.onNudge = (role) => this.showTeamNudgeInput(role);
|
|
316
|
+
this.taskPanel.onAttach = (role) => this.showTeamAttach(role);
|
|
317
|
+
this.taskPanel.onExitFocus = () => this.exitTeamFocus();
|
|
309
318
|
// Load hide thinking block setting
|
|
310
319
|
this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
|
|
311
320
|
// Register themes from resource loader and initialize
|
|
@@ -493,6 +502,7 @@ export class InteractiveMode {
|
|
|
493
502
|
hint("app.tools.expand", "to expand tools"),
|
|
494
503
|
hint("app.thinking.toggle", "to expand thinking"),
|
|
495
504
|
hint("app.tasks.cycleView", "to cycle task panel view"),
|
|
505
|
+
...(this.teamClient ? [hint("app.team.focus", "to focus team roster")] : []),
|
|
496
506
|
hint("app.editor.external", "for external editor"),
|
|
497
507
|
rawKeyHint("/", "for commands"),
|
|
498
508
|
rawKeyHint("!", "to run bash"),
|
|
@@ -2069,6 +2079,7 @@ export class InteractiveMode {
|
|
|
2069
2079
|
this.defaultEditor.onAction("app.tasks.cycleView", () => {
|
|
2070
2080
|
this.taskPanel.cycleView();
|
|
2071
2081
|
});
|
|
2082
|
+
this.defaultEditor.onAction("app.team.focus", () => this.enterTeamFocus());
|
|
2072
2083
|
this.defaultEditor.onAction("app.editor.external", () => this.openExternalEditor());
|
|
2073
2084
|
this.defaultEditor.onAction("app.message.followUp", () => this.handleFollowUp());
|
|
2074
2085
|
this.defaultEditor.onAction("app.message.dequeue", () => this.handleDequeue());
|
|
@@ -2109,6 +2120,141 @@ export class InteractiveMode {
|
|
|
2109
2120
|
// Silently ignore clipboard errors (may not have permission, etc.)
|
|
2110
2121
|
}
|
|
2111
2122
|
}
|
|
2123
|
+
// =========================================================================
|
|
2124
|
+
// hooteams team focus (--team): focus role rows, nudge, attach
|
|
2125
|
+
// =========================================================================
|
|
2126
|
+
/**
|
|
2127
|
+
* Wire a hooteams connection into the TUI. Called by main.ts when `--team`
|
|
2128
|
+
* is set, before run(). Enables team focus (app.team.focus), nudging (n),
|
|
2129
|
+
* the attach side panel (a) on the task panel's teams view, and approval
|
|
2130
|
+
* gates: task_paused events (and gates already pending on the server)
|
|
2131
|
+
* surface in the options pane; the answer goes back over
|
|
2132
|
+
* POST /tasks/:id/resume.
|
|
2133
|
+
*/
|
|
2134
|
+
attachTeamClient(client) {
|
|
2135
|
+
this.teamClient = client;
|
|
2136
|
+
const approvals = new TeamApprovalCoordinator({
|
|
2137
|
+
present: (approval, signal) => this.presentTeamApproval(approval, signal),
|
|
2138
|
+
resume: (taskId, option) => client.resume(taskId, option),
|
|
2139
|
+
info: (message) => this.showStatus(message),
|
|
2140
|
+
warn: (message) => this.showWarning(message),
|
|
2141
|
+
});
|
|
2142
|
+
client.subscribe((event) => approvals.handleEvent(event));
|
|
2143
|
+
// Gates that opened before we attached don't replay as task_paused.
|
|
2144
|
+
void client.pendingApprovals().then((pending) => {
|
|
2145
|
+
for (const gate of pending)
|
|
2146
|
+
approvals.enqueuePending(gate);
|
|
2147
|
+
}, () => {
|
|
2148
|
+
// Best-effort like the rest of the bridge; live gates still arrive via SSE.
|
|
2149
|
+
});
|
|
2150
|
+
}
|
|
2151
|
+
/**
|
|
2152
|
+
* Show one team approval gate in the options pane and resolve with the
|
|
2153
|
+
* chosen (or free-form) answer, undefined when skipped. Waits politely
|
|
2154
|
+
* while another ask is on screen; the signal (gate answered elsewhere)
|
|
2155
|
+
* dismisses both the wait and the pane.
|
|
2156
|
+
*/
|
|
2157
|
+
async presentTeamApproval(approval, signal) {
|
|
2158
|
+
while (this.askOptions && !signal.aborted) {
|
|
2159
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
2160
|
+
}
|
|
2161
|
+
if (signal.aborted)
|
|
2162
|
+
return undefined;
|
|
2163
|
+
const answers = await this.showAskOptions([
|
|
2164
|
+
{
|
|
2165
|
+
question: approval.question,
|
|
2166
|
+
short: approval.taskId,
|
|
2167
|
+
detail: `team task "${approval.taskId}"${approval.role ? ` (${approval.role})` : ""} is paused until answered`,
|
|
2168
|
+
options: approval.options.map((label) => ({ label })),
|
|
2169
|
+
allowCustom: true,
|
|
2170
|
+
},
|
|
2171
|
+
], { signal });
|
|
2172
|
+
return answers?.[0];
|
|
2173
|
+
}
|
|
2174
|
+
/** Move keyboard focus to the task panel's team roster. */
|
|
2175
|
+
enterTeamFocus() {
|
|
2176
|
+
if (!this.teamClient) {
|
|
2177
|
+
this.showStatus("No team connected. Start with --team <url> to mirror a hooteams server.");
|
|
2178
|
+
return;
|
|
2179
|
+
}
|
|
2180
|
+
if (!taskStore.agents().some((a) => a.kind === "role")) {
|
|
2181
|
+
this.showStatus("Team roster is empty — waiting for roles from the team server.");
|
|
2182
|
+
return;
|
|
2183
|
+
}
|
|
2184
|
+
this.taskPanel.setView("teams");
|
|
2185
|
+
this.ui.setFocus(this.taskPanel);
|
|
2186
|
+
this.ui.requestRender();
|
|
2187
|
+
}
|
|
2188
|
+
/** Leave team focus: detach any side panel and return focus to the editor. */
|
|
2189
|
+
exitTeamFocus() {
|
|
2190
|
+
this.closeTeamAttach();
|
|
2191
|
+
this.ui.setFocus(this.editor);
|
|
2192
|
+
this.ui.requestRender();
|
|
2193
|
+
}
|
|
2194
|
+
/**
|
|
2195
|
+
* Inline nudge editor for a role (n in team focus or while attached).
|
|
2196
|
+
* Swaps the prompt editor for a one-line input; submit fires POST /steer in
|
|
2197
|
+
* the background (the REPL and team focus are never blocked on the network).
|
|
2198
|
+
*/
|
|
2199
|
+
showTeamNudgeInput(role) {
|
|
2200
|
+
const client = this.teamClient;
|
|
2201
|
+
if (!client)
|
|
2202
|
+
return;
|
|
2203
|
+
const restoreFocus = () => {
|
|
2204
|
+
this.editorContainer.clear();
|
|
2205
|
+
this.editorContainer.addChild(this.editor);
|
|
2206
|
+
// Return focus where the nudge came from: the attach panel if one is
|
|
2207
|
+
// open, otherwise the team roster.
|
|
2208
|
+
if (this.teamAttachHandle)
|
|
2209
|
+
this.teamAttachHandle.focus();
|
|
2210
|
+
else
|
|
2211
|
+
this.ui.setFocus(this.taskPanel);
|
|
2212
|
+
this.ui.requestRender();
|
|
2213
|
+
};
|
|
2214
|
+
const input = new ExtensionInputComponent(`Nudge ${role}`, undefined, (value) => {
|
|
2215
|
+
input.dispose();
|
|
2216
|
+
restoreFocus();
|
|
2217
|
+
const message = value.trim();
|
|
2218
|
+
if (!message)
|
|
2219
|
+
return;
|
|
2220
|
+
void client.steer(role, message).then(() => this.showStatus(`Nudged ${role}`), (error) => this.showWarning(`Failed to nudge ${role}: ${String(error)}`));
|
|
2221
|
+
}, () => {
|
|
2222
|
+
input.dispose();
|
|
2223
|
+
restoreFocus();
|
|
2224
|
+
}, { tui: this.ui });
|
|
2225
|
+
this.editorContainer.clear();
|
|
2226
|
+
this.editorContainer.addChild(input);
|
|
2227
|
+
this.ui.setFocus(input);
|
|
2228
|
+
this.ui.requestRender();
|
|
2229
|
+
}
|
|
2230
|
+
/** Open the attach side panel for a role (a in team focus). */
|
|
2231
|
+
showTeamAttach(role) {
|
|
2232
|
+
const client = this.teamClient;
|
|
2233
|
+
if (!client)
|
|
2234
|
+
return;
|
|
2235
|
+
// One attached role at a time: re-attaching swaps the panel.
|
|
2236
|
+
this.closeTeamAttach();
|
|
2237
|
+
const panel = new TeamAttachPanelComponent(role, client, {
|
|
2238
|
+
onDetach: () => this.closeTeamAttach(),
|
|
2239
|
+
onNudge: (attachedRole) => this.showTeamNudgeInput(attachedRole),
|
|
2240
|
+
}, this.ui);
|
|
2241
|
+
this.teamAttachPanel = panel;
|
|
2242
|
+
// preFocus is the task panel (attach is triggered from team focus), so
|
|
2243
|
+
// hiding the overlay drops the user back on the role roster.
|
|
2244
|
+
this.teamAttachHandle = this.ui.showOverlay(panel, {
|
|
2245
|
+
anchor: "top-right",
|
|
2246
|
+
width: "45%",
|
|
2247
|
+
minWidth: 36,
|
|
2248
|
+
margin: { top: 1, right: 1 },
|
|
2249
|
+
});
|
|
2250
|
+
}
|
|
2251
|
+
/** Detach the side panel; the role keeps running. Safe to call when closed. */
|
|
2252
|
+
closeTeamAttach() {
|
|
2253
|
+
this.teamAttachPanel?.dispose();
|
|
2254
|
+
this.teamAttachHandle?.hide();
|
|
2255
|
+
this.teamAttachPanel = undefined;
|
|
2256
|
+
this.teamAttachHandle = undefined;
|
|
2257
|
+
}
|
|
2112
2258
|
setupEditorSubmitHandler() {
|
|
2113
2259
|
this.defaultEditor.onSubmit = async (text) => {
|
|
2114
2260
|
text = text.trim();
|
|
@@ -2814,6 +2960,7 @@ export class InteractiveMode {
|
|
|
2814
2960
|
// This prevents escape sequences from leaking to the parent shell over slow SSH.
|
|
2815
2961
|
await this.ui.terminal.drainInput(1000);
|
|
2816
2962
|
this.taskStoreUnsubscribe?.();
|
|
2963
|
+
this.closeTeamAttach();
|
|
2817
2964
|
this.taskPanel.dispose();
|
|
2818
2965
|
this.stop();
|
|
2819
2966
|
await this.runtimeHost.dispose();
|