@teammy/server 0.0.8 → 0.0.10
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 +8 -6
- package/dist/line-diff-worker.js +32 -0
- package/dist/main.js +1248 -510
- package/dist/native-pi-entry.js +247 -0
- package/dist/runtime/README.md +21 -2
- package/dist/runtime/shared/system.md +2 -2
- package/dist/runtime/shared/tools.json +11 -3
- package/dist/runtime/workflows/directory-analysis/config.json +6 -7
- package/dist/runtime/workflows/directory-analysis/prompt.md +4 -2
- package/dist/runtime/workflows/file-summary/config.json +4 -6
- package/dist/runtime/workflows/file-summary/prompt.md +2 -2
- package/dist/runtime/workflows/task-bootstrap/config.json +7 -8
- package/dist/runtime/workflows/task-bootstrap/prompt.md +43 -7
- package/dist/web/assets/Dashboard-wKygMUMn.js +1 -0
- package/dist/web/assets/Files-C9kRlKdR.js +1 -0
- package/dist/web/assets/Markdown-DQn2KtR3.js +29 -0
- package/dist/web/assets/Sessions-HD72N7MF.js +1 -0
- package/dist/web/assets/{index-hjEc8Pme.css → index-BgzSGwum.css} +1 -1
- package/dist/web/assets/{index-f4fiI3WA.js → index-Db4kzLA8.js} +6 -6
- package/dist/web/index.html +3 -3
- package/package.json +2 -1
- package/dist/web/assets/Dashboard-CsQ-GV-q.js +0 -1
- package/dist/web/assets/Files-cG0tkAfP.js +0 -1
- package/dist/web/assets/Markdown-Bt-6EDRz.js +0 -29
- package/dist/web/assets/Sessions-B7ABJvRH.js +0 -1
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
// apps/server/ai/native-pi-entry.ts
|
|
2
|
+
import { InMemoryCredentialStore, InMemoryModelsStore, Type as Type2 } from "@earendil-works/pi-ai";
|
|
3
|
+
import { createAssistantMessageEventStream } from "@earendil-works/pi-ai/utils/event-stream";
|
|
4
|
+
import {
|
|
5
|
+
createAgentSession,
|
|
6
|
+
createExtensionRuntime,
|
|
7
|
+
ModelRuntime,
|
|
8
|
+
SessionManager,
|
|
9
|
+
SettingsManager
|
|
10
|
+
} from "@earendil-works/pi-coding-agent";
|
|
11
|
+
import { z as z2 } from "zod";
|
|
12
|
+
|
|
13
|
+
// apps/server/ai/agent-channel.ts
|
|
14
|
+
import { createInterface } from "node:readline";
|
|
15
|
+
var AgentChannel = class {
|
|
16
|
+
constructor(input, output) {
|
|
17
|
+
this.output = output;
|
|
18
|
+
this.lines = createInterface({ input, crlfDelay: Infinity });
|
|
19
|
+
this.iterator = this.lines[Symbol.asyncIterator]();
|
|
20
|
+
output.on("error", () => {
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
output;
|
|
24
|
+
lines;
|
|
25
|
+
iterator;
|
|
26
|
+
writes = Promise.resolve();
|
|
27
|
+
async *messages() {
|
|
28
|
+
for await (const line of this.iterator) if (line) yield JSON.parse(line);
|
|
29
|
+
}
|
|
30
|
+
send(value) {
|
|
31
|
+
const write = this.writes.then(
|
|
32
|
+
() => new Promise((resolve, reject) => {
|
|
33
|
+
this.output.write(
|
|
34
|
+
JSON.stringify(value) + "\n",
|
|
35
|
+
(error) => error ? reject(error) : resolve()
|
|
36
|
+
);
|
|
37
|
+
})
|
|
38
|
+
);
|
|
39
|
+
this.writes = write.catch(() => {
|
|
40
|
+
});
|
|
41
|
+
return write;
|
|
42
|
+
}
|
|
43
|
+
close() {
|
|
44
|
+
this.lines.close();
|
|
45
|
+
this.output.end();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// apps/server/ai/agent-protocol.ts
|
|
50
|
+
import { z } from "zod";
|
|
51
|
+
var TaskQuerySchema = z.object({
|
|
52
|
+
id: z.uuid().optional(),
|
|
53
|
+
source: z.string().optional(),
|
|
54
|
+
query: z.string().optional(),
|
|
55
|
+
visibility: z.enum(["active", "archived", "all"]).optional()
|
|
56
|
+
}).strict();
|
|
57
|
+
|
|
58
|
+
// apps/server/ai/models.ts
|
|
59
|
+
import { createModels, createProvider, Type } from "@earendil-works/pi-ai";
|
|
60
|
+
import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
|
|
61
|
+
import { openAIResponsesApi } from "@earendil-works/pi-ai/api/openai-responses.lazy";
|
|
62
|
+
import { anthropicMessagesApi } from "@earendil-works/pi-ai/api/anthropic-messages.lazy";
|
|
63
|
+
function modelAccess(profile, key, reasoning = false) {
|
|
64
|
+
const model = {
|
|
65
|
+
id: profile.model,
|
|
66
|
+
name: profile.name,
|
|
67
|
+
api: profile.protocol,
|
|
68
|
+
provider: "teammy-gateway",
|
|
69
|
+
baseUrl: profile.protocol === "anthropic-messages" ? profile.baseUrl.replace(/\/v1\/?$/, "") : profile.baseUrl,
|
|
70
|
+
reasoning,
|
|
71
|
+
input: ["text"],
|
|
72
|
+
contextWindow: 0,
|
|
73
|
+
maxTokens: Number.MAX_SAFE_INTEGER,
|
|
74
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
75
|
+
...profile.protocol === "openai-completions" ? {
|
|
76
|
+
compat: {
|
|
77
|
+
supportsDeveloperRole: false,
|
|
78
|
+
supportsStore: false,
|
|
79
|
+
maxTokensField: "max_tokens"
|
|
80
|
+
}
|
|
81
|
+
} : {}
|
|
82
|
+
};
|
|
83
|
+
const models = createModels();
|
|
84
|
+
models.setProvider(
|
|
85
|
+
createProvider({
|
|
86
|
+
id: model.provider,
|
|
87
|
+
models: [model],
|
|
88
|
+
auth: {
|
|
89
|
+
apiKey: {
|
|
90
|
+
name: "Teammy",
|
|
91
|
+
resolve: async () => ({ auth: { apiKey: key || "teammy-keyless" } })
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
api: profile.protocol === "openai-completions" ? openAICompletionsApi() : profile.protocol === "openai-responses" ? openAIResponsesApi() : anthropicMessagesApi()
|
|
95
|
+
})
|
|
96
|
+
);
|
|
97
|
+
return { models, model };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// apps/server/ai/native-pi-entry.ts
|
|
101
|
+
var channel = new AgentChannel(process.stdin, process.stdout);
|
|
102
|
+
var sequence = 0;
|
|
103
|
+
var pending = /* @__PURE__ */ new Map();
|
|
104
|
+
function rpc(request) {
|
|
105
|
+
const id = ++sequence;
|
|
106
|
+
return new Promise((resolve, reject) => {
|
|
107
|
+
pending.set(id, { resolve: (value) => resolve(value), reject });
|
|
108
|
+
channel.send({ type: "request", id, ...request }).catch(reject);
|
|
109
|
+
}).finally(() => pending.delete(id));
|
|
110
|
+
}
|
|
111
|
+
async function run(config) {
|
|
112
|
+
const { model, runtime } = config;
|
|
113
|
+
const cwd = "/workspaces", agentDir = "/tmp/pi";
|
|
114
|
+
const modelRuntime = await ModelRuntime.create({
|
|
115
|
+
credentials: new InMemoryCredentialStore(),
|
|
116
|
+
modelsStore: new InMemoryModelsStore(),
|
|
117
|
+
modelsPath: null,
|
|
118
|
+
allowModelNetwork: false,
|
|
119
|
+
refreshOnCreate: false
|
|
120
|
+
});
|
|
121
|
+
const bridge = modelAccess(
|
|
122
|
+
{
|
|
123
|
+
id: "bridge",
|
|
124
|
+
name: model.name,
|
|
125
|
+
model: model.id,
|
|
126
|
+
protocol: model.api,
|
|
127
|
+
baseUrl: "http://model.invalid",
|
|
128
|
+
enabled: true
|
|
129
|
+
},
|
|
130
|
+
"stdio-bridge",
|
|
131
|
+
model.reasoning
|
|
132
|
+
);
|
|
133
|
+
modelRuntime.registerNativeProvider(bridge.models.getProvider(model.provider));
|
|
134
|
+
const loader = {
|
|
135
|
+
getExtensions: () => ({ extensions: [], errors: [], runtime: createExtensionRuntime() }),
|
|
136
|
+
getSkills: () => ({ skills: [], diagnostics: [] }),
|
|
137
|
+
getPrompts: () => ({ prompts: [], diagnostics: [] }),
|
|
138
|
+
getThemes: () => ({ themes: [], diagnostics: [] }),
|
|
139
|
+
getAgentsFiles: () => ({ agentsFiles: [] }),
|
|
140
|
+
// Preserve Pi's native operating prompt and append only the Teammy assignment.
|
|
141
|
+
getSystemPrompt: () => void 0,
|
|
142
|
+
getSystemPromptSource: () => void 0,
|
|
143
|
+
getAppendSystemPrompt: () => [config.systemPrompt],
|
|
144
|
+
getAppendSystemPromptSources: () => [],
|
|
145
|
+
extendResources: () => {
|
|
146
|
+
},
|
|
147
|
+
reload: async () => {
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
const query = {
|
|
151
|
+
name: "query_tasks",
|
|
152
|
+
...runtime.toolText.query_tasks,
|
|
153
|
+
parameters: Type2.Unsafe(z2.toJSONSchema(TaskQuerySchema)),
|
|
154
|
+
async execute(_id, params) {
|
|
155
|
+
const value = await rpc({ method: "query_tasks", input: TaskQuerySchema.parse(params) });
|
|
156
|
+
return { content: [{ type: "text", text: JSON.stringify(value) }], details: {} };
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
const { session } = await createAgentSession({
|
|
160
|
+
cwd,
|
|
161
|
+
agentDir,
|
|
162
|
+
modelRuntime,
|
|
163
|
+
model,
|
|
164
|
+
thinkingLevel: runtime.config.thinkingLevel,
|
|
165
|
+
resourceLoader: loader,
|
|
166
|
+
sessionManager: SessionManager.inMemory(cwd),
|
|
167
|
+
settingsManager: SettingsManager.inMemory({
|
|
168
|
+
compaction: { enabled: false },
|
|
169
|
+
retry: { enabled: false, maxRetries: 0 },
|
|
170
|
+
cacheWarming: "off"
|
|
171
|
+
}),
|
|
172
|
+
tools: runtime.config.tools,
|
|
173
|
+
customTools: runtime.config.tools.includes("query_tasks") ? [query] : []
|
|
174
|
+
});
|
|
175
|
+
session.agent.streamFunction = (_model, context) => {
|
|
176
|
+
const stream = createAssistantMessageEventStream();
|
|
177
|
+
void rpc({ method: "model", input: context }).then((message) => {
|
|
178
|
+
if (message.stopReason === "error" || message.stopReason === "aborted" || message.stopReason === "pending")
|
|
179
|
+
stream.push({
|
|
180
|
+
type: "error",
|
|
181
|
+
reason: message.stopReason === "aborted" ? "aborted" : "error",
|
|
182
|
+
error: message
|
|
183
|
+
});
|
|
184
|
+
else stream.push({ type: "done", reason: message.stopReason, message });
|
|
185
|
+
}).catch(() => {
|
|
186
|
+
stream.push({
|
|
187
|
+
type: "error",
|
|
188
|
+
reason: "error",
|
|
189
|
+
error: {
|
|
190
|
+
role: "assistant",
|
|
191
|
+
content: [],
|
|
192
|
+
api: model.api,
|
|
193
|
+
provider: model.provider,
|
|
194
|
+
model: model.id,
|
|
195
|
+
stopReason: "error",
|
|
196
|
+
errorMessage: "Model request failed.",
|
|
197
|
+
timestamp: Date.now(),
|
|
198
|
+
usage: {
|
|
199
|
+
input: 0,
|
|
200
|
+
output: 0,
|
|
201
|
+
cacheRead: 0,
|
|
202
|
+
cacheWrite: 0,
|
|
203
|
+
totalTokens: 0,
|
|
204
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
return stream;
|
|
210
|
+
};
|
|
211
|
+
const unsubscribe = session.subscribe((event) => {
|
|
212
|
+
if (event.type === "tool_execution_start")
|
|
213
|
+
void channel.send({ type: "progress", tool: event.toolName }).catch(() => {
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
try {
|
|
217
|
+
await session.prompt(config.input);
|
|
218
|
+
const last = session.messages.filter((message) => message.role === "assistant").at(-1);
|
|
219
|
+
if (!last || last.stopReason === "error" || last.stopReason === "aborted")
|
|
220
|
+
throw new Error("Analysis failed.");
|
|
221
|
+
await channel.send({
|
|
222
|
+
type: "result",
|
|
223
|
+
text: last.content.filter((c) => c.type === "text").map((c) => c.text).join("")
|
|
224
|
+
});
|
|
225
|
+
} finally {
|
|
226
|
+
unsubscribe();
|
|
227
|
+
session.dispose();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
var started = false;
|
|
231
|
+
try {
|
|
232
|
+
for await (const message of channel.messages()) {
|
|
233
|
+
if (message.type === "start" && !started) {
|
|
234
|
+
started = true;
|
|
235
|
+
void run(message.config).catch(async () => {
|
|
236
|
+
await channel.send({ type: "failed" });
|
|
237
|
+
process.exitCode = 1;
|
|
238
|
+
}).finally(() => channel.close());
|
|
239
|
+
} else if (message.type === "response") {
|
|
240
|
+
const request = pending.get(message.id);
|
|
241
|
+
if (message.error) request?.reject(new Error(message.error));
|
|
242
|
+
else request?.resolve(message.value);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
} finally {
|
|
246
|
+
for (const request of pending.values()) request.reject(new Error("Job channel closed."));
|
|
247
|
+
}
|
package/dist/runtime/README.md
CHANGED
|
@@ -8,8 +8,27 @@
|
|
|
8
8
|
|
|
9
9
|
模型和密钥继续在现有接入点设置中配置。执行配置不能增加未注册工具或突破快照访问范围。`settleSeconds` 用于冷启动及后续版本维护的合并等待,从该成员最新一次有效发布开始计算。
|
|
10
10
|
|
|
11
|
-
`task-bootstrap`
|
|
11
|
+
`task-bootstrap` v10 同时负责首次整理和后续维护,`directory-analysis` v3 复用原生沙箱。整个 Pi 进程在 Linux Bubblewrap 沙箱中运行,挂载只读成员工作区和可选历史基线。原生 read/bash/grep/find/ls 不由此目录改写描述,只有 query_tasks 是 Teammy 自定义工具,用于查询已有任务。业务提示词追加到 Pi 原生系统提示;最终输出 upsert / archive 与任务级 needsConfirmation,不生成分析级 uncertainties。旧冻结作业保留快照工具协议。详见 [任务维护设计](../../../../docs/task-agent-maintenance-v1.md)。
|
|
12
12
|
|
|
13
|
-
`thinkingLevel` 默认 off;设为其他级别时通过 Pi
|
|
13
|
+
`thinkingLevel` 默认 off;设为其他级别时通过 Pi 的通用推理参数映射传给适配器,接入点需支持对应推理能力。上下文和输出限额由网关处理,Teammy 不配置或发送这些额度。
|
|
14
|
+
|
|
15
|
+
任务整理默认 256 轮/30 分钟;目录分析与单文件摘要配置为 128 轮/20 分钟,单文件实际只有一次模型请求。各工作流单次请求 5 分钟,不限制工具调用次数,不配置累计正文读取预算。可配置上限为 384 轮、35 分钟总时限、5 分钟单次请求。旧冻结资源中的 maxReadBytes 仅供旧快照工具使用。移除 prompt 长度上限和读取覆盖门禁,输出形状仍由代码校验。详细说明见 [AI 执行规则](../../../../docs/ai-runtime-policy.md)。
|
|
14
16
|
|
|
15
17
|
真实模型效果检查按需进行,不在默认单测中调用外部模型。建议检查空目录、专题模板、自定义 README、单文档多事项和存在矛盾的资料;关注任务边界、状态依据及来源,而非固定措辞。
|
|
18
|
+
|
|
19
|
+
## 业务工具 query_tasks
|
|
20
|
+
|
|
21
|
+
当前原生 Agent 注册的 Teammy 业务工具只有 `query_tasks`,查询已有任务,不修改任务或文件。文件操作使用 Pi 原生工具。
|
|
22
|
+
|
|
23
|
+
所有参数均可省略,组合使用时取交集:
|
|
24
|
+
|
|
25
|
+
- `id`:任务 UUID,精确匹配。
|
|
26
|
+
- `source`:来源文件或目录,相对 `workspaces/`,包含成员前缀,例如 `alice/tasks`。匹配该路径及其下级路径。
|
|
27
|
+
- `query`:标题、目标、进展或来源路径的子串,不区分大小写。
|
|
28
|
+
- `visibility`:`active`、`archived` 或 `all`。默认 `active`;指定 `id` 时默认 `all`。`active` 表示未归档,也包含已完成任务。
|
|
29
|
+
|
|
30
|
+
返回 `{ items, total, omitted, note? }`。`total` 为匹配总数,`items` 最多 40 项,超出时给出省略数与提示,可缩小查询范围。每项包含 `taskId`、`title`、`goal`、`status`、`progress`、`needsConfirmation`、`sources`、`active`;来源包含 `path`、`sha256`、`revision` 与 `syncedAt`。任务表达已合并保存的人工修订。
|
|
31
|
+
|
|
32
|
+
当前数据范围是**作业创建时冻结的单成员任务**,不是实时的全项目查询;参数不支持项目或成员切换。调用范围由宿主提供的 `TaskContext` 和成员身份决定,工具自身不访问数据库,也没有同名 HTTP 路由。
|
|
33
|
+
|
|
34
|
+
参数定义见 [agent-protocol.ts](../agent-protocol.ts),查询实现见 [task-query.ts](../task-query.ts),上下文来源见 [task-context.ts](../task-context.ts),Pi 注册见 [native-pi-entry.ts](../native-pi-entry.ts),工具描述见 [shared/tools.json](shared/tools.json)。这些是当前可复用的定义与实现,不包含问答应用的接入方案。
|
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"list_files": {
|
|
3
3
|
"label": "列出文件",
|
|
4
|
-
"description": "List files in the fixed snapshot.
|
|
4
|
+
"description": "List files in the fixed snapshot. Task maintenance supports path (file/directory), query (path substring), pattern (glob), scope (member/project), version (current/baseline). Long listings are abbreviated with exact omitted counts; narrow the filters to explore further, not pagination."
|
|
5
5
|
},
|
|
6
6
|
"read_file": {
|
|
7
7
|
"label": "读取文件",
|
|
8
|
-
"description": "Read
|
|
8
|
+
"description": "Read a snapshot file as text with line numbers. Optional startLine and limit select a range; omit limit to read to the end. hasMore indicates more lines. Optional scope=project reads shared guidance. Task maintenance also supports version=baseline for the last successfully applied snapshot; default is current."
|
|
9
9
|
},
|
|
10
10
|
"search_files": {
|
|
11
11
|
"label": "搜索文件",
|
|
12
|
-
"description": "Case-insensitive literal search
|
|
12
|
+
"description": "Case-insensitive literal search of snapshot text. Returns up to 100 matching lines with full text; pass nextCursor as after to continue, including within the same file. Task maintenance supports path/directory, pattern (glob), scope and version filters."
|
|
13
|
+
},
|
|
14
|
+
"inspect_changes": {
|
|
15
|
+
"label": "查看变化",
|
|
16
|
+
"description": "Compare current files against the last successfully applied snapshot. Accepts path/directory, query (path substring), pattern (glob), scope (member/project). Returns added/modified/deleted paths and before/after line coordinates, without text. Counts of zero denote insertion/deletion anchors. Long file/range lists are abbreviated with omitted counts; narrow by path or startLine/endLine (version selects current or baseline coordinates). Unavailable diffs are explicitly marked; read_file can inspect originals."
|
|
17
|
+
},
|
|
18
|
+
"query_tasks": {
|
|
19
|
+
"label": "查询已有任务",
|
|
20
|
+
"description": "Query existing tasks of the current member by id, source (path relative to workspaces), or query (title/goal/progress/source substring). visibility=active (default), archived or all; an exact id defaults to all. Returns taskId, task fields, sources and active flag. Long results are abbreviated with an omitted count; narrow the query as needed."
|
|
13
21
|
}
|
|
14
22
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version":
|
|
2
|
+
"version": 3,
|
|
3
3
|
"thinkingLevel": "off",
|
|
4
|
-
"tools": ["
|
|
5
|
-
"maxRounds":
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"maxReadBytes": 128000
|
|
4
|
+
"tools": ["read", "bash", "grep", "find", "ls"],
|
|
5
|
+
"maxRounds": 128,
|
|
6
|
+
"timeoutMs": 1200000,
|
|
7
|
+
"requestTimeoutMs": 300000,
|
|
8
|
+
"execution": "native-sandbox"
|
|
10
9
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
理解当前目录的用途、正在开展的工作及资料之间的联系,结合用户问题整理主要发现。
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
当前资料位于 `/workspaces/<成员 ID>/`,只包含本轮选定范围,保持原始路径。通过原生文件工具和 Bash 自主探索;资料只读,`/tmp` 可用于临时分析。引用路径相对于 `/workspaces`,包含成员目录前缀。
|
|
4
|
+
|
|
5
|
+
自主选择相关资料阅读,建议提供支持结论的来源路径与行号,便于回看原文。uncertainties 是可选备注,仅说明影响结论的资料矛盾或信息缺失。
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version":
|
|
2
|
+
"version": 3,
|
|
3
3
|
"thinkingLevel": "off",
|
|
4
4
|
"tools": [],
|
|
5
|
-
"maxRounds":
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"requestTimeoutMs": 60000,
|
|
9
|
-
"maxReadBytes": 128000
|
|
5
|
+
"maxRounds": 128,
|
|
6
|
+
"timeoutMs": 1200000,
|
|
7
|
+
"requestTimeoutMs": 300000
|
|
10
8
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
|
|
1
|
+
概括当前文件的主要内容与重要事项,结合用户问题选择重点。
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
建议提供支持结论的来源路径与行号,便于回看原文。概括资料记载的事实,不补出未被记录的进展。uncertainties 是可选备注,仅说明影响结论的资料矛盾或信息缺失。
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version":
|
|
2
|
+
"version": 10,
|
|
3
3
|
"thinkingLevel": "off",
|
|
4
|
-
"tools": ["
|
|
5
|
-
"maxRounds":
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"settleSeconds": 60
|
|
4
|
+
"tools": ["read", "bash", "grep", "find", "ls", "query_tasks"],
|
|
5
|
+
"maxRounds": 256,
|
|
6
|
+
"timeoutMs": 1800000,
|
|
7
|
+
"requestTimeoutMs": 300000,
|
|
8
|
+
"settleSeconds": 60,
|
|
9
|
+
"execution": "native-sandbox"
|
|
11
10
|
}
|
|
@@ -1,13 +1,49 @@
|
|
|
1
|
-
|
|
1
|
+
## 任务
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
自主探索当前成员的工作记录,维护有依据的任务清单和进展。首次整理识别现有任务;增量整理结合变化和已有任务更新清单,避免重复处理无关历史。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## 环境与输入
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
当前目录为 `/workspaces`,仅包含本轮成员的完整目录和公共资料,保持原有组织方式:
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
```text
|
|
10
|
+
/workspaces/
|
|
11
|
+
README.md
|
|
12
|
+
_template/
|
|
13
|
+
<成员 ID>/
|
|
14
|
+
README.md
|
|
15
|
+
...成员的工作记录
|
|
16
|
+
```
|
|
10
17
|
|
|
11
|
-
|
|
18
|
+
公共 README 说明工作区用途与共同约定,模板仅作格式参考;成员及其子目录的 README 说明个人组织方式,也可能直接记录任务。文件可以缺失,成员不必遵循模板,按实际正文理解工作。
|
|
12
19
|
|
|
13
|
-
|
|
20
|
+
输入说明当前成员、整理模式及简要变化。增量时 `/baseline/workspaces` 保存上次成功应用的资料;变化列表可缩略,行号只用于定位。通过原生文件工具和 Bash 自主探索,通过 `query_tasks` 查询已有任务。
|
|
21
|
+
|
|
22
|
+
## 任务与字段
|
|
23
|
+
|
|
24
|
+
按独立工作目标识别任务,允许跨文件归并,也允许从综合记录中提取多个任务。明确提出的工作即使尚未正式建档也可纳入;讨论中的可能性、组织说明、模板和支撑数据不自动成为任务。
|
|
25
|
+
|
|
26
|
+
结合原始目标与后续进展判断现状,形成结论前检查相关记录中的验收、修订和遗留事项,避免局部摘录遗漏影响判断的信息。判断应与证据范围一致:局部修复不代表整体验收完成,未记录进展不代表尚未开始。
|
|
27
|
+
|
|
28
|
+
每项任务包含:
|
|
29
|
+
|
|
30
|
+
- `title`:简洁的任务标题,普通进展变化时保持稳定。
|
|
31
|
+
- `goal`:目标或完成边界,未明确时为 null。
|
|
32
|
+
- `status`:planned(计划)、in_progress(进行中)、in_review(等待决策或验收)、completed(整体完成);无法判断时为 null。
|
|
33
|
+
- `progress`:面向看板,优先用一两句话概括当前成果、所处阶段和影响完成的剩余事项;实现细节与无关统计保留在来源中。
|
|
34
|
+
- `needsConfirmation`:资料的歧义是否影响对任务目标、状态或进展的理解,需要人类确认;是为 true,否则为 false。已明确的待办或等待条件本身不构成歧义。仍正常填写该任务的其他字段。
|
|
35
|
+
- `sources`:支撑判断的当前成员文件,路径相对于 `/workspaces`,包含成员目录前缀。
|
|
36
|
+
|
|
37
|
+
归属、来源版本与同步日期由系统补齐。正文提及其他人不改变归属,无需生成下一步或任务关系,也无需验证记录所提及的外部代码或运行环境。
|
|
38
|
+
|
|
39
|
+
## 身份与更新
|
|
40
|
+
|
|
41
|
+
同一事项沿用查询到的 taskId,新事项使用 null。upsert 只返回需要新增或更新的完整任务,未涉及的任务由系统保留。合并重复事项或撤下已被替代的任务时,在 archive 中明确列出旧 ID;文件消失或本轮未涉及不代表应撤下。完成的任务保留为 completed。
|
|
42
|
+
|
|
43
|
+
更新关注任务表达和来源的实质变化,无需为了措辞或附带统计的变化重写任务。
|
|
44
|
+
|
|
45
|
+
## 边界与完成条件
|
|
46
|
+
|
|
47
|
+
工作区和基线只读,`/tmp` 可用于临时分析。公共资料用于理解约定,本轮只维护指定成员的任务。
|
|
48
|
+
|
|
49
|
+
提交前复核任务边界、完成判断及主要未完成部分是否与资料一致,并检查已有任务身份和来源。存在需要确认的任务不妨碍提交。最终仅返回符合 Schema 的 JSON,包含 upsert 与 archive;无需调整时两者为空数组,不附加说明文字。
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{A as e,I as t,M as n,N as r,P as i,_ as a,b as o,c as s,d as c,f as l,h as u,m as d,n as f,o as p,p as m,t as h,u as g,v as _,x as v,y}from"./index-Db4kzLA8.js";var b=t(),x=_(),S=[{status:`planned`,label:`计划`},{status:`in_progress`,label:`进行中`},{status:`in_review`,label:`待验收`},{status:`completed`,label:`已完成`},{status:null,label:`未明确`}];function C({members:e}){let{request:t}=p(),n=(0,b.useCallback)(e=>t(`tasks`,h,{signal:e}),[t]),r=o(n,3e3),[i,a]=(0,b.useState)(),[l,f]=(0,b.useState)(),m=r.data,g=m?.members.some(e=>[`queued`,`running`].includes(e.status)),_=new Map(e.map(e=>[e.id,e.displayName||e.id])),v=m?.members.filter(e=>e.sourceChanged||e.uncertainties.length||[`failed`,`cancelled`].includes(e.status))??[];return(0,x.jsxs)(`div`,{className:`task-board`,children:[(0,x.jsx)(u,{title:`工作看板`}),(0,x.jsx)(`span`,{className:`sr-only`,role:`status`,children:g?`正在整理工作区`:r.loading&&!m?`正在读取任务`:``}),(0,x.jsx)(`section`,{className:`task-board-lanes`,"aria-label":`任务进度`,"data-testid":`progress-board`,children:S.filter(e=>e.status!==null||m?.tasks.some(e=>e.status===null)).map(e=>{let t=m?.tasks.filter(t=>t.status===e.status)??[];return(0,x.jsxs)(`section`,{className:`task-lane`,"aria-label":e.label,"data-stage":e.status??`unknown`,children:[(0,x.jsxs)(`h2`,{children:[(0,x.jsx)(`span`,{className:`lane-dot`}),e.label,(0,x.jsx)(s,{children:m?t.length:`—`})]}),(0,x.jsx)(`div`,{className:`task-lane-content`,children:t.length?t.map(e=>(0,x.jsx)(w,{task:e,names:_,onEdit:()=>a(e),onSource:t=>f({jobId:e.jobId,path:t})},e.id)):(0,x.jsx)(`p`,{className:`task-lane-empty`,children:m?`暂无任务`:`读取中…`})})]},e.label)})}),(0,x.jsxs)(`details`,{className:`board-activity`,"data-testid":`task-activity`,open:r.error?!0:void 0,children:[(0,x.jsxs)(`summary`,{children:[(0,x.jsx)(c,{name:`notifications`}),(0,x.jsx)(`span`,{children:`活动与提醒`}),(v.length>0||m&&!m.modelAvailable)&&(0,x.jsx)(s,{children:v.length+(m&&!m.modelAvailable?1:0)}),(0,x.jsx)(c,{name:`chevronDown`,size:`small`})]}),(0,x.jsxs)(`div`,{className:`board-activity-content`,children:[(0,x.jsx)(d,{error:!0,children:r.error}),m&&!m.modelAvailable&&(0,x.jsxs)(`p`,{children:[`配置模型后将自动整理工作区。`,(0,x.jsx)(`a`,{href:`/site-settings`,children:`配置模型`})]}),v.map(e=>(0,x.jsxs)(`div`,{className:`task-board-member-note`,children:[(0,x.jsxs)(`div`,{children:[(0,x.jsx)(`strong`,{children:_.get(e.owner)??e.owner}),(0,x.jsx)(`p`,{children:e.status===`failed`?`整理未完成`:e.status===`cancelled`?`整理已取消`:e.sourceChanged?`资料已有更新`:`分析备注`})]}),e.uncertainties.map((e,t)=>(0,x.jsx)(`p`,{className:`task-board-note-detail`,children:e},t))]},e.owner)),m?.modelAvailable&&!v.length&&!r.error&&(0,x.jsx)(`p`,{children:`暂无提醒`})]})]}),i&&(0,x.jsx)(E,{task:i,onClose:()=>a(void 0),onSaved:()=>{a(void 0),r.reload()}},i.id),l&&(0,x.jsx)(O,{source:l,onClose:()=>f(void 0)},`${l.jobId}:${l.path}`)]})}function w({task:e,names:t,onEdit:n,onSource:r}){return(0,x.jsxs)(`article`,{className:`task-card`,"data-testid":`task-card`,"data-task-id":e.id,"data-owner":e.owner,"data-status":e.status??`unknown`,children:[(0,x.jsxs)(`div`,{className:`task-card-heading`,children:[(0,x.jsx)(`h3`,{children:e.title}),(0,x.jsx)(l,{icon:`edit`,className:`task-card-edit`,label:`纠正任务:${e.title}`,"data-testid":`task-correct`,onClick:n})]}),(0,x.jsxs)(`small`,{children:[t.get(e.owner)??e.owner,e.corrected?` · 已纠正`:``,e.needsConfirmation?` · 待确认`:``]}),(0,x.jsx)(`p`,{className:`task-progress`,children:(0,x.jsx)(T,{value:e.progress,names:t})}),(0,x.jsxs)(`details`,{className:`task-card-details`,children:[(0,x.jsxs)(`summary`,{children:[`查看详情`,(0,x.jsx)(c,{name:`chevronDown`,size:`small`})]}),e.needsReview&&(0,x.jsx)(`p`,{children:`本次整理未能明确对应,请核对来源。`}),(0,x.jsx)(`p`,{children:e.goal?(0,x.jsx)(T,{value:e.goal,names:t}):`目标未明确`}),(0,x.jsx)(`div`,{className:`task-card-sources`,children:e.sources.map(e=>(0,x.jsxs)(`button`,{"data-testid":`task-source`,onClick:()=>r(e.path),children:[(0,x.jsx)(c,{name:`file`,size:`small`}),e.path]},e.path))}),e.syncedAt&&(0,x.jsxs)(`small`,{children:[`资料同步于 `,(0,x.jsx)(a,{value:e.syncedAt})]})]})]})}function T({value:e,names:t}){return e.split(/(@[a-z][a-z0-9_-]*)/gi).map((e,n)=>e.startsWith(`@`)&&t.has(e.slice(1))?(0,x.jsx)(`mark`,{className:`task-mention`,children:e},n):e)}function E({task:e,onClose:t,onSaved:n}){let{request:r}=p(),i=y(),[a,o]=(0,b.useState)(e.title),[s,c]=(0,b.useState)(e.status??``),[l,u]=(0,b.useState)(e.goal??``),[h,_]=(0,b.useState)(e.progress),[C,w]=(0,b.useState)(e.needsConfirmation),[T,E]=(0,b.useState)(!1),[D,O]=(0,b.useState)(``),k={...a===e.title?{}:{title:a},...s===(e.status??``)?{}:{status:s||null},...l===(e.goal??``)?{}:{goal:l||null},...h===e.progress?{}:{progress:h},...C===e.needsConfirmation?{}:{needsConfirmation:C}};async function A(t=!1){if(!(T||!t&&!Object.keys(k).length)){E(!0),O(``);try{await r(`tasks/${e.id}/correction`,f,{method:t?`DELETE`:`PATCH`,signal:i,...t?{}:{body:k}}),i.aborted||n()}catch(e){i.aborted||O(v(e))}finally{E(!1)}}}return(0,x.jsx)(m,{id:`task-correction`,title:`纠正任务`,onClose:t,children:(0,x.jsxs)(`form`,{className:`stack`,"aria-busy":T,onSubmit:e=>{e.preventDefault(),A()},children:[(0,x.jsx)(g,{id:`task-title`,label:`标题`,children:(0,x.jsx)(`input`,{value:a,required:!0,onChange:e=>o(e.target.value)})}),(0,x.jsx)(g,{id:`task-status`,label:`状态`,children:(0,x.jsx)(`select`,{value:s,onChange:e=>c(e.target.value),children:S.map(e=>(0,x.jsx)(`option`,{value:e.status??``,children:e.label},e.label))})}),(0,x.jsx)(g,{id:`task-goal`,label:`目标`,children:(0,x.jsx)(`textarea`,{value:l,onChange:e=>u(e.target.value)})}),(0,x.jsx)(g,{id:`task-progress`,label:`当前进展`,children:(0,x.jsx)(`textarea`,{value:h,onChange:e=>_(e.target.value)})}),(0,x.jsxs)(`label`,{className:`row`,children:[(0,x.jsx)(`input`,{type:`checkbox`,checked:C,onChange:e=>w(e.target.checked)}),`需要确认`]}),(0,x.jsx)(`small`,{children:`仅调整看板,不修改原始文件。`}),(0,x.jsx)(d,{error:!0,children:D}),(0,x.jsxs)(`div`,{className:`row wrap`,children:[(0,x.jsx)(`button`,{type:`submit`,disabled:T||!Object.keys(k).length,"data-testid":`task-correction-save`,children:`保存`}),e.corrected&&(0,x.jsx)(`button`,{type:`button`,disabled:T,"data-testid":`task-correction-reset`,onClick:()=>void A(!0),children:`恢复自动结果`})]})]})})}var D=r({content:i(),endLine:n(),hasMore:e()});function O({source:e,onClose:t}){let{request:n}=p(),[r,i]=(0,b.useState)(1),[a,s]=(0,b.useState)([]),c=(0,b.useCallback)(t=>n(`ai/jobs/${e.jobId}/source?${new URLSearchParams({path:e.path,startLine:String(r)})}`,D,{signal:t}),[n,e.jobId,e.path,r]),l=o(c);return(0,x.jsxs)(m,{id:`task-source-dialog`,title:e.path,onClose:t,children:[(0,x.jsx)(`small`,{children:`整理时的资料版本`}),(0,x.jsx)(d,{error:!0,children:l.error}),(0,x.jsx)(`pre`,{className:`raw task-source-content`,"data-testid":`task-source-content`,children:l.loading?`加载中…`:l.data?.content}),(0,x.jsxs)(`div`,{className:`row`,children:[(0,x.jsx)(`button`,{disabled:l.loading||!a.length,onClick:()=>{i(a.at(-1)),s(e=>e.slice(0,-1))},children:`上一段`}),(0,x.jsx)(`button`,{disabled:l.loading||!!l.error||!l.data?.hasMore,onClick:()=>{s(e=>[...e,r]),i(l.data.endLine+1)},children:`下一段`})]})]})}function k({members:e}){return(0,x.jsx)(C,{members:e})}export{k as Dashboard};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{I as e,S as t,a as n,b as r,d as i,f as a,g as o,i as s,l as c,m as l,o as u,r as d,v as f,w as p}from"./index-Db4kzLA8.js";import{n as m,r as h,t as g}from"./Markdown-DQn2KtR3.js";var _=e();function v(e,t){if(!t||/^(?:[a-z][a-z0-9+.-]*:|\/)/i.test(t))return null;try{let n=t.split(/[?#]/,1)[0],r=n?e.split(`/`).slice(0,-1):e.split(`/`);for(let e of n.split(`/`)){let t=decodeURIComponent(e);if(t&&t!==`.`){if(t===`..`){if(!r.length)return null;r.pop()}else{if(t.includes(`/`)||t.includes(`\\`))return null;r.push(t)}}}return{path:r.length?p.parse(r.join(`/`)):``,directory:n.endsWith(`/`),anchor:t.includes(`#`)?decodeURIComponent(t.slice(t.indexOf(`#`)+1)):``}}catch{return null}}var y=f();function b({members:e,me:i,onRefresh:o}){let{api:f}=u(),[p,g]=(0,_.useState)(()=>new Set([h(i,``)])),[b,S]=(0,_.useState)(),[C,w]=(0,_.useState)(),[T,E]=(0,_.useState)(0),[D,O]=(0,_.useState)(``),k=(0,_.useCallback)(async e=>b?{owner:b.owner,...await f.file(b.owner,b.path,void 0,e)}:void 0,[b?.owner,b?.path,T]),A=r(k),j=A.data?.owner===b?.owner&&A.data?.file.path===b?.path?A.data:void 0;function M(e,t){let n=t.split(`/`).filter(Boolean);g(t=>{let r=new Set(t);for(let t=0;t<=n.length;t++)r.add(h(e,n.slice(0,t).join(`/`)));return r})}function N(e,t){O(``),M(e,t.split(`/`).slice(0,-1).join(`/`)),S({owner:e,path:t}),w({owner:e,path:t,kind:`file`})}function P(t){if(!b)return!1;let n=v(b.owner===`_project`?b.path:`${b.owner}/${b.path}`,t);if(!n)return!/^[a-z][a-z0-9+.-]*:/i.test(t)&&!t.startsWith(`//`)&&(O(`该链接超出工作区,请在原仓库查看。`),!0);if(s(n.path))return O(`配置文件不在文件浏览中显示。`),!0;let[r,...i]=n.path.split(`/`),a=e.some(e=>e.id===r),o=a?r:d,c=a?i.join(`/`):n.path;return O(``),n.directory?(M(o,c),w({owner:o,path:c,kind:`directory`})):o===b.owner&&c===b.path?n.anchor&&[...document.querySelectorAll(`.markdown h1,.markdown h2,.markdown h3,.markdown h4`)].find(e=>e.textContent?.trim().toLowerCase().replace(/\s+/g,`-`)===n.anchor.toLowerCase())?.scrollIntoView({block:`center`}):N(o,c),!0}return(0,y.jsxs)(`div`,{className:`split-layout file-explorer`,"data-testid":`file-browser`,children:[(0,y.jsxs)(`aside`,{className:`file-tree-pane`,"aria-label":`文件资源管理器`,children:[(0,y.jsx)(n,{title:`文件`,icon:`files`,children:(0,y.jsx)(a,{icon:`refresh`,label:`刷新文件`,"data-testid":`files-refresh`,onClick:()=>{O(``),E(e=>e+1),o()}})}),(0,y.jsx)(m,{members:e,expanded:p,selected:C,revealed:!A.loading&&!A.error&&j?{owner:j.owner,path:j.file.path,file:j.file}:void 0,onSelect:N,onToggle:(e,t)=>{w({owner:e,path:t,kind:`directory`}),g(n=>{let r=new Set(n),i=h(e,t);return r.has(i)?r.delete(i):r.add(i),r})}},T)]}),(0,y.jsx)(`section`,{className:`file-detail`,"data-testid":`file-preview`,"data-owner-id":b?.owner,"data-file-path":b?.path,"aria-label":t.filePreview,children:b?(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)(l,{error:!0,children:D||!A.loading&&A.error}),A.error&&(0,y.jsx)(`button`,{className:`file-entry-retry`,"data-testid":`files-retry`,onClick:()=>void A.reload(),children:t.retry}),A.loading?(0,y.jsx)(`p`,{className:`preview-body`,role:`status`,children:t.loading}):!A.error&&j&&(0,y.jsx)(x,{owner:j.owner,file:j.file,onLink:P},`${j.owner}:${j.file.path}:${j.file.sha256}`)]}):(0,y.jsx)(c,{mascot:!0,title:`从一份资料开始`,description:t.pickFile})})]})}function x({owner:e,file:n,onLink:s}){let{readTextBlob:c,blobUrl:d}=u(),[f,p]=(0,_.useState)(!1),m=n.size>524288,h=/\.(md|markdown)$/i.test(n.path),v=(0,_.useCallback)(e=>m?Promise.resolve(void 0):c(n,e),[n.sha256,n.size,m]),b=r(v);return(0,y.jsxs)(y.Fragment,{children:[(0,y.jsxs)(`header`,{className:`file-detail-header`,children:[(0,y.jsxs)(`span`,{className:`file-detail-path`,title:`${e===`_project`?`workspaces`:e}/${n.path}`,children:[(0,y.jsxs)(`span`,{children:[e===`_project`?`workspaces`:e,` / `]}),n.path]}),(0,y.jsxs)(o,{label:`文件操作`,children:[h&&(0,y.jsx)(a,{icon:f?`preview`:`code`,label:f?`切换为预览`:`切换为原文`,"data-testid":`file-toggle-raw`,"aria-pressed":f,onClick:()=>p(!f)}),(0,y.jsx)(`a`,{className:`icon-button`,"data-testid":`file-download`,"aria-label":t.download,title:t.download,href:d(n.sha256),download:n.path.split(`/`).at(-1),children:(0,y.jsx)(i,{name:`download`})})]})]}),m?(0,y.jsx)(`div`,{className:`empty`,children:(0,y.jsx)(`p`,{children:t.previewLarge})}):b.error?(0,y.jsxs)(`div`,{className:`preview-body`,children:[(0,y.jsx)(l,{error:!0,children:b.error}),(0,y.jsx)(`button`,{"data-testid":`file-preview-retry`,onClick:()=>void b.reload(),children:t.retry})]}):b.loading?(0,y.jsx)(`p`,{className:`preview-body`,role:`status`,children:t.loading}):(0,y.jsx)(`div`,{className:`preview-body`,children:h&&!f?(0,y.jsx)(`article`,{className:`markdown`,"data-testid":`file-content`,"data-format":`markdown`,children:(0,y.jsx)(g,{onLink:s,children:b.data??``})}):(0,y.jsx)(`pre`,{className:`raw`,"data-testid":`file-content`,"data-format":`text`,children:b.data})})]})}export{b as Files};
|