@tea-agent/loop-agent 0.43.0-next.13 → 0.43.0-next.15
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 +5 -0
- package/dist/build-stamp.json +3 -3
- package/dist/worker/console/chat/browser-automation.js +363 -0
- package/dist/worker/console/chat/browser-routes.js +36 -14
- package/dist/worker/console/chat/pi-runtime/custom-tools/browser-tools.js +89 -0
- package/dist/worker/console/chat/pi-runtime.js +9 -1
- package/dist/worker/console/chat/routes.js +6 -1
- package/dist/worker/console/chat/session-mode.js +1 -0
- package/dist/worker/console/chat/tools.js +4 -0
- package/dist/worker/console/console-update-and-init.js +1 -19
- package/dist/worker/console/console-update-runtime.js +5 -20
- package/dist/worker/console/operator-actions.js +13 -1
- package/dist/worker/console/routes.js +8 -0
- package/dist/worker/console/server.js +9 -1
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-BEg5712w.js → abnfDiagram-N423BO3Z-CQE0d0CU.js} +1 -1
- package/dist/worker/console/static/assets/{arc-CkQvaBGZ.js → arc-BFYYG0Wz.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-BrdwFNJ7.js → architectureDiagram-T3A2C74G-B0YlJL9r.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-M_LjP2kx.js → blockDiagram-VBNYF7ZC-DN9kD3TO.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-Bwr5Dtjc.js → c4Diagram-5PPSVZJV-ChK60XBH.js} +1 -1
- package/dist/worker/console/static/assets/channel-BF5AdCat.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-CDc44stW.js → chunk-2GRJ4B5K-D4AjoNa-.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DZELwsTt.js → chunk-2Q5K7J3B-B2HEK_D9.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-D7YI6SWX.js → chunk-5RXB4S5H-DXfZkTa0.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-Bc46bEyj.js → chunk-5VM5RSS4-DC1FyC_o.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-Bj4vc-Dp.js → chunk-6Q2QTUOP-U2E9RJkG.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-DRi-0nDX.js → chunk-GF5L2VYU-BeMabzLg.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-Dr-wFY60.js → chunk-JWPE2WC7-CWvf3gKN.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-CqIHE_D7.js → chunk-KBJHAD2P-BQE1P4so.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-C-Dl8wzk.js → chunk-RYQCIY6F-DobDWQXh.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CV4Mydz3.js → chunk-XXDRQBXY-IQe7Y7EH.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BFdnxKyb.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BFdnxKyb.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-C3MamSMJ.js → cose-bilkent-JH36ORCC-CyDcJU1F.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-zo_Jy2sf.js → cynefin-VYW2F7L2-CDt8Uivk.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-BB2PN1AG.js → cynefinDiagram-MW4NZA55-BmFT7svy.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BimYueoi.js → dagre-VZM6K2ZE-hRf4KDfM.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Di1ObJRS.js → diagram-7IWD3JNH-CaDSdUs-.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-C83l-IJx.js → diagram-B4RE2ZJO-Dw13MbVk.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-B3S3fOpm.js → diagram-LBJQPF4R-TLaO5F4A.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-urW_Zhe7.js → diagram-Q27KOJAE-B-YdjvlA.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-nvIbCwF6.js → diagram-UB23O5K3-DHR7IqG6.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DG6O3KpP.js → ebnfDiagram-BXEA7PRR-C5v-LKCD.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DpXdtdsj.js → erDiagram-JOGREHBK-7tPJ992C.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-DvXWHrND.js → flowDiagram-UKHOOZJN-CdeV10q2.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-6F7SbvUS.js → ganttDiagram-PKOTCBZU-Nuod51KB.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-D1tZnFec.js → gitGraphDiagram-DS77QQ5N-CiHmujkA.js} +1 -1
- package/dist/worker/console/static/assets/{index-SAANIK8g.css → index-BXrp_jW4.css} +1 -1
- package/dist/worker/console/static/assets/{index-CWarwRqc.js → index-BnZTkOuj.js} +96 -96
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-HN4qwJ2M.js → infoDiagram-6WML65LV-BsbKAEaZ.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-OJ4qwDRo.js → ishikawaDiagram-WSZJBQD7-BMg44jAM.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-CJZ6giQk.js → journeyDiagram-NVQOT4AX-B2HydpC5.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-co60m19S.js → kanban-definition-27J2QSJJ-Dym8tKBH.js} +1 -1
- package/dist/worker/console/static/assets/{linear-D1KeFqZY.js → linear-C2XLUUy6.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-i7dDGqov.js → mermaid.core-CWKyfsl6.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-1SkQxBh1.js → mindmap-definition-FAOFIHXS-D1kp3mJa.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-B10dhI-Y.js → pegDiagram-VL7TDLO6-yChJn9Q6.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-CiUKUo6j.js → pieDiagram-7S7Q4E2Y-BL0gor4I.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-C5vH4LXZ.js → quadrantDiagram-CIZ2JOQS-BQlU54QR.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-5nlWOCEj.js → railroadDiagram-AXF67PYL-CFaiPP19.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-CILd6QS3.js → requirementDiagram-LRYGKXZP-3ZmKxDyQ.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Dfp18ZO2.js → sankeyDiagram-W5VNT64P-C6ZDqbFT.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-B9pkswBi.js → sequenceDiagram-SI44F4Z6-DdB0hg2W.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-C1-eSgx4.js → sizeCapture-X5ZJPWSS-BdaLiAnv.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-COJsOoAF.js → stateDiagram-OKZ733FA-D75bWtSk.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BBtfTPWx.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-Dv5NXJUM.js → swimlanes-SLNWSIFB-BPhv358R.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Cso4j_rK.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-DRdrZ7eR.js → timeline-definition-Z64GVDOM-hrXh6Jie.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-0-2TlwCj.js → vennDiagram-T6HMQDX7-CxjAfZu8.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-3-o3q92P.js → wardleyDiagram-T6FBY63Y-C4cHKlnQ.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-DSgOgdUi.js → xychartDiagram-ELKLHX3M-388ITGyq.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/operator-chat/chat-scroll-position.js +24 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +14 -3
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +36 -50
- package/dist/worker/console/workspace-context.js +6 -0
- package/dist/worker/console/workspace-initialization.js +174 -0
- package/package.json +1 -3
- package/dist/commands/new-task.js +0 -5
- package/dist/commands/status.js +0 -57
- package/dist/commands/task-contract.js +0 -269
- package/dist/shared/prompts.js +0 -26
- package/dist/task/config.js +0 -2
- package/dist/task/contract/validate-draft.js +0 -106
- package/dist/task/goal.js +0 -3
- package/dist/task/index.js +0 -12
- package/dist/task/lifecycle.js +0 -1
- package/dist/task/operator/capabilities.js +0 -6
- package/dist/task/operator/envelope.js +0 -2
- package/dist/task/operator/index.js +0 -5
- package/dist/task/operator/registry.js +0 -2
- package/dist/task/operator/types.js +0 -1
- package/dist/task/paths.js +0 -1
- package/dist/task/source-state.js +0 -1
- package/dist/task/subagent-guidance.js +0 -1
- package/dist/worker/console/chat/sift-bridge.js +0 -1
- package/dist/worker/console/index.js +0 -20
- package/dist/worker/console/static/assets/channel-CKmyKgY-.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-D8Wc3jJP.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-D8Wc3jJP.js +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Bpfb0q5s.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BEwGGabW.js +0 -8
- package/dist/worker/scheduler/index.js +0 -25
- package/dist/workflows/dag/facts.js +0 -4
- package/dist/workflows/dag/index.js +0 -6
- package/dist/workflows/dynamic/index.js +0 -7
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { lstat, readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { acquireUpdateLock, updateLockPath } from "../../shared/update/state.js";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import { TERMINAL_OPERATION_STATES } from "../../infrastructure/console/operation-store.js";
|
|
6
|
+
import { readInitRuntimeActivity } from "../observability/init-runtime-activity.js";
|
|
7
|
+
const record = (value) => {
|
|
8
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
9
|
+
throw new Error("初始化检查返回了无效数据");
|
|
10
|
+
return value;
|
|
11
|
+
};
|
|
12
|
+
const entries = (value) => {
|
|
13
|
+
if (!Array.isArray(value))
|
|
14
|
+
throw new Error("初始化检查缺少状态清单");
|
|
15
|
+
return value.map(record);
|
|
16
|
+
};
|
|
17
|
+
/** A single registered workspace owns checks, CLI cwd and durable operations. */
|
|
18
|
+
export class WorkspaceInitializationService {
|
|
19
|
+
options;
|
|
20
|
+
cached;
|
|
21
|
+
pending;
|
|
22
|
+
activeOperationId;
|
|
23
|
+
generation = 0;
|
|
24
|
+
constructor(options) {
|
|
25
|
+
this.options = options;
|
|
26
|
+
}
|
|
27
|
+
snapshot(state, message, pendingPaths = []) {
|
|
28
|
+
const { workspaceId, name, repoRoot } = this.options;
|
|
29
|
+
return { workspaceId, name, displayPath: repoRoot, state, message, pendingPaths };
|
|
30
|
+
}
|
|
31
|
+
async jsonFile(relative) {
|
|
32
|
+
const file = path.join(this.options.repoRoot, relative);
|
|
33
|
+
try {
|
|
34
|
+
const stat = await lstat(file);
|
|
35
|
+
if (!stat.isFile() || stat.isSymbolicLink())
|
|
36
|
+
throw new Error(`${relative} 不是普通配置文件`);
|
|
37
|
+
return record(JSON.parse(await readFile(file, "utf8")));
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
if (error.code === "ENOENT")
|
|
41
|
+
return undefined;
|
|
42
|
+
throw new Error(`${relative} 无法读取,请检查文件内容与权限`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async command(args, name) {
|
|
46
|
+
const result = await this.options.client.run(args, {
|
|
47
|
+
cwd: this.options.repoRoot, artifactName: `workspace-init-${name}`,
|
|
48
|
+
expectJson: true, timeoutMs: 120_000,
|
|
49
|
+
});
|
|
50
|
+
if (!result.ok || result.exitCode !== 0 || result.timedOut || result.parseFailure) {
|
|
51
|
+
throw new Error(`工作区配置${name === "check" ? "检查" : "操作"}失败,请查看操作记录或检查模型与初始化配置`);
|
|
52
|
+
}
|
|
53
|
+
return record(result.json);
|
|
54
|
+
}
|
|
55
|
+
async catalog() {
|
|
56
|
+
const catalog = await this.command(["init", "model-catalog", "--json"], "models");
|
|
57
|
+
return entries(catalog.models).filter(item => item.eligibleForHarness === true && typeof item.reference === "string")
|
|
58
|
+
.map(item => ({ reference: item.reference, name: typeof item.name === "string" ? item.name : item.reference }));
|
|
59
|
+
}
|
|
60
|
+
async inspect() {
|
|
61
|
+
try {
|
|
62
|
+
try {
|
|
63
|
+
const harnessDir = await lstat(path.join(this.options.repoRoot, ".harness"));
|
|
64
|
+
if (!harnessDir.isDirectory() || harnessDir.isSymbolicLink())
|
|
65
|
+
throw new Error(".harness 必须是仓库内的普通目录");
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (error.code !== "ENOENT")
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
const surface = await this.jsonFile(".harness/init-surface.json");
|
|
72
|
+
const harness = await this.jsonFile("harness.json");
|
|
73
|
+
if (surface && (surface.schemaVersion !== 1 || !surface.files || typeof surface.files !== "object" || Array.isArray(surface.files))) {
|
|
74
|
+
throw new Error("初始化记录不完整,请检查 .harness/init-surface.json");
|
|
75
|
+
}
|
|
76
|
+
if (!surface && !harness) {
|
|
77
|
+
const snapshot = this.snapshot("uninitialized", "此仓库尚未初始化。初始化后可使用项目任务与工作流配置。");
|
|
78
|
+
try {
|
|
79
|
+
snapshot.models = await this.catalog();
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
snapshot.message = "此仓库尚未初始化;可用模型读取失败,请检查模型设置后重新检查。";
|
|
83
|
+
}
|
|
84
|
+
return snapshot;
|
|
85
|
+
}
|
|
86
|
+
const report = await this.command(["init", "check-update", "--json"], "check");
|
|
87
|
+
if (!["recorded", "inferred-baseline", "missing"].includes(String(report.surfaceState)) || typeof report.ok !== "boolean") {
|
|
88
|
+
throw new Error("初始化检查返回了未知状态");
|
|
89
|
+
}
|
|
90
|
+
const actions = entries(report.deterministicActions);
|
|
91
|
+
const merges = entries(report.modelMergeTasks);
|
|
92
|
+
const decisions = entries(report.humanDecisions);
|
|
93
|
+
const pendingPaths = [...new Set([...actions, ...merges, ...decisions].map(item => item.path).filter((p) => typeof p === "string"))];
|
|
94
|
+
const modelsReady = record(report.modelReadiness).status === "ready";
|
|
95
|
+
if (merges.length || decisions.length || !modelsReady || !report.ok && !actions.length) {
|
|
96
|
+
return this.snapshot("needs-attention", !modelsReady ? "项目模型配置需要处理,请在模型设置中检查;已有配置不会被覆盖。" : "部分配置需要合并或确认,请处理以下文件后重新检查。", pendingPaths);
|
|
97
|
+
}
|
|
98
|
+
if (actions.length || report.surfaceState !== "recorded")
|
|
99
|
+
return this.snapshot("update-available", "有可安全更新的项目配置。更新会保留用户修改。", pendingPaths);
|
|
100
|
+
return this.snapshot("ready", "工作区配置已就绪");
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
return this.snapshot("error", error instanceof Error ? error.message : "初始化状态检查失败");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async check(refresh = false) {
|
|
107
|
+
const latest = (await this.options.operations.list()).filter(op => op.action === "workspaceInitialize").at(-1);
|
|
108
|
+
const running = latest && !TERMINAL_OPERATION_STATES.has(latest.state) && latest.state !== "needs-reconcile";
|
|
109
|
+
if (!running && (refresh || !this.cached || Date.now() - this.cached.at > 30_000)) {
|
|
110
|
+
const generation = this.generation;
|
|
111
|
+
this.pending ??= this.inspect().then(value => { if (generation === this.generation)
|
|
112
|
+
this.cached = { value, at: Date.now() }; return value; }).finally(() => { this.pending = undefined; });
|
|
113
|
+
await this.pending;
|
|
114
|
+
}
|
|
115
|
+
const value = this.cached?.value ?? this.snapshot("uninitialized", "正在初始化工作区…");
|
|
116
|
+
return { ...value, ...(latest ? { operation: { operationId: latest.operationId, state: latest.state, ...(latest.errorMessage ? { message: latest.errorMessage } : {}) } } : {}) };
|
|
117
|
+
}
|
|
118
|
+
async start(clientRequestId, model) {
|
|
119
|
+
const created = await this.options.operations.create({ clientRequestId, action: "workspaceInitialize", actionParams: { ...(model ? { model } : {}) } });
|
|
120
|
+
if (created.kind !== "created")
|
|
121
|
+
return created;
|
|
122
|
+
const operationId = created.operation.operationId;
|
|
123
|
+
if (this.activeOperationId) {
|
|
124
|
+
await this.options.operations.update(operationId, { state: "failed", finishedAt: new Date().toISOString(), errorCode: "WORKSPACE_INIT_BUSY", errorMessage: "此工作区已有初始化操作正在运行" });
|
|
125
|
+
return created;
|
|
126
|
+
}
|
|
127
|
+
this.activeOperationId = operationId;
|
|
128
|
+
this.generation += 1;
|
|
129
|
+
queueMicrotask(() => { void this.execute(operationId, model); });
|
|
130
|
+
return created;
|
|
131
|
+
}
|
|
132
|
+
async execute(operationId, model) {
|
|
133
|
+
let release;
|
|
134
|
+
try {
|
|
135
|
+
await this.options.operations.update(operationId, { state: "running", startedAt: new Date().toISOString() });
|
|
136
|
+
this.options.events.append(operationId, { at: new Date().toISOString(), kind: "state", state: "running", message: `正在检查并配置 ${this.options.name}` });
|
|
137
|
+
release = await (this.options.acquireLock ?? (() => acquireUpdateLock(updateLockPath(os.homedir()))))();
|
|
138
|
+
if (!release)
|
|
139
|
+
throw new Error("程序更新或其他初始化操作正在进行,请稍后重试");
|
|
140
|
+
const activity = await (this.options.readActivity ?? readInitRuntimeActivity)(this.options.repoRoot);
|
|
141
|
+
if (activity.workerProjectionError || activity.activeDagRunIds.length || activity.activeWorkerTasks || activity.activeWorkerBatches || this.options.activeChatTurnCount())
|
|
142
|
+
throw new Error("当前工作区有任务或对话正在运行,请结束后重试");
|
|
143
|
+
const operations = await this.options.operations.list();
|
|
144
|
+
if (operations.some(op => op.operationId !== operationId && !TERMINAL_OPERATION_STATES.has(op.state) && op.state !== "needs-reconcile"))
|
|
145
|
+
throw new Error("当前工作区有其他操作正在运行,请稍后重试");
|
|
146
|
+
const before = await this.inspect();
|
|
147
|
+
if (before.state === "error" || before.state === "needs-attention")
|
|
148
|
+
throw new Error(before.message);
|
|
149
|
+
if (before.state === "uninitialized") {
|
|
150
|
+
if (!model || !before.models?.some(item => item.reference === model))
|
|
151
|
+
throw new Error("请选择一个可用的初始模型;可在模型设置中完成认证后重新检查");
|
|
152
|
+
await this.command(["init", "--profile", "full", "--merge", "--client-recovery", "project", "--model", model, "--json"], "apply");
|
|
153
|
+
}
|
|
154
|
+
else if (before.state === "update-available") {
|
|
155
|
+
await this.command(["init", "update", "--apply-safe", "--client-recovery", "project", "--json"], "apply");
|
|
156
|
+
}
|
|
157
|
+
const after = await this.inspect();
|
|
158
|
+
this.cached = { value: after, at: Date.now() };
|
|
159
|
+
const state = after.state === "ready" ? "succeeded" : "needs-reconcile";
|
|
160
|
+
await this.options.operations.update(operationId, { state, finishedAt: new Date().toISOString(), result: { initialization: after }, ...(state !== "succeeded" ? { errorCode: "INIT_NEEDS_RECONCILE", errorMessage: after.message } : {}) });
|
|
161
|
+
this.options.events.append(operationId, { at: new Date().toISOString(), kind: "state", state, message: after.message });
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
this.cached = undefined;
|
|
165
|
+
const message = error instanceof Error ? error.message : "工作区初始化失败";
|
|
166
|
+
await this.options.operations.update(operationId, { state: "failed", finishedAt: new Date().toISOString(), errorCode: "WORKSPACE_INIT_FAILED", errorMessage: message });
|
|
167
|
+
this.options.events.append(operationId, { at: new Date().toISOString(), kind: "error", state: "failed", message });
|
|
168
|
+
}
|
|
169
|
+
finally {
|
|
170
|
+
this.activeOperationId = undefined;
|
|
171
|
+
await release?.().catch(() => undefined);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tea-agent/loop-agent",
|
|
3
|
-
"version": "0.43.0-next.
|
|
3
|
+
"version": "0.43.0-next.15",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"loop-agent": "bin/loop-agent.js",
|
|
@@ -98,8 +98,6 @@
|
|
|
98
98
|
},
|
|
99
99
|
"devDependencies": {
|
|
100
100
|
"@remixicon/react": "^4.9.0",
|
|
101
|
-
"@stryker-mutator/core": "^10.0.0",
|
|
102
|
-
"@stryker-mutator/vitest-runner": "^10.0.0",
|
|
103
101
|
"@types/node": "^24.6.0",
|
|
104
102
|
"@types/react": "^19.1.8",
|
|
105
103
|
"@types/react-dom": "^19.1.6",
|
package/dist/commands/status.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { getTaskDir, getTaskPaths, getTaskStatus, loadTaskConfig, } from "../task/runtime.js";
|
|
2
|
-
import { getArtifactPath } from "../shared/artifacts-core.js";
|
|
3
|
-
import { buildTaskReadModel } from "../task/read-model.js";
|
|
4
|
-
export async function runStatus(repoRoot, taskId) {
|
|
5
|
-
// Read-only: do not refresh/repair/migrate. Mutations belong to task advance.
|
|
6
|
-
const state = await getTaskStatus(repoRoot, taskId);
|
|
7
|
-
const taskConfig = await loadTaskConfig(repoRoot, taskId);
|
|
8
|
-
const taskDir = getTaskDir(repoRoot, taskId);
|
|
9
|
-
const taskPaths = getTaskPaths(repoRoot, taskId);
|
|
10
|
-
const readModel = await buildTaskReadModel(repoRoot, taskId);
|
|
11
|
-
console.log(JSON.stringify({
|
|
12
|
-
taskId,
|
|
13
|
-
taskDir,
|
|
14
|
-
taskRoot: taskDir,
|
|
15
|
-
sourceOfTruth: "task",
|
|
16
|
-
artifactPaths: {
|
|
17
|
-
taskConfig: taskPaths.taskConfigPath,
|
|
18
|
-
workflowState: taskPaths.statePath,
|
|
19
|
-
sourceDir: taskPaths.sourceDir,
|
|
20
|
-
artifactsDir: `${taskDir}/artifacts`,
|
|
21
|
-
analysis: getArtifactPath(taskDir, "analyze"),
|
|
22
|
-
plan: getArtifactPath(taskDir, "plan"),
|
|
23
|
-
modifyLog: getArtifactPath(taskDir, "implement"),
|
|
24
|
-
verifyResult: getArtifactPath(taskDir, "verify"),
|
|
25
|
-
retrospective: getArtifactPath(taskDir, "retrospective"),
|
|
26
|
-
},
|
|
27
|
-
runRefs: {
|
|
28
|
-
oneShotRuns: readModel.runRefs.oneShotRuns,
|
|
29
|
-
dagRuns: readModel.runRefs.dagRuns,
|
|
30
|
-
},
|
|
31
|
-
readModel,
|
|
32
|
-
actionContext: {
|
|
33
|
-
mode: "dag",
|
|
34
|
-
allowedEditRoots: taskConfig.allowedPaths,
|
|
35
|
-
forbiddenPaths: taskConfig.forbiddenPaths,
|
|
36
|
-
constraints: taskConfig.hardConstraints,
|
|
37
|
-
requiresPromotion: readModel.promotion.required && !readModel.promotion.ready,
|
|
38
|
-
},
|
|
39
|
-
currentStep: state.currentStep,
|
|
40
|
-
status: state.status,
|
|
41
|
-
complexity: taskConfig.complexity,
|
|
42
|
-
maxFixLoops: taskConfig.maxFixLoops,
|
|
43
|
-
verifyEnv: taskConfig.verifyEnv ?? "clean",
|
|
44
|
-
maxGoalContinuationsPerRun: taskConfig.maxGoalContinuationsPerRun ?? 5,
|
|
45
|
-
goal: state.goal,
|
|
46
|
-
sourceState: state.sourceState,
|
|
47
|
-
completedSteps: state.completedSteps,
|
|
48
|
-
decisionLogSize: state.decisionLog.length,
|
|
49
|
-
artifacts: state.artifacts,
|
|
50
|
-
// Prefer task advance/status; keep single next action (no parallel command arrays).
|
|
51
|
-
next: {
|
|
52
|
-
kind: "advance-or-status",
|
|
53
|
-
command: readModel.nextAction.command,
|
|
54
|
-
reason: readModel.nextAction.reason,
|
|
55
|
-
},
|
|
56
|
-
}, null, 2));
|
|
57
|
-
}
|
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
import { ContractMutationError, adoptTaskContract, applyTaskContract, diffTaskContract, doctorTaskContract, loadDraftFromPath, observeTaskContractState, recoverTaskContract, validateDraftForTask, } from "../task/contract/index.js";
|
|
2
|
-
import { operatorFailed, operatorSucceeded, processExitCodeForOutcome, writeOperatorJson, } from "../shared/operator/index.js";
|
|
3
|
-
const USAGE = `usage:
|
|
4
|
-
task contract validate --task <id> --input <path> [--json]
|
|
5
|
-
task contract diff --task <id> --input <path> [--json]
|
|
6
|
-
|
|
7
|
-
Advanced-only public leaves. Managed apply/show/adopt/doctor/recover are internal services
|
|
8
|
-
reached via task advance / task status (or application APIs), not public CLI subcommands.`;
|
|
9
|
-
export function parseTaskContractArgs(args) {
|
|
10
|
-
const options = {};
|
|
11
|
-
const positional = [];
|
|
12
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
13
|
-
const token = args[i];
|
|
14
|
-
if (token === "--json") {
|
|
15
|
-
options.json = true;
|
|
16
|
-
continue;
|
|
17
|
-
}
|
|
18
|
-
if (token === "--task") {
|
|
19
|
-
options.task = args[i + 1];
|
|
20
|
-
i += 1;
|
|
21
|
-
continue;
|
|
22
|
-
}
|
|
23
|
-
if (token === "--input") {
|
|
24
|
-
options.input = args[i + 1];
|
|
25
|
-
i += 1;
|
|
26
|
-
continue;
|
|
27
|
-
}
|
|
28
|
-
if (token === "--expected-revision") {
|
|
29
|
-
const raw = args[i + 1];
|
|
30
|
-
const n = Number(raw);
|
|
31
|
-
if (!Number.isInteger(n) || n < 0) {
|
|
32
|
-
throw new Error(`invalid --expected-revision: ${raw}`);
|
|
33
|
-
}
|
|
34
|
-
options.expectedRevision = n;
|
|
35
|
-
i += 1;
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
if (token === "--expected-observed-hash") {
|
|
39
|
-
options.expectedObservedHash = args[i + 1];
|
|
40
|
-
i += 1;
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
if (token === "--request-id") {
|
|
44
|
-
options.requestId = args[i + 1];
|
|
45
|
-
i += 1;
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
if (token === "--request-payload-sha256") {
|
|
49
|
-
options.requestPayloadSha256 = args[i + 1];
|
|
50
|
-
i += 1;
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
if (token === "--tx-id") {
|
|
54
|
-
options.txId = args[i + 1];
|
|
55
|
-
i += 1;
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
if (token.startsWith("-")) {
|
|
59
|
-
throw new Error(`unknown flag ${token}\n${USAGE}`);
|
|
60
|
-
}
|
|
61
|
-
positional.push(token);
|
|
62
|
-
}
|
|
63
|
-
const subcommand = positional[0];
|
|
64
|
-
if (!subcommand) {
|
|
65
|
-
throw new Error(USAGE);
|
|
66
|
-
}
|
|
67
|
-
// show takes positional taskId; others prefer --task
|
|
68
|
-
let taskId = options.task;
|
|
69
|
-
if (subcommand === "show") {
|
|
70
|
-
taskId = positional[1] ?? options.task;
|
|
71
|
-
}
|
|
72
|
-
return { subcommand, taskId, options };
|
|
73
|
-
}
|
|
74
|
-
function mutationOutcome(code) {
|
|
75
|
-
switch (code) {
|
|
76
|
-
case "REVISION_CONFLICT":
|
|
77
|
-
case "OBSERVED_HASH_CONFLICT":
|
|
78
|
-
case "REQUEST_ID_REUSE_CONFLICT":
|
|
79
|
-
case "LEASE_HELD":
|
|
80
|
-
return "conflict";
|
|
81
|
-
case "NOT_FOUND":
|
|
82
|
-
return "not-found";
|
|
83
|
-
case "TRANSACTION_INCOMPLETE":
|
|
84
|
-
case "OPERATION_NEEDS_RECONCILE":
|
|
85
|
-
return "needs-reconcile";
|
|
86
|
-
case "EXTERNALLY_MODIFIED":
|
|
87
|
-
case "NOT_MANAGED":
|
|
88
|
-
case "BINDING_DRIFT":
|
|
89
|
-
return "blocked";
|
|
90
|
-
case "INVALID_INPUT":
|
|
91
|
-
case "PATH_OUTSIDE_REPO":
|
|
92
|
-
case "UNSUPPORTED_SCHEMA_VERSION":
|
|
93
|
-
return "invalid";
|
|
94
|
-
default:
|
|
95
|
-
return "rejected";
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
function emit(result, json) {
|
|
99
|
-
// Machine envelope is the default for task contract when --json is set;
|
|
100
|
-
// without --json still print the same document for dogfood consistency.
|
|
101
|
-
writeOperatorJson(result);
|
|
102
|
-
if (!json) {
|
|
103
|
-
// already wrote JSON; human path same for Phase 0.5
|
|
104
|
-
}
|
|
105
|
-
process.exitCode = processExitCodeForOutcome(result.outcome);
|
|
106
|
-
}
|
|
107
|
-
export async function runTaskContract(repoRoot, args) {
|
|
108
|
-
let subcommand;
|
|
109
|
-
let taskId;
|
|
110
|
-
let options;
|
|
111
|
-
try {
|
|
112
|
-
({ subcommand, taskId, options } = parseTaskContractArgs(args));
|
|
113
|
-
}
|
|
114
|
-
catch (error) {
|
|
115
|
-
const result = operatorFailed({
|
|
116
|
-
command: "task contract",
|
|
117
|
-
outcome: "invalid",
|
|
118
|
-
code: "INVALID_INPUT",
|
|
119
|
-
message: error instanceof Error ? error.message : String(error),
|
|
120
|
-
});
|
|
121
|
-
emit(result, true);
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
const command = `task contract ${subcommand}`;
|
|
125
|
-
try {
|
|
126
|
-
switch (subcommand) {
|
|
127
|
-
case "show": {
|
|
128
|
-
if (!taskId) {
|
|
129
|
-
throw new ContractMutationError("INVALID_INPUT", "task contract show requires <taskId>");
|
|
130
|
-
}
|
|
131
|
-
const state = await observeTaskContractState(repoRoot, taskId);
|
|
132
|
-
const result = operatorSucceeded(command, { taskId, state });
|
|
133
|
-
emit(result, options.json);
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
case "validate": {
|
|
137
|
-
if (!taskId || !options.input) {
|
|
138
|
-
throw new ContractMutationError("INVALID_INPUT", "task contract validate requires --task and --input");
|
|
139
|
-
}
|
|
140
|
-
const { draft } = await loadDraftFromPath(options.input, repoRoot);
|
|
141
|
-
const validation = await validateDraftForTask({
|
|
142
|
-
repoRoot,
|
|
143
|
-
taskId,
|
|
144
|
-
draft,
|
|
145
|
-
});
|
|
146
|
-
if (!validation.ok) {
|
|
147
|
-
const result = operatorFailed({
|
|
148
|
-
command,
|
|
149
|
-
outcome: "invalid",
|
|
150
|
-
code: "INVALID_INPUT",
|
|
151
|
-
message: validation.errors.join("; "),
|
|
152
|
-
details: { errors: validation.errors },
|
|
153
|
-
});
|
|
154
|
-
emit(result, options.json);
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
const result = operatorSucceeded(command, {
|
|
158
|
-
taskId,
|
|
159
|
-
valid: true,
|
|
160
|
-
warnings: validation.warnings,
|
|
161
|
-
});
|
|
162
|
-
emit(result, options.json);
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
case "diff": {
|
|
166
|
-
if (!taskId || !options.input) {
|
|
167
|
-
throw new ContractMutationError("INVALID_INPUT", "task contract diff requires --task and --input");
|
|
168
|
-
}
|
|
169
|
-
const { draft } = await loadDraftFromPath(options.input, repoRoot);
|
|
170
|
-
const diff = await diffTaskContract({ repoRoot, taskId, draft });
|
|
171
|
-
const result = operatorSucceeded(command, diff);
|
|
172
|
-
emit(result, options.json);
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
case "apply": {
|
|
176
|
-
if (!taskId ||
|
|
177
|
-
!options.input ||
|
|
178
|
-
options.expectedRevision === undefined ||
|
|
179
|
-
!options.expectedObservedHash ||
|
|
180
|
-
!options.requestId ||
|
|
181
|
-
!options.requestPayloadSha256) {
|
|
182
|
-
throw new ContractMutationError("INVALID_INPUT", "task contract apply requires --task --input --expected-revision --expected-observed-hash --request-id --request-payload-sha256");
|
|
183
|
-
}
|
|
184
|
-
const loaded = await loadDraftFromPath(options.input, repoRoot);
|
|
185
|
-
// Client-supplied payload hash must match file bytes
|
|
186
|
-
if (loaded.payloadSha256 !== options.requestPayloadSha256) {
|
|
187
|
-
throw new ContractMutationError("INVALID_INPUT", `request-payload-sha256 does not match input file hash (expected ${loaded.payloadSha256})`);
|
|
188
|
-
}
|
|
189
|
-
const applyResult = await applyTaskContract({
|
|
190
|
-
repoRoot,
|
|
191
|
-
taskId,
|
|
192
|
-
draft: loaded.draft,
|
|
193
|
-
expectedRevision: options.expectedRevision,
|
|
194
|
-
expectedObservedHash: options.expectedObservedHash,
|
|
195
|
-
requestId: options.requestId,
|
|
196
|
-
requestPayloadSha256: options.requestPayloadSha256,
|
|
197
|
-
rawDraftText: loaded.rawText,
|
|
198
|
-
});
|
|
199
|
-
const result = operatorSucceeded(command, applyResult);
|
|
200
|
-
emit(result, options.json);
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
case "adopt": {
|
|
204
|
-
if (!taskId ||
|
|
205
|
-
options.expectedRevision === undefined ||
|
|
206
|
-
!options.expectedObservedHash ||
|
|
207
|
-
!options.requestId ||
|
|
208
|
-
!options.requestPayloadSha256) {
|
|
209
|
-
throw new ContractMutationError("INVALID_INPUT", "task contract adopt requires --task --expected-revision --expected-observed-hash --request-id --request-payload-sha256");
|
|
210
|
-
}
|
|
211
|
-
const adoptResult = await adoptTaskContract({
|
|
212
|
-
repoRoot,
|
|
213
|
-
taskId,
|
|
214
|
-
expectedRevision: options.expectedRevision,
|
|
215
|
-
expectedObservedHash: options.expectedObservedHash,
|
|
216
|
-
requestId: options.requestId,
|
|
217
|
-
requestPayloadSha256: options.requestPayloadSha256,
|
|
218
|
-
});
|
|
219
|
-
const result = operatorSucceeded(command, adoptResult);
|
|
220
|
-
emit(result, options.json);
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
case "doctor": {
|
|
224
|
-
if (!taskId) {
|
|
225
|
-
throw new ContractMutationError("INVALID_INPUT", "task contract doctor requires --task");
|
|
226
|
-
}
|
|
227
|
-
const doctor = await doctorTaskContract({ repoRoot, taskId });
|
|
228
|
-
const result = operatorSucceeded(command, doctor);
|
|
229
|
-
emit(result, options.json);
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
case "recover": {
|
|
233
|
-
if (!taskId) {
|
|
234
|
-
throw new ContractMutationError("INVALID_INPUT", "task contract recover requires --task");
|
|
235
|
-
}
|
|
236
|
-
const recover = await recoverTaskContract({
|
|
237
|
-
repoRoot,
|
|
238
|
-
taskId,
|
|
239
|
-
txId: options.txId,
|
|
240
|
-
});
|
|
241
|
-
const result = operatorSucceeded(command, recover);
|
|
242
|
-
emit(result, options.json);
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
default:
|
|
246
|
-
throw new ContractMutationError("INVALID_INPUT", `unknown task contract subcommand: ${subcommand}\n${USAGE}`);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
catch (error) {
|
|
250
|
-
if (error instanceof ContractMutationError) {
|
|
251
|
-
const result = operatorFailed({
|
|
252
|
-
command,
|
|
253
|
-
outcome: mutationOutcome(error.code),
|
|
254
|
-
code: error.code,
|
|
255
|
-
message: error.message,
|
|
256
|
-
details: error.details,
|
|
257
|
-
});
|
|
258
|
-
emit(result, options.json);
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
const result = operatorFailed({
|
|
262
|
-
command,
|
|
263
|
-
outcome: "rejected",
|
|
264
|
-
code: "INTERNAL_ERROR",
|
|
265
|
-
message: error instanceof Error ? error.message : String(error),
|
|
266
|
-
});
|
|
267
|
-
emit(result, options.json);
|
|
268
|
-
}
|
|
269
|
-
}
|
package/dist/shared/prompts.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { isFeatureStudyTask } from './reference-context.js';
|
|
4
|
-
export async function loadPrompt(repoRoot, step, taskConfig) {
|
|
5
|
-
const filename = resolvePromptFilename(step, taskConfig);
|
|
6
|
-
const promptPath = path.join(repoRoot, '.harness', 'prompts', filename);
|
|
7
|
-
try {
|
|
8
|
-
return await readFile(promptPath, 'utf-8');
|
|
9
|
-
}
|
|
10
|
-
catch {
|
|
11
|
-
if (filename !== `${step}.md`) {
|
|
12
|
-
return readFile(path.join(repoRoot, '.harness', 'prompts', `${step}.md`), 'utf-8');
|
|
13
|
-
}
|
|
14
|
-
throw new Error(`prompt not found: ${promptPath}`);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
function resolvePromptFilename(step, taskConfig) {
|
|
18
|
-
if (!taskConfig || !isFeatureStudyTask(taskConfig)) {
|
|
19
|
-
return `${step}.md`;
|
|
20
|
-
}
|
|
21
|
-
if (step === 'analyze')
|
|
22
|
-
return 'feature-study-analyze.md';
|
|
23
|
-
if (step === 'plan')
|
|
24
|
-
return 'feature-study-plan.md';
|
|
25
|
-
return `${step}.md`;
|
|
26
|
-
}
|
package/dist/task/config.js
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { safeParseTaskContractDraft } from "./schema.js";
|
|
3
|
-
import { getTaskContractPaths } from "./paths.js";
|
|
4
|
-
import { readSourceManifest } from "../source-references.js";
|
|
5
|
-
function isPathOutsideRepoHint(p) {
|
|
6
|
-
if (path.isAbsolute(p))
|
|
7
|
-
return true;
|
|
8
|
-
if (p.includes("\0"))
|
|
9
|
-
return true;
|
|
10
|
-
const normalized = p.replace(/\\/g, "/");
|
|
11
|
-
if (normalized.startsWith("../") || normalized.includes("/../"))
|
|
12
|
-
return true;
|
|
13
|
-
return false;
|
|
14
|
-
}
|
|
15
|
-
export async function validateTaskContractDraft(input) {
|
|
16
|
-
const errors = [];
|
|
17
|
-
const warnings = [];
|
|
18
|
-
const parsed = safeParseTaskContractDraft(input.draft);
|
|
19
|
-
if (!parsed.success) {
|
|
20
|
-
for (const issue of parsed.error.issues) {
|
|
21
|
-
errors.push({
|
|
22
|
-
code: issue.message.includes("schemaVersion") ||
|
|
23
|
-
issue.path[0] === "schemaVersion"
|
|
24
|
-
? "UNSUPPORTED_SCHEMA_VERSION"
|
|
25
|
-
: "INVALID_INPUT",
|
|
26
|
-
path: issue.path.join("."),
|
|
27
|
-
message: issue.message,
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
return {
|
|
31
|
-
taskId: input.taskId,
|
|
32
|
-
valid: false,
|
|
33
|
-
errors,
|
|
34
|
-
warnings,
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
const draft = parsed.data;
|
|
38
|
-
if (draft.taskId !== input.taskId) {
|
|
39
|
-
errors.push({
|
|
40
|
-
code: "INVALID_INPUT",
|
|
41
|
-
path: "taskId",
|
|
42
|
-
message: `draft.taskId (${draft.taskId}) does not match task (${input.taskId})`,
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
for (const p of draft.constraints.allowedPaths) {
|
|
46
|
-
if (isPathOutsideRepoHint(p) && !p.includes("*")) {
|
|
47
|
-
// globs may be relative; absolute is out of repo
|
|
48
|
-
if (path.isAbsolute(p)) {
|
|
49
|
-
errors.push({
|
|
50
|
-
code: "PATH_OUTSIDE_REPO",
|
|
51
|
-
path: "constraints.allowedPaths",
|
|
52
|
-
message: `absolute path not allowed: ${p}`,
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
for (const p of draft.constraints.forbiddenPaths) {
|
|
58
|
-
if (path.isAbsolute(p)) {
|
|
59
|
-
errors.push({
|
|
60
|
-
code: "PATH_OUTSIDE_REPO",
|
|
61
|
-
path: "constraints.forbiddenPaths",
|
|
62
|
-
message: `absolute path not allowed: ${p}`,
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
if (draft.references && draft.references.length > 0) {
|
|
67
|
-
const paths = getTaskContractPaths(input.repoRoot, input.taskId);
|
|
68
|
-
const manifest = await readSourceManifest(paths.sourceDir);
|
|
69
|
-
if (!manifest) {
|
|
70
|
-
warnings.push({
|
|
71
|
-
code: "MISSING_SOURCE_MANIFEST",
|
|
72
|
-
message: "draft references present but source-manifest.json is missing; import-prd first for immutable evidence",
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
for (const ref of draft.references) {
|
|
77
|
-
const normalized = ref.ref.replace(/\\/g, "/");
|
|
78
|
-
const hit = manifest.documents.some((doc) => {
|
|
79
|
-
const mat = doc.materializedPath.replace(/\\/g, "/");
|
|
80
|
-
return (mat === normalized ||
|
|
81
|
-
`source/${mat}` === normalized ||
|
|
82
|
-
normalized.endsWith(mat));
|
|
83
|
-
});
|
|
84
|
-
if (!hit) {
|
|
85
|
-
warnings.push({
|
|
86
|
-
code: "REFERENCE_NOT_IN_MANIFEST",
|
|
87
|
-
message: `reference not found in source-manifest: ${ref.ref}`,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
if (draft.sourceResolutions &&
|
|
94
|
-
draft.sourceResolutions.some((r) => r.decidedBy !== "human")) {
|
|
95
|
-
warnings.push({
|
|
96
|
-
code: "NON_HUMAN_SOURCE_RESOLUTION",
|
|
97
|
-
message: "one or more sourceResolutions are not decidedBy=human; blocking conflicts require human resolution before apply",
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
return {
|
|
101
|
-
taskId: input.taskId,
|
|
102
|
-
valid: errors.length === 0,
|
|
103
|
-
errors,
|
|
104
|
-
warnings,
|
|
105
|
-
};
|
|
106
|
-
}
|
package/dist/task/goal.js
DELETED