@kolisachint/hoocode-agent 0.4.134 → 0.4.136
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 +14 -0
- package/dist/core/settings-defaults.d.ts +1 -0
- package/dist/core/settings-defaults.d.ts.map +1 -1
- package/dist/core/settings-defaults.js +1 -0
- package/dist/core/settings-defaults.js.map +1 -1
- package/dist/core/settings-manager.d.ts +2 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +11 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/settings-types.d.ts +1 -0
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/tools/subagent.d.ts.map +1 -1
- package/dist/core/tools/subagent.js +5 -3
- package/dist/core/tools/subagent.js.map +1 -1
- package/dist/modes/interactive/completion-chime.d.ts +74 -0
- package/dist/modes/interactive/completion-chime.d.ts.map +1 -0
- package/dist/modes/interactive/completion-chime.js +96 -0
- package/dist/modes/interactive/completion-chime.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +11 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +55 -1
- 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
|
@@ -25,6 +25,7 @@ import { ensureTool } from "../../utils/tools-manager.js";
|
|
|
25
25
|
import { checkForNewHooCodeVersion } from "../../utils/version-check.js";
|
|
26
26
|
import { BashExecutionController } from "./bash-execution-controller.js";
|
|
27
27
|
import { CommandExecutor } from "./command-executor.js";
|
|
28
|
+
import { BELL, CompletionChime } from "./completion-chime.js";
|
|
28
29
|
import { AssistantMessageComponent } from "./components/assistant-message.js";
|
|
29
30
|
import { BashExecutionComponent } from "./components/bash-execution.js";
|
|
30
31
|
import { BranchSummaryMessageComponent } from "./components/branch-summary-message.js";
|
|
@@ -172,6 +173,14 @@ export class InteractiveMode {
|
|
|
172
173
|
retryLoader = undefined;
|
|
173
174
|
retryCountdown = undefined;
|
|
174
175
|
retryEscapeHandler;
|
|
176
|
+
// Completion chime state
|
|
177
|
+
chime;
|
|
178
|
+
// True between auto_retry_start and the next agent_start: the turn is not done,
|
|
179
|
+
// it is about to re-run, so the deferred completion check must not fire the chime.
|
|
180
|
+
chimePendingRetry = false;
|
|
181
|
+
// Whether the current turn's final assistant message was user-aborted (Ctrl+C /
|
|
182
|
+
// Esc). An aborted turn means the user is present, so no chime.
|
|
183
|
+
chimeTurnAborted = false;
|
|
175
184
|
// Shutdown state
|
|
176
185
|
shutdownRequested = false;
|
|
177
186
|
// Extension UI state
|
|
@@ -394,6 +403,12 @@ export class InteractiveMode {
|
|
|
394
403
|
this.taskPanel.onExitFocus = () => this.teamFocus.exitFocus();
|
|
395
404
|
// Load hide thinking block setting
|
|
396
405
|
this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
|
|
406
|
+
// Completion chime: rings the terminal bell when a long turn finishes or the
|
|
407
|
+
// agent blocks awaiting input. Enable is read fresh so a live toggle applies.
|
|
408
|
+
this.chime = new CompletionChime({
|
|
409
|
+
isEnabled: () => this.settingsManager.getChimeOnTurnComplete(),
|
|
410
|
+
ring: () => this.ui.terminal.write(BELL),
|
|
411
|
+
});
|
|
397
412
|
// Register themes from resource loader and initialize
|
|
398
413
|
setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
|
|
399
414
|
initTheme(this.settingsManager.getTheme(), true);
|
|
@@ -1049,7 +1064,12 @@ export class InteractiveMode {
|
|
|
1049
1064
|
select: (title, options, opts) => this.dialogs.showSelector(title, options, opts),
|
|
1050
1065
|
confirm: (title, message, opts) => this.dialogs.confirm(title, message, opts),
|
|
1051
1066
|
input: (title, placeholder, opts) => this.dialogs.showInput(title, placeholder, opts),
|
|
1052
|
-
askOptions: (questions, opts) =>
|
|
1067
|
+
askOptions: (questions, opts) => {
|
|
1068
|
+
// The agent is blocking on the user (the ask_options pane): ring now,
|
|
1069
|
+
// bypassing the duration threshold — this is the "it needs you" cue.
|
|
1070
|
+
this.chime?.onBlockedForInput();
|
|
1071
|
+
return this.dialogs.showAskOptions(questions, opts);
|
|
1072
|
+
},
|
|
1053
1073
|
notify: (message, type) => this.showExtensionNotify(message, type),
|
|
1054
1074
|
onTerminalInput: (handler) => this.addExtensionTerminalInputListener(handler),
|
|
1055
1075
|
setStatus: (key, text) => this.setExtensionStatus(key, text),
|
|
@@ -1536,6 +1556,21 @@ export class InteractiveMode {
|
|
|
1536
1556
|
await this.handleEvent(event);
|
|
1537
1557
|
});
|
|
1538
1558
|
}
|
|
1559
|
+
/**
|
|
1560
|
+
* Deferred completion chime. Called from the agent_end handler, it waits one
|
|
1561
|
+
* tick so the streaming flag settles and any retry has had a chance to arm, then
|
|
1562
|
+
* rings only when the session is genuinely idle (not streaming, not compacting)
|
|
1563
|
+
* and no retry is pending. The chime itself enforces the enable/threshold/debounce
|
|
1564
|
+
* rules; here we only decide whether the turn has truly ended.
|
|
1565
|
+
*/
|
|
1566
|
+
maybeChimeOnIdle() {
|
|
1567
|
+
setTimeout(() => {
|
|
1568
|
+
if (this.chimePendingRetry || this.session.isStreaming || this.session.isCompacting) {
|
|
1569
|
+
return;
|
|
1570
|
+
}
|
|
1571
|
+
this.chime?.onTurnComplete({ aborted: this.chimeTurnAborted });
|
|
1572
|
+
}, 0);
|
|
1573
|
+
}
|
|
1539
1574
|
async handleEvent(event) {
|
|
1540
1575
|
if (!this.isInitialized) {
|
|
1541
1576
|
await this.init();
|
|
@@ -1544,6 +1579,10 @@ export class InteractiveMode {
|
|
|
1544
1579
|
switch (event.type) {
|
|
1545
1580
|
case "agent_start":
|
|
1546
1581
|
this.pendingTools.clear();
|
|
1582
|
+
// A (possibly retried) turn is (re)starting: anchor its duration on the
|
|
1583
|
+
// first start and clear the pending-retry gate now that it has resumed.
|
|
1584
|
+
this.chime?.onTurnStart();
|
|
1585
|
+
this.chimePendingRetry = false;
|
|
1547
1586
|
if (this.settingsManager.getShowTerminalProgress()) {
|
|
1548
1587
|
this.ui.terminal.setProgress(true);
|
|
1549
1588
|
}
|
|
@@ -1594,6 +1633,7 @@ export class InteractiveMode {
|
|
|
1594
1633
|
// glanceable for the whole turn. Active tasks are kept: a follow-up/steer
|
|
1595
1634
|
// message can arrive while a subagent is still running.
|
|
1596
1635
|
taskStore.reset();
|
|
1636
|
+
this.chimeTurnAborted = false;
|
|
1597
1637
|
this.addMessageToChat(event.message);
|
|
1598
1638
|
this.messageQueue.updatePendingMessagesDisplay();
|
|
1599
1639
|
this.ui.requestRender();
|
|
@@ -1635,6 +1675,11 @@ export class InteractiveMode {
|
|
|
1635
1675
|
case "message_end":
|
|
1636
1676
|
if (event.message.role === "user")
|
|
1637
1677
|
break;
|
|
1678
|
+
if (event.message.role === "assistant") {
|
|
1679
|
+
// Remember whether the turn's latest assistant message was user-aborted,
|
|
1680
|
+
// so the completion chime stays silent when the user is clearly present.
|
|
1681
|
+
this.chimeTurnAborted = event.message.stopReason === "aborted";
|
|
1682
|
+
}
|
|
1638
1683
|
if (this.streamingComponent && event.message.role === "assistant") {
|
|
1639
1684
|
this.streamingMessage = event.message;
|
|
1640
1685
|
let errorMessage;
|
|
@@ -1720,6 +1765,12 @@ export class InteractiveMode {
|
|
|
1720
1765
|
}
|
|
1721
1766
|
this.pendingTools.clear();
|
|
1722
1767
|
await this.checkShutdownRequested();
|
|
1768
|
+
// agent_end fires before the streaming flag clears and before the retry
|
|
1769
|
+
// decision is made (both happen after this listener returns), so defer a
|
|
1770
|
+
// tick and re-check: only chime once the session is genuinely idle and no
|
|
1771
|
+
// retry is pending — otherwise a retried or auto-continued turn would ring
|
|
1772
|
+
// a premature "it's done".
|
|
1773
|
+
this.maybeChimeOnIdle();
|
|
1723
1774
|
this.ui.requestRender();
|
|
1724
1775
|
break;
|
|
1725
1776
|
case "compaction_start": {
|
|
@@ -1782,6 +1833,9 @@ export class InteractiveMode {
|
|
|
1782
1833
|
break;
|
|
1783
1834
|
}
|
|
1784
1835
|
case "auto_retry_start": {
|
|
1836
|
+
// The turn is not done — it will re-run after a backoff (during which the
|
|
1837
|
+
// session reads as idle). Gate the deferred completion chime until then.
|
|
1838
|
+
this.chimePendingRetry = true;
|
|
1785
1839
|
// Set up escape to abort retry
|
|
1786
1840
|
this.retryEscapeHandler = this.defaultEditor.onEscape;
|
|
1787
1841
|
this.defaultEditor.onEscape = () => {
|