Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- packages/agent-core-v2/docs/config-manifest.toml +490 -0
- packages/agent-core-v2/docs/state-manifest.d.ts +1270 -0
- packages/agent-core-v2/docs/wire-manifest.d.ts +946 -0
- packages/agent-core-v2/scripts/check-import-boundaries.d.mts +10 -0
- packages/agent-core-v2/scripts/check-import-boundaries.mjs +228 -0
- packages/agent-core-v2/scripts/debarrel.mjs +474 -0
- packages/agent-core-v2/scripts/gen-config-manifest.mts +314 -0
- packages/agent-core-v2/scripts/gen-contract-types.mjs +171 -0
- packages/agent-core-v2/scripts/gen-state-manifest.mts +882 -0
- packages/agent-core-v2/scripts/gen-wire-manifest.mts +1039 -0
- packages/agent-core-v2/scripts/generate-webp-dec-wasm.mjs +24 -0
- packages/agent-core-v2/src/_base/asyncEventQueue.ts +58 -0
- packages/agent-core-v2/src/_base/event.ts +281 -0
- packages/agent-core-v2/src/_base/version.ts +3 -0
- packages/agent-core-v2/src/app/scopes.ts +15 -0
- packages/agent-core-v2/src/debug/debugCascade.ts +63 -0
- packages/agent-core-v2/src/debug/debugCascadeService.ts +183 -0
- packages/agent-core-v2/src/debug/debugGraph.ts +30 -0
- packages/agent-core-v2/src/debug/debugGraphService.ts +76 -0
- packages/agent-core-v2/src/debug/debugLedger.ts +29 -0
- packages/agent-core-v2/src/debug/debugLedgerService.ts +56 -0
- packages/agent-core-v2/src/debug/errors.ts +10 -0
- packages/agent-core-v2/src/debug/index.ts +7 -0
- packages/agent-core-v2/src/debug/scopeTree.ts +38 -0
- packages/agent-core-v2/src/env.d.ts +4 -0
- packages/agent-core-v2/src/errors.ts +257 -0
- packages/agent-core-v2/src/events.ts +87 -0
- packages/agent-core-v2/src/features/feature.ts +117 -0
- packages/agent-core-v2/src/features/featureAssembly.ts +8 -0
- packages/agent-core-v2/src/features/featureAssemblyService.ts +26 -0
- packages/agent-core-v2/src/features/featureRegistry.ts +15 -0
- packages/agent-core-v2/src/hooks.ts +112 -0
- packages/agent-core-v2/src/human/index.ts +97 -0
- packages/agent-core-v2/src/human/package.json +6 -0
- packages/agent-core-v2/src/human/plugin.ts +54 -0
- packages/agent-core-v2/src/human/xstate2.ts +49 -0
- packages/agent-core-v2/src/human/xstateInspection.ts +72 -0
- packages/agent-core-v2/src/index.ts +758 -0
- packages/agent-core-v2/src/llm-adapter/record-diff.ts +49 -0
- packages/agent-core-v2/src/mcpCore/client-http.ts +169 -0
- packages/agent-core-v2/src/mcpCore/client-remote.ts +29 -0
- packages/agent-core-v2/src/mcpCore/client-shared.ts +109 -0
- packages/agent-core-v2/src/mcpCore/client-sse.ts +161 -0
- packages/agent-core-v2/src/mcpCore/client-stdio.ts +304 -0
- packages/agent-core-v2/src/mcpCore/config-schema.ts +68 -0
- packages/agent-core-v2/src/mcpCore/configView.ts +18 -0
- packages/agent-core-v2/src/mcpCore/connection-manager.ts +603 -0
- packages/agent-core-v2/src/mcpCore/errors.ts +13 -0
- packages/agent-core-v2/src/mcpCore/tool-naming.ts +26 -0
- packages/agent-core-v2/src/mcpCore/types.ts +55 -0
packages/agent-core-v2/docs/config-manifest.toml
ADDED
|
@@ -0,0 +1,490 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Config Section Manifest
|
| 2 |
+
#
|
| 3 |
+
# Generated by scripts/gen-config-manifest.mts — do not edit by hand.
|
| 4 |
+
# Regenerate with: pnpm --filter @moonshot-ai/agent-core-v2 gen:config-manifest
|
| 5 |
+
#
|
| 6 |
+
# One [table] per registered config section, in the on-disk config.toml shape
|
| 7 |
+
# (snake_case keys). Un-commented assignments are registered defaults;
|
| 8 |
+
# commented "# field: type" lines describe the remaining schema fields.
|
| 9 |
+
# Values resolve as: default -> config.toml -> env overlay -> memory.
|
| 10 |
+
|
| 11 |
+
# Index (28 sections · 2 overlay(s))
|
| 12 |
+
# background src/agent/task/configSection.ts
|
| 13 |
+
# builtinProductSkills src/features/skill/catalog/configSection.ts
|
| 14 |
+
# cron src/features/cron/configSection.ts
|
| 15 |
+
# database src/persistence/configSection.ts
|
| 16 |
+
# defaultPermissionMode src/agent/permissionMode/configSection.ts
|
| 17 |
+
# defaultPlanMode src/features/plan/configSection.ts
|
| 18 |
+
# experimental src/app/flag/flag.ts
|
| 19 |
+
# extraAgentDirs src/workspace/workspaceAgentProfileLoader/configSection.ts
|
| 20 |
+
# extraSkillDirs src/features/skill/catalog/configSection.ts
|
| 21 |
+
# hooks src/features/externalHooks/configSection.ts
|
| 22 |
+
# identity src/app/agentIdentity/configSection.ts
|
| 23 |
+
# image src/agent/media/configSection.ts
|
| 24 |
+
# loopControl src/agent/loop/configSection.ts
|
| 25 |
+
# mcp src/app/mcpConfig/configSection.ts
|
| 26 |
+
# mergeAllAvailableSkills src/features/skill/catalog/configSection.ts
|
| 27 |
+
# modelCatalog src/app/kosongConfig/configSection.ts
|
| 28 |
+
# models src/app/kosongConfig/configSection.ts
|
| 29 |
+
# permission src/agent/permissionRules/configSection.ts
|
| 30 |
+
# providers src/app/kosongConfig/configSection.ts
|
| 31 |
+
# read src/agent/tools/os/read/configSection.ts
|
| 32 |
+
# secondaryModel src/session/subagent/configSection.ts
|
| 33 |
+
# services src/app/auth/configSection.ts
|
| 34 |
+
# subagent src/session/subagent/configSection.ts
|
| 35 |
+
# swarm src/features/swarm/configSection.ts
|
| 36 |
+
# task src/agent/task/configSection.ts
|
| 37 |
+
# thinking src/app/kosongConfig/configSection.ts
|
| 38 |
+
# tokenCounting src/agent/tokenCounting/configSection.ts
|
| 39 |
+
# tools src/agent/toolPolicy/configSection.ts
|
| 40 |
+
# (overlay) servicesCredentialEnvOverlay src/app/auth/configSection.ts
|
| 41 |
+
# (overlay) kimiModelEnvOverlay src/app/kosongConfig/envOverlay.ts
|
| 42 |
+
|
| 43 |
+
# ##########################################################################
|
| 44 |
+
# background
|
| 45 |
+
# owner: src/agent/task/configSection.ts
|
| 46 |
+
# scope: core
|
| 47 |
+
# hooks: stripEnv
|
| 48 |
+
# env:
|
| 49 |
+
# keep_alive_on_exit <- KIMI_CODE_BACKGROUND_KEEP_ALIVE_ON_EXIT (custom parse)
|
| 50 |
+
# max_running_tasks <- KIMI_CODE_BACKGROUND_MAX_RUNNING_TASKS (custom parse)
|
| 51 |
+
# bash_task_timeout_s <- KIMI_CODE_BACKGROUND_BASH_TASK_TIMEOUT_S (custom parse)
|
| 52 |
+
# print_wait_ceiling_s <- KIMI_CODE_BACKGROUND_PRINT_WAIT_CEILING_S (custom parse)
|
| 53 |
+
# print_background_mode <- KIMI_CODE_BACKGROUND_PRINT_BACKGROUND_MODE (custom parse)
|
| 54 |
+
# print_max_turns <- KIMI_CODE_BACKGROUND_PRINT_MAX_TURNS (custom parse)
|
| 55 |
+
# ##########################################################################
|
| 56 |
+
|
| 57 |
+
[background]
|
| 58 |
+
# max_running_tasks: integer
|
| 59 |
+
# keep_alive_on_exit: boolean
|
| 60 |
+
# bash_auto_background_on_timeout: boolean
|
| 61 |
+
# bash_task_timeout_s: integer
|
| 62 |
+
# kill_grace_period_ms: integer
|
| 63 |
+
# print_wait_ceiling_s: integer
|
| 64 |
+
# print_background_mode: "exit" | "drain" | "steer"
|
| 65 |
+
# print_max_turns: integer
|
| 66 |
+
|
| 67 |
+
# ##########################################################################
|
| 68 |
+
# builtinProductSkills (config.toml: builtin_product_skills)
|
| 69 |
+
# owner: src/features/skill/catalog/configSection.ts
|
| 70 |
+
# scope: core
|
| 71 |
+
# hooks: stripEnv
|
| 72 |
+
# env:
|
| 73 |
+
# <- KIMI_CODE_BUILTIN_PRODUCT_SKILLS (custom parse)
|
| 74 |
+
# ##########################################################################
|
| 75 |
+
|
| 76 |
+
builtin_product_skills = true
|
| 77 |
+
|
| 78 |
+
# ##########################################################################
|
| 79 |
+
# cron
|
| 80 |
+
# owner: src/features/cron/configSection.ts
|
| 81 |
+
# scope: core
|
| 82 |
+
# hooks: stripEnv
|
| 83 |
+
# env:
|
| 84 |
+
# debug <- KIMI_CRON_DEBUG (custom parse)
|
| 85 |
+
# no_jitter <- KIMI_CRON_NO_JITTER (custom parse)
|
| 86 |
+
# no_stale <- KIMI_CRON_NO_STALE (custom parse)
|
| 87 |
+
# disabled <- KIMI_DISABLE_CRON (custom parse)
|
| 88 |
+
# manual_tick <- KIMI_CRON_MANUAL_TICK (custom parse)
|
| 89 |
+
# clock <- KIMI_CRON_CLOCK
|
| 90 |
+
# poll_interval_ms <- KIMI_CRON_POLL_INTERVAL_MS (custom parse)
|
| 91 |
+
# ##########################################################################
|
| 92 |
+
|
| 93 |
+
[cron]
|
| 94 |
+
# (schema uses transforms; fields below come from the registered default)
|
| 95 |
+
debug = false
|
| 96 |
+
no_jitter = false
|
| 97 |
+
no_stale = false
|
| 98 |
+
disabled = false
|
| 99 |
+
manual_tick = false
|
| 100 |
+
|
| 101 |
+
# ##########################################################################
|
| 102 |
+
# database
|
| 103 |
+
# owner: src/persistence/configSection.ts
|
| 104 |
+
# scope: core
|
| 105 |
+
# hooks: stripEnv
|
| 106 |
+
# env:
|
| 107 |
+
# base <- KIMI_CODE_PERSISTENCE_MINIDB_READMODEL (custom parse)
|
| 108 |
+
# search <- KIMI_CODE_SEARCH_WORKER (custom parse)
|
| 109 |
+
# ##########################################################################
|
| 110 |
+
|
| 111 |
+
[database]
|
| 112 |
+
# base: boolean
|
| 113 |
+
# search: boolean
|
| 114 |
+
|
| 115 |
+
# ##########################################################################
|
| 116 |
+
# defaultPermissionMode (config.toml: default_permission_mode)
|
| 117 |
+
# owner: src/agent/permissionMode/configSection.ts
|
| 118 |
+
# scope: core
|
| 119 |
+
# ##########################################################################
|
| 120 |
+
|
| 121 |
+
# default_permission_mode: "manual" | "auto" | "yolo"
|
| 122 |
+
|
| 123 |
+
# ##########################################################################
|
| 124 |
+
# defaultPlanMode (config.toml: default_plan_mode)
|
| 125 |
+
# owner: src/features/plan/configSection.ts
|
| 126 |
+
# scope: core
|
| 127 |
+
# ##########################################################################
|
| 128 |
+
|
| 129 |
+
default_plan_mode = false
|
| 130 |
+
|
| 131 |
+
# ##########################################################################
|
| 132 |
+
# experimental
|
| 133 |
+
# owner: src/app/flag/flag.ts
|
| 134 |
+
# scope: core
|
| 135 |
+
# hooks: custom fromToml · custom toToml
|
| 136 |
+
# ##########################################################################
|
| 137 |
+
|
| 138 |
+
[experimental]
|
| 139 |
+
# <name>: boolean
|
| 140 |
+
|
| 141 |
+
# ##########################################################################
|
| 142 |
+
# extraAgentDirs (config.toml: extra_agent_dirs)
|
| 143 |
+
# owner: src/workspace/workspaceAgentProfileLoader/configSection.ts
|
| 144 |
+
# scope: core
|
| 145 |
+
# ##########################################################################
|
| 146 |
+
|
| 147 |
+
extra_agent_dirs = []
|
| 148 |
+
|
| 149 |
+
# ##########################################################################
|
| 150 |
+
# extraSkillDirs (config.toml: extra_skill_dirs)
|
| 151 |
+
# owner: src/features/skill/catalog/configSection.ts
|
| 152 |
+
# scope: core
|
| 153 |
+
# ##########################################################################
|
| 154 |
+
|
| 155 |
+
extra_skill_dirs = []
|
| 156 |
+
|
| 157 |
+
# ##########################################################################
|
| 158 |
+
# hooks
|
| 159 |
+
# owner: src/features/externalHooks/configSection.ts
|
| 160 |
+
# scope: core
|
| 161 |
+
# hooks: custom fromToml · custom toToml
|
| 162 |
+
# ##########################################################################
|
| 163 |
+
|
| 164 |
+
# one [[hooks]] table per entry:
|
| 165 |
+
# [[hooks]]
|
| 166 |
+
# event: "PreToolUse" | "PostToolUse" | "PostToolUseFailure" | "PermissionRequest" | "PermissionResult" | "UserPromptSubmit" | "UserPromptQueued" | "TurnStarted" | "Stop" | "StopFailure" | "Interrupt" | "SessionStart" | "SessionEnd" | "SessionHeartbeat" | "SubagentStart" | "SubagentStop" | "TaskStarted" | "PreCompact" | "PostCompact" | "Notification"
|
| 167 |
+
# matcher: string
|
| 168 |
+
# command: string
|
| 169 |
+
# timeout: integer
|
| 170 |
+
|
| 171 |
+
# ##########################################################################
|
| 172 |
+
# identity
|
| 173 |
+
# owner: src/app/agentIdentity/configSection.ts
|
| 174 |
+
# scope: core
|
| 175 |
+
# hooks: stripEnv
|
| 176 |
+
# env:
|
| 177 |
+
# name <- KIMI_CODE_IDENTITY_NAME (custom parse)
|
| 178 |
+
# slug <- KIMI_CODE_IDENTITY_SLUG (custom parse)
|
| 179 |
+
# ##########################################################################
|
| 180 |
+
|
| 181 |
+
[identity]
|
| 182 |
+
# name: string
|
| 183 |
+
# slug: string
|
| 184 |
+
|
| 185 |
+
# ##########################################################################
|
| 186 |
+
# image
|
| 187 |
+
# owner: src/agent/media/configSection.ts
|
| 188 |
+
# scope: core
|
| 189 |
+
# hooks: stripEnv
|
| 190 |
+
# env:
|
| 191 |
+
# max_edge_px <- KIMI_IMAGE_MAX_EDGE_PX (custom parse)
|
| 192 |
+
# read_byte_budget <- KIMI_IMAGE_READ_BYTE_BUDGET (custom parse)
|
| 193 |
+
# ##########################################################################
|
| 194 |
+
|
| 195 |
+
[image]
|
| 196 |
+
# max_edge_px: integer
|
| 197 |
+
# read_byte_budget: integer
|
| 198 |
+
|
| 199 |
+
# ##########################################################################
|
| 200 |
+
# loopControl (config.toml: loop_control)
|
| 201 |
+
# owner: src/agent/loop/configSection.ts
|
| 202 |
+
# scope: core
|
| 203 |
+
# hooks: custom toToml · stripEnv
|
| 204 |
+
# deprecations (old key is ignored + warns; rename manually):
|
| 205 |
+
# max_retries_per_step -> max_attempts_per_step
|
| 206 |
+
# max_steps_per_run -> max_steps_per_turn
|
| 207 |
+
# env:
|
| 208 |
+
# max_steps_per_turn <- KIMI_LOOP_MAX_STEPS_PER_TURN (custom parse)
|
| 209 |
+
# max_attempts_per_step <- KIMI_LOOP_MAX_ATTEMPTS_PER_STEP (custom parse; deprecated fallback KIMI_LOOP_MAX_RETRIES_PER_STEP)
|
| 210 |
+
# ##########################################################################
|
| 211 |
+
|
| 212 |
+
[loop_control]
|
| 213 |
+
# max_steps_per_turn: integer
|
| 214 |
+
# max_attempts_per_step: integer
|
| 215 |
+
# max_ralph_iterations: integer
|
| 216 |
+
# reserved_context_size: integer
|
| 217 |
+
# compaction_trigger_ratio: number
|
| 218 |
+
# compaction_max_attempts: integer
|
| 219 |
+
|
| 220 |
+
# ##########################################################################
|
| 221 |
+
# mcp
|
| 222 |
+
# owner: src/app/mcpConfig/configSection.ts
|
| 223 |
+
# scope: core
|
| 224 |
+
# hooks: stripEnv
|
| 225 |
+
# env:
|
| 226 |
+
# startup_timeout_ms <- KIMI_MCP_STARTUP_TIMEOUT_MS (custom parse)
|
| 227 |
+
# tool_timeout_ms <- KIMI_MCP_TOOL_TIMEOUT_MS (custom parse)
|
| 228 |
+
# ##########################################################################
|
| 229 |
+
|
| 230 |
+
[mcp]
|
| 231 |
+
# startup_timeout_ms: integer
|
| 232 |
+
# tool_timeout_ms: integer
|
| 233 |
+
|
| 234 |
+
# ##########################################################################
|
| 235 |
+
# mergeAllAvailableSkills (config.toml: merge_all_available_skills)
|
| 236 |
+
# owner: src/features/skill/catalog/configSection.ts
|
| 237 |
+
# scope: core
|
| 238 |
+
# ##########################################################################
|
| 239 |
+
|
| 240 |
+
merge_all_available_skills = true
|
| 241 |
+
|
| 242 |
+
# ##########################################################################
|
| 243 |
+
# modelCatalog (config.toml: model_catalog)
|
| 244 |
+
# owner: src/app/kosongConfig/configSection.ts
|
| 245 |
+
# scope: core
|
| 246 |
+
# ##########################################################################
|
| 247 |
+
|
| 248 |
+
[model_catalog]
|
| 249 |
+
# refresh_interval_ms: integer
|
| 250 |
+
# refresh_on_start: boolean
|
| 251 |
+
|
| 252 |
+
# ##########################################################################
|
| 253 |
+
# models
|
| 254 |
+
# owner: src/app/kosongConfig/configSection.ts
|
| 255 |
+
# scope: core
|
| 256 |
+
# hooks: custom fromToml · custom toToml
|
| 257 |
+
# ##########################################################################
|
| 258 |
+
|
| 259 |
+
[models]
|
| 260 |
+
|
| 261 |
+
# one [models."<name>"] table per entry:
|
| 262 |
+
# [models."<name>"]
|
| 263 |
+
# provider_id: string
|
| 264 |
+
# base_url: string
|
| 265 |
+
# api_key: string
|
| 266 |
+
# oauth: object
|
| 267 |
+
# storage: "file" | "keyring"
|
| 268 |
+
# key: string
|
| 269 |
+
# oauth_host: string
|
| 270 |
+
# protocol: "anthropic" | "openai" | "openai_responses" | "google-genai"
|
| 271 |
+
# name: string
|
| 272 |
+
# aliases: string[]
|
| 273 |
+
# provider: string
|
| 274 |
+
# model: string
|
| 275 |
+
# max_context_size: integer
|
| 276 |
+
# max_input_size: integer
|
| 277 |
+
# max_output_size: integer
|
| 278 |
+
# capabilities: string[]
|
| 279 |
+
# display_name: string
|
| 280 |
+
# reasoning_key: string
|
| 281 |
+
# adaptive_thinking: boolean
|
| 282 |
+
# beta_api: boolean
|
| 283 |
+
# support_efforts: string[]
|
| 284 |
+
# default_effort: string
|
| 285 |
+
# off_effort: string
|
| 286 |
+
# overrides: object
|
| 287 |
+
# max_context_size: integer
|
| 288 |
+
# max_input_size: integer
|
| 289 |
+
# max_output_size: integer
|
| 290 |
+
# capabilities: string[]
|
| 291 |
+
# display_name: string
|
| 292 |
+
# reasoning_key: string
|
| 293 |
+
# adaptive_thinking: boolean
|
| 294 |
+
# support_efforts: string[]
|
| 295 |
+
# default_effort: string
|
| 296 |
+
# off_effort: string
|
| 297 |
+
|
| 298 |
+
# ##########################################################################
|
| 299 |
+
# permission
|
| 300 |
+
# owner: src/agent/permissionRules/configSection.ts
|
| 301 |
+
# scope: core
|
| 302 |
+
# hooks: custom fromToml · custom toToml · stripEnv
|
| 303 |
+
# env:
|
| 304 |
+
# dangerous_command_guard <- KIMI_CODE_DANGEROUS_COMMAND_GUARD (custom parse)
|
| 305 |
+
# ##########################################################################
|
| 306 |
+
|
| 307 |
+
[permission]
|
| 308 |
+
# rules: object[] — one entry per item:
|
| 309 |
+
# decision: "allow" | "deny" | "ask"
|
| 310 |
+
# scope: "turn-override" | "session-runtime" | "project" | "user" (default: "user")
|
| 311 |
+
# pattern: string
|
| 312 |
+
# reason: string
|
| 313 |
+
# dangerous_command_guard: boolean
|
| 314 |
+
|
| 315 |
+
# ##########################################################################
|
| 316 |
+
# providers
|
| 317 |
+
# owner: src/app/kosongConfig/configSection.ts
|
| 318 |
+
# scope: core
|
| 319 |
+
# hooks: custom fromToml · custom toToml · stripEnv
|
| 320 |
+
# env:
|
| 321 |
+
# __kimi_env__.api_key <- KIMI_MODEL_API_KEY
|
| 322 |
+
# __kimi_env__.type <- KIMI_MODEL_PROVIDER_TYPE
|
| 323 |
+
# __kimi_env__.base_url <- KIMI_MODEL_BASE_URL
|
| 324 |
+
# ##########################################################################
|
| 325 |
+
|
| 326 |
+
[providers]
|
| 327 |
+
|
| 328 |
+
# one [providers."<name>"] table per entry:
|
| 329 |
+
# [providers."<name>"]
|
| 330 |
+
# model_source: "static" | "discover" | "oauth-catalog"
|
| 331 |
+
# base_url: string
|
| 332 |
+
# custom_headers: record<string, string>
|
| 333 |
+
# default_model: string
|
| 334 |
+
# type: string
|
| 335 |
+
# api_key: string
|
| 336 |
+
# oauth: object
|
| 337 |
+
# storage: "file" | "keyring"
|
| 338 |
+
# key: string
|
| 339 |
+
# oauth_host: string
|
| 340 |
+
# env: record<string, string>
|
| 341 |
+
# source: record<string, any>
|
| 342 |
+
|
| 343 |
+
# ##########################################################################
|
| 344 |
+
# read
|
| 345 |
+
# owner: src/agent/tools/os/read/configSection.ts
|
| 346 |
+
# scope: core
|
| 347 |
+
# ##########################################################################
|
| 348 |
+
|
| 349 |
+
[read]
|
| 350 |
+
# default_max_chars: integer
|
| 351 |
+
# max_chars: integer
|
| 352 |
+
|
| 353 |
+
# ##########################################################################
|
| 354 |
+
# secondaryModel (config.toml: secondary_model)
|
| 355 |
+
# owner: src/session/subagent/configSection.ts
|
| 356 |
+
# scope: core
|
| 357 |
+
# ##########################################################################
|
| 358 |
+
|
| 359 |
+
[secondary_model]
|
| 360 |
+
# default_model: string
|
| 361 |
+
# models: record<string, string>
|
| 362 |
+
# force: boolean
|
| 363 |
+
# model: string
|
| 364 |
+
# max_context_size: integer
|
| 365 |
+
# max_input_size: integer
|
| 366 |
+
# max_output_size: integer
|
| 367 |
+
# capabilities: string[]
|
| 368 |
+
# display_name: string
|
| 369 |
+
# reasoning_key: string
|
| 370 |
+
# adaptive_thinking: boolean
|
| 371 |
+
# support_efforts: string[]
|
| 372 |
+
# default_effort: string
|
| 373 |
+
# off_effort: string
|
| 374 |
+
|
| 375 |
+
# ##########################################################################
|
| 376 |
+
# services
|
| 377 |
+
# owner: src/app/auth/configSection.ts
|
| 378 |
+
# scope: core
|
| 379 |
+
# hooks: custom fromToml · custom toToml · stripEnv
|
| 380 |
+
# env:
|
| 381 |
+
# moonshot_search.base_url <- KIMI_WEB_SEARCH_BASE_URL (custom parse)
|
| 382 |
+
# moonshot_search.api_key <- KIMI_WEB_SEARCH_API_KEY (custom parse)
|
| 383 |
+
# moonshot_fetch.base_url <- KIMI_WEB_FETCH_BASE_URL (custom parse)
|
| 384 |
+
# moonshot_fetch.api_key <- KIMI_WEB_FETCH_API_KEY (custom parse)
|
| 385 |
+
# ##########################################################################
|
| 386 |
+
|
| 387 |
+
[services]
|
| 388 |
+
|
| 389 |
+
# [services.moonshot_search]
|
| 390 |
+
# base_url: string
|
| 391 |
+
# api_key: string
|
| 392 |
+
# oauth: object
|
| 393 |
+
# storage: "file" | "keyring"
|
| 394 |
+
# key: string
|
| 395 |
+
# oauth_host: string
|
| 396 |
+
# custom_headers: record<string, string>
|
| 397 |
+
|
| 398 |
+
# [services.moonshot_fetch]
|
| 399 |
+
# base_url: string
|
| 400 |
+
# api_key: string
|
| 401 |
+
# oauth: object
|
| 402 |
+
# storage: "file" | "keyring"
|
| 403 |
+
# key: string
|
| 404 |
+
# oauth_host: string
|
| 405 |
+
# custom_headers: record<string, string>
|
| 406 |
+
|
| 407 |
+
# ##########################################################################
|
| 408 |
+
# subagent
|
| 409 |
+
# owner: src/session/subagent/configSection.ts
|
| 410 |
+
# scope: core
|
| 411 |
+
# hooks: stripEnv
|
| 412 |
+
# env:
|
| 413 |
+
# timeout_ms <- KIMI_SUBAGENT_TIMEOUT_MS (custom parse)
|
| 414 |
+
# ##########################################################################
|
| 415 |
+
|
| 416 |
+
[subagent]
|
| 417 |
+
timeout_ms = 7200000
|
| 418 |
+
|
| 419 |
+
# ##########################################################################
|
| 420 |
+
# swarm
|
| 421 |
+
# owner: src/features/swarm/configSection.ts
|
| 422 |
+
# scope: core
|
| 423 |
+
# hooks: stripEnv
|
| 424 |
+
# env:
|
| 425 |
+
# timeout_ms <- KIMI_CODE_SWARM_TIMEOUT_MS (custom parse)
|
| 426 |
+
# ##########################################################################
|
| 427 |
+
|
| 428 |
+
[swarm]
|
| 429 |
+
timeout_ms = 7200000
|
| 430 |
+
|
| 431 |
+
# ##########################################################################
|
| 432 |
+
# task
|
| 433 |
+
# owner: src/agent/task/configSection.ts
|
| 434 |
+
# scope: core
|
| 435 |
+
# hooks: stripEnv
|
| 436 |
+
# env:
|
| 437 |
+
# keep_alive_on_exit <- KIMI_CODE_BACKGROUND_KEEP_ALIVE_ON_EXIT (custom parse)
|
| 438 |
+
# max_running_tasks <- KIMI_CODE_BACKGROUND_MAX_RUNNING_TASKS (custom parse)
|
| 439 |
+
# bash_task_timeout_s <- KIMI_CODE_BACKGROUND_BASH_TASK_TIMEOUT_S (custom parse)
|
| 440 |
+
# print_wait_ceiling_s <- KIMI_CODE_BACKGROUND_PRINT_WAIT_CEILING_S (custom parse)
|
| 441 |
+
# print_background_mode <- KIMI_CODE_BACKGROUND_PRINT_BACKGROUND_MODE (custom parse)
|
| 442 |
+
# print_max_turns <- KIMI_CODE_BACKGROUND_PRINT_MAX_TURNS (custom parse)
|
| 443 |
+
# ##########################################################################
|
| 444 |
+
|
| 445 |
+
[task]
|
| 446 |
+
# max_running_tasks: integer
|
| 447 |
+
# keep_alive_on_exit: boolean
|
| 448 |
+
# bash_auto_background_on_timeout: boolean
|
| 449 |
+
# bash_task_timeout_s: integer
|
| 450 |
+
# kill_grace_period_ms: integer
|
| 451 |
+
# print_wait_ceiling_s: integer
|
| 452 |
+
# print_background_mode: "exit" | "drain" | "steer"
|
| 453 |
+
# print_max_turns: integer
|
| 454 |
+
|
| 455 |
+
# ##########################################################################
|
| 456 |
+
# thinking
|
| 457 |
+
# owner: src/app/kosongConfig/configSection.ts
|
| 458 |
+
# scope: core
|
| 459 |
+
# hooks: stripEnv
|
| 460 |
+
# env:
|
| 461 |
+
# forced_effort <- KIMI_MODEL_THINKING_EFFORT
|
| 462 |
+
# ##########################################################################
|
| 463 |
+
|
| 464 |
+
[thinking]
|
| 465 |
+
# enabled: boolean
|
| 466 |
+
# effort: string
|
| 467 |
+
# forced_effort: string
|
| 468 |
+
# keep: string
|
| 469 |
+
|
| 470 |
+
# ##########################################################################
|
| 471 |
+
# tokenCounting (config.toml: token_counting)
|
| 472 |
+
# owner: src/agent/tokenCounting/configSection.ts
|
| 473 |
+
# scope: core
|
| 474 |
+
# hooks: stripEnv
|
| 475 |
+
# env:
|
| 476 |
+
# strategy <- KIMI_TOKEN_COUNTING_STRATEGY (custom parse)
|
| 477 |
+
# ##########################################################################
|
| 478 |
+
|
| 479 |
+
[token_counting]
|
| 480 |
+
strategy = "measured+estimated"
|
| 481 |
+
|
| 482 |
+
# ##########################################################################
|
| 483 |
+
# tools
|
| 484 |
+
# owner: src/agent/toolPolicy/configSection.ts
|
| 485 |
+
# scope: core
|
| 486 |
+
# ##########################################################################
|
| 487 |
+
|
| 488 |
+
[tools]
|
| 489 |
+
# enabled: string[]
|
| 490 |
+
# disabled: string[]
|
packages/agent-core-v2/docs/state-manifest.d.ts
ADDED
|
@@ -0,0 +1,1270 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// App, Workspace, Session & Agent State Manifest
|
| 2 |
+
//
|
| 3 |
+
// Generated by scripts/gen-state-manifest.mts — do not edit by hand.
|
| 4 |
+
// Regenerate with: pnpm --filter @moonshot-ai/agent-core-v2 gen:state-manifest
|
| 5 |
+
//
|
| 6 |
+
// Every state key registered into the App-scope IAppStateService, the
|
| 7 |
+
// Workspace-scope IWorkspaceStateService, the Session-scope
|
| 8 |
+
// ISessionStateService, or the Agent-scope IAgentStateService (see
|
| 9 |
+
// src/_base/state/stateRegistry.ts), collected statically from the
|
| 10 |
+
// `states.contributeState(...)` call sites and the replayable key chains — a
|
| 11 |
+
// `defineState(...).replayable(...)` key is contributed into the Agent-scope
|
| 12 |
+
// service by its owner service at construction, and
|
| 13 |
+
// carries a `// replayable · durable|transient · undoable? — folds: ...` line.
|
| 14 |
+
// Replayable values are excluded from snapshot()/inspect(). A key defined via
|
| 15 |
+
// defineState but never registered nor replayable does not appear here. Each entry shows the
|
| 16 |
+
// compile-time StateKey<T> value type fully expanded inline, so the manifest is
|
| 17 |
+
// self-contained (no imports, no helper declarations). A named type is marked
|
| 18 |
+
// at its expansion site with a `/* TypeName — source/file.ts */` comment; a
|
| 19 |
+
// `/* TypeName — recursive (...) */ unknown` marker stops a recursive expansion.
|
| 20 |
+
// Lib globals (Map/Set/Record/…) are referenced as-is. Generic instantiations
|
| 21 |
+
// expand structurally; classes render as their public instance shape. The
|
| 22 |
+
// defining source file heads each group.
|
| 23 |
+
//
|
| 24 |
+
// snapshot() returns JSON-safe deep copies of these values: Maps become plain
|
| 25 |
+
// objects (or [key, value] entry arrays when a key is not string/number), Sets
|
| 26 |
+
// become arrays, bigints become strings, functions are dropped, circular
|
| 27 |
+
// references become '(circular)', and class instances collapse to a '(ClassName)'
|
| 28 |
+
// marker — the wire shape of an entry is the JSON projection of the type here.
|
| 29 |
+
//
|
| 30 |
+
// Index (App: 0 keys · Workspace: 6 keys · Session: 9 keys · Agent: 73 keys)
|
| 31 |
+
// App
|
| 32 |
+
// Workspace
|
| 33 |
+
// workspaceDirs.ephemeralDirs src/workspace/workspaceDirs/workspaceDirsService.ts
|
| 34 |
+
// workspaceDirs.fileDirs src/workspace/workspaceDirs/workspaceDirsService.ts
|
| 35 |
+
// workspaceInstructions.current src/workspace/workspaceInstructions/workspaceInstructionsService.ts
|
| 36 |
+
// workspaceSkillCatalog.contributions src/features/skill/workspace/workspaceSkillCatalogService.ts
|
| 37 |
+
// workspaceSkillCatalog.merged src/features/skill/workspace/workspaceSkillCatalogService.ts
|
| 38 |
+
// workspaceTrust.trusted src/workspace/workspaceTrust/workspaceTrustService.ts
|
| 39 |
+
// Session
|
| 40 |
+
// sessionActivity.current src/session/sessionActivity/sessionActivityService.ts
|
| 41 |
+
// sessionActivity.folds src/session/sessionActivity/sessionActivityService.ts
|
| 42 |
+
// sessionLog.rootLevel src/session/sessionLog/sessionLogService.ts
|
| 43 |
+
// sessionMetadata.data src/session/sessionMetadata/sessionMetadataService.ts
|
| 44 |
+
// sessionSkillCatalog.contributions src/features/skill/session/skillCatalogService.ts
|
| 45 |
+
// sessionSkillCatalog.merged src/features/skill/session/skillCatalogService.ts
|
| 46 |
+
// sessionToolPolicy.state src/session/sessionToolPolicy/sessionToolPolicyService.ts
|
| 47 |
+
// workspaceContext.additionalDirs src/session/workspaceContext/workspaceContextService.ts
|
| 48 |
+
// workspaceContext.workDir src/session/workspaceContext/workspaceContextService.ts
|
| 49 |
+
// Agent
|
| 50 |
+
// agentPlugin.sessionStartRefreshPending src/agent/plugin/agentPluginService.ts
|
| 51 |
+
// agentsMdReminder.cwd src/agent/agentsMdReminder/agentsMdReminderService.ts
|
| 52 |
+
// agentsMdReminder.known src/agent/agentsMdReminder/agentsMdReminderService.ts
|
| 53 |
+
// agentsMdReminder.seeded src/agent/agentsMdReminder/agentsMdReminderService.ts
|
| 54 |
+
// contextMemory src/agent/contextMemory/contextOps.ts
|
| 55 |
+
// contextProjector.lastRepairSignature src/agent/contextProjector/contextProjectorService.ts
|
| 56 |
+
// externalHooks.stopHookContinuationUsed src/features/externalHooks/agent/agentExternalHooksService.ts
|
| 57 |
+
// fileHistory src/features/fileHistory/fileHistoryOps.ts
|
| 58 |
+
// fullCompaction src/agent/fullCompaction/compactionOps.ts
|
| 59 |
+
// fullCompaction.activeTurnId src/agent/fullCompaction/fullCompactionService.ts
|
| 60 |
+
// fullCompaction.compactionCountInTurn src/agent/fullCompaction/fullCompactionService.ts
|
| 61 |
+
// fullCompaction.consecutiveOverflowCompactions src/agent/fullCompaction/fullCompactionService.ts
|
| 62 |
+
// fullCompaction.lastCompactedTokenCount src/agent/fullCompaction/fullCompactionService.ts
|
| 63 |
+
// fullCompaction.observedMaxContextTokensByModel src/agent/fullCompaction/fullCompactionService.ts
|
| 64 |
+
// fullCompaction.wireRanges src/agent/fullCompaction/compactionOps.ts
|
| 65 |
+
// interruptionReminder src/agent/interruptionReminder/interruptionReminderOps.ts
|
| 66 |
+
// llm.requestTrace src/agent/llmRequester/llmRequestOps.ts
|
| 67 |
+
// llmRequester.emittedThinkingEffortWarnings src/agent/llmRequester/llmRequesterService.ts
|
| 68 |
+
// llmRequester.lastConfigLogSignature src/agent/llmRequester/llmRequesterService.ts
|
| 69 |
+
// llmRequester.mediaDegradedTurns src/agent/llmRequester/llmRequesterService.ts
|
| 70 |
+
// llmRequester.mediaStrippedTurns src/agent/llmRequester/llmRequesterService.ts
|
| 71 |
+
// llmRequester.turnConfigs src/agent/llmRequester/llmRequesterService.ts
|
| 72 |
+
// loop.disposing src/agent/loop/loopService.ts
|
| 73 |
+
// loop.lastRequestTraceId src/agent/loop/loopService.ts
|
| 74 |
+
// mcp.discovery src/agent/mcp/mcpDiscoveryOps.ts
|
| 75 |
+
// mcp.discoveryWritesReady src/agent/mcp/mcpService.ts
|
| 76 |
+
// mcp.mcpToolsByServer src/agent/mcp/mcpService.ts
|
| 77 |
+
// media.budgetDropped src/agent/media/mediaResolverService.ts
|
| 78 |
+
// media.registeredKey src/agent/media/mediaToolsRegistrar.ts
|
| 79 |
+
// media.resolved src/agent/media/mediaResolverService.ts
|
| 80 |
+
// permissionMode src/agent/permissionMode/permissionModeOps.ts
|
| 81 |
+
// permissionMode.configured src/agent/permissionMode/permissionModeOps.ts
|
| 82 |
+
// permissionMode.lastMode src/agent/permissionMode/injection/permissionModeInjection.ts
|
| 83 |
+
// permissionRules src/agent/permissionRules/permissionRulesOps.ts
|
| 84 |
+
// plan src/features/plan/planOps.ts
|
| 85 |
+
// plan.wasActive src/features/plan/injection/planModeInjection.ts
|
| 86 |
+
// pluginSessionStartSnapshot src/agent/plugin/agentPluginOps.ts
|
| 87 |
+
// profile src/agent/profile/profileOps.ts
|
| 88 |
+
// profile.activeToolNamesOverlay src/agent/profile/profileService.ts
|
| 89 |
+
// profile.activeTools src/agent/profile/profileOps.ts
|
| 90 |
+
// profile.agentsMdWarning src/agent/profile/profileService.ts
|
| 91 |
+
// profile.emittedPluginBudgetWarnings src/agent/profile/profileService.ts
|
| 92 |
+
// profile.emittedThinkingEffortWarnings src/agent/profile/profileService.ts
|
| 93 |
+
// profile.emittedToolPatternWarnings src/agent/profile/profileService.ts
|
| 94 |
+
// runtime.binding src/agent/runtimeBinding/runtimeBindingService.ts
|
| 95 |
+
// runtimeBinding src/agent/runtimeBinding/runtimeBindingOps.ts
|
| 96 |
+
// shellCommand.tasks src/agent/shellCommand/shellCommandService.ts
|
| 97 |
+
// swarm src/features/swarm/swarmOps.ts
|
| 98 |
+
// task src/agent/task/taskOps.ts
|
| 99 |
+
// task.activeTaskReminderPending src/agent/task/taskService.ts
|
| 100 |
+
// task.deliveredNotificationKeys src/agent/task/taskService.ts
|
| 101 |
+
// task.ghosts src/agent/task/taskService.ts
|
| 102 |
+
// task.notificationDelivery src/agent/task/taskService.ts
|
| 103 |
+
// task.scheduledNotificationKeys src/agent/task/taskService.ts
|
| 104 |
+
// toolDedupe.activeStep src/agent/toolDedupe/toolDedupeService.ts
|
| 105 |
+
// toolDedupe.activeTurnId src/agent/toolDedupe/toolDedupeService.ts
|
| 106 |
+
// toolDedupe.callKeyByCallId src/agent/toolDedupe/toolDedupeService.ts
|
| 107 |
+
// toolDedupe.consecutiveCount src/agent/toolDedupe/toolDedupeService.ts
|
| 108 |
+
// toolDedupe.consecutiveKey src/agent/toolDedupe/toolDedupeService.ts
|
| 109 |
+
// toolDedupe.handoffPhase src/agent/toolDedupe/toolDedupeService.ts
|
| 110 |
+
// toolDedupe.originalCallIndex src/agent/toolDedupe/toolDedupeService.ts
|
| 111 |
+
// toolDedupe.stepCalls src/agent/toolDedupe/toolDedupeService.ts
|
| 112 |
+
// toolDedupe.syntheticCallIds src/agent/toolDedupe/toolDedupeService.ts
|
| 113 |
+
// toolDedupe.turnCallRecords src/agent/toolDedupe/toolDedupeService.ts
|
| 114 |
+
// toolDedupe.turnRepeatCount src/agent/toolDedupe/toolDedupeService.ts
|
| 115 |
+
// toolExecutor.dupTypeTurnId src/agent/toolExecutor/toolExecutorService.ts
|
| 116 |
+
// toolExecutor.toolCallDupTypes src/agent/toolExecutor/toolExecutorService.ts
|
| 117 |
+
// toolSelect.pendingLoaded src/agent/toolSelect/toolSelectService.ts
|
| 118 |
+
// tower src/features/tower/towerOps.ts
|
| 119 |
+
// tower.base src/features/tower/towerOps.ts
|
| 120 |
+
// tower.owner src/features/tower/towerOps.ts
|
| 121 |
+
// turn src/agent/loop/turnOps.ts
|
| 122 |
+
// userTool src/agent/userTool/userToolOps.ts
|
| 123 |
+
|
| 124 |
+
/** App-scope keys registered into IAppStateService. */
|
| 125 |
+
export interface AppStateSnapshot {
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
export type AppStateKey = keyof AppStateSnapshot;
|
| 129 |
+
|
| 130 |
+
/** Workspace-scope keys registered into IWorkspaceStateService. */
|
| 131 |
+
export interface WorkspaceStateSnapshot {
|
| 132 |
+
// src/features/skill/workspace/workspaceSkillCatalogService.ts
|
| 133 |
+
'workspaceSkillCatalog.contributions': Map<string, {
|
| 134 |
+
readonly c: /* SkillContribution — packages/agent-core-v2/src/features/skill/catalog/skillSource.ts */ {
|
| 135 |
+
readonly skills: readonly /* SkillDefinition — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 136 |
+
readonly name: string;
|
| 137 |
+
readonly description: string;
|
| 138 |
+
readonly path: string;
|
| 139 |
+
readonly dir: string;
|
| 140 |
+
readonly content: string;
|
| 141 |
+
readonly metadata: /* SkillMetadata — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 142 |
+
readonly name?: string;
|
| 143 |
+
readonly description?: string;
|
| 144 |
+
readonly type?: string;
|
| 145 |
+
readonly whenToUse?: string;
|
| 146 |
+
readonly disableModelInvocation?: boolean;
|
| 147 |
+
readonly isSubSkill?: boolean;
|
| 148 |
+
readonly safe?: boolean;
|
| 149 |
+
readonly arguments?: string | readonly unknown[];
|
| 150 |
+
[key: string]: unknown;
|
| 151 |
+
};
|
| 152 |
+
readonly source: /* SkillSource — packages/agent-core-v2/src/features/skill/catalog/types.ts */ 'project' | 'user' | 'extra' | 'builtin';
|
| 153 |
+
readonly plugin?: /* SkillPluginContext — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 154 |
+
readonly id: string;
|
| 155 |
+
readonly instructions?: string;
|
| 156 |
+
};
|
| 157 |
+
readonly mermaid?: string;
|
| 158 |
+
readonly d2?: string;
|
| 159 |
+
readonly productSpecific?: boolean;
|
| 160 |
+
readonly experimentalFlag?: string;
|
| 161 |
+
}[];
|
| 162 |
+
readonly skipped?: readonly /* SkippedSkill — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 163 |
+
readonly path: string;
|
| 164 |
+
readonly type: string;
|
| 165 |
+
readonly reason: string;
|
| 166 |
+
}[];
|
| 167 |
+
readonly scannedRoots?: readonly string[];
|
| 168 |
+
};
|
| 169 |
+
readonly priority: number;
|
| 170 |
+
}>;
|
| 171 |
+
'workspaceSkillCatalog.merged': /* InMemorySkillCatalog — packages/agent-core-v2/src/features/skill/catalog/registry.ts */ {
|
| 172 |
+
registerBuiltinSkill: (skill: /* SkillDefinition — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 173 |
+
readonly name: string;
|
| 174 |
+
readonly description: string;
|
| 175 |
+
readonly path: string;
|
| 176 |
+
readonly dir: string;
|
| 177 |
+
readonly content: string;
|
| 178 |
+
readonly metadata: /* SkillMetadata — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 179 |
+
readonly name?: string;
|
| 180 |
+
readonly description?: string;
|
| 181 |
+
readonly type?: string;
|
| 182 |
+
readonly whenToUse?: string;
|
| 183 |
+
readonly disableModelInvocation?: boolean;
|
| 184 |
+
readonly isSubSkill?: boolean;
|
| 185 |
+
readonly safe?: boolean;
|
| 186 |
+
readonly arguments?: string | readonly unknown[];
|
| 187 |
+
[key: string]: unknown;
|
| 188 |
+
};
|
| 189 |
+
readonly source: /* SkillSource — packages/agent-core-v2/src/features/skill/catalog/types.ts */ 'project' | 'user' | 'extra' | 'builtin';
|
| 190 |
+
readonly plugin?: /* SkillPluginContext — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 191 |
+
readonly id: string;
|
| 192 |
+
readonly instructions?: string;
|
| 193 |
+
};
|
| 194 |
+
readonly mermaid?: string;
|
| 195 |
+
readonly d2?: string;
|
| 196 |
+
readonly productSpecific?: boolean;
|
| 197 |
+
readonly experimentalFlag?: string;
|
| 198 |
+
}) => void;
|
| 199 |
+
register: (skill: /* SkillDefinition — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 200 |
+
readonly name: string;
|
| 201 |
+
readonly description: string;
|
| 202 |
+
readonly path: string;
|
| 203 |
+
readonly dir: string;
|
| 204 |
+
readonly content: string;
|
| 205 |
+
readonly metadata: /* SkillMetadata — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 206 |
+
readonly name?: string;
|
| 207 |
+
readonly description?: string;
|
| 208 |
+
readonly type?: string;
|
| 209 |
+
readonly whenToUse?: string;
|
| 210 |
+
readonly disableModelInvocation?: boolean;
|
| 211 |
+
readonly isSubSkill?: boolean;
|
| 212 |
+
readonly safe?: boolean;
|
| 213 |
+
readonly arguments?: string | readonly unknown[];
|
| 214 |
+
[key: string]: unknown;
|
| 215 |
+
};
|
| 216 |
+
readonly source: /* SkillSource — packages/agent-core-v2/src/features/skill/catalog/types.ts */ 'project' | 'user' | 'extra' | 'builtin';
|
| 217 |
+
readonly plugin?: /* SkillPluginContext — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 218 |
+
readonly id: string;
|
| 219 |
+
readonly instructions?: string;
|
| 220 |
+
};
|
| 221 |
+
readonly mermaid?: string;
|
| 222 |
+
readonly d2?: string;
|
| 223 |
+
readonly productSpecific?: boolean;
|
| 224 |
+
readonly experimentalFlag?: string;
|
| 225 |
+
}, options?: {
|
| 226 |
+
readonly replace?: boolean;
|
| 227 |
+
}) => void;
|
| 228 |
+
recordSkipped: (skills: readonly /* SkippedSkill — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 229 |
+
readonly path: string;
|
| 230 |
+
readonly type: string;
|
| 231 |
+
readonly reason: string;
|
| 232 |
+
}[]) => void;
|
| 233 |
+
addRoots: (roots: readonly string[]) => void;
|
| 234 |
+
getSkill: (name: string) => /* SkillDefinition — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 235 |
+
readonly name: string;
|
| 236 |
+
readonly description: string;
|
| 237 |
+
readonly path: string;
|
| 238 |
+
readonly dir: string;
|
| 239 |
+
readonly content: string;
|
| 240 |
+
readonly metadata: /* SkillMetadata — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 241 |
+
readonly name?: string;
|
| 242 |
+
readonly description?: string;
|
| 243 |
+
readonly type?: string;
|
| 244 |
+
readonly whenToUse?: string;
|
| 245 |
+
readonly disableModelInvocation?: boolean;
|
| 246 |
+
readonly isSubSkill?: boolean;
|
| 247 |
+
readonly safe?: boolean;
|
| 248 |
+
readonly arguments?: string | readonly unknown[];
|
| 249 |
+
[key: string]: unknown;
|
| 250 |
+
};
|
| 251 |
+
readonly source: /* SkillSource — packages/agent-core-v2/src/features/skill/catalog/types.ts */ 'project' | 'user' | 'extra' | 'builtin';
|
| 252 |
+
readonly plugin?: /* SkillPluginContext — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 253 |
+
readonly id: string;
|
| 254 |
+
readonly instructions?: string;
|
| 255 |
+
};
|
| 256 |
+
readonly mermaid?: string;
|
| 257 |
+
readonly d2?: string;
|
| 258 |
+
readonly productSpecific?: boolean;
|
| 259 |
+
readonly experimentalFlag?: string;
|
| 260 |
+
} | undefined;
|
| 261 |
+
getPluginSkill: (pluginId: string, name: string) => /* SkillDefinition — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 262 |
+
readonly name: string;
|
| 263 |
+
readonly description: string;
|
| 264 |
+
readonly path: string;
|
| 265 |
+
readonly dir: string;
|
| 266 |
+
readonly content: string;
|
| 267 |
+
readonly metadata: /* SkillMetadata — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 268 |
+
readonly name?: string;
|
| 269 |
+
readonly description?: string;
|
| 270 |
+
readonly type?: string;
|
| 271 |
+
readonly whenToUse?: string;
|
| 272 |
+
readonly disableModelInvocation?: boolean;
|
| 273 |
+
readonly isSubSkill?: boolean;
|
| 274 |
+
readonly safe?: boolean;
|
| 275 |
+
readonly arguments?: string | readonly unknown[];
|
| 276 |
+
[key: string]: unknown;
|
| 277 |
+
};
|
| 278 |
+
readonly source: /* SkillSource — packages/agent-core-v2/src/features/skill/catalog/types.ts */ 'project' | 'user' | 'extra' | 'builtin';
|
| 279 |
+
readonly plugin?: /* SkillPluginContext — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 280 |
+
readonly id: string;
|
| 281 |
+
readonly instructions?: string;
|
| 282 |
+
};
|
| 283 |
+
readonly mermaid?: string;
|
| 284 |
+
readonly d2?: string;
|
| 285 |
+
readonly productSpecific?: boolean;
|
| 286 |
+
readonly experimentalFlag?: string;
|
| 287 |
+
} | undefined;
|
| 288 |
+
renderSkillPrompt: (skill: /* SkillDefinition — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 289 |
+
readonly name: string;
|
| 290 |
+
readonly description: string;
|
| 291 |
+
readonly path: string;
|
| 292 |
+
readonly dir: string;
|
| 293 |
+
readonly content: string;
|
| 294 |
+
readonly metadata: /* SkillMetadata — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 295 |
+
readonly name?: string;
|
| 296 |
+
readonly description?: string;
|
| 297 |
+
readonly type?: string;
|
| 298 |
+
readonly whenToUse?: string;
|
| 299 |
+
readonly disableModelInvocation?: boolean;
|
| 300 |
+
readonly isSubSkill?: boolean;
|
| 301 |
+
readonly safe?: boolean;
|
| 302 |
+
readonly arguments?: string | readonly unknown[];
|
| 303 |
+
[key: string]: unknown;
|
| 304 |
+
};
|
| 305 |
+
readonly source: /* SkillSource — packages/agent-core-v2/src/features/skill/catalog/types.ts */ 'project' | 'user' | 'extra' | 'builtin';
|
| 306 |
+
readonly plugin?: /* SkillPluginContext — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 307 |
+
readonly id: string;
|
| 308 |
+
readonly instructions?: string;
|
| 309 |
+
};
|
| 310 |
+
readonly mermaid?: string;
|
| 311 |
+
readonly d2?: string;
|
| 312 |
+
readonly productSpecific?: boolean;
|
| 313 |
+
readonly experimentalFlag?: string;
|
| 314 |
+
}, rawArgs: string, context?: {
|
| 315 |
+
readonly sessionId?: string;
|
| 316 |
+
}) => string;
|
| 317 |
+
listSkills: () => readonly /* SkillDefinition — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 318 |
+
readonly name: string;
|
| 319 |
+
readonly description: string;
|
| 320 |
+
readonly path: string;
|
| 321 |
+
readonly dir: string;
|
| 322 |
+
readonly content: string;
|
| 323 |
+
readonly metadata: /* SkillMetadata — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 324 |
+
readonly name?: string;
|
| 325 |
+
readonly description?: string;
|
| 326 |
+
readonly type?: string;
|
| 327 |
+
readonly whenToUse?: string;
|
| 328 |
+
readonly disableModelInvocation?: boolean;
|
| 329 |
+
readonly isSubSkill?: boolean;
|
| 330 |
+
readonly safe?: boolean;
|
| 331 |
+
readonly arguments?: string | readonly unknown[];
|
| 332 |
+
[key: string]: unknown;
|
| 333 |
+
};
|
| 334 |
+
readonly source: /* SkillSource — packages/agent-core-v2/src/features/skill/catalog/types.ts */ 'project' | 'user' | 'extra' | 'builtin';
|
| 335 |
+
readonly plugin?: /* SkillPluginContext — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 336 |
+
readonly id: string;
|
| 337 |
+
readonly instructions?: string;
|
| 338 |
+
};
|
| 339 |
+
readonly mermaid?: string;
|
| 340 |
+
readonly d2?: string;
|
| 341 |
+
readonly productSpecific?: boolean;
|
| 342 |
+
readonly experimentalFlag?: string;
|
| 343 |
+
}[];
|
| 344 |
+
listInvocableSkills: () => readonly /* SkillDefinition — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 345 |
+
readonly name: string;
|
| 346 |
+
readonly description: string;
|
| 347 |
+
readonly path: string;
|
| 348 |
+
readonly dir: string;
|
| 349 |
+
readonly content: string;
|
| 350 |
+
readonly metadata: /* SkillMetadata — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 351 |
+
readonly name?: string;
|
| 352 |
+
readonly description?: string;
|
| 353 |
+
readonly type?: string;
|
| 354 |
+
readonly whenToUse?: string;
|
| 355 |
+
readonly disableModelInvocation?: boolean;
|
| 356 |
+
readonly isSubSkill?: boolean;
|
| 357 |
+
readonly safe?: boolean;
|
| 358 |
+
readonly arguments?: string | readonly unknown[];
|
| 359 |
+
[key: string]: unknown;
|
| 360 |
+
};
|
| 361 |
+
readonly source: /* SkillSource — packages/agent-core-v2/src/features/skill/catalog/types.ts */ 'project' | 'user' | 'extra' | 'builtin';
|
| 362 |
+
readonly plugin?: /* SkillPluginContext — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 363 |
+
readonly id: string;
|
| 364 |
+
readonly instructions?: string;
|
| 365 |
+
};
|
| 366 |
+
readonly mermaid?: string;
|
| 367 |
+
readonly d2?: string;
|
| 368 |
+
readonly productSpecific?: boolean;
|
| 369 |
+
readonly experimentalFlag?: string;
|
| 370 |
+
}[];
|
| 371 |
+
getSkillRoots: () => readonly string[];
|
| 372 |
+
getSkippedByPolicy: () => readonly /* SkippedSkill — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 373 |
+
readonly path: string;
|
| 374 |
+
readonly type: string;
|
| 375 |
+
readonly reason: string;
|
| 376 |
+
}[];
|
| 377 |
+
getKimiSkillsDescription: () => string;
|
| 378 |
+
getModelSkillListing: () => string;
|
| 379 |
+
};
|
| 380 |
+
// src/workspace/workspaceDirs/workspaceDirsService.ts
|
| 381 |
+
'workspaceDirs.ephemeralDirs': readonly string[];
|
| 382 |
+
'workspaceDirs.fileDirs': readonly string[];
|
| 383 |
+
// src/workspace/workspaceInstructions/workspaceInstructionsService.ts
|
| 384 |
+
'workspaceInstructions.current': /* WorkspaceInstructionsSnapshot — packages/agent-core-v2/src/workspace/workspaceInstructions/workspaceInstructions.ts */ {
|
| 385 |
+
readonly agentsMd: string | undefined;
|
| 386 |
+
readonly agentsMdWarning: string | undefined;
|
| 387 |
+
readonly agentsMdPaths: readonly string[] | undefined;
|
| 388 |
+
};
|
| 389 |
+
// src/workspace/workspaceTrust/workspaceTrustService.ts
|
| 390 |
+
'workspaceTrust.trusted': boolean;
|
| 391 |
+
}
|
| 392 |
+
|
| 393 |
+
export type WorkspaceStateKey = keyof WorkspaceStateSnapshot;
|
| 394 |
+
|
| 395 |
+
/** Session-scope keys registered into ISessionStateService. */
|
| 396 |
+
export interface SessionStateSnapshot {
|
| 397 |
+
// src/features/skill/session/skillCatalogService.ts
|
| 398 |
+
'sessionSkillCatalog.contributions': Map<string, {
|
| 399 |
+
readonly c: /* SkillContribution — packages/agent-core-v2/src/features/skill/catalog/skillSource.ts */ {
|
| 400 |
+
readonly skills: readonly /* SkillDefinition — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 401 |
+
readonly name: string;
|
| 402 |
+
readonly description: string;
|
| 403 |
+
readonly path: string;
|
| 404 |
+
readonly dir: string;
|
| 405 |
+
readonly content: string;
|
| 406 |
+
readonly metadata: /* SkillMetadata — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 407 |
+
readonly name?: string;
|
| 408 |
+
readonly description?: string;
|
| 409 |
+
readonly type?: string;
|
| 410 |
+
readonly whenToUse?: string;
|
| 411 |
+
readonly disableModelInvocation?: boolean;
|
| 412 |
+
readonly isSubSkill?: boolean;
|
| 413 |
+
readonly safe?: boolean;
|
| 414 |
+
readonly arguments?: string | readonly unknown[];
|
| 415 |
+
[key: string]: unknown;
|
| 416 |
+
};
|
| 417 |
+
readonly source: /* SkillSource — packages/agent-core-v2/src/features/skill/catalog/types.ts */ 'project' | 'user' | 'extra' | 'builtin';
|
| 418 |
+
readonly plugin?: /* SkillPluginContext — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 419 |
+
readonly id: string;
|
| 420 |
+
readonly instructions?: string;
|
| 421 |
+
};
|
| 422 |
+
readonly mermaid?: string;
|
| 423 |
+
readonly d2?: string;
|
| 424 |
+
readonly productSpecific?: boolean;
|
| 425 |
+
readonly experimentalFlag?: string;
|
| 426 |
+
}[];
|
| 427 |
+
readonly skipped?: readonly /* SkippedSkill — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 428 |
+
readonly path: string;
|
| 429 |
+
readonly type: string;
|
| 430 |
+
readonly reason: string;
|
| 431 |
+
}[];
|
| 432 |
+
readonly scannedRoots?: readonly string[];
|
| 433 |
+
};
|
| 434 |
+
readonly priority: number;
|
| 435 |
+
}>;
|
| 436 |
+
'sessionSkillCatalog.merged': /* InMemorySkillCatalog — packages/agent-core-v2/src/features/skill/catalog/registry.ts */ {
|
| 437 |
+
registerBuiltinSkill: (skill: /* SkillDefinition — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 438 |
+
readonly name: string;
|
| 439 |
+
readonly description: string;
|
| 440 |
+
readonly path: string;
|
| 441 |
+
readonly dir: string;
|
| 442 |
+
readonly content: string;
|
| 443 |
+
readonly metadata: /* SkillMetadata — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 444 |
+
readonly name?: string;
|
| 445 |
+
readonly description?: string;
|
| 446 |
+
readonly type?: string;
|
| 447 |
+
readonly whenToUse?: string;
|
| 448 |
+
readonly disableModelInvocation?: boolean;
|
| 449 |
+
readonly isSubSkill?: boolean;
|
| 450 |
+
readonly safe?: boolean;
|
| 451 |
+
readonly arguments?: string | readonly unknown[];
|
| 452 |
+
[key: string]: unknown;
|
| 453 |
+
};
|
| 454 |
+
readonly source: /* SkillSource — packages/agent-core-v2/src/features/skill/catalog/types.ts */ 'project' | 'user' | 'extra' | 'builtin';
|
| 455 |
+
readonly plugin?: /* SkillPluginContext — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 456 |
+
readonly id: string;
|
| 457 |
+
readonly instructions?: string;
|
| 458 |
+
};
|
| 459 |
+
readonly mermaid?: string;
|
| 460 |
+
readonly d2?: string;
|
| 461 |
+
readonly productSpecific?: boolean;
|
| 462 |
+
readonly experimentalFlag?: string;
|
| 463 |
+
}) => void;
|
| 464 |
+
register: (skill: /* SkillDefinition — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 465 |
+
readonly name: string;
|
| 466 |
+
readonly description: string;
|
| 467 |
+
readonly path: string;
|
| 468 |
+
readonly dir: string;
|
| 469 |
+
readonly content: string;
|
| 470 |
+
readonly metadata: /* SkillMetadata — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 471 |
+
readonly name?: string;
|
| 472 |
+
readonly description?: string;
|
| 473 |
+
readonly type?: string;
|
| 474 |
+
readonly whenToUse?: string;
|
| 475 |
+
readonly disableModelInvocation?: boolean;
|
| 476 |
+
readonly isSubSkill?: boolean;
|
| 477 |
+
readonly safe?: boolean;
|
| 478 |
+
readonly arguments?: string | readonly unknown[];
|
| 479 |
+
[key: string]: unknown;
|
| 480 |
+
};
|
| 481 |
+
readonly source: /* SkillSource — packages/agent-core-v2/src/features/skill/catalog/types.ts */ 'project' | 'user' | 'extra' | 'builtin';
|
| 482 |
+
readonly plugin?: /* SkillPluginContext — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 483 |
+
readonly id: string;
|
| 484 |
+
readonly instructions?: string;
|
| 485 |
+
};
|
| 486 |
+
readonly mermaid?: string;
|
| 487 |
+
readonly d2?: string;
|
| 488 |
+
readonly productSpecific?: boolean;
|
| 489 |
+
readonly experimentalFlag?: string;
|
| 490 |
+
}, options?: {
|
| 491 |
+
readonly replace?: boolean;
|
| 492 |
+
}) => void;
|
| 493 |
+
recordSkipped: (skills: readonly /* SkippedSkill — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 494 |
+
readonly path: string;
|
| 495 |
+
readonly type: string;
|
| 496 |
+
readonly reason: string;
|
| 497 |
+
}[]) => void;
|
| 498 |
+
addRoots: (roots: readonly string[]) => void;
|
| 499 |
+
getSkill: (name: string) => /* SkillDefinition — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 500 |
+
readonly name: string;
|
| 501 |
+
readonly description: string;
|
| 502 |
+
readonly path: string;
|
| 503 |
+
readonly dir: string;
|
| 504 |
+
readonly content: string;
|
| 505 |
+
readonly metadata: /* SkillMetadata — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 506 |
+
readonly name?: string;
|
| 507 |
+
readonly description?: string;
|
| 508 |
+
readonly type?: string;
|
| 509 |
+
readonly whenToUse?: string;
|
| 510 |
+
readonly disableModelInvocation?: boolean;
|
| 511 |
+
readonly isSubSkill?: boolean;
|
| 512 |
+
readonly safe?: boolean;
|
| 513 |
+
readonly arguments?: string | readonly unknown[];
|
| 514 |
+
[key: string]: unknown;
|
| 515 |
+
};
|
| 516 |
+
readonly source: /* SkillSource — packages/agent-core-v2/src/features/skill/catalog/types.ts */ 'project' | 'user' | 'extra' | 'builtin';
|
| 517 |
+
readonly plugin?: /* SkillPluginContext — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 518 |
+
readonly id: string;
|
| 519 |
+
readonly instructions?: string;
|
| 520 |
+
};
|
| 521 |
+
readonly mermaid?: string;
|
| 522 |
+
readonly d2?: string;
|
| 523 |
+
readonly productSpecific?: boolean;
|
| 524 |
+
readonly experimentalFlag?: string;
|
| 525 |
+
} | undefined;
|
| 526 |
+
getPluginSkill: (pluginId: string, name: string) => /* SkillDefinition — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 527 |
+
readonly name: string;
|
| 528 |
+
readonly description: string;
|
| 529 |
+
readonly path: string;
|
| 530 |
+
readonly dir: string;
|
| 531 |
+
readonly content: string;
|
| 532 |
+
readonly metadata: /* SkillMetadata — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 533 |
+
readonly name?: string;
|
| 534 |
+
readonly description?: string;
|
| 535 |
+
readonly type?: string;
|
| 536 |
+
readonly whenToUse?: string;
|
| 537 |
+
readonly disableModelInvocation?: boolean;
|
| 538 |
+
readonly isSubSkill?: boolean;
|
| 539 |
+
readonly safe?: boolean;
|
| 540 |
+
readonly arguments?: string | readonly unknown[];
|
| 541 |
+
[key: string]: unknown;
|
| 542 |
+
};
|
| 543 |
+
readonly source: /* SkillSource — packages/agent-core-v2/src/features/skill/catalog/types.ts */ 'project' | 'user' | 'extra' | 'builtin';
|
| 544 |
+
readonly plugin?: /* SkillPluginContext — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 545 |
+
readonly id: string;
|
| 546 |
+
readonly instructions?: string;
|
| 547 |
+
};
|
| 548 |
+
readonly mermaid?: string;
|
| 549 |
+
readonly d2?: string;
|
| 550 |
+
readonly productSpecific?: boolean;
|
| 551 |
+
readonly experimentalFlag?: string;
|
| 552 |
+
} | undefined;
|
| 553 |
+
renderSkillPrompt: (skill: /* SkillDefinition — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 554 |
+
readonly name: string;
|
| 555 |
+
readonly description: string;
|
| 556 |
+
readonly path: string;
|
| 557 |
+
readonly dir: string;
|
| 558 |
+
readonly content: string;
|
| 559 |
+
readonly metadata: /* SkillMetadata — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 560 |
+
readonly name?: string;
|
| 561 |
+
readonly description?: string;
|
| 562 |
+
readonly type?: string;
|
| 563 |
+
readonly whenToUse?: string;
|
| 564 |
+
readonly disableModelInvocation?: boolean;
|
| 565 |
+
readonly isSubSkill?: boolean;
|
| 566 |
+
readonly safe?: boolean;
|
| 567 |
+
readonly arguments?: string | readonly unknown[];
|
| 568 |
+
[key: string]: unknown;
|
| 569 |
+
};
|
| 570 |
+
readonly source: /* SkillSource — packages/agent-core-v2/src/features/skill/catalog/types.ts */ 'project' | 'user' | 'extra' | 'builtin';
|
| 571 |
+
readonly plugin?: /* SkillPluginContext — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 572 |
+
readonly id: string;
|
| 573 |
+
readonly instructions?: string;
|
| 574 |
+
};
|
| 575 |
+
readonly mermaid?: string;
|
| 576 |
+
readonly d2?: string;
|
| 577 |
+
readonly productSpecific?: boolean;
|
| 578 |
+
readonly experimentalFlag?: string;
|
| 579 |
+
}, rawArgs: string, context?: {
|
| 580 |
+
readonly sessionId?: string;
|
| 581 |
+
}) => string;
|
| 582 |
+
listSkills: () => readonly /* SkillDefinition — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 583 |
+
readonly name: string;
|
| 584 |
+
readonly description: string;
|
| 585 |
+
readonly path: string;
|
| 586 |
+
readonly dir: string;
|
| 587 |
+
readonly content: string;
|
| 588 |
+
readonly metadata: /* SkillMetadata — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 589 |
+
readonly name?: string;
|
| 590 |
+
readonly description?: string;
|
| 591 |
+
readonly type?: string;
|
| 592 |
+
readonly whenToUse?: string;
|
| 593 |
+
readonly disableModelInvocation?: boolean;
|
| 594 |
+
readonly isSubSkill?: boolean;
|
| 595 |
+
readonly safe?: boolean;
|
| 596 |
+
readonly arguments?: string | readonly unknown[];
|
| 597 |
+
[key: string]: unknown;
|
| 598 |
+
};
|
| 599 |
+
readonly source: /* SkillSource — packages/agent-core-v2/src/features/skill/catalog/types.ts */ 'project' | 'user' | 'extra' | 'builtin';
|
| 600 |
+
readonly plugin?: /* SkillPluginContext — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 601 |
+
readonly id: string;
|
| 602 |
+
readonly instructions?: string;
|
| 603 |
+
};
|
| 604 |
+
readonly mermaid?: string;
|
| 605 |
+
readonly d2?: string;
|
| 606 |
+
readonly productSpecific?: boolean;
|
| 607 |
+
readonly experimentalFlag?: string;
|
| 608 |
+
}[];
|
| 609 |
+
listInvocableSkills: () => readonly /* SkillDefinition — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 610 |
+
readonly name: string;
|
| 611 |
+
readonly description: string;
|
| 612 |
+
readonly path: string;
|
| 613 |
+
readonly dir: string;
|
| 614 |
+
readonly content: string;
|
| 615 |
+
readonly metadata: /* SkillMetadata — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 616 |
+
readonly name?: string;
|
| 617 |
+
readonly description?: string;
|
| 618 |
+
readonly type?: string;
|
| 619 |
+
readonly whenToUse?: string;
|
| 620 |
+
readonly disableModelInvocation?: boolean;
|
| 621 |
+
readonly isSubSkill?: boolean;
|
| 622 |
+
readonly safe?: boolean;
|
| 623 |
+
readonly arguments?: string | readonly unknown[];
|
| 624 |
+
[key: string]: unknown;
|
| 625 |
+
};
|
| 626 |
+
readonly source: /* SkillSource — packages/agent-core-v2/src/features/skill/catalog/types.ts */ 'project' | 'user' | 'extra' | 'builtin';
|
| 627 |
+
readonly plugin?: /* SkillPluginContext — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 628 |
+
readonly id: string;
|
| 629 |
+
readonly instructions?: string;
|
| 630 |
+
};
|
| 631 |
+
readonly mermaid?: string;
|
| 632 |
+
readonly d2?: string;
|
| 633 |
+
readonly productSpecific?: boolean;
|
| 634 |
+
readonly experimentalFlag?: string;
|
| 635 |
+
}[];
|
| 636 |
+
getSkillRoots: () => readonly string[];
|
| 637 |
+
getSkippedByPolicy: () => readonly /* SkippedSkill — packages/agent-core-v2/src/features/skill/catalog/types.ts */ {
|
| 638 |
+
readonly path: string;
|
| 639 |
+
readonly type: string;
|
| 640 |
+
readonly reason: string;
|
| 641 |
+
}[];
|
| 642 |
+
getKimiSkillsDescription: () => string;
|
| 643 |
+
getModelSkillListing: () => string;
|
| 644 |
+
};
|
| 645 |
+
// src/session/sessionActivity/sessionActivityService.ts
|
| 646 |
+
'sessionActivity.current': /* SessionActivityState — packages/agent-core-v2/src/session/sessionActivity/sessionActivity.ts */ {
|
| 647 |
+
readonly busy: boolean;
|
| 648 |
+
readonly mainTurnActive: boolean;
|
| 649 |
+
readonly pendingInteraction: /* SessionPendingInteraction — packages/agent-core-v2/src/session/sessionActivity/sessionActivity.ts */ 'none' | 'approval' | 'question';
|
| 650 |
+
readonly lastTurnReason?: 'completed' | 'cancelled' | 'failed';
|
| 651 |
+
};
|
| 652 |
+
'sessionActivity.folds': Map<string, /* AgentWorkFold — packages/agent-core-v2/src/session/sessionActivity/sessionActivityService.ts */ {
|
| 653 |
+
turnActive: boolean;
|
| 654 |
+
background: ReadonlySet<string>;
|
| 655 |
+
compacting: boolean;
|
| 656 |
+
lastTurnReason?: 'completed' | 'cancelled' | 'failed';
|
| 657 |
+
}>;
|
| 658 |
+
// src/session/sessionLog/sessionLogService.ts
|
| 659 |
+
'sessionLog.rootLevel': /* LogLevelState — packages/agent-core-v2/src/_base/log/logService.ts */ {
|
| 660 |
+
level: /* LogLevel — packages/agent-core-v2/src/_base/log/log.ts */ 'info' | 'off' | 'error' | 'warn' | 'debug';
|
| 661 |
+
};
|
| 662 |
+
// src/session/sessionMetadata/sessionMetadataService.ts
|
| 663 |
+
'sessionMetadata.data': /* SessionMeta — packages/agent-core-v2/src/session/sessionMetadata/sessionMetadata.ts */ {
|
| 664 |
+
readonly id: string;
|
| 665 |
+
readonly version?: number;
|
| 666 |
+
readonly title?: string;
|
| 667 |
+
readonly titleKind?: 'replaceable' | 'generated' | 'custom';
|
| 668 |
+
readonly lastPrompt?: string;
|
| 669 |
+
readonly createdAt: number;
|
| 670 |
+
readonly updatedAt: number;
|
| 671 |
+
readonly archived: boolean;
|
| 672 |
+
readonly archivedAt?: number;
|
| 673 |
+
readonly cwd?: string;
|
| 674 |
+
readonly forkedFrom?: string;
|
| 675 |
+
readonly agents?: Readonly<Record<string, /* AgentMeta — packages/agent-core-v2/src/session/sessionMetadata/sessionMetadata.ts */ {
|
| 676 |
+
readonly homedir?: string;
|
| 677 |
+
readonly type?: 'main' | 'sub' | 'independent';
|
| 678 |
+
readonly parentAgentId?: string | null;
|
| 679 |
+
readonly forkedFrom?: string;
|
| 680 |
+
readonly labels?: Readonly<Record<string, string>>;
|
| 681 |
+
readonly swarmItem?: string;
|
| 682 |
+
}>>;
|
| 683 |
+
readonly custom?: Record<string, unknown>;
|
| 684 |
+
readonly lastTurnReason?: 'completed' | 'cancelled' | 'failed';
|
| 685 |
+
} | undefined;
|
| 686 |
+
// src/session/sessionToolPolicy/sessionToolPolicyService.ts
|
| 687 |
+
'sessionToolPolicy.state': /* SessionToolPolicyState — packages/agent-core-v2/src/session/sessionToolPolicy/sessionToolPolicyService.ts */ {
|
| 688 |
+
readonly disabledTools: readonly string[];
|
| 689 |
+
};
|
| 690 |
+
// src/session/workspaceContext/workspaceContextService.ts
|
| 691 |
+
'workspaceContext.additionalDirs': string[];
|
| 692 |
+
'workspaceContext.workDir': string;
|
| 693 |
+
}
|
| 694 |
+
|
| 695 |
+
export type SessionStateKey = keyof SessionStateSnapshot;
|
| 696 |
+
|
| 697 |
+
/** Agent-scope keys registered into IAgentStateService. */
|
| 698 |
+
export interface AgentStateSnapshot {
|
| 699 |
+
// src/agent/agentsMdReminder/agentsMdReminderService.ts
|
| 700 |
+
'agentsMdReminder.cwd': string | undefined;
|
| 701 |
+
'agentsMdReminder.known': Set<string>;
|
| 702 |
+
'agentsMdReminder.seeded': boolean;
|
| 703 |
+
// src/agent/contextMemory/contextOps.ts
|
| 704 |
+
// replayable · durable · undoable — folds: ContextAppendMessage, ContextAppendLoopEvent, ContextClear, ContextApplyCompaction
|
| 705 |
+
'contextMemory': (/* ContextMessage — packages/agent-core-v2/src/agent/contextMemory/types.ts */ /* Message — packages/agent-core-v2/src/llm-adapter/contract/message.ts */ {
|
| 706 |
+
readonly role: /* Role — packages/agent-core-v2/src/human/llm/message.ts */ 'user' | 'system' | 'assistant' | 'tool';
|
| 707 |
+
readonly name?: string;
|
| 708 |
+
readonly content: (/* ContentPart — packages/agent-core-v2/src/human/llm/message.ts */ /* TextPart — packages/agent-core-v2/src/human/llm/message.ts */ {
|
| 709 |
+
type: 'text';
|
| 710 |
+
text: string;
|
| 711 |
+
} | /* ThinkPart — packages/agent-core-v2/src/human/llm/message.ts */ {
|
| 712 |
+
type: 'think';
|
| 713 |
+
think: string;
|
| 714 |
+
encrypted?: string;
|
| 715 |
+
detailsIndex?: number;
|
| 716 |
+
hidden?: boolean;
|
| 717 |
+
} | /* ImageURLPart — packages/agent-core-v2/src/human/llm/message.ts */ {
|
| 718 |
+
type: 'image_url';
|
| 719 |
+
imageUrl: {
|
| 720 |
+
url: string;
|
| 721 |
+
id?: string;
|
| 722 |
+
name?: string;
|
| 723 |
+
};
|
| 724 |
+
} | /* AudioURLPart — packages/agent-core-v2/src/human/llm/message.ts */ {
|
| 725 |
+
type: 'audio_url';
|
| 726 |
+
audioUrl: {
|
| 727 |
+
url: string;
|
| 728 |
+
id?: string;
|
| 729 |
+
};
|
| 730 |
+
} | /* VideoURLPart — packages/agent-core-v2/src/human/llm/message.ts */ {
|
| 731 |
+
type: 'video_url';
|
| 732 |
+
videoUrl: {
|
| 733 |
+
url: string;
|
| 734 |
+
id?: string;
|
| 735 |
+
name?: string;
|
| 736 |
+
};
|
| 737 |
+
})[];
|
| 738 |
+
readonly toolCalls: /* ToolCall — packages/agent-core-v2/src/human/llm/message.ts */ {
|
| 739 |
+
type: 'function';
|
| 740 |
+
id: string;
|
| 741 |
+
name: string;
|
| 742 |
+
arguments: string | null;
|
| 743 |
+
extras?: Record<string, unknown>;
|
| 744 |
+
rawId?: string;
|
| 745 |
+
_streamIndex?: string | number;
|
| 746 |
+
}[];
|
| 747 |
+
readonly toolCallId?: string;
|
| 748 |
+
readonly partial?: boolean;
|
| 749 |
+
readonly tools?: readonly /* ToolDescription — packages/agent-core-v2/src/human/llm/message.ts */ {
|
| 750 |
+
name: string;
|
| 751 |
+
description: string;
|
| 752 |
+
parameters: Record<string, unknown>;
|
| 753 |
+
deferred?: true;
|
| 754 |
+
}[];
|
| 755 |
+
} & {
|
| 756 |
+
readonly id?: string;
|
| 757 |
+
readonly providerMessageId?: string;
|
| 758 |
+
readonly origin?: /* UserPromptOrigin — packages/agent-core-v2/src/agent/contextMemory/types.ts */ {
|
| 759 |
+
readonly kind: 'user';
|
| 760 |
+
readonly clientMetadata?: readonly Readonly<Record<string, unknown>>[];
|
| 761 |
+
readonly skillActivations?: readonly /* BundledSkillActivation — packages/agent-core-v2/src/agent/contextMemory/types.ts */ {
|
| 762 |
+
readonly activationId: string;
|
| 763 |
+
readonly skillName: string;
|
| 764 |
+
readonly skillArgs?: string;
|
| 765 |
+
readonly skillType?: string;
|
| 766 |
+
readonly skillPath?: string;
|
| 767 |
+
readonly skillSource?: 'project' | 'user' | 'extra' | 'builtin';
|
| 768 |
+
}[];
|
| 769 |
+
readonly attachments?: readonly /* PromptFileAttachment — packages/agent-core-v2/src/agent/contextMemory/types.ts */ {
|
| 770 |
+
readonly name: string;
|
| 771 |
+
readonly mediaType: string;
|
| 772 |
+
readonly size: number;
|
| 773 |
+
readonly path: string;
|
| 774 |
+
}[];
|
| 775 |
+
} | /* SkillActivationOrigin — packages/agent-core-v2/src/agent/contextMemory/types.ts */ {
|
| 776 |
+
readonly kind: 'skill_activation';
|
| 777 |
+
readonly clientMetadata?: readonly Readonly<Record<string, unknown>>[];
|
| 778 |
+
readonly activationId: string;
|
| 779 |
+
readonly skillName: string;
|
| 780 |
+
readonly skillArgs?: string;
|
| 781 |
+
readonly trigger: 'user-slash' | 'model-tool' | 'nested-skill';
|
| 782 |
+
readonly skillType?: string;
|
| 783 |
+
readonly skillPath?: string;
|
| 784 |
+
readonly skillSource?: 'project' | 'user' | 'extra' | 'builtin';
|
| 785 |
+
readonly attachments?: readonly /* PromptFileAttachment — packages/agent-core-v2/src/agent/contextMemory/types.ts */ {
|
| 786 |
+
readonly name: string;
|
| 787 |
+
readonly mediaType: string;
|
| 788 |
+
readonly size: number;
|
| 789 |
+
readonly path: string;
|
| 790 |
+
}[];
|
| 791 |
+
} | /* PluginCommandOrigin — packages/agent-core-v2/src/agent/contextMemory/types.ts */ {
|
| 792 |
+
readonly kind: 'plugin_command';
|
| 793 |
+
readonly activationId: string;
|
| 794 |
+
readonly pluginId: string;
|
| 795 |
+
readonly commandName: string;
|
| 796 |
+
readonly commandArgs?: string;
|
| 797 |
+
readonly trigger: 'user-slash';
|
| 798 |
+
} | /* InjectionOrigin — packages/agent-core-v2/src/agent/contextMemory/types.ts */ {
|
| 799 |
+
readonly kind: 'injection';
|
| 800 |
+
readonly variant: string;
|
| 801 |
+
readonly ownerPromptId?: string;
|
| 802 |
+
readonly disclosure?: unknown;
|
| 803 |
+
} | /* ShellCommandOrigin — packages/agent-core-v2/src/agent/contextMemory/types.ts */ {
|
| 804 |
+
readonly kind: 'shell_command';
|
| 805 |
+
readonly phase: 'input' | 'output';
|
| 806 |
+
readonly isError?: boolean;
|
| 807 |
+
} | /* CompactionSummaryOrigin — packages/agent-core-v2/src/agent/contextMemory/types.ts */ {
|
| 808 |
+
readonly kind: 'compaction_summary';
|
| 809 |
+
} | /* SystemTriggerOrigin — packages/agent-core-v2/src/agent/contextMemory/types.ts */ {
|
| 810 |
+
readonly kind: 'system_trigger';
|
| 811 |
+
readonly name: string;
|
| 812 |
+
} | /* TaskOrigin — packages/agent-core-v2/src/agent/contextMemory/types.ts */ {
|
| 813 |
+
readonly kind: 'task';
|
| 814 |
+
readonly taskId: string;
|
| 815 |
+
readonly status: /* AgentTaskStatus — packages/agent-core-v2/src/agent/task/types.ts */ 'completed' | 'failed' | 'running' | 'timed_out' | 'killed' | 'lost';
|
| 816 |
+
readonly notificationId: string;
|
| 817 |
+
} | /* CronJobOrigin — packages/agent-core-v2/src/agent/contextMemory/types.ts */ {
|
| 818 |
+
readonly kind: 'cron_job';
|
| 819 |
+
readonly jobId: string;
|
| 820 |
+
readonly cron: string;
|
| 821 |
+
readonly recurring: boolean;
|
| 822 |
+
readonly coalescedCount: number;
|
| 823 |
+
readonly stale: boolean;
|
| 824 |
+
} | /* CronMissedOrigin — packages/agent-core-v2/src/agent/contextMemory/types.ts */ {
|
| 825 |
+
readonly kind: 'cron_missed';
|
| 826 |
+
readonly count: number;
|
| 827 |
+
} | /* HookResultOrigin — packages/agent-core-v2/src/agent/contextMemory/types.ts */ {
|
| 828 |
+
readonly kind: 'hook_result';
|
| 829 |
+
readonly event: string;
|
| 830 |
+
readonly blocked?: boolean;
|
| 831 |
+
} | /* RetryOrigin — packages/agent-core-v2/src/agent/contextMemory/types.ts */ {
|
| 832 |
+
readonly kind: 'retry';
|
| 833 |
+
readonly trigger?: string;
|
| 834 |
+
};
|
| 835 |
+
readonly isError?: boolean;
|
| 836 |
+
toolCallDisplays?: Record<string, /* ToolInputDisplay — packages/agent-core-v2/src/tool/toolInputDisplay.ts */ {
|
| 837 |
+
kind: 'command';
|
| 838 |
+
command: string;
|
| 839 |
+
cwd?: string;
|
| 840 |
+
description?: string;
|
| 841 |
+
language?: 'bash';
|
| 842 |
+
} | {
|
| 843 |
+
kind: 'file_io';
|
| 844 |
+
operation: 'read' | 'write' | 'edit' | 'glob' | 'grep';
|
| 845 |
+
path: string;
|
| 846 |
+
detail?: string;
|
| 847 |
+
content?: string;
|
| 848 |
+
before?: string;
|
| 849 |
+
after?: string;
|
| 850 |
+
} | {
|
| 851 |
+
kind: 'diff';
|
| 852 |
+
path: string;
|
| 853 |
+
before: string;
|
| 854 |
+
after: string;
|
| 855 |
+
hunks?: number;
|
| 856 |
+
} | {
|
| 857 |
+
kind: 'search';
|
| 858 |
+
query: string;
|
| 859 |
+
scope?: string;
|
| 860 |
+
} | {
|
| 861 |
+
kind: 'url_fetch';
|
| 862 |
+
url: string;
|
| 863 |
+
method?: string;
|
| 864 |
+
} | {
|
| 865 |
+
kind: 'agent_call';
|
| 866 |
+
agent_name: string;
|
| 867 |
+
prompt: string;
|
| 868 |
+
background?: boolean;
|
| 869 |
+
} | {
|
| 870 |
+
kind: 'skill_call';
|
| 871 |
+
skill_name: string;
|
| 872 |
+
args?: string;
|
| 873 |
+
} | {
|
| 874 |
+
kind: 'todo_list';
|
| 875 |
+
items: {
|
| 876 |
+
title: string;
|
| 877 |
+
status: string;
|
| 878 |
+
}[];
|
| 879 |
+
} | {
|
| 880 |
+
kind: 'task';
|
| 881 |
+
task_id: string;
|
| 882 |
+
status: string;
|
| 883 |
+
description: string;
|
| 884 |
+
task_kind?: string;
|
| 885 |
+
} | {
|
| 886 |
+
kind: 'task_stop';
|
| 887 |
+
task_id: string;
|
| 888 |
+
task_description: string;
|
| 889 |
+
} | {
|
| 890 |
+
kind: 'plan_review';
|
| 891 |
+
plan: string;
|
| 892 |
+
path?: string;
|
| 893 |
+
options?: readonly {
|
| 894 |
+
label: string;
|
| 895 |
+
description: string;
|
| 896 |
+
}[];
|
| 897 |
+
} | {
|
| 898 |
+
kind: 'goal_start';
|
| 899 |
+
objective: string;
|
| 900 |
+
completionCriterion?: string;
|
| 901 |
+
mode: 'manual' | 'yolo';
|
| 902 |
+
} | {
|
| 903 |
+
kind: 'generic';
|
| 904 |
+
summary: string;
|
| 905 |
+
detail?: unknown;
|
| 906 |
+
}>;
|
| 907 |
+
readonly note?: string;
|
| 908 |
+
})[];
|
| 909 |
+
// src/agent/contextProjector/contextProjectorService.ts
|
| 910 |
+
'contextProjector.lastRepairSignature': string | null;
|
| 911 |
+
// src/agent/fullCompaction/compactionOps.ts
|
| 912 |
+
// replayable · durable — folds: FullCompactionBegin, FullCompactionCancel, FullCompactionComplete
|
| 913 |
+
'fullCompaction': /* CompactionState — packages/agent-core-v2/src/agent/fullCompaction/compactionOps.ts */ {
|
| 914 |
+
readonly phase: /* CompactionPhase — packages/agent-core-v2/src/agent/fullCompaction/compactionOps.ts */ 'completed' | 'cancelled' | 'running' | 'idle';
|
| 915 |
+
};
|
| 916 |
+
// replayable · durable — folds: ContextApplyCompaction, ContextClear
|
| 917 |
+
'fullCompaction.wireRanges': readonly /* WireLineRange — packages/agent-core-v2/src/wire/record.ts */ {
|
| 918 |
+
readonly start: number;
|
| 919 |
+
readonly end: number;
|
| 920 |
+
}[];
|
| 921 |
+
// src/agent/fullCompaction/fullCompactionService.ts
|
| 922 |
+
'fullCompaction.activeTurnId': number | undefined;
|
| 923 |
+
'fullCompaction.compactionCountInTurn': number;
|
| 924 |
+
'fullCompaction.consecutiveOverflowCompactions': number;
|
| 925 |
+
'fullCompaction.lastCompactedTokenCount': number | null;
|
| 926 |
+
'fullCompaction.observedMaxContextTokensByModel': Map<string, number>;
|
| 927 |
+
// src/agent/interruptionReminder/interruptionReminderOps.ts
|
| 928 |
+
// replayable · durable — folds: InterruptionReminderRecorded
|
| 929 |
+
'interruptionReminder': null;
|
| 930 |
+
// src/agent/llmRequester/llmRequestOps.ts
|
| 931 |
+
// replayable · durable — folds: LlmToolsSnapshot, LlmRequest
|
| 932 |
+
'llm.requestTrace': /* LlmRequestTraceState — packages/agent-core-v2/src/agent/llmRequester/llmRequestOps.ts */ {
|
| 933 |
+
readonly seenToolsHashes: readonly string[];
|
| 934 |
+
};
|
| 935 |
+
// src/agent/llmRequester/llmRequesterService.ts
|
| 936 |
+
'llmRequester.emittedThinkingEffortWarnings': Set<string>;
|
| 937 |
+
'llmRequester.lastConfigLogSignature': string | undefined;
|
| 938 |
+
'llmRequester.mediaDegradedTurns': Set<number>;
|
| 939 |
+
'llmRequester.mediaStrippedTurns': Map<number, /* MediaStripSnapshot — packages/agent-core-v2/src/agent/contextProjector/contextProjector.ts */ {
|
| 940 |
+
readonly "__@mediaStripSnapshotBrand": undefined;
|
| 941 |
+
}>;
|
| 942 |
+
'llmRequester.turnConfigs': Map<number, /* TurnRequestConfig — packages/agent-core-v2/src/agent/llmRequester/llmRequesterService.ts */ {
|
| 943 |
+
readonly resolved: /* ProfileModelContext — packages/agent-core-v2/src/agent/profile/profile.ts */ {
|
| 944 |
+
readonly modelAlias: string;
|
| 945 |
+
readonly modelCapabilities: /* ModelCapability — packages/agent-core-v2/src/llm-adapter/contract/capability.ts */ {
|
| 946 |
+
readonly image_in: boolean;
|
| 947 |
+
readonly video_in: boolean;
|
| 948 |
+
readonly audio_in: boolean;
|
| 949 |
+
readonly thinking: boolean;
|
| 950 |
+
readonly tool_use: boolean;
|
| 951 |
+
readonly max_context_tokens: number;
|
| 952 |
+
readonly max_input_tokens?: number;
|
| 953 |
+
readonly dynamically_loaded_tools?: boolean;
|
| 954 |
+
};
|
| 955 |
+
readonly maxOutputSize: number | undefined;
|
| 956 |
+
readonly alwaysThinking: boolean | undefined;
|
| 957 |
+
readonly thinkingLevel: /* ThinkingEffort — packages/agent-core-v2/src/human/llm/thinking.ts */ 'off' | 'on' | (string & {});
|
| 958 |
+
readonly reservedContextSize: number | undefined;
|
| 959 |
+
readonly compactionTriggerRatio: number | undefined;
|
| 960 |
+
readonly compactionMaxAttempts: number | undefined;
|
| 961 |
+
};
|
| 962 |
+
readonly params: /* ModelRequestParams — packages/agent-core-v2/src/llm-adapter/model/model-requester.ts */ {
|
| 963 |
+
readonly cacheKey?: string;
|
| 964 |
+
readonly sampling?: /* SamplingOptions — packages/agent-core-v2/src/llm-adapter/model/model-requester.ts */ {
|
| 965 |
+
readonly temperature?: number;
|
| 966 |
+
readonly topP?: number;
|
| 967 |
+
};
|
| 968 |
+
readonly thinkingEffort?: 'off' | 'on' | (string & {});
|
| 969 |
+
readonly thinkingKeep?: string;
|
| 970 |
+
readonly maxCompletionTokens?: number;
|
| 971 |
+
readonly usedContextTokens?: number;
|
| 972 |
+
readonly maxContextTokens?: number;
|
| 973 |
+
readonly onTraceId?: (traceId: string | null) => void;
|
| 974 |
+
};
|
| 975 |
+
readonly systemPrompt: string;
|
| 976 |
+
}>;
|
| 977 |
+
// src/agent/loop/loopService.ts
|
| 978 |
+
'loop.disposing': boolean;
|
| 979 |
+
'loop.lastRequestTraceId': string | undefined;
|
| 980 |
+
// src/agent/loop/turnOps.ts
|
| 981 |
+
// replayable · durable — folds: ContextAppendLoopEvent, TurnPrompt, TurnSteer, ContextUndo, ContextApplyCompaction, ContextClear, TurnCancel, TurnEnded
|
| 982 |
+
'turn': /* TurnModelState — packages/agent-core-v2/src/agent/loop/turnOps.ts */ {
|
| 983 |
+
readonly nextTurnId: number;
|
| 984 |
+
readonly cancelledTurnIds: readonly number[];
|
| 985 |
+
readonly anchorTurnIds: readonly number[];
|
| 986 |
+
readonly lastEnded?: {
|
| 987 |
+
readonly turnId: number;
|
| 988 |
+
readonly reason: 'completed' | 'cancelled' | 'failed' | 'blocked';
|
| 989 |
+
readonly durationMs?: number;
|
| 990 |
+
};
|
| 991 |
+
};
|
| 992 |
+
// src/agent/mcp/mcpDiscoveryOps.ts
|
| 993 |
+
// replayable · durable — folds: McpToolsDiscovered
|
| 994 |
+
'mcp.discovery': /* McpDiscoveryState — packages/agent-core-v2/src/agent/mcp/mcpDiscoveryOps.ts */ {
|
| 995 |
+
readonly seen: readonly string[];
|
| 996 |
+
};
|
| 997 |
+
// src/agent/mcp/mcpService.ts
|
| 998 |
+
'mcp.discoveryWritesReady': boolean;
|
| 999 |
+
'mcp.mcpToolsByServer': Map<string, string[]>;
|
| 1000 |
+
// src/agent/media/mediaResolverService.ts
|
| 1001 |
+
'media.budgetDropped': Set<string>;
|
| 1002 |
+
'media.resolved': Map<string, /* ContentPart — packages/agent-core-v2/src/human/llm/message.ts */ /* TextPart — packages/agent-core-v2/src/human/llm/message.ts */ {
|
| 1003 |
+
type: 'text';
|
| 1004 |
+
text: string;
|
| 1005 |
+
} | /* ThinkPart — packages/agent-core-v2/src/human/llm/message.ts */ {
|
| 1006 |
+
type: 'think';
|
| 1007 |
+
think: string;
|
| 1008 |
+
encrypted?: string;
|
| 1009 |
+
detailsIndex?: number;
|
| 1010 |
+
hidden?: boolean;
|
| 1011 |
+
} | /* ImageURLPart — packages/agent-core-v2/src/human/llm/message.ts */ {
|
| 1012 |
+
type: 'image_url';
|
| 1013 |
+
imageUrl: {
|
| 1014 |
+
url: string;
|
| 1015 |
+
id?: string;
|
| 1016 |
+
name?: string;
|
| 1017 |
+
};
|
| 1018 |
+
} | /* AudioURLPart — packages/agent-core-v2/src/human/llm/message.ts */ {
|
| 1019 |
+
type: 'audio_url';
|
| 1020 |
+
audioUrl: {
|
| 1021 |
+
url: string;
|
| 1022 |
+
id?: string;
|
| 1023 |
+
};
|
| 1024 |
+
} | /* VideoURLPart — packages/agent-core-v2/src/human/llm/message.ts */ {
|
| 1025 |
+
type: 'video_url';
|
| 1026 |
+
videoUrl: {
|
| 1027 |
+
url: string;
|
| 1028 |
+
id?: string;
|
| 1029 |
+
name?: string;
|
| 1030 |
+
};
|
| 1031 |
+
}>;
|
| 1032 |
+
// src/agent/media/mediaToolsRegistrar.ts
|
| 1033 |
+
'media.registeredKey': string | undefined;
|
| 1034 |
+
// src/agent/permissionMode/injection/permissionModeInjection.ts
|
| 1035 |
+
'permissionMode.lastMode': 'manual' | 'yolo' | 'auto' | undefined;
|
| 1036 |
+
// src/agent/permissionMode/permissionModeOps.ts
|
| 1037 |
+
// replayable · durable — folds: PermissionSetMode
|
| 1038 |
+
'permissionMode': /* PermissionMode — packages/agent-core-v2/src/agent/permissionPolicy/types.ts */ 'manual' | 'yolo' | 'auto';
|
| 1039 |
+
// replayable · durable — folds: PermissionSetMode
|
| 1040 |
+
'permissionMode.configured': boolean;
|
| 1041 |
+
// src/agent/permissionRules/permissionRulesOps.ts
|
| 1042 |
+
// replayable · durable — folds: PermissionRulesAdd, PermissionRecordApprovalResult
|
| 1043 |
+
'permissionRules': /* PermissionRulesModelState — packages/agent-core-v2/src/agent/permissionRules/permissionRulesOps.ts */ {
|
| 1044 |
+
readonly rules: readonly /* PermissionRule — packages/agent-core-v2/src/agent/permissionRules/permissionRules.ts */ {
|
| 1045 |
+
readonly decision: /* PermissionRuleDecision — packages/agent-core-v2/src/agent/permissionRules/permissionRules.ts */ 'allow' | 'deny' | 'ask';
|
| 1046 |
+
readonly scope: /* PermissionRuleScope — packages/agent-core-v2/src/agent/permissionRules/permissionRules.ts */ 'project' | 'user' | 'turn-override' | 'session-runtime';
|
| 1047 |
+
readonly pattern: string;
|
| 1048 |
+
readonly reason?: string;
|
| 1049 |
+
}[];
|
| 1050 |
+
readonly sessionApprovalRulePatterns: readonly string[];
|
| 1051 |
+
};
|
| 1052 |
+
// src/agent/plugin/agentPluginOps.ts
|
| 1053 |
+
// replayable · durable — folds: PluginSessionStartEvent
|
| 1054 |
+
'pluginSessionStartSnapshot': /* PluginSessionStartSnapshotState — packages/agent-core-v2/src/agent/plugin/agentPluginOps.ts */ {
|
| 1055 |
+
readonly initialized: boolean;
|
| 1056 |
+
readonly content?: string;
|
| 1057 |
+
};
|
| 1058 |
+
// src/agent/plugin/agentPluginService.ts
|
| 1059 |
+
'agentPlugin.sessionStartRefreshPending': boolean;
|
| 1060 |
+
// src/agent/profile/profileOps.ts
|
| 1061 |
+
// replayable · durable — folds: ProfileBind, ConfigUpdate
|
| 1062 |
+
'profile': /* ProfileModelState — packages/agent-core-v2/src/agent/profile/profileOps.ts */ {
|
| 1063 |
+
readonly modelAlias?: string;
|
| 1064 |
+
readonly profileName?: string;
|
| 1065 |
+
readonly thinkingLevel: string;
|
| 1066 |
+
readonly systemPrompt: string;
|
| 1067 |
+
readonly environmentDisclosure?: /* EnvironmentDisclosureSnapshot — packages/agent-core-v2/src/app/agentProfileCatalog/agentProfileCatalog.ts */ {
|
| 1068 |
+
readonly cwd: string;
|
| 1069 |
+
};
|
| 1070 |
+
readonly renderGeneration: number;
|
| 1071 |
+
readonly agentsMdPaths?: readonly string[];
|
| 1072 |
+
readonly disallowedTools?: readonly string[];
|
| 1073 |
+
readonly subagents?: readonly string[];
|
| 1074 |
+
};
|
| 1075 |
+
// replayable · durable — folds: ToolsSetActiveTools, ToolsResetActiveTools, ProfileBind
|
| 1076 |
+
'profile.activeTools': /* ActiveToolsState — packages/agent-core-v2/src/agent/profile/profileOps.ts */ readonly string[] | undefined;
|
| 1077 |
+
// src/agent/profile/profileService.ts
|
| 1078 |
+
'profile.activeToolNamesOverlay': readonly string[] | undefined;
|
| 1079 |
+
'profile.agentsMdWarning': string | undefined;
|
| 1080 |
+
'profile.emittedPluginBudgetWarnings': Set<string>;
|
| 1081 |
+
'profile.emittedThinkingEffortWarnings': Set<string>;
|
| 1082 |
+
'profile.emittedToolPatternWarnings': Set<string>;
|
| 1083 |
+
// src/agent/runtimeBinding/runtimeBindingOps.ts
|
| 1084 |
+
// replayable · durable — folds: RuntimeSetBinding
|
| 1085 |
+
'runtimeBinding': /* RuntimeBinding — packages/agent-core-v2/src/runtime/runtime.ts */ {
|
| 1086 |
+
readonly workspaceId: string;
|
| 1087 |
+
readonly runtimeId: string;
|
| 1088 |
+
} | undefined;
|
| 1089 |
+
// src/agent/runtimeBinding/runtimeBindingService.ts
|
| 1090 |
+
'runtime.binding': /* RuntimeBinding — packages/agent-core-v2/src/runtime/runtime.ts */ {
|
| 1091 |
+
readonly workspaceId: string;
|
| 1092 |
+
readonly runtimeId: string;
|
| 1093 |
+
};
|
| 1094 |
+
// src/agent/shellCommand/shellCommandService.ts
|
| 1095 |
+
'shellCommand.tasks': Map<string, string>;
|
| 1096 |
+
// src/agent/task/taskOps.ts
|
| 1097 |
+
// replayable · durable — folds: TaskStarted, TaskTerminated
|
| 1098 |
+
'task': /* TaskModelState — packages/agent-core-v2/src/agent/task/taskOps.ts */ Map<string, /* AgentTaskInfo — packages/agent-core-v2/src/agent/task/types.ts */ /* QuestionTaskInfo — packages/agent-core-v2/src/agent/tools/ask-user-question/question-background-task.ts */ {
|
| 1099 |
+
readonly kind: 'question';
|
| 1100 |
+
readonly questionCount: number;
|
| 1101 |
+
readonly toolCallId?: string;
|
| 1102 |
+
readonly taskId: string;
|
| 1103 |
+
readonly description: string;
|
| 1104 |
+
readonly status: /* AgentTaskStatus — packages/agent-core-v2/src/agent/task/types.ts */ 'completed' | 'failed' | 'running' | 'timed_out' | 'killed' | 'lost';
|
| 1105 |
+
readonly detached?: boolean;
|
| 1106 |
+
readonly startedAt: number;
|
| 1107 |
+
readonly endedAt: number | null;
|
| 1108 |
+
readonly stopReason?: string;
|
| 1109 |
+
readonly terminalNotificationSuppressed?: boolean;
|
| 1110 |
+
readonly resumeReminded?: boolean;
|
| 1111 |
+
readonly timeoutMs?: number;
|
| 1112 |
+
} | /* SubagentTaskInfo — packages/agent-core-v2/src/agent/tools/agent/subagent-task.ts */ {
|
| 1113 |
+
readonly kind: 'agent';
|
| 1114 |
+
readonly agentId?: string;
|
| 1115 |
+
readonly subagentType?: string;
|
| 1116 |
+
readonly parentToolCallId?: string;
|
| 1117 |
+
readonly model?: string;
|
| 1118 |
+
readonly thinkingEffort?: string;
|
| 1119 |
+
readonly stopCode?: string;
|
| 1120 |
+
readonly taskId: string;
|
| 1121 |
+
readonly description: string;
|
| 1122 |
+
readonly status: /* AgentTaskStatus — packages/agent-core-v2/src/agent/task/types.ts */ 'completed' | 'failed' | 'running' | 'timed_out' | 'killed' | 'lost';
|
| 1123 |
+
readonly detached?: boolean;
|
| 1124 |
+
readonly startedAt: number;
|
| 1125 |
+
readonly endedAt: number | null;
|
| 1126 |
+
readonly stopReason?: string;
|
| 1127 |
+
readonly terminalNotificationSuppressed?: boolean;
|
| 1128 |
+
readonly resumeReminded?: boolean;
|
| 1129 |
+
readonly timeoutMs?: number;
|
| 1130 |
+
} | /* ProcessTaskInfo — packages/agent-core-v2/src/agent/tools/os/bash/process-task.ts */ {
|
| 1131 |
+
readonly kind: 'process';
|
| 1132 |
+
readonly command: string;
|
| 1133 |
+
readonly pid: number;
|
| 1134 |
+
readonly exitCode: number | null;
|
| 1135 |
+
readonly parentToolCallId?: string;
|
| 1136 |
+
readonly taskId: string;
|
| 1137 |
+
readonly description: string;
|
| 1138 |
+
readonly status: /* AgentTaskStatus — packages/agent-core-v2/src/agent/task/types.ts */ 'completed' | 'failed' | 'running' | 'timed_out' | 'killed' | 'lost';
|
| 1139 |
+
readonly detached?: boolean;
|
| 1140 |
+
readonly startedAt: number;
|
| 1141 |
+
readonly endedAt: number | null;
|
| 1142 |
+
readonly stopReason?: string;
|
| 1143 |
+
readonly terminalNotificationSuppressed?: boolean;
|
| 1144 |
+
readonly resumeReminded?: boolean;
|
| 1145 |
+
readonly timeoutMs?: number;
|
| 1146 |
+
}>;
|
| 1147 |
+
// src/agent/task/taskService.ts
|
| 1148 |
+
'task.activeTaskReminderPending': boolean;
|
| 1149 |
+
'task.deliveredNotificationKeys': Set<string>;
|
| 1150 |
+
'task.ghosts': Map<string, /* AgentTaskInfo — packages/agent-core-v2/src/agent/task/types.ts */ /* QuestionTaskInfo — packages/agent-core-v2/src/agent/tools/ask-user-question/question-background-task.ts */ {
|
| 1151 |
+
readonly kind: 'question';
|
| 1152 |
+
readonly questionCount: number;
|
| 1153 |
+
readonly toolCallId?: string;
|
| 1154 |
+
readonly taskId: string;
|
| 1155 |
+
readonly description: string;
|
| 1156 |
+
readonly status: /* AgentTaskStatus — packages/agent-core-v2/src/agent/task/types.ts */ 'completed' | 'failed' | 'running' | 'timed_out' | 'killed' | 'lost';
|
| 1157 |
+
readonly detached?: boolean;
|
| 1158 |
+
readonly startedAt: number;
|
| 1159 |
+
readonly endedAt: number | null;
|
| 1160 |
+
readonly stopReason?: string;
|
| 1161 |
+
readonly terminalNotificationSuppressed?: boolean;
|
| 1162 |
+
readonly resumeReminded?: boolean;
|
| 1163 |
+
readonly timeoutMs?: number;
|
| 1164 |
+
} | /* SubagentTaskInfo — packages/agent-core-v2/src/agent/tools/agent/subagent-task.ts */ {
|
| 1165 |
+
readonly kind: 'agent';
|
| 1166 |
+
readonly agentId?: string;
|
| 1167 |
+
readonly subagentType?: string;
|
| 1168 |
+
readonly parentToolCallId?: string;
|
| 1169 |
+
readonly model?: string;
|
| 1170 |
+
readonly thinkingEffort?: string;
|
| 1171 |
+
readonly stopCode?: string;
|
| 1172 |
+
readonly taskId: string;
|
| 1173 |
+
readonly description: string;
|
| 1174 |
+
readonly status: /* AgentTaskStatus — packages/agent-core-v2/src/agent/task/types.ts */ 'completed' | 'failed' | 'running' | 'timed_out' | 'killed' | 'lost';
|
| 1175 |
+
readonly detached?: boolean;
|
| 1176 |
+
readonly startedAt: number;
|
| 1177 |
+
readonly endedAt: number | null;
|
| 1178 |
+
readonly stopReason?: string;
|
| 1179 |
+
readonly terminalNotificationSuppressed?: boolean;
|
| 1180 |
+
readonly resumeReminded?: boolean;
|
| 1181 |
+
readonly timeoutMs?: number;
|
| 1182 |
+
} | /* ProcessTaskInfo — packages/agent-core-v2/src/agent/tools/os/bash/process-task.ts */ {
|
| 1183 |
+
readonly kind: 'process';
|
| 1184 |
+
readonly command: string;
|
| 1185 |
+
readonly pid: number;
|
| 1186 |
+
readonly exitCode: number | null;
|
| 1187 |
+
readonly parentToolCallId?: string;
|
| 1188 |
+
readonly taskId: string;
|
| 1189 |
+
readonly description: string;
|
| 1190 |
+
readonly status: /* AgentTaskStatus — packages/agent-core-v2/src/agent/task/types.ts */ 'completed' | 'failed' | 'running' | 'timed_out' | 'killed' | 'lost';
|
| 1191 |
+
readonly detached?: boolean;
|
| 1192 |
+
readonly startedAt: number;
|
| 1193 |
+
readonly endedAt: number | null;
|
| 1194 |
+
readonly stopReason?: string;
|
| 1195 |
+
readonly terminalNotificationSuppressed?: boolean;
|
| 1196 |
+
readonly resumeReminded?: boolean;
|
| 1197 |
+
readonly timeoutMs?: number;
|
| 1198 |
+
}>;
|
| 1199 |
+
// replayable · durable · undoable — folds: ContextAppendMessage, TaskWaitDelivered
|
| 1200 |
+
'task.notificationDelivery': readonly string[];
|
| 1201 |
+
'task.scheduledNotificationKeys': Set<string>;
|
| 1202 |
+
// src/agent/toolDedupe/toolDedupeService.ts
|
| 1203 |
+
'toolDedupe.activeStep': number;
|
| 1204 |
+
'toolDedupe.activeTurnId': number | undefined;
|
| 1205 |
+
'toolDedupe.callKeyByCallId': Map<string, string>;
|
| 1206 |
+
'toolDedupe.consecutiveCount': number;
|
| 1207 |
+
'toolDedupe.consecutiveKey': string | null;
|
| 1208 |
+
'toolDedupe.handoffPhase': /* HandoffPhase — packages/agent-core-v2/src/agent/toolDedupe/toolDedupeService.ts */ 'idle' | 'active' | 'pending' | 'done';
|
| 1209 |
+
'toolDedupe.originalCallIndex': Map<string, number>;
|
| 1210 |
+
'toolDedupe.stepCalls': string[];
|
| 1211 |
+
'toolDedupe.syntheticCallIds': Set<string>;
|
| 1212 |
+
'toolDedupe.turnCallRecords': Map<string, /* TurnCallRecord — packages/agent-core-v2/src/agent/toolDedupe/toolDedupeService.ts */ {
|
| 1213 |
+
count: number;
|
| 1214 |
+
lastStep: number;
|
| 1215 |
+
}>;
|
| 1216 |
+
'toolDedupe.turnRepeatCount': number;
|
| 1217 |
+
// src/agent/toolExecutor/toolExecutorService.ts
|
| 1218 |
+
'toolExecutor.dupTypeTurnId': number | undefined;
|
| 1219 |
+
'toolExecutor.toolCallDupTypes': Map<string, /* ToolCallDupType — packages/agent-core-v2/src/agent/toolExecutor/toolExecutor.ts */ 'same_step' | 'cross_step'>;
|
| 1220 |
+
// src/agent/toolSelect/toolSelectService.ts
|
| 1221 |
+
'toolSelect.pendingLoaded': Set<string>;
|
| 1222 |
+
// src/agent/userTool/userToolOps.ts
|
| 1223 |
+
// replayable · durable — folds: ToolsRegisterUserTool, ToolsUnregisterUserTool
|
| 1224 |
+
'userTool': /* UserToolModelState — packages/agent-core-v2/src/agent/userTool/userToolOps.ts */ Map<string, /* UserToolRegistration — packages/agent-core-v2/src/agent/userTool/userTool.ts */ {
|
| 1225 |
+
readonly name: string;
|
| 1226 |
+
readonly description: string;
|
| 1227 |
+
readonly parameters: Record<string, unknown>;
|
| 1228 |
+
readonly disclosure?: 'deferred' | 'inline';
|
| 1229 |
+
}>;
|
| 1230 |
+
// src/features/externalHooks/agent/agentExternalHooksService.ts
|
| 1231 |
+
'externalHooks.stopHookContinuationUsed': boolean;
|
| 1232 |
+
// src/features/fileHistory/fileHistoryOps.ts
|
| 1233 |
+
// replayable · durable — folds: FileHistoryCheckpointed, FileHistoryTracked
|
| 1234 |
+
'fileHistory': /* FileHistoryState — packages/agent-core-v2/src/features/fileHistory/fileHistory.ts */ {
|
| 1235 |
+
readonly checkpoints: readonly /* FileHistoryCheckpointRecord — packages/agent-core-v2/src/features/fileHistory/fileHistory.ts */ {
|
| 1236 |
+
readonly turnId: number;
|
| 1237 |
+
readonly phase?: 'start' | 'end';
|
| 1238 |
+
readonly entries: Readonly<Record<string, /* FileBackupEntry — packages/agent-core-v2/src/features/fileHistory/fileHistory.ts */ {
|
| 1239 |
+
readonly key: string | null;
|
| 1240 |
+
readonly version: number;
|
| 1241 |
+
readonly contentHash?: string;
|
| 1242 |
+
readonly size?: number;
|
| 1243 |
+
readonly oversize?: boolean;
|
| 1244 |
+
readonly mtimeMs?: number;
|
| 1245 |
+
}>>;
|
| 1246 |
+
}[];
|
| 1247 |
+
readonly tracked: readonly string[];
|
| 1248 |
+
};
|
| 1249 |
+
// src/features/plan/injection/planModeInjection.ts
|
| 1250 |
+
'plan.wasActive': boolean;
|
| 1251 |
+
// src/features/plan/planOps.ts
|
| 1252 |
+
// replayable · durable · undoable — folds: PlanModeEnter, PlanModeCancel, PlanModeExit, PlanRevision
|
| 1253 |
+
'plan': /* PlanState — packages/agent-core-v2/src/features/plan/planOps.ts */ {
|
| 1254 |
+
readonly active: boolean;
|
| 1255 |
+
readonly id?: string;
|
| 1256 |
+
readonly revisionCount?: Readonly<Record<string, number>>;
|
| 1257 |
+
};
|
| 1258 |
+
// src/features/swarm/swarmOps.ts
|
| 1259 |
+
// replayable · durable — folds: SwarmModeEnter, SwarmModeExit
|
| 1260 |
+
'swarm': 'tool' | 'task' | 'manual' | null;
|
| 1261 |
+
// src/features/tower/towerOps.ts
|
| 1262 |
+
// replayable · durable — folds: TowerModeEnter, TowerModeExit
|
| 1263 |
+
'tower': boolean;
|
| 1264 |
+
// replayable · durable — folds: TowerModeEnter, TowerModeExit
|
| 1265 |
+
'tower.base': string | null;
|
| 1266 |
+
// replayable · durable — folds: TowerModeEnter, TowerModeExit
|
| 1267 |
+
'tower.owner': string | undefined;
|
| 1268 |
+
}
|
| 1269 |
+
|
| 1270 |
+
export type AgentStateKey = keyof AgentStateSnapshot;
|
packages/agent-core-v2/docs/wire-manifest.d.ts
ADDED
|
@@ -0,0 +1,946 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Wire Protocol Manifest
|
| 2 |
+
//
|
| 3 |
+
// Generated by scripts/gen-wire-manifest.mts — do not edit by hand.
|
| 4 |
+
// Regenerate with: pnpm --filter @moonshot-ai/agent-core-v2 gen:wire-manifest
|
| 5 |
+
//
|
| 6 |
+
// protocol_version: "1.5" (migrations: 1.0 -> 1.1 -> 1.2 -> 1.3 -> 1.4 -> 1.5)
|
| 7 |
+
//
|
| 8 |
+
// One declaration per durable record type — an Event2 subclass declaring
|
| 9 |
+
// `static type` + `static durable = true` + `static schema` — drained from the
|
| 10 |
+
// runtime EVENT2_REGISTRY ("import = register"). Every payload declaration
|
| 11 |
+
// carries its record type in a `_name` field. Payload sketches use TypeScript
|
| 12 |
+
// type syntax; when a named type is expanded inline, its name appears as a doc
|
| 13 |
+
// comment (`/** ContextMessage */`). Bare type names (ContentPart,
|
| 14 |
+
// ContextMessage, …) refer to the real types in src/ — they are intentionally
|
| 15 |
+
// not resolved here. `// …` marks a capped field list. On disk (wire.jsonl)
|
| 16 |
+
// the journal opens with a metadata line {"type": "metadata",
|
| 17 |
+
// "protocol_version", "created_at"}; each record is {"type", ...payload,
|
| 18 |
+
// "time"} — object payloads spread at the top level.
|
| 19 |
+
//
|
| 20 |
+
// Every listed type is durable by construction — transient Event2 classes
|
| 21 |
+
// never enter EVENT2_REGISTRY, so there is no persisted flag. Declaration
|
| 22 |
+
// header lines: states (every state folding this record type on dispatch and
|
| 23 |
+
// replay; any state beyond the first is what the retired format listed as
|
| 24 |
+
// cross-reducers), blobs (the folding states whose blob codec offloads inline
|
| 25 |
+
// media to blob storage), owner (the source file declaring the class).
|
| 26 |
+
|
| 27 |
+
// Index (59 record types)
|
| 28 |
+
// config.update profile src/agent/profile/profileOps.ts
|
| 29 |
+
// context.append_loop_event contextMemory, turn src/agent/contextMemory/contextEvents.ts
|
| 30 |
+
// context.append_message contextMemory, plan, task.notificationDelivery src/agent/contextMemory/contextEvents.ts
|
| 31 |
+
// context.apply_compaction contextMemory, plan, task.notificationDelivery, turn src/agent/contextMemory/contextEvents.ts
|
| 32 |
+
// context.clear contextMemory, plan, task.notificationDelivery, turn src/agent/contextMemory/contextEvents.ts
|
| 33 |
+
// context.undo contextMemory, plan, task.notificationDelivery, turn src/agent/contextMemory/contextEvents.ts
|
| 34 |
+
// cron.add (none) src/features/cron/cronOps.ts
|
| 35 |
+
// cron.cursor (none) src/features/cron/cronOps.ts
|
| 36 |
+
// cron.delete (none) src/features/cron/cronOps.ts
|
| 37 |
+
// file_history.checkpoint fileHistory src/features/fileHistory/fileHistoryOps.ts
|
| 38 |
+
// file_history.tracked fileHistory src/features/fileHistory/fileHistoryOps.ts
|
| 39 |
+
// forked (none) src/features/goal/goalOps.ts
|
| 40 |
+
// full_compaction.begin fullCompaction src/agent/fullCompaction/compactionOps.ts
|
| 41 |
+
// full_compaction.cancel fullCompaction src/agent/fullCompaction/compactionOps.ts
|
| 42 |
+
// full_compaction.complete fullCompaction src/agent/fullCompaction/compactionOps.ts
|
| 43 |
+
// goal.clear (none) src/features/goal/goalOps.ts
|
| 44 |
+
// goal.create (none) src/features/goal/goalOps.ts
|
| 45 |
+
// goal.update (none) src/features/goal/goalOps.ts
|
| 46 |
+
// interaction.request (none) src/agent/interaction/interactionOps.ts
|
| 47 |
+
// interaction.resolved (none) src/agent/interaction/interactionOps.ts
|
| 48 |
+
// interruptionReminder.recorded interruptionReminder src/agent/interruptionReminder/interruptionReminderOps.ts
|
| 49 |
+
// llm.request llm.requestTrace src/agent/llmRequester/llmRequestOps.ts
|
| 50 |
+
// llm.tools_snapshot llm.requestTrace src/agent/llmRequester/llmRequestOps.ts
|
| 51 |
+
// mcp.tools_discovered mcp.discovery src/agent/mcp/mcpDiscoveryOps.ts
|
| 52 |
+
// permission.record_approval_result permissionRules src/agent/permissionRules/permissionRulesOps.ts
|
| 53 |
+
// permission.set_mode permissionMode, permissionMode.configured src/agent/permissionMode/permissionModeOps.ts
|
| 54 |
+
// plan_mode.cancel plan src/features/plan/planOps.ts
|
| 55 |
+
// plan_mode.enter plan src/features/plan/planOps.ts
|
| 56 |
+
// plan_mode.exit plan src/features/plan/planOps.ts
|
| 57 |
+
// plan.revision plan src/features/plan/planOps.ts
|
| 58 |
+
// plugin.session_start pluginSessionStartSnapshot src/agent/plugin/agentPluginOps.ts
|
| 59 |
+
// profile.bind profile, profile.activeTools src/agent/profile/profileOps.ts
|
| 60 |
+
// prompt.aborted (none) src/agent/prompt/promptEvents.ts
|
| 61 |
+
// prompt.completed (none) src/agent/prompt/promptEvents.ts
|
| 62 |
+
// prompt.steered (none) src/agent/prompt/promptEvents.ts
|
| 63 |
+
// runtime.set_binding runtimeBinding src/agent/runtimeBinding/runtimeBindingOps.ts
|
| 64 |
+
// swarm_mode.enter swarm src/features/swarm/swarmOps.ts
|
| 65 |
+
// swarm_mode.exit contextMemory, swarm src/features/swarm/swarmOps.ts
|
| 66 |
+
// task.started task src/agent/task/taskOps.ts
|
| 67 |
+
// task.terminated task src/agent/task/taskOps.ts
|
| 68 |
+
// task.waitDelivered task.notificationDelivery src/agent/task/taskOps.ts
|
| 69 |
+
// token_counting.measured (none) src/agent/tokenCounting/tokenCountingOps.ts
|
| 70 |
+
// token_counting.rebased (none) src/agent/tokenCounting/tokenCountingOps.ts
|
| 71 |
+
// token_counting.truncated (none) src/agent/tokenCounting/tokenCountingOps.ts
|
| 72 |
+
// token_counting.turn_recorded (none) src/agent/tokenCounting/tokenCountingOps.ts
|
| 73 |
+
// tools.register_user_tool userTool src/agent/userTool/userToolOps.ts
|
| 74 |
+
// tools.reset_active_tools profile.activeTools src/agent/profile/profileOps.ts
|
| 75 |
+
// tools.set_active_tools profile.activeTools src/agent/profile/profileOps.ts
|
| 76 |
+
// tools.unregister_user_tool userTool src/agent/userTool/userToolOps.ts
|
| 77 |
+
// tools.update_store (none) src/features/todo/todoOps.ts
|
| 78 |
+
// tower_mode.enter tower, tower.base, tower.owner src/features/tower/towerOps.ts
|
| 79 |
+
// tower_mode.exit tower, tower.base, tower.owner src/features/tower/towerOps.ts
|
| 80 |
+
// turn.cancel turn src/agent/loop/turnOps.ts
|
| 81 |
+
// turn.ended turn src/agent/loop/turnOps.ts
|
| 82 |
+
// turn.prompt turn src/agent/loop/turnOps.ts
|
| 83 |
+
// turn.steer turn src/agent/loop/turnOps.ts
|
| 84 |
+
// turn.step.interrupted (none) src/agent/loop/turnEvents.ts
|
| 85 |
+
// turn.step.retrying (none) src/agent/loop/turnEvents.ts
|
| 86 |
+
// usage.record (none) src/agent/usage/usageOps.ts
|
| 87 |
+
|
| 88 |
+
/**
|
| 89 |
+
* states: profile
|
| 90 |
+
* owner: src/agent/profile/profileOps.ts
|
| 91 |
+
*/
|
| 92 |
+
interface ConfigUpdatePayload {
|
| 93 |
+
_name: 'config.update';
|
| 94 |
+
agentId: string;
|
| 95 |
+
modelAlias?: string;
|
| 96 |
+
profileName?: string;
|
| 97 |
+
thinkingEffort?: ThinkingEffort;
|
| 98 |
+
thinkingLevel?: ThinkingEffort;
|
| 99 |
+
systemPrompt?: string;
|
| 100 |
+
/** EnvironmentDisclosureSnapshot */
|
| 101 |
+
environmentDisclosure?: {
|
| 102 |
+
cwd: string;
|
| 103 |
+
};
|
| 104 |
+
renderGeneration?: number;
|
| 105 |
+
agentsMdPaths?: string[];
|
| 106 |
+
disallowedTools?: string[];
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
/**
|
| 110 |
+
* states: contextMemory, turn · blobs: contextMemory
|
| 111 |
+
* owner: src/agent/contextMemory/contextEvents.ts
|
| 112 |
+
*/
|
| 113 |
+
interface ContextAppendLoopEventPayload {
|
| 114 |
+
_name: 'context.append_loop_event';
|
| 115 |
+
agentId: string;
|
| 116 |
+
/** LoopRecordedEvent */
|
| 117 |
+
event: 'step.begin' | 'step.end' | 'content.part' | 'tool.call' | 'tool.result';
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
/**
|
| 121 |
+
* states: contextMemory, plan, task.notificationDelivery · blobs: contextMemory
|
| 122 |
+
* owner: src/agent/contextMemory/contextEvents.ts
|
| 123 |
+
*/
|
| 124 |
+
interface ContextAppendMessagePayload {
|
| 125 |
+
_name: 'context.append_message';
|
| 126 |
+
agentId: string;
|
| 127 |
+
/** ContextMessage */
|
| 128 |
+
message: {
|
| 129 |
+
role: Role;
|
| 130 |
+
name?: string;
|
| 131 |
+
content: ContentPart[];
|
| 132 |
+
toolCalls: ToolCall[];
|
| 133 |
+
toolCallId?: string;
|
| 134 |
+
partial?: boolean;
|
| 135 |
+
tools?: ToolDescription[];
|
| 136 |
+
id?: string;
|
| 137 |
+
providerMessageId?: string;
|
| 138 |
+
origin?: 'user' | 'skill_activation' | 'plugin_command' | 'injection' | 'shell_command' | 'compaction_summary' | 'system_trigger' | 'task' | 'cron_job' | 'cron_missed' | 'hook_result' | 'retry' | undefined;
|
| 139 |
+
isError?: boolean;
|
| 140 |
+
toolCallDisplays?: Record<string, ToolInputDisplay>;
|
| 141 |
+
note?: string;
|
| 142 |
+
};
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
/**
|
| 146 |
+
* states: contextMemory, plan, task.notificationDelivery, turn · blobs: contextMemory
|
| 147 |
+
* owner: src/agent/contextMemory/contextEvents.ts
|
| 148 |
+
* shared base: ...contextCompactionBaseShape
|
| 149 |
+
*/
|
| 150 |
+
type ContextApplyCompactionPayload = { _name: 'context.apply_compaction'; } & ({ summary: string, compactedCount: number, contextSummary?: string } | { contextSummary: string, compactedCount: number, summary?: string } | { summary: ContextMessage, count: number, compactedCount?: number });
|
| 151 |
+
|
| 152 |
+
/**
|
| 153 |
+
* states: contextMemory, plan, task.notificationDelivery, turn · blobs: contextMemory
|
| 154 |
+
* owner: src/agent/contextMemory/contextEvents.ts
|
| 155 |
+
*/
|
| 156 |
+
interface ContextClearPayload {
|
| 157 |
+
_name: 'context.clear';
|
| 158 |
+
agentId: string;
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
/**
|
| 162 |
+
* states: contextMemory, plan, task.notificationDelivery, turn · blobs: contextMemory
|
| 163 |
+
* owner: src/agent/contextMemory/contextEvents.ts
|
| 164 |
+
*/
|
| 165 |
+
interface ContextUndoPayload {
|
| 166 |
+
_name: 'context.undo';
|
| 167 |
+
agentId: string;
|
| 168 |
+
count: number;
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
/**
|
| 172 |
+
* states: (none)
|
| 173 |
+
* owner: src/features/cron/cronOps.ts
|
| 174 |
+
*/
|
| 175 |
+
interface CronAddPayload {
|
| 176 |
+
_name: 'cron.add';
|
| 177 |
+
task: {
|
| 178 |
+
id: string;
|
| 179 |
+
cron: string;
|
| 180 |
+
prompt: string;
|
| 181 |
+
createdAt: number;
|
| 182 |
+
recurring?: boolean;
|
| 183 |
+
lastFiredAt?: number;
|
| 184 |
+
tags?: Record<string, string>;
|
| 185 |
+
};
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
/**
|
| 189 |
+
* states: (none)
|
| 190 |
+
* owner: src/features/cron/cronOps.ts
|
| 191 |
+
*/
|
| 192 |
+
interface CronCursorPayload {
|
| 193 |
+
_name: 'cron.cursor';
|
| 194 |
+
id: string;
|
| 195 |
+
lastFiredAt: number;
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
+
/**
|
| 199 |
+
* states: (none)
|
| 200 |
+
* owner: src/features/cron/cronOps.ts
|
| 201 |
+
*/
|
| 202 |
+
interface CronDeletePayload {
|
| 203 |
+
_name: 'cron.delete';
|
| 204 |
+
ids: string[];
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
/**
|
| 208 |
+
* states: fileHistory
|
| 209 |
+
* owner: src/features/fileHistory/fileHistoryOps.ts
|
| 210 |
+
*/
|
| 211 |
+
interface FileHistoryCheckpointPayload {
|
| 212 |
+
_name: 'file_history.checkpoint';
|
| 213 |
+
agentId: string;
|
| 214 |
+
turnId: number;
|
| 215 |
+
phase?: 'start' | 'end';
|
| 216 |
+
entries: Record<string, object>;
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
/**
|
| 220 |
+
* states: fileHistory
|
| 221 |
+
* owner: src/features/fileHistory/fileHistoryOps.ts
|
| 222 |
+
*/
|
| 223 |
+
interface FileHistoryTrackedPayload {
|
| 224 |
+
_name: 'file_history.tracked';
|
| 225 |
+
agentId: string;
|
| 226 |
+
turnId: number;
|
| 227 |
+
path: string;
|
| 228 |
+
entry: {
|
| 229 |
+
key: string | null;
|
| 230 |
+
version: number;
|
| 231 |
+
contentHash?: string;
|
| 232 |
+
size?: number;
|
| 233 |
+
oversize?: boolean;
|
| 234 |
+
mtimeMs?: number;
|
| 235 |
+
};
|
| 236 |
+
}
|
| 237 |
+
|
| 238 |
+
/**
|
| 239 |
+
* states: (none)
|
| 240 |
+
* owner: src/features/goal/goalOps.ts
|
| 241 |
+
*/
|
| 242 |
+
interface ForkedPayload {
|
| 243 |
+
_name: 'forked';
|
| 244 |
+
agentId: string;
|
| 245 |
+
}
|
| 246 |
+
|
| 247 |
+
/**
|
| 248 |
+
* states: fullCompaction
|
| 249 |
+
* owner: src/agent/fullCompaction/compactionOps.ts
|
| 250 |
+
*/
|
| 251 |
+
interface FullCompactionBeginPayload {
|
| 252 |
+
_name: 'full_compaction.begin';
|
| 253 |
+
agentId: string;
|
| 254 |
+
instruction?: string;
|
| 255 |
+
/** CompactionSource */
|
| 256 |
+
source: 'manual' | 'auto';
|
| 257 |
+
}
|
| 258 |
+
|
| 259 |
+
/**
|
| 260 |
+
* states: fullCompaction
|
| 261 |
+
* owner: src/agent/fullCompaction/compactionOps.ts
|
| 262 |
+
*/
|
| 263 |
+
interface FullCompactionCancelPayload {
|
| 264 |
+
_name: 'full_compaction.cancel';
|
| 265 |
+
agentId: string;
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
/**
|
| 269 |
+
* states: fullCompaction
|
| 270 |
+
* owner: src/agent/fullCompaction/compactionOps.ts
|
| 271 |
+
*/
|
| 272 |
+
interface FullCompactionCompletePayload {
|
| 273 |
+
_name: 'full_compaction.complete';
|
| 274 |
+
agentId: string;
|
| 275 |
+
}
|
| 276 |
+
|
| 277 |
+
/**
|
| 278 |
+
* states: (none)
|
| 279 |
+
* owner: src/features/goal/goalOps.ts
|
| 280 |
+
*/
|
| 281 |
+
interface GoalClearPayload {
|
| 282 |
+
_name: 'goal.clear';
|
| 283 |
+
agentId: string;
|
| 284 |
+
}
|
| 285 |
+
|
| 286 |
+
/**
|
| 287 |
+
* states: (none)
|
| 288 |
+
* owner: src/features/goal/goalOps.ts
|
| 289 |
+
*/
|
| 290 |
+
interface GoalCreatePayload {
|
| 291 |
+
_name: 'goal.create';
|
| 292 |
+
agentId: string;
|
| 293 |
+
goalId: string;
|
| 294 |
+
objective: string;
|
| 295 |
+
completionCriterion?: string;
|
| 296 |
+
wallClockResumedAt?: number;
|
| 297 |
+
status?: 'active' | 'paused' | 'blocked' | 'complete';
|
| 298 |
+
actor?: 'user' | 'model' | 'runtime' | 'system';
|
| 299 |
+
budgetLimits?: {
|
| 300 |
+
tokenBudget?: number;
|
| 301 |
+
turnBudget?: number;
|
| 302 |
+
wallClockBudgetMs?: number;
|
| 303 |
+
};
|
| 304 |
+
}
|
| 305 |
+
|
| 306 |
+
/**
|
| 307 |
+
* states: (none)
|
| 308 |
+
* owner: src/features/goal/goalOps.ts
|
| 309 |
+
*/
|
| 310 |
+
interface GoalUpdatePayload {
|
| 311 |
+
_name: 'goal.update';
|
| 312 |
+
agentId: string;
|
| 313 |
+
goalId?: string;
|
| 314 |
+
status?: 'active' | 'paused' | 'blocked' | 'complete';
|
| 315 |
+
reason?: string;
|
| 316 |
+
turnsUsed?: number;
|
| 317 |
+
tokensUsed?: number;
|
| 318 |
+
wallClockMs?: number;
|
| 319 |
+
wallClockResumedAt?: number;
|
| 320 |
+
budgetLimits?: {
|
| 321 |
+
tokenBudget?: number;
|
| 322 |
+
turnBudget?: number;
|
| 323 |
+
wallClockBudgetMs?: number;
|
| 324 |
+
};
|
| 325 |
+
actor?: 'user' | 'model' | 'runtime' | 'system';
|
| 326 |
+
}
|
| 327 |
+
|
| 328 |
+
/**
|
| 329 |
+
* states: (none)
|
| 330 |
+
* owner: src/agent/interaction/interactionOps.ts
|
| 331 |
+
*/
|
| 332 |
+
interface InteractionRequestPayload {
|
| 333 |
+
_name: 'interaction.request';
|
| 334 |
+
agentId: string;
|
| 335 |
+
id: string;
|
| 336 |
+
kind: 'approval' | 'question' | 'user_tool';
|
| 337 |
+
toolCallId?: string;
|
| 338 |
+
request: any;
|
| 339 |
+
}
|
| 340 |
+
|
| 341 |
+
/**
|
| 342 |
+
* states: (none)
|
| 343 |
+
* owner: src/agent/interaction/interactionOps.ts
|
| 344 |
+
*/
|
| 345 |
+
interface InteractionResolvedPayload {
|
| 346 |
+
_name: 'interaction.resolved';
|
| 347 |
+
agentId: string;
|
| 348 |
+
id: string;
|
| 349 |
+
response: any;
|
| 350 |
+
}
|
| 351 |
+
|
| 352 |
+
/**
|
| 353 |
+
* states: interruptionReminder
|
| 354 |
+
* owner: src/agent/interruptionReminder/interruptionReminderOps.ts
|
| 355 |
+
*/
|
| 356 |
+
interface InterruptionReminderRecordedPayload {
|
| 357 |
+
_name: 'interruptionReminder.recorded';
|
| 358 |
+
agentId: string;
|
| 359 |
+
turnId: number;
|
| 360 |
+
}
|
| 361 |
+
|
| 362 |
+
/**
|
| 363 |
+
* states: llm.requestTrace
|
| 364 |
+
* owner: src/agent/llmRequester/llmRequestOps.ts
|
| 365 |
+
*/
|
| 366 |
+
interface LlmRequestPayload {
|
| 367 |
+
_name: 'llm.request';
|
| 368 |
+
agentId: string;
|
| 369 |
+
kind: 'loop' | 'compaction';
|
| 370 |
+
provider: string;
|
| 371 |
+
model: string;
|
| 372 |
+
modelAlias?: string;
|
| 373 |
+
thinkingEffort?: ThinkingEffort;
|
| 374 |
+
thinkingKeep?: string;
|
| 375 |
+
temperature?: number;
|
| 376 |
+
topP?: number;
|
| 377 |
+
maxTokens?: number;
|
| 378 |
+
betaApi?: boolean;
|
| 379 |
+
toolSelect: boolean;
|
| 380 |
+
systemPromptHash: string;
|
| 381 |
+
systemPrompt?: string;
|
| 382 |
+
toolsHash: string;
|
| 383 |
+
messageCount: number;
|
| 384 |
+
turnStep?: string;
|
| 385 |
+
attempt?: string;
|
| 386 |
+
projection?: 'strict' | 'media-degraded' | 'media-stripped' | 'strict-media-degraded' | 'strict-media-stripped';
|
| 387 |
+
droppedCount?: number;
|
| 388 |
+
}
|
| 389 |
+
|
| 390 |
+
/**
|
| 391 |
+
* states: llm.requestTrace
|
| 392 |
+
* owner: src/agent/llmRequester/llmRequestOps.ts
|
| 393 |
+
*/
|
| 394 |
+
interface LlmToolsSnapshotPayload {
|
| 395 |
+
_name: 'llm.tools_snapshot';
|
| 396 |
+
agentId: string;
|
| 397 |
+
hash: string;
|
| 398 |
+
tools: {
|
| 399 |
+
name: string;
|
| 400 |
+
description: string;
|
| 401 |
+
parameters: Record<string, any>;
|
| 402 |
+
}[];
|
| 403 |
+
}
|
| 404 |
+
|
| 405 |
+
/**
|
| 406 |
+
* states: mcp.discovery
|
| 407 |
+
* owner: src/agent/mcp/mcpDiscoveryOps.ts
|
| 408 |
+
*/
|
| 409 |
+
interface McpToolsDiscoveredPayload {
|
| 410 |
+
_name: 'mcp.tools_discovered';
|
| 411 |
+
agentId: string;
|
| 412 |
+
serverName: string;
|
| 413 |
+
hash: string;
|
| 414 |
+
tools: readonly MCPToolDefinition[];
|
| 415 |
+
enabledNames: string[];
|
| 416 |
+
collisions?: {
|
| 417 |
+
qualified: string;
|
| 418 |
+
toolName: string;
|
| 419 |
+
collidesWith: { kind: 'same_server', toolName: string } | { kind: 'other_server', serverName: string };
|
| 420 |
+
}[];
|
| 421 |
+
}
|
| 422 |
+
|
| 423 |
+
/**
|
| 424 |
+
* states: permissionRules
|
| 425 |
+
* owner: src/agent/permissionRules/permissionRulesOps.ts
|
| 426 |
+
*/
|
| 427 |
+
interface PermissionRecordApprovalResultPayload {
|
| 428 |
+
_name: 'permission.record_approval_result';
|
| 429 |
+
agentId: string;
|
| 430 |
+
turnId: number;
|
| 431 |
+
toolCallId: string;
|
| 432 |
+
toolName: string;
|
| 433 |
+
action: string;
|
| 434 |
+
sessionApprovalRule?: string;
|
| 435 |
+
result: PermissionApprovalResultRecord['result'];
|
| 436 |
+
}
|
| 437 |
+
|
| 438 |
+
/**
|
| 439 |
+
* states: permissionMode, permissionMode.configured
|
| 440 |
+
* owner: src/agent/permissionMode/permissionModeOps.ts
|
| 441 |
+
*/
|
| 442 |
+
interface PermissionSetModePayload {
|
| 443 |
+
_name: 'permission.set_mode';
|
| 444 |
+
agentId: string;
|
| 445 |
+
/** PermissionMode */
|
| 446 |
+
mode: 'manual' | 'yolo' | 'auto';
|
| 447 |
+
}
|
| 448 |
+
|
| 449 |
+
/**
|
| 450 |
+
* states: plan
|
| 451 |
+
* owner: src/features/plan/planOps.ts
|
| 452 |
+
*/
|
| 453 |
+
interface PlanModeCancelPayload {
|
| 454 |
+
_name: 'plan_mode.cancel';
|
| 455 |
+
agentId: string;
|
| 456 |
+
id?: string;
|
| 457 |
+
}
|
| 458 |
+
|
| 459 |
+
/**
|
| 460 |
+
* states: plan
|
| 461 |
+
* owner: src/features/plan/planOps.ts
|
| 462 |
+
*/
|
| 463 |
+
interface PlanModeEnterPayload {
|
| 464 |
+
_name: 'plan_mode.enter';
|
| 465 |
+
agentId: string;
|
| 466 |
+
id: string;
|
| 467 |
+
}
|
| 468 |
+
|
| 469 |
+
/**
|
| 470 |
+
* states: plan
|
| 471 |
+
* owner: src/features/plan/planOps.ts
|
| 472 |
+
*/
|
| 473 |
+
interface PlanModeExitPayload {
|
| 474 |
+
_name: 'plan_mode.exit';
|
| 475 |
+
agentId: string;
|
| 476 |
+
id?: string;
|
| 477 |
+
}
|
| 478 |
+
|
| 479 |
+
/**
|
| 480 |
+
* states: plan
|
| 481 |
+
* owner: src/features/plan/planOps.ts
|
| 482 |
+
*/
|
| 483 |
+
interface PlanRevisionPayload {
|
| 484 |
+
_name: 'plan.revision';
|
| 485 |
+
agentId: string;
|
| 486 |
+
id: string;
|
| 487 |
+
version: number;
|
| 488 |
+
key: string;
|
| 489 |
+
sha256: string;
|
| 490 |
+
bytes: number;
|
| 491 |
+
}
|
| 492 |
+
|
| 493 |
+
/**
|
| 494 |
+
* states: pluginSessionStartSnapshot
|
| 495 |
+
* owner: src/agent/plugin/agentPluginOps.ts
|
| 496 |
+
*/
|
| 497 |
+
interface PluginSessionStartPayload {
|
| 498 |
+
_name: 'plugin.session_start';
|
| 499 |
+
agentId: string;
|
| 500 |
+
content: string | null;
|
| 501 |
+
}
|
| 502 |
+
|
| 503 |
+
/**
|
| 504 |
+
* states: profile, profile.activeTools
|
| 505 |
+
* owner: src/agent/profile/profileOps.ts
|
| 506 |
+
*/
|
| 507 |
+
interface ProfileBindPayload {
|
| 508 |
+
_name: 'profile.bind';
|
| 509 |
+
agentId: string;
|
| 510 |
+
modelAlias?: string;
|
| 511 |
+
profileName?: string;
|
| 512 |
+
thinkingEffort: ThinkingEffort;
|
| 513 |
+
systemPrompt: string;
|
| 514 |
+
/** EnvironmentDisclosureSnapshot */
|
| 515 |
+
environmentDisclosure?: {
|
| 516 |
+
cwd: string;
|
| 517 |
+
};
|
| 518 |
+
renderGeneration?: number;
|
| 519 |
+
agentsMdPaths?: string[];
|
| 520 |
+
activeToolNames?: string[];
|
| 521 |
+
disallowedTools: string[];
|
| 522 |
+
subagents?: string[];
|
| 523 |
+
}
|
| 524 |
+
|
| 525 |
+
/**
|
| 526 |
+
* states: (none)
|
| 527 |
+
* owner: src/agent/prompt/promptEvents.ts
|
| 528 |
+
*/
|
| 529 |
+
interface PromptAbortedPayload {
|
| 530 |
+
_name: 'prompt.aborted';
|
| 531 |
+
agentId: string;
|
| 532 |
+
promptId: string;
|
| 533 |
+
abortedAt: string;
|
| 534 |
+
}
|
| 535 |
+
|
| 536 |
+
/**
|
| 537 |
+
* states: (none)
|
| 538 |
+
* owner: src/agent/prompt/promptEvents.ts
|
| 539 |
+
*/
|
| 540 |
+
interface PromptCompletedPayload {
|
| 541 |
+
_name: 'prompt.completed';
|
| 542 |
+
agentId: string;
|
| 543 |
+
promptId: string;
|
| 544 |
+
finishedAt: string;
|
| 545 |
+
reason: 'completed' | 'failed' | 'blocked';
|
| 546 |
+
}
|
| 547 |
+
|
| 548 |
+
/**
|
| 549 |
+
* states: (none)
|
| 550 |
+
* owner: src/agent/prompt/promptEvents.ts
|
| 551 |
+
*/
|
| 552 |
+
interface PromptSteeredPayload {
|
| 553 |
+
_name: 'prompt.steered';
|
| 554 |
+
agentId: string;
|
| 555 |
+
activePromptId: string;
|
| 556 |
+
promptIds: string[];
|
| 557 |
+
content: ContentPart[];
|
| 558 |
+
steeredAt: string;
|
| 559 |
+
}
|
| 560 |
+
|
| 561 |
+
/**
|
| 562 |
+
* states: runtimeBinding
|
| 563 |
+
* owner: src/agent/runtimeBinding/runtimeBindingOps.ts
|
| 564 |
+
*/
|
| 565 |
+
interface RuntimeSetBindingPayload {
|
| 566 |
+
_name: 'runtime.set_binding';
|
| 567 |
+
agentId: string;
|
| 568 |
+
workspaceId: string;
|
| 569 |
+
runtimeId: string;
|
| 570 |
+
}
|
| 571 |
+
|
| 572 |
+
/**
|
| 573 |
+
* states: swarm
|
| 574 |
+
* owner: src/features/swarm/swarmOps.ts
|
| 575 |
+
*/
|
| 576 |
+
interface SwarmModeEnterPayload {
|
| 577 |
+
_name: 'swarm_mode.enter';
|
| 578 |
+
agentId: string;
|
| 579 |
+
/** SwarmModeTrigger */
|
| 580 |
+
trigger: 'manual' | 'task' | 'tool';
|
| 581 |
+
}
|
| 582 |
+
|
| 583 |
+
/**
|
| 584 |
+
* states: contextMemory, swarm · blobs: contextMemory
|
| 585 |
+
* owner: src/features/swarm/swarmOps.ts
|
| 586 |
+
*/
|
| 587 |
+
interface SwarmModeExitPayload {
|
| 588 |
+
_name: 'swarm_mode.exit';
|
| 589 |
+
agentId: string;
|
| 590 |
+
}
|
| 591 |
+
|
| 592 |
+
/**
|
| 593 |
+
* states: task
|
| 594 |
+
* owner: src/agent/task/taskOps.ts
|
| 595 |
+
*/
|
| 596 |
+
interface TaskStartedPayload {
|
| 597 |
+
_name: 'task.started';
|
| 598 |
+
agentId: string;
|
| 599 |
+
/** AgentTaskInfo */
|
| 600 |
+
info: AgentTaskInfoByKind[AgentTaskKind];
|
| 601 |
+
}
|
| 602 |
+
|
| 603 |
+
/**
|
| 604 |
+
* states: task
|
| 605 |
+
* owner: src/agent/task/taskOps.ts
|
| 606 |
+
*/
|
| 607 |
+
interface TaskTerminatedPayload {
|
| 608 |
+
_name: 'task.terminated';
|
| 609 |
+
agentId: string;
|
| 610 |
+
/** AgentTaskInfo */
|
| 611 |
+
info: AgentTaskInfoByKind[AgentTaskKind];
|
| 612 |
+
outputTail?: string;
|
| 613 |
+
}
|
| 614 |
+
|
| 615 |
+
/**
|
| 616 |
+
* states: task.notificationDelivery
|
| 617 |
+
* owner: src/agent/task/taskOps.ts
|
| 618 |
+
*/
|
| 619 |
+
interface TaskWaitDeliveredPayload {
|
| 620 |
+
_name: 'task.waitDelivered';
|
| 621 |
+
agentId: string;
|
| 622 |
+
keys: string[];
|
| 623 |
+
}
|
| 624 |
+
|
| 625 |
+
/**
|
| 626 |
+
* states: (none)
|
| 627 |
+
* owner: src/agent/tokenCounting/tokenCountingOps.ts
|
| 628 |
+
*/
|
| 629 |
+
interface TokenCountingMeasuredPayload {
|
| 630 |
+
_name: 'token_counting.measured';
|
| 631 |
+
agentId: string;
|
| 632 |
+
length: number;
|
| 633 |
+
tokens: number;
|
| 634 |
+
}
|
| 635 |
+
|
| 636 |
+
/**
|
| 637 |
+
* states: (none)
|
| 638 |
+
* owner: src/agent/tokenCounting/tokenCountingOps.ts
|
| 639 |
+
*/
|
| 640 |
+
interface TokenCountingRebasedPayload {
|
| 641 |
+
_name: 'token_counting.rebased';
|
| 642 |
+
agentId: string;
|
| 643 |
+
length: number;
|
| 644 |
+
tokens: number;
|
| 645 |
+
measured: boolean;
|
| 646 |
+
}
|
| 647 |
+
|
| 648 |
+
/**
|
| 649 |
+
* states: (none)
|
| 650 |
+
* owner: src/agent/tokenCounting/tokenCountingOps.ts
|
| 651 |
+
*/
|
| 652 |
+
interface TokenCountingTruncatedPayload {
|
| 653 |
+
_name: 'token_counting.truncated';
|
| 654 |
+
agentId: string;
|
| 655 |
+
length: number;
|
| 656 |
+
tokens: number;
|
| 657 |
+
}
|
| 658 |
+
|
| 659 |
+
/**
|
| 660 |
+
* states: (none)
|
| 661 |
+
* owner: src/agent/tokenCounting/tokenCountingOps.ts
|
| 662 |
+
*/
|
| 663 |
+
interface TokenCountingTurnRecordedPayload {
|
| 664 |
+
_name: 'token_counting.turn_recorded';
|
| 665 |
+
agentId: string;
|
| 666 |
+
length: number;
|
| 667 |
+
tokens: number;
|
| 668 |
+
turnId: number;
|
| 669 |
+
}
|
| 670 |
+
|
| 671 |
+
/**
|
| 672 |
+
* states: userTool
|
| 673 |
+
* owner: src/agent/userTool/userToolOps.ts
|
| 674 |
+
*/
|
| 675 |
+
interface ToolsRegisterUserToolPayload {
|
| 676 |
+
_name: 'tools.register_user_tool';
|
| 677 |
+
agentId: string;
|
| 678 |
+
name: string;
|
| 679 |
+
description: string;
|
| 680 |
+
parameters: UserToolRegistration['parameters'];
|
| 681 |
+
disclosure?: UserToolRegistration['disclosure'];
|
| 682 |
+
}
|
| 683 |
+
|
| 684 |
+
/**
|
| 685 |
+
* states: profile.activeTools
|
| 686 |
+
* owner: src/agent/profile/profileOps.ts
|
| 687 |
+
*/
|
| 688 |
+
interface ToolsResetActiveToolsPayload {
|
| 689 |
+
_name: 'tools.reset_active_tools';
|
| 690 |
+
agentId: string;
|
| 691 |
+
}
|
| 692 |
+
|
| 693 |
+
/**
|
| 694 |
+
* states: profile.activeTools
|
| 695 |
+
* owner: src/agent/profile/profileOps.ts
|
| 696 |
+
*/
|
| 697 |
+
interface ToolsSetActiveToolsPayload {
|
| 698 |
+
_name: 'tools.set_active_tools';
|
| 699 |
+
agentId: string;
|
| 700 |
+
names: string[];
|
| 701 |
+
}
|
| 702 |
+
|
| 703 |
+
/**
|
| 704 |
+
* states: userTool
|
| 705 |
+
* owner: src/agent/userTool/userToolOps.ts
|
| 706 |
+
*/
|
| 707 |
+
interface ToolsUnregisterUserToolPayload {
|
| 708 |
+
_name: 'tools.unregister_user_tool';
|
| 709 |
+
agentId: string;
|
| 710 |
+
name: string;
|
| 711 |
+
}
|
| 712 |
+
|
| 713 |
+
/**
|
| 714 |
+
* states: (none)
|
| 715 |
+
* owner: src/features/todo/todoOps.ts
|
| 716 |
+
*/
|
| 717 |
+
interface ToolsUpdateStorePayload {
|
| 718 |
+
_name: 'tools.update_store';
|
| 719 |
+
agentId: string;
|
| 720 |
+
key: string;
|
| 721 |
+
value: any;
|
| 722 |
+
}
|
| 723 |
+
|
| 724 |
+
/**
|
| 725 |
+
* states: tower, tower.base, tower.owner
|
| 726 |
+
* owner: src/features/tower/towerOps.ts
|
| 727 |
+
*/
|
| 728 |
+
interface TowerModeEnterPayload {
|
| 729 |
+
_name: 'tower_mode.enter';
|
| 730 |
+
agentId: string;
|
| 731 |
+
sessionId?: string;
|
| 732 |
+
base?: string;
|
| 733 |
+
}
|
| 734 |
+
|
| 735 |
+
/**
|
| 736 |
+
* states: tower, tower.base, tower.owner
|
| 737 |
+
* owner: src/features/tower/towerOps.ts
|
| 738 |
+
*/
|
| 739 |
+
interface TowerModeExitPayload {
|
| 740 |
+
_name: 'tower_mode.exit';
|
| 741 |
+
agentId: string;
|
| 742 |
+
}
|
| 743 |
+
|
| 744 |
+
/**
|
| 745 |
+
* states: turn
|
| 746 |
+
* owner: src/agent/loop/turnOps.ts
|
| 747 |
+
*/
|
| 748 |
+
interface TurnCancelPayload {
|
| 749 |
+
_name: 'turn.cancel';
|
| 750 |
+
agentId: string;
|
| 751 |
+
turnId?: number;
|
| 752 |
+
target?: 'active' | 'queued';
|
| 753 |
+
reason?: 'user_cancelled' | 'aborted';
|
| 754 |
+
}
|
| 755 |
+
|
| 756 |
+
/**
|
| 757 |
+
* states: turn
|
| 758 |
+
* owner: src/agent/loop/turnOps.ts
|
| 759 |
+
*/
|
| 760 |
+
interface TurnEndedPayload {
|
| 761 |
+
_name: 'turn.ended';
|
| 762 |
+
agentId: string;
|
| 763 |
+
turnId: number;
|
| 764 |
+
reason: 'completed' | 'cancelled' | 'failed' | 'blocked';
|
| 765 |
+
/** KimiErrorPayload */
|
| 766 |
+
error?: {
|
| 767 |
+
code: (typeof ErrorCodes)[keyof typeof ErrorCodes];
|
| 768 |
+
message: string;
|
| 769 |
+
name?: string;
|
| 770 |
+
details?: Readonly<Record<string, unknown>>;
|
| 771 |
+
retryable: boolean;
|
| 772 |
+
cause?: {
|
| 773 |
+
code: (typeof ErrorCodes)[keyof typeof ErrorCodes];
|
| 774 |
+
message: string;
|
| 775 |
+
name?: string;
|
| 776 |
+
details?: Readonly<Record<string, unknown>>;
|
| 777 |
+
retryable: boolean;
|
| 778 |
+
cause?: {
|
| 779 |
+
code: (typeof ErrorCodes)[keyof typeof ErrorCodes];
|
| 780 |
+
message: string;
|
| 781 |
+
name?: string;
|
| 782 |
+
details?: Readonly<Record<string, unknown>>;
|
| 783 |
+
retryable: boolean;
|
| 784 |
+
cause?: {
|
| 785 |
+
code: (typeof ErrorCodes)[keyof typeof ErrorCodes];
|
| 786 |
+
message: string;
|
| 787 |
+
name?: string;
|
| 788 |
+
details?: Readonly<Record<string, unknown>>;
|
| 789 |
+
retryable: boolean;
|
| 790 |
+
cause?: {
|
| 791 |
+
code: (typeof ErrorCodes)[keyof typeof ErrorCodes];
|
| 792 |
+
message: string;
|
| 793 |
+
name?: string;
|
| 794 |
+
details?: Readonly<Record<string, unknown>>;
|
| 795 |
+
retryable: boolean;
|
| 796 |
+
cause?: {
|
| 797 |
+
code: ErrorCode;
|
| 798 |
+
message: string;
|
| 799 |
+
name?: string;
|
| 800 |
+
details?: Readonly<Record<string, unknown>>;
|
| 801 |
+
retryable: boolean;
|
| 802 |
+
cause?: ErrorPayload;
|
| 803 |
+
};
|
| 804 |
+
};
|
| 805 |
+
};
|
| 806 |
+
};
|
| 807 |
+
};
|
| 808 |
+
};
|
| 809 |
+
durationMs?: number;
|
| 810 |
+
stopReason?: string;
|
| 811 |
+
}
|
| 812 |
+
|
| 813 |
+
/**
|
| 814 |
+
* states: turn
|
| 815 |
+
* owner: src/agent/loop/turnOps.ts
|
| 816 |
+
*/
|
| 817 |
+
interface TurnPromptPayload {
|
| 818 |
+
_name: 'turn.prompt';
|
| 819 |
+
agentId: string;
|
| 820 |
+
input: readonly ContentPart[];
|
| 821 |
+
/** PromptOrigin */
|
| 822 |
+
origin: 'user' | 'skill_activation' | 'plugin_command' | 'injection' | 'shell_command' | 'compaction_summary' | 'system_trigger' | 'task' | 'cron_job' | 'cron_missed' | 'hook_result' | 'retry';
|
| 823 |
+
promptId?: string;
|
| 824 |
+
turnId?: number;
|
| 825 |
+
}
|
| 826 |
+
|
| 827 |
+
/**
|
| 828 |
+
* states: turn
|
| 829 |
+
* owner: src/agent/loop/turnOps.ts
|
| 830 |
+
*/
|
| 831 |
+
interface TurnSteerPayload {
|
| 832 |
+
_name: 'turn.steer';
|
| 833 |
+
agentId: string;
|
| 834 |
+
input: readonly ContentPart[];
|
| 835 |
+
/** PromptOrigin */
|
| 836 |
+
origin: 'user' | 'skill_activation' | 'plugin_command' | 'injection' | 'shell_command' | 'compaction_summary' | 'system_trigger' | 'task' | 'cron_job' | 'cron_missed' | 'hook_result' | 'retry';
|
| 837 |
+
}
|
| 838 |
+
|
| 839 |
+
/**
|
| 840 |
+
* states: (none)
|
| 841 |
+
* owner: src/agent/loop/turnEvents.ts
|
| 842 |
+
*/
|
| 843 |
+
interface TurnStepInterruptedPayload {
|
| 844 |
+
_name: 'turn.step.interrupted';
|
| 845 |
+
agentId: string;
|
| 846 |
+
turnId: number;
|
| 847 |
+
step: number;
|
| 848 |
+
stepId?: string;
|
| 849 |
+
reason: string;
|
| 850 |
+
message?: string;
|
| 851 |
+
}
|
| 852 |
+
|
| 853 |
+
/**
|
| 854 |
+
* states: (none)
|
| 855 |
+
* owner: src/agent/loop/turnEvents.ts
|
| 856 |
+
*/
|
| 857 |
+
interface TurnStepRetryingPayload {
|
| 858 |
+
_name: 'turn.step.retrying';
|
| 859 |
+
agentId: string;
|
| 860 |
+
turnId: number;
|
| 861 |
+
step: number;
|
| 862 |
+
stepId?: string;
|
| 863 |
+
failedAttempt: number;
|
| 864 |
+
nextAttempt: number;
|
| 865 |
+
maxAttempts: number;
|
| 866 |
+
delayMs: number;
|
| 867 |
+
errorName: string;
|
| 868 |
+
errorMessage: string;
|
| 869 |
+
statusCode?: number;
|
| 870 |
+
}
|
| 871 |
+
|
| 872 |
+
/**
|
| 873 |
+
* states: (none)
|
| 874 |
+
* owner: src/agent/usage/usageOps.ts
|
| 875 |
+
*/
|
| 876 |
+
interface UsageRecordPayload {
|
| 877 |
+
_name: 'usage.record';
|
| 878 |
+
agentId: string;
|
| 879 |
+
model: string;
|
| 880 |
+
usage: TokenUsage;
|
| 881 |
+
/** UsageRecordScope */
|
| 882 |
+
usageScope?: 'session' | 'turn';
|
| 883 |
+
}
|
| 884 |
+
|
| 885 |
+
/** Record type → payload sketch. */
|
| 886 |
+
interface WirePayloadMap {
|
| 887 |
+
"config.update": ConfigUpdatePayload;
|
| 888 |
+
"context.append_loop_event": ContextAppendLoopEventPayload;
|
| 889 |
+
"context.append_message": ContextAppendMessagePayload;
|
| 890 |
+
"context.apply_compaction": ContextApplyCompactionPayload;
|
| 891 |
+
"context.clear": ContextClearPayload;
|
| 892 |
+
"context.undo": ContextUndoPayload;
|
| 893 |
+
"cron.add": CronAddPayload;
|
| 894 |
+
"cron.cursor": CronCursorPayload;
|
| 895 |
+
"cron.delete": CronDeletePayload;
|
| 896 |
+
"file_history.checkpoint": FileHistoryCheckpointPayload;
|
| 897 |
+
"file_history.tracked": FileHistoryTrackedPayload;
|
| 898 |
+
"forked": ForkedPayload;
|
| 899 |
+
"full_compaction.begin": FullCompactionBeginPayload;
|
| 900 |
+
"full_compaction.cancel": FullCompactionCancelPayload;
|
| 901 |
+
"full_compaction.complete": FullCompactionCompletePayload;
|
| 902 |
+
"goal.clear": GoalClearPayload;
|
| 903 |
+
"goal.create": GoalCreatePayload;
|
| 904 |
+
"goal.update": GoalUpdatePayload;
|
| 905 |
+
"interaction.request": InteractionRequestPayload;
|
| 906 |
+
"interaction.resolved": InteractionResolvedPayload;
|
| 907 |
+
"interruptionReminder.recorded": InterruptionReminderRecordedPayload;
|
| 908 |
+
"llm.request": LlmRequestPayload;
|
| 909 |
+
"llm.tools_snapshot": LlmToolsSnapshotPayload;
|
| 910 |
+
"mcp.tools_discovered": McpToolsDiscoveredPayload;
|
| 911 |
+
"permission.record_approval_result": PermissionRecordApprovalResultPayload;
|
| 912 |
+
"permission.set_mode": PermissionSetModePayload;
|
| 913 |
+
"plan_mode.cancel": PlanModeCancelPayload;
|
| 914 |
+
"plan_mode.enter": PlanModeEnterPayload;
|
| 915 |
+
"plan_mode.exit": PlanModeExitPayload;
|
| 916 |
+
"plan.revision": PlanRevisionPayload;
|
| 917 |
+
"plugin.session_start": PluginSessionStartPayload;
|
| 918 |
+
"profile.bind": ProfileBindPayload;
|
| 919 |
+
"prompt.aborted": PromptAbortedPayload;
|
| 920 |
+
"prompt.completed": PromptCompletedPayload;
|
| 921 |
+
"prompt.steered": PromptSteeredPayload;
|
| 922 |
+
"runtime.set_binding": RuntimeSetBindingPayload;
|
| 923 |
+
"swarm_mode.enter": SwarmModeEnterPayload;
|
| 924 |
+
"swarm_mode.exit": SwarmModeExitPayload;
|
| 925 |
+
"task.started": TaskStartedPayload;
|
| 926 |
+
"task.terminated": TaskTerminatedPayload;
|
| 927 |
+
"task.waitDelivered": TaskWaitDeliveredPayload;
|
| 928 |
+
"token_counting.measured": TokenCountingMeasuredPayload;
|
| 929 |
+
"token_counting.rebased": TokenCountingRebasedPayload;
|
| 930 |
+
"token_counting.truncated": TokenCountingTruncatedPayload;
|
| 931 |
+
"token_counting.turn_recorded": TokenCountingTurnRecordedPayload;
|
| 932 |
+
"tools.register_user_tool": ToolsRegisterUserToolPayload;
|
| 933 |
+
"tools.reset_active_tools": ToolsResetActiveToolsPayload;
|
| 934 |
+
"tools.set_active_tools": ToolsSetActiveToolsPayload;
|
| 935 |
+
"tools.unregister_user_tool": ToolsUnregisterUserToolPayload;
|
| 936 |
+
"tools.update_store": ToolsUpdateStorePayload;
|
| 937 |
+
"tower_mode.enter": TowerModeEnterPayload;
|
| 938 |
+
"tower_mode.exit": TowerModeExitPayload;
|
| 939 |
+
"turn.cancel": TurnCancelPayload;
|
| 940 |
+
"turn.ended": TurnEndedPayload;
|
| 941 |
+
"turn.prompt": TurnPromptPayload;
|
| 942 |
+
"turn.steer": TurnSteerPayload;
|
| 943 |
+
"turn.step.interrupted": TurnStepInterruptedPayload;
|
| 944 |
+
"turn.step.retrying": TurnStepRetryingPayload;
|
| 945 |
+
"usage.record": UsageRecordPayload;
|
| 946 |
+
}
|
packages/agent-core-v2/scripts/check-import-boundaries.d.mts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export interface Violation {
|
| 2 |
+
file: string;
|
| 3 |
+
line: number;
|
| 4 |
+
message: string;
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
export const SRC_ROOT: string;
|
| 8 |
+
|
| 9 |
+
export function checkSource(source: string, absFile: string): Violation[];
|
| 10 |
+
export function checkFile(absFile: string): Violation[];
|
packages/agent-core-v2/scripts/check-import-boundaries.mjs
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env node
|
| 2 |
+
|
| 3 |
+
import { readFileSync, readdirSync, statSync } from 'node:fs';
|
| 4 |
+
import { dirname, join, relative, resolve } from 'node:path';
|
| 5 |
+
|
| 6 |
+
const PKG_ROOT = resolve(import.meta.dirname, '..');
|
| 7 |
+
export const SRC_ROOT = join(PKG_ROOT, 'src');
|
| 8 |
+
const TEST_ROOT = join(PKG_ROOT, 'test');
|
| 9 |
+
const HUMAN_ROOT = join(SRC_ROOT, 'human');
|
| 10 |
+
const ADAPTER_ROOT = join(SRC_ROOT, 'llm-adapter');
|
| 11 |
+
const LOOP_MACHINE_ADAPTER_ROOT = join(SRC_ROOT, 'agent/loop/machine');
|
| 12 |
+
const SESSION_LIFECYCLE_ADAPTER_ROOT = join(SRC_ROOT, 'session/agentLifecycle');
|
| 13 |
+
|
| 14 |
+
const SELF_PACKAGE_PREFIX = '@moonshot-ai/agent-core-v2/';
|
| 15 |
+
const KOSONG_PATH_RE = /(?:^|\/)kosong(?:\/|$)/;
|
| 16 |
+
const TRAIT_FILE_RE = /\/trait\.ts$/;
|
| 17 |
+
const FORMAT_LOWER_FILE_RE = /\/bases\/[^/]+\/(?:format|lower)\.ts$/;
|
| 18 |
+
const FORMAT_LOWER_MODULE_RE = /\/bases\/[^/]+\/(?:format|lower)$/;
|
| 19 |
+
const TRAIT_MODULE_RE = /\/trait$/;
|
| 20 |
+
const BASES_DIR_RE = /\/llm\/requester\/bases(?:\/|$)/;
|
| 21 |
+
const BASES_INTERNAL_MODULE_RE =
|
| 22 |
+
/\/llm\/requester\/bases\/[^/]+\/(?:format|lower|patterns|reasoning-key)$/;
|
| 23 |
+
const TEST_DIR_RE = /\/test(?:\/|$)/;
|
| 24 |
+
|
| 25 |
+
function traitBoundaryViolation(absFile, targetAbs, specifier) {
|
| 26 |
+
const message = `format and trait never import each other ('${specifier}') — both sides speak only the neutral wire/chunk types in the protocol's contract.ts`;
|
| 27 |
+
if (TRAIT_FILE_RE.test(absFile) && FORMAT_LOWER_MODULE_RE.test(targetAbs)) {
|
| 28 |
+
return message;
|
| 29 |
+
}
|
| 30 |
+
if (FORMAT_LOWER_FILE_RE.test(absFile) && TRAIT_MODULE_RE.test(targetAbs)) {
|
| 31 |
+
return message;
|
| 32 |
+
}
|
| 33 |
+
return undefined;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
function basesInternalViolation(absFile, targetAbs, specifier) {
|
| 37 |
+
if (!BASES_INTERNAL_MODULE_RE.test(targetAbs)) return undefined;
|
| 38 |
+
if (TRAIT_FILE_RE.test(absFile) && FORMAT_LOWER_MODULE_RE.test(targetAbs)) return undefined;
|
| 39 |
+
if (BASES_DIR_RE.test(absFile) || TEST_DIR_RE.test(absFile)) return undefined;
|
| 40 |
+
return `protocol format modules are internal to the requester pipeline ('${specifier}') — only llm/requester/bases code and tests may import format/lower/patterns; everyone else speaks contract/trait/requester`;
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
const HUMAN_VOCABULARY = new Set([
|
| 44 |
+
'agent/origin',
|
| 45 |
+
'llm/message',
|
| 46 |
+
'llm/usage',
|
| 47 |
+
'llm/capability',
|
| 48 |
+
'llm/thinking',
|
| 49 |
+
'llm/finish-reason',
|
| 50 |
+
'llm/response-format',
|
| 51 |
+
'llm/media/upload',
|
| 52 |
+
'llm/media/image-formats',
|
| 53 |
+
'llm/requester/requester',
|
| 54 |
+
'llm/toolCallIdNormalizer',
|
| 55 |
+
'llm-kimi/trait',
|
| 56 |
+
'interaction/interaction',
|
| 57 |
+
'interaction/machine',
|
| 58 |
+
'interaction/facade',
|
| 59 |
+
'utils/watch',
|
| 60 |
+
'xstate2',
|
| 61 |
+
]);
|
| 62 |
+
|
| 63 |
+
const V2_ONLY_FIRST_SEGMENTS = new Set([
|
| 64 |
+
'llm-adapter',
|
| 65 |
+
'app',
|
| 66 |
+
'workspace',
|
| 67 |
+
'features',
|
| 68 |
+
'state',
|
| 69 |
+
'wire',
|
| 70 |
+
'persistence',
|
| 71 |
+
'os',
|
| 72 |
+
'mcpCore',
|
| 73 |
+
'errors',
|
| 74 |
+
'debug',
|
| 75 |
+
'program',
|
| 76 |
+
'runtime',
|
| 77 |
+
'_base',
|
| 78 |
+
]);
|
| 79 |
+
|
| 80 |
+
function isInside(root, absPath) {
|
| 81 |
+
const rel = relative(root, absPath);
|
| 82 |
+
return rel !== '' && !rel.startsWith('..');
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
function humanSubpathOf(specifier) {
|
| 86 |
+
if (specifier.startsWith('#human/')) return specifier.slice('#human/'.length);
|
| 87 |
+
if (specifier.startsWith(`${SELF_PACKAGE_PREFIX}human/`)) {
|
| 88 |
+
return specifier.slice(`${SELF_PACKAGE_PREFIX}human/`.length);
|
| 89 |
+
}
|
| 90 |
+
return undefined;
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
function stripTs(path) {
|
| 94 |
+
return path.endsWith('.ts') ? path.slice(0, -'.ts'.length) : path;
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
function resolveIntraV2(specifier, fromFile) {
|
| 98 |
+
if (specifier.startsWith('#human/')) {
|
| 99 |
+
return join(HUMAN_ROOT, specifier.slice('#human/'.length));
|
| 100 |
+
}
|
| 101 |
+
if (specifier.startsWith('#/')) {
|
| 102 |
+
if (isInside(HUMAN_ROOT, fromFile)) {
|
| 103 |
+
return join(HUMAN_ROOT, specifier.slice(2));
|
| 104 |
+
}
|
| 105 |
+
return join(SRC_ROOT, specifier.slice(2));
|
| 106 |
+
}
|
| 107 |
+
if (specifier.startsWith(SELF_PACKAGE_PREFIX)) {
|
| 108 |
+
return join(SRC_ROOT, specifier.slice(SELF_PACKAGE_PREFIX.length));
|
| 109 |
+
}
|
| 110 |
+
if (specifier.startsWith('.')) {
|
| 111 |
+
return resolve(dirname(fromFile), specifier);
|
| 112 |
+
}
|
| 113 |
+
return undefined;
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
const IMPORT_RE =
|
| 117 |
+
/(?:import|export)\s+(?:type\s+)?(?:[^'";]*?\s+from\s+)?['"]([^'"]+)['"]|(?:import|require)\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
| 118 |
+
|
| 119 |
+
export function checkSource(source, absFile) {
|
| 120 |
+
const violations = [];
|
| 121 |
+
const inSrc = !relative(SRC_ROOT, absFile).startsWith('..');
|
| 122 |
+
const inHuman = isInside(HUMAN_ROOT, absFile);
|
| 123 |
+
const inAdapter =
|
| 124 |
+
isInside(ADAPTER_ROOT, absFile) ||
|
| 125 |
+
isInside(LOOP_MACHINE_ADAPTER_ROOT, absFile) ||
|
| 126 |
+
isInside(SESSION_LIFECYCLE_ADAPTER_ROOT, absFile);
|
| 127 |
+
|
| 128 |
+
let match;
|
| 129 |
+
IMPORT_RE.lastIndex = 0;
|
| 130 |
+
while ((match = IMPORT_RE.exec(source)) !== null) {
|
| 131 |
+
const specifier = match[1] ?? match[2];
|
| 132 |
+
if (!specifier) continue;
|
| 133 |
+
const line = source.slice(0, match.index).split('\n').length;
|
| 134 |
+
|
| 135 |
+
if (KOSONG_PATH_RE.test(specifier)) {
|
| 136 |
+
violations.push({
|
| 137 |
+
file: absFile,
|
| 138 |
+
line,
|
| 139 |
+
message: `the kosong kernel is deleted (${specifier}) — request/provider code lives in #human/llm, the v2 compatibility boundary is #/llm-adapter`,
|
| 140 |
+
});
|
| 141 |
+
continue;
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
if (!inSrc) continue;
|
| 145 |
+
|
| 146 |
+
const targetAbs = resolveIntraV2(specifier, absFile);
|
| 147 |
+
if (targetAbs !== undefined) {
|
| 148 |
+
const basesInternal = basesInternalViolation(absFile, stripTs(targetAbs), specifier);
|
| 149 |
+
if (basesInternal !== undefined) {
|
| 150 |
+
violations.push({ file: absFile, line, message: basesInternal });
|
| 151 |
+
}
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
if (inHuman) {
|
| 155 |
+
if (specifier.startsWith('#/')) {
|
| 156 |
+
const first = specifier.slice(2).split('/')[0];
|
| 157 |
+
if (first !== undefined && V2_ONLY_FIRST_SEGMENTS.has(first)) {
|
| 158 |
+
violations.push({
|
| 159 |
+
file: absFile,
|
| 160 |
+
line,
|
| 161 |
+
message: `human must not import outside its kernel ('${specifier}') — human is the pure LLM/agent kernel: it never imports llm-adapter or v2 domains`,
|
| 162 |
+
});
|
| 163 |
+
continue;
|
| 164 |
+
}
|
| 165 |
+
}
|
| 166 |
+
if (targetAbs !== undefined && !isInside(HUMAN_ROOT, targetAbs)) {
|
| 167 |
+
violations.push({
|
| 168 |
+
file: absFile,
|
| 169 |
+
line,
|
| 170 |
+
message: `human must not import outside its kernel ('${specifier}') — human is the pure LLM/agent kernel: it never imports llm-adapter or v2 domains`,
|
| 171 |
+
});
|
| 172 |
+
}
|
| 173 |
+
if (targetAbs !== undefined) {
|
| 174 |
+
const traitBoundary = traitBoundaryViolation(absFile, stripTs(targetAbs), specifier);
|
| 175 |
+
if (traitBoundary !== undefined) {
|
| 176 |
+
violations.push({ file: absFile, line, message: traitBoundary });
|
| 177 |
+
}
|
| 178 |
+
}
|
| 179 |
+
continue;
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
const humanSub = humanSubpathOf(specifier);
|
| 183 |
+
if (humanSub !== undefined && !inAdapter && !HUMAN_VOCABULARY.has(stripTs(humanSub))) {
|
| 184 |
+
violations.push({
|
| 185 |
+
file: absFile,
|
| 186 |
+
line,
|
| 187 |
+
message: `only llm-adapter, agent/loop/machine and session/agentLifecycle may import the human implementation ('${specifier}') — v2 code outside those adapter layers is limited to the vocabulary modules (${[...HUMAN_VOCABULARY].join(', ')})`,
|
| 188 |
+
});
|
| 189 |
+
}
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
return violations;
|
| 193 |
+
}
|
| 194 |
+
|
| 195 |
+
export function checkFile(absFile) {
|
| 196 |
+
return checkSource(readFileSync(absFile, 'utf8'), absFile);
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
function walk(dir) {
|
| 200 |
+
const out = [];
|
| 201 |
+
for (const entry of readdirSync(dir)) {
|
| 202 |
+
if (entry === 'node_modules' || entry === 'dist') continue;
|
| 203 |
+
const abs = join(dir, entry);
|
| 204 |
+
const st = statSync(abs);
|
| 205 |
+
if (st.isDirectory()) out.push(...walk(abs));
|
| 206 |
+
else if (abs.endsWith('.ts')) out.push(abs);
|
| 207 |
+
}
|
| 208 |
+
return out;
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
function main() {
|
| 212 |
+
const files = [...walk(SRC_ROOT), ...walk(TEST_ROOT)];
|
| 213 |
+
const violations = files.flatMap((f) => checkFile(f));
|
| 214 |
+
if (violations.length === 0) {
|
| 215 |
+
console.log(`check-import-boundaries: OK (${files.length} files)`);
|
| 216 |
+
return 0;
|
| 217 |
+
}
|
| 218 |
+
for (const v of violations) {
|
| 219 |
+
console.error(`${relative(PKG_ROOT, v.file)}:${v.line}: ${v.message}`);
|
| 220 |
+
}
|
| 221 |
+
console.error(`\ncheck-import-boundaries: ${violations.length} violation(s)`);
|
| 222 |
+
return 1;
|
| 223 |
+
}
|
| 224 |
+
|
| 225 |
+
const isMain = process.argv[1] && resolve(process.argv[1]) === import.meta.filename;
|
| 226 |
+
if (isMain) {
|
| 227 |
+
process.exit(main());
|
| 228 |
+
}
|
packages/agent-core-v2/scripts/debarrel.mjs
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env node
|
| 2 |
+
import { Project } from 'ts-morph';
|
| 3 |
+
import path from 'node:path';
|
| 4 |
+
import fs from 'node:fs';
|
| 5 |
+
import { fileURLToPath } from 'node:url';
|
| 6 |
+
|
| 7 |
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
| 8 |
+
const PKG = path.resolve(__dirname, '..');
|
| 9 |
+
const SRC = path.join(PKG, 'src');
|
| 10 |
+
const ENTRY = path.join(SRC, 'index.ts');
|
| 11 |
+
|
| 12 |
+
const args = process.argv.slice(2);
|
| 13 |
+
const DRY = args.includes('--dry-run');
|
| 14 |
+
const ONLY = (args.find((a) => a.startsWith('--only=')) || '').slice('--only='.length) || null;
|
| 15 |
+
const ENTRY_ONLY = args.includes('--entry');
|
| 16 |
+
const DELETE_BARRELS = args.includes('--delete-barrels');
|
| 17 |
+
const LIST_REGS = args.includes('--list-registers');
|
| 18 |
+
const VERIFY = args.includes('--verify-coverage');
|
| 19 |
+
|
| 20 |
+
const project = new Project({ tsConfigFilePath: path.join(PKG, 'tsconfig.json') });
|
| 21 |
+
|
| 22 |
+
const relSpec = (absFile) =>
|
| 23 |
+
'#/' + path.relative(SRC, absFile).split(path.sep).join('/').replace(/\.ts$/, '');
|
| 24 |
+
|
| 25 |
+
const isUnderSrc = (abs) => abs === SRC || abs.startsWith(SRC + path.sep);
|
| 26 |
+
const isIndexBasename = (abs) => path.basename(abs) === 'index.ts';
|
| 27 |
+
const isBarrelFile = (sf) => {
|
| 28 |
+
const f = sf.getFilePath();
|
| 29 |
+
return isUnderSrc(f) && isIndexBasename(f) && f !== ENTRY;
|
| 30 |
+
};
|
| 31 |
+
const resolvedFile = (decl) => decl.getModuleSpecifierSourceFile() || null;
|
| 32 |
+
const barrelOfDecl = (decl) => {
|
| 33 |
+
const sf = resolvedFile(decl);
|
| 34 |
+
return sf && isBarrelFile(sf) ? sf : null;
|
| 35 |
+
};
|
| 36 |
+
|
| 37 |
+
function resolveName(barrel, name) {
|
| 38 |
+
const decls = barrel.getExportedDeclarations().get(name);
|
| 39 |
+
if (!decls || decls.length === 0) return null;
|
| 40 |
+
const decl = decls[0];
|
| 41 |
+
const leaf = decl.getSourceFile();
|
| 42 |
+
const sym = decl.getSymbol();
|
| 43 |
+
let leafName = null;
|
| 44 |
+
for (const [ln, lds] of leaf.getExportedDeclarations()) {
|
| 45 |
+
if (lds.some((d) => d.getSymbol() === sym)) {
|
| 46 |
+
leafName = ln;
|
| 47 |
+
break;
|
| 48 |
+
}
|
| 49 |
+
}
|
| 50 |
+
if (leafName === null) leafName = (sym && sym.getName()) || name;
|
| 51 |
+
return { leafFile: leaf.getFilePath(), leafName };
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
function expandBarrelClauses(barrel) {
|
| 55 |
+
const clauses = [];
|
| 56 |
+
for (const ed of barrel.getExportDeclarations()) {
|
| 57 |
+
const target = resolvedFile(ed);
|
| 58 |
+
if (!target) continue;
|
| 59 |
+
if (isBarrelFile(target)) {
|
| 60 |
+
clauses.push(...expandBarrelClauses(target));
|
| 61 |
+
continue;
|
| 62 |
+
}
|
| 63 |
+
const file = target.getFilePath();
|
| 64 |
+
const isStar =
|
| 65 |
+
ed.getNamedExports().length === 0 && !ed.getNamespaceExport();
|
| 66 |
+
if (isStar) {
|
| 67 |
+
clauses.push({ kind: 'star', file });
|
| 68 |
+
} else if (ed.getNamespaceExport()) {
|
| 69 |
+
clauses.push({ kind: 'namespace', file, name: ed.getNamespaceExport().getName() });
|
| 70 |
+
} else {
|
| 71 |
+
const declType = ed.isTypeOnly();
|
| 72 |
+
const specs = ed.getNamedExports().map((s) => ({
|
| 73 |
+
name: s.getName(),
|
| 74 |
+
alias: s.getAliasNode()?.getText(),
|
| 75 |
+
isTypeOnly: declType || s.isTypeOnly(),
|
| 76 |
+
}));
|
| 77 |
+
clauses.push({ kind: 'named', file, isTypeOnly: declType, specs });
|
| 78 |
+
}
|
| 79 |
+
}
|
| 80 |
+
return clauses;
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
function allLeavesUnderDir(dirAbs) {
|
| 84 |
+
const out = [];
|
| 85 |
+
const walk = (d) => {
|
| 86 |
+
for (const ent of fs.readdirSync(d, { withFileTypes: true })) {
|
| 87 |
+
const p = path.join(d, ent.name);
|
| 88 |
+
if (ent.isDirectory()) walk(p);
|
| 89 |
+
else if (
|
| 90 |
+
ent.isFile() &&
|
| 91 |
+
p.endsWith('.ts') &&
|
| 92 |
+
!p.endsWith('.test.ts') &&
|
| 93 |
+
!p.endsWith('.d.ts') &&
|
| 94 |
+
path.basename(p) !== 'index.ts'
|
| 95 |
+
) {
|
| 96 |
+
out.push(p);
|
| 97 |
+
}
|
| 98 |
+
}
|
| 99 |
+
};
|
| 100 |
+
walk(dirAbs);
|
| 101 |
+
return out.sort((a, b) => a.localeCompare(b));
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
function rewriteConsumerFile(sf, onlyBarrelPath) {
|
| 105 |
+
const report = { imports: 0, exports: 0, manuals: [], sideEffects: 0 };
|
| 106 |
+
|
| 107 |
+
for (const decl of sf.getImportDeclarations()) {
|
| 108 |
+
const barrel = barrelOfDecl(decl);
|
| 109 |
+
if (!barrel) continue;
|
| 110 |
+
if (onlyBarrelPath && barrel.getFilePath() !== onlyBarrelPath) continue;
|
| 111 |
+
|
| 112 |
+
if (decl.getNamespaceImport()) {
|
| 113 |
+
report.manuals.push({ sf: sf.getFilePath(), text: decl.getText(), why: 'namespace import' });
|
| 114 |
+
continue;
|
| 115 |
+
}
|
| 116 |
+
const hasDefault = !!decl.getDefaultImport();
|
| 117 |
+
const named = decl.getNamedImports();
|
| 118 |
+
if (!hasDefault && named.length === 0) {
|
| 119 |
+
const leaves = [...new Set(expandBarrelClauses(barrel).map((c) => c.file))];
|
| 120 |
+
const idx = sf.getImportDeclarations().indexOf(decl);
|
| 121 |
+
sf.insertImportDeclarations(
|
| 122 |
+
idx,
|
| 123 |
+
leaves.map((leaf) => ({ moduleSpecifier: relSpec(leaf) })),
|
| 124 |
+
);
|
| 125 |
+
decl.remove();
|
| 126 |
+
report.sideEffects += leaves.length;
|
| 127 |
+
report.imports++;
|
| 128 |
+
continue;
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
const declType = decl.isTypeOnly();
|
| 132 |
+
const groups = new Map();
|
| 133 |
+
const add = (leaf, spec) => {
|
| 134 |
+
if (!groups.has(leaf)) groups.set(leaf, []);
|
| 135 |
+
groups.get(leaf).push(spec);
|
| 136 |
+
};
|
| 137 |
+
|
| 138 |
+
if (hasDefault) {
|
| 139 |
+
const r = resolveName(barrel, 'default');
|
| 140 |
+
if (!r) report.manuals.push({ sf: sf.getFilePath(), text: decl.getText(), why: 'default import unresolved' });
|
| 141 |
+
else add(r.leafFile, { default: decl.getDefaultImport().getText() });
|
| 142 |
+
}
|
| 143 |
+
for (const s of named) {
|
| 144 |
+
const lookup = s.getName();
|
| 145 |
+
const local = s.getAliasNode()?.getText() || s.getName();
|
| 146 |
+
const r = resolveName(barrel, lookup);
|
| 147 |
+
if (!r) {
|
| 148 |
+
report.manuals.push({ sf: sf.getFilePath(), text: s.getText(), why: 'named import unresolved' });
|
| 149 |
+
continue;
|
| 150 |
+
}
|
| 151 |
+
add(r.leafFile, {
|
| 152 |
+
name: r.leafName,
|
| 153 |
+
alias: local !== r.leafName ? local : undefined,
|
| 154 |
+
isTypeOnly: declType || s.isTypeOnly(),
|
| 155 |
+
});
|
| 156 |
+
}
|
| 157 |
+
const structures = buildImportStructures(groups);
|
| 158 |
+
const idx = sf.getImportDeclarations().indexOf(decl);
|
| 159 |
+
if (structures.length) sf.insertImportDeclarations(idx, structures);
|
| 160 |
+
decl.remove();
|
| 161 |
+
report.imports++;
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
for (const decl of sf.getExportDeclarations()) {
|
| 165 |
+
const barrel = barrelOfDecl(decl);
|
| 166 |
+
if (!barrel) continue;
|
| 167 |
+
if (onlyBarrelPath && barrel.getFilePath() !== onlyBarrelPath) continue;
|
| 168 |
+
|
| 169 |
+
const isStar = decl.getNamedExports().length === 0 && !decl.getNamespaceExport();
|
| 170 |
+
if (isStar) {
|
| 171 |
+
const clauses = expandBarrelClauses(barrel);
|
| 172 |
+
decl.replaceWithText(clauses.map(exportClauseToText).join('\n'));
|
| 173 |
+
report.exports++;
|
| 174 |
+
continue;
|
| 175 |
+
}
|
| 176 |
+
if (decl.getNamespaceExport()) {
|
| 177 |
+
report.manuals.push({ sf: sf.getFilePath(), text: decl.getText(), why: 'namespace export' });
|
| 178 |
+
continue;
|
| 179 |
+
}
|
| 180 |
+
const declType = decl.isTypeOnly();
|
| 181 |
+
const groups = new Map();
|
| 182 |
+
for (const s of decl.getNamedExports()) {
|
| 183 |
+
const lookup = s.getName();
|
| 184 |
+
const exportedAs = s.getAliasNode()?.getText() || s.getName();
|
| 185 |
+
const r = resolveName(barrel, lookup);
|
| 186 |
+
if (!r) {
|
| 187 |
+
report.manuals.push({ sf: sf.getFilePath(), text: s.getText(), why: 'named export unresolved' });
|
| 188 |
+
continue;
|
| 189 |
+
}
|
| 190 |
+
if (!groups.has(r.leafFile)) groups.set(r.leafFile, { specs: [], allType: true });
|
| 191 |
+
const g = groups.get(r.leafFile);
|
| 192 |
+
const t = declType || s.isTypeOnly();
|
| 193 |
+
g.allType = g.allType && t;
|
| 194 |
+
g.specs.push({
|
| 195 |
+
name: r.leafName,
|
| 196 |
+
alias: exportedAs !== r.leafName ? exportedAs : undefined,
|
| 197 |
+
isTypeOnly: t,
|
| 198 |
+
});
|
| 199 |
+
}
|
| 200 |
+
const lines = [];
|
| 201 |
+
for (const [leaf, { specs, allType }] of groups) {
|
| 202 |
+
lines.push(renderNamedExport(relSpec(leaf), specs, allType));
|
| 203 |
+
}
|
| 204 |
+
decl.replaceWithText(lines.join('\n'));
|
| 205 |
+
report.exports++;
|
| 206 |
+
}
|
| 207 |
+
return report;
|
| 208 |
+
}
|
| 209 |
+
|
| 210 |
+
function buildImportStructures(groups) {
|
| 211 |
+
const structures = [];
|
| 212 |
+
for (const [leaf, specs] of groups) {
|
| 213 |
+
const spec = relSpec(leaf);
|
| 214 |
+
const defaults = specs.filter((s) => s.default);
|
| 215 |
+
const namedSpecs = specs.filter((s) => !s.default);
|
| 216 |
+
for (const d of defaults) {
|
| 217 |
+
structures.push({ moduleSpecifier: spec, defaultImport: d.default });
|
| 218 |
+
}
|
| 219 |
+
const values = namedSpecs.filter((s) => !s.isTypeOnly);
|
| 220 |
+
const types = namedSpecs.filter((s) => s.isTypeOnly);
|
| 221 |
+
if (values.length)
|
| 222 |
+
structures.push({
|
| 223 |
+
moduleSpecifier: spec,
|
| 224 |
+
namedImports: values.map((v) => ({ name: v.name, alias: v.alias })),
|
| 225 |
+
});
|
| 226 |
+
if (types.length)
|
| 227 |
+
structures.push({
|
| 228 |
+
moduleSpecifier: spec,
|
| 229 |
+
isTypeOnly: true,
|
| 230 |
+
namedImports: types.map((t) => ({ name: t.name, alias: t.alias })),
|
| 231 |
+
});
|
| 232 |
+
}
|
| 233 |
+
return structures;
|
| 234 |
+
}
|
| 235 |
+
|
| 236 |
+
function renderNamedExport(spec, specs, allType) {
|
| 237 |
+
const body = specs
|
| 238 |
+
.map((s) => `${allType ? '' : s.isTypeOnly ? 'type ' : ''}${s.name}${s.alias ? ' as ' + s.alias : ''}`)
|
| 239 |
+
.join(', ');
|
| 240 |
+
return `${allType ? 'export type' : 'export'} { ${body} } from '${spec}';`;
|
| 241 |
+
}
|
| 242 |
+
|
| 243 |
+
function exportClauseToText(c) {
|
| 244 |
+
if (c.kind === 'star') return `export * from '${relSpec(c.file)}';`;
|
| 245 |
+
if (c.kind === 'namespace') return `export * as ${c.name} from '${relSpec(c.file)}';`;
|
| 246 |
+
return renderNamedExport(relSpec(c.file), c.specs, c.isTypeOnly);
|
| 247 |
+
}
|
| 248 |
+
|
| 249 |
+
function regenerateEntry() {
|
| 250 |
+
const entrySf = project.getSourceFileOrThrow(ENTRY);
|
| 251 |
+
const original = entrySf.getFullText();
|
| 252 |
+
const headerMatch = original.match(/^\s*\/\*\*[\s\S]*?\*\//);
|
| 253 |
+
const header = headerMatch ? headerMatch[0] : '/** agent-core-v2 public surface. */';
|
| 254 |
+
|
| 255 |
+
const refs = [];
|
| 256 |
+
for (const decl of [...entrySf.getExportDeclarations(), ...entrySf.getImportDeclarations()]) {
|
| 257 |
+
const barrel = barrelOfDecl(decl);
|
| 258 |
+
if (!barrel) continue;
|
| 259 |
+
let mode;
|
| 260 |
+
if (decl.getKindName() === 'ImportDeclaration') mode = 'side';
|
| 261 |
+
else {
|
| 262 |
+
const isStar = decl.getNamedExports().length === 0 && !decl.getNamespaceExport();
|
| 263 |
+
mode = isStar ? 'star' : 'named';
|
| 264 |
+
}
|
| 265 |
+
refs.push({ decl, barrel, mode });
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
const publicLines = [];
|
| 269 |
+
const loadingLines = [];
|
| 270 |
+
const processed = new Set();
|
| 271 |
+
|
| 272 |
+
for (const { decl, barrel, mode } of refs) {
|
| 273 |
+
const bf = barrel.getFilePath();
|
| 274 |
+
const dirAbs = path.dirname(bf);
|
| 275 |
+
const allLeaves = allLeavesUnderDir(dirAbs);
|
| 276 |
+
const clauses = expandBarrelClauses(barrel);
|
| 277 |
+
const starLeaves = new Set(clauses.filter((c) => c.kind === 'star').map((c) => c.file));
|
| 278 |
+
|
| 279 |
+
if (mode === 'star') {
|
| 280 |
+
for (const c of clauses) publicLines.push(exportClauseToText(c));
|
| 281 |
+
} else if (mode === 'named') {
|
| 282 |
+
const declType = decl.isTypeOnly();
|
| 283 |
+
const groups = new Map();
|
| 284 |
+
for (const s of decl.getNamedExports()) {
|
| 285 |
+
const lookup = s.getName();
|
| 286 |
+
const exportedAs = s.getAliasNode()?.getText() || s.getName();
|
| 287 |
+
const r = resolveName(barrel, lookup);
|
| 288 |
+
if (!r) continue;
|
| 289 |
+
if (!groups.has(r.leafFile)) groups.set(r.leafFile, { specs: [], allType: true });
|
| 290 |
+
const g = groups.get(r.leafFile);
|
| 291 |
+
const t = declType || s.isTypeOnly();
|
| 292 |
+
g.allType = g.allType && t;
|
| 293 |
+
g.specs.push({
|
| 294 |
+
name: r.leafName,
|
| 295 |
+
alias: exportedAs !== r.leafName ? exportedAs : undefined,
|
| 296 |
+
isTypeOnly: t,
|
| 297 |
+
});
|
| 298 |
+
}
|
| 299 |
+
for (const [leaf, { specs, allType }] of groups) {
|
| 300 |
+
publicLines.push(renderNamedExport(relSpec(leaf), specs, allType));
|
| 301 |
+
}
|
| 302 |
+
}
|
| 303 |
+
for (const leaf of allLeaves) {
|
| 304 |
+
const key = leaf;
|
| 305 |
+
if (starLeaves.has(leaf)) continue;
|
| 306 |
+
if (processed.has(key)) continue;
|
| 307 |
+
processed.add(key);
|
| 308 |
+
loadingLines.push(`import '${relSpec(leaf)}';`);
|
| 309 |
+
}
|
| 310 |
+
}
|
| 311 |
+
|
| 312 |
+
const body = [
|
| 313 |
+
header,
|
| 314 |
+
'',
|
| 315 |
+
'// Public surface — precise re-exports of each domain leaf (no barrels).',
|
| 316 |
+
...publicLines,
|
| 317 |
+
'',
|
| 318 |
+
'// Side-effect loading — ensure every domain leaf (and its top-level',
|
| 319 |
+
'// `register*` calls) is evaluated when the package is imported.',
|
| 320 |
+
...loadingLines,
|
| 321 |
+
'',
|
| 322 |
+
].join('\n');
|
| 323 |
+
|
| 324 |
+
if (!DRY) fs.writeFileSync(ENTRY, body);
|
| 325 |
+
return { publicLines: publicLines.length, loadingLines: loadingLines.length };
|
| 326 |
+
}
|
| 327 |
+
|
| 328 |
+
const REGISTER_NAMES = new Set([
|
| 329 |
+
'registerScopedService',
|
| 330 |
+
'registerAgentToolService',
|
| 331 |
+
'registerErrorDomain',
|
| 332 |
+
'registerConfigSection',
|
| 333 |
+
'registerAgentProfile',
|
| 334 |
+
'registerFlagDefinition',
|
| 335 |
+
]);
|
| 336 |
+
|
| 337 |
+
function isModuleScoped(call) {
|
| 338 |
+
let n = call.getParent();
|
| 339 |
+
while (n) {
|
| 340 |
+
const k = n.getKindName();
|
| 341 |
+
if (
|
| 342 |
+
k === 'FunctionDeclaration' ||
|
| 343 |
+
k === 'FunctionExpression' ||
|
| 344 |
+
k === 'ArrowFunction' ||
|
| 345 |
+
k === 'MethodDeclaration' ||
|
| 346 |
+
k === 'Constructor' ||
|
| 347 |
+
k === 'ClassDeclaration'
|
| 348 |
+
) {
|
| 349 |
+
return false;
|
| 350 |
+
}
|
| 351 |
+
n = n.getParent();
|
| 352 |
+
}
|
| 353 |
+
return true;
|
| 354 |
+
}
|
| 355 |
+
|
| 356 |
+
function findRegisterFiles() {
|
| 357 |
+
const files = [];
|
| 358 |
+
for (const sf of project.getSourceFiles()) {
|
| 359 |
+
const f = sf.getFilePath();
|
| 360 |
+
if (!isUnderSrc(f) || f.endsWith('.test.ts')) continue;
|
| 361 |
+
let hit = false;
|
| 362 |
+
sf.forEachDescendant((node) => {
|
| 363 |
+
if (hit) return;
|
| 364 |
+
if (node.getKindName() !== 'CallExpression') return;
|
| 365 |
+
const expr = node.getExpression();
|
| 366 |
+
if (expr.getKindName() !== 'Identifier') return;
|
| 367 |
+
if (!REGISTER_NAMES.has(expr.getText())) return;
|
| 368 |
+
if (isModuleScoped(node)) hit = true;
|
| 369 |
+
});
|
| 370 |
+
if (hit) files.push(f);
|
| 371 |
+
}
|
| 372 |
+
return files.sort();
|
| 373 |
+
}
|
| 374 |
+
|
| 375 |
+
function reachedFromEntry() {
|
| 376 |
+
const reached = new Set();
|
| 377 |
+
const visit = (sf) => {
|
| 378 |
+
const f = sf.getFilePath();
|
| 379 |
+
if (reached.has(f)) return;
|
| 380 |
+
reached.add(f);
|
| 381 |
+
if (!isUnderSrc(f)) return;
|
| 382 |
+
const edges = [...sf.getImportDeclarations(), ...sf.getExportDeclarations()];
|
| 383 |
+
for (const d of edges) {
|
| 384 |
+
if (d.isTypeOnly && d.isTypeOnly()) continue;
|
| 385 |
+
const t = resolvedFile(d);
|
| 386 |
+
if (t && isUnderSrc(t.getFilePath())) visit(t);
|
| 387 |
+
}
|
| 388 |
+
};
|
| 389 |
+
visit(project.getSourceFileOrThrow(ENTRY));
|
| 390 |
+
return reached;
|
| 391 |
+
}
|
| 392 |
+
|
| 393 |
+
function verifyCoverage() {
|
| 394 |
+
const regs = findRegisterFiles();
|
| 395 |
+
const reached = reachedFromEntry();
|
| 396 |
+
const missing = regs.filter((f) => !reached.has(f));
|
| 397 |
+
console.log(`register files: ${regs.length}; reachable from entry: ${reached.size}; missing: ${missing.length}`);
|
| 398 |
+
if (missing.length) {
|
| 399 |
+
console.log('MISSING (not reachable from src/index.ts):');
|
| 400 |
+
for (const m of missing) console.log(' ' + path.relative(PKG, m));
|
| 401 |
+
return false;
|
| 402 |
+
}
|
| 403 |
+
return true;
|
| 404 |
+
}
|
| 405 |
+
|
| 406 |
+
function deleteBarrels() {
|
| 407 |
+
let n = 0;
|
| 408 |
+
for (const sf of project.getSourceFiles()) {
|
| 409 |
+
if (!isBarrelFile(sf)) continue;
|
| 410 |
+
const f = sf.getFilePath();
|
| 411 |
+
if (!DRY) fs.unlinkSync(f);
|
| 412 |
+
n++;
|
| 413 |
+
}
|
| 414 |
+
return n;
|
| 415 |
+
}
|
| 416 |
+
|
| 417 |
+
function main() {
|
| 418 |
+
if (LIST_REGS) {
|
| 419 |
+
for (const f of findRegisterFiles()) console.log(path.relative(PKG, f));
|
| 420 |
+
return;
|
| 421 |
+
}
|
| 422 |
+
if (VERIFY) {
|
| 423 |
+
const ok = verifyCoverage();
|
| 424 |
+
process.exit(ok ? 0 : 1);
|
| 425 |
+
}
|
| 426 |
+
if (ENTRY_ONLY) {
|
| 427 |
+
const r = regenerateEntry();
|
| 428 |
+
console.log(`entry regenerated: ${r.publicLines} public lines, ${r.loadingLines} loading lines${DRY ? ' (dry-run)' : ''}`);
|
| 429 |
+
return;
|
| 430 |
+
}
|
| 431 |
+
|
| 432 |
+
let onlyBarrelPath = null;
|
| 433 |
+
if (ONLY) {
|
| 434 |
+
onlyBarrelPath = path.join(SRC, ONLY, 'index.ts');
|
| 435 |
+
if (!fs.existsSync(onlyBarrelPath)) {
|
| 436 |
+
console.error(`--only target not a barrel: ${path.relative(PKG, onlyBarrelPath)}`);
|
| 437 |
+
process.exit(2);
|
| 438 |
+
}
|
| 439 |
+
}
|
| 440 |
+
|
| 441 |
+
const totals = { files: 0, imports: 0, exports: 0, sideEffects: 0, manuals: [] };
|
| 442 |
+
for (const sf of project.getSourceFiles()) {
|
| 443 |
+
const f = sf.getFilePath();
|
| 444 |
+
if (!isUnderSrc(f) && !f.startsWith(path.join(PKG, 'test') + path.sep)) continue;
|
| 445 |
+
if (f === ENTRY) continue;
|
| 446 |
+
const before = sf.getFullText();
|
| 447 |
+
const r = rewriteConsumerFile(sf, onlyBarrelPath);
|
| 448 |
+
if (sf.getFullText() !== before) {
|
| 449 |
+
totals.files++;
|
| 450 |
+
totals.imports += r.imports;
|
| 451 |
+
totals.exports += r.exports;
|
| 452 |
+
totals.sideEffects += r.sideEffects;
|
| 453 |
+
}
|
| 454 |
+
totals.manuals.push(...r.manuals);
|
| 455 |
+
}
|
| 456 |
+
|
| 457 |
+
if (!DRY) project.saveSync();
|
| 458 |
+
|
| 459 |
+
console.log(
|
| 460 |
+
`rewrote ${totals.files} files: ${totals.imports} barrel imports, ${totals.exports} barrel exports, ${totals.sideEffects} side-effect loads${DRY ? ' (dry-run)' : ''}`,
|
| 461 |
+
);
|
| 462 |
+
if (totals.manuals.length) {
|
| 463 |
+
console.log(`MANUAL (${totals.manuals.length}) — could not auto-split:`);
|
| 464 |
+
for (const m of totals.manuals)
|
| 465 |
+
console.log(` ${path.relative(PKG, m.sf)} :: ${m.why} :: ${m.text.replace(/\s+/g, ' ').slice(0, 120)}`);
|
| 466 |
+
}
|
| 467 |
+
|
| 468 |
+
if (DELETE_BARRELS) {
|
| 469 |
+
const n = deleteBarrels();
|
| 470 |
+
console.log(`deleted ${n} domain barrels${DRY ? ' (dry-run)' : ''}`);
|
| 471 |
+
}
|
| 472 |
+
}
|
| 473 |
+
|
| 474 |
+
main();
|
packages/agent-core-v2/scripts/gen-config-manifest.mts
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
| 2 |
+
import { join, relative } from 'node:path';
|
| 3 |
+
import { pathToFileURL } from 'node:url';
|
| 4 |
+
|
| 5 |
+
import { getConfigOverlayContributions } from '#/app/config/configOverlayContributions';
|
| 6 |
+
import type { ConfigSectionContribution } from '#/app/config/configSectionContributions';
|
| 7 |
+
import { getConfigSectionContributions } from '#/app/config/configSectionContributions';
|
| 8 |
+
import { camelToSnake } from '#/app/config/toml';
|
| 9 |
+
|
| 10 |
+
import {
|
| 11 |
+
asJsonSchema,
|
| 12 |
+
describeType,
|
| 13 |
+
isRecord,
|
| 14 |
+
resolveRef,
|
| 15 |
+
toJsonSchema,
|
| 16 |
+
truncate,
|
| 17 |
+
type JsonSchema,
|
| 18 |
+
} from './lib/jsonSchema.mts';
|
| 19 |
+
|
| 20 |
+
const PKG = join(import.meta.dirname, '..');
|
| 21 |
+
const SRC = join(PKG, 'src');
|
| 22 |
+
export const MANIFEST_PATH = join(PKG, 'docs', 'config-manifest.toml');
|
| 23 |
+
|
| 24 |
+
function walk(dir: string, out: string[] = []): string[] {
|
| 25 |
+
for (const entry of readdirSync(dir)) {
|
| 26 |
+
const p = join(dir, entry);
|
| 27 |
+
if (statSync(p).isDirectory()) walk(p, out);
|
| 28 |
+
else if (entry.endsWith('.ts')) out.push(p);
|
| 29 |
+
}
|
| 30 |
+
return out;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
function constStringValue(source: string, ident: string): string | undefined {
|
| 34 |
+
const re = new RegExp(`(?:export\\s+)?const\\s+${ident}\\s*(?::[^=;]+)?=\\s*'([^']+)'`);
|
| 35 |
+
return re.exec(source)?.[1];
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
function scanSectionOwners(): Map<string, string> {
|
| 39 |
+
const owners = new Map<string, string>();
|
| 40 |
+
for (const file of walk(SRC)) {
|
| 41 |
+
const source = readFileSync(file, 'utf-8');
|
| 42 |
+
if (!source.includes('registerConfigSection(')) continue;
|
| 43 |
+
for (const match of source.matchAll(/registerConfigSection\(\s*(?:'([^']+)'|([A-Za-z0-9_$]+))/g)) {
|
| 44 |
+
const ident = match[2];
|
| 45 |
+
const domain = match[1] ?? (ident === undefined ? undefined : constStringValue(source, ident));
|
| 46 |
+
if (domain !== undefined) owners.set(domain, relative(PKG, file));
|
| 47 |
+
}
|
| 48 |
+
}
|
| 49 |
+
return owners;
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
function scanOverlayOwners(): Map<string, string> {
|
| 53 |
+
const owners = new Map<string, string>();
|
| 54 |
+
for (const file of walk(SRC)) {
|
| 55 |
+
if (file.endsWith('configOverlayContributions.ts')) continue;
|
| 56 |
+
const source = readFileSync(file, 'utf-8');
|
| 57 |
+
if (!source.includes('registerConfigOverlay(')) continue;
|
| 58 |
+
for (const match of source.matchAll(/registerConfigOverlay\(\s*([A-Za-z0-9_$]+)/g)) {
|
| 59 |
+
const ident = match[1];
|
| 60 |
+
if (ident !== undefined) owners.set(ident, relative(PKG, file));
|
| 61 |
+
}
|
| 62 |
+
}
|
| 63 |
+
return owners;
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
function toTomlValue(value: unknown): string {
|
| 67 |
+
if (typeof value === 'string') return JSON.stringify(value);
|
| 68 |
+
if (typeof value === 'number' || typeof value === 'boolean') return String(value);
|
| 69 |
+
if (Array.isArray(value)) return `[${value.map(toTomlValue).join(', ')}]`;
|
| 70 |
+
if (isRecord(value)) {
|
| 71 |
+
const entries = Object.entries(value).map(([k, v]) => `${camelToSnake(k)} = ${toTomlValue(v)}`);
|
| 72 |
+
return `{ ${entries.join(', ')} }`;
|
| 73 |
+
}
|
| 74 |
+
return '""';
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
interface EnvRow {
|
| 78 |
+
readonly field: string;
|
| 79 |
+
readonly env: string;
|
| 80 |
+
readonly detail: string;
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
interface EnvBindingFields {
|
| 84 |
+
readonly env?: unknown;
|
| 85 |
+
readonly deprecatedEnv?: unknown;
|
| 86 |
+
readonly parse?: unknown;
|
| 87 |
+
readonly default?: unknown;
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
function flattenEnvBindings(bindings: unknown, path: string[] = []): EnvRow[] {
|
| 91 |
+
if (typeof bindings === 'string') {
|
| 92 |
+
return [{ field: path.join('.'), env: bindings, detail: '' }];
|
| 93 |
+
}
|
| 94 |
+
if (!isRecord(bindings)) return [];
|
| 95 |
+
const binding = bindings as EnvBindingFields;
|
| 96 |
+
if (typeof binding.env === 'string') {
|
| 97 |
+
const detail: string[] = [];
|
| 98 |
+
if (binding.parse !== undefined) detail.push('custom parse');
|
| 99 |
+
if (binding.default !== undefined) detail.push(`default ${JSON.stringify(binding.default)}`);
|
| 100 |
+
if (typeof binding.deprecatedEnv === 'string') {
|
| 101 |
+
detail.push(`deprecated fallback ${binding.deprecatedEnv}`);
|
| 102 |
+
}
|
| 103 |
+
return [{ field: path.join('.'), env: binding.env, detail: detail.join('; ') }];
|
| 104 |
+
}
|
| 105 |
+
return Object.entries(bindings).flatMap(([key, value]) => flattenEnvBindings(value, [...path, key]));
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
function snakePath(field: string): string {
|
| 109 |
+
return field.split('.').map(camelToSnake).join('.');
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
const RULE = `# ${'#'.repeat(74)}`;
|
| 113 |
+
|
| 114 |
+
function renderFieldComments(
|
| 115 |
+
properties: Record<string, unknown>,
|
| 116 |
+
root: JsonSchema,
|
| 117 |
+
indent: string,
|
| 118 |
+
depth = 0,
|
| 119 |
+
): string[] {
|
| 120 |
+
const lines: string[] = [];
|
| 121 |
+
for (const [name, prop] of Object.entries(properties)) {
|
| 122 |
+
const resolved = resolveRef(prop, root);
|
| 123 |
+
const propDefault = asJsonSchema(resolved)?.default;
|
| 124 |
+
const defNote = propDefault !== undefined ? ` (default: ${JSON.stringify(propDefault)})` : '';
|
| 125 |
+
lines.push(`${indent}# ${camelToSnake(name)}: ${describeType(resolved)}${defNote}`);
|
| 126 |
+
const subProps = asJsonSchema(resolved)?.properties;
|
| 127 |
+
if (depth < 3 && isRecord(subProps) && Object.keys(subProps).length > 0) {
|
| 128 |
+
lines.push(...renderFieldComments(subProps, root, `${indent} `, depth + 1));
|
| 129 |
+
}
|
| 130 |
+
}
|
| 131 |
+
return lines;
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
function renderBody(section: ConfigSectionContribution): string[] {
|
| 135 |
+
const { domain, schema, options } = section;
|
| 136 |
+
const key = camelToSnake(domain);
|
| 137 |
+
const jsonSchema = schema === undefined ? undefined : toJsonSchema(schema);
|
| 138 |
+
|
| 139 |
+
if (jsonSchema === undefined) {
|
| 140 |
+
if (isRecord(options.defaultValue)) {
|
| 141 |
+
return [
|
| 142 |
+
`[${key}]`,
|
| 143 |
+
`# (${schema === undefined ? 'no schema — passthrough' : 'schema uses transforms'}; fields below come from the registered default)`,
|
| 144 |
+
...Object.entries(options.defaultValue).map(
|
| 145 |
+
([k, v]) => `${camelToSnake(k)} = ${truncate(toTomlValue(v))}`,
|
| 146 |
+
),
|
| 147 |
+
];
|
| 148 |
+
}
|
| 149 |
+
if (options.defaultValue !== undefined) {
|
| 150 |
+
return [`${key} = ${truncate(toTomlValue(options.defaultValue))}`];
|
| 151 |
+
}
|
| 152 |
+
return [`[${key}]`, `# (${schema === undefined ? 'no schema — passthrough' : 'schema uses transforms; see the owner file'})`];
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
if (isRecord(jsonSchema.properties) && Object.keys(jsonSchema.properties).length > 0) {
|
| 156 |
+
const defaults = isRecord(options.defaultValue) ? options.defaultValue : {};
|
| 157 |
+
const lines = [`[${key}]`];
|
| 158 |
+
for (const [name, prop] of Object.entries(jsonSchema.properties)) {
|
| 159 |
+
const fieldKey = camelToSnake(name);
|
| 160 |
+
if (defaults[name] !== undefined) {
|
| 161 |
+
lines.push(`${fieldKey} = ${truncate(toTomlValue(defaults[name]))}`);
|
| 162 |
+
continue;
|
| 163 |
+
}
|
| 164 |
+
const resolved = resolveRef(prop, jsonSchema);
|
| 165 |
+
const subProps = asJsonSchema(resolved)?.properties;
|
| 166 |
+
if (isRecord(subProps) && Object.keys(subProps).length > 0) {
|
| 167 |
+
lines.push('');
|
| 168 |
+
lines.push(`# [${key}.${fieldKey}]`);
|
| 169 |
+
lines.push(...renderFieldComments(subProps, jsonSchema, ' '));
|
| 170 |
+
continue;
|
| 171 |
+
}
|
| 172 |
+
const itemProps = asJsonSchema(
|
| 173 |
+
resolveRef(asJsonSchema(resolved)?.items, jsonSchema),
|
| 174 |
+
)?.properties;
|
| 175 |
+
if (isRecord(itemProps) && Object.keys(itemProps).length > 0) {
|
| 176 |
+
lines.push(`# ${fieldKey}: object[] — one entry per item:`);
|
| 177 |
+
lines.push(...renderFieldComments(itemProps, jsonSchema, ' '));
|
| 178 |
+
continue;
|
| 179 |
+
}
|
| 180 |
+
lines.push(...renderFieldComments({ [name]: prop }, jsonSchema, ''));
|
| 181 |
+
}
|
| 182 |
+
return lines;
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
if (jsonSchema.additionalProperties !== undefined) {
|
| 186 |
+
const valueSchema = resolveRef(jsonSchema.additionalProperties, jsonSchema);
|
| 187 |
+
const valueProps = asJsonSchema(valueSchema)?.properties;
|
| 188 |
+
const lines = [`[${key}]`];
|
| 189 |
+
if (isRecord(valueProps) && Object.keys(valueProps).length > 0) {
|
| 190 |
+
lines.push('');
|
| 191 |
+
lines.push(`# one [${key}."<name>"] table per entry:`);
|
| 192 |
+
lines.push(`# [${key}."<name>"]`);
|
| 193 |
+
lines.push(...renderFieldComments(valueProps, jsonSchema, ' '));
|
| 194 |
+
} else {
|
| 195 |
+
lines.push(`# <name>: ${describeType(valueSchema)}`);
|
| 196 |
+
}
|
| 197 |
+
return lines;
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
if (jsonSchema.type === 'array') {
|
| 201 |
+
const itemProps = asJsonSchema(resolveRef(jsonSchema.items, jsonSchema))?.properties;
|
| 202 |
+
if (isRecord(itemProps) && Object.keys(itemProps).length > 0) {
|
| 203 |
+
return [
|
| 204 |
+
`# one [[${key}]] table per entry:`,
|
| 205 |
+
`# [[${key}]]`,
|
| 206 |
+
...renderFieldComments(itemProps, jsonSchema, ' '),
|
| 207 |
+
];
|
| 208 |
+
}
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
if (options.defaultValue !== undefined) {
|
| 212 |
+
return [`${key} = ${truncate(toTomlValue(options.defaultValue))}`];
|
| 213 |
+
}
|
| 214 |
+
return [`# ${key}: ${describeType(jsonSchema)}`];
|
| 215 |
+
}
|
| 216 |
+
|
| 217 |
+
function renderSection(section: ConfigSectionContribution, owner: string | undefined): string[] {
|
| 218 |
+
const { domain, options } = section;
|
| 219 |
+
const key = camelToSnake(domain);
|
| 220 |
+
const lines: string[] = [RULE];
|
| 221 |
+
lines.push(`# ${domain}${key === domain ? '' : ` (config.toml: ${key})`}`);
|
| 222 |
+
lines.push(`# owner: ${owner ?? '(unresolved)'}`);
|
| 223 |
+
lines.push(`# scope: ${options.scope ?? 'core'}`);
|
| 224 |
+
const hooks: string[] = [];
|
| 225 |
+
if (options.merge !== undefined) hooks.push('custom merge');
|
| 226 |
+
if (options.fromToml !== undefined) hooks.push('custom fromToml');
|
| 227 |
+
if (options.toToml !== undefined) hooks.push('custom toToml');
|
| 228 |
+
if (options.stripEnv !== undefined) hooks.push('stripEnv');
|
| 229 |
+
if (hooks.length > 0) lines.push(`# hooks: ${hooks.join(' · ')}`);
|
| 230 |
+
const deprecations = options.deprecations ?? [];
|
| 231 |
+
if (deprecations.length > 0) {
|
| 232 |
+
lines.push('# deprecations (old key is ignored + warns; rename manually):');
|
| 233 |
+
for (const deprecation of deprecations) {
|
| 234 |
+
lines.push(`# ${deprecation.key} -> ${deprecation.replacement}`);
|
| 235 |
+
}
|
| 236 |
+
}
|
| 237 |
+
const envRows = flattenEnvBindings(options.env);
|
| 238 |
+
if (envRows.length > 0) {
|
| 239 |
+
lines.push('# env:');
|
| 240 |
+
for (const row of envRows) {
|
| 241 |
+
lines.push(`# ${snakePath(row.field)} <- ${row.env}${row.detail === '' ? '' : ` (${row.detail})`}`);
|
| 242 |
+
}
|
| 243 |
+
}
|
| 244 |
+
lines.push(RULE);
|
| 245 |
+
lines.push('');
|
| 246 |
+
lines.push(...renderBody(section));
|
| 247 |
+
return lines;
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
export async function buildConfigManifest(): Promise<string> {
|
| 251 |
+
await import('../src/index.ts');
|
| 252 |
+
const sections = getConfigSectionContributions().toSorted((a, b) =>
|
| 253 |
+
a.domain.localeCompare(b.domain),
|
| 254 |
+
);
|
| 255 |
+
const overlays = getConfigOverlayContributions();
|
| 256 |
+
const sectionOwners = scanSectionOwners();
|
| 257 |
+
const overlayOwners = scanOverlayOwners();
|
| 258 |
+
|
| 259 |
+
const out: string[] = [
|
| 260 |
+
'# Config Section Manifest',
|
| 261 |
+
'#',
|
| 262 |
+
'# Generated by scripts/gen-config-manifest.mts — do not edit by hand.',
|
| 263 |
+
'# Regenerate with: pnpm --filter @moonshot-ai/agent-core-v2 gen:config-manifest',
|
| 264 |
+
'#',
|
| 265 |
+
'# One [table] per registered config section, in the on-disk config.toml shape',
|
| 266 |
+
'# (snake_case keys). Un-commented assignments are registered defaults;',
|
| 267 |
+
'# commented "# field: type" lines describe the remaining schema fields.',
|
| 268 |
+
'# Values resolve as: default -> config.toml -> env overlay -> memory.',
|
| 269 |
+
'',
|
| 270 |
+
`# Index (${sections.length} sections · ${overlays.length} overlay(s))`,
|
| 271 |
+
];
|
| 272 |
+
const width = Math.max(...sections.map((s) => s.domain.length));
|
| 273 |
+
for (const { domain } of sections) {
|
| 274 |
+
out.push(`# ${domain.padEnd(width)} ${sectionOwners.get(domain) ?? '(unresolved)'}`);
|
| 275 |
+
}
|
| 276 |
+
for (const [ident, file] of overlayOwners) {
|
| 277 |
+
out.push(`# ${'(overlay) ' + ident} ${file}`);
|
| 278 |
+
}
|
| 279 |
+
out.push('');
|
| 280 |
+
|
| 281 |
+
for (const section of sections) {
|
| 282 |
+
out.push(...renderSection(section, sectionOwners.get(section.domain)));
|
| 283 |
+
out.push('');
|
| 284 |
+
}
|
| 285 |
+
return out.join('\n');
|
| 286 |
+
}
|
| 287 |
+
|
| 288 |
+
async function main(): Promise<void> {
|
| 289 |
+
const check = process.argv.includes('--check');
|
| 290 |
+
const manifest = await buildConfigManifest();
|
| 291 |
+
if (check) {
|
| 292 |
+
let current: string | undefined;
|
| 293 |
+
try {
|
| 294 |
+
current = readFileSync(MANIFEST_PATH, 'utf-8');
|
| 295 |
+
} catch {
|
| 296 |
+
current = undefined;
|
| 297 |
+
}
|
| 298 |
+
if (current !== manifest) {
|
| 299 |
+
console.error(
|
| 300 |
+
`[gen-config-manifest] ${relative(process.cwd(), MANIFEST_PATH)} is stale. ` +
|
| 301 |
+
'Regenerate with `pnpm --filter @moonshot-ai/agent-core-v2 gen:config-manifest`.',
|
| 302 |
+
);
|
| 303 |
+
process.exit(1);
|
| 304 |
+
}
|
| 305 |
+
console.log('[gen-config-manifest] up to date');
|
| 306 |
+
return;
|
| 307 |
+
}
|
| 308 |
+
writeFileSync(MANIFEST_PATH, manifest);
|
| 309 |
+
console.log(`[gen-config-manifest] wrote ${relative(process.cwd(), MANIFEST_PATH)}`);
|
| 310 |
+
}
|
| 311 |
+
|
| 312 |
+
if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
| 313 |
+
await main();
|
| 314 |
+
}
|
packages/agent-core-v2/scripts/gen-contract-types.mjs
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { execFileSync } from 'node:child_process';
|
| 2 |
+
import {
|
| 3 |
+
cpSync,
|
| 4 |
+
existsSync,
|
| 5 |
+
mkdirSync,
|
| 6 |
+
readdirSync,
|
| 7 |
+
readFileSync,
|
| 8 |
+
rmSync,
|
| 9 |
+
statSync,
|
| 10 |
+
} from 'node:fs';
|
| 11 |
+
import { dirname, join, relative } from 'node:path';
|
| 12 |
+
import { fileURLToPath } from 'node:url';
|
| 13 |
+
import { createRequire } from 'node:module';
|
| 14 |
+
|
| 15 |
+
import { Project, SyntaxKind } from 'ts-morph';
|
| 16 |
+
|
| 17 |
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
| 18 |
+
const PKG = join(__dirname, '..');
|
| 19 |
+
const SRC = join(PKG, 'src');
|
| 20 |
+
const TMP = join(PKG, '.contract-types-tmp');
|
| 21 |
+
const TSCONFIG = join(PKG, 'tsconfig.contract.json');
|
| 22 |
+
|
| 23 |
+
const repoRoot = join(PKG, '..', '..');
|
| 24 |
+
const defaultOut = join(repoRoot, '..', 'kimi-code-mini-bench', 'types', 'agent-core-v2');
|
| 25 |
+
const OUT = process.argv[2] ? join(process.cwd(), process.argv[2]) : defaultOut;
|
| 26 |
+
|
| 27 |
+
const require = createRequire(import.meta.url);
|
| 28 |
+
const tscBin = require.resolve('typescript/bin/tsc');
|
| 29 |
+
|
| 30 |
+
function log(msg) {
|
| 31 |
+
console.log(`[gen-contract-types] ${msg}`);
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
function walk(dir, out) {
|
| 35 |
+
for (const entry of readdirSync(dir)) {
|
| 36 |
+
const p = join(dir, entry);
|
| 37 |
+
const s = statSync(p);
|
| 38 |
+
if (s.isDirectory()) walk(p, out);
|
| 39 |
+
else out.push(p);
|
| 40 |
+
}
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
rmSync(TMP, { recursive: true, force: true });
|
| 44 |
+
mkdirSync(TMP, { recursive: true });
|
| 45 |
+
log(`emitting declarations via tsc -> ${relative(PKG, TMP)}`);
|
| 46 |
+
try {
|
| 47 |
+
execFileSync(process.execPath, [tscBin, '-p', TSCONFIG, '--outDir', TMP], {
|
| 48 |
+
cwd: PKG,
|
| 49 |
+
stdio: 'pipe',
|
| 50 |
+
});
|
| 51 |
+
} catch (err) {
|
| 52 |
+
const code = err && typeof err === 'object' && 'status' in err ? err.status : 'unknown';
|
| 53 |
+
log(`tsc exited ${String(code)} (non-fatal; declarations are still emitted)`);
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
log('scanning for registerScopedService(...) bindings');
|
| 57 |
+
const project = new Project();
|
| 58 |
+
project.addSourceFilesAtPaths(join(SRC, '**', '*.ts'));
|
| 59 |
+
|
| 60 |
+
const dropByDts = new Map();
|
| 61 |
+
const implFiles = [];
|
| 62 |
+
|
| 63 |
+
for (const sf of project.getSourceFiles()) {
|
| 64 |
+
const calls = sf
|
| 65 |
+
.getDescendantsOfKind(SyntaxKind.CallExpression)
|
| 66 |
+
.filter((c) => c.getExpression().getText() === 'registerScopedService');
|
| 67 |
+
if (calls.length === 0) continue;
|
| 68 |
+
|
| 69 |
+
implFiles.push(sf.getFilePath());
|
| 70 |
+
const names = new Set();
|
| 71 |
+
for (const call of calls) {
|
| 72 |
+
const args = call.getArguments();
|
| 73 |
+
if (args.length < 3) continue;
|
| 74 |
+
const text = args[2].getText().trim();
|
| 75 |
+
names.add(/^[A-Za-z_$][\w$]*$/.test(text) ? text : '*');
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
const rel = relative(SRC, sf.getFilePath()).replace(/\.ts$/, '.d.ts');
|
| 79 |
+
const dtsPath = join(TMP, rel);
|
| 80 |
+
const existing = dropByDts.get(dtsPath) ?? new Set();
|
| 81 |
+
for (const n of names) existing.add(n);
|
| 82 |
+
dropByDts.set(dtsPath, existing);
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
log(`found ${implFiles.length} impl files`);
|
| 86 |
+
|
| 87 |
+
let scrubbedFiles = 0;
|
| 88 |
+
let scrubbedClasses = 0;
|
| 89 |
+
for (const [dtsPath, names] of dropByDts) {
|
| 90 |
+
if (!existsSync(dtsPath)) continue;
|
| 91 |
+
const dtsProject = new Project();
|
| 92 |
+
const dts = dtsProject.addSourceFileAtPath(dtsPath);
|
| 93 |
+
const dropAll = names.has('*');
|
| 94 |
+
let removed = 0;
|
| 95 |
+
for (const cls of dts.getClasses()) {
|
| 96 |
+
const clsName = cls.getName();
|
| 97 |
+
if (dropAll || (clsName !== undefined && names.has(clsName))) {
|
| 98 |
+
cls.remove();
|
| 99 |
+
removed++;
|
| 100 |
+
}
|
| 101 |
+
}
|
| 102 |
+
if (removed > 0) {
|
| 103 |
+
dts.saveSync();
|
| 104 |
+
scrubbedFiles++;
|
| 105 |
+
scrubbedClasses += removed;
|
| 106 |
+
}
|
| 107 |
+
}
|
| 108 |
+
log(`scrubbed ${scrubbedClasses} impl class(es) across ${scrubbedFiles} file(s)`);
|
| 109 |
+
|
| 110 |
+
function resolveReexportTarget(dtsPath, spec) {
|
| 111 |
+
const clean = spec.endsWith('.js') ? spec.slice(0, -'.js'.length) : spec;
|
| 112 |
+
if (clean.startsWith('.')) return join(dirname(dtsPath), `${clean}.d.ts`);
|
| 113 |
+
if (clean.startsWith('#/')) return join(TMP, `${clean.slice(2)}.d.ts`);
|
| 114 |
+
return undefined;
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
let scrubbedReexports = 0;
|
| 118 |
+
const emittedDts = [];
|
| 119 |
+
walk(TMP, emittedDts);
|
| 120 |
+
const reexportProject = new Project();
|
| 121 |
+
for (const dtsPath of emittedDts) {
|
| 122 |
+
if (!dtsPath.endsWith('.d.ts')) continue;
|
| 123 |
+
const dts = reexportProject.addSourceFileAtPath(dtsPath);
|
| 124 |
+
let changed = false;
|
| 125 |
+
for (const exp of dts.getExportDeclarations()) {
|
| 126 |
+
const spec = exp.getModuleSpecifierValue();
|
| 127 |
+
if (spec === undefined) continue;
|
| 128 |
+
const target = resolveReexportTarget(dtsPath, spec);
|
| 129 |
+
const names = target === undefined ? undefined : dropByDts.get(target);
|
| 130 |
+
if (names === undefined) continue;
|
| 131 |
+
let removedHere = false;
|
| 132 |
+
for (const specifier of exp.getNamedExports()) {
|
| 133 |
+
const name = specifier.getNameNode().getText();
|
| 134 |
+
if (names.has('*') || names.has(name)) {
|
| 135 |
+
specifier.remove();
|
| 136 |
+
removedHere = true;
|
| 137 |
+
scrubbedReexports++;
|
| 138 |
+
}
|
| 139 |
+
}
|
| 140 |
+
if (removedHere && exp.getNamedExports().length === 0) exp.remove();
|
| 141 |
+
changed = changed || removedHere;
|
| 142 |
+
}
|
| 143 |
+
if (changed) dts.saveSync();
|
| 144 |
+
}
|
| 145 |
+
log(`scrubbed ${scrubbedReexports} re-export(s) of impl classes from alias modules`);
|
| 146 |
+
|
| 147 |
+
rmSync(OUT, { recursive: true, force: true });
|
| 148 |
+
mkdirSync(dirname(OUT), { recursive: true });
|
| 149 |
+
cpSync(TMP, OUT, { recursive: true });
|
| 150 |
+
|
| 151 |
+
const emitted = [];
|
| 152 |
+
walk(OUT, emitted);
|
| 153 |
+
const dtsCount = emitted.filter((f) => f.endsWith('.d.ts')).length;
|
| 154 |
+
log(`wrote ${dtsCount} declaration file(s) -> ${OUT}`);
|
| 155 |
+
|
| 156 |
+
const leaks = [];
|
| 157 |
+
for (const [dtsPath, names] of dropByDts) {
|
| 158 |
+
const outPath = join(OUT, relative(TMP, dtsPath));
|
| 159 |
+
if (!existsSync(outPath) || names.has('*')) continue;
|
| 160 |
+
const text = readFileSync(outPath, 'utf8');
|
| 161 |
+
for (const n of names) {
|
| 162 |
+
const re = new RegExp(`declare\\s+class\\s+${n}\\b`);
|
| 163 |
+
if (re.test(text)) leaks.push(`${relative(OUT, outPath)} still declares ${n}`);
|
| 164 |
+
}
|
| 165 |
+
}
|
| 166 |
+
if (leaks.length > 0) {
|
| 167 |
+
log(`WARNING: ${leaks.length} possible leak(s):`);
|
| 168 |
+
for (const l of leaks) log(` - ${l}`);
|
| 169 |
+
} else {
|
| 170 |
+
log('leak check passed: no registered impl class survives in its declaring file');
|
| 171 |
+
}
|
packages/agent-core-v2/scripts/gen-state-manifest.mts
ADDED
|
@@ -0,0 +1,882 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { readFileSync, writeFileSync } from 'node:fs';
|
| 2 |
+
import { join, relative } from 'node:path';
|
| 3 |
+
import { pathToFileURL } from 'node:url';
|
| 4 |
+
|
| 5 |
+
import {
|
| 6 |
+
type CallExpression,
|
| 7 |
+
Node,
|
| 8 |
+
Project,
|
| 9 |
+
SyntaxKind,
|
| 10 |
+
ts,
|
| 11 |
+
type Identifier,
|
| 12 |
+
type PropertyAccessExpression,
|
| 13 |
+
type Signature,
|
| 14 |
+
type SourceFile,
|
| 15 |
+
type Symbol as MorphSymbol,
|
| 16 |
+
type Type as MorphType,
|
| 17 |
+
type TypeChecker,
|
| 18 |
+
type VariableDeclaration,
|
| 19 |
+
} from 'ts-morph';
|
| 20 |
+
|
| 21 |
+
const PKG = join(import.meta.dirname, '..');
|
| 22 |
+
const REPO_ROOT = join(PKG, '..', '..');
|
| 23 |
+
const SRC = join(PKG, 'src');
|
| 24 |
+
export const MANIFEST_PATH = join(PKG, 'docs', 'state-manifest.d.ts');
|
| 25 |
+
|
| 26 |
+
const SCOPES = [
|
| 27 |
+
{
|
| 28 |
+
dir: 'app',
|
| 29 |
+
label: 'App',
|
| 30 |
+
interfaceName: 'AppStateSnapshot',
|
| 31 |
+
keyUnionName: 'AppStateKey',
|
| 32 |
+
},
|
| 33 |
+
{
|
| 34 |
+
dir: 'workspace',
|
| 35 |
+
label: 'Workspace',
|
| 36 |
+
interfaceName: 'WorkspaceStateSnapshot',
|
| 37 |
+
keyUnionName: 'WorkspaceStateKey',
|
| 38 |
+
},
|
| 39 |
+
{
|
| 40 |
+
dir: 'session',
|
| 41 |
+
label: 'Session',
|
| 42 |
+
interfaceName: 'SessionStateSnapshot',
|
| 43 |
+
keyUnionName: 'SessionStateKey',
|
| 44 |
+
},
|
| 45 |
+
{
|
| 46 |
+
dir: 'agent',
|
| 47 |
+
label: 'Agent',
|
| 48 |
+
interfaceName: 'AgentStateSnapshot',
|
| 49 |
+
keyUnionName: 'AgentStateKey',
|
| 50 |
+
},
|
| 51 |
+
] as const;
|
| 52 |
+
|
| 53 |
+
type ScopeDir = (typeof SCOPES)[number]['dir'];
|
| 54 |
+
|
| 55 |
+
interface KeyDef {
|
| 56 |
+
readonly constName: string;
|
| 57 |
+
readonly keyName: string;
|
| 58 |
+
readonly file: string;
|
| 59 |
+
readonly exported: boolean;
|
| 60 |
+
readonly declaration: VariableDeclaration;
|
| 61 |
+
readonly replayable?: {
|
| 62 |
+
readonly durable: boolean;
|
| 63 |
+
readonly undoable: boolean;
|
| 64 |
+
readonly folds: readonly string[];
|
| 65 |
+
};
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
interface Registration {
|
| 69 |
+
readonly def: KeyDef;
|
| 70 |
+
readonly scope: ScopeDir;
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
interface StateManifestModel {
|
| 74 |
+
readonly registrations: readonly Registration[];
|
| 75 |
+
readonly unregistered: readonly KeyDef[];
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
function scopeDirOf(file: string): ScopeDir | undefined {
|
| 79 |
+
const first = relative(SRC, file).split(/[\\/]/)[0];
|
| 80 |
+
return SCOPES.some((scope) => scope.dir === first) ? (first as ScopeDir) : undefined;
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
function isFeaturesFile(file: string): boolean {
|
| 84 |
+
return relative(SRC, file).split(/[\\/]/)[0] === 'features';
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
const FEATURES_RECEIVER_SCOPE: Readonly<Record<string, ScopeDir>> = {
|
| 88 |
+
IAppStateService: 'app',
|
| 89 |
+
IWorkspaceStateService: 'workspace',
|
| 90 |
+
ISessionStateService: 'session',
|
| 91 |
+
IAgentStateService: 'agent',
|
| 92 |
+
};
|
| 93 |
+
|
| 94 |
+
function receiverScope(
|
| 95 |
+
expression: PropertyAccessExpression,
|
| 96 |
+
checker: TypeChecker,
|
| 97 |
+
): ScopeDir | undefined {
|
| 98 |
+
const typeName = checker.getTypeAtLocation(expression.getExpression()).getSymbol()?.getName();
|
| 99 |
+
return typeName === undefined ? undefined : FEATURES_RECEIVER_SCOPE[typeName];
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
function featuresRegisterScope(
|
| 103 |
+
expression: PropertyAccessExpression,
|
| 104 |
+
checker: TypeChecker,
|
| 105 |
+
sf: SourceFile,
|
| 106 |
+
): ScopeDir {
|
| 107 |
+
const scope = receiverScope(expression, checker);
|
| 108 |
+
if (scope === undefined) {
|
| 109 |
+
throw new Error(
|
| 110 |
+
`[gen-state-manifest] cannot resolve the state-service scope of '${expression.getText()}' ` +
|
| 111 |
+
`in ${srcRelative(sf.getFilePath())} — register through an ` +
|
| 112 |
+
'I{App,Workspace,Session,Agent}StateService-typed member.',
|
| 113 |
+
);
|
| 114 |
+
}
|
| 115 |
+
return scope;
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
function srcRelative(file: string): string {
|
| 119 |
+
return relative(PKG, file).split('\\').join('/');
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
function repoRelative(file: string): string {
|
| 123 |
+
return relative(REPO_ROOT, file).split('\\').join('/');
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
function tsFieldKey(key: string): string {
|
| 127 |
+
return /^[$A-Z_a-z][$\w]*$/.test(key) ? key : JSON.stringify(key);
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
function stableSymbolKey(key: string): string {
|
| 131 |
+
const match = /^__@(.+)@\d+$/.exec(key);
|
| 132 |
+
return match === null ? key : `__@${match[1]}`;
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
function collectKeyDefs(project: Project): Map<VariableDeclaration, KeyDef> {
|
| 136 |
+
const defs = new Map<VariableDeclaration, KeyDef>();
|
| 137 |
+
for (const sf of project.getSourceFiles()) {
|
| 138 |
+
const filePath = sf.getFilePath();
|
| 139 |
+
if (scopeDirOf(filePath) === undefined && !isFeaturesFile(filePath)) continue;
|
| 140 |
+
for (const statement of sf.getVariableStatements()) {
|
| 141 |
+
for (const declaration of statement.getDeclarations()) {
|
| 142 |
+
const initializer = declaration.getInitializer();
|
| 143 |
+
if (initializer === undefined || !Node.isCallExpression(initializer)) continue;
|
| 144 |
+
const parsed = parseDefineStateChain(initializer);
|
| 145 |
+
if (parsed === undefined) continue;
|
| 146 |
+
defs.set(declaration, {
|
| 147 |
+
constName: declaration.getName(),
|
| 148 |
+
keyName: parsed.keyName,
|
| 149 |
+
file: sf.getFilePath(),
|
| 150 |
+
exported: statement.isExported(),
|
| 151 |
+
declaration,
|
| 152 |
+
replayable: parsed.replayable,
|
| 153 |
+
});
|
| 154 |
+
}
|
| 155 |
+
}
|
| 156 |
+
}
|
| 157 |
+
return defs;
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
function parseDefineStateChain(
|
| 161 |
+
initializer: CallExpression,
|
| 162 |
+
): { keyName: string; replayable?: KeyDef['replayable'] } | undefined {
|
| 163 |
+
let durable = true;
|
| 164 |
+
let undoable = false;
|
| 165 |
+
let replayable = false;
|
| 166 |
+
const folds: string[] = [];
|
| 167 |
+
let current: CallExpression = initializer;
|
| 168 |
+
for (;;) {
|
| 169 |
+
const expression = current.getExpression();
|
| 170 |
+
if (Node.isIdentifier(expression) && expression.getText() === 'defineState') {
|
| 171 |
+
const [nameArg] = current.getArguments();
|
| 172 |
+
if (nameArg === undefined || !Node.isStringLiteral(nameArg)) return undefined;
|
| 173 |
+
return {
|
| 174 |
+
keyName: nameArg.getLiteralValue(),
|
| 175 |
+
replayable: replayable ? { durable, undoable, folds } : undefined,
|
| 176 |
+
};
|
| 177 |
+
}
|
| 178 |
+
if (!Node.isPropertyAccessExpression(expression)) return undefined;
|
| 179 |
+
const method = expression.getName();
|
| 180 |
+
if (method === 'replayable') {
|
| 181 |
+
replayable = true;
|
| 182 |
+
const [arg] = current.getArguments();
|
| 183 |
+
if (arg !== undefined && Node.isObjectLiteralExpression(arg)) {
|
| 184 |
+
const durableProp = arg.getProperty('durable');
|
| 185 |
+
if (durableProp !== undefined && Node.isPropertyAssignment(durableProp)) {
|
| 186 |
+
durable = durableProp.getInitializer()?.getText() !== 'false';
|
| 187 |
+
}
|
| 188 |
+
}
|
| 189 |
+
} else if (method === 'undoable') {
|
| 190 |
+
undoable = true;
|
| 191 |
+
} else if (method === 'on') {
|
| 192 |
+
const [eventArg] = current.getArguments();
|
| 193 |
+
if (eventArg !== undefined) folds.unshift(eventArg.getText());
|
| 194 |
+
} else {
|
| 195 |
+
return undefined;
|
| 196 |
+
}
|
| 197 |
+
const inner = expression.getExpression();
|
| 198 |
+
if (!Node.isCallExpression(inner)) return undefined;
|
| 199 |
+
current = inner;
|
| 200 |
+
}
|
| 201 |
+
}
|
| 202 |
+
|
| 203 |
+
function resolveKeyDef(
|
| 204 |
+
identifier: Identifier,
|
| 205 |
+
defs: ReadonlyMap<VariableDeclaration, KeyDef>,
|
| 206 |
+
): KeyDef | undefined {
|
| 207 |
+
for (const info of identifier.getDefinitions()) {
|
| 208 |
+
const node = info.getDeclarationNode();
|
| 209 |
+
if (node !== undefined && Node.isVariableDeclaration(node)) {
|
| 210 |
+
const def = defs.get(node);
|
| 211 |
+
if (def !== undefined) return def;
|
| 212 |
+
}
|
| 213 |
+
}
|
| 214 |
+
return undefined;
|
| 215 |
+
}
|
| 216 |
+
|
| 217 |
+
function collectRegistrations(
|
| 218 |
+
project: Project,
|
| 219 |
+
defs: ReadonlyMap<VariableDeclaration, KeyDef>,
|
| 220 |
+
): Registration[] {
|
| 221 |
+
const checker = project.getTypeChecker();
|
| 222 |
+
const registrations: Registration[] = [];
|
| 223 |
+
const seen = new Map<string, string>();
|
| 224 |
+
for (const sf of project.getSourceFiles()) {
|
| 225 |
+
const fileScope = scopeDirOf(sf.getFilePath());
|
| 226 |
+
const featuresFile = isFeaturesFile(sf.getFilePath());
|
| 227 |
+
if (fileScope === undefined && !featuresFile) continue;
|
| 228 |
+
for (const call of sf.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
| 229 |
+
const expression = call.getExpression();
|
| 230 |
+
if (
|
| 231 |
+
!Node.isPropertyAccessExpression(expression) ||
|
| 232 |
+
expression.getName() !== 'contributeState'
|
| 233 |
+
) {
|
| 234 |
+
continue;
|
| 235 |
+
}
|
| 236 |
+
const args = call.getArguments();
|
| 237 |
+
const [arg] = args;
|
| 238 |
+
if (args.length !== 1 || arg === undefined || !Node.isIdentifier(arg)) continue;
|
| 239 |
+
const def = resolveKeyDef(arg, defs);
|
| 240 |
+
if (def === undefined) continue;
|
| 241 |
+
const scope = receiverScope(expression, checker) ?? fileScope ?? featuresRegisterScope(expression, checker, sf);
|
| 242 |
+
if (!def.exported) {
|
| 243 |
+
throw new Error(
|
| 244 |
+
`[gen-state-manifest] state key '${def.keyName}' (${srcRelative(def.file)}) is ` +
|
| 245 |
+
'registered but its key constant is not exported — the manifest cannot reference it.',
|
| 246 |
+
);
|
| 247 |
+
}
|
| 248 |
+
const dedupe = `${scope}:${def.keyName}`;
|
| 249 |
+
const seenFile = seen.get(dedupe);
|
| 250 |
+
if (seenFile !== undefined) {
|
| 251 |
+
if (seenFile === sf.getFilePath()) continue;
|
| 252 |
+
throw new Error(
|
| 253 |
+
`[gen-state-manifest] state key '${def.keyName}' is registered twice in ${scope} scope.`,
|
| 254 |
+
);
|
| 255 |
+
}
|
| 256 |
+
seen.set(dedupe, sf.getFilePath());
|
| 257 |
+
registrations.push({ def, scope });
|
| 258 |
+
}
|
| 259 |
+
}
|
| 260 |
+
return registrations;
|
| 261 |
+
}
|
| 262 |
+
|
| 263 |
+
function createProject(): Project {
|
| 264 |
+
const project = new Project({
|
| 265 |
+
tsConfigFilePath: join(PKG, 'tsconfig.json'),
|
| 266 |
+
skipAddingFilesFromTsConfig: true,
|
| 267 |
+
});
|
| 268 |
+
project.addSourceFilesAtPaths(join(SRC, '**', '*.ts'));
|
| 269 |
+
return project;
|
| 270 |
+
}
|
| 271 |
+
|
| 272 |
+
const NO_TRUNCATION = ts.TypeFormatFlags.NoTruncation;
|
| 273 |
+
|
| 274 |
+
class TypeRenderer {
|
| 275 |
+
private readonly checker: ts.TypeChecker;
|
| 276 |
+
private readonly expanding = new Set<ts.Type>();
|
| 277 |
+
private readonly expandingNamed: ts.Symbol[] = [];
|
| 278 |
+
readonly externals = new Set<string>();
|
| 279 |
+
readonly warnings = new Set<string>();
|
| 280 |
+
|
| 281 |
+
constructor(private readonly project: Project) {
|
| 282 |
+
this.checker = project.getTypeChecker().compilerObject;
|
| 283 |
+
}
|
| 284 |
+
|
| 285 |
+
renderKeyType(def: KeyDef): string {
|
| 286 |
+
const valueType = def.declaration.getType().getTypeArguments()[0];
|
| 287 |
+
if (valueType === undefined) {
|
| 288 |
+
throw new Error(
|
| 289 |
+
`[gen-state-manifest] cannot resolve the value type of '${def.keyName}' (${srcRelative(def.file)}).`,
|
| 290 |
+
);
|
| 291 |
+
}
|
| 292 |
+
return this.renderType(valueType, def.declaration, 0);
|
| 293 |
+
}
|
| 294 |
+
|
| 295 |
+
private renderType(
|
| 296 |
+
type: MorphType,
|
| 297 |
+
location: Node,
|
| 298 |
+
depth: number,
|
| 299 |
+
skipSymbol?: ts.Symbol,
|
| 300 |
+
): string {
|
| 301 |
+
if (depth > 40) return this.fallback(type, location, 'depth cap');
|
| 302 |
+
|
| 303 |
+
if ((type.getFlags() & ts.TypeFlags.EnumLiteral) !== 0) {
|
| 304 |
+
return this.renderEnumLiteral(type);
|
| 305 |
+
}
|
| 306 |
+
|
| 307 |
+
if (type.isUnion() && (type.getFlags() & ts.TypeFlags.Boolean) !== 0) return 'boolean';
|
| 308 |
+
|
| 309 |
+
if (type.isUnion()) {
|
| 310 |
+
const alias = this.tryRenderAlias(type, location, depth, skipSymbol);
|
| 311 |
+
if (alias !== undefined) return alias;
|
| 312 |
+
const enumUnion = this.tryRenderEnumUnion(type);
|
| 313 |
+
if (enumUnion !== undefined) return enumUnion;
|
| 314 |
+
return this.renderUnionMembers(type.getUnionTypes(), location, depth);
|
| 315 |
+
}
|
| 316 |
+
|
| 317 |
+
if (type.isIntersection()) {
|
| 318 |
+
const alias = this.tryRenderAlias(type, location, depth, skipSymbol);
|
| 319 |
+
if (alias !== undefined) return alias;
|
| 320 |
+
return type
|
| 321 |
+
.getIntersectionTypes()
|
| 322 |
+
.map((member) => this.renderType(member, location, depth + 1))
|
| 323 |
+
.join(' & ');
|
| 324 |
+
}
|
| 325 |
+
|
| 326 |
+
if (this.isLeaf(type)) return this.leafText(type);
|
| 327 |
+
|
| 328 |
+
if (type.isTuple()) {
|
| 329 |
+
const elements = type
|
| 330 |
+
.getTupleElements()
|
| 331 |
+
.map((element) => this.renderType(element, location, depth + 1));
|
| 332 |
+
return `[${elements.join(', ')}]`;
|
| 333 |
+
}
|
| 334 |
+
|
| 335 |
+
if (type.isArray()) {
|
| 336 |
+
const element = type.getArrayElementType();
|
| 337 |
+
if (element === undefined) return this.fallback(type, location, 'array without element');
|
| 338 |
+
const rendered = this.renderType(element, location, depth + 1);
|
| 339 |
+
const text =
|
| 340 |
+
element.isUnion() || element.isIntersection() ? `(${rendered})[]` : `${rendered}[]`;
|
| 341 |
+
return type.getSymbol()?.getName() === 'ReadonlyArray' ? `readonly ${text}` : text;
|
| 342 |
+
}
|
| 343 |
+
|
| 344 |
+
if (type.isObject()) {
|
| 345 |
+
return this.renderObjectType(type, location, depth, skipSymbol);
|
| 346 |
+
}
|
| 347 |
+
|
| 348 |
+
return this.fallback(type, location, 'unhandled type kind');
|
| 349 |
+
}
|
| 350 |
+
|
| 351 |
+
private renderUnionMembers(
|
| 352 |
+
members: readonly MorphType[],
|
| 353 |
+
location: Node,
|
| 354 |
+
depth: number,
|
| 355 |
+
): string {
|
| 356 |
+
const booleanLiterals = members.filter((m) => m.isBooleanLiteral());
|
| 357 |
+
const collapseBoolean =
|
| 358 |
+
booleanLiterals.length === 2 &&
|
| 359 |
+
new Set(booleanLiterals.map((m) => this.leafText(m))).size === 2;
|
| 360 |
+
const rest = collapseBoolean ? members.filter((m) => !m.isBooleanLiteral()) : members;
|
| 361 |
+
const rank = (type: MorphType): number => (type.isNull() ? 1 : type.isUndefined() ? 2 : 0);
|
| 362 |
+
const rendered = rest
|
| 363 |
+
.map((member) => ({ member, rank: rank(member) }))
|
| 364 |
+
.toSorted((a, b) => a.rank - b.rank)
|
| 365 |
+
.map(({ member }) => ({
|
| 366 |
+
member,
|
| 367 |
+
text: this.renderType(member, location, depth + 1),
|
| 368 |
+
}));
|
| 369 |
+
const multi = rendered.length + (collapseBoolean ? 1 : 0) > 1;
|
| 370 |
+
const parts = rendered.map(({ member, text }) =>
|
| 371 |
+
multi && this.needsParensInUnion(member) ? `(${text})` : text,
|
| 372 |
+
);
|
| 373 |
+
if (collapseBoolean) parts.unshift('boolean');
|
| 374 |
+
return [...new Set(parts)].join(' | ');
|
| 375 |
+
}
|
| 376 |
+
|
| 377 |
+
private isLeaf(type: MorphType): boolean {
|
| 378 |
+
const flags = type.getFlags();
|
| 379 |
+
return (
|
| 380 |
+
type.isString() ||
|
| 381 |
+
type.isNumber() ||
|
| 382 |
+
type.isBoolean() ||
|
| 383 |
+
type.isStringLiteral() ||
|
| 384 |
+
type.isNumberLiteral() ||
|
| 385 |
+
type.isBooleanLiteral() ||
|
| 386 |
+
type.isNull() ||
|
| 387 |
+
type.isUndefined() ||
|
| 388 |
+
type.isUnknown() ||
|
| 389 |
+
type.isAny() ||
|
| 390 |
+
type.isNever() ||
|
| 391 |
+
type.isTypeParameter() ||
|
| 392 |
+
(flags &
|
| 393 |
+
(ts.TypeFlags.Void |
|
| 394 |
+
ts.TypeFlags.BigInt |
|
| 395 |
+
ts.TypeFlags.BigIntLiteral |
|
| 396 |
+
ts.TypeFlags.ESSymbol |
|
| 397 |
+
ts.TypeFlags.UniqueESSymbol |
|
| 398 |
+
ts.TypeFlags.TemplateLiteral)) !==
|
| 399 |
+
0
|
| 400 |
+
);
|
| 401 |
+
}
|
| 402 |
+
|
| 403 |
+
private leafText(type: MorphType): string {
|
| 404 |
+
const text = this.checker.typeToString(type.compilerType, undefined, NO_TRUNCATION);
|
| 405 |
+
if (text.length >= 2 && text.startsWith('"') && text.endsWith('"')) {
|
| 406 |
+
const value = JSON.parse(text) as string;
|
| 407 |
+
return value.includes("'") ? JSON.stringify(value) : `'${value}'`;
|
| 408 |
+
}
|
| 409 |
+
return text;
|
| 410 |
+
}
|
| 411 |
+
|
| 412 |
+
private needsParensInUnion(type: MorphType): boolean {
|
| 413 |
+
return type.isIntersection() || (type.isObject() && type.getCallSignatures().length > 0);
|
| 414 |
+
}
|
| 415 |
+
|
| 416 |
+
private fallback(type: MorphType, location: Node, reason: string): string {
|
| 417 |
+
const text = this.checker.typeToString(type.compilerType, location.compilerNode, NO_TRUNCATION);
|
| 418 |
+
this.warnings.add(`${reason}: fell back to '${text.slice(0, 80)}'`);
|
| 419 |
+
return text;
|
| 420 |
+
}
|
| 421 |
+
|
| 422 |
+
private enumLiteralValue(type: MorphType): string {
|
| 423 |
+
const value = (type.compilerType as ts.LiteralType).value;
|
| 424 |
+
if (typeof value === 'string') {
|
| 425 |
+
return value.includes("'") ? JSON.stringify(value) : `'${value}'`;
|
| 426 |
+
}
|
| 427 |
+
if (typeof value === 'number') return String(value);
|
| 428 |
+
return this.leafText(type);
|
| 429 |
+
}
|
| 430 |
+
|
| 431 |
+
private enumDeclOf(type: MorphType): Node | undefined {
|
| 432 |
+
const memberDecl = type.getSymbol()?.getDeclarations()[0];
|
| 433 |
+
if (memberDecl === undefined || !Node.isEnumMember(memberDecl)) return undefined;
|
| 434 |
+
return memberDecl.getParent();
|
| 435 |
+
}
|
| 436 |
+
|
| 437 |
+
private renderEnumLiteral(type: MorphType): string {
|
| 438 |
+
const enumDecl = this.enumDeclOf(type);
|
| 439 |
+
const text = this.enumLiteralValue(type);
|
| 440 |
+
if (enumDecl !== undefined && Node.isEnumDeclaration(enumDecl)) {
|
| 441 |
+
const sym = enumDecl.getSymbol();
|
| 442 |
+
if (sym !== undefined) {
|
| 443 |
+
return `/* ${sym.getName()} — ${repoRelative(enumDecl.getSourceFile().getFilePath())} */ ${text}`;
|
| 444 |
+
}
|
| 445 |
+
}
|
| 446 |
+
return text;
|
| 447 |
+
}
|
| 448 |
+
|
| 449 |
+
private tryRenderEnumUnion(type: MorphType): string | undefined {
|
| 450 |
+
const members = type.getUnionTypes();
|
| 451 |
+
if (members.length === 0) return undefined;
|
| 452 |
+
let enumDecl: Node | undefined;
|
| 453 |
+
for (const member of members) {
|
| 454 |
+
if ((member.getFlags() & ts.TypeFlags.EnumLiteral) === 0) return undefined;
|
| 455 |
+
const parent = this.enumDeclOf(member);
|
| 456 |
+
if (parent === undefined) return undefined;
|
| 457 |
+
if (enumDecl === undefined) enumDecl = parent;
|
| 458 |
+
else if (parent !== enumDecl) return undefined;
|
| 459 |
+
}
|
| 460 |
+
if (enumDecl === undefined || !Node.isEnumDeclaration(enumDecl)) return undefined;
|
| 461 |
+
if (enumDecl.getMembers().length !== members.length) return undefined;
|
| 462 |
+
const sym = enumDecl.getSymbol();
|
| 463 |
+
if (sym === undefined) return undefined;
|
| 464 |
+
const values = members.map((member) => this.enumLiteralValue(member));
|
| 465 |
+
return `/* ${sym.getName()} — ${repoRelative(enumDecl.getSourceFile().getFilePath())} */ ${values.join(' | ')}`;
|
| 466 |
+
}
|
| 467 |
+
|
| 468 |
+
private classify(sym: MorphSymbol): 'named' | 'ambient' | 'inline' {
|
| 469 |
+
const decls = sym.getDeclarations();
|
| 470 |
+
if (decls.length === 0) return 'inline';
|
| 471 |
+
const inNodeModules = (file: string) => /[\\/]node_modules[\\/]/.test(file);
|
| 472 |
+
if (decls.every((d) => !inNodeModules(d.getSourceFile().getFilePath()))) {
|
| 473 |
+
const named = decls.some(
|
| 474 |
+
(d) =>
|
| 475 |
+
Node.isInterfaceDeclaration(d) ||
|
| 476 |
+
Node.isClassDeclaration(d) ||
|
| 477 |
+
Node.isTypeAliasDeclaration(d) ||
|
| 478 |
+
Node.isEnumDeclaration(d),
|
| 479 |
+
);
|
| 480 |
+
const generic = decls.some(
|
| 481 |
+
(d) =>
|
| 482 |
+
(Node.isInterfaceDeclaration(d) ||
|
| 483 |
+
Node.isClassDeclaration(d) ||
|
| 484 |
+
Node.isTypeAliasDeclaration(d)) &&
|
| 485 |
+
d.getTypeParameters().length > 0,
|
| 486 |
+
);
|
| 487 |
+
return named && !generic ? 'named' : 'inline';
|
| 488 |
+
}
|
| 489 |
+
if (decls.every((d) => inNodeModules(d.getSourceFile().getFilePath()))) return 'ambient';
|
| 490 |
+
return 'inline';
|
| 491 |
+
}
|
| 492 |
+
|
| 493 |
+
private noteExternal(sym: MorphSymbol): void {
|
| 494 |
+
const isTsLib = (file: string) => /[\\/]node_modules[\\/]typescript[\\/]lib[\\/]/.test(file);
|
| 495 |
+
if (sym.getDeclarations().some((d) => !isTsLib(d.getSourceFile().getFilePath()))) {
|
| 496 |
+
this.externals.add(sym.getName());
|
| 497 |
+
}
|
| 498 |
+
}
|
| 499 |
+
|
| 500 |
+
private renderNamed(sym: MorphSymbol, expand: () => string): string {
|
| 501 |
+
const decl = sym.getDeclarations()[0];
|
| 502 |
+
const origin =
|
| 503 |
+
decl !== undefined
|
| 504 |
+
? repoRelative(decl.getSourceFile().getFilePath())
|
| 505 |
+
: '(unknown source)';
|
| 506 |
+
const name = sym.getName();
|
| 507 |
+
if (this.expandingNamed.includes(sym.compilerSymbol)) {
|
| 508 |
+
return `/* ${name} — recursive (${origin}) */ unknown`;
|
| 509 |
+
}
|
| 510 |
+
this.expandingNamed.push(sym.compilerSymbol);
|
| 511 |
+
try {
|
| 512 |
+
return `/* ${name} — ${origin} */ ${expand()}`;
|
| 513 |
+
} finally {
|
| 514 |
+
this.expandingNamed.pop();
|
| 515 |
+
}
|
| 516 |
+
}
|
| 517 |
+
|
| 518 |
+
private tryRenderAlias(
|
| 519 |
+
type: MorphType,
|
| 520 |
+
location: Node,
|
| 521 |
+
depth: number,
|
| 522 |
+
skipSymbol?: ts.Symbol,
|
| 523 |
+
): string | undefined {
|
| 524 |
+
const alias = type.getAliasSymbol();
|
| 525 |
+
if (alias === undefined || alias.compilerSymbol === skipSymbol) return undefined;
|
| 526 |
+
const kind = this.classify(alias);
|
| 527 |
+
if (kind === 'named') {
|
| 528 |
+
const decl = alias.getDeclarations().find((d) => Node.isTypeAliasDeclaration(d));
|
| 529 |
+
if (decl === undefined || !Node.isTypeAliasDeclaration(decl)) return undefined;
|
| 530 |
+
return this.renderNamed(alias, () =>
|
| 531 |
+
this.renderType(decl.getType(), decl, depth + 1, alias.compilerSymbol),
|
| 532 |
+
);
|
| 533 |
+
}
|
| 534 |
+
if (kind === 'ambient') {
|
| 535 |
+
const args = type.getAliasTypeArguments();
|
| 536 |
+
if (args.length === 0) return undefined;
|
| 537 |
+
this.noteExternal(alias);
|
| 538 |
+
const rendered = args.map((arg) => this.renderType(arg, location, depth + 1));
|
| 539 |
+
return `${alias.getName()}<${rendered.join(', ')}>`;
|
| 540 |
+
}
|
| 541 |
+
return undefined;
|
| 542 |
+
}
|
| 543 |
+
|
| 544 |
+
private renderObjectType(
|
| 545 |
+
type: MorphType,
|
| 546 |
+
location: Node,
|
| 547 |
+
depth: number,
|
| 548 |
+
skipSymbol?: ts.Symbol,
|
| 549 |
+
): string {
|
| 550 |
+
const alias = this.tryRenderAlias(type, location, depth, skipSymbol);
|
| 551 |
+
if (alias !== undefined) return alias;
|
| 552 |
+
const sym = type.getSymbol();
|
| 553 |
+
const typeArgs = type.getTypeArguments();
|
| 554 |
+
const anonymous = sym === undefined || /^__(type|object)$/.test(sym.getName());
|
| 555 |
+
if (!anonymous && sym.compilerSymbol !== skipSymbol) {
|
| 556 |
+
const kind = this.classify(sym);
|
| 557 |
+
if (kind === 'named' && typeArgs.length === 0) {
|
| 558 |
+
return this.renderNamed(sym, () => this.renderStructural(type, location, depth));
|
| 559 |
+
}
|
| 560 |
+
if (kind === 'ambient') {
|
| 561 |
+
this.noteExternal(sym);
|
| 562 |
+
const name = sym.getName();
|
| 563 |
+
if (typeArgs.length === 0) return name;
|
| 564 |
+
const args = typeArgs.map((arg) => this.renderType(arg, location, depth + 1));
|
| 565 |
+
return `${name}<${args.join(', ')}>`;
|
| 566 |
+
}
|
| 567 |
+
}
|
| 568 |
+
return this.renderStructural(type, location, depth);
|
| 569 |
+
}
|
| 570 |
+
|
| 571 |
+
private renderStructural(type: MorphType, location: Node, depth: number): string {
|
| 572 |
+
if (this.expanding.has(type.compilerType)) {
|
| 573 |
+
return this.fallback(type, location, 'cycle expanding');
|
| 574 |
+
}
|
| 575 |
+
this.expanding.add(type.compilerType);
|
| 576 |
+
try {
|
| 577 |
+
const callSignatures = type.getCallSignatures();
|
| 578 |
+
const props = type.getProperties().filter((prop) => this.isPublic(prop));
|
| 579 |
+
const stringIndex = type.getStringIndexType();
|
| 580 |
+
const numberIndex = type.getNumberIndexType();
|
| 581 |
+
if (
|
| 582 |
+
callSignatures.length > 0 &&
|
| 583 |
+
props.length === 0 &&
|
| 584 |
+
stringIndex === undefined &&
|
| 585 |
+
numberIndex === undefined
|
| 586 |
+
) {
|
| 587 |
+
if (callSignatures.length === 1 && callSignatures[0] !== undefined) {
|
| 588 |
+
return this.renderSignature(callSignatures[0], location, depth, 'arrow');
|
| 589 |
+
}
|
| 590 |
+
return callSignatures
|
| 591 |
+
.map((sig) => `(${this.renderSignature(sig, location, depth, 'arrow')})`)
|
| 592 |
+
.join(' & ');
|
| 593 |
+
}
|
| 594 |
+
const body = this.renderObjectBody(type, location, depth, callSignatures, props);
|
| 595 |
+
if (body.length === 0) return '{}';
|
| 596 |
+
return `{\n${body.join('\n')}\n}`;
|
| 597 |
+
} finally {
|
| 598 |
+
this.expanding.delete(type.compilerType);
|
| 599 |
+
}
|
| 600 |
+
}
|
| 601 |
+
|
| 602 |
+
private renderObjectBody(
|
| 603 |
+
type: MorphType,
|
| 604 |
+
location: Node,
|
| 605 |
+
depth: number,
|
| 606 |
+
callSignatures?: readonly Signature[],
|
| 607 |
+
props?: readonly MorphSymbol[],
|
| 608 |
+
): string[] {
|
| 609 |
+
const lines: string[] = [];
|
| 610 |
+
for (const sig of callSignatures ?? type.getCallSignatures()) {
|
| 611 |
+
lines.push(` ${this.renderSignature(sig, location, depth, 'call')};`);
|
| 612 |
+
}
|
| 613 |
+
for (const prop of props ?? type.getProperties().filter((p) => this.isPublic(p))) {
|
| 614 |
+
const decl = prop.getDeclarations()[0];
|
| 615 |
+
const at = decl ?? location;
|
| 616 |
+
const propType = prop.getTypeAtLocation(at);
|
| 617 |
+
const optional = (prop.getFlags() & ts.SymbolFlags.Optional) !== 0;
|
| 618 |
+
const rendered =
|
| 619 |
+
optional && propType.isUnion()
|
| 620 |
+
? this.renderUnionMembers(
|
| 621 |
+
propType.getUnionTypes().filter((member) => !member.isUndefined()),
|
| 622 |
+
at,
|
| 623 |
+
depth,
|
| 624 |
+
)
|
| 625 |
+
: this.renderType(propType, at, depth + 1);
|
| 626 |
+
const readonly =
|
| 627 |
+
decl !== undefined && Node.isPropertySignature(decl) && decl.isReadonly()
|
| 628 |
+
? 'readonly '
|
| 629 |
+
: '';
|
| 630 |
+
const propLines = rendered.split('\n');
|
| 631 |
+
propLines[propLines.length - 1] += ';';
|
| 632 |
+
lines.push(
|
| 633 |
+
` ${readonly}${tsFieldKey(stableSymbolKey(prop.getName()))}${optional ? '?' : ''}: ${propLines[0]}`,
|
| 634 |
+
...propLines.slice(1).map((line) => ` ${line}`),
|
| 635 |
+
);
|
| 636 |
+
}
|
| 637 |
+
const stringIndex = type.getStringIndexType();
|
| 638 |
+
if (stringIndex !== undefined) {
|
| 639 |
+
lines.push(` [key: string]: ${this.renderType(stringIndex, location, depth + 1)};`);
|
| 640 |
+
}
|
| 641 |
+
const numberIndex = type.getNumberIndexType();
|
| 642 |
+
if (numberIndex !== undefined) {
|
| 643 |
+
lines.push(` [key: number]: ${this.renderType(numberIndex, location, depth + 1)};`);
|
| 644 |
+
}
|
| 645 |
+
return lines;
|
| 646 |
+
}
|
| 647 |
+
|
| 648 |
+
private renderSignature(
|
| 649 |
+
sig: Signature,
|
| 650 |
+
location: Node,
|
| 651 |
+
depth: number,
|
| 652 |
+
style: 'arrow' | 'call',
|
| 653 |
+
): string {
|
| 654 |
+
const params: string[] = [];
|
| 655 |
+
for (const param of sig.getParameters()) {
|
| 656 |
+
if (param.getName() === 'this') continue;
|
| 657 |
+
const decl = param.getDeclarations()[0];
|
| 658 |
+
const at = decl ?? location;
|
| 659 |
+
const paramType = param.getTypeAtLocation(at);
|
| 660 |
+
let optional = (param.getFlags() & ts.SymbolFlags.Optional) !== 0;
|
| 661 |
+
let rest = false;
|
| 662 |
+
if (decl !== undefined && Node.isParameterDeclaration(decl)) {
|
| 663 |
+
optional = optional || decl.hasQuestionToken() || decl.getInitializer() !== undefined;
|
| 664 |
+
rest = decl.isRestParameter();
|
| 665 |
+
}
|
| 666 |
+
const rendered =
|
| 667 |
+
optional && paramType.isUnion()
|
| 668 |
+
? this.renderUnionMembers(
|
| 669 |
+
paramType.getUnionTypes().filter((member) => !member.isUndefined()),
|
| 670 |
+
at,
|
| 671 |
+
depth,
|
| 672 |
+
)
|
| 673 |
+
: this.renderType(paramType, at, depth + 1);
|
| 674 |
+
params.push(`${rest ? '...' : ''}${param.getName()}${optional ? '?' : ''}: ${rendered}`);
|
| 675 |
+
}
|
| 676 |
+
const returnType = this.renderType(sig.getReturnType(), location, depth + 1);
|
| 677 |
+
return style === 'arrow'
|
| 678 |
+
? `(${params.join(', ')}) => ${returnType}`
|
| 679 |
+
: `(${params.join(', ')}): ${returnType}`;
|
| 680 |
+
}
|
| 681 |
+
|
| 682 |
+
private isPublic(prop: MorphSymbol): boolean {
|
| 683 |
+
if (prop.getName().startsWith('#')) return false;
|
| 684 |
+
return !prop.getDeclarations().some((decl) => {
|
| 685 |
+
const modifiers = ts.canHaveModifiers(decl.compilerNode)
|
| 686 |
+
? ts.getModifiers(decl.compilerNode)
|
| 687 |
+
: undefined;
|
| 688 |
+
return (
|
| 689 |
+
modifiers !== undefined &&
|
| 690 |
+
modifiers.some(
|
| 691 |
+
(m) =>
|
| 692 |
+
m.kind === ts.SyntaxKind.PrivateKeyword || m.kind === ts.SyntaxKind.ProtectedKeyword,
|
| 693 |
+
)
|
| 694 |
+
);
|
| 695 |
+
});
|
| 696 |
+
}
|
| 697 |
+
}
|
| 698 |
+
|
| 699 |
+
function renderManifest(
|
| 700 |
+
model: StateManifestModel,
|
| 701 |
+
project: Project,
|
| 702 |
+
): { manifest: string; warnings: readonly string[] } {
|
| 703 |
+
const renderer = new TypeRenderer(project);
|
| 704 |
+
const byScope = new Map<ScopeDir, Registration[]>();
|
| 705 |
+
for (const scope of SCOPES) byScope.set(scope.dir, []);
|
| 706 |
+
for (const registration of model.registrations) byScope.get(registration.scope)?.push(registration);
|
| 707 |
+
for (const [dir, regs] of byScope) {
|
| 708 |
+
byScope.set(
|
| 709 |
+
dir,
|
| 710 |
+
regs.toSorted((a, b) => a.def.keyName.localeCompare(b.def.keyName)),
|
| 711 |
+
);
|
| 712 |
+
}
|
| 713 |
+
|
| 714 |
+
const sections: string[] = [];
|
| 715 |
+
for (const scope of SCOPES) {
|
| 716 |
+
const regs = byScope.get(scope.dir) ?? [];
|
| 717 |
+
const lines = [
|
| 718 |
+
`/** ${scope.label}-scope keys registered into I${scope.label}StateService. */`,
|
| 719 |
+
`export interface ${scope.interfaceName} {`,
|
| 720 |
+
];
|
| 721 |
+
const byFile = new Map<string, Registration[]>();
|
| 722 |
+
for (const r of regs) {
|
| 723 |
+
const list = byFile.get(r.def.file) ?? [];
|
| 724 |
+
list.push(r);
|
| 725 |
+
byFile.set(r.def.file, list);
|
| 726 |
+
}
|
| 727 |
+
for (const file of [...byFile.keys()].toSorted()) {
|
| 728 |
+
lines.push(` // ${srcRelative(file)}`);
|
| 729 |
+
for (const r of byFile.get(file) ?? []) {
|
| 730 |
+
if (r.def.replayable !== undefined) {
|
| 731 |
+
const meta = r.def.replayable;
|
| 732 |
+
const flags = [
|
| 733 |
+
meta.durable ? 'durable' : 'transient',
|
| 734 |
+
...(meta.undoable ? ['undoable'] : []),
|
| 735 |
+
];
|
| 736 |
+
lines.push(
|
| 737 |
+
` // replayable · ${flags.join(' · ')} — folds: ${meta.folds.length > 0 ? meta.folds.join(', ') : '(protocol only)'}`,
|
| 738 |
+
);
|
| 739 |
+
}
|
| 740 |
+
const rendered = renderer.renderKeyType(r.def).split('\n');
|
| 741 |
+
rendered[rendered.length - 1] += ';';
|
| 742 |
+
lines.push(` '${r.def.keyName}': ${rendered[0]}`, ...rendered.slice(1).map((l) => ` ${l}`));
|
| 743 |
+
}
|
| 744 |
+
}
|
| 745 |
+
lines.push('}');
|
| 746 |
+
lines.push('');
|
| 747 |
+
lines.push(`export type ${scope.keyUnionName} = keyof ${scope.interfaceName};`);
|
| 748 |
+
sections.push(lines.join('\n'));
|
| 749 |
+
}
|
| 750 |
+
|
| 751 |
+
const counts = SCOPES.map((s) => `${s.label}: ${byScope.get(s.dir)?.length ?? 0} keys`).join(
|
| 752 |
+
' · ',
|
| 753 |
+
);
|
| 754 |
+
const out: string[] = [
|
| 755 |
+
'// App, Workspace, Session & Agent State Manifest',
|
| 756 |
+
'//',
|
| 757 |
+
'// Generated by scripts/gen-state-manifest.mts — do not edit by hand.',
|
| 758 |
+
'// Regenerate with: pnpm --filter @moonshot-ai/agent-core-v2 gen:state-manifest',
|
| 759 |
+
'//',
|
| 760 |
+
'// Every state key registered into the App-scope IAppStateService, the',
|
| 761 |
+
'// Workspace-scope IWorkspaceStateService, the Session-scope',
|
| 762 |
+
'// ISessionStateService, or the Agent-scope IAgentStateService (see',
|
| 763 |
+
'// src/_base/state/stateRegistry.ts), collected statically from the',
|
| 764 |
+
'// `states.contributeState(...)` call sites and the replayable key chains — a',
|
| 765 |
+
'// `defineState(...).replayable(...)` key is contributed into the Agent-scope',
|
| 766 |
+
'// service by its owner service at construction, and',
|
| 767 |
+
'// carries a `// replayable · durable|transient · undoable? — folds: ...` line.',
|
| 768 |
+
'// Replayable values are excluded from snapshot()/inspect(). A key defined via',
|
| 769 |
+
'// defineState but never registered nor replayable does not appear here. Each entry shows the',
|
| 770 |
+
'// compile-time StateKey<T> value type fully expanded inline, so the manifest is',
|
| 771 |
+
'// self-contained (no imports, no helper declarations). A named type is marked',
|
| 772 |
+
'// at its expansion site with a `/* TypeName — source/file.ts */` comment; a',
|
| 773 |
+
'// `/* TypeName — recursive (...) */ unknown` marker stops a recursive expansion.',
|
| 774 |
+
'// Lib globals (Map/Set/Record/…) are referenced as-is. Generic instantiations',
|
| 775 |
+
'// expand structurally; classes render as their public instance shape. The',
|
| 776 |
+
'// defining source file heads each group.',
|
| 777 |
+
];
|
| 778 |
+
if (renderer.externals.size > 0) {
|
| 779 |
+
out.push(
|
| 780 |
+
'//',
|
| 781 |
+
`// External ambient types referenced but not expanded (from node_modules): ${[...renderer.externals].toSorted().join(', ')}`,
|
| 782 |
+
);
|
| 783 |
+
}
|
| 784 |
+
out.push(
|
| 785 |
+
'//',
|
| 786 |
+
'// snapshot() returns JSON-safe deep copies of these values: Maps become plain',
|
| 787 |
+
'// objects (or [key, value] entry arrays when a key is not string/number), Sets',
|
| 788 |
+
'// become arrays, bigints become strings, functions are dropped, circular',
|
| 789 |
+
"// references become '(circular)', and class instances collapse to a '(ClassName)'",
|
| 790 |
+
'// marker — the wire shape of an entry is the JSON projection of the type here.',
|
| 791 |
+
'//',
|
| 792 |
+
`// Index (${counts})`,
|
| 793 |
+
);
|
| 794 |
+
for (const scope of SCOPES) {
|
| 795 |
+
const regs = byScope.get(scope.dir) ?? [];
|
| 796 |
+
const width = Math.max(0, ...regs.map((r) => r.def.keyName.length));
|
| 797 |
+
out.push(`// ${scope.label}`);
|
| 798 |
+
for (const r of regs) {
|
| 799 |
+
out.push(`// ${r.def.keyName.padEnd(width)} ${srcRelative(r.def.file)}`);
|
| 800 |
+
}
|
| 801 |
+
}
|
| 802 |
+
out.push('');
|
| 803 |
+
out.push(...sections.flatMap((section) => [section, '']));
|
| 804 |
+
return { manifest: `${out.join('\n').trimEnd()}\n`, warnings: [...renderer.warnings] };
|
| 805 |
+
}
|
| 806 |
+
|
| 807 |
+
interface BuildResult {
|
| 808 |
+
readonly model: StateManifestModel;
|
| 809 |
+
readonly manifest: string;
|
| 810 |
+
readonly warnings: readonly string[];
|
| 811 |
+
}
|
| 812 |
+
|
| 813 |
+
function buildAll(): BuildResult {
|
| 814 |
+
const project = createProject();
|
| 815 |
+
const defs = collectKeyDefs(project);
|
| 816 |
+
const registrations = collectRegistrations(project, defs);
|
| 817 |
+
const registered = new Set(registrations.map((r) => r.def));
|
| 818 |
+
for (const def of defs.values()) {
|
| 819 |
+
if (def.replayable === undefined) continue;
|
| 820 |
+
if (!registered.has(def)) {
|
| 821 |
+
throw new Error(
|
| 822 |
+
`[gen-state-manifest] replayable state key '${def.keyName}' (${srcRelative(def.file)}) is ` +
|
| 823 |
+
'never contributed — its owner service must contributeState it into the Agent-scope state service.',
|
| 824 |
+
);
|
| 825 |
+
}
|
| 826 |
+
for (const registration of registrations) {
|
| 827 |
+
if (registration.def === def && registration.scope !== 'agent') {
|
| 828 |
+
throw new Error(
|
| 829 |
+
`[gen-state-manifest] replayable state key '${def.keyName}' (${srcRelative(def.file)}) is ` +
|
| 830 |
+
`contributed into the ${registration.scope} scope — replayable keys belong to the Agent scope.`,
|
| 831 |
+
);
|
| 832 |
+
}
|
| 833 |
+
}
|
| 834 |
+
}
|
| 835 |
+
const unregistered = [...defs.values()].filter((def) => !registered.has(def));
|
| 836 |
+
const model: StateManifestModel = { registrations, unregistered };
|
| 837 |
+
const { manifest, warnings } = renderManifest(model, project);
|
| 838 |
+
return { model, manifest, warnings };
|
| 839 |
+
}
|
| 840 |
+
|
| 841 |
+
export function buildStateManifest(): string {
|
| 842 |
+
return buildAll().manifest;
|
| 843 |
+
}
|
| 844 |
+
|
| 845 |
+
function main(): void {
|
| 846 |
+
const check = process.argv.includes('--check');
|
| 847 |
+
const { model, manifest, warnings } = buildAll();
|
| 848 |
+
for (const warning of warnings) {
|
| 849 |
+
console.warn(`[gen-state-manifest] warning: ${warning}`);
|
| 850 |
+
}
|
| 851 |
+
if (check) {
|
| 852 |
+
let current: string | undefined;
|
| 853 |
+
try {
|
| 854 |
+
current = readFileSync(MANIFEST_PATH, 'utf-8');
|
| 855 |
+
} catch {
|
| 856 |
+
current = undefined;
|
| 857 |
+
}
|
| 858 |
+
if (current !== manifest) {
|
| 859 |
+
console.error(
|
| 860 |
+
`[gen-state-manifest] ${relative(process.cwd(), MANIFEST_PATH)} is stale. ` +
|
| 861 |
+
'Regenerate with `pnpm --filter @moonshot-ai/agent-core-v2 gen:state-manifest`.',
|
| 862 |
+
);
|
| 863 |
+
process.exit(1);
|
| 864 |
+
}
|
| 865 |
+
console.log('[gen-state-manifest] up to date');
|
| 866 |
+
return;
|
| 867 |
+
}
|
| 868 |
+
writeFileSync(MANIFEST_PATH, manifest);
|
| 869 |
+
console.log(`[gen-state-manifest] wrote ${relative(process.cwd(), MANIFEST_PATH)}`);
|
| 870 |
+
if (model.unregistered.length > 0) {
|
| 871 |
+
console.log(
|
| 872 |
+
`[gen-state-manifest] note: ${model.unregistered.length} defineState key(s) never registered (excluded):`,
|
| 873 |
+
);
|
| 874 |
+
for (const def of model.unregistered) {
|
| 875 |
+
console.log(` - ${def.keyName} (${srcRelative(def.file)})`);
|
| 876 |
+
}
|
| 877 |
+
}
|
| 878 |
+
}
|
| 879 |
+
|
| 880 |
+
if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
| 881 |
+
main();
|
| 882 |
+
}
|
packages/agent-core-v2/scripts/gen-wire-manifest.mts
ADDED
|
@@ -0,0 +1,1039 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { existsSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
| 2 |
+
import { dirname, join, relative } from 'node:path';
|
| 3 |
+
import { pathToFileURL } from 'node:url';
|
| 4 |
+
|
| 5 |
+
import { EVENT2_REGISTRY } from '#/app/event/event2';
|
| 6 |
+
|
| 7 |
+
import {
|
| 8 |
+
asJsonSchema,
|
| 9 |
+
describeType,
|
| 10 |
+
isRecord,
|
| 11 |
+
resolveRef,
|
| 12 |
+
toJsonSchema,
|
| 13 |
+
truncate,
|
| 14 |
+
type JsonSchema,
|
| 15 |
+
} from './lib/jsonSchema.mts';
|
| 16 |
+
|
| 17 |
+
const PKG = join(import.meta.dirname, '..');
|
| 18 |
+
const SRC = join(PKG, 'src');
|
| 19 |
+
export const MANIFEST_PATH = join(PKG, 'docs', 'wire-manifest.d.ts');
|
| 20 |
+
|
| 21 |
+
function walk(dir: string, out: string[] = []): string[] {
|
| 22 |
+
for (const entry of readdirSync(dir)) {
|
| 23 |
+
const p = join(dir, entry);
|
| 24 |
+
if (statSync(p).isDirectory()) walk(p, out);
|
| 25 |
+
else if (entry.endsWith('.ts')) out.push(p);
|
| 26 |
+
}
|
| 27 |
+
return out;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
const TYPE_DECL_RE = /static\s+override\s+readonly\s+type\s*=\s*'([^']+)'/g;
|
| 31 |
+
const DURABLE_DECL_RE = /static\s+override\s+readonly\s+durable\s*=\s*true/;
|
| 32 |
+
const CLASS_DECL_RE = /class\s+(\w+)\s+extends\s+(?:AgentEvent2|Event2)/g;
|
| 33 |
+
|
| 34 |
+
function scanEventDeclarations(): {
|
| 35 |
+
owners: Map<string, string>;
|
| 36 |
+
importFiles: string[];
|
| 37 |
+
durableTypes: Set<string>;
|
| 38 |
+
classTypes: Map<string, string>;
|
| 39 |
+
} {
|
| 40 |
+
const owners = new Map<string, string>();
|
| 41 |
+
const importFiles: string[] = [];
|
| 42 |
+
const durableTypes = new Set<string>();
|
| 43 |
+
const classTypes = new Map<string, string>();
|
| 44 |
+
for (const file of walk(SRC)) {
|
| 45 |
+
const source = readFileSync(file, 'utf-8');
|
| 46 |
+
const matches = [...source.matchAll(TYPE_DECL_RE)];
|
| 47 |
+
const hasStates = source.includes('.replayable(');
|
| 48 |
+
if (matches.length > 0 || hasStates) importFiles.push(file);
|
| 49 |
+
for (const [i, match] of matches.entries()) {
|
| 50 |
+
const type = match[1];
|
| 51 |
+
if (type === undefined) continue;
|
| 52 |
+
owners.set(type, relative(PKG, file));
|
| 53 |
+
const windowEnd = i + 1 < matches.length ? matches[i + 1]!.index : source.length;
|
| 54 |
+
if (DURABLE_DECL_RE.test(source.slice(match.index, windowEnd))) durableTypes.add(type);
|
| 55 |
+
}
|
| 56 |
+
const classMatches = [...source.matchAll(CLASS_DECL_RE)];
|
| 57 |
+
for (const [i, match] of classMatches.entries()) {
|
| 58 |
+
const className = match[1];
|
| 59 |
+
if (className === undefined) continue;
|
| 60 |
+
const windowEnd = i + 1 < classMatches.length ? classMatches[i + 1]!.index : source.length;
|
| 61 |
+
const typeMatch = /static\s+override\s+readonly\s+type\s*=\s*'([^']+)'/.exec(
|
| 62 |
+
source.slice(match.index, windowEnd),
|
| 63 |
+
);
|
| 64 |
+
if (typeMatch?.[1] !== undefined) classTypes.set(className, typeMatch[1]);
|
| 65 |
+
}
|
| 66 |
+
}
|
| 67 |
+
return { owners, importFiles, durableTypes, classTypes };
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
function scanMigrationChain(): string {
|
| 71 |
+
const dir = join(SRC, 'wire', 'migration');
|
| 72 |
+
const pairs: { source: string; target: string }[] = [];
|
| 73 |
+
for (const entry of readdirSync(dir)) {
|
| 74 |
+
if (!/^v[\d.]+\.ts$/.test(entry)) continue;
|
| 75 |
+
const source = readFileSync(join(dir, entry), 'utf-8');
|
| 76 |
+
const sourceVersion = /sourceVersion:\s*'([^']+)'/.exec(source)?.[1];
|
| 77 |
+
const targetVersion = /targetVersion:\s*'([^']+)'/.exec(source)?.[1];
|
| 78 |
+
if (sourceVersion !== undefined && targetVersion !== undefined) {
|
| 79 |
+
pairs.push({ source: sourceVersion, target: targetVersion });
|
| 80 |
+
}
|
| 81 |
+
}
|
| 82 |
+
pairs.sort((a, b) => a.source.localeCompare(b.source, undefined, { numeric: true }));
|
| 83 |
+
const chain = pairs.flatMap((p, i) => (i === 0 ? [p.source, p.target] : [p.target]));
|
| 84 |
+
return chain.join(' -> ');
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
interface ReplayableStateScan {
|
| 88 |
+
readonly keyName: string;
|
| 89 |
+
readonly constName: string;
|
| 90 |
+
readonly undoable: boolean;
|
| 91 |
+
readonly blobs: boolean;
|
| 92 |
+
readonly foldClasses: string[];
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
const ON_FOLD_RE = /\.on\(\s*([A-Za-z_$][\w$]*)/g;
|
| 96 |
+
const KEY_ON_RE = /\b([A-Za-z_$][\w$]*)\.on\(\s*([A-Za-z_$][\w$]*)/g;
|
| 97 |
+
const PROTOCOL_EVENT_RE = /(?:appendMessage|applyCompaction|clear|undo):\s*([A-Za-z_$][\w$]*)/g;
|
| 98 |
+
|
| 99 |
+
function readCallArguments(text: string, parenIndex: number): string {
|
| 100 |
+
let depth = 0;
|
| 101 |
+
for (let i = parenIndex; i < text.length; i++) {
|
| 102 |
+
const ch = text[i];
|
| 103 |
+
if (ch === "'" || ch === '"' || ch === '`') {
|
| 104 |
+
const quote = ch;
|
| 105 |
+
i += 1;
|
| 106 |
+
while (i < text.length && text[i] !== quote) {
|
| 107 |
+
if (text[i] === '\\') i += 1;
|
| 108 |
+
i += 1;
|
| 109 |
+
}
|
| 110 |
+
continue;
|
| 111 |
+
}
|
| 112 |
+
if (ch === '(') depth += 1;
|
| 113 |
+
else if (ch === ')') {
|
| 114 |
+
depth -= 1;
|
| 115 |
+
if (depth === 0) return text.slice(parenIndex + 1, i);
|
| 116 |
+
}
|
| 117 |
+
}
|
| 118 |
+
return text.slice(parenIndex + 1);
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
function readChain(source: string, start: number): string {
|
| 122 |
+
let depth = 0;
|
| 123 |
+
const n = source.length;
|
| 124 |
+
for (let i = start; i < n; i++) {
|
| 125 |
+
const ch = source[i];
|
| 126 |
+
if (ch === "'" || ch === '"' || ch === '`') {
|
| 127 |
+
const quote = ch;
|
| 128 |
+
i += 1;
|
| 129 |
+
while (i < n && source[i] !== quote) {
|
| 130 |
+
if (source[i] === '\\') i += 1;
|
| 131 |
+
i += 1;
|
| 132 |
+
}
|
| 133 |
+
continue;
|
| 134 |
+
}
|
| 135 |
+
if (ch === '{' || ch === '(' || ch === '[') depth += 1;
|
| 136 |
+
else if (ch === '}' || ch === ')' || ch === ']') depth = Math.max(0, depth - 1);
|
| 137 |
+
else if (ch === ';' && depth === 0) return source.slice(start, i);
|
| 138 |
+
}
|
| 139 |
+
return source.slice(start);
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
function scanReplayableStates(): ReplayableStateScan[] {
|
| 143 |
+
const states: ReplayableStateScan[] = [];
|
| 144 |
+
const byConst = new Map<string, ReplayableStateScan>();
|
| 145 |
+
const constChainRe =
|
| 146 |
+
/(?:export\s+)?const\s+([A-Za-z_$][\w$]*)\s*=\s*defineState\(\s*'([^']+)'/g;
|
| 147 |
+
for (const file of walk(SRC)) {
|
| 148 |
+
const source = readFileSync(file, 'utf-8');
|
| 149 |
+
if (!source.includes('.replayable(') && !source.includes('.on(')) continue;
|
| 150 |
+
for (const match of source.matchAll(constChainRe)) {
|
| 151 |
+
const constName = match[1];
|
| 152 |
+
const keyName = match[2];
|
| 153 |
+
if (constName === undefined || keyName === undefined) continue;
|
| 154 |
+
const chain = readChain(source, source.indexOf('defineState', match.index));
|
| 155 |
+
const replayableIndex = chain.indexOf('.replayable(');
|
| 156 |
+
if (replayableIndex === -1) continue;
|
| 157 |
+
const replayableArgs = readCallArguments(chain, replayableIndex + '.replayable'.length);
|
| 158 |
+
const scan: ReplayableStateScan = {
|
| 159 |
+
keyName,
|
| 160 |
+
constName,
|
| 161 |
+
undoable: chain.includes('.undoable('),
|
| 162 |
+
blobs: /\bblobs\s*:/.test(replayableArgs),
|
| 163 |
+
foldClasses: [...chain.matchAll(ON_FOLD_RE)].map((m) => m[1]!),
|
| 164 |
+
};
|
| 165 |
+
states.push(scan);
|
| 166 |
+
byConst.set(constName, scan);
|
| 167 |
+
}
|
| 168 |
+
}
|
| 169 |
+
for (const file of walk(SRC)) {
|
| 170 |
+
const source = readFileSync(file, 'utf-8');
|
| 171 |
+
if (!source.includes('.on(')) continue;
|
| 172 |
+
for (const match of source.matchAll(KEY_ON_RE)) {
|
| 173 |
+
const scan = byConst.get(match[1]!);
|
| 174 |
+
const cls = match[2];
|
| 175 |
+
if (scan === undefined || cls === undefined) continue;
|
| 176 |
+
if (!scan.foldClasses.includes(cls)) scan.foldClasses.push(cls);
|
| 177 |
+
}
|
| 178 |
+
}
|
| 179 |
+
return states;
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
function scanUndoableProtocolTypes(classTypes: ReadonlyMap<string, string>): string[] {
|
| 183 |
+
for (const file of walk(SRC)) {
|
| 184 |
+
const source = readFileSync(file, 'utf-8');
|
| 185 |
+
const index = source.indexOf('registerUndoableProtocol(');
|
| 186 |
+
if (index === -1) continue;
|
| 187 |
+
const window = readChain(source, index);
|
| 188 |
+
const types: string[] = [];
|
| 189 |
+
for (const match of window.matchAll(PROTOCOL_EVENT_RE)) {
|
| 190 |
+
const cls = match[1]!;
|
| 191 |
+
const type = classTypes.get(cls);
|
| 192 |
+
if (type === undefined) {
|
| 193 |
+
throw new Error(
|
| 194 |
+
`[gen-wire-manifest] undoable protocol event class '${cls}' has no resolved type`,
|
| 195 |
+
);
|
| 196 |
+
}
|
| 197 |
+
types.push(type);
|
| 198 |
+
}
|
| 199 |
+
return types;
|
| 200 |
+
}
|
| 201 |
+
throw new Error('[gen-wire-manifest] registerUndoableProtocol call not found under src/');
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
type SketchDict = { [key: string]: Sketch };
|
| 205 |
+
type Sketch = string | SketchDict | [Sketch];
|
| 206 |
+
|
| 207 |
+
const TYPE_KEY = '_type';
|
| 208 |
+
const MORE_KEY = '…';
|
| 209 |
+
|
| 210 |
+
function stringifySketch(sketch: Sketch): string {
|
| 211 |
+
if (typeof sketch === 'string') return sketch;
|
| 212 |
+
if (Array.isArray(sketch)) {
|
| 213 |
+
const inner = stringifySketch(sketch[0]);
|
| 214 |
+
return inner.includes('|') ? `(${inner})[]` : `${inner}[]`;
|
| 215 |
+
}
|
| 216 |
+
return `{ ${Object.entries(sketch)
|
| 217 |
+
.map(([k, v]) => `${k}: ${stringifySketch(v)}`)
|
| 218 |
+
.join(', ')} }`;
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
function sketchFromJsonSchema(schema: unknown, root: JsonSchema, depth: number): Sketch {
|
| 222 |
+
const resolved = resolveRef(schema, root);
|
| 223 |
+
const s = asJsonSchema(resolved);
|
| 224 |
+
if (s !== undefined && depth < 4) {
|
| 225 |
+
if (isRecord(s.properties) && Object.keys(s.properties).length > 0) {
|
| 226 |
+
const required = new Set(Array.isArray(s.required) ? s.required : []);
|
| 227 |
+
const dict: SketchDict = {};
|
| 228 |
+
for (const [name, prop] of Object.entries(s.properties)) {
|
| 229 |
+
dict[required.has(name) ? name : `${name}?`] = sketchFromJsonSchema(prop, root, depth + 1);
|
| 230 |
+
}
|
| 231 |
+
return dict;
|
| 232 |
+
}
|
| 233 |
+
if (s.type === 'array' && s.items !== undefined) {
|
| 234 |
+
const inner = sketchFromJsonSchema(s.items, root, depth + 1);
|
| 235 |
+
if (typeof inner !== 'string') return [inner];
|
| 236 |
+
}
|
| 237 |
+
}
|
| 238 |
+
return describeType(resolved, tsQuote);
|
| 239 |
+
}
|
| 240 |
+
|
| 241 |
+
function buildPayloadSketch(
|
| 242 |
+
schema: unknown,
|
| 243 |
+
staticSketch?: string | Map<string, Sketch>,
|
| 244 |
+
): Sketch {
|
| 245 |
+
const jsonSchema = toJsonSchema(schema);
|
| 246 |
+
if (jsonSchema === undefined) {
|
| 247 |
+
if (typeof staticSketch === 'string') return staticSketch;
|
| 248 |
+
if (staticSketch !== undefined && staticSketch.size > 0) {
|
| 249 |
+
return Object.fromEntries(staticSketch);
|
| 250 |
+
}
|
| 251 |
+
return '(schema uses transforms; see the owner file)';
|
| 252 |
+
}
|
| 253 |
+
if (isRecord(jsonSchema.properties) && Object.keys(jsonSchema.properties).length > 0) {
|
| 254 |
+
const required = new Set(Array.isArray(jsonSchema.required) ? jsonSchema.required : []);
|
| 255 |
+
const dict: SketchDict = {};
|
| 256 |
+
for (const [name, prop] of Object.entries(jsonSchema.properties)) {
|
| 257 |
+
dict[required.has(name) ? name : `${name}?`] = sketchFromJsonSchema(prop, jsonSchema, 0);
|
| 258 |
+
}
|
| 259 |
+
return dict;
|
| 260 |
+
}
|
| 261 |
+
if (
|
| 262 |
+
jsonSchema.type === 'object' &&
|
| 263 |
+
(jsonSchema.additionalProperties === undefined || jsonSchema.additionalProperties === false)
|
| 264 |
+
) {
|
| 265 |
+
return {};
|
| 266 |
+
}
|
| 267 |
+
return describeType(jsonSchema, tsQuote);
|
| 268 |
+
}
|
| 269 |
+
|
| 270 |
+
function pascalCase(name: string): string {
|
| 271 |
+
return name
|
| 272 |
+
.split(/[^A-Za-z0-9]+/)
|
| 273 |
+
.filter(Boolean)
|
| 274 |
+
.map((part) => (part[0] ?? '').toUpperCase() + part.slice(1))
|
| 275 |
+
.join('');
|
| 276 |
+
}
|
| 277 |
+
|
| 278 |
+
function tsFieldKey(key: string): string {
|
| 279 |
+
return /^[$A-Z_a-z][$\w]*$/.test(key) ? key : JSON.stringify(key);
|
| 280 |
+
}
|
| 281 |
+
|
| 282 |
+
function sketchStringToTs(text: string): { type: string; doc?: string } {
|
| 283 |
+
let t = text.trim();
|
| 284 |
+
const docs: string[] = [];
|
| 285 |
+
const named = /^([A-Z][$\w]*) = ([\s\S]+)$/.exec(t);
|
| 286 |
+
if (named?.[1] !== undefined && named[2] !== undefined) {
|
| 287 |
+
docs.push(named[1]);
|
| 288 |
+
t = named[2].trim();
|
| 289 |
+
}
|
| 290 |
+
const spread = /^((?:\.\.\.[$\w]+(?: \+ )?)+) & ([\s\S]+)$/.exec(t);
|
| 291 |
+
if (spread?.[1] !== undefined && spread[2] !== undefined) {
|
| 292 |
+
docs.push(`shared base: ${spread[1]}`);
|
| 293 |
+
t = spread[2].trim();
|
| 294 |
+
}
|
| 295 |
+
t = t.replaceAll(/union on [$\w]+: /g, '');
|
| 296 |
+
t = t.replaceAll(/\brecord</g, 'Record<');
|
| 297 |
+
t = t.replaceAll(/\binteger\b/g, 'number');
|
| 298 |
+
return { type: t, doc: docs.length > 0 ? docs.join(' · ') : undefined };
|
| 299 |
+
}
|
| 300 |
+
|
| 301 |
+
function renderTsType(sketch: Sketch, indent: string): { doc?: string; lines: string[] } {
|
| 302 |
+
if (typeof sketch === 'string') {
|
| 303 |
+
const { type, doc } = sketchStringToTs(sketch);
|
| 304 |
+
return { doc, lines: [type] };
|
| 305 |
+
}
|
| 306 |
+
if (Array.isArray(sketch)) {
|
| 307 |
+
const inner = renderTsType(sketch[0], indent);
|
| 308 |
+
const lines = [...inner.lines];
|
| 309 |
+
lines[lines.length - 1] += '[]';
|
| 310 |
+
return { doc: inner.doc, lines };
|
| 311 |
+
}
|
| 312 |
+
const doc = typeof sketch[TYPE_KEY] === 'string' ? sketch[TYPE_KEY] : undefined;
|
| 313 |
+
const lines = ['{'];
|
| 314 |
+
emitTsDict(lines, sketch, indent + ' ');
|
| 315 |
+
lines.push(`${indent}}`);
|
| 316 |
+
return { doc, lines };
|
| 317 |
+
}
|
| 318 |
+
|
| 319 |
+
function emitTsDict(lines: string[], dict: SketchDict, indent: string): void {
|
| 320 |
+
for (const [key, sketch] of Object.entries(dict)) {
|
| 321 |
+
if (key === MORE_KEY) {
|
| 322 |
+
lines.push(`${indent}// …`);
|
| 323 |
+
continue;
|
| 324 |
+
}
|
| 325 |
+
if (key === TYPE_KEY) continue;
|
| 326 |
+
if (key.startsWith('...')) {
|
| 327 |
+
lines.push(`${indent}// spread: ${key}`);
|
| 328 |
+
continue;
|
| 329 |
+
}
|
| 330 |
+
const optional = key.endsWith('?');
|
| 331 |
+
const fieldKey = tsFieldKey(optional ? key.slice(0, -1) : key);
|
| 332 |
+
const { doc, lines: typeLines } = renderTsType(sketch, indent);
|
| 333 |
+
if (doc !== undefined) lines.push(`${indent}/** ${doc} */`);
|
| 334 |
+
lines.push(`${indent}${fieldKey}${optional ? '?' : ''}: ${typeLines[0]}${typeLines.length === 1 ? ';' : ''}`);
|
| 335 |
+
if (typeLines.length > 1) {
|
| 336 |
+
lines.push(...typeLines.slice(1, -1));
|
| 337 |
+
lines.push(`${typeLines[typeLines.length - 1]};`);
|
| 338 |
+
}
|
| 339 |
+
}
|
| 340 |
+
}
|
| 341 |
+
|
| 342 |
+
function renderPayloadDecl(
|
| 343 |
+
entry: { type: string },
|
| 344 |
+
owner: string | undefined,
|
| 345 |
+
states: string[],
|
| 346 |
+
flags: string[],
|
| 347 |
+
sketch: Sketch,
|
| 348 |
+
): string[] {
|
| 349 |
+
const name = `${pascalCase(entry.type)}Payload`;
|
| 350 |
+
const nameField = `_name: '${entry.type}';`;
|
| 351 |
+
const header = [
|
| 352 |
+
'/**',
|
| 353 |
+
` * states: ${states.length > 0 ? states.join(', ') : '(none)'}${flags.length > 0 ? ` · ${flags.join(' · ')}` : ''}`,
|
| 354 |
+
` * owner: ${owner ?? '(unresolved)'}`,
|
| 355 |
+
];
|
| 356 |
+
if (typeof sketch === 'string') {
|
| 357 |
+
const { type, doc } = sketchStringToTs(sketch);
|
| 358 |
+
if (type.startsWith('(')) {
|
| 359 |
+
header.push(` * ${type.slice(1, -1)}`);
|
| 360 |
+
header.push(' */');
|
| 361 |
+
return [...header, `interface ${name} {\n ${nameField}\n}`, ''];
|
| 362 |
+
}
|
| 363 |
+
if (doc !== undefined) header.push(` * ${doc}`);
|
| 364 |
+
header.push(' */');
|
| 365 |
+
return [...header, `type ${name} = { ${nameField} } & (${type});`, ''];
|
| 366 |
+
}
|
| 367 |
+
if (Array.isArray(sketch)) {
|
| 368 |
+
const inner = renderTsType(sketch[0], ' ');
|
| 369 |
+
const lines = [...inner.lines];
|
| 370 |
+
lines[lines.length - 1] += '[]';
|
| 371 |
+
header.push(' */');
|
| 372 |
+
if (lines.length === 1) {
|
| 373 |
+
return [...header, `type ${name} = { ${nameField} } & (${lines[0]});`, ''];
|
| 374 |
+
}
|
| 375 |
+
return [
|
| 376 |
+
...header,
|
| 377 |
+
`type ${name} = { ${nameField} } & (${lines[0]}`,
|
| 378 |
+
...lines.slice(1, -1),
|
| 379 |
+
`${lines[lines.length - 1]});`,
|
| 380 |
+
'',
|
| 381 |
+
];
|
| 382 |
+
}
|
| 383 |
+
const payloadType = typeof sketch[TYPE_KEY] === 'string' ? sketch[TYPE_KEY] : undefined;
|
| 384 |
+
if (payloadType !== undefined) header.push(` * payload type: ${payloadType}`);
|
| 385 |
+
header.push(' */');
|
| 386 |
+
const lines = [...header, `interface ${name} {`, ` ${nameField}`];
|
| 387 |
+
emitTsDict(lines, sketch, ' ');
|
| 388 |
+
lines.push('}', '');
|
| 389 |
+
return lines;
|
| 390 |
+
}
|
| 391 |
+
|
| 392 |
+
function matchDelimiter(source: string, start: number, open: string, close: string): number {
|
| 393 |
+
let depth = 0;
|
| 394 |
+
for (let i = start; i < source.length; i++) {
|
| 395 |
+
const ch = source[i];
|
| 396 |
+
if (ch === '/' && source[i + 1] === '/') {
|
| 397 |
+
i = source.indexOf('\n', i);
|
| 398 |
+
if (i === -1) return -1;
|
| 399 |
+
continue;
|
| 400 |
+
}
|
| 401 |
+
if (ch === '/' && source[i + 1] === '*') {
|
| 402 |
+
i = source.indexOf('*/', i);
|
| 403 |
+
if (i === -1) return -1;
|
| 404 |
+
i += 1;
|
| 405 |
+
continue;
|
| 406 |
+
}
|
| 407 |
+
if (ch === "'" || ch === '"' || ch === '`') {
|
| 408 |
+
const quote = ch;
|
| 409 |
+
i += 1;
|
| 410 |
+
while (i < source.length && source[i] !== quote) {
|
| 411 |
+
if (source[i] === '\\') i += 1;
|
| 412 |
+
i += 1;
|
| 413 |
+
}
|
| 414 |
+
continue;
|
| 415 |
+
}
|
| 416 |
+
if (ch === open) depth += 1;
|
| 417 |
+
else if (ch === close) {
|
| 418 |
+
depth -= 1;
|
| 419 |
+
if (depth === 0) return i;
|
| 420 |
+
}
|
| 421 |
+
}
|
| 422 |
+
return -1;
|
| 423 |
+
}
|
| 424 |
+
|
| 425 |
+
function splitTopLevel(body: string, separators: readonly string[] = [',']): string[] {
|
| 426 |
+
const parts: string[] = [];
|
| 427 |
+
let depth = 0;
|
| 428 |
+
let partStart = 0;
|
| 429 |
+
const n = body.length;
|
| 430 |
+
for (let i = 0; i < n; i++) {
|
| 431 |
+
const ch = body[i];
|
| 432 |
+
if (ch === "'" || ch === '"' || ch === '`') {
|
| 433 |
+
const quote = ch;
|
| 434 |
+
i += 1;
|
| 435 |
+
while (i < n && body[i] !== quote) {
|
| 436 |
+
if (body[i] === '\\') i += 1;
|
| 437 |
+
i += 1;
|
| 438 |
+
}
|
| 439 |
+
continue;
|
| 440 |
+
}
|
| 441 |
+
if (ch === '{' || ch === '(' || ch === '[' || ch === '<') depth += 1;
|
| 442 |
+
else if (ch === '}' || ch === ')' || ch === ']' || ch === '>') depth = Math.max(0, depth - 1);
|
| 443 |
+
else if (ch !== undefined && depth === 0 && separators.includes(ch)) {
|
| 444 |
+
parts.push(body.slice(partStart, i).trim());
|
| 445 |
+
partStart = i + 1;
|
| 446 |
+
}
|
| 447 |
+
}
|
| 448 |
+
parts.push(body.slice(partStart).trim());
|
| 449 |
+
return parts.filter((p) => p !== '');
|
| 450 |
+
}
|
| 451 |
+
|
| 452 |
+
function splitObjectFields(body: string): Map<string, string> {
|
| 453 |
+
const fields = new Map<string, string>();
|
| 454 |
+
for (const part of splitTopLevel(body)) {
|
| 455 |
+
const keyMatch = /^([$\w]+|'[^']+'|"[^"]+")\s*:/.exec(part);
|
| 456 |
+
if (keyMatch?.[1] !== undefined) {
|
| 457 |
+
const key = keyMatch[1].replace(/^['"]|['"]$/g, '');
|
| 458 |
+
fields.set(key, part.slice(keyMatch[0].length).trim());
|
| 459 |
+
} else if (part.startsWith('...')) {
|
| 460 |
+
fields.set(part, '');
|
| 461 |
+
}
|
| 462 |
+
}
|
| 463 |
+
return fields;
|
| 464 |
+
}
|
| 465 |
+
|
| 466 |
+
function objectBody(text: string, braceIndex: number): string | undefined {
|
| 467 |
+
const end = matchDelimiter(text, braceIndex, '{', '}');
|
| 468 |
+
return end === -1 ? undefined : text.slice(braceIndex + 1, end);
|
| 469 |
+
}
|
| 470 |
+
|
| 471 |
+
function readExpression(source: string, start: number): string {
|
| 472 |
+
let depth = 0;
|
| 473 |
+
const n = source.length;
|
| 474 |
+
for (let i = start; i < n; i++) {
|
| 475 |
+
const ch = source[i];
|
| 476 |
+
if (ch === "'" || ch === '"' || ch === '`') {
|
| 477 |
+
const quote = ch;
|
| 478 |
+
i += 1;
|
| 479 |
+
while (i < n && source[i] !== quote) {
|
| 480 |
+
if (source[i] === '\\') i += 1;
|
| 481 |
+
i += 1;
|
| 482 |
+
}
|
| 483 |
+
continue;
|
| 484 |
+
}
|
| 485 |
+
if (ch === '{' || ch === '(' || ch === '[' || ch === '<') depth += 1;
|
| 486 |
+
else if (ch === '}' || ch === ')' || ch === ']' || ch === '>') depth = Math.max(0, depth - 1);
|
| 487 |
+
else if (ch === ';' && depth === 0) return source.slice(start, i);
|
| 488 |
+
}
|
| 489 |
+
return source.slice(start);
|
| 490 |
+
}
|
| 491 |
+
|
| 492 |
+
function tsQuote(raw: string): string {
|
| 493 |
+
return raw.includes("'") ? JSON.stringify(raw) : `'${raw}'`;
|
| 494 |
+
}
|
| 495 |
+
|
| 496 |
+
function escapeRegExp(raw: string): string {
|
| 497 |
+
return raw.replaceAll(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
| 498 |
+
}
|
| 499 |
+
|
| 500 |
+
function resolveSchemaLiteral(expr: string, source: string, depth = 0): string | undefined {
|
| 501 |
+
if (depth > 2) return undefined;
|
| 502 |
+
const inline = /^z\.\w*[oO]bject\s*\(/.exec(expr);
|
| 503 |
+
if (inline !== null) {
|
| 504 |
+
const rest = expr.slice(inline[0].length).trimStart();
|
| 505 |
+
if (rest.startsWith('{')) return objectBody(rest, 0);
|
| 506 |
+
const shapeName = /^([$\w]+)/.exec(rest)?.[1];
|
| 507 |
+
if (shapeName !== undefined) {
|
| 508 |
+
const constRe = new RegExp(`const\\s+${shapeName}\\s*(?::[^=;]+)?=\\s*\\{`);
|
| 509 |
+
const m = constRe.exec(source);
|
| 510 |
+
if (m !== null) return objectBody(source, m.index + m[0].length - 1);
|
| 511 |
+
}
|
| 512 |
+
return undefined;
|
| 513 |
+
}
|
| 514 |
+
const ident = /^([$\w]+)$/.exec(expr.trim())?.[1];
|
| 515 |
+
if (ident !== undefined) {
|
| 516 |
+
const constRe = new RegExp(`const\\s+${ident}\\s*(?::[^=;]+)?=\\s*`);
|
| 517 |
+
const m = constRe.exec(source);
|
| 518 |
+
if (m !== null) {
|
| 519 |
+
const rhs = readExpression(source, m.index + m[0].length).trim();
|
| 520 |
+
return resolveSchemaLiteral(rhs, source, depth + 1);
|
| 521 |
+
}
|
| 522 |
+
}
|
| 523 |
+
return undefined;
|
| 524 |
+
}
|
| 525 |
+
|
| 526 |
+
interface Budget {
|
| 527 |
+
remaining: number;
|
| 528 |
+
}
|
| 529 |
+
|
| 530 |
+
function spend(budget: Budget): boolean {
|
| 531 |
+
if (budget.remaining <= 0) return false;
|
| 532 |
+
budget.remaining -= 1;
|
| 533 |
+
return true;
|
| 534 |
+
}
|
| 535 |
+
|
| 536 |
+
const TS_BUDGET = (): Budget => ({ remaining: 24 });
|
| 537 |
+
|
| 538 |
+
const _fileCache = new Map<string, string>();
|
| 539 |
+
|
| 540 |
+
function readCached(file: string): string {
|
| 541 |
+
let text = _fileCache.get(file);
|
| 542 |
+
if (text === undefined) {
|
| 543 |
+
text = readFileSync(file, 'utf-8');
|
| 544 |
+
_fileCache.set(file, text);
|
| 545 |
+
}
|
| 546 |
+
return text;
|
| 547 |
+
}
|
| 548 |
+
|
| 549 |
+
interface TsField {
|
| 550 |
+
readonly type: string;
|
| 551 |
+
readonly optional: boolean;
|
| 552 |
+
}
|
| 553 |
+
|
| 554 |
+
function splitTsTypeFields(body: string): Map<string, TsField> {
|
| 555 |
+
const fields = new Map<string, TsField>();
|
| 556 |
+
for (const part of splitTopLevel(body, [';', ','])) {
|
| 557 |
+
const m = /^(?:readonly\s+)?([$\w]+|'[^']+'|"[^"]+")\s*(\?)?\s*:\s*(.+)$/.exec(part);
|
| 558 |
+
if (m?.[1] !== undefined && m[3] !== undefined) {
|
| 559 |
+
fields.set(m[1].replace(/^['"]|['"]$/g, ''), {
|
| 560 |
+
type: m[3].trim(),
|
| 561 |
+
optional: m[2] !== undefined,
|
| 562 |
+
});
|
| 563 |
+
}
|
| 564 |
+
}
|
| 565 |
+
return fields;
|
| 566 |
+
}
|
| 567 |
+
|
| 568 |
+
const TS_PRIMITIVES = new Set(['string', 'number', 'boolean', 'unknown', 'any', 'null', 'undefined', 'void']);
|
| 569 |
+
|
| 570 |
+
function renderTsFields(
|
| 571 |
+
fields: Map<string, TsField>,
|
| 572 |
+
file: string,
|
| 573 |
+
budget: Budget,
|
| 574 |
+
charBudget: number,
|
| 575 |
+
depth: number,
|
| 576 |
+
): SketchDict {
|
| 577 |
+
const dict: SketchDict = {};
|
| 578 |
+
let count = 0;
|
| 579 |
+
for (const [name, f] of fields) {
|
| 580 |
+
if (count >= 8) {
|
| 581 |
+
dict[MORE_KEY] = '…';
|
| 582 |
+
break;
|
| 583 |
+
}
|
| 584 |
+
count += 1;
|
| 585 |
+
dict[`${name}${f.optional ? '?' : ''}`] = summarizeTsTypeExpr(
|
| 586 |
+
f.type,
|
| 587 |
+
file,
|
| 588 |
+
budget,
|
| 589 |
+
Math.max(120, Math.floor(charBudget / 2)),
|
| 590 |
+
depth + 1,
|
| 591 |
+
);
|
| 592 |
+
}
|
| 593 |
+
return dict;
|
| 594 |
+
}
|
| 595 |
+
|
| 596 |
+
function findTsTypeDef(name: string, file: string): string | undefined {
|
| 597 |
+
const source = readCached(file);
|
| 598 |
+
const typeRe = new RegExp(`(?:export\\s+)?type\\s+${name}(?:<[^>;=]*>)?\\s*=\\s*`);
|
| 599 |
+
const m = typeRe.exec(source);
|
| 600 |
+
if (m !== null) return readExpression(source, m.index + m[0].length).trim();
|
| 601 |
+
const ifaceRe = new RegExp(`(?:export\\s+)?interface\\s+${name}(?:<[^>]*>)?(?:\\s+extends[^{]+)?\\s*\\{`);
|
| 602 |
+
const im = ifaceRe.exec(source);
|
| 603 |
+
if (im !== null) {
|
| 604 |
+
const body = objectBody(source, im.index + im[0].length - 1);
|
| 605 |
+
if (body !== undefined) return `{ ${body} }`;
|
| 606 |
+
}
|
| 607 |
+
return undefined;
|
| 608 |
+
}
|
| 609 |
+
|
| 610 |
+
function findImportSource(file: string, name: string): string | undefined {
|
| 611 |
+
const source = readCached(file);
|
| 612 |
+
const re = /(?:import|export)\s+(?:type\s+)?\{([^}]+)\}\s*from\s*'([^']+)'/g;
|
| 613 |
+
for (const m of source.matchAll(re)) {
|
| 614 |
+
for (const part of m[1]!.split(',')) {
|
| 615 |
+
const named = /^(?:type\s+)?([\w$]+)(?:\s+as\s+([\w$]+))?$/.exec(part.trim());
|
| 616 |
+
if (named === null) continue;
|
| 617 |
+
if ((named[2] ?? named[1]) === name) return m[2];
|
| 618 |
+
}
|
| 619 |
+
}
|
| 620 |
+
return undefined;
|
| 621 |
+
}
|
| 622 |
+
|
| 623 |
+
function resolveModuleFile(fromFile: string, specifier: string): string | undefined {
|
| 624 |
+
let base: string;
|
| 625 |
+
if (specifier.startsWith('#/')) base = join(SRC, specifier.slice(2));
|
| 626 |
+
else if (specifier.startsWith('.')) base = join(dirname(fromFile), specifier);
|
| 627 |
+
else return undefined;
|
| 628 |
+
for (const candidate of [`${base}.ts`, join(base, 'index.ts')]) {
|
| 629 |
+
if (existsSync(candidate)) return candidate;
|
| 630 |
+
}
|
| 631 |
+
return undefined;
|
| 632 |
+
}
|
| 633 |
+
|
| 634 |
+
function summarizeTsUnion(
|
| 635 |
+
members: string[],
|
| 636 |
+
file: string,
|
| 637 |
+
budget: Budget,
|
| 638 |
+
charBudget: number,
|
| 639 |
+
depth: number,
|
| 640 |
+
): string {
|
| 641 |
+
const resolved = members.map((m) => {
|
| 642 |
+
const t = m.trim();
|
| 643 |
+
if (/^[$\w]+$/.test(t)) {
|
| 644 |
+
const def = findTsTypeDef(t, file);
|
| 645 |
+
if (def !== undefined) return def;
|
| 646 |
+
}
|
| 647 |
+
return t;
|
| 648 |
+
});
|
| 649 |
+
const bodies = resolved.map((m) => (m.trim().startsWith('{') ? objectBody(m.trim(), 0) : undefined));
|
| 650 |
+
if (bodies.length > 0 && bodies.every((b) => b !== undefined)) {
|
| 651 |
+
const fieldMaps = bodies.map((b) => splitTsTypeFields(b!));
|
| 652 |
+
for (const [name, info] of fieldMaps[0]!) {
|
| 653 |
+
if (
|
| 654 |
+
/^'[^']*'$/.test(info.type) &&
|
| 655 |
+
fieldMaps.every((fm) => /^'[^']*'$/.test(fm.get(name)?.type ?? ''))
|
| 656 |
+
) {
|
| 657 |
+
const values = fieldMaps.map((fm) => tsQuote(fm.get(name)!.type.slice(1, -1)));
|
| 658 |
+
return truncate(`union on ${name}: ${values.join(' | ')}`, charBudget);
|
| 659 |
+
}
|
| 660 |
+
}
|
| 661 |
+
return truncate(
|
| 662 |
+
fieldMaps
|
| 663 |
+
.map((fm) => stringifySketch(renderTsFields(fm, file, budget, charBudget, depth + 1)))
|
| 664 |
+
.join(' | '),
|
| 665 |
+
charBudget * 2,
|
| 666 |
+
);
|
| 667 |
+
}
|
| 668 |
+
return truncate(
|
| 669 |
+
members
|
| 670 |
+
.map((m) => stringifySketch(summarizeTsTypeExpr(m, file, budget, charBudget, depth + 1)))
|
| 671 |
+
.join(' | '),
|
| 672 |
+
charBudget * 2,
|
| 673 |
+
);
|
| 674 |
+
}
|
| 675 |
+
|
| 676 |
+
function summarizeTsTypeExpr(
|
| 677 |
+
rhs: string,
|
| 678 |
+
file: string,
|
| 679 |
+
budget: Budget,
|
| 680 |
+
charBudget = 480,
|
| 681 |
+
depth = 0,
|
| 682 |
+
): Sketch {
|
| 683 |
+
let text = rhs.replaceAll(/\s+/g, ' ').trim();
|
| 684 |
+
if (text.startsWith('readonly ')) text = text.slice('readonly '.length).trim();
|
| 685 |
+
const literal = /^'([^']*)'$/.exec(text);
|
| 686 |
+
if (literal?.[1] !== undefined) return tsQuote(literal[1]);
|
| 687 |
+
if (TS_PRIMITIVES.has(text)) return text;
|
| 688 |
+
if (text.endsWith('[]')) {
|
| 689 |
+
const inner = summarizeTsTypeExpr(text.slice(0, -2), file, budget, charBudget, depth + 1);
|
| 690 |
+
if (typeof inner !== 'string') return [inner];
|
| 691 |
+
return inner.includes('|') ? `(${inner})[]` : `${inner}[]`;
|
| 692 |
+
}
|
| 693 |
+
const members = splitTopLevel(text, ['|']);
|
| 694 |
+
if (members.length > 1) {
|
| 695 |
+
return spend(budget)
|
| 696 |
+
? summarizeTsUnion(members, file, budget, charBudget, depth)
|
| 697 |
+
: truncate(text, 80);
|
| 698 |
+
}
|
| 699 |
+
const intersections = splitTopLevel(text, ['&']);
|
| 700 |
+
if (intersections.length > 1) {
|
| 701 |
+
if (!spend(budget)) return truncate(text, 80);
|
| 702 |
+
const sides = intersections.map((m) => summarizeTsTypeExpr(m, file, budget, charBudget, depth + 1));
|
| 703 |
+
if (sides.every((side) => typeof side !== 'string' && !Array.isArray(side))) {
|
| 704 |
+
return Object.assign({}, ...sides) as SketchDict;
|
| 705 |
+
}
|
| 706 |
+
return truncate(sides.map(stringifySketch).join(' & '), charBudget * 2);
|
| 707 |
+
}
|
| 708 |
+
if (text.startsWith('{')) {
|
| 709 |
+
if (!spend(budget) || depth >= 4) return 'object';
|
| 710 |
+
const body = objectBody(text, 0);
|
| 711 |
+
if (body !== undefined) {
|
| 712 |
+
return renderTsFields(splitTsTypeFields(body), file, budget, charBudget, depth);
|
| 713 |
+
}
|
| 714 |
+
}
|
| 715 |
+
if (/^[$\w]+$/.test(text)) {
|
| 716 |
+
const summary = summarizeTsType(text, file, budget);
|
| 717 |
+
if (summary !== undefined) return summary;
|
| 718 |
+
}
|
| 719 |
+
return truncate(text, 80);
|
| 720 |
+
}
|
| 721 |
+
|
| 722 |
+
function summarizeTsType(name: string, fromFile: string, budget: Budget): Sketch | undefined {
|
| 723 |
+
if (!spend(budget)) return undefined;
|
| 724 |
+
const def = findTsTypeDef(name, fromFile);
|
| 725 |
+
if (def !== undefined) return summarizeTsTypeExpr(def, fromFile, budget);
|
| 726 |
+
const specifier = findImportSource(fromFile, name);
|
| 727 |
+
if (specifier !== undefined) {
|
| 728 |
+
const target = resolveModuleFile(fromFile, specifier);
|
| 729 |
+
if (target !== undefined) return summarizeTsType(name, target, budget);
|
| 730 |
+
}
|
| 731 |
+
for (const m of readCached(fromFile).matchAll(/export\s+\*\s+from\s*'([^']+)'/g)) {
|
| 732 |
+
const target = m[1] === undefined ? undefined : resolveModuleFile(fromFile, m[1]);
|
| 733 |
+
if (target === undefined) continue;
|
| 734 |
+
const summary = summarizeTsType(name, target, budget);
|
| 735 |
+
if (summary !== undefined) return summary;
|
| 736 |
+
}
|
| 737 |
+
return undefined;
|
| 738 |
+
}
|
| 739 |
+
|
| 740 |
+
function friendlyZodExpr(expr: string, ownerFile: string, depth = 0): Sketch {
|
| 741 |
+
let text = expr.replaceAll(/\s+/g, ' ').trim();
|
| 742 |
+
let stripped = true;
|
| 743 |
+
while (stripped) {
|
| 744 |
+
stripped = false;
|
| 745 |
+
for (const suffix of ['.optional()', '.nullable()', '.nullish()', '.readonly()']) {
|
| 746 |
+
if (text.endsWith(suffix)) {
|
| 747 |
+
text = text.slice(0, -suffix.length).trim();
|
| 748 |
+
stripped = true;
|
| 749 |
+
}
|
| 750 |
+
}
|
| 751 |
+
}
|
| 752 |
+
const stringLiteral = /^'([^']*)'$/.exec(text);
|
| 753 |
+
if (stringLiteral?.[1] !== undefined) return tsQuote(stringLiteral[1]);
|
| 754 |
+
const custom = /^z\.custom<(.+)>\(\)$/.exec(text);
|
| 755 |
+
if (custom?.[1] !== undefined) {
|
| 756 |
+
const typeName = custom[1].trim();
|
| 757 |
+
if (depth > 1) return typeName;
|
| 758 |
+
const summary = summarizeTsType(typeName, ownerFile, TS_BUDGET());
|
| 759 |
+
if (summary === undefined) return typeName;
|
| 760 |
+
if (typeof summary !== 'string' && !Array.isArray(summary)) {
|
| 761 |
+
return { [TYPE_KEY]: typeName, ...summary };
|
| 762 |
+
}
|
| 763 |
+
return truncate(`${typeName} = ${stringifySketch(summary)}`, 1024);
|
| 764 |
+
}
|
| 765 |
+
if (/^z\.string\(\)$/.test(text)) return 'string';
|
| 766 |
+
if (/^z\.number\(\)$/.test(text)) return 'number';
|
| 767 |
+
if (/^z\.boolean\(\)$/.test(text)) return 'boolean';
|
| 768 |
+
if (/^z\.(?:int|integer)\(\)$/.test(text)) return 'integer';
|
| 769 |
+
const array = /^z\.array\((.+)\)$/.exec(text);
|
| 770 |
+
if (array?.[1] !== undefined) {
|
| 771 |
+
const inner = friendlyZodExpr(array[1], ownerFile, depth + 1);
|
| 772 |
+
if (typeof inner !== 'string') return [inner];
|
| 773 |
+
return `${inner}[]`;
|
| 774 |
+
}
|
| 775 |
+
const literal = /^z\.literal\((.+)\)$/.exec(text);
|
| 776 |
+
if (literal?.[1] !== undefined) return friendlyZodExpr(literal[1], ownerFile, depth + 1);
|
| 777 |
+
const union = /^z\.union\((.+)\)$/.exec(text);
|
| 778 |
+
if (union?.[1] !== undefined) return friendlyZodUnion(union[1], ownerFile, depth);
|
| 779 |
+
const enumMatch = /^z\.enum\((.+)\)$/.exec(text);
|
| 780 |
+
if (enumMatch?.[1] !== undefined) {
|
| 781 |
+
const body = enumMatch[1].trim().replace(/^\[/, '').replace(/\]$/, '');
|
| 782 |
+
return splitTopLevel(body)
|
| 783 |
+
.map((member) => stringifySketch(friendlyZodExpr(member, ownerFile, depth + 1)))
|
| 784 |
+
.join(' | ');
|
| 785 |
+
}
|
| 786 |
+
const record = /^z\.record\((.+)\)$/.exec(text);
|
| 787 |
+
if (record?.[1] !== undefined) {
|
| 788 |
+
const parts = splitTopLevel(record[1]);
|
| 789 |
+
if (parts.length === 2) {
|
| 790 |
+
return `record<string, ${stringifySketch(friendlyZodExpr(parts[1]!, ownerFile, depth + 1))}>`;
|
| 791 |
+
}
|
| 792 |
+
}
|
| 793 |
+
if (/^z\.\w*[oO]bject\(/.test(text)) {
|
| 794 |
+
if (depth >= 3) return 'object';
|
| 795 |
+
const body = resolveSchemaLiteral(text, readCached(ownerFile));
|
| 796 |
+
if (body === undefined) return 'object';
|
| 797 |
+
const dict: SketchDict = {};
|
| 798 |
+
for (const [key, fieldExpr] of splitObjectFields(body)) {
|
| 799 |
+
if (fieldExpr === '') {
|
| 800 |
+
dict[key] = '(spread)';
|
| 801 |
+
continue;
|
| 802 |
+
}
|
| 803 |
+
const optional = /\.(?:optional|nullish)\(\)$/.test(fieldExpr);
|
| 804 |
+
dict[`${key}${optional ? '?' : ''}`] = friendlyZodExpr(fieldExpr, ownerFile, depth + 1);
|
| 805 |
+
}
|
| 806 |
+
return dict;
|
| 807 |
+
}
|
| 808 |
+
const ident = /^([$\w]+)$/.exec(text)?.[1];
|
| 809 |
+
if (ident !== undefined && depth < 4) {
|
| 810 |
+
const source = readCached(ownerFile);
|
| 811 |
+
const constRe = new RegExp(`const\\s+${ident}\\s*(?::[^=;]+)?=\\s*`);
|
| 812 |
+
const m = constRe.exec(source);
|
| 813 |
+
if (m !== null) {
|
| 814 |
+
const rhs = readExpression(source, m.index + m[0].length).trim();
|
| 815 |
+
return friendlyZodExpr(rhs, ownerFile, depth + 1);
|
| 816 |
+
}
|
| 817 |
+
if (depth <= 1) {
|
| 818 |
+
const summary = summarizeTsType(ident, ownerFile, TS_BUDGET());
|
| 819 |
+
if (summary !== undefined) {
|
| 820 |
+
if (typeof summary !== 'string' && !Array.isArray(summary)) {
|
| 821 |
+
return { [TYPE_KEY]: ident, ...summary };
|
| 822 |
+
}
|
| 823 |
+
return truncate(`${ident} = ${stringifySketch(summary)}`, 320);
|
| 824 |
+
}
|
| 825 |
+
}
|
| 826 |
+
}
|
| 827 |
+
return truncate(text, 80);
|
| 828 |
+
}
|
| 829 |
+
|
| 830 |
+
function friendlyZodUnion(body: string, ownerFile: string, depth: number): string {
|
| 831 |
+
const members = splitTopLevel(body.trim().replace(/^\[/, '').replace(/\]$/, ''));
|
| 832 |
+
const source = readCached(ownerFile);
|
| 833 |
+
const bodies = members.map((m) => resolveSchemaLiteral(m, source));
|
| 834 |
+
if (members.length > 0 && bodies.every((b) => b !== undefined)) {
|
| 835 |
+
const fieldMaps = bodies.map((b) => splitObjectFields(b));
|
| 836 |
+
const spreadSets = fieldMaps.map((fm) => [...fm.keys()].filter((k) => fm.get(k) === ''));
|
| 837 |
+
const commonSpreads = (spreadSets[0] ?? []).filter((s) =>
|
| 838 |
+
spreadSets.every((set) => set.includes(s)),
|
| 839 |
+
);
|
| 840 |
+
const sketches = fieldMaps.map((fm) => {
|
| 841 |
+
const dict: SketchDict = {};
|
| 842 |
+
for (const [key, expr] of fm) {
|
| 843 |
+
if (expr === '') continue;
|
| 844 |
+
const optional = /\.(?:optional|nullish)\(\)$/.test(expr);
|
| 845 |
+
dict[`${key}${optional ? '?' : ''}`] = friendlyZodExpr(expr, ownerFile, depth + 1);
|
| 846 |
+
}
|
| 847 |
+
return stringifySketch(dict);
|
| 848 |
+
});
|
| 849 |
+
const prefix = commonSpreads.length > 0 ? `${commonSpreads.join(' + ')} & ` : '';
|
| 850 |
+
return truncate(`${prefix}${sketches.join(' | ')}`, 320);
|
| 851 |
+
}
|
| 852 |
+
return truncate(
|
| 853 |
+
members.map((m) => stringifySketch(friendlyZodExpr(m, ownerFile, depth + 1))).join(' | '),
|
| 854 |
+
320,
|
| 855 |
+
);
|
| 856 |
+
}
|
| 857 |
+
|
| 858 |
+
function sketchPayloadFromSource(
|
| 859 |
+
ownerFile: string,
|
| 860 |
+
type: string,
|
| 861 |
+
): string | Map<string, Sketch> | undefined {
|
| 862 |
+
const absFile = join(PKG, ownerFile);
|
| 863 |
+
const source = readCached(absFile);
|
| 864 |
+
const typeRe = new RegExp(
|
| 865 |
+
`static\\s+override\\s+readonly\\s+type\\s*=\\s*'${escapeRegExp(type)}'`,
|
| 866 |
+
);
|
| 867 |
+
const typeMatch = typeRe.exec(source);
|
| 868 |
+
if (typeMatch === null) return undefined;
|
| 869 |
+
const rest = source.slice(typeMatch.index + typeMatch[0].length);
|
| 870 |
+
const nextType = /static\s+override\s+readonly\s+type\s*=/.exec(rest);
|
| 871 |
+
const classWindow = nextType === null ? rest : rest.slice(0, nextType.index);
|
| 872 |
+
const schemaMatch = /static\s+override\s+readonly\s+schema\s*=\s*/.exec(classWindow);
|
| 873 |
+
if (schemaMatch === null) return undefined;
|
| 874 |
+
const schemaExpr = readExpression(
|
| 875 |
+
classWindow,
|
| 876 |
+
schemaMatch.index + schemaMatch[0].length,
|
| 877 |
+
).trim();
|
| 878 |
+
if (schemaExpr === '') return undefined;
|
| 879 |
+
const literal = resolveSchemaLiteral(schemaExpr, source);
|
| 880 |
+
if (literal === undefined) {
|
| 881 |
+
const sketch = friendlyZodExpr(schemaExpr, absFile);
|
| 882 |
+
if (typeof sketch === 'string') return sketch;
|
| 883 |
+
if (!Array.isArray(sketch)) return new Map(Object.entries(sketch));
|
| 884 |
+
return stringifySketch(sketch);
|
| 885 |
+
}
|
| 886 |
+
const sketch = new Map<string, Sketch>();
|
| 887 |
+
for (const [key, expr] of splitObjectFields(literal)) {
|
| 888 |
+
if (expr === '') {
|
| 889 |
+
sketch.set(key, '(spread)');
|
| 890 |
+
continue;
|
| 891 |
+
}
|
| 892 |
+
const optional = /\.(?:optional|nullish)\(\)$/.test(expr);
|
| 893 |
+
sketch.set(`${key}${optional ? '?' : ''}`, friendlyZodExpr(expr, absFile));
|
| 894 |
+
}
|
| 895 |
+
return sketch;
|
| 896 |
+
}
|
| 897 |
+
|
| 898 |
+
export async function buildWireManifest(): Promise<string> {
|
| 899 |
+
const { owners, importFiles, durableTypes, classTypes } = scanEventDeclarations();
|
| 900 |
+
await import('../src/index.ts');
|
| 901 |
+
for (const file of importFiles) {
|
| 902 |
+
await import(relative(join(PKG, 'scripts'), file));
|
| 903 |
+
}
|
| 904 |
+
const { WIRE_PROTOCOL_VERSION } = (await import('#/wire/migration/migration')) as {
|
| 905 |
+
WIRE_PROTOCOL_VERSION: string;
|
| 906 |
+
};
|
| 907 |
+
|
| 908 |
+
const entries = [...EVENT2_REGISTRY.values()].toSorted((a, b) => a.type.localeCompare(b.type));
|
| 909 |
+
const migrationChain = scanMigrationChain();
|
| 910 |
+
|
| 911 |
+
const folding = new Map<string, { states: string[]; blobs: string[] }>();
|
| 912 |
+
const protocolTypes = scanUndoableProtocolTypes(classTypes);
|
| 913 |
+
for (const state of scanReplayableStates()) {
|
| 914 |
+
const eventTypes = new Set<string>();
|
| 915 |
+
for (const cls of state.foldClasses) {
|
| 916 |
+
const type = classTypes.get(cls);
|
| 917 |
+
if (type === undefined) {
|
| 918 |
+
throw new Error(
|
| 919 |
+
`[gen-wire-manifest] state '${state.keyName}' folds unresolved event class '${cls}'`,
|
| 920 |
+
);
|
| 921 |
+
}
|
| 922 |
+
eventTypes.add(type);
|
| 923 |
+
}
|
| 924 |
+
if (state.undoable) {
|
| 925 |
+
for (const type of protocolTypes) eventTypes.add(type);
|
| 926 |
+
}
|
| 927 |
+
for (const type of eventTypes) {
|
| 928 |
+
let info = folding.get(type);
|
| 929 |
+
if (info === undefined) {
|
| 930 |
+
info = { states: [], blobs: [] };
|
| 931 |
+
folding.set(type, info);
|
| 932 |
+
}
|
| 933 |
+
info.states.push(state.keyName);
|
| 934 |
+
if (state.blobs) info.blobs.push(state.keyName);
|
| 935 |
+
}
|
| 936 |
+
}
|
| 937 |
+
for (const info of folding.values()) {
|
| 938 |
+
info.states.sort();
|
| 939 |
+
info.blobs.sort();
|
| 940 |
+
}
|
| 941 |
+
|
| 942 |
+
const unregistered = [...durableTypes].filter((type) => !EVENT2_REGISTRY.has(type));
|
| 943 |
+
if (unregistered.length > 0) {
|
| 944 |
+
console.error(
|
| 945 |
+
`[gen-wire-manifest] declared durable but never registered (no fold, not in EVENT2_REGISTRY): ${unregistered.toSorted().join(', ')}`,
|
| 946 |
+
);
|
| 947 |
+
}
|
| 948 |
+
|
| 949 |
+
const out: string[] = [
|
| 950 |
+
'// Wire Protocol Manifest',
|
| 951 |
+
'//',
|
| 952 |
+
'// Generated by scripts/gen-wire-manifest.mts — do not edit by hand.',
|
| 953 |
+
'// Regenerate with: pnpm --filter @moonshot-ai/agent-core-v2 gen:wire-manifest',
|
| 954 |
+
'//',
|
| 955 |
+
`// protocol_version: "${WIRE_PROTOCOL_VERSION}" (migrations: ${migrationChain})`,
|
| 956 |
+
'//',
|
| 957 |
+
'// One declaration per durable record type — an Event2 subclass declaring',
|
| 958 |
+
'// `static type` + `static durable = true` + `static schema` — drained from the',
|
| 959 |
+
'// runtime EVENT2_REGISTRY ("import = register"). Every payload declaration',
|
| 960 |
+
'// carries its record type in a `_name` field. Payload sketches use TypeScript',
|
| 961 |
+
'// type syntax; when a named type is expanded inline, its name appears as a doc',
|
| 962 |
+
'// comment (`/** ContextMessage */`). Bare type names (ContentPart,',
|
| 963 |
+
'// ContextMessage, …) refer to the real types in src/ — they are intentionally',
|
| 964 |
+
'// not resolved here. `// …` marks a capped field list. On disk (wire.jsonl)',
|
| 965 |
+
'// the journal opens with a metadata line {"type": "metadata",',
|
| 966 |
+
'// "protocol_version", "created_at"}; each record is {"type", ...payload,',
|
| 967 |
+
'// "time"} — object payloads spread at the top level.',
|
| 968 |
+
'//',
|
| 969 |
+
'// Every listed type is durable by construction — transient Event2 classes',
|
| 970 |
+
'// never enter EVENT2_REGISTRY, so there is no persisted flag. Declaration',
|
| 971 |
+
'// header lines: states (every state folding this record type on dispatch and',
|
| 972 |
+
'// replay; any state beyond the first is what the retired format listed as',
|
| 973 |
+
'// cross-reducers), blobs (the folding states whose blob codec offloads inline',
|
| 974 |
+
'// media to blob storage), owner (the source file declaring the class).',
|
| 975 |
+
'',
|
| 976 |
+
`// Index (${entries.length} record types)`,
|
| 977 |
+
];
|
| 978 |
+
const width = Math.max(...entries.map((e) => e.type.length));
|
| 979 |
+
const statesWidth = Math.max(
|
| 980 |
+
...entries.map((e) => (folding.get(e.type)?.states.join(', ') ?? '(none)').length),
|
| 981 |
+
);
|
| 982 |
+
for (const entry of entries) {
|
| 983 |
+
const states = folding.get(entry.type)?.states.join(', ') ?? '(none)';
|
| 984 |
+
out.push(
|
| 985 |
+
`// ${entry.type.padEnd(width)} ${states.padEnd(statesWidth)} ${owners.get(entry.type) ?? '(unresolved)'}`,
|
| 986 |
+
);
|
| 987 |
+
}
|
| 988 |
+
out.push('');
|
| 989 |
+
const declNames: [string, string][] = [];
|
| 990 |
+
for (const entry of entries) {
|
| 991 |
+
const info = folding.get(entry.type);
|
| 992 |
+
const states = info?.states ?? [];
|
| 993 |
+
const flags: string[] = [];
|
| 994 |
+
if (info !== undefined && info.blobs.length > 0) flags.push(`blobs: ${info.blobs.join(', ')}`);
|
| 995 |
+
const owner = owners.get(entry.type);
|
| 996 |
+
const staticSketch =
|
| 997 |
+
owner === undefined ? undefined : sketchPayloadFromSource(owner, entry.type);
|
| 998 |
+
const sketch = buildPayloadSketch(entry.schema as unknown, staticSketch);
|
| 999 |
+
out.push(...renderPayloadDecl(entry, owner, states, flags, sketch));
|
| 1000 |
+
declNames.push([entry.type, `${pascalCase(entry.type)}Payload`]);
|
| 1001 |
+
}
|
| 1002 |
+
|
| 1003 |
+
out.push('/** Record type → payload sketch. */');
|
| 1004 |
+
out.push('interface WirePayloadMap {');
|
| 1005 |
+
for (const [type, declName] of declNames) {
|
| 1006 |
+
out.push(` ${JSON.stringify(type)}: ${declName};`);
|
| 1007 |
+
}
|
| 1008 |
+
out.push('}');
|
| 1009 |
+
out.push('');
|
| 1010 |
+
return out.join('\n');
|
| 1011 |
+
}
|
| 1012 |
+
|
| 1013 |
+
async function main(): Promise<void> {
|
| 1014 |
+
const check = process.argv.includes('--check');
|
| 1015 |
+
const manifest = await buildWireManifest();
|
| 1016 |
+
if (check) {
|
| 1017 |
+
let current: string | undefined;
|
| 1018 |
+
try {
|
| 1019 |
+
current = readFileSync(MANIFEST_PATH, 'utf-8');
|
| 1020 |
+
} catch {
|
| 1021 |
+
current = undefined;
|
| 1022 |
+
}
|
| 1023 |
+
if (current !== manifest) {
|
| 1024 |
+
console.error(
|
| 1025 |
+
`[gen-wire-manifest] ${relative(process.cwd(), MANIFEST_PATH)} is stale. ` +
|
| 1026 |
+
'Regenerate with `pnpm --filter @moonshot-ai/agent-core-v2 gen:wire-manifest`.',
|
| 1027 |
+
);
|
| 1028 |
+
process.exit(1);
|
| 1029 |
+
}
|
| 1030 |
+
console.log('[gen-wire-manifest] up to date');
|
| 1031 |
+
return;
|
| 1032 |
+
}
|
| 1033 |
+
writeFileSync(MANIFEST_PATH, manifest);
|
| 1034 |
+
console.log(`[gen-wire-manifest] wrote ${relative(process.cwd(), MANIFEST_PATH)}`);
|
| 1035 |
+
}
|
| 1036 |
+
|
| 1037 |
+
if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
| 1038 |
+
await main();
|
| 1039 |
+
}
|
packages/agent-core-v2/scripts/generate-webp-dec-wasm.mjs
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { createRequire } from 'node:module';
|
| 2 |
+
import { readFileSync, writeFileSync } from 'node:fs';
|
| 3 |
+
import { resolve } from 'node:path';
|
| 4 |
+
|
| 5 |
+
const packageRoot = resolve(import.meta.dirname, '..');
|
| 6 |
+
const require = createRequire(resolve(packageRoot, 'package.json'));
|
| 7 |
+
|
| 8 |
+
const wasmPath = require.resolve('@jsquash/webp/codec/dec/webp_dec.wasm');
|
| 9 |
+
const version = require('@jsquash/webp/package.json').version;
|
| 10 |
+
const wasm = readFileSync(wasmPath);
|
| 11 |
+
|
| 12 |
+
const target = resolve(packageRoot, 'src/agent/media/webp-dec-wasm.ts');
|
| 13 |
+
writeFileSync(
|
| 14 |
+
target,
|
| 15 |
+
`// GENERATED FILE — do not edit by hand.
|
| 16 |
+
// WebP decoder wasm from @jsquash/webp@${version} (codec/dec/webp_dec.wasm),
|
| 17 |
+
// base64-encoded so the bundled CLI needs no on-disk wasm asset.
|
| 18 |
+
// Regenerate with: node scripts/generate-webp-dec-wasm.mjs
|
| 19 |
+
|
| 20 |
+
export const WEBP_DECODER_WASM_BASE64 =
|
| 21 |
+
'${wasm.toString('base64')}';
|
| 22 |
+
`,
|
| 23 |
+
);
|
| 24 |
+
console.log(`Wrote ${target} (${wasm.length} bytes of wasm)`);
|
packages/agent-core-v2/src/_base/asyncEventQueue.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export class AsyncEventQueue<T> implements AsyncIterable<T>, AsyncIterator<T> {
|
| 2 |
+
private readonly values: T[] = [];
|
| 3 |
+
private readonly waiters: Array<{
|
| 4 |
+
resolve: (result: IteratorResult<T>) => void;
|
| 5 |
+
reject: (reason?: unknown) => void;
|
| 6 |
+
}> = [];
|
| 7 |
+
private error: unknown;
|
| 8 |
+
private failed = false;
|
| 9 |
+
private ended = false;
|
| 10 |
+
|
| 11 |
+
push(value: T): void {
|
| 12 |
+
if (this.failed || this.ended) return;
|
| 13 |
+
const waiter = this.waiters.shift();
|
| 14 |
+
if (waiter !== undefined) {
|
| 15 |
+
waiter.resolve({ done: false, value });
|
| 16 |
+
return;
|
| 17 |
+
}
|
| 18 |
+
this.values.push(value);
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
end(): void {
|
| 22 |
+
if (this.failed || this.ended) return;
|
| 23 |
+
this.ended = true;
|
| 24 |
+
for (const waiter of this.waiters.splice(0)) {
|
| 25 |
+
waiter.resolve({ done: true, value: undefined });
|
| 26 |
+
}
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
fail(error: unknown): void {
|
| 30 |
+
if (this.failed || this.ended) return;
|
| 31 |
+
this.error = error;
|
| 32 |
+
this.failed = true;
|
| 33 |
+
if (this.values.length > 0) return;
|
| 34 |
+
for (const waiter of this.waiters.splice(0)) {
|
| 35 |
+
waiter.reject(error);
|
| 36 |
+
}
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
next(): Promise<IteratorResult<T>> {
|
| 40 |
+
if (this.values.length > 0) {
|
| 41 |
+
const value = this.values.shift()!;
|
| 42 |
+
return Promise.resolve({ done: false, value });
|
| 43 |
+
}
|
| 44 |
+
if (this.failed) {
|
| 45 |
+
return Promise.reject(this.error);
|
| 46 |
+
}
|
| 47 |
+
if (this.ended) {
|
| 48 |
+
return Promise.resolve({ done: true, value: undefined });
|
| 49 |
+
}
|
| 50 |
+
return new Promise<IteratorResult<T>>((resolve, reject) => {
|
| 51 |
+
this.waiters.push({ resolve, reject });
|
| 52 |
+
});
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
[Symbol.asyncIterator](): AsyncIterator<T> {
|
| 56 |
+
return this;
|
| 57 |
+
}
|
| 58 |
+
}
|
packages/agent-core-v2/src/_base/event.ts
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { onUnexpectedError, safelyCallListener } from './errors/unexpectedError';
|
| 2 |
+
import {
|
| 3 |
+
Disposable,
|
| 4 |
+
DisposableStore,
|
| 5 |
+
combinedDisposable,
|
| 6 |
+
type IDisposable,
|
| 7 |
+
type IDisposableDebugLabel,
|
| 8 |
+
} from './di/lifecycle';
|
| 9 |
+
import { LinkedList } from './di/util/linkedList';
|
| 10 |
+
|
| 11 |
+
export interface Event<T> {
|
| 12 |
+
(
|
| 13 |
+
listener: (e: T) => unknown,
|
| 14 |
+
thisArg?: unknown,
|
| 15 |
+
disposables?: IDisposable[] | DisposableStore,
|
| 16 |
+
): IDisposable;
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
interface ListenerEntry<T> {
|
| 20 |
+
listener: (e: T) => unknown;
|
| 21 |
+
thisArg: unknown;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
export class EventSubscription implements IDisposable, IDisposableDebugLabel {
|
| 25 |
+
readonly debugLabel: string | undefined;
|
| 26 |
+
private _removed = false;
|
| 27 |
+
|
| 28 |
+
constructor(
|
| 29 |
+
debugName: string | undefined,
|
| 30 |
+
private readonly _remove: () => void,
|
| 31 |
+
) {
|
| 32 |
+
this.debugLabel = debugName === undefined ? undefined : `on:${debugName}`;
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
dispose(): void {
|
| 36 |
+
if (this._removed) return;
|
| 37 |
+
this._removed = true;
|
| 38 |
+
this._remove();
|
| 39 |
+
}
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
export class Emitter<T> {
|
| 43 |
+
protected _listeners: Set<ListenerEntry<T>> | undefined;
|
| 44 |
+
private _disposed = false;
|
| 45 |
+
private _event: Event<T> | undefined;
|
| 46 |
+
|
| 47 |
+
constructor(public readonly debugName?: string) {}
|
| 48 |
+
|
| 49 |
+
get event(): Event<T> {
|
| 50 |
+
this._event ??= (listener, thisArg, disposables) => {
|
| 51 |
+
if (this._disposed) {
|
| 52 |
+
return Disposable.None;
|
| 53 |
+
}
|
| 54 |
+
this._listeners ??= new Set();
|
| 55 |
+
const entry: ListenerEntry<T> = { listener, thisArg };
|
| 56 |
+
this._listeners.add(entry);
|
| 57 |
+
|
| 58 |
+
const subscription = new EventSubscription(this.debugName, () => {
|
| 59 |
+
if (this._disposed) {
|
| 60 |
+
return;
|
| 61 |
+
}
|
| 62 |
+
this._listeners?.delete(entry);
|
| 63 |
+
});
|
| 64 |
+
|
| 65 |
+
if (disposables !== undefined) {
|
| 66 |
+
if (disposables instanceof DisposableStore) {
|
| 67 |
+
disposables.add(subscription);
|
| 68 |
+
} else {
|
| 69 |
+
disposables.push(subscription);
|
| 70 |
+
}
|
| 71 |
+
}
|
| 72 |
+
return subscription;
|
| 73 |
+
};
|
| 74 |
+
return this._event;
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
get listenerCount(): number {
|
| 78 |
+
return this._listeners?.size ?? 0;
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
fire(value: T): void {
|
| 82 |
+
if (this._disposed || this._listeners === undefined) {
|
| 83 |
+
return;
|
| 84 |
+
}
|
| 85 |
+
const snapshot = Array.from(this._listeners);
|
| 86 |
+
for (const entry of snapshot) {
|
| 87 |
+
safelyCallListener(() => {
|
| 88 |
+
entry.listener.call(entry.thisArg, value);
|
| 89 |
+
});
|
| 90 |
+
}
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
dispose(): void {
|
| 94 |
+
if (this._disposed) return;
|
| 95 |
+
this._disposed = true;
|
| 96 |
+
this._listeners?.clear();
|
| 97 |
+
this._listeners = undefined;
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
get isDisposed(): boolean {
|
| 101 |
+
return this._disposed;
|
| 102 |
+
}
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
export interface IWaitUntil {
|
| 106 |
+
readonly signal: AbortSignal;
|
| 107 |
+
waitUntil(thenable: Promise<unknown>): void;
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
export type IWaitUntilData<T> = Omit<T, 'waitUntil' | 'signal'>;
|
| 111 |
+
|
| 112 |
+
export class AsyncEmitter<T extends IWaitUntil> extends Emitter<T> {
|
| 113 |
+
private _asyncDeliveryQueue?: LinkedList<[(event: T) => void, IWaitUntilData<T>]>;
|
| 114 |
+
|
| 115 |
+
async fireAsyncConcurrent(data: IWaitUntilData<T>, signal: AbortSignal): Promise<void> {
|
| 116 |
+
if (this.isDisposed || this._listeners === undefined || signal.aborted) {
|
| 117 |
+
return;
|
| 118 |
+
}
|
| 119 |
+
const snapshot = Array.from(this._listeners);
|
| 120 |
+
await Promise.all(
|
| 121 |
+
snapshot.map((entry) =>
|
| 122 |
+
this.deliverAsync(
|
| 123 |
+
(event) => {
|
| 124 |
+
entry.listener.call(entry.thisArg, event);
|
| 125 |
+
},
|
| 126 |
+
data,
|
| 127 |
+
signal,
|
| 128 |
+
),
|
| 129 |
+
),
|
| 130 |
+
);
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
async fireAsync(data: IWaitUntilData<T>, signal: AbortSignal): Promise<void> {
|
| 134 |
+
if (this.isDisposed || this._listeners === undefined) {
|
| 135 |
+
return;
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
this._asyncDeliveryQueue ??= new LinkedList();
|
| 139 |
+
for (const entry of this._listeners) {
|
| 140 |
+
this._asyncDeliveryQueue.push([
|
| 141 |
+
(event) => {
|
| 142 |
+
entry.listener.call(entry.thisArg, event);
|
| 143 |
+
},
|
| 144 |
+
data,
|
| 145 |
+
]);
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
while (this._asyncDeliveryQueue.size > 0 && !signal.aborted) {
|
| 149 |
+
const [deliver, eventData] = this._asyncDeliveryQueue.shift()!;
|
| 150 |
+
await this.deliverAsync(deliver, eventData, signal);
|
| 151 |
+
}
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
private async deliverAsync(
|
| 155 |
+
deliver: (event: T) => void,
|
| 156 |
+
data: IWaitUntilData<T>,
|
| 157 |
+
signal: AbortSignal,
|
| 158 |
+
): Promise<void> {
|
| 159 |
+
const thenables: Promise<unknown>[] = [];
|
| 160 |
+
const event = {
|
| 161 |
+
...data,
|
| 162 |
+
signal,
|
| 163 |
+
waitUntil: (p: Promise<unknown>): void => {
|
| 164 |
+
if (Object.isFrozen(thenables)) {
|
| 165 |
+
throw new Error('waitUntil can NOT be called asynchronously');
|
| 166 |
+
}
|
| 167 |
+
thenables.push(p);
|
| 168 |
+
},
|
| 169 |
+
} as T;
|
| 170 |
+
try {
|
| 171 |
+
deliver(event);
|
| 172 |
+
} catch (error) {
|
| 173 |
+
onUnexpectedError(error);
|
| 174 |
+
return;
|
| 175 |
+
}
|
| 176 |
+
void Object.freeze(thenables);
|
| 177 |
+
const settled = await Promise.allSettled(thenables);
|
| 178 |
+
for (const result of settled) {
|
| 179 |
+
if (result.status === 'rejected') {
|
| 180 |
+
onUnexpectedError(result.reason);
|
| 181 |
+
}
|
| 182 |
+
}
|
| 183 |
+
}
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
export function handleVetos(
|
| 187 |
+
vetos: (boolean | Promise<boolean>)[],
|
| 188 |
+
onError: (error: unknown) => void,
|
| 189 |
+
): Promise<boolean> {
|
| 190 |
+
if (vetos.length === 0) {
|
| 191 |
+
return Promise.resolve(false);
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
const promises: Promise<void>[] = [];
|
| 195 |
+
let lazyValue = false;
|
| 196 |
+
|
| 197 |
+
for (const valueOrPromise of vetos) {
|
| 198 |
+
if (valueOrPromise === true) {
|
| 199 |
+
return Promise.resolve(true);
|
| 200 |
+
}
|
| 201 |
+
if (typeof valueOrPromise === 'boolean') {
|
| 202 |
+
continue;
|
| 203 |
+
}
|
| 204 |
+
promises.push(
|
| 205 |
+
valueOrPromise.then(
|
| 206 |
+
(value) => {
|
| 207 |
+
if (value) {
|
| 208 |
+
lazyValue = true;
|
| 209 |
+
}
|
| 210 |
+
},
|
| 211 |
+
(error) => {
|
| 212 |
+
onError(error);
|
| 213 |
+
lazyValue = true;
|
| 214 |
+
},
|
| 215 |
+
),
|
| 216 |
+
);
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
return Promise.allSettled(promises).then(() => lazyValue);
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
export namespace Event {
|
| 223 |
+
export const None: Event<unknown> = () => Disposable.None;
|
| 224 |
+
|
| 225 |
+
export function once<T>(event: Event<T>): Event<T> {
|
| 226 |
+
return (listener, thisArg, disposables) => {
|
| 227 |
+
let fired = false;
|
| 228 |
+
const subscription = event(
|
| 229 |
+
(e) => {
|
| 230 |
+
if (fired) return;
|
| 231 |
+
fired = true;
|
| 232 |
+
subscription.dispose();
|
| 233 |
+
try {
|
| 234 |
+
listener.call(thisArg, e);
|
| 235 |
+
} catch (error) {
|
| 236 |
+
onUnexpectedError(error);
|
| 237 |
+
}
|
| 238 |
+
},
|
| 239 |
+
undefined,
|
| 240 |
+
disposables,
|
| 241 |
+
);
|
| 242 |
+
return subscription;
|
| 243 |
+
};
|
| 244 |
+
}
|
| 245 |
+
|
| 246 |
+
export function map<I, O>(event: Event<I>, map: (i: I) => O): Event<O> {
|
| 247 |
+
return (listener, thisArg, disposables) =>
|
| 248 |
+
event(
|
| 249 |
+
(i) => listener.call(thisArg, map(i)),
|
| 250 |
+
undefined,
|
| 251 |
+
disposables,
|
| 252 |
+
);
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
export function filter<T>(event: Event<T>, filter: (e: T) => boolean): Event<T> {
|
| 256 |
+
return (listener, thisArg, disposables) =>
|
| 257 |
+
event(
|
| 258 |
+
(e) => {
|
| 259 |
+
if (filter(e)) listener.call(thisArg, e);
|
| 260 |
+
},
|
| 261 |
+
undefined,
|
| 262 |
+
disposables,
|
| 263 |
+
);
|
| 264 |
+
}
|
| 265 |
+
|
| 266 |
+
export function any<T>(...events: Event<T>[]): Event<T> {
|
| 267 |
+
return (listener, thisArg, disposables) => {
|
| 268 |
+
const combined = combinedDisposable(
|
| 269 |
+
...events.map((e) => e((value) => listener.call(thisArg, value))),
|
| 270 |
+
);
|
| 271 |
+
if (disposables !== undefined) {
|
| 272 |
+
if (disposables instanceof DisposableStore) {
|
| 273 |
+
disposables.add(combined);
|
| 274 |
+
} else {
|
| 275 |
+
disposables.push(combined);
|
| 276 |
+
}
|
| 277 |
+
}
|
| 278 |
+
return combined;
|
| 279 |
+
};
|
| 280 |
+
}
|
| 281 |
+
}
|
packages/agent-core-v2/src/_base/version.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export function getCoreVersion(): string {
|
| 2 |
+
return '0.0.0';
|
| 3 |
+
}
|
packages/agent-core-v2/src/app/scopes.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { setScopeTopology } from '#/_base/di/scope';
|
| 2 |
+
|
| 3 |
+
export enum LifecycleScope {
|
| 4 |
+
App = 'app',
|
| 5 |
+
Session = 'session',
|
| 6 |
+
Agent = 'agent',
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
export const SCOPE_TOPOLOGY: readonly LifecycleScope[] = [
|
| 10 |
+
LifecycleScope.App,
|
| 11 |
+
LifecycleScope.Session,
|
| 12 |
+
LifecycleScope.Agent,
|
| 13 |
+
];
|
| 14 |
+
|
| 15 |
+
setScopeTopology(SCOPE_TOPOLOGY);
|
packages/agent-core-v2/src/debug/debugCascade.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* oxlint-disable typescript-eslint/no-unsafe-declaration-merging, eslint-plugin-import/namespace -- Event2 class+payload-interface declaration merging is the sanctioned event-declaration idiom. */
|
| 2 |
+
import type { CascadeAction, UnitState } from '#/_base/di/cascadeEngine';
|
| 3 |
+
import { createDecorator } from '#/_base/di/instantiation';
|
| 4 |
+
import { Event2 } from '#/app/event/event2';
|
| 5 |
+
|
| 6 |
+
export interface DebugCascadeEntry {
|
| 7 |
+
readonly scopePath: string;
|
| 8 |
+
readonly seq: number;
|
| 9 |
+
readonly reason: string;
|
| 10 |
+
readonly changes: ReadonlyArray<{ token: string; action: CascadeAction }>;
|
| 11 |
+
readonly affected: readonly string[];
|
| 12 |
+
readonly tornDown: readonly string[];
|
| 13 |
+
readonly rebuilt: readonly string[];
|
| 14 |
+
readonly failed: readonly string[];
|
| 15 |
+
readonly abortWaited: boolean;
|
| 16 |
+
readonly abortTimedOut: boolean;
|
| 17 |
+
readonly durationMs: number;
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
export interface DebugPendingUnit {
|
| 21 |
+
readonly token: string;
|
| 22 |
+
readonly missing: string[];
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
export interface DebugFailedUnit {
|
| 26 |
+
readonly token: string;
|
| 27 |
+
readonly error?: string;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
export interface DebugPendingGroup {
|
| 31 |
+
readonly scopePath: string;
|
| 32 |
+
readonly waiting: DebugPendingUnit[];
|
| 33 |
+
readonly failed: DebugFailedUnit[];
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
export interface DiUnitChangedPayload {
|
| 37 |
+
readonly scope: string;
|
| 38 |
+
readonly token: string;
|
| 39 |
+
readonly state: UnitState;
|
| 40 |
+
readonly error?: string;
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
export class DiUnitChanged extends Event2<{ readonly payload: DiUnitChangedPayload }> {
|
| 44 |
+
static override readonly type = 'event.di.unit_changed';
|
| 45 |
+
}
|
| 46 |
+
export interface DiUnitChanged {
|
| 47 |
+
readonly payload: DiUnitChangedPayload;
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
export const DI_UNIT_CHANGED_EVENT = DiUnitChanged.type;
|
| 51 |
+
|
| 52 |
+
export interface IDebugCascadeService {
|
| 53 |
+
readonly _serviceBrand: undefined;
|
| 54 |
+
|
| 55 |
+
history(): DebugCascadeEntry[];
|
| 56 |
+
pending(): DebugPendingGroup[];
|
| 57 |
+
unprovide(scopePath: string, token: string): Promise<void>;
|
| 58 |
+
update(scopePath: string, token: string, config?: unknown): Promise<void>;
|
| 59 |
+
dispose(scopePath: string, token: string): Promise<void>;
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
export const IDebugCascadeService =
|
| 63 |
+
createDecorator<IDebugCascadeService>('debugCascadeService');
|
packages/agent-core-v2/src/debug/debugCascadeService.ts
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { CascadeEngine } from '#/_base/di/cascadeEngine';
|
| 2 |
+
import {
|
| 3 |
+
IInstantiationService,
|
| 4 |
+
type ServiceIdentifier,
|
| 5 |
+
} from '#/_base/di/instantiation';
|
| 6 |
+
import type { InstantiationService } from '#/_base/di/instantiationService';
|
| 7 |
+
import { DisposableStore, type IDisposable } from '#/_base/di/lifecycle';
|
| 8 |
+
import { ScopeActivation, registerScopedService } from '#/_base/di/scope';
|
| 9 |
+
import { IEventService } from '#/app/event/event';
|
| 10 |
+
import { LifecycleScope } from '#/app/scopes';
|
| 11 |
+
import { Error2, ErrorCodes } from '#/errors';
|
| 12 |
+
|
| 13 |
+
import {
|
| 14 |
+
DiUnitChanged,
|
| 15 |
+
IDebugCascadeService,
|
| 16 |
+
type DebugCascadeEntry,
|
| 17 |
+
type DebugFailedUnit,
|
| 18 |
+
type DebugPendingGroup,
|
| 19 |
+
type DebugPendingUnit,
|
| 20 |
+
type DiUnitChangedPayload,
|
| 21 |
+
} from './debugCascade';
|
| 22 |
+
import {
|
| 23 |
+
resolveScopeContainer,
|
| 24 |
+
scopePathOfEngine,
|
| 25 |
+
walkScopeContainers,
|
| 26 |
+
} from './scopeTree';
|
| 27 |
+
|
| 28 |
+
export class DebugCascadeService implements IDebugCascadeService {
|
| 29 |
+
declare readonly _serviceBrand: undefined;
|
| 30 |
+
|
| 31 |
+
private readonly root: InstantiationService;
|
| 32 |
+
private readonly events: IEventService;
|
| 33 |
+
private readonly store = new DisposableStore();
|
| 34 |
+
private readonly engineSubscriptions = new Map<CascadeEngine, IDisposable>();
|
| 35 |
+
private tornDown = false;
|
| 36 |
+
|
| 37 |
+
constructor(
|
| 38 |
+
@IInstantiationService instantiation: IInstantiationService,
|
| 39 |
+
@IEventService events: IEventService,
|
| 40 |
+
) {
|
| 41 |
+
this.root = instantiation as InstantiationService;
|
| 42 |
+
this.events = events;
|
| 43 |
+
const tree = this.root.cascadeTree;
|
| 44 |
+
for (const engine of tree.engines) {
|
| 45 |
+
this._watchEngine(engine);
|
| 46 |
+
}
|
| 47 |
+
this.store.add(
|
| 48 |
+
tree.onDidAddEngine((engine) => {
|
| 49 |
+
this._watchEngine(engine);
|
| 50 |
+
}),
|
| 51 |
+
);
|
| 52 |
+
this.store.add(
|
| 53 |
+
tree.onDidRemoveEngine((engine) => {
|
| 54 |
+
this._unwatchEngine(engine);
|
| 55 |
+
}),
|
| 56 |
+
);
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
history(): DebugCascadeEntry[] {
|
| 60 |
+
const entries: DebugCascadeEntry[] = [];
|
| 61 |
+
for (const info of walkScopeContainers(this.root)) {
|
| 62 |
+
for (const entry of info.container.cascade.history()) {
|
| 63 |
+
entries.push({ scopePath: info.path, ...entry });
|
| 64 |
+
}
|
| 65 |
+
}
|
| 66 |
+
return entries.toSorted(
|
| 67 |
+
(a, b) => a.seq - b.seq || a.scopePath.localeCompare(b.scopePath),
|
| 68 |
+
);
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
pending(): DebugPendingGroup[] {
|
| 72 |
+
const groups: DebugPendingGroup[] = [];
|
| 73 |
+
for (const info of walkScopeContainers(this.root)) {
|
| 74 |
+
const waiting: DebugPendingUnit[] = [];
|
| 75 |
+
for (const [token, missing] of info.container.cascade.pendingSnapshot()) {
|
| 76 |
+
waiting.push({ token, missing: [...missing] });
|
| 77 |
+
}
|
| 78 |
+
const failed: DebugFailedUnit[] = info.container.cascade
|
| 79 |
+
.unitsSnapshot()
|
| 80 |
+
.filter((unit) => unit.state === 'Failed')
|
| 81 |
+
.map((unit) => ({ token: unit.token, error: unit.error }));
|
| 82 |
+
if (waiting.length > 0 || failed.length > 0) {
|
| 83 |
+
groups.push({ scopePath: info.path, waiting, failed });
|
| 84 |
+
}
|
| 85 |
+
}
|
| 86 |
+
return groups;
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
async unprovide(scopePath: string, token: string): Promise<void> {
|
| 90 |
+
const { container, id } = this._resolve(scopePath, token);
|
| 91 |
+
container.unprovide(id);
|
| 92 |
+
await container.cascade.whenIdle();
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
async update(scopePath: string, token: string, config?: unknown): Promise<void> {
|
| 96 |
+
const { container, id } = this._resolve(scopePath, token);
|
| 97 |
+
if (config === undefined) {
|
| 98 |
+
await container.cascade.update(id, `debug update ${token}`);
|
| 99 |
+
} else {
|
| 100 |
+
await container.fiberHost.updateToken(id, config, true);
|
| 101 |
+
}
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
async dispose(scopePath: string, token: string): Promise<void>;
|
| 105 |
+
dispose(): void;
|
| 106 |
+
async dispose(scopePath?: string, token?: string): Promise<void> {
|
| 107 |
+
if (scopePath === undefined && token === undefined) {
|
| 108 |
+
if (!this.tornDown) {
|
| 109 |
+
this.tornDown = true;
|
| 110 |
+
this.store.dispose();
|
| 111 |
+
for (const subscription of this.engineSubscriptions.values()) {
|
| 112 |
+
subscription.dispose();
|
| 113 |
+
}
|
| 114 |
+
this.engineSubscriptions.clear();
|
| 115 |
+
}
|
| 116 |
+
return;
|
| 117 |
+
}
|
| 118 |
+
if (scopePath === undefined || token === undefined) {
|
| 119 |
+
throw new Error2(
|
| 120 |
+
ErrorCodes.DEBUG_TOKEN_NOT_FOUND,
|
| 121 |
+
'dispose requires both a scope path and a token',
|
| 122 |
+
);
|
| 123 |
+
}
|
| 124 |
+
const { container, id } = this._resolve(scopePath, token);
|
| 125 |
+
await container.cascade.submit({
|
| 126 |
+
action: 'unprovide',
|
| 127 |
+
token: id,
|
| 128 |
+
reason: `debug dispose ${token}`,
|
| 129 |
+
});
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
private _resolve(
|
| 133 |
+
scopePath: string,
|
| 134 |
+
token: string,
|
| 135 |
+
): { container: InstantiationService; id: ServiceIdentifier<unknown> } {
|
| 136 |
+
const container = resolveScopeContainer(this.root, scopePath);
|
| 137 |
+
if (container === undefined) {
|
| 138 |
+
throw new Error2(
|
| 139 |
+
ErrorCodes.DEBUG_SCOPE_NOT_FOUND,
|
| 140 |
+
`no DI container at scope path '${scopePath}'`,
|
| 141 |
+
);
|
| 142 |
+
}
|
| 143 |
+
const id = container.findIdentifier(token);
|
| 144 |
+
if (id === undefined) {
|
| 145 |
+
throw new Error2(
|
| 146 |
+
ErrorCodes.DEBUG_TOKEN_NOT_FOUND,
|
| 147 |
+
`token '${token}' is not registered in container '${scopePath}'`,
|
| 148 |
+
);
|
| 149 |
+
}
|
| 150 |
+
return { container, id };
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
private _watchEngine(engine: CascadeEngine): void {
|
| 154 |
+
if (this.engineSubscriptions.has(engine)) {
|
| 155 |
+
return;
|
| 156 |
+
}
|
| 157 |
+
this.engineSubscriptions.set(
|
| 158 |
+
engine,
|
| 159 |
+
engine.onDidChangeUnitState((change) => {
|
| 160 |
+
const payload: DiUnitChangedPayload = {
|
| 161 |
+
scope: scopePathOfEngine(this.root, engine) ?? '#unknown',
|
| 162 |
+
token: change.token,
|
| 163 |
+
state: change.state,
|
| 164 |
+
error: change.error,
|
| 165 |
+
};
|
| 166 |
+
this.events.publish(new DiUnitChanged({ payload }));
|
| 167 |
+
}),
|
| 168 |
+
);
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
private _unwatchEngine(engine: CascadeEngine): void {
|
| 172 |
+
this.engineSubscriptions.get(engine)?.dispose();
|
| 173 |
+
this.engineSubscriptions.delete(engine);
|
| 174 |
+
}
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
registerScopedService(
|
| 178 |
+
LifecycleScope.App,
|
| 179 |
+
IDebugCascadeService,
|
| 180 |
+
DebugCascadeService,
|
| 181 |
+
ScopeActivation.OnScopeCreated,
|
| 182 |
+
'debug',
|
| 183 |
+
);
|
packages/agent-core-v2/src/debug/debugGraph.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { UnitState } from '#/_base/di/cascadeEngine';
|
| 2 |
+
import type { DependencyEdgeKind } from '#/_base/di/dependencyGraph';
|
| 3 |
+
import { createDecorator } from '#/_base/di/instantiation';
|
| 4 |
+
|
| 5 |
+
export interface DebugGraphNode {
|
| 6 |
+
readonly id: string;
|
| 7 |
+
readonly token: string;
|
| 8 |
+
readonly scopePath: string;
|
| 9 |
+
readonly uid?: number;
|
| 10 |
+
readonly state?: UnitState;
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
export interface DebugGraphEdge {
|
| 14 |
+
readonly from: string;
|
| 15 |
+
readonly to: string;
|
| 16 |
+
readonly kind: DependencyEdgeKind;
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
export interface DebugGraph {
|
| 20 |
+
readonly nodes: DebugGraphNode[];
|
| 21 |
+
readonly edges: DebugGraphEdge[];
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
export interface IDebugGraphService {
|
| 25 |
+
readonly _serviceBrand: undefined;
|
| 26 |
+
|
| 27 |
+
graph(): DebugGraph;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
export const IDebugGraphService = createDecorator<IDebugGraphService>('debugGraphService');
|
packages/agent-core-v2/src/debug/debugGraphService.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { IInstantiationService } from '#/_base/di/instantiation';
|
| 2 |
+
import type { InstantiationService } from '#/_base/di/instantiationService';
|
| 3 |
+
import { ScopeActivation, registerScopedService } from '#/_base/di/scope';
|
| 4 |
+
import { LifecycleScope } from '#/app/scopes';
|
| 5 |
+
|
| 6 |
+
import {
|
| 7 |
+
IDebugGraphService,
|
| 8 |
+
type DebugGraph,
|
| 9 |
+
type DebugGraphEdge,
|
| 10 |
+
type DebugGraphNode,
|
| 11 |
+
} from './debugGraph';
|
| 12 |
+
import { walkScopeContainers } from './scopeTree';
|
| 13 |
+
|
| 14 |
+
export class DebugGraphService implements IDebugGraphService {
|
| 15 |
+
declare readonly _serviceBrand: undefined;
|
| 16 |
+
|
| 17 |
+
private readonly root: InstantiationService;
|
| 18 |
+
|
| 19 |
+
constructor(@IInstantiationService instantiation: IInstantiationService) {
|
| 20 |
+
this.root = instantiation as InstantiationService;
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
graph(): DebugGraph {
|
| 24 |
+
const infos = walkScopeContainers(this.root);
|
| 25 |
+
const pathByContainer = new Map<InstantiationService, string>(
|
| 26 |
+
infos.map((info) => [info.container, info.path]),
|
| 27 |
+
);
|
| 28 |
+
const nodes = new Map<string, DebugGraphNode>();
|
| 29 |
+
for (const info of infos) {
|
| 30 |
+
const states = new Map(
|
| 31 |
+
info.container.cascade.unitsSnapshot().map((unit) => [unit.token, unit]),
|
| 32 |
+
);
|
| 33 |
+
for (const registration of info.container.servicesSnapshot()) {
|
| 34 |
+
const id = nodeId(info.path, registration.token);
|
| 35 |
+
nodes.set(id, {
|
| 36 |
+
id,
|
| 37 |
+
token: registration.token,
|
| 38 |
+
scopePath: info.path,
|
| 39 |
+
uid: registration.uid,
|
| 40 |
+
state: states.get(registration.token)?.state,
|
| 41 |
+
});
|
| 42 |
+
}
|
| 43 |
+
}
|
| 44 |
+
const pathOf = (scope: object): string =>
|
| 45 |
+
pathByContainer.get(scope as InstantiationService) ??
|
| 46 |
+
`#${this.root.cascadeTree.seqOf(scope)}`;
|
| 47 |
+
const edges: DebugGraphEdge[] = [];
|
| 48 |
+
const endpointNode = (path: string, token: string): string => {
|
| 49 |
+
const id = nodeId(path, token);
|
| 50 |
+
if (!nodes.has(id)) {
|
| 51 |
+
nodes.set(id, { id, token, scopePath: path });
|
| 52 |
+
}
|
| 53 |
+
return id;
|
| 54 |
+
};
|
| 55 |
+
for (const edge of this.root.dependencyGraph.edges()) {
|
| 56 |
+
edges.push({
|
| 57 |
+
from: endpointNode(pathOf(edge.consumer.scope), edge.consumer.token.toString()),
|
| 58 |
+
to: endpointNode(pathOf(edge.dependency.scope), edge.dependency.token.toString()),
|
| 59 |
+
kind: edge.kind,
|
| 60 |
+
});
|
| 61 |
+
}
|
| 62 |
+
return { nodes: [...nodes.values()], edges };
|
| 63 |
+
}
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
function nodeId(scopePath: string, token: string): string {
|
| 67 |
+
return `${scopePath}::${token}`;
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
registerScopedService(
|
| 71 |
+
LifecycleScope.App,
|
| 72 |
+
IDebugGraphService,
|
| 73 |
+
DebugGraphService,
|
| 74 |
+
ScopeActivation.OnDemand,
|
| 75 |
+
'debug',
|
| 76 |
+
);
|
packages/agent-core-v2/src/debug/debugLedger.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { UnitState } from '#/_base/di/cascadeEngine';
|
| 2 |
+
import { createDecorator } from '#/_base/di/instantiation';
|
| 3 |
+
import type { LedgerEntryInfo } from '#/_base/lifecycle/ledger';
|
| 4 |
+
|
| 5 |
+
export interface DebugUnit {
|
| 6 |
+
readonly token: string;
|
| 7 |
+
readonly uid: number;
|
| 8 |
+
readonly state?: UnitState;
|
| 9 |
+
readonly error?: string;
|
| 10 |
+
readonly everActive?: boolean;
|
| 11 |
+
readonly inFlight?: boolean;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
export interface DebugLedgerNode {
|
| 15 |
+
readonly path: string;
|
| 16 |
+
readonly label: string;
|
| 17 |
+
readonly units: DebugUnit[];
|
| 18 |
+
readonly ledger: LedgerEntryInfo[];
|
| 19 |
+
readonly children: DebugLedgerNode[];
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
export interface IDebugLedgerService {
|
| 23 |
+
readonly _serviceBrand: undefined;
|
| 24 |
+
|
| 25 |
+
tree(): DebugLedgerNode;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
export const IDebugLedgerService =
|
| 29 |
+
createDecorator<IDebugLedgerService>('debugLedgerService');
|
packages/agent-core-v2/src/debug/debugLedgerService.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { IInstantiationService } from '#/_base/di/instantiation';
|
| 2 |
+
import type { InstantiationService } from '#/_base/di/instantiationService';
|
| 3 |
+
import { ScopeActivation, registerScopedService } from '#/_base/di/scope';
|
| 4 |
+
import { LifecycleScope } from '#/app/scopes';
|
| 5 |
+
|
| 6 |
+
import { IDebugLedgerService, type DebugLedgerNode, type DebugUnit } from './debugLedger';
|
| 7 |
+
import { scopeContainerLabel } from './scopeTree';
|
| 8 |
+
|
| 9 |
+
export class DebugLedgerService implements IDebugLedgerService {
|
| 10 |
+
declare readonly _serviceBrand: undefined;
|
| 11 |
+
|
| 12 |
+
private readonly root: InstantiationService;
|
| 13 |
+
|
| 14 |
+
constructor(@IInstantiationService instantiation: IInstantiationService) {
|
| 15 |
+
this.root = instantiation as InstantiationService;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
tree(): DebugLedgerNode {
|
| 19 |
+
return this._node(this.root, scopeContainerLabel(this.root));
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
private _node(container: InstantiationService, path: string): DebugLedgerNode {
|
| 23 |
+
return {
|
| 24 |
+
path,
|
| 25 |
+
label: scopeContainerLabel(container),
|
| 26 |
+
units: joinUnits(container),
|
| 27 |
+
ledger: container.ledger.entries(),
|
| 28 |
+
children: container.children.map((child) =>
|
| 29 |
+
this._node(child, `${path}/${scopeContainerLabel(child)}`),
|
| 30 |
+
),
|
| 31 |
+
};
|
| 32 |
+
}
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
function joinUnits(container: InstantiationService): DebugUnit[] {
|
| 36 |
+
const states = new Map(container.cascade.unitsSnapshot().map((unit) => [unit.token, unit]));
|
| 37 |
+
return container.servicesSnapshot().map((registration) => {
|
| 38 |
+
const unit = states.get(registration.token);
|
| 39 |
+
return {
|
| 40 |
+
token: registration.token,
|
| 41 |
+
uid: registration.uid,
|
| 42 |
+
state: unit?.state,
|
| 43 |
+
error: unit?.error,
|
| 44 |
+
everActive: unit?.everActive,
|
| 45 |
+
inFlight: unit?.inFlight,
|
| 46 |
+
};
|
| 47 |
+
});
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
registerScopedService(
|
| 51 |
+
LifecycleScope.App,
|
| 52 |
+
IDebugLedgerService,
|
| 53 |
+
DebugLedgerService,
|
| 54 |
+
ScopeActivation.OnDemand,
|
| 55 |
+
'debug',
|
| 56 |
+
);
|
packages/agent-core-v2/src/debug/errors.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { registerErrorDomain, type ErrorDomain } from '#/_base/errors/codes';
|
| 2 |
+
|
| 3 |
+
export const DebugErrors = {
|
| 4 |
+
codes: {
|
| 5 |
+
DEBUG_SCOPE_NOT_FOUND: 'debug.scope_not_found',
|
| 6 |
+
DEBUG_TOKEN_NOT_FOUND: 'debug.token_not_found',
|
| 7 |
+
},
|
| 8 |
+
} as const satisfies ErrorDomain;
|
| 9 |
+
|
| 10 |
+
registerErrorDomain(DebugErrors);
|
packages/agent-core-v2/src/debug/index.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export * from './debugLedger';
|
| 2 |
+
export * from './debugGraph';
|
| 3 |
+
export * from './debugCascade';
|
| 4 |
+
export * from './errors';
|
| 5 |
+
export * from './debugLedgerService';
|
| 6 |
+
export * from './debugGraphService';
|
| 7 |
+
export * from './debugCascadeService';
|
packages/agent-core-v2/src/debug/scopeTree.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { CascadeEngine } from '#/_base/di/cascadeEngine';
|
| 2 |
+
import type { InstantiationService } from '#/_base/di/instantiationService';
|
| 3 |
+
|
| 4 |
+
export interface ScopeContainerInfo {
|
| 5 |
+
readonly container: InstantiationService;
|
| 6 |
+
readonly path: string;
|
| 7 |
+
readonly label: string;
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
export function scopeContainerLabel(container: InstantiationService): string {
|
| 11 |
+
return container.debugLabel ?? `#${container.cascadeTree.seqOf(container)}`;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
export function walkScopeContainers(root: InstantiationService): ScopeContainerInfo[] {
|
| 15 |
+
const out: ScopeContainerInfo[] = [];
|
| 16 |
+
const visit = (container: InstantiationService, path: string): void => {
|
| 17 |
+
out.push({ container, path, label: scopeContainerLabel(container) });
|
| 18 |
+
for (const child of container.children) {
|
| 19 |
+
visit(child, `${path}/${scopeContainerLabel(child)}`);
|
| 20 |
+
}
|
| 21 |
+
};
|
| 22 |
+
visit(root, scopeContainerLabel(root));
|
| 23 |
+
return out;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
export function resolveScopeContainer(
|
| 27 |
+
root: InstantiationService,
|
| 28 |
+
path: string,
|
| 29 |
+
): InstantiationService | undefined {
|
| 30 |
+
return walkScopeContainers(root).find((info) => info.path === path)?.container;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
export function scopePathOfEngine(
|
| 34 |
+
root: InstantiationService,
|
| 35 |
+
engine: CascadeEngine,
|
| 36 |
+
): string | undefined {
|
| 37 |
+
return walkScopeContainers(root).find((info) => info.container.cascade === engine)?.path;
|
| 38 |
+
}
|
packages/agent-core-v2/src/env.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
declare module '*?raw' {
|
| 2 |
+
const content: string;
|
| 3 |
+
export default content;
|
| 4 |
+
}
|
packages/agent-core-v2/src/errors.ts
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { CoreErrors } from '#/_base/errors/codes';
|
| 2 |
+
import type { KimiErrorPayload } from '#/_base/errors/serialize';
|
| 3 |
+
import { AgentLifecycleErrors } from '#/session/agentLifecycle/errors';
|
| 4 |
+
import { AuthErrors } from '#/app/auth/errors';
|
| 5 |
+
import { TaskErrors } from '#/agent/task/errors';
|
| 6 |
+
import { ProtocolErrors } from '#/llm-adapter/protocol/errors';
|
| 7 |
+
import { ConfigErrors } from '#/app/config/errors';
|
| 8 |
+
import { CapabilityErrors } from '#/app/capability/errors';
|
| 9 |
+
import { CronErrors } from '#/features/cron/errors';
|
| 10 |
+
import { DebugErrors } from '#/debug/errors';
|
| 11 |
+
import { EventErrors } from '#/app/event/errors';
|
| 12 |
+
import { FileErrors } from '#/app/file/fileService';
|
| 13 |
+
import { FsErrors } from '#/workspace/workspaceFs/internal/errors';
|
| 14 |
+
import { FullCompactionErrors } from '#/agent/fullCompaction/errors';
|
| 15 |
+
import { GoalErrors } from '#/features/goal/errors';
|
| 16 |
+
import { LoopErrors } from '#/agent/loop/errors';
|
| 17 |
+
import { McpErrors } from '#/mcpCore/errors';
|
| 18 |
+
import { ModelCatalogErrors } from '#/llm-adapter/model/errors';
|
| 19 |
+
import { OsFsErrors } from '#/os/interface/hostFsErrors';
|
| 20 |
+
import { OsProcessErrors } from '#/os/interface/hostProcess';
|
| 21 |
+
import { PluginErrors } from '#/app/plugin/errors';
|
| 22 |
+
import { ProfileErrors } from '#/agent/profile/errors';
|
| 23 |
+
import { PromptErrors } from '#/agent/prompt/errors';
|
| 24 |
+
import { ModelsDevImportErrors } from '#/app/kosongConfig/errors';
|
| 25 |
+
import { SessionExportErrors } from '#/app/sessionExport/errors';
|
| 26 |
+
import { SessionErrors } from '#/session/errors';
|
| 27 |
+
import { SkillErrors } from '#/features/skill/catalog/errors';
|
| 28 |
+
import { StateErrors } from '#/state/errors';
|
| 29 |
+
import { StorageErrors } from '#/persistence/interface/storage';
|
| 30 |
+
import { TerminalErrors } from '#/os/interface/terminalErrors';
|
| 31 |
+
import { UsageErrors } from '#/agent/usage/errors';
|
| 32 |
+
import { WebErrors } from '#/app/web/errors';
|
| 33 |
+
import { WireErrors } from '#/wire/errors';
|
| 34 |
+
import { WorkspaceErrors } from '#/app/workspace/errors';
|
| 35 |
+
|
| 36 |
+
export * from '#/_base/errors/codes';
|
| 37 |
+
export * from '#/_base/errors/errorMessage';
|
| 38 |
+
export * from '#/_base/errors/errors';
|
| 39 |
+
export * from '#/_base/errors/serialize';
|
| 40 |
+
export * from '#/_base/errors/unexpectedError';
|
| 41 |
+
export { AgentLifecycleErrors } from '#/session/agentLifecycle/errors';
|
| 42 |
+
export { AuthErrors } from '#/app/auth/errors';
|
| 43 |
+
export { TaskErrors } from '#/agent/task/errors';
|
| 44 |
+
export { ProtocolErrors } from '#/llm-adapter/protocol/errors';
|
| 45 |
+
export { ConfigErrors } from '#/app/config/errors';
|
| 46 |
+
export { CapabilityErrors } from '#/app/capability/errors';
|
| 47 |
+
export { CronErrors } from '#/features/cron/errors';
|
| 48 |
+
export { DebugErrors } from '#/debug/errors';
|
| 49 |
+
export { FileErrors } from '#/app/file/fileService';
|
| 50 |
+
export { FsErrors } from '#/workspace/workspaceFs/internal/errors';
|
| 51 |
+
export { FullCompactionErrors } from '#/agent/fullCompaction/errors';
|
| 52 |
+
export { GoalErrors } from '#/features/goal/errors';
|
| 53 |
+
export { LoopErrors } from '#/agent/loop/errors';
|
| 54 |
+
export { McpErrors } from '#/mcpCore/errors';
|
| 55 |
+
export { ModelCatalogErrors } from '#/llm-adapter/model/errors';
|
| 56 |
+
export { OsFsErrors } from '#/os/interface/hostFsErrors';
|
| 57 |
+
export { OsProcessErrors } from '#/os/interface/hostProcess';
|
| 58 |
+
export { PluginErrors } from '#/app/plugin/errors';
|
| 59 |
+
export { ProfileErrors } from '#/agent/profile/errors';
|
| 60 |
+
export { PromptErrors } from '#/agent/prompt/errors';
|
| 61 |
+
export { ModelsDevImportErrors } from '#/app/kosongConfig/errors';
|
| 62 |
+
export { SessionExportErrors } from '#/app/sessionExport/errors';
|
| 63 |
+
export { SessionErrors } from '#/session/errors';
|
| 64 |
+
export { SkillErrors } from '#/features/skill/catalog/errors';
|
| 65 |
+
export { StorageErrors } from '#/persistence/interface/storage';
|
| 66 |
+
export { TerminalErrors } from '#/os/interface/terminalErrors';
|
| 67 |
+
export { UsageErrors } from '#/agent/usage/errors';
|
| 68 |
+
export { WebErrors } from '#/app/web/errors';
|
| 69 |
+
export { WireErrors } from '#/wire/errors';
|
| 70 |
+
export { WorkspaceErrors } from '#/app/workspace/errors';
|
| 71 |
+
export { EventErrors } from '#/app/event/errors';
|
| 72 |
+
export { StateErrors } from '#/state/errors';
|
| 73 |
+
|
| 74 |
+
export const ErrorCodes = {
|
| 75 |
+
...CoreErrors.codes,
|
| 76 |
+
...AgentLifecycleErrors.codes,
|
| 77 |
+
...AuthErrors.codes,
|
| 78 |
+
...TaskErrors.codes,
|
| 79 |
+
...ProtocolErrors.codes,
|
| 80 |
+
...ConfigErrors.codes,
|
| 81 |
+
...CapabilityErrors.codes,
|
| 82 |
+
...CronErrors.codes,
|
| 83 |
+
...DebugErrors.codes,
|
| 84 |
+
...FileErrors.codes,
|
| 85 |
+
...FsErrors.codes,
|
| 86 |
+
...FullCompactionErrors.codes,
|
| 87 |
+
...GoalErrors.codes,
|
| 88 |
+
...LoopErrors.codes,
|
| 89 |
+
...McpErrors.codes,
|
| 90 |
+
...ModelCatalogErrors.codes,
|
| 91 |
+
...OsFsErrors.codes,
|
| 92 |
+
...OsProcessErrors.codes,
|
| 93 |
+
...PluginErrors.codes,
|
| 94 |
+
...ProfileErrors.codes,
|
| 95 |
+
...PromptErrors.codes,
|
| 96 |
+
...ModelsDevImportErrors.codes,
|
| 97 |
+
...SessionExportErrors.codes,
|
| 98 |
+
...SessionErrors.codes,
|
| 99 |
+
...SkillErrors.codes,
|
| 100 |
+
...StorageErrors.codes,
|
| 101 |
+
...TerminalErrors.codes,
|
| 102 |
+
...UsageErrors.codes,
|
| 103 |
+
...WebErrors.codes,
|
| 104 |
+
...WireErrors.codes,
|
| 105 |
+
...WorkspaceErrors.codes,
|
| 106 |
+
...EventErrors.codes,
|
| 107 |
+
...StateErrors.codes,
|
| 108 |
+
} as const;
|
| 109 |
+
|
| 110 |
+
export type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];
|
| 111 |
+
|
| 112 |
+
export type KimiErrorCode =
|
| 113 |
+
| 'config.invalid'
|
| 114 |
+
| 'config.persist_blocked'
|
| 115 |
+
| 'session.not_found'
|
| 116 |
+
| 'session.already_exists'
|
| 117 |
+
| 'session.id_invalid'
|
| 118 |
+
| 'session.id_required'
|
| 119 |
+
| 'session.id_empty'
|
| 120 |
+
| 'session.title_empty'
|
| 121 |
+
| 'session.state_not_found'
|
| 122 |
+
| 'session.state_invalid'
|
| 123 |
+
| 'session.fork_active_turn'
|
| 124 |
+
| 'session.undo_unavailable'
|
| 125 |
+
| 'session.export_not_found'
|
| 126 |
+
| 'session.export_missing_version'
|
| 127 |
+
| 'session.export_output_conflict'
|
| 128 |
+
| 'session.export_too_large'
|
| 129 |
+
| 'session.closed'
|
| 130 |
+
| 'session.permission_mode_invalid'
|
| 131 |
+
| 'session.thinking_empty'
|
| 132 |
+
| 'session.model_empty'
|
| 133 |
+
| 'session.plan_mode_invalid'
|
| 134 |
+
| 'session.approval_handler_error'
|
| 135 |
+
| 'session.question_handler_error'
|
| 136 |
+
| 'session.init_failed'
|
| 137 |
+
| 'agent.not_found'
|
| 138 |
+
| 'agent.already_exists'
|
| 139 |
+
| 'agent.already_running'
|
| 140 |
+
| 'agent.not_a_subagent'
|
| 141 |
+
| 'agent.not_owned'
|
| 142 |
+
| 'agent.type_not_allowed'
|
| 143 |
+
| 'agent.max_tokens_exceeded'
|
| 144 |
+
| 'turn.agent_busy'
|
| 145 |
+
| 'goal.already_exists'
|
| 146 |
+
| 'goal.not_found'
|
| 147 |
+
| 'goal.objective_empty'
|
| 148 |
+
| 'goal.objective_too_long'
|
| 149 |
+
| 'goal.status_invalid'
|
| 150 |
+
| 'goal.metadata_reserved'
|
| 151 |
+
| 'goal.not_resumable'
|
| 152 |
+
| 'goal.unsupported_agent'
|
| 153 |
+
| 'model.not_configured'
|
| 154 |
+
| 'model.config_invalid'
|
| 155 |
+
| 'profile.thinking_alias_conflict'
|
| 156 |
+
| 'profile.unknown'
|
| 157 |
+
| 'profile.already_bound'
|
| 158 |
+
| 'profile.not_bound'
|
| 159 |
+
| 'model.not_found'
|
| 160 |
+
| 'auth.login_required'
|
| 161 |
+
| 'auth.provisioning_required'
|
| 162 |
+
| 'auth.token_missing'
|
| 163 |
+
| 'auth.token_unauthorized'
|
| 164 |
+
| 'auth.model_not_resolved'
|
| 165 |
+
| 'context.overflow'
|
| 166 |
+
| 'loop.max_steps_exceeded'
|
| 167 |
+
| 'provider.api_error'
|
| 168 |
+
| 'provider.filtered'
|
| 169 |
+
| 'provider.rate_limit'
|
| 170 |
+
| 'provider.auth_error'
|
| 171 |
+
| 'provider.connection_error'
|
| 172 |
+
| 'provider.overloaded'
|
| 173 |
+
| 'provider.not_found'
|
| 174 |
+
| 'skill.not_found'
|
| 175 |
+
| 'skill.type_unsupported'
|
| 176 |
+
| 'skill.name_empty'
|
| 177 |
+
| 'skill.parse_failed'
|
| 178 |
+
| 'skill.nested_too_deep'
|
| 179 |
+
| 'records.write_failed'
|
| 180 |
+
| 'compaction.failed'
|
| 181 |
+
| 'compaction.unable'
|
| 182 |
+
| 'task.task_id_empty'
|
| 183 |
+
| 'task.limit_exceeded'
|
| 184 |
+
| 'usage.turn_id_conflict'
|
| 185 |
+
| 'mcp.server_not_found'
|
| 186 |
+
| 'mcp.server_disabled'
|
| 187 |
+
| 'mcp.startup_failed'
|
| 188 |
+
| 'mcp.tool_name_collision'
|
| 189 |
+
| 'mcp.oauth_failed'
|
| 190 |
+
| 'message.not_found'
|
| 191 |
+
| 'plugin.not_found'
|
| 192 |
+
| 'plugin.load_failed'
|
| 193 |
+
| 'request.invalid'
|
| 194 |
+
| 'request.work_dir_required'
|
| 195 |
+
| 'request.prompt_input_empty'
|
| 196 |
+
| 'prompt.id_conflict'
|
| 197 |
+
| 'prompt.not_found'
|
| 198 |
+
| 'session.busy'
|
| 199 |
+
| 'shell.git_bash_not_found'
|
| 200 |
+
| 'workspace.not_found'
|
| 201 |
+
| 'terminal.not_found'
|
| 202 |
+
| 'file.not_found'
|
| 203 |
+
| 'file.too_large'
|
| 204 |
+
| 'fs.path_not_found'
|
| 205 |
+
| 'fs.permission_denied'
|
| 206 |
+
| 'fs.path_escapes'
|
| 207 |
+
| 'fs.is_directory'
|
| 208 |
+
| 'fs.is_binary'
|
| 209 |
+
| 'fs.too_large'
|
| 210 |
+
| 'fs.already_exists'
|
| 211 |
+
| 'fs.too_many_results'
|
| 212 |
+
| 'fs.grep_timeout'
|
| 213 |
+
| 'fs.git_unavailable'
|
| 214 |
+
| 'os.fs.not_found'
|
| 215 |
+
| 'os.fs.is_directory'
|
| 216 |
+
| 'os.fs.not_directory'
|
| 217 |
+
| 'os.fs.already_exists'
|
| 218 |
+
| 'os.fs.permission_denied'
|
| 219 |
+
| 'os.fs.not_empty'
|
| 220 |
+
| 'os.fs.unavailable'
|
| 221 |
+
| 'os.fs.unknown'
|
| 222 |
+
| 'os.process.spawn_failed'
|
| 223 |
+
| 'os.process.kill_failed'
|
| 224 |
+
| 'storage.not_found'
|
| 225 |
+
| 'storage.decode_failed'
|
| 226 |
+
| 'storage.corrupted'
|
| 227 |
+
| 'storage.io_failed'
|
| 228 |
+
| 'storage.locked'
|
| 229 |
+
| 'storage.permission_denied'
|
| 230 |
+
| 'storage.disk_full'
|
| 231 |
+
| 'wire.duplicate_op'
|
| 232 |
+
| 'wire.cycle'
|
| 233 |
+
| 'wire.unknown_record'
|
| 234 |
+
| 'wire.migration_missing'
|
| 235 |
+
| 'cron.expression_invalid'
|
| 236 |
+
| 'web.invalid_url'
|
| 237 |
+
| 'web.private_address'
|
| 238 |
+
| 'web.fetch_failed'
|
| 239 |
+
| 'validation.failed'
|
| 240 |
+
| 'not_implemented'
|
| 241 |
+
| 'internal';
|
| 242 |
+
|
| 243 |
+
export interface ErrorEvent {
|
| 244 |
+
readonly type: 'error';
|
| 245 |
+
readonly code: KimiErrorCode;
|
| 246 |
+
readonly message: string;
|
| 247 |
+
readonly name?: string;
|
| 248 |
+
readonly details?: Record<string, unknown>;
|
| 249 |
+
readonly retryable: boolean;
|
| 250 |
+
readonly cause?: KimiErrorPayload;
|
| 251 |
+
}
|
| 252 |
+
|
| 253 |
+
export interface WarningEvent {
|
| 254 |
+
readonly type: 'warning';
|
| 255 |
+
readonly message: string;
|
| 256 |
+
readonly code?: string;
|
| 257 |
+
}
|
packages/agent-core-v2/src/events.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { CompactionBlockedEvent, CompactionCancelledEvent, CompactionCompletedEvent, CompactionStartedEvent } from '#/agent/fullCompaction/compactionOps';
|
| 2 |
+
import type { TurnStartedEvent, TurnStepCompletedEvent, TurnStepInterruptedEvent, TurnStepRetryingEvent, TurnStepStartedEvent, AssistantDeltaEvent, ThinkingDeltaEvent } from '#/agent/loop/turnEvents';
|
| 3 |
+
import type { TurnEndedEvent } from '#/agent/loop/turnOps';
|
| 4 |
+
import type { PluginCommandActivatedEvent } from '#/agent/pluginCommand/pluginCommand';
|
| 5 |
+
import type { PromptAbortedEvent, PromptCompletedEvent, PromptSteeredEvent, PromptSubmittedEvent } from '#/agent/prompt/promptEvents';
|
| 6 |
+
import type { BackgroundTaskStartedEvent, BackgroundTaskTerminatedEvent, TaskStartedEvent, TaskTerminatedEvent } from '#/agent/task/types';
|
| 7 |
+
import type { McpServerStatusEvent, ShellCompletedEvent, ShellOutputEvent, ShellStartedEvent, ToolCallDeltaEvent, ToolCallStartedEvent, ToolListUpdatedEvent, ToolProgressEvent } from '#/agent/toolExecutor/toolExecutorEvents';
|
| 8 |
+
import type { ToolResultEventPayload } from '#/agent/toolExecutor/toolExecutorEvents';
|
| 9 |
+
import type { AgentStatusUpdatedEvent } from '#/agent/usage/usageEvents';
|
| 10 |
+
import type { CapabilityChangedEvent } from '#/app/capability/capabilityEvents';
|
| 11 |
+
import type { ConfigChangedEvent, ConfigWarningEvent } from '#/app/config/configEvents';
|
| 12 |
+
import type { ModelCatalogChangedEvent } from '#/app/kosongConfig/discovery';
|
| 13 |
+
import type { PluginChangedEvent } from '#/app/plugin/pluginEvents';
|
| 14 |
+
import type { SessionCreatedEvent, SessionStatusChangedEvent, SessionWorkChangedEvent } from '#/app/sessionLegacy/sessionProtocol';
|
| 15 |
+
import type { WorkspaceCreatedEvent, WorkspaceDeletedEvent, WorkspaceUpdatedEvent } from '#/app/workspace/workspaceProtocol';
|
| 16 |
+
import type { CronFiredEvent } from '#/features/cron/cronOps';
|
| 17 |
+
import type { HookResultEvent } from '#/features/externalHooks/agent/agentExternalHooksService';
|
| 18 |
+
import type { GoalUpdatedEvent } from '#/features/goal/goalOps';
|
| 19 |
+
import type { SkillActivatedEvent } from '#/features/skill/skillOps';
|
| 20 |
+
import type { SubagentSuspendedEvent } from '#/features/swarm/session/sessionSwarmService';
|
| 21 |
+
import type { SessionMetaUpdatedEvent } from '#/session/sessionMetadata/sessionMetaEvents';
|
| 22 |
+
import type { SubagentCancelledEvent, SubagentCompletedEvent, SubagentFailedEvent, SubagentSpawnedEvent, SubagentStartedEvent } from '#/session/subagent/mirrorAgentRun';
|
| 23 |
+
|
| 24 |
+
import type { ErrorEvent, WarningEvent } from './errors';
|
| 25 |
+
|
| 26 |
+
export interface ToolResultEvent extends Omit<ToolResultEventPayload, 'agentId'> {
|
| 27 |
+
readonly type: 'tool.result';
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
export type AgentEvent =
|
| 31 |
+
| ErrorEvent
|
| 32 |
+
| WarningEvent
|
| 33 |
+
| AgentStatusUpdatedEvent
|
| 34 |
+
| SessionMetaUpdatedEvent
|
| 35 |
+
| SessionCreatedEvent
|
| 36 |
+
| WorkspaceCreatedEvent
|
| 37 |
+
| WorkspaceUpdatedEvent
|
| 38 |
+
| WorkspaceDeletedEvent
|
| 39 |
+
| SessionWorkChangedEvent
|
| 40 |
+
| SessionStatusChangedEvent
|
| 41 |
+
| ConfigChangedEvent
|
| 42 |
+
| ConfigWarningEvent
|
| 43 |
+
| ModelCatalogChangedEvent
|
| 44 |
+
| PluginChangedEvent
|
| 45 |
+
| CapabilityChangedEvent
|
| 46 |
+
| GoalUpdatedEvent
|
| 47 |
+
| SkillActivatedEvent
|
| 48 |
+
| PluginCommandActivatedEvent
|
| 49 |
+
| TurnStartedEvent
|
| 50 |
+
| TurnEndedEvent
|
| 51 |
+
| TurnStepStartedEvent
|
| 52 |
+
| TurnStepCompletedEvent
|
| 53 |
+
| TurnStepRetryingEvent
|
| 54 |
+
| TurnStepInterruptedEvent
|
| 55 |
+
| AssistantDeltaEvent
|
| 56 |
+
| HookResultEvent
|
| 57 |
+
| ThinkingDeltaEvent
|
| 58 |
+
| ToolCallDeltaEvent
|
| 59 |
+
| ToolCallStartedEvent
|
| 60 |
+
| ToolProgressEvent
|
| 61 |
+
| ShellOutputEvent
|
| 62 |
+
| ShellStartedEvent
|
| 63 |
+
| ShellCompletedEvent
|
| 64 |
+
| ToolResultEvent
|
| 65 |
+
| ToolListUpdatedEvent
|
| 66 |
+
| McpServerStatusEvent
|
| 67 |
+
| SubagentSpawnedEvent
|
| 68 |
+
| SubagentStartedEvent
|
| 69 |
+
| SubagentSuspendedEvent
|
| 70 |
+
| SubagentCompletedEvent
|
| 71 |
+
| SubagentFailedEvent
|
| 72 |
+
| SubagentCancelledEvent
|
| 73 |
+
| CompactionStartedEvent
|
| 74 |
+
| CompactionBlockedEvent
|
| 75 |
+
| CompactionCancelledEvent
|
| 76 |
+
| CompactionCompletedEvent
|
| 77 |
+
| TaskStartedEvent
|
| 78 |
+
| TaskTerminatedEvent
|
| 79 |
+
| BackgroundTaskStartedEvent
|
| 80 |
+
| BackgroundTaskTerminatedEvent
|
| 81 |
+
| CronFiredEvent
|
| 82 |
+
| PromptSubmittedEvent
|
| 83 |
+
| PromptCompletedEvent
|
| 84 |
+
| PromptAbortedEvent
|
| 85 |
+
| PromptSteeredEvent;
|
| 86 |
+
|
| 87 |
+
export type Event = AgentEvent & { agentId: string; sessionId: string };
|
packages/agent-core-v2/src/features/feature.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { type CollectionToken } from '#/_base/di/collection';
|
| 2 |
+
import {
|
| 3 |
+
ScopeUnits,
|
| 4 |
+
type Fiber,
|
| 5 |
+
type FiberHandle,
|
| 6 |
+
type FiberProvideOptions,
|
| 7 |
+
type ServiceClassRecipe,
|
| 8 |
+
} from '#/_base/di/fiber';
|
| 9 |
+
import { ScopeActivation, type ServiceIdentifier } from '#/_base/di/instantiation';
|
| 10 |
+
import { toDisposable } from '#/_base/di/lifecycle';
|
| 11 |
+
import { Service } from '#/_base/di/service';
|
| 12 |
+
import {
|
| 13 |
+
AgentProfileContribution,
|
| 14 |
+
AGENT_PROFILE_SOURCE_PRIORITY,
|
| 15 |
+
} from '#/app/agentProfileCatalog/agentProfileContribution';
|
| 16 |
+
import { FeatureServiceContribution } from '#/app/feature/featureServiceContribution';
|
| 17 |
+
import type { AgentProfile } from '#/app/agentProfileCatalog/agentProfileCatalog';
|
| 18 |
+
import type { ConfigSchema, RegisterSectionOptions } from '#/app/config/config';
|
| 19 |
+
import { ConfigSectionContribution } from '#/app/config/configSectionContributions';
|
| 20 |
+
import { LifecycleScope } from '#/app/scopes';
|
| 21 |
+
import {
|
| 22 |
+
CommandContribution,
|
| 23 |
+
type CommandContribution as CommandContributionPayload,
|
| 24 |
+
} from '#/agent/command/commandContribution';
|
| 25 |
+
import {
|
| 26 |
+
AgentToolContribution,
|
| 27 |
+
type AgentToolContributionOptions,
|
| 28 |
+
type AgentToolCtor,
|
| 29 |
+
type AnyAgentTool,
|
| 30 |
+
} from '#/agent/toolRegistry/toolContribution';
|
| 31 |
+
import type {
|
| 32 |
+
AgentModel,
|
| 33 |
+
AgentModelDefinition,
|
| 34 |
+
SessionModelDefinition,
|
| 35 |
+
} from '#/state/agentModel';
|
| 36 |
+
import { AgentModelContribution, SessionModelContribution } from '#/state/agentModel';
|
| 37 |
+
|
| 38 |
+
export abstract class Feature extends Service {
|
| 39 |
+
contribute<T>(token: CollectionToken<T>, value: T): FiberHandle {
|
| 40 |
+
return this.provide(token, value);
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
contributeSessionModel<State>(definition: SessionModelDefinition<State>): FiberHandle {
|
| 44 |
+
return this.provide(SessionModelContribution, definition as SessionModelDefinition);
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
contributeAgentModel<S, M extends AgentModel<S>>(
|
| 48 |
+
definition: AgentModelDefinition<S, M>,
|
| 49 |
+
): FiberHandle {
|
| 50 |
+
return this.provide(AgentModelContribution, definition as AgentModelDefinition<any, any>);
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
contributeConfig<T>(
|
| 54 |
+
domain: string,
|
| 55 |
+
schema: ConfigSchema<T>,
|
| 56 |
+
options: RegisterSectionOptions<T> = {},
|
| 57 |
+
): FiberHandle {
|
| 58 |
+
return this.provide(ConfigSectionContribution, {
|
| 59 |
+
domain,
|
| 60 |
+
schema: schema as ConfigSchema<unknown>,
|
| 61 |
+
options: options as RegisterSectionOptions<unknown>,
|
| 62 |
+
});
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
contributeService<T>(
|
| 66 |
+
scope: LifecycleScope,
|
| 67 |
+
id: ServiceIdentifier<T>,
|
| 68 |
+
ctor: ServiceClassRecipe,
|
| 69 |
+
opts?: FiberProvideOptions,
|
| 70 |
+
): FiberHandle {
|
| 71 |
+
this.provide(FeatureServiceContribution, { scope, id });
|
| 72 |
+
return this.provide(ScopeUnits(scope), {
|
| 73 |
+
name: `${this.name}:${String(id)}`,
|
| 74 |
+
apply(fiber: Fiber): void {
|
| 75 |
+
fiber.provide(id, ctor, opts);
|
| 76 |
+
},
|
| 77 |
+
});
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
contributeAgentService<T>(
|
| 81 |
+
id: ServiceIdentifier<T>,
|
| 82 |
+
ctor: ServiceClassRecipe,
|
| 83 |
+
opts?: FiberProvideOptions,
|
| 84 |
+
): FiberHandle {
|
| 85 |
+
return this.contributeService(LifecycleScope.Agent, id, ctor, opts);
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
contributeTool<T extends AnyAgentTool>(
|
| 89 |
+
id: ServiceIdentifier<T>,
|
| 90 |
+
ctor: AgentToolCtor<T>,
|
| 91 |
+
options: AgentToolContributionOptions,
|
| 92 |
+
): void {
|
| 93 |
+
this.contributeService(LifecycleScope.Agent, id, ctor, {
|
| 94 |
+
activation: ScopeActivation.OnDemand,
|
| 95 |
+
});
|
| 96 |
+
this.provide(AgentToolContribution, { id, ctor, options });
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
contributeCommand(contribution: CommandContributionPayload): FiberHandle {
|
| 100 |
+
return this.provide(CommandContribution, contribution);
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
contributeProfiles(
|
| 104 |
+
profiles: readonly AgentProfile[],
|
| 105 |
+
opts?: { readonly sourceId?: string; readonly priority?: number },
|
| 106 |
+
): FiberHandle {
|
| 107 |
+
return this.provide(AgentProfileContribution, {
|
| 108 |
+
sourceId: opts?.sourceId ?? `feature:${this.name}`,
|
| 109 |
+
priority: opts?.priority ?? AGENT_PROFILE_SOURCE_PRIORITY.builtin,
|
| 110 |
+
contribution: { profiles },
|
| 111 |
+
});
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
onDispose(fn: () => void): void {
|
| 115 |
+
this._register(toDisposable(fn));
|
| 116 |
+
}
|
| 117 |
+
}
|
packages/agent-core-v2/src/features/featureAssembly.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { createDecorator, type ServiceIdentifier } from '#/_base/di/instantiation';
|
| 2 |
+
|
| 3 |
+
export interface IFeatureAssemblyService {
|
| 4 |
+
readonly _serviceBrand: undefined;
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
export const IFeatureAssemblyService: ServiceIdentifier<IFeatureAssemblyService> =
|
| 8 |
+
createDecorator<IFeatureAssemblyService>('featureAssemblyService');
|
packages/agent-core-v2/src/features/featureAssemblyService.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { IFeatureManager } from '#/app/feature/featureManager';
|
| 2 |
+
import { LifecycleScope } from '#/app/scopes';
|
| 3 |
+
import { ScopeActivation, registerScopedService } from '#/_base/di/scope';
|
| 4 |
+
import { Service } from '#/_base/di/service';
|
| 5 |
+
|
| 6 |
+
import { IFeatureAssemblyService } from './featureAssembly';
|
| 7 |
+
import { getFeatureRecipes } from './featureRegistry';
|
| 8 |
+
|
| 9 |
+
export class FeatureAssemblyService extends Service implements IFeatureAssemblyService {
|
| 10 |
+
declare readonly _serviceBrand: undefined;
|
| 11 |
+
|
| 12 |
+
constructor(@IFeatureManager featureManager: IFeatureManager) {
|
| 13 |
+
super();
|
| 14 |
+
for (const recipe of getFeatureRecipes()) {
|
| 15 |
+
featureManager.provideUnit(recipe);
|
| 16 |
+
}
|
| 17 |
+
}
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
registerScopedService(
|
| 21 |
+
LifecycleScope.App,
|
| 22 |
+
IFeatureAssemblyService,
|
| 23 |
+
FeatureAssemblyService,
|
| 24 |
+
ScopeActivation.OnScopeCreated,
|
| 25 |
+
'features',
|
| 26 |
+
);
|
packages/agent-core-v2/src/features/featureRegistry.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { ServiceClassRecipe } from '#/_base/di/fiber';
|
| 2 |
+
|
| 3 |
+
const _featureRecipes: ServiceClassRecipe[] = [];
|
| 4 |
+
|
| 5 |
+
export function registerFeature(recipe: ServiceClassRecipe): void {
|
| 6 |
+
_featureRecipes.push(recipe);
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
export function getFeatureRecipes(): readonly ServiceClassRecipe[] {
|
| 10 |
+
return _featureRecipes;
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
export function _clearFeatureRecipesForTests(): void {
|
| 14 |
+
_featureRecipes.length = 0;
|
| 15 |
+
}
|
packages/agent-core-v2/src/hooks.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { toDisposable, type IDisposable } from "#/_base/di/lifecycle";
|
| 2 |
+
import { BugIndicatingError } from "#/errors";
|
| 3 |
+
|
| 4 |
+
export type Hooks<TEvents extends Record<string, unknown>> = {
|
| 5 |
+
readonly [K in keyof TEvents]: HookSlot<TEvents[K]>;
|
| 6 |
+
};
|
| 7 |
+
|
| 8 |
+
export interface HookSlot<TContext> {
|
| 9 |
+
register(
|
| 10 |
+
id: string,
|
| 11 |
+
handler: HookHandler<TContext>,
|
| 12 |
+
options?: HookRegisterOptions,
|
| 13 |
+
): IDisposable;
|
| 14 |
+
|
| 15 |
+
delete(id: string): boolean;
|
| 16 |
+
|
| 17 |
+
run(context: TContext, terminal?: (context: TContext) => Promise<void>): Promise<void>;
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
export type HookHandler<TContext> = (
|
| 21 |
+
context: TContext,
|
| 22 |
+
next: (context?: TContext) => Promise<void>,
|
| 23 |
+
) => void | Promise<void>;
|
| 24 |
+
|
| 25 |
+
export interface HookRegisterOptions {
|
| 26 |
+
before?: string;
|
| 27 |
+
after?: string;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
interface HookEntry<TContext> {
|
| 31 |
+
readonly id: string;
|
| 32 |
+
readonly handler: HookHandler<TContext>;
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
export class OrderedHookSlot<TContext> implements HookSlot<TContext> {
|
| 36 |
+
private entries: HookEntry<TContext>[] = [];
|
| 37 |
+
|
| 38 |
+
register(
|
| 39 |
+
id: string,
|
| 40 |
+
handler: HookHandler<TContext>,
|
| 41 |
+
options: HookRegisterOptions = {},
|
| 42 |
+
): IDisposable {
|
| 43 |
+
if (options.before !== undefined && options.after !== undefined) {
|
| 44 |
+
throw new BugIndicatingError('Hook registration cannot specify both before and after');
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
this.delete(id);
|
| 48 |
+
const entry = { id, handler };
|
| 49 |
+
const target = options.before ?? options.after;
|
| 50 |
+
if (target === undefined) {
|
| 51 |
+
this.entries.push(entry);
|
| 52 |
+
return this.toEntryDisposable(entry);
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
const targetIndex = this.entries.findIndex((item) => item.id === target);
|
| 56 |
+
if (targetIndex < 0) {
|
| 57 |
+
throw new BugIndicatingError(`Hook target "${target}" is not registered`);
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
const insertAt = options.before !== undefined ? targetIndex : targetIndex + 1;
|
| 61 |
+
this.entries.splice(insertAt, 0, entry);
|
| 62 |
+
return this.toEntryDisposable(entry);
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
delete(id: string): boolean {
|
| 66 |
+
const index = this.entries.findIndex((entry) => entry.id === id);
|
| 67 |
+
if (index < 0) return false;
|
| 68 |
+
this.entries.splice(index, 1);
|
| 69 |
+
return true;
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
asDisposable(id: string): IDisposable {
|
| 73 |
+
return toDisposable(() => {
|
| 74 |
+
this.delete(id);
|
| 75 |
+
});
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
private toEntryDisposable(entry: HookEntry<TContext>): IDisposable {
|
| 79 |
+
return toDisposable(() => {
|
| 80 |
+
const index = this.entries.indexOf(entry);
|
| 81 |
+
if (index < 0) return;
|
| 82 |
+
this.entries.splice(index, 1);
|
| 83 |
+
});
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
async run(
|
| 87 |
+
context: TContext,
|
| 88 |
+
terminal: (context: TContext) => Promise<void> = async () => {},
|
| 89 |
+
): Promise<void> {
|
| 90 |
+
const entries = [...this.entries];
|
| 91 |
+
const dispatch = (index: number, ctx: TContext): ((override?: TContext) => Promise<void>) => {
|
| 92 |
+
return async (override?: TContext): Promise<void> => {
|
| 93 |
+
const current = override ?? ctx;
|
| 94 |
+
const entry = entries[index];
|
| 95 |
+
if (entry === undefined) {
|
| 96 |
+
await terminal(current);
|
| 97 |
+
return;
|
| 98 |
+
}
|
| 99 |
+
await entry.handler(current, dispatch(index + 1, current));
|
| 100 |
+
};
|
| 101 |
+
};
|
| 102 |
+
await dispatch(0, context)();
|
| 103 |
+
}
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
export function createHooks<TEvents extends Record<string, unknown>, TKeys extends keyof TEvents>(
|
| 107 |
+
keys: readonly TKeys[],
|
| 108 |
+
): Hooks<TEvents> {
|
| 109 |
+
return Object.fromEntries(
|
| 110 |
+
keys.map((key) => [key, new OrderedHookSlot<TEvents[TKeys]>()]),
|
| 111 |
+
) as unknown as Hooks<TEvents>;
|
| 112 |
+
}
|
packages/agent-core-v2/src/human/index.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export * from './llm/message';
|
| 2 |
+
export * from './llm/model';
|
| 3 |
+
export * from './llm/capability';
|
| 4 |
+
export * from './models-dev/models-dev';
|
| 5 |
+
export * from './llm/errors';
|
| 6 |
+
export * from './llm/syntax-errors';
|
| 7 |
+
export * from './llm/thinking';
|
| 8 |
+
export * from './llm/response-format';
|
| 9 |
+
export * from './llm/finish-reason';
|
| 10 |
+
export * from './llm/usage';
|
| 11 |
+
export * from './plugin';
|
| 12 |
+
export * from './llm/protocol/format';
|
| 13 |
+
export * from './llm/protocol/base';
|
| 14 |
+
export * from './llm/protocol/connection';
|
| 15 |
+
export * from './llm/protocol/thinking';
|
| 16 |
+
export * from './llm/protocol/rewrite';
|
| 17 |
+
export * from './llm/protocol/patterns';
|
| 18 |
+
export * from './llm/media';
|
| 19 |
+
export * from './llm/requester/requester';
|
| 20 |
+
export * from './llm/empty-response';
|
| 21 |
+
export * from './llm/requester/actor';
|
| 22 |
+
export * from './llm/requester/recovery';
|
| 23 |
+
export * from './llm/requester/retry';
|
| 24 |
+
export * from './llm/requester/bases/openai/contract';
|
| 25 |
+
export * from './llm/requester/bases/openai/capability';
|
| 26 |
+
export * from './llm/requester/bases/openai/trait';
|
| 27 |
+
export * from './llm/requester/bases/openai/extra-params';
|
| 28 |
+
export * from './llm/requester/bases/openai/requester';
|
| 29 |
+
export * from './llm/requester/bases/openai-responses/contract';
|
| 30 |
+
export * from './llm/requester/bases/openai-responses/capability';
|
| 31 |
+
export * from './llm/requester/bases/openai-responses/trait';
|
| 32 |
+
export * from './llm/requester/bases/openai-responses/extra-params';
|
| 33 |
+
export * from './llm/requester/bases/openai-responses/requester';
|
| 34 |
+
export * from './llm/requester/bases/google-genai/contract';
|
| 35 |
+
export * from './llm/requester/bases/google-genai/capability';
|
| 36 |
+
export * from './llm/requester/bases/google-genai/trait';
|
| 37 |
+
export * from './llm/requester/bases/google-genai/extra-params';
|
| 38 |
+
export * from './llm/requester/bases/google-genai/requester';
|
| 39 |
+
export * from './llm/requester/bases/anthropic/contract';
|
| 40 |
+
export * from './llm/requester/bases/anthropic/capability';
|
| 41 |
+
export * from './llm/requester/bases/anthropic/trait';
|
| 42 |
+
export * from './llm/requester/bases/anthropic/extra-params';
|
| 43 |
+
export * from './llm/requester/bases/anthropic/profile';
|
| 44 |
+
export * from './llm/requester/bases/anthropic/requester';
|
| 45 |
+
export * from './llm/requester/bases/tool-call-id';
|
| 46 |
+
export * from './llm/requester/bases/tool-result-text';
|
| 47 |
+
export * from './llm/provider/definition';
|
| 48 |
+
export * from './llm/provider-catalog';
|
| 49 |
+
export * from './llm-kimi/provider';
|
| 50 |
+
export * from './llm-kimi/errors';
|
| 51 |
+
export * from './llm-kimi/files';
|
| 52 |
+
export * from './llm-kimi/media';
|
| 53 |
+
export * from './llm-kimi/schema';
|
| 54 |
+
export * from './llm-kimi/trait';
|
| 55 |
+
export * from './llm/provider/providers/standard';
|
| 56 |
+
export * from './credentials';
|
| 57 |
+
export * from './tool/executor';
|
| 58 |
+
export * from './tool/machine';
|
| 59 |
+
export * from './tool/wait-for';
|
| 60 |
+
export * from './tool/tool';
|
| 61 |
+
export * from './media/tool';
|
| 62 |
+
export * from './agent/errors';
|
| 63 |
+
export * from './agent/machine';
|
| 64 |
+
export * from './agent/wait-for';
|
| 65 |
+
export * from './agent/turn';
|
| 66 |
+
export * from './agent/context-usage';
|
| 67 |
+
export * from './agent/events';
|
| 68 |
+
export * from './agent/slices';
|
| 69 |
+
export * from './agent/historySchema';
|
| 70 |
+
export * from './persist/open';
|
| 71 |
+
export * from './eventStore/index';
|
| 72 |
+
export * from './persist/v2/migrate';
|
| 73 |
+
export * from './session/machine';
|
| 74 |
+
export * from './session/events';
|
| 75 |
+
export * from './session/slices';
|
| 76 |
+
export * from './session/stores';
|
| 77 |
+
export * from './compaction/controller';
|
| 78 |
+
export * from './compaction/errors';
|
| 79 |
+
export * from './compaction/shape';
|
| 80 |
+
export * from './compaction/summarize';
|
| 81 |
+
export * from './usage/usage';
|
| 82 |
+
export * from './usage/machine';
|
| 83 |
+
export * from './usage/plugin';
|
| 84 |
+
export * from './timing/plugin';
|
| 85 |
+
export * from './interaction/interaction';
|
| 86 |
+
export * from './interaction/machine';
|
| 87 |
+
export * from './interaction/facade';
|
| 88 |
+
export * from './kimi/trace';
|
| 89 |
+
export * from './todo/todoItem';
|
| 90 |
+
export * from './todo/slice';
|
| 91 |
+
export * from './todo/tool';
|
| 92 |
+
export * from './todo/plugin';
|
| 93 |
+
export * from './tool-select/state';
|
| 94 |
+
export * from './tool-select/tool';
|
| 95 |
+
export * from './tool-select/plugin';
|
| 96 |
+
export * from './tool-select/resolver';
|
| 97 |
+
export * from './store';
|
packages/agent-core-v2/src/human/package.json
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"type": "module",
|
| 3 |
+
"imports": {
|
| 4 |
+
"#/*": "./*.ts"
|
| 5 |
+
}
|
| 6 |
+
}
|
packages/agent-core-v2/src/human/plugin.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { SystemMessage, UserMessage } from '#/llm/message';
|
| 2 |
+
import type { AgentEmitted } from '#/agent/machine';
|
| 3 |
+
import { createSystemEntry, createUserEntry, type SystemEntry, type UserEntry } from '#/agent/turn';
|
| 4 |
+
import type { ToolDefinition } from '#/tool/tool';
|
| 5 |
+
|
| 6 |
+
export interface AgentPluginTarget {
|
| 7 |
+
kind: 'agent';
|
| 8 |
+
on(type: AgentEmitted['type'], handler: (event: AgentEmitted) => void): unknown;
|
| 9 |
+
notify(message: UserMessage): void;
|
| 10 |
+
remind(key: string, message: UserMessage | SystemMessage): void;
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
export type PluginTarget = AgentPluginTarget;
|
| 14 |
+
|
| 15 |
+
export interface Plugin {
|
| 16 |
+
readonly name: string;
|
| 17 |
+
tools?(): readonly ToolDefinition[];
|
| 18 |
+
connect?(target: PluginTarget): void;
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
export function collectPluginTools(plugins: readonly Plugin[]): readonly ToolDefinition[] {
|
| 22 |
+
return plugins.flatMap((plugin) => plugin.tools?.() ?? []);
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
export interface AgentPluginSource {
|
| 26 |
+
on(type: AgentEmitted['type'], handler: (event: AgentEmitted) => void): unknown;
|
| 27 |
+
send(
|
| 28 |
+
event:
|
| 29 |
+
| { type: 'input.notify'; entry: UserEntry }
|
| 30 |
+
| { type: 'input.remind'; key: string; entry: SystemEntry | UserEntry },
|
| 31 |
+
): void;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
export function connectPlugins(actor: AgentPluginSource, plugins: readonly Plugin[]): void {
|
| 35 |
+
const target: AgentPluginTarget = {
|
| 36 |
+
kind: 'agent',
|
| 37 |
+
on: (type, handler) => {
|
| 38 |
+
actor.on(type, handler);
|
| 39 |
+
},
|
| 40 |
+
notify: (message) => {
|
| 41 |
+
actor.send({ type: 'input.notify', entry: { message } });
|
| 42 |
+
},
|
| 43 |
+
remind: (key, message) => {
|
| 44 |
+
actor.send({
|
| 45 |
+
type: 'input.remind',
|
| 46 |
+
key,
|
| 47 |
+
entry: message.role === 'system' ? createSystemEntry(message) : createUserEntry(message),
|
| 48 |
+
});
|
| 49 |
+
},
|
| 50 |
+
};
|
| 51 |
+
for (const plugin of plugins) {
|
| 52 |
+
plugin.connect?.(target);
|
| 53 |
+
}
|
| 54 |
+
}
|
packages/agent-core-v2/src/human/xstate2.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { createActor as createXStateActor } from 'xstate';
|
| 2 |
+
import type { Actor, ActorOptions, AnyActorLogic } from 'xstate';
|
| 3 |
+
|
| 4 |
+
import { isAbortError } from '#/llm/errors';
|
| 5 |
+
import { xstateInspectionCollector } from '#/xstateInspection';
|
| 6 |
+
|
| 7 |
+
export * from 'xstate';
|
| 8 |
+
|
| 9 |
+
export type RootActorErrorReporter = (err: unknown) => void;
|
| 10 |
+
|
| 11 |
+
let reportRootActorError: RootActorErrorReporter = () => {};
|
| 12 |
+
|
| 13 |
+
export function setRootActorErrorReporter(reporter: RootActorErrorReporter): void {
|
| 14 |
+
reportRootActorError = reporter;
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
function createActorWithInspect<TLogic extends AnyActorLogic>(
|
| 18 |
+
logic: TLogic,
|
| 19 |
+
options?: ActorOptions<TLogic>,
|
| 20 |
+
): Actor<TLogic> {
|
| 21 |
+
const inspect = options?.inspect;
|
| 22 |
+
const actor = createXStateActor(logic, {
|
| 23 |
+
...options,
|
| 24 |
+
inspect: (event) => {
|
| 25 |
+
xstateInspectionCollector.publish(event);
|
| 26 |
+
if (typeof inspect === 'function') {
|
| 27 |
+
inspect(event);
|
| 28 |
+
} else {
|
| 29 |
+
inspect?.next?.(event);
|
| 30 |
+
}
|
| 31 |
+
},
|
| 32 |
+
});
|
| 33 |
+
swallowRootAbortError(actor);
|
| 34 |
+
return actor;
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
function swallowRootAbortError(actor: Actor<AnyActorLogic>): void {
|
| 38 |
+
const internal = actor as unknown as { _reportError(err: unknown): void };
|
| 39 |
+
const reportError = internal._reportError.bind(actor);
|
| 40 |
+
internal._reportError = (err: unknown) => {
|
| 41 |
+
if (isAbortError(err)) {
|
| 42 |
+
reportRootActorError(err);
|
| 43 |
+
return;
|
| 44 |
+
}
|
| 45 |
+
reportError(err);
|
| 46 |
+
};
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
export const createActor = createActorWithInspect as typeof createXStateActor;
|
packages/agent-core-v2/src/human/xstateInspection.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { InspectionEvent } from 'xstate';
|
| 2 |
+
|
| 3 |
+
export type XstateInspectionEventType = InspectionEvent['type'];
|
| 4 |
+
|
| 5 |
+
export interface XstateInspectionEnvelope {
|
| 6 |
+
readonly type: XstateInspectionEventType;
|
| 7 |
+
readonly timestamp: number;
|
| 8 |
+
readonly actorSessionId: string;
|
| 9 |
+
readonly actorId?: string;
|
| 10 |
+
readonly logicId?: string;
|
| 11 |
+
readonly eventType?: string;
|
| 12 |
+
readonly stateValue?: unknown;
|
| 13 |
+
readonly unhandled?: boolean;
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
export type XstateInspectionListener = (envelope: XstateInspectionEnvelope) => void;
|
| 17 |
+
|
| 18 |
+
export interface XstateInspectionCollector {
|
| 19 |
+
subscribe(listener: XstateInspectionListener): () => void;
|
| 20 |
+
publish(event: InspectionEvent): void;
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
function scalar(value: unknown): string | undefined {
|
| 24 |
+
return typeof value === 'string' ? value : undefined;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
function toEnvelope(event: InspectionEvent, now: () => number): XstateInspectionEnvelope {
|
| 28 |
+
const actorRef = event.actorRef as { id?: unknown; logic?: unknown };
|
| 29 |
+
const logic = actorRef.logic as { id?: unknown } | undefined;
|
| 30 |
+
const snapshot = 'snapshot' in event ? (event.snapshot as { value?: unknown }) : undefined;
|
| 31 |
+
const unhandled =
|
| 32 |
+
event.type === '@xstate.microstep' &&
|
| 33 |
+
event._transitions.length === 0 &&
|
| 34 |
+
!event.event.type.startsWith('xstate.');
|
| 35 |
+
return {
|
| 36 |
+
type: event.type,
|
| 37 |
+
timestamp: now(),
|
| 38 |
+
actorSessionId: event.actorRef.sessionId,
|
| 39 |
+
actorId: scalar(actorRef.id),
|
| 40 |
+
logicId: scalar(logic?.id),
|
| 41 |
+
eventType:
|
| 42 |
+
'event' in event
|
| 43 |
+
? event.event.type
|
| 44 |
+
: event.type === '@xstate.action'
|
| 45 |
+
? event.action.type
|
| 46 |
+
: undefined,
|
| 47 |
+
stateValue: snapshot?.value,
|
| 48 |
+
unhandled: unhandled || undefined,
|
| 49 |
+
};
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
export function createXstateInspectionCollector(input?: {
|
| 53 |
+
now?: () => number;
|
| 54 |
+
}): XstateInspectionCollector {
|
| 55 |
+
const now = input?.now ?? Date.now;
|
| 56 |
+
const listeners = new Set<XstateInspectionListener>();
|
| 57 |
+
return {
|
| 58 |
+
subscribe(listener) {
|
| 59 |
+
listeners.add(listener);
|
| 60 |
+
return () => {
|
| 61 |
+
listeners.delete(listener);
|
| 62 |
+
};
|
| 63 |
+
},
|
| 64 |
+
publish(event) {
|
| 65 |
+
if (listeners.size === 0) return;
|
| 66 |
+
const envelope = toEnvelope(event, now);
|
| 67 |
+
for (const listener of listeners) listener(envelope);
|
| 68 |
+
},
|
| 69 |
+
};
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
export const xstateInspectionCollector = createXstateInspectionCollector();
|
packages/agent-core-v2/src/index.ts
ADDED
|
@@ -0,0 +1,758 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export * from '#/_base/di/descriptors';
|
| 2 |
+
export * from '#/_base/di/errors';
|
| 3 |
+
export * from '#/_base/di/graph';
|
| 4 |
+
export * from '#/_base/di/instantiation';
|
| 5 |
+
export * from '#/_base/di/instantiationService';
|
| 6 |
+
export * from '#/_base/di/lifecycle';
|
| 7 |
+
export * from '#/_base/di/scope';
|
| 8 |
+
export * from './app/scopes';
|
| 9 |
+
export * from '#/_base/di/serviceCollection';
|
| 10 |
+
export * from '#/_base/di/cascadeEngine';
|
| 11 |
+
export * from '#/_base/di/dependencyGraph';
|
| 12 |
+
export * from '#/_base/lifecycle/ledger';
|
| 13 |
+
export {
|
| 14 |
+
collection,
|
| 15 |
+
isCollectionToken,
|
| 16 |
+
type CollectionChange,
|
| 17 |
+
type CollectionRecord,
|
| 18 |
+
type CollectionToken,
|
| 19 |
+
type CollectionView,
|
| 20 |
+
} from '#/_base/di/collection';
|
| 21 |
+
export {
|
| 22 |
+
FiberProtocolError,
|
| 23 |
+
FiberState,
|
| 24 |
+
ScopeUnits,
|
| 25 |
+
ServiceRecipeError,
|
| 26 |
+
setFiberEventResolver,
|
| 27 |
+
type ConfigSchema,
|
| 28 |
+
type Fiber,
|
| 29 |
+
type FiberHandle,
|
| 30 |
+
type FiberProvideOptions,
|
| 31 |
+
type RecipeStatics,
|
| 32 |
+
type ServiceRecipe,
|
| 33 |
+
} from '#/_base/di/fiber';
|
| 34 |
+
export { Service } from '#/_base/di/service';
|
| 35 |
+
export * from './errors';
|
| 36 |
+
export * from './events';
|
| 37 |
+
export * from '#/runtime/runtime';
|
| 38 |
+
export * from '#/runtime/runtimeRegistry';
|
| 39 |
+
export * from '#/runtime/runtimeWorkspaceView';
|
| 40 |
+
export * from '#/runtime/runtimeProvider';
|
| 41 |
+
export * from '#/runtime/runtimeUnitHost';
|
| 42 |
+
export * from '#/runtime/localRuntime';
|
| 43 |
+
export * from '#/runtime/standaloneRuntime';
|
| 44 |
+
export * from '#/program/program';
|
| 45 |
+
export * from '#/workspace/workspaceInstance/workspaceInstance';
|
| 46 |
+
export * from '#/workspace/workspaceInstance/workspaceInstanceManager';
|
| 47 |
+
export * from '#/workspace/workspaceInstance/workspaceInstanceManagerService';
|
| 48 |
+
export * from '#/agent/runtimeBinding/runtimeBinding';
|
| 49 |
+
export * from '#/agent/runtimeBinding/runtimeBindingService';
|
| 50 |
+
export * from '#/agent/runtimeBinding/agentRuntime';
|
| 51 |
+
export * from '#/app/sessionManager/sessionManager';
|
| 52 |
+
export * from '#/app/sessionManager/sessionManagerService';
|
| 53 |
+
|
| 54 |
+
export * from '#/_base/log/log';
|
| 55 |
+
export * from '#/_base/log/logConfig';
|
| 56 |
+
export * from '#/_base/log/formatter';
|
| 57 |
+
export * from '#/_base/log/fileLog';
|
| 58 |
+
export * from '#/_base/log/logService';
|
| 59 |
+
export * from '#/wire/wire';
|
| 60 |
+
export * from '#/wire/wireService';
|
| 61 |
+
export * from '#/wire/journal';
|
| 62 |
+
export * from '#/wire/tree/index';
|
| 63 |
+
export * from '#/wire/record';
|
| 64 |
+
export * from '#/wire/migration/migration';
|
| 65 |
+
export * from '#/session/sessionLog/sessionLogService';
|
| 66 |
+
export * from '#/app/telemetry/telemetry';
|
| 67 |
+
export * from '#/app/telemetry/context';
|
| 68 |
+
export * from '#/app/telemetry/events';
|
| 69 |
+
export * from '#/app/telemetry/telemetryService';
|
| 70 |
+
export * from '#/app/telemetry/consoleAppender';
|
| 71 |
+
export * from '#/app/telemetry/cloudAppender';
|
| 72 |
+
export * from '#/app/bootstrap/bootstrap';
|
| 73 |
+
export * from '#/app/bootstrap/bootstrapService';
|
| 74 |
+
export * from '#/os/interface/hostClock';
|
| 75 |
+
export * from '#/os/interface/hostEnvironment';
|
| 76 |
+
export * from '#/os/interface/hostFileSystem';
|
| 77 |
+
export * from '#/os/interface/hostProcess';
|
| 78 |
+
export * from '#/os/interface/terminal';
|
| 79 |
+
export * from '#/os/interface/terminalErrors';
|
| 80 |
+
export * from '#/os/backends/node-local/hostClockService';
|
| 81 |
+
export * from '#/os/backends/node-local/hostEnvironmentService';
|
| 82 |
+
export * from '#/os/backends/node-local/hostFsService';
|
| 83 |
+
export * from '#/os/backends/node-local/hostProcessService';
|
| 84 |
+
export * from '#/os/backends/node-local/hostTerminalService';
|
| 85 |
+
export * from '#/agent/tools/os/bash/bash';
|
| 86 |
+
import '#/agent/tools/os/bash/bashTool';
|
| 87 |
+
export * from '#/agent/tools/os/glob/glob';
|
| 88 |
+
import '#/agent/tools/os/glob/globTool';
|
| 89 |
+
export * from '#/agent/tools/os/grep/grep';
|
| 90 |
+
import '#/agent/tools/os/grep/grepTool';
|
| 91 |
+
export * from '#/agent/tools/os/read/read';
|
| 92 |
+
import '#/agent/tools/os/read/readTool';
|
| 93 |
+
export * from '#/agent/tools/os/write/write';
|
| 94 |
+
import '#/agent/tools/os/write/writeTool';
|
| 95 |
+
export * from '#/os/interface/terminal';
|
| 96 |
+
export * from '#/os/interface/terminalErrors';
|
| 97 |
+
export * from '#/os/backends/node-local/hostTerminalService';
|
| 98 |
+
export * from '#/session/terminal/terminalService';
|
| 99 |
+
export * from '#/app/task/task';
|
| 100 |
+
import '#/app/task/taskService';
|
| 101 |
+
export { TaskService } from '#/app/task/taskService';
|
| 102 |
+
import '#/app/event/eventBusService';
|
| 103 |
+
import '#/app/event/eventService';
|
| 104 |
+
import '#/app/event/fiberEventResolver';
|
| 105 |
+
export { IEventBus } from '#/app/event/eventBus';
|
| 106 |
+
export { IEventService } from '#/app/event/event';
|
| 107 |
+
export * from '#/app/event/errors';
|
| 108 |
+
export * from '#/app/event/event2';
|
| 109 |
+
export * from '#/state/errors';
|
| 110 |
+
export * from '#/state/state';
|
| 111 |
+
export * from '#/state/stateContribution';
|
| 112 |
+
export * from '#/state/agentModel';
|
| 113 |
+
export * from '#/state/eventDispatcher';
|
| 114 |
+
import '#/state/eventDispatcherService';
|
| 115 |
+
export * from '#/_base/state/stateRegistry';
|
| 116 |
+
export * from '#/_base/contribution/registry';
|
| 117 |
+
export * from '#/app/state/appState';
|
| 118 |
+
import '#/app/state/appStateService';
|
| 119 |
+
export * from '#/workspace/state/workspaceState';
|
| 120 |
+
import '#/workspace/state/workspaceStateService';
|
| 121 |
+
export * from '#/session/state/sessionState';
|
| 122 |
+
import '#/session/state/sessionStateService';
|
| 123 |
+
export * from '#/agent/state/agentState';
|
| 124 |
+
import '#/agent/state/agentStateService';
|
| 125 |
+
export * from '#/llm-adapter/contract/capability';
|
| 126 |
+
export * from '#/llm-adapter/contract/errors';
|
| 127 |
+
export {
|
| 128 |
+
createAssistantMessage,
|
| 129 |
+
createToolMessage,
|
| 130 |
+
createUserMessage,
|
| 131 |
+
isToolDeclarationOnlyMessage,
|
| 132 |
+
mergeInPlace,
|
| 133 |
+
type Message,
|
| 134 |
+
} from '#/llm-adapter/contract/message';
|
| 135 |
+
export {
|
| 136 |
+
extractText,
|
| 137 |
+
getTextContent,
|
| 138 |
+
isContentPart,
|
| 139 |
+
isToolCall,
|
| 140 |
+
isToolCallPart,
|
| 141 |
+
type AudioURLPart,
|
| 142 |
+
type ContentPart,
|
| 143 |
+
type ImageURLPart,
|
| 144 |
+
type Role,
|
| 145 |
+
type StreamedMessagePart,
|
| 146 |
+
type TextPart,
|
| 147 |
+
type ThinkPart,
|
| 148 |
+
type ToolCall,
|
| 149 |
+
type ToolCallPart,
|
| 150 |
+
type VideoURLPart,
|
| 151 |
+
} from '#human/llm/message';
|
| 152 |
+
export type { ToolDescription as Tool } from '#human/llm/message';
|
| 153 |
+
export { addUsage, emptyUsage, grandTotal, inputTotal, type TokenUsage } from '#human/llm/usage';
|
| 154 |
+
export type { FinishReason } from '#human/llm/finish-reason';
|
| 155 |
+
export type {
|
| 156 |
+
JsonObjectResponseFormat,
|
| 157 |
+
JsonSchemaObject,
|
| 158 |
+
JsonSchemaResponseFormat,
|
| 159 |
+
ResponseFormat,
|
| 160 |
+
} from '#human/llm/response-format';
|
| 161 |
+
export type { ThinkingEffort, ThinkingRequestOptions } from '#human/llm/thinking';
|
| 162 |
+
export type { VideoUploadInput } from '#human/llm/media/upload';
|
| 163 |
+
export type { ToolCallIdPolicy } from '#human/llm/requester/requester';
|
| 164 |
+
export type { SamplingOptions } from '#/llm-adapter/model/model-requester';
|
| 165 |
+
export * from '#/llm-adapter/contract/request-trace';
|
| 166 |
+
export type { KimiThinkingConfig } from '#human/llm-kimi/trait';
|
| 167 |
+
|
| 168 |
+
export * from '#/app/sessionIndex/sessionIndex';
|
| 169 |
+
export * from '#/app/sessionIndex/sessionIndexService';
|
| 170 |
+
export * from '#/app/sessionIndex/sessionIndexMirrorService';
|
| 171 |
+
export * from '#/session/sessionMetadata/sessionMetadata';
|
| 172 |
+
export * from '#/session/sessionMetadata/sessionMetadataService';
|
| 173 |
+
export * from '#/session/sessionMetadata/promptMetadata';
|
| 174 |
+
export * from '#/session/sessionActivity/sessionActivity';
|
| 175 |
+
export * from '#/session/sessionActivity/sessionActivityService';
|
| 176 |
+
export * from '#/session/sessionActivity/sessionOutcomeMirror';
|
| 177 |
+
export * from '#/session/sessionActivity/sessionOutcomeMirrorService';
|
| 178 |
+
export * from '#/session/sessionTitle/agentTitlePromptSource';
|
| 179 |
+
import '#/session/sessionTitle/agentTitlePromptSourceService';
|
| 180 |
+
export * from '#/session/sessionTitle/sessionTitle';
|
| 181 |
+
export * from '#/session/sessionTitle/sessionTitleService';
|
| 182 |
+
export * from '#/session/sessionToolPolicy/sessionToolPolicy';
|
| 183 |
+
export * from '#/session/sessionToolPolicy/sessionToolPolicyService';
|
| 184 |
+
export * from '#/app/config/config';
|
| 185 |
+
export * from '#/app/config/configEvents';
|
| 186 |
+
export type { ConfigChangedEvent } from '#/app/config/configEvents';
|
| 187 |
+
export * from '#/app/config/configService';
|
| 188 |
+
export * from '#/app/config/configSectionContributions';
|
| 189 |
+
import '#/app/kosongConfig/configSection';
|
| 190 |
+
export * from '#/llm-adapter/provider/provider';
|
| 191 |
+
export * from '#/llm-adapter/provider/provider-service';
|
| 192 |
+
export * from '#/llm-adapter/provider/provider-definition';
|
| 193 |
+
export * from '#/llm-adapter/protocol/protocolAdapterRegistry';
|
| 194 |
+
import '#/features/skill/catalog/configSection';
|
| 195 |
+
import '#/app/agentIdentity/configSection';
|
| 196 |
+
export * from '#/app/agentIdentity/configSection';
|
| 197 |
+
export * from '#/app/agentIdentity/agentIdentity';
|
| 198 |
+
export * from '#/app/agentIdentity/agentIdentityService';
|
| 199 |
+
import '#/llm-adapter/protocol/errors';
|
| 200 |
+
export * from '#/llm-adapter/protocol/errors';
|
| 201 |
+
export * from '#/llm-adapter/protocol/protocol';
|
| 202 |
+
export * from '#/llm-adapter/protocol/protocol-base';
|
| 203 |
+
import '#/app/kosongConfig/envOverlay';
|
| 204 |
+
export * from '#/llm-adapter/model/completion-budget';
|
| 205 |
+
export * from '#/llm-adapter/model/host-request-headers';
|
| 206 |
+
export * from '#/llm-adapter/model/model';
|
| 207 |
+
export * from '#/llm-adapter/model/model.types';
|
| 208 |
+
export * from '#/llm-adapter/model/model-service';
|
| 209 |
+
export * from '#/llm-adapter/model/thinking';
|
| 210 |
+
export * from '#/llm-adapter/model/catalog';
|
| 211 |
+
export * from '#/llm-adapter/model/catalog-service';
|
| 212 |
+
export * from '#/llm-adapter/model/model-requester';
|
| 213 |
+
import '#/llm-adapter/model/errors';
|
| 214 |
+
export {
|
| 215 |
+
MODEL_CATALOG_SECTION,
|
| 216 |
+
ModelCatalogConfigSchema,
|
| 217 |
+
type ModelCatalogConfig,
|
| 218 |
+
} from '#/app/kosongConfig/configSection';
|
| 219 |
+
export * from '#/app/kosongConfig/kosongConfig';
|
| 220 |
+
export * from '#/app/kosongConfig/kosongConfigService';
|
| 221 |
+
export * from '#/llm-adapter/model/model-oauth';
|
| 222 |
+
export * from '#/app/kosongConfig/oauthTokenAdapter';
|
| 223 |
+
export * from '#/app/kosongConfig/hostRequestHeadersAdapter';
|
| 224 |
+
export * from '#/app/kosongConfig/discovery';
|
| 225 |
+
export * from '#/app/kosongConfig/discoveryService';
|
| 226 |
+
export * from '#/app/kosongConfig/errors';
|
| 227 |
+
export * from '#/app/kosongConfig/modelsDevImport';
|
| 228 |
+
export * from '#/app/kosongConfig/modelsDevImportService';
|
| 229 |
+
export * from '#/app/kosongConfig/modelsDevUpstream';
|
| 230 |
+
export * from '#/app/kosongConfig/modelsDev';
|
| 231 |
+
export * from '#/app/agentProfileCatalog/agentProfileCatalog';
|
| 232 |
+
export * from '#/app/agentProfileCatalog/agentProfileContribution';
|
| 233 |
+
export * from '#/app/agentProfileCatalog/agentProfileRegistry';
|
| 234 |
+
export * from '#/app/agentProfileCatalog/agentProfileRegistryService';
|
| 235 |
+
export * from '#/app/agentProfileCatalog/builtinAgentProfileLoader';
|
| 236 |
+
export * from '#/app/agentProfileCatalog/builtinAgentProfileLoaderService';
|
| 237 |
+
export * from '#/app/agentProfileCatalog/profile-shared';
|
| 238 |
+
export * from '#/app/agentProfileCatalog/promptPrefix';
|
| 239 |
+
export {
|
| 240 |
+
registerAgentProfile,
|
| 241 |
+
getAgentProfileContributions,
|
| 242 |
+
_clearAgentProfileContributionsForTests,
|
| 243 |
+
} from '#/app/agentProfileCatalog/contribution';
|
| 244 |
+
export * from '#/workspace/workspaceAgentProfileLoader/configSection';
|
| 245 |
+
export { parseAgentFileText } from '#/workspace/workspaceAgentProfileLoader/internal/agentFile';
|
| 246 |
+
export { resolveAgentPath } from '#/workspace/workspaceAgentProfileLoader/internal/paths';
|
| 247 |
+
export * from '#/workspace/workspaceAgentProfileLoader/userAgentProfileLoader';
|
| 248 |
+
export * from '#/workspace/workspaceAgentProfileLoader/userAgentProfileLoaderService';
|
| 249 |
+
export * from '#/app/plugin/types';
|
| 250 |
+
export * from '#/app/plugin/commands';
|
| 251 |
+
export * from '#/app/plugin/manifest';
|
| 252 |
+
export * from '#/app/plugin/store';
|
| 253 |
+
export * from '#/app/plugin/source';
|
| 254 |
+
export * from '#/app/plugin/github-resolver';
|
| 255 |
+
export * from '#/app/plugin/archive';
|
| 256 |
+
export * from '#/app/plugin/manager';
|
| 257 |
+
export * from '#/app/plugin/marketplace';
|
| 258 |
+
export * from '#/app/plugin/plugin';
|
| 259 |
+
export * from '#/app/plugin/pluginEvents';
|
| 260 |
+
export * from '#/app/plugin/pluginService';
|
| 261 |
+
export * from '#/app/capability/capability';
|
| 262 |
+
export * from '#/app/capability/capabilityEvents';
|
| 263 |
+
export * from '#/app/capability/capabilityService';
|
| 264 |
+
export * from '#/app/capability/errors';
|
| 265 |
+
export * from '#/app/capability/types';
|
| 266 |
+
export * from '#/app/feature/featureManager';
|
| 267 |
+
export * from '#/app/feature/featureServiceContribution';
|
| 268 |
+
import '#/app/feature/featureManagerService';
|
| 269 |
+
export * from '#/features/feature';
|
| 270 |
+
export * from '#/features/featureAssembly';
|
| 271 |
+
export * from '#/features/featureRegistry';
|
| 272 |
+
import '#/features/featureAssemblyService';
|
| 273 |
+
export * from '#/agent/command/agentCommand';
|
| 274 |
+
export * from '#/agent/command/commandContribution';
|
| 275 |
+
import '#/agent/command/agentCommandService';
|
| 276 |
+
export * from '#/debug/index';
|
| 277 |
+
export * from '#/workspace/workspaceAgentProfileLoader/pluginAgentProfileLoader';
|
| 278 |
+
export * from '#/workspace/workspaceAgentProfileLoader/pluginAgentProfileLoaderService';
|
| 279 |
+
|
| 280 |
+
export type { SkillSource } from '#/features/skill/catalog/types';
|
| 281 |
+
export * from '#/features/skill/tools/skill';
|
| 282 |
+
export * from '#/features/skill/skill';
|
| 283 |
+
export * from '#/features/skill/skillService';
|
| 284 |
+
import '#/features/skill/skillFeature';
|
| 285 |
+
export * from '#/features/skill/catalog/types';
|
| 286 |
+
export * from '#/features/skill/catalog/configSection';
|
| 287 |
+
export * from '#/features/skill/catalog/parser';
|
| 288 |
+
export * from '#/features/skill/catalog/registry';
|
| 289 |
+
export * from '#/features/skill/catalog/errors';
|
| 290 |
+
export * from '#/features/skill/catalog/skillDiscovery';
|
| 291 |
+
export * from '#/features/skill/catalog/inMemorySkillDiscovery';
|
| 292 |
+
export * from '#/features/skill/catalog/skillSource';
|
| 293 |
+
export * from '#/features/skill/catalog/skillRoots';
|
| 294 |
+
export * from '#/features/skill/catalog/builtin/builtin';
|
| 295 |
+
export * from '#/features/skill/catalog/builtinSkillSource';
|
| 296 |
+
export * from '#/features/skill/catalog/userFileSkillSource';
|
| 297 |
+
export * from '#/features/skill/session/skillCatalog';
|
| 298 |
+
export * from '#/features/skill/session/skillCatalogData';
|
| 299 |
+
export * from '#/features/skill/session/skillCatalogService';
|
| 300 |
+
export * from '#/session/sessionAgentProfileCatalog/sessionAgentProfileCatalog';
|
| 301 |
+
export * from '#/session/sessionAgentProfileCatalog/agentProfileCatalogSeed';
|
| 302 |
+
export * from '#/session/sessionAgentProfileCatalog/sessionAgentProfileCatalogService';
|
| 303 |
+
export * from '#/session/sessionInstructions/instructionsProvider';
|
| 304 |
+
export * from '#/session/workspaceInfo/workspaceInfo';
|
| 305 |
+
export * from '#/workspace/workspaceDirs/workspaceDirs';
|
| 306 |
+
export * from '#/workspace/workspaceDirs/workspaceDirsService';
|
| 307 |
+
export * from '#/features/skill/workspace/workspaceSkillCatalog';
|
| 308 |
+
export * from '#/features/skill/workspace/workspaceSkillCatalogService';
|
| 309 |
+
export * from '#/features/skill/workspace/extraFileSkillSource';
|
| 310 |
+
export * from '#/features/skill/workspace/explicitFileSkillSource';
|
| 311 |
+
export * from '#/features/skill/workspace/rootFileSkillSource';
|
| 312 |
+
export * from '#/features/skill/workspace/pluginSkillSource';
|
| 313 |
+
export * from '#/workspace/workspaceAgentProfileLoader/workspaceAgentProfileLoader';
|
| 314 |
+
export * from '#/workspace/workspaceAgentProfileLoader/workspaceAgentProfileLoaderService';
|
| 315 |
+
export * from '#/workspace/workspaceAgentProfileLoader/extraAgentProfileLoader';
|
| 316 |
+
export * from '#/workspace/workspaceAgentProfileLoader/extraAgentProfileLoaderService';
|
| 317 |
+
export * from '#/workspace/workspaceAgentProfileLoader/explicitAgentProfileLoader';
|
| 318 |
+
export * from '#/workspace/workspaceAgentProfileLoader/explicitAgentProfileLoaderService';
|
| 319 |
+
export * from '#/workspace/workspaceInstructions/workspaceInstructions';
|
| 320 |
+
export * from '#/workspace/workspaceInstructions/workspaceInstructionsService';
|
| 321 |
+
export * from '#/agent/permissionGate/permissionGate';
|
| 322 |
+
export * from '#/agent/permissionGate/permissionGateService';
|
| 323 |
+
export * from '#/agent/toolApproval/toolApproval';
|
| 324 |
+
export * from '#/agent/toolApproval/toolApprovalService';
|
| 325 |
+
import '#/app/flag/flag';
|
| 326 |
+
import '#/app/flag/flagRegistry';
|
| 327 |
+
import '#/app/flag/flagRegistryService';
|
| 328 |
+
import '#/app/flag/flagService';
|
| 329 |
+
export * from '#/app/flag/flagRegistry';
|
| 330 |
+
export * from '#/app/flag/flagRegistryService';
|
| 331 |
+
export * from '#/app/flag/flag';
|
| 332 |
+
export * from '#/app/flag/flagService';
|
| 333 |
+
|
| 334 |
+
export * from '#/agent/modeMutex/modeMutex';
|
| 335 |
+
import '#/agent/modeMutex/modeMutexService';
|
| 336 |
+
export * from '#/features/btw/btw';
|
| 337 |
+
export * from '#/features/btw/btwService';
|
| 338 |
+
import '#/features/btw/btwFeature';
|
| 339 |
+
import '#/features/plan/profile/plan';
|
| 340 |
+
export * from '#/features/plan/tools/enter-plan-mode/enter-plan-mode';
|
| 341 |
+
import '#/features/plan/tools/enter-plan-mode/enterPlanModeTool';
|
| 342 |
+
export * from '#/features/plan/tools/exit-plan-mode/exit-plan-mode';
|
| 343 |
+
import '#/features/plan/tools/exit-plan-mode/exitPlanModeTool';
|
| 344 |
+
export * from '#/features/plan/configSection';
|
| 345 |
+
export * from '#/features/plan/plan';
|
| 346 |
+
export * from '#/features/plan/planOps';
|
| 347 |
+
export * from '#/features/plan/planService';
|
| 348 |
+
import '#/features/dateChange/dateChangeFeature';
|
| 349 |
+
import '#/features/plan/planFeature';
|
| 350 |
+
export * from '#/features/fileHistory/fileHistory';
|
| 351 |
+
export * from '#/features/fileHistory/fileHistoryOps';
|
| 352 |
+
export * from '#/features/fileHistory/fileHistoryService';
|
| 353 |
+
import '#/features/fileHistory/fileHistoryFeature';
|
| 354 |
+
export * from '#/features/externalHooks/configSection';
|
| 355 |
+
export * from '#/features/externalHooks/app/externalHooksRunner';
|
| 356 |
+
export * from '#/features/externalHooks/app/externalHooksRunnerService';
|
| 357 |
+
export * from '#/features/externalHooks/session/sessionExternalHooks';
|
| 358 |
+
export * from '#/features/externalHooks/session/sessionExternalHooksService';
|
| 359 |
+
export * from '#/features/externalHooks/agent/agentExternalHooks';
|
| 360 |
+
export * from '#/features/externalHooks/agent/agentExternalHooksService';
|
| 361 |
+
import '#/features/externalHooks/externalHooksFeature';
|
| 362 |
+
export * from '#/features/debugEvents/debugEvents';
|
| 363 |
+
export * from '#/features/debugEvents/debugEventsService';
|
| 364 |
+
import '#/features/debugEvents/debugEventsFeature';
|
| 365 |
+
export * from '#/features/swarm/configSection';
|
| 366 |
+
export * from '#/features/swarm/agent/swarm';
|
| 367 |
+
export * from '#/features/swarm/agent/swarmService';
|
| 368 |
+
export * from '#/features/swarm/session/sessionSwarm';
|
| 369 |
+
export * from '#/features/swarm/session/sessionSwarmService';
|
| 370 |
+
export * from '#/features/swarm/tools/agent-swarm/agent-swarm';
|
| 371 |
+
import '#/features/swarm/tools/agent-swarm/agentSwarmTool';
|
| 372 |
+
import '#/features/swarm/swarmFeature';
|
| 373 |
+
export * from '#/features/goal/tools/create-goal/create-goal';
|
| 374 |
+
import '#/features/goal/tools/create-goal/createGoalTool';
|
| 375 |
+
export * from '#/features/goal/tools/get-goal/get-goal';
|
| 376 |
+
import '#/features/goal/tools/get-goal/getGoalTool';
|
| 377 |
+
export * from '#/features/goal/tools/set-goal-budget/set-goal-budget';
|
| 378 |
+
import '#/features/goal/tools/set-goal-budget/setGoalBudgetTool';
|
| 379 |
+
export * from '#/features/goal/tools/update-goal/update-goal';
|
| 380 |
+
import '#/features/goal/tools/update-goal/updateGoalTool';
|
| 381 |
+
export * from '#/features/goal/goalDeadlineScheduler';
|
| 382 |
+
export * from '#/features/goal/goal';
|
| 383 |
+
export * from '#/features/goal/goalService';
|
| 384 |
+
export * from '#/features/goal/goalOps';
|
| 385 |
+
export * from '#/features/goal/types';
|
| 386 |
+
import '#/features/goal/goalFeature';
|
| 387 |
+
export * from '#/features/tower/flag';
|
| 388 |
+
export * from '#/features/tower/tower';
|
| 389 |
+
export * from '#/features/tower/towerFeature';
|
| 390 |
+
export * from '#/features/tower/towerService';
|
| 391 |
+
export * from '#/features/tower/towerRateLimit';
|
| 392 |
+
export * from '#/features/tower/towerRateLimitService';
|
| 393 |
+
export * from '#/features/tower/tools/init/init';
|
| 394 |
+
export * from '#/features/tower/tools/plan/plan';
|
| 395 |
+
export * from '#/features/tower/tools/spawn/spawn';
|
| 396 |
+
export * from '#/features/tower/tools/merge/merge';
|
| 397 |
+
export * from '#/features/tower/tools/teardown/teardown';
|
| 398 |
+
export * from '#/features/tower/tools/send/send';
|
| 399 |
+
export * from '#/features/tower/tools/inbox/inbox';
|
| 400 |
+
export * from '#/features/tower/tools/finding/finding';
|
| 401 |
+
export * from '#/features/tower/tools/review/review';
|
| 402 |
+
export * from '#/features/tower/tools/mission/mission';
|
| 403 |
+
export * from '#/features/tower/tools/status/status';
|
| 404 |
+
import '#/features/tower/flag';
|
| 405 |
+
import '#/features/tower/towerFeature';
|
| 406 |
+
export * from '#/agent/usage/usage';
|
| 407 |
+
export * from '#/agent/usage/cacheProbe';
|
| 408 |
+
export * from '#/agent/usage/cacheProbeService';
|
| 409 |
+
export * from '#/session/usage/sessionUsage';
|
| 410 |
+
export * from '#/session/usage/usageAgentModel';
|
| 411 |
+
export * from '#/session/usage/sessionUsageService';
|
| 412 |
+
import '#/features/usage/usageFeature';
|
| 413 |
+
export * from '#/agent/toolDedupe/toolDedupe';
|
| 414 |
+
export * from '#/agent/toolDedupe/toolDedupeService';
|
| 415 |
+
export * from '#/agent/agentsMdReminder/agentsMdReminder';
|
| 416 |
+
export * from '#/agent/agentsMdReminder/agentsMdReminderService';
|
| 417 |
+
import '#/agent/toolSelect/flag';
|
| 418 |
+
export * from '#/agent/tools/select-tools/select-tools';
|
| 419 |
+
import '#/agent/tools/select-tools/selectToolsTool';
|
| 420 |
+
export * from '#/agent/toolSelect/dynamicTools';
|
| 421 |
+
export * from '#/agent/toolSelect/toolSelect';
|
| 422 |
+
export * from '#/agent/toolSelect/toolSelectService';
|
| 423 |
+
export * from '#/agent/toolSelect/toolSelectAnnouncements';
|
| 424 |
+
export * from '#/agent/toolSelect/toolSelectAnnouncementsService';
|
| 425 |
+
export * from '#/agent/toolSelect/toolSelectSchemas';
|
| 426 |
+
export * from '#/agent/toolSelect/toolSelectSchemasService';
|
| 427 |
+
import '#/agent/toolPolicy/configSection';
|
| 428 |
+
export * from '#/agent/toolPolicy/configSection';
|
| 429 |
+
export * from '#/agent/toolPolicy/evaluate';
|
| 430 |
+
export * from '#/agent/toolPolicy/toolPolicy';
|
| 431 |
+
export * from '#/agent/toolPolicy/toolPolicyService';
|
| 432 |
+
|
| 433 |
+
import '#/agent/task/configSection';
|
| 434 |
+
export {
|
| 435 |
+
resolveAgentTaskConfig,
|
| 436 |
+
resolvePrintBackgroundMode,
|
| 437 |
+
type AgentTaskConfig,
|
| 438 |
+
type PrintBackgroundMode,
|
| 439 |
+
} from '#/agent/task/configSection';
|
| 440 |
+
export * from '#/agent/task/printDefaults';
|
| 441 |
+
export * from '#/agent/tools/task/task-list/task-list';
|
| 442 |
+
import '#/agent/tools/task/task-list/taskListTool';
|
| 443 |
+
export * from '#/agent/tools/task/task-output/task-output';
|
| 444 |
+
import '#/agent/tools/task/task-output/taskOutputTool';
|
| 445 |
+
export * from '#/agent/tools/task/task-stop/task-stop';
|
| 446 |
+
import '#/agent/tools/task/task-stop/taskStopTool';
|
| 447 |
+
export * from '#/agent/tools/task/task-wait/task-wait';
|
| 448 |
+
import '#/agent/tools/task/task-wait/taskWaitTool';
|
| 449 |
+
export * from '#/agent/task/task';
|
| 450 |
+
export * from '#/agent/task/taskOps';
|
| 451 |
+
export * from '#/agent/task/taskService';
|
| 452 |
+
import '#/features/cron/configSection';
|
| 453 |
+
export * from '#/features/cron/cronTask';
|
| 454 |
+
export * from '#/features/cron/configSection';
|
| 455 |
+
export * from '#/features/cron/cronService';
|
| 456 |
+
export * from '#/features/cron/cronOps';
|
| 457 |
+
export type { CronFiredEvent } from '#/features/cron/cronOps';
|
| 458 |
+
import '#/features/cron/cronFeature';
|
| 459 |
+
export * from '#/features/cron/tools/cron-create/cron-create';
|
| 460 |
+
export * from '#/features/cron/tools/cron-list/cron-list';
|
| 461 |
+
export * from '#/features/cron/tools/cron-delete/cron-delete';
|
| 462 |
+
|
| 463 |
+
import '#/session/agentLifecycle/profile/profiles';
|
| 464 |
+
export * from '#/session/agentLifecycle/agentLifecycle';
|
| 465 |
+
export * from '#/session/agentLifecycle/agentLifecycleService';
|
| 466 |
+
export * from '#/session/agentLifecycle/mainAgent';
|
| 467 |
+
export * from '#/session/mcp/sessionMcpHandle';
|
| 468 |
+
import '#/app/mcpConfig/configSection';
|
| 469 |
+
export {
|
| 470 |
+
MCP_SECTION,
|
| 471 |
+
McpSectionSchema,
|
| 472 |
+
type McpSection,
|
| 473 |
+
} from '#/app/mcpConfig/configSection';
|
| 474 |
+
export * from '#/app/mcpConfig/oauthStore';
|
| 475 |
+
export { IMcpConfigStore } from '#/app/mcpConfig/configStore';
|
| 476 |
+
import '#/app/mcpConfig/configStore';
|
| 477 |
+
export { IMcpOAuthService } from '#/app/mcpConfig/oauthService';
|
| 478 |
+
import '#/app/mcpConfig/oauthService';
|
| 479 |
+
export * from '#/app/mcpRegistry/mcpRegistry';
|
| 480 |
+
import '#/app/mcpRegistry/mcpRegistryService';
|
| 481 |
+
export * from '#/app/mcpManagement/mcpManagement';
|
| 482 |
+
import '#/app/mcpManagement/mcpManagementService';
|
| 483 |
+
export * from '#/workspace/workspaceMcpConfig/workspaceMcpConfig';
|
| 484 |
+
export * from '#/workspace/workspaceMcpConfig/workspaceMcpConfigService';
|
| 485 |
+
export * from '#/workspace/workspaceMcp/workspaceMcp';
|
| 486 |
+
export * from '#/workspace/workspaceMcp/workspaceMcpService';
|
| 487 |
+
export * from '#/session/subagent/subagent';
|
| 488 |
+
export * from '#/session/subagent/subagentService';
|
| 489 |
+
export * from '#/session/subagent/spawn';
|
| 490 |
+
import '#/session/subagent/flag';
|
| 491 |
+
export * from '#/session/subagent/subagentModelsValidation';
|
| 492 |
+
import '#/session/subagent/subagentModelsValidationService';
|
| 493 |
+
export * from '#/agent/tools/agent/subagent-task';
|
| 494 |
+
export { AGENT_RUN_PROMPT_ORIGIN } from '#/session/subagent/runAgentTurn';
|
| 495 |
+
export * from '#/session/subagent/mirrorAgentRun';
|
| 496 |
+
export * from '#/session/subagent/subagentScopeCache';
|
| 497 |
+
import '#/session/subagent/subagentScopeCacheService';
|
| 498 |
+
import '#/session/subagent/configSection';
|
| 499 |
+
export * from '#/agent/tools/agent/agent';
|
| 500 |
+
import '#/agent/tools/agent/agentTool';
|
| 501 |
+
export * from '#/app/sessionManager/sessionLookup';
|
| 502 |
+
export * from '#/workspace/workspaceContext/workspaceContext';
|
| 503 |
+
export * from '#/workspace/sessionLifecycle/sessionLifecycle';
|
| 504 |
+
export * from '#/workspace/sessionLifecycle/sessionLifecycleEvents';
|
| 505 |
+
export * from '#/workspace/sessionLifecycle/sessionLifecycleService';
|
| 506 |
+
export * from '#/workspace/sessionLifecycle/coldSessionArchive';
|
| 507 |
+
export * from '#/workspace/sessionLifecycle/internal/addressing';
|
| 508 |
+
import '#/app/sessionExport/errors';
|
| 509 |
+
export * from '#/app/sessionExport/sessionExport';
|
| 510 |
+
export * from '#/app/sessionExport/sessionExportService';
|
| 511 |
+
export * from '#/app/sessionExport/manifest';
|
| 512 |
+
export * from '#/app/sessionExport/wire-scan';
|
| 513 |
+
export * from '#/app/sessionExport/zip';
|
| 514 |
+
export * from '#/app/sessionLegacy/sessionLegacy';
|
| 515 |
+
export * from '#/app/sessionLegacy/sessionLegacyService';
|
| 516 |
+
export * from '#/human/interaction/interaction';
|
| 517 |
+
export * from '#/human/interaction/facade';
|
| 518 |
+
export * from '#/agent/interaction/interactionOps';
|
| 519 |
+
export * from '#/session/sessionContext/sessionContext';
|
| 520 |
+
|
| 521 |
+
export * from '#/agent/interaction/question';
|
| 522 |
+
export {
|
| 523 |
+
type ApprovalDecision,
|
| 524 |
+
type ApprovalRequest as SessionApprovalRequest,
|
| 525 |
+
type ApprovalResponse as SessionApprovalResponse,
|
| 526 |
+
} from '#/agent/interaction/approval';
|
| 527 |
+
export * from '#/agent/tools/ask-user-question/ask-user-question';
|
| 528 |
+
import '#/agent/tools/ask-user-question/askUserQuestionTool';
|
| 529 |
+
export * from '#/app/gateway/gateway';
|
| 530 |
+
export * from '#/app/gateway/gatewayService';
|
| 531 |
+
|
| 532 |
+
export * from '#/session/workspaceContext/workspaceContext';
|
| 533 |
+
export * from '#/session/workspaceContext/workspaceContextService';
|
| 534 |
+
export * from '#/app/projectLocalConfig/projectLocalConfig';
|
| 535 |
+
export * from '#/app/workspace/workspace';
|
| 536 |
+
export * from '#/app/workspace/workspaceService';
|
| 537 |
+
export * from '#/app/workspace/workspaceAlias';
|
| 538 |
+
export * from '#/app/workspace/workspaceEvents';
|
| 539 |
+
export * from '#/app/workspace/workspacePersistence';
|
| 540 |
+
export * from '#/app/workspace/fileWorkspacePersistence';
|
| 541 |
+
export * from '#/app/workspaceAliases/workspaceAliases';
|
| 542 |
+
import '#/app/workspaceAliases/workspaceAliasesService';
|
| 543 |
+
export * from '#/app/workspaceSessions/workspaceSessions';
|
| 544 |
+
import '#/app/workspaceSessions/workspaceSessionsService';
|
| 545 |
+
import '#/app/git/gitService';
|
| 546 |
+
export * from '#/app/bashParser/bashParser';
|
| 547 |
+
import '#/app/bashParser/bashParserService';
|
| 548 |
+
export * from '#/workspace/workspaceFs/internal/errors';
|
| 549 |
+
export * from '#/workspace/workspaceFs/fs';
|
| 550 |
+
export * from '#/workspace/workspaceFs/fsService';
|
| 551 |
+
export * from '#/session/agentLifecycle/profile/gitContext';
|
| 552 |
+
export * from '#/workspace/workspaceFs/internal/rgLocator';
|
| 553 |
+
export * from '#/workspace/workspaceFs/internal/runRg';
|
| 554 |
+
export * from '#/workspace/workspaceGit/workspaceGit';
|
| 555 |
+
export * from '#/workspace/workspaceGit/workspaceGitService';
|
| 556 |
+
export * from '#/session/sessionToolPolicyGate/sessionToolPolicyGate';
|
| 557 |
+
export * from '#/session/sessionToolPolicyGate/sessionToolPolicyGateService';
|
| 558 |
+
export * from '#/workspace/workspaceTrust/workspaceTrust';
|
| 559 |
+
export * from '#/workspace/workspaceTrust/workspaceTrustService';
|
| 560 |
+
export * from '#/app/hostFolderBrowser/hostFolderBrowser';
|
| 561 |
+
export * from '#/app/hostFolderBrowser/hostFolderBrowserService';
|
| 562 |
+
export * from '#/persistence/interface/storage';
|
| 563 |
+
export * from '#/persistence/interface/appendLogStore';
|
| 564 |
+
export * from '#/persistence/interface/atomicDocumentStore';
|
| 565 |
+
export * from '#/persistence/interface/queryStore';
|
| 566 |
+
export * from '#/persistence/interface/blobStore';
|
| 567 |
+
export * from '#/persistence/backends/node-fs/fileStorageService';
|
| 568 |
+
export * from '#/persistence/backends/node-fs/appendLogStore';
|
| 569 |
+
export * from '#/persistence/backends/node-fs/atomicDocumentStore';
|
| 570 |
+
export * from '#/persistence/backends/node-fs/blobStoreService';
|
| 571 |
+
export * from '#/persistence/backends/node-fs/projectLocalConfigService';
|
| 572 |
+
export * from '#/persistence/configSection';
|
| 573 |
+
import '#/persistence/configSection';
|
| 574 |
+
export * from '#/persistence/backends/minidb/miniDbQueryStore';
|
| 575 |
+
export * from '#/persistence/backends/memory/inMemoryStorageService';
|
| 576 |
+
export * from '#/agent/tools/web-search/web-search';
|
| 577 |
+
import '#/agent/tools/web-search/webSearchTool';
|
| 578 |
+
export * from '#/app/auth/auth';
|
| 579 |
+
export * from '#/app/auth/authService';
|
| 580 |
+
export * from '#/app/auth/configSection';
|
| 581 |
+
export * from '#/app/auth/webSearch/webSearch';
|
| 582 |
+
export * from '#/app/auth/webSearch/webSearchService';
|
| 583 |
+
export * from '#/app/auth/webSearch/providers/moonshot-web-search';
|
| 584 |
+
export * from '#/app/authLegacy/authLegacy';
|
| 585 |
+
export * from '#/app/authLegacy/authLegacyService';
|
| 586 |
+
export * from '#/app/file/fileService';
|
| 587 |
+
export * from '#/app/file/fileServiceImpl';
|
| 588 |
+
export {
|
| 589 |
+
buildImageCompressionCaption,
|
| 590 |
+
compressBase64ForModel,
|
| 591 |
+
compressImageForModel,
|
| 592 |
+
gateImageFormatParts,
|
| 593 |
+
IMAGE_BYTE_BUDGET,
|
| 594 |
+
MAX_IMAGE_DECODE_BYTES,
|
| 595 |
+
MAX_IMAGE_EDGE_PX,
|
| 596 |
+
READ_IMAGE_BYTE_BUDGET,
|
| 597 |
+
resolveMaxImageEdgePx,
|
| 598 |
+
resolveReadImageByteBudget,
|
| 599 |
+
} from '#/agent/media/image-compress';
|
| 600 |
+
export { providerImagePolicy, type ProviderImagePolicy } from '#human/llm/media/image-formats';
|
| 601 |
+
export {
|
| 602 |
+
buildImageConversionGuidance,
|
| 603 |
+
buildUnsupportedImageNotice,
|
| 604 |
+
decodeBase64Prefix,
|
| 605 |
+
isModelAcceptedImageMime,
|
| 606 |
+
normalizeImageMime,
|
| 607 |
+
parseImageDataUrl,
|
| 608 |
+
resolveEffectiveImageMime,
|
| 609 |
+
unsupportedImageMimeFromUrl,
|
| 610 |
+
} from '#/agent/media/image-format-policy';
|
| 611 |
+
export {
|
| 612 |
+
persistOriginalImage,
|
| 613 |
+
sessionMediaOriginalsDir,
|
| 614 |
+
} from '#/agent/media/image-originals';
|
| 615 |
+
export * from '#/app/edit/fileEdit';
|
| 616 |
+
export * from '#/app/edit/fileEditService';
|
| 617 |
+
export * from '#/app/edit/editService';
|
| 618 |
+
export * from '#/app/edit/textModel';
|
| 619 |
+
export * from '#/agent/tools/edit/edit';
|
| 620 |
+
import '#/agent/tools/edit/editTool';
|
| 621 |
+
export * from '#/agent/tools/fetch-url/fetch-url';
|
| 622 |
+
import '#/agent/tools/fetch-url/fetchUrlTool';
|
| 623 |
+
export * from '#/app/web/web';
|
| 624 |
+
export * from '#/app/web/webService';
|
| 625 |
+
export * from '#/app/web/providers/local-fetch-url';
|
| 626 |
+
export * from '#/app/web/providers/moonshot-fetch-url';
|
| 627 |
+
|
| 628 |
+
export * from '#/agent/blob/agentBlobService';
|
| 629 |
+
export * from '#/agent/blob/agentBlobServiceImpl';
|
| 630 |
+
export * from '#/agent/contextMemory/contextMemory';
|
| 631 |
+
export * from '#/agent/contextMemory/contextMemoryService';
|
| 632 |
+
export * from '#/agent/contextMemory/contextOps';
|
| 633 |
+
export * from '#/agent/contextMemory/compactionHandoff';
|
| 634 |
+
export * from '#/agent/contextMemory/conversationUndoParticipants';
|
| 635 |
+
export * from '#/agent/contextMemory/conversationTime';
|
| 636 |
+
export * from '#/agent/contextMemory/loopEventFold';
|
| 637 |
+
export * from '#/agent/contextMemory/messageId';
|
| 638 |
+
export * from '#/agent/contextMemory/contextTranscript';
|
| 639 |
+
export * from '#/agent/contextMemory/types';
|
| 640 |
+
export * from '#/features/reminder/reminderService';
|
| 641 |
+
export * from '#/features/reminder/systemReminder';
|
| 642 |
+
export * from '#/features/reminder/types';
|
| 643 |
+
import '#/features/reminder/reminderFeature';
|
| 644 |
+
export * from '#/features/dateChange/dateChange';
|
| 645 |
+
export * from '#/features/dateChange/dateChangeService';
|
| 646 |
+
export * from '#/agent/contextProjector/contextProjector';
|
| 647 |
+
export * from '#/agent/contextProjector/contextProjectorService';
|
| 648 |
+
export * from '#/agent/contextProjector/mediaProjection';
|
| 649 |
+
export * from '#/agent/tokenCounting/tokenCounting';
|
| 650 |
+
export * from '#/agent/tokenCounting/tokenCountingOps';
|
| 651 |
+
export * from '#/session/tokenCounting/sessionTokenCounting';
|
| 652 |
+
export * from '#/session/tokenCounting/tokenCountingAgentModel';
|
| 653 |
+
export * from '#/session/tokenCounting/sessionTokenCountingService';
|
| 654 |
+
import '#/features/tokenCounting/tokenCountingFeature';
|
| 655 |
+
export * from '#/agent/plugin/agentPlugin';
|
| 656 |
+
export * from '#/agent/plugin/agentPluginOps';
|
| 657 |
+
export * from '#/agent/plugin/agentPluginService';
|
| 658 |
+
export * from '#/agent/fullCompaction/strategy';
|
| 659 |
+
export * from '#/agent/fullCompaction/fullCompaction';
|
| 660 |
+
export * from '#/agent/fullCompaction/fullCompactionService';
|
| 661 |
+
export * from '#/agent/fullCompaction/compactionOps';
|
| 662 |
+
export * from '#/agent/fullCompaction/types';
|
| 663 |
+
export * from '#/agent/fullCompaction/contextRecovery';
|
| 664 |
+
export * from '#/agent/fullCompaction/compactionInstruction';
|
| 665 |
+
export * from '#/agent/llmRequester/llmRequester';
|
| 666 |
+
export * from '#/agent/llmRequester/llmRequesterService';
|
| 667 |
+
export * from '#/agent/llmRequester/llmRequestOps';
|
| 668 |
+
export * from '#/_base/utils/promise';
|
| 669 |
+
export * from '#/_base/utils/retry';
|
| 670 |
+
export * from '#/_base/utils/timer';
|
| 671 |
+
import '#/agent/loop/configSection';
|
| 672 |
+
export * from '#/agent/loop/loop';
|
| 673 |
+
export * from '#/agent/loop/loopService';
|
| 674 |
+
export * from '#/agent/loop/promptChannel';
|
| 675 |
+
export * from '#/agent/interruptionReminder/interruptionReminder';
|
| 676 |
+
export * from '#/agent/interruptionReminder/interruptionReminderService';
|
| 677 |
+
export * from '#/agent/interruptionReminder/interruptionReminderOps';
|
| 678 |
+
export * from '#/agent/mcp/mcp';
|
| 679 |
+
export * from '#/agent/mcp/mcpService';
|
| 680 |
+
export * from '#/agent/mcp/mcpDiscoveryOps';
|
| 681 |
+
export * from '#/mcpCore/config-schema';
|
| 682 |
+
export * from '#/agent/media/mediaTools';
|
| 683 |
+
export * from '#/agent/media/mediaToolsRegistrar';
|
| 684 |
+
export * from '#/agent/media/registerMediaTools';
|
| 685 |
+
export {
|
| 686 |
+
buildDaemonFileUrl,
|
| 687 |
+
buildMediaPathTag,
|
| 688 |
+
daemonFileRefFromPart,
|
| 689 |
+
mediaExtensionForMime,
|
| 690 |
+
matchSingleMediaPathTag,
|
| 691 |
+
parseDaemonFileUrl,
|
| 692 |
+
} from '#/agent/media/mediaRef';
|
| 693 |
+
export type { DaemonFileRef, MediaKind } from '#/agent/media/mediaRef';
|
| 694 |
+
export * from '#/agent/media/sessionMediaStore';
|
| 695 |
+
import '#/agent/media/sessionMediaStoreService';
|
| 696 |
+
export * from '#/agent/media/kimiFileUrl';
|
| 697 |
+
export * from '#/agent/media/videoUpload';
|
| 698 |
+
export * from '#/agent/media/mediaResolver';
|
| 699 |
+
export * from '#/agent/media/mediaResolverService';
|
| 700 |
+
import '#/agent/media/configSection';
|
| 701 |
+
export * from '#/agent/media/imageConfigBridge';
|
| 702 |
+
import '#/agent/permissionMode/configSection';
|
| 703 |
+
export * from '#/agent/permissionMode/permissionMode';
|
| 704 |
+
export * from '#/agent/permissionMode/permissionModeService';
|
| 705 |
+
export * from '#/agent/permissionPolicy/permissionPolicy';
|
| 706 |
+
export * from '#/agent/permissionPolicy/permissionPolicyService';
|
| 707 |
+
export * from '#/agent/permissionPolicy/types';
|
| 708 |
+
import '#/agent/permissionRules/configSection';
|
| 709 |
+
export * from '#/agent/permissionRules/permissionRules';
|
| 710 |
+
export * from '#/agent/permissionRules/matchesRule';
|
| 711 |
+
export * from '#/agent/permissionRules/permissionRulesService';
|
| 712 |
+
export * from '#/agent/pluginCommand/pluginCommand';
|
| 713 |
+
export * from '#/agent/pluginCommand/pluginCommandService';
|
| 714 |
+
export * from '#/agent/profile/profile';
|
| 715 |
+
export * from '#/agent/profile/profileService';
|
| 716 |
+
export * from '#/agent/profile/context';
|
| 717 |
+
export * from '#/agent/prompt/promptEvents';
|
| 718 |
+
export * from '#/agent/prompt/promptMetadataText';
|
| 719 |
+
export * from '#/agent/replayBuilder/types';
|
| 720 |
+
export * from '#/agent/replayBuilder/fold';
|
| 721 |
+
export { type SessionSummary } from '#/app/sessionIndex/sessionIndex';
|
| 722 |
+
export * from '#/agent/undo/undo';
|
| 723 |
+
export * from '#/agent/undo/undoService';
|
| 724 |
+
export * from '#/agent/shellCommand/shellCommand';
|
| 725 |
+
export * from '#/agent/shellCommand/shellCommandService';
|
| 726 |
+
export * from '#/agent/agentContext/agentContext';
|
| 727 |
+
export * from '#/agent/agentContext/agentSpace';
|
| 728 |
+
export * from '#/agent/scopeContext/scopeContext';
|
| 729 |
+
export * from '#/features/sessionInit/sessionInit';
|
| 730 |
+
export * from '#/features/sessionInit/sessionInitService';
|
| 731 |
+
export * from '#/features/sessionInit/profile/init';
|
| 732 |
+
import '#/features/sessionInit/sessionInitFeature';
|
| 733 |
+
export * from '#/features/todo/todoItem';
|
| 734 |
+
export * from '#/features/todo/todoListReminder';
|
| 735 |
+
export * from '#/features/todo/todoService';
|
| 736 |
+
export * from '#/features/todo/tools/todo-list/todo-list';
|
| 737 |
+
import '#/features/todo/todoFeature';
|
| 738 |
+
export * from '#/features/notify/flag';
|
| 739 |
+
export * from '#/features/notify/notifyUserAvailability';
|
| 740 |
+
export * from '#/features/notify/tools/notify-user/notify-user';
|
| 741 |
+
import '#/features/notify/notifyFeature';
|
| 742 |
+
export * from '#/tool/toolContract';
|
| 743 |
+
export * from '#/agent/toolExecutor/toolHooks';
|
| 744 |
+
export * from '#/agent/toolExecutor/toolExecutor';
|
| 745 |
+
export * from '#/agent/toolExecutor/toolExecutorService';
|
| 746 |
+
export * from '#/agent/toolResultTruncation/toolResultTruncation';
|
| 747 |
+
import '#/agent/toolResultTruncation/toolResultTruncationService';
|
| 748 |
+
import '#/agent/toolActivation/toolActivationService';
|
| 749 |
+
import '#/agent/toolRegistry/toolContribution';
|
| 750 |
+
import '#/agent/toolRegistry/toolRegistry';
|
| 751 |
+
import '#/agent/toolRegistry/toolRegistryService';
|
| 752 |
+
export { IAgentToolActivationService } from '#/agent/toolActivation/toolActivation';
|
| 753 |
+
export { IAgentToolRegistryService } from '#/agent/toolRegistry/toolRegistry';
|
| 754 |
+
export { registerAgentToolService, AgentToolContribution } from '#/agent/toolRegistry/toolContribution';
|
| 755 |
+
export type { AgentToolContributionOptions } from '#/agent/toolRegistry/toolContribution';
|
| 756 |
+
export * from '#/agent/userTool/userTool';
|
| 757 |
+
export * from '#/agent/userTool/userToolOps';
|
| 758 |
+
export * from '#/agent/userTool/userToolService';
|
packages/agent-core-v2/src/llm-adapter/record-diff.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export interface RecordDiff {
|
| 2 |
+
readonly added: readonly string[];
|
| 3 |
+
readonly removed: readonly string[];
|
| 4 |
+
readonly changed: readonly string[];
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
export function isEmptyDiff(diff: RecordDiff): boolean {
|
| 8 |
+
return diff.added.length === 0 && diff.removed.length === 0 && diff.changed.length === 0;
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
export function diffRecords<T>(
|
| 12 |
+
previous: Readonly<Record<string, T>> | undefined,
|
| 13 |
+
current: Readonly<Record<string, T>> | undefined,
|
| 14 |
+
): RecordDiff {
|
| 15 |
+
const prev = previous ?? {};
|
| 16 |
+
const curr = current ?? {};
|
| 17 |
+
const added: string[] = [];
|
| 18 |
+
const removed: string[] = [];
|
| 19 |
+
const changed: string[] = [];
|
| 20 |
+
for (const key of Object.keys(curr)) {
|
| 21 |
+
if (!(key in prev)) {
|
| 22 |
+
added.push(key);
|
| 23 |
+
} else if (!deepEqual(prev[key], curr[key])) {
|
| 24 |
+
changed.push(key);
|
| 25 |
+
}
|
| 26 |
+
}
|
| 27 |
+
for (const key of Object.keys(prev)) {
|
| 28 |
+
if (!(key in curr)) {
|
| 29 |
+
removed.push(key);
|
| 30 |
+
}
|
| 31 |
+
}
|
| 32 |
+
return { added, removed, changed };
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
export function deepEqual(a: unknown, b: unknown): boolean {
|
| 36 |
+
if (Object.is(a, b)) return true;
|
| 37 |
+
if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false;
|
| 38 |
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
| 39 |
+
const aKeys = Object.keys(a);
|
| 40 |
+
const bKeys = Object.keys(b);
|
| 41 |
+
if (aKeys.length !== bKeys.length) return false;
|
| 42 |
+
for (const key of aKeys) {
|
| 43 |
+
if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
|
| 44 |
+
if (!deepEqual((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key])) {
|
| 45 |
+
return false;
|
| 46 |
+
}
|
| 47 |
+
}
|
| 48 |
+
return true;
|
| 49 |
+
}
|
packages/agent-core-v2/src/mcpCore/client-http.ts
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { ErrorCodes, Error2 } from '#/errors';
|
| 2 |
+
import type { McpServerHttpConfig } from './config-schema';
|
| 3 |
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
| 4 |
+
import type { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js';
|
| 5 |
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
| 6 |
+
|
| 7 |
+
import {
|
| 8 |
+
buildRequestOptions,
|
| 9 |
+
KIMI_MCP_CLIENT_NAME,
|
| 10 |
+
KIMI_MCP_CLIENT_VERSION,
|
| 11 |
+
MCP_LIVENESS_PROBE_TIMEOUT_MS,
|
| 12 |
+
toMcpToolDefinition,
|
| 13 |
+
toMcpToolResult,
|
| 14 |
+
type UnexpectedCloseListener,
|
| 15 |
+
type UnexpectedCloseReason,
|
| 16 |
+
} from './client-shared';
|
| 17 |
+
import { buildMcpRemoteHeaders } from './client-remote';
|
| 18 |
+
import { createMcpOAuthFetch } from './oauth/provider';
|
| 19 |
+
import type { MCPClient, MCPToolDefinition, MCPToolResult } from './types';
|
| 20 |
+
|
| 21 |
+
export interface HttpMcpClientOptions {
|
| 22 |
+
readonly clientName?: string;
|
| 23 |
+
readonly clientVersion?: string;
|
| 24 |
+
readonly startupTimeoutMs?: number;
|
| 25 |
+
readonly toolCallTimeoutMs?: number;
|
| 26 |
+
readonly envLookup?: (name: string) => string | undefined;
|
| 27 |
+
readonly fetch?: typeof fetch;
|
| 28 |
+
readonly oauthProvider?: OAuthClientProvider;
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
export class HttpMcpClient implements MCPClient {
|
| 32 |
+
private readonly client: Client;
|
| 33 |
+
private readonly transport: StreamableHTTPClientTransport;
|
| 34 |
+
private readonly startupTimeoutMs?: number;
|
| 35 |
+
private readonly toolCallTimeoutMs?: number;
|
| 36 |
+
private started = false;
|
| 37 |
+
private closed = false;
|
| 38 |
+
private ready = false;
|
| 39 |
+
private hooksInstalled = false;
|
| 40 |
+
private unexpectedCloseListener: UnexpectedCloseListener | undefined;
|
| 41 |
+
private lastTransportError: Error | undefined;
|
| 42 |
+
private pendingUnexpectedClose: UnexpectedCloseReason | undefined;
|
| 43 |
+
private unexpectedCloseFired = false;
|
| 44 |
+
|
| 45 |
+
constructor(config: McpServerHttpConfig, options: HttpMcpClientOptions = {}) {
|
| 46 |
+
const envLookup = options.envLookup ?? ((name) => process.env[name]);
|
| 47 |
+
const headers = buildMcpHttpHeaders(config, envLookup);
|
| 48 |
+
|
| 49 |
+
this.transport = new StreamableHTTPClientTransport(new URL(config.url), {
|
| 50 |
+
requestInit: headers !== undefined ? { headers } : undefined,
|
| 51 |
+
fetch: createMcpOAuthFetch(options.oauthProvider, options.fetch),
|
| 52 |
+
authProvider: options.oauthProvider,
|
| 53 |
+
});
|
| 54 |
+
this.client = new Client({
|
| 55 |
+
name: options.clientName ?? KIMI_MCP_CLIENT_NAME,
|
| 56 |
+
version: options.clientVersion ?? KIMI_MCP_CLIENT_VERSION,
|
| 57 |
+
});
|
| 58 |
+
this.startupTimeoutMs = options.startupTimeoutMs;
|
| 59 |
+
this.toolCallTimeoutMs = options.toolCallTimeoutMs;
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
async connect(): Promise<void> {
|
| 63 |
+
if (this.closed) {
|
| 64 |
+
throw new Error2(ErrorCodes.MCP_STARTUP_FAILED, 'MCP HTTP client is closed');
|
| 65 |
+
}
|
| 66 |
+
if (this.started) return;
|
| 67 |
+
this.started = true;
|
| 68 |
+
this.installTransportHooks();
|
| 69 |
+
try {
|
| 70 |
+
await this.client.connect(
|
| 71 |
+
this.transport,
|
| 72 |
+
buildRequestOptions(this.startupTimeoutMs, undefined),
|
| 73 |
+
);
|
| 74 |
+
} catch (error) {
|
| 75 |
+
await this.closeStartedClient();
|
| 76 |
+
throw error;
|
| 77 |
+
}
|
| 78 |
+
if (this.closed) {
|
| 79 |
+
await this.closeStartedClient();
|
| 80 |
+
throw new Error2(ErrorCodes.MCP_STARTUP_FAILED, 'MCP HTTP client was closed during startup');
|
| 81 |
+
}
|
| 82 |
+
this.ready = true;
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
async close(): Promise<void> {
|
| 86 |
+
if (this.closed) return;
|
| 87 |
+
this.closed = true;
|
| 88 |
+
await this.closeStartedClient();
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
onUnexpectedClose(listener: UnexpectedCloseListener): void {
|
| 92 |
+
this.unexpectedCloseListener = listener;
|
| 93 |
+
const pending = this.pendingUnexpectedClose;
|
| 94 |
+
if (pending !== undefined) {
|
| 95 |
+
this.pendingUnexpectedClose = undefined;
|
| 96 |
+
listener(pending);
|
| 97 |
+
}
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
async listTools(): Promise<MCPToolDefinition[]> {
|
| 101 |
+
const result = await this.client.listTools(
|
| 102 |
+
undefined,
|
| 103 |
+
buildRequestOptions(this.startupTimeoutMs, undefined),
|
| 104 |
+
);
|
| 105 |
+
return result.tools.map(toMcpToolDefinition);
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
async callTool(
|
| 109 |
+
name: string,
|
| 110 |
+
args: Record<string, unknown>,
|
| 111 |
+
signal?: AbortSignal,
|
| 112 |
+
): Promise<MCPToolResult> {
|
| 113 |
+
const requestOptions = buildRequestOptions(this.toolCallTimeoutMs, signal);
|
| 114 |
+
const result = await this.client.callTool({ name, arguments: args }, undefined, requestOptions);
|
| 115 |
+
return toMcpToolResult(result);
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
async ping(signal?: AbortSignal): Promise<void> {
|
| 119 |
+
await this.client.ping(buildRequestOptions(MCP_LIVENESS_PROBE_TIMEOUT_MS, signal));
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
private async closeStartedClient(): Promise<void> {
|
| 123 |
+
if (!this.started) return;
|
| 124 |
+
this.started = false;
|
| 125 |
+
await this.client.close();
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
private installTransportHooks(): void {
|
| 129 |
+
if (this.hooksInstalled) return;
|
| 130 |
+
this.hooksInstalled = true;
|
| 131 |
+
this.client.onclose = () => {
|
| 132 |
+
if (this.closed) return;
|
| 133 |
+
if (!this.ready) return;
|
| 134 |
+
this.fireUnexpectedClose({ error: this.lastTransportError });
|
| 135 |
+
};
|
| 136 |
+
this.client.onerror = (error) => {
|
| 137 |
+
this.lastTransportError = error;
|
| 138 |
+
if (this.closed) return;
|
| 139 |
+
if (!this.ready) return;
|
| 140 |
+
if (isTerminalTransportError(error)) {
|
| 141 |
+
this.fireUnexpectedClose({ error });
|
| 142 |
+
}
|
| 143 |
+
};
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
private fireUnexpectedClose(reason: UnexpectedCloseReason): void {
|
| 147 |
+
if (this.unexpectedCloseFired) return;
|
| 148 |
+
this.unexpectedCloseFired = true;
|
| 149 |
+
const listener = this.unexpectedCloseListener;
|
| 150 |
+
if (listener !== undefined) {
|
| 151 |
+
listener(reason);
|
| 152 |
+
} else {
|
| 153 |
+
this.pendingUnexpectedClose = reason;
|
| 154 |
+
}
|
| 155 |
+
}
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
export function isTerminalTransportError(error: Error): boolean {
|
| 159 |
+
if (error.name === 'UnauthorizedError') return true;
|
| 160 |
+
if (/Maximum reconnection attempts/i.test(error.message)) return true;
|
| 161 |
+
return false;
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
export function buildMcpHttpHeaders(
|
| 165 |
+
config: McpServerHttpConfig,
|
| 166 |
+
envLookup: (name: string) => string | undefined,
|
| 167 |
+
): Record<string, string> | undefined {
|
| 168 |
+
return buildMcpRemoteHeaders(config, envLookup);
|
| 169 |
+
}
|
packages/agent-core-v2/src/mcpCore/client-remote.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { McpRemoteServerConfig, McpServerConfig } from './config-schema';
|
| 2 |
+
import { ErrorCodes, Error2 } from '#/errors';
|
| 3 |
+
|
| 4 |
+
export function buildMcpRemoteHeaders(
|
| 5 |
+
config: McpRemoteServerConfig,
|
| 6 |
+
envLookup: (name: string) => string | undefined,
|
| 7 |
+
): Record<string, string> | undefined {
|
| 8 |
+
const headers: Record<string, string> = { ...config.headers };
|
| 9 |
+
if (config.bearerTokenEnvVar !== undefined) {
|
| 10 |
+
const token = envLookup(config.bearerTokenEnvVar);
|
| 11 |
+
if (token === undefined || token.length === 0) {
|
| 12 |
+
throw new Error2(
|
| 13 |
+
ErrorCodes.CONFIG_INVALID,
|
| 14 |
+
`MCP ${config.transport.toUpperCase()} bearer token env var "${config.bearerTokenEnvVar}" is not set or is empty`,
|
| 15 |
+
);
|
| 16 |
+
}
|
| 17 |
+
for (const key of Object.keys(headers)) {
|
| 18 |
+
if (key.toLowerCase() === 'authorization') {
|
| 19 |
+
delete headers[key];
|
| 20 |
+
}
|
| 21 |
+
}
|
| 22 |
+
headers['Authorization'] = `Bearer ${token}`;
|
| 23 |
+
}
|
| 24 |
+
return Object.keys(headers).length > 0 ? headers : undefined;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
export function isRemoteMcpConfig(config: McpServerConfig): config is McpRemoteServerConfig {
|
| 28 |
+
return config.transport === 'http' || config.transport === 'sse';
|
| 29 |
+
}
|
packages/agent-core-v2/src/mcpCore/client-shared.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { getCoreVersion } from '#/_base/version';
|
| 2 |
+
import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
|
| 3 |
+
|
| 4 |
+
import type { MCPClient, MCPToolDefinition, MCPToolResult } from './types';
|
| 5 |
+
|
| 6 |
+
export const KIMI_MCP_CLIENT_NAME = 'kimi-code';
|
| 7 |
+
export const KIMI_MCP_CLIENT_VERSION = getCoreVersion();
|
| 8 |
+
|
| 9 |
+
export interface UnexpectedCloseReason {
|
| 10 |
+
readonly error?: Error;
|
| 11 |
+
readonly stderr?: string;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
export type UnexpectedCloseListener = (reason: UnexpectedCloseReason) => void;
|
| 15 |
+
|
| 16 |
+
export function isMcpConnectionClosedError(error: unknown): boolean {
|
| 17 |
+
return (
|
| 18 |
+
error instanceof Error &&
|
| 19 |
+
(error as Error & { readonly code?: unknown }).code === ErrorCode.ConnectionClosed
|
| 20 |
+
);
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
export function isMcpTransportFailure(error: unknown): boolean {
|
| 24 |
+
if (!(error instanceof Error)) return false;
|
| 25 |
+
if (isMcpConnectionClosedError(error)) return true;
|
| 26 |
+
return !(error instanceof McpError);
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
export const MCP_LIVENESS_PROBE_TIMEOUT_MS = 5_000;
|
| 30 |
+
|
| 31 |
+
export function isMcpMalformedResultError(error: unknown): boolean {
|
| 32 |
+
return error instanceof Error && error.name === 'ZodError';
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
export async function probeMcpLiveness(client: MCPClient, signal: AbortSignal): Promise<boolean> {
|
| 36 |
+
try {
|
| 37 |
+
await client.ping(signal);
|
| 38 |
+
return true;
|
| 39 |
+
} catch (error) {
|
| 40 |
+
if (isMcpConnectionClosedError(error)) return false;
|
| 41 |
+
if (isMcpMalformedResultError(error)) return true;
|
| 42 |
+
if (error instanceof McpError) {
|
| 43 |
+
return (error as Error & { readonly code?: unknown }).code !== ErrorCode.RequestTimeout;
|
| 44 |
+
}
|
| 45 |
+
return false;
|
| 46 |
+
}
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
export interface McpRequestOptions {
|
| 50 |
+
readonly timeout?: number;
|
| 51 |
+
readonly signal?: AbortSignal;
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
export function buildRequestOptions(
|
| 55 |
+
timeoutMs: number | undefined,
|
| 56 |
+
signal: AbortSignal | undefined,
|
| 57 |
+
): McpRequestOptions | undefined {
|
| 58 |
+
if (timeoutMs === undefined && signal === undefined) return undefined;
|
| 59 |
+
return { timeout: timeoutMs, signal };
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
interface SdkListedTool {
|
| 63 |
+
readonly name: string;
|
| 64 |
+
readonly description?: string;
|
| 65 |
+
readonly inputSchema: Record<string, unknown>;
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
export function toMcpToolDefinition(tool: SdkListedTool): MCPToolDefinition {
|
| 69 |
+
return {
|
| 70 |
+
name: tool.name,
|
| 71 |
+
description: tool.description ?? '',
|
| 72 |
+
inputSchema: tool.inputSchema,
|
| 73 |
+
};
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
export function toMcpToolResult(result: unknown): MCPToolResult {
|
| 77 |
+
if (typeof result === 'object' && result !== null && 'content' in result) {
|
| 78 |
+
const typed = result as {
|
| 79 |
+
content: unknown;
|
| 80 |
+
isError?: unknown;
|
| 81 |
+
structuredContent?: unknown;
|
| 82 |
+
_meta?: unknown;
|
| 83 |
+
};
|
| 84 |
+
if (Array.isArray(typed.content)) {
|
| 85 |
+
return {
|
| 86 |
+
content: typed.content as MCPToolResult['content'],
|
| 87 |
+
isError: typed.isError === true,
|
| 88 |
+
structuredContent: typed.structuredContent,
|
| 89 |
+
_meta:
|
| 90 |
+
typeof typed._meta === 'object' && typed._meta !== null
|
| 91 |
+
? (typed._meta as Record<string, unknown>)
|
| 92 |
+
: undefined,
|
| 93 |
+
};
|
| 94 |
+
}
|
| 95 |
+
}
|
| 96 |
+
if (typeof result === 'object' && result !== null && 'toolResult' in result) {
|
| 97 |
+
const legacy = (result as { toolResult: unknown }).toolResult;
|
| 98 |
+
return {
|
| 99 |
+
content: [
|
| 100 |
+
{
|
| 101 |
+
type: 'text',
|
| 102 |
+
text: typeof legacy === 'string' ? legacy : JSON.stringify(legacy),
|
| 103 |
+
},
|
| 104 |
+
],
|
| 105 |
+
isError: false,
|
| 106 |
+
};
|
| 107 |
+
}
|
| 108 |
+
return { content: [], isError: false };
|
| 109 |
+
}
|
packages/agent-core-v2/src/mcpCore/client-sse.ts
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { ErrorCodes, Error2 } from '#/errors';
|
| 2 |
+
import type { McpServerSseConfig } from './config-schema';
|
| 3 |
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
| 4 |
+
import type { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js';
|
| 5 |
+
import { SSEClientTransport, SseError } from '@modelcontextprotocol/sdk/client/sse.js';
|
| 6 |
+
|
| 7 |
+
import {
|
| 8 |
+
buildRequestOptions,
|
| 9 |
+
KIMI_MCP_CLIENT_NAME,
|
| 10 |
+
KIMI_MCP_CLIENT_VERSION,
|
| 11 |
+
MCP_LIVENESS_PROBE_TIMEOUT_MS,
|
| 12 |
+
toMcpToolDefinition,
|
| 13 |
+
toMcpToolResult,
|
| 14 |
+
type UnexpectedCloseListener,
|
| 15 |
+
type UnexpectedCloseReason,
|
| 16 |
+
} from './client-shared';
|
| 17 |
+
import { buildMcpRemoteHeaders } from './client-remote';
|
| 18 |
+
import { createMcpOAuthFetch } from './oauth/provider';
|
| 19 |
+
import type { MCPClient, MCPToolDefinition, MCPToolResult } from './types';
|
| 20 |
+
|
| 21 |
+
export interface SseMcpClientOptions {
|
| 22 |
+
readonly clientName?: string;
|
| 23 |
+
readonly clientVersion?: string;
|
| 24 |
+
readonly startupTimeoutMs?: number;
|
| 25 |
+
readonly toolCallTimeoutMs?: number;
|
| 26 |
+
readonly envLookup?: (name: string) => string | undefined;
|
| 27 |
+
readonly fetch?: typeof fetch;
|
| 28 |
+
readonly oauthProvider?: OAuthClientProvider;
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
export class SseMcpClient implements MCPClient {
|
| 32 |
+
private readonly client: Client;
|
| 33 |
+
private readonly transport: SSEClientTransport;
|
| 34 |
+
private readonly startupTimeoutMs?: number;
|
| 35 |
+
private readonly toolCallTimeoutMs?: number;
|
| 36 |
+
private started = false;
|
| 37 |
+
private closed = false;
|
| 38 |
+
private ready = false;
|
| 39 |
+
private hooksInstalled = false;
|
| 40 |
+
private unexpectedCloseListener: UnexpectedCloseListener | undefined;
|
| 41 |
+
private lastTransportError: Error | undefined;
|
| 42 |
+
private pendingUnexpectedClose: UnexpectedCloseReason | undefined;
|
| 43 |
+
private unexpectedCloseFired = false;
|
| 44 |
+
|
| 45 |
+
constructor(config: McpServerSseConfig, options: SseMcpClientOptions = {}) {
|
| 46 |
+
const envLookup = options.envLookup ?? ((name) => process.env[name]);
|
| 47 |
+
const headers = buildMcpRemoteHeaders(config, envLookup);
|
| 48 |
+
|
| 49 |
+
this.transport = new SSEClientTransport(new URL(config.url), {
|
| 50 |
+
requestInit: headers !== undefined ? { headers } : undefined,
|
| 51 |
+
fetch: createMcpOAuthFetch(options.oauthProvider, options.fetch),
|
| 52 |
+
authProvider: options.oauthProvider,
|
| 53 |
+
});
|
| 54 |
+
this.client = new Client({
|
| 55 |
+
name: options.clientName ?? KIMI_MCP_CLIENT_NAME,
|
| 56 |
+
version: options.clientVersion ?? KIMI_MCP_CLIENT_VERSION,
|
| 57 |
+
});
|
| 58 |
+
this.startupTimeoutMs = options.startupTimeoutMs;
|
| 59 |
+
this.toolCallTimeoutMs = options.toolCallTimeoutMs;
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
async connect(): Promise<void> {
|
| 63 |
+
if (this.closed) {
|
| 64 |
+
throw new Error2(ErrorCodes.MCP_STARTUP_FAILED, 'MCP SSE client is closed');
|
| 65 |
+
}
|
| 66 |
+
if (this.started) return;
|
| 67 |
+
this.started = true;
|
| 68 |
+
this.installTransportHooks();
|
| 69 |
+
try {
|
| 70 |
+
await this.client.connect(
|
| 71 |
+
this.transport,
|
| 72 |
+
buildRequestOptions(this.startupTimeoutMs, undefined),
|
| 73 |
+
);
|
| 74 |
+
} catch (error) {
|
| 75 |
+
await this.closeStartedClient();
|
| 76 |
+
throw error;
|
| 77 |
+
}
|
| 78 |
+
if (this.closed) {
|
| 79 |
+
await this.closeStartedClient();
|
| 80 |
+
throw new Error2(ErrorCodes.MCP_STARTUP_FAILED, 'MCP SSE client was closed during startup');
|
| 81 |
+
}
|
| 82 |
+
this.ready = true;
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
async close(): Promise<void> {
|
| 86 |
+
if (this.closed) return;
|
| 87 |
+
this.closed = true;
|
| 88 |
+
await this.closeStartedClient();
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
onUnexpectedClose(listener: UnexpectedCloseListener): void {
|
| 92 |
+
this.unexpectedCloseListener = listener;
|
| 93 |
+
const pending = this.pendingUnexpectedClose;
|
| 94 |
+
if (pending !== undefined) {
|
| 95 |
+
this.pendingUnexpectedClose = undefined;
|
| 96 |
+
listener(pending);
|
| 97 |
+
}
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
async listTools(): Promise<MCPToolDefinition[]> {
|
| 101 |
+
const result = await this.client.listTools(
|
| 102 |
+
undefined,
|
| 103 |
+
buildRequestOptions(this.startupTimeoutMs, undefined),
|
| 104 |
+
);
|
| 105 |
+
return result.tools.map(toMcpToolDefinition);
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
async callTool(
|
| 109 |
+
name: string,
|
| 110 |
+
args: Record<string, unknown>,
|
| 111 |
+
signal?: AbortSignal,
|
| 112 |
+
): Promise<MCPToolResult> {
|
| 113 |
+
const requestOptions = buildRequestOptions(this.toolCallTimeoutMs, signal);
|
| 114 |
+
const result = await this.client.callTool({ name, arguments: args }, undefined, requestOptions);
|
| 115 |
+
return toMcpToolResult(result);
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
async ping(signal?: AbortSignal): Promise<void> {
|
| 119 |
+
await this.client.ping(buildRequestOptions(MCP_LIVENESS_PROBE_TIMEOUT_MS, signal));
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
private async closeStartedClient(): Promise<void> {
|
| 123 |
+
if (!this.started) return;
|
| 124 |
+
this.started = false;
|
| 125 |
+
await this.client.close();
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
private installTransportHooks(): void {
|
| 129 |
+
if (this.hooksInstalled) return;
|
| 130 |
+
this.hooksInstalled = true;
|
| 131 |
+
this.client.onclose = () => {
|
| 132 |
+
if (this.closed) return;
|
| 133 |
+
if (!this.ready) return;
|
| 134 |
+
this.fireUnexpectedClose({ error: this.lastTransportError });
|
| 135 |
+
};
|
| 136 |
+
this.client.onerror = (error) => {
|
| 137 |
+
this.lastTransportError = error;
|
| 138 |
+
if (this.closed) return;
|
| 139 |
+
if (!this.ready) return;
|
| 140 |
+
if (isTerminalSseTransportError(error)) {
|
| 141 |
+
this.fireUnexpectedClose({ error });
|
| 142 |
+
}
|
| 143 |
+
};
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
private fireUnexpectedClose(reason: UnexpectedCloseReason): void {
|
| 147 |
+
if (this.unexpectedCloseFired) return;
|
| 148 |
+
this.unexpectedCloseFired = true;
|
| 149 |
+
const listener = this.unexpectedCloseListener;
|
| 150 |
+
if (listener !== undefined) {
|
| 151 |
+
listener(reason);
|
| 152 |
+
} else {
|
| 153 |
+
this.pendingUnexpectedClose = reason;
|
| 154 |
+
}
|
| 155 |
+
}
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
export function isTerminalSseTransportError(error: Error): boolean {
|
| 159 |
+
if (error.name === 'UnauthorizedError') return true;
|
| 160 |
+
return error instanceof SseError && error.code !== undefined;
|
| 161 |
+
}
|
packages/agent-core-v2/src/mcpCore/client-stdio.ts
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
| 2 |
+
import { ReadBuffer, serializeMessage } from '@modelcontextprotocol/sdk/shared/stdio.js';
|
| 3 |
+
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
| 4 |
+
import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
|
| 5 |
+
|
| 6 |
+
import { ErrorCodes, Error2 } from '#/errors';
|
| 7 |
+
import type { IHostProcess } from '#/os/interface/hostProcess';
|
| 8 |
+
import type { IRuntimeResolver } from '#/workspace/workspaceInstance/workspaceInstanceManager';
|
| 9 |
+
import { proxyEnvForChild, reconcileChildNoProxy } from '#/_base/utils/proxy';
|
| 10 |
+
|
| 11 |
+
import {
|
| 12 |
+
buildRequestOptions,
|
| 13 |
+
KIMI_MCP_CLIENT_NAME,
|
| 14 |
+
KIMI_MCP_CLIENT_VERSION,
|
| 15 |
+
MCP_LIVENESS_PROBE_TIMEOUT_MS,
|
| 16 |
+
toMcpToolDefinition,
|
| 17 |
+
toMcpToolResult,
|
| 18 |
+
type UnexpectedCloseListener,
|
| 19 |
+
type UnexpectedCloseReason,
|
| 20 |
+
} from './client-shared';
|
| 21 |
+
import type { McpServerStdioConfig } from './config-schema';
|
| 22 |
+
import type { MCPClient, MCPToolDefinition, MCPToolResult } from './types';
|
| 23 |
+
|
| 24 |
+
export interface StdioMcpClientOptions {
|
| 25 |
+
readonly clientName?: string;
|
| 26 |
+
readonly clientVersion?: string;
|
| 27 |
+
readonly startupTimeoutMs?: number;
|
| 28 |
+
readonly toolCallTimeoutMs?: number;
|
| 29 |
+
readonly defaultCwd?: string;
|
| 30 |
+
readonly runtimeResolver: IRuntimeResolver;
|
| 31 |
+
readonly workspaceId: string;
|
| 32 |
+
readonly runtimeId: string;
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
const STDERR_BUFFER_CAPACITY = 4 * 1024;
|
| 36 |
+
|
| 37 |
+
export class StdioMcpClient implements MCPClient {
|
| 38 |
+
private readonly client: Client;
|
| 39 |
+
private readonly transport: RuntimeStdioTransport;
|
| 40 |
+
private readonly startupTimeoutMs?: number;
|
| 41 |
+
private readonly toolCallTimeoutMs?: number;
|
| 42 |
+
private readonly stderrBuffer = new BoundedTail(STDERR_BUFFER_CAPACITY);
|
| 43 |
+
private started = false;
|
| 44 |
+
private closed = false;
|
| 45 |
+
private ready = false;
|
| 46 |
+
private hooksInstalled = false;
|
| 47 |
+
private unexpectedCloseListener: UnexpectedCloseListener | undefined;
|
| 48 |
+
private lastTransportError: Error | undefined;
|
| 49 |
+
private pendingUnexpectedClose: UnexpectedCloseReason | undefined;
|
| 50 |
+
|
| 51 |
+
static readonly stderrBufferCapacity = STDERR_BUFFER_CAPACITY;
|
| 52 |
+
|
| 53 |
+
constructor(config: McpServerStdioConfig, options: StdioMcpClientOptions) {
|
| 54 |
+
if (config.executor !== undefined && config.executor !== 'local') {
|
| 55 |
+
throw new Error2(ErrorCodes.NOT_IMPLEMENTED, `MCP stdio executor '${config.executor}' is not yet implemented`);
|
| 56 |
+
}
|
| 57 |
+
this.transport = new RuntimeStdioTransport(config, options, this.stderrBuffer);
|
| 58 |
+
this.client = new Client({
|
| 59 |
+
name: options.clientName ?? KIMI_MCP_CLIENT_NAME,
|
| 60 |
+
version: options.clientVersion ?? KIMI_MCP_CLIENT_VERSION,
|
| 61 |
+
});
|
| 62 |
+
this.startupTimeoutMs = options.startupTimeoutMs;
|
| 63 |
+
this.toolCallTimeoutMs = options.toolCallTimeoutMs;
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
async connect(): Promise<void> {
|
| 67 |
+
if (this.closed) {
|
| 68 |
+
throw new Error2(ErrorCodes.MCP_STARTUP_FAILED, 'MCP stdio client is closed');
|
| 69 |
+
}
|
| 70 |
+
if (this.started) return;
|
| 71 |
+
this.started = true;
|
| 72 |
+
this.installTransportHooks();
|
| 73 |
+
try {
|
| 74 |
+
await this.client.connect(
|
| 75 |
+
this.transport,
|
| 76 |
+
buildRequestOptions(this.startupTimeoutMs, undefined),
|
| 77 |
+
);
|
| 78 |
+
} catch (error) {
|
| 79 |
+
await this.closeStartedClient();
|
| 80 |
+
throw error;
|
| 81 |
+
}
|
| 82 |
+
if (this.closed) {
|
| 83 |
+
await this.closeStartedClient();
|
| 84 |
+
throw new Error2(ErrorCodes.MCP_STARTUP_FAILED, 'MCP stdio client was closed during startup');
|
| 85 |
+
}
|
| 86 |
+
this.ready = true;
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
async close(): Promise<void> {
|
| 90 |
+
if (this.closed) return;
|
| 91 |
+
this.closed = true;
|
| 92 |
+
await this.closeStartedClient();
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
onUnexpectedClose(listener: UnexpectedCloseListener): void {
|
| 96 |
+
this.unexpectedCloseListener = listener;
|
| 97 |
+
const pending = this.pendingUnexpectedClose;
|
| 98 |
+
if (pending !== undefined) {
|
| 99 |
+
this.pendingUnexpectedClose = undefined;
|
| 100 |
+
listener(pending);
|
| 101 |
+
}
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
stderrSnapshot(): string {
|
| 105 |
+
return this.stderrBuffer.snapshot();
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
async listTools(): Promise<MCPToolDefinition[]> {
|
| 109 |
+
const result = await this.client.listTools(
|
| 110 |
+
undefined,
|
| 111 |
+
buildRequestOptions(this.startupTimeoutMs, undefined),
|
| 112 |
+
);
|
| 113 |
+
return result.tools.map(toMcpToolDefinition);
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
async callTool(
|
| 117 |
+
name: string,
|
| 118 |
+
args: Record<string, unknown>,
|
| 119 |
+
signal?: AbortSignal,
|
| 120 |
+
): Promise<MCPToolResult> {
|
| 121 |
+
const requestOptions = buildRequestOptions(this.toolCallTimeoutMs, signal);
|
| 122 |
+
const result = await this.client.callTool({ name, arguments: args }, undefined, requestOptions);
|
| 123 |
+
return toMcpToolResult(result);
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
async ping(signal?: AbortSignal): Promise<void> {
|
| 127 |
+
await this.client.ping(buildRequestOptions(MCP_LIVENESS_PROBE_TIMEOUT_MS, signal));
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
private async closeStartedClient(): Promise<void> {
|
| 131 |
+
if (!this.started) return;
|
| 132 |
+
this.started = false;
|
| 133 |
+
await this.client.close();
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
private installTransportHooks(): void {
|
| 137 |
+
if (this.hooksInstalled) return;
|
| 138 |
+
this.hooksInstalled = true;
|
| 139 |
+
this.client.onclose = () => {
|
| 140 |
+
if (this.closed) return;
|
| 141 |
+
if (!this.ready) return;
|
| 142 |
+
const stderr = this.stderrBuffer.snapshot();
|
| 143 |
+
const reason: UnexpectedCloseReason = {
|
| 144 |
+
error: this.lastTransportError,
|
| 145 |
+
stderr: stderr.length > 0 ? stderr : undefined,
|
| 146 |
+
};
|
| 147 |
+
const listener = this.unexpectedCloseListener;
|
| 148 |
+
if (listener !== undefined) {
|
| 149 |
+
listener(reason);
|
| 150 |
+
} else {
|
| 151 |
+
this.pendingUnexpectedClose = reason;
|
| 152 |
+
}
|
| 153 |
+
};
|
| 154 |
+
this.client.onerror = (error) => {
|
| 155 |
+
this.lastTransportError = error;
|
| 156 |
+
};
|
| 157 |
+
}
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
class RuntimeStdioTransport implements Transport {
|
| 161 |
+
onclose?: () => void;
|
| 162 |
+
onerror?: (error: Error) => void;
|
| 163 |
+
onmessage?: <T extends JSONRPCMessage>(message: T) => void;
|
| 164 |
+
private readonly readBuffer = new ReadBuffer();
|
| 165 |
+
private process: IHostProcess | undefined;
|
| 166 |
+
private lease: ReturnType<IRuntimeResolver['acquire']> | undefined;
|
| 167 |
+
private started = false;
|
| 168 |
+
private closed = false;
|
| 169 |
+
|
| 170 |
+
constructor(
|
| 171 |
+
private readonly config: McpServerStdioConfig,
|
| 172 |
+
private readonly options: StdioMcpClientOptions,
|
| 173 |
+
private readonly stderr: BoundedTail,
|
| 174 |
+
) {}
|
| 175 |
+
|
| 176 |
+
async start(): Promise<void> {
|
| 177 |
+
if (this.started) throw new Error('Runtime stdio transport is already started');
|
| 178 |
+
if (this.closed) throw new Error('Runtime stdio transport is closed');
|
| 179 |
+
this.started = true;
|
| 180 |
+
const lease = this.options.runtimeResolver.acquire(
|
| 181 |
+
{ workspaceId: this.options.workspaceId, runtimeId: this.options.runtimeId },
|
| 182 |
+
['process'],
|
| 183 |
+
);
|
| 184 |
+
this.lease = lease;
|
| 185 |
+
try {
|
| 186 |
+
const base = lease.runtime.path.resolve(this.options.defaultCwd ?? lease.runtime.environment.homeDir);
|
| 187 |
+
const cwd = this.config.cwd === undefined ? base : lease.runtime.path.resolve(base, this.config.cwd);
|
| 188 |
+
const process = lease.track(await lease.runtime.process!.spawn(
|
| 189 |
+
this.config.command,
|
| 190 |
+
this.config.args,
|
| 191 |
+
{ cwd, env: mergeStdioEnv(this.config.env) },
|
| 192 |
+
));
|
| 193 |
+
this.process = process;
|
| 194 |
+
lease.track(this);
|
| 195 |
+
process.stdin.on('error', (error: Error) => this.onerror?.(error));
|
| 196 |
+
process.stdout.on('data', (chunk: Buffer | string) => this.onData(chunk));
|
| 197 |
+
process.stdout.on('end', () => this.finish());
|
| 198 |
+
process.stdout.on('error', (error: Error) => this.onerror?.(error));
|
| 199 |
+
process.stderr.on('data', (chunk: Buffer | string) => {
|
| 200 |
+
this.stderr.push(typeof chunk === 'string' ? chunk : chunk.toString('utf8'));
|
| 201 |
+
});
|
| 202 |
+
process.stderr.on('error', (error: Error) => this.onerror?.(error));
|
| 203 |
+
void process.wait().then(
|
| 204 |
+
() => this.finish(),
|
| 205 |
+
(error: unknown) => {
|
| 206 |
+
this.onerror?.(error instanceof Error ? error : new Error(String(error)));
|
| 207 |
+
this.finish();
|
| 208 |
+
},
|
| 209 |
+
);
|
| 210 |
+
} catch (error) {
|
| 211 |
+
this.lease = undefined;
|
| 212 |
+
lease.dispose();
|
| 213 |
+
throw error;
|
| 214 |
+
}
|
| 215 |
+
}
|
| 216 |
+
|
| 217 |
+
async send(message: JSONRPCMessage): Promise<void> {
|
| 218 |
+
const process = this.process;
|
| 219 |
+
if (process === undefined || this.closed) throw new Error('Runtime stdio transport is not running');
|
| 220 |
+
const data = serializeMessage(message);
|
| 221 |
+
await new Promise<void>((resolve, reject) => {
|
| 222 |
+
process.stdin.write(data, (error) => {
|
| 223 |
+
if (error !== null && error !== undefined) reject(error);
|
| 224 |
+
else resolve();
|
| 225 |
+
});
|
| 226 |
+
});
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
dispose(): Promise<void> {
|
| 230 |
+
return this.close();
|
| 231 |
+
}
|
| 232 |
+
|
| 233 |
+
async close(): Promise<void> {
|
| 234 |
+
if (this.closed) return;
|
| 235 |
+
this.closed = true;
|
| 236 |
+
const process = this.process;
|
| 237 |
+
this.process = undefined;
|
| 238 |
+
if (process !== undefined) {
|
| 239 |
+
try {
|
| 240 |
+
await process.kill();
|
| 241 |
+
} catch {}
|
| 242 |
+
void process.dispose();
|
| 243 |
+
}
|
| 244 |
+
this.readBuffer.clear();
|
| 245 |
+
const lease = this.lease;
|
| 246 |
+
this.lease = undefined;
|
| 247 |
+
lease?.dispose();
|
| 248 |
+
this.onclose?.();
|
| 249 |
+
}
|
| 250 |
+
|
| 251 |
+
private onData(chunk: Buffer | string): void {
|
| 252 |
+
this.readBuffer.append(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);
|
| 253 |
+
while (true) {
|
| 254 |
+
try {
|
| 255 |
+
const message = this.readBuffer.readMessage();
|
| 256 |
+
if (message === null) return;
|
| 257 |
+
this.onmessage?.(message);
|
| 258 |
+
} catch (error) {
|
| 259 |
+
this.onerror?.(error instanceof Error ? error : new Error(String(error)));
|
| 260 |
+
}
|
| 261 |
+
}
|
| 262 |
+
}
|
| 263 |
+
|
| 264 |
+
private finish(): void {
|
| 265 |
+
if (this.closed) return;
|
| 266 |
+
this.closed = true;
|
| 267 |
+
this.process = undefined;
|
| 268 |
+
this.readBuffer.clear();
|
| 269 |
+
const lease = this.lease;
|
| 270 |
+
this.lease = undefined;
|
| 271 |
+
lease?.dispose();
|
| 272 |
+
this.onclose?.();
|
| 273 |
+
}
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
class BoundedTail {
|
| 277 |
+
private buffer = '';
|
| 278 |
+
constructor(private readonly capacity: number) {}
|
| 279 |
+
|
| 280 |
+
push(chunk: string): void {
|
| 281 |
+
this.buffer += chunk;
|
| 282 |
+
if (this.buffer.length > this.capacity) {
|
| 283 |
+
this.buffer = this.buffer.slice(this.buffer.length - this.capacity);
|
| 284 |
+
}
|
| 285 |
+
}
|
| 286 |
+
|
| 287 |
+
snapshot(): string {
|
| 288 |
+
return this.buffer;
|
| 289 |
+
}
|
| 290 |
+
}
|
| 291 |
+
|
| 292 |
+
export function mergeStdioEnv(
|
| 293 |
+
configEnv?: Record<string, string>,
|
| 294 |
+
parentEnv: Readonly<Record<string, string | undefined>> = process.env,
|
| 295 |
+
): Record<string, string> {
|
| 296 |
+
const merged: Record<string, string> = {};
|
| 297 |
+
for (const [key, value] of Object.entries(parentEnv)) {
|
| 298 |
+
if (value !== undefined) merged[key] = value;
|
| 299 |
+
}
|
| 300 |
+
if (configEnv !== undefined) Object.assign(merged, configEnv);
|
| 301 |
+
Object.assign(merged, proxyEnvForChild(merged));
|
| 302 |
+
reconcileChildNoProxy(merged, configEnv);
|
| 303 |
+
return merged;
|
| 304 |
+
}
|
packages/agent-core-v2/src/mcpCore/config-schema.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { z } from 'zod';
|
| 2 |
+
|
| 3 |
+
const StringRecordSchema = z.record(z.string(), z.string());
|
| 4 |
+
|
| 5 |
+
export const MAX_MCP_TIMEOUT_MS = 2_147_483_647;
|
| 6 |
+
export const McpTimeoutMsSchema = z.number().int().min(1).max(MAX_MCP_TIMEOUT_MS);
|
| 7 |
+
|
| 8 |
+
const McpServerCommonFields = {
|
| 9 |
+
enabled: z.boolean().optional(),
|
| 10 |
+
deferred: z.boolean().optional(),
|
| 11 |
+
startupTimeoutMs: McpTimeoutMsSchema.optional(),
|
| 12 |
+
toolTimeoutMs: McpTimeoutMsSchema.optional(),
|
| 13 |
+
enabledTools: z.array(z.string()).optional(),
|
| 14 |
+
disabledTools: z.array(z.string()).optional(),
|
| 15 |
+
} as const;
|
| 16 |
+
|
| 17 |
+
export const McpServerStdioConfigSchema = z.object({
|
| 18 |
+
transport: z.literal('stdio'),
|
| 19 |
+
command: z.string().min(1),
|
| 20 |
+
args: z.array(z.string()).optional(),
|
| 21 |
+
env: StringRecordSchema.optional(),
|
| 22 |
+
cwd: z.string().optional(),
|
| 23 |
+
executor: z.enum(['local', 'kaos']).optional(),
|
| 24 |
+
runtime_id: z.string().min(1).optional(),
|
| 25 |
+
...McpServerCommonFields,
|
| 26 |
+
});
|
| 27 |
+
|
| 28 |
+
export type McpServerStdioConfig = z.infer<typeof McpServerStdioConfigSchema>;
|
| 29 |
+
|
| 30 |
+
export const McpServerHttpConfigSchema = z.object({
|
| 31 |
+
transport: z.literal('http'),
|
| 32 |
+
url: z.string().url(),
|
| 33 |
+
headers: StringRecordSchema.optional(),
|
| 34 |
+
auth: z.literal('oauth').optional(),
|
| 35 |
+
bearerTokenEnvVar: z.string().min(1).optional(),
|
| 36 |
+
...McpServerCommonFields,
|
| 37 |
+
});
|
| 38 |
+
|
| 39 |
+
export type McpServerHttpConfig = z.infer<typeof McpServerHttpConfigSchema>;
|
| 40 |
+
|
| 41 |
+
export const McpServerSseConfigSchema = z.object({
|
| 42 |
+
transport: z.literal('sse'),
|
| 43 |
+
url: z.string().url(),
|
| 44 |
+
headers: StringRecordSchema.optional(),
|
| 45 |
+
auth: z.literal('oauth').optional(),
|
| 46 |
+
bearerTokenEnvVar: z.string().min(1).optional(),
|
| 47 |
+
...McpServerCommonFields,
|
| 48 |
+
});
|
| 49 |
+
|
| 50 |
+
export type McpServerSseConfig = z.infer<typeof McpServerSseConfigSchema>;
|
| 51 |
+
export type McpRemoteServerConfig = McpServerHttpConfig | McpServerSseConfig;
|
| 52 |
+
|
| 53 |
+
const McpServerConfigDiscriminatedSchema = z.discriminatedUnion('transport', [
|
| 54 |
+
McpServerStdioConfigSchema,
|
| 55 |
+
McpServerHttpConfigSchema,
|
| 56 |
+
McpServerSseConfigSchema,
|
| 57 |
+
]);
|
| 58 |
+
|
| 59 |
+
export const McpServerConfigSchema = z.preprocess((raw) => {
|
| 60 |
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) return raw;
|
| 61 |
+
const obj = raw as Record<string, unknown>;
|
| 62 |
+
if ('transport' in obj) return obj;
|
| 63 |
+
if (typeof obj['command'] === 'string') return { ...obj, transport: 'stdio' };
|
| 64 |
+
if (typeof obj['url'] === 'string') return { ...obj, transport: 'http' };
|
| 65 |
+
return obj;
|
| 66 |
+
}, McpServerConfigDiscriminatedSchema);
|
| 67 |
+
|
| 68 |
+
export type McpServerConfig = z.infer<typeof McpServerConfigSchema>;
|
packages/agent-core-v2/src/mcpCore/configView.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { McpServerConfig } from './config-schema';
|
| 2 |
+
|
| 3 |
+
export type McpServerConfigView =
|
| 4 |
+
| (Omit<Extract<McpServerConfig, { readonly transport: 'stdio' }>, 'env'> & {
|
| 5 |
+
readonly envKeys?: readonly string[];
|
| 6 |
+
})
|
| 7 |
+
| (Omit<Exclude<McpServerConfig, { readonly transport: 'stdio' }>, 'headers'> & {
|
| 8 |
+
readonly headerKeys?: readonly string[];
|
| 9 |
+
});
|
| 10 |
+
|
| 11 |
+
export function toMcpServerConfigView(config: McpServerConfig): McpServerConfigView {
|
| 12 |
+
if (config.transport === 'stdio') {
|
| 13 |
+
const { env, ...safe } = config;
|
| 14 |
+
return env === undefined ? safe : { ...safe, envKeys: Object.keys(env).toSorted() };
|
| 15 |
+
}
|
| 16 |
+
const { headers, ...safe } = config;
|
| 17 |
+
return headers === undefined ? safe : { ...safe, headerKeys: Object.keys(headers).toSorted() };
|
| 18 |
+
}
|
packages/agent-core-v2/src/mcpCore/connection-manager.ts
ADDED
|
@@ -0,0 +1,603 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { ErrorCodes, Error2 } from '#/errors';
|
| 2 |
+
import type { McpServerConfig } from './config-schema';
|
| 3 |
+
import type { ILogger as Logger } from '#/_base/log/log';
|
| 4 |
+
import type { ToolDescription as Tool } from '#human/llm/message';
|
| 5 |
+
import { HostProcessError, HostProcessErrorCode } from '#/os/interface/hostProcess';
|
| 6 |
+
|
| 7 |
+
import { abortable } from '#/_base/utils/abort';
|
| 8 |
+
import { HttpMcpClient } from './client-http';
|
| 9 |
+
import { isRemoteMcpConfig } from './client-remote';
|
| 10 |
+
import { SseMcpClient } from './client-sse';
|
| 11 |
+
import type { UnexpectedCloseReason } from './client-shared';
|
| 12 |
+
import { StdioMcpClient } from './client-stdio';
|
| 13 |
+
import type { McpOAuthService } from '#/mcpCore/oauth/service';
|
| 14 |
+
import { assertMcpInputSchema, type MCPClient, type MCPToolDefinition } from './types';
|
| 15 |
+
|
| 16 |
+
export type McpServerStatus = 'pending' | 'connected' | 'failed' | 'disabled' | 'needs-auth' | 'removed';
|
| 17 |
+
|
| 18 |
+
export interface McpServerEntry {
|
| 19 |
+
readonly name: string;
|
| 20 |
+
readonly transport: McpServerConfig['transport'];
|
| 21 |
+
readonly status: McpServerStatus;
|
| 22 |
+
readonly toolCount: number;
|
| 23 |
+
readonly error?: string;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
interface InternalEntry {
|
| 27 |
+
readonly name: string;
|
| 28 |
+
readonly config: McpServerConfig;
|
| 29 |
+
attemptId: number;
|
| 30 |
+
status: McpServerStatus;
|
| 31 |
+
tools?: readonly Tool[];
|
| 32 |
+
rawTools?: readonly MCPToolDefinition[];
|
| 33 |
+
enabledNames?: ReadonlySet<string>;
|
| 34 |
+
error?: string;
|
| 35 |
+
client?: RuntimeMcpClient;
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
export type McpStatusListener = (entry: McpServerEntry) => void;
|
| 39 |
+
|
| 40 |
+
export interface McpConnectionView {
|
| 41 |
+
readonly oauthService: McpOAuthService | undefined;
|
| 42 |
+
list(): readonly McpServerEntry[];
|
| 43 |
+
get(name: string): McpServerEntry | undefined;
|
| 44 |
+
configOf(name: string): McpServerConfig | undefined;
|
| 45 |
+
resolved(
|
| 46 |
+
name: string,
|
| 47 |
+
):
|
| 48 |
+
| {
|
| 49 |
+
client: MCPClient;
|
| 50 |
+
tools: readonly Tool[];
|
| 51 |
+
rawTools: readonly MCPToolDefinition[];
|
| 52 |
+
enabledNames: ReadonlySet<string>;
|
| 53 |
+
deferred: boolean;
|
| 54 |
+
}
|
| 55 |
+
| undefined;
|
| 56 |
+
getRemoteServerUrl(name: string): string | undefined;
|
| 57 |
+
reconnect(name: string): Promise<void>;
|
| 58 |
+
reconnectAndJoin(name: string): Promise<void>;
|
| 59 |
+
waitForInitialLoad(signal?: AbortSignal): Promise<void>;
|
| 60 |
+
initialLoadDurationMs(): number;
|
| 61 |
+
onStatusChange(listener: McpStatusListener): () => void;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
const DEFAULT_STARTUP_TIMEOUT_MS = 30_000;
|
| 65 |
+
|
| 66 |
+
type RuntimeMcpClient = StdioMcpClient | HttpMcpClient | SseMcpClient;
|
| 67 |
+
const defaultLog: Logger = {
|
| 68 |
+
error: () => {},
|
| 69 |
+
warn: () => {},
|
| 70 |
+
info: () => {},
|
| 71 |
+
debug: () => {},
|
| 72 |
+
child: () => defaultLog,
|
| 73 |
+
};
|
| 74 |
+
|
| 75 |
+
export interface McpDefaultTimeouts {
|
| 76 |
+
readonly startupTimeoutMs?: number;
|
| 77 |
+
readonly toolTimeoutMs?: number;
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
export interface McpConnectionManagerOptions {
|
| 81 |
+
readonly envLookup?: (name: string) => string | undefined;
|
| 82 |
+
readonly stdioCwd?: string;
|
| 83 |
+
readonly runtimeResolver?: import('#/workspace/workspaceInstance/workspaceInstanceManager').IRuntimeResolver;
|
| 84 |
+
readonly workspaceId?: string;
|
| 85 |
+
readonly runtimeId?: string;
|
| 86 |
+
readonly requireStdioRuntimeId?: boolean;
|
| 87 |
+
readonly oauthService?: McpOAuthService;
|
| 88 |
+
readonly log?: Logger;
|
| 89 |
+
readonly resolveDefaultTimeouts?: () => McpDefaultTimeouts;
|
| 90 |
+
readonly resolveClientName?: () => string | undefined;
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
export class McpConnectionManager implements McpConnectionView {
|
| 94 |
+
private readonly entries = new Map<string, InternalEntry>();
|
| 95 |
+
private readonly listeners = new Set<McpStatusListener>();
|
| 96 |
+
private readonly inFlightReconnects = new Map<string, Promise<void>>();
|
| 97 |
+
private initialLoad: Promise<void> = Promise.resolve();
|
| 98 |
+
private initialLoadAttemptId = 0;
|
| 99 |
+
private initialLoadStartedAt: number | undefined;
|
| 100 |
+
private initialLoadFinishedAt: number | undefined;
|
| 101 |
+
|
| 102 |
+
readonly oauthService: McpOAuthService | undefined;
|
| 103 |
+
private readonly log: Logger;
|
| 104 |
+
|
| 105 |
+
constructor(private readonly options: McpConnectionManagerOptions = {}) {
|
| 106 |
+
this.oauthService = options.oauthService;
|
| 107 |
+
this.log = options.log ?? defaultLog;
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
getRemoteServerUrl(name: string): string | undefined {
|
| 111 |
+
const entry = this.entries.get(name);
|
| 112 |
+
if (entry === undefined) return undefined;
|
| 113 |
+
if (!isRemoteMcpConfig(entry.config)) return undefined;
|
| 114 |
+
return entry.config.url;
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
getHttpServerUrl(name: string): string | undefined {
|
| 118 |
+
return this.getRemoteServerUrl(name);
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
onStatusChange(listener: McpStatusListener): () => void {
|
| 122 |
+
this.listeners.add(listener);
|
| 123 |
+
return () => {
|
| 124 |
+
this.listeners.delete(listener);
|
| 125 |
+
};
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
list(): readonly McpServerEntry[] {
|
| 129 |
+
return Array.from(this.entries.values(), toPublicEntry);
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
get(name: string): McpServerEntry | undefined {
|
| 133 |
+
const entry = this.entries.get(name);
|
| 134 |
+
return entry !== undefined ? toPublicEntry(entry) : undefined;
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
configOf(name: string): McpServerConfig | undefined {
|
| 138 |
+
return this.entries.get(name)?.config;
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
resolved(
|
| 142 |
+
name: string,
|
| 143 |
+
):
|
| 144 |
+
| {
|
| 145 |
+
client: MCPClient;
|
| 146 |
+
tools: readonly Tool[];
|
| 147 |
+
rawTools: readonly MCPToolDefinition[];
|
| 148 |
+
enabledNames: ReadonlySet<string>;
|
| 149 |
+
deferred: boolean;
|
| 150 |
+
}
|
| 151 |
+
| undefined {
|
| 152 |
+
const entry = this.entries.get(name);
|
| 153 |
+
if (
|
| 154 |
+
entry?.status !== 'connected' ||
|
| 155 |
+
entry.tools === undefined ||
|
| 156 |
+
entry.rawTools === undefined ||
|
| 157 |
+
entry.client === undefined
|
| 158 |
+
) {
|
| 159 |
+
return undefined;
|
| 160 |
+
}
|
| 161 |
+
return {
|
| 162 |
+
client: entry.client,
|
| 163 |
+
tools: entry.tools,
|
| 164 |
+
rawTools: entry.rawTools,
|
| 165 |
+
enabledNames: entry.enabledNames ?? new Set(entry.tools.map((t) => t.name)),
|
| 166 |
+
deferred: entry.config.deferred === true,
|
| 167 |
+
};
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
connectAll(configs: Record<string, McpServerConfig>): Promise<void> {
|
| 171 |
+
const attemptId = ++this.initialLoadAttemptId;
|
| 172 |
+
this.initialLoadStartedAt = Date.now();
|
| 173 |
+
this.initialLoadFinishedAt = undefined;
|
| 174 |
+
const initialLoad = this.connectAllNow(configs).finally(() => {
|
| 175 |
+
if (this.initialLoadAttemptId === attemptId) {
|
| 176 |
+
this.initialLoadFinishedAt = Date.now();
|
| 177 |
+
}
|
| 178 |
+
});
|
| 179 |
+
this.initialLoad = initialLoad;
|
| 180 |
+
return initialLoad;
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
async connect(name: string, config: McpServerConfig): Promise<void> {
|
| 184 |
+
const previous = this.entries.get(name);
|
| 185 |
+
if (previous !== undefined) {
|
| 186 |
+
if (
|
| 187 |
+
(previous.status === 'pending' || previous.status === 'connected') &&
|
| 188 |
+
mcpServerConfigsEqual(previous.config, config)
|
| 189 |
+
) {
|
| 190 |
+
return;
|
| 191 |
+
}
|
| 192 |
+
await this.closeClient(previous);
|
| 193 |
+
}
|
| 194 |
+
const disabled = config.enabled === false;
|
| 195 |
+
const entry: InternalEntry = {
|
| 196 |
+
name,
|
| 197 |
+
config,
|
| 198 |
+
attemptId: 0,
|
| 199 |
+
status: disabled ? 'disabled' : 'pending',
|
| 200 |
+
};
|
| 201 |
+
this.entries.set(name, entry);
|
| 202 |
+
this.emit(entry);
|
| 203 |
+
if (!disabled) {
|
| 204 |
+
await this.connectOne(entry, this.beginConnectAttempt(entry));
|
| 205 |
+
}
|
| 206 |
+
}
|
| 207 |
+
|
| 208 |
+
async remove(name: string): Promise<boolean> {
|
| 209 |
+
const entry = this.entries.get(name);
|
| 210 |
+
if (entry === undefined) return false;
|
| 211 |
+
await this.closeClient(entry);
|
| 212 |
+
entry.status = 'disabled';
|
| 213 |
+
entry.tools = undefined;
|
| 214 |
+
entry.enabledNames = undefined;
|
| 215 |
+
entry.rawTools = undefined;
|
| 216 |
+
entry.error = undefined;
|
| 217 |
+
this.emit(entry);
|
| 218 |
+
this.entries.delete(name);
|
| 219 |
+
return true;
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
async markRemoved(name: string): Promise<boolean> {
|
| 223 |
+
const entry = this.entries.get(name);
|
| 224 |
+
if (entry === undefined) return false;
|
| 225 |
+
await this.closeClient(entry);
|
| 226 |
+
entry.status = 'removed';
|
| 227 |
+
entry.tools = undefined;
|
| 228 |
+
entry.enabledNames = undefined;
|
| 229 |
+
entry.rawTools = undefined;
|
| 230 |
+
entry.error = undefined;
|
| 231 |
+
this.emit(entry);
|
| 232 |
+
return true;
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
+
waitForInitialLoad(signal?: AbortSignal): Promise<void> {
|
| 236 |
+
signal?.throwIfAborted();
|
| 237 |
+
if (signal === undefined) return this.initialLoad;
|
| 238 |
+
return abortable(this.initialLoad, signal);
|
| 239 |
+
}
|
| 240 |
+
|
| 241 |
+
initialLoadDurationMs(): number {
|
| 242 |
+
if (this.initialLoadStartedAt === undefined) return 0;
|
| 243 |
+
const endedAt = this.initialLoadFinishedAt ?? Date.now();
|
| 244 |
+
return Math.max(0, endedAt - this.initialLoadStartedAt);
|
| 245 |
+
}
|
| 246 |
+
|
| 247 |
+
private async connectAllNow(configs: Record<string, McpServerConfig>): Promise<void> {
|
| 248 |
+
const tasks: Promise<unknown>[] = [];
|
| 249 |
+
for (const [name, config] of Object.entries(configs)) {
|
| 250 |
+
const disabled = config.enabled === false;
|
| 251 |
+
const entry: InternalEntry = {
|
| 252 |
+
name,
|
| 253 |
+
config,
|
| 254 |
+
attemptId: 0,
|
| 255 |
+
status: disabled ? 'disabled' : 'pending',
|
| 256 |
+
};
|
| 257 |
+
this.entries.set(name, entry);
|
| 258 |
+
this.emit(entry);
|
| 259 |
+
if (!disabled) {
|
| 260 |
+
tasks.push(this.connectOne(entry, this.beginConnectAttempt(entry)));
|
| 261 |
+
}
|
| 262 |
+
}
|
| 263 |
+
await Promise.allSettled(tasks);
|
| 264 |
+
}
|
| 265 |
+
|
| 266 |
+
async reconnect(name: string): Promise<void> {
|
| 267 |
+
const entry = this.entries.get(name);
|
| 268 |
+
if (entry === undefined || entry.status === 'removed') {
|
| 269 |
+
throw new Error2(ErrorCodes.MCP_SERVER_NOT_FOUND, `Unknown MCP server: ${name}`);
|
| 270 |
+
}
|
| 271 |
+
if (entry.config.enabled === false) {
|
| 272 |
+
throw new Error2(ErrorCodes.MCP_SERVER_DISABLED, `MCP server is disabled: ${name}`);
|
| 273 |
+
}
|
| 274 |
+
const attemptId = this.beginConnectAttempt(entry);
|
| 275 |
+
await this.closeClient(entry);
|
| 276 |
+
if (!this.isCurrent(entry, attemptId)) return;
|
| 277 |
+
entry.status = 'pending';
|
| 278 |
+
entry.tools = undefined;
|
| 279 |
+
entry.enabledNames = undefined;
|
| 280 |
+
entry.rawTools = undefined;
|
| 281 |
+
entry.error = undefined;
|
| 282 |
+
this.emit(entry);
|
| 283 |
+
await this.connectOne(entry, attemptId);
|
| 284 |
+
}
|
| 285 |
+
|
| 286 |
+
reconnectAndJoin(name: string): Promise<void> {
|
| 287 |
+
const existing = this.inFlightReconnects.get(name);
|
| 288 |
+
if (existing !== undefined) return existing;
|
| 289 |
+
const work = this.reconnect(name).finally(() => {
|
| 290 |
+
if (this.inFlightReconnects.get(name) === work) {
|
| 291 |
+
this.inFlightReconnects.delete(name);
|
| 292 |
+
}
|
| 293 |
+
});
|
| 294 |
+
this.inFlightReconnects.set(name, work);
|
| 295 |
+
return work;
|
| 296 |
+
}
|
| 297 |
+
|
| 298 |
+
async reconnectAfterCurrent(name: string): Promise<void> {
|
| 299 |
+
const existing = this.inFlightReconnects.get(name);
|
| 300 |
+
if (existing !== undefined) await existing.catch(() => undefined);
|
| 301 |
+
await this.reconnectAndJoin(name);
|
| 302 |
+
}
|
| 303 |
+
|
| 304 |
+
async shutdown(): Promise<void> {
|
| 305 |
+
const entries = Array.from(this.entries.values());
|
| 306 |
+
this.entries.clear();
|
| 307 |
+
const tasks = entries.map((entry) => this.closeClient(entry));
|
| 308 |
+
await Promise.allSettled(tasks);
|
| 309 |
+
}
|
| 310 |
+
|
| 311 |
+
private async connectOne(entry: InternalEntry, attemptId: number): Promise<void> {
|
| 312 |
+
const timeoutMs =
|
| 313 |
+
entry.config.startupTimeoutMs ??
|
| 314 |
+
this.options.resolveDefaultTimeouts?.().startupTimeoutMs ??
|
| 315 |
+
DEFAULT_STARTUP_TIMEOUT_MS;
|
| 316 |
+
|
| 317 |
+
let client: RuntimeMcpClient | undefined;
|
| 318 |
+
try {
|
| 319 |
+
const startupClient = await this.createClient(entry.config, entry.name, timeoutMs);
|
| 320 |
+
client = startupClient;
|
| 321 |
+
entry.client = startupClient;
|
| 322 |
+
const discovered = await withTimeout(
|
| 323 |
+
this.connectAndDiscoverTools(startupClient),
|
| 324 |
+
timeoutMs,
|
| 325 |
+
() => {
|
| 326 |
+
void this.closeRuntimeClient(startupClient);
|
| 327 |
+
},
|
| 328 |
+
);
|
| 329 |
+
if (!this.isCurrent(entry, attemptId)) {
|
| 330 |
+
await this.closeRuntimeClient(startupClient);
|
| 331 |
+
return;
|
| 332 |
+
}
|
| 333 |
+
entry.tools = discovered.tools;
|
| 334 |
+
entry.rawTools = discovered.rawTools;
|
| 335 |
+
entry.enabledNames = computeEnabledNames(entry.config, discovered.tools);
|
| 336 |
+
entry.status = 'connected';
|
| 337 |
+
this.watchForUnexpectedClose(entry, startupClient, attemptId);
|
| 338 |
+
} catch (error) {
|
| 339 |
+
if (!this.isCurrent(entry, attemptId)) {
|
| 340 |
+
if (client !== undefined) {
|
| 341 |
+
await this.closeRuntimeClient(client);
|
| 342 |
+
}
|
| 343 |
+
return;
|
| 344 |
+
}
|
| 345 |
+
if (this.shouldMarkNeedsAuth(entry, error)) {
|
| 346 |
+
entry.status = 'needs-auth';
|
| 347 |
+
entry.error = `${entry.name} requires OAuth — run /mcp-config login ${entry.name}`;
|
| 348 |
+
} else {
|
| 349 |
+
entry.status = 'failed';
|
| 350 |
+
entry.error = formatStartupError(error, client);
|
| 351 |
+
}
|
| 352 |
+
entry.tools = undefined;
|
| 353 |
+
entry.enabledNames = undefined;
|
| 354 |
+
entry.rawTools = undefined;
|
| 355 |
+
await this.closeClient(entry);
|
| 356 |
+
}
|
| 357 |
+
if (!this.isCurrent(entry, attemptId)) return;
|
| 358 |
+
this.emit(entry);
|
| 359 |
+
}
|
| 360 |
+
|
| 361 |
+
private watchForUnexpectedClose(
|
| 362 |
+
entry: InternalEntry,
|
| 363 |
+
client: RuntimeMcpClient,
|
| 364 |
+
attemptId: number,
|
| 365 |
+
): void {
|
| 366 |
+
client.onUnexpectedClose((reason) => {
|
| 367 |
+
if (!this.isCurrent(entry, attemptId)) return;
|
| 368 |
+
if (entry.client !== client) return;
|
| 369 |
+
entry.status = 'failed';
|
| 370 |
+
entry.error = formatUnexpectedCloseError(entry.name, reason);
|
| 371 |
+
entry.tools = undefined;
|
| 372 |
+
entry.enabledNames = undefined;
|
| 373 |
+
entry.rawTools = undefined;
|
| 374 |
+
entry.client = undefined;
|
| 375 |
+
void this.closeRuntimeClient(client);
|
| 376 |
+
this.emit(entry);
|
| 377 |
+
});
|
| 378 |
+
}
|
| 379 |
+
|
| 380 |
+
private beginConnectAttempt(entry: InternalEntry): number {
|
| 381 |
+
entry.attemptId += 1;
|
| 382 |
+
return entry.attemptId;
|
| 383 |
+
}
|
| 384 |
+
|
| 385 |
+
private async createClient(
|
| 386 |
+
config: McpServerConfig,
|
| 387 |
+
name: string,
|
| 388 |
+
startupTimeoutMs: number,
|
| 389 |
+
): Promise<RuntimeMcpClient> {
|
| 390 |
+
const toolCallTimeoutMs =
|
| 391 |
+
config.toolTimeoutMs ?? this.options.resolveDefaultTimeouts?.().toolTimeoutMs;
|
| 392 |
+
const clientName = this.options.resolveClientName?.();
|
| 393 |
+
if (config.transport === 'stdio') {
|
| 394 |
+
const runtimeResolver = this.options.runtimeResolver;
|
| 395 |
+
const workspaceId = this.options.workspaceId;
|
| 396 |
+
const runtimeId = config.runtime_id ?? this.options.runtimeId;
|
| 397 |
+
if (runtimeResolver === undefined || workspaceId === undefined || runtimeId === undefined || (this.options.requireStdioRuntimeId === true && config.runtime_id === undefined)) {
|
| 398 |
+
throw new Error('MCP stdio requires runtime_id and runtime binding');
|
| 399 |
+
}
|
| 400 |
+
return new StdioMcpClient(config, {
|
| 401 |
+
startupTimeoutMs,
|
| 402 |
+
toolCallTimeoutMs,
|
| 403 |
+
defaultCwd: this.options.stdioCwd,
|
| 404 |
+
clientName,
|
| 405 |
+
runtimeResolver,
|
| 406 |
+
workspaceId,
|
| 407 |
+
runtimeId,
|
| 408 |
+
});
|
| 409 |
+
}
|
| 410 |
+
if (config.transport === 'sse') {
|
| 411 |
+
return new SseMcpClient(config, {
|
| 412 |
+
startupTimeoutMs,
|
| 413 |
+
toolCallTimeoutMs,
|
| 414 |
+
envLookup: this.options.envLookup,
|
| 415 |
+
oauthProvider: await this.resolveOAuthProvider(config, name),
|
| 416 |
+
clientName,
|
| 417 |
+
});
|
| 418 |
+
}
|
| 419 |
+
return new HttpMcpClient(config, {
|
| 420 |
+
startupTimeoutMs,
|
| 421 |
+
toolCallTimeoutMs,
|
| 422 |
+
envLookup: this.options.envLookup,
|
| 423 |
+
oauthProvider: await this.resolveOAuthProvider(config, name),
|
| 424 |
+
clientName,
|
| 425 |
+
});
|
| 426 |
+
}
|
| 427 |
+
|
| 428 |
+
private async resolveOAuthProvider(
|
| 429 |
+
config: McpServerConfig,
|
| 430 |
+
name: string,
|
| 431 |
+
): Promise<ReturnType<McpOAuthService['getProvider']> | undefined> {
|
| 432 |
+
const oauthService = this.oauthService;
|
| 433 |
+
if (oauthService === undefined) return undefined;
|
| 434 |
+
if (!isRemoteMcpConfig(config)) return undefined;
|
| 435 |
+
if (config.bearerTokenEnvVar !== undefined) return undefined;
|
| 436 |
+
if (!(await oauthService.hasTokens(name, config.url))) return undefined;
|
| 437 |
+
return oauthService.getProvider(name, config.url);
|
| 438 |
+
}
|
| 439 |
+
|
| 440 |
+
private shouldMarkNeedsAuth(entry: InternalEntry, error: unknown): boolean {
|
| 441 |
+
if (this.oauthService === undefined) return false;
|
| 442 |
+
if (!isRemoteMcpConfig(entry.config)) return false;
|
| 443 |
+
if (entry.config.bearerTokenEnvVar !== undefined) return false;
|
| 444 |
+
if (entry.config.headers !== undefined && entry.config.auth !== 'oauth') return false;
|
| 445 |
+
return isUnauthorizedLikeError(error);
|
| 446 |
+
}
|
| 447 |
+
|
| 448 |
+
private async connectAndDiscoverTools(
|
| 449 |
+
client: RuntimeMcpClient,
|
| 450 |
+
): Promise<{ tools: Tool[]; rawTools: MCPToolDefinition[] }> {
|
| 451 |
+
await client.connect();
|
| 452 |
+
const mcpTools = await client.listTools();
|
| 453 |
+
return {
|
| 454 |
+
rawTools: mcpTools,
|
| 455 |
+
tools: mcpTools.map((mcpTool) => ({
|
| 456 |
+
name: mcpTool.name,
|
| 457 |
+
description: mcpTool.description,
|
| 458 |
+
parameters: assertMcpInputSchema(mcpTool.name, mcpTool.inputSchema),
|
| 459 |
+
})),
|
| 460 |
+
};
|
| 461 |
+
}
|
| 462 |
+
|
| 463 |
+
private async closeClient(entry: InternalEntry): Promise<void> {
|
| 464 |
+
if (entry.client === undefined) return;
|
| 465 |
+
const client = entry.client;
|
| 466 |
+
entry.client = undefined;
|
| 467 |
+
await this.closeRuntimeClient(client);
|
| 468 |
+
}
|
| 469 |
+
|
| 470 |
+
private async closeRuntimeClient(client: RuntimeMcpClient): Promise<void> {
|
| 471 |
+
try {
|
| 472 |
+
await client.close();
|
| 473 |
+
} catch {
|
| 474 |
+
}
|
| 475 |
+
}
|
| 476 |
+
|
| 477 |
+
private isCurrent(entry: InternalEntry, attemptId: number): boolean {
|
| 478 |
+
return this.entries.get(entry.name) === entry && entry.attemptId === attemptId;
|
| 479 |
+
}
|
| 480 |
+
|
| 481 |
+
private emit(entry: InternalEntry): void {
|
| 482 |
+
const view = toPublicEntry(entry);
|
| 483 |
+
if (view.status === 'failed' || view.status === 'needs-auth') {
|
| 484 |
+
this.log.error('mcp server unavailable', {
|
| 485 |
+
server: view.name,
|
| 486 |
+
transport: view.transport,
|
| 487 |
+
status: view.status,
|
| 488 |
+
reason: view.error,
|
| 489 |
+
});
|
| 490 |
+
}
|
| 491 |
+
for (const listener of this.listeners) {
|
| 492 |
+
try {
|
| 493 |
+
listener(view);
|
| 494 |
+
} catch {
|
| 495 |
+
}
|
| 496 |
+
}
|
| 497 |
+
}
|
| 498 |
+
}
|
| 499 |
+
|
| 500 |
+
function toPublicEntry(entry: InternalEntry): McpServerEntry {
|
| 501 |
+
return {
|
| 502 |
+
name: entry.name,
|
| 503 |
+
transport: entry.config.transport,
|
| 504 |
+
status: entry.status,
|
| 505 |
+
toolCount:
|
| 506 |
+
entry.status === 'connected' && entry.enabledNames !== undefined
|
| 507 |
+
? entry.enabledNames.size
|
| 508 |
+
: 0,
|
| 509 |
+
error: entry.error,
|
| 510 |
+
};
|
| 511 |
+
}
|
| 512 |
+
|
| 513 |
+
function computeEnabledNames(config: McpServerConfig, tools: readonly Tool[]): Set<string> {
|
| 514 |
+
const all = tools.map((t) => t.name);
|
| 515 |
+
const enabledFilter =
|
| 516 |
+
config.enabledTools !== undefined ? new Set(config.enabledTools) : undefined;
|
| 517 |
+
const disabledFilter =
|
| 518 |
+
config.disabledTools !== undefined ? new Set(config.disabledTools) : undefined;
|
| 519 |
+
const allowed = new Set<string>();
|
| 520 |
+
for (const name of all) {
|
| 521 |
+
if (enabledFilter !== undefined && !enabledFilter.has(name)) continue;
|
| 522 |
+
if (disabledFilter !== undefined && disabledFilter.has(name)) continue;
|
| 523 |
+
allowed.add(name);
|
| 524 |
+
}
|
| 525 |
+
return allowed;
|
| 526 |
+
}
|
| 527 |
+
|
| 528 |
+
function isUnauthorizedLikeError(error: unknown): boolean {
|
| 529 |
+
if (!(error instanceof Error)) return false;
|
| 530 |
+
if (error.name === 'UnauthorizedError') return true;
|
| 531 |
+
const code = (error as { code?: unknown }).code;
|
| 532 |
+
if (typeof code === 'number' && code === 401) return true;
|
| 533 |
+
if (typeof code === 'string' && code === '401') return true;
|
| 534 |
+
return /\b401\b/.test(error.message) || /unauthorized/i.test(error.message);
|
| 535 |
+
}
|
| 536 |
+
|
| 537 |
+
function formatStartupError(error: unknown, client: RuntimeMcpClient | undefined): string {
|
| 538 |
+
const source = error instanceof HostProcessError &&
|
| 539 |
+
error.code === HostProcessErrorCode.SpawnFailed &&
|
| 540 |
+
error.cause instanceof Error
|
| 541 |
+
? error.cause
|
| 542 |
+
: error;
|
| 543 |
+
const base = source instanceof Error ? source.message : String(source);
|
| 544 |
+
const tail = stderrTail(client);
|
| 545 |
+
if (tail === undefined) return base;
|
| 546 |
+
return `${base}\nstderr: ${tail}`;
|
| 547 |
+
}
|
| 548 |
+
|
| 549 |
+
function formatUnexpectedCloseError(name: string, reason: UnexpectedCloseReason): string {
|
| 550 |
+
const parts = [`MCP server "${name}" closed unexpectedly`];
|
| 551 |
+
if (reason.error !== undefined) {
|
| 552 |
+
parts.push(reason.error.message);
|
| 553 |
+
}
|
| 554 |
+
if (reason.stderr !== undefined && reason.stderr.length > 0) {
|
| 555 |
+
parts.push(`stderr: ${reason.stderr.trimEnd()}`);
|
| 556 |
+
}
|
| 557 |
+
return parts.join('\n');
|
| 558 |
+
}
|
| 559 |
+
|
| 560 |
+
function stderrTail(client: RuntimeMcpClient | undefined): string | undefined {
|
| 561 |
+
if (client === undefined) return undefined;
|
| 562 |
+
if (!(client instanceof StdioMcpClient)) return undefined;
|
| 563 |
+
const snapshot = client.stderrSnapshot();
|
| 564 |
+
if (snapshot.length === 0) return undefined;
|
| 565 |
+
return snapshot.trimEnd();
|
| 566 |
+
}
|
| 567 |
+
|
| 568 |
+
export function mcpServerConfigsEqual(a: McpServerConfig, b: McpServerConfig): boolean {
|
| 569 |
+
return stableConfigJson(a) === stableConfigJson(b);
|
| 570 |
+
}
|
| 571 |
+
|
| 572 |
+
function stableConfigJson(value: unknown): string {
|
| 573 |
+
if (Array.isArray(value)) {
|
| 574 |
+
return `[${value.map(stableConfigJson).join(',')}]`;
|
| 575 |
+
}
|
| 576 |
+
if (typeof value === 'object' && value !== null) {
|
| 577 |
+
const entries = Object.entries(value)
|
| 578 |
+
.filter(([, entryValue]) => entryValue !== undefined)
|
| 579 |
+
.map(([key, entryValue]) => `${JSON.stringify(key)}:${stableConfigJson(entryValue)}`)
|
| 580 |
+
.toSorted();
|
| 581 |
+
return `{${entries.join(',')}}`;
|
| 582 |
+
}
|
| 583 |
+
return JSON.stringify(value) ?? 'undefined';
|
| 584 |
+
}
|
| 585 |
+
|
| 586 |
+
async function withTimeout<T>(
|
| 587 |
+
promise: Promise<T>,
|
| 588 |
+
timeoutMs: number,
|
| 589 |
+
onTimeout?: () => void,
|
| 590 |
+
): Promise<T> {
|
| 591 |
+
let timer: NodeJS.Timeout | undefined;
|
| 592 |
+
try {
|
| 593 |
+
return await new Promise<T>((resolve, reject) => {
|
| 594 |
+
timer = setTimeout(() => {
|
| 595 |
+
onTimeout?.();
|
| 596 |
+
reject(new Error2(ErrorCodes.MCP_STARTUP_FAILED, `Timed out after ${timeoutMs}ms`));
|
| 597 |
+
}, timeoutMs);
|
| 598 |
+
promise.then(resolve, reject);
|
| 599 |
+
});
|
| 600 |
+
} finally {
|
| 601 |
+
if (timer !== undefined) clearTimeout(timer);
|
| 602 |
+
}
|
| 603 |
+
}
|
packages/agent-core-v2/src/mcpCore/errors.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { registerErrorDomain, type ErrorDomain } from '#/_base/errors/codes';
|
| 2 |
+
|
| 3 |
+
export const McpErrors = {
|
| 4 |
+
codes: {
|
| 5 |
+
MCP_SERVER_NOT_FOUND: 'mcp.server_not_found',
|
| 6 |
+
MCP_SERVER_DISABLED: 'mcp.server_disabled',
|
| 7 |
+
MCP_STARTUP_FAILED: 'mcp.startup_failed',
|
| 8 |
+
MCP_TOOL_NAME_COLLISION: 'mcp.tool_name_collision',
|
| 9 |
+
MCP_OAUTH_FAILED: 'mcp.oauth_failed',
|
| 10 |
+
},
|
| 11 |
+
} as const satisfies ErrorDomain;
|
| 12 |
+
|
| 13 |
+
registerErrorDomain(McpErrors);
|
packages/agent-core-v2/src/mcpCore/tool-naming.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const MCP_NAME_PREFIX = 'mcp__';
|
| 2 |
+
const MCP_NAME_SEPARATOR = '__';
|
| 3 |
+
|
| 4 |
+
const MAX_QUALIFIED_LENGTH = 64;
|
| 5 |
+
|
| 6 |
+
export function sanitizeMcpNamePart(part: string): string {
|
| 7 |
+
return part.replaceAll(/[^a-zA-Z0-9_-]/g, '_').replaceAll(/_+/g, '_');
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
export function qualifyMcpToolName(serverName: string, toolName: string): string {
|
| 11 |
+
const full = `${MCP_NAME_PREFIX}${sanitizeMcpNamePart(serverName)}${MCP_NAME_SEPARATOR}${sanitizeMcpNamePart(toolName)}`;
|
| 12 |
+
if (full.length <= MAX_QUALIFIED_LENGTH) return full;
|
| 13 |
+
|
| 14 |
+
const hash = stableHash8(full);
|
| 15 |
+
const head = full.slice(0, MAX_QUALIFIED_LENGTH - hash.length - 1);
|
| 16 |
+
return `${head}_${hash}`;
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
function stableHash8(input: string): string {
|
| 20 |
+
let hash = 0x811c9dc5;
|
| 21 |
+
for (let i = 0; i < input.length; i++) {
|
| 22 |
+
hash ^= input.codePointAt(i)!;
|
| 23 |
+
hash = Math.trunc(Math.imul(hash, 0x01000193));
|
| 24 |
+
}
|
| 25 |
+
return hash.toString(16).padStart(8, '0');
|
| 26 |
+
}
|
packages/agent-core-v2/src/mcpCore/types.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { ErrorCodes, Error2 } from '#/errors';
|
| 2 |
+
|
| 3 |
+
export interface MCPEmbeddedResourceContents {
|
| 4 |
+
uri: string;
|
| 5 |
+
mimeType?: string;
|
| 6 |
+
text?: string;
|
| 7 |
+
blob?: string;
|
| 8 |
+
[key: string]: unknown;
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
export interface MCPContentBlock {
|
| 12 |
+
type: string;
|
| 13 |
+
text?: string;
|
| 14 |
+
data?: string;
|
| 15 |
+
mimeType?: string;
|
| 16 |
+
uri?: string;
|
| 17 |
+
resource?: MCPEmbeddedResourceContents;
|
| 18 |
+
[key: string]: unknown;
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
export interface MCPToolResult {
|
| 22 |
+
content: MCPContentBlock[];
|
| 23 |
+
isError: boolean;
|
| 24 |
+
structuredContent?: unknown;
|
| 25 |
+
_meta?: Record<string, unknown>;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
export interface MCPToolDefinition {
|
| 29 |
+
name: string;
|
| 30 |
+
description: string;
|
| 31 |
+
inputSchema: unknown;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
export interface MCPClient {
|
| 35 |
+
listTools(): Promise<MCPToolDefinition[]>;
|
| 36 |
+
callTool(
|
| 37 |
+
name: string,
|
| 38 |
+
args: Record<string, unknown>,
|
| 39 |
+
signal?: AbortSignal,
|
| 40 |
+
): Promise<MCPToolResult>;
|
| 41 |
+
ping(signal?: AbortSignal): Promise<void>;
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
export function assertMcpInputSchema(
|
| 45 |
+
toolName: string,
|
| 46 |
+
inputSchema: unknown,
|
| 47 |
+
): Record<string, unknown> {
|
| 48 |
+
if (typeof inputSchema === 'object' && inputSchema !== null && !Array.isArray(inputSchema)) {
|
| 49 |
+
return inputSchema as Record<string, unknown>;
|
| 50 |
+
}
|
| 51 |
+
throw new Error2(
|
| 52 |
+
ErrorCodes.MCP_STARTUP_FAILED,
|
| 53 |
+
`Invalid inputSchema for MCP tool "${toolName}": schema must be a JSON object`,
|
| 54 |
+
);
|
| 55 |
+
}
|