@jcheesepkg/nanobot 0.9.0 → 0.9.2
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/README.md +18 -18
- package/dist/agent/context.d.mts +4 -4
- package/dist/agent/context.d.mts.map +1 -1
- package/dist/agent/context.mjs +27 -28
- package/dist/agent/context.mjs.map +1 -1
- package/dist/agent/loop.d.mts +5 -3
- package/dist/agent/loop.d.mts.map +1 -1
- package/dist/agent/loop.mjs +64 -55
- package/dist/agent/loop.mjs.map +1 -1
- package/dist/agent/memory.d.mts.map +1 -1
- package/dist/agent/memory.mjs +3 -3
- package/dist/agent/memory.mjs.map +1 -1
- package/dist/agent/skills.d.mts.map +1 -1
- package/dist/agent/skills.mjs +4 -4
- package/dist/agent/skills.mjs.map +1 -1
- package/dist/agent/subagent.d.mts.map +1 -1
- package/dist/agent/subagent.mjs +22 -22
- package/dist/agent/subagent.mjs.map +1 -1
- package/dist/agent/tools/base.mjs +2 -2
- package/dist/agent/tools/base.mjs.map +1 -1
- package/dist/agent/tools/cron.d.mts +1 -1
- package/dist/agent/tools/cron.d.mts.map +1 -1
- package/dist/agent/tools/cron.mjs +11 -11
- package/dist/agent/tools/cron.mjs.map +1 -1
- package/dist/agent/tools/filesystem.d.mts +4 -4
- package/dist/agent/tools/filesystem.d.mts.map +1 -1
- package/dist/agent/tools/filesystem.mjs +20 -20
- package/dist/agent/tools/filesystem.mjs.map +1 -1
- package/dist/agent/tools/flex.d.mts +1 -1
- package/dist/agent/tools/flex.d.mts.map +1 -1
- package/dist/agent/tools/flex.mjs +112 -112
- package/dist/agent/tools/flex.mjs.map +1 -1
- package/dist/agent/tools/flex.test.mjs +60 -59
- package/dist/agent/tools/flex.test.mjs.map +1 -1
- package/dist/agent/tools/message.d.mts +1 -1
- package/dist/agent/tools/message.d.mts.map +1 -1
- package/dist/agent/tools/message.mjs +4 -4
- package/dist/agent/tools/message.mjs.map +1 -1
- package/dist/agent/tools/registry.d.mts.map +1 -1
- package/dist/agent/tools/registry.mjs +4 -4
- package/dist/agent/tools/registry.mjs.map +1 -1
- package/dist/agent/tools/shell.d.mts +1 -1
- package/dist/agent/tools/shell.mjs +4 -4
- package/dist/agent/tools/shell.mjs.map +1 -1
- package/dist/agent/tools/spawn.d.mts +1 -1
- package/dist/agent/tools/spawn.d.mts.map +1 -1
- package/dist/agent/tools/spawn.mjs +4 -4
- package/dist/agent/tools/spawn.mjs.map +1 -1
- package/dist/agent/tools/web.d.mts +2 -2
- package/dist/agent/tools/web.d.mts.map +1 -1
- package/dist/agent/tools/web.mjs +36 -36
- package/dist/agent/tools/web.mjs.map +1 -1
- package/dist/bus/events.mjs +1 -1
- package/dist/bus/events.mjs.map +1 -1
- package/dist/bus/queue.d.mts.map +1 -1
- package/dist/bus/queue.mjs.map +1 -1
- package/dist/channels/base.d.mts.map +1 -1
- package/dist/channels/base.mjs +2 -2
- package/dist/channels/base.mjs.map +1 -1
- package/dist/channels/line.d.mts +1 -0
- package/dist/channels/line.d.mts.map +1 -1
- package/dist/channels/line.mjs +65 -65
- package/dist/channels/line.mjs.map +1 -1
- package/dist/channels/line.test.mjs +26 -27
- package/dist/channels/line.test.mjs.map +1 -1
- package/dist/channels/manager.d.mts.map +1 -1
- package/dist/channels/manager.mjs +9 -9
- package/dist/channels/manager.mjs.map +1 -1
- package/dist/channels/telegram.mjs +34 -34
- package/dist/channels/telegram.mjs.map +1 -1
- package/dist/cli/index.mjs +36 -36
- package/dist/cli/index.mjs.map +1 -1
- package/dist/config/loader.d.mts.map +1 -1
- package/dist/config/loader.mjs +1 -1
- package/dist/config/loader.mjs.map +1 -1
- package/dist/config/schema.d.mts +381 -381
- package/dist/config/schema.d.mts.map +1 -1
- package/dist/config/schema.mjs +42 -42
- package/dist/config/schema.mjs.map +1 -1
- package/dist/gateway/server.d.mts.map +1 -1
- package/dist/gateway/server.mjs +48 -54
- package/dist/gateway/server.mjs.map +1 -1
- package/dist/heartbeat/service.d.mts.map +1 -1
- package/dist/heartbeat/service.mjs +8 -8
- package/dist/heartbeat/service.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/node_modules/{@jridgewell → .bun/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell}/sourcemap-codec/dist/sourcemap-codec.mjs +1 -1
- package/dist/node_modules/.bun/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_expect@2.1.9/node_modules/@vitest}/expect/dist/index.mjs +8 -8
- package/dist/node_modules/.bun/@vitest_expect@2.1.9/node_modules/@vitest/expect/dist/index.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_pretty-format@2.1.9/node_modules/@vitest}/pretty-format/dist/index.mjs +2 -2
- package/dist/node_modules/.bun/@vitest_pretty-format@2.1.9/node_modules/@vitest/pretty-format/dist/index.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_runner@2.1.9/node_modules/@vitest}/runner/dist/chunk-tasks.mjs +1 -1
- package/dist/node_modules/.bun/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/chunk-tasks.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_runner@2.1.9/node_modules/@vitest}/runner/dist/index.mjs +6 -6
- package/dist/node_modules/.bun/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/index.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_snapshot@2.1.9/node_modules/@vitest}/snapshot/dist/index.mjs +5 -5
- package/dist/node_modules/.bun/@vitest_snapshot@2.1.9/node_modules/@vitest/snapshot/dist/index.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_spy@2.1.9/node_modules/@vitest}/spy/dist/index.mjs +2 -2
- package/dist/node_modules/.bun/@vitest_spy@2.1.9/node_modules/@vitest/spy/dist/index.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_utils@2.1.9/node_modules/@vitest}/utils/dist/chunk-_commonjsHelpers.mjs +3 -3
- package/dist/node_modules/.bun/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_utils@2.1.9/node_modules/@vitest}/utils/dist/diff.mjs +4 -4
- package/dist/node_modules/.bun/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/diff.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_utils@2.1.9/node_modules/@vitest}/utils/dist/error.mjs +3 -3
- package/dist/node_modules/.bun/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/error.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_utils@2.1.9/node_modules/@vitest}/utils/dist/helpers.mjs +1 -1
- package/dist/node_modules/.bun/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/helpers.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_utils@2.1.9/node_modules/@vitest}/utils/dist/index.mjs +3 -3
- package/dist/node_modules/.bun/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/index.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_utils@2.1.9/node_modules/@vitest}/utils/dist/source-map.mjs +1 -1
- package/dist/node_modules/.bun/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/source-map.mjs.map +1 -0
- package/dist/node_modules/{chai → .bun/chai@5.3.3/node_modules/chai}/index.mjs +1 -1
- package/dist/node_modules/.bun/chai@5.3.3/node_modules/chai/index.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/arguments.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/arguments.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/array.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/array.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/bigint.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/bigint.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/class.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/class.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/date.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/date.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/error.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/error.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/function.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/function.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/helpers.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/helpers.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/html.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/html.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/index.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/index.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/map.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/map.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/number.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/number.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/object.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/object.mjs.map +1 -0
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/promise.mjs +6 -0
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/promise.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/regexp.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/regexp.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/set.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/set.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/string.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/string.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/symbol.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/symbol.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/typedarray.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/typedarray.mjs.map +1 -0
- package/dist/node_modules/{magic-string → .bun/magic-string@0.30.21/node_modules/magic-string}/dist/magic-string.es.mjs +2 -2
- package/dist/node_modules/.bun/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.mjs.map +1 -0
- package/dist/node_modules/{@vitest/snapshot → .bun/pathe@1.1.2}/node_modules/pathe/dist/shared/pathe.ff20891b.mjs +1 -1
- package/dist/node_modules/.bun/pathe@1.1.2/node_modules/pathe/dist/shared/pathe.ff20891b.mjs.map +1 -0
- package/dist/node_modules/{tinyrainbow → .bun/tinyrainbow@1.2.0/node_modules/tinyrainbow}/dist/chunk-BVHSVHOK.mjs +1 -1
- package/dist/node_modules/.bun/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.mjs.map +1 -0
- package/dist/node_modules/{tinyrainbow → .bun/tinyrainbow@1.2.0/node_modules/tinyrainbow}/dist/node.mjs +1 -1
- package/dist/node_modules/.bun/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/node.mjs.map +1 -0
- package/dist/node_modules/{tinyspy → .bun/tinyspy@3.0.2/node_modules/tinyspy}/dist/index.mjs +1 -1
- package/dist/node_modules/.bun/tinyspy@3.0.2/node_modules/tinyspy/dist/index.mjs.map +1 -0
- package/dist/node_modules/{vitest → .bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest}/dist/chunks/_commonjsHelpers.BFTU3MAI.mjs +1 -1
- package/dist/node_modules/.bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest/dist/chunks/_commonjsHelpers.BFTU3MAI.mjs.map +1 -0
- package/dist/node_modules/{vitest → .bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest}/dist/chunks/date.W2xKR2qe.mjs +1 -1
- package/dist/node_modules/.bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest/dist/chunks/date.W2xKR2qe.mjs.map +1 -0
- package/dist/node_modules/{vitest → .bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest}/dist/chunks/utils.C8RiOc4B.mjs +2 -2
- package/dist/node_modules/.bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest/dist/chunks/utils.C8RiOc4B.mjs.map +1 -0
- package/dist/node_modules/{vitest → .bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest}/dist/chunks/vi.DgezovHB.mjs +11 -11
- package/dist/node_modules/.bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest/dist/chunks/vi.DgezovHB.mjs.map +1 -0
- package/dist/providers/base.d.mts +2 -2
- package/dist/providers/base.d.mts.map +1 -1
- package/dist/providers/openai-provider.d.mts.map +1 -1
- package/dist/providers/openai-provider.mjs +10 -9
- package/dist/providers/openai-provider.mjs.map +1 -1
- package/dist/providers/registry.d.mts +1 -1
- package/dist/providers/registry.d.mts.map +1 -1
- package/dist/providers/registry.mjs +99 -99
- package/dist/providers/registry.mjs.map +1 -1
- package/dist/session/manager.d.mts +2 -2
- package/dist/session/manager.d.mts.map +1 -1
- package/dist/session/manager.mjs +18 -19
- package/dist/session/manager.mjs.map +1 -1
- package/dist/utils/helpers.d.mts.map +1 -1
- package/dist/utils/helpers.mjs.map +1 -1
- package/package.json +11 -11
- package/skills/cron/SKILL.md +12 -8
- package/skills/daily-summary/SKILL.md +5 -1
- package/skills/english/SKILL.md +72 -14
- package/skills/expense/SKILL.md +15 -11
- package/skills/fortune/SKILL.md +25 -21
- package/skills/habit/SKILL.md +7 -6
- package/skills/hydration/SKILL.md +8 -5
- package/skills/memory/SKILL.md +1 -0
- package/skills/mood/SKILL.md +13 -9
- package/skills/skill-creator/SKILL.md +22 -0
- package/skills/summarize/SKILL.md +1 -0
- package/skills/weather/SKILL.md +11 -9
- package/dist/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs.map +0 -1
- package/dist/node_modules/@vitest/expect/dist/index.mjs.map +0 -1
- package/dist/node_modules/@vitest/pretty-format/dist/index.mjs.map +0 -1
- package/dist/node_modules/@vitest/runner/dist/chunk-tasks.mjs.map +0 -1
- package/dist/node_modules/@vitest/runner/dist/index.mjs.map +0 -1
- package/dist/node_modules/@vitest/snapshot/dist/index.mjs.map +0 -1
- package/dist/node_modules/@vitest/snapshot/node_modules/pathe/dist/shared/pathe.ff20891b.mjs.map +0 -1
- package/dist/node_modules/@vitest/spy/dist/index.mjs.map +0 -1
- package/dist/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.mjs.map +0 -1
- package/dist/node_modules/@vitest/utils/dist/diff.mjs.map +0 -1
- package/dist/node_modules/@vitest/utils/dist/error.mjs.map +0 -1
- package/dist/node_modules/@vitest/utils/dist/helpers.mjs.map +0 -1
- package/dist/node_modules/@vitest/utils/dist/index.mjs.map +0 -1
- package/dist/node_modules/@vitest/utils/dist/source-map.mjs.map +0 -1
- package/dist/node_modules/chai/index.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/arguments.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/array.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/bigint.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/class.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/date.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/error.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/function.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/helpers.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/html.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/index.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/map.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/number.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/object.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/promise.mjs +0 -6
- package/dist/node_modules/loupe/lib/promise.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/regexp.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/set.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/string.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/symbol.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/typedarray.mjs.map +0 -1
- package/dist/node_modules/magic-string/dist/magic-string.es.mjs.map +0 -1
- package/dist/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.mjs.map +0 -1
- package/dist/node_modules/tinyrainbow/dist/node.mjs.map +0 -1
- package/dist/node_modules/tinyspy/dist/index.mjs.map +0 -1
- package/dist/node_modules/vitest/dist/chunks/_commonjsHelpers.BFTU3MAI.mjs.map +0 -1
- package/dist/node_modules/vitest/dist/chunks/date.W2xKR2qe.mjs.map +0 -1
- package/dist/node_modules/vitest/dist/chunks/utils.C8RiOc4B.mjs.map +0 -1
- package/dist/node_modules/vitest/dist/chunks/vi.DgezovHB.mjs.map +0 -1
- /package/dist/node_modules/{@vitest → .bun/@vitest_runner@2.1.9/node_modules/@vitest}/runner/dist/utils.mjs +0 -0
package/dist/agent/loop.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop.mjs","names":[],"sources":["../../src/agent/loop.ts"],"sourcesContent":["import type { LLMProvider, ChatMessage, ToolCallRequest } from \"../providers/base.js\";\nimport type { MessageBus } from \"../bus/queue.js\";\nimport type {\n InboundMessage,\n OutboundMessage,\n} from \"../bus/events.js\";\nimport { createOutboundMessage } from \"../bus/events.js\";\nimport { ContextBuilder } from \"./context.js\";\nimport { ToolRegistry } from \"./tools/registry.js\";\nimport {\n ReadFileTool,\n WriteFileTool,\n EditFileTool,\n ListDirTool,\n} from \"./tools/filesystem.js\";\nimport { ExecTool } from \"./tools/shell.js\";\nimport { WebSearchTool, WebFetchTool } from \"./tools/web.js\";\nimport { MessageTool } from \"./tools/message.js\";\nimport { SpawnTool } from \"./tools/spawn.js\";\nimport { CronTool } from \"./tools/cron.js\";\nimport { FlexTool } from \"./tools/flex.js\";\nimport { SubagentManager } from \"./subagent.js\";\nimport { getBuiltinSkillsDir } from \"./skills.js\";\nimport { SessionManager, Session } from \"../session/manager.js\";\nimport { MemoryStore } from \"./memory.js\";\nimport type { ExecToolConfig } from \"../config/schema.js\";\nimport type { Tool } from \"./tools/base.js\";\n\n/**\n * The agent loop: core processing engine.\n *\n * 1. Receives messages from the bus\n * 2. Builds context with history, memory, skills\n * 3. Calls the LLM\n * 4. Executes tool calls\n * 5. Sends responses back\n */\nexport class AgentLoop {\n private bus: MessageBus;\n private provider: LLMProvider;\n private workspace: string;\n private model: string;\n private consolidationModel: string;\n private maxTokens: number;\n private maxIterations: number;\n private memoryWindow: number;\n\n readonly context: ContextBuilder;\n readonly sessions: SessionManager;\n readonly tools: ToolRegistry;\n readonly subagents: SubagentManager;\n\n private _running = false;\n\n /** In-flight AbortControllers keyed by session key. */\n private inflight = new Map<string, AbortController>();\n\n constructor(params: {\n bus: MessageBus;\n provider: LLMProvider;\n workspace: string;\n model?: string;\n consolidationModel?: string;\n maxTokens?: number;\n maxIterations?: number;\n memoryWindow?: number;\n braveApiKey?: string;\n execConfig?: ExecToolConfig;\n restrictToWorkspace?: boolean;\n toolsEnabled?: string[];\n toolsDisabled?: string[];\n customTools?: Tool[];\n }) {\n this.bus = params.bus;\n this.provider = params.provider;\n this.workspace = params.workspace;\n this.model = params.model ?? params.provider.getDefaultModel();\n this.consolidationModel = params.consolidationModel ?? this.model;\n this.maxTokens = params.maxTokens ?? 8192;\n this.maxIterations = params.maxIterations ?? 20;\n this.memoryWindow = params.memoryWindow ?? 50;\n\n const execConfig = params.execConfig ?? { timeout: 60 };\n const restrictToWorkspace = params.restrictToWorkspace ?? false;\n\n this.context = new ContextBuilder(params.workspace);\n this.sessions = new SessionManager(params.workspace);\n this.tools = new ToolRegistry();\n this.subagents = new SubagentManager({\n provider: params.provider,\n workspace: params.workspace,\n bus: params.bus,\n model: this.model,\n braveApiKey: params.braveApiKey,\n execConfig,\n restrictToWorkspace,\n });\n\n this.registerDefaultTools(\n execConfig,\n restrictToWorkspace,\n params.braveApiKey,\n params.toolsEnabled,\n params.toolsDisabled,\n params.customTools,\n );\n }\n\n private registerDefaultTools(\n execConfig: ExecToolConfig,\n restrictToWorkspace: boolean,\n braveApiKey?: string,\n toolsEnabled?: string[],\n toolsDisabled?: string[],\n customTools?: Tool[],\n ): void {\n const enabled = new Set(toolsEnabled ?? []);\n const disabled = new Set(toolsDisabled ?? []);\n const hasAllowlist = enabled.size > 0;\n const shouldRegister = (name: string): boolean =>\n (hasAllowlist ? enabled.has(name) : true) && !disabled.has(name);\n\n const registerIfEnabled = (tool: Tool): void => {\n if (shouldRegister(tool.name)) {\n this.tools.register(tool);\n }\n };\n\n // File tools — pass allowedDir when restrictToWorkspace is enabled\n const allowedDir = restrictToWorkspace ? this.workspace : undefined;\n const readOnlyPaths = restrictToWorkspace ? [getBuiltinSkillsDir()] : undefined;\n registerIfEnabled(new ReadFileTool({ allowedDir, readOnlyPaths }));\n registerIfEnabled(new WriteFileTool({ allowedDir }));\n registerIfEnabled(new EditFileTool({ allowedDir }));\n registerIfEnabled(new ListDirTool({ allowedDir, readOnlyPaths }));\n\n // Shell tool\n registerIfEnabled(\n new ExecTool({\n workingDir: this.workspace,\n timeout: execConfig.timeout,\n restrictToWorkspace,\n }),\n );\n\n // Web tools\n registerIfEnabled(new WebSearchTool({ apiKey: braveApiKey }));\n registerIfEnabled(new WebFetchTool());\n\n // Message tool\n const messageTool = new MessageTool({\n sendCallback: (msg) => this.bus.publishOutbound(msg),\n });\n registerIfEnabled(messageTool);\n\n // Spawn tool\n const spawnTool = new SpawnTool(this.subagents);\n registerIfEnabled(spawnTool);\n\n // Cron tool — always registered, uses DO scheduling via worker API\n registerIfEnabled(new CronTool());\n\n // Flex Message tool — builds LINE Flex JSON from structured data\n registerIfEnabled(new FlexTool());\n\n if (customTools && customTools.length > 0) {\n for (const tool of customTools) {\n registerIfEnabled(tool);\n }\n }\n }\n\n /** Run the agent loop, processing messages from the bus. */\n async run(): Promise<void> {\n this._running = true;\n console.log(\"Agent loop started\");\n\n while (this._running) {\n try {\n const msg = await this.bus.consumeInboundTimeout(1000);\n\n // Process concurrently so new messages can abort in-flight ones\n this.processMessage(msg)\n .then(async (response) => {\n if (response) {\n await this.bus.publishOutbound(response);\n }\n })\n .catch(async (err) => {\n if (isAbortError(err)) return; // Already handled\n console.error(\"Error processing message:\", err);\n await this.bus.publishOutbound(\n createOutboundMessage({\n channel: msg.channel,\n chatId: msg.chatId,\n content: `Sorry, I encountered an error: ${err instanceof Error ? err.message : err}`,\n }),\n );\n });\n } catch {\n // timeout, continue\n }\n }\n }\n\n /** Stop the agent loop. */\n stop(): void {\n this._running = false;\n console.log(\"Agent loop stopping\");\n }\n\n /** Process a single inbound message. */\n private async processMessage(\n msg: InboundMessage,\n ): Promise<OutboundMessage | null> {\n // Handle system messages (subagent announces)\n if (msg.channel === \"system\") {\n return this.processSystemMessage(msg);\n }\n\n console.log(`Processing message from ${msg.channel}:${msg.senderId}`);\n\n const sessionKey = `${msg.channel}:${msg.chatId}`;\n\n // Abort any in-flight request for this session\n const existing = this.inflight.get(sessionKey);\n if (existing) {\n console.log(`Aborting in-flight request for ${sessionKey}`);\n existing.abort();\n }\n\n // Create a new AbortController for this request\n const controller = new AbortController();\n this.inflight.set(sessionKey, controller);\n\n const session = this.sessions.getOrCreate(sessionKey);\n\n // Consolidate memory if session is too large\n if (session.history.length > this.memoryWindow) {\n await this.consolidateMemory(session);\n }\n\n // Update tool contexts\n this.updateToolContexts(msg.channel, msg.chatId);\n\n // Build initial messages\n const messages = this.context.buildMessages({\n history: session.getHistory(),\n currentMessage: msg.content,\n media: msg.media.length > 0 ? msg.media : undefined,\n channel: msg.channel,\n chatId: msg.chatId,\n });\n\n // The messages array is: [system, ...history, currentUser]\n // We want to save from the current user message onward (skip system + old history).\n const savedHistoryLen = session.getHistory().length;\n const newMsgStart = 1 + savedHistoryLen; // 1 for system prompt\n\n try {\n // Agent loop (mutates messages by appending assistant/tool messages)\n const result = await this.runAgentLoop(messages, controller.signal, (text) => {\n if (text && text.trim().length > 0) {\n this.bus.publishOutbound(\n createOutboundMessage({\n channel: msg.channel,\n chatId: msg.chatId,\n content: text,\n }),\n );\n }\n });\n\n // Save the new messages from this turn (user + all agent loop messages)\n session.addTurnMessages(messages.slice(newMsgStart), result.toolsUsed);\n this.sessions.save(session);\n\n return createOutboundMessage({\n channel: msg.channel,\n chatId: msg.chatId,\n content: result.content,\n });\n } catch (err) {\n if (isAbortError(err)) {\n // Request was aborted because a new message arrived.\n // Save the user message to history so the next request has context,\n // but don't save any assistant response.\n const userMessages = messages.slice(newMsgStart).filter((m) => m.role === \"user\");\n if (userMessages.length > 0) {\n session.addTurnMessages(userMessages);\n this.sessions.save(session);\n }\n console.log(`Request aborted for ${sessionKey}, user message saved to history`);\n return null; // No response -- the new message will handle it\n }\n throw err; // Re-throw non-abort errors\n } finally {\n // Clean up if this is still our controller\n if (this.inflight.get(sessionKey) === controller) {\n this.inflight.delete(sessionKey);\n }\n }\n }\n\n private async processSystemMessage(\n msg: InboundMessage,\n ): Promise<OutboundMessage | null> {\n console.log(`Processing system message from ${msg.senderId}`);\n\n let originChannel: string;\n let originChatId: string;\n\n if (msg.chatId.includes(\":\")) {\n const [ch, id] = msg.chatId.split(\":\", 2);\n originChannel = ch;\n originChatId = id;\n } else {\n originChannel = \"cli\";\n originChatId = msg.chatId;\n }\n\n const sessionKey = `${originChannel}:${originChatId}`;\n const session = this.sessions.getOrCreate(sessionKey);\n\n this.updateToolContexts(originChannel, originChatId);\n\n const messages = this.context.buildMessages({\n history: session.getHistory(),\n currentMessage: msg.content,\n channel: originChannel,\n chatId: originChatId,\n });\n\n const savedHistoryLen = session.getHistory().length;\n const newMsgStart = 1 + savedHistoryLen;\n\n const result = await this.runAgentLoop(messages);\n\n session.addTurnMessages(messages.slice(newMsgStart), result.toolsUsed);\n this.sessions.save(session);\n\n return createOutboundMessage({\n channel: originChannel,\n chatId: originChatId,\n content: result.content,\n });\n }\n\n private async runAgentLoop(\n messages: ChatMessage[],\n signal?: AbortSignal,\n onToolCallText?: (text: string) => void,\n ): Promise<{ content: string; toolsUsed: string[] }> {\n let finalContent: string | null = null;\n let sentToolCallNotice = false;\n const toolsUsed: string[] = [];\n\n for (let i = 0; i < this.maxIterations; i++) {\n const response = await this.provider.chat({\n messages,\n tools: this.tools.getDefinitions(),\n model: this.model,\n maxTokens: this.maxTokens,\n signal,\n });\n\n if (response.hasToolCalls) {\n // Send an interim message so the user knows we're working\n if (!sentToolCallNotice && onToolCallText) {\n const interimText = response.content?.trim()\n || this.getToolCallFallbackText(response.toolCalls);\n if (interimText) {\n onToolCallText(interimText);\n }\n sentToolCallNotice = true;\n }\n const toolCallDicts = response.toolCalls.map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: {\n name: tc.name,\n arguments: JSON.stringify(tc.arguments),\n },\n }));\n\n this.context.addAssistantMessage(\n messages,\n response.content,\n toolCallDicts,\n );\n\n for (const tc of response.toolCalls) {\n toolsUsed.push(tc.name);\n console.log(`Tool: ${tc.name}(${JSON.stringify(tc.arguments)})`);\n\n // Detect skill reads\n if (tc.name === \"read_file\") {\n const path = String(tc.arguments?.path ?? \"\");\n const skillMatch = path.match(/skills\\/([^/]+)\\/SKILL\\.md$/);\n if (skillMatch) {\n console.log(`Skill activated: ${skillMatch[1]}`);\n }\n }\n\n const result = await this.tools.execute(tc.name, tc.arguments);\n this.context.addToolResult(messages, tc.id, tc.name, result);\n\n // Auto-send flex_message results so they get rendered as Flex Messages\n // regardless of whether the LLM echoes the JSON in its response\n if (tc.name === \"flex_message\" && onToolCallText && !result.startsWith(\"Error\")) {\n // Extract just the JSON part (before the instruction suffix)\n const jsonOnly = result.split(\"\\n\\n(\")[0];\n onToolCallText(jsonOnly);\n }\n }\n } else {\n finalContent = response.content;\n\n // If the LLM returned empty content after tool use, nudge it to respond\n if ((!finalContent || finalContent.trim().length === 0) && i > 0) {\n messages.push({\n role: \"assistant\",\n content: \"\",\n });\n messages.push({\n role: \"user\",\n content: \"(You used tools but didn't respond to the user. Please provide a brief response summarizing what you did.)\",\n });\n continue;\n }\n\n // Push the final assistant message so it gets persisted with the turn\n messages.push({ role: \"assistant\", content: finalContent ?? \"\" });\n break;\n }\n }\n\n if (!finalContent || finalContent.trim().length === 0) {\n finalContent = \"I've completed processing but have no response to give.\";\n }\n\n // If we exhausted iterations without a non-tool-call response, still persist the final text\n if (messages[messages.length - 1]?.role !== \"assistant\" || messages[messages.length - 1]?.content !== finalContent) {\n messages.push({ role: \"assistant\", content: finalContent });\n }\n\n return { content: finalContent, toolsUsed };\n }\n\n /** Generate a fallback interim message based on which tools are being called. */\n private getToolCallFallbackText(toolCalls: ToolCallRequest[]): string {\n const toolNames = toolCalls.map((tc) => tc.name);\n const fallbacks: Record<string, string> = {\n web_search: \"検索中...\",\n web_fetch: \"ページを読み込み中...\",\n spawn: \"サブエージェントを起動中...\",\n flex_message: \"カードを作成中...\",\n };\n for (const name of toolNames) {\n if (fallbacks[name]) return fallbacks[name];\n }\n return \"ちょっと待ってね...\";\n }\n\n private updateToolContexts(channel: string, chatId: string): void {\n const messageTool = this.tools.get(\"message\");\n if (messageTool instanceof MessageTool) {\n messageTool.setContext(channel, chatId);\n }\n\n const spawnTool = this.tools.get(\"spawn\");\n if (spawnTool instanceof SpawnTool) {\n spawnTool.setContext(channel, chatId);\n }\n\n const cronTool = this.tools.get(\"cron\");\n if (cronTool instanceof CronTool) {\n cronTool.setContext(channel, chatId);\n }\n }\n\n /** Process a message directly (for CLI or cron usage). */\n async processDirect(\n content: string,\n sessionKey = \"cli:direct\",\n channel = \"cli\",\n chatId = \"direct\",\n ): Promise<string> {\n // Use inline version of processMessage for direct calls\n const session = this.sessions.getOrCreate(sessionKey);\n this.updateToolContexts(channel, chatId);\n\n const messages = this.context.buildMessages({\n history: session.getHistory(),\n currentMessage: content,\n channel,\n chatId,\n });\n\n const savedHistoryLen = session.getHistory().length;\n const newMsgStart = 1 + savedHistoryLen;\n\n const result = await this.runAgentLoop(messages);\n\n session.addTurnMessages(messages.slice(newMsgStart), result.toolsUsed);\n this.sessions.save(session);\n\n return result.content;\n }\n\n /** Consolidate old messages into MEMORY.md + HISTORY.md, then trim session. */\n private async consolidateMemory(session: Session): Promise<void> {\n const memory = new MemoryStore(this.workspace);\n const keepCount = Math.min(10, Math.max(2, Math.floor(this.memoryWindow / 2)));\n const oldMessages = session.history.slice(0, -keepCount);\n if (oldMessages.length === 0) return;\n\n console.log(`Memory consolidation: ${session.history.length} messages, archiving ${oldMessages.length}, keeping ${keepCount}`);\n\n // Format messages for LLM\n const lines: string[] = [];\n for (const m of oldMessages) {\n const content = typeof m.content === \"string\" ? m.content : \"\";\n if (!content) continue;\n const role = m.role.toUpperCase();\n const tools = m.toolsUsed ? ` [tools: ${m.toolsUsed.join(\", \")}]` : \"\";\n const ts = m.timestamp ? `[${m.timestamp.slice(0, 16)}] ` : \"\";\n lines.push(`${ts}${role}${tools}: ${content}`);\n }\n const conversation = lines.join(\"\\n\");\n const currentMemory = memory.readLongTerm();\n\n const prompt = `You are a memory consolidation agent. Process this conversation and return a JSON object with exactly two keys:\n\n1. \"history_entry\": A paragraph (2-5 sentences) summarizing the key events/decisions/topics. Start with a timestamp like [YYYY-MM-DD HH:MM]. Include enough detail to be useful when found by grep search later.\n\n2. \"memory_update\": The updated long-term memory content. Add any new facts: user location, preferences, personal info, habits, project context, technical decisions, tools/services used. If nothing new, return the existing content unchanged.\n\n## Current Long-term Memory\n${currentMemory || \"(empty)\"}\n\n## Conversation to Process\n${conversation}\n\nRespond with ONLY valid JSON, no markdown fences.`;\n\n try {\n const response = await this.provider.chat({\n messages: [\n { role: \"system\", content: \"You are a memory consolidation agent. Respond only with valid JSON.\" },\n { role: \"user\", content: prompt },\n ],\n model: this.consolidationModel,\n });\n\n let text = (response.content || \"\").trim();\n if (text.startsWith(\"```\")) {\n text = text.split(\"\\n\").slice(1).join(\"\\n\").split(\"```\")[0].trim();\n }\n\n const result = JSON.parse(text);\n\n if (result.history_entry) {\n const entry = typeof result.history_entry === \"string\"\n ? result.history_entry\n : JSON.stringify(result.history_entry, null, 2);\n memory.appendHistory(entry);\n }\n if (result.memory_update && result.memory_update !== currentMemory) {\n const update = typeof result.memory_update === \"string\"\n ? result.memory_update\n : JSON.stringify(result.memory_update, null, 2);\n memory.writeLongTerm(update);\n }\n } catch (err) {\n console.error(\"Memory consolidation failed:\", err);\n // Fallback: append raw conversation to history so it's not lost\n const fallbackEntry = `[${new Date().toISOString().slice(0, 16)}] Consolidation failed, archiving raw messages:\\n${conversation.slice(0, 2000)}`;\n memory.appendHistory(fallbackEntry);\n }\n\n // Always trim session to prevent unbounded growth\n session.trimHistory(keepCount);\n this.sessions.save(session);\n console.log(`Memory consolidation done, session trimmed to ${session.history.length} messages`);\n }\n}\n\n/** Check if an error is an abort/cancellation error. */\nfunction isAbortError(err: unknown): boolean {\n if (err instanceof DOMException && err.name === \"AbortError\") return true;\n if (err instanceof Error) {\n if (err.name === \"AbortError\") return true;\n // OpenAI SDK wraps abort as APIUserAbortError\n if (err.name === \"APIUserAbortError\") return true;\n if (err.message.includes(\"abort\")) return true;\n }\n return false;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"loop.mjs","names":[],"sources":["../../src/agent/loop.ts"],"sourcesContent":["import type { InboundMessage, OutboundMessage } from \"../bus/events.js\";\nimport type { MessageBus } from \"../bus/queue.js\";\nimport type { ExecToolConfig } from \"../config/schema.js\";\nimport type {\n LLMProvider,\n ChatMessage,\n ToolCallRequest,\n} from \"../providers/base.js\";\nimport type { Session } from \"../session/manager.js\";\nimport type { Tool } from \"./tools/base.js\";\n\nimport { createOutboundMessage } from \"../bus/events.js\";\nimport { SessionManager } from \"../session/manager.js\";\nimport { ContextBuilder } from \"./context.js\";\nimport { MemoryStore } from \"./memory.js\";\nimport { getBuiltinSkillsDir } from \"./skills.js\";\nimport { SubagentManager } from \"./subagent.js\";\nimport { CronTool } from \"./tools/cron.js\";\nimport {\n ReadFileTool,\n WriteFileTool,\n EditFileTool,\n ListDirTool,\n} from \"./tools/filesystem.js\";\nimport { FlexTool } from \"./tools/flex.js\";\nimport { MessageTool } from \"./tools/message.js\";\nimport { ToolRegistry } from \"./tools/registry.js\";\nimport { ExecTool } from \"./tools/shell.js\";\nimport { SpawnTool } from \"./tools/spawn.js\";\nimport { WebSearchTool, WebFetchTool } from \"./tools/web.js\";\n\n/**\n * The agent loop: core processing engine.\n *\n * 1. Receives messages from the bus\n * 2. Builds context with history, memory, skills\n * 3. Calls the LLM\n * 4. Executes tool calls\n * 5. Sends responses back\n */\nexport class AgentLoop {\n private bus: MessageBus;\n private provider: LLMProvider;\n private workspace: string;\n private model: string;\n private consolidationModel: string;\n private maxTokens: number;\n private maxIterations: number;\n private memoryWindow: number;\n\n readonly context: ContextBuilder;\n readonly sessions: SessionManager;\n readonly tools: ToolRegistry;\n readonly subagents: SubagentManager;\n\n private _running = false;\n\n /** In-flight AbortControllers keyed by session key. */\n private inflight = new Map<string, AbortController>();\n\n /** Promises that resolve when an aborted processMessage finishes saving. */\n private inflightDone = new Map<string, Promise<void>>();\n\n constructor(params: {\n bus: MessageBus;\n provider: LLMProvider;\n workspace: string;\n model?: string;\n consolidationModel?: string;\n maxTokens?: number;\n maxIterations?: number;\n memoryWindow?: number;\n braveApiKey?: string;\n execConfig?: ExecToolConfig;\n restrictToWorkspace?: boolean;\n toolsEnabled?: string[];\n toolsDisabled?: string[];\n customTools?: Tool[];\n }) {\n this.bus = params.bus;\n this.provider = params.provider;\n this.workspace = params.workspace;\n this.model = params.model ?? params.provider.getDefaultModel();\n this.consolidationModel = params.consolidationModel ?? this.model;\n this.maxTokens = params.maxTokens ?? 8192;\n this.maxIterations = params.maxIterations ?? 20;\n this.memoryWindow = params.memoryWindow ?? 50;\n\n const execConfig = params.execConfig ?? { timeout: 60 };\n const restrictToWorkspace = params.restrictToWorkspace ?? false;\n\n this.context = new ContextBuilder(params.workspace);\n this.sessions = new SessionManager(params.workspace);\n this.tools = new ToolRegistry();\n this.subagents = new SubagentManager({\n braveApiKey: params.braveApiKey,\n bus: params.bus,\n execConfig,\n model: this.model,\n provider: params.provider,\n restrictToWorkspace,\n workspace: params.workspace,\n });\n\n this.registerDefaultTools(\n execConfig,\n restrictToWorkspace,\n params.braveApiKey,\n params.toolsEnabled,\n params.toolsDisabled,\n params.customTools\n );\n }\n\n private registerDefaultTools(\n execConfig: ExecToolConfig,\n restrictToWorkspace: boolean,\n braveApiKey?: string,\n toolsEnabled?: string[],\n toolsDisabled?: string[],\n customTools?: Tool[]\n ): void {\n const enabled = new Set(toolsEnabled ?? []);\n const disabled = new Set(toolsDisabled ?? []);\n const hasAllowlist = enabled.size > 0;\n const shouldRegister = (name: string): boolean =>\n (hasAllowlist ? enabled.has(name) : true) && !disabled.has(name);\n\n const registerIfEnabled = (tool: Tool): void => {\n if (shouldRegister(tool.name)) {\n this.tools.register(tool);\n }\n };\n\n // File tools — pass allowedDir when restrictToWorkspace is enabled\n const allowedDir = restrictToWorkspace ? this.workspace : undefined;\n const readOnlyPaths = restrictToWorkspace\n ? [getBuiltinSkillsDir()]\n : undefined;\n registerIfEnabled(new ReadFileTool({ allowedDir, readOnlyPaths }));\n registerIfEnabled(new WriteFileTool({ allowedDir }));\n registerIfEnabled(new EditFileTool({ allowedDir }));\n registerIfEnabled(new ListDirTool({ allowedDir, readOnlyPaths }));\n\n // Shell tool\n registerIfEnabled(\n new ExecTool({\n restrictToWorkspace,\n timeout: execConfig.timeout,\n workingDir: this.workspace,\n })\n );\n\n // Web tools\n registerIfEnabled(new WebSearchTool({ apiKey: braveApiKey }));\n registerIfEnabled(new WebFetchTool());\n\n // Message tool\n const messageTool = new MessageTool({\n sendCallback: (msg) => this.bus.publishOutbound(msg),\n });\n registerIfEnabled(messageTool);\n\n // Spawn tool\n const spawnTool = new SpawnTool(this.subagents);\n registerIfEnabled(spawnTool);\n\n // Cron tool — always registered, uses DO scheduling via worker API\n registerIfEnabled(new CronTool());\n\n // Flex Message tool — builds LINE Flex JSON from structured data\n registerIfEnabled(new FlexTool());\n\n if (customTools && customTools.length > 0) {\n for (const tool of customTools) {\n registerIfEnabled(tool);\n }\n }\n }\n\n /** Run the agent loop, processing messages from the bus. */\n async run(): Promise<void> {\n this._running = true;\n console.log(\"Agent loop started\");\n\n while (this._running) {\n try {\n const msg = await this.bus.consumeInboundTimeout(1000);\n\n const sessionKey = `${msg.channel}:${msg.chatId}`;\n\n // Process concurrently so new messages can abort in-flight ones.\n // Store the promise so the next message for the same session can\n // wait for the aborted request to finish saving its user message.\n const done = this.processMessage(msg)\n .then(async (response) => {\n if (response) {\n await this.bus.publishOutbound(response);\n }\n })\n .catch(async (error) => {\n if (isAbortError(error)) return; // Already handled\n console.error(\"Error processing message:\", error);\n await this.bus.publishOutbound(\n createOutboundMessage({\n channel: msg.channel,\n chatId: msg.chatId,\n content: `Sorry, I encountered an error: ${error instanceof Error ? error.message : error}`,\n })\n );\n });\n\n this.inflightDone.set(sessionKey, done);\n } catch {\n // timeout, continue\n }\n }\n }\n\n /** Stop the agent loop. */\n stop(): void {\n this._running = false;\n console.log(\"Agent loop stopping\");\n }\n\n /** Process a single inbound message. */\n private async processMessage(\n msg: InboundMessage\n ): Promise<OutboundMessage | null> {\n // Handle system messages (subagent announces)\n if (msg.channel === \"system\") {\n return this.processSystemMessage(msg);\n }\n\n console.log(`Processing message from ${msg.channel}:${msg.senderId}`);\n\n const sessionKey = `${msg.channel}:${msg.chatId}`;\n\n // Abort any in-flight request for this session\n const existing = this.inflight.get(sessionKey);\n if (existing) {\n console.log(`Aborting in-flight request for ${sessionKey}`);\n existing.abort();\n }\n\n // Wait for the previous request to finish saving its user message\n // so that session.getHistory() includes it (e.g. an image message\n // that was aborted by this text message).\n const prev = this.inflightDone.get(sessionKey);\n if (prev) {\n await prev;\n this.inflightDone.delete(sessionKey);\n }\n\n // Create a new AbortController for this request\n const controller = new AbortController();\n this.inflight.set(sessionKey, controller);\n\n const session = this.sessions.getOrCreate(sessionKey);\n\n // Consolidate memory if session is too large\n if (session.history.length > this.memoryWindow) {\n await this.consolidateMemory(session);\n }\n\n // Update tool contexts\n this.updateToolContexts(msg.channel, msg.chatId);\n\n // Build initial messages\n const messages = this.context.buildMessages({\n channel: msg.channel,\n chatId: msg.chatId,\n currentMessage: msg.content,\n history: session.getHistory(),\n media: msg.media.length > 0 ? msg.media : undefined,\n });\n\n // The messages array is: [system, ...history, currentUser]\n // We want to save from the current user message onward (skip system + old history).\n const savedHistoryLen = session.getHistory().length;\n const newMsgStart = 1 + savedHistoryLen; // 1 for system prompt\n\n try {\n // Agent loop (mutates messages by appending assistant/tool messages)\n const result = await this.runAgentLoop(\n messages,\n controller.signal,\n (text) => {\n if (text && text.trim().length > 0) {\n this.bus.publishOutbound(\n createOutboundMessage({\n channel: msg.channel,\n chatId: msg.chatId,\n content: text,\n })\n );\n }\n }\n );\n\n // Save the new messages from this turn (user + all agent loop messages)\n session.addTurnMessages(messages.slice(newMsgStart), result.toolsUsed);\n this.sessions.save(session);\n\n return createOutboundMessage({\n channel: msg.channel,\n chatId: msg.chatId,\n content: result.content,\n });\n } catch (error) {\n if (isAbortError(error)) {\n // Request was aborted because a new message arrived.\n // Save the user message to history so the next request has context,\n // but don't save any assistant response.\n const userMessages = messages\n .slice(newMsgStart)\n .filter((m) => m.role === \"user\");\n if (userMessages.length > 0) {\n session.addTurnMessages(userMessages);\n this.sessions.save(session);\n }\n console.log(\n `Request aborted for ${sessionKey}, user message saved to history`\n );\n return null; // No response -- the new message will handle it\n }\n throw error; // Re-throw non-abort errors\n } finally {\n // Clean up if this is still our controller\n if (this.inflight.get(sessionKey) === controller) {\n this.inflight.delete(sessionKey);\n }\n }\n }\n\n private async processSystemMessage(\n msg: InboundMessage\n ): Promise<OutboundMessage | null> {\n console.log(`Processing system message from ${msg.senderId}`);\n\n let originChannel: string;\n let originChatId: string;\n\n if (msg.chatId.includes(\":\")) {\n const [ch, id] = msg.chatId.split(\":\", 2);\n originChannel = ch;\n originChatId = id;\n } else {\n originChannel = \"cli\";\n originChatId = msg.chatId;\n }\n\n const sessionKey = `${originChannel}:${originChatId}`;\n const session = this.sessions.getOrCreate(sessionKey);\n\n this.updateToolContexts(originChannel, originChatId);\n\n const messages = this.context.buildMessages({\n channel: originChannel,\n chatId: originChatId,\n currentMessage: msg.content,\n history: session.getHistory(),\n });\n\n const savedHistoryLen = session.getHistory().length;\n const newMsgStart = 1 + savedHistoryLen;\n\n const result = await this.runAgentLoop(messages);\n\n session.addTurnMessages(messages.slice(newMsgStart), result.toolsUsed);\n this.sessions.save(session);\n\n return createOutboundMessage({\n channel: originChannel,\n chatId: originChatId,\n content: result.content,\n });\n }\n\n private async runAgentLoop(\n messages: ChatMessage[],\n signal?: AbortSignal,\n onToolCallText?: (text: string) => void\n ): Promise<{ content: string; toolsUsed: string[] }> {\n let finalContent: string | null = null;\n let sentToolCallNotice = false;\n const toolsUsed: string[] = [];\n\n for (let i = 0; i < this.maxIterations; i += 1) {\n const response = await this.provider.chat({\n maxTokens: this.maxTokens,\n messages,\n model: this.model,\n signal,\n tools: this.tools.getDefinitions(),\n });\n\n if (response.hasToolCalls) {\n // Send an interim message so the user knows we're working\n if (!sentToolCallNotice && onToolCallText) {\n const interimText =\n response.content?.trim() ||\n this.getToolCallFallbackText(response.toolCalls);\n if (interimText) {\n onToolCallText(interimText);\n }\n sentToolCallNotice = true;\n }\n const toolCallDicts = response.toolCalls.map((tc) => ({\n function: {\n name: tc.name,\n arguments: JSON.stringify(tc.arguments),\n },\n id: tc.id,\n type: \"function\" as const,\n }));\n\n this.context.addAssistantMessage(\n messages,\n response.content,\n toolCallDicts\n );\n\n for (const tc of response.toolCalls) {\n toolsUsed.push(tc.name);\n console.log(`Tool: ${tc.name}(${JSON.stringify(tc.arguments)})`);\n\n // Detect skill reads\n if (tc.name === \"read_file\") {\n const path = String(tc.arguments?.path ?? \"\");\n const skillMatch = path.match(/skills\\/([^/]+)\\/SKILL\\.md$/);\n if (skillMatch) {\n console.log(`Skill activated: ${skillMatch[1]}`);\n }\n }\n\n const result = await this.tools.execute(tc.name, tc.arguments);\n this.context.addToolResult(messages, tc.id, tc.name, result);\n\n // Auto-send flex_message results so they get rendered as Flex Messages\n // regardless of whether the LLM echoes the JSON in its response\n if (\n tc.name === \"flex_message\" &&\n onToolCallText &&\n !result.startsWith(\"Error\")\n ) {\n // Extract just the JSON part (before the instruction suffix)\n const jsonOnly = result.split(\"\\n\\n(\")[0];\n onToolCallText(jsonOnly);\n }\n }\n } else {\n finalContent = response.content;\n\n // If the LLM returned empty content after tool use, nudge it to respond\n if ((!finalContent || finalContent.trim().length === 0) && i > 0) {\n messages.push({\n content: \"\",\n role: \"assistant\",\n });\n messages.push({\n content:\n \"(You used tools but didn't respond to the user. Please provide a brief response summarizing what you did.)\",\n role: \"user\",\n });\n continue;\n }\n\n // Push the final assistant message so it gets persisted with the turn\n messages.push({ content: finalContent ?? \"\", role: \"assistant\" });\n break;\n }\n }\n\n if (!finalContent || finalContent.trim().length === 0) {\n finalContent = \"I've completed processing but have no response to give.\";\n }\n\n // If we exhausted iterations without a non-tool-call response, still persist the final text\n if (\n messages.at(-1)?.role !== \"assistant\" ||\n messages.at(-1)?.content !== finalContent\n ) {\n messages.push({ content: finalContent, role: \"assistant\" });\n }\n\n return { content: finalContent, toolsUsed };\n }\n\n /** Generate a fallback interim message based on which tools are being called. */\n private getToolCallFallbackText(toolCalls: ToolCallRequest[]): string {\n const toolNames = toolCalls.map((tc) => tc.name);\n const fallbacks: Record<string, string> = {\n flex_message: \"カードを作成中...\",\n spawn: \"サブエージェントを起動中...\",\n web_fetch: \"ページを読み込み中...\",\n web_search: \"検索中...\",\n };\n for (const name of toolNames) {\n if (fallbacks[name]) {\n return fallbacks[name];\n }\n }\n return \"ちょっと待ってね...\";\n }\n\n private updateToolContexts(channel: string, chatId: string): void {\n const messageTool = this.tools.get(\"message\");\n if (messageTool instanceof MessageTool) {\n messageTool.setContext(channel, chatId);\n }\n\n const spawnTool = this.tools.get(\"spawn\");\n if (spawnTool instanceof SpawnTool) {\n spawnTool.setContext(channel, chatId);\n }\n\n const cronTool = this.tools.get(\"cron\");\n if (cronTool instanceof CronTool) {\n cronTool.setContext(channel, chatId);\n }\n }\n\n /** Process a message directly (for CLI or cron usage). */\n async processDirect(\n content: string,\n sessionKey = \"cli:direct\",\n channel = \"cli\",\n chatId = \"direct\"\n ): Promise<string> {\n // Use inline version of processMessage for direct calls\n const session = this.sessions.getOrCreate(sessionKey);\n this.updateToolContexts(channel, chatId);\n\n const messages = this.context.buildMessages({\n channel,\n chatId,\n currentMessage: content,\n history: session.getHistory(),\n });\n\n const savedHistoryLen = session.getHistory().length;\n const newMsgStart = 1 + savedHistoryLen;\n\n const result = await this.runAgentLoop(messages);\n\n session.addTurnMessages(messages.slice(newMsgStart), result.toolsUsed);\n this.sessions.save(session);\n\n return result.content;\n }\n\n /** Consolidate old messages into MEMORY.md + HISTORY.md, then trim session. */\n private async consolidateMemory(session: Session): Promise<void> {\n const memory = new MemoryStore(this.workspace);\n const keepCount = Math.min(\n 10,\n Math.max(2, Math.floor(this.memoryWindow / 2))\n );\n const oldMessages = session.history.slice(0, -keepCount);\n if (oldMessages.length === 0) {\n return;\n }\n\n console.log(\n `Memory consolidation: ${session.history.length} messages, archiving ${oldMessages.length}, keeping ${keepCount}`\n );\n\n // Format messages for LLM\n const lines: string[] = [];\n for (const m of oldMessages) {\n const content = typeof m.content === \"string\" ? m.content : \"\";\n if (!content) {\n continue;\n }\n const role = m.role.toUpperCase();\n const tools = m.toolsUsed ? ` [tools: ${m.toolsUsed.join(\", \")}]` : \"\";\n const ts = m.timestamp ? `[${m.timestamp.slice(0, 16)}] ` : \"\";\n lines.push(`${ts}${role}${tools}: ${content}`);\n }\n const conversation = lines.join(\"\\n\");\n const currentMemory = memory.readLongTerm();\n\n const prompt = `You are a memory consolidation agent. Process this conversation and return a JSON object with exactly two keys:\n\n1. \"history_entry\": A paragraph (2-5 sentences) summarizing the key events/decisions/topics. Start with a timestamp like [YYYY-MM-DD HH:MM]. Include enough detail to be useful when found by grep search later.\n\n2. \"memory_update\": The updated long-term memory content. Add any new facts: user location, preferences, personal info, habits, project context, technical decisions, tools/services used. If nothing new, return the existing content unchanged.\n\n## Current Long-term Memory\n${currentMemory || \"(empty)\"}\n\n## Conversation to Process\n${conversation}\n\nRespond with ONLY valid JSON, no markdown fences.`;\n\n try {\n const response = await this.provider.chat({\n messages: [\n {\n content:\n \"You are a memory consolidation agent. Respond only with valid JSON.\",\n role: \"system\",\n },\n { content: prompt, role: \"user\" },\n ],\n model: this.consolidationModel,\n });\n\n let text = (response.content || \"\").trim();\n if (text.startsWith(\"```\")) {\n text = text.split(\"\\n\").slice(1).join(\"\\n\").split(\"```\")[0].trim();\n }\n\n const result = JSON.parse(text);\n\n if (result.history_entry) {\n const entry =\n typeof result.history_entry === \"string\"\n ? result.history_entry\n : JSON.stringify(result.history_entry, null, 2);\n memory.appendHistory(entry);\n }\n if (result.memory_update && result.memory_update !== currentMemory) {\n const update =\n typeof result.memory_update === \"string\"\n ? result.memory_update\n : JSON.stringify(result.memory_update, null, 2);\n memory.writeLongTerm(update);\n }\n } catch (error) {\n console.error(\"Memory consolidation failed:\", error);\n // Fallback: append raw conversation to history so it's not lost\n const fallbackEntry = `[${new Date().toISOString().slice(0, 16)}] Consolidation failed, archiving raw messages:\\n${conversation.slice(0, 2000)}`;\n memory.appendHistory(fallbackEntry);\n }\n\n // Always trim session to prevent unbounded growth\n session.trimHistory(keepCount);\n this.sessions.save(session);\n console.log(\n `Memory consolidation done, session trimmed to ${session.history.length} messages`\n );\n }\n}\n\n/** Check if an error is an abort/cancellation error. */\nfunction isAbortError(err: unknown): boolean {\n if (err instanceof DOMException && err.name === \"AbortError\") {\n return true;\n }\n if (err instanceof Error) {\n if (err.name === \"AbortError\") {\n return true;\n }\n // OpenAI SDK wraps abort as APIUserAbortError\n if (err.name === \"APIUserAbortError\") {\n return true;\n }\n if (err.message.includes(\"abort\")) {\n return true;\n }\n }\n return false;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.d.mts","names":[],"sources":["../../src/agent/memory.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"memory.d.mts","names":[],"sources":["../../src/agent/memory.ts"],"mappings":";;cAWa,WAAA;EAAA,QACH,SAAA;EAAA,QACA,UAAA;EAAA,QACA,WAAA;cAEI,SAAA;EAMZ,YAAA,CAAA;EAOA,aAAA,CAAc,OAAA;EAId,aAAA,CAAc,KAAA;EAId,gBAAA,CAAA;AAAA"}
|
package/dist/agent/memory.mjs
CHANGED
|
@@ -14,14 +14,14 @@ var MemoryStore = class {
|
|
|
14
14
|
this.historyFile = join(this.memoryDir, "HISTORY.md");
|
|
15
15
|
}
|
|
16
16
|
readLongTerm() {
|
|
17
|
-
if (existsSync(this.memoryFile)) return readFileSync(this.memoryFile, "
|
|
17
|
+
if (existsSync(this.memoryFile)) return readFileSync(this.memoryFile, "utf8");
|
|
18
18
|
return "";
|
|
19
19
|
}
|
|
20
20
|
writeLongTerm(content) {
|
|
21
|
-
writeFileSync(this.memoryFile, content, "
|
|
21
|
+
writeFileSync(this.memoryFile, content, "utf8");
|
|
22
22
|
}
|
|
23
23
|
appendHistory(entry) {
|
|
24
|
-
appendFileSync(this.historyFile, entry.trimEnd() + "\n\n", "
|
|
24
|
+
appendFileSync(this.historyFile, entry.trimEnd() + "\n\n", "utf8");
|
|
25
25
|
}
|
|
26
26
|
getMemoryContext() {
|
|
27
27
|
const longTerm = this.readLongTerm();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.mjs","names":[],"sources":["../../src/agent/memory.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"memory.mjs","names":[],"sources":["../../src/agent/memory.ts"],"sourcesContent":["import {\n readFileSync,\n writeFileSync,\n existsSync,\n appendFileSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport { ensureDir } from \"../utils/helpers.js\";\n\n/** Two-layer memory: MEMORY.md (long-term facts) + HISTORY.md (grep-searchable log). */\nexport class MemoryStore {\n private memoryDir: string;\n private memoryFile: string;\n private historyFile: string;\n\n constructor(workspace: string) {\n this.memoryDir = ensureDir(join(workspace, \"memory\"));\n this.memoryFile = join(this.memoryDir, \"MEMORY.md\");\n this.historyFile = join(this.memoryDir, \"HISTORY.md\");\n }\n\n readLongTerm(): string {\n if (existsSync(this.memoryFile)) {\n return readFileSync(this.memoryFile, \"utf8\");\n }\n return \"\";\n }\n\n writeLongTerm(content: string): void {\n writeFileSync(this.memoryFile, content, \"utf8\");\n }\n\n appendHistory(entry: string): void {\n appendFileSync(this.historyFile, entry.trimEnd() + \"\\n\\n\", \"utf8\");\n }\n\n getMemoryContext(): string {\n const longTerm = this.readLongTerm();\n return longTerm ? `## Long-term Memory\\n${longTerm}` : \"\";\n }\n}\n"],"mappings":";;;;;;AAWA,IAAa,cAAb,MAAyB;CACvB,AAAQ;CACR,AAAQ;CACR,AAAQ;CAER,YAAY,WAAmB;AAC7B,OAAK,YAAY,UAAU,KAAK,WAAW,SAAS,CAAC;AACrD,OAAK,aAAa,KAAK,KAAK,WAAW,YAAY;AACnD,OAAK,cAAc,KAAK,KAAK,WAAW,aAAa;;CAGvD,eAAuB;AACrB,MAAI,WAAW,KAAK,WAAW,CAC7B,QAAO,aAAa,KAAK,YAAY,OAAO;AAE9C,SAAO;;CAGT,cAAc,SAAuB;AACnC,gBAAc,KAAK,YAAY,SAAS,OAAO;;CAGjD,cAAc,OAAqB;AACjC,iBAAe,KAAK,aAAa,MAAM,SAAS,GAAG,QAAQ,OAAO;;CAGpE,mBAA2B;EACzB,MAAM,WAAW,KAAK,cAAc;AACpC,SAAO,WAAW,wBAAwB,aAAa"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skills.d.mts","names":[],"sources":["../../src/agent/skills.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"skills.d.mts","names":[],"sources":["../../src/agent/skills.ts"],"mappings":";;iBAUgB,mBAAA,CAAA;AAAA,UAMN,SAAA;EACR,IAAA;EACA,IAAA;EACA,MAAA;AAAA;AAAA,UAGQ,SAAA;EACR,WAAA;EACA,MAAA;EACA,QAAA;EAAA,CACC,GAAA;AAAA;;;AAPK;;cAgBK,YAAA;EAAA,QACH,SAAA;EAAA,QACA,eAAA;EAAA,QACA,aAAA;cAEI,SAAA,UAAmB,gBAAA;EAd9B;EAqBD,UAAA,CAAW,iBAAA,aAA2B,SAAA;EArB1B;EA2DZ,SAAA,CAAU,IAAA;EAlDa;EAiEvB,oBAAA,CAAqB,UAAA;EA0DoB;EA7CzC,kBAAA,CAAA;EA5EQ;EA4GR,eAAA,CAAA;;EAaA,gBAAA,CAAiB,IAAA,WAAe,SAAA;EAAA,QA2BxB,gBAAA;EAAA,QAUA,oBAAA;EAAA,QAWA,iBAAA;EAAA,QAeA,YAAA;EAAA,QAKA,mBAAA;EAAA,QAKA,sBAAA;AAAA"}
|
package/dist/agent/skills.mjs
CHANGED
|
@@ -9,6 +9,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
9
9
|
function getBuiltinSkillsDir() {
|
|
10
10
|
return join(dirname(dirname(__dirname)), "skills");
|
|
11
11
|
}
|
|
12
|
+
const escapeXml = (s) => s.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
12
13
|
/**
|
|
13
14
|
* Loader for agent skills.
|
|
14
15
|
* Skills are markdown files (SKILL.md) that teach the agent specific capabilities.
|
|
@@ -49,9 +50,9 @@ var SkillsLoader = class {
|
|
|
49
50
|
/** Load a skill by name. */
|
|
50
51
|
loadSkill(name) {
|
|
51
52
|
const wsSkill = join(this.workspaceSkills, name, "SKILL.md");
|
|
52
|
-
if (existsSync(wsSkill)) return readFileSync(wsSkill, "
|
|
53
|
+
if (existsSync(wsSkill)) return readFileSync(wsSkill, "utf8");
|
|
53
54
|
const builtinSkill = join(this.builtinSkills, name, "SKILL.md");
|
|
54
|
-
if (existsSync(builtinSkill)) return readFileSync(builtinSkill, "
|
|
55
|
+
if (existsSync(builtinSkill)) return readFileSync(builtinSkill, "utf8");
|
|
55
56
|
return null;
|
|
56
57
|
}
|
|
57
58
|
/** Load specific skills for inclusion in agent context. */
|
|
@@ -70,7 +71,6 @@ var SkillsLoader = class {
|
|
|
70
71
|
buildSkillsSummary() {
|
|
71
72
|
const allSkills = this.listSkills(false);
|
|
72
73
|
if (allSkills.length === 0) return "";
|
|
73
|
-
const escapeXml = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
74
74
|
const lines = ["<skills>"];
|
|
75
75
|
for (const s of allSkills) {
|
|
76
76
|
const name = escapeXml(s.name);
|
|
@@ -111,7 +111,7 @@ var SkillsLoader = class {
|
|
|
111
111
|
const colonIdx = line.indexOf(":");
|
|
112
112
|
if (colonIdx !== -1) {
|
|
113
113
|
const key = line.slice(0, colonIdx).trim();
|
|
114
|
-
metadata[key] = line.slice(colonIdx + 1).trim().
|
|
114
|
+
metadata[key] = line.slice(colonIdx + 1).trim().replaceAll(/^["']|["']$/g, "");
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
return metadata;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skills.mjs","names":[],"sources":["../../src/agent/skills.ts"],"sourcesContent":["import { readFileSync, existsSync, readdirSync, statSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { which } from \"../utils/which.js\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/** Default builtin skills directory (relative to this file in dist). */\nexport function getBuiltinSkillsDir(): string {\n // In dist: dist/agent/skills.js -> ../../skills\n // In src: src/agent/skills.ts -> ../../skills\n return join(dirname(dirname(__dirname)), \"skills\");\n}\n\ninterface SkillInfo {\n name: string;\n path: string;\n source: \"workspace\" | \"builtin\";\n}\n\ninterface SkillMeta {\n description?: string;\n always?: string;\n metadata?: string;\n [key: string]: string | undefined;\n}\n\n/**\n * Loader for agent skills.\n * Skills are markdown files (SKILL.md) that teach the agent specific capabilities.\n */\nexport class SkillsLoader {\n private workspace: string;\n private workspaceSkills: string;\n private builtinSkills: string;\n\n constructor(workspace: string, builtinSkillsDir?: string) {\n this.workspace = workspace;\n this.workspaceSkills = join(workspace, \"skills\");\n this.builtinSkills = builtinSkillsDir ?? getBuiltinSkillsDir();\n }\n\n /** List all available skills. */\n listSkills(filterUnavailable = true): SkillInfo[] {\n const skills: SkillInfo[] = [];\n\n // Workspace skills (highest priority)\n if (existsSync(this.workspaceSkills)) {\n for (const entry of readdirSync(this.workspaceSkills)) {\n const skillDir = join(this.workspaceSkills, entry);\n const skillFile = join(skillDir, \"SKILL.md\");\n if (statSync(skillDir).isDirectory() && existsSync(skillFile)) {\n skills.push({ name: entry, path: skillFile, source: \"workspace\" });\n }\n }\n }\n\n // Built-in skills\n if (existsSync(this.builtinSkills)) {\n for (const entry of readdirSync(this.builtinSkills)) {\n const skillDir = join(this.builtinSkills, entry);\n const skillFile = join(skillDir, \"SKILL.md\");\n if (\n statSync(skillDir).isDirectory() &&\n existsSync(skillFile) &&\n !skills.some((s) => s.name === entry)\n ) {\n skills.push({ name: entry, path: skillFile, source: \"builtin\" });\n }\n }\n }\n\n if (filterUnavailable) {\n return skills.filter((s) =>\n this.checkRequirements(this.getSkillMeta(s.name)),\n );\n }\n return skills;\n }\n\n /** Load a skill by name. */\n loadSkill(name: string): string | null {\n const wsSkill = join(this.workspaceSkills, name, \"SKILL.md\");\n if (existsSync(wsSkill)) {\n return readFileSync(wsSkill, \"utf-8\");\n }\n\n const builtinSkill = join(this.builtinSkills, name, \"SKILL.md\");\n if (existsSync(builtinSkill)) {\n return readFileSync(builtinSkill, \"utf-8\");\n }\n\n return null;\n }\n\n /** Load specific skills for inclusion in agent context. */\n loadSkillsForContext(skillNames: string[]): string {\n const parts: string[] = [];\n for (const name of skillNames) {\n const content = this.loadSkill(name);\n if (content) {\n const stripped = this.stripFrontmatter(content);\n parts.push(`### Skill: ${name}\\n\\n${stripped}`);\n }\n }\n return parts.join(\"\\n\\n---\\n\\n\");\n }\n\n /** Build a summary of all skills. */\n buildSkillsSummary(): string {\n const allSkills = this.listSkills(false);\n if (allSkills.length === 0) return \"\";\n\n const escapeXml = (s: string) =>\n s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n\n const lines = [\"<skills>\"];\n for (const s of allSkills) {\n const name = escapeXml(s.name);\n const desc = escapeXml(this.getSkillDescription(s.name));\n const meta = this.getSkillMeta(s.name);\n const available = this.checkRequirements(meta);\n\n lines.push(` <skill available=\"${available}\">`);\n lines.push(` <name>${name}</name>`);\n lines.push(` <description>${desc}</description>`);\n lines.push(` <location>${s.path}</location>`);\n\n if (!available) {\n const missing = this.getMissingRequirements(meta);\n if (missing) {\n lines.push(` <requires>${escapeXml(missing)}</requires>`);\n }\n }\n\n lines.push(\" </skill>\");\n }\n lines.push(\"</skills>\");\n return lines.join(\"\\n\");\n }\n\n /** Get skills marked as always=true. */\n getAlwaysSkills(): string[] {\n const result: string[] = [];\n for (const s of this.listSkills(true)) {\n const meta = this.getSkillMetadata(s.name);\n const skillMeta = this.parseNanobotMetadata(meta?.metadata ?? \"\");\n if (skillMeta.always || meta?.always) {\n result.push(s.name);\n }\n }\n return result;\n }\n\n /** Get metadata from a skill's frontmatter. */\n getSkillMetadata(name: string): SkillMeta | null {\n const content = this.loadSkill(name);\n if (!content) return null;\n\n if (content.startsWith(\"---\")) {\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (match) {\n const metadata: SkillMeta = {};\n for (const line of match[1].split(\"\\n\")) {\n const colonIdx = line.indexOf(\":\");\n if (colonIdx !== -1) {\n const key = line.slice(0, colonIdx).trim();\n const value = line.slice(colonIdx + 1).trim().replace(/^[\"']|[\"']$/g, \"\");\n metadata[key] = value;\n }\n }\n return metadata;\n }\n }\n return null;\n }\n\n private stripFrontmatter(content: string): string {\n if (content.startsWith(\"---\")) {\n const match = content.match(/^---\\n[\\s\\S]*?\\n---\\n/);\n if (match) return content.slice(match[0].length).trim();\n }\n return content;\n }\n\n private parseNanobotMetadata(raw: string): Record<string, unknown> {\n try {\n const data = JSON.parse(raw);\n return typeof data === \"object\" && data !== null\n ? (data.nanobot ?? {})\n : {};\n } catch {\n return {};\n }\n }\n\n private checkRequirements(meta: Record<string, unknown>): boolean {\n const requires = (meta.requires ?? {}) as Record<string, string[]>;\n for (const bin of requires.bins ?? []) {\n if (!which(bin)) return false;\n }\n for (const env of requires.env ?? []) {\n if (!process.env[env]) return false;\n }\n return true;\n }\n\n private getSkillMeta(name: string): Record<string, unknown> {\n const meta = this.getSkillMetadata(name) ?? {};\n return this.parseNanobotMetadata(String(meta.metadata ?? \"\"));\n }\n\n private getSkillDescription(name: string): string {\n const meta = this.getSkillMetadata(name);\n return meta?.description ?? name;\n }\n\n private getMissingRequirements(meta: Record<string, unknown>): string {\n const missing: string[] = [];\n const requires = (meta.requires ?? {}) as Record<string, string[]>;\n for (const bin of requires.bins ?? []) {\n if (!which(bin)) missing.push(`CLI: ${bin}`);\n }\n for (const env of requires.env ?? []) {\n if (!process.env[env]) missing.push(`ENV: ${env}`);\n }\n return missing.join(\", \");\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"skills.mjs","names":[],"sources":["../../src/agent/skills.ts"],"sourcesContent":["import { readFileSync, existsSync, readdirSync, statSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { which } from \"../utils/which.js\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/** Default builtin skills directory (relative to this file in dist). */\nexport function getBuiltinSkillsDir(): string {\n // In dist: dist/agent/skills.js -> ../../skills\n // In src: src/agent/skills.ts -> ../../skills\n return join(dirname(dirname(__dirname)), \"skills\");\n}\n\ninterface SkillInfo {\n name: string;\n path: string;\n source: \"workspace\" | \"builtin\";\n}\n\ninterface SkillMeta {\n description?: string;\n always?: string;\n metadata?: string;\n [key: string]: string | undefined;\n}\n\nconst escapeXml = (s: string) =>\n s.replaceAll(\"&\", \"&\").replaceAll(\"<\", \"<\").replaceAll(\">\", \">\");\n/**\n * Loader for agent skills.\n * Skills are markdown files (SKILL.md) that teach the agent specific capabilities.\n */\nexport class SkillsLoader {\n private workspace: string;\n private workspaceSkills: string;\n private builtinSkills: string;\n\n constructor(workspace: string, builtinSkillsDir?: string) {\n this.workspace = workspace;\n this.workspaceSkills = join(workspace, \"skills\");\n this.builtinSkills = builtinSkillsDir ?? getBuiltinSkillsDir();\n }\n\n /** List all available skills. */\n listSkills(filterUnavailable = true): SkillInfo[] {\n const skills: SkillInfo[] = [];\n\n // Workspace skills (highest priority)\n if (existsSync(this.workspaceSkills)) {\n for (const entry of readdirSync(this.workspaceSkills)) {\n const skillDir = join(this.workspaceSkills, entry);\n const skillFile = join(skillDir, \"SKILL.md\");\n if (statSync(skillDir).isDirectory() && existsSync(skillFile)) {\n skills.push({ name: entry, path: skillFile, source: \"workspace\" });\n }\n }\n }\n\n // Built-in skills\n if (existsSync(this.builtinSkills)) {\n for (const entry of readdirSync(this.builtinSkills)) {\n const skillDir = join(this.builtinSkills, entry);\n const skillFile = join(skillDir, \"SKILL.md\");\n if (\n statSync(skillDir).isDirectory() &&\n existsSync(skillFile) &&\n !skills.some((s) => s.name === entry)\n ) {\n skills.push({ name: entry, path: skillFile, source: \"builtin\" });\n }\n }\n }\n\n if (filterUnavailable) {\n return skills.filter((s) =>\n this.checkRequirements(this.getSkillMeta(s.name))\n );\n }\n return skills;\n }\n\n /** Load a skill by name. */\n loadSkill(name: string): string | null {\n const wsSkill = join(this.workspaceSkills, name, \"SKILL.md\");\n if (existsSync(wsSkill)) {\n return readFileSync(wsSkill, \"utf8\");\n }\n\n const builtinSkill = join(this.builtinSkills, name, \"SKILL.md\");\n if (existsSync(builtinSkill)) {\n return readFileSync(builtinSkill, \"utf8\");\n }\n\n return null;\n }\n\n /** Load specific skills for inclusion in agent context. */\n loadSkillsForContext(skillNames: string[]): string {\n const parts: string[] = [];\n for (const name of skillNames) {\n const content = this.loadSkill(name);\n if (content) {\n const stripped = this.stripFrontmatter(content);\n parts.push(`### Skill: ${name}\\n\\n${stripped}`);\n }\n }\n return parts.join(\"\\n\\n---\\n\\n\");\n }\n\n /** Build a summary of all skills. */\n buildSkillsSummary(): string {\n const allSkills = this.listSkills(false);\n if (allSkills.length === 0) {\n return \"\";\n }\n\n const lines = [\"<skills>\"];\n for (const s of allSkills) {\n const name = escapeXml(s.name);\n const desc = escapeXml(this.getSkillDescription(s.name));\n const meta = this.getSkillMeta(s.name);\n const available = this.checkRequirements(meta);\n\n lines.push(` <skill available=\"${available}\">`);\n lines.push(` <name>${name}</name>`);\n lines.push(` <description>${desc}</description>`);\n lines.push(` <location>${s.path}</location>`);\n\n if (!available) {\n const missing = this.getMissingRequirements(meta);\n if (missing) {\n lines.push(` <requires>${escapeXml(missing)}</requires>`);\n }\n }\n\n lines.push(\" </skill>\");\n }\n lines.push(\"</skills>\");\n return lines.join(\"\\n\");\n }\n\n /** Get skills marked as always=true. */\n getAlwaysSkills(): string[] {\n const result: string[] = [];\n for (const s of this.listSkills(true)) {\n const meta = this.getSkillMetadata(s.name);\n const skillMeta = this.parseNanobotMetadata(meta?.metadata ?? \"\");\n if (skillMeta.always || meta?.always) {\n result.push(s.name);\n }\n }\n return result;\n }\n\n /** Get metadata from a skill's frontmatter. */\n getSkillMetadata(name: string): SkillMeta | null {\n const content = this.loadSkill(name);\n if (!content) {\n return null;\n }\n\n if (content.startsWith(\"---\")) {\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (match) {\n const metadata: SkillMeta = {};\n for (const line of match[1].split(\"\\n\")) {\n const colonIdx = line.indexOf(\":\");\n if (colonIdx !== -1) {\n const key = line.slice(0, colonIdx).trim();\n const value = line\n .slice(colonIdx + 1)\n .trim()\n .replaceAll(/^[\"']|[\"']$/g, \"\");\n metadata[key] = value;\n }\n }\n return metadata;\n }\n }\n return null;\n }\n\n private stripFrontmatter(content: string): string {\n if (content.startsWith(\"---\")) {\n const match = content.match(/^---\\n[\\s\\S]*?\\n---\\n/);\n if (match) {\n return content.slice(match[0].length).trim();\n }\n }\n return content;\n }\n\n private parseNanobotMetadata(raw: string): Record<string, unknown> {\n try {\n const data = JSON.parse(raw);\n return typeof data === \"object\" && data !== null\n ? (data.nanobot ?? {})\n : {};\n } catch {\n return {};\n }\n }\n\n private checkRequirements(meta: Record<string, unknown>): boolean {\n const requires = (meta.requires ?? {}) as Record<string, string[]>;\n for (const bin of requires.bins ?? []) {\n if (!which(bin)) {\n return false;\n }\n }\n for (const env of requires.env ?? []) {\n if (!process.env[env]) {\n return false;\n }\n }\n return true;\n }\n\n private getSkillMeta(name: string): Record<string, unknown> {\n const meta = this.getSkillMetadata(name) ?? {};\n return this.parseNanobotMetadata(String(meta.metadata ?? \"\"));\n }\n\n private getSkillDescription(name: string): string {\n const meta = this.getSkillMetadata(name);\n return meta?.description ?? name;\n }\n\n private getMissingRequirements(meta: Record<string, unknown>): string {\n const missing: string[] = [];\n const requires = (meta.requires ?? {}) as Record<string, string[]>;\n for (const bin of requires.bins ?? []) {\n if (!which(bin)) {\n missing.push(`CLI: ${bin}`);\n }\n }\n for (const env of requires.env ?? []) {\n if (!process.env[env]) {\n missing.push(`ENV: ${env}`);\n }\n }\n return missing.join(\", \");\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subagent.d.mts","names":[],"sources":["../../src/agent/subagent.ts"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"subagent.d.mts","names":[],"sources":["../../src/agent/subagent.ts"],"mappings":";;;;;;;AAoBA;cAAa,eAAA;EAAA,QACH,QAAA;EAAA,QACA,SAAA;EAAA,QACA,GAAA;EAAA,QACA,KAAA;EAAA,QACA,WAAA;EAAA,QACA,UAAA;EAAA,QACA,mBAAA;EAAA,QACA,YAAA;cAEI,MAAA;IACV,QAAA,EAAU,WAAA;IACV,SAAA;IACA,GAAA,EAAK,UAAA;IACL,KAAA;IACA,WAAA;IACA,UAAA,GAAa,cAAA;IACb,mBAAA;EAAA;EANU;EAkBN,KAAA,CAAM,MAAA;IACV,IAAA;IACA,KAAA;IACA,aAAA;IACA,YAAA;EAAA,IACE,OAAA;EAAA,QAyBU,WAAA;EAAA,QAkGA,cAAA;EAAA,QA6BN,mBAAA;EAAA,IA+CJ,YAAA,CAAA;AAAA"}
|
package/dist/agent/subagent.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createInboundMessage } from "../bus/events.mjs";
|
|
2
2
|
import { SkillsLoader, getBuiltinSkillsDir } from "./skills.mjs";
|
|
3
|
-
import { ToolRegistry } from "./tools/registry.mjs";
|
|
4
3
|
import { ListDirTool, ReadFileTool, WriteFileTool } from "./tools/filesystem.mjs";
|
|
4
|
+
import { ToolRegistry } from "./tools/registry.mjs";
|
|
5
5
|
import { ExecTool } from "./tools/shell.mjs";
|
|
6
6
|
import { WebFetchTool, WebSearchTool } from "./tools/web.mjs";
|
|
7
7
|
import { randomUUID } from "node:crypto";
|
|
@@ -58,39 +58,39 @@ var SubagentManager = class {
|
|
|
58
58
|
readOnlyPaths
|
|
59
59
|
}));
|
|
60
60
|
tools.register(new ExecTool({
|
|
61
|
-
|
|
61
|
+
restrictToWorkspace: this.restrictToWorkspace,
|
|
62
62
|
timeout: this.execConfig.timeout,
|
|
63
|
-
|
|
63
|
+
workingDir: this.workspace
|
|
64
64
|
}));
|
|
65
65
|
tools.register(new WebSearchTool({ apiKey: this.braveApiKey }));
|
|
66
66
|
tools.register(new WebFetchTool());
|
|
67
67
|
const messages = [{
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
content: this.buildSubagentPrompt(task),
|
|
69
|
+
role: "system"
|
|
70
70
|
}, {
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
content: task,
|
|
72
|
+
role: "user"
|
|
73
73
|
}];
|
|
74
74
|
const maxIterations = 15;
|
|
75
75
|
let finalResult = null;
|
|
76
|
-
for (let i = 0; i < maxIterations; i
|
|
76
|
+
for (let i = 0; i < maxIterations; i += 1) {
|
|
77
77
|
const response = await this.provider.chat({
|
|
78
78
|
messages,
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
model: this.model,
|
|
80
|
+
tools: tools.getDefinitions()
|
|
81
81
|
});
|
|
82
82
|
if (response.hasToolCalls) {
|
|
83
83
|
const toolCallDicts = response.toolCalls.map((tc) => ({
|
|
84
|
-
id: tc.id,
|
|
85
|
-
type: "function",
|
|
86
84
|
function: {
|
|
87
85
|
name: tc.name,
|
|
88
86
|
arguments: JSON.stringify(tc.arguments)
|
|
89
|
-
}
|
|
87
|
+
},
|
|
88
|
+
id: tc.id,
|
|
89
|
+
type: "function"
|
|
90
90
|
}));
|
|
91
91
|
messages.push({
|
|
92
|
-
role: "assistant",
|
|
93
92
|
content: response.content ?? "",
|
|
93
|
+
role: "assistant",
|
|
94
94
|
tool_calls: toolCallDicts
|
|
95
95
|
});
|
|
96
96
|
for (const tc of response.toolCalls) {
|
|
@@ -101,10 +101,10 @@ var SubagentManager = class {
|
|
|
101
101
|
}
|
|
102
102
|
const result = await tools.execute(tc.name, tc.arguments);
|
|
103
103
|
messages.push({
|
|
104
|
-
|
|
105
|
-
tool_call_id: tc.id,
|
|
104
|
+
content: result,
|
|
106
105
|
name: tc.name,
|
|
107
|
-
|
|
106
|
+
role: "tool",
|
|
107
|
+
tool_call_id: tc.id
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
110
|
} else {
|
|
@@ -115,9 +115,9 @@ var SubagentManager = class {
|
|
|
115
115
|
finalResult ??= "Task completed but no final response was generated.";
|
|
116
116
|
console.log(`Subagent [${taskId}] completed successfully`);
|
|
117
117
|
await this.announceResult(taskId, label, task, finalResult, origin, "ok");
|
|
118
|
-
} catch (
|
|
119
|
-
const errorMsg = `Error: ${
|
|
120
|
-
console.error(`Subagent [${taskId}] failed:`,
|
|
118
|
+
} catch (error) {
|
|
119
|
+
const errorMsg = `Error: ${error instanceof Error ? error.message : error}`;
|
|
120
|
+
console.error(`Subagent [${taskId}] failed:`, error);
|
|
121
121
|
await this.announceResult(taskId, label, task, errorMsg, origin, "error");
|
|
122
122
|
}
|
|
123
123
|
}
|
|
@@ -132,9 +132,9 @@ ${result}
|
|
|
132
132
|
Summarize this naturally for the user. Keep it brief (1-2 sentences). Do not mention technical details like "subagent" or task IDs.`;
|
|
133
133
|
const msg = createInboundMessage({
|
|
134
134
|
channel: "system",
|
|
135
|
-
senderId: "subagent",
|
|
136
135
|
chatId: `${origin.channel}:${origin.chatId}`,
|
|
137
|
-
content
|
|
136
|
+
content,
|
|
137
|
+
senderId: "subagent"
|
|
138
138
|
});
|
|
139
139
|
await this.bus.publishInbound(msg);
|
|
140
140
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subagent.mjs","names":[],"sources":["../../src/agent/subagent.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport type { LLMProvider, ChatMessage } from \"../providers/base.js\";\nimport type { MessageBus } from \"../bus/queue.js\";\nimport { createInboundMessage } from \"../bus/events.js\";\nimport { ToolRegistry } from \"./tools/registry.js\";\nimport { ReadFileTool, WriteFileTool, ListDirTool } from \"./tools/filesystem.js\";\nimport { ExecTool } from \"./tools/shell.js\";\nimport { WebSearchTool, WebFetchTool } from \"./tools/web.js\";\nimport { getBuiltinSkillsDir, SkillsLoader } from \"./skills.js\";\nimport type { ExecToolConfig } from \"../config/schema.js\";\n\n/**\n * Manages background subagent execution.\n */\nexport class SubagentManager {\n private provider: LLMProvider;\n private workspace: string;\n private bus: MessageBus;\n private model: string;\n private braveApiKey?: string;\n private execConfig: ExecToolConfig;\n private restrictToWorkspace: boolean;\n private runningTasks = new Map<string, AbortController>();\n\n constructor(params: {\n provider: LLMProvider;\n workspace: string;\n bus: MessageBus;\n model?: string;\n braveApiKey?: string;\n execConfig?: ExecToolConfig;\n restrictToWorkspace?: boolean;\n }) {\n this.provider = params.provider;\n this.workspace = params.workspace;\n this.bus = params.bus;\n this.model = params.model ?? params.provider.getDefaultModel();\n this.braveApiKey = params.braveApiKey;\n this.execConfig = params.execConfig ?? { timeout: 60 };\n this.restrictToWorkspace = params.restrictToWorkspace ?? false;\n }\n\n /** Spawn a subagent to execute a task in the background. */\n async spawn(params: {\n task: string;\n label?: string;\n originChannel?: string;\n originChatId?: string;\n }): Promise<string> {\n const taskId = randomUUID().slice(0, 8);\n const displayLabel =\n params.label ??\n (params.task.length > 30\n ? params.task.slice(0, 30) + \"...\"\n : params.task);\n\n const origin = {\n channel: params.originChannel ?? \"cli\",\n chatId: params.originChatId ?? \"direct\",\n };\n\n const controller = new AbortController();\n this.runningTasks.set(taskId, controller);\n\n // Run in background (don't await)\n this.runSubagent(taskId, params.task, displayLabel, origin)\n .finally(() => this.runningTasks.delete(taskId));\n\n console.log(`Spawned subagent [${taskId}]: ${displayLabel}`);\n return `Subagent [${displayLabel}] started (id: ${taskId}). I'll notify you when it completes.`;\n }\n\n private async runSubagent(\n taskId: string,\n task: string,\n label: string,\n origin: { channel: string; chatId: string },\n ): Promise<void> {\n console.log(`Subagent [${taskId}] starting task: ${label}`);\n\n try {\n // Build subagent tools (no message, no spawn)\n const tools = new ToolRegistry();\n const allowedDir = this.restrictToWorkspace ? this.workspace : undefined;\n const readOnlyPaths = this.restrictToWorkspace ? [getBuiltinSkillsDir()] : undefined;\n tools.register(new ReadFileTool({ allowedDir, readOnlyPaths }));\n tools.register(new WriteFileTool({ allowedDir }));\n tools.register(new ListDirTool({ allowedDir, readOnlyPaths }));\n tools.register(\n new ExecTool({\n workingDir: this.workspace,\n timeout: this.execConfig.timeout,\n restrictToWorkspace: this.restrictToWorkspace,\n }),\n );\n tools.register(new WebSearchTool({ apiKey: this.braveApiKey }));\n tools.register(new WebFetchTool());\n\n const systemPrompt = this.buildSubagentPrompt(task);\n const messages: ChatMessage[] = [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: task },\n ];\n\n const maxIterations = 15;\n let finalResult: string | null = null;\n\n for (let i = 0; i < maxIterations; i++) {\n const response = await this.provider.chat({\n messages,\n tools: tools.getDefinitions(),\n model: this.model,\n });\n\n if (response.hasToolCalls) {\n const toolCallDicts = response.toolCalls.map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: {\n name: tc.name,\n arguments: JSON.stringify(tc.arguments),\n },\n }));\n\n messages.push({\n role: \"assistant\",\n content: response.content ?? \"\",\n tool_calls: toolCallDicts,\n });\n\n for (const tc of response.toolCalls) {\n console.log(`Subagent [${taskId}] Tool: ${tc.name}(${JSON.stringify(tc.arguments)})`);\n\n // Detect skill reads\n if (tc.name === \"read_file\") {\n const path = String(tc.arguments?.path ?? \"\");\n const skillMatch = path.match(/skills\\/([^/]+)\\/SKILL\\.md$/);\n if (skillMatch) {\n console.log(`Skill activated: ${skillMatch[1]}`);\n }\n }\n \n const result = await tools.execute(tc.name, tc.arguments);\n messages.push({\n role: \"tool\",\n tool_call_id: tc.id,\n name: tc.name,\n content: result,\n });\n }\n } else {\n finalResult = response.content;\n break;\n }\n }\n\n finalResult ??= \"Task completed but no final response was generated.\";\n console.log(`Subagent [${taskId}] completed successfully`);\n await this.announceResult(taskId, label, task, finalResult, origin, \"ok\");\n } catch (err) {\n const errorMsg = `Error: ${err instanceof Error ? err.message : err}`;\n console.error(`Subagent [${taskId}] failed:`, err);\n await this.announceResult(\n taskId,\n label,\n task,\n errorMsg,\n origin,\n \"error\",\n );\n }\n }\n\n private async announceResult(\n taskId: string,\n label: string,\n task: string,\n result: string,\n origin: { channel: string; chatId: string },\n status: string,\n ): Promise<void> {\n const statusText =\n status === \"ok\" ? \"completed successfully\" : \"failed\";\n\n const content = `[Subagent '${label}' ${statusText}]\n\nTask: ${task}\n\nResult:\n${result}\n\nSummarize this naturally for the user. Keep it brief (1-2 sentences). Do not mention technical details like \"subagent\" or task IDs.`;\n\n const msg = createInboundMessage({\n channel: \"system\",\n senderId: \"subagent\",\n chatId: `${origin.channel}:${origin.chatId}`,\n content,\n });\n\n await this.bus.publishInbound(msg);\n }\n\n private buildSubagentPrompt(task: string): string {\n const skills = new SkillsLoader(this.workspace);\n const alwaysSkills = skills.getAlwaysSkills();\n const alwaysContent = alwaysSkills.length > 0\n ? skills.loadSkillsForContext(alwaysSkills)\n : \"\";\n const skillsSummary = skills.buildSkillsSummary();\n\n let prompt = `# Subagent\n\nYou are a subagent spawned by the main agent to complete a specific task.\n\n## Your Task\n${task}\n\n## Rules\n1. Stay focused - complete only the assigned task, nothing else\n2. Your final response will be reported back to the main agent\n3. Do not initiate conversations or take on side tasks\n4. Be concise but informative in your findings\n\n## What You Can Do\n- Read and write files in the workspace\n- Execute shell commands\n- Search the web and fetch web pages\n- Complete the task thoroughly\n\n## What You Cannot Do\n- Send messages directly to users (no message tool available)\n- Spawn other subagents\n- Access the main agent's conversation history\n\n## Workspace\nYour workspace is at: ${this.workspace}`;\n\n if (alwaysContent) {\n prompt += `\\n\\n## Active Skills\\n\\n${alwaysContent}`;\n }\n\n if (skillsSummary) {\n prompt += `\\n\\n## Skills (mandatory)\\n\\nBefore starting, scan the <skills> entries below.\\n- If a skill clearly applies to the task: read its SKILL.md at the <location> path using read_file, then follow its instructions.\\n- If no skill applies: proceed normally.\\n\\n${skillsSummary}`;\n }\n\n prompt += `\\n\\nWhen you have completed the task, provide a clear summary of your findings or actions.`;\n\n return prompt;\n }\n\n get runningCount(): number {\n return this.runningTasks.size;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"subagent.mjs","names":[],"sources":["../../src/agent/subagent.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\n\nimport type { MessageBus } from \"../bus/queue.js\";\nimport type { ExecToolConfig } from \"../config/schema.js\";\nimport type { LLMProvider, ChatMessage } from \"../providers/base.js\";\n\nimport { createInboundMessage } from \"../bus/events.js\";\nimport { getBuiltinSkillsDir, SkillsLoader } from \"./skills.js\";\nimport {\n ReadFileTool,\n WriteFileTool,\n ListDirTool,\n} from \"./tools/filesystem.js\";\nimport { ToolRegistry } from \"./tools/registry.js\";\nimport { ExecTool } from \"./tools/shell.js\";\nimport { WebSearchTool, WebFetchTool } from \"./tools/web.js\";\n\n/**\n * Manages background subagent execution.\n */\nexport class SubagentManager {\n private provider: LLMProvider;\n private workspace: string;\n private bus: MessageBus;\n private model: string;\n private braveApiKey?: string;\n private execConfig: ExecToolConfig;\n private restrictToWorkspace: boolean;\n private runningTasks = new Map<string, AbortController>();\n\n constructor(params: {\n provider: LLMProvider;\n workspace: string;\n bus: MessageBus;\n model?: string;\n braveApiKey?: string;\n execConfig?: ExecToolConfig;\n restrictToWorkspace?: boolean;\n }) {\n this.provider = params.provider;\n this.workspace = params.workspace;\n this.bus = params.bus;\n this.model = params.model ?? params.provider.getDefaultModel();\n this.braveApiKey = params.braveApiKey;\n this.execConfig = params.execConfig ?? { timeout: 60 };\n this.restrictToWorkspace = params.restrictToWorkspace ?? false;\n }\n\n /** Spawn a subagent to execute a task in the background. */\n async spawn(params: {\n task: string;\n label?: string;\n originChannel?: string;\n originChatId?: string;\n }): Promise<string> {\n const taskId = randomUUID().slice(0, 8);\n const displayLabel =\n params.label ??\n (params.task.length > 30\n ? params.task.slice(0, 30) + \"...\"\n : params.task);\n\n const origin = {\n channel: params.originChannel ?? \"cli\",\n chatId: params.originChatId ?? \"direct\",\n };\n\n const controller = new AbortController();\n this.runningTasks.set(taskId, controller);\n\n // Run in background (don't await)\n this.runSubagent(taskId, params.task, displayLabel, origin).finally(() =>\n this.runningTasks.delete(taskId)\n );\n\n console.log(`Spawned subagent [${taskId}]: ${displayLabel}`);\n return `Subagent [${displayLabel}] started (id: ${taskId}). I'll notify you when it completes.`;\n }\n\n private async runSubagent(\n taskId: string,\n task: string,\n label: string,\n origin: { channel: string; chatId: string }\n ): Promise<void> {\n console.log(`Subagent [${taskId}] starting task: ${label}`);\n\n try {\n // Build subagent tools (no message, no spawn)\n const tools = new ToolRegistry();\n const allowedDir = this.restrictToWorkspace ? this.workspace : undefined;\n const readOnlyPaths = this.restrictToWorkspace\n ? [getBuiltinSkillsDir()]\n : undefined;\n tools.register(new ReadFileTool({ allowedDir, readOnlyPaths }));\n tools.register(new WriteFileTool({ allowedDir }));\n tools.register(new ListDirTool({ allowedDir, readOnlyPaths }));\n tools.register(\n new ExecTool({\n restrictToWorkspace: this.restrictToWorkspace,\n timeout: this.execConfig.timeout,\n workingDir: this.workspace,\n })\n );\n tools.register(new WebSearchTool({ apiKey: this.braveApiKey }));\n tools.register(new WebFetchTool());\n\n const systemPrompt = this.buildSubagentPrompt(task);\n const messages: ChatMessage[] = [\n { content: systemPrompt, role: \"system\" },\n { content: task, role: \"user\" },\n ];\n\n const maxIterations = 15;\n let finalResult: string | null = null;\n\n for (let i = 0; i < maxIterations; i += 1) {\n const response = await this.provider.chat({\n messages,\n model: this.model,\n tools: tools.getDefinitions(),\n });\n\n if (response.hasToolCalls) {\n const toolCallDicts = response.toolCalls.map((tc) => ({\n function: {\n name: tc.name,\n arguments: JSON.stringify(tc.arguments),\n },\n id: tc.id,\n type: \"function\" as const,\n }));\n\n messages.push({\n content: response.content ?? \"\",\n role: \"assistant\",\n tool_calls: toolCallDicts,\n });\n\n for (const tc of response.toolCalls) {\n console.log(\n `Subagent [${taskId}] Tool: ${tc.name}(${JSON.stringify(tc.arguments)})`\n );\n\n // Detect skill reads\n if (tc.name === \"read_file\") {\n const path = String(tc.arguments?.path ?? \"\");\n const skillMatch = path.match(/skills\\/([^/]+)\\/SKILL\\.md$/);\n if (skillMatch) {\n console.log(`Skill activated: ${skillMatch[1]}`);\n }\n }\n\n const result = await tools.execute(tc.name, tc.arguments);\n messages.push({\n content: result,\n name: tc.name,\n role: \"tool\",\n tool_call_id: tc.id,\n });\n }\n } else {\n finalResult = response.content;\n break;\n }\n }\n\n finalResult ??= \"Task completed but no final response was generated.\";\n console.log(`Subagent [${taskId}] completed successfully`);\n await this.announceResult(taskId, label, task, finalResult, origin, \"ok\");\n } catch (error) {\n const errorMsg = `Error: ${error instanceof Error ? error.message : error}`;\n console.error(`Subagent [${taskId}] failed:`, error);\n await this.announceResult(taskId, label, task, errorMsg, origin, \"error\");\n }\n }\n\n private async announceResult(\n taskId: string,\n label: string,\n task: string,\n result: string,\n origin: { channel: string; chatId: string },\n status: string\n ): Promise<void> {\n const statusText = status === \"ok\" ? \"completed successfully\" : \"failed\";\n\n const content = `[Subagent '${label}' ${statusText}]\n\nTask: ${task}\n\nResult:\n${result}\n\nSummarize this naturally for the user. Keep it brief (1-2 sentences). Do not mention technical details like \"subagent\" or task IDs.`;\n\n const msg = createInboundMessage({\n channel: \"system\",\n chatId: `${origin.channel}:${origin.chatId}`,\n content,\n senderId: \"subagent\",\n });\n\n await this.bus.publishInbound(msg);\n }\n\n private buildSubagentPrompt(task: string): string {\n const skills = new SkillsLoader(this.workspace);\n const alwaysSkills = skills.getAlwaysSkills();\n const alwaysContent =\n alwaysSkills.length > 0 ? skills.loadSkillsForContext(alwaysSkills) : \"\";\n const skillsSummary = skills.buildSkillsSummary();\n\n let prompt = `# Subagent\n\nYou are a subagent spawned by the main agent to complete a specific task.\n\n## Your Task\n${task}\n\n## Rules\n1. Stay focused - complete only the assigned task, nothing else\n2. Your final response will be reported back to the main agent\n3. Do not initiate conversations or take on side tasks\n4. Be concise but informative in your findings\n\n## What You Can Do\n- Read and write files in the workspace\n- Execute shell commands\n- Search the web and fetch web pages\n- Complete the task thoroughly\n\n## What You Cannot Do\n- Send messages directly to users (no message tool available)\n- Spawn other subagents\n- Access the main agent's conversation history\n\n## Workspace\nYour workspace is at: ${this.workspace}`;\n\n if (alwaysContent) {\n prompt += `\\n\\n## Active Skills\\n\\n${alwaysContent}`;\n }\n\n if (skillsSummary) {\n prompt += `\\n\\n## Skills (mandatory)\\n\\nBefore starting, scan the <skills> entries below.\\n- If a skill clearly applies to the task: read its SKILL.md at the <location> path using read_file, then follow its instructions.\\n- If no skill applies: proceed normally.\\n\\n${skillsSummary}`;\n }\n\n prompt += `\\n\\nWhen you have completed the task, provide a clear summary of your findings or actions.`;\n\n return prompt;\n }\n\n get runningCount(): number {\n return this.runningTasks.size;\n }\n}\n"],"mappings":"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"}
|
|
@@ -4,12 +4,12 @@ var Tool = class {
|
|
|
4
4
|
/** Get the tool definition for the LLM. */
|
|
5
5
|
getDefinition() {
|
|
6
6
|
return {
|
|
7
|
-
type: "function",
|
|
8
7
|
function: {
|
|
9
8
|
name: this.name,
|
|
10
9
|
description: this.description,
|
|
11
10
|
parameters: this.parameters
|
|
12
|
-
}
|
|
11
|
+
},
|
|
12
|
+
type: "function"
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
15
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.mjs","names":[],"sources":["../../../src/agent/tools/base.ts"],"sourcesContent":["import type { ToolDefinition } from \"../../providers/base.js\";\n\n/** Abstract base class for tools. */\nexport abstract class Tool {\n abstract readonly name: string;\n abstract readonly description: string;\n abstract readonly parameters: Record<string, unknown>;\n\n /** Execute the tool with the given arguments. */\n abstract execute(args: Record<string, unknown>): Promise<string>;\n\n /** Get the tool definition for the LLM. */\n getDefinition(): ToolDefinition {\n return {\n
|
|
1
|
+
{"version":3,"file":"base.mjs","names":[],"sources":["../../../src/agent/tools/base.ts"],"sourcesContent":["import type { ToolDefinition } from \"../../providers/base.js\";\n\n/** Abstract base class for tools. */\nexport abstract class Tool {\n abstract readonly name: string;\n abstract readonly description: string;\n abstract readonly parameters: Record<string, unknown>;\n\n /** Execute the tool with the given arguments. */\n abstract execute(args: Record<string, unknown>): Promise<string>;\n\n /** Get the tool definition for the LLM. */\n getDefinition(): ToolDefinition {\n return {\n function: {\n name: this.name,\n description: this.description,\n parameters: this.parameters,\n },\n type: \"function\",\n };\n }\n}\n"],"mappings":";;AAGA,IAAsB,OAAtB,MAA2B;;CASzB,gBAAgC;AAC9B,SAAO;GACL,UAAU;IACR,MAAM,KAAK;IACX,aAAa,KAAK;IAClB,YAAY,KAAK;IAClB;GACD,MAAM;GACP"}
|
|
@@ -6,7 +6,6 @@ declare class CronTool extends Tool {
|
|
|
6
6
|
readonly name = "cron";
|
|
7
7
|
readonly description = "Schedule one-off or recurring tasks. Actions: add, list, remove.";
|
|
8
8
|
readonly parameters: {
|
|
9
|
-
type: string;
|
|
10
9
|
properties: {
|
|
11
10
|
action: {
|
|
12
11
|
type: string;
|
|
@@ -44,6 +43,7 @@ declare class CronTool extends Tool {
|
|
|
44
43
|
};
|
|
45
44
|
};
|
|
46
45
|
required: string[];
|
|
46
|
+
type: string;
|
|
47
47
|
};
|
|
48
48
|
private channel;
|
|
49
49
|
private chatId;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cron.d.mts","names":[],"sources":["../../../src/agent/tools/cron.ts"],"mappings":";;;;cAGa,QAAA,SAAiB,IAAA;EAAA,SACnB,IAAA;EAAA,SACA,WAAA;EAAA,SAEA,UAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA6CD,OAAA;EAAA,QACA,MAAA;EAKA;EAAA,QAFA,SAAA;EAWR;EAAA,QATQ,MAAA;;EAcF;EALN,UAAA,CAAW,OAAA,UAAiB,MAAA;EAKtB,OAAA,CAAQ,IAAA,EAAM,MAAA,oBAA0B,OAAA;EAAA,
|
|
1
|
+
{"version":3,"file":"cron.d.mts","names":[],"sources":["../../../src/agent/tools/cron.ts"],"mappings":";;;;cAGa,QAAA,SAAiB,IAAA;EAAA,SACnB,IAAA;EAAA,SACA,WAAA;EAAA,SAEA,UAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA6CD,OAAA;EAAA,QACA,MAAA;EAKA;EAAA,QAFA,SAAA;EAWR;EAAA,QATQ,MAAA;;EAcF;EALN,UAAA,CAAW,OAAA,UAAiB,MAAA;EAKtB,OAAA,CAAQ,IAAA,EAAM,MAAA,oBAA0B,OAAA;EAAA,QAsBhC,MAAA;EAAA,QAiEA,QAAA;EAAA,QA4BA,SAAA;AAAA"}
|
|
@@ -6,7 +6,6 @@ var CronTool = class extends Tool {
|
|
|
6
6
|
name = "cron";
|
|
7
7
|
description = "Schedule one-off or recurring tasks. Actions: add, list, remove.";
|
|
8
8
|
parameters = {
|
|
9
|
-
type: "object",
|
|
10
9
|
properties: {
|
|
11
10
|
action: {
|
|
12
11
|
type: "string",
|
|
@@ -47,7 +46,8 @@ var CronTool = class extends Tool {
|
|
|
47
46
|
description: "Job ID (for remove)"
|
|
48
47
|
}
|
|
49
48
|
},
|
|
50
|
-
required: ["action"]
|
|
49
|
+
required: ["action"],
|
|
50
|
+
type: "object"
|
|
51
51
|
};
|
|
52
52
|
channel = "";
|
|
53
53
|
chatId = "";
|
|
@@ -87,7 +87,7 @@ var CronTool = class extends Tool {
|
|
|
87
87
|
let schedule;
|
|
88
88
|
if (atRaw) {
|
|
89
89
|
const atMs = Date.parse(atRaw);
|
|
90
|
-
if (isNaN(atMs)) return `Error: could not parse time '${atRaw}' — use ISO 8601 format`;
|
|
90
|
+
if (Number.isNaN(atMs)) return `Error: could not parse time '${atRaw}' — use ISO 8601 format`;
|
|
91
91
|
if (atMs <= Date.now()) return "Error: scheduled time is in the past";
|
|
92
92
|
schedule = atRaw;
|
|
93
93
|
} else if (everySeconds) schedule = everySeconds;
|
|
@@ -95,8 +95,6 @@ var CronTool = class extends Tool {
|
|
|
95
95
|
else return "Error: one of 'at', 'every_seconds', or 'cron_expr' is required";
|
|
96
96
|
try {
|
|
97
97
|
const res = await fetch(`${this.workerUrl}/api/users/${this.userId}/cron`, {
|
|
98
|
-
method: "POST",
|
|
99
|
-
headers: { "Content-Type": "application/json" },
|
|
100
98
|
body: JSON.stringify({
|
|
101
99
|
name: message,
|
|
102
100
|
type: everySeconds ? "every" : cronExpr ? "cron" : "scheduled",
|
|
@@ -108,13 +106,15 @@ var CronTool = class extends Tool {
|
|
|
108
106
|
chatId: this.chatId,
|
|
109
107
|
...timezone && cronExpr ? { timezone } : {}
|
|
110
108
|
}),
|
|
109
|
+
headers: { "Content-Type": "application/json" },
|
|
110
|
+
method: "POST",
|
|
111
111
|
signal: AbortSignal.timeout(1e4)
|
|
112
112
|
});
|
|
113
113
|
const data = await res.json();
|
|
114
114
|
if (!res.ok) return `Error: ${data.error ?? res.statusText}`;
|
|
115
115
|
return `Created ${kind} job (id: ${data.id})`;
|
|
116
|
-
} catch (
|
|
117
|
-
return `Error scheduling job: ${
|
|
116
|
+
} catch (error) {
|
|
117
|
+
return `Error scheduling job: ${error instanceof Error ? error.message : error}`;
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
async listJobs() {
|
|
@@ -122,8 +122,8 @@ var CronTool = class extends Tool {
|
|
|
122
122
|
const data = await (await fetch(`${this.workerUrl}/api/users/${this.userId}/cron`, { signal: AbortSignal.timeout(1e4) })).json();
|
|
123
123
|
if (data.crons.length === 0) return "No scheduled jobs.";
|
|
124
124
|
return "Scheduled jobs:\n" + data.crons.map((c) => `- ${c.name} (id: ${c.id}, ${c.type}, next: ${new Date(c.nextRunAt).toISOString()})`).join("\n");
|
|
125
|
-
} catch (
|
|
126
|
-
return `Error listing jobs: ${
|
|
125
|
+
} catch (error) {
|
|
126
|
+
return `Error listing jobs: ${error instanceof Error ? error.message : error}`;
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
async removeJob(args) {
|
|
@@ -134,8 +134,8 @@ var CronTool = class extends Tool {
|
|
|
134
134
|
method: "DELETE",
|
|
135
135
|
signal: AbortSignal.timeout(1e4)
|
|
136
136
|
})).json()).ok ? `Removed job ${jobId}` : `Job ${jobId} not found`;
|
|
137
|
-
} catch (
|
|
138
|
-
return `Error removing job: ${
|
|
137
|
+
} catch (error) {
|
|
138
|
+
return `Error removing job: ${error instanceof Error ? error.message : error}`;
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
};
|