indusagi 0.12.13 → 0.12.16
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/dist/agent/agent-session.d.ts +352 -0
- package/dist/agent/agent-session.d.ts.map +1 -0
- package/dist/agent/agent-session.js +870 -0
- package/dist/agent/agent-session.js.map +1 -0
- package/dist/agent/agent-session.test.d.ts +5 -0
- package/dist/agent/agent-session.test.d.ts.map +1 -0
- package/dist/agent/agent-session.test.js +129 -0
- package/dist/agent/agent-session.test.js.map +1 -0
- package/dist/agent/agent.d.ts +108 -2
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/agent.js +210 -17
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/bash-executor.d.ts +33 -0
- package/dist/agent/bash-executor.d.ts.map +1 -0
- package/dist/agent/bash-executor.js +197 -0
- package/dist/agent/bash-executor.js.map +1 -0
- package/dist/agent/compaction/compaction.d.ts +87 -0
- package/dist/agent/compaction/compaction.d.ts.map +1 -0
- package/dist/agent/compaction/compaction.js +598 -0
- package/dist/agent/compaction/compaction.js.map +1 -0
- package/dist/agent/compaction/index.d.ts +6 -0
- package/dist/agent/compaction/index.d.ts.map +1 -0
- package/dist/agent/compaction/index.js +6 -0
- package/dist/agent/compaction/index.js.map +1 -0
- package/dist/agent/compaction/utils.d.ts +41 -0
- package/dist/agent/compaction/utils.d.ts.map +1 -0
- package/dist/agent/compaction/utils.js +240 -0
- package/dist/agent/compaction/utils.js.map +1 -0
- package/dist/agent/event-bus.js +1 -3
- package/dist/agent/event-bus.js.map +1 -1
- package/dist/agent/index.d.ts +11 -5
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +19 -8
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/messages.d.ts +2 -48
- package/dist/agent/messages.d.ts.map +1 -1
- package/dist/agent/messages.js.map +1 -1
- package/dist/agent/proxy.js.map +1 -1
- package/dist/agent/session-manager.d.ts +2 -3
- package/dist/agent/session-manager.d.ts.map +1 -1
- package/dist/agent/session-manager.js +10 -6
- package/dist/agent/session-manager.js.map +1 -1
- package/dist/agent/settings-manager.d.ts +204 -0
- package/dist/agent/settings-manager.d.ts.map +1 -0
- package/dist/agent/settings-manager.js +594 -0
- package/dist/agent/settings-manager.js.map +1 -0
- package/dist/agent/state-manager.js +1 -0
- package/dist/agent/state-manager.js.map +1 -1
- package/dist/agent/telemetry.js +1 -3
- package/dist/agent/telemetry.js.map +1 -1
- package/dist/agent/tools/bash.js +1 -0
- package/dist/agent/tools/bash.js.map +1 -1
- package/dist/agent/tools/edit.test.d.ts +6 -0
- package/dist/agent/tools/edit.test.d.ts.map +1 -0
- package/dist/agent/tools/edit.test.js +196 -0
- package/dist/agent/tools/edit.test.js.map +1 -0
- package/dist/agent/tools/index.d.ts +26 -26
- package/dist/agent/tools/registry.d.ts +1 -1
- package/dist/agent/tools/registry.d.ts.map +1 -1
- package/dist/agent/tools/registry.js +2 -4
- package/dist/agent/tools/registry.js.map +1 -1
- package/dist/agent/tools/todo-store.js +2 -1
- package/dist/agent/tools/todo-store.js.map +1 -1
- package/dist/agent/tools/utils/hook-runner.js +1 -3
- package/dist/agent/tools/utils/hook-runner.js.map +1 -1
- package/dist/agent/types.d.ts +139 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/ai/api-registry.js +1 -3
- package/dist/ai/api-registry.js.map +1 -1
- package/dist/ai/models.generated.d.ts +621 -53
- package/dist/ai/models.generated.d.ts.map +1 -1
- package/dist/ai/models.generated.js +548 -0
- package/dist/ai/models.generated.js.map +1 -1
- package/dist/ai/models.js +1 -0
- package/dist/ai/models.js.map +1 -1
- package/dist/ai/providers/amazon-bedrock.d.ts +6 -0
- package/dist/ai/providers/amazon-bedrock.d.ts.map +1 -1
- package/dist/ai/providers/amazon-bedrock.js +47 -9
- package/dist/ai/providers/amazon-bedrock.js.map +1 -1
- package/dist/ai/providers/anthropic.d.ts +8 -0
- package/dist/ai/providers/anthropic.d.ts.map +1 -1
- package/dist/ai/providers/anthropic.js +28 -12
- package/dist/ai/providers/anthropic.js.map +1 -1
- package/dist/ai/providers/azure-openai-responses.js +1 -0
- package/dist/ai/providers/azure-openai-responses.js.map +1 -1
- package/dist/ai/providers/github-copilot-headers.d.ts +33 -0
- package/dist/ai/providers/github-copilot-headers.d.ts.map +1 -0
- package/dist/ai/providers/github-copilot-headers.js +51 -0
- package/dist/ai/providers/github-copilot-headers.js.map +1 -0
- package/dist/ai/providers/google-shared.d.ts.map +1 -1
- package/dist/ai/providers/google-shared.js +16 -22
- package/dist/ai/providers/google-shared.js.map +1 -1
- package/dist/ai/providers/google-vertex.d.ts.map +1 -1
- package/dist/ai/providers/google-vertex.js +28 -5
- package/dist/ai/providers/google-vertex.js.map +1 -1
- package/dist/ai/providers/google.js +1 -0
- package/dist/ai/providers/google.js.map +1 -1
- package/dist/ai/providers/kimi.js +1 -0
- package/dist/ai/providers/kimi.js.map +1 -1
- package/dist/ai/providers/mistral.d.ts +22 -0
- package/dist/ai/providers/mistral.d.ts.map +1 -0
- package/dist/ai/providers/mistral.js +497 -0
- package/dist/ai/providers/mistral.js.map +1 -0
- package/dist/ai/providers/openai-codex-responses.js +1 -0
- package/dist/ai/providers/openai-codex-responses.js.map +1 -1
- package/dist/ai/providers/openai-completions.js +2 -0
- package/dist/ai/providers/openai-completions.js.map +1 -1
- package/dist/ai/providers/openai-responses.d.ts.map +1 -1
- package/dist/ai/providers/openai-responses.js +30 -0
- package/dist/ai/providers/openai-responses.js.map +1 -1
- package/dist/ai/providers/register-builtins.d.ts.map +1 -1
- package/dist/ai/providers/register-builtins.js +6 -0
- package/dist/ai/providers/register-builtins.js.map +1 -1
- package/dist/ai/providers/simple-options.js +2 -0
- package/dist/ai/providers/simple-options.js.map +1 -1
- package/dist/ai/providers/transform-messages.js +3 -9
- package/dist/ai/providers/transform-messages.js.map +1 -1
- package/dist/ai/stream.js +1 -3
- package/dist/ai/stream.js.map +1 -1
- package/dist/ai/types.d.ts +34 -0
- package/dist/ai/types.d.ts.map +1 -1
- package/dist/ai/types.js.map +1 -1
- package/dist/ai/utils/base-stream-handler.js +1 -0
- package/dist/ai/utils/base-stream-handler.js.map +1 -1
- package/dist/ai/utils/event-stream.js +9 -4
- package/dist/ai/utils/event-stream.js.map +1 -1
- package/dist/ai/utils/hash.d.ts +6 -0
- package/dist/ai/utils/hash.d.ts.map +1 -0
- package/dist/ai/utils/hash.js +17 -0
- package/dist/ai/utils/hash.js.map +1 -0
- package/dist/ai/utils/index.d.ts +1 -0
- package/dist/ai/utils/index.d.ts.map +1 -1
- package/dist/ai/utils/index.js +1 -0
- package/dist/ai/utils/index.js.map +1 -1
- package/dist/ai/utils/oauth/github-copilot.d.ts.map +1 -1
- package/dist/ai/utils/oauth/github-copilot.js +19 -8
- package/dist/ai/utils/oauth/github-copilot.js.map +1 -1
- package/dist/ai/utils/overflow.d.ts.map +1 -1
- package/dist/ai/utils/overflow.js +4 -0
- package/dist/ai/utils/overflow.js.map +1 -1
- package/dist/ai/utils/provider-adapter.js +9 -0
- package/dist/ai/utils/provider-adapter.js.map +1 -1
- package/dist/ai/utils/provider-client-builder.js +1 -1
- package/dist/ai/utils/provider-client-builder.js.map +1 -1
- package/dist/ai/utils/provider-errors.js +2 -0
- package/dist/ai/utils/provider-errors.js.map +1 -1
- package/dist/ai/utils/stream-event-helper.js +3 -0
- package/dist/ai/utils/stream-event-helper.js.map +1 -1
- package/dist/ai/utils/stream-handler-types.js +5 -0
- package/dist/ai/utils/stream-handler-types.js.map +1 -1
- package/dist/ai/utils/streaming-state-manager.js +4 -2
- package/dist/ai/utils/streaming-state-manager.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/client-pool.d.ts +106 -0
- package/dist/mcp/client-pool.d.ts.map +1 -0
- package/dist/mcp/client-pool.js +234 -0
- package/dist/mcp/client-pool.js.map +1 -0
- package/dist/mcp/client.d.ts +158 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +599 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/config.d.ts +61 -0
- package/dist/mcp/config.d.ts.map +1 -0
- package/dist/mcp/config.js +250 -0
- package/dist/mcp/config.js.map +1 -0
- package/dist/mcp/errors.d.ts +104 -0
- package/dist/mcp/errors.d.ts.map +1 -0
- package/dist/mcp/errors.js +146 -0
- package/dist/mcp/errors.js.map +1 -0
- package/dist/mcp/index.d.ts +56 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +83 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/schema-converter.d.ts +68 -0
- package/dist/mcp/schema-converter.d.ts.map +1 -0
- package/dist/mcp/schema-converter.js +230 -0
- package/dist/mcp/schema-converter.js.map +1 -0
- package/dist/mcp/server.d.ts +111 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +305 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tool-factory.d.ts +63 -0
- package/dist/mcp/tool-factory.d.ts.map +1 -0
- package/dist/mcp/tool-factory.js +228 -0
- package/dist/mcp/tool-factory.js.map +1 -0
- package/dist/mcp/types.d.ts +289 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +8 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/mcp.d.ts +6 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +6 -0
- package/dist/mcp.js.map +1 -0
- package/dist/memory/embedder/base.d.ts +41 -0
- package/dist/memory/embedder/base.d.ts.map +1 -0
- package/dist/memory/embedder/base.js +10 -0
- package/dist/memory/embedder/base.js.map +1 -0
- package/dist/memory/embedder/index.d.ts +8 -0
- package/dist/memory/embedder/index.d.ts.map +1 -0
- package/dist/memory/embedder/index.js +6 -0
- package/dist/memory/embedder/index.js.map +1 -0
- package/dist/memory/embedder/openai.d.ts +35 -0
- package/dist/memory/embedder/openai.d.ts.map +1 -0
- package/dist/memory/embedder/openai.js +109 -0
- package/dist/memory/embedder/openai.js.map +1 -0
- package/dist/memory/index.d.ts +33 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +31 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/memory.d.ts +126 -0
- package/dist/memory/memory.d.ts.map +1 -0
- package/dist/memory/memory.js +285 -0
- package/dist/memory/memory.js.map +1 -0
- package/dist/memory/processors/base.d.ts +42 -0
- package/dist/memory/processors/base.d.ts.map +1 -0
- package/dist/memory/processors/base.js +6 -0
- package/dist/memory/processors/base.js.map +1 -0
- package/dist/memory/processors/index.d.ts +16 -0
- package/dist/memory/processors/index.d.ts.map +1 -0
- package/dist/memory/processors/index.js +18 -0
- package/dist/memory/processors/index.js.map +1 -0
- package/dist/memory/processors/message-history.d.ts +35 -0
- package/dist/memory/processors/message-history.d.ts.map +1 -0
- package/dist/memory/processors/message-history.js +53 -0
- package/dist/memory/processors/message-history.js.map +1 -0
- package/dist/memory/processors/observational-memory/index.d.ts +82 -0
- package/dist/memory/processors/observational-memory/index.d.ts.map +1 -0
- package/dist/memory/processors/observational-memory/index.js +239 -0
- package/dist/memory/processors/observational-memory/index.js.map +1 -0
- package/dist/memory/processors/observational-memory/observer-agent.d.ts +64 -0
- package/dist/memory/processors/observational-memory/observer-agent.d.ts.map +1 -0
- package/dist/memory/processors/observational-memory/observer-agent.js +362 -0
- package/dist/memory/processors/observational-memory/observer-agent.js.map +1 -0
- package/dist/memory/processors/observational-memory/reflector-agent.d.ts +38 -0
- package/dist/memory/processors/observational-memory/reflector-agent.d.ts.map +1 -0
- package/dist/memory/processors/observational-memory/reflector-agent.js +213 -0
- package/dist/memory/processors/observational-memory/reflector-agent.js.map +1 -0
- package/dist/memory/processors/observational-memory/token-counter.d.ts +35 -0
- package/dist/memory/processors/observational-memory/token-counter.d.ts.map +1 -0
- package/dist/memory/processors/observational-memory/token-counter.js +90 -0
- package/dist/memory/processors/observational-memory/token-counter.js.map +1 -0
- package/dist/memory/processors/semantic-recall.d.ts +55 -0
- package/dist/memory/processors/semantic-recall.d.ts.map +1 -0
- package/dist/memory/processors/semantic-recall.js +152 -0
- package/dist/memory/processors/semantic-recall.js.map +1 -0
- package/dist/memory/processors/working-memory.d.ts +41 -0
- package/dist/memory/processors/working-memory.d.ts.map +1 -0
- package/dist/memory/processors/working-memory.js +87 -0
- package/dist/memory/processors/working-memory.js.map +1 -0
- package/dist/memory/storage/base.d.ts +288 -0
- package/dist/memory/storage/base.d.ts.map +1 -0
- package/dist/memory/storage/base.js +209 -0
- package/dist/memory/storage/base.js.map +1 -0
- package/dist/memory/storage/index.d.ts +9 -0
- package/dist/memory/storage/index.d.ts.map +1 -0
- package/dist/memory/storage/index.js +7 -0
- package/dist/memory/storage/index.js.map +1 -0
- package/dist/memory/storage/inmemory.d.ts +93 -0
- package/dist/memory/storage/inmemory.d.ts.map +1 -0
- package/dist/memory/storage/inmemory.js +647 -0
- package/dist/memory/storage/inmemory.js.map +1 -0
- package/dist/memory/tools/working-memory.d.ts +100 -0
- package/dist/memory/tools/working-memory.d.ts.map +1 -0
- package/dist/memory/tools/working-memory.js +237 -0
- package/dist/memory/tools/working-memory.js.map +1 -0
- package/dist/memory/types.d.ts +386 -0
- package/dist/memory/types.d.ts.map +1 -0
- package/dist/memory/types.js +58 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/memory/vector/base.d.ts +145 -0
- package/dist/memory/vector/base.d.ts.map +1 -0
- package/dist/memory/vector/base.js +81 -0
- package/dist/memory/vector/base.js.map +1 -0
- package/dist/memory/vector/index.d.ts +8 -0
- package/dist/memory/vector/index.d.ts.map +1 -0
- package/dist/memory/vector/index.js +7 -0
- package/dist/memory/vector/index.js.map +1 -0
- package/dist/memory/vector/inmemory.d.ts +47 -0
- package/dist/memory/vector/inmemory.d.ts.map +1 -0
- package/dist/memory/vector/inmemory.js +231 -0
- package/dist/memory/vector/inmemory.js.map +1 -0
- package/dist/tui/autocomplete.js +5 -1
- package/dist/tui/autocomplete.js.map +1 -1
- package/dist/tui/components/box.js +9 -1
- package/dist/tui/components/box.js.map +1 -1
- package/dist/tui/components/cancellable-loader.js +3 -4
- package/dist/tui/components/cancellable-loader.js.map +1 -1
- package/dist/tui/components/editor.d.ts +39 -3
- package/dist/tui/components/editor.d.ts.map +1 -1
- package/dist/tui/components/editor.js +162 -133
- package/dist/tui/components/editor.js.map +1 -1
- package/dist/tui/components/image.js +8 -0
- package/dist/tui/components/image.js.map +1 -1
- package/dist/tui/components/input.js +10 -11
- package/dist/tui/components/input.js.map +1 -1
- package/dist/tui/components/loader.js +7 -4
- package/dist/tui/components/loader.js.map +1 -1
- package/dist/tui/components/markdown.js +10 -0
- package/dist/tui/components/markdown.js.map +1 -1
- package/dist/tui/components/select-list.js +8 -4
- package/dist/tui/components/select-list.js.map +1 -1
- package/dist/tui/components/settings-list.js +12 -4
- package/dist/tui/components/settings-list.js.map +1 -1
- package/dist/tui/components/spacer.js +1 -0
- package/dist/tui/components/spacer.js.map +1 -1
- package/dist/tui/components/text.js +5 -0
- package/dist/tui/components/text.js.map +1 -1
- package/dist/tui/components/truncated-text.js +3 -0
- package/dist/tui/components/truncated-text.js.map +1 -1
- package/dist/tui/fuzzy.js +4 -1
- package/dist/tui/fuzzy.js.map +1 -1
- package/dist/tui/keybindings.js +3 -2
- package/dist/tui/keybindings.js.map +1 -1
- package/dist/tui/keys.d.ts +17 -1
- package/dist/tui/keys.d.ts.map +1 -1
- package/dist/tui/keys.js +56 -0
- package/dist/tui/keys.js.map +1 -1
- package/dist/tui/kill-ring.d.ts +28 -0
- package/dist/tui/kill-ring.d.ts.map +1 -0
- package/dist/tui/kill-ring.js +44 -0
- package/dist/tui/kill-ring.js.map +1 -0
- package/dist/tui/stdin-buffer.js +6 -5
- package/dist/tui/stdin-buffer.js.map +1 -1
- package/dist/tui/terminal-image.js +1 -3
- package/dist/tui/terminal-image.js.map +1 -1
- package/dist/tui/terminal.d.ts +13 -0
- package/dist/tui/terminal.d.ts.map +1 -1
- package/dist/tui/terminal.js +70 -7
- package/dist/tui/terminal.js.map +1 -1
- package/dist/tui/tui.d.ts +36 -0
- package/dist/tui/tui.d.ts.map +1 -1
- package/dist/tui/tui.js +101 -29
- package/dist/tui/tui.js.map +1 -1
- package/dist/tui/undo-stack.d.ts +17 -0
- package/dist/tui/undo-stack.d.ts.map +1 -0
- package/dist/tui/undo-stack.js +25 -0
- package/dist/tui/undo-stack.js.map +1 -0
- package/dist/tui/utils.js +12 -16
- package/dist/tui/utils.js.map +1 -1
- package/package.json +32 -2
|
@@ -0,0 +1,870 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentSession - Core orchestration layer for agent lifecycle and session management.
|
|
3
|
+
*
|
|
4
|
+
* This is the main entry point for agent operations, providing:
|
|
5
|
+
* - Event subscription and emission
|
|
6
|
+
* - Model and thinking level management
|
|
7
|
+
* - Compaction and retry logic
|
|
8
|
+
* - Extension system integration
|
|
9
|
+
* - Bash execution
|
|
10
|
+
* - Session management (fork, navigate, export)
|
|
11
|
+
*
|
|
12
|
+
* Adapted from PI-MONO coding-agent (3,044 lines).
|
|
13
|
+
*/
|
|
14
|
+
import { Agent } from "./agent.js";
|
|
15
|
+
import { SessionManager } from "./session-manager.js";
|
|
16
|
+
import { SettingsManager } from "./settings-manager.js";
|
|
17
|
+
import { AgentEventBus } from "./event-bus.js";
|
|
18
|
+
import { compact, prepareCompaction, shouldCompact, isContextOverflow, calculateContextTokens, estimateContextTokens, getLatestCompactionEntry, } from "./compaction/index.js";
|
|
19
|
+
import { executeBashCommand, executeBashWithOperations } from "./bash-executor.js";
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Helper Functions
|
|
22
|
+
// ============================================================================
|
|
23
|
+
function sleep(ms, signal) {
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
if (signal?.aborted) {
|
|
26
|
+
reject(new Error("Aborted"));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const timeout = setTimeout(resolve, ms);
|
|
30
|
+
signal?.addEventListener("abort", () => {
|
|
31
|
+
clearTimeout(timeout);
|
|
32
|
+
reject(new Error("Aborted"));
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
const DEFAULT_THINKING_LEVEL = "medium";
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// AgentSession Class
|
|
39
|
+
// ============================================================================
|
|
40
|
+
export class AgentSession {
|
|
41
|
+
_agent;
|
|
42
|
+
_sessionManager;
|
|
43
|
+
_settingsManager;
|
|
44
|
+
_eventBus;
|
|
45
|
+
_cwd;
|
|
46
|
+
// Event handling
|
|
47
|
+
_sessionEventListeners = new Set();
|
|
48
|
+
_agentEventUnsubscribe;
|
|
49
|
+
// State
|
|
50
|
+
_isStreaming = false;
|
|
51
|
+
_model;
|
|
52
|
+
// Compaction state
|
|
53
|
+
_compactionAbortController;
|
|
54
|
+
_autoCompactionAbortController;
|
|
55
|
+
_overflowRecoveryAttempted = false;
|
|
56
|
+
// Retry state
|
|
57
|
+
_retryAbortController;
|
|
58
|
+
_retryPromise;
|
|
59
|
+
_retryResolve;
|
|
60
|
+
_retryAttempt = 0;
|
|
61
|
+
// Bash state
|
|
62
|
+
_bashAbortController;
|
|
63
|
+
_pendingBashMessages = [];
|
|
64
|
+
// Steering and follow-up
|
|
65
|
+
_steeringMessages = [];
|
|
66
|
+
_followUpMessages = [];
|
|
67
|
+
_pendingNextTurnMessages = [];
|
|
68
|
+
// Tools
|
|
69
|
+
_activeToolNames = new Set(["read", "bash", "edit", "write"]);
|
|
70
|
+
_toolRegistry = new Map();
|
|
71
|
+
_baseToolsOverride;
|
|
72
|
+
constructor(config) {
|
|
73
|
+
this._cwd = config.cwd;
|
|
74
|
+
this._sessionManager = config.sessionManager ?? SessionManager.inMemory(config.cwd);
|
|
75
|
+
this._settingsManager = config.settingsManager ?? SettingsManager.inMemory();
|
|
76
|
+
this._eventBus = config.eventBus ?? new AgentEventBus();
|
|
77
|
+
// Apply settings overrides
|
|
78
|
+
if (config.autoCompact !== undefined) {
|
|
79
|
+
this._settingsManager.setCompactionEnabled(config.autoCompact);
|
|
80
|
+
}
|
|
81
|
+
if (config.autoRetry !== undefined) {
|
|
82
|
+
this._settingsManager.setRetryEnabled(config.autoRetry);
|
|
83
|
+
}
|
|
84
|
+
// Store base tools override
|
|
85
|
+
this._baseToolsOverride = config.tools;
|
|
86
|
+
// Create agent
|
|
87
|
+
this._agent = new Agent({
|
|
88
|
+
initialState: {
|
|
89
|
+
thinkingLevel: config.thinkingLevel ?? this._settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL,
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
// Set initial model
|
|
93
|
+
if (config.model) {
|
|
94
|
+
this._model = config.model;
|
|
95
|
+
this._agent.setModel(config.model);
|
|
96
|
+
}
|
|
97
|
+
// Connect to agent events
|
|
98
|
+
this._reconnectToAgent();
|
|
99
|
+
}
|
|
100
|
+
// =========================================================================
|
|
101
|
+
// Properties
|
|
102
|
+
// =========================================================================
|
|
103
|
+
get agent() {
|
|
104
|
+
return this._agent;
|
|
105
|
+
}
|
|
106
|
+
get sessionManager() {
|
|
107
|
+
return this._sessionManager;
|
|
108
|
+
}
|
|
109
|
+
get settingsManager() {
|
|
110
|
+
return this._settingsManager;
|
|
111
|
+
}
|
|
112
|
+
get eventBus() {
|
|
113
|
+
return this._eventBus;
|
|
114
|
+
}
|
|
115
|
+
get cwd() {
|
|
116
|
+
return this._cwd;
|
|
117
|
+
}
|
|
118
|
+
get sessionFile() {
|
|
119
|
+
return this._sessionManager.getSessionFile();
|
|
120
|
+
}
|
|
121
|
+
get sessionId() {
|
|
122
|
+
return this._sessionManager.getSessionId();
|
|
123
|
+
}
|
|
124
|
+
get model() {
|
|
125
|
+
return this._model;
|
|
126
|
+
}
|
|
127
|
+
get messages() {
|
|
128
|
+
return this._agent.state.messages;
|
|
129
|
+
}
|
|
130
|
+
get state() {
|
|
131
|
+
return this._agent.state;
|
|
132
|
+
}
|
|
133
|
+
get isStreaming() {
|
|
134
|
+
return this._isStreaming;
|
|
135
|
+
}
|
|
136
|
+
get thinkingLevel() {
|
|
137
|
+
return this._agent.state.thinkingLevel;
|
|
138
|
+
}
|
|
139
|
+
get pendingMessageCount() {
|
|
140
|
+
return this._steeringMessages.length + this._followUpMessages.length + this._pendingNextTurnMessages.length;
|
|
141
|
+
}
|
|
142
|
+
// =========================================================================
|
|
143
|
+
// Event Handling
|
|
144
|
+
// =========================================================================
|
|
145
|
+
/**
|
|
146
|
+
* Subscribe to session events.
|
|
147
|
+
*/
|
|
148
|
+
subscribe(callback) {
|
|
149
|
+
this._sessionEventListeners.add(callback);
|
|
150
|
+
return () => this._sessionEventListeners.delete(callback);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Emit a session event to all listeners.
|
|
154
|
+
*/
|
|
155
|
+
_emit(event) {
|
|
156
|
+
for (const listener of this._sessionEventListeners) {
|
|
157
|
+
try {
|
|
158
|
+
listener(event);
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
console.error("Session event listener error:", err);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Connect to agent events.
|
|
167
|
+
*/
|
|
168
|
+
_reconnectToAgent() {
|
|
169
|
+
this._disconnectFromAgent();
|
|
170
|
+
this._agentEventUnsubscribe = this._agent.subscribe((event) => {
|
|
171
|
+
this._handleAgentEvent(event);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Disconnect from agent events.
|
|
176
|
+
*/
|
|
177
|
+
_disconnectFromAgent() {
|
|
178
|
+
if (this._agentEventUnsubscribe) {
|
|
179
|
+
this._agentEventUnsubscribe();
|
|
180
|
+
this._agentEventUnsubscribe = undefined;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Handle agent events.
|
|
185
|
+
*/
|
|
186
|
+
_handleAgentEvent(event) {
|
|
187
|
+
switch (event.type) {
|
|
188
|
+
case "agent_start":
|
|
189
|
+
this._isStreaming = true;
|
|
190
|
+
this._overflowRecoveryAttempted = false;
|
|
191
|
+
break;
|
|
192
|
+
case "agent_end": {
|
|
193
|
+
this._isStreaming = false;
|
|
194
|
+
this._flushPendingBashMessages();
|
|
195
|
+
const messages = this._agent.state.messages;
|
|
196
|
+
const lastMessage = messages[messages.length - 1];
|
|
197
|
+
if (lastMessage && lastMessage.role === "assistant") {
|
|
198
|
+
const assistantMsg = lastMessage;
|
|
199
|
+
// Check for retryable errors
|
|
200
|
+
if (this._isRetryableError(assistantMsg)) {
|
|
201
|
+
this._handleRetryableError(assistantMsg).catch(() => { });
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
// Check for compaction
|
|
205
|
+
this._checkCompaction(assistantMsg).catch(() => { });
|
|
206
|
+
}
|
|
207
|
+
// Resolve retry promise if exists
|
|
208
|
+
this._resolveRetry();
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
case "message_start":
|
|
212
|
+
case "message_update":
|
|
213
|
+
case "message_end":
|
|
214
|
+
case "tool_execution_start":
|
|
215
|
+
case "tool_execution_end":
|
|
216
|
+
// Forward these events to session manager for persistence
|
|
217
|
+
if (event.type === "message_end" && "message" in event) {
|
|
218
|
+
this._sessionManager.appendMessage(event.message);
|
|
219
|
+
}
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// =========================================================================
|
|
224
|
+
// Model Management
|
|
225
|
+
// =========================================================================
|
|
226
|
+
/**
|
|
227
|
+
* Set the current model.
|
|
228
|
+
*/
|
|
229
|
+
async setModel(model) {
|
|
230
|
+
const previousModel = this._model;
|
|
231
|
+
this._model = model;
|
|
232
|
+
this._agent.setModel(model);
|
|
233
|
+
// Save to session
|
|
234
|
+
this._sessionManager.appendModelChange(model.provider, model.id);
|
|
235
|
+
this._emit({
|
|
236
|
+
type: "model_select",
|
|
237
|
+
model,
|
|
238
|
+
previousModel,
|
|
239
|
+
source: "set",
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
// =========================================================================
|
|
243
|
+
// Thinking Level Management
|
|
244
|
+
// =========================================================================
|
|
245
|
+
/**
|
|
246
|
+
* Set thinking level.
|
|
247
|
+
*/
|
|
248
|
+
setThinkingLevel(level) {
|
|
249
|
+
const previousLevel = this._agent.state.thinkingLevel;
|
|
250
|
+
// Clamp to model capabilities
|
|
251
|
+
const availableLevels = this.getAvailableThinkingLevels();
|
|
252
|
+
const effectiveLevel = availableLevels.includes(level)
|
|
253
|
+
? level
|
|
254
|
+
: this._clampThinkingLevel(level, availableLevels);
|
|
255
|
+
this._agent.setThinkingLevel(effectiveLevel);
|
|
256
|
+
this._sessionManager.appendThinkingLevelChange(effectiveLevel);
|
|
257
|
+
this._emit({
|
|
258
|
+
type: "thinking_level_change",
|
|
259
|
+
level: effectiveLevel,
|
|
260
|
+
previousLevel,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Get available thinking levels for current model.
|
|
265
|
+
*/
|
|
266
|
+
getAvailableThinkingLevels() {
|
|
267
|
+
if (!this._model) {
|
|
268
|
+
return ["off", "minimal", "low", "medium", "high"];
|
|
269
|
+
}
|
|
270
|
+
// Check if model supports thinking
|
|
271
|
+
if (!this._model.reasoning) {
|
|
272
|
+
return ["off"];
|
|
273
|
+
}
|
|
274
|
+
return ["off", "minimal", "low", "medium", "high", "xhigh"];
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Cycle to next thinking level.
|
|
278
|
+
*/
|
|
279
|
+
cycleThinkingLevel() {
|
|
280
|
+
const levels = this.getAvailableThinkingLevels();
|
|
281
|
+
const currentIndex = levels.indexOf(this.thinkingLevel);
|
|
282
|
+
const nextIndex = (currentIndex + 1) % levels.length;
|
|
283
|
+
this.setThinkingLevel(levels[nextIndex]);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Check if model supports thinking.
|
|
287
|
+
*/
|
|
288
|
+
supportsThinking() {
|
|
289
|
+
return this._model ? Boolean(this._model.reasoning) : false;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Clamp thinking level to available levels.
|
|
293
|
+
*/
|
|
294
|
+
_clampThinkingLevel(requested, available) {
|
|
295
|
+
if (available.includes(requested))
|
|
296
|
+
return requested;
|
|
297
|
+
const order = ["off", "minimal", "low", "medium", "high", "xhigh"];
|
|
298
|
+
const requestedIndex = order.indexOf(requested);
|
|
299
|
+
// Find closest available level
|
|
300
|
+
for (let i = requestedIndex; i >= 0; i--) {
|
|
301
|
+
if (available.includes(order[i]))
|
|
302
|
+
return order[i];
|
|
303
|
+
}
|
|
304
|
+
return available[0] ?? "off";
|
|
305
|
+
}
|
|
306
|
+
// =========================================================================
|
|
307
|
+
// Queue Mode Management
|
|
308
|
+
// =========================================================================
|
|
309
|
+
/**
|
|
310
|
+
* Set steering message mode.
|
|
311
|
+
*/
|
|
312
|
+
setSteeringMode(mode) {
|
|
313
|
+
this._settingsManager.setSteeringMode(mode);
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Set follow-up message mode.
|
|
317
|
+
*/
|
|
318
|
+
setFollowUpMode(mode) {
|
|
319
|
+
this._settingsManager.setFollowUpMode(mode);
|
|
320
|
+
}
|
|
321
|
+
// =========================================================================
|
|
322
|
+
// Compaction
|
|
323
|
+
// =========================================================================
|
|
324
|
+
/**
|
|
325
|
+
* Manually compact the session context.
|
|
326
|
+
*/
|
|
327
|
+
async compact(customInstructions) {
|
|
328
|
+
this._disconnectFromAgent();
|
|
329
|
+
await this.abort();
|
|
330
|
+
this._compactionAbortController = new AbortController();
|
|
331
|
+
try {
|
|
332
|
+
if (!this.model) {
|
|
333
|
+
throw new Error("No model selected");
|
|
334
|
+
}
|
|
335
|
+
const pathEntries = this.sessionManager.getBranch();
|
|
336
|
+
const settings = this.settingsManager.getCompactionSettings();
|
|
337
|
+
const preparation = prepareCompaction(pathEntries, settings);
|
|
338
|
+
if (!preparation) {
|
|
339
|
+
const lastEntry = pathEntries[pathEntries.length - 1];
|
|
340
|
+
if (lastEntry?.type === "compaction") {
|
|
341
|
+
throw new Error("Already compacted");
|
|
342
|
+
}
|
|
343
|
+
throw new Error("Nothing to compact (session too small)");
|
|
344
|
+
}
|
|
345
|
+
// Get API key (would need ModelRegistry in real implementation)
|
|
346
|
+
const apiKey = process.env[`${this.model.provider.toUpperCase()}_API_KEY`] || "";
|
|
347
|
+
if (!apiKey) {
|
|
348
|
+
throw new Error(`No API key for ${this.model.provider}`);
|
|
349
|
+
}
|
|
350
|
+
const result = await compact(preparation, this.model, apiKey, customInstructions, this._compactionAbortController.signal);
|
|
351
|
+
if (this._compactionAbortController.signal.aborted) {
|
|
352
|
+
throw new Error("Compaction cancelled");
|
|
353
|
+
}
|
|
354
|
+
this.sessionManager.appendCompaction(result.summary, result.firstKeptEntryId, result.tokensBefore, result.details, false);
|
|
355
|
+
const sessionContext = this.sessionManager.buildSessionContext();
|
|
356
|
+
this.agent.replaceMessages(sessionContext.messages);
|
|
357
|
+
return result;
|
|
358
|
+
}
|
|
359
|
+
finally {
|
|
360
|
+
this._compactionAbortController = undefined;
|
|
361
|
+
this._reconnectToAgent();
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Cancel in-progress compaction.
|
|
366
|
+
*/
|
|
367
|
+
abortCompaction() {
|
|
368
|
+
this._compactionAbortController?.abort();
|
|
369
|
+
this._autoCompactionAbortController?.abort();
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Check if compaction is needed and run it.
|
|
373
|
+
*/
|
|
374
|
+
async _checkCompaction(assistantMessage, skipAbortedCheck = true) {
|
|
375
|
+
const settings = this.settingsManager.getCompactionSettings();
|
|
376
|
+
if (!settings.enabled)
|
|
377
|
+
return;
|
|
378
|
+
if (skipAbortedCheck && assistantMessage.stopReason === "aborted")
|
|
379
|
+
return;
|
|
380
|
+
const contextWindow = this.model?.contextWindow ?? 0;
|
|
381
|
+
// Check for overflow
|
|
382
|
+
if (isContextOverflow(assistantMessage, contextWindow)) {
|
|
383
|
+
if (this._overflowRecoveryAttempted) {
|
|
384
|
+
this._emit({
|
|
385
|
+
type: "auto_compaction_end",
|
|
386
|
+
result: undefined,
|
|
387
|
+
aborted: false,
|
|
388
|
+
willRetry: false,
|
|
389
|
+
errorMessage: "Context overflow recovery failed",
|
|
390
|
+
});
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
this._overflowRecoveryAttempted = true;
|
|
394
|
+
const messages = this.agent.state.messages;
|
|
395
|
+
if (messages.length > 0 && messages[messages.length - 1].role === "assistant") {
|
|
396
|
+
this.agent.replaceMessages(messages.slice(0, -1));
|
|
397
|
+
}
|
|
398
|
+
await this._runAutoCompaction("overflow", true);
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
// Check threshold
|
|
402
|
+
const contextTokens = calculateContextTokens(assistantMessage.usage);
|
|
403
|
+
if (shouldCompact(contextTokens, contextWindow, settings)) {
|
|
404
|
+
await this._runAutoCompaction("threshold", false);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Run auto-compaction with events.
|
|
409
|
+
*/
|
|
410
|
+
async _runAutoCompaction(reason, willRetry) {
|
|
411
|
+
this._emit({ type: "auto_compaction_start", reason });
|
|
412
|
+
this._autoCompactionAbortController = new AbortController();
|
|
413
|
+
try {
|
|
414
|
+
if (!this.model) {
|
|
415
|
+
this._emit({ type: "auto_compaction_end", result: undefined, aborted: false, willRetry: false });
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
const apiKey = process.env[`${this.model.provider.toUpperCase()}_API_KEY`] || "";
|
|
419
|
+
if (!apiKey) {
|
|
420
|
+
this._emit({ type: "auto_compaction_end", result: undefined, aborted: false, willRetry: false });
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
const pathEntries = this.sessionManager.getBranch();
|
|
424
|
+
const settings = this.settingsManager.getCompactionSettings();
|
|
425
|
+
const preparation = prepareCompaction(pathEntries, settings);
|
|
426
|
+
if (!preparation) {
|
|
427
|
+
this._emit({ type: "auto_compaction_end", result: undefined, aborted: false, willRetry: false });
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
const result = await compact(preparation, this.model, apiKey, undefined, this._autoCompactionAbortController.signal);
|
|
431
|
+
if (this._autoCompactionAbortController.signal.aborted) {
|
|
432
|
+
this._emit({ type: "auto_compaction_end", result: undefined, aborted: true, willRetry: false });
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
this.sessionManager.appendCompaction(result.summary, result.firstKeptEntryId, result.tokensBefore, result.details, false);
|
|
436
|
+
const sessionContext = this.sessionManager.buildSessionContext();
|
|
437
|
+
this.agent.replaceMessages(sessionContext.messages);
|
|
438
|
+
this._emit({ type: "auto_compaction_end", result, aborted: false, willRetry });
|
|
439
|
+
if (willRetry) {
|
|
440
|
+
setTimeout(() => {
|
|
441
|
+
this.agent.continue().catch(() => { });
|
|
442
|
+
}, 100);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
catch (error) {
|
|
446
|
+
const errorMessage = error instanceof Error ? error.message : "compaction failed";
|
|
447
|
+
this._emit({
|
|
448
|
+
type: "auto_compaction_end",
|
|
449
|
+
result: undefined,
|
|
450
|
+
aborted: false,
|
|
451
|
+
willRetry: false,
|
|
452
|
+
errorMessage,
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
finally {
|
|
456
|
+
this._autoCompactionAbortController = undefined;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Toggle auto-compaction setting.
|
|
461
|
+
*/
|
|
462
|
+
setAutoCompactionEnabled(enabled) {
|
|
463
|
+
this.settingsManager.setCompactionEnabled(enabled);
|
|
464
|
+
}
|
|
465
|
+
get autoCompactionEnabled() {
|
|
466
|
+
return this.settingsManager.getCompactionEnabled();
|
|
467
|
+
}
|
|
468
|
+
// =========================================================================
|
|
469
|
+
// Auto-Retry
|
|
470
|
+
// =========================================================================
|
|
471
|
+
/**
|
|
472
|
+
* Check if an error is retryable.
|
|
473
|
+
*/
|
|
474
|
+
_isRetryableError(message) {
|
|
475
|
+
if (message.stopReason !== "error" || !message.errorMessage)
|
|
476
|
+
return false;
|
|
477
|
+
const contextWindow = this.model?.contextWindow ?? 0;
|
|
478
|
+
if (isContextOverflow(message, contextWindow))
|
|
479
|
+
return false;
|
|
480
|
+
const err = message.errorMessage;
|
|
481
|
+
return /overloaded|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server error|internal error|connection.?error|connection.?refused|fetch failed/i.test(err);
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Handle retryable errors with exponential backoff.
|
|
485
|
+
*/
|
|
486
|
+
async _handleRetryableError(message) {
|
|
487
|
+
const settings = this.settingsManager.getRetrySettings();
|
|
488
|
+
if (!settings.enabled) {
|
|
489
|
+
this._resolveRetry();
|
|
490
|
+
return false;
|
|
491
|
+
}
|
|
492
|
+
if (!this._retryPromise) {
|
|
493
|
+
this._retryPromise = new Promise((resolve) => {
|
|
494
|
+
this._retryResolve = resolve;
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
this._retryAttempt++;
|
|
498
|
+
if (this._retryAttempt > settings.maxRetries) {
|
|
499
|
+
this._emit({
|
|
500
|
+
type: "auto_retry_end",
|
|
501
|
+
success: false,
|
|
502
|
+
attempt: this._retryAttempt - 1,
|
|
503
|
+
finalError: message.errorMessage,
|
|
504
|
+
});
|
|
505
|
+
this._retryAttempt = 0;
|
|
506
|
+
this._resolveRetry();
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
const delayMs = settings.baseDelayMs * 2 ** (this._retryAttempt - 1);
|
|
510
|
+
this._emit({
|
|
511
|
+
type: "auto_retry_start",
|
|
512
|
+
attempt: this._retryAttempt,
|
|
513
|
+
maxAttempts: settings.maxRetries,
|
|
514
|
+
delayMs,
|
|
515
|
+
errorMessage: message.errorMessage || "Unknown error",
|
|
516
|
+
});
|
|
517
|
+
// Remove error message from agent state
|
|
518
|
+
const messages = this.agent.state.messages;
|
|
519
|
+
if (messages.length > 0 && messages[messages.length - 1].role === "assistant") {
|
|
520
|
+
this.agent.replaceMessages(messages.slice(0, -1));
|
|
521
|
+
}
|
|
522
|
+
// Wait with exponential backoff
|
|
523
|
+
this._retryAbortController = new AbortController();
|
|
524
|
+
try {
|
|
525
|
+
await sleep(delayMs, this._retryAbortController.signal);
|
|
526
|
+
}
|
|
527
|
+
catch {
|
|
528
|
+
const attempt = this._retryAttempt;
|
|
529
|
+
this._retryAttempt = 0;
|
|
530
|
+
this._retryAbortController = undefined;
|
|
531
|
+
this._emit({
|
|
532
|
+
type: "auto_retry_end",
|
|
533
|
+
success: false,
|
|
534
|
+
attempt,
|
|
535
|
+
finalError: "Retry cancelled",
|
|
536
|
+
});
|
|
537
|
+
this._resolveRetry();
|
|
538
|
+
return false;
|
|
539
|
+
}
|
|
540
|
+
this._retryAbortController = undefined;
|
|
541
|
+
// Retry via continue()
|
|
542
|
+
setTimeout(() => {
|
|
543
|
+
this.agent.continue().catch(() => { });
|
|
544
|
+
}, 0);
|
|
545
|
+
return true;
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Cancel in-progress retry.
|
|
549
|
+
*/
|
|
550
|
+
abortRetry() {
|
|
551
|
+
this._retryAbortController?.abort();
|
|
552
|
+
this._resolveRetry();
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Resolve retry promise.
|
|
556
|
+
*/
|
|
557
|
+
_resolveRetry() {
|
|
558
|
+
if (this._retryResolve) {
|
|
559
|
+
this._retryResolve();
|
|
560
|
+
this._retryResolve = undefined;
|
|
561
|
+
}
|
|
562
|
+
this._retryPromise = undefined;
|
|
563
|
+
}
|
|
564
|
+
get isRetrying() {
|
|
565
|
+
return this._retryPromise !== undefined;
|
|
566
|
+
}
|
|
567
|
+
get autoRetryEnabled() {
|
|
568
|
+
return this.settingsManager.getRetryEnabled();
|
|
569
|
+
}
|
|
570
|
+
setAutoRetryEnabled(enabled) {
|
|
571
|
+
this.settingsManager.setRetryEnabled(enabled);
|
|
572
|
+
}
|
|
573
|
+
// =========================================================================
|
|
574
|
+
// Bash Execution
|
|
575
|
+
// =========================================================================
|
|
576
|
+
/**
|
|
577
|
+
* Execute a bash command.
|
|
578
|
+
*/
|
|
579
|
+
async executeBash(command, onChunk, options) {
|
|
580
|
+
this._bashAbortController = new AbortController();
|
|
581
|
+
const prefix = this.settingsManager.getShellCommandPrefix();
|
|
582
|
+
const resolvedCommand = prefix ? `${prefix}\n${command}` : command;
|
|
583
|
+
this._emit({ type: "bash_start", command });
|
|
584
|
+
try {
|
|
585
|
+
const result = options?.operations
|
|
586
|
+
? await executeBashWithOperations(resolvedCommand, this._cwd, options.operations, {
|
|
587
|
+
onChunk,
|
|
588
|
+
signal: this._bashAbortController.signal,
|
|
589
|
+
})
|
|
590
|
+
: await executeBashCommand(resolvedCommand, {
|
|
591
|
+
onChunk,
|
|
592
|
+
signal: this._bashAbortController.signal,
|
|
593
|
+
});
|
|
594
|
+
this.recordBashResult(command, result, options);
|
|
595
|
+
this._emit({ type: "bash_end", result });
|
|
596
|
+
return result;
|
|
597
|
+
}
|
|
598
|
+
finally {
|
|
599
|
+
this._bashAbortController = undefined;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Record a bash execution result.
|
|
604
|
+
*/
|
|
605
|
+
recordBashResult(command, result, options) {
|
|
606
|
+
const bashMessage = {
|
|
607
|
+
role: "bashExecution",
|
|
608
|
+
command,
|
|
609
|
+
output: result.output,
|
|
610
|
+
exitCode: result.exitCode,
|
|
611
|
+
cancelled: result.cancelled,
|
|
612
|
+
truncated: result.truncated,
|
|
613
|
+
fullOutputPath: result.fullOutputPath,
|
|
614
|
+
timestamp: Date.now(),
|
|
615
|
+
excludeFromContext: options?.excludeFromContext,
|
|
616
|
+
};
|
|
617
|
+
if (this.isStreaming) {
|
|
618
|
+
this._pendingBashMessages.push(bashMessage);
|
|
619
|
+
}
|
|
620
|
+
else {
|
|
621
|
+
this.agent.appendMessage(bashMessage);
|
|
622
|
+
this.sessionManager.appendMessage(bashMessage);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Cancel running bash command.
|
|
627
|
+
*/
|
|
628
|
+
abortBash() {
|
|
629
|
+
this._bashAbortController?.abort();
|
|
630
|
+
}
|
|
631
|
+
get isBashRunning() {
|
|
632
|
+
return this._bashAbortController !== undefined;
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Flush pending bash messages.
|
|
636
|
+
*/
|
|
637
|
+
_flushPendingBashMessages() {
|
|
638
|
+
if (this._pendingBashMessages.length === 0)
|
|
639
|
+
return;
|
|
640
|
+
for (const bashMessage of this._pendingBashMessages) {
|
|
641
|
+
this.agent.appendMessage(bashMessage);
|
|
642
|
+
this.sessionManager.appendMessage(bashMessage);
|
|
643
|
+
}
|
|
644
|
+
this._pendingBashMessages = [];
|
|
645
|
+
}
|
|
646
|
+
// =========================================================================
|
|
647
|
+
// Session Management
|
|
648
|
+
// =========================================================================
|
|
649
|
+
/**
|
|
650
|
+
* Abort current operation.
|
|
651
|
+
*/
|
|
652
|
+
async abort() {
|
|
653
|
+
this._agent.abort();
|
|
654
|
+
this.abortCompaction();
|
|
655
|
+
this.abortRetry();
|
|
656
|
+
this.abortBash();
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Switch to a different session.
|
|
660
|
+
*/
|
|
661
|
+
async switchSession(sessionPath) {
|
|
662
|
+
this._disconnectFromAgent();
|
|
663
|
+
await this.abort();
|
|
664
|
+
this._steeringMessages = [];
|
|
665
|
+
this._followUpMessages = [];
|
|
666
|
+
this._pendingNextTurnMessages = [];
|
|
667
|
+
const previousSessionFile = this.sessionManager.getSessionFile();
|
|
668
|
+
this.sessionManager.setSessionFile(sessionPath);
|
|
669
|
+
this.agent.sessionId = this.sessionManager.getSessionId();
|
|
670
|
+
const sessionContext = this.sessionManager.buildSessionContext();
|
|
671
|
+
this.agent.replaceMessages(sessionContext.messages);
|
|
672
|
+
this._emit({
|
|
673
|
+
type: "session_switch",
|
|
674
|
+
reason: "resume",
|
|
675
|
+
previousSessionFile,
|
|
676
|
+
});
|
|
677
|
+
this._reconnectToAgent();
|
|
678
|
+
return true;
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Create a new session.
|
|
682
|
+
*/
|
|
683
|
+
newSession(options) {
|
|
684
|
+
this._disconnectFromAgent();
|
|
685
|
+
this._steeringMessages = [];
|
|
686
|
+
this._followUpMessages = [];
|
|
687
|
+
this._pendingNextTurnMessages = [];
|
|
688
|
+
const previousSessionFile = this.sessionManager.getSessionFile();
|
|
689
|
+
this.sessionManager.newSession(options);
|
|
690
|
+
this.agent.sessionId = this.sessionManager.getSessionId();
|
|
691
|
+
this.agent.replaceMessages([]);
|
|
692
|
+
this._reconnectToAgent();
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Set session display name.
|
|
696
|
+
*/
|
|
697
|
+
setSessionName(name) {
|
|
698
|
+
this.sessionManager.appendSessionInfo(name);
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Get session statistics.
|
|
702
|
+
*/
|
|
703
|
+
getSessionStats() {
|
|
704
|
+
const state = this.state;
|
|
705
|
+
const userMessages = state.messages.filter((m) => m.role === "user").length;
|
|
706
|
+
const assistantMessages = state.messages.filter((m) => m.role === "assistant").length;
|
|
707
|
+
const toolResults = state.messages.filter((m) => m.role === "toolResult").length;
|
|
708
|
+
let toolCalls = 0;
|
|
709
|
+
let totalInput = 0;
|
|
710
|
+
let totalOutput = 0;
|
|
711
|
+
let totalCacheRead = 0;
|
|
712
|
+
let totalCacheWrite = 0;
|
|
713
|
+
let totalCost = 0;
|
|
714
|
+
for (const message of state.messages) {
|
|
715
|
+
if (message.role === "assistant") {
|
|
716
|
+
const assistantMsg = message;
|
|
717
|
+
toolCalls += assistantMsg.content.filter((c) => c.type === "toolCall").length;
|
|
718
|
+
totalInput += assistantMsg.usage.input;
|
|
719
|
+
totalOutput += assistantMsg.usage.output;
|
|
720
|
+
totalCacheRead += assistantMsg.usage.cacheRead;
|
|
721
|
+
totalCacheWrite += assistantMsg.usage.cacheWrite;
|
|
722
|
+
totalCost += assistantMsg.usage.cost.total;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
return {
|
|
726
|
+
sessionFile: this.sessionFile,
|
|
727
|
+
sessionId: this.sessionId,
|
|
728
|
+
userMessages,
|
|
729
|
+
assistantMessages,
|
|
730
|
+
toolCalls,
|
|
731
|
+
toolResults,
|
|
732
|
+
totalMessages: state.messages.length,
|
|
733
|
+
tokens: {
|
|
734
|
+
input: totalInput,
|
|
735
|
+
output: totalOutput,
|
|
736
|
+
cacheRead: totalCacheRead,
|
|
737
|
+
cacheWrite: totalCacheWrite,
|
|
738
|
+
total: totalInput + totalOutput + totalCacheRead + totalCacheWrite,
|
|
739
|
+
},
|
|
740
|
+
cost: totalCost,
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Get context usage.
|
|
745
|
+
*/
|
|
746
|
+
getContextUsage() {
|
|
747
|
+
const model = this.model;
|
|
748
|
+
if (!model)
|
|
749
|
+
return undefined;
|
|
750
|
+
const contextWindow = model.contextWindow ?? 0;
|
|
751
|
+
if (contextWindow <= 0)
|
|
752
|
+
return undefined;
|
|
753
|
+
const branchEntries = this.sessionManager.getBranch();
|
|
754
|
+
const latestCompaction = getLatestCompactionEntry(branchEntries);
|
|
755
|
+
if (latestCompaction) {
|
|
756
|
+
const compactionIndex = branchEntries.lastIndexOf(latestCompaction);
|
|
757
|
+
let hasPostCompactionUsage = false;
|
|
758
|
+
for (let i = branchEntries.length - 1; i > compactionIndex; i--) {
|
|
759
|
+
const entry = branchEntries[i];
|
|
760
|
+
if (entry.type === "message" && entry.message.role === "assistant") {
|
|
761
|
+
const assistant = entry.message;
|
|
762
|
+
if (assistant.stopReason !== "aborted" && assistant.stopReason !== "error") {
|
|
763
|
+
hasPostCompactionUsage = true;
|
|
764
|
+
break;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
if (!hasPostCompactionUsage) {
|
|
769
|
+
return { tokens: null, contextWindow, percent: null };
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
const estimate = estimateContextTokens(this.messages);
|
|
773
|
+
const percent = (estimate.tokens / contextWindow) * 100;
|
|
774
|
+
return {
|
|
775
|
+
tokens: estimate.tokens,
|
|
776
|
+
contextWindow,
|
|
777
|
+
percent,
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
// =========================================================================
|
|
781
|
+
// Steering & Follow-up
|
|
782
|
+
// =========================================================================
|
|
783
|
+
/**
|
|
784
|
+
* Send steering message (mid-stream guidance).
|
|
785
|
+
*/
|
|
786
|
+
steer(content, options) {
|
|
787
|
+
this._steeringMessages.push({ content, options });
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Send follow-up message (post-stream continuation).
|
|
791
|
+
*/
|
|
792
|
+
followUp(content, options) {
|
|
793
|
+
this._followUpMessages.push({ content, options });
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* Clear all queued messages.
|
|
797
|
+
*/
|
|
798
|
+
clearQueue() {
|
|
799
|
+
this._steeringMessages = [];
|
|
800
|
+
this._followUpMessages = [];
|
|
801
|
+
this._pendingNextTurnMessages = [];
|
|
802
|
+
}
|
|
803
|
+
getSteeringMessages() {
|
|
804
|
+
return [...this._steeringMessages];
|
|
805
|
+
}
|
|
806
|
+
getFollowUpMessages() {
|
|
807
|
+
return [...this._followUpMessages];
|
|
808
|
+
}
|
|
809
|
+
// =========================================================================
|
|
810
|
+
// Tool Management
|
|
811
|
+
// =========================================================================
|
|
812
|
+
/**
|
|
813
|
+
* Get active tool names.
|
|
814
|
+
*/
|
|
815
|
+
getActiveToolNames() {
|
|
816
|
+
return [...this._activeToolNames];
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Get all available tools.
|
|
820
|
+
*/
|
|
821
|
+
getAllTools() {
|
|
822
|
+
const tools = [];
|
|
823
|
+
for (const [name, tool] of this._toolRegistry) {
|
|
824
|
+
tools.push({ name, description: tool.description });
|
|
825
|
+
}
|
|
826
|
+
return tools;
|
|
827
|
+
}
|
|
828
|
+
/**
|
|
829
|
+
* Set active tools by name.
|
|
830
|
+
*/
|
|
831
|
+
setActiveToolsByName(toolNames) {
|
|
832
|
+
this._activeToolNames = new Set(toolNames);
|
|
833
|
+
this._emit({ type: "tools_changed", activeTools: toolNames });
|
|
834
|
+
}
|
|
835
|
+
// =========================================================================
|
|
836
|
+
// Utilities
|
|
837
|
+
// =========================================================================
|
|
838
|
+
/**
|
|
839
|
+
* Get text content of last assistant message.
|
|
840
|
+
*/
|
|
841
|
+
getLastAssistantText() {
|
|
842
|
+
const lastAssistant = this.messages
|
|
843
|
+
.slice()
|
|
844
|
+
.reverse()
|
|
845
|
+
.find((m) => {
|
|
846
|
+
if (m.role !== "assistant")
|
|
847
|
+
return false;
|
|
848
|
+
const msg = m;
|
|
849
|
+
if (msg.stopReason === "aborted" && msg.content.length === 0)
|
|
850
|
+
return false;
|
|
851
|
+
return true;
|
|
852
|
+
});
|
|
853
|
+
if (!lastAssistant)
|
|
854
|
+
return undefined;
|
|
855
|
+
let text = "";
|
|
856
|
+
for (const content of lastAssistant.content) {
|
|
857
|
+
if (content.type === "text") {
|
|
858
|
+
text += content.text;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
return text.trim() || undefined;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
// ============================================================================
|
|
865
|
+
// Factory Function
|
|
866
|
+
// ============================================================================
|
|
867
|
+
export function createAgentSession(config) {
|
|
868
|
+
return new AgentSession(config);
|
|
869
|
+
}
|
|
870
|
+
//# sourceMappingURL=agent-session.js.map
|