@luchenglong/cli-bridge 0.0.0-stage → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +148 -0
- package/LICENSE +21 -0
- package/MCP-POLICY.md +53 -0
- package/PROVIDER-CONTRACT.md +235 -0
- package/README.en.md +98 -0
- package/README.md +150 -2
- package/assets/indicator-states.svg +62 -0
- package/bin/agy-quota.mjs +223 -0
- package/core/bridge-kit.mjs +549 -0
- package/core/scheduling.mjs +149 -0
- package/docs/ARCHITECTURE.md +117 -0
- package/docs/FALLBACK-AND-INDICATOR.md +93 -0
- package/docs/INSTALL.md +118 -0
- package/docs/MCP-POLICY.zh.md +49 -0
- package/docs/MIGRATION.md +43 -0
- package/docs/RELEASE-SOP.md +77 -0
- package/docs/history/agy/CHANGELOG.md +326 -0
- package/docs/history/codebuddy/CHANGELOG.md +668 -0
- package/docs/history/mimo/CHANGELOG.md +346 -0
- package/dynamic/agy.host.js +1237 -0
- package/dynamic/agy.host.template.mjs +140 -0
- package/dynamic/codebuddy.host.js +1787 -0
- package/dynamic/codebuddy.host.template.mjs +162 -0
- package/dynamic/mimo.host.js +1732 -0
- package/dynamic/mimo.host.template.mjs +169 -0
- package/home-plugin/cli-bridge-indicator/cordis.patch.yml +31 -0
- package/home-plugin/cli-bridge-indicator/lib/client.js +606 -0
- package/home-plugin/cli-bridge-indicator/lib/index.mjs +420 -0
- package/home-plugin/cli-bridge-indicator/lib/preset-declarations.mjs +137 -0
- package/home-plugin/cli-bridge-indicator/lib/settings.mjs +300 -0
- package/mcp/agy-mcp-server.mjs +6 -0
- package/mcp/cli-bridge-mcp.mjs +184 -0
- package/mcp/codebuddy-mcp-server.mjs +6 -0
- package/mcp/mimo-mcp-server.mjs +6 -0
- package/mcp/providers/agy.mjs +254 -0
- package/mcp/providers/codebuddy.mjs +397 -0
- package/mcp/providers/mimo.mjs +290 -0
- package/package.json +87 -4
- package/preset/agy-first/agent.cordis.yml +141 -0
- package/preset/agy-first/cli-bridge.mjs +169 -0
- package/preset/agy-first/plugin-list.mjs +26 -0
- package/preset/agy-first/preset.yml +3 -0
- package/preset/codebuddy-first/agent.cordis.yml +141 -0
- package/preset/codebuddy-first/cli-bridge.mjs +188 -0
- package/preset/codebuddy-first/plugin-list.mjs +20 -0
- package/preset/codebuddy-first/preset.yml +3 -0
- package/preset/mimo-first/agent.cordis.yml +143 -0
- package/preset/mimo-first/cli-bridge.mjs +236 -0
- package/preset/mimo-first/plugin-list.mjs +39 -0
- package/preset/mimo-first/preset.yml +3 -0
- package/preset/standard-base.mjs +102 -0
- package/providers/agy.mjs +398 -0
- package/providers/codebuddy.mjs +926 -0
- package/providers/mimo.mjs +864 -0
- package/providers/registry.mjs +34 -0
- package/scripts/build.mjs +235 -0
- package/scripts/deploy.mjs +61 -0
- package/scripts/verify.mjs +148 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# 生成物 — 由 node scripts/build.mjs 从 preset/mimo-first/plugin-list.mjs 生成,勿手改。
|
|
2
|
+
# legacy 目录形态($DSH_HOME/.agent-presets/cordis-mimo/);新 DSH 走声明制
|
|
3
|
+
# (home-plugin/cli-bridge-indicator/lib/preset-declarations.mjs 直接 import 同一源)。
|
|
4
|
+
- id: persona
|
|
5
|
+
name: '@deepseek-ai/dsh-persona'
|
|
6
|
+
config:
|
|
7
|
+
suffix: 'Your working directory is {{cwd}}.'
|
|
8
|
+
prefix: 'You are a coding agent powered by the {{model}} model.'
|
|
9
|
+
- id: agent-instructions
|
|
10
|
+
name: '@deepseek-ai/dsh-agent-instructions'
|
|
11
|
+
config:
|
|
12
|
+
maxBytes: 65536
|
|
13
|
+
- id: tool-bash
|
|
14
|
+
name: '@deepseek-ai/dsh-tool-bash'
|
|
15
|
+
disabled: !!js 'process.platform === ''win32'''
|
|
16
|
+
- id: tool-pwsh
|
|
17
|
+
name: '@deepseek-ai/dsh-tool-pwsh'
|
|
18
|
+
disabled: !!js 'process.platform !== ''win32'''
|
|
19
|
+
- id: tool-fs
|
|
20
|
+
name: '@deepseek-ai/dsh-tool-fs'
|
|
21
|
+
- id: tool-fs-search
|
|
22
|
+
name: '@deepseek-ai/dsh-tool-fs-search'
|
|
23
|
+
config:
|
|
24
|
+
sampleOverCapGlobResults: false
|
|
25
|
+
- id: tool-jobs
|
|
26
|
+
name: '@deepseek-ai/dsh-tool-jobs'
|
|
27
|
+
- id: skill-filesystem
|
|
28
|
+
name: '@deepseek-ai/dsh-skill-filesystem'
|
|
29
|
+
- id: tool-skill
|
|
30
|
+
name: '@deepseek-ai/dsh-tool-skill'
|
|
31
|
+
- id: command-goal
|
|
32
|
+
name: '@deepseek-ai/dsh-command-goal'
|
|
33
|
+
- id: tool-goal
|
|
34
|
+
name: '@deepseek-ai/dsh-tool-goal'
|
|
35
|
+
- id: planning
|
|
36
|
+
name: 'cordis:group'
|
|
37
|
+
group: true
|
|
38
|
+
isolate:
|
|
39
|
+
planMode: true
|
|
40
|
+
config:
|
|
41
|
+
- id: plan-mode
|
|
42
|
+
name: '@deepseek-ai/dsh-plan-mode'
|
|
43
|
+
config:
|
|
44
|
+
section: |
|
|
45
|
+
You are in plan mode. Stay in plan mode until exit_plan_mode succeeds or the user switches the session mode. Imperative language to implement changes means plan the implementation, not execute it. A user's conversational agreement — including an answer confirming something you asked — approves nothing and does not end plan mode; fold the confirmed decision into the plan and submit it through exit_plan_mode.
|
|
46
|
+
|
|
47
|
+
Explore first. Use non-mutating reads, searches, static analysis, and checks to ground the plan in the actual repository. Do not edit or write files, change configuration, run formatters or code generation that rewrites tracked files, commit, or otherwise carry out the plan. Prefer existing functions and patterns over new machinery.
|
|
48
|
+
|
|
49
|
+
The tool catalog stays the same across modes for request-cache stability. These plan-mode rules override any later tool description or guidance that suggests using mutation tools; those tools remain listed to keep the tool catalog unchanged. Do not use todo_write to track this planning phase: it tracks implementation after an approved plan, while the plan itself belongs in exit_plan_mode.
|
|
50
|
+
|
|
51
|
+
Resolve discoverable facts by inspection. Use ask_user_question only for user-owned choices or material ambiguity that inspection cannot answer. Do not ask the user where code lives or how current behavior works when you can find out.
|
|
52
|
+
|
|
53
|
+
Make the plan decision-complete: state the goal and success criteria; group implementation changes by subsystem; identify public API, schema, and data-flow changes; cover edge cases, failure modes, tests, acceptance criteria, and explicit assumptions. Keep it concise enough to review but detailed enough that another engineer can implement it without making design decisions.
|
|
54
|
+
|
|
55
|
+
When ready, call exit_plan_mode with the complete plan markdown, starting with a # title. Make exit_plan_mode the only and final tool call in that assistant response: it presents the plan for approval, and implementation begins only in a later step after approval. Do not paste the final plan as a plain reply or ask "should I proceed?" through prose or ask_user_question. If review rejects it, incorporate the feedback and present again. If the review channel is unavailable or aborted, stay in plan mode and ask the user to switch modes manually; do not proceed with implementation.
|
|
56
|
+
- id: compaction
|
|
57
|
+
name: 'cordis:group'
|
|
58
|
+
group: true
|
|
59
|
+
isolate:
|
|
60
|
+
compaction: true
|
|
61
|
+
toolResultPruner: true
|
|
62
|
+
config:
|
|
63
|
+
- id: compaction-basic
|
|
64
|
+
name: '@deepseek-ai/dsh-compaction-basic'
|
|
65
|
+
- id: command-compact
|
|
66
|
+
name: '@deepseek-ai/dsh-command-compact'
|
|
67
|
+
- id: tool-result-pruner
|
|
68
|
+
name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
|
|
69
|
+
config:
|
|
70
|
+
thresholdChars: 8192
|
|
71
|
+
headChars: 4096
|
|
72
|
+
tailChars: 1024
|
|
73
|
+
- id: delegation
|
|
74
|
+
name: 'cordis:group'
|
|
75
|
+
group: true
|
|
76
|
+
isolate:
|
|
77
|
+
workflowEngine: true
|
|
78
|
+
config:
|
|
79
|
+
- id: tool-subagent-control
|
|
80
|
+
name: '@deepseek-ai/dsh-tool-subagent-control'
|
|
81
|
+
- id: tool-subagent-list-agents
|
|
82
|
+
name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
|
|
83
|
+
- id: tool-subagent
|
|
84
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
85
|
+
config:
|
|
86
|
+
provider: spawn
|
|
87
|
+
toolName: subagent
|
|
88
|
+
modelSelectionSettings: true
|
|
89
|
+
backgroundMode: continuable
|
|
90
|
+
persona: 'Delegation directive: you are a child agent in a MiMo-first session. Before doing any coding, building, debugging, or multi-file investigation work yourself, check whether the mimo_run tool (or the MCP form mcp__mimo__mimo_run) is available to you; if it is, dispatch that work to mimo first per the MiMo-first policy, then verify its output with your own read-only tools. Reserve your own direct work for quick read-only lookups and final verification.'
|
|
91
|
+
- id: tool-subagent-fork
|
|
92
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
93
|
+
config:
|
|
94
|
+
provider: fork
|
|
95
|
+
toolName: subagent_fork
|
|
96
|
+
backgroundMode: continuable
|
|
97
|
+
persona: 'Delegation directive: you are a child agent in a MiMo-first session. Before doing any coding, building, debugging, or multi-file investigation work yourself, check whether the mimo_run tool (or the MCP form mcp__mimo__mimo_run) is available to you; if it is, dispatch that work to mimo first per the MiMo-first policy, then verify its output with your own read-only tools. Reserve your own direct work for quick read-only lookups and final verification.'
|
|
98
|
+
- id: tool-subagent-codex
|
|
99
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
100
|
+
disabled: true
|
|
101
|
+
config:
|
|
102
|
+
provider: codex
|
|
103
|
+
toolName: subagent_codex
|
|
104
|
+
backgroundMode: one-shot
|
|
105
|
+
maxDepth: provider-managed
|
|
106
|
+
- id: tool-subagent-claude-code
|
|
107
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
108
|
+
disabled: true
|
|
109
|
+
config:
|
|
110
|
+
provider: claude-code
|
|
111
|
+
toolName: subagent_claude_code
|
|
112
|
+
backgroundMode: one-shot
|
|
113
|
+
maxDepth: provider-managed
|
|
114
|
+
- id: workflow-ptc
|
|
115
|
+
name: '@deepseek-ai/dsh-workflow-ptc'
|
|
116
|
+
config:
|
|
117
|
+
provider: spawn
|
|
118
|
+
- id: tool-workflow
|
|
119
|
+
name: '@deepseek-ai/dsh-tool-workflow'
|
|
120
|
+
- id: tool-ralph
|
|
121
|
+
name: '@deepseek-ai/dsh-tool-ralph'
|
|
122
|
+
disabled: true
|
|
123
|
+
config:
|
|
124
|
+
subagentProvider: spawn
|
|
125
|
+
maxRounds: 64
|
|
126
|
+
- id: tool-ask-user
|
|
127
|
+
name: '@deepseek-ai/dsh-tool-ask-user'
|
|
128
|
+
- id: tool-todo
|
|
129
|
+
name: '@deepseek-ai/dsh-tool-todo'
|
|
130
|
+
config:
|
|
131
|
+
allowParallelInProgress: true
|
|
132
|
+
- id: tool-web
|
|
133
|
+
name: '@deepseek-ai/dsh-tool-web'
|
|
134
|
+
config:
|
|
135
|
+
fetch: true
|
|
136
|
+
searchTimeoutMs: 60000
|
|
137
|
+
- id: present
|
|
138
|
+
name: '@deepseek-ai/dsh-tool-present'
|
|
139
|
+
- id: tool-plugin-manager
|
|
140
|
+
name: '@deepseek-ai/dsh-plugin-manager/tools'
|
|
141
|
+
disabled: true
|
|
142
|
+
- id: cli-bridge-mimo
|
|
143
|
+
name: '@luchenglong/cli-bridge/preset-mimo'
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
// preset/mimo-first/cli-bridge.mjs — cordis-mimo preset 的桥接插件(preset 宿主形态)。
|
|
2
|
+
//
|
|
3
|
+
// PROVIDER-CONTRACT.md §5:本模块只做「宿主接线」——构造描述符、attach、注册三工具
|
|
4
|
+
// (+mimo_settings)、心跳/事件发布、mimo:policy 提示词段。全部执行/状态语义在
|
|
5
|
+
// providers/mimo.mjs + core/bridge-kit.mjs。
|
|
6
|
+
//
|
|
7
|
+
// 移植基线:mimo-first-bridge v1.x preset/mimo-first/mimo-first-bridge.mjs 的 apply()。
|
|
8
|
+
// preset id 'cordis-mimo' 由 plugin-list.mjs 单源声明(旧会话头引用,不可变)。
|
|
9
|
+
//
|
|
10
|
+
// 本模块是宿主进程内真 ESM:可 import node: 内建与包内相对路径;禁止 import
|
|
11
|
+
// @deepseek-ai/*(解析根够不到 harness 包,历史论证 —— 契约 §8)。
|
|
12
|
+
import fs from 'node:fs'
|
|
13
|
+
import path from 'node:path'
|
|
14
|
+
|
|
15
|
+
import defineProvider from '../../providers/mimo.mjs'
|
|
16
|
+
import { renderResult, renderStatus } from '../../core/bridge-kit.mjs'
|
|
17
|
+
import { isBridgeEnabled } from '../../core/scheduling.mjs'
|
|
18
|
+
import { PROVIDER_IDS } from '../../providers/registry.mjs'
|
|
19
|
+
|
|
20
|
+
export const name = 'cli-bridge-mimo'
|
|
21
|
+
export const inject = ['tools', 'subprocess', 'systemPrompt', 'timer']
|
|
22
|
+
|
|
23
|
+
const OUTPUT_SCHEMA = { type: 'object', additionalProperties: true }
|
|
24
|
+
const SETTINGS_FILE_NAME = 'cli-bridge-settings.json'
|
|
25
|
+
const BACKEND_DESKTOP = 'mimo-desktop'
|
|
26
|
+
|
|
27
|
+
// 统一设置文件路径解析(与 home-plugin/lib/settings.mjs 同规则,本模块不 import keystone,
|
|
28
|
+
// 只复刻只读逻辑):CLI_BRIDGE_SETTINGS_FILE > DSH_HOME/<file>。解析不到 → null(回退默认)。
|
|
29
|
+
function resolveSettingsFile(env) {
|
|
30
|
+
try {
|
|
31
|
+
if (env.CLI_BRIDGE_SETTINGS_FILE) return env.CLI_BRIDGE_SETTINGS_FILE
|
|
32
|
+
if (env.DSH_HOME) return path.join(env.DSH_HOME, SETTINGS_FILE_NAME)
|
|
33
|
+
} catch (e) { }
|
|
34
|
+
return null
|
|
35
|
+
}
|
|
36
|
+
function readPartition(env) {
|
|
37
|
+
const p = resolveSettingsFile(env)
|
|
38
|
+
if (!p) return { partition: null, path: null, home: null }
|
|
39
|
+
try {
|
|
40
|
+
const all = JSON.parse(fs.readFileSync(p, 'utf8'))
|
|
41
|
+
return { partition: (all && typeof all === 'object' && all.mimo) ? all.mimo : null, path: p, home: path.dirname(p) }
|
|
42
|
+
} catch (e) {
|
|
43
|
+
return { partition: null, path: p, home: path.dirname(p) }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 旧 resolveMimoExe 候选链 verbatim(MIMO_BIN > MIMO_CLI > ~/.mimocode/bin/mimo[.exe])。
|
|
48
|
+
function mimoExeFromEnvCandidates(env) {
|
|
49
|
+
const candidates = []
|
|
50
|
+
if (env.MIMO_BIN) candidates.push(env.MIMO_BIN)
|
|
51
|
+
if (env.MIMO_CLI) candidates.push(env.MIMO_CLI)
|
|
52
|
+
if (env.USERPROFILE) candidates.push(env.USERPROFILE + '\\.mimocode\\bin\\mimo.exe')
|
|
53
|
+
if (env.HOME) {
|
|
54
|
+
candidates.push(env.HOME + '\\.mimocode\\bin\\mimo.exe')
|
|
55
|
+
candidates.push(env.HOME + '/.mimocode/bin/mimo')
|
|
56
|
+
}
|
|
57
|
+
for (const exe of candidates) {
|
|
58
|
+
try { if (exe && fs.existsSync(exe)) return exe } catch (e) { }
|
|
59
|
+
}
|
|
60
|
+
return null
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function apply(ctx, config) {
|
|
64
|
+
// 调度门禁(0.2.0):用户关掉 mimo → 本形态不注册工具/政策段/心跳;fail-open。
|
|
65
|
+
if (!isBridgeEnabled('mimo', PROVIDER_IDS)) return
|
|
66
|
+
const subprocess = ctx.subprocess
|
|
67
|
+
const planMode = ctx.get('planMode')
|
|
68
|
+
const sandboxPolicy = ctx.get('sandboxPolicy')
|
|
69
|
+
const timer = ctx.timer
|
|
70
|
+
const env = process.env
|
|
71
|
+
const cfg = config || {}
|
|
72
|
+
|
|
73
|
+
// 预选链(旧 loadRuntime 语义):config.backend > env > 统一设置文件。工具参数级在
|
|
74
|
+
// descriptor.resolveBackend 内处理(arg 优先),这里给 runner 的是「非 arg」的最高优先源。
|
|
75
|
+
function preferredBackend() {
|
|
76
|
+
const b = cfg.backend || env.MIMO_BRIDGE_BACKEND || env.MIMO_BACKEND || (readPartition(env).partition || {}).preferredBackend
|
|
77
|
+
return b || null
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const provider = defineProvider({
|
|
81
|
+
process,
|
|
82
|
+
env,
|
|
83
|
+
config: cfg,
|
|
84
|
+
nodeFs: fs,
|
|
85
|
+
nodePath: path,
|
|
86
|
+
readSettings: () => readPartition(env)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
// 状态快照 → 家级灯(keystone 听 'mimo/status',事件是 app 级广播)。
|
|
90
|
+
// 附带 defaultBackend/backend 便利字段(旧 mimo/status payload,契约容忍额外键)。
|
|
91
|
+
function publish(snapshot) {
|
|
92
|
+
try {
|
|
93
|
+
const snap = Object.assign({ defaultBackend: preferredBackend() || provider.defaultBackend, backend: preferredBackend() || provider.defaultBackend }, snapshot)
|
|
94
|
+
ctx.emit('mimo/status', { snapshot: snap })
|
|
95
|
+
} catch (e) { /* ignore */ }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 旧仓 CWD_FALLBACK 语义:MIMO_MCP_CWD > MIMO_BRIDGE_CWD > workspaceRoot > process.cwd() > '.'。
|
|
99
|
+
function getCwdFallback() {
|
|
100
|
+
if (env.MIMO_MCP_CWD) return String(env.MIMO_MCP_CWD)
|
|
101
|
+
if (env.MIMO_BRIDGE_CWD) return String(env.MIMO_BRIDGE_CWD)
|
|
102
|
+
if (sandboxPolicy && typeof sandboxPolicy.workspaceRoot === 'string' && sandboxPolicy.workspaceRoot) return sandboxPolicy.workspaceRoot
|
|
103
|
+
return process.cwd()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// exe:mimo-desktop 走本地 HTTP 非 spawn → 返回占位 'mimo'(永不 throw,让 kit 落到
|
|
107
|
+
// delegates→execute);mimocode 走 MIMO_BIN/MIMO_CLI > PATH 候选 > subprocess 查找(
|
|
108
|
+
// 找不到 throw → kit 发 MIMO_UNAVAILABLE + 回退弹窗)。
|
|
109
|
+
async function resolveExe(backend, signal) {
|
|
110
|
+
if (backend === BACKEND_DESKTOP) return 'mimo'
|
|
111
|
+
const fromCandidates = mimoExeFromEnvCandidates(env)
|
|
112
|
+
if (fromCandidates) {
|
|
113
|
+
if (/\.(mjs|cjs|js)$/i.test(fromCandidates)) return [process.execPath, fromCandidates]
|
|
114
|
+
return fromCandidates
|
|
115
|
+
}
|
|
116
|
+
return await subprocess.resolveExecutable('mimo', undefined, signal)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 旧 planActiveFor verbatim:DSH plan 态决定 mimo 的 --agent(auto→plan|build)。
|
|
120
|
+
function planActiveFor(exec) {
|
|
121
|
+
try {
|
|
122
|
+
if (planMode && exec && exec.agent) {
|
|
123
|
+
const st = planMode.get(exec.agent)
|
|
124
|
+
return !!(st && st.active)
|
|
125
|
+
}
|
|
126
|
+
} catch (e) { }
|
|
127
|
+
return false
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
provider.attach({
|
|
131
|
+
ctx,
|
|
132
|
+
subprocess,
|
|
133
|
+
resolveExe,
|
|
134
|
+
getCwdFallback,
|
|
135
|
+
planActiveFor,
|
|
136
|
+
getPreferredBackend: preferredBackend,
|
|
137
|
+
getSettings: () => (readPartition(env).partition || {}),
|
|
138
|
+
publish
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
// 宣告 mimo 优先模式:家级灯据此决定常驻显示(presetActive)。挂载即宣告 +
|
|
142
|
+
// 每 30s 心跳续租(TTL 75s,契约 §2)。旧 payload 带 backend,新契约要 sessionId;都带。
|
|
143
|
+
function announceMode() {
|
|
144
|
+
try {
|
|
145
|
+
const sid = (ctx.session && ctx.session.id) || ctx.sessionId || null
|
|
146
|
+
const bk = preferredBackend() || provider.defaultBackend
|
|
147
|
+
const payload = { active: true, backend: bk }
|
|
148
|
+
if (sid) payload.sessionId = sid
|
|
149
|
+
ctx.emit('mimo/mode', payload)
|
|
150
|
+
} catch (e) { /* ignore */ }
|
|
151
|
+
}
|
|
152
|
+
announceMode()
|
|
153
|
+
try {
|
|
154
|
+
if (timer && typeof timer.interval === 'function') {
|
|
155
|
+
ctx.effect(() => timer.interval(announceMode, 30 * 1000))
|
|
156
|
+
} else if (ctx.setInterval) {
|
|
157
|
+
const t = ctx.setInterval(announceMode, 30000)
|
|
158
|
+
if (t && ctx.effect) ctx.effect(() => () => { try { t() } catch (e) { } })
|
|
159
|
+
}
|
|
160
|
+
} catch (e) { }
|
|
161
|
+
|
|
162
|
+
// ── 工具注册(schema verbatim 旧 preset;执行进描述符逻辑)──────────────────────
|
|
163
|
+
ctx.effect(() => ctx.tools.register({
|
|
164
|
+
name: provider.tools.run.name,
|
|
165
|
+
description: provider.tools.run.description,
|
|
166
|
+
parameters: provider.tools.run.inputSchema,
|
|
167
|
+
output: { schema: OUTPUT_SCHEMA, render: (args, value) => renderResult(value, provider) },
|
|
168
|
+
execute: (args, exec) => provider.runToolLogic(args, exec)
|
|
169
|
+
}))
|
|
170
|
+
ctx.effect(() => ctx.tools.register({
|
|
171
|
+
name: provider.tools.continue.name,
|
|
172
|
+
description: provider.tools.continue.description,
|
|
173
|
+
parameters: provider.tools.continue.inputSchema,
|
|
174
|
+
output: { schema: OUTPUT_SCHEMA, render: (args, value) => renderResult(value, provider) },
|
|
175
|
+
execute: (args, exec) => provider.continueToolLogic(args, exec)
|
|
176
|
+
}))
|
|
177
|
+
ctx.effect(() => ctx.tools.register({
|
|
178
|
+
name: provider.tools.status.name,
|
|
179
|
+
description: provider.tools.status.description,
|
|
180
|
+
parameters: provider.tools.status.inputSchema,
|
|
181
|
+
output: { schema: OUTPUT_SCHEMA, render: (args, value) => renderStatus(value, provider) },
|
|
182
|
+
execute: (args) => provider.statusToolLogic(args)
|
|
183
|
+
}))
|
|
184
|
+
// mimo_settings:preset 有 nodeFs → 可持久化(描述符工厂恒给出该 extraTool)。
|
|
185
|
+
// 注意 render 适配:harness 契约是 render(args, value),extraTools[].render 是
|
|
186
|
+
// renderSettings(value) 单参 —— 直接透传会把 args 当 value(渲染全空)。
|
|
187
|
+
for (const et of provider.extraTools) {
|
|
188
|
+
ctx.effect(() => ctx.tools.register({
|
|
189
|
+
name: et.name,
|
|
190
|
+
description: et.description,
|
|
191
|
+
parameters: et.inputSchema,
|
|
192
|
+
output: { schema: OUTPUT_SCHEMA, render: et.render ? ((a, v) => et.render(v)) : ((a, v) => renderResult(v, provider)) },
|
|
193
|
+
execute: (args, exec) => et.logic(args, exec)
|
|
194
|
+
}))
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 旧 policy 文案 verbatim(契约 §8:提示词连续性,不许顺手改)。
|
|
198
|
+
const policyText = [
|
|
199
|
+
'mimo-first execution policy (Xiaomi MiMo: MiMoCode CLI or MiMo Desktop client).',
|
|
200
|
+
'',
|
|
201
|
+
'Local backends (selectable in settings: backend = mimocode | mimo-desktop):',
|
|
202
|
+
'- mimocode (default): local `mimo` CLI (MiMoCode). Full DSH control (--dangerously-skip-permissions, never prompts).',
|
|
203
|
+
'- mimo-desktop: Xiaomi MiMo Desktop client local API (running Desktop app; uses its login/model/session UI).',
|
|
204
|
+
'',
|
|
205
|
+
'By default, dispatch runs in the background and returns a jobId immediately; collect via job_output and watch live progress via mimo_status.',
|
|
206
|
+
'',
|
|
207
|
+
'Decision table for tool selection:',
|
|
208
|
+
'- Single read/glob/grep or short (<30s) command -> Native tools.',
|
|
209
|
+
'- Implementing, editing, refactoring, debugging, builds, multi-file investigation -> mimo_run.',
|
|
210
|
+
'- Follow-ups on the same topic -> mimo_continue (with latest=true or sessionId; context is preserved).',
|
|
211
|
+
'- Final build/test verification of mimo output -> Native tools.',
|
|
212
|
+
'- Plan / read-only analysis -> mimo_run with mode=plan.',
|
|
213
|
+
'- Desktop-native work (Desktop session UI, shared Desktop login/model) -> prefer backend=mimo-desktop (or preselect it in settings).',
|
|
214
|
+
'',
|
|
215
|
+
'Backend preselection (settings):',
|
|
216
|
+
'- Default backend is configured OUT-OF-BAND (plugin config.backend / env MIMO_BRIDGE_BACKEND / settings file).',
|
|
217
|
+
'- Per-call `backend` argument overrides the default only when necessary.',
|
|
218
|
+
'- Do NOT invent a backend every call — use the preselected default.',
|
|
219
|
+
'',
|
|
220
|
+
'Context protocol (one topic = one mimo session):',
|
|
221
|
+
'- First dispatch on a topic: compact CONTEXT preamble (point to paths, do not paste file contents).',
|
|
222
|
+
'- Follow-ups: call mimo_continue and state only what is new.',
|
|
223
|
+
'',
|
|
224
|
+
'Execution & Fallback protocol:',
|
|
225
|
+
'- Dispatch defaults to background=true. Collect via job_output.',
|
|
226
|
+
'- On rate-limit/network/auth failure, a confirmation dialog offers DSH-local fallback.',
|
|
227
|
+
'- If result has fallback=true (status FALLBACK_TO_DSH), finish with native tools and NEVER call mimo again for this task.',
|
|
228
|
+
'- Never loop mimo calls; never ask mimo to call back into DSH.'
|
|
229
|
+
].join('\n')
|
|
230
|
+
|
|
231
|
+
ctx.effect(() => ctx.systemPrompt.section({
|
|
232
|
+
name: 'mimo:policy',
|
|
233
|
+
order: (typeof ctx.systemPrompt.getSectionOrder === 'function' ? ctx.systemPrompt.getSectionOrder('TOOL_SUBAGENT') : 2800) + 10,
|
|
234
|
+
text: policyText
|
|
235
|
+
}))
|
|
236
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// preset/mimo-first/plugin-list.mjs — cordis-mimo 预设的单源插件清单(PROVIDER-CONTRACT §5)。
|
|
2
|
+
//
|
|
3
|
+
// = 官方 standard 基线(standard-base.mjs)+ mimo 桥接行 + persona/委派差异(旧仓
|
|
4
|
+
// preset/mimo-first/agent.cordis.yml 的 persona 与 tool-subagent 委派文案 verbatim)。
|
|
5
|
+
// preset id 'cordis-mimo' 是旧会话头引用,不可变。
|
|
6
|
+
import { standardPluginRows, bridgeRow } from '../standard-base.mjs'
|
|
7
|
+
|
|
8
|
+
export const meta = {
|
|
9
|
+
id: 'cordis-mimo',
|
|
10
|
+
name: 'MiMo 执行代理',
|
|
11
|
+
description: '完整编码 Agent(standard 全部能力),额外提供 mimo_run/mimo_continue 工具并注入"MiMo 优先"策略:各种模式下先把实现、编辑、调试、构建、跨文件排查派给本机 mimo CLI(Xiaomi MiMo Desktop / MiMoCode,DSH 完全控制、mimo 无提示改文件、默认后台执行),原生工具仅用于只读查询与最终验证。',
|
|
12
|
+
order: 10
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// 旧 agent.cordis.yml 的 tool-subagent(spawn/fork)委派指令文案 verbatim:
|
|
16
|
+
// 子 agent 在 MiMo-first 会话里优先把编码工作派给 mimo_run。
|
|
17
|
+
const MIMO_DELEGATION_PERSONA = 'Delegation directive: you are a child agent in a MiMo-first session. Before doing any coding, building, debugging, or multi-file investigation work yourself, check whether the mimo_run tool (or the MCP form mcp__mimo__mimo_run) is available to you; if it is, dispatch that work to mimo first per the MiMo-first policy, then verify its output with your own read-only tools. Reserve your own direct work for quick read-only lookups and final verification.'
|
|
18
|
+
|
|
19
|
+
export function buildPlugins(deps) {
|
|
20
|
+
const rows = standardPluginRows()
|
|
21
|
+
// persona 行:旧仓 mimo 的 prefix/suffix 与官方 standard 逐字一致({{model}}/{{cwd}}),
|
|
22
|
+
// 无差异;保留取回逻辑以便上游若分化时单点修改。
|
|
23
|
+
const persona = rows.find((r) => r.id === 'persona')
|
|
24
|
+
if (persona && persona.config) {
|
|
25
|
+
persona.config.prefix = 'You are a coding agent powered by the {{model}} model.'
|
|
26
|
+
persona.config.suffix = 'Your working directory is {{cwd}}.'
|
|
27
|
+
}
|
|
28
|
+
// 委派组:给 spawn/fork 两个 tool-subagent 行注入 MiMo-first 委派 persona(旧 yml)。
|
|
29
|
+
const delegation = rows.find((r) => r.id === 'delegation')
|
|
30
|
+
if (delegation && Array.isArray(delegation.config)) {
|
|
31
|
+
for (const child of delegation.config) {
|
|
32
|
+
if (child && child.name === '@deepseek-ai/dsh-tool-subagent' && child.config && (child.config.provider === 'spawn' || child.config.provider === 'fork')) {
|
|
33
|
+
child.config.persona = MIMO_DELEGATION_PERSONA
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
rows.push(bridgeRow('mimo', deps && deps.bridgeName))
|
|
38
|
+
return rows
|
|
39
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// preset/standard-base.mjs — 官方 standard preset 的忠实移植基线(keystone 单源)。
|
|
2
|
+
//
|
|
3
|
+
// 三个 cli-bridge preset(cordis-agy / codebuddy-first / cordis-mimo)都 = 本基线 +
|
|
4
|
+
// 各自桥接行 + 各自的 persona/门禁差异。基线内容对齐官方 dsh-web-app
|
|
5
|
+
// presets/standard.patch.yml(0.1.7-rc.1):行序、id、config 逐项一致(上游改版后由
|
|
6
|
+
// test/preset-declaration.test.mjs 与已安装 standard.patch.yml 逐行比对防漂移)。
|
|
7
|
+
//
|
|
8
|
+
// `!!js` 表达式的 JS 字面量等价物是 { __jsExpr: "…" } —— 这是 loader interpolate 的
|
|
9
|
+
// 原生输入形态(agentPresets.register 直接接受,见 codebuddy-first v1.2.0 实测),
|
|
10
|
+
// **不要**把它编译成真函数。
|
|
11
|
+
|
|
12
|
+
/** YAML `!!js expr` 的 JS 字面量等价物(loader interpolate 的原生输入形态)。 */
|
|
13
|
+
export function jsExpr(expr) {
|
|
14
|
+
return { __jsExpr: String(expr) }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// 官方 standard preset 的 plan-mode 段全文(0.1.7-rc.1,块标量内容,段间双换行)。
|
|
18
|
+
export const PLAN_MODE_SECTION = [
|
|
19
|
+
'You are in plan mode. Stay in plan mode until exit_plan_mode succeeds or the user switches the session mode. Imperative language to implement changes means plan the implementation, not execute it. A user\'s conversational agreement — including an answer confirming something you asked — approves nothing and does not end plan mode; fold the confirmed decision into the plan and submit it through exit_plan_mode.',
|
|
20
|
+
'Explore first. Use non-mutating reads, searches, static analysis, and checks to ground the plan in the actual repository. Do not edit or write files, change configuration, run formatters or code generation that rewrites tracked files, commit, or otherwise carry out the plan. Prefer existing functions and patterns over new machinery.',
|
|
21
|
+
'The tool catalog stays the same across modes for request-cache stability. These plan-mode rules override any later tool description or guidance that suggests using mutation tools; those tools remain listed to keep the tool catalog unchanged. Do not use todo_write to track this planning phase: it tracks implementation after an approved plan, while the plan itself belongs in exit_plan_mode.',
|
|
22
|
+
'Resolve discoverable facts by inspection. Use ask_user_question only for user-owned choices or material ambiguity that inspection cannot answer. Do not ask the user where code lives or how current behavior works when you can find out.',
|
|
23
|
+
'Make the plan decision-complete: state the goal and success criteria; group implementation changes by subsystem; identify public API, schema, and data-flow changes; cover edge cases, failure modes, tests, acceptance criteria, and explicit assumptions. Keep it concise enough to review but detailed enough that another engineer can implement it without making design decisions.',
|
|
24
|
+
'When ready, call exit_plan_mode with the complete plan markdown, starting with a # title. Make exit_plan_mode the only and final tool call in that assistant response: it presents the plan for approval, and implementation begins only in a later step after approval. Do not paste the final plan as a plain reply or ask "should I proceed?" through prose or ask_user_question. If review rejects it, incorporate the feedback and present again. If the review channel is unavailable or aborted, stay in plan mode and ask the user to switch modes manually; do not proceed with implementation.',
|
|
25
|
+
].join('\n\n') + '\n'
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 官方 standard preset 的插件行表(不含 persona 差异与桥接行)。
|
|
29
|
+
* persona 行返回【副本】供 provider 修改 prefix/suffix;其余行按原样返回。
|
|
30
|
+
* @returns {object[]}
|
|
31
|
+
*/
|
|
32
|
+
export function standardPluginRows() {
|
|
33
|
+
return [
|
|
34
|
+
{ id: 'persona', name: '@deepseek-ai/dsh-persona', config: {
|
|
35
|
+
suffix: 'Your working directory is {{cwd}}.',
|
|
36
|
+
prefix: 'You are a coding agent powered by the {{model}} model.',
|
|
37
|
+
} },
|
|
38
|
+
{ id: 'agent-instructions', name: '@deepseek-ai/dsh-agent-instructions', config: { maxBytes: 65536 } },
|
|
39
|
+
{ id: 'tool-bash', name: '@deepseek-ai/dsh-tool-bash', disabled: jsExpr("process.platform === 'win32'") },
|
|
40
|
+
{ id: 'tool-pwsh', name: '@deepseek-ai/dsh-tool-pwsh', disabled: jsExpr("process.platform !== 'win32'") },
|
|
41
|
+
{ id: 'tool-fs', name: '@deepseek-ai/dsh-tool-fs' },
|
|
42
|
+
{ id: 'tool-fs-search', name: '@deepseek-ai/dsh-tool-fs-search', config: { sampleOverCapGlobResults: false } },
|
|
43
|
+
{ id: 'tool-jobs', name: '@deepseek-ai/dsh-tool-jobs' },
|
|
44
|
+
{ id: 'skill-filesystem', name: '@deepseek-ai/dsh-skill-filesystem' },
|
|
45
|
+
{ id: 'tool-skill', name: '@deepseek-ai/dsh-tool-skill' },
|
|
46
|
+
{ id: 'command-goal', name: '@deepseek-ai/dsh-command-goal' },
|
|
47
|
+
{ id: 'tool-goal', name: '@deepseek-ai/dsh-tool-goal' },
|
|
48
|
+
{
|
|
49
|
+
id: 'planning', name: 'cordis:group', group: true,
|
|
50
|
+
isolate: { planMode: true },
|
|
51
|
+
config: [
|
|
52
|
+
{ id: 'plan-mode', name: '@deepseek-ai/dsh-plan-mode', config: { section: PLAN_MODE_SECTION } },
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: 'compaction', name: 'cordis:group', group: true,
|
|
57
|
+
isolate: { compaction: true, toolResultPruner: true },
|
|
58
|
+
config: [
|
|
59
|
+
{ id: 'compaction-basic', name: '@deepseek-ai/dsh-compaction-basic' },
|
|
60
|
+
{ id: 'command-compact', name: '@deepseek-ai/dsh-command-compact' },
|
|
61
|
+
{ id: 'tool-result-pruner', name: '@deepseek-ai/dsh-compaction-tool-result-pruner', config: {
|
|
62
|
+
thresholdChars: 8192, headChars: 4096, tailChars: 1024,
|
|
63
|
+
} },
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: 'delegation', name: 'cordis:group', group: true,
|
|
68
|
+
isolate: { workflowEngine: true },
|
|
69
|
+
config: [
|
|
70
|
+
{ id: 'tool-subagent-control', name: '@deepseek-ai/dsh-tool-subagent-control' },
|
|
71
|
+
{ id: 'tool-subagent-list-agents', name: '@deepseek-ai/dsh-tool-subagent-control/list-agents' },
|
|
72
|
+
{ id: 'tool-subagent', name: '@deepseek-ai/dsh-tool-subagent', config: {
|
|
73
|
+
provider: 'spawn', toolName: 'subagent', modelSelectionSettings: true, backgroundMode: 'continuable',
|
|
74
|
+
} },
|
|
75
|
+
{ id: 'tool-subagent-fork', name: '@deepseek-ai/dsh-tool-subagent', config: {
|
|
76
|
+
provider: 'fork', toolName: 'subagent_fork', backgroundMode: 'continuable',
|
|
77
|
+
} },
|
|
78
|
+
{ id: 'tool-subagent-codex', name: '@deepseek-ai/dsh-tool-subagent', disabled: true, config: {
|
|
79
|
+
provider: 'codex', toolName: 'subagent_codex', backgroundMode: 'one-shot', maxDepth: 'provider-managed',
|
|
80
|
+
} },
|
|
81
|
+
{ id: 'tool-subagent-claude-code', name: '@deepseek-ai/dsh-tool-subagent', disabled: true, config: {
|
|
82
|
+
provider: 'claude-code', toolName: 'subagent_claude_code', backgroundMode: 'one-shot', maxDepth: 'provider-managed',
|
|
83
|
+
} },
|
|
84
|
+
{ id: 'workflow-ptc', name: '@deepseek-ai/dsh-workflow-ptc', config: { provider: 'spawn' } },
|
|
85
|
+
{ id: 'tool-workflow', name: '@deepseek-ai/dsh-tool-workflow' },
|
|
86
|
+
{ id: 'tool-ralph', name: '@deepseek-ai/dsh-tool-ralph', disabled: true, config: {
|
|
87
|
+
subagentProvider: 'spawn', maxRounds: 64,
|
|
88
|
+
} },
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
{ id: 'tool-ask-user', name: '@deepseek-ai/dsh-tool-ask-user' },
|
|
92
|
+
{ id: 'tool-todo', name: '@deepseek-ai/dsh-tool-todo', config: { allowParallelInProgress: true } },
|
|
93
|
+
{ id: 'tool-web', name: '@deepseek-ai/dsh-tool-web', config: { fetch: true, searchTimeoutMs: 60000 } },
|
|
94
|
+
{ id: 'present', name: '@deepseek-ai/dsh-tool-present' },
|
|
95
|
+
{ id: 'tool-plugin-manager', name: '@deepseek-ai/dsh-plugin-manager/tools', disabled: true },
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** 桥接行(每个 preset 的唯一增量)。deps.bridgeName 必须等于 package.json 的 exports 子路径。 */
|
|
100
|
+
export function bridgeRow(id, bridgeName) {
|
|
101
|
+
return { id: 'cli-bridge-' + id, name: bridgeName || ('@luchenglong/cli-bridge/preset-' + id) }
|
|
102
|
+
}
|