@kolisachint/hoocode-agent 0.4.135 → 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 +12 -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/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
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Completion chime — a short audible cue (terminal BEL) played when an assistant
|
|
3
|
+
* turn finishes after the user has likely stepped away, or when the agent blocks
|
|
4
|
+
* awaiting the user's input (the ask_options pane).
|
|
5
|
+
*
|
|
6
|
+
* Design constraints:
|
|
7
|
+
* - Output-only, zero dependencies: the cue is a single BEL byte written to the
|
|
8
|
+
* terminal. No bundled audio, no subprocesses.
|
|
9
|
+
* - Side-effect-free from the turn's perspective: the ring is wrapped in a
|
|
10
|
+
* try/catch so a failed write can never throw into the turn path. Worst case
|
|
11
|
+
* is no sound.
|
|
12
|
+
*
|
|
13
|
+
* Firing conditions (the caller drives these; this class only decides whether a
|
|
14
|
+
* given signal should actually ring):
|
|
15
|
+
* - Turn-complete: the turn ran longer than {@link CompletionChimeOptions.thresholdMs}
|
|
16
|
+
* (default 10s), measured from the turn's start to when the agent goes truly
|
|
17
|
+
* idle, and the turn was not user-aborted.
|
|
18
|
+
* - Blocked-for-input: the agent opened the ask_options pane. This bypasses the
|
|
19
|
+
* duration threshold — "it needs you" is worth surfacing immediately.
|
|
20
|
+
*
|
|
21
|
+
* Both paths are gated by an enable check and share a single debounce window so
|
|
22
|
+
* rapid successive turns (or a long turn that ends in an ask_options prompt) do
|
|
23
|
+
* not spam the bell.
|
|
24
|
+
*/
|
|
25
|
+
/** Terminal bell (BEL, `\a`). Rings the terminal's configured audible/visual alert. */
|
|
26
|
+
export const BELL = "\x07";
|
|
27
|
+
const DEFAULT_THRESHOLD_MS = 10_000;
|
|
28
|
+
const DEFAULT_DEBOUNCE_MS = 5_000;
|
|
29
|
+
export class CompletionChime {
|
|
30
|
+
isEnabled;
|
|
31
|
+
doRing;
|
|
32
|
+
thresholdMs;
|
|
33
|
+
debounceMs;
|
|
34
|
+
now;
|
|
35
|
+
/** Start of the current turn, or undefined when no turn is in flight. */
|
|
36
|
+
turnStart;
|
|
37
|
+
/** Timestamp of the last chime, for debouncing. */
|
|
38
|
+
lastChimeAt;
|
|
39
|
+
constructor(options) {
|
|
40
|
+
this.isEnabled = options.isEnabled;
|
|
41
|
+
this.doRing = options.ring;
|
|
42
|
+
this.thresholdMs = options.thresholdMs ?? DEFAULT_THRESHOLD_MS;
|
|
43
|
+
this.debounceMs = options.debounceMs ?? DEFAULT_DEBOUNCE_MS;
|
|
44
|
+
this.now = options.now ?? Date.now;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Anchor the start of a turn. Only the first call per turn takes effect, so a
|
|
48
|
+
* turn that internally retries (re-entering agent_start) still measures its
|
|
49
|
+
* duration from the original start.
|
|
50
|
+
*/
|
|
51
|
+
onTurnStart() {
|
|
52
|
+
if (this.turnStart === undefined) {
|
|
53
|
+
this.turnStart = this.now();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The agent has gone truly idle after a turn. Rings if the turn ran longer than
|
|
58
|
+
* the threshold and was not aborted. Clears the turn anchor either way.
|
|
59
|
+
*/
|
|
60
|
+
onTurnComplete(options) {
|
|
61
|
+
const start = this.turnStart;
|
|
62
|
+
this.turnStart = undefined;
|
|
63
|
+
if (options.aborted || start === undefined) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (this.now() - start < this.thresholdMs) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
this.fire();
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The agent is blocked awaiting user input (ask_options pane). Rings
|
|
73
|
+
* immediately, bypassing the duration threshold. Leaves the turn anchor intact
|
|
74
|
+
* — the turn is still in flight and will complete once the user answers.
|
|
75
|
+
*/
|
|
76
|
+
onBlockedForInput() {
|
|
77
|
+
this.fire();
|
|
78
|
+
}
|
|
79
|
+
fire() {
|
|
80
|
+
if (!this.isEnabled()) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const at = this.now();
|
|
84
|
+
if (this.lastChimeAt !== undefined && at - this.lastChimeAt < this.debounceMs) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
this.lastChimeAt = at;
|
|
88
|
+
try {
|
|
89
|
+
this.doRing();
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Side-effect-free: worst case is no sound. Never throw into the turn path.
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=completion-chime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion-chime.js","sourceRoot":"","sources":["../../../src/modes/interactive/completion-chime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,uFAAuF;AACvF,MAAM,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC;AAE3B,MAAM,oBAAoB,GAAG,MAAM,CAAC;AACpC,MAAM,mBAAmB,GAAG,KAAK,CAAC;AAkBlC,MAAM,OAAO,eAAe;IACV,SAAS,CAAgB;IACzB,MAAM,CAAa;IACnB,WAAW,CAAS;IACpB,UAAU,CAAS;IACnB,GAAG,CAAe;IAEnC,yEAAyE;IACjE,SAAS,CAAqB;IACtC,mDAAmD;IAC3C,WAAW,CAAqB;IAExC,YAAY,OAA+B,EAAE;QAC5C,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACnC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;QAC3B,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,oBAAoB,CAAC;QAC/D,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,mBAAmB,CAAC;QAC5D,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IAAA,CACnC;IAED;;;;OAIG;IACH,WAAW,GAAS;QACnB,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YAClC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,CAAC;IAAA,CACD;IAED;;;OAGG;IACH,cAAc,CAAC,OAA6B,EAAQ;QACnD,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC;QAC7B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,OAAO,CAAC,OAAO,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YAC5C,OAAO;QACR,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;YAC3C,OAAO;QACR,CAAC;QACD,IAAI,CAAC,IAAI,EAAE,CAAC;IAAA,CACZ;IAED;;;;OAIG;IACH,iBAAiB,GAAS;QACzB,IAAI,CAAC,IAAI,EAAE,CAAC;IAAA,CACZ;IAEO,IAAI,GAAS;QACpB,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;YACvB,OAAO;QACR,CAAC;QACD,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACtB,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS,IAAI,EAAE,GAAG,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;YAC/E,OAAO;QACR,CAAC;QACD,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC;QACtB,IAAI,CAAC;YACJ,IAAI,CAAC,MAAM,EAAE,CAAC;QACf,CAAC;QAAC,MAAM,CAAC;YACR,4EAA4E;QAC7E,CAAC;IAAA,CACD;CACD","sourcesContent":["/**\n * Completion chime — a short audible cue (terminal BEL) played when an assistant\n * turn finishes after the user has likely stepped away, or when the agent blocks\n * awaiting the user's input (the ask_options pane).\n *\n * Design constraints:\n * - Output-only, zero dependencies: the cue is a single BEL byte written to the\n * terminal. No bundled audio, no subprocesses.\n * - Side-effect-free from the turn's perspective: the ring is wrapped in a\n * try/catch so a failed write can never throw into the turn path. Worst case\n * is no sound.\n *\n * Firing conditions (the caller drives these; this class only decides whether a\n * given signal should actually ring):\n * - Turn-complete: the turn ran longer than {@link CompletionChimeOptions.thresholdMs}\n * (default 10s), measured from the turn's start to when the agent goes truly\n * idle, and the turn was not user-aborted.\n * - Blocked-for-input: the agent opened the ask_options pane. This bypasses the\n * duration threshold — \"it needs you\" is worth surfacing immediately.\n *\n * Both paths are gated by an enable check and share a single debounce window so\n * rapid successive turns (or a long turn that ends in an ask_options prompt) do\n * not spam the bell.\n */\n\n/** Terminal bell (BEL, `\\a`). Rings the terminal's configured audible/visual alert. */\nexport const BELL = \"\\x07\";\n\nconst DEFAULT_THRESHOLD_MS = 10_000;\nconst DEFAULT_DEBOUNCE_MS = 5_000;\n\nexport interface CompletionChimeOptions {\n\t/** Read fresh on every potential ring so live setting changes take effect immediately. */\n\tisEnabled: () => boolean;\n\t/**\n\t * Emit the audible cue. Called inside a try/catch — implementations need not\n\t * guard against throwing, but nothing they throw will reach the turn path.\n\t */\n\tring: () => void;\n\t/** Minimum turn duration before a turn-complete chime fires. Defaults to 10s. */\n\tthresholdMs?: number;\n\t/** Minimum gap between two chimes. Defaults to 5s. */\n\tdebounceMs?: number;\n\t/** Clock, injectable for tests. Defaults to {@link Date.now}. */\n\tnow?: () => number;\n}\n\nexport class CompletionChime {\n\tprivate readonly isEnabled: () => boolean;\n\tprivate readonly doRing: () => void;\n\tprivate readonly thresholdMs: number;\n\tprivate readonly debounceMs: number;\n\tprivate readonly now: () => number;\n\n\t/** Start of the current turn, or undefined when no turn is in flight. */\n\tprivate turnStart: number | undefined;\n\t/** Timestamp of the last chime, for debouncing. */\n\tprivate lastChimeAt: number | undefined;\n\n\tconstructor(options: CompletionChimeOptions) {\n\t\tthis.isEnabled = options.isEnabled;\n\t\tthis.doRing = options.ring;\n\t\tthis.thresholdMs = options.thresholdMs ?? DEFAULT_THRESHOLD_MS;\n\t\tthis.debounceMs = options.debounceMs ?? DEFAULT_DEBOUNCE_MS;\n\t\tthis.now = options.now ?? Date.now;\n\t}\n\n\t/**\n\t * Anchor the start of a turn. Only the first call per turn takes effect, so a\n\t * turn that internally retries (re-entering agent_start) still measures its\n\t * duration from the original start.\n\t */\n\tonTurnStart(): void {\n\t\tif (this.turnStart === undefined) {\n\t\t\tthis.turnStart = this.now();\n\t\t}\n\t}\n\n\t/**\n\t * The agent has gone truly idle after a turn. Rings if the turn ran longer than\n\t * the threshold and was not aborted. Clears the turn anchor either way.\n\t */\n\tonTurnComplete(options: { aborted: boolean }): void {\n\t\tconst start = this.turnStart;\n\t\tthis.turnStart = undefined;\n\t\tif (options.aborted || start === undefined) {\n\t\t\treturn;\n\t\t}\n\t\tif (this.now() - start < this.thresholdMs) {\n\t\t\treturn;\n\t\t}\n\t\tthis.fire();\n\t}\n\n\t/**\n\t * The agent is blocked awaiting user input (ask_options pane). Rings\n\t * immediately, bypassing the duration threshold. Leaves the turn anchor intact\n\t * — the turn is still in flight and will complete once the user answers.\n\t */\n\tonBlockedForInput(): void {\n\t\tthis.fire();\n\t}\n\n\tprivate fire(): void {\n\t\tif (!this.isEnabled()) {\n\t\t\treturn;\n\t\t}\n\t\tconst at = this.now();\n\t\tif (this.lastChimeAt !== undefined && at - this.lastChimeAt < this.debounceMs) {\n\t\t\treturn;\n\t\t}\n\t\tthis.lastChimeAt = at;\n\t\ttry {\n\t\t\tthis.doRing();\n\t\t} catch {\n\t\t\t// Side-effect-free: worst case is no sound. Never throw into the turn path.\n\t\t}\n\t}\n}\n"]}
|
|
@@ -75,6 +75,9 @@ export declare class InteractiveMode {
|
|
|
75
75
|
private retryLoader;
|
|
76
76
|
private retryCountdown;
|
|
77
77
|
private retryEscapeHandler?;
|
|
78
|
+
private chime?;
|
|
79
|
+
private chimePendingRetry;
|
|
80
|
+
private chimeTurnAborted;
|
|
78
81
|
private shutdownRequested;
|
|
79
82
|
private dialogs;
|
|
80
83
|
private extensionTerminalInputUnsubscribers;
|
|
@@ -190,6 +193,14 @@ export declare class InteractiveMode {
|
|
|
190
193
|
private createBuiltInSlashCommands;
|
|
191
194
|
private setupEditorSubmitHandler;
|
|
192
195
|
private subscribeToAgent;
|
|
196
|
+
/**
|
|
197
|
+
* Deferred completion chime. Called from the agent_end handler, it waits one
|
|
198
|
+
* tick so the streaming flag settles and any retry has had a chance to arm, then
|
|
199
|
+
* rings only when the session is genuinely idle (not streaming, not compacting)
|
|
200
|
+
* and no retry is pending. The chime itself enforces the enable/threshold/debounce
|
|
201
|
+
* rules; here we only decide whether the turn has truly ended.
|
|
202
|
+
*/
|
|
203
|
+
private maybeChimeOnIdle;
|
|
193
204
|
private handleEvent;
|
|
194
205
|
/** Extract text content from a user message */
|
|
195
206
|
private getUserMessageText;
|