@teammy/server 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/main.js +1410 -350
- package/dist/project-pi-entry.js +5 -3
- package/dist/web/assets/Assistant-BeYamS0w.js +1 -0
- package/dist/web/assets/Assistant-C7QfFYLo.css +1 -0
- package/dist/web/assets/Dashboard-BzmrE4Xv.js +1 -0
- package/dist/web/assets/Files-BvCYgjVV.js +1 -0
- package/dist/web/assets/{Markdown-D3cU0ani.js → Markdown-BRPMsc7D.js} +4 -4
- package/dist/web/assets/Materials-DK3CkLr0.js +6 -0
- package/dist/web/assets/Sessions-D_579m-e.js +1 -0
- package/dist/web/assets/WorkspaceTree-fMDtGIFa.js +1 -0
- package/dist/web/assets/{file-links-GfCXWkEh.js → file-links-I1K8CwVK.js} +1 -1
- package/dist/web/assets/index-DQsx-e5d.js +66 -0
- package/dist/web/downloads/teammy-skill.zip +0 -0
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/dist/web/assets/Assistant-4yZWjYm-.css +0 -1
- package/dist/web/assets/Assistant-BNVXOHQZ.js +0 -1
- package/dist/web/assets/Dashboard-zKTK6EZD.js +0 -1
- package/dist/web/assets/Files-DI98WkdW.js +0 -1
- package/dist/web/assets/Materials-CWptgAHz.js +0 -6
- package/dist/web/assets/Sessions-DiDwqXLI.js +0 -1
- package/dist/web/assets/WorkspaceTree-DeAA1QtV.js +0 -1
- package/dist/web/assets/index-BwEl1PIa.js +0 -66
package/dist/main.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// apps/server/ai/snapshots.ts
|
|
2
|
-
import { createHash as
|
|
2
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
3
3
|
|
|
4
4
|
// apps/server/files.ts
|
|
5
|
-
import { createHash as
|
|
5
|
+
import { createHash as createHash8, randomUUID as randomUUID12 } from "node:crypto";
|
|
6
6
|
|
|
7
7
|
// packages/protocol/browse-order.ts
|
|
8
8
|
var names = new Intl.Collator("zh-CN", { numeric: true, sensitivity: "base" });
|
|
@@ -549,7 +549,7 @@ var SiteRepository = class {
|
|
|
549
549
|
};
|
|
550
550
|
|
|
551
551
|
// apps/server/workspace-import.ts
|
|
552
|
-
import { randomUUID as
|
|
552
|
+
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
553
553
|
import { constants as constants2 } from "node:fs";
|
|
554
554
|
import { lstat, open as open3, readdir } from "node:fs/promises";
|
|
555
555
|
import { isAbsolute, join as join9 } from "node:path";
|
|
@@ -609,7 +609,7 @@ function resolveReference(hub, project, ref, task = null) {
|
|
|
609
609
|
}
|
|
610
610
|
|
|
611
611
|
// apps/server/agents/matters.ts
|
|
612
|
-
import { randomUUID as
|
|
612
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
613
613
|
import { z as z14 } from "zod";
|
|
614
614
|
|
|
615
615
|
// apps/server/diagnostics/store.ts
|
|
@@ -902,11 +902,12 @@ import { join as join3 } from "node:path";
|
|
|
902
902
|
|
|
903
903
|
// packages/protocol/project-agents.ts
|
|
904
904
|
import { z as z9 } from "zod";
|
|
905
|
-
var AgentTextSchema = z9.object({ text: z9.string().trim().min(1) }).strict();
|
|
906
|
-
var AgentReplySchema = z9.object({ body: z9.string().trim().min(1) }).strict();
|
|
905
|
+
var AgentTextSchema = z9.object({ text: z9.string().trim().min(1), requestId: z9.string().min(1).optional() }).strict();
|
|
906
|
+
var AgentReplySchema = z9.object({ body: z9.string().trim().min(1), requestId: z9.string().min(1).optional() }).strict();
|
|
907
907
|
var OutboxSendSchema = z9.object({
|
|
908
908
|
to: z9.union([z9.literal("debug"), z9.array(NameSchema).min(1)]),
|
|
909
|
-
body: z9.string().trim().min(1)
|
|
909
|
+
body: z9.string().trim().min(1),
|
|
910
|
+
branch: z9.string().trim().min(1).optional()
|
|
910
911
|
}).strict();
|
|
911
912
|
var interval = z9.number().int().positive().nullable();
|
|
912
913
|
var GitProviderSchema = z9.enum(["github", "gongfeng"]);
|
|
@@ -961,10 +962,21 @@ var AgentRunSchema = z9.object({
|
|
|
961
962
|
error: z9.string().nullable(),
|
|
962
963
|
createdAt: z9.string()
|
|
963
964
|
});
|
|
965
|
+
var AgentMessageSchema = z9.object({
|
|
966
|
+
id: z9.string(),
|
|
967
|
+
seq: z9.number(),
|
|
968
|
+
runId: z9.string().nullable(),
|
|
969
|
+
role: z9.enum(["user", "assistant"]),
|
|
970
|
+
author: z9.string(),
|
|
971
|
+
body: z9.string(),
|
|
972
|
+
state: AgentRunSchema.shape.state,
|
|
973
|
+
source: z9.string().nullable(),
|
|
974
|
+
createdAt: z9.string()
|
|
975
|
+
});
|
|
964
976
|
var AgentChatSummarySchema = z9.object({
|
|
965
977
|
id: z9.string(),
|
|
966
978
|
title: z9.string(),
|
|
967
|
-
kind: z9.
|
|
979
|
+
kind: z9.enum(["matter", "notice"]).optional(),
|
|
968
980
|
status: z9.enum(["active", "waiting", "completed"]).optional(),
|
|
969
981
|
unread: z9.boolean().optional(),
|
|
970
982
|
createdAt: z9.string(),
|
|
@@ -976,7 +988,13 @@ var AgentChatSchema = z9.object({
|
|
|
976
988
|
id: z9.string(),
|
|
977
989
|
title: z9.string().optional(),
|
|
978
990
|
runs: z9.array(AgentRunSchema),
|
|
979
|
-
|
|
991
|
+
messages: z9.array(AgentMessageSchema),
|
|
992
|
+
readCursor: z9.number(),
|
|
993
|
+
redirect: z9.string().nullable().optional(),
|
|
994
|
+
kind: z9.enum(["matter", "notice"]).optional(),
|
|
995
|
+
status: z9.enum(["active", "waiting", "completed"]).optional(),
|
|
996
|
+
pending: z9.object({ members: z9.array(z9.string()), question: z9.string() }).nullable().optional(),
|
|
997
|
+
hold: z9.object({ scope: z9.enum(["all", "writes"]), reason: z9.string() }).passthrough().nullable().optional(),
|
|
980
998
|
participants: z9.array(z9.string()).optional(),
|
|
981
999
|
visibility: z9.enum(["project", "participants"]).optional(),
|
|
982
1000
|
cursor: z9.number()
|
|
@@ -989,10 +1007,10 @@ var OutboxMessageSchema = z9.object({
|
|
|
989
1007
|
createdAt: z9.string(),
|
|
990
1008
|
readAt: z9.string().nullable(),
|
|
991
1009
|
archivedAt: z9.string().nullable(),
|
|
1010
|
+
conversationId: z9.string().nullable(),
|
|
992
1011
|
recipients: z9.array(
|
|
993
1012
|
z9.object({
|
|
994
|
-
userId: z9.string()
|
|
995
|
-
reply: z9.object({ body: z9.string(), createdAt: z9.string() }).nullable()
|
|
1013
|
+
userId: z9.string()
|
|
996
1014
|
})
|
|
997
1015
|
)
|
|
998
1016
|
});
|
|
@@ -1128,6 +1146,11 @@ var SessionCommitSchema = z11.object({
|
|
|
1128
1146
|
parentRevision: RevisionSchema,
|
|
1129
1147
|
blob: BlobRefSchema
|
|
1130
1148
|
}).strict();
|
|
1149
|
+
var SessionBaselineSchema = z11.object({
|
|
1150
|
+
revision: RevisionSchema,
|
|
1151
|
+
hash: HashSchema.nullable(),
|
|
1152
|
+
cleared: z11.boolean()
|
|
1153
|
+
});
|
|
1131
1154
|
var SessionInfoSchema = z11.object({
|
|
1132
1155
|
owner: NameSchema,
|
|
1133
1156
|
sessionKey: HashSchema,
|
|
@@ -1891,8 +1914,10 @@ async function runSessionAnalysisLoop(hub, stop, once = false) {
|
|
|
1891
1914
|
|
|
1892
1915
|
// apps/server/agents/inputs.ts
|
|
1893
1916
|
var fingerprint = (value) => createHash4("sha256").update(JSON.stringify(value)).digest("hex");
|
|
1894
|
-
function enqueueInput(hub, project, text, sourceKey = null,
|
|
1917
|
+
function enqueueInput(hub, project, text, sourceKey = null, details2 = {}) {
|
|
1895
1918
|
return diagnosticTransaction(hub, () => {
|
|
1919
|
+
if (sourceKey && hub.db.prepare("SELECT 1 FROM reset_event_keys WHERE project_id=? AND source_key=?").get(project, sourceKey))
|
|
1920
|
+
return 0;
|
|
1896
1921
|
const row = hub.db.prepare(
|
|
1897
1922
|
`INSERT INTO project_agent_inbox(project_id,task_id,text,source_key,created_at)
|
|
1898
1923
|
SELECT ?,COALESCE(MAX(task_id),0)+1,?,?,? FROM project_agent_inbox WHERE project_id=?
|
|
@@ -1901,18 +1926,23 @@ function enqueueInput(hub, project, text, sourceKey = null, details = {}) {
|
|
|
1901
1926
|
const taskId = Number(
|
|
1902
1927
|
row?.task_id ?? hub.db.prepare("SELECT task_id FROM project_agent_inbox WHERE project_id=? AND source_key=?").get(project, sourceKey)?.task_id
|
|
1903
1928
|
);
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1929
|
+
details2 = {
|
|
1930
|
+
...details2,
|
|
1931
|
+
origin: details2.origin ?? { kind: "event" },
|
|
1932
|
+
routing: details2.routing ?? { inputKey: sourceKey ?? `inbox:${project}:${taskId}` }
|
|
1933
|
+
};
|
|
1934
|
+
if (row)
|
|
1935
|
+
hub.db.prepare("INSERT INTO agent_input_details VALUES(?,?,?)").run(project, taskId, JSON.stringify(details2));
|
|
1936
|
+
const refs = Object.entries(details2.references ?? {}).filter(([, target]) => target.kind !== "matter").map(([ref]) => ref);
|
|
1937
|
+
if (row && !details2.routing?.excluded?.length && refs.length && refs.length === Object.keys(details2.references ?? {}).length) {
|
|
1908
1938
|
const targets = hub.db.prepare(
|
|
1909
|
-
`SELECT m.conversation_id FROM agent_matters m JOIN agent_matter_sources s ON s.conversation_id=m.conversation_id
|
|
1910
|
-
WHERE m.project_id=? AND m.status<>'completed' AND s.ref IN (${refs.map(() => "?").join(",")})
|
|
1911
|
-
GROUP BY m.conversation_id
|
|
1912
|
-
).all(project, ...refs
|
|
1913
|
-
if (targets.length) {
|
|
1939
|
+
`SELECT m.conversation_id,COUNT(DISTINCT s.ref) n FROM agent_matters m JOIN agent_matter_sources s ON s.conversation_id=m.conversation_id
|
|
1940
|
+
WHERE m.project_id=? AND m.status<>'completed' AND s.relation='follow' AND s.ref IN (${refs.map(() => "?").join(",")})
|
|
1941
|
+
GROUP BY m.conversation_id`
|
|
1942
|
+
).all(project, ...refs);
|
|
1943
|
+
if (targets.length === 1 && Number(targets[0].n) === refs.length) {
|
|
1914
1944
|
for (const target of targets)
|
|
1915
|
-
new Matters(hub).receive(String(target.conversation_id), text, "",
|
|
1945
|
+
new Matters(hub).receive(String(target.conversation_id), text, "", details2);
|
|
1916
1946
|
hub.db.prepare(
|
|
1917
1947
|
"UPDATE project_agent_inbox SET state='succeeded' WHERE project_id=? AND task_id=?"
|
|
1918
1948
|
).run(project, taskId);
|
|
@@ -2016,7 +2046,10 @@ function recordTaskChanges(hub, project, additionalText = "", owner = "") {
|
|
|
2016
2046
|
"\n\n"
|
|
2017
2047
|
),
|
|
2018
2048
|
null,
|
|
2019
|
-
{
|
|
2049
|
+
{
|
|
2050
|
+
references,
|
|
2051
|
+
coalesce: { source: `tasks:${Object.keys(references).sort().join(",")}`, mode: "changes" }
|
|
2052
|
+
}
|
|
2020
2053
|
);
|
|
2021
2054
|
}
|
|
2022
2055
|
function recordSessionChange(hub, project, owner, key, revision) {
|
|
@@ -2039,7 +2072,7 @@ function recordSessionChange(hub, project, owner, key, revision) {
|
|
|
2039
2072
|
[${ref}] ${info?.title || "\u672A\u547D\u540D\u5BF9\u8BDD"}
|
|
2040
2073
|
${summary}`,
|
|
2041
2074
|
`session:${owner}:${key}:${revision}`,
|
|
2042
|
-
{ references: { [ref]: target } }
|
|
2075
|
+
{ references: { [ref]: target }, coalesce: { source: `session:${ref}`, mode: "latest" } }
|
|
2043
2076
|
);
|
|
2044
2077
|
}
|
|
2045
2078
|
|
|
@@ -2190,8 +2223,8 @@ var diagnosticCodes = {
|
|
|
2190
2223
|
AI_ANALYSIS_FAILED: ["\u8D44\u6599\u5206\u6790\u672A\u5B8C\u6210", "\u8BF7\u68C0\u67E5\u672C\u6B21\u4F5C\u4E1A\u65E5\u5FD7\u3002"],
|
|
2191
2224
|
TASK_SOURCE_WARNING: ["\u4EFB\u52A1\u6765\u6E90\u5173\u8054\u5F02\u5E38", "\u8BF7\u68C0\u67E5\u65E5\u5FD7\u4E2D\u7684\u6765\u6E90\u8DEF\u5F84\u53CA\u5BF9\u5E94\u8D44\u6599\u3002"],
|
|
2192
2225
|
LIFETIME_FAILED: ["\u540E\u53F0\u9879\u76EE\u5206\u6790\u672A\u5B8C\u6210", "\u672C\u6B21\u8F93\u5165\u5DF2\u7ED3\u675F\uFF0C\u540E\u7EED\u8F93\u5165\u5C06\u7EE7\u7EED\u5904\u7406\u3002"],
|
|
2193
|
-
CONSTANT_FAILED: ["\
|
|
2194
|
-
INSTANT_FAILED: ["\
|
|
2226
|
+
CONSTANT_FAILED: ["Tim\u56DE\u7B54\u672A\u5B8C\u6210", "\u7528\u6237\u5DF2\u5728\u804A\u5929\u7A97\u53E3\u6536\u5230\u5931\u8D25\u63D0\u793A\uFF0C\u8BF7\u68C0\u67E5\u8FD0\u884C\u65E5\u5FD7\u3002"],
|
|
2227
|
+
INSTANT_FAILED: ["Tim\u56DE\u7B54\u672A\u5B8C\u6210", "\u7528\u6237\u5DF2\u5728\u804A\u5929\u7A97\u53E3\u6536\u5230\u5931\u8D25\u63D0\u793A\uFF0C\u8BF7\u68C0\u67E5\u8FD0\u884C\u65E5\u5FD7\u3002"],
|
|
2195
2228
|
REPOSITORY_FAILED: ["\u53C2\u8003\u4ED3\u5E93\u540C\u6B65\u5931\u8D25", "\u8BF7\u68C0\u67E5\u4ED3\u5E93\u8FDE\u63A5\u548C\u51ED\u8BC1\uFF1B\u4EFB\u52A1\u53CA\u4F1A\u8BDD\u5E93\u8D44\u6599\u4ECD\u53EF\u4F7F\u7528\u3002"],
|
|
2196
2229
|
SANDBOX_FAILED: ["\u9879\u76EE\u8FD0\u884C\u73AF\u5883\u51C6\u5907\u5931\u8D25", "\u8BF7\u68C0\u67E5\u6C99\u7BB1\u4F9D\u8D56\u3001\u6587\u4EF6\u6743\u9650\u53CA\u8FD0\u884C\u65E5\u5FD7\u3002"],
|
|
2197
2230
|
PR_OPERATION_FAILED: ["PR \u5904\u7406\u672A\u5B8C\u6210", "\u8BF7\u68C0\u67E5\u4ED3\u5E93\u6743\u9650\u548C PR \u63A5\u53E3\uFF1B\u5931\u8D25\u4E0D\u4EE3\u8868\u5BA1\u6838\u901A\u8FC7\u3002"],
|
|
@@ -2433,6 +2466,106 @@ function maintainDiagnostics(hub) {
|
|
|
2433
2466
|
};
|
|
2434
2467
|
}
|
|
2435
2468
|
|
|
2469
|
+
// apps/server/agents/messages.ts
|
|
2470
|
+
import { createHash as createHash6, randomUUID as randomUUID4 } from "node:crypto";
|
|
2471
|
+
var AgentMessages = class {
|
|
2472
|
+
constructor(hub) {
|
|
2473
|
+
this.hub = hub;
|
|
2474
|
+
}
|
|
2475
|
+
hub;
|
|
2476
|
+
view(row) {
|
|
2477
|
+
return AgentMessageSchema.parse({
|
|
2478
|
+
id: row.id,
|
|
2479
|
+
seq: Number(row.seq),
|
|
2480
|
+
runId: row.run_id,
|
|
2481
|
+
role: row.role,
|
|
2482
|
+
author: row.author,
|
|
2483
|
+
body: row.body,
|
|
2484
|
+
state: row.state,
|
|
2485
|
+
source: row.source,
|
|
2486
|
+
createdAt: row.created_at
|
|
2487
|
+
});
|
|
2488
|
+
}
|
|
2489
|
+
list(conversation, after = 0, limit) {
|
|
2490
|
+
const rows = limit === void 0 ? this.hub.db.prepare("SELECT * FROM agent_messages WHERE conversation_id=? AND seq>? ORDER BY seq").all(conversation, after) : this.hub.db.prepare(
|
|
2491
|
+
"SELECT * FROM agent_messages WHERE conversation_id=? AND seq>? ORDER BY seq LIMIT ?"
|
|
2492
|
+
).all(conversation, after, limit);
|
|
2493
|
+
return rows.map((row) => this.view(row));
|
|
2494
|
+
}
|
|
2495
|
+
visible(conversation) {
|
|
2496
|
+
return !!this.hub.db.prepare(
|
|
2497
|
+
`SELECT 1 FROM project_agent_conversations c LEFT JOIN agent_contexts x ON x.conversation_id=c.id
|
|
2498
|
+
WHERE c.id=? AND c.role='instant' AND (x.kind IS NULL OR x.kind IN('matter','notice'))`
|
|
2499
|
+
).get(conversation);
|
|
2500
|
+
}
|
|
2501
|
+
put(input) {
|
|
2502
|
+
if (!this.visible(input.conversation)) return;
|
|
2503
|
+
const date = (/* @__PURE__ */ new Date()).toISOString();
|
|
2504
|
+
const row = this.hub.db.prepare(
|
|
2505
|
+
`INSERT INTO agent_messages(id,conversation_id,run_id,role,author,body,state,source,operation_key,created_at,updated_at)
|
|
2506
|
+
VALUES(?,?,?,?,?,?,?,?,?,?,?) ON CONFLICT(operation_key) DO UPDATE SET
|
|
2507
|
+
seq=CASE WHEN agent_messages.state<>'succeeded' AND excluded.state='succeeded' THEN excluded.seq ELSE agent_messages.seq END,
|
|
2508
|
+
body=excluded.body,state=excluded.state,updated_at=excluded.updated_at RETURNING *`
|
|
2509
|
+
).get(
|
|
2510
|
+
randomUUID4(),
|
|
2511
|
+
input.conversation,
|
|
2512
|
+
input.runId ?? null,
|
|
2513
|
+
input.role,
|
|
2514
|
+
input.author ?? "",
|
|
2515
|
+
input.body,
|
|
2516
|
+
input.state ?? "succeeded",
|
|
2517
|
+
input.source ?? null,
|
|
2518
|
+
input.key,
|
|
2519
|
+
date,
|
|
2520
|
+
date
|
|
2521
|
+
);
|
|
2522
|
+
const value = this.view(row);
|
|
2523
|
+
this.hub.db.prepare("INSERT INTO project_agent_stream(conversation_id,kind,data) VALUES(?,?,?)").run(input.conversation, "message", JSON.stringify(value));
|
|
2524
|
+
return value;
|
|
2525
|
+
}
|
|
2526
|
+
input(conversation, runId, text, author, source) {
|
|
2527
|
+
return this.put({
|
|
2528
|
+
conversation,
|
|
2529
|
+
runId,
|
|
2530
|
+
body: text,
|
|
2531
|
+
author,
|
|
2532
|
+
source,
|
|
2533
|
+
role: "user",
|
|
2534
|
+
key: `input:${runId}`
|
|
2535
|
+
});
|
|
2536
|
+
}
|
|
2537
|
+
answer(conversation, runId, body, state) {
|
|
2538
|
+
return this.put({
|
|
2539
|
+
conversation,
|
|
2540
|
+
runId,
|
|
2541
|
+
body,
|
|
2542
|
+
state,
|
|
2543
|
+
role: "assistant",
|
|
2544
|
+
key: `answer:${runId}`
|
|
2545
|
+
});
|
|
2546
|
+
}
|
|
2547
|
+
delta(runId, text) {
|
|
2548
|
+
this.hub.db.prepare(
|
|
2549
|
+
"UPDATE agent_messages SET body=body||?,state='running',updated_at=? WHERE operation_key=?"
|
|
2550
|
+
).run(text, (/* @__PURE__ */ new Date()).toISOString(), `answer:${runId}`);
|
|
2551
|
+
}
|
|
2552
|
+
request(key, input, work) {
|
|
2553
|
+
return diagnosticTransaction(this.hub, () => {
|
|
2554
|
+
if (!key) return work();
|
|
2555
|
+
const fingerprint3 = createHash6("sha256").update(JSON.stringify(input)).digest("hex");
|
|
2556
|
+
const old = this.hub.db.prepare("SELECT * FROM agent_message_requests WHERE request_key=?").get(key);
|
|
2557
|
+
if (old) {
|
|
2558
|
+
if (old.fingerprint !== fingerprint3)
|
|
2559
|
+
throw new HubError(409, "OPERATION_MISMATCH", "\u8BE5\u8BF7\u6C42\u6807\u8BC6\u5DF2\u7528\u4E8E\u53E6\u4E00\u6761\u6D88\u606F\u3002");
|
|
2560
|
+
return JSON.parse(String(old.result_json));
|
|
2561
|
+
}
|
|
2562
|
+
const result = work();
|
|
2563
|
+
this.hub.db.prepare("INSERT INTO agent_message_requests VALUES(?,?,?)").run(key, fingerprint3, JSON.stringify(result));
|
|
2564
|
+
return result;
|
|
2565
|
+
});
|
|
2566
|
+
}
|
|
2567
|
+
};
|
|
2568
|
+
|
|
2436
2569
|
// apps/server/agents/matters.ts
|
|
2437
2570
|
var DelegateMatterSchema = z14.object({
|
|
2438
2571
|
ref: z14.string().optional(),
|
|
@@ -2440,11 +2573,22 @@ var DelegateMatterSchema = z14.object({
|
|
|
2440
2573
|
participants: z14.array(NameSchema).default([]),
|
|
2441
2574
|
text: z14.string().trim().min(1),
|
|
2442
2575
|
sources: z14.array(z14.string()).default([]),
|
|
2576
|
+
follows: z14.array(z14.string()).default([]),
|
|
2577
|
+
branch: z14.string().trim().min(1).optional(),
|
|
2443
2578
|
visibility: z14.enum(["project", "participants"]).optional()
|
|
2444
2579
|
}).strict();
|
|
2445
2580
|
var ReportMatterSchema = z14.object({
|
|
2581
|
+
kind: z14.enum(["progress", "coordination", "result"]),
|
|
2446
2582
|
status: z14.enum(["active", "waiting", "completed"]),
|
|
2447
|
-
summary: z14.string().trim().min(1)
|
|
2583
|
+
summary: z14.string().trim().min(1),
|
|
2584
|
+
pending: z14.object({ members: z14.array(NameSchema), question: z14.string().trim().min(1) }).strict().nullable().optional(),
|
|
2585
|
+
request: z14.string().trim().min(1).optional()
|
|
2586
|
+
}).strict().refine((value) => value.kind !== "coordination" || !!value.request, "\u534F\u8C03\u8BF7\u6C42\u9700\u8981\u5177\u4F53 request").refine(
|
|
2587
|
+
(value) => value.status !== "completed" || value.kind === "result",
|
|
2588
|
+
"\u5B8C\u6210\u65F6\u4F7F\u7528 result \u56DE\u62A5"
|
|
2589
|
+
);
|
|
2590
|
+
var MatterHoldSchema = z14.object({
|
|
2591
|
+
hold: z14.object({ scope: z14.enum(["all", "writes"]), reason: z14.string().trim().min(1) }).strict().nullable()
|
|
2448
2592
|
}).strict();
|
|
2449
2593
|
var now = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
2450
2594
|
var Matters = class {
|
|
@@ -2462,6 +2606,19 @@ var Matters = class {
|
|
|
2462
2606
|
"SELECT member_id FROM agent_matter_members WHERE conversation_id=? ORDER BY member_id"
|
|
2463
2607
|
).all(id).map((r) => String(r.member_id));
|
|
2464
2608
|
}
|
|
2609
|
+
changed(id) {
|
|
2610
|
+
const row = this.hub.db.prepare("SELECT * FROM agent_matters WHERE conversation_id=?").get(id);
|
|
2611
|
+
if (!row) return;
|
|
2612
|
+
new AgentStore(this.hub).event(id, "context", {
|
|
2613
|
+
kind: "matter",
|
|
2614
|
+
title: row.title,
|
|
2615
|
+
status: row.status,
|
|
2616
|
+
visibility: row.visibility,
|
|
2617
|
+
participants: this.members(id),
|
|
2618
|
+
pending: row.pending_json ? JSON.parse(String(row.pending_json)) : null,
|
|
2619
|
+
hold: row.hold_json ? JSON.parse(String(row.hold_json)) : null
|
|
2620
|
+
});
|
|
2621
|
+
}
|
|
2465
2622
|
requireMember(identity, id) {
|
|
2466
2623
|
const matter = this.get(identity.workspaceId, id);
|
|
2467
2624
|
if (!new AgentStore(this.hub).members(identity.workspaceId).includes(identity.memberId) || matter.visibility !== "project" && !this.members(id).includes(identity.memberId))
|
|
@@ -2471,57 +2628,83 @@ var Matters = class {
|
|
|
2471
2628
|
return Number(
|
|
2472
2629
|
this.hub.db.prepare(
|
|
2473
2630
|
`SELECT COUNT(*) n FROM agent_matter_members m JOIN agent_matters a ON a.conversation_id=m.conversation_id
|
|
2474
|
-
WHERE a.project_id=? AND m.member_id=? AND EXISTS(SELECT 1 FROM
|
|
2631
|
+
WHERE a.project_id=? AND m.member_id=? AND EXISTS(SELECT 1 FROM agent_messages s WHERE s.conversation_id=a.conversation_id AND s.role='assistant' AND s.state='succeeded' AND s.body<>'' AND s.seq>m.read_seq)`
|
|
2475
2632
|
).get(identity.workspaceId, identity.memberId).n
|
|
2476
2633
|
);
|
|
2477
2634
|
}
|
|
2478
2635
|
mark(identity, id, cursor) {
|
|
2479
2636
|
this.requireMember(identity, id);
|
|
2480
2637
|
this.hub.db.prepare(
|
|
2481
|
-
`UPDATE agent_matter_members SET read_seq=MAX(read_seq,MIN(?,COALESCE((SELECT MAX(seq) FROM
|
|
2638
|
+
`UPDATE agent_matter_members SET read_seq=MAX(read_seq,MIN(?,COALESCE((SELECT MAX(seq) FROM agent_messages WHERE conversation_id=?),0))) WHERE conversation_id=? AND member_id=?`
|
|
2482
2639
|
).run(cursor, id, id, identity.memberId);
|
|
2483
2640
|
}
|
|
2484
|
-
receive(id, text, actor = "",
|
|
2485
|
-
const
|
|
2641
|
+
receive(id, text, actor = "", details2 = {}, channel) {
|
|
2642
|
+
const prior = details2.routing && this.hub.db.prepare("SELECT run_id FROM agent_matter_inputs WHERE conversation_id=? AND input_key=?").get(id, details2.routing.inputKey);
|
|
2643
|
+
if (prior)
|
|
2644
|
+
return new AgentStore(this.hub).runView(
|
|
2645
|
+
this.hub.db.prepare("SELECT * FROM project_agent_runs WHERE id=?").get(prior.run_id)
|
|
2646
|
+
);
|
|
2647
|
+
const runId = randomUUID5(), date = now();
|
|
2648
|
+
details2 = {
|
|
2649
|
+
...details2,
|
|
2650
|
+
origin: details2.origin ?? {
|
|
2651
|
+
kind: actor || channel ? "human" : "event",
|
|
2652
|
+
...actor ? { actor } : {}
|
|
2653
|
+
}
|
|
2654
|
+
};
|
|
2486
2655
|
this.hub.db.prepare(
|
|
2487
2656
|
"INSERT INTO project_agent_runs(id,conversation_id,text,created_at,updated_at) VALUES(?,?,?,?,?)"
|
|
2488
2657
|
).run(runId, id, text, date, date);
|
|
2489
2658
|
this.hub.db.prepare("INSERT INTO agent_run_actors VALUES(?,?)").run(runId, actor);
|
|
2490
2659
|
if (channel)
|
|
2491
2660
|
this.hub.db.prepare("INSERT INTO agent_run_channels VALUES(?,?,?,?)").run(runId, channel.bindingId, channel.externalId, channel.speaker);
|
|
2492
|
-
this.hub.db.prepare("INSERT INTO agent_run_details VALUES(?,?)").run(runId, JSON.stringify(
|
|
2661
|
+
this.hub.db.prepare("INSERT INTO agent_run_details VALUES(?,?)").run(runId, JSON.stringify(details2));
|
|
2662
|
+
if (details2.routing)
|
|
2663
|
+
this.hub.db.prepare("INSERT INTO agent_matter_inputs VALUES(?,?,?)").run(id, details2.routing.inputKey, runId);
|
|
2493
2664
|
this.hub.db.prepare("UPDATE agent_matters SET status='active',updated_at=? WHERE conversation_id=?").run(date, id);
|
|
2494
2665
|
this.hub.db.prepare("UPDATE agent_contexts SET last_active=? WHERE conversation_id=?").run(Date.now(), id);
|
|
2495
2666
|
const store = new AgentStore(this.hub);
|
|
2667
|
+
const messages = new AgentMessages(this.hub);
|
|
2668
|
+
if (details2.visible !== false && (actor || channel))
|
|
2669
|
+
messages.input(id, runId, text, actor || channel.speaker, channel ? "wecom" : void 0);
|
|
2496
2670
|
const view = store.runView(
|
|
2497
2671
|
this.hub.db.prepare("SELECT * FROM project_agent_runs WHERE id=?").get(runId)
|
|
2498
2672
|
);
|
|
2499
2673
|
store.event(id, "run", view);
|
|
2674
|
+
messages.answer(id, runId, "", "queued");
|
|
2675
|
+
this.changed(id);
|
|
2500
2676
|
return view;
|
|
2501
2677
|
}
|
|
2502
|
-
ask(identity, id, text) {
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2678
|
+
ask(identity, id, text, requestId) {
|
|
2679
|
+
this.requireMember(identity, id);
|
|
2680
|
+
return new AgentMessages(this.hub).request(
|
|
2681
|
+
requestId ? `chat:${identity.workspaceId}:${identity.memberId}:${requestId}` : void 0,
|
|
2682
|
+
{ chatId: id, text },
|
|
2683
|
+
() => {
|
|
2684
|
+
this.invite(id, [identity.memberId]);
|
|
2685
|
+
return { ...this.receive(id, text, identity.memberId), conversationId: id };
|
|
2686
|
+
}
|
|
2687
|
+
);
|
|
2508
2688
|
}
|
|
2509
2689
|
invite(id, members) {
|
|
2510
2690
|
for (const member of members)
|
|
2511
2691
|
this.hub.db.prepare(
|
|
2512
2692
|
"INSERT INTO agent_matter_members(conversation_id,member_id) VALUES(?,?) ON CONFLICT DO NOTHING"
|
|
2513
2693
|
).run(id, member);
|
|
2694
|
+
this.changed(id);
|
|
2514
2695
|
}
|
|
2515
|
-
create(project, title2, participants, visibility) {
|
|
2516
|
-
const id =
|
|
2696
|
+
create(project, title2, participants, visibility, existingId) {
|
|
2697
|
+
const id = existingId ?? randomUUID5();
|
|
2517
2698
|
const channel = this.hub.db.prepare(
|
|
2518
2699
|
`SELECT c.* FROM wecom_channels c JOIN wecom_config b ON b.bot_id=c.bot_id AND b.enabled=1 WHERE c.project_id=?`
|
|
2519
2700
|
).get(project);
|
|
2520
2701
|
const scope2 = visibility ?? (channel && participants.length ? "project" : "participants");
|
|
2521
2702
|
this.hub.db.prepare(
|
|
2522
|
-
"INSERT INTO project_agent_conversations(id,project_id,role,owner,created_at) VALUES(?,?,'instant','',?)"
|
|
2703
|
+
"INSERT INTO project_agent_conversations(id,project_id,role,owner,created_at) VALUES(?,?,'instant','',?) ON CONFLICT(id) DO NOTHING"
|
|
2523
2704
|
).run(id, project, now());
|
|
2524
|
-
this.hub.db.prepare(
|
|
2705
|
+
this.hub.db.prepare(
|
|
2706
|
+
"INSERT INTO agent_contexts VALUES(?,'matter',?,?,0) ON CONFLICT(conversation_id) DO UPDATE SET kind='matter',scope_key=excluded.scope_key"
|
|
2707
|
+
).run(id, id, Date.now());
|
|
2525
2708
|
const ref = reference(this.hub, project, { kind: "matter", id });
|
|
2526
2709
|
this.hub.db.prepare(
|
|
2527
2710
|
"INSERT INTO agent_matters(conversation_id,project_id,ref,title,visibility,created_at,updated_at) VALUES(?,?,?,?,?,?,?)"
|
|
@@ -2533,17 +2716,66 @@ var Matters = class {
|
|
|
2533
2716
|
).run(id, channel.id, channel.bot_id, channel.topic_id);
|
|
2534
2717
|
return { id, ref };
|
|
2535
2718
|
}
|
|
2719
|
+
details(run) {
|
|
2720
|
+
const batch = this.hub.db.prepare("SELECT details_json FROM agent_run_batches WHERE run_id=?").get(run.id);
|
|
2721
|
+
if (batch) return JSON.parse(String(batch.details_json));
|
|
2722
|
+
const row = this.hub.db.prepare("SELECT details_json FROM agent_run_details WHERE run_id=?").get(run.id);
|
|
2723
|
+
return row ? JSON.parse(String(row.details_json)) : inputDetails(this.hub, run.project_id, run.task_id);
|
|
2724
|
+
}
|
|
2725
|
+
context(run) {
|
|
2726
|
+
if (run.context_kind === "matter") {
|
|
2727
|
+
const row = this.get(run.project_id, run.conversation_id);
|
|
2728
|
+
return `\u5F53\u524D\u4E8B\u9879\u76EE\u6807\u4E0E\u72B6\u6001\uFF08\u4EE5\u6570\u636E\u5E93\u4E3A\u51C6\uFF09\uFF1A
|
|
2729
|
+
${JSON.stringify({
|
|
2730
|
+
ref: row.ref,
|
|
2731
|
+
title: row.title,
|
|
2732
|
+
goal: row.goal,
|
|
2733
|
+
status: row.status,
|
|
2734
|
+
summary: row.summary,
|
|
2735
|
+
pending: row.pending_json ? JSON.parse(String(row.pending_json)) : null,
|
|
2736
|
+
hold: row.hold_json ? JSON.parse(String(row.hold_json)) : null
|
|
2737
|
+
})}`;
|
|
2738
|
+
}
|
|
2739
|
+
if (run.role !== "lifetime") return "";
|
|
2740
|
+
const refs = Object.keys(this.details(run).references ?? {});
|
|
2741
|
+
const matches = this.hub.db.prepare(
|
|
2742
|
+
`SELECT DISTINCT m.ref,m.title,m.status,m.summary,m.pending_json,m.hold_json FROM agent_matters m
|
|
2743
|
+
LEFT JOIN agent_matter_sources s ON s.conversation_id=m.conversation_id
|
|
2744
|
+
WHERE m.project_id=? AND ${refs.length ? `(s.ref IN (${refs.map(() => "?").join(",")}) OR m.ref IN (${refs.map(() => "?").join(",")}))` : "m.status<>'completed'"}
|
|
2745
|
+
ORDER BY m.updated_at DESC LIMIT 20`
|
|
2746
|
+
).all(run.project_id, ...refs.length ? [...refs, ...refs] : []);
|
|
2747
|
+
return matches.length ? `\u5F53\u524D\u76F8\u5173\u4E8B\u9879\u6982\u89C8\uFF08\u66F4\u591A\u5185\u5BB9\u7528 query_matters/read_matter \u67E5\u8BE2\uFF0C\u4E0D\u4F9D\u8D56\u5386\u53F2\u8BB0\u5FC6\uFF09\uFF1A
|
|
2748
|
+
${JSON.stringify(matches)}` : "";
|
|
2749
|
+
}
|
|
2536
2750
|
delegate(run, input, operation) {
|
|
2537
2751
|
return diagnosticTransaction(this.hub, () => {
|
|
2538
|
-
const
|
|
2752
|
+
const details2 = this.details(run);
|
|
2753
|
+
const inputKey = (details2.routing?.inputKey ?? `run:${run.id}`) + (details2.routing?.excluded?.length ? `:returned:${[...details2.routing.excluded].sort().join(",")}` : "");
|
|
2754
|
+
const branch = input.branch ?? "";
|
|
2755
|
+
const prior = this.hub.db.prepare(
|
|
2756
|
+
"SELECT result_json FROM agent_event_routes WHERE project_id=? AND input_key=? AND branch=?"
|
|
2757
|
+
).get(run.project_id, inputKey, branch);
|
|
2539
2758
|
if (prior) return JSON.parse(String(prior.result_json));
|
|
2540
2759
|
const store = new AgentStore(this.hub);
|
|
2541
2760
|
const participants = [...new Set(input.participants)], valid = new Set(store.members(run.project_id));
|
|
2542
2761
|
if (participants.some((member) => !valid.has(member)))
|
|
2543
2762
|
throw new HubError(400, "MATTER_MEMBER_INVALID", "\u53C2\u4E0E\u4EBA\u5FC5\u987B\u662F\u672C\u9879\u76EE\u6709\u6548\u6210\u5458\u3002");
|
|
2763
|
+
const notice = details2.notification;
|
|
2764
|
+
const linked = notice && this.hub.db.prepare(
|
|
2765
|
+
"SELECT target_id FROM agent_notice_threads WHERE conversation_id=? AND user_id=?"
|
|
2766
|
+
).get(notice.conversationId, notice.memberId)?.target_id;
|
|
2767
|
+
if (linked) input = { ...input, ref: String(linked) };
|
|
2768
|
+
const promote = !input.ref && notice && participants.every((member) => member === notice.memberId) && input.visibility !== "project" ? notice.conversationId : void 0;
|
|
2769
|
+
if (notice && !participants.includes(notice.memberId)) participants.push(notice.memberId);
|
|
2544
2770
|
let id, ref;
|
|
2545
2771
|
if (input.ref) {
|
|
2546
2772
|
const current = this.get(run.project_id, input.ref);
|
|
2773
|
+
if (details2.routing?.excluded?.includes(String(current.conversation_id)))
|
|
2774
|
+
throw new HubError(
|
|
2775
|
+
409,
|
|
2776
|
+
"MATTER_INPUT_RETURNED",
|
|
2777
|
+
"\u8FD9\u4E2A\u4E8B\u9879\u5DF2\u9000\u56DE\u6B64\u8F93\u5165\uFF0C\u8BF7\u9009\u62E9\u5176\u4ED6\u4E8B\u9879\u6216\u65B0\u5EFA\u3002"
|
|
2778
|
+
);
|
|
2547
2779
|
if (input.visibility && input.visibility !== current.visibility)
|
|
2548
2780
|
throw new HubError(
|
|
2549
2781
|
400,
|
|
@@ -2555,55 +2787,237 @@ var Matters = class {
|
|
|
2555
2787
|
} else {
|
|
2556
2788
|
if (!input.title)
|
|
2557
2789
|
throw new HubError(400, "MATTER_TITLE_REQUIRED", "\u65B0\u5EFA\u4E8B\u9879\u9700\u8981\u7B80\u77ED\u6807\u9898\u3002");
|
|
2558
|
-
({ id, ref } = this.create(
|
|
2790
|
+
({ id, ref } = this.create(
|
|
2791
|
+
run.project_id,
|
|
2792
|
+
input.title,
|
|
2793
|
+
participants,
|
|
2794
|
+
promote ? "participants" : input.visibility,
|
|
2795
|
+
promote
|
|
2796
|
+
));
|
|
2797
|
+
this.hub.db.prepare("UPDATE agent_matters SET goal=? WHERE conversation_id=?").run(input.text, id);
|
|
2559
2798
|
}
|
|
2560
2799
|
this.invite(id, participants);
|
|
2561
2800
|
const references = {};
|
|
2562
|
-
for (const source of input.sources) {
|
|
2563
|
-
references[source] = resolveReference(this.hub, run.project_id, source, run.task_id);
|
|
2564
|
-
this.hub.db.prepare(
|
|
2801
|
+
for (const source of [.../* @__PURE__ */ new Set([...input.sources, ...input.follows])]) {
|
|
2802
|
+
references[source] = details2.references?.[source] ?? resolveReference(this.hub, run.project_id, source, run.task_id);
|
|
2803
|
+
this.hub.db.prepare(
|
|
2804
|
+
`INSERT INTO agent_matter_sources(conversation_id,ref,relation) VALUES(?,?,?)
|
|
2805
|
+
ON CONFLICT(conversation_id,ref) DO UPDATE SET relation=CASE WHEN excluded.relation='follow' THEN 'follow' ELSE relation END`
|
|
2806
|
+
).run(id, source, input.follows.includes(source) ? "follow" : "evidence");
|
|
2565
2807
|
}
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2808
|
+
let runId;
|
|
2809
|
+
if (promote && notice) {
|
|
2810
|
+
runId = notice.runId;
|
|
2811
|
+
this.hub.db.prepare("UPDATE agent_run_details SET details_json=? WHERE run_id=?").run(
|
|
2812
|
+
JSON.stringify({
|
|
2813
|
+
references,
|
|
2814
|
+
origin: { kind: "human", actor: notice.memberId },
|
|
2815
|
+
routing: { inputKey },
|
|
2816
|
+
audit: { sourceRunId: run.id }
|
|
2817
|
+
}),
|
|
2818
|
+
runId
|
|
2819
|
+
);
|
|
2820
|
+
const siblings = this.hub.db.prepare(
|
|
2821
|
+
`SELECT task_id FROM agent_input_details WHERE project_id=?
|
|
2822
|
+
AND json_extract(details_json,'$.notification.conversationId')=?`
|
|
2823
|
+
).all(run.project_id, promote);
|
|
2824
|
+
for (const item of siblings)
|
|
2825
|
+
if (item.task_id !== run.task_id) {
|
|
2826
|
+
this.hub.db.prepare(
|
|
2827
|
+
"UPDATE project_agent_inbox SET state='succeeded' WHERE project_id=? AND task_id=? AND state='queued'"
|
|
2828
|
+
).run(run.project_id, item.task_id);
|
|
2829
|
+
this.hub.db.prepare(
|
|
2830
|
+
`UPDATE project_agent_runs SET state='succeeded' WHERE task_id=? AND state='queued'
|
|
2831
|
+
AND conversation_id IN(SELECT id FROM project_agent_conversations WHERE project_id=? AND role='lifetime')`
|
|
2832
|
+
).run(item.task_id, run.project_id);
|
|
2833
|
+
}
|
|
2834
|
+
} else {
|
|
2835
|
+
const original = notice ? "" : run.text;
|
|
2836
|
+
const text = original && original !== input.text ? `${input.text}
|
|
2837
|
+
|
|
2838
|
+
\u539F\u59CB\u8F93\u5165\uFF1A
|
|
2839
|
+
${original}` : input.text;
|
|
2840
|
+
runId = this.receive(id, text, "", {
|
|
2841
|
+
references,
|
|
2842
|
+
origin: details2.origin ?? { kind: "event" },
|
|
2843
|
+
routing: { inputKey: `${inputKey}:${branch}`, excluded: details2.routing?.excluded },
|
|
2844
|
+
visible: false,
|
|
2845
|
+
audit: { sourceRunId: run.id }
|
|
2846
|
+
}).id;
|
|
2847
|
+
}
|
|
2848
|
+
if (notice) {
|
|
2849
|
+
this.hub.db.prepare(
|
|
2850
|
+
"UPDATE agent_notice_threads SET target_id=? WHERE conversation_id=? AND user_id=?"
|
|
2851
|
+
).run(id, notice.conversationId, notice.memberId);
|
|
2852
|
+
if (!promote) {
|
|
2853
|
+
this.hub.db.prepare(
|
|
2854
|
+
"UPDATE project_agent_runs SET state='succeeded',answer='',updated_at=? WHERE id=?"
|
|
2855
|
+
).run(now(), notice.runId);
|
|
2856
|
+
new AgentMessages(this.hub).answer(notice.conversationId, notice.runId, "", "succeeded");
|
|
2857
|
+
store.event(notice.conversationId, "context", { redirect: id });
|
|
2858
|
+
}
|
|
2859
|
+
}
|
|
2860
|
+
const result = { ref, conversationId: id, runId };
|
|
2861
|
+
this.hub.db.prepare("INSERT INTO agent_event_routes VALUES(?,?,?,?,?)").run(run.project_id, inputKey, branch, id, JSON.stringify(result));
|
|
2862
|
+
this.hub.db.prepare("INSERT OR IGNORE INTO agent_matter_operations VALUES(?,?)").run(operation, JSON.stringify(result));
|
|
2572
2863
|
return result;
|
|
2573
2864
|
});
|
|
2574
2865
|
}
|
|
2575
2866
|
report(run, input, operation) {
|
|
2576
2867
|
return diagnosticTransaction(this.hub, () => {
|
|
2577
|
-
const
|
|
2578
|
-
if (
|
|
2868
|
+
const old = this.hub.db.prepare("SELECT * FROM agent_matter_reports WHERE operation_key=?").get(operation);
|
|
2869
|
+
if (old && old.state !== "abandoned")
|
|
2870
|
+
return {
|
|
2871
|
+
ref: this.get(run.project_id, run.conversation_id).ref,
|
|
2872
|
+
status: old.status,
|
|
2873
|
+
taskId: old.task_id,
|
|
2874
|
+
recorded: true
|
|
2875
|
+
};
|
|
2579
2876
|
const matter = this.get(run.project_id, run.conversation_id);
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
this.hub,
|
|
2588
|
-
run.project_id,
|
|
2589
|
-
`[${ref}] ${matter.title} \xB7 ${{ active: "\u8FDB\u5C55", waiting: "\u9700\u8981\u534F\u8C03", completed: "\u5DF2\u5B8C\u6210" }[status]}
|
|
2590
|
-
${input.summary}`,
|
|
2591
|
-
operation,
|
|
2592
|
-
{
|
|
2593
|
-
references: { [ref]: { kind: "matter", id: run.conversation_id } },
|
|
2594
|
-
audit: { sourceRunId: run.id }
|
|
2595
|
-
}
|
|
2877
|
+
if (input.pending?.members.some((member) => !this.members(run.conversation_id).includes(member)))
|
|
2878
|
+
throw new HubError(400, "MATTER_MEMBER_INVALID", "\u5F85\u786E\u8BA4\u5BF9\u8C61\u5FC5\u987B\u662F\u4E8B\u9879\u53C2\u4E0E\u4EBA\u3002");
|
|
2879
|
+
if (input.status === "completed" && (input.pending || input.pending === void 0 && matter.pending_json || matter.hold_json))
|
|
2880
|
+
throw new HubError(
|
|
2881
|
+
409,
|
|
2882
|
+
"MATTER_NOT_FINISHED",
|
|
2883
|
+
"\u4ECD\u6709\u5F85\u786E\u8BA4\u95EE\u9898\u6216\u660E\u786E\u6682\u505C\u8981\u6C42\uFF0C\u4E0D\u80FD\u62A5\u544A\u5B8C\u6210\u3002"
|
|
2596
2884
|
);
|
|
2597
|
-
|
|
2598
|
-
const
|
|
2599
|
-
this.hub.db.prepare(
|
|
2600
|
-
|
|
2885
|
+
if (old) this.hub.db.prepare("DELETE FROM agent_matter_reports WHERE id=?").run(old.id);
|
|
2886
|
+
const id = randomUUID5();
|
|
2887
|
+
this.hub.db.prepare(
|
|
2888
|
+
"INSERT INTO agent_matter_reports(id,conversation_id,run_id,kind,status,summary,details_json,created_at,operation_key) VALUES(?,?,?,?,?,?,?,?,?)"
|
|
2889
|
+
).run(
|
|
2890
|
+
id,
|
|
2891
|
+
run.conversation_id,
|
|
2892
|
+
run.id,
|
|
2893
|
+
input.kind,
|
|
2894
|
+
input.status,
|
|
2895
|
+
input.summary,
|
|
2896
|
+
JSON.stringify(input),
|
|
2897
|
+
now(),
|
|
2898
|
+
operation
|
|
2899
|
+
);
|
|
2900
|
+
if (input.kind !== "result") this.commitReport(run, id);
|
|
2901
|
+
const current = this.hub.db.prepare("SELECT * FROM agent_matter_reports WHERE id=?").get(id);
|
|
2902
|
+
return {
|
|
2903
|
+
ref: matter.ref,
|
|
2904
|
+
status: input.status,
|
|
2905
|
+
taskId: current.task_id,
|
|
2906
|
+
recorded: true,
|
|
2907
|
+
pending: current.state === "pending"
|
|
2908
|
+
};
|
|
2909
|
+
});
|
|
2910
|
+
}
|
|
2911
|
+
commitReports(run, answer) {
|
|
2912
|
+
const pending = this.hub.db.prepare(
|
|
2913
|
+
"SELECT id FROM agent_matter_reports WHERE run_id=? AND state='pending' ORDER BY rowid"
|
|
2914
|
+
).all(run.id);
|
|
2915
|
+
for (const row of pending.slice(0, -1))
|
|
2916
|
+
this.hub.db.prepare("UPDATE agent_matter_reports SET state='superseded' WHERE id=?").run(row.id);
|
|
2917
|
+
if (pending.length) {
|
|
2918
|
+
const id = String(pending.at(-1).id);
|
|
2919
|
+
const row = this.hub.db.prepare("SELECT status FROM agent_matter_reports WHERE id=?").get(id);
|
|
2920
|
+
const matter = this.get(run.project_id, run.conversation_id);
|
|
2921
|
+
if (row.status === "completed" && this.members(run.conversation_id).length && !answer.trim())
|
|
2922
|
+
throw new HubError(409, "MATTER_ANSWER_REQUIRED", "\u5B8C\u6210\u59D4\u6258\u65F6\u9700\u8981\u7ED9\u53C2\u4E0E\u4EBA\u4E00\u6761\u53EF\u89C1\u7ED3\u8BBA\u3002");
|
|
2923
|
+
if (row.status === "completed" && matter.hold_json)
|
|
2924
|
+
throw new HubError(409, "MATTER_NOT_FINISHED", "\u4E8B\u9879\u4ECD\u6709\u660E\u786E\u6682\u505C\u8981\u6C42\uFF0C\u4E0D\u80FD\u62A5\u544A\u5B8C\u6210\u3002");
|
|
2925
|
+
this.commitReport(run, id);
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
abandonDrafts(runId) {
|
|
2929
|
+
this.hub.db.prepare(
|
|
2930
|
+
"UPDATE agent_matter_reports SET state='abandoned' WHERE run_id=? AND state='pending'"
|
|
2931
|
+
).run(runId);
|
|
2932
|
+
this.hub.db.prepare("DELETE FROM agent_answer_drafts WHERE run_id=?").run(runId);
|
|
2933
|
+
this.hub.db.prepare("DELETE FROM agent_run_notifications WHERE run_id=?").run(runId);
|
|
2934
|
+
}
|
|
2935
|
+
commitReport(run, id) {
|
|
2936
|
+
const row = this.hub.db.prepare("SELECT * FROM agent_matter_reports WHERE id=? AND state='pending'").get(id);
|
|
2937
|
+
if (!row) return;
|
|
2938
|
+
const input = JSON.parse(String(row.details_json));
|
|
2939
|
+
const matter = this.get(run.project_id, run.conversation_id);
|
|
2940
|
+
const queued = this.hub.db.prepare("SELECT 1 FROM project_agent_runs WHERE conversation_id=? AND state='queued'").get(run.conversation_id);
|
|
2941
|
+
const status = queued && input.status === "completed" ? "active" : input.status;
|
|
2942
|
+
this.hub.db.prepare(
|
|
2943
|
+
"UPDATE agent_matters SET status=?,summary=?,pending_json=?,revision=revision+1,updated_at=? WHERE conversation_id=?"
|
|
2944
|
+
).run(
|
|
2945
|
+
status,
|
|
2946
|
+
input.summary,
|
|
2947
|
+
input.pending === void 0 ? matter.pending_json : input.pending === null ? null : JSON.stringify(input.pending),
|
|
2948
|
+
now(),
|
|
2949
|
+
run.conversation_id
|
|
2950
|
+
);
|
|
2951
|
+
let taskId = null;
|
|
2952
|
+
const previous = this.hub.db.prepare(
|
|
2953
|
+
"SELECT details_json FROM agent_matter_reports WHERE conversation_id=? AND kind=? AND state='committed' ORDER BY rowid DESC LIMIT 1"
|
|
2954
|
+
).get(run.conversation_id, input.kind);
|
|
2955
|
+
if (input.kind !== "progress" && previous?.details_json !== row.details_json) {
|
|
2956
|
+
const ref = String(matter.ref);
|
|
2957
|
+
taskId = enqueueInput(
|
|
2958
|
+
this.hub,
|
|
2959
|
+
run.project_id,
|
|
2960
|
+
`[${ref}] ${matter.title} \xB7 ${input.kind === "coordination" ? "\u9700\u8981\u534F\u8C03" : status === "completed" ? "\u5DF2\u5B8C\u6210" : "\u91CD\u8981\u7ED3\u8BBA"}
|
|
2961
|
+
${input.summary}${input.request ? "\n\u534F\u8C03\u8BF7\u6C42\uFF1A" + input.request : ""}`,
|
|
2962
|
+
`matter-report:${id}`,
|
|
2963
|
+
{
|
|
2964
|
+
references: { [ref]: { kind: "matter", id: run.conversation_id } },
|
|
2965
|
+
origin: { kind: "report" },
|
|
2966
|
+
audit: { sourceRunId: run.id }
|
|
2967
|
+
}
|
|
2968
|
+
);
|
|
2969
|
+
}
|
|
2970
|
+
this.hub.db.prepare("UPDATE agent_matter_reports SET state='committed',task_id=? WHERE id=?").run(taskId, id);
|
|
2971
|
+
this.changed(run.conversation_id);
|
|
2972
|
+
}
|
|
2973
|
+
hold(run, value) {
|
|
2974
|
+
const details2 = this.details(run);
|
|
2975
|
+
if (details2.origin?.kind !== "human")
|
|
2976
|
+
throw new HubError(403, "MATTER_HOLD_SOURCE", "\u6682\u505C\u6216\u6062\u590D\u9700\u8981\u5F53\u524D\u7528\u6237\u7684\u660E\u786E\u8981\u6C42\u3002");
|
|
2977
|
+
const hold = value.hold ? { ...value.hold, sourceRunId: run.id, actor: details2.origin.actor ?? run.owner } : null;
|
|
2978
|
+
this.hub.db.prepare(
|
|
2979
|
+
"UPDATE agent_matters SET hold_json=?,revision=revision+1,updated_at=? WHERE conversation_id=?"
|
|
2980
|
+
).run(hold ? JSON.stringify(hold) : null, now(), run.conversation_id);
|
|
2981
|
+
this.changed(run.conversation_id);
|
|
2982
|
+
return { hold };
|
|
2983
|
+
}
|
|
2984
|
+
assertWritable(run) {
|
|
2985
|
+
if (run.context_kind !== "matter") return;
|
|
2986
|
+
if (this.get(run.project_id, run.conversation_id).hold_json)
|
|
2987
|
+
throw new HubError(
|
|
2988
|
+
409,
|
|
2989
|
+
"MATTER_ON_HOLD",
|
|
2990
|
+
"\u7528\u6237\u8981\u6C42\u6682\u7F13\u64CD\u4F5C\uFF0C\u8BF7\u5148\u5728\u5F53\u524D\u4E8B\u9879\u6F84\u6E05\u6216\u53D6\u5F97\u660E\u786E\u6062\u590D\u8981\u6C42\u3002"
|
|
2991
|
+
);
|
|
2992
|
+
}
|
|
2993
|
+
handback(run, reason) {
|
|
2994
|
+
return diagnosticTransaction(this.hub, () => {
|
|
2995
|
+
const prior = this.hub.db.prepare("SELECT task_id FROM agent_input_handbacks WHERE run_id=?").get(run.id);
|
|
2996
|
+
if (prior) return { taskId: Number(prior.task_id) };
|
|
2997
|
+
this.abandonDrafts(run.id);
|
|
2998
|
+
const details2 = this.details(run), current = this.get(run.project_id, run.conversation_id);
|
|
2999
|
+
const routing = {
|
|
3000
|
+
inputKey: details2.routing?.inputKey ?? `run:${run.id}`,
|
|
3001
|
+
excluded: [.../* @__PURE__ */ new Set([...details2.routing?.excluded ?? [], run.conversation_id])]
|
|
3002
|
+
};
|
|
3003
|
+
const taskId = enqueueInput(
|
|
3004
|
+
this.hub,
|
|
3005
|
+
run.project_id,
|
|
3006
|
+
`[${current.ref}] ${current.title} \u9000\u56DE\u8F93\u5165
|
|
3007
|
+
\u539F\u56E0\uFF1A${reason}
|
|
3008
|
+
\u539F\u8F93\u5165\uFF1A
|
|
3009
|
+
${run.text}`,
|
|
3010
|
+
`handback:${run.id}`,
|
|
3011
|
+
{ ...details2, routing, visible: false }
|
|
3012
|
+
);
|
|
3013
|
+
this.hub.db.prepare("INSERT INTO agent_input_handbacks VALUES(?,?,?,?)").run(run.id, run.conversation_id, reason, taskId);
|
|
3014
|
+
return { taskId };
|
|
2601
3015
|
});
|
|
2602
3016
|
}
|
|
2603
3017
|
};
|
|
2604
3018
|
|
|
2605
|
-
// apps/server/agents/
|
|
2606
|
-
import { randomUUID as
|
|
3019
|
+
// apps/server/agents/notices.ts
|
|
3020
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
2607
3021
|
|
|
2608
3022
|
// apps/server/agents/prompts.ts
|
|
2609
3023
|
var materials = `## \u8D44\u6599\u4E0E\u5224\u65AD\u4F9D\u636E
|
|
@@ -2614,7 +3028,7 @@ var queryTools = `\u53EF\u81EA\u4E3B\u4F7F\u7528\u6587\u4EF6\u5DE5\u5177\u3001Gi
|
|
|
2614
3028
|
\u8054\u7CFB\u6210\u5458\u524D\u53EF\u7528 list_members \u67E5\u8BE2\u6709\u6548\u6210\u5458 ID\uFF1B\u5BBF\u4E3B\u6821\u9A8C\u5B9E\u9645\u6295\u9012\u5BF9\u8C61\u3002\u8D44\u6599\u77ED\u5F15\u7528\u901A\u8FC7 read_reference \u56DE\u67E5\u3002`;
|
|
2615
3029
|
var citations = `\u5F15\u7528\u9879\u76EE\u8D44\u6599\u65F6\u5199\u6210 [\u8D44\u6599\u540D\u79F0](\u94FE\u63A5)\uFF0C\u540D\u79F0\u5E94\u4FBF\u4E8E\u7406\u89E3\uFF1A\u804A\u5929\u548C outbox \u4F7F\u7528\u5DE5\u5177\u8FD4\u56DE\u7684 url\uFF0CPR \u8BC4\u8BBA\u4F7F\u7528 publicUrl\u3002\u4FDD\u7559\u5B8C\u6574\u94FE\u63A5\u4E2D\u7684\u7248\u672C\u548C\u6D88\u606F\u5B9A\u4F4D\u4FE1\u606F\u3002
|
|
2616
3030
|
\u5F15\u7528\u6C99\u7BB1\u6587\u4EF6\u524D\uFF0C\u5148\u8C03\u7528 get_file_link \u83B7\u53D6\u7F51\u9875\u94FE\u63A5\uFF1B\u4E0D\u8981\u628A /project/... \u672C\u5730\u8DEF\u5F84\u5F53\u7F51\u9875\u94FE\u63A5\u53D1\u9001\u3002`;
|
|
2617
|
-
var voice = "\u5BF9\u5916\u8868\u8FBE\u76F4\u63A5\u8FDB\u5165\u4E3B\u9898\uFF1B\u786E\u9700\u81EA\u6211\u4ECB\u7ECD\u65F6\u7EDF\u4E00\u4F7F\u7528\u300C\u6211\
|
|
3031
|
+
var voice = "\u5BF9\u5916\u8868\u8FBE\u76F4\u63A5\u8FDB\u5165\u4E3B\u9898\uFF1B\u786E\u9700\u81EA\u6211\u4ECB\u7ECD\u65F6\u7EDF\u4E00\u4F7F\u7528\u300C\u6211\u662FTim\u300D\uFF0C\u4E0D\u4ECB\u7ECD lifetime\u3001instant \u7B49\u5185\u90E8\u8FD0\u884C\u89D2\u8272\u3002";
|
|
2618
3032
|
var expression = `## \u8868\u8FBE\u98CE\u683C
|
|
2619
3033
|
${voice}
|
|
2620
3034
|
\u9ED8\u8BA4\u7B80\u77ED\uFF0C\u5148\u7ED9\u7ED3\u8BBA\uFF0C\u518D\u8865\u5145\u5F71\u54CD\u7406\u89E3\u6216\u884C\u52A8\u7684\u5FC5\u8981\u4FE1\u606F\u3002\u627F\u63A5\u5DF2\u6709\u5BF9\u8BDD\uFF0C\u7701\u53BB\u5BA2\u5957\u3001\u91CD\u590D\u80CC\u666F\u548C\u91CD\u590D\u603B\u7ED3\uFF1B\u590D\u6742\u95EE\u9898\u6216\u7528\u6237\u8981\u6C42\u8BE6\u89E3\u65F6\u518D\u5C55\u5F00\u3002
|
|
@@ -2628,7 +3042,7 @@ var audience = `## \u6C9F\u901A\u8303\u56F4
|
|
|
2628
3042
|
\u56DE\u590D\u56F4\u7ED5\u539F\u95EE\u9898\u5C55\u5F00\uFF0C\u4E0D\u56E0\u5171\u4EAB\u9879\u76EE\u4E0A\u4E0B\u6587\u6DF7\u5165\u5176\u4ED6\u4EBA\u7684\u4E8B\u9879\u3002`;
|
|
2629
3043
|
function projectPrompt(role) {
|
|
2630
3044
|
return role === "lifetime" ? `## \u5B9A\u4F4D\u4E0E\u804C\u8D23
|
|
2631
|
-
\u4F60\u662F Teammy \u7684\u9879\u76EE\u534F\u4F5C\u52A9\u624B\
|
|
3045
|
+
\u4F60\u662F Teammy \u7684\u9879\u76EE\u534F\u4F5C\u52A9\u624B\u300CTim\u300D\uFF0C\u6301\u7EED\u63A5\u6536\u9879\u76EE\u53D8\u5316\u548C\u7528\u6237\u59D4\u6258\uFF0C\u5173\u6CE8\u5F71\u54CD\u56E2\u961F\u534F\u4F5C\u65B9\u5411\u3001\u6295\u5165\u4E0E\u7ED3\u679C\u7684\u91CD\u8981\u4E8B\u9879\u3002
|
|
2632
3046
|
|
|
2633
3047
|
${materials}
|
|
2634
3048
|
|
|
@@ -2644,11 +3058,11 @@ ${audience}
|
|
|
2644
3058
|
## \u534F\u8C03\u4E0E\u4E8B\u9879
|
|
2645
3059
|
${queryTools}
|
|
2646
3060
|
\u4F60\u8D1F\u8D23\u9879\u76EE\u7406\u89E3\u548C\u534F\u8C03\u3002\u666E\u901A\u8FDB\u5C55\u9759\u9ED8\u5904\u7406\uFF1B\u7B80\u5355\u67E5\u8BE2\u548C\u5224\u65AD\u53EF\u76F4\u63A5\u5B8C\u6210\u3002\u9700\u8981\u6DF1\u5165\u5206\u6790\u3001\u6301\u7EED\u8DDF\u8FDB\u3001\u8054\u7CFB\u6210\u5458\u6216\u5BA1\u6838 PR \u65F6\uFF0C\u4F7F\u7528 delegate_matter \u4EA4\u7ED9\u4E8B\u9879 Agent\uFF0C\u4E0D\u628A\u5177\u4F53\u5DE5\u4F5C\u548C\u591A\u8F6E\u8BA8\u8BBA\u7559\u5728\u672C\u4F1A\u8BDD\u3002
|
|
2647
|
-
\u5148\u901A\u8FC7 query_matters \u67E5\u627E\u5DF2\u6709\u76F8\u5173\u4E8B\u9879\uFF1B\u540E\u7EED\u53D8\u5316\u7528 ref \u7EE7\u7EED\u540C\u4E00\u4E8B\u9879\uFF0C\u4E0D\u9010\u6761\u65B0\u5EFA\u3002\u65B0\u5EFA\u65F6\u63D0\u4F9B\u7B80\u77ED\u6807\u9898\u3001\u76EE\u6807\u3001\u5FC5\u8981\u4F9D\u636E\u3001\u7528\u6237\u660E\u786E\u6388\u6743\u4E0E\u8FB9\u754C\uFF1Bsources \
|
|
3061
|
+
\u5148\u901A\u8FC7 query_matters \u67E5\u627E\u5DF2\u6709\u76F8\u5173\u4E8B\u9879\uFF0C\u9ED8\u8BA4\u53EA\u8BFB\u76EE\u6807\u548C\u6458\u8981\uFF0C\u5FC5\u8981\u65F6\u518D\u5206\u9875\u67E5\u770B\u6B63\u6587\uFF1B\u540E\u7EED\u53D8\u5316\u7528 ref \u7EE7\u7EED\u540C\u4E00\u4E8B\u9879\uFF0C\u4E0D\u9010\u6761\u65B0\u5EFA\u3002\u65B0\u5EFA\u65F6\u63D0\u4F9B\u7B80\u77ED\u6807\u9898\u3001\u76EE\u6807\u3001\u5FC5\u8981\u4F9D\u636E\u3001\u7528\u6237\u660E\u786E\u6388\u6743\u4E0E\u8FB9\u754C\uFF1Bsources \u4EC5\u4F5C\u4E3A\u53C2\u8003\u4F9D\u636E\uFF1Bfollows \u53EA\u586B\u5199\u672C\u4E8B\u9879\u660E\u786E\u8D1F\u8D23\u5176\u540E\u7EED\u53D8\u5316\u7684\u6765\u6E90\u3002\u4E00\u6B21\u8F93\u5165\u62C6\u6210\u72EC\u7ACB\u4E8B\u9879\u65F6\u586B\u5199\u7A33\u5B9A branch\u3002\u5BBF\u4E3B\u4F1A\u4FDD\u7559\u539F\u59CB\u4E8B\u4EF6\uFF0C\u4E0D\u628A\u7528\u6237\u8981\u6C42\u6269\u5199\u4E3A\u989D\u5916\u5DE5\u4F5C\u6E05\u5355\u3002\u9700\u8981\u6C9F\u901A\u65F6\u9080\u8BF7\u76F8\u5173\u6210\u5458\uFF0C\u4ED6\u4EEC\u53EF\u5728\u540C\u4E00\u7F51\u9875\u4F1A\u8BDD\u4E2D\u591A\u8F6E\u53D1\u8A00\u3002text \u4F1A\u8FDB\u5165\u4E8B\u9879\u4E0A\u4E0B\u6587\uFF0C\u53EA\u5199\u53EF\u5206\u4EAB\u7684\u5185\u5BB9\uFF0C\u4E0D\u590D\u5236\u79C1\u4EBA\u804A\u5929\uFF1B\u65B0\u589E\u53C2\u4E0E\u4EBA\u610F\u5473\u7740\u5206\u4EAB\u8BE5\u4E8B\u9879\u5DF2\u6709\u5386\u53F2\uFF0C\u4EC5\u9080\u8BF7\u786E\u5B9E\u9700\u8981\u53C2\u4E0E\u7684\u4EBA\u3002
|
|
2648
3062
|
\u9879\u76EE\u8FDE\u63A5\u4F01\u4E1A\u5FAE\u4FE1\u9891\u9053\u540E\uFF0C\u6709\u53C2\u4E0E\u4EBA\u7684\u65B0\u4E8B\u9879\u9ED8\u8BA4\u5411\u9879\u76EE\u5171\u4EAB\uFF0C\u4E00\u4E8B\u4E00\u5E16\uFF0C\u6309\u9700\u63D0\u9192\u76F8\u5173\u6210\u5458\u3002\u4EC5\u9002\u5408\u6307\u5B9A\u53C2\u4E0E\u4EBA\u67E5\u770B\u7684\u5185\u5BB9\uFF0C\u5728\u65B0\u5EFA\u65F6\u6307\u5B9A visibility=participants\uFF0C\u4FDD\u7559\u5728\u7F51\u9875\u3002\u5DF2\u6709\u4E8B\u9879\u4FDD\u6301\u539F\u5171\u4EAB\u8303\u56F4\uFF0C\u4E0D\u56E0\u9891\u9053\u7ED1\u5B9A\u6216\u540E\u7EED\u59D4\u6258\u81EA\u52A8\u516C\u5F00\u5386\u53F2\u3002\u4E8B\u9879\u5DF2\u627F\u62C5\u6C9F\u901A\u65F6\uFF0C\u4E0D\u53E6\u53D1\u540C\u5185\u5BB9\u7684\u79C1\u804A\u901A\u77E5\u3002
|
|
2649
3063
|
\u4E8B\u9879 Agent \u53EF\u4EE5\u5728\u72EC\u7ACB\u76EE\u5F55\u4E2D\u505A\u5206\u6790\u3001\u4E0B\u8F7D\u3001\u8F6C\u6362\u3001\u811A\u672C\u548C\u8349\u7A3F\uFF0C\u4E0D\u5B9E\u73B0\u4E1A\u52A1\u4EE3\u7801\u6216\u63D0\u4EA4 MR\u3002\u5171\u4EAB\u5DE5\u4F5C\u533A\u3001\u8D44\u6599\u548C\u4ED3\u5E93\u53EA\u8BFB\uFF1B\u7ECF\u660E\u786E\u786E\u8BA4\u7684\u4EFB\u52A1\u4FEE\u8BA2\u7531\u4E8B\u9879 Agent \u4F7F\u7528\u4E1A\u52A1\u5DE5\u5177\u5B8C\u6210\u3002\u4F60\u53EA\u67E5\u8BE2\u4EFB\u52A1\uFF0C\u4E0D\u5199\u4EFB\u52A1\u3002
|
|
2650
3064
|
\u4E8B\u9879\u56DE\u62A5\u53EA\u63D0\u4F9B\u5173\u952E\u7ED3\u679C\u6216\u963B\u585E\u3002\u6CA1\u6709\u65B0\u7684\u534F\u8C03\u9700\u8981\u65F6\u9759\u9ED8\u6536\u4E0B\uFF0C\u4E0D\u4E3A\u5B8C\u6210\u901A\u77E5\u91CD\u65B0\u6D3E\u53D1\u76F8\u540C\u5DE5\u4F5C\uFF0C\u4E0D\u5411\u539F\u4E8B\u9879\u91CD\u590D\u56DE\u9001\u5B83\u521A\u62A5\u544A\u7684\u7ED3\u8BBA\u3002
|
|
2651
|
-
\u7528\u6237\u660E\u786E\u59D4\u6258\u5E94\u5F97\u5230\u7ED3\u679C\u6216\u65E0\u6CD5\u7EE7\u7EED\u7684\u539F\u56E0\uFF1B\u666E\u901A\u4E8B\u4EF6\u6CA1\u6709\u91CD\u8981\u53D1\u73B0\u65F6\u65E0\u9700\u901A\u77E5\u3002\u7B80\u77ED\u3001\u65E0\u9700\u8DDF\u8FDB\u7684\u544A\u77E5\u4ECD\u53EF\u7528 send_outbox\uFF1B\u9884\u8BA1\u9700\u8981\u56DE\u590D\u65F6\u4F18\u5148\u5EFA\u7ACB\u4E8B\u9879\u4F1A\u8BDD\u3002\
|
|
3065
|
+
\u7528\u6237\u660E\u786E\u59D4\u6258\u5E94\u5F97\u5230\u7ED3\u679C\u6216\u65E0\u6CD5\u7EE7\u7EED\u7684\u539F\u56E0\uFF1B\u666E\u901A\u4E8B\u4EF6\u6CA1\u6709\u91CD\u8981\u53D1\u73B0\u65F6\u65E0\u9700\u901A\u77E5\u3002\u7B80\u77ED\u3001\u65E0\u9700\u8DDF\u8FDB\u7684\u544A\u77E5\u4ECD\u53EF\u7528 send_outbox\uFF1B\u9884\u8BA1\u9700\u8981\u56DE\u590D\u65F6\u4F18\u5148\u5EFA\u7ACB\u4E8B\u9879\u4F1A\u8BDD\u3002\u666E\u901A\u901A\u77E5\u7684\u56DE\u590D\u5E26\u539F\u95EE\u9898\uFF0C\u53EF\u4EE5\u8FDE\u7EED\u8865\u5145\uFF1B\u7B80\u5355\u7B54\u590D\u76F4\u63A5\u56DE\u7B54\uFF0C\u9700\u8DDF\u8FDB\u65F6\u5EFA\u7ACB\u6216\u5173\u8054\u4E8B\u9879\u3002\u901A\u77E5\u6536\u4EF6\u4EBA\u7684\u79C1\u4EBA\u56DE\u590D\u4E0D\u81EA\u52A8\u5171\u4EAB\u3002\u6536\u5230\u9000\u56DE\u8F93\u5165\u65F6\u6839\u636E\u539F\u56E0\u91CD\u65B0\u5224\u65AD\uFF0C\u4E0D\u5206\u56DE\u5DF2\u7ECF\u9000\u56DE\u5B83\u7684\u4E8B\u9879\u3002
|
|
2652
3066
|
PR \u4E8B\u4EF6\u7531\u4E8B\u9879 Agent \u6309\u9879\u76EE\u7EA7\u5BA1\u6838\u539F\u5219\u68C0\u67E5\u5E76\u53D1\u5E03\u91CD\u70B9\u610F\u89C1\uFF1B\u540C\u4E00 PR \u7684\u540E\u7EED\u5173\u8054\u5DF2\u6709\u4E8B\u9879\uFF0C\u4E0D\u8981\u6C42\u6BCF\u6B21\u53D8\u5316\u53D1\u8868\u8BC4\u8BBA\u3002\u9996\u6B21\u68C0\u67E5\u6CA1\u6709\u91CD\u8981\u95EE\u9898\u65F6\u4E00\u53E5\u7ED3\u8BBA\u5373\u53EF\uFF0C\u540E\u7EED\u4EC5\u8865\u5145\u5B9E\u8D28\u53D8\u5316\u3002
|
|
2653
3067
|
|
|
2654
3068
|
${expression}
|
|
@@ -2659,7 +3073,7 @@ ${review}
|
|
|
2659
3073
|
\u6B63\u6587\u76F4\u63A5\u9762\u5411\u6536\u4EF6\u4EBA\uFF1B\u9700\u8981\u5BF9\u65B9\u56DE\u590D\u65F6\uFF0C\u660E\u786E\u9700\u8981\u786E\u8BA4\u7684\u95EE\u9898\u3002
|
|
2660
3074
|
\u81EA\u52A8\u4E8B\u4EF6\u6CA1\u6709\u91CD\u8981\u65B0\u53D1\u73B0\u65F6\u9759\u9ED8\u5B8C\u6210\u3002\u7528\u6237\u660E\u786E\u59D4\u6258\u5E94\u901A\u8FC7\u4E8B\u9879\u4F1A\u8BDD\u6216\u5FC5\u8981\u7684\u6D88\u606F\u53CD\u9988\u7ED3\u8BBA\uFF0C\u5373\u4F7F\u672A\u53D1\u73B0\u95EE\u9898\u3002\u6D3E\u53D1\u540E\u7ED3\u675F\u672C\u8F6E\uFF0C\u4E0D\u7B49\u5F85\u4E8B\u9879\u5B8C\u6210\uFF1B\u4E8B\u9879\u6267\u884C\u5931\u8D25\u65F6\u5148\u6838\u5BF9\u539F\u56E0\uFF0C\u4E0D\u53CD\u590D\u91CD\u65B0\u6D3E\u53D1\u540C\u4E00\u5931\u8D25\u64CD\u4F5C\u3002
|
|
2661
3075
|
${citations}` : `## \u5B9A\u4F4D\u4E0E\u804C\u8D23
|
|
2662
|
-
\u4F60\u662F Teammy \u7684\u9879\u76EE\u95EE\u7B54\u52A9\u624B\
|
|
3076
|
+
\u4F60\u662F Teammy \u7684\u9879\u76EE\u95EE\u7B54\u52A9\u624B\u300CTim\u300D\uFF0C\u5E2E\u52A9\u5F53\u524D\u7528\u6237\u7406\u89E3\u9879\u76EE\u8FDB\u5C55\u3001\u5DE5\u4F5C\u5173\u8054\u548C\u534F\u4F5C\u95EE\u9898\u3002\u9ED8\u8BA4\u56F4\u7ED5\u7528\u6237\u7684\u95EE\u9898\u67E5\u8BE2\u3001\u5206\u6790\uFF0C\u5E76\u5728\u5F53\u524D\u4F1A\u8BDD\u56DE\u7B54\u3002
|
|
2663
3077
|
|
|
2664
3078
|
${materials}
|
|
2665
3079
|
\u5176\u4ED6 instant \u5BF9\u8BDD\u53CA lifetime \u5185\u90E8\u4F1A\u8BDD\u4E0D\u4F5C\u4E3A\u9879\u76EE\u534F\u540C\u8D44\u6599\u3002
|
|
@@ -2708,23 +3122,174 @@ function projectInputPrompt(run, repositoryError) {
|
|
|
2708
3122
|
].filter(Boolean).join("\n\n");
|
|
2709
3123
|
}
|
|
2710
3124
|
var matterPrompt = `## \u804C\u8D23
|
|
2711
|
-
\u4F60\u662F Teammy \u7684\u9879\u76EE\u534F\u4F5C\u52A9\u624B\
|
|
3125
|
+
\u4F60\u662F Teammy \u7684\u9879\u76EE\u534F\u4F5C\u52A9\u624B\u300CTim\u300D\uFF0C\u8D1F\u8D23\u5F53\u524D\u4E8B\u9879\u7684\u5206\u6790\u3001\u6C9F\u901A\u548C\u8DDF\u8FDB\u3002\u5F53\u524D\u4F1A\u8BDD\u53EF\u80FD\u6709\u591A\u4F4D\u6210\u5458\uFF0C\u6309\u53D1\u8A00\u8005\u533A\u5206\u610F\u89C1\uFF0C\u4E0D\u628A\u4E00\u4EBA\u7684\u8BF4\u6CD5\u5F53\u4F5C\u5168\u4F53\u7ED3\u8BBA\u3002\u56DE\u590D\u76F4\u63A5\u5BF9\u53C2\u4E0E\u4EBA\u53EF\u89C1\uFF0C\u652F\u6301\u6301\u7EED\u591A\u8F6E\u4EA4\u6D41\u3002
|
|
2712
3126
|
${materials.replace("\u4E34\u65F6\u4E0B\u8F7D\u4E0E\u8F6C\u6362\u4F7F\u7528 /tmp\uFF0C\u4E8B\u9879\u7684\u6301\u4E45\u5206\u6790\u6587\u4EF6\u4F7F\u7528 /work\u3002", "\u4E0B\u8F7D\u3001\u8F6C\u6362\u548C\u6301\u4E45\u5206\u6790\u6587\u4EF6\u4F7F\u7528 /work\u3002")}
|
|
2713
3127
|
|
|
2714
3128
|
## \u5DE5\u4F5C\u4E0E\u5DE5\u5177
|
|
2715
3129
|
\u901A\u8FC7 list_members \u67E5\u8BE2\u6210\u5458\uFF0Cread_reference \u56DE\u67E5\u5173\u8054\u8D44\u6599\u3002/work \u662F\u672C\u4E8B\u9879\u72EC\u7ACB\u4E14\u6301\u4E45\u7684\u53EF\u5199\u76EE\u5F55\uFF0C\u7528\u4E8E\u4E0B\u8F7D\u3001\u8F6C\u6362\u3001\u811A\u672C\u3001\u5B9E\u9A8C\u3001\u8349\u7A3F\u548C\u5206\u6790\u4EA7\u7269\uFF1B\u53EF\u6309\u9700\u8981\u590D\u5236\u53C2\u8003\u6587\u4EF6\u5230\u6B64\u76EE\u5F55\uFF0C\u4E0D\u4E3A\u6BCF\u4EF6\u4E8B\u9879\u590D\u5236\u5B8C\u6574\u4ED3\u5E93\u3002\u5171\u4EAB\u5DE5\u4F5C\u533A\u3001\u8D44\u6599\u548C\u53C2\u8003\u4ED3\u5E93\u59CB\u7EC8\u53EA\u8BFB\u3002\u4E0D\u8981\u5B9E\u73B0\u4E1A\u52A1\u4EE3\u7801\u3001\u521B\u5EFA\u63D0\u4EA4\u3001\u63A8\u9001\u5206\u652F\u6216\u521B\u5EFA MR\uFF1B\u5177\u4F53\u5B9E\u73B0\u7559\u7ED9\u6210\u5458\u3002
|
|
2716
|
-
\u9700\u8981\u52A0\u5165\u5176\u4ED6\u53C2\u4E0E\u4EBA\u6216\u8DE8\u4E8B\u9879\u534F\u8C03\u65F6\uFF0C\u901A\u8FC7 report_matter \u8BF4\u660E\u9700\u6C42\uFF0C\
|
|
3130
|
+
\u9700\u8981\u52A0\u5165\u5176\u4ED6\u53C2\u4E0E\u4EBA\u6216\u8DE8\u4E8B\u9879\u534F\u8C03\u65F6\uFF0C\u901A\u8FC7 report_matter(kind=coordination) \u8BF4\u660E\u9700\u6C42\u3002\u5F53\u524D\u8F93\u5165\u5C5E\u4E8E\u5176\u4ED6\u5DE5\u4F5C\u65F6\uFF0C\u7528 handback_input \u9000\u56DE\u539F\u8F93\u5165\u548C\u539F\u56E0\uFF0C\u4E0D\u7EE7\u7EED\u6267\u884C\u5B83\uFF0C\u4E5F\u4E0D\u7ED3\u675F\u6574\u4E2A\u4E8B\u9879\u3002\u4E0D\u8981\u590D\u5236\u539F\u59CB\u79C1\u4EBA\u804A\u5929\u3002
|
|
2717
3131
|
\u9879\u76EE\u5171\u4EAB\u4E8B\u9879\u53EF\u80FD\u540C\u6B65\u5230\u4F01\u4E1A\u5FAE\u4FE1\u9891\u9053\u7684\u72EC\u7ACB\u5E16\u5B50\uFF1B\u9891\u9053\u4E0E\u7F51\u9875\u7684\u56DE\u590D\u5C5E\u4E8E\u540C\u4E00\u4E8B\u9879\u3002\u53EA\u4F7F\u7528 Teammy \u6210\u5458\u540D\uFF0C\u4E0D\u67E5\u8BE2\u6216\u8F93\u51FA\u4F01\u4E1A\u5FAE\u4FE1\u8EAB\u4EFD ID\u3002\u9700\u8981\u67D0\u4F4D\u53C2\u4E0E\u4EBA\u8865\u5145\u3001\u786E\u8BA4\u6216\u884C\u52A8\u65F6\uFF0C\u8C03\u7528 notify_members \u6807\u8BB0\u8BE5\u6210\u5458\uFF0C\u5BBF\u4E3B\u4F1A\u5728\u672C\u8F6E\u56DE\u7B54\u4E2D\u6DFB\u52A0\u539F\u751F @\uFF1B\u666E\u901A\u56DE\u590D\u4E0D\u989D\u5916\u63D0\u9192\uFF0C\u4E0D\u91CD\u590D\u79C1\u804A\u8F6C\u53D1\u3002\u9891\u9053\u53D1\u8A00\u8005\u672A\u7ED1\u5B9A\u4E2A\u4EBA\u8D26\u53F7\u65F6\u6309\u6765\u6E90\u6807\u8BC6\u533A\u5206\uFF0C\u4E0D\u731C\u6D4B\u5176\u9879\u76EE\u7528\u6237\u540D\u3002\u6D88\u606F\u4FEE\u8BA2\u6216\u64A4\u56DE\u53EA\u66F4\u65B0\u7406\u89E3\uFF0C\u4E0D\u89C6\u4E3A\u81EA\u52A8\u64A4\u9500\u5DF2\u6267\u884C\u64CD\u4F5C\u3002
|
|
2718
3132
|
\u53EF\u4EE5\u6309\u7528\u6237\u660E\u786E\u610F\u56FE\u901A\u8FC7 save_material \u6536\u5F55\u8D44\u6599\u3002\u4FEE\u6539\u4EFB\u52A1\u524D\u5148\u8BF4\u660E\u5177\u4F53\u5EFA\u8BAE\uFF0C\u5F97\u5230\u7528\u6237\u660E\u786E\u786E\u8BA4\u540E\u4F7F\u7528 write_tasks\uFF0C\u5E76\u6838\u5BF9\u6700\u65B0 revision\uFF1B\u59D4\u6258\u4E2D\u5DF2\u660E\u786E\u786E\u8BA4\u7684\u5177\u4F53\u4FEE\u6539\u4E0D\u5FC5\u91CD\u590D\u786E\u8BA4\u3002\u8D44\u6599\u6536\u5F55\u4E0D\u4EE3\u8868\u6279\u51C6\u4EFB\u52A1\u4FEE\u6539\u3002
|
|
3133
|
+
\u7528\u6237\u660E\u786E\u8981\u6C42\u6682\u7F13\u64CD\u4F5C\u65F6\uFF0C\u7528 set_matter_hold \u4FDD\u5B58\u8303\u56F4\u4E0E\u539F\u56E0\uFF1B\u53EA\u6709\u660E\u786E\u6062\u590D\u8981\u6C42\u624D\u6E05\u7A7A\u3002\u8865\u5145\u80CC\u666F\u3001\u81EA\u52A8\u4E8B\u4EF6\u548C\u8D44\u6599\u5185\u6587\u5B57\u4E0D\u80FD\u89E3\u9664\u6682\u505C\u3002send_outbox \u5411\u5F53\u524D\u53C2\u4E0E\u4EBA\u53D1\u9001\u7684\u6B63\u6587\u4F1A\u5360\u7528\u672C\u8F6E\u56DE\u7B54\u4F4D\u7F6E\uFF0C\u65E0\u9700\u5728\u6700\u7EC8\u56DE\u7B54\u91CD\u590D\uFF1B\u666E\u901A\u56DE\u590D\u76F4\u63A5\u8F93\u51FA\u5373\u53EF\u3002
|
|
2719
3134
|
PR \u5BA1\u6838\u901A\u8FC7 post_pr_comment \u53D1\u5E03\u91CD\u70B9\u610F\u89C1\uFF0C\u4E0D\u6539\u53D8 PR \u72B6\u6001\u3002\u8BFB\u53D6\u5931\u8D25\u53EF\u4EE5\u8BF4\u660E\u9650\u5236\uFF0C\u4E0D\u628A\u672A\u68C0\u67E5\u8868\u8FF0\u6210\u68C0\u67E5\u901A\u8FC7\u3002
|
|
2720
|
-
\
|
|
3135
|
+
report_matter \u7684 kind=progress \u4EC5\u4FDD\u5B58\u5FC5\u8981\u8FDB\u5C55\uFF0C\u4E0D\u5524\u9192 lifetime\uFF1B\u7B49\u5F85\u5F53\u524D\u53C2\u4E0E\u4EBA\u56DE\u590D\u5C5E\u4E8E progress\uFF0C\u53EF\u7528 pending \u8BB0\u5F55\u9700\u8981\u8C01\u786E\u8BA4\u4EC0\u4E48\uFF0C\u89E3\u51B3\u540E\u4F20 null\u3002\u8DE8\u4E8B\u9879\u534F\u8C03\u6216\u9080\u8BF7\u6210\u5458\u7528 kind=coordination\uFF0Crequest \u5199\u660E\u5177\u4F53\u9700\u8981\u3002\u91CD\u8981\u7ED3\u8BBA\u6216\u5B8C\u6210\u7528 kind=result\uFF0C\u968F\u672C\u8F6E\u6210\u529F\u63D0\u4EA4\u3002\u666E\u901A\u4EA4\u6D41\u65E0\u9700\u9010\u8F6E\u4E0A\u62A5\u3002completed \u8868\u793A\u4E8B\u9879\u5DF2\u7ECF\u5B8C\u6210\uFF0C\u4E0D\u662F\u672C\u8F6E\u56DE\u7B54\u5B8C\u6BD5\uFF1B\u7B49\u5F85\u6210\u5458\u786E\u8BA4\u65F6\u53EF\u4EE5\u7ED3\u675F\u672C\u8F6E\uFF0C\u540E\u7EED\u6D88\u606F\u4F1A\u7EE7\u7EED\u539F\u4F1A\u8BDD\u3002\u62A5\u544A\u4E0D\u80FD\u66FF\u4EE3\u7ED9\u5F53\u524D\u53C2\u4E0E\u4EBA\u7684\u5FC5\u8981\u56DE\u7B54\u3002
|
|
2721
3136
|
\u76EE\u6807\u8FBE\u6210\u4E14\u5FC5\u8981\u786E\u8BA4\u5DF2\u9F50\u5907\u65F6\uFF0C\u7B80\u77ED\u8BF4\u660E\u7ED3\u8BBA\u5E76\u56DE\u62A5\u5B8C\u6210\uFF1B\u4E0D\u8981\u628A\u6C89\u9ED8\u3001\u6682\u65F6\u79BB\u5F00\u6216\u5355\u4E2A\u6210\u5458\u7684\u8BA4\u53EF\u5F53\u4F5C\u5168\u4F53\u786E\u8BA4\u3002\u8BA8\u8BBA\u5B8C\u6210\u4E0D\u4EE3\u8868\u76F8\u5173\u5F00\u53D1\u4EFB\u52A1\u5DF2\u7ECF\u5B8C\u6210\u3002\u7528\u6237\u53EF\u4EE5\u81EA\u7136\u8868\u8FBE\u201C\u5DF2\u7ECF\u89E3\u51B3\u201D\u7B49\u610F\u56FE\uFF0C\u65E0\u9700\u8F93\u5165\u7ED3\u675F\u547D\u4EE4\uFF1B\u5B8C\u6210\u540E\u6709\u65B0\u8865\u5145\u65F6\u7EE7\u7EED\u7406\u89E3\u548C\u5904\u7406\u3002
|
|
2722
3137
|
${audience}
|
|
2723
3138
|
${expression}
|
|
2724
3139
|
${review}
|
|
2725
3140
|
${citations}`;
|
|
2726
3141
|
|
|
3142
|
+
// apps/server/agents/notices.ts
|
|
3143
|
+
var AgentNotices = class {
|
|
3144
|
+
constructor(hub) {
|
|
3145
|
+
this.hub = hub;
|
|
3146
|
+
}
|
|
3147
|
+
hub;
|
|
3148
|
+
ensure(identity, messageId) {
|
|
3149
|
+
return diagnosticTransaction(this.hub, () => {
|
|
3150
|
+
const old = this.hub.db.prepare("SELECT * FROM agent_notice_threads WHERE message_id=? AND user_id=?").get(messageId, identity.memberId);
|
|
3151
|
+
if (old) return String(old.conversation_id);
|
|
3152
|
+
const message = this.hub.db.prepare(
|
|
3153
|
+
`SELECT o.* FROM project_agent_outbox o JOIN project_agent_recipients r ON r.message_id=o.id
|
|
3154
|
+
WHERE o.id=? AND o.project_id=? AND r.user_id=?`
|
|
3155
|
+
).get(messageId, identity.workspaceId, identity.memberId);
|
|
3156
|
+
if (!message) throw new HubError(404, "OUTBOX_NOT_FOUND", "\u6D88\u606F\u4E0D\u5B58\u5728\u3002");
|
|
3157
|
+
const id = randomUUID6();
|
|
3158
|
+
this.hub.db.prepare(
|
|
3159
|
+
"INSERT INTO project_agent_conversations(id,project_id,role,owner,created_at) VALUES(?,?,'instant',?,?)"
|
|
3160
|
+
).run(id, identity.workspaceId, identity.memberId, message.created_at);
|
|
3161
|
+
this.hub.db.prepare("INSERT INTO agent_contexts VALUES(?,'notice',?,?,0)").run(id, messageId, Date.now());
|
|
3162
|
+
this.hub.db.prepare(
|
|
3163
|
+
"INSERT INTO agent_notice_threads(message_id,user_id,conversation_id) VALUES(?,?,?)"
|
|
3164
|
+
).run(messageId, identity.memberId, id);
|
|
3165
|
+
new AgentMessages(this.hub).put({
|
|
3166
|
+
conversation: id,
|
|
3167
|
+
key: `notice:${messageId}:${identity.memberId}`,
|
|
3168
|
+
role: "assistant",
|
|
3169
|
+
body: String(message.body)
|
|
3170
|
+
});
|
|
3171
|
+
return id;
|
|
3172
|
+
});
|
|
3173
|
+
}
|
|
3174
|
+
ask(identity, conversation, body, requestId) {
|
|
3175
|
+
const store = new AgentStore(this.hub);
|
|
3176
|
+
store.ownedChat(identity, conversation);
|
|
3177
|
+
const thread = this.hub.db.prepare("SELECT * FROM agent_notice_threads WHERE conversation_id=? AND user_id=?").get(conversation, identity.memberId);
|
|
3178
|
+
if (!thread) throw new HubError(404, "OUTBOX_NOT_FOUND", "\u6D88\u606F\u4E0D\u5B58\u5728\u3002");
|
|
3179
|
+
return new AgentMessages(this.hub).request(
|
|
3180
|
+
requestId ? `chat:${identity.workspaceId}:${identity.memberId}:${requestId}` : void 0,
|
|
3181
|
+
{ chatId: conversation, text: body },
|
|
3182
|
+
() => {
|
|
3183
|
+
if (thread.target_id && thread.target_id !== conversation) {
|
|
3184
|
+
const target = String(thread.target_id);
|
|
3185
|
+
return { ...store.ask(identity, body, target), conversationId: target };
|
|
3186
|
+
}
|
|
3187
|
+
const message = store.message(identity, String(thread.message_id));
|
|
3188
|
+
const runId = randomUUID6(), date = (/* @__PURE__ */ new Date()).toISOString();
|
|
3189
|
+
this.hub.db.prepare(
|
|
3190
|
+
"INSERT INTO project_agent_runs(id,conversation_id,text,created_at,updated_at) VALUES(?,?,?,?,?)"
|
|
3191
|
+
).run(runId, conversation, body, date, date);
|
|
3192
|
+
this.hub.db.prepare("INSERT INTO agent_run_actors VALUES(?,?)").run(runId, identity.memberId);
|
|
3193
|
+
const source = this.hub.db.prepare("SELECT source_user_id FROM project_agent_outbox WHERE id=?").get(thread.message_id);
|
|
3194
|
+
const taskId = enqueueInput(
|
|
3195
|
+
this.hub,
|
|
3196
|
+
identity.workspaceId,
|
|
3197
|
+
outboxReplyPrompt({
|
|
3198
|
+
user: identity.memberId,
|
|
3199
|
+
origin: source?.source_user_id ? { owner: String(source.source_user_id) } : null,
|
|
3200
|
+
message: message.body,
|
|
3201
|
+
reply: body
|
|
3202
|
+
}),
|
|
3203
|
+
`notice-reply:${runId}`,
|
|
3204
|
+
{
|
|
3205
|
+
notification: {
|
|
3206
|
+
conversationId: conversation,
|
|
3207
|
+
messageId: message.id,
|
|
3208
|
+
memberId: identity.memberId,
|
|
3209
|
+
runId
|
|
3210
|
+
},
|
|
3211
|
+
origin: { kind: "human", actor: identity.memberId },
|
|
3212
|
+
audit: {
|
|
3213
|
+
requester: source?.source_user_id ?? identity.memberId,
|
|
3214
|
+
sourceTaskId: message.taskId
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
);
|
|
3218
|
+
this.hub.db.prepare("INSERT INTO agent_run_details VALUES(?,?)").run(
|
|
3219
|
+
runId,
|
|
3220
|
+
JSON.stringify({
|
|
3221
|
+
noticeTaskId: taskId,
|
|
3222
|
+
origin: { kind: "human", actor: identity.memberId }
|
|
3223
|
+
})
|
|
3224
|
+
);
|
|
3225
|
+
const messages = new AgentMessages(this.hub);
|
|
3226
|
+
messages.input(conversation, runId, body, identity.memberId);
|
|
3227
|
+
messages.answer(conversation, runId, "", "queued");
|
|
3228
|
+
this.hub.db.prepare(
|
|
3229
|
+
"UPDATE project_agent_recipients SET read_at=COALESCE(read_at,?) WHERE message_id=? AND user_id=?"
|
|
3230
|
+
).run(date, message.id, identity.memberId);
|
|
3231
|
+
return {
|
|
3232
|
+
...store.runView(
|
|
3233
|
+
this.hub.db.prepare("SELECT * FROM project_agent_runs WHERE id=?").get(runId)
|
|
3234
|
+
),
|
|
3235
|
+
taskId,
|
|
3236
|
+
conversationId: conversation
|
|
3237
|
+
};
|
|
3238
|
+
}
|
|
3239
|
+
);
|
|
3240
|
+
}
|
|
3241
|
+
};
|
|
3242
|
+
|
|
3243
|
+
// apps/server/agents/batching.ts
|
|
3244
|
+
function details(hub, project, row) {
|
|
3245
|
+
const saved = hub.db.prepare("SELECT details_json FROM agent_run_details WHERE run_id=?").get(String(row.id));
|
|
3246
|
+
return saved ? JSON.parse(String(saved.details_json)) : inputDetails(hub, project, row.task_id == null ? null : Number(row.task_id));
|
|
3247
|
+
}
|
|
3248
|
+
function batchInput(hub, project, run) {
|
|
3249
|
+
const saved = hub.db.prepare("SELECT text FROM agent_run_batches WHERE run_id=?").get(String(run.id));
|
|
3250
|
+
if (saved) return String(saved.text);
|
|
3251
|
+
const first = details(hub, project, run);
|
|
3252
|
+
if (!first.coalesce || first.origin?.kind !== "event" || first.routing?.excluded?.length)
|
|
3253
|
+
return String(run.text);
|
|
3254
|
+
const rows = hub.db.prepare(
|
|
3255
|
+
`SELECT * FROM project_agent_runs WHERE conversation_id=? AND rowid>(SELECT rowid FROM project_agent_runs WHERE id=?) AND state IN('queued','running') AND batch_run_id IS NULL ORDER BY rowid`
|
|
3256
|
+
).all(String(run.conversation_id), String(run.id));
|
|
3257
|
+
const batch = [{ row: run, details: first }];
|
|
3258
|
+
for (const row of rows) {
|
|
3259
|
+
const next = details(hub, project, row);
|
|
3260
|
+
if (row.state !== "queued" || next.origin?.kind !== "event" || next.coalesce?.source !== first.coalesce.source || next.coalesce.mode !== first.coalesce.mode || next.routing?.excluded?.length)
|
|
3261
|
+
break;
|
|
3262
|
+
batch.push({ row, details: next });
|
|
3263
|
+
}
|
|
3264
|
+
if (batch.length === 1) return String(run.text);
|
|
3265
|
+
const combined = {
|
|
3266
|
+
...first,
|
|
3267
|
+
references: Object.assign({}, ...batch.map((item) => item.details.references))
|
|
3268
|
+
};
|
|
3269
|
+
const text = first.coalesce.mode === "latest" ? `${batch.at(-1).row.text}
|
|
3270
|
+
\uFF08\u672C\u8F6E\u5408\u5E76 ${batch.length} \u6B21\u8FDE\u7EED\u66F4\u65B0\uFF0C\u5F15\u7528\u5BF9\u5E94\u6700\u65B0\u7248\u672C\uFF1B\u539F\u59CB\u7248\u672C\u5747\u5DF2\u4FDD\u5B58\u3002\uFF09` : `\u540C\u4E00\u6765\u6E90\u7684\u8FDE\u7EED\u66F4\u65B0\uFF08\u6309\u53D1\u751F\u987A\u5E8F\uFF09\uFF1A
|
|
3271
|
+
${batch.map((item) => item.row.text).join("\n\n")}`;
|
|
3272
|
+
hub.db.prepare("INSERT INTO agent_run_batches VALUES(?,?,?)").run(String(run.id), text, JSON.stringify(combined));
|
|
3273
|
+
for (const item of batch.slice(1)) {
|
|
3274
|
+
hub.db.prepare("UPDATE project_agent_runs SET batch_run_id=? WHERE id=?").run(String(run.id), String(item.row.id));
|
|
3275
|
+
const removed = hub.db.prepare(
|
|
3276
|
+
"DELETE FROM agent_messages WHERE run_id=? AND role='assistant' AND body='' RETURNING id"
|
|
3277
|
+
).get(String(item.row.id));
|
|
3278
|
+
if (removed)
|
|
3279
|
+
hub.db.prepare("INSERT INTO project_agent_stream(conversation_id,kind,data) VALUES(?,?,?)").run(String(run.conversation_id), "message_removed", JSON.stringify({ id: removed.id }));
|
|
3280
|
+
}
|
|
3281
|
+
return text;
|
|
3282
|
+
}
|
|
3283
|
+
function settleBatch(hub, runId, state, error) {
|
|
3284
|
+
hub.db.prepare("UPDATE project_agent_runs SET state=?,error=?,updated_at=? WHERE batch_run_id=?").run(state, error, (/* @__PURE__ */ new Date()).toISOString(), runId);
|
|
3285
|
+
hub.db.prepare(
|
|
3286
|
+
`UPDATE project_agent_inbox SET state=? WHERE (project_id,task_id) IN (
|
|
3287
|
+
SELECT c.project_id,r.task_id FROM project_agent_runs r JOIN project_agent_conversations c ON c.id=r.conversation_id WHERE r.batch_run_id=? AND r.task_id IS NOT NULL)`
|
|
3288
|
+
).run(state, runId);
|
|
3289
|
+
}
|
|
3290
|
+
|
|
2727
3291
|
// apps/server/agents/store.ts
|
|
3292
|
+
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
2728
3293
|
var now2 = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
2729
3294
|
var AgentStore = class {
|
|
2730
3295
|
constructor(hub) {
|
|
@@ -2736,7 +3301,7 @@ var AgentStore = class {
|
|
|
2736
3301
|
"SELECT * FROM project_agent_conversations WHERE project_id=? AND role=? AND owner=? AND NOT EXISTS(SELECT 1 FROM agent_contexts x WHERE x.conversation_id=project_agent_conversations.id) ORDER BY rowid DESC LIMIT 1"
|
|
2737
3302
|
).get(project, role, owner);
|
|
2738
3303
|
if (prior) return prior;
|
|
2739
|
-
const id =
|
|
3304
|
+
const id = randomUUID7();
|
|
2740
3305
|
this.hub.db.prepare(
|
|
2741
3306
|
`INSERT INTO project_agent_conversations(id,project_id,role,owner,created_at)
|
|
2742
3307
|
VALUES(?,?,?,?,?)`
|
|
@@ -2749,7 +3314,7 @@ var AgentStore = class {
|
|
|
2749
3314
|
).get(String(row.id));
|
|
2750
3315
|
return AgentRunSchema.parse({
|
|
2751
3316
|
id: row.id,
|
|
2752
|
-
text: row.
|
|
3317
|
+
text: this.hub.db.prepare("SELECT body FROM agent_messages WHERE run_id=? AND role='user'").get(String(row.id))?.body ?? "",
|
|
2753
3318
|
answer: row.answer,
|
|
2754
3319
|
state: row.state,
|
|
2755
3320
|
error: row.error,
|
|
@@ -2766,7 +3331,7 @@ var AgentStore = class {
|
|
|
2766
3331
|
return this.hub.db.prepare("SELECT * FROM project_agent_conversations WHERE id=?").get(id);
|
|
2767
3332
|
}
|
|
2768
3333
|
const row = this.hub.db.prepare(
|
|
2769
|
-
"SELECT * FROM project_agent_conversations WHERE id=? AND project_id=? AND owner=? AND role='instant' AND NOT EXISTS(SELECT 1 FROM agent_contexts x WHERE x.conversation_id=project_agent_conversations.id)"
|
|
3334
|
+
"SELECT * FROM project_agent_conversations WHERE id=? AND project_id=? AND owner=? AND role='instant' AND NOT EXISTS(SELECT 1 FROM agent_contexts x WHERE x.conversation_id=project_agent_conversations.id AND x.kind<>'notice')"
|
|
2770
3335
|
).get(id, identity.workspaceId, identity.memberId);
|
|
2771
3336
|
if (!row) throw new HubError(404, "AGENT_CHAT_NOT_FOUND", "\u5BF9\u8BDD\u4E0D\u5B58\u5728\u3002");
|
|
2772
3337
|
return row;
|
|
@@ -2775,14 +3340,17 @@ var AgentStore = class {
|
|
|
2775
3340
|
return {
|
|
2776
3341
|
chats: this.hub.db.prepare(
|
|
2777
3342
|
`SELECT c.*,m.title matter_title,m.status matter_status,
|
|
2778
|
-
(SELECT
|
|
3343
|
+
(SELECT body FROM agent_messages s WHERE s.conversation_id=c.id ORDER BY s.seq LIMIT 1) title,
|
|
2779
3344
|
(SELECT state FROM project_agent_runs r WHERE r.conversation_id=c.id ORDER BY CASE WHEN state='running' THEN 0 WHEN state='queued' THEN 1 ELSE 2 END,r.rowid DESC LIMIT 1) state,
|
|
2780
3345
|
(SELECT MAX(updated_at) FROM project_agent_runs r WHERE r.conversation_id=c.id) updated_at,
|
|
2781
|
-
EXISTS(SELECT 1 FROM
|
|
3346
|
+
EXISTS(SELECT 1 FROM agent_messages s WHERE s.conversation_id=c.id AND s.role='assistant' AND s.state='succeeded' AND s.body<>'' AND s.seq>COALESCE(p.read_seq,n.read_seq,0)) unread,
|
|
3347
|
+
n.conversation_id notice_id
|
|
2782
3348
|
FROM project_agent_conversations c LEFT JOIN agent_matters m ON m.conversation_id=c.id
|
|
2783
3349
|
LEFT JOIN agent_matter_members p ON p.conversation_id=c.id AND p.member_id=?
|
|
3350
|
+
LEFT JOIN agent_notice_threads n ON n.conversation_id=c.id AND n.user_id=c.owner
|
|
2784
3351
|
WHERE c.project_id=? AND c.role='instant' AND
|
|
2785
|
-
((c.owner=? AND NOT EXISTS(SELECT 1 FROM agent_contexts x WHERE x.conversation_id=c.id)) OR p.member_id IS NOT NULL)
|
|
3352
|
+
((c.owner=? AND NOT EXISTS(SELECT 1 FROM agent_contexts x WHERE x.conversation_id=c.id AND x.kind<>'notice')) OR p.member_id IS NOT NULL)
|
|
3353
|
+
AND (n.target_id IS NULL OR n.target_id=c.id)
|
|
2786
3354
|
ORDER BY COALESCE(updated_at,c.created_at) DESC,c.rowid DESC`
|
|
2787
3355
|
).all(identity.memberId, identity.workspaceId, identity.memberId).map((row) => ({
|
|
2788
3356
|
id: String(row.id),
|
|
@@ -2790,13 +3358,13 @@ var AgentStore = class {
|
|
|
2790
3358
|
state: row.state ?? null,
|
|
2791
3359
|
createdAt: String(row.created_at),
|
|
2792
3360
|
updatedAt: String(row.updated_at ?? row.created_at),
|
|
2793
|
-
...row.matter_title ? { kind: "matter", status: row.matter_status, unread: !!row.unread } : {}
|
|
3361
|
+
...row.matter_title ? { kind: "matter", status: row.matter_status, unread: !!row.unread } : row.notice_id ? { kind: "notice", unread: !!row.unread } : {}
|
|
2794
3362
|
}))
|
|
2795
3363
|
};
|
|
2796
3364
|
}
|
|
2797
3365
|
createChat(identity) {
|
|
2798
3366
|
return diagnosticTransaction(this.hub, () => {
|
|
2799
|
-
const id =
|
|
3367
|
+
const id = randomUUID7();
|
|
2800
3368
|
this.hub.db.prepare(
|
|
2801
3369
|
`INSERT INTO project_agent_conversations(id,project_id,role,owner,created_at)
|
|
2802
3370
|
VALUES(?,?,'instant',?,?)`
|
|
@@ -2811,25 +3379,35 @@ var AgentStore = class {
|
|
|
2811
3379
|
});
|
|
2812
3380
|
}
|
|
2813
3381
|
chatView(id) {
|
|
2814
|
-
const matter = this.hub.db.prepare("SELECT
|
|
2815
|
-
|
|
3382
|
+
const matter = this.hub.db.prepare("SELECT * FROM agent_matters WHERE conversation_id=?").get(id);
|
|
3383
|
+
const notice = this.hub.db.prepare("SELECT * FROM agent_notice_threads WHERE conversation_id=?").get(id);
|
|
3384
|
+
const messages = new AgentMessages(this.hub).list(id);
|
|
3385
|
+
return AgentChatSchema.parse({
|
|
2816
3386
|
id,
|
|
2817
3387
|
...matter ? {
|
|
2818
3388
|
kind: "matter",
|
|
2819
3389
|
title: matter.title,
|
|
2820
3390
|
participants: new Matters(this.hub).members(id),
|
|
2821
|
-
visibility: matter.visibility
|
|
2822
|
-
|
|
3391
|
+
visibility: matter.visibility,
|
|
3392
|
+
status: matter.status,
|
|
3393
|
+
pending: matter.pending_json ? JSON.parse(String(matter.pending_json)) : null,
|
|
3394
|
+
hold: matter.hold_json ? JSON.parse(String(matter.hold_json)) : null
|
|
3395
|
+
} : notice ? { kind: "notice" } : {},
|
|
3396
|
+
messages,
|
|
3397
|
+
readCursor: messages.at(-1)?.seq ?? 0,
|
|
3398
|
+
redirect: notice?.target_id && notice.target_id !== id ? String(notice.target_id) : null,
|
|
2823
3399
|
runs: this.hub.db.prepare("SELECT * FROM project_agent_runs WHERE conversation_id=? ORDER BY rowid").all(id).map((row) => this.runView(row)),
|
|
2824
3400
|
cursor: Number(
|
|
2825
3401
|
this.hub.db.prepare(
|
|
2826
3402
|
"SELECT COALESCE(MAX(seq),0) n FROM project_agent_stream WHERE conversation_id=?"
|
|
2827
3403
|
).get(id).n
|
|
2828
3404
|
)
|
|
2829
|
-
};
|
|
3405
|
+
});
|
|
2830
3406
|
}
|
|
2831
3407
|
event(conversation, kind, data) {
|
|
2832
|
-
if (this.hub.db.prepare(
|
|
3408
|
+
if (this.hub.db.prepare(
|
|
3409
|
+
"SELECT 1 FROM agent_contexts WHERE conversation_id=? AND kind NOT IN('matter','notice')"
|
|
3410
|
+
).get(conversation))
|
|
2833
3411
|
return;
|
|
2834
3412
|
this.hub.db.prepare("INSERT INTO project_agent_stream(conversation_id,kind,data) VALUES(?,?,?)").run(conversation, kind, JSON.stringify(data));
|
|
2835
3413
|
}
|
|
@@ -2838,24 +3416,34 @@ var AgentStore = class {
|
|
|
2838
3416
|
"SELECT seq,kind,data FROM project_agent_stream WHERE conversation_id=? AND seq>? ORDER BY seq"
|
|
2839
3417
|
).all(conversation, after);
|
|
2840
3418
|
}
|
|
2841
|
-
ask(identity, text, chatId) {
|
|
3419
|
+
ask(identity, text, chatId, requestId) {
|
|
3420
|
+
if (chatId && this.hub.db.prepare("SELECT 1 FROM agent_contexts WHERE conversation_id=? AND kind='notice'").get(chatId))
|
|
3421
|
+
return new AgentNotices(this.hub).ask(identity, chatId, text, requestId);
|
|
2842
3422
|
if (chatId && this.hub.db.prepare("SELECT 1 FROM agent_matters WHERE conversation_id=?").get(chatId))
|
|
2843
|
-
return new Matters(this.hub).ask(identity, chatId, text);
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
this.hub.db.prepare(
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
3423
|
+
return new Matters(this.hub).ask(identity, chatId, text, requestId);
|
|
3424
|
+
if (chatId) this.ownedChat(identity, chatId);
|
|
3425
|
+
return new AgentMessages(this.hub).request(
|
|
3426
|
+
requestId ? `chat:${identity.workspaceId}:${identity.memberId}:${requestId}` : void 0,
|
|
3427
|
+
{ chatId, text },
|
|
3428
|
+
() => {
|
|
3429
|
+
const conversation = chatId ? this.ownedChat(identity, chatId) : this.conversation(identity.workspaceId, "instant", identity.memberId);
|
|
3430
|
+
const id = randomUUID7(), date = now2();
|
|
3431
|
+
this.hub.db.prepare(
|
|
3432
|
+
"INSERT INTO project_agent_runs(id,conversation_id,text,created_at,updated_at) VALUES(?,?,?,?,?)"
|
|
3433
|
+
).run(id, conversation.id, text, date, date);
|
|
3434
|
+
const messages = new AgentMessages(this.hub);
|
|
3435
|
+
messages.input(String(conversation.id), id, text, identity.memberId);
|
|
3436
|
+
messages.answer(String(conversation.id), id, "", "queued");
|
|
3437
|
+
const value = this.runView(
|
|
3438
|
+
this.hub.db.prepare("SELECT * FROM project_agent_runs WHERE id=?").get(id)
|
|
3439
|
+
);
|
|
3440
|
+
this.event(String(conversation.id), "run", value);
|
|
3441
|
+
return value;
|
|
3442
|
+
}
|
|
3443
|
+
);
|
|
2856
3444
|
}
|
|
2857
|
-
enqueue(project, text, sourceKey = null,
|
|
2858
|
-
return { taskId: enqueueInput(this.hub, project, text, sourceKey,
|
|
3445
|
+
enqueue(project, text, sourceKey = null, details2 = {}) {
|
|
3446
|
+
return { taskId: enqueueInput(this.hub, project, text, sourceKey, details2) };
|
|
2859
3447
|
}
|
|
2860
3448
|
members(project) {
|
|
2861
3449
|
return this.hub.db.prepare(
|
|
@@ -2889,19 +3477,25 @@ var AgentStore = class {
|
|
|
2889
3477
|
db.prepare(
|
|
2890
3478
|
`INSERT INTO project_agent_runs(id,conversation_id,task_id,text,created_at,updated_at)
|
|
2891
3479
|
VALUES(?,?,?,?,?,?) ON CONFLICT(conversation_id,task_id) DO NOTHING`
|
|
2892
|
-
).run(
|
|
3480
|
+
).run(randomUUID7(), lifetime.id, input.task_id, input.text, input.created_at, now2());
|
|
2893
3481
|
}
|
|
2894
3482
|
const run = db.prepare(
|
|
2895
3483
|
`SELECT r.*,c.project_id,c.role,COALESCE(a.member_id,c.owner) owner,c.session_file,x.kind context_kind,x.scope_key context_scope,w.binding_id channel_binding,w.speaker FROM project_agent_runs r
|
|
2896
3484
|
JOIN project_agent_conversations c ON c.id=r.conversation_id
|
|
2897
3485
|
LEFT JOIN agent_contexts x ON x.conversation_id=c.id LEFT JOIN agent_run_actors a ON a.run_id=r.id LEFT JOIN agent_run_channels w ON w.run_id=r.id WHERE c.project_id=? AND r.state='queued' AND r.due_at<=?
|
|
3486
|
+
AND r.batch_run_id IS NULL AND (x.kind IS NULL OR x.kind<>'notice')
|
|
3487
|
+
AND (COALESCE(json_extract((SELECT hold_json FROM agent_matters WHERE conversation_id=r.conversation_id),'$.scope'),'')<>'all'
|
|
3488
|
+
OR json_extract((SELECT details_json FROM agent_run_details WHERE run_id=r.id),'$.origin.kind')='human')
|
|
2898
3489
|
AND NOT EXISTS(SELECT 1 FROM project_agent_runs earlier WHERE earlier.conversation_id=r.conversation_id
|
|
2899
|
-
AND earlier.rowid<r.rowid AND earlier.state IN('queued','running')
|
|
3490
|
+
AND earlier.rowid<r.rowid AND earlier.state IN('queued','running')
|
|
3491
|
+
AND (COALESCE(json_extract((SELECT hold_json FROM agent_matters WHERE conversation_id=r.conversation_id),'$.scope'),'')<>'all'
|
|
3492
|
+
OR json_extract((SELECT details_json FROM agent_run_details WHERE run_id=earlier.id),'$.origin.kind')='human'))
|
|
2900
3493
|
AND NOT EXISTS(SELECT 1 FROM project_agent_runs active WHERE active.conversation_id=r.conversation_id AND active.state='running')
|
|
2901
3494
|
ORDER BY r.rowid LIMIT 1`
|
|
2902
3495
|
).get(project, at);
|
|
2903
3496
|
if (!run) return void 0;
|
|
2904
|
-
const
|
|
3497
|
+
const text = batchInput(this.hub, project, run);
|
|
3498
|
+
const token = randomUUID7();
|
|
2905
3499
|
db.prepare(
|
|
2906
3500
|
"UPDATE project_agent_runs SET state='running',attempts=attempts+1,run_token=?,lease_until=?,updated_at=? WHERE id=?"
|
|
2907
3501
|
).run(token, at + 3e4, now2(), run.id);
|
|
@@ -2912,6 +3506,7 @@ var AgentStore = class {
|
|
|
2912
3506
|
this.event(String(run.conversation_id), "state", { id: run.id, state: "running" });
|
|
2913
3507
|
return {
|
|
2914
3508
|
...run,
|
|
3509
|
+
text,
|
|
2915
3510
|
run_token: token,
|
|
2916
3511
|
attempts: Number(run.attempts) + 1
|
|
2917
3512
|
};
|
|
@@ -2937,16 +3532,22 @@ var AgentStore = class {
|
|
|
2937
3532
|
if (!this.active(run)) return;
|
|
2938
3533
|
this.hub.db.prepare("UPDATE project_agent_runs SET answer=answer||?,updated_at=? WHERE id=?").run(text, now2(), run.id);
|
|
2939
3534
|
this.event(run.conversation_id, "delta", { id: run.id, text });
|
|
3535
|
+
new AgentMessages(this.hub).delta(run.id, text);
|
|
2940
3536
|
});
|
|
2941
3537
|
}
|
|
2942
3538
|
resetAnswer(run) {
|
|
2943
3539
|
if (!this.active(run)) return;
|
|
2944
3540
|
this.hub.db.prepare("UPDATE project_agent_runs SET answer='' WHERE id=?").run(run.id);
|
|
2945
3541
|
this.event(run.conversation_id, "reset", { id: run.id });
|
|
3542
|
+
new AgentMessages(this.hub).answer(run.conversation_id, run.id, "", "running");
|
|
2946
3543
|
}
|
|
2947
3544
|
finish(run, answer, error = null, retryable = false, diagnostic) {
|
|
2948
3545
|
transaction(this.hub.db, () => {
|
|
2949
3546
|
if (!this.active(run)) return;
|
|
3547
|
+
if (!error) {
|
|
3548
|
+
const draft = this.hub.db.prepare("SELECT body FROM agent_answer_drafts WHERE run_id=?").get(run.id);
|
|
3549
|
+
if (draft) answer = String(draft.body);
|
|
3550
|
+
}
|
|
2950
3551
|
const retry = !!error && retryable && run.attempts <= 3;
|
|
2951
3552
|
const state = retry ? "queued" : error ? "failed" : "succeeded";
|
|
2952
3553
|
if (error && !answer)
|
|
@@ -2966,12 +3567,30 @@ var AgentStore = class {
|
|
|
2966
3567
|
if (run.task_id !== null)
|
|
2967
3568
|
this.hub.db.prepare("UPDATE project_agent_inbox SET state=? WHERE project_id=? AND task_id=?").run(state, run.project_id, run.task_id);
|
|
2968
3569
|
this.event(run.conversation_id, "state", { id: run.id, state, error, answer });
|
|
3570
|
+
settleBatch(this.hub, run.id, state, error);
|
|
3571
|
+
new AgentMessages(this.hub).answer(run.conversation_id, run.id, answer, state);
|
|
3572
|
+
if (!error && run.context_kind === "matter") new Matters(this.hub).commitReports(run, answer);
|
|
3573
|
+
if (error) new Matters(this.hub).abandonDrafts(run.id);
|
|
3574
|
+
if (run.role === "lifetime") {
|
|
3575
|
+
const notice = inputDetails(this.hub, run.project_id, run.task_id).notification;
|
|
3576
|
+
if (notice) {
|
|
3577
|
+
const target = this.hub.db.prepare("SELECT target_id FROM agent_notice_threads WHERE conversation_id=?").get(notice.conversationId);
|
|
3578
|
+
if (!target?.target_id) {
|
|
3579
|
+
this.hub.db.prepare(
|
|
3580
|
+
"UPDATE project_agent_runs SET state=?,answer=?,error=?,updated_at=? WHERE id=?"
|
|
3581
|
+
).run(state, answer, error, now2(), notice.runId);
|
|
3582
|
+
new AgentMessages(this.hub).answer(notice.conversationId, notice.runId, answer, state);
|
|
3583
|
+
}
|
|
3584
|
+
}
|
|
3585
|
+
}
|
|
2969
3586
|
if (error && !retry && run.context_kind === "matter")
|
|
2970
3587
|
new Matters(this.hub).report(
|
|
2971
3588
|
run,
|
|
2972
3589
|
{
|
|
3590
|
+
kind: "coordination",
|
|
2973
3591
|
status: "waiting",
|
|
2974
|
-
summary: "\u672C\u8F6E\u6267\u884C\u672A\u5B8C\u6210\uFF0C\u8BCA\u65AD\u5DF2\u8BB0\u5F55\u5728\u8FD0\u884C\u63D0\u9192\u4E2D\u3002\u8BF7\u6838\u5BF9\u539F\u56E0\u540E\u51B3\u5B9A\u662F\u5426\u7EE7\u7EED\u3002"
|
|
3592
|
+
summary: "\u672C\u8F6E\u6267\u884C\u672A\u5B8C\u6210\uFF0C\u8BCA\u65AD\u5DF2\u8BB0\u5F55\u5728\u8FD0\u884C\u63D0\u9192\u4E2D\u3002\u8BF7\u6838\u5BF9\u539F\u56E0\u540E\u51B3\u5B9A\u662F\u5426\u7EE7\u7EED\u3002",
|
|
3593
|
+
request: "\u8BF7\u6838\u5BF9\u7EC8\u6B62\u5931\u8D25\u7684\u539F\u56E0\uFF1B\u4E0D\u8981\u91CD\u590D\u6D3E\u53D1\u540C\u4E00\u5931\u8D25\u64CD\u4F5C\u3002"
|
|
2975
3594
|
},
|
|
2976
3595
|
`matter-failure:${run.id}`
|
|
2977
3596
|
);
|
|
@@ -3015,7 +3634,7 @@ var AgentStore = class {
|
|
|
3015
3634
|
).get(sourceRunId) : null;
|
|
3016
3635
|
const sourceAudit = source?.task_id != null ? inputDetails(this.hub, project, Number(source.task_id)).audit : void 0;
|
|
3017
3636
|
const sourceUser = source?.owner || sourceAudit?.requester || null;
|
|
3018
|
-
const id =
|
|
3637
|
+
const id = randomUUID7();
|
|
3019
3638
|
this.hub.db.prepare(
|
|
3020
3639
|
`INSERT INTO project_agent_outbox
|
|
3021
3640
|
(id,project_id,task_id,box,body,created_at,operation_key,source_run_id,source_user_id) VALUES(?,?,?,?,?,?,?,?,?)`
|
|
@@ -3032,6 +3651,9 @@ var AgentStore = class {
|
|
|
3032
3651
|
);
|
|
3033
3652
|
for (const user of valid)
|
|
3034
3653
|
this.hub.db.prepare("INSERT INTO project_agent_recipients(message_id,user_id) VALUES(?,?)").run(id, user);
|
|
3654
|
+
if (value.to !== "debug")
|
|
3655
|
+
for (const user of valid)
|
|
3656
|
+
new AgentNotices(this.hub).ensure({ workspaceId: project, memberId: user }, id);
|
|
3035
3657
|
return { messageId: id };
|
|
3036
3658
|
}
|
|
3037
3659
|
send(project, task, input, operationKey = null, sourceRunId = null) {
|
|
@@ -3055,9 +3677,11 @@ var AgentStore = class {
|
|
|
3055
3677
|
createdAt: row.created_at,
|
|
3056
3678
|
readAt: row.read_at,
|
|
3057
3679
|
archivedAt: row.archived_at,
|
|
3680
|
+
conversationId: this.hub.db.prepare(
|
|
3681
|
+
"SELECT COALESCE(target_id,conversation_id) id FROM agent_notice_threads WHERE message_id=? AND user_id=?"
|
|
3682
|
+
).get(id, identity.memberId)?.id ?? null,
|
|
3058
3683
|
recipients: recipients.map((r) => ({
|
|
3059
|
-
userId: r.user_id
|
|
3060
|
-
reply: r.reply_body === null ? null : { body: r.reply_body, createdAt: r.replied_at }
|
|
3684
|
+
userId: r.user_id
|
|
3061
3685
|
}))
|
|
3062
3686
|
});
|
|
3063
3687
|
}
|
|
@@ -3069,6 +3693,17 @@ var AgentStore = class {
|
|
|
3069
3693
|
const messages = rows.map((row) => this.message(identity, String(row.id)));
|
|
3070
3694
|
return { messages, unread: messages.filter((m) => !m.readAt && !m.archivedAt).length };
|
|
3071
3695
|
}
|
|
3696
|
+
unread(identity) {
|
|
3697
|
+
return new Matters(this.hub).unread(identity) + Number(
|
|
3698
|
+
this.hub.db.prepare(
|
|
3699
|
+
`SELECT COUNT(*) n FROM agent_notice_threads t
|
|
3700
|
+
JOIN project_agent_conversations c ON c.id=t.conversation_id WHERE c.project_id=? AND t.user_id=? AND t.target_id IS NULL
|
|
3701
|
+
AND EXISTS(SELECT 1 FROM agent_messages m WHERE m.conversation_id=t.conversation_id AND m.role='assistant' AND m.state='succeeded' AND m.body<>'' AND m.seq>t.read_seq)`
|
|
3702
|
+
).get(identity.workspaceId, identity.memberId).n
|
|
3703
|
+
) + this.messages(identity).messages.filter(
|
|
3704
|
+
(item) => item.box === "debug" && !item.readAt && !item.archivedAt
|
|
3705
|
+
).length;
|
|
3706
|
+
}
|
|
3072
3707
|
mark(identity, id, archive = false) {
|
|
3073
3708
|
this.message(identity, id);
|
|
3074
3709
|
this.hub.db.prepare(
|
|
@@ -3076,44 +3711,15 @@ var AgentStore = class {
|
|
|
3076
3711
|
).run(...archive ? [now2(), now2()] : [now2()], id, identity.memberId);
|
|
3077
3712
|
return this.message(identity, id);
|
|
3078
3713
|
}
|
|
3079
|
-
reply(identity, id, body) {
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
if (recipient.reply_body === body) return { taskId: Number(recipient.reply_task_id) };
|
|
3084
|
-
if (recipient.replied_at || recipient.archived_at)
|
|
3085
|
-
throw new HubError(409, "OUTBOX_ARCHIVED", "\u8FD9\u6761\u6D88\u606F\u5DF2\u7ECF\u7ED3\u675F\u3002");
|
|
3086
|
-
const source = this.hub.db.prepare(
|
|
3087
|
-
`SELECT COALESCE(o.source_user_id,c.owner) owner,r.id FROM project_agent_outbox o
|
|
3088
|
-
LEFT JOIN project_agent_runs r ON r.id=o.source_run_id
|
|
3089
|
-
LEFT JOIN project_agent_conversations c ON c.id=r.conversation_id
|
|
3090
|
-
WHERE o.id=? AND o.project_id=? AND (o.source_user_id IS NOT NULL OR (c.role='instant' AND c.owner<>''))`
|
|
3091
|
-
).get(id, identity.workspaceId);
|
|
3092
|
-
const text = outboxReplyPrompt({
|
|
3093
|
-
user: identity.memberId,
|
|
3094
|
-
origin: source ? { owner: String(source.owner) } : null,
|
|
3095
|
-
message: message.body,
|
|
3096
|
-
reply: body
|
|
3097
|
-
});
|
|
3098
|
-
const taskId = enqueueInput(
|
|
3099
|
-
this.hub,
|
|
3100
|
-
identity.workspaceId,
|
|
3101
|
-
text,
|
|
3102
|
-
`reply:${id}:${identity.memberId}`,
|
|
3103
|
-
{
|
|
3104
|
-
audit: { messageId: id, sourceTaskId: message.taskId, requester: source?.owner ?? null }
|
|
3105
|
-
}
|
|
3106
|
-
);
|
|
3107
|
-
this.hub.db.prepare(
|
|
3108
|
-
"UPDATE project_agent_recipients SET reply_body=?,replied_at=?,archived_at=?,read_at=?,reply_task_id=? WHERE message_id=? AND user_id=?"
|
|
3109
|
-
).run(body, now2(), now2(), now2(), taskId, id, identity.memberId);
|
|
3110
|
-
return { taskId };
|
|
3111
|
-
});
|
|
3714
|
+
reply(identity, id, body, requestId) {
|
|
3715
|
+
this.message(identity, id);
|
|
3716
|
+
const conversation = new AgentNotices(this.hub).ensure(identity, id);
|
|
3717
|
+
return this.ask(identity, body, conversation, requestId);
|
|
3112
3718
|
}
|
|
3113
3719
|
};
|
|
3114
3720
|
|
|
3115
3721
|
// apps/server/materials/store.ts
|
|
3116
|
-
import { createHash as
|
|
3722
|
+
import { createHash as createHash7, randomUUID as randomUUID8 } from "node:crypto";
|
|
3117
3723
|
import { mkdir as mkdir2, readFile as readFile2, writeFile, rename, stat } from "node:fs/promises";
|
|
3118
3724
|
import { join as join5 } from "node:path";
|
|
3119
3725
|
|
|
@@ -3230,7 +3836,7 @@ function requireProjectActor(hub, actor, owner) {
|
|
|
3230
3836
|
|
|
3231
3837
|
// apps/server/materials/store.ts
|
|
3232
3838
|
var exports = /* @__PURE__ */ new Map();
|
|
3233
|
-
var fingerprint2 = (value) =>
|
|
3839
|
+
var fingerprint2 = (value) => createHash7("sha256").update(JSON.stringify(value)).digest("hex");
|
|
3234
3840
|
function materialHref(project, id, revision) {
|
|
3235
3841
|
return `/projects/${encodeURIComponent(project)}/materials?${new URLSearchParams({ id, ...revision ? { revision: String(revision) } : {} })}`;
|
|
3236
3842
|
}
|
|
@@ -3415,7 +4021,7 @@ var MaterialStore = class {
|
|
|
3415
4021
|
const materials2 = this.all(project);
|
|
3416
4022
|
await mkdir2(root, { recursive: true });
|
|
3417
4023
|
const atomic = async (path, bytes) => {
|
|
3418
|
-
const temp = `${path}.${
|
|
4024
|
+
const temp = `${path}.${randomUUID8()}.tmp`;
|
|
3419
4025
|
await writeFile(temp, bytes, { mode: 292 });
|
|
3420
4026
|
await rename(temp, path);
|
|
3421
4027
|
};
|
|
@@ -3514,10 +4120,10 @@ async function gitCredentials(hub, project) {
|
|
|
3514
4120
|
import { spawn } from "node:child_process";
|
|
3515
4121
|
import { mkdir as mkdir4, readFile as readFile3, writeFile as writeFile3, rename as rename2, rm as rm2, mkdtemp as mkdtemp2, stat as stat2, rmdir } from "node:fs/promises";
|
|
3516
4122
|
import { dirname as dirname4, join as join7 } from "node:path";
|
|
3517
|
-
import { randomUUID as
|
|
4123
|
+
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
3518
4124
|
|
|
3519
4125
|
// apps/server/runtime-status.ts
|
|
3520
|
-
import { randomUUID as
|
|
4126
|
+
import { randomUUID as randomUUID9 } from "node:crypto";
|
|
3521
4127
|
|
|
3522
4128
|
// apps/server/deployment.ts
|
|
3523
4129
|
function normalizePublicUrl(value) {
|
|
@@ -3682,7 +4288,7 @@ var RuntimeStatus = class {
|
|
|
3682
4288
|
};
|
|
3683
4289
|
}
|
|
3684
4290
|
startWorker() {
|
|
3685
|
-
const id =
|
|
4291
|
+
const id = randomUUID9();
|
|
3686
4292
|
observe(() => {
|
|
3687
4293
|
this.hub.db.prepare(
|
|
3688
4294
|
"DELETE FROM runtime_workers WHERE last_seen<? AND id<>(SELECT id FROM runtime_workers ORDER BY last_seen DESC LIMIT 1)"
|
|
@@ -3714,7 +4320,7 @@ var RuntimeStatus = class {
|
|
|
3714
4320
|
};
|
|
3715
4321
|
}
|
|
3716
4322
|
startRepository(project, identity, fetching) {
|
|
3717
|
-
const attempt =
|
|
4323
|
+
const attempt = randomUUID9();
|
|
3718
4324
|
observe(() => {
|
|
3719
4325
|
this.hub.db.prepare(
|
|
3720
4326
|
`INSERT INTO runtime_repositories VALUES(?,?,?,?,?,NULL)
|
|
@@ -3828,7 +4434,7 @@ var ProjectWorkspace = class {
|
|
|
3828
4434
|
const target = join7(this.workspaces, path);
|
|
3829
4435
|
await mkdir4(dirname4(target), { recursive: true });
|
|
3830
4436
|
const bytes = await files.download({ workspaceId: this.project, memberId: "" }, hash3);
|
|
3831
|
-
const temporary = `${target}.${
|
|
4437
|
+
const temporary = `${target}.${randomUUID10()}.tmp`;
|
|
3832
4438
|
try {
|
|
3833
4439
|
await writeFile3(temporary, bytes, { mode: 292 });
|
|
3834
4440
|
await rename2(temporary, target);
|
|
@@ -4039,7 +4645,7 @@ var WorkspaceImports = class {
|
|
|
4039
4645
|
`INSERT INTO project_workspace_imports VALUES (?,?,?,?,'pending',NULL,NULL)
|
|
4040
4646
|
ON CONFLICT(project_id) DO UPDATE SET request_id=excluded.request_id,requester=excluded.requester,
|
|
4041
4647
|
source_json=excluded.source_json,state='pending',result_json=NULL,error=NULL`
|
|
4042
|
-
).run(project,
|
|
4648
|
+
).run(project, randomUUID11(), identity.memberId, json);
|
|
4043
4649
|
return this.status(project);
|
|
4044
4650
|
}
|
|
4045
4651
|
cancelRepository(project) {
|
|
@@ -4053,7 +4659,7 @@ var WorkspaceImports = class {
|
|
|
4053
4659
|
"SELECT * FROM project_workspace_imports WHERE project_id=? AND state IN ('pending','running')"
|
|
4054
4660
|
).get(project);
|
|
4055
4661
|
if (!row) return false;
|
|
4056
|
-
const store = new AgentStore(this.hub), lease =
|
|
4662
|
+
const store = new AgentStore(this.hub), lease = randomUUID11();
|
|
4057
4663
|
if (!store.ownProject(project, lease)) return false;
|
|
4058
4664
|
const cancelled = new AbortController(), active = AbortSignal.any([signal, cancelled.signal]);
|
|
4059
4665
|
const current = () => !!this.hub.db.prepare(
|
|
@@ -4471,7 +5077,7 @@ var FileRepository = class {
|
|
|
4471
5077
|
}
|
|
4472
5078
|
async upload(authorization, device, hash3, bytes) {
|
|
4473
5079
|
this.hub.authorize(authorization, device);
|
|
4474
|
-
if (
|
|
5080
|
+
if (createHash8("sha256").update(bytes).digest("hex") !== HashSchema.parse(hash3))
|
|
4475
5081
|
throw new HubError(400, "HASH_MISMATCH", "Content does not match its SHA-256.");
|
|
4476
5082
|
const ref = await this.blobs.put(bytes);
|
|
4477
5083
|
transaction(this.hub.db, () => {
|
|
@@ -4524,7 +5130,7 @@ var FileRepository = class {
|
|
|
4524
5130
|
const head = this.head(identity, owner);
|
|
4525
5131
|
if (head > 0 || publicationComplete(this.hub, identity.workspaceId, owner, head)) return false;
|
|
4526
5132
|
this.writeVersion(identity, owner, {
|
|
4527
|
-
operationId:
|
|
5133
|
+
operationId: randomUUID12(),
|
|
4528
5134
|
parentRevision: 0,
|
|
4529
5135
|
changes: entries.map((entry) => ({ kind: "upsert", ...entry }))
|
|
4530
5136
|
});
|
|
@@ -4537,7 +5143,7 @@ var FileRepository = class {
|
|
|
4537
5143
|
const changes = [...batch.changes].sort(
|
|
4538
5144
|
(a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0
|
|
4539
5145
|
);
|
|
4540
|
-
const fingerprint3 =
|
|
5146
|
+
const fingerprint3 = createHash8("sha256").update(JSON.stringify({ parentRevision: batch.parentRevision, changes })).digest("hex");
|
|
4541
5147
|
const workspace = identity.workspaceId;
|
|
4542
5148
|
const prior = this.hub.db.prepare(
|
|
4543
5149
|
"SELECT fingerprint, revision FROM file_operations WHERE workspace_id=? AND owner=? AND operation_id=?"
|
|
@@ -4774,7 +5380,7 @@ function taskSnapshotCurrent(hub, workspace, snapshot) {
|
|
|
4774
5380
|
return taskInputComplete(hub, { workspaceId: workspace, memberId: snapshot.owner }) && Number(member?.file_revision) === snapshot.revision && Number(project?.file_revision ?? 0) === (snapshot.project?.revision ?? 0);
|
|
4775
5381
|
}
|
|
4776
5382
|
function snapshotDigest(files) {
|
|
4777
|
-
return
|
|
5383
|
+
return createHash9("sha256").update(JSON.stringify(files.map((f) => [f.path, f.sha256]))).digest("hex");
|
|
4778
5384
|
}
|
|
4779
5385
|
function captureScope(hub, identity, selection, allowEmpty = false) {
|
|
4780
5386
|
const repository = new FileRepository(hub);
|
|
@@ -4821,7 +5427,7 @@ function captureSnapshot(hub, identity, selection, options = {}) {
|
|
|
4821
5427
|
fingerprint: shared.fingerprint
|
|
4822
5428
|
};
|
|
4823
5429
|
if (shared.revision)
|
|
4824
|
-
snapshot.fingerprint =
|
|
5430
|
+
snapshot.fingerprint = createHash9("sha256").update(snapshot.fingerprint + ":" + shared.fingerprint).digest("hex");
|
|
4825
5431
|
}
|
|
4826
5432
|
return snapshot;
|
|
4827
5433
|
}
|
|
@@ -4888,7 +5494,7 @@ var SnapshotReader = class _SnapshotReader {
|
|
|
4888
5494
|
const entry = this.snapshot.files.find((f) => f.path === path);
|
|
4889
5495
|
if (!entry) throw new HubError(403, "AI_SCOPE_VIOLATION", "File is outside the task snapshot.");
|
|
4890
5496
|
const bytes = await new FileRepository(this.hub).download(this.identity, entry.sha256);
|
|
4891
|
-
if (bytes.byteLength !== entry.size ||
|
|
5497
|
+
if (bytes.byteLength !== entry.size || createHash9("sha256").update(bytes).digest("hex") !== entry.sha256)
|
|
4892
5498
|
throw new HubError(500, "AI_CONTENT_CORRUPT", "Snapshot content verification failed.");
|
|
4893
5499
|
return bytes;
|
|
4894
5500
|
}
|
|
@@ -4975,8 +5581,8 @@ var SnapshotReader = class _SnapshotReader {
|
|
|
4975
5581
|
};
|
|
4976
5582
|
|
|
4977
5583
|
// apps/server/store.ts
|
|
4978
|
-
import { createHash as
|
|
4979
|
-
import { join as
|
|
5584
|
+
import { createHash as createHash14, randomBytes as randomBytes7 } from "node:crypto";
|
|
5585
|
+
import { join as join13 } from "node:path";
|
|
4980
5586
|
import { existsSync } from "node:fs";
|
|
4981
5587
|
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
4982
5588
|
|
|
@@ -5224,6 +5830,65 @@ CREATE TABLE agent_run_details (
|
|
|
5224
5830
|
run_id TEXT PRIMARY KEY REFERENCES project_agent_runs(id) ON DELETE CASCADE, details_json TEXT NOT NULL
|
|
5225
5831
|
);
|
|
5226
5832
|
`;
|
|
5833
|
+
var agentCoordinationMigration = `
|
|
5834
|
+
ALTER TABLE project_agent_recipients DROP COLUMN reply_body;
|
|
5835
|
+
ALTER TABLE project_agent_recipients DROP COLUMN replied_at;
|
|
5836
|
+
ALTER TABLE project_agent_recipients DROP COLUMN reply_task_id;
|
|
5837
|
+
CREATE TABLE agent_messages (
|
|
5838
|
+
seq INTEGER PRIMARY KEY AUTOINCREMENT, id TEXT NOT NULL UNIQUE,
|
|
5839
|
+
conversation_id TEXT NOT NULL REFERENCES project_agent_conversations(id),
|
|
5840
|
+
run_id TEXT REFERENCES project_agent_runs(id) ON DELETE SET NULL,
|
|
5841
|
+
role TEXT NOT NULL, author TEXT NOT NULL DEFAULT '', body TEXT NOT NULL,
|
|
5842
|
+
state TEXT NOT NULL DEFAULT 'succeeded', source TEXT,
|
|
5843
|
+
operation_key TEXT NOT NULL UNIQUE, created_at TEXT NOT NULL, updated_at TEXT NOT NULL
|
|
5844
|
+
);
|
|
5845
|
+
CREATE INDEX agent_messages_conversation ON agent_messages(conversation_id,seq);
|
|
5846
|
+
CREATE TABLE agent_notice_threads (
|
|
5847
|
+
message_id TEXT NOT NULL REFERENCES project_agent_outbox(id), user_id TEXT NOT NULL,
|
|
5848
|
+
conversation_id TEXT NOT NULL UNIQUE REFERENCES project_agent_conversations(id),
|
|
5849
|
+
target_id TEXT REFERENCES agent_matters(conversation_id), read_seq INTEGER NOT NULL DEFAULT 0,
|
|
5850
|
+
PRIMARY KEY(message_id,user_id)
|
|
5851
|
+
);
|
|
5852
|
+
CREATE TABLE agent_message_requests (
|
|
5853
|
+
request_key TEXT PRIMARY KEY, fingerprint TEXT NOT NULL, result_json TEXT NOT NULL
|
|
5854
|
+
);
|
|
5855
|
+
CREATE TABLE agent_event_routes (
|
|
5856
|
+
project_id TEXT NOT NULL, input_key TEXT NOT NULL, branch TEXT NOT NULL DEFAULT '',
|
|
5857
|
+
conversation_id TEXT NOT NULL REFERENCES agent_matters(conversation_id),
|
|
5858
|
+
result_json TEXT NOT NULL, PRIMARY KEY(project_id,input_key,branch)
|
|
5859
|
+
);
|
|
5860
|
+
CREATE TABLE agent_matter_inputs (
|
|
5861
|
+
conversation_id TEXT NOT NULL REFERENCES agent_matters(conversation_id), input_key TEXT NOT NULL,
|
|
5862
|
+
run_id TEXT NOT NULL REFERENCES project_agent_runs(id) ON DELETE CASCADE,
|
|
5863
|
+
PRIMARY KEY(conversation_id,input_key)
|
|
5864
|
+
);
|
|
5865
|
+
CREATE TABLE agent_input_handbacks (
|
|
5866
|
+
run_id TEXT PRIMARY KEY REFERENCES project_agent_runs(id) ON DELETE CASCADE,
|
|
5867
|
+
conversation_id TEXT NOT NULL REFERENCES agent_matters(conversation_id),
|
|
5868
|
+
reason TEXT NOT NULL, task_id INTEGER NOT NULL
|
|
5869
|
+
);
|
|
5870
|
+
ALTER TABLE agent_matter_sources ADD COLUMN relation TEXT NOT NULL DEFAULT 'evidence';
|
|
5871
|
+
ALTER TABLE agent_matters ADD COLUMN goal TEXT NOT NULL DEFAULT '';
|
|
5872
|
+
ALTER TABLE agent_matters ADD COLUMN pending_json TEXT;
|
|
5873
|
+
ALTER TABLE agent_matters ADD COLUMN hold_json TEXT;
|
|
5874
|
+
ALTER TABLE agent_matters ADD COLUMN revision INTEGER NOT NULL DEFAULT 0;
|
|
5875
|
+
CREATE TABLE agent_matter_reports (
|
|
5876
|
+
id TEXT PRIMARY KEY, conversation_id TEXT NOT NULL REFERENCES agent_matters(conversation_id),
|
|
5877
|
+
run_id TEXT NOT NULL REFERENCES project_agent_runs(id) ON DELETE CASCADE,
|
|
5878
|
+
kind TEXT NOT NULL, status TEXT NOT NULL, summary TEXT NOT NULL, details_json TEXT NOT NULL,
|
|
5879
|
+
state TEXT NOT NULL DEFAULT 'pending', task_id INTEGER, created_at TEXT NOT NULL,
|
|
5880
|
+
operation_key TEXT NOT NULL UNIQUE
|
|
5881
|
+
);
|
|
5882
|
+
CREATE TABLE agent_answer_drafts (
|
|
5883
|
+
run_id TEXT PRIMARY KEY REFERENCES project_agent_runs(id) ON DELETE CASCADE,
|
|
5884
|
+
body TEXT NOT NULL
|
|
5885
|
+
);
|
|
5886
|
+
ALTER TABLE project_agent_runs ADD COLUMN batch_run_id TEXT REFERENCES project_agent_runs(id);
|
|
5887
|
+
CREATE TABLE agent_run_batches (
|
|
5888
|
+
run_id TEXT PRIMARY KEY REFERENCES project_agent_runs(id) ON DELETE CASCADE,
|
|
5889
|
+
text TEXT NOT NULL, details_json TEXT NOT NULL
|
|
5890
|
+
);
|
|
5891
|
+
`;
|
|
5227
5892
|
|
|
5228
5893
|
// apps/server/diagnostics/schema.ts
|
|
5229
5894
|
var diagnosticsMigration = `
|
|
@@ -5296,7 +5961,7 @@ CREATE INDEX device_connect_expiry ON device_connect_requests(expires_at);
|
|
|
5296
5961
|
`;
|
|
5297
5962
|
|
|
5298
5963
|
// apps/server/ai/task-writes.ts
|
|
5299
|
-
import { createHash as
|
|
5964
|
+
import { createHash as createHash10, randomUUID as randomUUID13 } from "node:crypto";
|
|
5300
5965
|
import { z as z19 } from "zod";
|
|
5301
5966
|
var taskWritesMigration = `
|
|
5302
5967
|
CREATE TABLE task_cards_new (
|
|
@@ -5336,7 +6001,7 @@ var TaskWriteSchema = z19.object({
|
|
|
5336
6001
|
).min(1)
|
|
5337
6002
|
}).strict();
|
|
5338
6003
|
function writeTasks(hub, identity, operation, input) {
|
|
5339
|
-
const value = TaskWriteSchema.parse(input), signature2 =
|
|
6004
|
+
const value = TaskWriteSchema.parse(input), signature2 = createHash10("sha256").update(JSON.stringify(value)).digest("hex"), actor = identity.matterId ? JSON.stringify({ matter: identity.matterId, sender: identity.memberId || null }) : identity.group ? JSON.stringify({ group: identity.group, sender: identity.memberId }) : identity.memberId;
|
|
5340
6005
|
return diagnosticTransaction(hub, () => {
|
|
5341
6006
|
requireProjectActor(hub, identity);
|
|
5342
6007
|
const prior = hub.db.prepare("SELECT * FROM task_write_operations WHERE project_id=? AND operation_id=?").get(identity.workspaceId, operation);
|
|
@@ -5353,14 +6018,14 @@ function writeTasks(hub, identity, operation, input) {
|
|
|
5353
6018
|
kind: "material",
|
|
5354
6019
|
materialId: material.id,
|
|
5355
6020
|
path: `materials/${material.id}/content.md`,
|
|
5356
|
-
sha256:
|
|
6021
|
+
sha256: createHash10("sha256").update(material.content ?? JSON.stringify(material.originals)).digest("hex"),
|
|
5357
6022
|
revision: material.revision,
|
|
5358
6023
|
syncedAt: material.createdAt
|
|
5359
6024
|
};
|
|
5360
6025
|
});
|
|
5361
6026
|
if (change.action === "create") {
|
|
5362
6027
|
requireProjectActor(hub, identity, change.owner);
|
|
5363
|
-
const id =
|
|
6028
|
+
const id = randomUUID13(), fields2 = JSON.stringify(change.fields);
|
|
5364
6029
|
hub.db.prepare(
|
|
5365
6030
|
`INSERT INTO task_cards(id,workspace_id,owner,job_id,fields_json,sources_json,synced_at,correction_json,origin) VALUES(?,?,?,NULL,?,?,?,?,'confirmed')`
|
|
5366
6031
|
).run(
|
|
@@ -5417,10 +6082,10 @@ function writeTasks(hub, identity, operation, input) {
|
|
|
5417
6082
|
}
|
|
5418
6083
|
|
|
5419
6084
|
// apps/server/wecom/groups.ts
|
|
5420
|
-
import { createHash as
|
|
6085
|
+
import { createHash as createHash11, randomBytes as randomBytes6, randomUUID as randomUUID15 } from "node:crypto";
|
|
5421
6086
|
|
|
5422
6087
|
// apps/server/agents/contexts.ts
|
|
5423
|
-
import { randomUUID as
|
|
6088
|
+
import { randomUUID as randomUUID14 } from "node:crypto";
|
|
5424
6089
|
var agentContextsMigration = `
|
|
5425
6090
|
CREATE TABLE agent_contexts (
|
|
5426
6091
|
conversation_id TEXT PRIMARY KEY REFERENCES project_agent_conversations(id),
|
|
@@ -5447,7 +6112,7 @@ var AgentContexts = class {
|
|
|
5447
6112
|
context_scope: scope2
|
|
5448
6113
|
})
|
|
5449
6114
|
);
|
|
5450
|
-
const id =
|
|
6115
|
+
const id = randomUUID14();
|
|
5451
6116
|
this.hub.db.prepare(
|
|
5452
6117
|
`INSERT INTO project_agent_conversations(id,project_id,role,owner,created_at) VALUES(?,?,'instant','',?)`
|
|
5453
6118
|
).run(id, identity.workspaceId, (/* @__PURE__ */ new Date()).toISOString());
|
|
@@ -5470,7 +6135,7 @@ var AgentContexts = class {
|
|
|
5470
6135
|
context_scope: String(context.scope_key)
|
|
5471
6136
|
})
|
|
5472
6137
|
);
|
|
5473
|
-
const id =
|
|
6138
|
+
const id = randomUUID14(), date = (/* @__PURE__ */ new Date()).toISOString();
|
|
5474
6139
|
this.hub.db.prepare(
|
|
5475
6140
|
"INSERT INTO project_agent_runs(id,conversation_id,text,created_at,updated_at) VALUES(?,?,?,?,?)"
|
|
5476
6141
|
).run(id, conversation, text, date, date);
|
|
@@ -5677,7 +6342,7 @@ var commands = [
|
|
|
5677
6342
|
{ name: "use", usage: "/use <\u9879\u76EE\u540D\u79F0\u6216ID>", description: "\u5207\u6362\u4E2A\u4EBA\u5F53\u524D\u9879\u76EE", scope: "\u4EC5\u79C1\u804A" }
|
|
5678
6343
|
];
|
|
5679
6344
|
function commandHelp() {
|
|
5680
|
-
return commands.map((c) => `${c.usage} \u2014 ${c.description}${c.scope === "\u4EC5\u79C1\u804A" ? "\uFF08\u4EC5\u79C1\u804A\uFF09" : ""}`).join("\n") + "\n\n\u79C1\u804A\u53EF\u76F4\u63A5\u63D0\u95EE\uFF1B\u7FA4\u5185\u8BF7
|
|
6345
|
+
return commands.map((c) => `${c.usage} \u2014 ${c.description}${c.scope === "\u4EC5\u79C1\u804A" ? "\uFF08\u4EC5\u79C1\u804A\uFF09" : ""}`).join("\n") + "\n\n\u79C1\u804A\u53EF\u76F4\u63A5\u63D0\u95EE\uFF1B\u7FA4\u5185\u8BF7 @Tim\u3002\u901A\u77E5\u8BF7\u5F15\u7528\u539F\u6D88\u606F\u56DE\u590D\u3002\u76EE\u524D\u652F\u6301\u6587\u5B57\u548C\u94FE\u63A5\u3002";
|
|
5681
6346
|
}
|
|
5682
6347
|
function bindingWelcome(confirmation) {
|
|
5683
6348
|
return `${confirmation}
|
|
@@ -5723,7 +6388,7 @@ CREATE TABLE wecom_group_codes(code_hash TEXT PRIMARY KEY,bot_id TEXT NOT NULL,p
|
|
|
5723
6388
|
CREATE TABLE wecom_groups(bot_id TEXT NOT NULL,chat_id TEXT NOT NULL,project_id TEXT NOT NULL,conversation_id TEXT,bound_by TEXT NOT NULL,created_at TEXT NOT NULL,PRIMARY KEY(bot_id,chat_id));
|
|
5724
6389
|
CREATE TABLE wecom_group_requests(run_id TEXT PRIMARY KEY,bot_id TEXT NOT NULL,chat_id TEXT NOT NULL,project_id TEXT NOT NULL,user_id TEXT NOT NULL,collected INTEGER NOT NULL DEFAULT 0);
|
|
5725
6390
|
`;
|
|
5726
|
-
var digest2 = (code) =>
|
|
6391
|
+
var digest2 = (code) => createHash11("sha256").update(code.toUpperCase()).digest("hex");
|
|
5727
6392
|
function wecomParts(text) {
|
|
5728
6393
|
const parts = [];
|
|
5729
6394
|
let part = "", bytes = 0;
|
|
@@ -5788,7 +6453,7 @@ var WecomGroups = class {
|
|
|
5788
6453
|
this.hub.db.prepare(
|
|
5789
6454
|
`INSERT OR IGNORE INTO wecom_deliveries(id,bot_id,external_id,user_id,project_id,kind,source_id,body,created_at,chat_type) VALUES(?,?,?,?,?,?,?,?,?,2)`
|
|
5790
6455
|
).run(
|
|
5791
|
-
|
|
6456
|
+
randomUUID15(),
|
|
5792
6457
|
bot,
|
|
5793
6458
|
chat,
|
|
5794
6459
|
identity?.memberId ?? null,
|
|
@@ -5865,7 +6530,7 @@ var WecomGroups = class {
|
|
|
5865
6530
|
conversation = contexts.create(identity, "group", JSON.stringify([bot, chat]));
|
|
5866
6531
|
this.hub.db.prepare("UPDATE wecom_groups SET conversation_id=? WHERE bot_id=? AND chat_id=?").run(conversation, bot, chat);
|
|
5867
6532
|
if (command?.name === "new") {
|
|
5868
|
-
notice("\u5DF2\u5F00\u59CB\u65B0\u8BDD\u9898\uFF0C\u8BF7
|
|
6533
|
+
notice("\u5DF2\u5F00\u59CB\u65B0\u8BDD\u9898\uFF0C\u8BF7 @Tim \u53D1\u9001\u95EE\u9898\u3002");
|
|
5869
6534
|
return;
|
|
5870
6535
|
}
|
|
5871
6536
|
}
|
|
@@ -6105,12 +6770,12 @@ function sessionPageHref(projectId2, owner, sessionKey2, target = {}) {
|
|
|
6105
6770
|
}
|
|
6106
6771
|
|
|
6107
6772
|
// apps/server/sessions.ts
|
|
6108
|
-
import { createHash as
|
|
6773
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
6109
6774
|
|
|
6110
6775
|
// packages/protocol/session-key.ts
|
|
6111
|
-
import { createHash as
|
|
6776
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
6112
6777
|
function sessionKey(agent, nativeId) {
|
|
6113
|
-
return
|
|
6778
|
+
return createHash12("sha256").update(JSON.stringify([agent, nativeId])).digest("hex");
|
|
6114
6779
|
}
|
|
6115
6780
|
|
|
6116
6781
|
// apps/server/sessions.ts
|
|
@@ -6119,6 +6784,21 @@ var SessionRepository = class {
|
|
|
6119
6784
|
this.hub = hub;
|
|
6120
6785
|
}
|
|
6121
6786
|
hub;
|
|
6787
|
+
baseline(identity, owner, key) {
|
|
6788
|
+
if (identity.memberId !== owner)
|
|
6789
|
+
throw new HubError(403, "SESSION_OWNER_REQUIRED", "\u53EA\u80FD\u6838\u5BF9\u81EA\u5DF1\u7684\u91C7\u96C6\u57FA\u7EBF\u3002");
|
|
6790
|
+
const current = this.hub.db.prepare(
|
|
6791
|
+
"SELECT revision,hash FROM sessions WHERE workspace_id=? AND owner=? AND session_key=?"
|
|
6792
|
+
).get(identity.workspaceId, owner, key);
|
|
6793
|
+
const cleared = current ? void 0 : this.hub.db.prepare(
|
|
6794
|
+
"SELECT revision,hash FROM reset_session_heads WHERE workspace_id=? AND owner=? AND session_key=?"
|
|
6795
|
+
).get(identity.workspaceId, owner, key);
|
|
6796
|
+
return {
|
|
6797
|
+
revision: Number(current?.revision ?? cleared?.revision ?? 0),
|
|
6798
|
+
hash: current?.hash ?? cleared?.hash ?? null,
|
|
6799
|
+
cleared: !!cleared
|
|
6800
|
+
};
|
|
6801
|
+
}
|
|
6122
6802
|
async commit(authorization, device, input) {
|
|
6123
6803
|
const request = SessionCommitSchema.parse(input);
|
|
6124
6804
|
const identity = this.hub.authorize(authorization, device);
|
|
@@ -6140,7 +6820,7 @@ var SessionRepository = class {
|
|
|
6140
6820
|
owner: identity.memberId,
|
|
6141
6821
|
key: request.sessionKey
|
|
6142
6822
|
});
|
|
6143
|
-
const fingerprint3 =
|
|
6823
|
+
const fingerprint3 = createHash13("sha256").update(JSON.stringify([request.sessionKey, request.parentRevision, request.blob])).digest("hex");
|
|
6144
6824
|
return transaction(this.hub.db, () => {
|
|
6145
6825
|
const { workspaceId: workspace, memberId: owner } = this.hub.authorize(authorization, device);
|
|
6146
6826
|
const db = this.hub.db;
|
|
@@ -6159,14 +6839,17 @@ var SessionRepository = class {
|
|
|
6159
6839
|
const current = db.prepare(
|
|
6160
6840
|
"SELECT revision, hash, title FROM sessions WHERE workspace_id=? AND owner=? AND session_key=?"
|
|
6161
6841
|
).get(workspace, owner, request.sessionKey);
|
|
6162
|
-
const
|
|
6842
|
+
const cleared = current ? void 0 : db.prepare(
|
|
6843
|
+
"SELECT revision,hash FROM reset_session_heads WHERE workspace_id=? AND owner=? AND session_key=?"
|
|
6844
|
+
).get(workspace, owner, request.sessionKey);
|
|
6845
|
+
const head = Number(current?.revision ?? cleared?.revision ?? 0);
|
|
6163
6846
|
if (head !== request.parentRevision)
|
|
6164
6847
|
throw new HubError(
|
|
6165
6848
|
409,
|
|
6166
6849
|
"STALE_REVISION",
|
|
6167
6850
|
"Session changed on the server; inspect the current revision before retrying."
|
|
6168
6851
|
);
|
|
6169
|
-
const revision = current?.hash === request.blob.sha256 ? head : head + 1;
|
|
6852
|
+
const revision = (current?.hash ?? cleared?.hash) === request.blob.sha256 ? head : head + 1;
|
|
6170
6853
|
if (revision !== head) {
|
|
6171
6854
|
const info = {
|
|
6172
6855
|
owner,
|
|
@@ -6541,7 +7224,7 @@ var GitHostError = class extends HubError {
|
|
|
6541
7224
|
provider;
|
|
6542
7225
|
upstreamStatus;
|
|
6543
7226
|
};
|
|
6544
|
-
var commentPrefix = "\u8BE5\u6D88\u606F\
|
|
7227
|
+
var commentPrefix = "\u8BE5\u6D88\u606F\u7531Tim\u53D1\u9001\uFF0C\u76F8\u5173\u5185\u5BB9\u4EC5\u4F9B\u53C2\u8003";
|
|
6545
7228
|
var legacyCommentBody = (body, key) => `${body}
|
|
6546
7229
|
|
|
6547
7230
|
<!-- teammy:${key} -->`;
|
|
@@ -6897,13 +7580,13 @@ var Gongfeng = class {
|
|
|
6897
7580
|
}
|
|
6898
7581
|
async read(number, signal, section = "details", by = "number") {
|
|
6899
7582
|
const pr = await this.details(number, signal, by);
|
|
6900
|
-
const
|
|
7583
|
+
const details2 = { ...pr, ...this.summary(pr) };
|
|
6901
7584
|
if (section === "comments") {
|
|
6902
7585
|
const [comments, review2] = await Promise.all([
|
|
6903
7586
|
this.pages(`merge_requests/${pr.id}/notes`, signal),
|
|
6904
7587
|
this.request(`merge_request/${pr.id}/review`, signal)
|
|
6905
7588
|
]);
|
|
6906
|
-
return { details, comments, review: review2 };
|
|
7589
|
+
return { details: details2, comments, review: review2 };
|
|
6907
7590
|
}
|
|
6908
7591
|
if (section === "diff") {
|
|
6909
7592
|
const changes = await this.request(`merge_request/${pr.id}/changes`, signal);
|
|
@@ -6913,7 +7596,7 @@ var Gongfeng = class {
|
|
|
6913
7596
|
(file) => file.is_too_large || file.is_collapse || typeof file.diff !== "string"
|
|
6914
7597
|
);
|
|
6915
7598
|
return {
|
|
6916
|
-
details,
|
|
7599
|
+
details: details2,
|
|
6917
7600
|
...changes,
|
|
6918
7601
|
incomplete,
|
|
6919
7602
|
...incomplete ? {
|
|
@@ -6921,7 +7604,7 @@ var Gongfeng = class {
|
|
|
6921
7604
|
} : {}
|
|
6922
7605
|
};
|
|
6923
7606
|
}
|
|
6924
|
-
return
|
|
7607
|
+
return details2;
|
|
6925
7608
|
}
|
|
6926
7609
|
async publish(number, body, key, taskId, signal) {
|
|
6927
7610
|
const publications = new PrPublications(this.hub, this.project, this.config);
|
|
@@ -7186,12 +7869,23 @@ async function executeProjectQuery(hub, identity, name, input, signal, includeIn
|
|
|
7186
7869
|
// apps/server/agents/personal-tools.ts
|
|
7187
7870
|
import { z as z23 } from "zod";
|
|
7188
7871
|
var personalQuerySchemas = {
|
|
7189
|
-
list_messages: z23.object({
|
|
7190
|
-
|
|
7872
|
+
list_messages: z23.object({
|
|
7873
|
+
offset: z23.number().int().nonnegative().default(0),
|
|
7874
|
+
limit: z23.number().int().positive().default(20)
|
|
7875
|
+
}).strict(),
|
|
7876
|
+
read_message: z23.object({
|
|
7877
|
+
messageId: z23.uuid().optional(),
|
|
7878
|
+
conversationId: z23.uuid().optional(),
|
|
7879
|
+
after: z23.number().int().nonnegative().default(0),
|
|
7880
|
+
limit: z23.number().int().positive().default(20)
|
|
7881
|
+
}).strict().refine(
|
|
7882
|
+
(value) => !!value.messageId !== !!value.conversationId,
|
|
7883
|
+
"\u586B\u5199 messageId \u6216 conversationId"
|
|
7884
|
+
)
|
|
7191
7885
|
};
|
|
7192
7886
|
var personalQueryDescriptions = {
|
|
7193
|
-
list_messages: "\u67E5\u8BE2\u5F53\u524D\u7528\u6237\u5728\u672C\u9879\u76EE\u6536\u5230\u7684\
|
|
7194
|
-
read_message: "\u8BFB\u53D6\
|
|
7887
|
+
list_messages: "\u5206\u9875\u67E5\u8BE2\u5F53\u524D\u7528\u6237\u5728\u672C\u9879\u76EE\u6536\u5230\u7684\u901A\u77E5\u53CA\u53C2\u4E0E\u7684\u4E8B\u9879\uFF0C\u8FD4\u56DE\u53EF\u7EE7\u7EED\u7684 conversationId\uFF1B\u4E0D\u4F1A\u6807\u8BB0\u5DF2\u8BFB\u3002",
|
|
7888
|
+
read_message: "\u6309 messageId \u8BFB\u53D6\u901A\u77E5\uFF0C\u6216\u6309 conversationId \u8BFB\u53D6\u6709\u6743\u8BBF\u95EE\u7684\u4F1A\u8BDD\u6B63\u6587\uFF1Bafter \u662F\u6D88\u606F\u6E38\u6807\uFF0C\u4E0D\u4F1A\u6807\u8BB0\u5DF2\u8BFB\u3002"
|
|
7195
7889
|
};
|
|
7196
7890
|
function requirePersonalActor(hub, identity) {
|
|
7197
7891
|
if (identity.group || identity.materialId || !identity.memberId)
|
|
@@ -7202,11 +7896,26 @@ function executePersonalQuery(hub, identity, name, input) {
|
|
|
7202
7896
|
requirePersonalActor(hub, identity);
|
|
7203
7897
|
const store = new AgentStore(hub);
|
|
7204
7898
|
if (name === "list_messages") {
|
|
7205
|
-
personalQuerySchemas.list_messages.parse(input);
|
|
7206
|
-
|
|
7899
|
+
const { offset, limit } = personalQuerySchemas.list_messages.parse(input);
|
|
7900
|
+
const all = store.messages(identity), chats = store.chats(identity).chats;
|
|
7901
|
+
return {
|
|
7902
|
+
messages: all.messages.slice(offset, offset + limit),
|
|
7903
|
+
conversations: chats.slice(offset, offset + limit),
|
|
7904
|
+
unread: all.unread,
|
|
7905
|
+
nextOffset: offset + limit < Math.max(all.messages.length, chats.length) ? offset + limit : null
|
|
7906
|
+
};
|
|
7907
|
+
}
|
|
7908
|
+
if (name === "read_message") {
|
|
7909
|
+
const value = personalQuerySchemas.read_message.parse(input);
|
|
7910
|
+
if (value.messageId) return store.message(identity, value.messageId);
|
|
7911
|
+
store.ownedChat(identity, value.conversationId);
|
|
7912
|
+
const items = new AgentMessages(hub).list(value.conversationId, value.after, value.limit + 1), page = items.slice(0, value.limit);
|
|
7913
|
+
return {
|
|
7914
|
+
conversationId: value.conversationId,
|
|
7915
|
+
messages: page,
|
|
7916
|
+
next: items.length > value.limit ? page.at(-1).seq : null
|
|
7917
|
+
};
|
|
7207
7918
|
}
|
|
7208
|
-
if (name === "read_message")
|
|
7209
|
-
return store.message(identity, personalQuerySchemas.read_message.parse(input).messageId);
|
|
7210
7919
|
throw new HubError(404, "QUERY_NOT_FOUND", "\u67E5\u8BE2\u5DE5\u5177\u4E0D\u5B58\u5728\u3002");
|
|
7211
7920
|
}
|
|
7212
7921
|
|
|
@@ -7228,8 +7937,8 @@ var schemas = {
|
|
|
7228
7937
|
var descriptions = {
|
|
7229
7938
|
...queryDescriptions,
|
|
7230
7939
|
...personalQueryDescriptions,
|
|
7231
|
-
ask_teammy: "\u5411\u672C\u9879\
|
|
7232
|
-
get_teammy_reply: "\u67E5\u8BE2\u6307\
|
|
7940
|
+
ask_teammy: "\u5411\u672C\u9879\u76EETim\u63D0\u95EE\u6216\u59D4\u6258\u5DE5\u4F5C\uFF0C\u62E5\u6709\u4E0E\u7F51\u9875\u5BF9\u8BDD\u76F8\u540C\u7684\u5B8C\u6574\u80FD\u529B\u3002\u9996\u6B21\u4E0D\u586B conversationId \u521B\u5EFA\u72EC\u7ACB\u7F51\u9875\u5BF9\u8BDD\uFF0C\u540E\u7EED\u643A\u5E26\u8FD4\u56DE\u7684 ID \u7EE7\u7EED\u3002requestId \u4E3A\u672C\u6761\u6D88\u606F\u7684\u552F\u4E00\u6807\u8BC6\uFF0C\u91CD\u8BD5\u5FC5\u987B\u539F\u6837\u590D\u7528\u3002\u8FD4\u56DE queued/running \u65F6\u7528 get_teammy_reply \u67E5\u8BE2\u7ED3\u679C\uFF0C\u4E0D\u91CD\u590D\u53D1\u9001\u3002",
|
|
7941
|
+
get_teammy_reply: "\u67E5\u8BE2\u6307\u5B9ATim\u8BF7\u6C42\u7684\u6267\u884C\u72B6\u6001\u548C\u7B54\u590D\uFF0C\u4E0D\u53D1\u9001\u65B0\u6D88\u606F\u3002\u5931\u8D25\u65F6\u8FD4\u56DE error\uFF1B\u9700\u8981\u7528\u6237\u51B3\u5B9A\u65F6\u8F6C\u8FBE\u95EE\u9898\uFF0C\u5728\u540C\u4E00\u5BF9\u8BDD\u4F20\u56DE\u7528\u6237\u7B54\u590D\u3002"
|
|
7233
7942
|
};
|
|
7234
7943
|
function externalToolCatalog() {
|
|
7235
7944
|
return {
|
|
@@ -7277,8 +7986,9 @@ var ExternalTools = class {
|
|
|
7277
7986
|
new AiDefaults(this.hub).resolve(identity.workspaceId);
|
|
7278
7987
|
new AgentConfig(this.hub).initialize(identity.workspaceId);
|
|
7279
7988
|
const store = new AgentStore(this.hub);
|
|
7280
|
-
|
|
7989
|
+
let chat = value.conversationId ?? store.createChat(identity).id;
|
|
7281
7990
|
const run = store.ask(identity, value.text, chat);
|
|
7991
|
+
chat = run.conversationId ?? chat;
|
|
7282
7992
|
this.hub.db.prepare("INSERT INTO agent_external_requests VALUES(?,?,?,?,?,?)").run(identity.workspaceId, identity.memberId, value.requestId, hash3, chat, run.id);
|
|
7283
7993
|
const base = new SiteRepository(this.hub).config().publicServerUrl.replace(/\/+$/, "");
|
|
7284
7994
|
return {
|
|
@@ -7302,7 +8012,7 @@ var ExternalTools = class {
|
|
|
7302
8012
|
throw new HubError(
|
|
7303
8013
|
403,
|
|
7304
8014
|
"TOOL_UNAVAILABLE",
|
|
7305
|
-
"\u672A\u5F00\u653E\u6B64\u5DE5\u5177\u3002\u4FEE\u6539\u9879\u76EE\u8BF7\u901A\u8FC7 ask_teammy \u59D4\
|
|
8015
|
+
"\u672A\u5F00\u653E\u6B64\u5DE5\u5177\u3002\u4FEE\u6539\u9879\u76EE\u8BF7\u901A\u8FC7 ask_teammy \u59D4\u6258Tim\u3002"
|
|
7306
8016
|
);
|
|
7307
8017
|
}
|
|
7308
8018
|
};
|
|
@@ -7319,6 +8029,169 @@ function registerExternalTools(server, hub) {
|
|
|
7319
8029
|
});
|
|
7320
8030
|
}
|
|
7321
8031
|
|
|
8032
|
+
// apps/server/maintenance.ts
|
|
8033
|
+
import { randomUUID as randomUUID16 } from "node:crypto";
|
|
8034
|
+
import { rm as rm3 } from "node:fs/promises";
|
|
8035
|
+
import { join as join12 } from "node:path";
|
|
8036
|
+
var maintenanceMigration = `
|
|
8037
|
+
CREATE TABLE data_baseline(id INTEGER PRIMARY KEY CHECK(id=1), generation TEXT NOT NULL, phase TEXT NOT NULL DEFAULT 'ready', reset_at TEXT);
|
|
8038
|
+
INSERT INTO data_baseline(id,generation) VALUES(1,lower(hex(randomblob(16))));
|
|
8039
|
+
CREATE TABLE runtime_api(id TEXT PRIMARY KEY, expires_at INTEGER NOT NULL);
|
|
8040
|
+
CREATE TABLE reset_session_heads(workspace_id TEXT NOT NULL,owner TEXT NOT NULL,session_key TEXT NOT NULL,revision INTEGER NOT NULL,hash TEXT NOT NULL,PRIMARY KEY(workspace_id,owner,session_key));
|
|
8041
|
+
CREATE TABLE reset_event_keys(project_id TEXT NOT NULL,source_key TEXT NOT NULL,PRIMARY KEY(project_id,source_key));
|
|
8042
|
+
CREATE TABLE reset_channel_posts(bot_id TEXT NOT NULL,topic_id TEXT NOT NULL,post_id TEXT NOT NULL,PRIMARY KEY(bot_id,topic_id,post_id));
|
|
8043
|
+
`;
|
|
8044
|
+
var Maintenance = class {
|
|
8045
|
+
constructor(hub) {
|
|
8046
|
+
this.hub = hub;
|
|
8047
|
+
}
|
|
8048
|
+
hub;
|
|
8049
|
+
baseline() {
|
|
8050
|
+
return this.hub.db.prepare("SELECT * FROM data_baseline WHERE id=1").get();
|
|
8051
|
+
}
|
|
8052
|
+
assertReady() {
|
|
8053
|
+
if (this.baseline().phase !== "ready")
|
|
8054
|
+
throw new HubError(
|
|
8055
|
+
503,
|
|
8056
|
+
"MAINTENANCE",
|
|
8057
|
+
"\u6D4B\u8BD5\u6570\u636E\u6E05\u7406\u5C1A\u672A\u5B8C\u6210\uFF0C\u8BF7\u7EE7\u7EED\u6267\u884C\u7EF4\u62A4\u547D\u4EE4\u540E\u542F\u52A8\u670D\u52A1\u3002"
|
|
8058
|
+
);
|
|
8059
|
+
}
|
|
8060
|
+
apiLease() {
|
|
8061
|
+
this.assertReady();
|
|
8062
|
+
const id = randomUUID16();
|
|
8063
|
+
const beat = () => this.hub.db.prepare(
|
|
8064
|
+
"INSERT INTO runtime_api VALUES(?,?) ON CONFLICT(id) DO UPDATE SET expires_at=excluded.expires_at"
|
|
8065
|
+
).run(id, Date.now() + 3e4);
|
|
8066
|
+
beat();
|
|
8067
|
+
const timer = setInterval(() => {
|
|
8068
|
+
try {
|
|
8069
|
+
beat();
|
|
8070
|
+
} catch {
|
|
8071
|
+
}
|
|
8072
|
+
}, 5e3);
|
|
8073
|
+
timer.unref();
|
|
8074
|
+
return () => {
|
|
8075
|
+
clearInterval(timer);
|
|
8076
|
+
this.hub.db.prepare("DELETE FROM runtime_api WHERE id=?").run(id);
|
|
8077
|
+
};
|
|
8078
|
+
}
|
|
8079
|
+
/** Explicit offline maintenance, never part of an ordinary schema upgrade. */
|
|
8080
|
+
async resetTestData() {
|
|
8081
|
+
const db = this.hub.db;
|
|
8082
|
+
transaction(db, () => {
|
|
8083
|
+
const active = db.prepare(
|
|
8084
|
+
`SELECT 1 FROM runtime_api WHERE expires_at>?
|
|
8085
|
+
UNION ALL SELECT 1 FROM runtime_workers WHERE stopped=0 AND last_seen>?
|
|
8086
|
+
UNION ALL SELECT 1 FROM wecom_runtime WHERE expires_at>?
|
|
8087
|
+
UNION ALL SELECT 1 FROM project_agent_leases WHERE expires_at>? LIMIT 1`
|
|
8088
|
+
).get(Date.now(), Date.now() - 3e4, Date.now(), Date.now());
|
|
8089
|
+
if (active)
|
|
8090
|
+
throw new HubError(
|
|
8091
|
+
409,
|
|
8092
|
+
"MAINTENANCE_BUSY",
|
|
8093
|
+
"\u8BF7\u5148\u505C\u6B62 API\u3001Worker \u548C\u4F01\u4E1A\u5FAE\u4FE1\u8FDE\u63A5\uFF1B\u9000\u51FA\u540E\u7684\u79DF\u7EA6\u6700\u591A\u9700\u8981 30 \u79D2\u91CA\u653E\u3002"
|
|
8094
|
+
);
|
|
8095
|
+
if (this.baseline().phase === "ready")
|
|
8096
|
+
db.prepare(
|
|
8097
|
+
"UPDATE data_baseline SET generation=?,phase='clearing',reset_at=? WHERE id=1"
|
|
8098
|
+
).run(randomUUID16(), (/* @__PURE__ */ new Date()).toISOString());
|
|
8099
|
+
});
|
|
8100
|
+
transaction(db, () => {
|
|
8101
|
+
db.exec(`INSERT OR REPLACE INTO reset_session_heads SELECT workspace_id,owner,session_key,revision,hash FROM sessions;
|
|
8102
|
+
INSERT OR IGNORE INTO reset_event_keys SELECT project_id,source_key FROM project_agent_inbox WHERE source_key IS NOT NULL AND source_key<>'initial';
|
|
8103
|
+
INSERT OR IGNORE INTO reset_channel_posts SELECT bot_id,topic_id,post_id FROM wecom_matter_threads WHERE post_id IS NOT NULL;
|
|
8104
|
+
UPDATE wecom_subscriptions SET chat_id=NULL,outbox_after=0;
|
|
8105
|
+
UPDATE wecom_groups SET conversation_id=NULL;
|
|
8106
|
+
UPDATE wecom_channel_messages SET run_id=NULL;
|
|
8107
|
+
PRAGMA defer_foreign_keys=ON;`);
|
|
8108
|
+
const tables = [
|
|
8109
|
+
"agent_notice_threads",
|
|
8110
|
+
"agent_event_routes",
|
|
8111
|
+
"agent_messages",
|
|
8112
|
+
"agent_message_requests",
|
|
8113
|
+
"agent_external_requests",
|
|
8114
|
+
"agent_references",
|
|
8115
|
+
"agent_input_details",
|
|
8116
|
+
"agent_matter_inputs",
|
|
8117
|
+
"agent_input_handbacks",
|
|
8118
|
+
"agent_matter_reports",
|
|
8119
|
+
"agent_answer_drafts",
|
|
8120
|
+
"agent_run_batches",
|
|
8121
|
+
"agent_matter_sources",
|
|
8122
|
+
"agent_matter_members",
|
|
8123
|
+
"agent_matter_operations",
|
|
8124
|
+
"wecom_matter_threads",
|
|
8125
|
+
"agent_matters",
|
|
8126
|
+
"project_agent_recipients",
|
|
8127
|
+
"project_agent_outbox",
|
|
8128
|
+
"project_agent_publications",
|
|
8129
|
+
"project_agent_runs",
|
|
8130
|
+
"project_agent_stream",
|
|
8131
|
+
"agent_contexts",
|
|
8132
|
+
"project_agent_conversations",
|
|
8133
|
+
"project_agent_inbox",
|
|
8134
|
+
"project_agent_sources",
|
|
8135
|
+
"project_agent_leases",
|
|
8136
|
+
"wecom_requests",
|
|
8137
|
+
"wecom_group_requests",
|
|
8138
|
+
"wecom_deliveries",
|
|
8139
|
+
"wecom_channel_events",
|
|
8140
|
+
"wecom_codes",
|
|
8141
|
+
"wecom_group_codes",
|
|
8142
|
+
"wecom_runtime",
|
|
8143
|
+
"task_write_operations",
|
|
8144
|
+
"task_cards",
|
|
8145
|
+
"task_board_members",
|
|
8146
|
+
"ai_events",
|
|
8147
|
+
"ai_attempts",
|
|
8148
|
+
"ai_operations",
|
|
8149
|
+
"ai_jobs",
|
|
8150
|
+
"session_analysis_results",
|
|
8151
|
+
"session_analysis_jobs",
|
|
8152
|
+
"session_content",
|
|
8153
|
+
"session_fts",
|
|
8154
|
+
"session_messages",
|
|
8155
|
+
"session_revisions",
|
|
8156
|
+
"sessions",
|
|
8157
|
+
"materials",
|
|
8158
|
+
"material_versions",
|
|
8159
|
+
"resource_documents",
|
|
8160
|
+
"diagnostic_records",
|
|
8161
|
+
"diagnostic_conditions",
|
|
8162
|
+
"runtime_repositories",
|
|
8163
|
+
"runtime_sandbox",
|
|
8164
|
+
"runtime_workers",
|
|
8165
|
+
"runtime_api"
|
|
8166
|
+
];
|
|
8167
|
+
for (const table of tables) db.exec(`DELETE FROM ${table}`);
|
|
8168
|
+
if (db.prepare("PRAGMA foreign_key_check").all().length)
|
|
8169
|
+
throw new Error("Reset would leave invalid references. No business rows were removed.");
|
|
8170
|
+
});
|
|
8171
|
+
for (const name of ["materials", "logs"])
|
|
8172
|
+
await rm3(join12(this.hub.directory, name), { recursive: true, force: true });
|
|
8173
|
+
const projects = db.prepare("SELECT id FROM projects").all();
|
|
8174
|
+
for (const project of projects) {
|
|
8175
|
+
const root = join12(this.hub.directory, "project-agents", String(project.id));
|
|
8176
|
+
for (const name of [
|
|
8177
|
+
"lifetime",
|
|
8178
|
+
"instant",
|
|
8179
|
+
"constant",
|
|
8180
|
+
"contexts",
|
|
8181
|
+
"workspaces",
|
|
8182
|
+
"files.json"
|
|
8183
|
+
])
|
|
8184
|
+
await rm3(join12(root, name), { recursive: true, force: true });
|
|
8185
|
+
}
|
|
8186
|
+
db.prepare("UPDATE data_baseline SET phase='ready' WHERE id=1").run();
|
|
8187
|
+
return {
|
|
8188
|
+
generation: String(this.baseline().generation),
|
|
8189
|
+
projects: projects.length,
|
|
8190
|
+
retained: "accounts, memberships, connections, credentials and workspace sync baseline"
|
|
8191
|
+
};
|
|
8192
|
+
}
|
|
8193
|
+
};
|
|
8194
|
+
|
|
7322
8195
|
// apps/server/ai/schema.ts
|
|
7323
8196
|
var aiConfigMigration = `
|
|
7324
8197
|
CREATE TABLE ai_profiles (
|
|
@@ -7354,7 +8227,7 @@ ALTER TABLE ai_jobs ADD COLUMN next_retry_at INTEGER;
|
|
|
7354
8227
|
|
|
7355
8228
|
// apps/server/store.ts
|
|
7356
8229
|
function digest3(token) {
|
|
7357
|
-
return
|
|
8230
|
+
return createHash14("sha256").update(token).digest("hex");
|
|
7358
8231
|
}
|
|
7359
8232
|
var migrations = [
|
|
7360
8233
|
String.raw`
|
|
@@ -7401,15 +8274,18 @@ var migrations = [
|
|
|
7401
8274
|
externalToolsMigration,
|
|
7402
8275
|
agentEventsMigration,
|
|
7403
8276
|
agentMattersMigration,
|
|
7404
|
-
wecomChannelsMigration
|
|
8277
|
+
wecomChannelsMigration,
|
|
8278
|
+
agentCoordinationMigration,
|
|
8279
|
+
maintenanceMigration
|
|
7405
8280
|
];
|
|
7406
8281
|
var HubStore = class {
|
|
7407
8282
|
constructor(directory) {
|
|
7408
8283
|
this.directory = directory;
|
|
7409
|
-
const path =
|
|
8284
|
+
const path = join13(directory, "hub.sqlite3");
|
|
7410
8285
|
if (existsSync(path)) {
|
|
7411
8286
|
const existing = new DatabaseSync2(path, { readOnly: true });
|
|
7412
8287
|
try {
|
|
8288
|
+
existing.exec("PRAGMA busy_timeout=5000");
|
|
7413
8289
|
const version2 = Number(existing.prepare("PRAGMA user_version").get().user_version);
|
|
7414
8290
|
if (version2 > 0 && version2 < 9)
|
|
7415
8291
|
throw new Error(
|
|
@@ -7497,10 +8373,10 @@ var HubStore = class {
|
|
|
7497
8373
|
};
|
|
7498
8374
|
|
|
7499
8375
|
// apps/server/ai/jobs.ts
|
|
7500
|
-
import { createHash as
|
|
8376
|
+
import { createHash as createHash16, randomUUID as randomUUID18 } from "node:crypto";
|
|
7501
8377
|
|
|
7502
8378
|
// apps/server/ai/runtime-resources.ts
|
|
7503
|
-
import { createHash as
|
|
8379
|
+
import { createHash as createHash15 } from "node:crypto";
|
|
7504
8380
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
7505
8381
|
import { resolve as resolve3 } from "node:path";
|
|
7506
8382
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
@@ -7583,7 +8459,7 @@ function loadWorkflow(workflow, directory = process.env.TEAMMY_AI_RUNTIME_DIR) {
|
|
|
7583
8459
|
throw new Error("Missing tool description");
|
|
7584
8460
|
return WorkflowRuntimeSchema.parse({
|
|
7585
8461
|
...resource,
|
|
7586
|
-
fingerprint:
|
|
8462
|
+
fingerprint: createHash15("sha256").update(JSON.stringify(resource)).digest("hex")
|
|
7587
8463
|
});
|
|
7588
8464
|
} catch {
|
|
7589
8465
|
throw new HubError(
|
|
@@ -7603,7 +8479,7 @@ ${JSON.stringify(z25.toJSONSchema(schema))}`;
|
|
|
7603
8479
|
}
|
|
7604
8480
|
|
|
7605
8481
|
// apps/server/ai/task-store.ts
|
|
7606
|
-
import { randomUUID as
|
|
8482
|
+
import { randomUUID as randomUUID17 } from "node:crypto";
|
|
7607
8483
|
var fields = (row) => JSON.parse(String(row.fields_json));
|
|
7608
8484
|
var sources = (row) => JSON.parse(String(row.sources_json));
|
|
7609
8485
|
var paths = (value) => JSON.stringify(value.map((s) => s.path).sort());
|
|
@@ -7662,7 +8538,7 @@ function applyTaskResult(hub, job, result) {
|
|
|
7662
8538
|
if (oldGroup.length === 1 && newGroup.length === 1 && !used.has(String(oldGroup[0].id)) && !explicit.has(String(oldGroup[0].id)))
|
|
7663
8539
|
old = oldGroup[0];
|
|
7664
8540
|
}
|
|
7665
|
-
const id = old ? String(old.id) :
|
|
8541
|
+
const id = old ? String(old.id) : randomUUID17();
|
|
7666
8542
|
used.add(id);
|
|
7667
8543
|
const { sources: fileSources, taskId: _taskId, ...content } = task;
|
|
7668
8544
|
const source = [
|
|
@@ -7916,7 +8792,7 @@ var AiJobs = class {
|
|
|
7916
8792
|
this.hub.db.prepare("INSERT INTO ai_events(job_id,attempt,kind,message,created_at) VALUES(?,?,?,?,?)").run(id, attempt, kind, message.slice(0, 2e3), now3());
|
|
7917
8793
|
}
|
|
7918
8794
|
operation(session, operationId, input, action) {
|
|
7919
|
-
const fingerprint3 =
|
|
8795
|
+
const fingerprint3 = createHash16("sha256").update(JSON.stringify(input)).digest("hex");
|
|
7920
8796
|
return transaction(this.hub.db, () => {
|
|
7921
8797
|
const old = this.hub.db.prepare(
|
|
7922
8798
|
"SELECT * FROM ai_operations WHERE workspace_id=? AND requester=? AND operation_id=?"
|
|
@@ -7942,7 +8818,7 @@ var AiJobs = class {
|
|
|
7942
8818
|
return this.enqueue(
|
|
7943
8819
|
identity,
|
|
7944
8820
|
{
|
|
7945
|
-
operationId:
|
|
8821
|
+
operationId: randomUUID18(),
|
|
7946
8822
|
workflow: "task-bootstrap",
|
|
7947
8823
|
question: "",
|
|
7948
8824
|
selection: { owner: identity.memberId, kind: "directory", path: "" }
|
|
@@ -8029,7 +8905,7 @@ var AiJobs = class {
|
|
|
8029
8905
|
const runtime = resource ?? loadWorkflow(input.workflow);
|
|
8030
8906
|
if (runtime.workflow !== input.workflow)
|
|
8031
8907
|
throw new HubError(500, "AI_RUNTIME_INVALID", "Workflow resource mismatch.");
|
|
8032
|
-
const id2 =
|
|
8908
|
+
const id2 = randomUUID18(), date = now3();
|
|
8033
8909
|
const taskContext = input.workflow === "task-bootstrap" ? captureTaskContext(this.hub, session.workspaceId, snapshot) : null;
|
|
8034
8910
|
this.hub.db.prepare(
|
|
8035
8911
|
"INSERT INTO ai_jobs(id,workspace_id,requester,request_json,profile_json,snapshot_json,prompt_version,status,created_at,updated_at,runtime_json,origin,task_context_json) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)"
|
|
@@ -8147,7 +9023,7 @@ var AiJobs = class {
|
|
|
8147
9023
|
"SELECT * FROM ai_jobs WHERE status='queued' AND (next_retry_at IS NULL OR next_retry_at<=?) ORDER BY created_at,id LIMIT 1"
|
|
8148
9024
|
).get(at);
|
|
8149
9025
|
if (!row) return void 0;
|
|
8150
|
-
const token =
|
|
9026
|
+
const token = randomUUID18(), attempt = Number(row.attempt_count) + 1, date = now3();
|
|
8151
9027
|
this.hub.db.prepare(
|
|
8152
9028
|
"UPDATE ai_jobs SET status='running',attempt_count=?,run_token=?,lease_until=?,updated_at=?,error=NULL,next_retry_at=NULL WHERE id=?"
|
|
8153
9029
|
).run(attempt, token, at + 3e4, date, row.id);
|
|
@@ -8282,9 +9158,9 @@ var AiJobs = class {
|
|
|
8282
9158
|
};
|
|
8283
9159
|
|
|
8284
9160
|
// apps/server/ai/pi-runner.ts
|
|
8285
|
-
import { mkdtemp as mkdtemp4, rm as
|
|
9161
|
+
import { mkdtemp as mkdtemp4, rm as rm5 } from "node:fs/promises";
|
|
8286
9162
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
8287
|
-
import { join as
|
|
9163
|
+
import { join as join16 } from "node:path";
|
|
8288
9164
|
import { InMemoryCredentialStore, InMemoryModelsStore } from "@earendil-works/pi-ai";
|
|
8289
9165
|
|
|
8290
9166
|
// apps/server/ai/task-tools.ts
|
|
@@ -8454,9 +9330,9 @@ function analysisTools(reader, signal, progress, runtime, explorer) {
|
|
|
8454
9330
|
|
|
8455
9331
|
// apps/server/ai/native-sandbox.ts
|
|
8456
9332
|
import { spawn as spawn2 } from "node:child_process";
|
|
8457
|
-
import { mkdtemp as mkdtemp3, rm as
|
|
9333
|
+
import { mkdtemp as mkdtemp3, rm as rm4 } from "node:fs/promises";
|
|
8458
9334
|
import { tmpdir } from "node:os";
|
|
8459
|
-
import { basename, dirname as dirname6, join as
|
|
9335
|
+
import { basename, dirname as dirname6, join as join15 } from "node:path";
|
|
8460
9336
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
8461
9337
|
|
|
8462
9338
|
// apps/server/ai/agent-channel.ts
|
|
@@ -8497,7 +9373,7 @@ var AgentChannel = class {
|
|
|
8497
9373
|
|
|
8498
9374
|
// apps/server/ai/agent-workspace.ts
|
|
8499
9375
|
import { mkdir as mkdir5, writeFile as writeFile5 } from "node:fs/promises";
|
|
8500
|
-
import { dirname as dirname5, join as
|
|
9376
|
+
import { dirname as dirname5, join as join14 } from "node:path";
|
|
8501
9377
|
|
|
8502
9378
|
// apps/server/ai/line-diff.ts
|
|
8503
9379
|
import { Worker } from "node:worker_threads";
|
|
@@ -8670,13 +9546,13 @@ var TaskExplorer = class {
|
|
|
8670
9546
|
// apps/server/ai/agent-workspace.ts
|
|
8671
9547
|
async function materializeWorkspace(root, reader, signal) {
|
|
8672
9548
|
const owner = FileOwnerSchema.parse(reader.snapshot.owner);
|
|
8673
|
-
await mkdir5(
|
|
9549
|
+
await mkdir5(join14(root, owner), { recursive: true });
|
|
8674
9550
|
async function copy(source, prefix, shared = false) {
|
|
8675
9551
|
for (const file of source.snapshot.files) {
|
|
8676
9552
|
signal.throwIfAborted();
|
|
8677
9553
|
const path = FilePathSchema.parse(file.path);
|
|
8678
9554
|
if (shared && !sharedPathAllowed(path, DEFAULT_SHARED_PATHS)) continue;
|
|
8679
|
-
const target =
|
|
9555
|
+
const target = join14(root, prefix, path);
|
|
8680
9556
|
await mkdir5(dirname5(target), { recursive: true });
|
|
8681
9557
|
await writeFile5(target, await source.bytes(path), { flag: "wx", mode: 292 });
|
|
8682
9558
|
}
|
|
@@ -8866,21 +9742,21 @@ async function sandboxCommand(root, baseline) {
|
|
|
8866
9742
|
fileURLToPath3(new URL("./native-pi-entry.js", import.meta.url)),
|
|
8867
9743
|
"/opt/teammy/native-pi-entry.mjs",
|
|
8868
9744
|
"--ro-bind",
|
|
8869
|
-
|
|
9745
|
+
join15(root, "workspaces"),
|
|
8870
9746
|
"/workspaces"
|
|
8871
9747
|
);
|
|
8872
|
-
if (baseline) args.push("--ro-bind",
|
|
9748
|
+
if (baseline) args.push("--ro-bind", join15(root, "baseline/workspaces"), "/baseline/workspaces");
|
|
8873
9749
|
args.push("--chdir", "/workspaces", "/opt/node-bin/node", "/opt/teammy/native-pi-entry.mjs");
|
|
8874
9750
|
return { command: "bwrap", args, env: resources.env };
|
|
8875
9751
|
}
|
|
8876
9752
|
async function runNativeAgent(profile, reader, key, signal, progress, runtime, systemPrompt, input, context = null) {
|
|
8877
9753
|
signal.throwIfAborted();
|
|
8878
|
-
const root = await mkdtemp3(
|
|
9754
|
+
const root = await mkdtemp3(join15(tmpdir(), "teammy-agent-"));
|
|
8879
9755
|
try {
|
|
8880
|
-
await materializeWorkspace(
|
|
9756
|
+
await materializeWorkspace(join15(root, "workspaces"), reader, signal);
|
|
8881
9757
|
if (context?.baseline)
|
|
8882
9758
|
await materializeWorkspace(
|
|
8883
|
-
|
|
9759
|
+
join15(root, "baseline/workspaces"),
|
|
8884
9760
|
reader.forSnapshot(context.baseline),
|
|
8885
9761
|
signal
|
|
8886
9762
|
);
|
|
@@ -8995,7 +9871,7 @@ async function runNativeAgent(profile, reader, key, signal, progress, runtime, s
|
|
|
8995
9871
|
await Promise.allSettled(pending);
|
|
8996
9872
|
}
|
|
8997
9873
|
} finally {
|
|
8998
|
-
await
|
|
9874
|
+
await rm4(root, { recursive: true, force: true });
|
|
8999
9875
|
}
|
|
9000
9876
|
}
|
|
9001
9877
|
|
|
@@ -9020,7 +9896,7 @@ async function runAgent(profile, reader, key, signal, progress, runtime, systemP
|
|
|
9020
9896
|
SessionManager,
|
|
9021
9897
|
SettingsManager
|
|
9022
9898
|
} = await import("@earendil-works/pi-coding-agent");
|
|
9023
|
-
const cwd = await mkdtemp4(
|
|
9899
|
+
const cwd = await mkdtemp4(join16(tmpdir2(), "teammy-ai-run-"));
|
|
9024
9900
|
try {
|
|
9025
9901
|
const { models, model } = modelAccess(profile, key, runtime.config.thinkingLevel !== "off");
|
|
9026
9902
|
const modelRuntime = await ModelRuntime.create({
|
|
@@ -9110,7 +9986,7 @@ async function runAgent(profile, reader, key, signal, progress, runtime, systemP
|
|
|
9110
9986
|
session.dispose();
|
|
9111
9987
|
}
|
|
9112
9988
|
} finally {
|
|
9113
|
-
await
|
|
9989
|
+
await rm5(cwd, { recursive: true, force: true });
|
|
9114
9990
|
}
|
|
9115
9991
|
}
|
|
9116
9992
|
|
|
@@ -9382,8 +10258,8 @@ function scheduleTaskBootstraps(hub, at = Date.now()) {
|
|
|
9382
10258
|
}
|
|
9383
10259
|
|
|
9384
10260
|
// apps/server/agents/runtime.ts
|
|
9385
|
-
import { rm as
|
|
9386
|
-
import { join as
|
|
10261
|
+
import { rm as rm6 } from "node:fs/promises";
|
|
10262
|
+
import { join as join18 } from "node:path";
|
|
9387
10263
|
|
|
9388
10264
|
// apps/server/materials/prompts.ts
|
|
9389
10265
|
var materialPrompt = `## \u804C\u8D23
|
|
@@ -9407,20 +10283,20 @@ var groupPrompt = `## \u5F53\u524D\u5165\u53E3\uFF1A\u9879\u76EE\u7FA4
|
|
|
9407
10283
|
\u4EFB\u52A1\u8C03\u6574\u5148\u8BF4\u660E\u5177\u4F53\u5EFA\u8BAE\uFF0C\u7528\u6237\u660E\u786E\u786E\u8BA4\u540E\u624D\u4F7F\u7528 write_tasks\u3002\u786E\u8BA4\u9488\u5BF9\u5DF2\u8BA8\u8BBA\u7684\u8C03\u6574\uFF0C\u4E0D\u6269\u5C55\u4E3A\u4EFB\u610F\u4EFB\u52A1\u4FEE\u6539\u3002`;
|
|
9408
10284
|
|
|
9409
10285
|
// apps/server/agents/runtime.ts
|
|
9410
|
-
import { randomUUID as
|
|
10286
|
+
import { randomUUID as randomUUID19 } from "node:crypto";
|
|
9411
10287
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
9412
10288
|
import { z as z34 } from "zod";
|
|
9413
10289
|
|
|
9414
10290
|
// apps/server/agents/sandbox.ts
|
|
9415
10291
|
import { spawn as spawn3 } from "node:child_process";
|
|
9416
10292
|
import { mkdir as mkdir6, rename as rename3 } from "node:fs/promises";
|
|
9417
|
-
import { basename as basename2, dirname as dirname7, join as
|
|
10293
|
+
import { basename as basename2, dirname as dirname7, join as join17 } from "node:path";
|
|
9418
10294
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
9419
10295
|
async function projectAgentStateDirectory(root, role) {
|
|
9420
|
-
const state =
|
|
10296
|
+
const state = join17(root, role);
|
|
9421
10297
|
if (role === "instant") {
|
|
9422
10298
|
try {
|
|
9423
|
-
await rename3(
|
|
10299
|
+
await rename3(join17(root, "constant"), state);
|
|
9424
10300
|
} catch (error) {
|
|
9425
10301
|
if (error.code !== "ENOENT") throw error;
|
|
9426
10302
|
}
|
|
@@ -9441,13 +10317,13 @@ async function projectSandboxCommand(workspace, role, contextId, writable = fals
|
|
|
9441
10317
|
if (parent === dependencies) throw new Error("Pi dependencies not found");
|
|
9442
10318
|
dependencies = parent;
|
|
9443
10319
|
}
|
|
9444
|
-
const state = contextId ?
|
|
10320
|
+
const state = contextId ? join17(workspace.root, "contexts", contextId) : await projectAgentStateDirectory(workspace.root, role);
|
|
9445
10321
|
await mkdir6(state, { recursive: true, mode: 448 });
|
|
9446
10322
|
const args = await sandboxSystemArgs();
|
|
9447
10323
|
const resources = await sandboxResources();
|
|
9448
10324
|
args.push(...resources.args);
|
|
9449
10325
|
if (writable) {
|
|
9450
|
-
const work =
|
|
10326
|
+
const work = join17(state, "work");
|
|
9451
10327
|
await mkdir6(work, { recursive: true, mode: 448 });
|
|
9452
10328
|
args.push("--bind", work, "/work");
|
|
9453
10329
|
}
|
|
@@ -9616,19 +10492,35 @@ var coordinationSchemas = {
|
|
|
9616
10492
|
latest: z31.boolean().optional(),
|
|
9617
10493
|
section: z31.enum(["details", "comments", "diff", "overview", "handoff"]).optional()
|
|
9618
10494
|
}).strict(),
|
|
9619
|
-
query_matters: z31.object({
|
|
9620
|
-
|
|
10495
|
+
query_matters: z31.object({
|
|
10496
|
+
status: z31.enum(["active", "waiting", "completed"]).optional(),
|
|
10497
|
+
query: z31.string().optional(),
|
|
10498
|
+
member: z31.string().optional(),
|
|
10499
|
+
source: z31.string().optional(),
|
|
10500
|
+
offset: z31.number().int().nonnegative().default(0),
|
|
10501
|
+
limit: z31.number().int().positive().default(20)
|
|
10502
|
+
}).strict(),
|
|
10503
|
+
read_matter: z31.object({
|
|
10504
|
+
ref: z31.string(),
|
|
10505
|
+
section: z31.enum(["overview", "messages", "reports"]).default("overview"),
|
|
10506
|
+
after: z31.number().int().nonnegative().default(0),
|
|
10507
|
+
limit: z31.number().int().positive().default(20)
|
|
10508
|
+
}).strict(),
|
|
9621
10509
|
delegate_matter: DelegateMatterSchema,
|
|
9622
10510
|
report_matter: ReportMatterSchema,
|
|
10511
|
+
handback_input: z31.object({ reason: z31.string().trim().min(1) }).strict(),
|
|
10512
|
+
set_matter_hold: MatterHoldSchema,
|
|
9623
10513
|
notify_members: z31.object({ members: z31.array(z31.string().min(1)).min(1) }).strict()
|
|
9624
10514
|
};
|
|
9625
10515
|
var coordinationDescriptions = {
|
|
9626
10516
|
list_members: "\u67E5\u8BE2\u672C\u9879\u76EE\u53EF\u8054\u7CFB\u7684\u6210\u5458 ID\u3002",
|
|
9627
10517
|
read_reference: "\u8BFB\u53D6\u77ED\u5F15\u7528\u3002\u4EFB\u52A1\u548C\u4F1A\u8BDD\u9ED8\u8BA4\u4F7F\u7528\u672C\u6B21\u4E8B\u4EF6\u6240\u4F9D\u636E\u7684\u7248\u672C\uFF0Clatest=true \u67E5\u8BE2\u6700\u65B0\u5185\u5BB9\u3002\u4F1A\u8BDD\u9ED8\u8BA4\u8BFB\u53D6\u6B63\u6587\uFF0Coverview/handoff \u8BFB\u53D6\u5DF2\u6709\u6574\u7406\u3002PR \u7684 details/comments/diff \u8BFB\u53D6\u8FDC\u7AEF\u5F53\u524D\u5185\u5BB9\uFF0C\u53EF\u80FD\u665A\u4E8E\u539F\u4E8B\u4EF6\u3002",
|
|
9628
10518
|
query_matters: "\u67E5\u8BE2\u4E8B\u9879\u6807\u9898\u3001\u72B6\u6001\u3001\u5FC5\u8981\u7ED3\u8BBA\u548C\u53C2\u4E0E\u4EBA\uFF1B\u5148\u68C0\u67E5\u76F8\u5173\u4E8B\u9879\u662F\u5426\u5DF2\u5B58\u5728\uFF0C\u907F\u514D\u91CD\u590D\u521B\u5EFA\u3002",
|
|
9629
|
-
read_matter: "\
|
|
9630
|
-
delegate_matter: "\u65B0\u5EFA\u6216\u7EE7\u7EED\u4E8B\u9879\u3002\u65B0\u5EFA\u586B title\uFF1B\u7EE7\u7EED\u586B ref\u3002text \u662F\u53EF\u5206\u4EAB\u7684\u76EE\u6807\
|
|
9631
|
-
report_matter: "\u4E8B\u9879\
|
|
10519
|
+
read_matter: "\u9ED8\u8BA4\u8BFB\u53D6\u4E8B\u9879\u76EE\u6807\u3001\u6700\u65B0\u7ED3\u8BBA\u3001\u5F85\u786E\u8BA4\u95EE\u9898\u53CA\u6682\u505C\u7EA6\u675F\u3002section=messages/reports \u6309 after \u6E38\u6807\u8BFB\u53D6\u8BE6\u7EC6\u8BB0\u5F55\uFF1B\u4E0D\u8BFB\u53D6\u539F\u59CB\u79C1\u4EBA\u804A\u5929\u3002",
|
|
10520
|
+
delegate_matter: "\u65B0\u5EFA\u6216\u7EE7\u7EED\u4E8B\u9879\u3002\u65B0\u5EFA\u586B title\uFF1B\u7EE7\u7EED\u586B ref\u3002text \u662F\u53EF\u5206\u4EAB\u7684\u76EE\u6807\u548C\u5FC5\u8981\u534F\u8C03\u80CC\u666F\uFF0C\u4FDD\u7559\u7528\u6237\u539F\u610F\uFF1B\u5BBF\u4E3B\u9644\u52A0\u539F\u59CB\u4E8B\u4EF6\u3002sources \u4EC5\u4F5C\u53C2\u8003\uFF0Cfollows \u8868\u793A\u660E\u786E\u8D1F\u8D23\u5176\u540E\u7EED\u4E8B\u4EF6\u3002participants \u586B\u9700\u8981\u9080\u8BF7\u7684\u6210\u5458\u3002\u79C1\u4EBA\u5185\u5BB9\u6307\u5B9A visibility=participants\u3002\u4E00\u6B21\u8F93\u5165\u91CD\u590D\u6D3E\u53D1\u8FD4\u56DE\u539F\u7ED3\u679C\uFF1B\u660E\u786E\u62C6\u6210\u72EC\u7ACB\u4E8B\u9879\u65F6\u4E3A\u6BCF\u4EF6\u5DE5\u4F5C\u586B\u5199\u7A33\u5B9A\u7684 branch\u3002\u6D3E\u53D1\u540E\u7ED3\u675F\u672C\u8F6E\u3002",
|
|
10521
|
+
report_matter: "\u4FDD\u5B58\u4E8B\u9879\u72B6\u6001\u548C\u7B80\u77ED summary\u3002kind=progress \u4EC5\u66F4\u65B0\u8BB0\u5F55\uFF0C\u4E0D\u5524\u9192\u534F\u8C03\u8005\uFF1Bkind=coordination \u9700\u5177\u4F53 request\uFF1Bkind=result \u7528\u4E8E\u91CD\u8981\u7ED3\u8BBA\u6216\u5B8C\u6210\uFF0C\u968F\u672C\u8F6E\u6210\u529F\u63D0\u4EA4\u3002pending \u8BB0\u5F55\u9700\u8981\u8C01\u786E\u8BA4\u4EC0\u4E48\uFF0Cnull \u8868\u793A\u95EE\u9898\u5DF2\u89E3\u51B3\uFF1B\u7701\u7565\u4FDD\u6301\u539F\u503C\u3002\u62A5\u544A\u4E0D\u66FF\u4EE3\u5BF9\u7528\u6237\u7684\u56DE\u7B54\u3002",
|
|
10522
|
+
handback_input: "\u5F53\u524D\u8F93\u5165\u5C5E\u4E8E\u53E6\u4E00\u4EF6\u5DE5\u4F5C\u65F6\uFF0C\u4EA4\u8FD8\u539F\u8F93\u5165\u4E0E\u7B80\u77ED\u539F\u56E0\u7ED9 lifetime\uFF1B\u4E0D\u91CD\u65B0\u6284\u5199\u6D88\u606F\uFF0C\u4E0D\u505C\u6B62\u6574\u4E2A\u4E8B\u9879\u3002\u9000\u56DE\u540E\u4E0D\u8981\u518D\u6267\u884C\u8FD9\u6761\u8BF7\u6C42\u3002",
|
|
10523
|
+
set_matter_hold: "\u4FDD\u5B58\u5F53\u524D\u7528\u6237\u660E\u786E\u63D0\u51FA\u7684\u6682\u505C\u8303\u56F4\uFF1Ascope=all \u6682\u505C\u6574\u4E2A\u4E8B\u9879\u7684\u81EA\u52A8\u5904\u7406\uFF0Cwrites \u6682\u7F13\u4E1A\u52A1\u5199\u5165\u3002\u660E\u786E\u6062\u590D\u65F6 hold=null\uFF1B\u666E\u901A\u8865\u5145\u3001\u8D44\u6599\u5F15\u7528\u548C\u81EA\u52A8\u4E8B\u4EF6\u4E0D\u6388\u6743\u89E3\u9664\u3002",
|
|
9632
10524
|
notify_members: "\u672C\u8F6E\u786E\u5B9E\u9700\u8981\u6307\u5B9A\u53C2\u4E0E\u4EBA\u8865\u5145\u3001\u786E\u8BA4\u6216\u91C7\u53D6\u884C\u52A8\u65F6\uFF0C\u6807\u8BB0\u9700\u8981\u63D0\u9192\u7684\u6210\u5458\u3002\u53EA\u586B Teammy \u6210\u5458 ID\uFF1B\u5BBF\u4E3B\u5728\u672C\u8F6E\u56DE\u7B54\u53D1\u51FA\u65F6\u6620\u5C04\u6210\u4F01\u4E1A\u5FAE\u4FE1 @\u3002\u4E0D\u5355\u72EC\u53D1\u9001\u6D88\u606F\uFF0C\u4E0D\u8981\u6BCF\u8F6E\u8C03\u7528\uFF0C\u4E0D\u8981\u67E5\u627E\u6216\u8F93\u51FA\u4F01\u5FAE ID\u3002"
|
|
9633
10525
|
};
|
|
9634
10526
|
async function executeCoordinationTool(hub, run, name, input, signal, operation) {
|
|
@@ -9641,6 +10533,9 @@ async function executeCoordinationTool(hub, run, name, input, signal, operation)
|
|
|
9641
10533
|
return matters.delegate(run, DelegateMatterSchema.parse(input), operation);
|
|
9642
10534
|
if (name === "report_matter")
|
|
9643
10535
|
return matters.report(run, ReportMatterSchema.parse(input), operation);
|
|
10536
|
+
if (name === "handback_input")
|
|
10537
|
+
return matters.handback(run, coordinationSchemas.handback_input.parse(input).reason);
|
|
10538
|
+
if (name === "set_matter_hold") return matters.hold(run, MatterHoldSchema.parse(input));
|
|
9644
10539
|
if (name === "notify_members") {
|
|
9645
10540
|
const { members } = coordinationSchemas.notify_members.parse(input);
|
|
9646
10541
|
const valid = new Set(
|
|
@@ -9666,9 +10561,9 @@ async function executeCoordinationTool(hub, run, name, input, signal, operation)
|
|
|
9666
10561
|
}
|
|
9667
10562
|
const allowedMatter = (id) => run.role === "lifetime" || run.context_kind === "matter" && run.conversation_id === id || !run.context_kind && (matters.members(id).includes(run.owner) || matters.get(run.project_id, id).visibility === "project");
|
|
9668
10563
|
if (name === "query_matters") {
|
|
9669
|
-
const { status } = coordinationSchemas.query_matters.parse(input);
|
|
9670
|
-
|
|
9671
|
-
(row) => (!status || row.status === status) && allowedMatter(String(row.conversation_id))
|
|
10564
|
+
const { status, query: query2, member, source, offset, limit } = coordinationSchemas.query_matters.parse(input);
|
|
10565
|
+
const items = hub.db.prepare("SELECT * FROM agent_matters WHERE project_id=? ORDER BY updated_at DESC").all(run.project_id).filter(
|
|
10566
|
+
(row) => (!status || row.status === status) && allowedMatter(String(row.conversation_id)) && (!query2 || `${row.title} ${row.goal} ${row.summary}`.toLowerCase().includes(query2.toLowerCase())) && (!member || matters.members(String(row.conversation_id)).includes(member)) && (!source || !!hub.db.prepare("SELECT 1 FROM agent_matter_sources WHERE conversation_id=? AND ref=?").get(row.conversation_id, source))
|
|
9672
10567
|
).map((row) => ({
|
|
9673
10568
|
ref: row.ref,
|
|
9674
10569
|
title: row.title,
|
|
@@ -9676,6 +10571,10 @@ async function executeCoordinationTool(hub, run, name, input, signal, operation)
|
|
|
9676
10571
|
summary: row.summary,
|
|
9677
10572
|
participants: matters.members(String(row.conversation_id))
|
|
9678
10573
|
}));
|
|
10574
|
+
return {
|
|
10575
|
+
items: items.slice(offset, offset + limit),
|
|
10576
|
+
nextOffset: offset + limit < items.length ? offset + limit : null
|
|
10577
|
+
};
|
|
9679
10578
|
}
|
|
9680
10579
|
const readMatter = (ref) => {
|
|
9681
10580
|
const row = matters.get(run.project_id, ref);
|
|
@@ -9686,20 +10585,33 @@ async function executeCoordinationTool(hub, run, name, input, signal, operation)
|
|
|
9686
10585
|
title: row.title,
|
|
9687
10586
|
status: row.status,
|
|
9688
10587
|
summary: row.summary,
|
|
9689
|
-
|
|
9690
|
-
|
|
9691
|
-
|
|
9692
|
-
|
|
9693
|
-
|
|
9694
|
-
|
|
9695
|
-
|
|
10588
|
+
goal: row.goal,
|
|
10589
|
+
pending: row.pending_json ? JSON.parse(String(row.pending_json)) : null,
|
|
10590
|
+
hold: row.hold_json ? JSON.parse(String(row.hold_json)) : null,
|
|
10591
|
+
sources: hub.db.prepare(
|
|
10592
|
+
"SELECT ref,relation FROM agent_matter_sources WHERE conversation_id=? ORDER BY ref"
|
|
10593
|
+
).all(row.conversation_id),
|
|
10594
|
+
updatedAt: row.updated_at,
|
|
10595
|
+
participants: matters.members(String(row.conversation_id))
|
|
9696
10596
|
};
|
|
9697
10597
|
};
|
|
9698
|
-
if (name === "read_matter")
|
|
10598
|
+
if (name === "read_matter") {
|
|
10599
|
+
const value2 = coordinationSchemas.read_matter.parse(input), overview = readMatter(value2.ref), row = matters.get(run.project_id, value2.ref);
|
|
10600
|
+
if (value2.section === "overview") return overview;
|
|
10601
|
+
const items = value2.section === "messages" ? new AgentMessages(hub).list(String(row.conversation_id), value2.after, value2.limit + 1) : hub.db.prepare(
|
|
10602
|
+
"SELECT rowid seq,kind,status,summary,details_json,created_at FROM agent_matter_reports WHERE conversation_id=? AND state='committed' AND rowid>? ORDER BY rowid LIMIT ?"
|
|
10603
|
+
).all(row.conversation_id, value2.after, value2.limit + 1);
|
|
10604
|
+
const page = items.slice(0, value2.limit);
|
|
10605
|
+
return {
|
|
10606
|
+
...overview,
|
|
10607
|
+
items: page,
|
|
10608
|
+
next: items.length > value2.limit ? Number(page.at(-1).seq) : null
|
|
10609
|
+
};
|
|
10610
|
+
}
|
|
9699
10611
|
const value = coordinationSchemas.read_reference.parse(input);
|
|
9700
10612
|
const detailsRow = hub.db.prepare("SELECT details_json FROM agent_run_details WHERE run_id=?").get(run.id);
|
|
9701
|
-
const
|
|
9702
|
-
const target = !value.latest &&
|
|
10613
|
+
const details2 = detailsRow ? JSON.parse(String(detailsRow.details_json)) : {};
|
|
10614
|
+
const target = !value.latest && details2.references?.[value.ref] || resolveReference(hub, run.project_id, value.ref, value.latest ? null : run.task_id);
|
|
9703
10615
|
const query = (tool, args) => executeProjectQuery(hub, runActor(run), tool, args, signal, run.role === "lifetime");
|
|
9704
10616
|
if (target.kind === "matter") return readMatter(target.id);
|
|
9705
10617
|
if (target.kind === "task") {
|
|
@@ -9753,7 +10665,7 @@ async function executeCoordinationTool(hub, run, name, input, signal, operation)
|
|
|
9753
10665
|
}
|
|
9754
10666
|
|
|
9755
10667
|
// apps/server/materials/agent.ts
|
|
9756
|
-
import { createHash as
|
|
10668
|
+
import { createHash as createHash17 } from "node:crypto";
|
|
9757
10669
|
import { z as z32 } from "zod";
|
|
9758
10670
|
var SaveMaterialSchema = z32.object({
|
|
9759
10671
|
id: MaterialIdSchema.optional(),
|
|
@@ -9772,7 +10684,7 @@ var transport = SaveMaterialSchema.extend({
|
|
|
9772
10684
|
_files: z32.array(z32.object({ name: z32.string(), base64: z32.string() }).strict()).optional()
|
|
9773
10685
|
});
|
|
9774
10686
|
function operationUuid(value) {
|
|
9775
|
-
const bytes =
|
|
10687
|
+
const bytes = createHash17("sha256").update(JSON.stringify(value)).digest().subarray(0, 16);
|
|
9776
10688
|
bytes[6] = bytes[6] & 15 | 64;
|
|
9777
10689
|
bytes[8] = bytes[8] & 63 | 128;
|
|
9778
10690
|
const hex = bytes.toString("hex");
|
|
@@ -9854,26 +10766,31 @@ var schemas2 = {
|
|
|
9854
10766
|
reply_message: z33.object({ messageId: z33.uuid(), body: z33.string().trim().min(1) }).strict(),
|
|
9855
10767
|
write_tasks: TaskWriteSchema,
|
|
9856
10768
|
save_material: SaveMaterialSchema,
|
|
9857
|
-
send_inbox: AgentTextSchema,
|
|
10769
|
+
send_inbox: AgentTextSchema.extend({ branch: z33.string().trim().min(1).optional() }),
|
|
9858
10770
|
send_outbox: OutboxSendSchema,
|
|
9859
|
-
post_pr_comment: z33.object({
|
|
10771
|
+
post_pr_comment: z33.object({
|
|
10772
|
+
number: z33.number().int().positive(),
|
|
10773
|
+
body: z33.string().trim().min(1),
|
|
10774
|
+
branch: z33.string().trim().min(1).optional()
|
|
10775
|
+
}).strict()
|
|
9860
10776
|
};
|
|
9861
10777
|
var descriptions2 = {
|
|
9862
10778
|
...coordinationDescriptions,
|
|
9863
10779
|
...queryDescriptions,
|
|
9864
10780
|
...personalQueryDescriptions,
|
|
9865
|
-
reply_message: "\u6839\u636E\u5F53\u524D\u7528\u6237\u660E\u786E\u63D0\u4F9B\u7684\u7B54\u590D\uFF0C\u56DE\u590D\u5176\u6536\u5230\
|
|
10781
|
+
reply_message: "\u6839\u636E\u5F53\u524D\u7528\u6237\u660E\u786E\u63D0\u4F9B\u7684\u7B54\u590D\uFF0C\u56DE\u590D\u5176\u6536\u5230\u7684Tim\u6D88\u606F\u3002\u5148 read_message \u6838\u5BF9\u539F\u95EE\u9898\uFF1B\u652F\u6301\u6301\u7EED\u591A\u8F6E\u56DE\u590D\u3002\u4E0D\u8981\u81EA\u884C\u731C\u6D4B\u7528\u6237\u7684\u51B3\u5B9A\u3002",
|
|
9866
10782
|
write_tasks: "\u7528\u6237\u660E\u786E\u786E\u8BA4\u5177\u4F53\u8C03\u6574\u540E\uFF0C\u65B0\u589E\u6216\u4FEE\u6539\u672C\u9879\u76EE\u4EFB\u52A1\u3002\u5148\u7528 query_tasks \u67E5\u8BE2\u5F53\u524D\u4EFB\u52A1\uFF0C\u66F4\u65B0\u586B\u5199 expectedRevision\u3002materials \u586B\u5DF2\u6536\u5F55\u8D44\u6599\u7F16\u53F7\u53CA\u7248\u672C\u4F5C\u4E3A\u4F9D\u636E\uFF1B\u4E0D\u4FEE\u6539\u5DE5\u4F5C\u533A\u6587\u4EF6\u3002needsConfirmation \u53EA\u8868\u793A\u4EFB\u52A1\u8868\u8FF0\u5B58\u5728\u5F71\u54CD\u7406\u89E3\u7684\u6B67\u4E49\uFF0C\u4E0D\u8868\u793A\u7B49\u5F85\u63A5\u5355\u6216\u9A8C\u6536\u3002\u666E\u901A\u8BA8\u8BBA\u6216\u6536\u5F55\u8D44\u6599\u4E0D\u6388\u6743\u5199\u4EFB\u52A1\u3002",
|
|
9867
10783
|
save_material: "\u6309\u7528\u6237\u6536\u5F55\u610F\u56FE\u4FDD\u5B58\u9879\u76EE\u8D44\u6599\uFF0C\u6216\u6574\u7406\u6307\u5B9A\u7684\u5DF2\u6709\u8D44\u6599\u3002\u65B0\u8D44\u6599\u63D0\u4F9B text\u3001url \u6216 originals \u6587\u4EF6\u8DEF\u5F84\uFF1B\u957F\u6B63\u6587\u7528 contentFile \u6307\u5411\u672C\u5730 Markdown\uFF0C\u539F\u4EF6\u7531\u5DE5\u5177\u76F4\u63A5\u4F20\u8F93\u3002\u4FEE\u6539\u5DF2\u6709\u8D44\u6599\u9700 id \u548C expectedRevision\u3002\u8FD4\u56DE\u7F16\u53F7\u53CA\u7F51\u9875\u94FE\u63A5\uFF1B\u4E0D\u4F1A\u4FEE\u6539\u4EFB\u52A1\u3002",
|
|
9868
|
-
send_inbox: "\u5C06\u7528\u6237\u660E\u786E\u59D4\u6258\u7684\
|
|
9869
|
-
send_outbox: "\u5411\u9879\u76EE\u6210\u5458\u53D1\u9001\
|
|
9870
|
-
post_pr_comment: "\u5C06\u9879\u76EE\u7EA7\u5BA1\u6838\u610F\u89C1\u53D1\u5E03\u4E3A PR \u666E\u901A\u8BC4\u8BBA\u3002\u9700\u8981\u5B9A\u5411\u6C9F\u901A\u65F6\u4F7F\u7528 outbox\uFF0C\u4E0D\u4FEE\u6539 PR \u8BC4\u5BA1\u6216\u5408\u5E76\u72B6\u6001\u3002"
|
|
10784
|
+
send_inbox: "\u5C06\u7528\u6237\u660E\u786E\u59D4\u6258\u7684\u5DE5\u4F5C\u4EA4\u7ED9\u9879\u76EE\u534F\u8C03\u8005\u3002text \u4FDD\u7559\u76EE\u6807\u3001\u5FC5\u8981\u80CC\u666F\u3001\u6765\u6E90\u548C\u7EA6\u675F\uFF1B\u9700\u8981\u53D1\u5E03 PR \u5BA1\u6838\u610F\u89C1\u65F6\u5199\u660E\u53D1\u5E03\u8981\u6C42\u3002\u8FD4\u56DE taskId \u8868\u793A\u5DF2\u5165\u961F\uFF0C\u540E\u7EED\u53EF\u8FDB\u5165\u4E8B\u9879\u6301\u7EED\u6C9F\u901A\uFF1B\u4E0D\u8981\u627F\u8BFA\u5DF2\u5B8C\u6210\u3002\u72EC\u7ACB\u62C6\u5206\u591A\u4EF6\u5DE5\u4F5C\u65F6\u7528\u4E0D\u540C branch\uFF0C\u91CD\u8BD5\u6CBF\u7528\u539F branch\u3002",
|
|
10785
|
+
send_outbox: "\u5411\u9879\u76EE\u6210\u5458\u53D1\u9001\u7B80\u77ED\u901A\u77E5\u3002to \u586B\u6210\u5458 ID \u6570\u7EC4\uFF0Cdebug \u7528\u4E8E\u8BCA\u65AD\u3002\u6B63\u6587\u5199\u6E05\u5FC5\u8981\u80CC\u666F\u548C\u5177\u4F53\u95EE\u9898\u3002\u4E8B\u9879\u4E2D\u5199\u5165\u672C\u8F6E\u540C\u4E00\u4E2A\u56DE\u7B54\u4F4D\u7F6E\uFF0Cto \u53EA\u63D0\u9192\u5DF2\u6709\u53C2\u4E0E\u4EBA\uFF0C\u4E0D\u53E6\u5F00\u79C1\u804A\uFF1B\u8FD4\u56DE staged \u8868\u793A\u968F\u672C\u8F6E\u6210\u529F\u63D0\u4EA4\u3002\u5176\u4ED6\u573A\u666F\u7684 messageId \u8868\u793A\u5DF2\u5165\u6295\u9012\u961F\u5217\uFF0C\u56DE\u590D\u53EF\u4EE5\u591A\u8F6E\u7EE7\u7EED\u3002instant \u4EC5\u5728\u7528\u6237\u660E\u786E\u8981\u6C42\u8054\u7CFB\u65F6\u4F7F\u7528\uFF1B\u9700\u5411\u540C\u4E00\u53D7\u4F17\u53D1\u9001\u72EC\u7ACB\u901A\u77E5\u65F6\u586B\u4E0D\u540C branch\uFF0C\u91CD\u8BD5\u6CBF\u7528\u3002",
|
|
10786
|
+
post_pr_comment: "\u5C06\u9879\u76EE\u7EA7\u5BA1\u6838\u610F\u89C1\u53D1\u5E03\u4E3A PR \u666E\u901A\u8BC4\u8BBA\u3002\u9700\u8981\u5B9A\u5411\u6C9F\u901A\u65F6\u4F7F\u7528 outbox\uFF0C\u4E0D\u4FEE\u6539 PR \u8BC4\u5BA1\u6216\u5408\u5E76\u72B6\u6001\u3002\u540C\u4E00\u8F6E\u3001\u540C\u4E00 PR \u7684\u91CD\u8BD5\u590D\u7528\u539F\u8BC4\u8BBA\uFF1B\u786E\u9700\u53D1\u5E03\u72EC\u7ACB\u8BC4\u8BBA\u65F6\u6307\u5B9A\u4E0D\u540C branch\u3002"
|
|
9871
10787
|
};
|
|
9872
10788
|
function projectOutputOperation(run, name, input) {
|
|
9873
10789
|
if (name === "delegate_matter" && run.role === "lifetime") {
|
|
9874
10790
|
const value = schemas2.delegate_matter.parse(input);
|
|
9875
10791
|
value.participants = [...new Set(value.participants)].sort();
|
|
9876
10792
|
value.sources = [...new Set(value.sources)].sort();
|
|
10793
|
+
value.follows = [...new Set(value.follows)].sort();
|
|
9877
10794
|
return { name, value, key: fingerprint([run.project_id, run.id, name, value]) };
|
|
9878
10795
|
}
|
|
9879
10796
|
if (name === "report_matter" && run.context_kind === "matter") {
|
|
@@ -9882,28 +10799,46 @@ function projectOutputOperation(run, name, input) {
|
|
|
9882
10799
|
}
|
|
9883
10800
|
if (name === "reply_message" && run.role === "instant" && !run.context_kind) {
|
|
9884
10801
|
const value = schemas2.reply_message.parse(input);
|
|
9885
|
-
return {
|
|
10802
|
+
return {
|
|
10803
|
+
name,
|
|
10804
|
+
value,
|
|
10805
|
+
key: fingerprint([run.project_id, run.id, name, value.messageId])
|
|
10806
|
+
};
|
|
9886
10807
|
}
|
|
9887
10808
|
if (name === "send_inbox" && run.role === "instant") {
|
|
9888
10809
|
const value = schemas2.send_inbox.parse(input);
|
|
9889
|
-
return {
|
|
10810
|
+
return {
|
|
10811
|
+
name,
|
|
10812
|
+
value,
|
|
10813
|
+
key: fingerprint([run.project_id, run.id, name, value.branch ?? ""])
|
|
10814
|
+
};
|
|
9890
10815
|
}
|
|
9891
10816
|
if (name === "send_outbox") {
|
|
9892
10817
|
const value = schemas2.send_outbox.parse(input);
|
|
9893
10818
|
if (Array.isArray(value.to)) value.to = [...new Set(value.to)].sort();
|
|
9894
|
-
return {
|
|
10819
|
+
return {
|
|
10820
|
+
name,
|
|
10821
|
+
value,
|
|
10822
|
+
key: fingerprint([run.project_id, run.id, name, value.to, value.branch ?? ""])
|
|
10823
|
+
};
|
|
9895
10824
|
}
|
|
9896
10825
|
if (name === "post_pr_comment" && (run.role === "lifetime" || run.context_kind === "matter")) {
|
|
9897
10826
|
const value = schemas2.post_pr_comment.parse(input);
|
|
9898
|
-
return {
|
|
10827
|
+
return {
|
|
10828
|
+
name,
|
|
10829
|
+
value,
|
|
10830
|
+
key: fingerprint([run.project_id, run.id, name, value.number, value.branch ?? ""])
|
|
10831
|
+
};
|
|
9899
10832
|
}
|
|
9900
10833
|
return void 0;
|
|
9901
10834
|
}
|
|
9902
10835
|
function toolDefinitions(role, contextKind) {
|
|
9903
|
-
return Object.keys(schemas2).filter((name) => name !== "delegate_matter" || role === "lifetime").filter((name) => name !== "report_matter" || contextKind === "matter").filter((name) => name !== "notify_members" || contextKind === "matter").filter(
|
|
10836
|
+
return Object.keys(schemas2).filter((name) => name !== "delegate_matter" || role === "lifetime").filter((name) => name !== "report_matter" || contextKind === "matter").filter((name) => name !== "notify_members" || contextKind === "matter").filter(
|
|
10837
|
+
(name) => !["handback_input", "set_matter_hold"].includes(name) || contextKind === "matter"
|
|
10838
|
+
).filter((name) => contextKind !== "matter" || name !== "send_inbox").filter(
|
|
9904
10839
|
(name) => !["list_messages", "read_message", "reply_message"].includes(name) || role === "instant" && !contextKind
|
|
9905
10840
|
).filter(
|
|
9906
|
-
(name) => contextKind === "material" ? name === "save_material" : role === "lifetime" ? !["send_inbox", "save_material", "write_tasks"].includes(name) : name !== "post_pr_comment" || contextKind === "matter"
|
|
10841
|
+
(name) => contextKind === "material" ? name === "save_material" : role === "lifetime" ? !["send_inbox", "save_material", "write_tasks", "post_pr_comment"].includes(name) : name !== "post_pr_comment" || contextKind === "matter"
|
|
9907
10842
|
).map((name) => ({
|
|
9908
10843
|
name,
|
|
9909
10844
|
label: name,
|
|
@@ -9918,6 +10853,10 @@ async function executeProjectTool(hub, run, name, input, signal) {
|
|
|
9918
10853
|
throw new HubError(403, "AGENT_TOOL_UNAVAILABLE", "\u5F53\u524D Agent \u65E0\u6B64\u5DE5\u5177\u3002");
|
|
9919
10854
|
const identity = runActor(run);
|
|
9920
10855
|
if (run.context_kind) requireProjectActor(hub, identity);
|
|
10856
|
+
if (["write_tasks", "save_material", "post_pr_comment"].includes(name))
|
|
10857
|
+
new Matters(hub).assertWritable(run);
|
|
10858
|
+
if (run.context_kind === "matter" && !["handback_input", "read_matter", "query_matters"].includes(name) && hub.db.prepare("SELECT 1 FROM agent_input_handbacks WHERE run_id=?").get(run.id))
|
|
10859
|
+
throw new HubError(409, "MATTER_INPUT_RETURNED", "\u672C\u6761\u8F93\u5165\u5DF2\u9000\u56DE\u534F\u8C03\u8005\uFF0C\u4E0D\u518D\u6267\u884C\u5176\u64CD\u4F5C\u3002");
|
|
9921
10860
|
if (Object.hasOwn(coordinationSchemas, name))
|
|
9922
10861
|
return executeCoordinationTool(
|
|
9923
10862
|
hub,
|
|
@@ -9947,7 +10886,7 @@ async function executeProjectTool(hub, run, name, input, signal) {
|
|
|
9947
10886
|
const output = projectOutputOperation(run, name, input);
|
|
9948
10887
|
if (output?.name === "reply_message") {
|
|
9949
10888
|
requirePersonalActor(hub, identity);
|
|
9950
|
-
return store.reply(identity, output.value.messageId, output.value.body);
|
|
10889
|
+
return store.reply(identity, output.value.messageId, output.value.body, output.key);
|
|
9951
10890
|
}
|
|
9952
10891
|
if (output?.name === "send_inbox") {
|
|
9953
10892
|
return store.enqueue(
|
|
@@ -9956,12 +10895,38 @@ async function executeProjectTool(hub, run, name, input, signal) {
|
|
|
9956
10895
|
${output.value.text}` : delegationPrompt(run.owner, output.value.text),
|
|
9957
10896
|
output.key,
|
|
9958
10897
|
{
|
|
10898
|
+
origin: { kind: "human", ...run.owner ? { actor: run.owner } : {} },
|
|
9959
10899
|
audit: { sourceRunId: run.id, requester: run.context_kind === "group" ? null : run.owner }
|
|
9960
10900
|
}
|
|
9961
10901
|
);
|
|
9962
10902
|
}
|
|
9963
10903
|
if (output?.name === "send_outbox") {
|
|
9964
10904
|
const { value, key } = output;
|
|
10905
|
+
const notice = run.role === "lifetime" ? new Matters(hub).details(run).notification : void 0;
|
|
10906
|
+
if (notice && Array.isArray(value.to) && value.to.length === 1 && value.to[0] === notice.memberId) {
|
|
10907
|
+
hub.db.prepare(
|
|
10908
|
+
"INSERT INTO agent_answer_drafts VALUES(?,?) ON CONFLICT(run_id) DO UPDATE SET body=excluded.body"
|
|
10909
|
+
).run(run.id, value.body);
|
|
10910
|
+
return {
|
|
10911
|
+
conversationId: notice.conversationId,
|
|
10912
|
+
status: "staged",
|
|
10913
|
+
note: "\u5DF2\u6682\u5B58\u5F53\u524D\u901A\u77E5\u7684\u7B54\u590D\uFF0C\u65E0\u9700\u518D\u6B21\u53D1\u9001\u3002"
|
|
10914
|
+
};
|
|
10915
|
+
}
|
|
10916
|
+
if (run.context_kind === "matter" && value.to !== "debug") {
|
|
10917
|
+
const members = new Matters(hub).members(run.conversation_id);
|
|
10918
|
+
if (value.to.some((member) => !members.includes(member)))
|
|
10919
|
+
throw new HubError(400, "MATTER_MEMBER_INVALID", "\u8BF7\u5148\u534F\u8C03\u9080\u8BF7\u9700\u8981\u53C2\u4E0E\u7684\u6210\u5458\u3002");
|
|
10920
|
+
hub.db.prepare(
|
|
10921
|
+
"INSERT INTO agent_answer_drafts VALUES(?,?) ON CONFLICT(run_id) DO UPDATE SET body=excluded.body"
|
|
10922
|
+
).run(run.id, value.body);
|
|
10923
|
+
await executeCoordinationTool(hub, run, "notify_members", { members: value.to }, signal);
|
|
10924
|
+
return {
|
|
10925
|
+
conversationId: run.conversation_id,
|
|
10926
|
+
status: "staged",
|
|
10927
|
+
note: "\u5DF2\u6682\u5B58\uFF0C\u5C06\u968F\u672C\u8F6E\u6210\u529F\u63D0\u4EA4\uFF1B\u65E0\u9700\u91CD\u590D\u8F93\u51FA\u6B63\u6587\u3002"
|
|
10928
|
+
};
|
|
10929
|
+
}
|
|
9965
10930
|
if (value.to === "debug") {
|
|
9966
10931
|
const id = new Diagnostics(hub).record({
|
|
9967
10932
|
project: run.project_id,
|
|
@@ -9984,9 +10949,10 @@ ${output.value.text}` : delegationPrompt(run.owner, output.value.text),
|
|
|
9984
10949
|
}
|
|
9985
10950
|
if (output?.name === "post_pr_comment") {
|
|
9986
10951
|
const { value, key } = output;
|
|
10952
|
+
const prior = hub.db.prepare("SELECT body FROM project_agent_publications WHERE project_id=? AND operation_key=?").get(run.project_id, key);
|
|
9987
10953
|
return gitProvider(hub, run.project_id).publish(
|
|
9988
10954
|
value.number,
|
|
9989
|
-
value.body,
|
|
10955
|
+
prior ? String(prior.body) : value.body,
|
|
9990
10956
|
key,
|
|
9991
10957
|
run.task_id,
|
|
9992
10958
|
signal
|
|
@@ -10061,7 +11027,7 @@ var ProjectAgentRuntime = class {
|
|
|
10061
11027
|
}
|
|
10062
11028
|
hub;
|
|
10063
11029
|
signal;
|
|
10064
|
-
owner =
|
|
11030
|
+
owner = randomUUID19();
|
|
10065
11031
|
store;
|
|
10066
11032
|
projects = /* @__PURE__ */ new Map();
|
|
10067
11033
|
pending = /* @__PURE__ */ new Set();
|
|
@@ -10181,7 +11147,10 @@ var ProjectAgentRuntime = class {
|
|
|
10181
11147
|
runId: run.id,
|
|
10182
11148
|
cwd: run.context_kind === "matter" ? "/work" : "/project",
|
|
10183
11149
|
attempt: run.attempts,
|
|
10184
|
-
text:
|
|
11150
|
+
text: [
|
|
11151
|
+
new Matters(this.hub).context(run),
|
|
11152
|
+
projectInputPrompt(run, project.workspace.repositoryError)
|
|
11153
|
+
].filter(Boolean).join("\n\n"),
|
|
10185
11154
|
sessionFile: run.session_file,
|
|
10186
11155
|
sessionDir: `/state/${run.conversation_id}`,
|
|
10187
11156
|
model,
|
|
@@ -10190,6 +11159,10 @@ var ProjectAgentRuntime = class {
|
|
|
10190
11159
|
tools: toolDefinitions(run.role, run.context_kind)
|
|
10191
11160
|
},
|
|
10192
11161
|
async (message) => {
|
|
11162
|
+
if (message.type === "attempt_start") {
|
|
11163
|
+
if (this.store.active(run)) new Matters(this.hub).abandonDrafts(run.id);
|
|
11164
|
+
return;
|
|
11165
|
+
}
|
|
10193
11166
|
if (message.type === "session") {
|
|
10194
11167
|
this.store.sessionFile(run, message.file);
|
|
10195
11168
|
return;
|
|
@@ -10198,6 +11171,10 @@ var ProjectAgentRuntime = class {
|
|
|
10198
11171
|
this.store.delta(run, message.text);
|
|
10199
11172
|
return;
|
|
10200
11173
|
}
|
|
11174
|
+
if (message.type === "answer_reset") {
|
|
11175
|
+
this.store.resetAnswer(run);
|
|
11176
|
+
return;
|
|
11177
|
+
}
|
|
10201
11178
|
if (message.type === "model_abort") {
|
|
10202
11179
|
modelRequests.get(message.id)?.abort();
|
|
10203
11180
|
return;
|
|
@@ -10235,7 +11212,9 @@ var ProjectAgentRuntime = class {
|
|
|
10235
11212
|
if (output) outputErrors.delete(output.key);
|
|
10236
11213
|
response = { value };
|
|
10237
11214
|
} catch (error) {
|
|
10238
|
-
|
|
11215
|
+
const rejectedProposal = output && ["delegate_matter", "report_matter"].includes(output.name) && error instanceof HubError && error.statusCode < 500;
|
|
11216
|
+
if (output && !rejectedProposal)
|
|
11217
|
+
outputErrors.set(output.key, { name: output.name, error });
|
|
10239
11218
|
logs.record({
|
|
10240
11219
|
project: run.project_id,
|
|
10241
11220
|
code: "TOOL_FAILED",
|
|
@@ -10294,7 +11273,7 @@ var ProjectAgentRuntime = class {
|
|
|
10294
11273
|
await project.boxes[conversation]?.close();
|
|
10295
11274
|
delete project.boxes[conversation];
|
|
10296
11275
|
delete project.repositories[conversation];
|
|
10297
|
-
await
|
|
11276
|
+
await rm6(join18(project.workspace.root, "contexts", conversation), {
|
|
10298
11277
|
recursive: true,
|
|
10299
11278
|
force: true
|
|
10300
11279
|
});
|
|
@@ -10391,13 +11370,13 @@ async function runProjectAgents(hub, signal, once = false) {
|
|
|
10391
11370
|
// apps/server/ai/sandbox-check.ts
|
|
10392
11371
|
import { execFile } from "node:child_process";
|
|
10393
11372
|
import { promisify } from "node:util";
|
|
10394
|
-
import { mkdtemp as mkdtemp5, mkdir as mkdir7, rm as
|
|
11373
|
+
import { mkdtemp as mkdtemp5, mkdir as mkdir7, rm as rm7 } from "node:fs/promises";
|
|
10395
11374
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
10396
|
-
import { join as
|
|
11375
|
+
import { join as join19 } from "node:path";
|
|
10397
11376
|
async function checkWorkerSandbox(hub) {
|
|
10398
|
-
const root = await mkdtemp5(
|
|
11377
|
+
const root = await mkdtemp5(join19(tmpdir3(), "teammy-sandbox-check-"));
|
|
10399
11378
|
try {
|
|
10400
|
-
await mkdir7(
|
|
11379
|
+
await mkdir7(join19(root, "workspaces"));
|
|
10401
11380
|
const { command, args } = await sandboxCommand(root, false);
|
|
10402
11381
|
args.splice(-1, 1, "-e", 'process.stdout.write("ready")');
|
|
10403
11382
|
const result = await promisify(execFile)(command, args, {
|
|
@@ -10409,7 +11388,7 @@ async function checkWorkerSandbox(hub) {
|
|
|
10409
11388
|
} catch {
|
|
10410
11389
|
new RuntimeStatus(hub).recordSandbox("failed");
|
|
10411
11390
|
} finally {
|
|
10412
|
-
await
|
|
11391
|
+
await rm7(root, { recursive: true, force: true });
|
|
10413
11392
|
}
|
|
10414
11393
|
}
|
|
10415
11394
|
|
|
@@ -10517,6 +11496,7 @@ async function runAiCycle(hub, stop, claimed) {
|
|
|
10517
11496
|
}
|
|
10518
11497
|
async function runAiWorker(directory, once = false) {
|
|
10519
11498
|
const hub = new HubStore(directory), stop = new AbortController();
|
|
11499
|
+
new Maintenance(hub).assertReady();
|
|
10520
11500
|
const shutdown = () => stop.abort();
|
|
10521
11501
|
process.once("SIGINT", shutdown);
|
|
10522
11502
|
process.once("SIGTERM", shutdown);
|
|
@@ -10742,17 +11722,17 @@ function registerAiRoutes(server, store) {
|
|
|
10742
11722
|
}
|
|
10743
11723
|
|
|
10744
11724
|
// apps/server/static.ts
|
|
10745
|
-
import { join as
|
|
11725
|
+
import { join as join20 } from "node:path";
|
|
10746
11726
|
import fastifyStatic from "@fastify/static";
|
|
10747
11727
|
import { readFile as readFile4 } from "node:fs/promises";
|
|
10748
11728
|
function registerWeb(server, root) {
|
|
10749
|
-
const html = () => readFile4(
|
|
11729
|
+
const html = () => readFile4(join20(root, "index.html"), "utf8");
|
|
10750
11730
|
server.get(
|
|
10751
11731
|
"/downloads/teammy-skill.zip",
|
|
10752
|
-
async (_request, reply) => reply.type("application/zip").header("cache-control", "no-cache").header("content-disposition", 'attachment; filename="teammy-skill.zip"').send(await readFile4(
|
|
11732
|
+
async (_request, reply) => reply.type("application/zip").header("cache-control", "no-cache").header("content-disposition", 'attachment; filename="teammy-skill.zip"').send(await readFile4(join20(root, "downloads/teammy-skill.zip")))
|
|
10753
11733
|
);
|
|
10754
11734
|
server.register(fastifyStatic, {
|
|
10755
|
-
root:
|
|
11735
|
+
root: join20(root, "assets"),
|
|
10756
11736
|
prefix: "/assets/",
|
|
10757
11737
|
immutable: true,
|
|
10758
11738
|
maxAge: "1y",
|
|
@@ -10796,7 +11776,7 @@ function registerWeb(server, root) {
|
|
|
10796
11776
|
}
|
|
10797
11777
|
|
|
10798
11778
|
// apps/server/server.ts
|
|
10799
|
-
import { randomUUID as
|
|
11779
|
+
import { randomUUID as randomUUID24 } from "node:crypto";
|
|
10800
11780
|
import Fastify from "fastify";
|
|
10801
11781
|
|
|
10802
11782
|
// packages/protocol/index.ts
|
|
@@ -11258,6 +12238,10 @@ function registerSessionRoutes(server, store) {
|
|
|
11258
12238
|
const q = z43.object({ revision: z43.coerce.number().pipe(RevisionSchema).optional() }).parse(request.query);
|
|
11259
12239
|
return sessions.read(authorizeRead(store, request), p.owner, p.key, q.revision);
|
|
11260
12240
|
});
|
|
12241
|
+
server.get("/sessions/:owner/:key/baseline", guard, async (request) => {
|
|
12242
|
+
const p = z43.object({ owner: NameSchema, key: HashSchema }).parse(request.params);
|
|
12243
|
+
return sessions.baseline(authorize(request), p.owner, p.key);
|
|
12244
|
+
});
|
|
11261
12245
|
server.get("/sessions/:owner/:key/analysis/:kind", async (request) => {
|
|
11262
12246
|
const p = z43.object({ owner: NameSchema, key: HashSchema, kind: SessionAnalysisKindSchema }).parse(request.params);
|
|
11263
12247
|
const q = z43.object({ revision: z43.coerce.number().pipe(RevisionSchema).optional() }).parse(request.query);
|
|
@@ -11385,21 +12369,21 @@ function registerFileRoutes(server, store) {
|
|
|
11385
12369
|
}
|
|
11386
12370
|
|
|
11387
12371
|
// apps/server/agents/routes.ts
|
|
11388
|
-
import { createHash as
|
|
12372
|
+
import { createHash as createHash18 } from "node:crypto";
|
|
11389
12373
|
import { z as z45 } from "zod";
|
|
11390
12374
|
|
|
11391
12375
|
// apps/server/agents/ssh-key.ts
|
|
11392
12376
|
import { execFile as execFile2 } from "node:child_process";
|
|
11393
12377
|
import { promisify as promisify2 } from "node:util";
|
|
11394
|
-
import { mkdtemp as mkdtemp6, writeFile as writeFile6, readFile as readFile5, rm as
|
|
11395
|
-
import { join as
|
|
12378
|
+
import { mkdtemp as mkdtemp6, writeFile as writeFile6, readFile as readFile5, rm as rm8 } from "node:fs/promises";
|
|
12379
|
+
import { join as join21 } from "node:path";
|
|
11396
12380
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
11397
12381
|
async function projectSshKey(hub, project) {
|
|
11398
12382
|
const config = new AgentConfig(hub), initial = config.secrets(project);
|
|
11399
12383
|
if (initial.sshPublicKey) return config.view(project);
|
|
11400
|
-
const directory = await mkdtemp6(
|
|
12384
|
+
const directory = await mkdtemp6(join21(tmpdir4(), "teammy-ssh-key-"));
|
|
11401
12385
|
try {
|
|
11402
|
-
const path =
|
|
12386
|
+
const path = join21(directory, "key");
|
|
11403
12387
|
let publicKey;
|
|
11404
12388
|
if (initial.sshPrivateKey) {
|
|
11405
12389
|
await writeFile6(path, initial.sshPrivateKey.replace(/\n?$/, "\n"), { mode: 384 });
|
|
@@ -11423,7 +12407,7 @@ async function projectSshKey(hub, project) {
|
|
|
11423
12407
|
publicKey
|
|
11424
12408
|
);
|
|
11425
12409
|
} finally {
|
|
11426
|
-
await
|
|
12410
|
+
await rm8(directory, { recursive: true, force: true });
|
|
11427
12411
|
}
|
|
11428
12412
|
}
|
|
11429
12413
|
|
|
@@ -11534,7 +12518,8 @@ ${text}`));
|
|
|
11534
12518
|
if (id) store.chat(identity, id);
|
|
11535
12519
|
new AiDefaults(hub).resolve(identity.workspaceId);
|
|
11536
12520
|
config.initialize(identity.workspaceId);
|
|
11537
|
-
|
|
12521
|
+
const input = AgentTextSchema.parse(req.body);
|
|
12522
|
+
return reply.code(202).send(store.ask(identity, input.text, id, input.requestId));
|
|
11538
12523
|
});
|
|
11539
12524
|
server.get(`${path}/events`, async (req, reply) => {
|
|
11540
12525
|
const identity = authorizeWeb(hub, req), snapshot = store.chat(identity, chatId(req.params));
|
|
@@ -11581,19 +12566,26 @@ data: ${event.data}
|
|
|
11581
12566
|
webMutation(req);
|
|
11582
12567
|
const identity = authorizeWeb(hub, req);
|
|
11583
12568
|
const { cursor } = z45.object({ cursor: z45.number().int().nonnegative() }).strict().parse(req.body);
|
|
11584
|
-
|
|
12569
|
+
const id = messageId(req.params);
|
|
12570
|
+
store.ownedChat(identity, id);
|
|
12571
|
+
if (hub.db.prepare("SELECT 1 FROM agent_matters WHERE conversation_id=?").get(id))
|
|
12572
|
+
new Matters(hub).mark(identity, id, cursor);
|
|
12573
|
+
else {
|
|
12574
|
+
hub.db.prepare(
|
|
12575
|
+
`UPDATE agent_notice_threads SET read_seq=MAX(read_seq,MIN(?,COALESCE((SELECT MAX(seq) FROM agent_messages WHERE conversation_id=?),0))) WHERE conversation_id=? AND user_id=?`
|
|
12576
|
+
).run(cursor, id, id, identity.memberId);
|
|
12577
|
+
const thread = hub.db.prepare(
|
|
12578
|
+
"SELECT message_id FROM agent_notice_threads WHERE conversation_id=? AND user_id=?"
|
|
12579
|
+
).get(id, identity.memberId);
|
|
12580
|
+
if (thread) store.mark(identity, String(thread.message_id));
|
|
12581
|
+
}
|
|
11585
12582
|
return { ok: true };
|
|
11586
12583
|
});
|
|
11587
12584
|
server.get("/agents/outbox", async (req) => store.messages(authorizeWeb(hub, req)));
|
|
11588
12585
|
server.get("/agents/outbox/unread", async (req) => {
|
|
11589
12586
|
const identity = authorizeWeb(hub, req);
|
|
11590
12587
|
return {
|
|
11591
|
-
unread:
|
|
11592
|
-
hub.db.prepare(
|
|
11593
|
-
`SELECT COUNT(*) n FROM project_agent_outbox o JOIN project_agent_recipients r ON r.message_id=o.id
|
|
11594
|
-
WHERE o.project_id=? AND r.user_id=? AND r.read_at IS NULL AND r.archived_at IS NULL`
|
|
11595
|
-
).get(identity.workspaceId, identity.memberId).n
|
|
11596
|
-
)
|
|
12588
|
+
unread: store.unread(identity)
|
|
11597
12589
|
};
|
|
11598
12590
|
});
|
|
11599
12591
|
server.get(
|
|
@@ -11610,11 +12602,8 @@ data: ${event.data}
|
|
|
11610
12602
|
});
|
|
11611
12603
|
server.post("/agents/outbox/:id/reply", async (req) => {
|
|
11612
12604
|
webMutation(req);
|
|
11613
|
-
|
|
11614
|
-
|
|
11615
|
-
messageId(req.params),
|
|
11616
|
-
AgentReplySchema.parse(req.body).body
|
|
11617
|
-
);
|
|
12605
|
+
const input = AgentReplySchema.parse(req.body);
|
|
12606
|
+
return store.reply(authorizeWeb(hub, req), messageId(req.params), input.body, input.requestId);
|
|
11618
12607
|
});
|
|
11619
12608
|
server.register(async (scope2) => {
|
|
11620
12609
|
scope2.removeContentTypeParser("application/json");
|
|
@@ -11627,7 +12616,7 @@ data: ${event.data}
|
|
|
11627
12616
|
const raw = req.body;
|
|
11628
12617
|
const event = String(req.headers["x-github-event"] ?? "");
|
|
11629
12618
|
const delivery = String(
|
|
11630
|
-
req.headers["x-github-delivery"] ??
|
|
12619
|
+
req.headers["x-github-delivery"] ?? createHash18("sha256").update(raw).digest("hex")
|
|
11631
12620
|
);
|
|
11632
12621
|
return new GitHub(hub, projectId(req)).receive(
|
|
11633
12622
|
event,
|
|
@@ -11811,7 +12800,7 @@ function registerClientAccess(server, store) {
|
|
|
11811
12800
|
import { z as z50 } from "zod";
|
|
11812
12801
|
|
|
11813
12802
|
// apps/server/wecom/channels.ts
|
|
11814
|
-
import { createHash as
|
|
12803
|
+
import { createHash as createHash19, randomUUID as randomUUID20 } from "node:crypto";
|
|
11815
12804
|
import { z as z48 } from "zod";
|
|
11816
12805
|
var channelEventSchema = z48.object({
|
|
11817
12806
|
msgid: z48.string().min(1),
|
|
@@ -11821,7 +12810,7 @@ var channelEventSchema = z48.object({
|
|
|
11821
12810
|
from: z48.object({ userid: z48.string().optional(), aibotid: z48.string().optional() }),
|
|
11822
12811
|
event: z48.object({ eventtype: z48.string() }).catchall(z48.unknown())
|
|
11823
12812
|
});
|
|
11824
|
-
var digest4 = (value) =>
|
|
12813
|
+
var digest4 = (value) => createHash19("sha256").update(value).digest("hex");
|
|
11825
12814
|
var WecomChannels = class {
|
|
11826
12815
|
constructor(hub) {
|
|
11827
12816
|
this.hub = hub;
|
|
@@ -11894,7 +12883,7 @@ var WecomChannels = class {
|
|
|
11894
12883
|
"UPDATE wecom_deliveries SET state='cancelled' WHERE bot_id=? AND project_id=? AND chat_type=3 AND state='pending'"
|
|
11895
12884
|
).run(bot, identity.workspaceId);
|
|
11896
12885
|
if (topic)
|
|
11897
|
-
this.hub.db.prepare("INSERT INTO wecom_channels VALUES(?,?,?,?,?)").run(
|
|
12886
|
+
this.hub.db.prepare("INSERT INTO wecom_channels VALUES(?,?,?,?,?)").run(randomUUID20(), bot, topic, identity.workspaceId, (/* @__PURE__ */ new Date()).toISOString());
|
|
11898
12887
|
});
|
|
11899
12888
|
return this.list(identity);
|
|
11900
12889
|
}
|
|
@@ -11935,7 +12924,7 @@ var WecomChannels = class {
|
|
|
11935
12924
|
this.hub.db.prepare(
|
|
11936
12925
|
`INSERT OR IGNORE INTO wecom_deliveries(id,bot_id,external_id,project_id,kind,source_id,body,created_at,chat_type,channel_json) VALUES(?,?,?,?,?,?,'',?,3,?)`
|
|
11937
12926
|
).run(
|
|
11938
|
-
|
|
12927
|
+
randomUUID20(),
|
|
11939
12928
|
binding.bot_id,
|
|
11940
12929
|
binding.topic_id,
|
|
11941
12930
|
binding.project_id,
|
|
@@ -11980,19 +12969,21 @@ var WecomChannels = class {
|
|
|
11980
12969
|
hasPost = true;
|
|
11981
12970
|
};
|
|
11982
12971
|
const runs = this.hub.db.prepare(
|
|
11983
|
-
`SELECT r.*,a.member_id,w.run_id external_run,n.members_json FROM project_agent_runs r JOIN agent_run_actors a ON a.run_id=r.id LEFT JOIN agent_run_channels w ON w.run_id=r.id LEFT JOIN agent_run_notifications n ON n.run_id=r.id
|
|
12972
|
+
`SELECT r.*,a.member_id,w.run_id external_run,n.members_json,m.body visible_answer,p.body visible_input FROM project_agent_runs r JOIN agent_run_actors a ON a.run_id=r.id LEFT JOIN agent_run_channels w ON w.run_id=r.id LEFT JOIN agent_run_notifications n ON n.run_id=r.id
|
|
12973
|
+
LEFT JOIN agent_messages m ON m.run_id=r.id AND m.role='assistant'
|
|
12974
|
+
LEFT JOIN agent_messages p ON p.run_id=r.id AND p.role='user' WHERE r.conversation_id=? ORDER BY r.rowid`
|
|
11984
12975
|
).all(conversationId);
|
|
11985
12976
|
for (const [runIndex, run] of runs.entries()) {
|
|
11986
12977
|
this.hub.db.prepare("INSERT OR IGNORE INTO wecom_matter_runs(run_id) VALUES(?)").run(run.id);
|
|
11987
12978
|
const synced = this.hub.db.prepare("SELECT * FROM wecom_matter_runs WHERE run_id=?").get(run.id);
|
|
11988
12979
|
if (!synced.input_collected && hasPost) {
|
|
11989
|
-
if (run.member_id && !run.external_run)
|
|
12980
|
+
if (run.member_id && run.visible_input && !run.external_run)
|
|
11990
12981
|
this.queue(binding, `web:${run.id}`, {
|
|
11991
12982
|
conversationId,
|
|
11992
12983
|
action: "reply",
|
|
11993
12984
|
text: `**${run.member_id}**
|
|
11994
12985
|
|
|
11995
|
-
${run.
|
|
12986
|
+
${run.visible_input}`,
|
|
11996
12987
|
members: []
|
|
11997
12988
|
});
|
|
11998
12989
|
this.hub.db.prepare("UPDATE wecom_matter_runs SET input_collected=1 WHERE run_id=?").run(run.id);
|
|
@@ -12015,9 +13006,13 @@ ${run.text}`,
|
|
|
12015
13006
|
}
|
|
12016
13007
|
break;
|
|
12017
13008
|
}
|
|
12018
|
-
|
|
13009
|
+
if (run.state === "succeeded" && !run.visible_answer) {
|
|
13010
|
+
this.hub.db.prepare("UPDATE wecom_matter_runs SET answer_collected=1 WHERE run_id=?").run(run.id);
|
|
13011
|
+
continue;
|
|
13012
|
+
}
|
|
13013
|
+
const body = run.state === "failed" ? `\u672C\u6B21\u5904\u7406\u672A\u5B8C\u6210\uFF0C\u53EF\u4EE5\u5728\u672C\u5E16\u7EE7\u7EED\u56DE\u590D\u6216\u5230\u7F51\u9875\u67E5\u770B\u3002${run.visible_answer ? `
|
|
12019
13014
|
|
|
12020
|
-
${run.
|
|
13015
|
+
${run.visible_answer}` : ""}` : String(run.visible_answer);
|
|
12021
13016
|
const members = run.members_json ? JSON.parse(String(run.members_json)) : [];
|
|
12022
13017
|
const initialMembers = runIndex === 0 ? new Matters(this.hub).members(conversationId) : [];
|
|
12023
13018
|
const recipients = [.../* @__PURE__ */ new Set([...initialMembers, ...members])];
|
|
@@ -12057,13 +13052,13 @@ ${run.answer}` : ""}` : String(run.answer || "\u672C\u8F6E\u5904\u7406\u5DF2\u5B
|
|
|
12057
13052
|
};
|
|
12058
13053
|
|
|
12059
13054
|
// apps/server/wecom/runtime.ts
|
|
12060
|
-
import { randomUUID as
|
|
13055
|
+
import { randomUUID as randomUUID23 } from "node:crypto";
|
|
12061
13056
|
import "@wecom/aibot-node-sdk";
|
|
12062
13057
|
|
|
12063
13058
|
// apps/server/wecom/store.ts
|
|
12064
|
-
import { createHash as
|
|
13059
|
+
import { createHash as createHash20, randomBytes as randomBytes8, randomUUID as randomUUID21 } from "node:crypto";
|
|
12065
13060
|
import { z as z49 } from "zod";
|
|
12066
|
-
var hash2 = (text) =>
|
|
13061
|
+
var hash2 = (text) => createHash20("sha256").update(text).digest("hex");
|
|
12067
13062
|
var referencePattern = /\bTM-([A-F0-9]{12})\b/i;
|
|
12068
13063
|
var incomingSchema = z49.object({
|
|
12069
13064
|
msgid: z49.string().min(1),
|
|
@@ -12076,7 +13071,7 @@ var incomingSchema = z49.object({
|
|
|
12076
13071
|
quote: z49.object({ text: z49.object({ content: z49.string() }).optional() }).passthrough().optional(),
|
|
12077
13072
|
compat_info: z49.unknown().optional()
|
|
12078
13073
|
}).passthrough();
|
|
12079
|
-
var replyHint = "\
|
|
13074
|
+
var replyHint = "\u53EF\u5F15\u7528\u672C\u6761\u6D88\u606F\u7EE7\u7EED\u56DE\u590D\u3002";
|
|
12080
13075
|
var WecomStore = class {
|
|
12081
13076
|
constructor(hub) {
|
|
12082
13077
|
this.hub = hub;
|
|
@@ -12156,7 +13151,7 @@ var WecomStore = class {
|
|
|
12156
13151
|
`INSERT OR IGNORE INTO wecom_deliveries
|
|
12157
13152
|
(id,bot_id,external_id,user_id,project_id,kind,source_id,reference,body,created_at) VALUES(?,?,?,?,?,?,?,?,?,?)`
|
|
12158
13153
|
).run(
|
|
12159
|
-
|
|
13154
|
+
randomUUID21(),
|
|
12160
13155
|
bot,
|
|
12161
13156
|
external,
|
|
12162
13157
|
identity?.memberId ?? null,
|
|
@@ -12274,8 +13269,19 @@ ${projects.map((p) => `${this.projectName(String(p.project_id))}\uFF08${p.projec
|
|
|
12274
13269
|
notice("\u6B64\u9879\u76EE\u5DF2\u89E3\u9664\u4F01\u4E1A\u5FAE\u4FE1\u8FDE\u63A5\uFF0C\u8BF7\u5728\u7F51\u9875\u56DE\u590D\u539F\u6D88\u606F\u3002");
|
|
12275
13270
|
return;
|
|
12276
13271
|
}
|
|
12277
|
-
this.agents.reply(
|
|
12278
|
-
|
|
13272
|
+
const run2 = this.agents.reply(
|
|
13273
|
+
identity2,
|
|
13274
|
+
String(delivery.source_id),
|
|
13275
|
+
text,
|
|
13276
|
+
`wecom:${bot}:${msg.msgid}`
|
|
13277
|
+
);
|
|
13278
|
+
const target = this.hub.db.prepare("SELECT conversation_id FROM project_agent_runs WHERE id=?").get(run2.id);
|
|
13279
|
+
if (target && this.hub.db.prepare("SELECT 1 FROM wecom_matter_threads WHERE conversation_id=?").get(target.conversation_id))
|
|
13280
|
+
notice(
|
|
13281
|
+
`\u8FD9\u6761\u6D88\u606F\u5DF2\u8F6C\u5165\u4E8B\u9879\uFF0C\u8BF7\u5728\u4E8B\u9879\u4E2D\u7EE7\u7EED\u8BA8\u8BBA\uFF1A${this.url(identity2.workspaceId, `chat=${target.conversation_id}`)}`
|
|
13282
|
+
);
|
|
13283
|
+
else
|
|
13284
|
+
this.hub.db.prepare("INSERT OR IGNORE INTO wecom_requests VALUES(?,?,?,?,?)").run(run2.id, bot, msg.from.userid, user, identity2.workspaceId);
|
|
12279
13285
|
return;
|
|
12280
13286
|
}
|
|
12281
13287
|
const identity = { memberId: user, workspaceId: String(binding.active_project) };
|
|
@@ -12304,7 +13310,7 @@ ${projects.map((p) => `${this.projectName(String(p.project_id))}\uFF08${p.projec
|
|
|
12304
13310
|
${msg.quote.text.content}
|
|
12305
13311
|
|
|
12306
13312
|
${text}` : text;
|
|
12307
|
-
const run = this.agents.ask(identity, question, chat);
|
|
13313
|
+
const run = this.agents.ask(identity, question, chat, `wecom:${bot}:${msg.msgid}`);
|
|
12308
13314
|
this.hub.db.prepare("INSERT INTO wecom_requests VALUES(?,?,?,?,?)").run(run.id, bot, msg.from.userid, user, identity.workspaceId);
|
|
12309
13315
|
} catch (error) {
|
|
12310
13316
|
if (!(error instanceof HubError)) throw error;
|
|
@@ -12328,7 +13334,7 @@ ${text}` : text;
|
|
|
12328
13334
|
shortened += char;
|
|
12329
13335
|
}
|
|
12330
13336
|
if (shortened !== body) shortened += "\n\n\u2026\u2026\u5B8C\u6574\u5185\u5BB9\u8BF7\u5728\u7F51\u9875\u67E5\u770B\u3002";
|
|
12331
|
-
return `**${this.projectName(project)} \xB7
|
|
13337
|
+
return `**${this.projectName(project)} \xB7 Tim${reference3 ? " \xB7 " + reference3 : ""}**
|
|
12332
13338
|
|
|
12333
13339
|
${shortened}
|
|
12334
13340
|
|
|
@@ -12375,13 +13381,14 @@ ${shortened}
|
|
|
12375
13381
|
);
|
|
12376
13382
|
}
|
|
12377
13383
|
const answers = this.hub.db.prepare(
|
|
12378
|
-
`SELECT r.*,w.external_id,w.user_id,w.project_id FROM wecom_requests w
|
|
12379
|
-
JOIN project_agent_runs r ON r.id=w.run_id
|
|
13384
|
+
`SELECT r.*,m.body visible_answer,w.external_id,w.user_id,w.project_id FROM wecom_requests w
|
|
13385
|
+
JOIN project_agent_runs r ON r.id=w.run_id JOIN agent_messages m ON m.run_id=r.id AND m.role='assistant'
|
|
13386
|
+
WHERE w.bot_id=? AND r.state IN ('succeeded','failed') AND (m.body<>'' OR r.state='failed')
|
|
12380
13387
|
AND NOT EXISTS(SELECT 1 FROM wecom_deliveries d WHERE d.bot_id=w.bot_id AND d.external_id=w.external_id AND d.kind='answer' AND d.source_id=r.id)`
|
|
12381
13388
|
).all(bot);
|
|
12382
13389
|
for (const row of answers) {
|
|
12383
13390
|
const body = String(
|
|
12384
|
-
row.
|
|
13391
|
+
row.visible_answer || (row.state === "failed" ? "\u672C\u6B21\u5904\u7406\u672A\u5B8C\u6210\uFF0C\u8BF7\u5728\u7F51\u9875\u67E5\u770B\u8FD0\u884C\u60C5\u51B5\u3002" : "\u672C\u8F6E\u5904\u7406\u5DF2\u5B8C\u6210\u3002")
|
|
12385
13392
|
);
|
|
12386
13393
|
this.queue(
|
|
12387
13394
|
bot,
|
|
@@ -12418,7 +13425,7 @@ ${shortened}
|
|
|
12418
13425
|
|
|
12419
13426
|
// apps/server/wecom/transport.ts
|
|
12420
13427
|
import { EventEmitter } from "node:events";
|
|
12421
|
-
import { randomUUID as
|
|
13428
|
+
import { randomUUID as randomUUID22 } from "node:crypto";
|
|
12422
13429
|
import WebSocket from "ws";
|
|
12423
13430
|
var WecomTransport = class extends EventEmitter {
|
|
12424
13431
|
constructor(options, open4 = () => new WebSocket("wss://openws.work.weixin.qq.com", { handshakeTimeout: 15e3 })) {
|
|
@@ -12448,7 +13455,7 @@ var WecomTransport = class extends EventEmitter {
|
|
|
12448
13455
|
socket.addEventListener("open", () => {
|
|
12449
13456
|
if (this.socket !== socket || this.stopped) return;
|
|
12450
13457
|
void this.reply(
|
|
12451
|
-
{ headers: { req_id:
|
|
13458
|
+
{ headers: { req_id: randomUUID22() } },
|
|
12452
13459
|
{ bot_id: this.options.botId, secret: this.options.secret },
|
|
12453
13460
|
"aibot_subscribe"
|
|
12454
13461
|
).then((frame) => {
|
|
@@ -12457,7 +13464,7 @@ var WecomTransport = class extends EventEmitter {
|
|
|
12457
13464
|
this.attempts = 0;
|
|
12458
13465
|
this.emit("authenticated");
|
|
12459
13466
|
this.heartbeat = setInterval(() => {
|
|
12460
|
-
void this.reply({ headers: { req_id:
|
|
13467
|
+
void this.reply({ headers: { req_id: randomUUID22() } }, {}, "ping").then((result) => {
|
|
12461
13468
|
if (result.errcode !== 0) socket.close();
|
|
12462
13469
|
}).catch(() => socket.close());
|
|
12463
13470
|
}, 25e3);
|
|
@@ -12556,7 +13563,7 @@ var WecomTransport = class extends EventEmitter {
|
|
|
12556
13563
|
};
|
|
12557
13564
|
|
|
12558
13565
|
// apps/server/wecom/channel-runtime.ts
|
|
12559
|
-
import { createHash as
|
|
13566
|
+
import { createHash as createHash21 } from "node:crypto";
|
|
12560
13567
|
var channelRetryDelay = (attempts) => Math.round(
|
|
12561
13568
|
Math.min(30 * 6e4, 5e3 * 2 ** Math.min(attempts, 9)) * (0.75 + Math.random() * 0.5)
|
|
12562
13569
|
);
|
|
@@ -12650,7 +13657,7 @@ var WecomChannelRuntime = class {
|
|
|
12650
13657
|
text(bot, project, item) {
|
|
12651
13658
|
let text = item.content ?? "";
|
|
12652
13659
|
for (const mention of item.inline_mention_list ?? []) {
|
|
12653
|
-
const name = mention.member.type === 1 ? "
|
|
13660
|
+
const name = mention.member.type === 1 ? "Tim" : this.store.speaker(bot, project, mention.member.userid ?? "");
|
|
12654
13661
|
text = text.split("${inline_mention:" + mention.placeholder + "}").join(mention.member.type === 1 ? "" : `@${name}`);
|
|
12655
13662
|
}
|
|
12656
13663
|
for (const file of item.inline_file_list ?? [])
|
|
@@ -12732,6 +13739,22 @@ var WecomChannelRuntime = class {
|
|
|
12732
13739
|
).get(bot, topic);
|
|
12733
13740
|
if (!binding) return;
|
|
12734
13741
|
const project = String(binding.project_id);
|
|
13742
|
+
if (this.hub.db.prepare("SELECT 1 FROM reset_channel_posts WHERE bot_id=? AND topic_id=? AND post_id=?").get(bot, topic, post)) {
|
|
13743
|
+
if (type === "topic_at_bot")
|
|
13744
|
+
this.store.queue(
|
|
13745
|
+
{ id: String(binding.id), bot_id: bot, topic_id: topic, project_id: project },
|
|
13746
|
+
`retired:${post}`,
|
|
13747
|
+
{
|
|
13748
|
+
conversationId: "",
|
|
13749
|
+
action: "reply",
|
|
13750
|
+
postId: post,
|
|
13751
|
+
text: "\u8FD9\u6761\u6D4B\u8BD5\u4E8B\u9879\u5DF2\u6E05\u7406\u3002\u8BF7\u65B0\u5EFA\u5E16\u5B50\u5E76 @Tim \u5F00\u59CB\u8BA8\u8BBA\u3002",
|
|
13752
|
+
members: []
|
|
13753
|
+
},
|
|
13754
|
+
"channel_retired"
|
|
13755
|
+
);
|
|
13756
|
+
return;
|
|
13757
|
+
}
|
|
12735
13758
|
const thread = this.hub.db.prepare(
|
|
12736
13759
|
"SELECT * FROM wecom_matter_threads WHERE bot_id=? AND topic_id=? AND post_id=? AND binding_id=?"
|
|
12737
13760
|
).get(bot, topic, post, binding.id);
|
|
@@ -12754,7 +13777,7 @@ var WecomChannelRuntime = class {
|
|
|
12754
13777
|
const external = creator?.userid || event.from.userid;
|
|
12755
13778
|
const text = recalled ? "\u64A4\u56DE\u4E86\u6B64\u524D\u7684\u4E00\u6761\u53D1\u8A00\u3002\u8BF7\u636E\u6B64\u66F4\u65B0\u7406\u89E3\uFF1B\u5DF2\u6267\u884C\u7684\u64CD\u4F5C\u4E0D\u4F1A\u81EA\u52A8\u64A4\u9500\u3002" : this.text(bot, project, item);
|
|
12756
13779
|
if (!text) return;
|
|
12757
|
-
const hash3 =
|
|
13780
|
+
const hash3 = createHash21("sha256").update(JSON.stringify([recalled, text])).digest("hex");
|
|
12758
13781
|
const modified = Number(item?.modify_time || item?.create_time || event.create_time || 0);
|
|
12759
13782
|
const existing = this.hub.db.prepare(
|
|
12760
13783
|
"SELECT * FROM wecom_channel_messages WHERE bot_id=? AND topic_id=? AND post_id=? AND item_id=?"
|
|
@@ -12787,12 +13810,12 @@ var WecomChannelRuntime = class {
|
|
|
12787
13810
|
if (command) {
|
|
12788
13811
|
this.store.queue(
|
|
12789
13812
|
{ id: String(binding.id), bot_id: bot, topic_id: topic, project_id: project },
|
|
12790
|
-
`command:${
|
|
13813
|
+
`command:${createHash21("sha256").update(JSON.stringify([topic, post, itemId, hash3])).digest("hex")}`,
|
|
12791
13814
|
{
|
|
12792
13815
|
conversationId: conversation,
|
|
12793
13816
|
action: "reply",
|
|
12794
13817
|
postId: post,
|
|
12795
|
-
text: "\u9891\u9053\u4E2D\u6BCF\u4E2A\u5E16\u5B50\u72EC\u7ACB\u8BA8\u8BBA\u3002\u65B0\u95EE\u9898\u8BF7\u65B0\u5EFA\u5E16\u5B50\u5E76
|
|
13818
|
+
text: "\u9891\u9053\u4E2D\u6BCF\u4E2A\u5E16\u5B50\u72EC\u7ACB\u8BA8\u8BBA\u3002\u65B0\u95EE\u9898\u8BF7\u65B0\u5EFA\u5E16\u5B50\u5E76 @Tim\uFF1B\u5DF2\u63A5\u5165\u7684\u4E8B\u9879\u53EF\u76F4\u63A5\u5728\u5E16\u4E0B\u56DE\u590D\u3002/help \u67E5\u770B\u8BF4\u660E\u3002/bind\u3001/projects\u3001/use \u7528\u4E8E\u79C1\u804A\u6216\u7FA4\u804A\uFF0C\u9891\u9053\u8FDE\u63A5\u8BF7\u5728\u9879\u76EE\u8BBE\u7F6E\u4E2D\u7BA1\u7406\u3002",
|
|
12796
13819
|
members: []
|
|
12797
13820
|
}
|
|
12798
13821
|
);
|
|
@@ -12818,7 +13841,7 @@ ${this.text(bot, project, value)}`;
|
|
|
12818
13841
|
conversation,
|
|
12819
13842
|
input,
|
|
12820
13843
|
member,
|
|
12821
|
-
{},
|
|
13844
|
+
{ routing: { inputKey: `channel:${bot}:${topic}:${post}:${itemId}:${hash3}` } },
|
|
12822
13845
|
{ bindingId: String(binding.id), externalId: external, speaker }
|
|
12823
13846
|
);
|
|
12824
13847
|
const save = (id, contentHash2, at) => this.hub.db.prepare(
|
|
@@ -12828,7 +13851,7 @@ ${this.text(bot, project, value)}`;
|
|
|
12828
13851
|
const body = this.text(bot, project, value);
|
|
12829
13852
|
save(
|
|
12830
13853
|
String(value.reply_id || value.post_id),
|
|
12831
|
-
|
|
13854
|
+
createHash21("sha256").update(JSON.stringify([!!value.is_recalled, body])).digest("hex"),
|
|
12832
13855
|
Number(value.modify_time || value.create_time || 0)
|
|
12833
13856
|
);
|
|
12834
13857
|
}
|
|
@@ -12934,7 +13957,7 @@ var WecomRuntime = class {
|
|
|
12934
13957
|
}
|
|
12935
13958
|
hub;
|
|
12936
13959
|
factory;
|
|
12937
|
-
owner =
|
|
13960
|
+
owner = randomUUID23();
|
|
12938
13961
|
store;
|
|
12939
13962
|
client;
|
|
12940
13963
|
revision = 0;
|
|
@@ -13001,7 +14024,7 @@ var WecomRuntime = class {
|
|
|
13001
14024
|
this.hub,
|
|
13002
14025
|
async (cmd, body) => {
|
|
13003
14026
|
if (!channelActive() || !this.own()) throw new Error("\u4F01\u4E1A\u5FAE\u4FE1\u8FDE\u63A5\u5DF2\u53D8\u66F4\u3002");
|
|
13004
|
-
const response = await client.reply({ headers: { req_id:
|
|
14027
|
+
const response = await client.reply({ headers: { req_id: randomUUID23() } }, body, cmd);
|
|
13005
14028
|
if (!channelActive()) throw new Error("\u4F01\u4E1A\u5FAE\u4FE1\u8FDE\u63A5\u5DF2\u53D8\u66F4\u3002");
|
|
13006
14029
|
return response;
|
|
13007
14030
|
},
|
|
@@ -13214,19 +14237,28 @@ function buildServer(options) {
|
|
|
13214
14237
|
]
|
|
13215
14238
|
} : false,
|
|
13216
14239
|
requestIdHeader: false,
|
|
13217
|
-
genReqId: () =>
|
|
14240
|
+
genReqId: () => randomUUID24(),
|
|
13218
14241
|
bodyLimit: 1024 * 1024,
|
|
13219
14242
|
requestTimeout: BLOB_TRANSFER_TIMEOUT_MS
|
|
13220
14243
|
});
|
|
13221
14244
|
server.decorate("teammyDeployment", deployment);
|
|
13222
14245
|
if (store) {
|
|
14246
|
+
const stopApi = new Maintenance(store).apiLease();
|
|
13223
14247
|
const stopLogs = maintainDiagnostics(store);
|
|
13224
14248
|
server.addHook("onClose", async () => {
|
|
13225
14249
|
stopLogs();
|
|
14250
|
+
stopApi();
|
|
13226
14251
|
store.close();
|
|
13227
14252
|
});
|
|
13228
14253
|
}
|
|
13229
14254
|
server.addHook("onRequest", async (request, reply) => {
|
|
14255
|
+
if (store) {
|
|
14256
|
+
new Maintenance(store).assertReady();
|
|
14257
|
+
reply.header(
|
|
14258
|
+
"x-teammy-data-generation",
|
|
14259
|
+
String(new Maintenance(store).baseline().generation)
|
|
14260
|
+
);
|
|
14261
|
+
}
|
|
13230
14262
|
reply.header("x-request-id", request.id);
|
|
13231
14263
|
if (request.url.startsWith("/api/")) reply.header("cache-control", "no-store");
|
|
13232
14264
|
});
|
|
@@ -13238,7 +14270,7 @@ function buildServer(options) {
|
|
|
13238
14270
|
});
|
|
13239
14271
|
server.setErrorHandler((error, request, reply) => {
|
|
13240
14272
|
const candidate = error.statusCode;
|
|
13241
|
-
const status = error instanceof ZodError ? 400 : candidate && candidate >= 400 && candidate < 500 ? candidate : 500;
|
|
14273
|
+
const status = error instanceof HubError ? error.statusCode : error instanceof ZodError ? 400 : candidate && candidate >= 400 && candidate < 500 ? candidate : 500;
|
|
13242
14274
|
if (status === 500)
|
|
13243
14275
|
request.log.error(
|
|
13244
14276
|
{ code: "REQUEST_FAILED", requestId: request.id },
|
|
@@ -13288,6 +14320,7 @@ function buildServer(options) {
|
|
|
13288
14320
|
"atomic-file-publications",
|
|
13289
14321
|
"project-files",
|
|
13290
14322
|
"session-revisions",
|
|
14323
|
+
"session-baselines",
|
|
13291
14324
|
"session-search",
|
|
13292
14325
|
"session-analysis",
|
|
13293
14326
|
"session-history-search",
|
|
@@ -13337,12 +14370,13 @@ function buildServer(options) {
|
|
|
13337
14370
|
|
|
13338
14371
|
// apps/server/main.ts
|
|
13339
14372
|
import { homedir } from "node:os";
|
|
13340
|
-
import { join as
|
|
14373
|
+
import { join as join22, resolve as resolve4 } from "node:path";
|
|
13341
14374
|
import { spawn as spawn4 } from "node:child_process";
|
|
13342
14375
|
var help = `teammy-server \u2014 the Teammy collaboration hub
|
|
13343
14376
|
|
|
13344
14377
|
Usage: teammy-server [--host HOST] [--port PORT]
|
|
13345
14378
|
teammy-server ai-worker [--data-dir DIR] [--once]
|
|
14379
|
+
teammy-server reset-test-data --confirm [--data-dir DIR]
|
|
13346
14380
|
teammy-server member <add|reset|revoke> --workspace ID --member ID [--data-dir DIR]
|
|
13347
14381
|
|
|
13348
14382
|
Options:
|
|
@@ -13366,16 +14400,42 @@ Endpoints:
|
|
|
13366
14400
|
Open the server URL to initialize the Web dashboard and preset member IDs.
|
|
13367
14401
|
File synchronization, the conversation library (\u5BF9\u8BDD\u5E93) and automatic AI analysis are available.
|
|
13368
14402
|
AI tasks require --with-worker or a separate ai-worker using the same data directory.
|
|
14403
|
+
reset-test-data removes test tasks, chats, materials and agent memory while retaining
|
|
14404
|
+
accounts, connections and workspace sync data. Stop API and Worker before running it.
|
|
13369
14405
|
`;
|
|
13370
14406
|
async function runServerCli(args, version2) {
|
|
13371
14407
|
try {
|
|
14408
|
+
if (args[0] === "reset-test-data") {
|
|
14409
|
+
const { values: values2 } = parseArgs({
|
|
14410
|
+
args: args.slice(1),
|
|
14411
|
+
options: {
|
|
14412
|
+
confirm: { type: "boolean" },
|
|
14413
|
+
"data-dir": {
|
|
14414
|
+
type: "string",
|
|
14415
|
+
default: process.env.TEAMMY_DATA_DIR ?? join22(homedir(), ".teammy", "server")
|
|
14416
|
+
}
|
|
14417
|
+
}
|
|
14418
|
+
});
|
|
14419
|
+
if (!values2.confirm)
|
|
14420
|
+
throw new Error(
|
|
14421
|
+
"This removes all test business data. Stop API/Worker, then pass --confirm to proceed."
|
|
14422
|
+
);
|
|
14423
|
+
const hub = new HubStore(resolve4(values2["data-dir"]));
|
|
14424
|
+
try {
|
|
14425
|
+
process.stdout.write(`${JSON.stringify(await new Maintenance(hub).resetTestData())}
|
|
14426
|
+
`);
|
|
14427
|
+
} finally {
|
|
14428
|
+
hub.close();
|
|
14429
|
+
}
|
|
14430
|
+
return 0;
|
|
14431
|
+
}
|
|
13372
14432
|
if (args[0] === "ai-worker") {
|
|
13373
14433
|
const { values: values2 } = parseArgs({
|
|
13374
14434
|
args: args.slice(1),
|
|
13375
14435
|
options: {
|
|
13376
14436
|
"data-dir": {
|
|
13377
14437
|
type: "string",
|
|
13378
|
-
default: process.env.TEAMMY_DATA_DIR ??
|
|
14438
|
+
default: process.env.TEAMMY_DATA_DIR ?? join22(homedir(), ".teammy", "server")
|
|
13379
14439
|
},
|
|
13380
14440
|
once: { type: "boolean" }
|
|
13381
14441
|
}
|
|
@@ -13394,7 +14454,7 @@ async function runServerCli(args, version2) {
|
|
|
13394
14454
|
member: { type: "string" },
|
|
13395
14455
|
"data-dir": {
|
|
13396
14456
|
type: "string",
|
|
13397
|
-
default: process.env.TEAMMY_DATA_DIR ??
|
|
14457
|
+
default: process.env.TEAMMY_DATA_DIR ?? join22(homedir(), ".teammy", "server")
|
|
13398
14458
|
}
|
|
13399
14459
|
}
|
|
13400
14460
|
});
|
|
@@ -13422,7 +14482,7 @@ async function runServerCli(args, version2) {
|
|
|
13422
14482
|
port: { type: "string", default: process.env.TEAMMY_PORT ?? "4317" },
|
|
13423
14483
|
"data-dir": {
|
|
13424
14484
|
type: "string",
|
|
13425
|
-
default: process.env.TEAMMY_DATA_DIR ??
|
|
14485
|
+
default: process.env.TEAMMY_DATA_DIR ?? join22(homedir(), ".teammy", "server")
|
|
13426
14486
|
},
|
|
13427
14487
|
"with-worker": { type: "boolean", default: false }
|
|
13428
14488
|
}
|