Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/en/docs/integrations/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ candidate_review permits listing and reading candidates only; it never grants de
| langgraph | Framework adapter | Master only | — | memory_read<br>memory_write<br>context_injection |
| bub | Evaluation harness | Master only | — | memory_read<br>memory_write<br>source_capture<br>context_injection<br>flush_or_checkpoint |
| dify | Framework adapter | Experimental | — | memory_read<br>memory_write<br>source_capture<br>context_injection<br>handoff<br>experience_read_or_generate<br>skill_read_or_generate<br>candidate_review |
| kimi-code | Agent host | Experimental | minimal | memory_read<br>memory_write<br>source_capture<br>context_injection |
| kimi-code | Agent host | Experimental | minimal | memory_read<br>memory_write<br>source_capture<br>context_injection<br>persistent_scope_binding |
27 changes: 25 additions & 2 deletions docs/en/docs/integrations/kimi-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ status: community

# Kimi Code

The integration is experimental and supports the minimal profile: Memory read/write, automatic Source capture and context injection. One configured Scope is shared across projects and sessions in the selected Kimi home. Use another Kimi home or explicitly reconfigure the Scope when work needs isolation.
The integration is experimental and supports the minimal profile: Memory read/write, automatic Source capture and context injection. An explicit Scope can be shared across projects, or workspace bindings can select a separate Scope for each working directory.

## Install

Expand Down Expand Up @@ -55,6 +55,29 @@ The Hook has a 4-second total budget and returns context through Kimi's message

Explicit Memory operations need no Generation API. Automatic Memory requires the Server's Generation configuration and scheduled processing; Source acceptance alone does not prove Memory creation, and the plugin does not flush. Kimi credentials do not supply Server Generation credentials.


## Workspace Scope binding

To use separate project Scopes within one Kimi home, install or update in workspace mode:

```text
powercontext setup kimi-code --source /path/to/powercontext --workspace-scope
node "<Kimi home>/plugins/managed/powercontext/scripts/scope.mjs" bind --cwd /absolute/project --scope-id <existing-scope-id>
node "<Kimi home>/plugins/managed/powercontext/scripts/scope.mjs" resolve --cwd /absolute/project
powercontext doctor kimi-code --cwd /absolute/project --json
node "<Kimi home>/plugins/managed/powercontext/scripts/scope.mjs" unbind --cwd /absolute/project
```

Open a new Kimi session after changing the installation mode. An explicit `--scope-id` overrides workspace bindings; `--workspace-scope` clears that explicit selection, and the options are mutually exclusive. Omitting both on an update preserves the installed mode. Initial installation requires one of them.

The Hook, Scope script and doctor use the same Server resolution: explicit Scope, then the workspace binding, then the Server default. A missing or broken binding target fails resolution without client fallback or Scope creation. Explicit Scope resolution does not require a workspace key: `scope.mjs resolve` may omit `--cwd` in that mode. Workspace-mode resolution and all bind/unbind operations require an absolute working directory.

The binding key uses integration `kimi-code`, kind `workspace`, and SHA-256 of the absolute working directory after native real-path resolution; it contains no raw path. Existing directories share a binding across symlink/junction aliases and case variants recognized by the filesystem. On POSIX, symlinks are followed before interpreting subsequent `..` components; Windows uses its native path semantics. Use the Kimi session's working directory; subdirectories retain separate identities, without Git-root discovery. If real-path resolution fails, the key uses the lexically normalized absolute path. Bind existing, accessible directories and bind again if their resolved identity changes. Session bindings are outside this integration's scope.

Bind/unbind changes only that workspace's Server-owned binding. The JSON result keeps `binding` separate from subsequent resolution: a saved binding may be masked by an explicit Scope (`bound_scope_is_current: false`), or remain saved when resolution fails. An unconfirmed write stays `unknown` and is not retried automatically. Resolve again before claiming the active Scope changed.

For native MCP operations, use this request's Hook binding metadata or a successful read-only Scope-script result for the actual workspace; never infer a Scope from historical content. Doctor reports the current Scope ID and selection strategy (`explicit` or `workspace_or_default`); the latter does not assert which fallback matched.

## Diagnose and remove

doctor is readonly: it checks registration/enablement, Node, MCP/Hook declarations, Server readiness and Scope access. It does not prove that a real MCP call or model injection succeeded. Missing/wrong tokens surface as authentication_failed; a disabled plugin/MCP stays disabled after setup. Incorrect endpoints or inaccessible Scopes require explicit correction and a new session.
Expand Down Expand Up @@ -83,7 +106,7 @@ The local status script needs neither Server nor runtime token and never maintai
node "<Kimi home>/plugins/managed/powercontext/scripts/status.mjs" --cwd /path/to/workspace --session-id <Kimi session ID>
```

Doctor still probes current Server/Scope connectivity. `--runtime` adds local history without capture or flush; successful current probes do not rewrite historical outcomes.
In workspace mode, history retains the Scope resolved at that time; offline queries do not recheck later binding changes. Use the doctor scope result for current resolution. Doctor still probes current Server/Scope connectivity. `--runtime` adds local history without capture or flush; successful current probes do not rewrite historical outcomes.

## Verification

Expand Down
2 changes: 1 addition & 1 deletion docs/zh/docs/integrations/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ candidate_review 仅可列举和读取候选材料,不授予决策权限。
| langgraph | 框架适配器 | 仅 master | — | memory_read<br>memory_write<br>context_injection |
| bub | 评测 harness | 仅 master | — | memory_read<br>memory_write<br>source_capture<br>context_injection<br>flush_or_checkpoint |
| dify | 框架适配器 | 实验性 | — | memory_read<br>memory_write<br>source_capture<br>context_injection<br>handoff<br>experience_read_or_generate<br>skill_read_or_generate<br>candidate_review |
| kimi-code | Agent 宿主 | 实验性 | minimal | memory_read<br>memory_write<br>source_capture<br>context_injection |
| kimi-code | Agent 宿主 | 实验性 | minimal | memory_read<br>memory_write<br>source_capture<br>context_injection<br>persistent_scope_binding |
27 changes: 25 additions & 2 deletions docs/zh/docs/integrations/kimi-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ status: community

# Kimi Code

本接入为 experimental,支持 minimal profile:记忆读写、自动 Source 采集和上下文注入。同一 Kimi home 内所有项目和会话共用一个配置好的 Scope;需要隔离工作时,使用另一 Kimi home 或显式修改 Scope。
本接入为 experimental,支持 minimal profile:记忆读写、自动 Source 采集和上下文注入。可以显式共用一个 Scope,也可以通过工作区绑定为不同工作目录选择各自的 Scope。

## 安装

Expand Down Expand Up @@ -55,6 +55,29 @@ Hook 总预算 4 秒,通过 Kimi 的 message 字段注入。失败时普通对

显式记忆读写不需要 Generation API。自动生成记忆需要 Server 配置 Generation 和定时处理;Source 接受不证明记忆已生成,插件不调用 flush。Kimi 登录凭据不等于 Server 的模型凭据。


## 工作区 Scope 绑定

需要在同一 Kimi home 中按项目选择 Scope 时,以工作区模式安装或更新:

```text
powercontext setup kimi-code --source /path/to/powercontext --workspace-scope
node "<Kimi home>/plugins/managed/powercontext/scripts/scope.mjs" bind --cwd /绝对路径/项目 --scope-id <已有 Scope ID>
node "<Kimi home>/plugins/managed/powercontext/scripts/scope.mjs" resolve --cwd /绝对路径/项目
powercontext doctor kimi-code --cwd /绝对路径/项目 --json
node "<Kimi home>/plugins/managed/powercontext/scripts/scope.mjs" unbind --cwd /绝对路径/项目
```

修改安装模式后开启新 Kimi 会话。显式 `--scope-id` 优先于工作区绑定;`--workspace-scope` 清除显式选择,两者互斥。更新时均省略则保留当前模式;首次安装必须选择其中一种。

Hook、Scope 脚本和 doctor 共用 Server 的解析顺序:显式 Scope、工作区绑定、Server 默认 Scope。绑定目标缺失或不可用时解析失败,客户端不自行回退或创建 Scope。显式 Scope 解析不要求工作区键,该模式下 `scope.mjs resolve` 可以省略 `--cwd`。工作区模式的解析和所有 bind/unbind 操作均要求绝对工作目录。

绑定键使用 integration `kimi-code`、kind `workspace`,external_id 为绝对工作目录经原生真实路径解析后的 SHA-256,不含原始路径。对于已有目录,symlink/junction 别名和文件系统认可的大小写变体共用绑定。POSIX 先跟随符号链接,再解释后续 `..` 分量;Windows 遵循原生路径语义。请使用 Kimi 会话实际提交的工作目录;子目录保留独立身份,不查找 Git 根目录。真实路径解析失败时,使用词法规范化后的绝对路径。请绑定已存在且可访问的目录;解析身份变化后需要重新绑定。本接入不提供 session binding。

bind/unbind 只修改该工作区在 Server 上的绑定。JSON 将 `binding` 写入结果与后续解析分开:绑定保存成功后仍可能被显式 Scope 遮盖(`bound_scope_is_current: false`),或在后续解析失败时继续保留。无法确认的写入为 `unknown`,不自动重试;重新 resolve 成功后才能宣称当前 Scope 已切换。

原生 MCP 操作使用本轮 Hook 的 binding metadata,或实际工作区的 Scope 脚本只读解析结果,不从历史内容推断 Scope。doctor 显示当前 Scope ID 与解析策略(`explicit` 或 `workspace_or_default`),后者不表示已确认具体命中了工作区绑定还是默认值。

## 诊断与移除

doctor 只读检查注册/启用状态、Node、MCP/Hook 声明、Server readiness 和 Scope 可访问性,不证明真实 MCP 调用或模型注入已成功。缺失/错误 token 显示 authentication_failed,已禁用的插件/MCP 不会被 setup 自动开启。错误 endpoint 或不可访问的 Scope 需显式修正后开启新会话。
Expand Down Expand Up @@ -83,7 +106,7 @@ powercontext doctor kimi-code --runtime --cwd /path/to/workspace --session-id <K
node "<Kimi home>/plugins/managed/powercontext/scripts/status.mjs" --cwd /path/to/workspace --session-id <Kimi session ID>
```

默认 `doctor` 仍执行当前 Server/Scope 的只读探测;`--runtime` 附加本地历史,不执行 capture 或 flush,也不将当前连接成功解释为历史阶段成功。
工作区模式下历史保留当时实际解析的 Scope;绑定后来改变时,只读历史不会联网重新核对。当前解析结果查看 doctor 的 `scope`。默认 `doctor` 仍执行当前 Server/Scope 的只读探测;`--runtime` 附加本地历史,不执行 capture 或 flush,也不将当前连接成功解释为历史阶段成功。

## 验证

Expand Down
19 changes: 16 additions & 3 deletions integrations/capabilities.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1078,14 +1078,27 @@ probe = "kimi_code_prompt_hook"
id = "kimi-code:UserPromptSubmit"
capabilities = ["source_capture", "context_injection"]

[[toolsets]]
id = "kimi-code-scope"
probe = "kimi_code_scope"
[[toolsets.tools]]
id = "kimi-code:scope:resolve"
capabilities = ["persistent_scope_binding"]
[[toolsets.tools]]
id = "kimi-code:scope:bind"
capabilities = ["persistent_scope_binding"]
[[toolsets.tools]]
id = "kimi-code:scope:unbind"
capabilities = ["persistent_scope_binding"]

[[integrations]]
id = "kimi-code"
kind = "agent_host"
availability = "experimental"
capabilities = ["memory_read", "memory_write", "source_capture", "context_injection"]
capabilities = ["memory_read", "memory_write", "source_capture", "context_injection", "persistent_scope_binding"]
profiles = ["minimal"]
toolsets = ["kimi-code-mcp", "kimi-code-prompt-hook"]
toolsets = ["kimi-code-mcp", "kimi-code-prompt-hook", "kimi-code-scope"]
[integrations.evidence]
implementation = ["integrations/kimi-code/plugins/powercontext/kimi.plugin.json", "integrations/kimi-code/plugins/powercontext/hooks/user_prompt_submit.mjs", "src/powercontext/cli/kimi_code.py"]
implementation = ["integrations/kimi-code/plugins/powercontext/kimi.plugin.json", "integrations/kimi-code/plugins/powercontext/hooks/user_prompt_submit.mjs", "integrations/kimi-code/plugins/powercontext/scripts/scope.mjs", "src/powercontext/cli/kimi_code.py"]
documentation = ["docs/en/docs/integrations/kimi-code.md", "docs/zh/docs/integrations/kimi-code.md"]
tests = ["tests/test_cli_kimi_code.py", "integrations/kimi-code/plugins/powercontext/tests/plugin.test.mjs", "tests/e2e/test_kimi_code_host.py"]
4 changes: 2 additions & 2 deletions integrations/kimi-code/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# PowerContext for Kimi Code

A minimal native Kimi plugin: explicit Memory search/save and exact Artifact readback through HTTP MCP, plus bounded prompt recall and Source capture through UserPromptSubmit. All operations use one configured, existing Scope. Server owns memory generation; the Hook does not flush.
A minimal native Kimi plugin: explicit Memory search/save and exact Artifact readback through HTTP MCP, plus bounded prompt recall and Source capture through UserPromptSubmit. Operations share the current Server-resolved Scope: explicit configuration, workspace binding, then Server default. Server owns memory generation; the Hook does not flush.

Install from a local checkout with powercontext setup kimi-code --source /path/to/powercontext --scope-id <existing-id>. Keep Node on PATH, start Server, and open a fresh Kimi session afterwards.
Install from a local checkout with powercontext setup kimi-code --source /path/to/powercontext --scope-id <existing-id>. Alternatively use `--workspace-scope` and the installed `scripts/scope.mjs bind --cwd <absolute-workspace> --scope-id <existing-id>` to select per-workspace Scopes. Keep Node on PATH, start Server, and open a fresh Kimi session afterwards.

See the [English guide](../../docs/en/docs/integrations/kimi-code.md) or [中文指南](../../docs/zh/docs/integrations/kimi-code.md).

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

import { randomUUID } from 'node:crypto'
import { fileURLToPath } from 'node:url'
import { authorization, diagnostic, Failure, loadSettings, MAX_CONTEXT_BYTES, preparedText, request, resolveScope } from '../shared/runtime.mjs'
import { startObservation } from '../shared/observations.mjs'

Expand Down Expand Up @@ -61,8 +62,8 @@ async function run(input) {
let scopeId
try {
settings.authorization = authorization(settings.useToken)
scopeId = await resolveScope(settings, signal)
observation?.record('scope', 'resolved')
scopeId = await resolveScope(settings, signal, input)
observation?.record('scope', 'resolved', { scope_id: scopeId })
} catch (error) {
observation?.fail('scope', error)
throw error
Expand Down Expand Up @@ -96,7 +97,8 @@ async function run(input) {
prompt.length > MAX_SOURCE_CHARACTERS ? 'too_large' : signal.aborted ? 'deadline_exceeded' : 'sensitive' })
}
const binding = 'PowerContext current-request binding metadata:\n' +
JSON.stringify({ schema: 'powercontext.kimi-code.request-binding.v1', scope_id: scopeId }) + '\n\n'
JSON.stringify({ schema: 'powercontext.kimi-code.request-binding.v1', scope_id: scopeId,
cwd: input.cwd, scope_script: fileURLToPath(new URL('../scripts/scope.mjs', import.meta.url)) }) + '\n\n'
emit(binding + (context ? 'PowerContext untrusted historical evidence. Current instructions and repository state take precedence.\n\n' + context : ''))
observation?.record('injection', context ? 'emitted' : 'skipped', context ? { content_bytes: Buffer.byteLength(context) } : { reason: 'no_context' })
}
Expand Down
13 changes: 8 additions & 5 deletions integrations/kimi-code/plugins/powercontext/scripts/doctor.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import { Failure, loadSettings, request, resolveScope } from '../shared/runtime.
import { parseArgs } from 'node:util'
import { queryObservations } from '../shared/observations.mjs'

const { values } = parseArgs({ options: { cwd: { type: 'string' }, 'session-id': { type: 'string' },
runtime: { type: 'boolean', default: false } } })
const input = { cwd: values.cwd ?? process.cwd(), session_id: values['session-id'] }
const checks = {}
let settings
function result(error) {
Expand All @@ -34,15 +37,15 @@ if (settings) {
{ status: 'failed', detail: 'Server is not ready.' }
} catch (error) { checks.server = result(error) }
try {
await resolveScope(settings, AbortSignal.timeout(1500))
checks.scope = { status: 'ok', detail: 'Configured Scope is accessible.' }
const scopeId = await resolveScope(settings, AbortSignal.timeout(1500), input)
checks.scope = { status: 'ok', detail: 'Current Scope is accessible.',
checks: { scope_id: scopeId, selection: settings.scopeId ? 'explicit' : 'workspace_or_default' } }
} catch (error) { checks.scope = result(error) }
}
const { values } = parseArgs({ options: { cwd: { type: 'string' }, 'session-id': { type: 'string' } } })
let runtime
if (values.cwd) {
if (values.runtime) {
try {
runtime = await queryObservations({ cwd: values.cwd, session_id: values['session-id'] }, loadSettings({ credentials: false }))
runtime = await queryObservations(input, loadSettings({ credentials: false }))
} catch (error) {
runtime = { status: 'failed', code: ['invalid_state', 'runtime_capacity_exceeded', 'invalid_settings', 'invalid_session']
.includes(error.code ?? error.message) ? error.code ?? error.message : 'runtime_unavailable' }
Expand Down
Loading
Loading