@realqiyan/agent-task 0.1.0-rc.2 → 0.1.0-rc.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 +4 -25
- package/dist/protocol/mcp-contract.js +38 -6
- package/dist/protocol/mcp.js +4 -4
- package/dist/protocol/task-contract.js +7 -7
- package/dist/tasks/cron.js +10 -0
- package/dist/tasks/schedule.js +15 -6
- package/dist/tasks/validation.js +12 -1
- package/docs/cli.md +9 -4
- package/docs/deployment.md +2 -2
- package/docs/design-proposal.md +2 -2
- package/docs/release.md +4 -4
- package/docs/technical-design.md +4 -6
- package/docs/validation.md +39 -144
- package/docs/web-console.md +1 -4
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -34,37 +34,16 @@ agent-task status
|
|
|
34
34
|
agent-task restart
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
stdio 入口从私有文件读取内部服务 token;直接 HTTP 接入需配置独立服务 token。源码开发辅助命令(凭据须安全注入环境,不写命令行):
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
node scripts/mcp-call.mjs --list
|
|
43
|
-
node scripts/mcp-call.mjs service_status
|
|
44
|
-
```
|
|
37
|
+
setup 确认本机配置;MCP 连接自动启动或复用独立后台,宿主退出后后台继续运行。首次使用须安装至少一种原生执行 CLI 并准备其认证。完整接入配置、执行端准备与服务管理见[部署说明](docs/deployment.md)。
|
|
45
38
|
|
|
46
39
|
任务由 `task_create` 提交固定 DAG,每个节点选择已就绪执行端。显式工作目录优先,否则 stdio 入口补入自己的启动目录;直接 HTTP 调用必须提供目录。具体契约见 [task-contract.ts](src/protocol/task-contract.ts),管理工具见 [mcp.ts](src/protocol/mcp.ts)。Prompt、人工回复和工作空间选择都不授予工具权限。
|
|
47
40
|
|
|
48
41
|
## CLI 命令索引
|
|
49
42
|
|
|
50
|
-
|
|
43
|
+
完整命令与参数使用 `agent-task --help`、`agent-task <命令> --help` 查询,不在文档中维护副本。
|
|
51
44
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
| `agent-task setup` | — | 初始化、检查或接续安装配置 |
|
|
55
|
-
| `agent-task mcp` | — | stdio 接入,自动启动或连接共用后台 |
|
|
56
|
-
| `agent-task start` | — | 前台启动服务并调度任务 |
|
|
57
|
-
| `agent-task stop` | — | 停止共用服务 |
|
|
58
|
-
| `agent-task restart` | — | 应用已保存配置并重启、检查可用性 |
|
|
59
|
-
| `agent-task status` | — | 查看服务健康、活动任务及待生效配置 |
|
|
60
|
-
| `agent-task config` | `show`、`diff`、`edit`、`export`、`validate`、`apply` | 查看、编辑、导出、校验及保存配置 |
|
|
61
|
-
| `agent-task executor` | `list`、`show`、`refresh`、`upgrade`、`enable`、`disable` | 查看与刷新执行端、升级适配器、设置启停 |
|
|
62
|
-
| `agent-task coordinator` | `show`、`set`、`clear` | 查看、设置或清除协调模型配置 |
|
|
63
|
-
| `agent-task credential` | `list`、`set`、`delete` | 查看凭据状态、设置或删除凭据 |
|
|
64
|
-
| `agent-task task` | `list`、`show`、`pause`、`resume`、`cancel` | 查看任务,控制未来触发 |
|
|
65
|
-
| `agent-task run` | `list`、`show` | 查看运行历史与交付详情 |
|
|
66
|
-
| `agent-task help` / `agent-task --help` | — | 查看总帮助 |
|
|
67
|
-
| `agent-task --version` | — | 查看版本 |
|
|
45
|
+
- 安装与运行:`setup`、`mcp`、`start`、`stop`、`restart`、`status`,见[部署说明](docs/deployment.md)。
|
|
46
|
+
- 配置与凭据:`config`、`executor`、`coordinator`、`credential`;任务与运行:`task`、`run`,见 [CLI 说明](docs/cli.md)。
|
|
68
47
|
|
|
69
48
|
## 许可证
|
|
70
49
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { absolutePath, identifier, timestamp } from "../shared/validation.js";
|
|
3
|
-
import { nodeData, nodeStatusSchema, questionData, runStatusSchema, taskData, taskStatusSchema } from "../tasks/validation.js";
|
|
3
|
+
import { cronScheduleData, nodeData, nodeStatusSchema, questionData, runStatusSchema, taskData, taskStatusSchema } from "../tasks/validation.js";
|
|
4
4
|
/** Shared MCP initialization text; both the HTTP server and the local stdio proxy advertise it. */
|
|
5
5
|
export const serverInstructions = `Agent Task runs caller-defined DAGs asynchronously with local ACP executors. A task is a persistent definition and schedule; a run is one execution. Task and run UUIDs are distinct from caller-defined DAG node IDs.
|
|
6
6
|
Check service_status and executor_list before task_create. Poll task_run_list(taskId) at intervals for progress; use task_run_get for full outputs and task_run_reply for requested user facts. Creation returns a task, not a run or completed result. Disconnecting does not cancel work.
|
|
@@ -17,11 +17,10 @@ const scheduleOutput = z.discriminatedUnion("type", [
|
|
|
17
17
|
type: z.literal("once"),
|
|
18
18
|
at: timestamp.describe("Scheduled trigger, not actual execution start."),
|
|
19
19
|
}).strict(),
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}).strict(),
|
|
20
|
+
cronScheduleData.safeExtend({
|
|
21
|
+
expression: cronScheduleData.shape.expression.describe("Five-field calendar Cron expression."),
|
|
22
|
+
timezone: cronScheduleData.shape.timezone.describe("Explicit timezone used to calculate calendar triggers."),
|
|
23
|
+
}),
|
|
25
24
|
]).describe("Stored schedule. Task status and nextAt are authoritative for future eligibility.");
|
|
26
25
|
const taskNodeOutput = nodeData.safeExtend({
|
|
27
26
|
id: nodeData.shape.id.describe("Caller-defined DAG node ID within this task; not a node-run UUID."),
|
|
@@ -181,6 +180,39 @@ export function pageOutput(item) {
|
|
|
181
180
|
next: z.number().int().nonnegative().nullable().describe("Next page's after offset with the same filters; null means the scan is exhausted now, not that all work is complete."),
|
|
182
181
|
}).strict();
|
|
183
182
|
}
|
|
183
|
+
/** Keep explicit nulls without array-valued types that some MCP clients reject. */
|
|
184
|
+
export function mcpJsonSchema(schema, io) {
|
|
185
|
+
const json = z.toJSONSchema(schema, { target: "draft-7", io });
|
|
186
|
+
// Normalize after conversion: Zod compacts unions after its override callback.
|
|
187
|
+
function visit(node) {
|
|
188
|
+
if (!node || typeof node !== "object")
|
|
189
|
+
return;
|
|
190
|
+
if (Array.isArray(node.type)) {
|
|
191
|
+
const anyOf = node.type.map(type => ({ type }));
|
|
192
|
+
delete node.type;
|
|
193
|
+
if (node.anyOf)
|
|
194
|
+
node.allOf = [...(node.allOf ?? []), { anyOf }];
|
|
195
|
+
else
|
|
196
|
+
node.anyOf = anyOf;
|
|
197
|
+
}
|
|
198
|
+
for (const map of [node.properties, node.patternProperties, node.$defs]) {
|
|
199
|
+
for (const child of Object.values(map ?? {}))
|
|
200
|
+
visit(child);
|
|
201
|
+
}
|
|
202
|
+
for (const branches of [node.anyOf, node.oneOf, node.allOf]) {
|
|
203
|
+
for (const child of branches ?? [])
|
|
204
|
+
visit(child);
|
|
205
|
+
}
|
|
206
|
+
for (const child of [node.additionalProperties, node.not, node.if, node.then, node.else, node.contains, node.propertyNames])
|
|
207
|
+
visit(child);
|
|
208
|
+
if (Array.isArray(node.items))
|
|
209
|
+
node.items.forEach(visit);
|
|
210
|
+
else
|
|
211
|
+
visit(node.items);
|
|
212
|
+
}
|
|
213
|
+
visit(json);
|
|
214
|
+
return json;
|
|
215
|
+
}
|
|
184
216
|
export function toolOutput(data) {
|
|
185
217
|
return z.discriminatedUnion("ok", [
|
|
186
218
|
z.object({ ok: z.literal(true), data }).strict(),
|
package/dist/protocol/mcp.js
CHANGED
|
@@ -3,7 +3,7 @@ import { CallToolRequestSchema, ListToolsRequestSchema, ToolSchema } from "@mode
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { errorCode, errorText, StorageError } from "../shared/errors.js";
|
|
5
5
|
import { replySchema, taskSchema, taskUpdateSchema, taskIdSchema, runIdSchema, taskListSchema, taskRunListSchema } from "./task-contract.js";
|
|
6
|
-
import { executorsOutput, pageOutput, runOutput, runSummaryOutput, serverInstructions, statusOutput, taskOutput, taskSummaryOutput, toolOutput } from "./mcp-contract.js";
|
|
6
|
+
import { executorsOutput, mcpJsonSchema, pageOutput, runOutput, runSummaryOutput, serverInstructions, statusOutput, taskOutput, taskSummaryOutput, toolOutput } from "./mcp-contract.js";
|
|
7
7
|
import { version } from "../infrastructure/version.js";
|
|
8
8
|
const readOnly = { readOnly: true, destructive: false, idempotent: true, openWorld: false };
|
|
9
9
|
export function createMcpServer(service, fatal) {
|
|
@@ -17,8 +17,8 @@ export function createMcpServer(service, fatal) {
|
|
|
17
17
|
definition: ToolSchema.parse({
|
|
18
18
|
name,
|
|
19
19
|
description,
|
|
20
|
-
inputSchema: { ...
|
|
21
|
-
outputSchema: { ...
|
|
20
|
+
inputSchema: { ...mcpJsonSchema(inputSchema, "input"), type: "object" },
|
|
21
|
+
outputSchema: { ...mcpJsonSchema(outputSchema, "output"), type: "object" },
|
|
22
22
|
annotations: { readOnlyHint: hints.readOnly, destructiveHint: hints.destructive, idempotentHint: hints.idempotent, openWorldHint: hints.openWorld },
|
|
23
23
|
}),
|
|
24
24
|
call(input) {
|
|
@@ -39,7 +39,7 @@ export function createMcpServer(service, fatal) {
|
|
|
39
39
|
}
|
|
40
40
|
register("service_status", "Check scheduler/storage health, coordinator configuration and capacity before creating work or when diagnosing a stalled run. Does not test model credentials or availability; use executor_list for executor readiness.", z.object({}).strict(), statusOutput, () => service.supervisor.status());
|
|
41
41
|
register("executor_list", "Find local ACP executor IDs for task_create and inspect adapter preparation. Select only state=ready; this confirms launch dependencies, not native authentication or tool authorization. Does not refresh discovery or install adapters.", z.object({}).strict(), executorsOutput, () => ({ executors: service.executors.list().executors.map(executorStatus) }));
|
|
42
|
-
register("task_create", "Create a persistent task for immediate, one-time or
|
|
42
|
+
register("task_create", "Create a persistent task for immediate, one-time or Cron-scheduled execution. Supply the complete fixed DAG and a ready executor per node; the coordinator reviews deliveries and requests missing facts, but does not plan or reassign work. Requires service_status.coordinatorConfigured=true. Returns a task, not an execution result; find runs with task_run_list(taskId). Background model/ACP work survives MCP disconnection. Retry the same submission with the same requestKey and parameters, not a replacement task.", taskSchema, taskOutput, input => taskView(service.definitions.create(input)), { readOnly: false, destructive: false, idempotent: true, openWorld: true });
|
|
43
43
|
register("task_list", "Find tasks and their future-trigger state, including cancelled and completed tasks. Returns compact records, oldest first; follow next for more. Use task_get for the full definition and task_run_list for execution outcomes.", taskListSchema, pageOutput(taskSummaryOutput), input => {
|
|
44
44
|
const page = service.definitions.list(input.after, input.limit);
|
|
45
45
|
return { items: page.items.map(taskSummary), next: page.next };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { requestTimestamp } from "../shared/validation.js";
|
|
3
|
-
import { nodeData, replyData, runStatusSchema, taskData, taskUpdateData } from "../tasks/validation.js";
|
|
3
|
+
import { cronScheduleData, nodeData, replyData, runStatusSchema, taskData, taskUpdateData } from "../tasks/validation.js";
|
|
4
4
|
export const nodeSchema = nodeData.extend({
|
|
5
5
|
id: nodeData.shape.id.describe("Unique caller-defined ID in this DAG, referenced by dependsOn; not a run UUID."),
|
|
6
6
|
prompt: z.string().trim().min(1).max(64_000).describe("Self-contained work and acceptance criteria for this executor. The task-level prompt is not copied here. Plain text/Markdown delivery is valid unless a format is required."),
|
|
@@ -26,7 +26,7 @@ export const taskRunListSchema = taskListSchema.extend({
|
|
|
26
26
|
});
|
|
27
27
|
export const taskUpdateSchema = taskUpdateData.extend({
|
|
28
28
|
id: taskIdSchema.shape.id,
|
|
29
|
-
action: taskUpdateData.shape.action.describe("pause keeps the next trigger but suspends it; resume re-enables it without catch-up (past
|
|
29
|
+
action: taskUpdateData.shape.action.describe("pause keeps the next trigger but suspends it; resume re-enables it without catch-up (past Cron slots advance; past now/once slots become completed); cancel permanently removes future triggers. pause/resume require a remaining trigger. resume fails if any run has an interrupted node."),
|
|
30
30
|
});
|
|
31
31
|
export const taskSchema = taskData.extend({
|
|
32
32
|
requestKey: taskData.shape.requestKey.describe("Caller-chosen creation key, scoped to the canonical workspace. Keep it stable for retries: identical normalized parameters return the existing task, including cancelled/completed tasks; changes conflict. A new key creates separate work, not a retry."),
|
|
@@ -40,11 +40,11 @@ export const taskSchema = taskData.extend({
|
|
|
40
40
|
type: z.literal("once").describe("One slot at the specified future time."),
|
|
41
41
|
at: requestTimestamp.describe("Future ISO 8601 trigger time with UTC Z or numeric offset; normalized to UTC."),
|
|
42
42
|
}).strict(),
|
|
43
|
-
|
|
44
|
-
type: z.literal("
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
})
|
|
43
|
+
cronScheduleData.safeExtend({
|
|
44
|
+
type: z.literal("cron").describe("Repeating calendar slots; the first match strictly after creation, not after the previous run completes."),
|
|
45
|
+
expression: z.string().trim().min(1).max(200).describe("Five fields: minute hour day-of-month month day-of-week, e.g. 0 9 * * 1-5. No seconds, @ aliases or hashed H fields. Day-of-month and day-of-week use OR when both are restricted."),
|
|
46
|
+
timezone: z.string().trim().min(1).max(100).describe("Required IANA timezone, e.g. Asia/Shanghai or UTC; never inferred from the server environment. DST follows cron-parser calendar rules."),
|
|
47
|
+
}),
|
|
48
48
|
]).describe("When runs become eligible, not guaranteed start times. Slots over 30 seconds late or overlapping a running/waiting run of this task are skipped, not queued for catch-up."),
|
|
49
49
|
expiresAt: requestTimestamp.optional().describe("Cutoff for new runs and previously unstarted nodes, not a completion timeout. Must follow the first trigger. Existing ACP sessions may continue. ISO 8601 with timezone; omit for no cutoff."),
|
|
50
50
|
maxRunSeconds: taskData.shape.maxRunSeconds.default(1800).describe("Run execution budget in seconds from coordinator start, excluding human-input waits; includes ACP execution. Separate from expiresAt and each node's session lifetime."),
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { CronExpressionParser } from "cron-parser";
|
|
2
|
+
/** Five calendar fields, deterministic expressions and an explicit IANA timezone. */
|
|
3
|
+
export function parseCron(schedule, now) {
|
|
4
|
+
if (schedule.expression.trim().split(/\s+/).length !== 5 || /\bH\b/i.test(schedule.expression)) {
|
|
5
|
+
throw new Error("Cron requires five fields (minute hour day-of-month month day-of-week); hashed fields are not supported");
|
|
6
|
+
}
|
|
7
|
+
new Intl.DateTimeFormat("en", { timeZone: schedule.timezone });
|
|
8
|
+
return CronExpressionParser.parse(schedule.expression, { currentDate: now, tz: schedule.timezone });
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=cron.js.map
|
package/dist/tasks/schedule.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { TaskError } from "../shared/errors.js";
|
|
2
|
+
import { parseCron } from "./cron.js";
|
|
2
3
|
export function hasUncertainOutcome(run) {
|
|
3
4
|
return run.nodes.some(node => node.status === "interrupted");
|
|
4
5
|
}
|
|
5
6
|
export function initialTime(schedule, now) {
|
|
6
|
-
return schedule.type === "now" ? now :
|
|
7
|
+
return schedule.type === "now" ? now : schedule.type === "once" ? Date.parse(schedule.at) : parseCron(schedule, now).next().getTime();
|
|
7
8
|
}
|
|
8
9
|
export function resumeSchedule(task, now) {
|
|
9
10
|
const next = structuredClone(task);
|
|
@@ -11,8 +12,8 @@ export function resumeSchedule(task, now) {
|
|
|
11
12
|
throw new TaskError("precondition", "Task has no future schedule");
|
|
12
13
|
next.status = "active";
|
|
13
14
|
if (next.nextAt < now) {
|
|
14
|
-
if (next.input.schedule.type === "
|
|
15
|
-
next.nextAt
|
|
15
|
+
if (next.input.schedule.type === "cron")
|
|
16
|
+
next.nextAt = parseCron(next.input.schedule, now).next().getTime();
|
|
16
17
|
else {
|
|
17
18
|
next.nextAt = null;
|
|
18
19
|
next.status = "completed";
|
|
@@ -29,9 +30,17 @@ export function dueTrigger(task, overlaps, now) {
|
|
|
29
30
|
if (task.nextAt > now)
|
|
30
31
|
return { kind: "none" };
|
|
31
32
|
const schedule = task.input.schedule;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
let count = 1, at = task.nextAt, nextAt = null;
|
|
34
|
+
if (schedule.type === "cron") {
|
|
35
|
+
const cron = parseCron(schedule, at);
|
|
36
|
+
nextAt = cron.next().getTime();
|
|
37
|
+
while (nextAt <= now) {
|
|
38
|
+
at = nextAt;
|
|
39
|
+
count++;
|
|
40
|
+
nextAt = cron.next().getTime();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return { kind: "due", at, count, nextAt,
|
|
35
44
|
reason: overlaps ? "Previous round still active; slot skipped" : now - at > 30_000 ? "Late slot skipped" : null };
|
|
36
45
|
}
|
|
37
46
|
//# sourceMappingURL=schedule.js.map
|
package/dist/tasks/validation.js
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { absolutePath, identifier, text, timestamp } from "../shared/validation.js";
|
|
3
|
+
import { parseCron } from "./cron.js";
|
|
4
|
+
export const cronScheduleData = z.object({
|
|
5
|
+
type: z.literal("cron"), expression: text(200), timezone: text(100),
|
|
6
|
+
}).strict().superRefine((schedule, ctx) => {
|
|
7
|
+
try {
|
|
8
|
+
parseCron(schedule, Date.UTC(2000, 0, 1)).next();
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
ctx.addIssue({ code: "custom", message: "Invalid five-field Cron expression or timezone" });
|
|
12
|
+
}
|
|
13
|
+
});
|
|
3
14
|
export const scheduleData = z.discriminatedUnion("type", [
|
|
4
15
|
z.object({ type: z.literal("now") }).strict(),
|
|
5
16
|
z.object({ type: z.literal("once"), at: timestamp }).strict(),
|
|
6
|
-
|
|
17
|
+
cronScheduleData,
|
|
7
18
|
]);
|
|
8
19
|
export const taskStatusSchema = z.enum(["active", "paused", "cancelled", "completed", "expired"]);
|
|
9
20
|
export const runStatusSchema = z.enum(["running", "succeeded", "failed", "interrupted", "skipped", "cancelled", "timed_out", "waiting_input"]);
|
package/docs/cli.md
CHANGED
|
@@ -44,11 +44,16 @@ agent-task config show --running
|
|
|
44
44
|
|
|
45
45
|
## 执行端发现与更新
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
```bash
|
|
48
|
+
agent-task executor list
|
|
49
|
+
agent-task executor show codex
|
|
50
|
+
agent-task executor refresh
|
|
51
|
+
# 仅在需要升级适配器时执行:
|
|
52
|
+
agent-task executor upgrade codex --yes
|
|
53
|
+
agent-task executor list
|
|
54
|
+
```
|
|
50
55
|
|
|
51
|
-
|
|
56
|
+
发现、安装复用、升级影响和原生认证统一见[部署说明](deployment.md#执行端发现与准备)。就绪不证明工具授权或任务成功;PATH 变化也不能仅靠刷新生效。
|
|
52
57
|
|
|
53
58
|
## 凭据与任务
|
|
54
59
|
|
package/docs/deployment.md
CHANGED
|
@@ -54,7 +54,7 @@ agent-task setup --home /absolute/private/agent-task \
|
|
|
54
54
|
|
|
55
55
|
发现原生 CLI 后,后台为缺少 ACP 适配器的执行端安装当时最新版,已有安装直接复用;OpenCode 使用原生 ACP 命令。安装在后台进行,可用 MCP `executor_list`、`agent-task executor list` 或 Web 查看准备结果。任务只能选择就绪执行端。适配器安装失败时保留管理入口和其他就绪执行端,修正原因后显式刷新或升级。包映射和安装实现见 [discovery.ts](../src/integrations/acp/discovery.ts)。
|
|
56
56
|
|
|
57
|
-
适配器保存在安装目录的私有 `adapters/` 中,不修改全局 npm 包或执行 CLI 的原生配置。普通重连不访问 registry 升级已有适配器。显式更新使用 `agent-task executor upgrade codex --yes
|
|
57
|
+
适配器保存在安装目录的私有 `adapters/` 中,不修改全局 npm 包或执行 CLI 的原生配置。普通重连不访问 registry 升级已有适配器。显式更新使用 `agent-task executor upgrade codex --yes`,再查询状态;升级期间暂停向该执行端派发新工作,新版本发布成功前保留原安装,活动 ACP 进程不因升级被重启。重新检测和升级直接作用于当前执行端目录,无需重启,也不保存启动配置。OpenCode 通过原生 CLI 的安装渠道更新。命令入口见 [CLI 说明](cli.md#执行端发现与更新)。
|
|
58
58
|
|
|
59
59
|
后台的原生环境来自启动它的进程,不随不同 MCP 客户端切换。新 CLI 位于已有 PATH 时可用 `executor refresh` 重新检测;PATH 本身变化时先停止后台,再从正确环境连接或启动。原生登录须与后台使用同一 OS 用户及 HOME/配置目录。
|
|
60
60
|
|
|
@@ -82,7 +82,7 @@ MCP 接入不会自动重启一个已存在但不可用的后台。故障时查
|
|
|
82
82
|
|
|
83
83
|
运行配置、私有环境文件、任务和协调会话记录、结果及服务日志位于当前 `dataDir`。安装目录保留 `launch.json` 定位文件、初始化清单、适配器及启动器控制资源。npm 包、项目工作空间和执行端原生配置与数据目录分开。路径实现见 [startup.ts](../src/application/startup.ts)。
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
Web 访问 `status` 返回地址同源的 `/admin/`,连接与令牌保护见[控制台说明](web-console.md#启用与连接);命令行管理见 [CLI 说明](cli.md)。两端使用同一管理服务。
|
|
86
86
|
|
|
87
87
|
修改数据目录后,下次启动只准备目标配置和私有环境,并切换定位文件;不复制任务、会话、结果或日志,不删除旧目录。目标已有不同启动文件时拒绝覆盖,须停服核对并显式准备目标。新目录没有旧任务,目录切换不是恢复或重放许可。
|
|
88
88
|
|
package/docs/design-proposal.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
## 产品目标
|
|
8
8
|
|
|
9
|
-
- 调用方通过 MCP 提交整体目标、DAG 和每节点的 ACP
|
|
9
|
+
- 调用方通过 MCP 提交整体目标、DAG 和每节点的 ACP 执行端指派,支持立即、定时及 Cron 周期执行;Cron 使用五段表达式与显式时区,契约见 [task-contract.ts](../src/protocol/task-contract.ts)。
|
|
10
10
|
- 任务不依赖发起客户端持续在线,保留执行、交付、失败和缺少用户事实的记录。
|
|
11
11
|
- 初始化由用户确认目录、端口、令牌及协调模型;执行端自动发现并按需准备适配器。stdio 接入连接独立后台,宿主退出后后台继续调度;宿主配置由用户添加,不自动识别宿主当前项目。
|
|
12
12
|
- 协调员推进既定 DAG、解释中间结果、处理澄清并组织交付;领域分析和工具操作交给执行端。
|
|
@@ -45,7 +45,7 @@ ACP 执行端:原生会话、领域推理和工具操作
|
|
|
45
45
|
|
|
46
46
|
当前是调研和 MVP 验证阶段,直接调整接口和数据结构,不维护旧版兼容或业务库版本递增;这不授权迁移或删除历史数据。
|
|
47
47
|
|
|
48
|
-
暂不实现:执行中修改 DAG、自动重试或跨端故障转移、丢失 ACP
|
|
48
|
+
暂不实现:执行中修改 DAG、自动重试或跨端故障转移、丢失 ACP 会话的重连、人工核对后强制恢复、远程执行、多租户、技能同步及动态 MCP 转发。它们需要独立产品决策,不因引入 durable 自动获得。
|
|
49
49
|
|
|
50
50
|
启动方式见[部署说明](deployment.md)。[本机控制台](web-console.md)复用任务服务,使用独立管理员认证与浏览器请求边界;不提供文件下载或其他任务写操作。
|
|
51
51
|
|
package/docs/release.md
CHANGED
|
@@ -16,17 +16,17 @@ VERSION=$(node -p "require('./package.json').version")
|
|
|
16
16
|
npm run test:install -- "./realqiyan-agent-task-$VERSION.tgz"
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
[安装验证](../scripts/verify-install.mjs)在独立临时目录执行真正的全局 npm 安装,禁用 lifecycle scripts,不依赖项目源码或编译器。它检查服务生命周期和认证,不调用模型或启动 ACP 工作;具体检查项以脚本为准。
|
|
20
20
|
|
|
21
21
|
输出报告只包含结果和证据目录,不包含 token;只将实际运行的宿主平台记为通过,其他平台明确记为未执行。临时目录权限为私有,内有验证专用随机凭据及空任务库,不上传这些目录。基础测试中的真实 PTY 用例先检查平台及 `script` 能否创建终端;能力不可用时明确跳过,不能把跳过当成终端验收通过。
|
|
22
22
|
|
|
23
23
|
脚本只验证当前环境,不在 Linux 上推断 macOS 行为;需要 macOS 证据时,在相应环境单独运行同一命令。本地可设置 `npm_config_cache` 复用下载缓存,tarball 验证可用 `npm_config_registry` 指定依赖镜像,报告会记录其地址;`--registry` 发布后验收固定使用 npmjs。不能以复制项目的 node_modules 替代安装验证。
|
|
24
24
|
|
|
25
|
-
npm
|
|
25
|
+
npm 接收发布后可能仍在处理包。[发布后验收](../scripts/verify-install.mjs)有限等待公开 `latest` 指向目标版本,再执行默认全局安装;等待只读取 registry。超时先核对版本状态,不重复上传。
|
|
26
26
|
|
|
27
27
|
## GitHub Actions 门禁与受控发布
|
|
28
28
|
|
|
29
|
-
- [Validate](../.github/workflows/ci.yml):PR/master 或手动触发时在 Ubuntu 执行 TypeScript 与依赖边界检查、基础测试及构建;检查包清单。安装任务**不 checkout 项目源码**,只接收 tarball 和验收脚本,在当前 Linux runner 验证启动、重启及认证检查。常规 CI
|
|
29
|
+
- [Validate](../.github/workflows/ci.yml):PR/master 或手动触发时在 Ubuntu 执行 TypeScript 与依赖边界检查、基础测试及构建;检查包清单。安装任务**不 checkout 项目源码**,只接收 tarball 和验收脚本,在当前 Linux runner 验证启动、重启及认证检查。常规 CI 使用 Linux runner,不能据此推断 macOS 验证结果。
|
|
30
30
|
- [Publish npm](../.github/workflows/publish.yml):推送 `v*` tag 自动触发,也保留手动入口;仅账号 realqiyan 可发起或重跑。[发布准入](../scripts/check-release.mjs)要求 tag 与包版本一致、锁文件版本一致,且提交已进入远端 master。手动入口仅接受 master 或版本 tag,输入版本须匹配。通过全部门禁后发布同一 tarball,不重建、不执行包脚本;上传前确认版本未占用,发布后核查 latest 并执行默认全局安装验证。
|
|
31
31
|
- 普通 CI 没有 npm 凭据。发布作业使用 npm Trusted Publisher 和 GitHub OIDC,不读取 `NPM_TOKEN`;并发发布被串行化。
|
|
32
32
|
|
|
@@ -70,6 +70,6 @@ EXPECTED_VERSION="$VERSION" node scripts/verify-install.mjs --registry
|
|
|
70
70
|
|
|
71
71
|
登录成功不等于发布授权已完成。若 registry 返回需要 2FA 的 E403,停止发布;维护者须在自己的终端完成发布时认证,或配置有目标包发布权限、短有效期且获准 bypass 2FA 的 granular token。不要关闭账号 2FA,不在聊天、命令参数或日志中传递一次性验证码或 token;不把本机登录凭据自动上传到 GitHub。
|
|
72
72
|
|
|
73
|
-
不得把 dry-run、上传成功提示或候选包安装当成 npmjs
|
|
73
|
+
不得把 dry-run、上传成功提示或候选包安装当成 npmjs 完成证据。验收结论须有 registry 版本与 latest、默认安装和平台报告作为证据,并注明未执行的检查;按 [AGENTS.md](../AGENTS.md#tests-and-documentation)合并维护,不追加发布经过。发布结果不明时先查询 registry;同一版本不可覆盖,不盲目重试,也不擅自换版本。
|
|
74
74
|
|
|
75
75
|
已发布候选有问题时,停止继续安装验证,保留证据并通知维护者决定弃用提示、tag 调整或新版本。不自动撤包、覆盖历史或删除用户数据。
|
package/docs/technical-design.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
| 责任 | 实现入口 | 基础验证 |
|
|
10
10
|
| --- | --- | --- |
|
|
11
|
-
| 任务定义、执行端快照、未来触发控制 | [TaskDefinitions](../src/tasks/definitions.ts)、[图校验](../src/tasks/graph.ts)、[时间规则](../src/tasks/schedule.ts) | [tasks.test.mjs](../tests/tasks.test.mjs) |
|
|
11
|
+
| 任务定义、执行端快照、未来触发控制 | [TaskDefinitions](../src/tasks/definitions.ts)、[图校验](../src/tasks/graph.ts)、[时间规则](../src/tasks/schedule.ts)、[Cron 解析](../src/tasks/cron.ts) | [tasks.test.mjs](../tests/tasks.test.mjs)、[schedule.test.mjs](../tests/schedule.test.mjs) |
|
|
12
12
|
| Run 与节点状态的业务修改入口、派发与提交顺序 | [TaskRuns](../src/tasks/runs.ts)、[数据模型](../src/tasks/model.ts)、[窄能力契约](../src/tasks/ports.ts) | [tasks.test.mjs](../tests/tasks.test.mjs)、[task-storage.test.mjs](../tests/task-storage.test.mjs) |
|
|
13
13
|
| 时钟、协调会话监督、取消与收尾 | [RunSupervisor](../src/tasks/supervisor.ts)、[NodeSessions](../src/tasks/sessions.ts) | [tasks.test.mjs](../tests/tasks.test.mjs)、[restart.test.mjs](../tests/restart.test.mjs) |
|
|
14
14
|
| 人工事实等待与回复 | [纯状态规则](../src/tasks/human-input.ts)、[TaskRuns](../src/tasks/runs.ts) | [human-input.test.mjs](../tests/human-input.test.mjs) |
|
|
@@ -22,15 +22,13 @@
|
|
|
22
22
|
| MCP、HTTP 与 Web | [MCP 工具](../src/protocol/mcp.ts)、[输出契约](../src/protocol/mcp-contract.ts)、[stdio 接入](../src/protocol/stdio.ts)、[HTTP 服务](../src/protocol/http/server.ts)、[Web 路由](../src/protocol/http/web.ts)、[页面](../src/web/) | [service.test.mjs](../tests/service.test.mjs)、[web.test.mjs](../tests/web.test.mjs) |
|
|
23
23
|
| CLI 与本机控制协议 | [CLI](../src/protocol/cli/commands.ts)、[管理客户端](../src/protocol/cli/admin-client.ts)、[启动器客户端](../src/integrations/local-service/control.ts) | [web.test.mjs](../tests/web.test.mjs)、[restart.test.mjs](../tests/restart.test.mjs) |
|
|
24
24
|
| 装配、启动、关闭与重启 | [main](../src/application/main.ts)、[MCP 后台启动](../src/application/mcp-entry.ts)、[业务运行实例](../src/application/task-runtime.ts)、[launcher](../src/application/launcher.ts)、[启动准备](../src/application/startup.ts) | [stdio.test.mjs](../tests/stdio.test.mjs)、[restart.test.mjs](../tests/restart.test.mjs)、[service.test.mjs](../tests/service.test.mjs) |
|
|
25
|
-
| 安装与发布 | [命令入口](../bin/agent-task.mjs)、[setup](../scripts/setup.mjs)、[安装库](../scripts/install-lib.mjs)、[包门禁](../scripts/pack-release.mjs)、[工作流](../.github/workflows/) | [install.test.mjs](../tests/install.test.mjs)、[setup.test.mjs](../tests/setup.test.mjs)、[发布说明](release.md) |
|
|
25
|
+
| 安装与发布 | [命令入口](../bin/agent-task.mjs)、[setup](../scripts/setup.mjs)、[安装库](../scripts/install-lib.mjs)、[包门禁](../scripts/pack-release.mjs)、[发布准入](../scripts/check-release.mjs)、[工作流](../.github/workflows/) | [install.test.mjs](../tests/install.test.mjs)、[setup.test.mjs](../tests/setup.test.mjs)、[release.test.mjs](../tests/release.test.mjs)、[发布说明](release.md) |
|
|
26
26
|
|
|
27
27
|
## 依赖与一致性边界
|
|
28
28
|
|
|
29
|
-
[依赖检查器](../scripts/check-boundaries.mjs)随 `npm run check`
|
|
29
|
+
依赖方向由[架构原则](architecture-guidelines.md#职责与依赖)定义,[依赖检查器](../scripts/check-boundaries.mjs)随 `npm run check` 执行。模块分类、允许引用及 Run 写入口检查以该脚本为准;新增文件须明确归属。
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
[TaskRuns](../src/tasks/runs.ts)持有 Run 写能力。定义服务不写 Run;监督器、模型工具、ACP 回调、超时、取消和人工回复通过运行服务的业务操作修改它。任务游标与 Run 仍使用同一事务,节点派发在创建 worker 前提交;异步结果返回后重新读取当前状态。缺少持久字段或非规范记录直接拒绝,不使用请求默认值修复。配置文件是管理员提供的下次启动输入,按配置请求契约解析;它不与 Task/Run 的持久记录契约共用默认值。
|
|
31
|
+
[TaskRuns](../src/tasks/runs.ts)是 Run 状态的唯一业务写入口。状态转换与提交顺序在该服务维护,事务实现在 [TaskStore](../src/tasks/storage/store.ts)。[持久记录校验](../src/tasks/storage/records.ts)严格拒绝缺失或非规范记录,不使用请求默认值修复;管理员提供的下次启动配置另用[配置请求契约](../src/protocol/settings-contract.ts)解析。
|
|
34
32
|
|
|
35
33
|
检查器约束源码依赖,不是权限沙箱,也不证明运行时副作用安全。真实模型和执行端的证据边界见[验证说明](validation.md)。
|
|
36
34
|
|
package/docs/validation.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 验证导航与证据边界
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
项目处于调研阶段,不宣称生产可用或外部副作用 exactly-once。以下结论只适用于所列版本、环境和用例;安装、协议连接与真实任务成功是不同证据,不能互相替代。
|
|
4
4
|
|
|
5
5
|
## 本地验证
|
|
6
6
|
|
|
@@ -10,36 +10,15 @@ npm test
|
|
|
10
10
|
git diff --check
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
基础测试使用真实本地文件、SQLite、进程和 SDK,不调用外部模型,不使用假模型或模拟 provider。各职责的测试入口统一见[代码导航](technical-design.md)。失败进程和 `no_model` 只证明失败路径;结构化状态断言不证明模型语义判断。静态依赖检查不证明运行时权限或外部副作用安全。
|
|
14
14
|
|
|
15
|
-
[MCP 服务测试](../tests/service.test.mjs)
|
|
16
|
-
|
|
17
|
-
2026-10-23,Linux x64、Node 24.15.0:使用流程简化通过类型与依赖边界检查、49 项基础测试和包清单门禁。重点证据:
|
|
18
|
-
|
|
19
|
-
- [stdio 进程测试](../tests/stdio.test.mjs):两个项目并发连接只保留一个后台;目录默认值、显式覆盖及跨项目请求键独立。关闭 stdin 和向客户端进程组发送 SIGHUP 后,后台仍触发未来任务。这些运行因 `no_model` 失败,只证明持续调度与无误派发,不证明 ACP 成功。
|
|
20
|
-
- [MCP 服务测试](../tests/service.test.mjs):没有原生 CLI 时拒绝连接,管理接口可用;安装到已有 PATH 后,下次连接重新发现。直接 HTTP 提交缺少目录时拒绝。
|
|
21
|
-
- [执行端测试](../tests/executors.test.mjs):原生 CLI 与适配器分开检测,禁用不安装,已有安装离线复用;真实失败安装进程不替换已发布组件,关闭时取消等待且不释放其他进程的锁。
|
|
22
|
-
- [初始化 PTY 测试](../tests/setup.test.mjs):目录、端口和令牌须确认,取消不写文件;协调员密钥隐藏输入,生成令牌不输出。初始化不安装执行端。
|
|
23
|
-
- [启动锁测试](../tests/sqlite.test.mjs):四个真实进程同时争用全新锁文件时,只有一个取得所有权;持锁进程强杀后可重新取得锁。修复前曾复现所有启动器同时争锁失败,修复后使用有限的错开重试,不触发任务重放。
|
|
24
|
-
- [存储测试](../tests/task-storage.test.mjs)与[任务测试](../tests/tasks.test.mjs):固定规范工作目录、按目录去重、严格记录拒绝而不修复、事务失败回滚、先持久派发意图再启动真实进程,以及关闭与迟到回调。
|
|
25
|
-
|
|
26
|
-
当前版本的真实模型证据见 [0.1.0-rc.1 复验](#010-rc1-复验)。本次未运行 macOS 验收。此前模块化重构的 42 项测试及临时源码副本中的依赖违规拒绝实验也已通过,属于本次改造前的证据。
|
|
15
|
+
[MCP 服务测试](../tests/service.test.mjs)通过真实 HTTP 和 SDK 校验器检查输入/输出契约、成功与失败响应及列表查询。契约不使用数组形式的 `type`,可空字段仍必填并接受 `null`;实现见 [mcp-contract.ts](../src/protocol/mcp-contract.ts)。这不替代曾报告契约警告的客户端复验,其名称和版本尚未提供。
|
|
27
16
|
|
|
28
17
|
## 控制台验证
|
|
29
18
|
|
|
30
|
-
[Web/CLI
|
|
31
|
-
|
|
32
|
-
[配置用例](../tests/settings.test.mjs)验证私有文件边界、旧编辑拒绝、显式凭据环境、端点与密钥引用分离、服务令牌别名不能作为模型凭据、失败时保留文件,以及关闭后拒绝操作。这些用例不构成断电、磁盘满或目录同步失败窗口的完整证据。
|
|
33
|
-
|
|
34
|
-
尚未完成真实浏览器交互、CSP 执行和跨浏览器缓存/自动填充验收;CLI 的编辑器交互和隐藏终端凭据输入也未做终端验收,凭据测试使用 stdin。需在浏览器核对:不可信任务文本不执行脚本或加载外链;离开、刷新和退出后无令牌残留;配置草稿不被轮询覆盖;密钥保存与配置保存的提示明确。HTTP 头检查不能替代这些验证,也不证明模型认证或 ACP 执行成功。
|
|
35
|
-
|
|
36
|
-
### 执行端与安装包
|
|
37
|
-
|
|
38
|
-
同日使用隔离 HOME,从 npm registry 真实安装 pi-acp 0.0.34、codex-acp 2.2.2 和 claude-agent-acp 0.94.0,再确认后续准备复用同一安装。报告位于 `/tmp/agent-task-adapter-install-T68wMA/report.json`。没有启动原生 CLI、发送 ACP 会话或调用模型;成功下载与准备不等于认证或任务成功。
|
|
19
|
+
[Web/CLI](../tests/web.test.mjs)、[配置](../tests/settings.test.mjs)和[重启](../tests/restart.test.mjs)测试覆盖真实本机服务的管理互通、旧编辑拒绝、凭据隔离、浏览器请求边界、配置生效与目录切换,以及重启后不确定派发仍被阻断。配置文件测试不构成断电、磁盘满或目录同步失败窗口的完整证据。
|
|
39
20
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
当前候选的发布状态见下文;历史包验收不代替当前 macOS、真实宿主应用或四种执行端的模型任务复验。
|
|
21
|
+
尚未完成真实浏览器交互、CSP 执行和跨浏览器缓存/自动填充验收。仍需核对不可信文本不执行脚本或加载外链、离开页面后无令牌残留、配置草稿不被轮询覆盖,以及密钥与配置保存的提示。HTTP 头检查不能替代这些验证。CLI 编辑器交互和隐藏终端凭据输入也未做终端验收,凭据测试使用 stdin。
|
|
43
22
|
|
|
44
23
|
## 真实模型验收入口
|
|
45
24
|
|
|
@@ -51,140 +30,56 @@ git diff --check
|
|
|
51
30
|
|
|
52
31
|
完整前置检查、参数和断言见 [E2E 用例](../tests/e2e/)、[Pi 启动器](../scripts/run-pi-human-e2e.mjs)。Pi 启动器为两端共用获准测试密钥,创建独立原生配置,只启用 read;这不是 OS 沙箱或独立账户隔离。强杀或断电不能保证临时认证清理,需管理员核查。
|
|
53
32
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
### 0.1.0-rc.1 复验
|
|
57
|
-
|
|
58
|
-
2026-10-23,Linux x64、Node 24.15.0,使用已有获准 DeepSeek 测试凭据、`deepseek-flash` 协调员、隔离的只读 Pi 原生配置及连接时安装的 pi-acp 0.0.34。现有单节点和三节点用例各执行一次,均通过;没有修改交付断言或重试任务。
|
|
59
|
-
|
|
60
|
-
- 单节点约 16.3 秒:真实 ACP 交付包含标识和结果 323,一个会话、一轮执行,结果文件持久化通过。证据:`/tmp/agent-task-real-e2e-Igj91J`;Run:`5d4f2b74-eb41-402c-874e-441b1fb29d91`。
|
|
61
|
-
- 三节点约 36.5 秒:测试客户端提供数量 4,结果 1292;同一 Run 等待并接受回复,节点轮数 1 / 2 / 1,已接受的上游不重放,三个交付均保留标识和必需数值。证据:`/tmp/agent-task-real-e2e-oreA3j`;Run:`3e6dd91b-35a5-4038-945c-c9d402d2829f`。
|
|
62
|
-
|
|
63
|
-
两项测试均在提交任务后断开客户端,再由新连接观察结果。原始测试日志:`/tmp/agent-task-pi-human-6N0Zlj/test-1.log`。服务正常退出,临时原生认证和服务环境文件已清空,任务、会话、结果及验收摘要保留。临时目录可能被系统清理,长期证据需安全归档。
|
|
64
|
-
|
|
65
|
-
此证据覆盖当前任务契约和适配器准备路径。系统级自启、真实 Pi 宿主退出、模型执行中重启及 Codex、Claude、OpenCode 模型任务未验收;stdio 后台生命周期由本地进程测试覆盖。只读配置不是 OS 沙箱。
|
|
66
|
-
|
|
67
|
-
### 本机三节点验收
|
|
68
|
-
|
|
69
|
-
2026-10-22,Linux x64、Node 24.15.0,使用 DeepSeek 协调员和只读 Pi ACP 执行端,此前带 JSON 格式约束的用例运行 `npm run test:e2e:human -- --quantity 4` 一次通过,约 28 秒。数量由测试客户端提供,结果为 1292,不是真人输入。
|
|
70
|
-
|
|
71
|
-
该次运行验证了固定 DAG 的真实 ACP 交付、人工等待后续接同一运行、同一 ACP 会话内澄清,以及已接受上游不重放。未执行独立单节点用例、真人回答文件模式或重启/故障恢复。
|
|
72
|
-
|
|
73
|
-
复核业务库、durable 记录和 Pi 原生会话后,确认只有一个 TaskRun、三个 ACP 会话和两次上游文件读取。`audit` 的工具前说明与最终 JSON 被聚合进同一轮输出,导致额外一轮格式澄清;没有重复读取上游文件。
|
|
74
|
-
|
|
75
|
-
执行端启动时还留下了三次 `npm view @earendil-works/pi-coding-agent version` 记录,来源是 `pi-acp` 的版本检查,Pi 子进程的 `--offline` 未阻止该行为。日志没有查询结果,不能确认 registry 请求是否完成。服务和执行端原始 stderr、完整 ACP 通信未保留,不能据此证明整个执行栈无其他外部访问或诊断错误。
|
|
76
|
-
|
|
77
|
-
测试日志:`/tmp/agent-task-pi-human-H1OKJO/test-1.log`;Pi 原生会话及 npm 日志位于同目录的 `home/`;运行证据:`/tmp/agent-task-real-e2e-O9N9RY`。临时原生认证已清空。临时文件可能被系统清理,长期留存需管理员安全归档。
|
|
78
|
-
|
|
79
|
-
### 文本交付复验
|
|
80
|
-
|
|
81
|
-
同日移除用例中的 JSON 格式要求和固定澄清口令,以相同命令复验一次,约 26 秒。三个节点均输出普通文本或 Markdown,轮数为 1 / 2 / 1;没有为了格式追加澄清,计算结果仍为 1292。
|
|
82
|
-
|
|
83
|
-
但测试未通过:`audit` 原始输出包含必需标识,协调员接受的片段遗漏了该标识,验收理由仍声称包含它,运行被标为 `succeeded`。[三节点用例](../tests/e2e/human-dag.test.mjs)的内容断言拦住了这一遗漏,没有降低断言或重跑。此结果说明文本传递可执行,但不能把运行成功状态当成完整交付的证据;随后补充了摘录完整性指令,后续复验见下节。交付格式原则见[总体方案](design-proposal.md#架构选择)。
|
|
84
|
-
|
|
85
|
-
测试日志:`/tmp/agent-task-pi-human-K8huNN/test-1.log`;运行证据:`/tmp/agent-task-real-e2e-XgRgeo`。旧记录保留,临时原生认证已清空。
|
|
86
|
-
|
|
87
|
-
### 模块化重构后复验
|
|
88
|
-
|
|
89
|
-
2026-10-23,Linux x64、Node 24.15.0,使用 DeepSeek `deepseek-flash` 协调员、独立只读 Pi 配置和 pi-acp 0.0.34。两项现有用例各执行一次,均通过;没有修改用例断言或重复运行失败任务。
|
|
33
|
+
### 已验证结论
|
|
90
34
|
|
|
91
|
-
|
|
92
|
-
| --- | --- |
|
|
93
|
-
| 三节点 DAG 与人工回复 | `npm run test:e2e:human -- --quantity 4`,约 31.4 秒。一个 TaskRun、三个 ACP 会话,节点轮数为 1 / 2 / 1。测试客户端提供数量 4,结果为 1292;等待约 2.1 秒后续接原 Run,在同一 `total` 会话内澄清,已接受的 `price` 记录保持不变。三个交付均保留必需标识,内容断言通过。 |
|
|
94
|
-
| 单节点 | 复用同一隔离配置的临时启动器运行现有 `deepseek-acp.test.mjs`,约 7.6 秒。一个 TaskRun、一个 ACP 会话、一轮执行,接受包含标识和结果 323 的交付,验证结果文件已持久化。 |
|
|
35
|
+
`0.1.0-rc.1`:2026-10-23,Linux x64、Node 24.15.0,DeepSeek `deepseek-flash` 协调员、隔离的只读 Pi 原生配置及连接时安装的 pi-acp 0.0.34。单节点和三节点 DAG 用例均通过内容断言:
|
|
95
36
|
|
|
96
|
-
|
|
37
|
+
| 范围 | 结论 | 证据目录 |
|
|
38
|
+
| --- | --- | --- |
|
|
39
|
+
| 单节点 | 真实 ACP 交付保留必需标识与数值,结果文件持久化成功 | `/tmp/agent-task-real-e2e-Igj91J` |
|
|
40
|
+
| 三节点 DAG 与人工回复 | 等待后续接同一 Run,在同一节点会话内澄清;已接受上游不重放,文本交付保留必需内容 | `/tmp/agent-task-real-e2e-oreA3j` |
|
|
97
41
|
|
|
98
|
-
|
|
99
|
-
- 单节点日志:`/tmp/agent-task-pi-single-lseufC/test-1.log`;证据:`/tmp/agent-task-real-e2e-iV24X1`;Run:`7320bfda-3968-4305-acf6-10d7a9dae4e7`。
|
|
100
|
-
- 两个证据目录均保留 `acceptance-summary.json`、`record-audit.json`、业务 SQLite、运行快照及事件记录。临时目录可能被系统清理,长期留存需管理员安全归档。
|
|
42
|
+
两项用例均在提交后断开客户端,再由新连接观察结果。原始测试日志:`/tmp/agent-task-pi-human-6N0Zlj/test-1.log`。数量来自测试客户端,不是真人文件回复。此证据不代表后续版本已完成模型复验,也不覆盖 Codex、Claude、OpenCode 的模型任务、真实宿主退出或模型执行中重启。
|
|
101
43
|
|
|
102
|
-
|
|
44
|
+
### 必须保留的限制
|
|
103
45
|
|
|
104
|
-
|
|
46
|
+
- **运行成功状态不证明交付完整。** 文本交付验收曾发现:原始输出含必需标识,但协调员接受的摘录遗漏标识,仍将运行标为 `succeeded`;内容断言拒绝了该结果。后续通过不能证明模型始终完整摘录。反例证据:`/tmp/agent-task-real-e2e-XgRgeo`。
|
|
47
|
+
- **原生只读配置和 `--offline` 不证明整个执行栈无外部访问。** Pi ACP 验收留下适配器启动时的 `npm view` 版本检查记录;日志没有查询结果,不能确认 registry 请求是否完成。原生记录:`/tmp/agent-task-pi-human-lpMTR3/home/`。原始 stderr 与完整 ACP 通信未保留,也不能据此排除其他访问或诊断错误。
|
|
48
|
+
- **检查点不保证外部副作用 exactly-once。** 早期 durable 的真实 SQLite/文件/SIGKILL 研究曾观察到,外部写入后、结果提交前崩溃会使恢复重复写入。这不是 ACP 恢复验收;原记录未提供实验附件链接,不能据此扩大恢复保证。
|
|
105
49
|
|
|
106
|
-
|
|
50
|
+
## 执行端准备与协议连接
|
|
107
51
|
|
|
108
|
-
|
|
52
|
+
Linux x64、Node 24.15.0 的隔离环境证据:
|
|
109
53
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
Linux x64、Node 24.15.0 的本机记录:
|
|
115
|
-
|
|
116
|
-
- 专属目录安装、重复安装及带 token 的 MCP 健康检查通过。
|
|
117
|
-
- Pi 1.3.0 + pi-acp 0.0.34、codex-acp 2.2.2、OpenCode 1.19.3 在独立无认证 HOME 下均通过 initialize;没有发送 session/new 或模型任务,也未自动批准执行端。
|
|
118
|
-
- 首次安装实验遭遇磁盘满并损坏实验清单;未删除任务数据修复。修正后在新的专属目录完成上述部署复验,不构成业务运行时磁盘满恢复的证据。
|
|
119
|
-
|
|
120
|
-
证据目录:`/tmp/agent-task-deploy-verified`、`/tmp/agent-task-deploy-final`;工作空间:`/tmp/agent-task-deploy-workspace`;空认证 HOME:`/tmp/agent-task-deploy-native-home`。临时文件可能被系统清理,长期留存需管理员安全归档。
|
|
54
|
+
| 范围 | 结论与条件 | 证据 |
|
|
55
|
+
| --- | --- | --- |
|
|
56
|
+
| 适配器安装 | pi-acp 0.0.34、codex-acp 2.2.2、claude-agent-acp 0.94.0 从 npm registry 安装并复用成功;未启动原生 CLI 或模型 | `/tmp/agent-task-adapter-install-T68wMA/report.json` |
|
|
57
|
+
| ACP initialize | Pi 1.3.0 + pi-acp 0.0.34、codex-acp 2.2.2、OpenCode 1.19.3 在无认证 HOME 下通过;未发送 `session/new` 或模型任务 | `/tmp/agent-task-deploy-verified`、`/tmp/agent-task-deploy-final` |
|
|
121
58
|
|
|
59
|
+
下载成功与 initialize 不证明原生认证、工具授权或任务成功。
|
|
122
60
|
|
|
123
61
|
## npm 发布候选验证
|
|
124
62
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
本候选包含 stdio 共用后台、本机执行端发现与适配器准备、固定任务目录和按目录去重,以及调度周期调整。初始化须确认目录、端口、令牌及协调模型;普通启动不转换旧配置或业务数据。
|
|
128
|
-
|
|
129
|
-
2026-10-23,Linux x64、Node 24.15.0:锁定依赖重装、类型与依赖边界检查、49 项基础测试、两项真实模型测试、包清单门禁及独立安装验收均通过。真实模型范围见 [0.1.0-rc.1 复验](#010-rc1-复验)。
|
|
130
|
-
|
|
131
|
-
本地候选包安装报告:`/tmp/agent-task-npm-SxC74r/report.json`。使用独立 prefix 与原生 HOME,不依赖源码或 TypeScript;前台、Web/CLI 重启、stdio 自动启动、断开后后台健康和 stop 均通过。此次依赖下载使用 npm 镜像;发布后的默认安装须另以 npmjs 验证。报告仅把本机 Linux 记为通过,未执行 macOS 验收。发布完成以工作流结果和 registry 核对为准。
|
|
132
|
-
|
|
133
|
-
[GitHub 发布运行 40005739943](https://github.com/realqiyan/agent-task/actions/runs/40005739943)验证提交 `b6d138d`:Node 22.19.0/24.x 检查与 49 项测试、包门禁及 Linux 独立安装全部通过。GitHub OIDC 发布步骤成功,npm 随后提示包正在处理;即时 latest 检查仍读到旧版本,导致该运行最终失败。此失败发生在发布后的校验阶段。
|
|
134
|
-
|
|
135
|
-
后续匿名 npmjs 核对确认 **[@realqiyan/agent-task@0.1.0-rc.1](https://www.npmjs.com/package/@realqiyan/agent-task/v/0.1.0-rc.1)** 存在、`latest=0.1.0-rc.1`,并包含 GitHub provenance。未指定版本的默认全局安装及前台、Web/CLI、stdio 后台验收通过,报告:`/tmp/agent-task-npm-lTc9FG/report.json`。捕获输出未发现验证凭据,无模型或 ACP 工作。发布后校验现已加入有限等待和 `verify_only` 入口,后续复核无需再次上传同一版本。
|
|
136
|
-
|
|
137
|
-
[仅验证运行 40006683937](https://github.com/realqiyan/agent-task/actions/runs/40006683937)在提交 `3e8e7cd` 上全部通过:Node 22.19.0/24.x 检查和 49 项测试、打包、Linux 独立安装及 npmjs 默认安装复核。该运行跳过发布上传,未重复发布同一版本。
|
|
138
|
-
|
|
139
|
-
### 0.0.1-rc.3(历史证据)
|
|
140
|
-
|
|
141
|
-
2026-10-23,[GitHub 发布运行 39980466554](https://github.com/realqiyan/agent-task/actions/runs/39980466554)验证提交 `80b12de`:Node 22.19.0/24.x 类型与依赖检查、基础测试、包清单门禁,以及当前 Linux runner 的独立安装、启动、重启和认证检查均通过。当前发布门禁验证宿主环境;macOS 需在对应环境另行验收。安装报告明确标记其他平台未执行,PTY 用例在缺少终端能力时明确跳过。
|
|
142
|
-
|
|
143
|
-
发布作业使用 Node 24.21.0、npm 11.19.0 和 GitHub OIDC。包版本检查及 provenance 签名成功,但上传包的 npm PUT 请求返回 E404;发布后的默认安装验证因此未执行。随后 registry 查询未发现 `0.0.1-rc.3`,不能把生成 provenance 当作包已发布。
|
|
144
|
-
|
|
145
|
-
错误指向 npm 发布授权未生效,需核对该包的 Trusted Publisher 与仓库、工作流、Environment 和允许操作是否匹配。本机凭据读取 trust 设置返回 403,尚未确认具体缺失或不匹配项。GitHub 的 `npm` Environment 已限制为 master;工作流配置与 npm 端授权仍需分别核实,见[发布说明](release.md)。
|
|
146
|
-
|
|
147
|
-
本机检查与 42 项基础测试通过;独立空工具 PATH 下的真实测试进程确认 PTY 用例明确跳过。发布诊断及已去除下载 URL 查询参数的日志保留于 `/tmp/agent-task-rc3-final-d4tqqpt9`,临时目录可能被系统清理。
|
|
148
|
-
|
|
149
|
-
2026-10-23 后续匿名 registry 核对确认 `0.0.1-rc.3` 已存在,发布元数据包含 provenance;当时 `latest=0.0.1-rc.3`。上述失败运行是历史记录,不代表后续 registry 状态,也不能单凭元数据判定最终成功发布的操作渠道。
|
|
150
|
-
|
|
151
|
-
### 0.0.1-rc.2(历史证据)
|
|
152
|
-
|
|
153
|
-
2026-10-22,`realqiyan` 已将 **[@realqiyan/agent-task@0.0.1-rc.2](https://www.npmjs.com/package/@realqiyan/agent-task/v/0.0.1-rc.2)** 发布到 npmjs。匿名 registry 查询确认版本、发布账号、MIT 许可证及 `latest=0.0.1-rc.2`。本机直接发布了下述 CI 已验收的同一 tarball,没有重新构建,也没有 GitHub provenance。
|
|
154
|
-
|
|
155
|
-
- [Validate 运行 39829900075](https://github.com/realqiyan/agent-task/actions/runs/39829900075)在提交 `48549ea` 上全部通过:Node 22.19.0/24.x 类型检查、35 项基础测试、构建和包清单门禁。
|
|
156
|
-
- Ubuntu x64 与 macOS ARM64 的 Node 22.19.0 安装任务不 checkout 源码,使用同一 CI tarball 验证前台、Web/CLI 重启、启动目录/端口/token 变量名切换、认证健康检查。macOS 证据来自 hosted runner,不包括 Intel Mac、浏览器、休眠或用户注销场景。
|
|
157
|
-
- 发布后在 Linux x64、Node 24.15.0 以独立空 npm 配置匿名执行默认 `npm install -g @realqiyan/agent-task`,未指定版本或使用本地 tarball;固定使用 npmjs。前台、Web HTTP 配置、重启、健康检查均通过。
|
|
158
|
-
|
|
159
|
-
发布后安装报告:`/tmp/agent-task-npm-vseswA/report.json`。捕获输出未发现验证凭据,测试服务已移除。没有模型调用或 ACP 任务,不替代原生认证、权限或真实模型复验。临时目录可能被系统清理;包内文档是打包时快照,后续证据以本仓库为准。
|
|
160
|
-
|
|
161
|
-
### 0.0.1-rc.1(历史证据)
|
|
162
|
-
|
|
163
|
-
2026-10-22,`realqiyan` 已将 **[@realqiyan/agent-task@0.0.1-rc.1](https://www.npmjs.com/package/@realqiyan/agent-task/v/0.0.1-rc.1)** 发布到 npmjs。匿名 registry 查询确认版本、发布账号、MIT 许可证及 `latest=0.0.1-rc.1`。本次经授权从本机发布,没有 GitHub provenance。包内文档是打包时快照,后续证据以[仓库中的最新记录](https://github.com/realqiyan/agent-task/blob/master/docs/validation.md#npm-发布候选验证)为准。
|
|
164
|
-
|
|
165
|
-
npm 因包名保护拒绝了未加 scope 的 `agent-task`:名称被判定与 `agent-base` 过于相似。维护者已明确批准使用 `@realqiyan/agent-task`,安装命令随之改为 `npm install -g @realqiyan/agent-task`,CLI 仍为 `agent-task`。未关闭账号 2FA,也未上传本机凭据。
|
|
166
|
-
|
|
167
|
-
本次 Linux x64 证据:
|
|
168
|
-
|
|
169
|
-
- Node 22.19.0 和 24.15.0 的 TypeScript 检查、33 项基础测试及构建均通过;真实 PTY 用例验证 setup 的确认/取消、生成独立 token 和不输出凭据。
|
|
170
|
-
- [包清单门禁](../scripts/pack-release.mjs)通过:仅包含预编译 JS、Web 资源、明确列出的操作脚本、示例及公开文档;不包含源码、开发工具、凭据文件、运行数据库或本地记录。
|
|
171
|
-
- Node 22.19.0 先验收 scoped tarball,再从 npmjs 按 `npm install -g @realqiyan/agent-task` 的默认版本选择全局安装到独立临时 prefix;没有指定版本或使用本地 tarball 替代 registry 安装。禁用 lifecycle scripts,不使用项目源码或 TypeScript。前台启动、Web HTTP 配置保存/重启及带认证的健康检查均通过。
|
|
172
|
-
- tarball 预验收使用 npm 镜像依赖;发布后验收固定使用 `https://registry.npmjs.org/`,通过独立空 npm 配置文件进行匿名安装。复用 npm 下载缓存,没有复制项目 node_modules;不构成空缓存下载时延的保证。
|
|
63
|
+
发布与安装操作由[发布说明](release.md)维护。此处只保留有适用范围的证据,不逐版记录发布过程。
|
|
173
64
|
|
|
174
|
-
|
|
65
|
+
| 范围 | 结论 | 证据 |
|
|
66
|
+
| --- | --- | --- |
|
|
67
|
+
| `0.1.0-rc.2`,提交 `7ffed5c` | Node 22.19.0/24.x 基础检查、包门禁、Linux 独立安装、GitHub OIDC 发布及 npmjs 默认安装验收通过;公开包有 provenance | [发布运行](https://github.com/realqiyan/agent-task/actions/runs/40067373860)、[npm 版本](https://www.npmjs.com/package/@realqiyan/agent-task/v/0.1.0-rc.2) |
|
|
68
|
+
| 同版本,Linux x64、Node 24.15.0 | 从 npmjs 默认安装到独立 prefix,通过前台、Web/CLI 重启、stdio 后台、断开后健康及 stop 检查;捕获输出未发现验证凭据 | `/tmp/agent-task-npm-qiT8oy/report.json` |
|
|
69
|
+
| `0.0.1-rc.2`,提交 `48549ea`,历史平台证据 | Ubuntu x64 与 macOS ARM64 hosted runner 在 Node 22.19.0 下使用 CI tarball、无源码完成安装、前台、重启及认证检查 | [Validate 运行](https://github.com/realqiyan/agent-task/actions/runs/39829900075) |
|
|
175
70
|
|
|
176
|
-
|
|
71
|
+
`0.1.0-rc.2` 的发布验收未执行 macOS、浏览器或真实模型/ACP 任务;旧 macOS 证据不覆盖当前版本、Intel Mac、休眠或用户注销。安装检查使用真实失败程序提供可发现路径,不证明成功 ACP 执行。PTY 能力不可用时测试明确跳过,跳过不是通过。
|
|
177
72
|
|
|
178
|
-
|
|
73
|
+
上述发布状态是验收时的事实,不是实时 registry 查询;包内文档是发布提交的快照。
|
|
179
74
|
|
|
180
|
-
|
|
75
|
+
## 证据保管与未验证边界
|
|
181
76
|
|
|
182
|
-
|
|
77
|
+
本地证据目录可能含敏感的任务、会话和原生配置,不上传到 Git 或 npm,也不自动删除。`/tmp` 路径是原始证据定位,不保证仍存在;需长期复核时由管理员安全归档。测试入口说明可验证什么,不等于已经执行。
|
|
183
78
|
|
|
184
|
-
|
|
185
|
-
- 各执行端原生权限、认证续期、沙箱、技能/MCP 发现及文件访问;其他模型和 OpenAI 兼容端点的协议差异。
|
|
186
|
-
- 真实模型崩溃窗口、重复收费和预算行为;人工等待期间保留真实 ACP 会话的续答、并发回复、长时间等待及重启。
|
|
187
|
-
- 系统级强杀、断电、业务库磁盘满、真实任务的长期监督和原生权限。
|
|
188
|
-
- SDK 升级、历史数据转换,以及执行中修改计划等当前未开放能力。
|
|
79
|
+
除各节限制外,以下尚未完成真实验收:
|
|
189
80
|
|
|
190
|
-
|
|
81
|
+
- 复杂任务语义、并行分支与汇合、跨执行端交付;其他模型及 OpenAI 兼容端点差异。
|
|
82
|
+
- 各执行端原生权限、认证续期、沙箱、技能/MCP 发现及文件访问边界。
|
|
83
|
+
- 真人回答文件模式、并发回复,以及长时间等待或重启后的真实 ACP 会话续答。
|
|
84
|
+
- 真实模型崩溃窗口、重复收费和预算行为;系统级强杀、断电、业务库磁盘满、系统自启及真实任务长期监督。
|
|
85
|
+
- SDK 升级和历史数据转换;[当前未开放能力](design-proposal.md#当前范围与暂缓能力)不因已有测试通过而获得保证。
|
package/docs/web-console.md
CHANGED
|
@@ -19,8 +19,7 @@
|
|
|
19
19
|
- 任务列表、固定 DAG、运行历史、节点进度、已接受交付、错误和问题只读。触发计划结束不等于运行成功,原始输出不等于交付。
|
|
20
20
|
- 任务写操作仅复用 `task_update`,暂停、恢复或取消未来触发。不停止当前运行,不补跑错过的触发,不回答问题或修改 DAG。已有不确定外部结果的任务拒绝恢复未来触发;当前不提供人工核对后强制恢复能力。
|
|
21
21
|
- 协调模型是常规配置入口。数据目录、监听端口、MCP token 环境变量名和执行端启停位于高级设置,下次启动生效。修改 token 变量名保留令牌值,不在页面回显。
|
|
22
|
-
-
|
|
23
|
-
- 无须手工注册执行端、设置默认工作目录或另行批准。原生认证和权限继续用官方工具管理;任务工作目录通过 MCP 上下文或创建参数确定。
|
|
22
|
+
- 执行端页面显示本机路径、准备结果和错误,提供重新检测与显式适配器升级。就绪不代表原生认证或工具权限已经验收;安装、更新及原生环境边界见[部署说明](deployment.md#执行端发现与准备)。
|
|
24
23
|
|
|
25
24
|
页面与接口入口见 [web.ts](../src/protocol/http/web.ts)、[页面模块](../src/web/),任务契约见 [task-contract.ts](../src/protocol/task-contract.ts)。提供受管理员认证保护的重启入口;没有通用 MCP 代理、文件浏览器或下载接口。固定结果目录不授予整个 dataDir 的读取能力。
|
|
26
25
|
|
|
@@ -34,8 +33,6 @@
|
|
|
34
33
|
|
|
35
34
|
待回答运行也可能持有 ACP 会话,重启不保证无损续接。执行端未就绪、被禁用或本机路径与任务快照不一致时,旧任务的派发会被拒绝;页面不改写快照、DAG 或执行端指派。需要新任务时由 MCP 调用方处理。
|
|
36
35
|
|
|
37
|
-
重新检测和适配器升级直接作用于当前执行端目录,无需重启;它们不保存启动配置。普通刷新复用已经准备的适配器,只有显式升级才请求更新。升级期间暂停向该执行端派发新工作,完成后可在状态表核对结果。OpenCode 使用原生 ACP,通过原生 CLI 的安装渠道更新。
|
|
38
|
-
|
|
39
36
|
Web 与 CLI 的旧编辑都会被拒绝,不做自动合并。离线编辑配置须先停服。校验与保存职责见 [settings.ts](../src/configuration/settings.ts)、[配置影响预览](../src/configuration/management.ts)、[settings-contract.ts](../src/protocol/settings-contract.ts)、[配置文件适配](../src/configuration/files.ts)。
|
|
40
37
|
|
|
41
38
|
## 安全与验收
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@realqiyan/agent-task",
|
|
3
|
-
"version": "0.1.0-rc.
|
|
3
|
+
"version": "0.1.0-rc.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "realqiyan",
|
|
6
6
|
"repository": {
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
"@earendil-works/pi-ai": "1.3.0",
|
|
62
62
|
"@earendil-works/pi-durable": "1.3.0",
|
|
63
63
|
"@modelcontextprotocol/sdk": "1.32.1",
|
|
64
|
+
"cron-parser": "5.10.2",
|
|
64
65
|
"express": "5.3.0",
|
|
65
66
|
"zod": "4.6.7"
|
|
66
67
|
},
|