@gencode/agents 0.0.1
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/bootstrap/bootstrap-layout.d.ts +23 -0
- package/dist/bootstrap/bootstrap-layout.d.ts.map +1 -0
- package/dist/bootstrap/bootstrap-layout.js +151 -0
- package/dist/bootstrap/bootstrap-layout.js.map +1 -0
- package/dist/bootstrap/bootstrap.d.ts +36 -0
- package/dist/bootstrap/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap/bootstrap.js +136 -0
- package/dist/bootstrap/bootstrap.js.map +1 -0
- package/dist/bootstrap/templates/AGENTS_MD.d.ts +2 -0
- package/dist/bootstrap/templates/AGENTS_MD.d.ts.map +1 -0
- package/dist/bootstrap/templates/AGENTS_MD.js +223 -0
- package/dist/bootstrap/templates/AGENTS_MD.js.map +1 -0
- package/dist/bootstrap/templates/BOOTSTRAP_MD.d.ts +2 -0
- package/dist/bootstrap/templates/BOOTSTRAP_MD.d.ts.map +1 -0
- package/dist/bootstrap/templates/BOOTSTRAP_MD.js +66 -0
- package/dist/bootstrap/templates/BOOTSTRAP_MD.js.map +1 -0
- package/dist/bootstrap/templates/HEARTBEAT_MD.d.ts +2 -0
- package/dist/bootstrap/templates/HEARTBEAT_MD.d.ts.map +1 -0
- package/dist/bootstrap/templates/HEARTBEAT_MD.js +16 -0
- package/dist/bootstrap/templates/HEARTBEAT_MD.js.map +1 -0
- package/dist/bootstrap/templates/IDENTITY_MD.d.ts +2 -0
- package/dist/bootstrap/templates/IDENTITY_MD.d.ts.map +1 -0
- package/dist/bootstrap/templates/IDENTITY_MD.js +31 -0
- package/dist/bootstrap/templates/IDENTITY_MD.js.map +1 -0
- package/dist/bootstrap/templates/SOUL_MD.d.ts +2 -0
- package/dist/bootstrap/templates/SOUL_MD.d.ts.map +1 -0
- package/dist/bootstrap/templates/SOUL_MD.js +47 -0
- package/dist/bootstrap/templates/SOUL_MD.js.map +1 -0
- package/dist/bootstrap/templates/TOOLS_MD.d.ts +2 -0
- package/dist/bootstrap/templates/TOOLS_MD.d.ts.map +1 -0
- package/dist/bootstrap/templates/TOOLS_MD.js +51 -0
- package/dist/bootstrap/templates/TOOLS_MD.js.map +1 -0
- package/dist/bootstrap/templates/USER_MD.d.ts +2 -0
- package/dist/bootstrap/templates/USER_MD.d.ts.map +1 -0
- package/dist/bootstrap/templates/USER_MD.js +27 -0
- package/dist/bootstrap/templates/USER_MD.js.map +1 -0
- package/dist/bootstrap/templates/index.d.ts +3 -0
- package/dist/bootstrap/templates/index.d.ts.map +1 -0
- package/dist/bootstrap/templates/index.js +26 -0
- package/dist/bootstrap/templates/index.js.map +1 -0
- package/dist/commands/compact.d.ts +28 -0
- package/dist/commands/compact.d.ts.map +1 -0
- package/dist/commands/compact.js +57 -0
- package/dist/commands/compact.js.map +1 -0
- package/dist/commands/help.d.ts +3 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/help.js +50 -0
- package/dist/commands/help.js.map +1 -0
- package/dist/commands/index.d.ts +6 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +32 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/new.d.ts +3 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +4 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/registry.d.ts +11 -0
- package/dist/commands/registry.d.ts.map +1 -0
- package/dist/commands/registry.js +122 -0
- package/dist/commands/registry.js.map +1 -0
- package/dist/commands/reset.d.ts +3 -0
- package/dist/commands/reset.d.ts.map +1 -0
- package/dist/commands/reset.js +4 -0
- package/dist/commands/reset.js.map +1 -0
- package/dist/commands/skill.d.ts +7 -0
- package/dist/commands/skill.d.ts.map +1 -0
- package/dist/commands/skill.js +23 -0
- package/dist/commands/skill.js.map +1 -0
- package/dist/commands/types.d.ts +57 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +2 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/config/agents-config.d.ts +85 -0
- package/dist/config/agents-config.d.ts.map +1 -0
- package/dist/config/agents-config.js +257 -0
- package/dist/config/agents-config.js.map +1 -0
- package/dist/config/index.d.ts +9 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +9 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/types.d.ts +87 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +6 -0
- package/dist/config/types.js.map +1 -0
- package/dist/history/compaction.d.ts +42 -0
- package/dist/history/compaction.d.ts.map +1 -0
- package/dist/history/compaction.js +135 -0
- package/dist/history/compaction.js.map +1 -0
- package/dist/history/index.d.ts +57 -0
- package/dist/history/index.d.ts.map +1 -0
- package/dist/history/index.js +145 -0
- package/dist/history/index.js.map +1 -0
- package/dist/history/limit.d.ts +13 -0
- package/dist/history/limit.d.ts.map +1 -0
- package/dist/history/limit.js +32 -0
- package/dist/history/limit.js.map +1 -0
- package/dist/history/repair.d.ts +13 -0
- package/dist/history/repair.d.ts.map +1 -0
- package/dist/history/repair.js +25 -0
- package/dist/history/repair.js.map +1 -0
- package/dist/history/token-estimate.d.ts +22 -0
- package/dist/history/token-estimate.d.ts.map +1 -0
- package/dist/history/token-estimate.js +38 -0
- package/dist/history/token-estimate.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/client.d.ts +53 -0
- package/dist/llm/client.d.ts.map +1 -0
- package/dist/llm/client.js +214 -0
- package/dist/llm/client.js.map +1 -0
- package/dist/llm/openai-completions-compat.d.ts +23 -0
- package/dist/llm/openai-completions-compat.d.ts.map +1 -0
- package/dist/llm/openai-completions-compat.js +602 -0
- package/dist/llm/openai-completions-compat.js.map +1 -0
- package/dist/llm/openai-stream-error.d.ts +11 -0
- package/dist/llm/openai-stream-error.d.ts.map +1 -0
- package/dist/llm/openai-stream-error.js +77 -0
- package/dist/llm/openai-stream-error.js.map +1 -0
- package/dist/loop-detection/session-state.d.ts +5 -0
- package/dist/loop-detection/session-state.d.ts.map +1 -0
- package/dist/loop-detection/session-state.js +16 -0
- package/dist/loop-detection/session-state.js.map +1 -0
- package/dist/loop-detection/tool-loop-detection.d.ts +122 -0
- package/dist/loop-detection/tool-loop-detection.d.ts.map +1 -0
- package/dist/loop-detection/tool-loop-detection.js +516 -0
- package/dist/loop-detection/tool-loop-detection.js.map +1 -0
- package/dist/memory/builtin-provider.d.ts +5 -0
- package/dist/memory/builtin-provider.d.ts.map +1 -0
- package/dist/memory/builtin-provider.js +42 -0
- package/dist/memory/builtin-provider.js.map +1 -0
- package/dist/memory/config.d.ts +50 -0
- package/dist/memory/config.d.ts.map +1 -0
- package/dist/memory/config.js +46 -0
- package/dist/memory/config.js.map +1 -0
- package/dist/memory/embedding-registry.d.ts +37 -0
- package/dist/memory/embedding-registry.d.ts.map +1 -0
- package/dist/memory/embedding-registry.js +56 -0
- package/dist/memory/embedding-registry.js.map +1 -0
- package/dist/memory/embeddings.d.ts +11 -0
- package/dist/memory/embeddings.d.ts.map +1 -0
- package/dist/memory/embeddings.js +40 -0
- package/dist/memory/embeddings.js.map +1 -0
- package/dist/memory/fs-utils.d.ts +12 -0
- package/dist/memory/fs-utils.d.ts.map +1 -0
- package/dist/memory/fs-utils.js +24 -0
- package/dist/memory/fs-utils.js.map +1 -0
- package/dist/memory/hybrid.d.ts +43 -0
- package/dist/memory/hybrid.d.ts.map +1 -0
- package/dist/memory/hybrid.js +80 -0
- package/dist/memory/hybrid.js.map +1 -0
- package/dist/memory/internal.d.ts +28 -0
- package/dist/memory/internal.d.ts.map +1 -0
- package/dist/memory/internal.js +270 -0
- package/dist/memory/internal.js.map +1 -0
- package/dist/memory/manager-search.d.ts +61 -0
- package/dist/memory/manager-search.d.ts.map +1 -0
- package/dist/memory/manager-search.js +106 -0
- package/dist/memory/manager-search.js.map +1 -0
- package/dist/memory/manager.d.ts +126 -0
- package/dist/memory/manager.d.ts.map +1 -0
- package/dist/memory/manager.js +1006 -0
- package/dist/memory/manager.js.map +1 -0
- package/dist/memory/memory-schema.d.ts +11 -0
- package/dist/memory/memory-schema.d.ts.map +1 -0
- package/dist/memory/memory-schema.js +78 -0
- package/dist/memory/memory-schema.js.map +1 -0
- package/dist/memory/memory.d.ts +46 -0
- package/dist/memory/memory.d.ts.map +1 -0
- package/dist/memory/memory.js +127 -0
- package/dist/memory/memory.js.map +1 -0
- package/dist/memory/mmr.d.ts +22 -0
- package/dist/memory/mmr.d.ts.map +1 -0
- package/dist/memory/mmr.js +112 -0
- package/dist/memory/mmr.js.map +1 -0
- package/dist/memory/provider-registry.d.ts +29 -0
- package/dist/memory/provider-registry.d.ts.map +1 -0
- package/dist/memory/provider-registry.js +58 -0
- package/dist/memory/provider-registry.js.map +1 -0
- package/dist/memory/provider.d.ts +40 -0
- package/dist/memory/provider.d.ts.map +1 -0
- package/dist/memory/provider.js +2 -0
- package/dist/memory/provider.js.map +1 -0
- package/dist/memory/session-files.d.ts +13 -0
- package/dist/memory/session-files.d.ts.map +1 -0
- package/dist/memory/session-files.js +116 -0
- package/dist/memory/session-files.js.map +1 -0
- package/dist/memory/sqlite-vec.d.ts +10 -0
- package/dist/memory/sqlite-vec.d.ts.map +1 -0
- package/dist/memory/sqlite-vec.js +20 -0
- package/dist/memory/sqlite-vec.js.map +1 -0
- package/dist/memory/sqlite.d.ts +2 -0
- package/dist/memory/sqlite.d.ts.map +1 -0
- package/dist/memory/sqlite.js +12 -0
- package/dist/memory/sqlite.js.map +1 -0
- package/dist/memory/temporal-decay.d.ts +26 -0
- package/dist/memory/temporal-decay.d.ts.map +1 -0
- package/dist/memory/temporal-decay.js +119 -0
- package/dist/memory/temporal-decay.js.map +1 -0
- package/dist/memory/watch-bridge.d.ts +20 -0
- package/dist/memory/watch-bridge.d.ts.map +1 -0
- package/dist/memory/watch-bridge.js +128 -0
- package/dist/memory/watch-bridge.js.map +1 -0
- package/dist/plugins/boundary-file-read.d.ts +13 -0
- package/dist/plugins/boundary-file-read.d.ts.map +1 -0
- package/dist/plugins/boundary-file-read.js +31 -0
- package/dist/plugins/boundary-file-read.js.map +1 -0
- package/dist/plugins/config-state.d.ts +35 -0
- package/dist/plugins/config-state.d.ts.map +1 -0
- package/dist/plugins/config-state.js +97 -0
- package/dist/plugins/config-state.js.map +1 -0
- package/dist/plugins/discovery.d.ts +21 -0
- package/dist/plugins/discovery.d.ts.map +1 -0
- package/dist/plugins/discovery.js +425 -0
- package/dist/plugins/discovery.js.map +1 -0
- package/dist/plugins/hooks.d.ts +112 -0
- package/dist/plugins/hooks.d.ts.map +1 -0
- package/dist/plugins/hooks.js +19 -0
- package/dist/plugins/hooks.js.map +1 -0
- package/dist/plugins/index.d.ts +20 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +11 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/loader.d.ts +60 -0
- package/dist/plugins/loader.d.ts.map +1 -0
- package/dist/plugins/loader.js +224 -0
- package/dist/plugins/loader.js.map +1 -0
- package/dist/plugins/manager.d.ts +20 -0
- package/dist/plugins/manager.d.ts.map +1 -0
- package/dist/plugins/manager.js +33 -0
- package/dist/plugins/manager.js.map +1 -0
- package/dist/plugins/manifest-registry.d.ts +15 -0
- package/dist/plugins/manifest-registry.d.ts.map +1 -0
- package/dist/plugins/manifest-registry.js +37 -0
- package/dist/plugins/manifest-registry.js.map +1 -0
- package/dist/plugins/manifest.d.ts +6 -0
- package/dist/plugins/manifest.d.ts.map +1 -0
- package/dist/plugins/manifest.js +80 -0
- package/dist/plugins/manifest.js.map +1 -0
- package/dist/plugins/path-safety.d.ts +6 -0
- package/dist/plugins/path-safety.d.ts.map +1 -0
- package/dist/plugins/path-safety.js +32 -0
- package/dist/plugins/path-safety.js.map +1 -0
- package/dist/plugins/runtime-context.d.ts +12 -0
- package/dist/plugins/runtime-context.d.ts.map +1 -0
- package/dist/plugins/runtime-context.js +2 -0
- package/dist/plugins/runtime-context.js.map +1 -0
- package/dist/plugins/runtime.d.ts +12 -0
- package/dist/plugins/runtime.d.ts.map +1 -0
- package/dist/plugins/runtime.js +13 -0
- package/dist/plugins/runtime.js.map +1 -0
- package/dist/plugins/schema-validator.d.ts +17 -0
- package/dist/plugins/schema-validator.d.ts.map +1 -0
- package/dist/plugins/schema-validator.js +26 -0
- package/dist/plugins/schema-validator.js.map +1 -0
- package/dist/plugins/tool-hooks.d.ts +4 -0
- package/dist/plugins/tool-hooks.d.ts.map +1 -0
- package/dist/plugins/tool-hooks.js +60 -0
- package/dist/plugins/tool-hooks.js.map +1 -0
- package/dist/plugins/tools.d.ts +20 -0
- package/dist/plugins/tools.d.ts.map +1 -0
- package/dist/plugins/tools.js +42 -0
- package/dist/plugins/tools.js.map +1 -0
- package/dist/plugins/types.d.ts +61 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +2 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/plugins/utils.d.ts +4 -0
- package/dist/plugins/utils.d.ts.map +1 -0
- package/dist/plugins/utils.js +22 -0
- package/dist/plugins/utils.js.map +1 -0
- package/dist/runner/runner.d.ts +14 -0
- package/dist/runner/runner.d.ts.map +1 -0
- package/dist/runner/runner.js +893 -0
- package/dist/runner/runner.js.map +1 -0
- package/dist/session/session.d.ts +45 -0
- package/dist/session/session.d.ts.map +1 -0
- package/dist/session/session.js +167 -0
- package/dist/session/session.js.map +1 -0
- package/dist/skills/skills.d.ts +23 -0
- package/dist/skills/skills.d.ts.map +1 -0
- package/dist/skills/skills.js +109 -0
- package/dist/skills/skills.js.map +1 -0
- package/dist/subagent/registry-persist.d.ts +31 -0
- package/dist/subagent/registry-persist.d.ts.map +1 -0
- package/dist/subagent/registry-persist.js +115 -0
- package/dist/subagent/registry-persist.js.map +1 -0
- package/dist/subagent/registry.d.ts +52 -0
- package/dist/subagent/registry.d.ts.map +1 -0
- package/dist/subagent/registry.js +122 -0
- package/dist/subagent/registry.js.map +1 -0
- package/dist/subagent/types.d.ts +20 -0
- package/dist/subagent/types.d.ts.map +1 -0
- package/dist/subagent/types.js +2 -0
- package/dist/subagent/types.js.map +1 -0
- package/dist/system-prompt/builder.d.ts +50 -0
- package/dist/system-prompt/builder.d.ts.map +1 -0
- package/dist/system-prompt/builder.js +335 -0
- package/dist/system-prompt/builder.js.map +1 -0
- package/dist/tools/apply-patch.d.ts +15 -0
- package/dist/tools/apply-patch.d.ts.map +1 -0
- package/dist/tools/apply-patch.js +425 -0
- package/dist/tools/apply-patch.js.map +1 -0
- package/dist/tools/bash.d.ts +11 -0
- package/dist/tools/bash.d.ts.map +1 -0
- package/dist/tools/bash.js +13 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/cron.d.ts +23 -0
- package/dist/tools/cron.d.ts.map +1 -0
- package/dist/tools/cron.js +37 -0
- package/dist/tools/cron.js.map +1 -0
- package/dist/tools/exec.d.ts +18 -0
- package/dist/tools/exec.d.ts.map +1 -0
- package/dist/tools/exec.js +125 -0
- package/dist/tools/exec.js.map +1 -0
- package/dist/tools/files.d.ts +37 -0
- package/dist/tools/files.d.ts.map +1 -0
- package/dist/tools/files.js +191 -0
- package/dist/tools/files.js.map +1 -0
- package/dist/tools/image.d.ts +14 -0
- package/dist/tools/image.d.ts.map +1 -0
- package/dist/tools/image.js +26 -0
- package/dist/tools/image.js.map +1 -0
- package/dist/tools/index.d.ts +35 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +53 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/memory.d.ts +41 -0
- package/dist/tools/memory.d.ts.map +1 -0
- package/dist/tools/memory.js +114 -0
- package/dist/tools/memory.js.map +1 -0
- package/dist/tools/process-registry.d.ts +47 -0
- package/dist/tools/process-registry.d.ts.map +1 -0
- package/dist/tools/process-registry.js +252 -0
- package/dist/tools/process-registry.js.map +1 -0
- package/dist/tools/process.d.ts +17 -0
- package/dist/tools/process.d.ts.map +1 -0
- package/dist/tools/process.js +114 -0
- package/dist/tools/process.js.map +1 -0
- package/dist/tools/subagent-spawn.d.ts +44 -0
- package/dist/tools/subagent-spawn.d.ts.map +1 -0
- package/dist/tools/subagent-spawn.js +100 -0
- package/dist/tools/subagent-spawn.js.map +1 -0
- package/dist/tools/subagents.d.ts +30 -0
- package/dist/tools/subagents.d.ts.map +1 -0
- package/dist/tools/subagents.js +101 -0
- package/dist/tools/subagents.js.map +1 -0
- package/dist/types.d.ts +241 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,893 @@
|
|
|
1
|
+
import { Agent } from "@mariozechner/pi-agent-core";
|
|
2
|
+
import { registerApiProvider, registerBuiltInApiProviders } from "@mariozechner/pi-ai";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import { createSession, ensureSession, loadTranscript, appendTranscriptEntry, saveSessionMetadata } from "../session/session.js";
|
|
5
|
+
import { loadBootstrapFiles, buildBootstrapContextFiles } from "../bootstrap/bootstrap.js";
|
|
6
|
+
import { loadSkillsWithPluginDirs } from "../skills/skills.js";
|
|
7
|
+
import { buildSystemPrompt } from "../system-prompt/builder.js";
|
|
8
|
+
import { createAgentTools } from "../tools/index.js";
|
|
9
|
+
import { createBuiltinMemoryProvider } from "../memory/builtin-provider.js";
|
|
10
|
+
import { resolveMemoryProvider } from "../memory/provider-registry.js";
|
|
11
|
+
import { startMemoryWatchBridge } from "../memory/watch-bridge.js";
|
|
12
|
+
import { SubagentRegistry } from "../subagent/registry.js";
|
|
13
|
+
import { buildSubagentAnnounceMessage } from "../tools/subagent-spawn.js";
|
|
14
|
+
import { manageHistory } from "../history/index.js";
|
|
15
|
+
import { wrapToolsWithLoopDetection, } from "../loop-detection/tool-loop-detection.js";
|
|
16
|
+
import { clearLoopDetectionState, getLoopDetectionState } from "../loop-detection/session-state.js";
|
|
17
|
+
import path from "node:path";
|
|
18
|
+
import { initializePluginSystem } from "../plugins/manager.js";
|
|
19
|
+
import { PluginHookRegistry } from "../plugins/hooks.js";
|
|
20
|
+
import { wrapToolsWithHooks } from "../plugins/tool-hooks.js";
|
|
21
|
+
import { handleSlashCommand, parseResetCommand } from "../commands/index.js";
|
|
22
|
+
import { handleNewCommand } from "../commands/new.js";
|
|
23
|
+
import { handleResetCommand } from "../commands/reset.js";
|
|
24
|
+
import { compactTranscriptNow } from "../commands/compact.js";
|
|
25
|
+
import { LlmRequestError } from "../llm/client.js";
|
|
26
|
+
import { streamOpenAICompletionsCompat, streamSimpleOpenAICompletionsCompat, } from "../llm/openai-completions-compat.js";
|
|
27
|
+
// Register built-in API providers (must be called once at startup)
|
|
28
|
+
registerBuiltInApiProviders();
|
|
29
|
+
registerApiProvider({
|
|
30
|
+
api: "openai-completions",
|
|
31
|
+
stream: streamOpenAICompletionsCompat,
|
|
32
|
+
streamSimple: streamSimpleOpenAICompletionsCompat,
|
|
33
|
+
});
|
|
34
|
+
/** Maximum rounds of subagent announce to prevent infinite delegation loops */
|
|
35
|
+
const MAX_ANNOUNCE_ROUNDS = 10;
|
|
36
|
+
const MEMORY_EVENT_DEDUPE_WINDOW_MS = 1_000;
|
|
37
|
+
const EXTERNAL_WATCH_SUPPRESS_WINDOW_MS = 3_000;
|
|
38
|
+
/** Generates a session title by truncating the first user message */
|
|
39
|
+
export function generateSessionTitle(message, maxLength = 80) {
|
|
40
|
+
const trimmed = message.trim().replace(/\s+/g, " ");
|
|
41
|
+
return trimmed.length <= maxLength ? trimmed : trimmed.slice(0, maxLength - 1) + "…";
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Creates a Model object for use with vLLM/OpenAI-compatible APIs.
|
|
45
|
+
*/
|
|
46
|
+
function createOpenAICompatModel(params) {
|
|
47
|
+
return {
|
|
48
|
+
id: params.model,
|
|
49
|
+
name: params.model,
|
|
50
|
+
api: "openai-completions",
|
|
51
|
+
provider: "openai",
|
|
52
|
+
baseUrl: params.baseUrl,
|
|
53
|
+
reasoning: false,
|
|
54
|
+
input: ["text"],
|
|
55
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
56
|
+
contextWindow: params.contextWindow ?? 200_000,
|
|
57
|
+
maxTokens: 32_768,
|
|
58
|
+
headers: { "Client-Code": "AIMax" },
|
|
59
|
+
compat: {
|
|
60
|
+
supportsStore: false,
|
|
61
|
+
supportsDeveloperRole: false,
|
|
62
|
+
supportsReasoningEffort: false,
|
|
63
|
+
supportsUsageInStreaming: true,
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function normalizeMemoryChangedFiles(files) {
|
|
68
|
+
const result = [];
|
|
69
|
+
const seen = new Set();
|
|
70
|
+
for (const raw of files) {
|
|
71
|
+
const normalized = raw.replace(/\\/g, "/").replace(/^\.\//, "").trim();
|
|
72
|
+
if (!normalized) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const canonical = normalized.toLowerCase() === "memory.md" ? "MEMORY.md" : normalized;
|
|
76
|
+
const key = canonical.toLowerCase();
|
|
77
|
+
if (seen.has(key)) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
seen.add(key);
|
|
81
|
+
result.push(canonical);
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
function isTextRunParams(params) {
|
|
86
|
+
return typeof params.message === "string";
|
|
87
|
+
}
|
|
88
|
+
function serializeUserInput(input) {
|
|
89
|
+
return typeof input === "string" ? input : JSON.stringify(input);
|
|
90
|
+
}
|
|
91
|
+
/** Convert a captured AssistantMessage to an AssistantEntry for the transcript */
|
|
92
|
+
function assistantMessageToEntry(msg) {
|
|
93
|
+
const text = msg.content
|
|
94
|
+
.filter((b) => b.type === "text")
|
|
95
|
+
.map((b) => b.text)
|
|
96
|
+
.join("");
|
|
97
|
+
const toolCalls = msg.content
|
|
98
|
+
.filter((b) => b.type === "toolCall")
|
|
99
|
+
.map((b) => ({ id: b.id, name: b.name, arguments: b.arguments }));
|
|
100
|
+
if (text.trim().length === 0 && toolCalls.length === 0) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
const entry = { role: "assistant", content: text, timestamp: new Date().toISOString() };
|
|
104
|
+
if (toolCalls.length > 0)
|
|
105
|
+
entry.toolCalls = toolCalls;
|
|
106
|
+
return entry;
|
|
107
|
+
}
|
|
108
|
+
/** Convert a captured tool result to a ToolResultEntry for the transcript */
|
|
109
|
+
function capturedToolResultToEntry(tr) {
|
|
110
|
+
return {
|
|
111
|
+
role: "tool_result",
|
|
112
|
+
toolCallId: tr.toolCallId,
|
|
113
|
+
toolName: tr.toolName,
|
|
114
|
+
content: tr.content,
|
|
115
|
+
isError: tr.isError,
|
|
116
|
+
timestamp: new Date().toISOString(),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
// ── Callbacks ─────────────────────────────────────────────────────────────
|
|
120
|
+
/**
|
|
121
|
+
* Sends an HTTP callback to the given URL.
|
|
122
|
+
* Errors are silently ignored to not interfere with the main flow.
|
|
123
|
+
*/
|
|
124
|
+
async function sendCallback(url, payload) {
|
|
125
|
+
try {
|
|
126
|
+
await fetch(url, {
|
|
127
|
+
method: "POST",
|
|
128
|
+
headers: { "content-type": "application/json" },
|
|
129
|
+
body: JSON.stringify(payload),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// Callback failures are non-fatal
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Dispatches a progress event to both the in-process callback and the HTTP callback.
|
|
138
|
+
*/
|
|
139
|
+
async function dispatchProgress(sessionId, event, params) {
|
|
140
|
+
const eventWithMessageId = params.messageId ? { ...event, messageId: params.messageId } : event;
|
|
141
|
+
await params.onProgress?.(eventWithMessageId);
|
|
142
|
+
if (params.callbackUrl) {
|
|
143
|
+
await sendCallback(params.callbackUrl, {
|
|
144
|
+
sessionId,
|
|
145
|
+
channel: params.channel,
|
|
146
|
+
messageId: params.messageId,
|
|
147
|
+
type: "progress",
|
|
148
|
+
event: eventWithMessageId,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// ── Helpers ───────────────────────────────────────────────────────────────
|
|
153
|
+
/**
|
|
154
|
+
* Builds a single announce message from a batch of completed subagent runs.
|
|
155
|
+
* Batching reduces the number of extra LLM turns needed.
|
|
156
|
+
*/
|
|
157
|
+
function buildBatchAnnounceMessage(completed) {
|
|
158
|
+
if (completed.length === 1) {
|
|
159
|
+
const r = completed[0];
|
|
160
|
+
return buildSubagentAnnounceMessage({
|
|
161
|
+
task: r.task,
|
|
162
|
+
label: r.label,
|
|
163
|
+
status: r.status,
|
|
164
|
+
result: r.result,
|
|
165
|
+
error: r.error,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
const parts = completed.map((r) => buildSubagentAnnounceMessage({
|
|
169
|
+
task: r.task,
|
|
170
|
+
label: r.label,
|
|
171
|
+
status: r.status,
|
|
172
|
+
result: r.result,
|
|
173
|
+
error: r.error,
|
|
174
|
+
}));
|
|
175
|
+
return `[${completed.length} subagents completed]\n\n${parts.join("\n\n---\n\n")}`;
|
|
176
|
+
}
|
|
177
|
+
// ── Agent turn runner ─────────────────────────────────────────────────────
|
|
178
|
+
/**
|
|
179
|
+
* Runs one invocation of agent.prompt() and collects text output, usage, and
|
|
180
|
+
* full turn records (via `turn_end` events when the SDK fires them).
|
|
181
|
+
*/
|
|
182
|
+
async function runAgentTurn(agent, message, sessionId, modelId, historyMessages, params, hooks, hookCtx, abortSignal) {
|
|
183
|
+
let responseText = "";
|
|
184
|
+
let inputTokens = 0;
|
|
185
|
+
let outputTokens = 0;
|
|
186
|
+
let runError;
|
|
187
|
+
let sdkError;
|
|
188
|
+
const turnRecords = [];
|
|
189
|
+
const unsubscribe = agent.subscribe(async (event) => {
|
|
190
|
+
if (event.type === "message_update") {
|
|
191
|
+
const assistantEvent = event.assistantMessageEvent;
|
|
192
|
+
if (assistantEvent.type === "text_delta" && typeof assistantEvent.delta === "string") {
|
|
193
|
+
responseText += assistantEvent.delta;
|
|
194
|
+
}
|
|
195
|
+
if (assistantEvent.type === "done") {
|
|
196
|
+
const msg = assistantEvent.message;
|
|
197
|
+
if (msg.usage) {
|
|
198
|
+
inputTokens = msg.usage.input;
|
|
199
|
+
outputTokens = msg.usage.output;
|
|
200
|
+
}
|
|
201
|
+
if (responseText) {
|
|
202
|
+
await dispatchProgress(sessionId, { type: "text", text: responseText }, params);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
else if (event.type === "message_end") {
|
|
207
|
+
const msg = event.message;
|
|
208
|
+
if (msg.usage) {
|
|
209
|
+
inputTokens = msg.usage.input;
|
|
210
|
+
outputTokens = msg.usage.output;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
else if (event.type === "turn_end") {
|
|
214
|
+
// Capture full turn for high-fidelity transcript persistence
|
|
215
|
+
const msg = event.message;
|
|
216
|
+
if (msg.stopReason === "error" || msg.stopReason === "aborted") {
|
|
217
|
+
sdkError = msg.errorMessage ?? (msg.stopReason === "aborted" ? "aborted" : "Unknown LLM error");
|
|
218
|
+
}
|
|
219
|
+
const rawResults = (event.toolResults ?? []);
|
|
220
|
+
const toolResults = rawResults.map((r) => ({
|
|
221
|
+
toolCallId: r.toolCallId,
|
|
222
|
+
toolName: r.toolName,
|
|
223
|
+
content: r.content
|
|
224
|
+
.filter((c) => c.type === "text")
|
|
225
|
+
.map((c) => c.text ?? "")
|
|
226
|
+
.join(""),
|
|
227
|
+
isError: r.isError,
|
|
228
|
+
}));
|
|
229
|
+
turnRecords.push({ message: msg, toolResults });
|
|
230
|
+
}
|
|
231
|
+
else if (event.type === "tool_execution_start") {
|
|
232
|
+
await dispatchProgress(sessionId, { type: "tool_start", name: event.toolName, input: event.args }, params);
|
|
233
|
+
}
|
|
234
|
+
else if (event.type === "tool_execution_end") {
|
|
235
|
+
const output = typeof event.result === "string" ? event.result : JSON.stringify(event.result);
|
|
236
|
+
await dispatchProgress(sessionId, { type: "tool_end", name: event.toolName, output, isError: event.isError }, params);
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
try {
|
|
240
|
+
await hooks.dispatch("llm_input", {
|
|
241
|
+
sessionId,
|
|
242
|
+
model: modelId,
|
|
243
|
+
prompt: typeof message === "string" ? message : JSON.stringify(message),
|
|
244
|
+
historyMessages,
|
|
245
|
+
}, hookCtx);
|
|
246
|
+
if (abortSignal?.aborted) {
|
|
247
|
+
throw new Error("aborted");
|
|
248
|
+
}
|
|
249
|
+
if (typeof message === "string") {
|
|
250
|
+
await agent.prompt(message);
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
await agent.prompt(message);
|
|
254
|
+
}
|
|
255
|
+
if (!runError) {
|
|
256
|
+
const stateError = typeof agent.state.error === "string" ? agent.state.error : undefined;
|
|
257
|
+
const latentError = sdkError ?? stateError;
|
|
258
|
+
if (latentError) {
|
|
259
|
+
runError = formatRunError(latentError);
|
|
260
|
+
await dispatchProgress(sessionId, { type: "error", message: runError }, params);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
catch (err) {
|
|
265
|
+
runError = formatRunError(err);
|
|
266
|
+
await dispatchProgress(sessionId, { type: "error", message: runError }, params);
|
|
267
|
+
}
|
|
268
|
+
finally {
|
|
269
|
+
unsubscribe();
|
|
270
|
+
}
|
|
271
|
+
const lastAssistant = turnRecords.length > 0 ? turnRecords[turnRecords.length - 1]?.message : undefined;
|
|
272
|
+
await hooks.dispatch("llm_output", {
|
|
273
|
+
sessionId,
|
|
274
|
+
model: modelId,
|
|
275
|
+
assistantTexts: responseText ? [responseText] : [],
|
|
276
|
+
lastAssistant,
|
|
277
|
+
usage: { input: inputTokens, output: outputTokens, total: inputTokens + outputTokens },
|
|
278
|
+
}, hookCtx);
|
|
279
|
+
return { text: responseText, inputTokens, outputTokens, error: runError, turnRecords };
|
|
280
|
+
}
|
|
281
|
+
function formatRunError(error) {
|
|
282
|
+
const llmError = toLlmRequestErrorLike(error);
|
|
283
|
+
if (llmError) {
|
|
284
|
+
if (llmError.code === "http_error" && llmError.statusCode) {
|
|
285
|
+
return llmError.providerMessage
|
|
286
|
+
? `LLM upstream returned HTTP ${llmError.statusCode}${llmError.statusText ? ` ${llmError.statusText}` : ""}: ${llmError.providerMessage}`
|
|
287
|
+
: `LLM upstream returned HTTP ${llmError.statusCode}${llmError.statusText ? ` ${llmError.statusText}` : ""}`;
|
|
288
|
+
}
|
|
289
|
+
if (llmError.code === "timeout") {
|
|
290
|
+
return "LLM request timed out";
|
|
291
|
+
}
|
|
292
|
+
if (llmError.code === "network_error") {
|
|
293
|
+
return llmError.message;
|
|
294
|
+
}
|
|
295
|
+
if (llmError.code === "aborted") {
|
|
296
|
+
return "LLM request was aborted";
|
|
297
|
+
}
|
|
298
|
+
return llmError.message;
|
|
299
|
+
}
|
|
300
|
+
return error instanceof Error ? error.message : String(error);
|
|
301
|
+
}
|
|
302
|
+
function toLlmRequestErrorLike(error) {
|
|
303
|
+
if (error instanceof LlmRequestError) {
|
|
304
|
+
return error;
|
|
305
|
+
}
|
|
306
|
+
if (!(error instanceof Error)) {
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
const candidate = error;
|
|
310
|
+
if (error.name !== "LlmRequestError" && !candidate.code) {
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
return {
|
|
314
|
+
message: error.message,
|
|
315
|
+
code: candidate.code,
|
|
316
|
+
statusCode: candidate.statusCode,
|
|
317
|
+
statusText: candidate.statusText,
|
|
318
|
+
providerMessage: candidate.providerMessage,
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Persist the assistant's response to the transcript.
|
|
323
|
+
* Uses turn_end records (with tool calls/results) when available;
|
|
324
|
+
* falls back to a plain text entry otherwise.
|
|
325
|
+
*/
|
|
326
|
+
async function persistTurnEntries(dataDir, sessionId, turn, options) {
|
|
327
|
+
if (turn.turnRecords.length > 0) {
|
|
328
|
+
for (const record of turn.turnRecords) {
|
|
329
|
+
const assistantEntry = assistantMessageToEntry(record.message);
|
|
330
|
+
if (assistantEntry) {
|
|
331
|
+
await appendTranscriptEntry(dataDir, sessionId, assistantEntry, options);
|
|
332
|
+
}
|
|
333
|
+
for (const tr of record.toolResults) {
|
|
334
|
+
await appendTranscriptEntry(dataDir, sessionId, capturedToolResultToEntry(tr), options);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
else if (turn.text) {
|
|
339
|
+
await appendTranscriptEntry(dataDir, sessionId, {
|
|
340
|
+
role: "assistant",
|
|
341
|
+
content: turn.text,
|
|
342
|
+
timestamp: new Date().toISOString(),
|
|
343
|
+
}, options);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
// ── Internal runner ───────────────────────────────────────────────────────
|
|
347
|
+
/**
|
|
348
|
+
* Internal runner shared by both top-level and subagent invocations.
|
|
349
|
+
*/
|
|
350
|
+
async function runAgentInternal(params, registry) {
|
|
351
|
+
const startTime = Date.now();
|
|
352
|
+
const inputMode = isTextRunParams(params) ? "text" : "messages";
|
|
353
|
+
const rawMessage = inputMode === "text" ? params.message : undefined;
|
|
354
|
+
const inputMessages = inputMode === "messages" ? params.messages : undefined;
|
|
355
|
+
const resetCommand = rawMessage ? parseResetCommand(rawMessage) : null;
|
|
356
|
+
const resetRemainder = resetCommand?.remainder ?? "";
|
|
357
|
+
const resetShortCircuit = Boolean(resetCommand && !resetRemainder);
|
|
358
|
+
const messageForRun = resetCommand && resetRemainder ? resetRemainder : rawMessage;
|
|
359
|
+
const promptInput = inputMode === "text" ? (messageForRun ?? "") : inputMessages;
|
|
360
|
+
let transcriptMessage = inputMode === "text"
|
|
361
|
+
? (resetShortCircuit ? rawMessage : (messageForRun ?? ""))
|
|
362
|
+
: serializeUserInput(inputMessages);
|
|
363
|
+
const previousSessionId = resetCommand ? params.sessionId : undefined;
|
|
364
|
+
const workspaceDir = path.join(params.dataDir, "workspace");
|
|
365
|
+
// 1. Resolve or create session
|
|
366
|
+
let sessionId = resetCommand ? undefined : params.sessionId;
|
|
367
|
+
const isNewSession = !sessionId;
|
|
368
|
+
if (sessionId) {
|
|
369
|
+
await ensureSession(params.dataDir, sessionId);
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
sessionId = await createSession(params.dataDir, params.channel);
|
|
373
|
+
}
|
|
374
|
+
const hookContext = {
|
|
375
|
+
sessionId,
|
|
376
|
+
workspaceDir,
|
|
377
|
+
channel: params.channel,
|
|
378
|
+
};
|
|
379
|
+
const pluginSystem = params.plugins
|
|
380
|
+
? initializePluginSystem({
|
|
381
|
+
...params.plugins,
|
|
382
|
+
runtime: {
|
|
383
|
+
llm: params.llm,
|
|
384
|
+
hookCtx: hookContext,
|
|
385
|
+
llmAllowlist: params.plugins.llmAllowlist,
|
|
386
|
+
},
|
|
387
|
+
})
|
|
388
|
+
: undefined;
|
|
389
|
+
const hookRegistry = pluginSystem?.registry.hooks ?? new PluginHookRegistry();
|
|
390
|
+
const memoryProviderId = params.memory?.providerId;
|
|
391
|
+
const memoryPluginId = params.memory?.pluginId ?? pluginSystem?.normalizedConfig.slots?.memory;
|
|
392
|
+
const recentMemoryWrites = new Map();
|
|
393
|
+
const recentMemoryEvents = new Map();
|
|
394
|
+
const emitMemoryChanged = async (event) => {
|
|
395
|
+
const now = Date.now();
|
|
396
|
+
const normalizedFiles = normalizeMemoryChangedFiles(event.files);
|
|
397
|
+
if (normalizedFiles.length === 0) {
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
const filteredFiles = event.reason === "external-watch" && event.source === "memory"
|
|
401
|
+
? normalizedFiles.filter((file) => {
|
|
402
|
+
const lastWriteAt = recentMemoryWrites.get(file.toLowerCase()) ?? 0;
|
|
403
|
+
return now - lastWriteAt > EXTERNAL_WATCH_SUPPRESS_WINDOW_MS;
|
|
404
|
+
})
|
|
405
|
+
: normalizedFiles;
|
|
406
|
+
if (filteredFiles.length === 0) {
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
if (event.reason !== "external-watch" && event.source === "memory") {
|
|
410
|
+
for (const file of filteredFiles) {
|
|
411
|
+
recentMemoryWrites.set(file.toLowerCase(), now);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
const dedupeKey = `${event.reason}|${event.source}|${filteredFiles
|
|
415
|
+
.map((file) => file.toLowerCase())
|
|
416
|
+
.sort()
|
|
417
|
+
.join("|")}`;
|
|
418
|
+
const previousEventAt = recentMemoryEvents.get(dedupeKey) ?? 0;
|
|
419
|
+
if (now - previousEventAt < MEMORY_EVENT_DEDUPE_WINDOW_MS) {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
recentMemoryEvents.set(dedupeKey, now);
|
|
423
|
+
const eventToEmit = {
|
|
424
|
+
...event,
|
|
425
|
+
files: filteredFiles,
|
|
426
|
+
};
|
|
427
|
+
await dispatchProgress(event.sessionId ?? sessionId, {
|
|
428
|
+
type: "memory_changed",
|
|
429
|
+
reason: eventToEmit.reason,
|
|
430
|
+
files: eventToEmit.files,
|
|
431
|
+
source: eventToEmit.source,
|
|
432
|
+
providerId: eventToEmit.providerId,
|
|
433
|
+
timestamp: eventToEmit.timestamp,
|
|
434
|
+
}, params).catch(() => { });
|
|
435
|
+
await hookRegistry
|
|
436
|
+
.dispatch("memory_changed", eventToEmit, {
|
|
437
|
+
...hookContext,
|
|
438
|
+
sessionId: eventToEmit.sessionId ?? hookContext.sessionId,
|
|
439
|
+
})
|
|
440
|
+
.catch(() => { });
|
|
441
|
+
};
|
|
442
|
+
const transcriptOptions = memoryProviderId || memoryPluginId
|
|
443
|
+
? {
|
|
444
|
+
providerId: memoryProviderId,
|
|
445
|
+
pluginId: memoryPluginId,
|
|
446
|
+
onMemoryChanged: emitMemoryChanged,
|
|
447
|
+
}
|
|
448
|
+
: { onMemoryChanged: emitMemoryChanged };
|
|
449
|
+
const memoryRoot = path.join(params.dataDir, ".aimax");
|
|
450
|
+
const resolvedProvider = resolveMemoryProvider({
|
|
451
|
+
providerId: memoryProviderId,
|
|
452
|
+
pluginId: memoryPluginId,
|
|
453
|
+
dataDir: params.dataDir,
|
|
454
|
+
memoryDir: memoryRoot,
|
|
455
|
+
sessionId,
|
|
456
|
+
});
|
|
457
|
+
const provider = resolvedProvider?.provider ??
|
|
458
|
+
createBuiltinMemoryProvider({ dataDir: params.dataDir, memoryDir: memoryRoot, sessionId });
|
|
459
|
+
const stopMemoryWatchBridge = startMemoryWatchBridge({
|
|
460
|
+
dataDir: params.dataDir,
|
|
461
|
+
sessionId,
|
|
462
|
+
providerId: memoryProviderId ?? memoryPluginId ?? provider.id,
|
|
463
|
+
provider,
|
|
464
|
+
onMemoryChanged: emitMemoryChanged,
|
|
465
|
+
});
|
|
466
|
+
if (provider.sync) {
|
|
467
|
+
void provider.sync("session-start").catch(() => { });
|
|
468
|
+
}
|
|
469
|
+
if (resetCommand && params.callbackUrl) {
|
|
470
|
+
await sendCallback(params.callbackUrl, {
|
|
471
|
+
sessionId,
|
|
472
|
+
channel: params.channel,
|
|
473
|
+
messageId: params.messageId,
|
|
474
|
+
type: "session_reset",
|
|
475
|
+
action: resetCommand.action,
|
|
476
|
+
previousSessionId,
|
|
477
|
+
message: rawMessage,
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
if (resetCommand) {
|
|
481
|
+
await hookRegistry.dispatch("session_reset", {
|
|
482
|
+
action: resetCommand.action,
|
|
483
|
+
sessionId,
|
|
484
|
+
previousSessionId,
|
|
485
|
+
message: rawMessage,
|
|
486
|
+
}, hookContext);
|
|
487
|
+
}
|
|
488
|
+
if (params.callbackUrl) {
|
|
489
|
+
await sendCallback(params.callbackUrl, {
|
|
490
|
+
sessionId,
|
|
491
|
+
channel: params.channel,
|
|
492
|
+
messageId: params.messageId,
|
|
493
|
+
type: "start",
|
|
494
|
+
message: typeof promptInput === "string" ? promptInput : transcriptMessage,
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
await hookRegistry.dispatch("session_start", { sessionId }, hookContext);
|
|
498
|
+
// 2. Set up abort controller early (needed for manageHistory signal)
|
|
499
|
+
const abortController = new AbortController();
|
|
500
|
+
if (params.abortSignal?.aborted) {
|
|
501
|
+
abortController.abort();
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
params.abortSignal?.addEventListener("abort", () => abortController.abort());
|
|
505
|
+
}
|
|
506
|
+
// 3. Load bootstrap identity files
|
|
507
|
+
const bootstrapFiles = await loadBootstrapFiles(params.dataDir);
|
|
508
|
+
const bootstrapWarnings = [];
|
|
509
|
+
const contextFiles = buildBootstrapContextFiles(bootstrapFiles, {
|
|
510
|
+
warn: (message) => bootstrapWarnings.push(message),
|
|
511
|
+
});
|
|
512
|
+
// 4. Load skills (user + plugin skills)
|
|
513
|
+
const pluginSkillDirs = pluginSystem?.registry.skills ?? [];
|
|
514
|
+
const skills = await loadSkillsWithPluginDirs(params.dataDir, pluginSkillDirs);
|
|
515
|
+
if (resetShortCircuit) {
|
|
516
|
+
const resetReply = resetCommand?.action === "reset" ? handleResetCommand() : handleNewCommand();
|
|
517
|
+
return finalizeSlashCommandResult({
|
|
518
|
+
replyText: resetReply.text,
|
|
519
|
+
sessionId,
|
|
520
|
+
isNewSession,
|
|
521
|
+
transcriptMessage,
|
|
522
|
+
params,
|
|
523
|
+
hookRegistry,
|
|
524
|
+
hookContext,
|
|
525
|
+
startTime,
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
let effectivePrompt = promptInput;
|
|
529
|
+
if (inputMode === "text") {
|
|
530
|
+
const slashResult = handleSlashCommand({ message: messageForRun ?? "", skills });
|
|
531
|
+
if (slashResult.kind === "reply") {
|
|
532
|
+
return finalizeSlashCommandResult({
|
|
533
|
+
replyText: slashResult.text,
|
|
534
|
+
sessionId,
|
|
535
|
+
isNewSession,
|
|
536
|
+
transcriptMessage,
|
|
537
|
+
params,
|
|
538
|
+
hookRegistry,
|
|
539
|
+
hookContext,
|
|
540
|
+
startTime,
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
if (slashResult.kind === "compact") {
|
|
544
|
+
const rawHistory = params.channel === "CRON" ? [] : await loadTranscript(params.dataDir, sessionId);
|
|
545
|
+
const compactResult = await compactTranscriptNow({
|
|
546
|
+
entries: rawHistory,
|
|
547
|
+
contextWindowTokens: params.llm.contextWindow ?? 200_000,
|
|
548
|
+
llm: {
|
|
549
|
+
baseUrl: params.llm.baseUrl,
|
|
550
|
+
apiKey: params.llm.apiKey,
|
|
551
|
+
model: params.llm.model,
|
|
552
|
+
},
|
|
553
|
+
instructions: slashResult.instructions,
|
|
554
|
+
signal: params.abortSignal,
|
|
555
|
+
hooks: hookRegistry,
|
|
556
|
+
hookCtx: hookContext,
|
|
557
|
+
});
|
|
558
|
+
const compactText = compactResult.status === "compacted"
|
|
559
|
+
? `⚙️ Compacted (kept ${compactResult.keptCount}, dropped ${compactResult.droppedCount}).`
|
|
560
|
+
: `⚙️ Compaction skipped: ${compactResult.reason}`;
|
|
561
|
+
return finalizeSlashCommandResult({
|
|
562
|
+
replyText: compactText,
|
|
563
|
+
sessionId,
|
|
564
|
+
isNewSession,
|
|
565
|
+
transcriptMessage,
|
|
566
|
+
params,
|
|
567
|
+
hookRegistry,
|
|
568
|
+
hookContext,
|
|
569
|
+
startTime,
|
|
570
|
+
compactionEntry: compactResult.status === "compacted" ? compactResult.entry : undefined,
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
effectivePrompt = slashResult.kind === "rewrite" ? slashResult.message : (messageForRun ?? "");
|
|
574
|
+
transcriptMessage = typeof effectivePrompt === "string" ? effectivePrompt : transcriptMessage;
|
|
575
|
+
}
|
|
576
|
+
const effectivePromptText = typeof effectivePrompt === "string" ? effectivePrompt : transcriptMessage;
|
|
577
|
+
// 5. Load and manage conversation history
|
|
578
|
+
const skipHistory = params.channel === "CRON";
|
|
579
|
+
const rawHistory = skipHistory ? [] : await loadTranscript(params.dataDir, sessionId);
|
|
580
|
+
await hookRegistry.dispatch("before_compaction", { messageCount: rawHistory.length }, hookContext);
|
|
581
|
+
const modelInfo = { model: params.llm.model, api: "openai-completions" };
|
|
582
|
+
const historyResult = skipHistory
|
|
583
|
+
? {
|
|
584
|
+
messages: [],
|
|
585
|
+
priorSummary: undefined,
|
|
586
|
+
compactionEntry: undefined,
|
|
587
|
+
stats: { originalCount: 0, keptCount: 0, estimatedTokens: 0, compacted: false },
|
|
588
|
+
}
|
|
589
|
+
: await manageHistory({
|
|
590
|
+
entries: rawHistory,
|
|
591
|
+
modelInfo,
|
|
592
|
+
contextWindowTokens: params.llm.contextWindow ?? 200_000,
|
|
593
|
+
llm: {
|
|
594
|
+
baseUrl: params.llm.baseUrl,
|
|
595
|
+
apiKey: params.llm.apiKey,
|
|
596
|
+
model: params.llm.model,
|
|
597
|
+
},
|
|
598
|
+
historyLimit: params.historyLimit,
|
|
599
|
+
compactionEnabled: true,
|
|
600
|
+
signal: abortController.signal,
|
|
601
|
+
hooks: hookRegistry,
|
|
602
|
+
hookCtx: hookContext,
|
|
603
|
+
});
|
|
604
|
+
const depth = params.subagentContext?.depth ?? 0;
|
|
605
|
+
const baseTools = createAgentTools(params.dataDir, {
|
|
606
|
+
registry,
|
|
607
|
+
parentSessionId: sessionId,
|
|
608
|
+
depth,
|
|
609
|
+
channel: params.channel,
|
|
610
|
+
llm: params.llm,
|
|
611
|
+
loopDetection: params.loopDetection,
|
|
612
|
+
memoryOptions: {
|
|
613
|
+
providerId: memoryProviderId,
|
|
614
|
+
pluginId: memoryPluginId,
|
|
615
|
+
sessionId,
|
|
616
|
+
onMemoryChanged: emitMemoryChanged,
|
|
617
|
+
},
|
|
618
|
+
spawnFn: (childParams) => runAgentInternal(childParams, new SubagentRegistry()),
|
|
619
|
+
});
|
|
620
|
+
const pluginTools = pluginSystem?.registry.tools.resolveEnabled(params.plugins?.toolAllowlist) ?? [];
|
|
621
|
+
const rawTools = [...baseTools, ...pluginTools];
|
|
622
|
+
const toolNames = rawTools
|
|
623
|
+
.map((tool) => (typeof tool.name === "string" ? tool.name.trim() : ""))
|
|
624
|
+
.filter(Boolean);
|
|
625
|
+
const toolSummaries = {};
|
|
626
|
+
for (const tool of rawTools) {
|
|
627
|
+
const name = typeof tool.name === "string" ? tool.name.trim() : "";
|
|
628
|
+
const description = typeof tool.description === "string" ? tool.description.trim() : "";
|
|
629
|
+
if (!name || !description || toolSummaries[name])
|
|
630
|
+
continue;
|
|
631
|
+
toolSummaries[name] = description;
|
|
632
|
+
}
|
|
633
|
+
const messagingEnabled = params.messaging?.enabled ?? params.channel !== "CRON";
|
|
634
|
+
const messagingChannels = params.messaging?.channels && params.messaging.channels.length > 0
|
|
635
|
+
? params.messaging.channels
|
|
636
|
+
: [params.channel];
|
|
637
|
+
// 6. Build system prompt (with prior summary injected when available)
|
|
638
|
+
let systemPrompt = buildSystemPrompt({
|
|
639
|
+
dataDir: params.dataDir,
|
|
640
|
+
skills,
|
|
641
|
+
contextFiles,
|
|
642
|
+
toolNames,
|
|
643
|
+
toolSummaries,
|
|
644
|
+
promptMode: depth > 0 ? "minimal" : "full",
|
|
645
|
+
bootstrapWarnings,
|
|
646
|
+
memoryCitationsMode: params.memory?.citationsMode ?? "off",
|
|
647
|
+
messaging: {
|
|
648
|
+
enabled: messagingEnabled,
|
|
649
|
+
channels: messagingChannels,
|
|
650
|
+
},
|
|
651
|
+
docs: {
|
|
652
|
+
localPath: params.docs?.localPath,
|
|
653
|
+
webUrl: params.docs?.webUrl,
|
|
654
|
+
sourceUrl: params.docs?.sourceUrl,
|
|
655
|
+
},
|
|
656
|
+
sandboxInfo: {
|
|
657
|
+
enabled: true,
|
|
658
|
+
hostWorkspaceDir: workspaceDir,
|
|
659
|
+
containerWorkspaceDir: workspaceDir,
|
|
660
|
+
},
|
|
661
|
+
runtimeInfo: {
|
|
662
|
+
os: process.platform,
|
|
663
|
+
node: process.version,
|
|
664
|
+
model: params.llm.model,
|
|
665
|
+
hostname: os.hostname(),
|
|
666
|
+
},
|
|
667
|
+
currentDate: new Date().toISOString().split("T")[0],
|
|
668
|
+
priorConversationSummary: historyResult.priorSummary,
|
|
669
|
+
});
|
|
670
|
+
// 7. Persist new compaction entry (if history was summarised this run)
|
|
671
|
+
if (historyResult.compactionEntry) {
|
|
672
|
+
await appendTranscriptEntry(params.dataDir, sessionId, historyResult.compactionEntry, transcriptOptions);
|
|
673
|
+
await dispatchProgress(sessionId, {
|
|
674
|
+
type: "compaction",
|
|
675
|
+
reason: `Summarised ${historyResult.stats.originalCount - historyResult.stats.keptCount} older entries`,
|
|
676
|
+
}, params);
|
|
677
|
+
await hookRegistry.dispatch("after_compaction", {
|
|
678
|
+
messageCount: historyResult.stats.originalCount,
|
|
679
|
+
compactedCount: historyResult.stats.originalCount - historyResult.stats.keptCount,
|
|
680
|
+
}, hookContext);
|
|
681
|
+
}
|
|
682
|
+
const beforePromptResults = await hookRegistry.dispatch("before_prompt_build", { prompt: effectivePromptText }, hookContext);
|
|
683
|
+
for (const result of beforePromptResults) {
|
|
684
|
+
if (!result)
|
|
685
|
+
continue;
|
|
686
|
+
if (result.systemPrompt) {
|
|
687
|
+
systemPrompt = result.systemPrompt;
|
|
688
|
+
}
|
|
689
|
+
if (result.prependContext) {
|
|
690
|
+
systemPrompt = `${result.prependContext}\n\n${systemPrompt}`;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
// 8. Create tools (subagent tools included for all agents)
|
|
694
|
+
const toolsWithHooks = wrapToolsWithHooks(rawTools, hookRegistry, hookContext);
|
|
695
|
+
const tools = wrapToolsWithLoopDetection(toolsWithHooks, { sessionId, config: params.loopDetection }, getLoopDetectionState);
|
|
696
|
+
// 9. Create the model and agent
|
|
697
|
+
let resolvedModelId = params.llm.model;
|
|
698
|
+
const beforeModelResults = await hookRegistry.dispatch("before_model_resolve", { prompt: effectivePromptText }, hookContext);
|
|
699
|
+
for (const result of beforeModelResults) {
|
|
700
|
+
if (result && result.modelOverride) {
|
|
701
|
+
resolvedModelId = result.modelOverride;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
const model = createOpenAICompatModel({ ...params.llm, model: resolvedModelId });
|
|
705
|
+
const apiKey = params.llm.apiKey;
|
|
706
|
+
const agent = new Agent({
|
|
707
|
+
initialState: { systemPrompt, model, tools, messages: [] },
|
|
708
|
+
getApiKey: (_provider) => apiKey,
|
|
709
|
+
});
|
|
710
|
+
// 10. Wire timeout to abort the agent
|
|
711
|
+
const timeoutMs = params.timeoutMs ?? 600_000;
|
|
712
|
+
const timeoutHandle = setTimeout(() => {
|
|
713
|
+
abortController.abort();
|
|
714
|
+
agent.abort();
|
|
715
|
+
}, timeoutMs);
|
|
716
|
+
// Also propagate future aborts to the agent
|
|
717
|
+
abortController.signal.addEventListener("abort", () => agent.abort());
|
|
718
|
+
// 11. Restore managed history into the agent
|
|
719
|
+
if (historyResult.messages.length > 0) {
|
|
720
|
+
agent.replaceMessages(historyResult.messages);
|
|
721
|
+
}
|
|
722
|
+
let totalInputTokens = 0;
|
|
723
|
+
let totalOutputTokens = 0;
|
|
724
|
+
let lastResponseText = "";
|
|
725
|
+
let runError;
|
|
726
|
+
try {
|
|
727
|
+
// 12. Persist the initial user turn before the first model call so the
|
|
728
|
+
// transcript still records the request if the turn aborts or fails.
|
|
729
|
+
await appendTranscriptEntry(params.dataDir, sessionId, {
|
|
730
|
+
role: "user",
|
|
731
|
+
content: transcriptMessage,
|
|
732
|
+
timestamp: new Date().toISOString(),
|
|
733
|
+
}, transcriptOptions);
|
|
734
|
+
// 13. First agent turn
|
|
735
|
+
const turn = await runAgentTurn(agent, effectivePrompt, sessionId, resolvedModelId, historyResult.messages, params, hookRegistry, hookContext, abortController.signal);
|
|
736
|
+
lastResponseText = turn.text;
|
|
737
|
+
totalInputTokens += turn.inputTokens;
|
|
738
|
+
totalOutputTokens += turn.outputTokens;
|
|
739
|
+
if (turn.error)
|
|
740
|
+
runError = turn.error;
|
|
741
|
+
// 14. Persist the assistant/tool outputs from the first turn.
|
|
742
|
+
await persistTurnEntries(params.dataDir, sessionId, turn, transcriptOptions);
|
|
743
|
+
// 15. Announce loop — deliver subagent results back to the parent agent.
|
|
744
|
+
let announceRound = 0;
|
|
745
|
+
while (!abortController.signal.aborted &&
|
|
746
|
+
registry.needsAnnounce(sessionId) &&
|
|
747
|
+
announceRound < MAX_ANNOUNCE_ROUNDS) {
|
|
748
|
+
announceRound++;
|
|
749
|
+
await registry.waitForAll(sessionId);
|
|
750
|
+
const completed = registry.consumeCompleted(sessionId);
|
|
751
|
+
if (completed.length === 0)
|
|
752
|
+
break;
|
|
753
|
+
for (const r of completed) {
|
|
754
|
+
await dispatchProgress(sessionId, {
|
|
755
|
+
type: "subagent_complete",
|
|
756
|
+
childSessionId: r.childSessionId,
|
|
757
|
+
task: r.task,
|
|
758
|
+
status: r.status,
|
|
759
|
+
}, params);
|
|
760
|
+
}
|
|
761
|
+
const announceMsg = buildBatchAnnounceMessage(completed);
|
|
762
|
+
await appendTranscriptEntry(params.dataDir, sessionId, {
|
|
763
|
+
role: "user",
|
|
764
|
+
content: announceMsg,
|
|
765
|
+
timestamp: new Date().toISOString(),
|
|
766
|
+
}, transcriptOptions);
|
|
767
|
+
const announceTurn = await runAgentTurn(agent, announceMsg, sessionId, resolvedModelId, [], params, hookRegistry, hookContext, abortController.signal);
|
|
768
|
+
lastResponseText = announceTurn.text;
|
|
769
|
+
totalInputTokens += announceTurn.inputTokens;
|
|
770
|
+
totalOutputTokens += announceTurn.outputTokens;
|
|
771
|
+
if (announceTurn.error && !runError)
|
|
772
|
+
runError = announceTurn.error;
|
|
773
|
+
await persistTurnEntries(params.dataDir, sessionId, announceTurn, transcriptOptions);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
finally {
|
|
777
|
+
clearTimeout(timeoutHandle);
|
|
778
|
+
clearLoopDetectionState(sessionId);
|
|
779
|
+
stopMemoryWatchBridge();
|
|
780
|
+
}
|
|
781
|
+
// 15. Save session metadata for new sessions
|
|
782
|
+
if (isNewSession) {
|
|
783
|
+
const title = generateSessionTitle(transcriptMessage);
|
|
784
|
+
const now = new Date().toISOString();
|
|
785
|
+
await saveSessionMetadata(params.dataDir, {
|
|
786
|
+
id: sessionId,
|
|
787
|
+
title,
|
|
788
|
+
channel: params.channel,
|
|
789
|
+
createdAt: now,
|
|
790
|
+
updatedAt: now,
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
const durationMs = Date.now() - startTime;
|
|
794
|
+
const usage = {
|
|
795
|
+
input: totalInputTokens,
|
|
796
|
+
output: totalOutputTokens,
|
|
797
|
+
total: totalInputTokens + totalOutputTokens,
|
|
798
|
+
};
|
|
799
|
+
const result = {
|
|
800
|
+
sessionId,
|
|
801
|
+
text: lastResponseText,
|
|
802
|
+
usage,
|
|
803
|
+
durationMs,
|
|
804
|
+
error: runError,
|
|
805
|
+
};
|
|
806
|
+
await hookRegistry.dispatch("agent_end", { success: !runError, error: runError, durationMs }, hookContext);
|
|
807
|
+
const finalTranscript = await loadTranscript(params.dataDir, sessionId);
|
|
808
|
+
await hookRegistry.dispatch("session_end", { sessionId, messageCount: finalTranscript.length, durationMs }, hookContext);
|
|
809
|
+
// 16. Send final callback
|
|
810
|
+
if (params.callbackUrl) {
|
|
811
|
+
if (runError) {
|
|
812
|
+
await sendCallback(params.callbackUrl, {
|
|
813
|
+
sessionId,
|
|
814
|
+
channel: params.channel,
|
|
815
|
+
messageId: params.messageId,
|
|
816
|
+
type: "error",
|
|
817
|
+
message: runError,
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
else {
|
|
821
|
+
await sendCallback(params.callbackUrl, {
|
|
822
|
+
sessionId,
|
|
823
|
+
channel: params.channel,
|
|
824
|
+
messageId: params.messageId,
|
|
825
|
+
type: "done",
|
|
826
|
+
result: { text: lastResponseText, usage, durationMs },
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
return result;
|
|
831
|
+
}
|
|
832
|
+
async function finalizeSlashCommandResult(params) {
|
|
833
|
+
const { replyText, sessionId, isNewSession, transcriptMessage, params: runParams, hookRegistry, hookContext, startTime, compactionEntry, } = params;
|
|
834
|
+
await appendTranscriptEntry(runParams.dataDir, sessionId, {
|
|
835
|
+
role: "user",
|
|
836
|
+
content: transcriptMessage,
|
|
837
|
+
timestamp: new Date().toISOString(),
|
|
838
|
+
});
|
|
839
|
+
if (compactionEntry) {
|
|
840
|
+
await appendTranscriptEntry(runParams.dataDir, sessionId, compactionEntry);
|
|
841
|
+
}
|
|
842
|
+
await appendTranscriptEntry(runParams.dataDir, sessionId, {
|
|
843
|
+
role: "assistant",
|
|
844
|
+
content: replyText,
|
|
845
|
+
timestamp: new Date().toISOString(),
|
|
846
|
+
});
|
|
847
|
+
if (isNewSession) {
|
|
848
|
+
const titleSource = transcriptMessage.trim() ? transcriptMessage : "New session";
|
|
849
|
+
const title = generateSessionTitle(titleSource);
|
|
850
|
+
const now = new Date().toISOString();
|
|
851
|
+
await saveSessionMetadata(runParams.dataDir, {
|
|
852
|
+
id: sessionId,
|
|
853
|
+
title,
|
|
854
|
+
channel: runParams.channel,
|
|
855
|
+
createdAt: now,
|
|
856
|
+
updatedAt: now,
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
const durationMs = Date.now() - startTime;
|
|
860
|
+
const usage = { input: 0, output: 0, total: 0 };
|
|
861
|
+
const result = {
|
|
862
|
+
sessionId,
|
|
863
|
+
text: replyText,
|
|
864
|
+
usage,
|
|
865
|
+
durationMs,
|
|
866
|
+
};
|
|
867
|
+
await hookRegistry.dispatch("agent_end", { success: true, error: undefined, durationMs }, hookContext);
|
|
868
|
+
const finalTranscript = await loadTranscript(runParams.dataDir, sessionId);
|
|
869
|
+
await hookRegistry.dispatch("session_end", { sessionId, messageCount: finalTranscript.length, durationMs }, hookContext);
|
|
870
|
+
if (runParams.callbackUrl) {
|
|
871
|
+
await sendCallback(runParams.callbackUrl, {
|
|
872
|
+
sessionId,
|
|
873
|
+
channel: runParams.channel,
|
|
874
|
+
messageId: runParams.messageId,
|
|
875
|
+
type: "done",
|
|
876
|
+
result: { text: replyText, usage, durationMs },
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
return result;
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Runs an agent session end-to-end.
|
|
883
|
+
* Supports multi-agent: subagents spawned via sessions_spawn are automatically
|
|
884
|
+
* awaited and their results announced back to the parent agent.
|
|
885
|
+
*
|
|
886
|
+
* @param params - Run parameters.
|
|
887
|
+
* @param _registryForTesting - Internal: inject a pre-populated registry for unit tests.
|
|
888
|
+
*/
|
|
889
|
+
export async function runAgent(params, _registryForTesting) {
|
|
890
|
+
const registry = _registryForTesting ?? new SubagentRegistry();
|
|
891
|
+
return runAgentInternal(params, registry);
|
|
892
|
+
}
|
|
893
|
+
//# sourceMappingURL=runner.js.map
|