mocode-ai 1.7.1 → 1.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/dist/config/index.js +1 -1
- package/dist/config/profiles.js +2 -2
- package/dist/flows/cli.js +168 -0
- package/dist/flows/flow.js +483 -0
- package/dist/flows/runner.js +259 -0
- package/dist/flows/trace.js +242 -0
- package/dist/i18n/index.js +28 -0
- package/dist/index.js +7 -0
- package/dist/llm/index.js +1 -1
- package/dist/permissions/computer-review.js +40 -0
- package/dist/permissions/flow-review.js +41 -0
- package/dist/permissions/index.js +47 -28
- package/dist/repl/commands/flow.js +210 -0
- package/dist/repl/commands/registry.js +2 -0
- package/dist/repl/commands/tool-group.js +9 -1
- package/dist/repl/commands.js +10 -0
- package/dist/repl/running-input.js +75 -14
- package/dist/repl/runtime.js +4 -1
- package/dist/runtime/input-injector.js +39 -26
- package/dist/runtime/shutdown.js +3 -1
- package/dist/runtime/uia-selector.js +324 -0
- package/dist/runtime/uia-service.js +510 -0
- package/dist/runtime/wait-until.js +199 -0
- package/dist/runtime/window-list.js +164 -0
- package/dist/session/gui-actions.js +47 -0
- package/dist/session/notes.js +58 -8
- package/dist/tools/builtins/computer.js +647 -10
- package/dist/tools/builtins/index.js +4 -0
- package/dist/tools/builtins/run-flow.js +123 -0
- package/dist/tools/constants.js +1 -0
- package/dist/tools/router.js +1 -1
- package/dist/tools/validation.js +57 -0
- package/dist/ui/composer.js +6 -2
- package/dist/ui/layout-internal/content-write.js +74 -6
- package/dist/ui/layout-internal/core.js +32 -8
- package/dist/ui/layout-internal/screen.js +4 -0
- package/dist/ui/layout-internal/scroll.js +5 -3
- package/dist/ui/layout-internal/state.js +6 -0
- package/dist/ui/layout-internal/statusbar.js +16 -1
- package/dist/ui/render.js +19 -11
- package/docs/usage.md +344 -344
- package/package.json +92 -92
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ The implementation is defined by [budget accounting](src/context/budget.ts) and
|
|
|
63
63
|
|
|
64
64
|
### Multi-agent work in a shared workspace
|
|
65
65
|
|
|
66
|
-
Sub-agents keep independent history branches, can reuse the parent's conversation prefix, and cannot expand beyond the tool set delegated by the parent step. Consecutive sub-agent calls run in bounded parallel batches (`SUB_AGENT_CONCURRENCY`, default `
|
|
66
|
+
Sub-agents keep independent history branches, can reuse the parent's conversation prefix, and cannot expand beyond the tool set delegated by the parent step. Consecutive sub-agent calls run in bounded parallel batches (`SUB_AGENT_CONCURRENCY`, default `10`; set `1` for sequential dispatch).
|
|
67
67
|
|
|
68
68
|
**History isolation is not filesystem isolation.** Sub-agents write directly to the shared workspace and inherit the parent's current rollback turn. There is no private filesystem overlay, per-worker worktree, or post-task ChangeSet merge. Nested tools acquire their own resource locks; supported file edits enforce their own expected-hash checks. These guards do not make an entire multi-step sub-task an isolated transaction. The parent receives the final summary, status, usage, and tracked changed files rather than the full worker transcript. Validation remains an explicit agent choice.
|
|
69
69
|
|
|
@@ -225,7 +225,7 @@ Common backend `base_url` values:
|
|
|
225
225
|
| `MOCODE_LIFECYCLE` | Provenance metadata tracking; never ages or rewrites content | `true` |
|
|
226
226
|
| `MAX_STEPS` | Max agent loop steps per turn (infinite-loop safety only) | `1000` |
|
|
227
227
|
| `SUB_AGENT_MAX_STEPS` | Sub-agent loop safety ceiling; defaults to the main-agent value | `1000` |
|
|
228
|
-
| `SUB_AGENT_CONCURRENCY` | Concurrent sub-agent calls per dispatch batch; `1` dispatches them sequentially | `
|
|
228
|
+
| `SUB_AGENT_CONCURRENCY` | Concurrent sub-agent calls per dispatch batch; `1` dispatches them sequentially | `10` |
|
|
229
229
|
| `SUB_AGENT_MAX_DEPTH` | Maximum recursive delegation depth | `3` |
|
|
230
230
|
| `SANDBOX_ROOT` | Sandbox root directory (file operation boundary; falls back to cwd if unset) | none |
|
|
231
231
|
| `MOCODE_SUBAGENT_ENABLED` | Set `false` to veto the `orchestration` route group; unset/`true` allows on-demand routing | unset |
|
package/README.zh-CN.md
CHANGED
|
@@ -54,7 +54,7 @@ MoCode 是一个分层的自治运行时:终端交互层驱动 Agent 内核,
|
|
|
54
54
|
|
|
55
55
|
### 多 Agent 协作:共享工作区,有界并发
|
|
56
56
|
|
|
57
|
-
子 agent 维护独立历史分支,可以复用父级对话前缀,工具能力不能超出父 step 委派的范围。连续的子 agent 调用按并发上限分批执行(`SUB_AGENT_CONCURRENCY` 默认 `
|
|
57
|
+
子 agent 维护独立历史分支,可以复用父级对话前缀,工具能力不能超出父 step 委派的范围。连续的子 agent 调用按并发上限分批执行(`SUB_AGENT_CONCURRENCY` 默认 `10`,设为 `1` 时逐个派发)。
|
|
58
58
|
|
|
59
59
|
**历史隔离不等于文件系统隔离。** 子 agent 直接写入共享工作区,并继承父级当前轮次的回滚记录;没有私有文件系统 overlay、每个 worker 独立的 worktree,也没有任务结束后的 ChangeSet 合并阶段。嵌套工具各自获取资源锁,支持的文件编辑各自执行 expected hash 校验;这些保护不意味着整个多步子任务具备事务隔离。主线接收最终摘要、状态、用量和已追踪的改动文件,而不是完整的子任务过程记录。是否验证及验证范围仍由 agent 自主决定。
|
|
60
60
|
|
|
@@ -224,7 +224,7 @@ LLM_MODEL=glm-4.6 # 换成你的模型名
|
|
|
224
224
|
| `MOCODE_LIFECYCLE` | 只维护 provenance 元数据,不按次数改写正文 | `true` |
|
|
225
225
|
| `MAX_STEPS` | 每轮 Agent 循环最大步数(仅防无限循环) | `1000` |
|
|
226
226
|
| `SUB_AGENT_MAX_STEPS` | 子 Agent 循环安全上限,默认与主 Agent 一致 | `1000` |
|
|
227
|
-
| `SUB_AGENT_CONCURRENCY` | 每批子 agent 调用的并发上限;设为 `1` 时逐个派发 | `
|
|
227
|
+
| `SUB_AGENT_CONCURRENCY` | 每批子 agent 调用的并发上限;设为 `1` 时逐个派发 | `10` |
|
|
228
228
|
| `SUB_AGENT_MAX_DEPTH` | 递归委派深度上限 | `3` |
|
|
229
229
|
| `SANDBOX_ROOT` | 沙箱根目录(文件操作边界;未配则用 cwd 兜底) | 无 |
|
|
230
230
|
| `MOCODE_SUBAGENT_ENABLED` | 设 `false` 硬禁用 `orchestration`;unset/`true` 允许按需路由 | 未设置 |
|
package/dist/config/index.js
CHANGED
|
@@ -612,7 +612,7 @@ export const config = {
|
|
|
612
612
|
maxSteps: Number(process.env.MAX_STEPS) || 1000,
|
|
613
613
|
subAgentEnabled: process.env.MOCODE_SUBAGENT_ENABLED === 'true',
|
|
614
614
|
subAgentMaxSteps: Number(process.env.SUB_AGENT_MAX_STEPS) || Number(process.env.MAX_STEPS) || 1000,
|
|
615
|
-
subAgentConcurrency: Math.max(1, Number(process.env.SUB_AGENT_CONCURRENCY) ||
|
|
615
|
+
subAgentConcurrency: Math.max(1, Number(process.env.SUB_AGENT_CONCURRENCY) || 10),
|
|
616
616
|
subAgentMaxDepth: Math.max(1, Number(process.env.SUB_AGENT_MAX_DEPTH) || 3),
|
|
617
617
|
frontendToolsEnabled: process.env.MOCODE_FRONTEND_TOOLS_ENABLED === 'true',
|
|
618
618
|
computerUseEnabled: process.env.MOCODE_COMPUTER_USE_ENABLED === 'true',
|
package/dist/config/profiles.js
CHANGED
|
@@ -18,7 +18,7 @@ export const TOOL_GROUPS = {
|
|
|
18
18
|
'agent-meta': ['plan_update', 'note_append', 'ask_human', 'use_skill', 'run_skill'],
|
|
19
19
|
web: ['web_search', 'web_fetch'],
|
|
20
20
|
frontend: ['browser', 'screenshot'],
|
|
21
|
-
computer: ['computer'],
|
|
21
|
+
computer: ['computer', 'run_flow'],
|
|
22
22
|
memory: [
|
|
23
23
|
'memory_save',
|
|
24
24
|
'memory_search',
|
|
@@ -83,7 +83,7 @@ export const TOOL_ROUTE_GROUPS = {
|
|
|
83
83
|
gateEnv: 'MOCODE_FRONTEND_TOOLS_ENABLED',
|
|
84
84
|
},
|
|
85
85
|
'computer-control': {
|
|
86
|
-
tools: ['computer'],
|
|
86
|
+
tools: ['computer', 'run_flow'],
|
|
87
87
|
description: 'Control real desktop applications with mouse, keyboard, scrolling, and visual feedback.',
|
|
88
88
|
gateEnv: 'MOCODE_COMPUTER_USE_ENABLED',
|
|
89
89
|
},
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `mocode flow list | show <name> | run <name> [k=v ...] [--window <正则>] [--yes] [--sandbox-root <path>]`
|
|
3
|
+
* (design-notes/computer-use-rpa.md §5.4)。
|
|
4
|
+
*
|
|
5
|
+
* 非交互回放入口:不经过模型、不依赖 REPL。权限语义与 `/flow run` 一致但 fail-closed:
|
|
6
|
+
* - 受 Computer Use 总开关否决(MOCODE_COMPUTER_USE_ENABLED=false);
|
|
7
|
+
* - 命中敏感审查(secret 参数、敏感文本、删除/发送/支付类目标)的 flow,没有显式 `--yes` 一律拒绝,
|
|
8
|
+
* 因为这里没有人可以点确认面板;
|
|
9
|
+
* - 未命中敏感审查的 flow 直接运行(用户亲手敲了命令,等价于 `/flow run`)。
|
|
10
|
+
* 退出码:completed 0;handoff / failed / rejected / 被拒绝 1;用户参数错误 2;aborted 130。
|
|
11
|
+
*
|
|
12
|
+
* 重模块(computer.ts 及其 UIA/抓屏依赖)经 deps 注入,默认实现只在 run 时动态加载。
|
|
13
|
+
*/
|
|
14
|
+
import { formatFlowRunResult } from './runner.js';
|
|
15
|
+
import { listFlows, loadFlow, summarizeFlow } from './flow.js';
|
|
16
|
+
import { reviewFlowRun } from '../permissions/flow-review.js';
|
|
17
|
+
import { getSandboxRoot, setSandboxRoot } from '../sandbox/root.js';
|
|
18
|
+
export const FLOW_CLI_USAGE = 'Usage: mocode flow list | show <name> | run <name> [param=value ...] [--window <regex>] [--yes] [--sandbox-root <path>]';
|
|
19
|
+
function defaultDeps() {
|
|
20
|
+
return {
|
|
21
|
+
out: (s) => void process.stdout.write(s),
|
|
22
|
+
err: (s) => void process.stderr.write(s),
|
|
23
|
+
async computerAllowed() {
|
|
24
|
+
const { isComputerUseRouteAllowed } = await import('../config/index.js');
|
|
25
|
+
return isComputerUseRouteAllowed();
|
|
26
|
+
},
|
|
27
|
+
async run(flow, params, signal, options) {
|
|
28
|
+
const { runFlowWithComputer } = await import('../tools/builtins/run-flow.js');
|
|
29
|
+
return runFlowWithComputer(flow, params, signal, options);
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/** 解析 run 子命令的参数(纯函数,便于单测)。 */
|
|
34
|
+
export function parseRunArgs(tokens) {
|
|
35
|
+
const out = { params: {}, yes: false };
|
|
36
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
37
|
+
const tok = tokens[i];
|
|
38
|
+
if (tok === '--yes' || tok === '-y') {
|
|
39
|
+
out.yes = true;
|
|
40
|
+
}
|
|
41
|
+
else if (tok === '--window') {
|
|
42
|
+
const re = tokens[++i];
|
|
43
|
+
if (!re || re.startsWith('--'))
|
|
44
|
+
return { ...out, error: '--window requires a regular expression' };
|
|
45
|
+
try {
|
|
46
|
+
new RegExp(re, 'i');
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return { ...out, error: `--window is not a valid regular expression: ${re}` };
|
|
50
|
+
}
|
|
51
|
+
out.windowTitleRegex = re;
|
|
52
|
+
}
|
|
53
|
+
else if (tok.startsWith('--')) {
|
|
54
|
+
return { ...out, error: `unknown option ${tok}` };
|
|
55
|
+
}
|
|
56
|
+
else if (out.name === undefined) {
|
|
57
|
+
out.name = tok;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
const eq = tok.indexOf('=');
|
|
61
|
+
if (eq <= 0)
|
|
62
|
+
return { ...out, error: `parameter "${tok}" must look like name=value` };
|
|
63
|
+
out.params[tok.slice(0, eq)] = tok.slice(eq + 1);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
export async function runFlowCli(rawArgs, deps = defaultDeps()) {
|
|
69
|
+
// 取出 --sandbox-root <path>(与主入口同语义),其余交给子命令。
|
|
70
|
+
const args = [];
|
|
71
|
+
let sandboxRoot;
|
|
72
|
+
for (let i = 0; i < rawArgs.length; i++) {
|
|
73
|
+
if (rawArgs[i] === '--sandbox-root') {
|
|
74
|
+
sandboxRoot = rawArgs[++i];
|
|
75
|
+
if (!sandboxRoot || sandboxRoot.startsWith('--')) {
|
|
76
|
+
deps.err('mocode flow: --sandbox-root requires a path\n');
|
|
77
|
+
return 2;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
args.push(rawArgs[i]);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const prevRoot = sandboxRoot !== undefined ? setSandboxRoot(sandboxRoot) : getSandboxRoot();
|
|
85
|
+
try {
|
|
86
|
+
return await dispatch(args, deps);
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
if (sandboxRoot !== undefined)
|
|
90
|
+
setSandboxRoot(prevRoot);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async function dispatch(args, deps) {
|
|
94
|
+
const sub = (args.shift() ?? '').toLowerCase();
|
|
95
|
+
if (sub === 'list') {
|
|
96
|
+
const items = listFlows();
|
|
97
|
+
if (items.length === 0) {
|
|
98
|
+
deps.out('No saved flows (.mocode/flows/).\n');
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
for (const it of items) {
|
|
102
|
+
deps.out(`${it.name} ${it.steps} steps${it.description ? ` · ${it.description}` : ''}\n`);
|
|
103
|
+
}
|
|
104
|
+
return 0;
|
|
105
|
+
}
|
|
106
|
+
if (sub === 'show') {
|
|
107
|
+
const name = args[0];
|
|
108
|
+
if (!name) {
|
|
109
|
+
deps.err(`${FLOW_CLI_USAGE}\n`);
|
|
110
|
+
return 2;
|
|
111
|
+
}
|
|
112
|
+
const loaded = loadFlow(name);
|
|
113
|
+
if (!loaded.ok) {
|
|
114
|
+
deps.err(`mocode flow: ${loaded.error}\n`);
|
|
115
|
+
return 1;
|
|
116
|
+
}
|
|
117
|
+
deps.out(`${summarizeFlow(loaded.flow)}\n${loaded.path}\n`);
|
|
118
|
+
return 0;
|
|
119
|
+
}
|
|
120
|
+
if (sub === 'run') {
|
|
121
|
+
const parsed = parseRunArgs(args);
|
|
122
|
+
if (parsed.error || !parsed.name) {
|
|
123
|
+
deps.err(`mocode flow: ${parsed.error ?? 'missing flow name'}\n${FLOW_CLI_USAGE}\n`);
|
|
124
|
+
return 2;
|
|
125
|
+
}
|
|
126
|
+
if (!(await deps.computerAllowed())) {
|
|
127
|
+
deps.err('mocode flow: Computer Use is disabled (MOCODE_COMPUTER_USE_ENABLED=false); flows will not run.\n');
|
|
128
|
+
return 1;
|
|
129
|
+
}
|
|
130
|
+
const loaded = loadFlow(parsed.name);
|
|
131
|
+
if (!loaded.ok) {
|
|
132
|
+
deps.err(`mocode flow: ${loaded.error}\n`);
|
|
133
|
+
return 1;
|
|
134
|
+
}
|
|
135
|
+
const review = reviewFlowRun({
|
|
136
|
+
name: parsed.name,
|
|
137
|
+
params: parsed.params,
|
|
138
|
+
...(parsed.windowTitleRegex ? { window_title_regex: parsed.windowTitleRegex } : {}),
|
|
139
|
+
});
|
|
140
|
+
if (review.reasons.length > 0 && !parsed.yes) {
|
|
141
|
+
deps.err(`mocode flow: flow "${parsed.name}" needs explicit approval and there is no interactive prompt here:\n` +
|
|
142
|
+
`${review.reasons.map((r) => ` - ${r}`).join('\n')}\n` +
|
|
143
|
+
'Review it with `mocode flow show`, then re-run with --yes to confirm.\n');
|
|
144
|
+
return 1;
|
|
145
|
+
}
|
|
146
|
+
const controller = new AbortController();
|
|
147
|
+
const onSigint = () => controller.abort();
|
|
148
|
+
process.once('SIGINT', onSigint);
|
|
149
|
+
let result;
|
|
150
|
+
try {
|
|
151
|
+
result = await deps.run(loaded.flow, parsed.params, controller.signal, {
|
|
152
|
+
...(parsed.windowTitleRegex ? { windowTitleRegex: parsed.windowTitleRegex } : {}),
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
process.removeListener('SIGINT', onSigint);
|
|
157
|
+
}
|
|
158
|
+
const text = formatFlowRunResult(loaded.flow, result);
|
|
159
|
+
if (result.status === 'completed') {
|
|
160
|
+
deps.out(`${text}\n`);
|
|
161
|
+
return 0;
|
|
162
|
+
}
|
|
163
|
+
deps.err(`${text}\n`);
|
|
164
|
+
return result.status === 'aborted' ? 130 : 1;
|
|
165
|
+
}
|
|
166
|
+
deps.err(`${FLOW_CLI_USAGE}\n`);
|
|
167
|
+
return 2;
|
|
168
|
+
}
|