oasis_test_v2 2.2.1 → 2.2.2
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/dist/index.js +664 -195
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -11107,13 +11107,22 @@ var init_store_postgres = __esm({
|
|
|
11107
11107
|
async close() {
|
|
11108
11108
|
await this.pool.end();
|
|
11109
11109
|
}
|
|
11110
|
+
/**
|
|
11111
|
+
* ★ 与 {@link tx} 同一条纪律:schema 名进的是**标识符位**,必须 {@link quoteIdent}。
|
|
11112
|
+
*
|
|
11113
|
+
* 这两个直连 pool 的方法(本方法与 {@link listNeverStartedWorks})是上一轮修连字符 schema 时
|
|
11114
|
+
* 漏掉的两处——`tx()` 那条路已经引号化了,它们没有。表现不是「一读就 500」而是**两口钟在带
|
|
11115
|
+
* 连字符的公司上永远转不动**:2026-09-09 v2 生产日志里 `[deadline-clock] [company:test-ryzs]`
|
|
11116
|
+
* 与 `[dispatch-drop]` 每 5 秒 / 每分钟各报一次 `syntax error at or near "-"`,
|
|
11117
|
+
* 一分钟约 50 行;也就是说这些公司的 deadline 最迟触发保证与丢单补派**从来没生效过**。
|
|
11118
|
+
*/
|
|
11110
11119
|
async listOpenWorkDeadlines(opts) {
|
|
11111
11120
|
const res = await this.pool.query(
|
|
11112
11121
|
`SELECT workorder_id AS "workorderId",
|
|
11113
11122
|
id AS "workId",
|
|
11114
11123
|
assignee_actor_id AS "assigneeActorId",
|
|
11115
11124
|
COALESCE(last_activity_at, started_at, created_at) AS "deadlineFrom"
|
|
11116
|
-
FROM ${this.schema}.works
|
|
11125
|
+
FROM ${quoteIdent(this.schema)}.works
|
|
11117
11126
|
WHERE ended_at IS NULL AND dead_at IS NULL AND cancelled_at IS NULL
|
|
11118
11127
|
AND ($1::text IS NULL OR id > $1)
|
|
11119
11128
|
ORDER BY id
|
|
@@ -11134,7 +11143,7 @@ var init_store_postgres = __esm({
|
|
|
11134
11143
|
node_id AS "nodeId",
|
|
11135
11144
|
assignee_actor_id AS "assigneeActorId",
|
|
11136
11145
|
created_at AS "createdAt"
|
|
11137
|
-
FROM ${this.schema}.works
|
|
11146
|
+
FROM ${quoteIdent(this.schema)}.works
|
|
11138
11147
|
WHERE ended_at IS NULL AND dead_at IS NULL AND cancelled_at IS NULL
|
|
11139
11148
|
AND started_at IS NULL AND session_ref IS NULL
|
|
11140
11149
|
AND created_at < $1::timestamptz
|
|
@@ -11865,6 +11874,7 @@ function markNodeForRetry(state, nodeId, at) {
|
|
|
11865
11874
|
state.updateWork(lw.id, { retryAt: at, status: "retry" });
|
|
11866
11875
|
}
|
|
11867
11876
|
if (lw && !lw.deadAt && workState(lw, hasOutput(lw, state.artifactsOf(lw.id).length)) === "success") {
|
|
11877
|
+
if (lw.acceptanceState === "rejected") state.updateWork(lw.id, { retryAt: at });
|
|
11868
11878
|
for (const req of state.requirementsOf(node2.id)) {
|
|
11869
11879
|
if (!req.latestReviewId) continue;
|
|
11870
11880
|
const r = state.review(req.latestReviewId);
|
|
@@ -28028,6 +28038,15 @@ var init_chat_item_ledger = __esm({
|
|
|
28028
28038
|
this.messageId = deps.messageId ?? null;
|
|
28029
28039
|
this.telemetryTextInContent = deps.telemetryTextInContent === true;
|
|
28030
28040
|
this.persistEnabled = Boolean(deps.items) && chatItemsDoubleWriteEnabled();
|
|
28041
|
+
this.clock = Math.max(0, deps.versionSeed ?? 0);
|
|
28042
|
+
if (deps.versionSeed === void 0 && deps.items) {
|
|
28043
|
+
void deps.items.sessionVersionCursor(deps.sessionId).then(
|
|
28044
|
+
(seed) => {
|
|
28045
|
+
if (seed > this.clock) this.clock = seed;
|
|
28046
|
+
},
|
|
28047
|
+
() => void 0
|
|
28048
|
+
);
|
|
28049
|
+
}
|
|
28031
28050
|
}
|
|
28032
28051
|
/** 账本轮次 id(`chat_session_turns.id`;未接账本时是 {@link fallbackTurnId})。v3 wire 上 item 帧的 turnId 用它(ADR-0510 D1)。 */
|
|
28033
28052
|
get turnId() {
|
|
@@ -28043,6 +28062,11 @@ var init_chat_item_ledger = __esm({
|
|
|
28043
28062
|
/** 内存账本:itemId → 行。插入顺序即 seq 顺序。 */
|
|
28044
28063
|
rows = /* @__PURE__ */ new Map();
|
|
28045
28064
|
seqCounter = 0;
|
|
28065
|
+
/**
|
|
28066
|
+
* 会话逻辑时钟(ADR-0510 附录 A)。**一次写走一格**——不描述某条 item 改了第几次,描述
|
|
28067
|
+
* 「这条会话被写了第几次」。于是「一次建行要发两帧」这个死结不存在:想发几帧就取几个号。
|
|
28068
|
+
*/
|
|
28069
|
+
clock;
|
|
28046
28070
|
segment = 0;
|
|
28047
28071
|
messageId;
|
|
28048
28072
|
/** 已排队但还没执行的那一次 `append_text`——排队期间到的片段合并进它(见 queueAppend)。 */
|
|
@@ -28055,6 +28079,18 @@ var init_chat_item_ledger = __esm({
|
|
|
28055
28079
|
finished = false;
|
|
28056
28080
|
/** 上一次见到的归一层 turnId——变了就清映射(provider 开了新 turn)。 */
|
|
28057
28081
|
lastTurnId = null;
|
|
28082
|
+
/** 会话逻辑时钟走一格。**唯一发号点**(ADR-0510 附录 A.4)——别在别处自己数。 */
|
|
28083
|
+
tick() {
|
|
28084
|
+
return ++this.clock;
|
|
28085
|
+
}
|
|
28086
|
+
/**
|
|
28087
|
+
* 该 item 的服务端展示序;INSERT 还没返回(或没开双写)时是 undefined。
|
|
28088
|
+
* 调用方(`emitV3`)拿不到就不在帧上带号——**不许自己编一个**。
|
|
28089
|
+
*/
|
|
28090
|
+
ordFor(itemId) {
|
|
28091
|
+
const ord = this.rows.get(itemId)?.ord;
|
|
28092
|
+
return ord === null ? void 0 : ord;
|
|
28093
|
+
}
|
|
28058
28094
|
get failures() {
|
|
28059
28095
|
return this.failureCount;
|
|
28060
28096
|
}
|
|
@@ -28091,20 +28127,17 @@ var init_chat_item_ledger = __esm({
|
|
|
28091
28127
|
if (event.type === "turn") {
|
|
28092
28128
|
this.itemKeyMap.clear();
|
|
28093
28129
|
this.lastTurnId = null;
|
|
28094
|
-
return;
|
|
28130
|
+
return null;
|
|
28095
28131
|
}
|
|
28096
28132
|
if (this.lastTurnId !== null && this.lastTurnId !== event.turnId) {
|
|
28097
28133
|
this.itemKeyMap.clear();
|
|
28098
28134
|
}
|
|
28099
28135
|
this.lastTurnId = event.turnId;
|
|
28100
28136
|
const existingId = this.itemKeyMap.get(event.providerItemKey);
|
|
28101
|
-
if (existingId === void 0)
|
|
28102
|
-
this.createFrom(event);
|
|
28103
|
-
return;
|
|
28104
|
-
}
|
|
28137
|
+
if (existingId === void 0) return this.createFrom(event);
|
|
28105
28138
|
const row = this.rows.get(existingId);
|
|
28106
|
-
if (!row) return;
|
|
28107
|
-
this.applyOpTo(row, event);
|
|
28139
|
+
if (!row) return null;
|
|
28140
|
+
return this.applyOpTo(row, event);
|
|
28108
28141
|
}
|
|
28109
28142
|
// ── 非 provider 来源(服务端自己产的行)────────────────────────────────────
|
|
28110
28143
|
/**
|
|
@@ -28299,34 +28332,49 @@ var init_chat_item_ledger = __esm({
|
|
|
28299
28332
|
metadata: { [CHAT_ITEM_META_LEGACY_CONTENT]: inContent }
|
|
28300
28333
|
});
|
|
28301
28334
|
this.itemKeyMap.set(event.providerItemKey, id);
|
|
28302
|
-
|
|
28335
|
+
const row = this.rows.get(id);
|
|
28336
|
+
const outcome = {
|
|
28337
|
+
itemId: id,
|
|
28338
|
+
version: row.wireVersion,
|
|
28339
|
+
startedVersion: row.startedVersion,
|
|
28340
|
+
ord: row.ord
|
|
28341
|
+
};
|
|
28342
|
+
if (event.opShape.op === "set_status") {
|
|
28343
|
+
const followUp = this.applyOpTo(row, event);
|
|
28344
|
+
if (followUp) outcome.version = followUp.version;
|
|
28345
|
+
}
|
|
28346
|
+
return outcome;
|
|
28303
28347
|
}
|
|
28304
28348
|
applyOpTo(row, event) {
|
|
28305
28349
|
const op = event.opShape;
|
|
28306
28350
|
if (event.attrs) row.attrs = { ...row.attrs, ...event.attrs };
|
|
28307
28351
|
switch (op.op) {
|
|
28308
28352
|
case "append_text":
|
|
28309
|
-
if (op.text === "") return;
|
|
28353
|
+
if (op.text === "") return null;
|
|
28310
28354
|
row.text += op.text;
|
|
28311
28355
|
this.queueAppend(row, op.text);
|
|
28312
|
-
return;
|
|
28356
|
+
return this.outcomeOf(row);
|
|
28313
28357
|
case "set_text":
|
|
28314
|
-
if (row.text === op.text) return;
|
|
28358
|
+
if (row.text === op.text) return null;
|
|
28315
28359
|
row.text = op.text;
|
|
28316
28360
|
this.sealPendingAppend();
|
|
28317
28361
|
this.persist(row, row.kind === "text" || row.kind === "thinking" ? { op: "set_text", text: op.text } : { op: "set_payload", payload: this.payloadOf(row) }, "set_text");
|
|
28318
|
-
return;
|
|
28362
|
+
return this.outcomeOf(row);
|
|
28319
28363
|
case "patch_input":
|
|
28320
28364
|
row.attrs = { ...row.attrs, input: op.input };
|
|
28321
28365
|
this.persist(row, { op: "patch_input", input: op.input }, "patch_input");
|
|
28322
|
-
return;
|
|
28366
|
+
return this.outcomeOf(row);
|
|
28323
28367
|
case "set_status":
|
|
28324
|
-
if (row.status === op.status) return;
|
|
28368
|
+
if (row.status === op.status) return null;
|
|
28325
28369
|
row.status = op.status;
|
|
28326
28370
|
this.persist(row, { op: "set_status", status: op.status }, "set_status");
|
|
28327
|
-
return;
|
|
28371
|
+
return this.outcomeOf(row);
|
|
28328
28372
|
}
|
|
28329
28373
|
}
|
|
28374
|
+
/** 这一次写的回执。`persist` 已经在里面走过钟了,这里只是把读数带出去给 wire。 */
|
|
28375
|
+
outcomeOf(row) {
|
|
28376
|
+
return { itemId: row.id, version: row.wireVersion, ord: row.ord };
|
|
28377
|
+
}
|
|
28330
28378
|
/** 关掉所有还 streaming 的行(收尾/切段)。 */
|
|
28331
28379
|
closeStreamingRows(status) {
|
|
28332
28380
|
this.sealPendingAppend();
|
|
@@ -28361,9 +28409,14 @@ var init_chat_item_ledger = __esm({
|
|
|
28361
28409
|
}
|
|
28362
28410
|
insert(input) {
|
|
28363
28411
|
const id = input.deterministicId ?? this.newId();
|
|
28412
|
+
const startedVersion = this.tick();
|
|
28413
|
+
const writeVersion = this.tick();
|
|
28364
28414
|
const row = {
|
|
28365
28415
|
id,
|
|
28366
28416
|
seq: ++this.seqCounter,
|
|
28417
|
+
ord: null,
|
|
28418
|
+
wireVersion: writeVersion,
|
|
28419
|
+
startedVersion,
|
|
28367
28420
|
kind: input.kind,
|
|
28368
28421
|
role: input.role,
|
|
28369
28422
|
status: input.status,
|
|
@@ -28383,7 +28436,7 @@ var init_chat_item_ledger = __esm({
|
|
|
28383
28436
|
const payload = input.payload ?? this.payloadOf(row);
|
|
28384
28437
|
const metadata = input.metadata ?? { [CHAT_ITEM_META_LEGACY_CONTENT]: input.inContent };
|
|
28385
28438
|
this.enqueue(async () => {
|
|
28386
|
-
await this.deps.items.createItem({
|
|
28439
|
+
const created = await this.deps.items.createItem({
|
|
28387
28440
|
id,
|
|
28388
28441
|
sessionId: this.deps.sessionId,
|
|
28389
28442
|
messageId,
|
|
@@ -28395,8 +28448,10 @@ var init_chat_item_ledger = __esm({
|
|
|
28395
28448
|
payload,
|
|
28396
28449
|
metadata,
|
|
28397
28450
|
providerItemKey: input.providerItemKey ?? null,
|
|
28398
|
-
createdAt
|
|
28451
|
+
createdAt,
|
|
28452
|
+
version: writeVersion
|
|
28399
28453
|
});
|
|
28454
|
+
row.ord = created.ord;
|
|
28400
28455
|
}, `create:${input.kind}`);
|
|
28401
28456
|
return id;
|
|
28402
28457
|
}
|
|
@@ -28405,6 +28460,7 @@ var init_chat_item_ledger = __esm({
|
|
|
28405
28460
|
* 语义没变(`version` 数的是落库次数),投影是拼接,合不合并结果逐字相同。
|
|
28406
28461
|
*/
|
|
28407
28462
|
queueAppend(row, text5) {
|
|
28463
|
+
row.wireVersion = this.tick();
|
|
28408
28464
|
if (!row.persist || !this.persistEnabled) return;
|
|
28409
28465
|
const pending = this.pendingAppend;
|
|
28410
28466
|
if (pending && pending.id === row.id) {
|
|
@@ -28418,7 +28474,11 @@ var init_chat_item_ledger = __esm({
|
|
|
28418
28474
|
next.text = "";
|
|
28419
28475
|
if (this.pendingAppend === next) this.pendingAppend = null;
|
|
28420
28476
|
if (!merged) return;
|
|
28421
|
-
const applied = await this.deps.items.applyOp(
|
|
28477
|
+
const applied = await this.deps.items.applyOp(
|
|
28478
|
+
row.id,
|
|
28479
|
+
{ op: "append_text", text: merged },
|
|
28480
|
+
{ at: this.now(), version: row.wireVersion }
|
|
28481
|
+
);
|
|
28422
28482
|
if (!applied) throw new Error(`item ${row.id} \u4E0D\u5B58\u5728\uFF08append_text \u843D\u7A7A\uFF09`);
|
|
28423
28483
|
}, "append_text");
|
|
28424
28484
|
}
|
|
@@ -28427,9 +28487,11 @@ var init_chat_item_ledger = __esm({
|
|
|
28427
28487
|
this.pendingAppend = null;
|
|
28428
28488
|
}
|
|
28429
28489
|
persist(row, op, what) {
|
|
28490
|
+
const version2 = this.tick();
|
|
28491
|
+
row.wireVersion = version2;
|
|
28430
28492
|
if (!row.persist || !this.persistEnabled) return;
|
|
28431
28493
|
this.enqueue(async () => {
|
|
28432
|
-
await this.deps.items.applyOp(row.id, op, { at: this.now() });
|
|
28494
|
+
await this.deps.items.applyOp(row.id, op, { at: this.now(), version: version2 });
|
|
28433
28495
|
}, what);
|
|
28434
28496
|
}
|
|
28435
28497
|
/** 串行链 + 失败吞掉计数。**串行是必须的**:append_text 依赖建行已经落地。 */
|
|
@@ -159267,13 +159329,15 @@ var init_live_chat = __esm({
|
|
|
159267
159329
|
* 账本坏掉不影响对话;v3 emit 抛异常也不影响 v2 通路与账本落盘——两条独立通路。
|
|
159268
159330
|
*/
|
|
159269
159331
|
applyNormalizedEvent(turn, event) {
|
|
159332
|
+
let outcome = null;
|
|
159270
159333
|
try {
|
|
159271
|
-
turn.items?.apply(event);
|
|
159334
|
+
outcome = turn.items?.apply(event) ?? null;
|
|
159272
159335
|
} catch {
|
|
159273
159336
|
}
|
|
159274
159337
|
if (event.type === "turn") return;
|
|
159338
|
+
if (!outcome) return;
|
|
159275
159339
|
try {
|
|
159276
|
-
this.emitV3(turn, event);
|
|
159340
|
+
this.emitV3(turn, event, outcome);
|
|
159277
159341
|
} catch {
|
|
159278
159342
|
}
|
|
159279
159343
|
}
|
|
@@ -159293,18 +159357,17 @@ var init_live_chat = __esm({
|
|
|
159293
159357
|
wireTurnId(turn) {
|
|
159294
159358
|
return turn.items?.turnId ?? turn.liveTurnId;
|
|
159295
159359
|
}
|
|
159296
|
-
emitV3(turn, event) {
|
|
159360
|
+
emitV3(turn, event, outcome) {
|
|
159297
159361
|
if (!turn.items) return;
|
|
159298
|
-
const itemId =
|
|
159299
|
-
if (!itemId) return;
|
|
159362
|
+
const itemId = outcome.itemId;
|
|
159300
159363
|
const itemType = normalizedKindToItemType(event.kind);
|
|
159301
159364
|
const operation = normalizedOpToV3Op(event.opShape.op, event.kind);
|
|
159302
159365
|
if (!operation) return;
|
|
159303
|
-
const perItem = turn.v3Items.get(itemId) ?? {
|
|
159366
|
+
const perItem = turn.v3Items.get(itemId) ?? { textOffset: 0, started: false };
|
|
159304
159367
|
const isTextual2 = event.kind === "text" || event.kind === "thinking";
|
|
159305
159368
|
const opShape = event.opShape;
|
|
159306
|
-
|
|
159307
|
-
|
|
159369
|
+
const ord = turn.items.ordFor(itemId) ?? outcome.ord ?? void 0;
|
|
159370
|
+
if (!perItem.started && outcome.startedVersion !== void 0) {
|
|
159308
159371
|
const startedFrame = {
|
|
159309
159372
|
protocolVersion: 3,
|
|
159310
159373
|
streamId: turn.liveStreamId,
|
|
@@ -159312,7 +159375,8 @@ var init_live_chat = __esm({
|
|
|
159312
159375
|
itemId,
|
|
159313
159376
|
itemType,
|
|
159314
159377
|
operation: "started",
|
|
159315
|
-
itemVersion:
|
|
159378
|
+
itemVersion: outcome.startedVersion,
|
|
159379
|
+
...ord !== void 0 ? { ord } : {},
|
|
159316
159380
|
offset: 0,
|
|
159317
159381
|
payload: v3StartedPayload(event.role, event.attrs, itemType, event.kind)
|
|
159318
159382
|
};
|
|
@@ -159334,7 +159398,6 @@ var init_live_chat = __esm({
|
|
|
159334
159398
|
} else if (opShape.op === "set_status") {
|
|
159335
159399
|
if (opShape.status === "streaming") return;
|
|
159336
159400
|
}
|
|
159337
|
-
perItem.version += 1;
|
|
159338
159401
|
turn.v3Items.set(itemId, perItem);
|
|
159339
159402
|
const frame = {
|
|
159340
159403
|
protocolVersion: 3,
|
|
@@ -159343,7 +159406,9 @@ var init_live_chat = __esm({
|
|
|
159343
159406
|
itemId,
|
|
159344
159407
|
itemType,
|
|
159345
159408
|
operation,
|
|
159346
|
-
|
|
159409
|
+
// 账本这一次写用掉的那一格钟——**库里那一行的 version 就是它**,快照与增量流同一套版本空间。
|
|
159410
|
+
itemVersion: outcome.version,
|
|
159411
|
+
...ord !== void 0 ? { ord } : {},
|
|
159347
159412
|
...offset !== void 0 ? { offset } : {},
|
|
159348
159413
|
...payload !== void 0 ? { payload } : {}
|
|
159349
159414
|
};
|
|
@@ -159369,6 +159434,7 @@ var init_live_chat = __esm({
|
|
|
159369
159434
|
const payload = pending.frame.payload;
|
|
159370
159435
|
pending.frame.payload = { ...payload, text: String(payload.text ?? "") + text5 };
|
|
159371
159436
|
pending.frame.itemVersion = frame.itemVersion;
|
|
159437
|
+
if (frame.ord !== void 0) pending.frame.ord = frame.ord;
|
|
159372
159438
|
pending.chunks += 1;
|
|
159373
159439
|
pending.chars += text5.length;
|
|
159374
159440
|
return;
|
|
@@ -160175,7 +160241,7 @@ var init_workorder_drafts = __esm({
|
|
|
160175
160241
|
}
|
|
160176
160242
|
};
|
|
160177
160243
|
ident = (s2) => {
|
|
160178
|
-
if (!/^[
|
|
160244
|
+
if (!/^[A-Za-z0-9_][A-Za-z0-9_.-]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
|
|
160179
160245
|
return s2;
|
|
160180
160246
|
};
|
|
160181
160247
|
seqNoOf = (id, fallback) => {
|
|
@@ -160433,7 +160499,7 @@ var init_workorder_draft_planner_issues = __esm({
|
|
|
160433
160499
|
}
|
|
160434
160500
|
};
|
|
160435
160501
|
ident2 = (s2) => {
|
|
160436
|
-
if (!/^[
|
|
160502
|
+
if (!/^[A-Za-z0-9_][A-Za-z0-9_.-]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
|
|
160437
160503
|
return s2;
|
|
160438
160504
|
};
|
|
160439
160505
|
PostgresWorkorderDraftPlannerIssuesStore = class _PostgresWorkorderDraftPlannerIssuesStore extends MemoryWorkorderDraftPlannerIssuesStore {
|
|
@@ -201362,14 +201428,15 @@ async function runCommand(kernel, blobs, oplog, engineStore, actor, command, arg
|
|
|
201362
201428
|
}
|
|
201363
201429
|
const projectId2 = optStr(args, "project_id") ?? optStr(args, "projectId");
|
|
201364
201430
|
const schemaForSeed = (createdViaArg ?? "manual-spawn") === "seed" ? ctx.schema : void 0;
|
|
201431
|
+
const bindingState = ctx.workspaceBindingState ?? ctx.artifactState;
|
|
201365
201432
|
if (projectId2 !== void 0) {
|
|
201366
|
-
if (!ctx.projectState || !
|
|
201433
|
+
if (!ctx.projectState || !bindingState) {
|
|
201367
201434
|
throw new Error("spawn \u5E26 project_id \u9700\u8981\u670D\u52A1\u7AEF\u6CE8\u5165\u9879\u76EE\u5B58\u50A8\u4E0E\u5DE5\u5355\u7ED1\u5B9A\u5B58\u50A8");
|
|
201368
201435
|
}
|
|
201369
201436
|
const project = await ctx.projectState.getProject(projectId2);
|
|
201370
201437
|
if (!project) throw new Error(`project not found: ${projectId2}`);
|
|
201371
|
-
await
|
|
201372
|
-
} else if (createdViaArg === "seed" && ctx.projectState &&
|
|
201438
|
+
await bindingState.upsertWorkspaceBinding({ workOrderId: workspace, projectId: projectId2 });
|
|
201439
|
+
} else if (createdViaArg === "seed" && ctx.projectState && bindingState) {
|
|
201373
201440
|
await ensureWorkorderProject({
|
|
201374
201441
|
workOrderId: workspace,
|
|
201375
201442
|
...optStr(args, "title") ? { title: optStr(args, "title") } : {},
|
|
@@ -201380,7 +201447,7 @@ async function runCommand(kernel, blobs, oplog, engineStore, actor, command, arg
|
|
|
201380
201447
|
// 由读侧从工单 brief.owner 派生(service.resolveProjectCreator)。
|
|
201381
201448
|
...ctx.workorderInitiator?.humanActorId ? { createdBy: ctx.workorderInitiator.humanActorId } : !isAgent(actor) ? { createdBy: actor } : {},
|
|
201382
201449
|
createProject: makeEnsureProjectFromStore(ctx.projectState),
|
|
201383
|
-
upsertBinding: (b2) =>
|
|
201450
|
+
upsertBinding: (b2) => bindingState.upsertWorkspaceBinding(b2),
|
|
201384
201451
|
onWarn: (m2) => console.warn(m2)
|
|
201385
201452
|
});
|
|
201386
201453
|
}
|
|
@@ -203175,6 +203242,7 @@ async function startOasisServer(opts) {
|
|
|
203175
203242
|
}
|
|
203176
203243
|
};
|
|
203177
203244
|
const artifactStateNow = async () => opts.artifactStateFor ? await opts.artifactStateFor(currentCompanyId) : opts.artifactState;
|
|
203245
|
+
const projectStateNow = async () => opts.projectStateFor ? await opts.projectStateFor(currentCompanyId) : opts.projectState;
|
|
203178
203246
|
if (url.pathname.startsWith("/api/channels")) {
|
|
203179
203247
|
const json = (status, body2) => {
|
|
203180
203248
|
res.writeHead(status, { "content-type": "application/json" }).end(JSON.stringify(body2));
|
|
@@ -203273,6 +203341,10 @@ async function startOasisServer(opts) {
|
|
|
203273
203341
|
}
|
|
203274
203342
|
}
|
|
203275
203343
|
const chatAnchorMessageId = await anchorMessageIdFor(chatOrigin, chatStoreNowOrNull);
|
|
203344
|
+
const spawnArgs = body2.args;
|
|
203345
|
+
const needsProject = body2.command === "spawn" && (spawnArgs?.["createdVia"] === "seed" || spawnArgs?.["project_id"] !== void 0 || spawnArgs?.["projectId"] !== void 0);
|
|
203346
|
+
const commandProjects = needsProject ? await projectStateNow() : opts.projectState;
|
|
203347
|
+
const commandBindings = needsProject && commandProjects ? await artifactStateNow() : void 0;
|
|
203276
203348
|
const result = await runCommand(
|
|
203277
203349
|
engine.kernel,
|
|
203278
203350
|
engine.blobs,
|
|
@@ -203291,13 +203363,14 @@ async function startOasisServer(opts) {
|
|
|
203291
203363
|
...chatOrigin !== void 0 ? { chatOrigin } : {},
|
|
203292
203364
|
...chatAnchorMessageId !== void 0 ? { chatAnchorMessageId } : {},
|
|
203293
203365
|
...sessionEscalationId !== void 0 ? { sessionEscalationId } : {},
|
|
203294
|
-
...
|
|
203366
|
+
...commandProjects ? { projectState: commandProjects } : {},
|
|
203367
|
+
...commandBindings ? { workspaceBindingState: commandBindings } : {},
|
|
203295
203368
|
...opts.artifactState ? { artifactState: opts.artifactState } : {},
|
|
203296
203369
|
...opts.schema ? { schema: opts.schema } : {},
|
|
203297
203370
|
...engine.registry ? { registry: engine.registry } : {},
|
|
203298
203371
|
...opts.resolveAssignableRoles ? { resolveAssignableRoles: opts.resolveAssignableRoles } : {},
|
|
203299
203372
|
...opts.planWorkorderWithAgent ? { planWorkorderWithAgent: opts.planWorkorderWithAgent } : {},
|
|
203300
|
-
...opts.resolveCodeRepo ? { resolveCodeRepo: opts.resolveCodeRepo } : {},
|
|
203373
|
+
...opts.resolveCodeRepo ? { resolveCodeRepo: (ws) => opts.resolveCodeRepo(ws, currentCompanyId) } : {},
|
|
203301
203374
|
...opts.dispatchProduce ? { dispatchProduce: opts.dispatchProduce } : {},
|
|
203302
203375
|
...wodrafts ? { workorderDrafts: wodrafts } : {},
|
|
203303
203376
|
workorderDraftPlannerIssues: wodraftPlannerIssues,
|
|
@@ -203585,6 +203658,7 @@ async function startOasisServer(opts) {
|
|
|
203585
203658
|
}
|
|
203586
203659
|
const resuming = resumeState.hasNodes;
|
|
203587
203660
|
let sessionProjectId;
|
|
203661
|
+
let sessionBindingState;
|
|
203588
203662
|
if (body2.chatSessionId && opts.chatSession) {
|
|
203589
203663
|
const applyChatStore = await chatStoreNow();
|
|
203590
203664
|
const session = await applyChatStore.getSession(body2.chatSessionId);
|
|
@@ -203593,10 +203667,12 @@ async function startOasisServer(opts) {
|
|
|
203593
203667
|
}
|
|
203594
203668
|
sessionProjectId = session.projectId ?? void 0;
|
|
203595
203669
|
if (sessionProjectId) {
|
|
203596
|
-
|
|
203670
|
+
const projects = await projectStateNow();
|
|
203671
|
+
sessionBindingState = await artifactStateNow();
|
|
203672
|
+
if (!projects || !sessionBindingState) {
|
|
203597
203673
|
throw new Error("chat session \u5E26 projectId \u65F6\u9700\u8981\u670D\u52A1\u7AEF\u6CE8\u5165\u9879\u76EE\u5B58\u50A8\u4E0E\u5DE5\u5355\u7ED1\u5B9A\u5B58\u50A8");
|
|
203598
203674
|
}
|
|
203599
|
-
const project = await
|
|
203675
|
+
const project = await projects.getProject(sessionProjectId);
|
|
203600
203676
|
if (!project) throw new Error(`project not found: ${sessionProjectId}`);
|
|
203601
203677
|
}
|
|
203602
203678
|
}
|
|
@@ -203646,7 +203722,7 @@ async function startOasisServer(opts) {
|
|
|
203646
203722
|
return;
|
|
203647
203723
|
}
|
|
203648
203724
|
if (targetWs && sessionProjectId) {
|
|
203649
|
-
await
|
|
203725
|
+
await sessionBindingState.upsertWorkspaceBinding({ workOrderId: targetWs, projectId: sessionProjectId });
|
|
203650
203726
|
}
|
|
203651
203727
|
const result = resuming ? { interventionId: "", applied: 0, resumed: true } : await engine.kernel.applyIntervention(body2.plan, body2.baseSeqs, actor);
|
|
203652
203728
|
if (resuming) {
|
|
@@ -204829,8 +204905,10 @@ ${composed}`;
|
|
|
204829
204905
|
});
|
|
204830
204906
|
}
|
|
204831
204907
|
const itemStore = persistTarget ? opts.resolveChatItems ? await opts.resolveChatItems(currentCompanyId).catch(() => void 0) : opts.chatItems : void 0;
|
|
204908
|
+
const versionSeed = itemStore ? await itemStore.sessionVersionCursor(persistTarget?.id ?? session.id).catch(() => 0) : 0;
|
|
204832
204909
|
const itemLedger = new ChatItemLedger({
|
|
204833
204910
|
...itemStore ? { items: itemStore } : {},
|
|
204911
|
+
versionSeed,
|
|
204834
204912
|
sessionId: persistTarget?.id ?? session.id,
|
|
204835
204913
|
// 优先账本 id(`chat_session_turns.id`,跨进程唯一、重启不失忆);账本没接入才兜底。
|
|
204836
204914
|
turnId: heldTurn?.id ?? fallbackTurnId(session.runId),
|
|
@@ -206762,7 +206840,12 @@ var init_memory_registry_store = __esm({
|
|
|
206762
206840
|
async putVariable(v2) {
|
|
206763
206841
|
const mk = this._varKey(v2.key, v2.actorId, v2.projectId);
|
|
206764
206842
|
const ex = this.variables.get(mk);
|
|
206765
|
-
|
|
206843
|
+
const keptName = v2.name ?? ex?.name;
|
|
206844
|
+
this.variables.set(mk, {
|
|
206845
|
+
...v2,
|
|
206846
|
+
...ex?.lastUsedAt !== void 0 ? { lastUsedAt: ex.lastUsedAt } : {},
|
|
206847
|
+
...keptName ? { name: keptName } : {}
|
|
206848
|
+
});
|
|
206766
206849
|
}
|
|
206767
206850
|
async touchVariableUsage(key, actorId, projectId2, when) {
|
|
206768
206851
|
const mk = this._varKey(key, actorId, projectId2);
|
|
@@ -209263,6 +209346,9 @@ function foldSingleNodeTasks(items) {
|
|
|
209263
209346
|
}
|
|
209264
209347
|
return folded;
|
|
209265
209348
|
}
|
|
209349
|
+
function engineContentKind(k2) {
|
|
209350
|
+
return k2 === "manifest" ? "manifest" : k2 === "external" ? "external-pin" : "inline-blob";
|
|
209351
|
+
}
|
|
209266
209352
|
function namedError(name, message) {
|
|
209267
209353
|
const err = new Error(message);
|
|
209268
209354
|
err.name = name;
|
|
@@ -209484,6 +209570,13 @@ var init_service4 = __esm({
|
|
|
209484
209570
|
async listProjects() {
|
|
209485
209571
|
return [...this.projects.values()].map((project) => ({ ...project }));
|
|
209486
209572
|
}
|
|
209573
|
+
/** 与 Postgres 同语义:主记录 + 成员 + 上传文件 + 内容位置一并清掉;不存在的 id 幂等返回。 */
|
|
209574
|
+
async deleteProject(id) {
|
|
209575
|
+
this.projects.delete(id);
|
|
209576
|
+
this.members.delete(id);
|
|
209577
|
+
for (const [fileId, f2] of this.files) if (f2.projectId === id) this.files.delete(fileId);
|
|
209578
|
+
for (const [locId, l] of this.contentLocations) if (l.projectId === id) this.contentLocations.delete(locId);
|
|
209579
|
+
}
|
|
209487
209580
|
async replaceProjectMembers(projectId2, members) {
|
|
209488
209581
|
this.members.set(projectId2, new Map(members.map((member) => [member.actorId, cloneProjectMember(member)])));
|
|
209489
209582
|
}
|
|
@@ -209629,6 +209722,7 @@ var init_service4 = __esm({
|
|
|
209629
209722
|
}
|
|
209630
209723
|
projectCreatedHandler;
|
|
209631
209724
|
onProjectNameChanged;
|
|
209725
|
+
onProjectDeleted;
|
|
209632
209726
|
/**
|
|
209633
209727
|
* Cross-domain follow-up for project-scoped resources. Project persistence stays authoritative:
|
|
209634
209728
|
* a failed follow-up is reported but never turns an already-created project into a false 500.
|
|
@@ -209643,6 +209737,10 @@ var init_service4 = __esm({
|
|
|
209643
209737
|
setOnProjectNameChanged(handler) {
|
|
209644
209738
|
this.onProjectNameChanged = handler;
|
|
209645
209739
|
}
|
|
209740
|
+
/** 删项目后的跨域清理钩子(会话解绑 + 项目变量清理)。装配层注入;缺省 = 无副作用。 */
|
|
209741
|
+
setOnProjectDeleted(handler) {
|
|
209742
|
+
this.onProjectDeleted = handler;
|
|
209743
|
+
}
|
|
209646
209744
|
/** 工单→项目绑定查询(content-location add 的项目匹配守卫用)。 */
|
|
209647
209745
|
async getWorkspaceBinding(workOrderId) {
|
|
209648
209746
|
return this.artifacts.getWorkspaceBinding(workOrderId);
|
|
@@ -210031,6 +210129,60 @@ var init_service4 = __esm({
|
|
|
210031
210129
|
await this.artifacts.upsertWorkspaceBinding(binding);
|
|
210032
210130
|
return binding;
|
|
210033
210131
|
}
|
|
210132
|
+
/**
|
|
210133
|
+
* 删除项目(帧 `444:1792` 三点菜单第二项,口径由发起人 2026-09-09 拍板)。
|
|
210134
|
+
*
|
|
210135
|
+
* **只删项目这个壳**——项目主记录 + 成员名单 + 上传文件记录 + 内容位置。任务和会话一条都不删:
|
|
210136
|
+
*
|
|
210137
|
+
* 1. 绑到本项目的工单**不管在跑还是没在跑**,一律 `unlinkWorkorder` 回落到自己的
|
|
210138
|
+
* `proj_tmp_<slug>`,也就是读侧的「无分类项目」。发起人明确不要「有任务在跑就不让删」
|
|
210139
|
+
* 这道闸,所以这里没有任何在跑判断。
|
|
210140
|
+
* 2. 搬工单的同时**把本项目的 git 类内容位置复制一份到每个工单的临时项目上**。
|
|
210141
|
+
* 这条不是可选的:`git/resolve.ts` 的 `resolveCodeRepo` 是**派发时**沿
|
|
210142
|
+
* `binding → project → listContentLocations` 实时解的,不带过去的话,正在跑的任务
|
|
210143
|
+
* 下一个节点就取不到代码仓——正是 `ephemeral-project.ts` 顶部那条五环事故链。
|
|
210144
|
+
* 3. 会话的 `project_id` 置空、项目作用域的变量/密钥清理,走 {@link OnProjectDeleted} 钩子
|
|
210145
|
+
* (本 service 对会话域与凭据域零耦合,同 `onProjectNameChanged` 的注入模式)。
|
|
210146
|
+
*
|
|
210147
|
+
* 拒绝两类 id:`proj:uncategorized`(读侧虚拟卡,存储里没有真行)与 `proj_tmp_*`
|
|
210148
|
+
* (工单的兜底归属,删掉等于把工单变成无绑定态——契约里 `Workspace.projectId` 必填)。
|
|
210149
|
+
*/
|
|
210150
|
+
async deleteProject(id) {
|
|
210151
|
+
const projectId2 = id?.trim();
|
|
210152
|
+
if (!projectId2) throw new Error("project id is required");
|
|
210153
|
+
if (isUncategorizedProjectId(projectId2)) throw new Error(`project not deletable: ${projectId2}`);
|
|
210154
|
+
if (isEphemeralProject(projectId2)) throw new Error(`project not deletable: ${projectId2}`);
|
|
210155
|
+
const project = await this.projects.getProject(projectId2);
|
|
210156
|
+
if (!project) throw new Error(`project not found: ${projectId2}`);
|
|
210157
|
+
const gitLocations = (await this.projects.listContentLocations(projectId2)).filter((loc) => loc.kind === "external-home" && loc.externalHome?.system === "git");
|
|
210158
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
210159
|
+
const bindings = await this.artifacts.listWorkspaceBindings();
|
|
210160
|
+
const movedWorkOrderIds = [];
|
|
210161
|
+
for (const binding of bindings) {
|
|
210162
|
+
if (binding.projectId !== projectId2) continue;
|
|
210163
|
+
const moved = await this.unlinkWorkorder(binding.workOrderId);
|
|
210164
|
+
movedWorkOrderIds.push(binding.workOrderId);
|
|
210165
|
+
for (const loc of gitLocations) {
|
|
210166
|
+
await this.projects.addContentLocation({
|
|
210167
|
+
...loc,
|
|
210168
|
+
projectId: moved.projectId,
|
|
210169
|
+
// 派生 id:同一条位置搬到同一个临时项目永远算出同一个 id(addContentLocation 是
|
|
210170
|
+
// upsert),重跑不会堆出重复的仓库行。
|
|
210171
|
+
locationId: `pcl-${moved.projectId}-carried-${loc.locationId}`,
|
|
210172
|
+
updatedAt: now
|
|
210173
|
+
});
|
|
210174
|
+
}
|
|
210175
|
+
}
|
|
210176
|
+
await this.projects.deleteProject(projectId2);
|
|
210177
|
+
if (this.onProjectDeleted) {
|
|
210178
|
+
try {
|
|
210179
|
+
await this.onProjectDeleted({ projectId: projectId2, projectName: project.name });
|
|
210180
|
+
} catch (error2) {
|
|
210181
|
+
console.warn(`[projects] project-deleted follow-up failed for ${projectId2}: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
210182
|
+
}
|
|
210183
|
+
}
|
|
210184
|
+
return { projectId: projectId2, movedWorkOrderIds, carriedGitLocationCount: gitLocations.length };
|
|
210185
|
+
}
|
|
210034
210186
|
/**
|
|
210035
210187
|
* 项目关联工单(帧 `1355:4918`):把内部私有的 `listProjectWorkorders` 暴露给路由层。
|
|
210036
210188
|
* `projectId="proj:uncategorized"` 时聚合所有 `proj_tmp_*` 绑定的工单。
|
|
@@ -210140,6 +210292,9 @@ var init_service4 = __esm({
|
|
|
210140
210292
|
size: file?.size ?? null,
|
|
210141
210293
|
taskId,
|
|
210142
210294
|
taskTitle,
|
|
210295
|
+
// 内容形态/引用只挂在**整份产物**那一行;装箱单摊出来的成员行有自己的后缀,
|
|
210296
|
+
// 按后缀走图标即可,挂上 manifest 反而会把 `Dockerfile` 这种无后缀成员画成压缩包。
|
|
210297
|
+
...file ? {} : { contentKind: revision.contentKind, contentRef: revision.contentRef },
|
|
210143
210298
|
creatorKind: creatorId ? creatorId.startsWith("actor:human:") ? "human" : "agent" : null,
|
|
210144
210299
|
creatorId,
|
|
210145
210300
|
creatorName: creatorId ? this.resolveActorName(creatorId) : null,
|
|
@@ -210294,6 +210449,8 @@ var init_service4 = __esm({
|
|
|
210294
210449
|
depth: 2,
|
|
210295
210450
|
type: artifactType || "",
|
|
210296
210451
|
size: null,
|
|
210452
|
+
contentKind: engineContentKind(a.contentKind),
|
|
210453
|
+
...a.contentRef ? { contentRef: a.contentRef } : {},
|
|
210297
210454
|
taskId: workOrderId,
|
|
210298
210455
|
taskTitle,
|
|
210299
210456
|
creatorKind,
|
|
@@ -210645,7 +210802,6 @@ var init_service4 = __esm({
|
|
|
210645
210802
|
const worksById = new Map(snap.works.map((w2) => [w2.id, w2]));
|
|
210646
210803
|
const binding = this.artifacts.getWorkspaceBinding ? await this.artifacts.getWorkspaceBinding(workOrderId).catch(() => null) : null;
|
|
210647
210804
|
const projectId2 = binding?.projectId ?? ephemeralProjectIdFor(workOrderId);
|
|
210648
|
-
const mapContentKind = (k2) => k2 === "manifest" ? "manifest" : k2 === "external" ? "external-pin" : "inline-blob";
|
|
210649
210805
|
const out = [];
|
|
210650
210806
|
for (const node2 of snap.nodes) {
|
|
210651
210807
|
if (!node2.latestAcceptId) continue;
|
|
@@ -210665,7 +210821,7 @@ var init_service4 = __esm({
|
|
|
210665
210821
|
contentRef: a.contentRef,
|
|
210666
210822
|
title: node2.title || a.name,
|
|
210667
210823
|
// 节点标题优先;否则用 engine artifact 的 "output" 兜底
|
|
210668
|
-
contentKind:
|
|
210824
|
+
contentKind: engineContentKind(a.contentKind)
|
|
210669
210825
|
}));
|
|
210670
210826
|
out.push({
|
|
210671
210827
|
outputId: `engine:${acceptedId}`,
|
|
@@ -217718,6 +217874,8 @@ ${input.description}
|
|
|
217718
217874
|
}
|
|
217719
217875
|
await this.opts.store.putVariable({
|
|
217720
217876
|
key: v2.key,
|
|
217877
|
+
// 展示名(子 PRD《密钥管理》§2)。没传就不传下去——存储层按「不动」处理,不抹既有名字。
|
|
217878
|
+
...v2.name !== void 0 && v2.name !== "" ? { name: v2.name } : {},
|
|
217721
217879
|
scope: v2.scope,
|
|
217722
217880
|
...v2.actorId !== void 0 ? { actorId: v2.actorId } : {},
|
|
217723
217881
|
...v2.projectId !== void 0 ? { projectId: v2.projectId } : {},
|
|
@@ -217786,6 +217944,7 @@ ${input.description}
|
|
|
217786
217944
|
const isPlain = r.valueEncrypted.startsWith("plain:");
|
|
217787
217945
|
return {
|
|
217788
217946
|
key: r.key,
|
|
217947
|
+
...r.name !== void 0 ? { name: r.name } : {},
|
|
217789
217948
|
scope: r.scope,
|
|
217790
217949
|
...r.actorId !== void 0 ? { actorId: r.actorId } : {},
|
|
217791
217950
|
...r.projectId !== void 0 ? { projectId: r.projectId } : {},
|
|
@@ -219613,6 +219772,8 @@ function actorsDomain(opts) {
|
|
|
219613
219772
|
key: b2.key,
|
|
219614
219773
|
value: b2.value,
|
|
219615
219774
|
scope,
|
|
219775
|
+
// 展示名(子 PRD《密钥管理》§2「名称」);不传 = 不改既有名字。
|
|
219776
|
+
...typeof b2.name === "string" && b2.name.trim() ? { name: b2.name.trim() } : {},
|
|
219616
219777
|
...scope === "project" ? { projectId: b2.projectId } : {},
|
|
219617
219778
|
...b2.deliveryMode !== void 0 ? { deliveryMode: b2.deliveryMode } : {},
|
|
219618
219779
|
...b2.connectorId !== void 0 ? { connectorId: b2.connectorId } : {},
|
|
@@ -219684,6 +219845,8 @@ function actorsDomain(opts) {
|
|
|
219684
219845
|
value: b2.value,
|
|
219685
219846
|
actorId,
|
|
219686
219847
|
scope: "personal",
|
|
219848
|
+
// 展示名(同上)。组织页「添加密钥 → 新建」走的就是这条端点。
|
|
219849
|
+
...typeof b2.name === "string" && b2.name.trim() ? { name: b2.name.trim() } : {},
|
|
219687
219850
|
...b2.deliveryMode !== void 0 ? { deliveryMode: b2.deliveryMode } : {},
|
|
219688
219851
|
...b2.connectorId !== void 0 ? { connectorId: b2.connectorId } : {},
|
|
219689
219852
|
...b2.overrides !== void 0 ? { overrides: b2.overrides } : {},
|
|
@@ -220740,6 +220903,22 @@ function projectsDomain(opts) {
|
|
|
220740
220903
|
throw mapProjectError(err);
|
|
220741
220904
|
}
|
|
220742
220905
|
});
|
|
220906
|
+
router.delete("/api/projects/:id", async (req) => {
|
|
220907
|
+
try {
|
|
220908
|
+
const result = await (await svc(req)).deleteProject(req.params.id);
|
|
220909
|
+
return {
|
|
220910
|
+
status: 200,
|
|
220911
|
+
body: {
|
|
220912
|
+
ok: true,
|
|
220913
|
+
project_id: result.projectId,
|
|
220914
|
+
moved_work_order_ids: result.movedWorkOrderIds,
|
|
220915
|
+
carried_git_location_count: result.carriedGitLocationCount
|
|
220916
|
+
}
|
|
220917
|
+
};
|
|
220918
|
+
} catch (err) {
|
|
220919
|
+
throw mapProjectError(err);
|
|
220920
|
+
}
|
|
220921
|
+
});
|
|
220743
220922
|
router.get("/api/projects/:id/members", async (req) => {
|
|
220744
220923
|
try {
|
|
220745
220924
|
const members = await (await svc(req)).listProjectMembers(req.params.id);
|
|
@@ -221162,6 +221341,9 @@ function mapProjectError(err) {
|
|
|
221162
221341
|
if (err instanceof Error && err.message.startsWith("project not found:")) {
|
|
221163
221342
|
return new ApiError(404, "NOT_FOUND", err.message);
|
|
221164
221343
|
}
|
|
221344
|
+
if (err instanceof Error && err.message.startsWith("project not deletable:")) {
|
|
221345
|
+
return new ApiError(400, "PROJECT_NOT_DELETABLE", err.message);
|
|
221346
|
+
}
|
|
221165
221347
|
if (err instanceof Error && err.message.startsWith("missing artifact revision registration:")) {
|
|
221166
221348
|
return new ApiError(400, "MISSING_ARTIFACT_REVISION", err.message);
|
|
221167
221349
|
}
|
|
@@ -221469,6 +221651,10 @@ function toApiProjectFilesView(view) {
|
|
|
221469
221651
|
file_path: item.filePath ?? null,
|
|
221470
221652
|
depth: item.depth,
|
|
221471
221653
|
type: item.type,
|
|
221654
|
+
// 内容形态 + 内容引用:前端名称列给**没有后缀**的行选图标用(正文→文本、git 提交→代码、
|
|
221655
|
+
// 其它外部引用→链接)。交付物整份产物行才有,上传文件与文件夹为 null。
|
|
221656
|
+
content_kind: item.contentKind ?? null,
|
|
221657
|
+
content_ref: item.contentRef ?? null,
|
|
221472
221658
|
size: item.size,
|
|
221473
221659
|
task_id: item.taskId,
|
|
221474
221660
|
task_title: item.taskTitle,
|
|
@@ -221524,6 +221710,10 @@ function createProjectsDomain(opts) {
|
|
|
221524
221710
|
const handler = opts.onProjectNameChangedFor(companyId);
|
|
221525
221711
|
if (handler) svc.setOnProjectNameChanged(handler);
|
|
221526
221712
|
}
|
|
221713
|
+
if (opts.onProjectDeletedFor) {
|
|
221714
|
+
const handler = opts.onProjectDeletedFor(companyId);
|
|
221715
|
+
if (handler) svc.setOnProjectDeleted(handler);
|
|
221716
|
+
}
|
|
221527
221717
|
return svc;
|
|
221528
221718
|
};
|
|
221529
221719
|
const service = build(void 0, void 0);
|
|
@@ -222349,8 +222539,8 @@ var init_offboarding = __esm({
|
|
|
222349
222539
|
return { ownedAgents, inFlight };
|
|
222350
222540
|
}
|
|
222351
222541
|
/**
|
|
222352
|
-
* 现在**能不能直接删**这个成员。DELETE
|
|
222353
|
-
*
|
|
222542
|
+
* 现在**能不能直接删**这个成员。DELETE 与 commit 共用它,不会两处各判一套。
|
|
222543
|
+
* 交接删掉之后这里只剩两条:不是成员(NOT_FOUND)、公司最后一位 owner(LAST_OWNER)。
|
|
222354
222544
|
*/
|
|
222355
222545
|
async blockersForRemoval(companyId, accountId) {
|
|
222356
222546
|
const blockers = [];
|
|
@@ -222533,6 +222723,15 @@ function companiesDomain(opts) {
|
|
|
222533
222723
|
if (!agentCredentials) throw new ApiError(501, "NOT_IMPLEMENTED", "\u672C\u90E8\u7F72\u672A\u63A5\u5165 agent \u51ED\u8BC1");
|
|
222534
222724
|
return agentCredentials;
|
|
222535
222725
|
};
|
|
222726
|
+
const syncActorNameToDisplayName = async (member, by) => {
|
|
222727
|
+
if (!resolveActorsCtx) return;
|
|
222728
|
+
const name = member.displayName?.trim();
|
|
222729
|
+
if (!name) return;
|
|
222730
|
+
const { service: actorsSvc } = await resolveActorsCtx(member.companyId);
|
|
222731
|
+
const existing = await actorsSvc.getActor(member.actorId);
|
|
222732
|
+
if (!existing || existing.name === name) return;
|
|
222733
|
+
await actorsSvc.upsertActor({ ...existing, name }, by);
|
|
222734
|
+
};
|
|
222536
222735
|
const requireIfMatch = (req) => {
|
|
222537
222736
|
const raw = req.raw.headers["if-match"];
|
|
222538
222737
|
const text5 = (typeof raw === "string" ? raw : Array.isArray(raw) ? raw[0] : void 0)?.replace(/^"|"$/g, "");
|
|
@@ -222648,6 +222847,7 @@ function companiesDomain(opts) {
|
|
|
222648
222847
|
throw new ApiError(400, "BAD_REQUEST", "displayName \u987B\u4E3A\u975E\u7A7A\u5B57\u7B26\u4E32");
|
|
222649
222848
|
}
|
|
222650
222849
|
member = await service.setMemberDisplayName(cid, aid, b2.displayName).catch(mapErr);
|
|
222850
|
+
await syncActorNameToDisplayName(member, req.auth.actor);
|
|
222651
222851
|
}
|
|
222652
222852
|
return { status: 200, body: member };
|
|
222653
222853
|
});
|
|
@@ -224698,6 +224898,8 @@ function buildWorkorderActivity(input) {
|
|
|
224698
224898
|
const groupKey = (nodeId) => `${nodeId}#${groupIndexOf.get(nodeId) ?? 0}`;
|
|
224699
224899
|
const reviewCards = /* @__PURE__ */ new Map();
|
|
224700
224900
|
const issueCards = /* @__PURE__ */ new Map();
|
|
224901
|
+
const workOfIssue = /* @__PURE__ */ new Map();
|
|
224902
|
+
const plainCommentIssueIds = /* @__PURE__ */ new Set();
|
|
224701
224903
|
const acceptCards = /* @__PURE__ */ new Map();
|
|
224702
224904
|
const standalone = [];
|
|
224703
224905
|
const touch = (d, rec) => {
|
|
@@ -224755,10 +224957,22 @@ function buildWorkorderActivity(input) {
|
|
|
224755
224957
|
const executorId = w2?.assigneeActorId ?? rec.actorId;
|
|
224756
224958
|
if (isPendingActor(executorId)) break;
|
|
224757
224959
|
const status = reasonKinds.has("rework") ? ACTIVITY_COPY.reworking(nodeName(nodeId)) : reasonKinds.has("resumed") ? ACTIVITY_COPY.resumed(nameOf(executorId), nodeName(nodeId)) : reasonKinds.has("spec_changed") ? ACTIVITY_COPY.specChanged(nodeName(nodeId)) : reasonKinds.has("issue") ? ACTIVITY_COPY.issueDriven(nodeName(nodeId)) : ACTIVITY_COPY.working(nodeName(nodeId));
|
|
224960
|
+
const drivingIssueId = str4(reasons.find((r) => r.kind === "issue")?.issueId);
|
|
224961
|
+
if (drivingIssueId) {
|
|
224962
|
+
workOfIssue.set(drivingIssueId, workId);
|
|
224963
|
+
const ic = plainCommentIssueIds.has(drivingIssueId) ? issueCards.get(drivingIssueId) : void 0;
|
|
224964
|
+
const commentAuthor = issueById.get(drivingIssueId)?.authorActorId;
|
|
224965
|
+
if (ic && commentAuthor) {
|
|
224966
|
+
touch(ic, rec);
|
|
224967
|
+
ic.executorId = executorId;
|
|
224968
|
+
ic.status = ACTIVITY_COPY.commentHandling(nameOf(executorId), nameOf(commentAuthor));
|
|
224969
|
+
}
|
|
224970
|
+
}
|
|
224758
224971
|
const reworkReason = str4(reasons.find((r) => r.kind === "rework")?.reason);
|
|
224759
224972
|
const key = groupKey(nodeId);
|
|
224760
224973
|
const existingGroup = groupCards.get(key);
|
|
224761
224974
|
if (existingGroup) {
|
|
224975
|
+
if (!existingGroup.traceWorkIds.includes(workId)) existingGroup.traceWorkIds.push(workId);
|
|
224762
224976
|
touch(existingGroup, rec);
|
|
224763
224977
|
existingGroup.executorId = executorId;
|
|
224764
224978
|
existingGroup.phase = "running";
|
|
@@ -224771,6 +224985,7 @@ function buildWorkorderActivity(input) {
|
|
|
224771
224985
|
}
|
|
224772
224986
|
const fresh = {
|
|
224773
224987
|
id: `work:${workId}`,
|
|
224988
|
+
traceWorkIds: [workId],
|
|
224774
224989
|
seq: rec.seq,
|
|
224775
224990
|
at: rec.createdAt,
|
|
224776
224991
|
updatedAt: rec.createdAt,
|
|
@@ -224813,8 +225028,9 @@ function buildWorkorderActivity(input) {
|
|
|
224813
225028
|
}
|
|
224814
225029
|
break;
|
|
224815
225030
|
}
|
|
225031
|
+
const engineVerdict = workById.get(workId)?.status;
|
|
224816
225032
|
touch(d, rec);
|
|
224817
|
-
if (ev.outcome === "failed") {
|
|
225033
|
+
if (ev.outcome === "failed" && engineVerdict !== "success") {
|
|
224818
225034
|
d.phase = "stuck";
|
|
224819
225035
|
d.stuckReason = "failed";
|
|
224820
225036
|
d.status = ACTIVITY_COPY.failed(nameOf(d.executorId));
|
|
@@ -225046,6 +225262,7 @@ function buildWorkorderActivity(input) {
|
|
|
225046
225262
|
});
|
|
225047
225263
|
break;
|
|
225048
225264
|
}
|
|
225265
|
+
if (!isGap && !isEscalation && !isBackpressure) plainCommentIssueIds.add(issueId);
|
|
225049
225266
|
const nodeAssignee = nodeId ? nodeById.get(nodeId)?.assigneeActorId : void 0;
|
|
225050
225267
|
const commentCounterpart = nodeAssignee ?? ownerId;
|
|
225051
225268
|
const commentTargetId = commentCounterpart && commentCounterpart !== authorId ? commentCounterpart : void 0;
|
|
@@ -225134,7 +225351,10 @@ function buildWorkorderActivity(input) {
|
|
|
225134
225351
|
d.phase = "done";
|
|
225135
225352
|
d.actions = [];
|
|
225136
225353
|
d.executorId = resolverId;
|
|
225137
|
-
|
|
225354
|
+
const drivenWorkId = workOfIssue.get(issueId);
|
|
225355
|
+
const producedNewVersion = !!(drivenWorkId && workById.get(drivenWorkId)?.conclusion);
|
|
225356
|
+
const commentAuthorId = issue2?.authorActorId;
|
|
225357
|
+
d.status = producedNewVersion && commentAuthorId ? ACTIVITY_COPY.commentAddressed(nameOf(resolverId), nameOf(commentAuthorId), nodeName(d.nodeId)) : ACTIVITY_COPY.commentResolved(nameOf(resolverId), nodeName(d.nodeId));
|
|
225138
225358
|
break;
|
|
225139
225359
|
}
|
|
225140
225360
|
case "plan.node_retry": {
|
|
@@ -225189,12 +225409,37 @@ function buildWorkorderActivity(input) {
|
|
|
225189
225409
|
...issueCards.values(),
|
|
225190
225410
|
...acceptCards.values()
|
|
225191
225411
|
].sort((a, b2) => a.updatedAt.localeCompare(b2.updatedAt) || a.seq - b2.seq || a.id.localeCompare(b2.id));
|
|
225192
|
-
const
|
|
225193
|
-
const
|
|
225194
|
-
|
|
225195
|
-
|
|
225196
|
-
if (
|
|
225197
|
-
|
|
225412
|
+
const issueWorks = /* @__PURE__ */ new Map();
|
|
225413
|
+
const reasonIds = /* @__PURE__ */ new Map();
|
|
225414
|
+
for (const rec of input.events) {
|
|
225415
|
+
const ev = rec.event;
|
|
225416
|
+
if (ev.kind === "work.create") reasonIds.set(
|
|
225417
|
+
ev.workId,
|
|
225418
|
+
ev.reasons.filter((r) => r.kind === "issue").map((r) => r.issueId)
|
|
225419
|
+
);
|
|
225420
|
+
}
|
|
225421
|
+
for (const w2 of [...snap?.works ?? []].sort((a, b2) => a.createdAt.localeCompare(b2.createdAt) || a.id.localeCompare(b2.id))) {
|
|
225422
|
+
const replyIssueId = replyIssueIdOf(w2);
|
|
225423
|
+
for (const id of new Set(w2.frozenIssueIds ?? reasonIds.get(w2.id) ?? (replyIssueId ? [replyIssueId] : []))) {
|
|
225424
|
+
const issue2 = issueById.get(id);
|
|
225425
|
+
if (issue2?.kind !== "comment" && issue2?.kind !== "change_request") continue;
|
|
225426
|
+
const ids2 = issueWorks.get(id) ?? [];
|
|
225427
|
+
ids2.push(w2.id);
|
|
225428
|
+
issueWorks.set(id, ids2);
|
|
225429
|
+
}
|
|
225430
|
+
}
|
|
225431
|
+
for (const d of drafts) {
|
|
225432
|
+
if (d.id.startsWith("issue:")) d.traceWorkIds = issueWorks.get(d.id.slice("issue:".length));
|
|
225433
|
+
}
|
|
225434
|
+
const runIdOfCard = (d) => {
|
|
225435
|
+
const map = input.runIdByTarget;
|
|
225436
|
+
if (d.traceWorkIds?.length) {
|
|
225437
|
+
for (const id of [...d.traceWorkIds].reverse()) {
|
|
225438
|
+
const run = map?.get(`work:${id}`);
|
|
225439
|
+
if (run) return run;
|
|
225440
|
+
}
|
|
225441
|
+
}
|
|
225442
|
+
return d.id.startsWith("review:") ? map?.get(d.id) : void 0;
|
|
225198
225443
|
};
|
|
225199
225444
|
const cards = drafts.map((d) => ({
|
|
225200
225445
|
id: d.id,
|
|
@@ -225209,7 +225454,8 @@ function buildWorkorderActivity(input) {
|
|
|
225209
225454
|
...d.detail ? { detail: d.detail } : {},
|
|
225210
225455
|
artifacts: d.artifacts,
|
|
225211
225456
|
actions: d.actions,
|
|
225212
|
-
|
|
225457
|
+
...d.traceWorkIds?.length ? { traceWorkIds: d.traceWorkIds, traceAvailable: true } : {},
|
|
225458
|
+
.../* @__PURE__ */ ((r) => r ? { runId: r, traceAvailable: true } : {})(runIdOfCard(d))
|
|
225213
225459
|
}));
|
|
225214
225460
|
return { workorderId, cards, truncated };
|
|
225215
225461
|
}
|
|
@@ -225218,6 +225464,7 @@ var init_activity2 = __esm({
|
|
|
225218
225464
|
"../server/src/domains/collab/activity.ts"() {
|
|
225219
225465
|
"use strict";
|
|
225220
225466
|
init_src();
|
|
225467
|
+
init_src4();
|
|
225221
225468
|
init_workorder_manager();
|
|
225222
225469
|
ACTIVITY_EVENT_KINDS = [
|
|
225223
225470
|
"plan.changed",
|
|
@@ -225347,6 +225594,14 @@ var init_activity2 = __esm({
|
|
|
225347
225594
|
* 留言 / 回压收口——**同样一句索引**。收口 note 与开口正文同源、同样能长到几千字:
|
|
225348
225595
|
* 全库实测 `ws:wo-56bba3c1` 那条收口后小状态仍是 **8096 字**。全文去留言线看。
|
|
225349
225596
|
*/
|
|
225597
|
+
/** 有人接手这条留言、开工了——由 `work.create` 的 `reasons` 引用该 issueId 判定。 */
|
|
225598
|
+
commentHandling: (executor, author) => `${executor} \u6B63\u5728\u5904\u7406\u6765\u81EA ${author} \u7684\u7559\u8A00\u3002`,
|
|
225599
|
+
/**
|
|
225600
|
+
* 处理完**并且真出了新一版**。
|
|
225601
|
+
* ⚠ 只在那一轮真有 conclusion 时才说「更新了一版」——resolve 也可能是「看了但不改」,
|
|
225602
|
+
* 那种情况说出了新版就是一句卡面兑现不了的断言(同「未经 X 确认」那一族)。
|
|
225603
|
+
*/
|
|
225604
|
+
commentAddressed: (executor, author, node2) => `${executor} \u5DF2\u57FA\u4E8E ${author} \u7684\u7559\u8A00\u66F4\u65B0\u4E86\u4E00\u7248\u300A${node2}\u300B\u3002`,
|
|
225350
225605
|
commentResolved: (by, node2) => `${by} \u5DF2\u5904\u7406\u300A${node2}\u300B\u7684\u7559\u8A00\u3002`,
|
|
225351
225606
|
issueResolved: (note) => note
|
|
225352
225607
|
};
|
|
@@ -225370,6 +225625,125 @@ function targetOf(cardId) {
|
|
|
225370
225625
|
return null;
|
|
225371
225626
|
}
|
|
225372
225627
|
function buildWorkorderActivityTrace(input) {
|
|
225628
|
+
const { workorderId, cardId, snap } = input;
|
|
225629
|
+
const ref2 = input.ref ?? ((id) => ({ id }));
|
|
225630
|
+
const empty2 = { workorderId, cardId, attempts: [] };
|
|
225631
|
+
if (!snap) return empty2;
|
|
225632
|
+
const card2 = input.events ? buildWorkorderActivity({
|
|
225633
|
+
workorderId,
|
|
225634
|
+
snap,
|
|
225635
|
+
events: input.events,
|
|
225636
|
+
ref: ref2
|
|
225637
|
+
}).cards.find((c) => c.id === cardId) : void 0;
|
|
225638
|
+
const workIds = card2?.traceWorkIds ?? (cardId.startsWith("work:") ? [cardId.slice(5)] : []);
|
|
225639
|
+
if (workIds.length > 0) {
|
|
225640
|
+
let firstBriefing;
|
|
225641
|
+
const works = [];
|
|
225642
|
+
for (const workId of workIds) {
|
|
225643
|
+
const work = snap.works.find((w2) => w2.id === workId);
|
|
225644
|
+
if (!work) continue;
|
|
225645
|
+
const single = buildSingleTrace({ ...input, cardId: `work:${workId}` });
|
|
225646
|
+
const briefing = single.briefing;
|
|
225647
|
+
if (!briefing) continue;
|
|
225648
|
+
firstBriefing ??= briefing;
|
|
225649
|
+
const sources = sourcesOfWork(work, input, briefing);
|
|
225650
|
+
const artifacts = single.attempts.at(-1)?.artifacts ?? artifactsOfWork(work, input);
|
|
225651
|
+
works.push({
|
|
225652
|
+
workId,
|
|
225653
|
+
at: work.createdAt,
|
|
225654
|
+
nodeTitle: briefing.nodeTitle,
|
|
225655
|
+
...briefing.spec ? { spec: briefing.spec } : {},
|
|
225656
|
+
sources,
|
|
225657
|
+
attempts: single.attempts,
|
|
225658
|
+
...work.conclusion?.trim() ? { conclusion: work.conclusion.trim() } : {},
|
|
225659
|
+
artifacts,
|
|
225660
|
+
...single.attempts.length ? {} : { emptyReason: input.dispatches === void 0 ? "records_unavailable" : work.startedAt || work.endedAt || work.deadAt || work.cancelledAt || work.conclusion || work.status && work.status !== "running" ? "records_missing" : "not_dispatched" }
|
|
225661
|
+
});
|
|
225662
|
+
}
|
|
225663
|
+
if (works.length) {
|
|
225664
|
+
return {
|
|
225665
|
+
workorderId,
|
|
225666
|
+
cardId,
|
|
225667
|
+
briefing: firstBriefing,
|
|
225668
|
+
works,
|
|
225669
|
+
attempts: works.flatMap((w2) => w2.attempts).sort((a, b2) => a.at.localeCompare(b2.at) || a.runId.localeCompare(b2.runId))
|
|
225670
|
+
};
|
|
225671
|
+
}
|
|
225672
|
+
}
|
|
225673
|
+
return buildSingleTrace(input);
|
|
225674
|
+
}
|
|
225675
|
+
function sourcesOfWork(work, input, briefing) {
|
|
225676
|
+
const snap = input.snap;
|
|
225677
|
+
const ref2 = input.ref ?? ((id) => ({ id }));
|
|
225678
|
+
const created = input.events?.find((r) => r.event.kind === "work.create" && r.event.workId === work.id);
|
|
225679
|
+
const event = created?.event;
|
|
225680
|
+
const replyIssueId = replyIssueIdOf(work);
|
|
225681
|
+
const ids2 = work.frozenIssueIds ?? (event?.kind === "work.create" ? event.reasons.filter((r) => r.kind === "issue").map((r) => r.issueId) : replyIssueId ? [replyIssueId] : void 0);
|
|
225682
|
+
if (!ids2) return [{ kind: "unavailable", at: work.createdAt }];
|
|
225683
|
+
const sources = [];
|
|
225684
|
+
for (const issueId of new Set(ids2)) {
|
|
225685
|
+
const issue2 = snap.issues.find((i) => i.id === issueId);
|
|
225686
|
+
if (!issue2 || issue2.createdAt > work.createdAt) {
|
|
225687
|
+
sources.push({ kind: "unavailable", at: work.createdAt, issueId });
|
|
225688
|
+
continue;
|
|
225689
|
+
}
|
|
225690
|
+
if (issue2.kind !== "comment" && issue2.kind !== "change_request") continue;
|
|
225691
|
+
const replySeq = (id) => input.events?.find((e) => e.event.kind === "issue.reply" && e.event.replyId === id)?.seq;
|
|
225692
|
+
if (snap.issueReplies.some((r) => r.issueId === issueId && r.createdAt === work.createdAt && (!created || replySeq(r.id) === void 0))) {
|
|
225693
|
+
sources.push({ kind: "unavailable", at: work.createdAt, issueId });
|
|
225694
|
+
continue;
|
|
225695
|
+
}
|
|
225696
|
+
const replies = snap.issueReplies.filter((r) => {
|
|
225697
|
+
if (r.issueId !== issueId || r.createdAt > work.createdAt) return false;
|
|
225698
|
+
const seq = replySeq(r.id);
|
|
225699
|
+
return created && seq !== void 0 ? seq < created.seq : r.createdAt < work.createdAt;
|
|
225700
|
+
}).sort((a, b2) => a.createdAt.localeCompare(b2.createdAt) || (replySeq(a.id) ?? 0) - (replySeq(b2.id) ?? 0) || a.id.localeCompare(b2.id));
|
|
225701
|
+
const reply = replies.at(-1);
|
|
225702
|
+
if (reply?.viaWorkId && snap.works.some((w2) => w2.id === reply.viaWorkId && w2.nodeId === work.nodeId)) {
|
|
225703
|
+
sources.push({ kind: "unavailable", at: work.createdAt, issueId });
|
|
225704
|
+
continue;
|
|
225705
|
+
}
|
|
225706
|
+
sources.push({
|
|
225707
|
+
kind: issueId.startsWith("ann:rework:") ? "gap_resume" : "comment",
|
|
225708
|
+
actor: ref2(reply?.authorActorId ?? issue2.authorActorId),
|
|
225709
|
+
at: reply?.createdAt ?? issue2.createdAt,
|
|
225710
|
+
content: reply?.body ?? issue2.body,
|
|
225711
|
+
issueId,
|
|
225712
|
+
...reply ? { replyId: reply.id } : {}
|
|
225713
|
+
});
|
|
225714
|
+
}
|
|
225715
|
+
return sources.length ? sources : [{
|
|
225716
|
+
kind: "assignment",
|
|
225717
|
+
actor: briefing.actor,
|
|
225718
|
+
at: work.createdAt,
|
|
225719
|
+
...briefing.spec ? { content: briefing.spec } : {}
|
|
225720
|
+
}];
|
|
225721
|
+
}
|
|
225722
|
+
function artifactsOfWork(work, input) {
|
|
225723
|
+
const snap = input.snap;
|
|
225724
|
+
const workId = work.id;
|
|
225725
|
+
const nodeId = work.nodeId;
|
|
225726
|
+
const node2 = snap.nodes.find((n) => n.id === nodeId);
|
|
225727
|
+
const nodesWithInEdges = new Set(snap.edges.map((e) => e.toNodeId));
|
|
225728
|
+
const isRootBrief = node2?.type === "brief" && !nodesWithInEdges.has(nodeId);
|
|
225729
|
+
const list2 = [];
|
|
225730
|
+
for (const a of snap.artifacts) {
|
|
225731
|
+
if (a.workId !== workId) continue;
|
|
225732
|
+
const artifactNodeId = a.nodeId;
|
|
225733
|
+
const files = a.contentKind === "manifest" ? input.manifestFiles?.get(a.contentRef) : void 0;
|
|
225734
|
+
if (files && files.length > 0) {
|
|
225735
|
+
for (const path41 of files) {
|
|
225736
|
+
list2.push({ id: a.id, name: path41.split("/").pop() || path41, path: path41, nodeId: artifactNodeId });
|
|
225737
|
+
}
|
|
225738
|
+
} else if (isRootBrief) {
|
|
225739
|
+
list2.push({ id: a.id, name: "\u4EFB\u52A1\u76EE\u6807", path: "\u4EFB\u52A1\u76EE\u6807.md", nodeId: artifactNodeId });
|
|
225740
|
+
} else {
|
|
225741
|
+
list2.push({ id: a.id, name: a.name, nodeId: artifactNodeId });
|
|
225742
|
+
}
|
|
225743
|
+
}
|
|
225744
|
+
return list2;
|
|
225745
|
+
}
|
|
225746
|
+
function buildSingleTrace(input) {
|
|
225373
225747
|
const { workorderId, cardId, snap } = input;
|
|
225374
225748
|
const ref2 = input.ref ?? ((id) => ({ id }));
|
|
225375
225749
|
const empty2 = { workorderId, cardId, attempts: [] };
|
|
@@ -225382,26 +225756,6 @@ function buildWorkorderActivityTrace(input) {
|
|
|
225382
225756
|
const nodeId = work?.nodeId ?? review?.nodeId;
|
|
225383
225757
|
if (!nodeId) return empty2;
|
|
225384
225758
|
const node2 = nodeById.get(nodeId);
|
|
225385
|
-
const nodesWithInEdges = new Set(snap.edges.map((e) => e.toNodeId));
|
|
225386
|
-
const isRootBrief = node2?.type === "brief" && !nodesWithInEdges.has(nodeId);
|
|
225387
|
-
const artifactsOfWork = (workId) => {
|
|
225388
|
-
const list2 = [];
|
|
225389
|
-
for (const a of snap.artifacts) {
|
|
225390
|
-
if (a.workId !== workId) continue;
|
|
225391
|
-
const artifactNodeId = a.nodeId;
|
|
225392
|
-
const files = a.contentKind === "manifest" ? input.manifestFiles?.get(a.contentRef) : void 0;
|
|
225393
|
-
if (files && files.length > 0) {
|
|
225394
|
-
for (const path41 of files) {
|
|
225395
|
-
list2.push({ id: a.id, name: path41.split("/").pop() || path41, path: path41, nodeId: artifactNodeId });
|
|
225396
|
-
}
|
|
225397
|
-
} else if (isRootBrief) {
|
|
225398
|
-
list2.push({ id: a.id, name: "\u4EFB\u52A1\u76EE\u6807", path: "\u4EFB\u52A1\u76EE\u6807.md", nodeId: artifactNodeId });
|
|
225399
|
-
} else {
|
|
225400
|
-
list2.push({ id: a.id, name: a.name, nodeId: artifactNodeId });
|
|
225401
|
-
}
|
|
225402
|
-
}
|
|
225403
|
-
return list2;
|
|
225404
|
-
};
|
|
225405
225759
|
const managerId = resolveWorkorderManager(snap) ?? snap.workorder.ownerActorId;
|
|
225406
225760
|
const startedAt = work?.createdAt ?? review?.createdAt;
|
|
225407
225761
|
const spec = node2?.spec?.trim();
|
|
@@ -225411,18 +225765,18 @@ function buildWorkorderActivityTrace(input) {
|
|
|
225411
225765
|
nodeTitle: node2?.title || nodeId.split(":").slice(2).join(":") || nodeId,
|
|
225412
225766
|
...spec ? { spec } : {}
|
|
225413
225767
|
};
|
|
225414
|
-
const rows = (input.dispatches ?? []).filter((d) => d.targetKind === target.kind && d.targetId === target.id).slice().sort((a, b2) => a.createdAt.localeCompare(b2.createdAt));
|
|
225768
|
+
const rows = (input.dispatches ?? []).filter((d) => d.targetKind === target.kind && d.targetId === target.id).slice().sort((a, b2) => a.createdAt.localeCompare(b2.createdAt) || a.id.localeCompare(b2.id));
|
|
225415
225769
|
const executorId = work?.assigneeActorId ?? review?.reviewerActorId;
|
|
225416
225770
|
const conclusion = work?.conclusion?.trim() || review?.note?.trim() || void 0;
|
|
225417
225771
|
const attempts = rows.map((d, i) => {
|
|
225418
225772
|
const isLast = i === rows.length - 1;
|
|
225419
|
-
const actorId =
|
|
225773
|
+
const actorId = d.actorId ?? executorId;
|
|
225420
225774
|
return {
|
|
225421
225775
|
runId: d.id,
|
|
225422
225776
|
at: d.createdAt,
|
|
225423
225777
|
actor: ref2(actorId),
|
|
225424
225778
|
...isLast && conclusion ? { conclusion } : {},
|
|
225425
|
-
artifacts: isLast && work ? artifactsOfWork(work
|
|
225779
|
+
artifacts: isLast && work ? artifactsOfWork(work, input) : []
|
|
225426
225780
|
};
|
|
225427
225781
|
});
|
|
225428
225782
|
return { workorderId, cardId, briefing, attempts };
|
|
@@ -225430,6 +225784,8 @@ function buildWorkorderActivityTrace(input) {
|
|
|
225430
225784
|
var init_activity_trace = __esm({
|
|
225431
225785
|
"../server/src/domains/collab/activity-trace.ts"() {
|
|
225432
225786
|
"use strict";
|
|
225787
|
+
init_src4();
|
|
225788
|
+
init_activity2();
|
|
225433
225789
|
init_workorder_manager();
|
|
225434
225790
|
}
|
|
225435
225791
|
});
|
|
@@ -227283,10 +227639,7 @@ function collabDomain(opts) {
|
|
|
227283
227639
|
const hit = cache.get(companyId);
|
|
227284
227640
|
if (hit) return hit;
|
|
227285
227641
|
const e = await opts.resolveEngine(companyId);
|
|
227286
|
-
|
|
227287
|
-
cache.set(companyId, defaultCtx);
|
|
227288
|
-
return defaultCtx;
|
|
227289
|
-
}
|
|
227642
|
+
const artifacts = opts.resolveArtifacts ? await opts.resolveArtifacts(companyId) : e.kernel === opts.kernel ? opts.artifacts : void 0;
|
|
227290
227643
|
const ctx = {
|
|
227291
227644
|
kernel: e.kernel,
|
|
227292
227645
|
oplog: e.oplog,
|
|
@@ -227296,7 +227649,7 @@ function collabDomain(opts) {
|
|
|
227296
227649
|
// ★ 产物旁存也要按公司取(见 resolveArtifacts 的注释):漏了这一行,非默认公司的
|
|
227297
227650
|
// `/api/workorders/:id/files` 与 node_output 清单恒空——写侧写对了、读侧根本没接线。
|
|
227298
227651
|
// 抛出来就抛出来:宁可 500 说"这家公司的数据面解析不了",也不能回落默认公司的库。
|
|
227299
|
-
...
|
|
227652
|
+
...artifacts ? { artifacts } : {}
|
|
227300
227653
|
};
|
|
227301
227654
|
cache.set(companyId, ctx);
|
|
227302
227655
|
return ctx;
|
|
@@ -227550,7 +227903,10 @@ function collabDomain(opts) {
|
|
|
227550
227903
|
...extra?.avatar ? { avatar: extra.avatar } : {}
|
|
227551
227904
|
};
|
|
227552
227905
|
};
|
|
227553
|
-
const snap = engineStore ? await engineStore.transaction((tx) =>
|
|
227906
|
+
const [snap, events] = engineStore ? await engineStore.transaction(async (tx) => [
|
|
227907
|
+
await tx.loadWorkorder(workorderId),
|
|
227908
|
+
await tx.listEvents(workorderId, 0, ACTIVITY_EVENT_LIMIT, ACTIVITY_EVENT_KINDS)
|
|
227909
|
+
]) : [null, []];
|
|
227554
227910
|
const manifestFiles = snap ? await resolveActivityManifestFiles(snap.artifacts, blobs) : void 0;
|
|
227555
227911
|
const dispatches = await listDispatchRows(workorderId, opts.dispatchesOfWorkorder);
|
|
227556
227912
|
return {
|
|
@@ -227559,6 +227915,7 @@ function collabDomain(opts) {
|
|
|
227559
227915
|
workorderId,
|
|
227560
227916
|
cardId,
|
|
227561
227917
|
snap,
|
|
227918
|
+
events,
|
|
227562
227919
|
ref: ref2,
|
|
227563
227920
|
...dispatches ? { dispatches } : {},
|
|
227564
227921
|
...manifestFiles ? { manifestFiles } : {}
|
|
@@ -231108,42 +231465,17 @@ function mapChatItemToSnapshotEntry(row) {
|
|
|
231108
231465
|
itemType,
|
|
231109
231466
|
role,
|
|
231110
231467
|
status,
|
|
231111
|
-
|
|
231468
|
+
// ADR-0510 附录 A:版本轴 = 会话逻辑时钟(`chat_items.version`)。
|
|
231469
|
+
// 此前这里填的是 `turnVersion`(轮级 resync 游标),而增量流填的是进程内每 item 计数——
|
|
231470
|
+
// 两套编号空间,一轮中途拉快照就把随后每一帧判 stale,进 resync 死循环。
|
|
231471
|
+
itemVersion: row.version,
|
|
231112
231472
|
seq: row.seq,
|
|
231473
|
+
ord: row.ord,
|
|
231113
231474
|
messageId: row.messageId,
|
|
231114
231475
|
updatedAt: row.updatedAt,
|
|
231115
231476
|
payload: row.payload
|
|
231116
231477
|
};
|
|
231117
231478
|
}
|
|
231118
|
-
function synthesizeMessageItem(msg, seq) {
|
|
231119
|
-
const attachments = Array.isArray(msg.attachments) ? msg.attachments.map((a) => ({
|
|
231120
|
-
name: a.name,
|
|
231121
|
-
...a.blobRef !== void 0 ? { blobRef: a.blobRef } : {},
|
|
231122
|
-
...a.contentType !== void 0 ? { contentType: a.contentType } : {}
|
|
231123
|
-
})) : void 0;
|
|
231124
|
-
const clientSubmitId = msg.role === "user" ? msg.clientSubmitId ?? clientSubmitIdFromMessageKey(msg.messageKey) : void 0;
|
|
231125
|
-
const role = msg.role === "user" || msg.role === "system" ? msg.role : "assistant";
|
|
231126
|
-
const status = msg.status === "running" ? "streaming" : msg.status === "error" ? "failed" : "completed";
|
|
231127
|
-
return {
|
|
231128
|
-
// ADR-0510 D1:有 csid 就用**与账本 user item、前端乐观桶位同名**的身份;没有(历史行 / 老客户端)
|
|
231129
|
-
// 才退回合成 id。三方同名,桶按 itemId 合并才闭环——否则同一条 user 消息画两个气泡。
|
|
231130
|
-
itemId: clientSubmitId ? optimisticUserItemId(clientSubmitId) : `chat-message:${msg.id}`,
|
|
231131
|
-
turnId: msg.runId ?? `chat-message-turn:${msg.id}`,
|
|
231132
|
-
itemType: "message",
|
|
231133
|
-
role,
|
|
231134
|
-
status,
|
|
231135
|
-
itemVersion: 0,
|
|
231136
|
-
seq,
|
|
231137
|
-
messageId: msg.id,
|
|
231138
|
-
updatedAt: msg.completedAt ?? msg.createdAt,
|
|
231139
|
-
payload: {
|
|
231140
|
-
role,
|
|
231141
|
-
text: msg.content ?? "",
|
|
231142
|
-
...attachments?.length ? { attachments } : {},
|
|
231143
|
-
...clientSubmitId ? { clientSubmitId } : {}
|
|
231144
|
-
}
|
|
231145
|
-
};
|
|
231146
|
-
}
|
|
231147
231479
|
function throwWorkdirError(code2) {
|
|
231148
231480
|
switch (code2) {
|
|
231149
231481
|
case "PATH_ESCAPE":
|
|
@@ -231505,57 +231837,50 @@ function createChatSessionsDomain(opts) {
|
|
|
231505
231837
|
if (!session || session.humanActorId !== req.auth.actor) throw new ApiError(404, "NOT_FOUND", "session not found");
|
|
231506
231838
|
const chatSessionId = req.params.id;
|
|
231507
231839
|
const sinceVersion = Math.max(0, Number(req.query?.get("sinceVersion") ?? "0") || 0);
|
|
231840
|
+
const beforeOrdRaw = Number(req.query?.get("beforeOrd") ?? "0") || 0;
|
|
231841
|
+
const beforeOrd = beforeOrdRaw > 0 ? beforeOrdRaw : void 0;
|
|
231842
|
+
const limitRaw = Number(req.query?.get("limit") ?? "0") || 0;
|
|
231843
|
+
const limit = limitRaw > 0 ? Math.min(limitRaw, MAX_SNAPSHOT_ITEMS) : DEFAULT_SNAPSHOT_ITEMS;
|
|
231508
231844
|
const cursor = opts.liveChat?.cursorFor(chatSessionId) ?? null;
|
|
231509
231845
|
let turnId = cursor?.turnId ?? null;
|
|
231510
|
-
let inflightRunId = opts.liveChat?.runFor(chatSessionId) ?? null;
|
|
231511
231846
|
if (!turnId) {
|
|
231512
231847
|
const turns = await turnsFor(req);
|
|
231513
231848
|
const active = turns ? await turns.activeTurn(chatSessionId).catch(() => null) : null;
|
|
231514
231849
|
turnId = active?.id ?? null;
|
|
231515
|
-
inflightRunId ??= active?.assistantRunId ?? null;
|
|
231516
231850
|
}
|
|
231517
231851
|
const snapshotEntries = [];
|
|
231518
231852
|
let nextSinceVersion = sinceVersion;
|
|
231519
|
-
|
|
231520
|
-
|
|
231853
|
+
let hasMoreBefore = false;
|
|
231854
|
+
if (sinceVersion > 0) {
|
|
231855
|
+
if (turnId) {
|
|
231856
|
+
const rows = await items.listItemsByTurn(chatSessionId, turnId, { sinceTurnVersion: sinceVersion }).catch(() => []);
|
|
231857
|
+
for (const row of rows) {
|
|
231858
|
+
snapshotEntries.push(mapChatItemToSnapshotEntry(row));
|
|
231859
|
+
if (row.turnVersion > nextSinceVersion) nextSinceVersion = row.turnVersion;
|
|
231860
|
+
}
|
|
231861
|
+
}
|
|
231862
|
+
} else {
|
|
231863
|
+
const rows = await items.listSessionItems(chatSessionId, {
|
|
231864
|
+
limit,
|
|
231865
|
+
...beforeOrd !== void 0 ? { beforeOrd } : {}
|
|
231866
|
+
}).catch(() => []);
|
|
231867
|
+
hasMoreBefore = rows.length >= limit;
|
|
231521
231868
|
for (const row of rows) {
|
|
231522
231869
|
snapshotEntries.push(mapChatItemToSnapshotEntry(row));
|
|
231523
231870
|
if (row.turnVersion > nextSinceVersion) nextSinceVersion = row.turnVersion;
|
|
231524
231871
|
}
|
|
231525
231872
|
}
|
|
231526
|
-
const
|
|
231527
|
-
const HISTORY_TURNS = 12;
|
|
231528
|
-
const historicalEntries = [];
|
|
231529
|
-
if (sinceVersion === 0) {
|
|
231530
|
-
const listRecentTurns2 = store2.listRecentTurns;
|
|
231531
|
-
const messages = listRecentTurns2 ? (await listRecentTurns2.call(store2, chatSessionId, HISTORY_TURNS).catch(() => ({ messages: [], hasMoreBefore: false }))).messages : windowMessagesByTurns(await store2.listMessages(chatSessionId).catch(() => []), HISTORY_TURNS).messages;
|
|
231532
|
-
let historySeq = 0;
|
|
231533
|
-
for (const msg of messages) {
|
|
231534
|
-
if (!msg.id) continue;
|
|
231535
|
-
const rows = await items.listItemsByMessage(msg.id).catch(() => []);
|
|
231536
|
-
const historical = turnId ? rows.filter((row) => row.turnId !== turnId) : rows;
|
|
231537
|
-
if (rows.length > 0) {
|
|
231538
|
-
for (const row of [...historical].sort((a, b2) => a.seq - b2.seq)) {
|
|
231539
|
-
historySeq += 1;
|
|
231540
|
-
historicalEntries.push({ ...mapChatItemToSnapshotEntry(row), seq: historySeq });
|
|
231541
|
-
}
|
|
231542
|
-
continue;
|
|
231543
|
-
}
|
|
231544
|
-
if (msg.role === "assistant" && msg.status === "running" && inflightRunId && msg.runId === inflightRunId) continue;
|
|
231545
|
-
const synthesized = synthesizeMessageItem(msg, historySeq + 1);
|
|
231546
|
-
if (activeTurnItemIds.has(synthesized.itemId)) continue;
|
|
231547
|
-
historySeq += 1;
|
|
231548
|
-
historicalEntries.push(synthesized);
|
|
231549
|
-
}
|
|
231550
|
-
}
|
|
231873
|
+
const oldestOrd = snapshotEntries.find((e) => e.ord !== null)?.ord ?? null;
|
|
231551
231874
|
const body2 = {
|
|
231552
231875
|
chatSessionId,
|
|
231553
231876
|
turnId,
|
|
231554
|
-
//
|
|
231555
|
-
items:
|
|
231877
|
+
// 已按 ord 升序——服务端给什么顺序,客户端就按 ord 排出什么顺序,两边同一把尺子。
|
|
231878
|
+
items: snapshotEntries,
|
|
231556
231879
|
nextSinceVersion,
|
|
231557
231880
|
frameCursor: cursor ? { epoch: cursor.epoch, seq: cursor.seq } : { epoch: 0, seq: 0 },
|
|
231558
|
-
canAppend: cursor?.canAppend ?? false
|
|
231881
|
+
canAppend: cursor?.canAppend ?? false,
|
|
231882
|
+
oldestOrd,
|
|
231883
|
+
hasMoreBefore
|
|
231559
231884
|
};
|
|
231560
231885
|
return { status: 200, body: body2 };
|
|
231561
231886
|
});
|
|
@@ -231908,7 +232233,7 @@ function createChatSessionsDomain(opts) {
|
|
|
231908
232233
|
});
|
|
231909
232234
|
};
|
|
231910
232235
|
}
|
|
231911
|
-
var import_node_crypto59, WORKDIR_READ_MAX_BYTES;
|
|
232236
|
+
var import_node_crypto59, DEFAULT_SNAPSHOT_ITEMS, MAX_SNAPSHOT_ITEMS, WORKDIR_READ_MAX_BYTES;
|
|
231912
232237
|
var init_chat_sessions = __esm({
|
|
231913
232238
|
"../server/src/domains/chat-sessions/index.ts"() {
|
|
231914
232239
|
"use strict";
|
|
@@ -231929,6 +232254,8 @@ var init_chat_sessions = __esm({
|
|
|
231929
232254
|
init_workorder_terminal();
|
|
231930
232255
|
init_session_workdir();
|
|
231931
232256
|
init_project_bundle();
|
|
232257
|
+
DEFAULT_SNAPSHOT_ITEMS = 400;
|
|
232258
|
+
MAX_SNAPSHOT_ITEMS = 1e3;
|
|
231932
232259
|
WORKDIR_READ_MAX_BYTES = 2 * 1024 * 1024;
|
|
231933
232260
|
}
|
|
231934
232261
|
});
|
|
@@ -236591,6 +236918,9 @@ function makeStoreCodeRepoResolver(deps) {
|
|
|
236591
236918
|
}
|
|
236592
236919
|
};
|
|
236593
236920
|
}
|
|
236921
|
+
function makeCompanyCodeRepoResolver(storesFor) {
|
|
236922
|
+
return async (ws, companyId) => makeStoreCodeRepoResolver(await storesFor(companyId)).resolveCodeRepo(ws);
|
|
236923
|
+
}
|
|
236594
236924
|
var init_resolve = __esm({
|
|
236595
236925
|
"../server/src/git/resolve.ts"() {
|
|
236596
236926
|
"use strict";
|
|
@@ -237541,16 +237871,28 @@ var init_fused_signal_router = __esm({
|
|
|
237541
237871
|
}
|
|
237542
237872
|
});
|
|
237543
237873
|
|
|
237874
|
+
// ../storage/src/pg-ident.ts
|
|
237875
|
+
function quoteIdent2(name) {
|
|
237876
|
+
if (!/^[A-Za-z0-9_][A-Za-z0-9_.-]*$/.test(name)) throw new Error(`invalid schema name: ${name}`);
|
|
237877
|
+
return `"${name}"`;
|
|
237878
|
+
}
|
|
237879
|
+
var init_pg_ident = __esm({
|
|
237880
|
+
"../storage/src/pg-ident.ts"() {
|
|
237881
|
+
"use strict";
|
|
237882
|
+
}
|
|
237883
|
+
});
|
|
237884
|
+
|
|
237544
237885
|
// ../storage/src/postgres.ts
|
|
237545
237886
|
var import_node_crypto67, ident3, isUniqueViolation, PostgresOplogStore, PostgresBlobStore;
|
|
237546
237887
|
var init_postgres = __esm({
|
|
237547
237888
|
"../storage/src/postgres.ts"() {
|
|
237548
237889
|
"use strict";
|
|
237549
237890
|
import_node_crypto67 = require("node:crypto");
|
|
237891
|
+
init_pg_ident();
|
|
237550
237892
|
init_esm();
|
|
237551
237893
|
init_src();
|
|
237552
237894
|
ident3 = (s2) => {
|
|
237553
|
-
|
|
237895
|
+
quoteIdent2(s2);
|
|
237554
237896
|
return s2;
|
|
237555
237897
|
};
|
|
237556
237898
|
isUniqueViolation = (err) => typeof err === "object" && err !== null && err.code === "23505";
|
|
@@ -237772,7 +238114,9 @@ function rowToVariable(row) {
|
|
|
237772
238114
|
updatedAt: new Date(row.updated_at).toISOString(),
|
|
237773
238115
|
// ADR 凭据保险库补章 §D2.1:到期/取用时间戳(text 列存 ISO 字符串,原样带出;null/空 = undefined)。
|
|
237774
238116
|
...row.expires_at ? { expiresAt: row.expires_at } : {},
|
|
237775
|
-
...row.last_used_at ? { lastUsedAt: row.last_used_at } : {}
|
|
238117
|
+
...row.last_used_at ? { lastUsedAt: row.last_used_at } : {},
|
|
238118
|
+
// 展示名(存量行没有 → 不带这一格,展示侧回落成 key)。
|
|
238119
|
+
...row.name ? { name: row.name } : {}
|
|
237776
238120
|
};
|
|
237777
238121
|
}
|
|
237778
238122
|
var ident4, PostgresRegistryStore, rowToActor, rowToConfig, rowToAgentPrefs;
|
|
@@ -237780,9 +238124,10 @@ var init_postgres_registry = __esm({
|
|
|
237780
238124
|
"../storage/src/postgres-registry.ts"() {
|
|
237781
238125
|
"use strict";
|
|
237782
238126
|
init_esm();
|
|
238127
|
+
init_pg_ident();
|
|
237783
238128
|
init_src();
|
|
237784
238129
|
ident4 = (s2) => {
|
|
237785
|
-
|
|
238130
|
+
quoteIdent2(s2);
|
|
237786
238131
|
return s2;
|
|
237787
238132
|
};
|
|
237788
238133
|
PostgresRegistryStore = class _PostgresRegistryStore {
|
|
@@ -237938,6 +238283,7 @@ var init_postgres_registry = __esm({
|
|
|
237938
238283
|
await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS project_id text`);
|
|
237939
238284
|
await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS environment text`);
|
|
237940
238285
|
await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS delivery_mode text`);
|
|
238286
|
+
await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS name text`);
|
|
237941
238287
|
await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS expires_at text`);
|
|
237942
238288
|
await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS last_used_at text`);
|
|
237943
238289
|
await pool.query(`DROP INDEX IF EXISTS "${s2}"."${s2}_variables_key_actor_uq"`);
|
|
@@ -238436,10 +238782,13 @@ var init_postgres_registry = __esm({
|
|
|
238436
238782
|
/* ---------- 变量 ---------- */
|
|
238437
238783
|
async putVariable(v2) {
|
|
238438
238784
|
await this.pool.query(
|
|
238439
|
-
`
|
|
238440
|
-
|
|
238785
|
+
/* `name` 走 COALESCE:**没传 = 不动**。连接器授权流程写变量时手上没有「名称」,
|
|
238786
|
+
普通赋值会把人在密钥表单里填的名字抹掉——同 connectors.connected_by 那条。 */
|
|
238787
|
+
`INSERT INTO ${this.s}.variables (key, scope, actor_id, project_id, connector_id, overrides, delivery_mode, value_encrypted, updated_at, expires_at, name)
|
|
238788
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11)
|
|
238441
238789
|
ON CONFLICT (key, COALESCE(actor_id, ''), COALESCE(project_id, '')) DO UPDATE
|
|
238442
|
-
SET scope=$2, actor_id=$3, project_id=$4, connector_id=$5, overrides=$6, delivery_mode=$7, value_encrypted=$8, updated_at=$9, expires_at=$10
|
|
238790
|
+
SET scope=$2, actor_id=$3, project_id=$4, connector_id=$5, overrides=$6, delivery_mode=$7, value_encrypted=$8, updated_at=$9, expires_at=$10,
|
|
238791
|
+
name=COALESCE($11, ${this.s}.variables.name)`,
|
|
238443
238792
|
[
|
|
238444
238793
|
v2.key,
|
|
238445
238794
|
v2.scope,
|
|
@@ -238450,7 +238799,8 @@ var init_postgres_registry = __esm({
|
|
|
238450
238799
|
v2.deliveryMode ?? null,
|
|
238451
238800
|
v2.valueEncrypted,
|
|
238452
238801
|
v2.updatedAt,
|
|
238453
|
-
v2.expiresAt ?? null
|
|
238802
|
+
v2.expiresAt ?? null,
|
|
238803
|
+
v2.name ?? null
|
|
238454
238804
|
]
|
|
238455
238805
|
);
|
|
238456
238806
|
}
|
|
@@ -238546,17 +238896,6 @@ var init_postgres_registry = __esm({
|
|
|
238546
238896
|
}
|
|
238547
238897
|
});
|
|
238548
238898
|
|
|
238549
|
-
// ../storage/src/pg-ident.ts
|
|
238550
|
-
function quoteIdent2(name) {
|
|
238551
|
-
if (!/^[A-Za-z0-9_][A-Za-z0-9_.-]*$/.test(name)) throw new Error(`invalid schema name: ${name}`);
|
|
238552
|
-
return `"${name}"`;
|
|
238553
|
-
}
|
|
238554
|
-
var init_pg_ident = __esm({
|
|
238555
|
-
"../storage/src/pg-ident.ts"() {
|
|
238556
|
-
"use strict";
|
|
238557
|
-
}
|
|
238558
|
-
});
|
|
238559
|
-
|
|
238560
238899
|
// ../storage/src/postgres-assistant-store.ts
|
|
238561
238900
|
var ASSISTANT_LOCK_NAMESPACE, rowToBinding, rowToHistory, PostgresAssistantBindStore, PostgresHumanPrefsStore;
|
|
238562
238901
|
var init_postgres_assistant_store = __esm({
|
|
@@ -238865,8 +239204,9 @@ var init_postgres_channels = __esm({
|
|
|
238865
239204
|
"../storage/src/postgres-channels.ts"() {
|
|
238866
239205
|
"use strict";
|
|
238867
239206
|
init_esm();
|
|
239207
|
+
init_pg_ident();
|
|
238868
239208
|
ident5 = (s2) => {
|
|
238869
|
-
|
|
239209
|
+
quoteIdent2(s2);
|
|
238870
239210
|
return s2;
|
|
238871
239211
|
};
|
|
238872
239212
|
PostgresChannelStore = class _PostgresChannelStore {
|
|
@@ -239140,6 +239480,24 @@ var init_postgres_projects = __esm({
|
|
|
239140
239480
|
const r = await this.pool.query(`SELECT * FROM ${this.s}.projects ORDER BY created_at`);
|
|
239141
239481
|
return r.rows.map(rowToProject);
|
|
239142
239482
|
}
|
|
239483
|
+
/** 主行 + 三张子表一并删,一个事务里做完——四张表之间没有外键,分开删失败一半就留孤儿行。 */
|
|
239484
|
+
async deleteProject(id) {
|
|
239485
|
+
const client = await this.pool.connect();
|
|
239486
|
+
try {
|
|
239487
|
+
await client.query("BEGIN");
|
|
239488
|
+
await client.query(`DELETE FROM ${this.s}.project_members WHERE project_id = $1`, [id]);
|
|
239489
|
+
await client.query(`DELETE FROM ${this.s}.project_files WHERE project_id = $1`, [id]);
|
|
239490
|
+
await client.query(`DELETE FROM ${this.s}.project_content_locations WHERE project_id = $1`, [id]);
|
|
239491
|
+
await client.query(`DELETE FROM ${this.s}.projects WHERE id = $1`, [id]);
|
|
239492
|
+
await client.query("COMMIT");
|
|
239493
|
+
} catch (e) {
|
|
239494
|
+
await client.query("ROLLBACK").catch(() => {
|
|
239495
|
+
});
|
|
239496
|
+
throw e;
|
|
239497
|
+
} finally {
|
|
239498
|
+
client.release();
|
|
239499
|
+
}
|
|
239500
|
+
}
|
|
239143
239501
|
async replaceProjectMembers(projectId2, members) {
|
|
239144
239502
|
const client = await this.pool.connect();
|
|
239145
239503
|
try {
|
|
@@ -239934,8 +240292,9 @@ var init_postgres_control_plane = __esm({
|
|
|
239934
240292
|
"../storage/src/postgres-control-plane.ts"() {
|
|
239935
240293
|
"use strict";
|
|
239936
240294
|
init_esm();
|
|
240295
|
+
init_pg_ident();
|
|
239937
240296
|
ident7 = (s2) => {
|
|
239938
|
-
|
|
240297
|
+
quoteIdent2(s2);
|
|
239939
240298
|
return s2;
|
|
239940
240299
|
};
|
|
239941
240300
|
PostgresControlPlaneStore = class _PostgresControlPlaneStore {
|
|
@@ -240860,11 +241219,12 @@ var ident8, mergeContinuityRefs3, PostgresTraceStore, rowToRun, terminalHandoffO
|
|
|
240860
241219
|
var init_postgres_trace = __esm({
|
|
240861
241220
|
"../storage/src/postgres-trace.ts"() {
|
|
240862
241221
|
"use strict";
|
|
241222
|
+
init_pg_ident();
|
|
240863
241223
|
init_esm();
|
|
240864
241224
|
init_src();
|
|
240865
241225
|
init_pg_sanitize();
|
|
240866
241226
|
ident8 = (s2) => {
|
|
240867
|
-
|
|
241227
|
+
quoteIdent2(s2);
|
|
240868
241228
|
return s2;
|
|
240869
241229
|
};
|
|
240870
241230
|
mergeContinuityRefs3 = (...lists) => {
|
|
@@ -242849,10 +243209,11 @@ var init_postgres_automations = __esm({
|
|
|
242849
243209
|
"../storage/src/postgres-automations.ts"() {
|
|
242850
243210
|
"use strict";
|
|
242851
243211
|
init_esm();
|
|
243212
|
+
init_pg_ident();
|
|
242852
243213
|
init_postgres_chat_turns();
|
|
242853
243214
|
init_src();
|
|
242854
243215
|
ident10 = (s2) => {
|
|
242855
|
-
|
|
243216
|
+
quoteIdent2(s2);
|
|
242856
243217
|
return s2;
|
|
242857
243218
|
};
|
|
242858
243219
|
genId = (prefix) => `${prefix}_${Math.random().toString(36).slice(2, 12)}`;
|
|
@@ -244091,6 +244452,15 @@ var init_postgres_chat_sessions = __esm({
|
|
|
244091
244452
|
args
|
|
244092
244453
|
);
|
|
244093
244454
|
}
|
|
244455
|
+
/** 删项目时把本公司里绑到它的会话回落成「不指定项目」。会话一条都不删,只清 project_id。 */
|
|
244456
|
+
async clearProjectBinding(projectId2) {
|
|
244457
|
+
if (!projectId2) return 0;
|
|
244458
|
+
const r = await this.pool.query(
|
|
244459
|
+
`UPDATE ${this.s}.chat_sessions SET project_id = NULL WHERE project_id = $1 AND company_id = $2`,
|
|
244460
|
+
[projectId2, this.companyId]
|
|
244461
|
+
);
|
|
244462
|
+
return r.rowCount ?? 0;
|
|
244463
|
+
}
|
|
244094
244464
|
async deleteSession(id) {
|
|
244095
244465
|
if (!await this.scopedSessionExists(id)) return;
|
|
244096
244466
|
await this.pool.query(`DELETE FROM ${this.s}.chat_messages WHERE session_id = $1`, [id]);
|
|
@@ -244914,12 +245284,15 @@ var init_postgres_chat_items = __esm({
|
|
|
244914
245284
|
const r = await this.pool.query(
|
|
244915
245285
|
`INSERT INTO ${this.s}.chat_items
|
|
244916
245286
|
(id, session_id, message_id, turn_id, run_id, seq, kind, role, status,
|
|
244917
|
-
version, turn_version, provider_item_key, payload, metadata, created_at, updated_at, completed_at)
|
|
245287
|
+
version, turn_version, provider_item_key, payload, metadata, created_at, updated_at, ord, completed_at)
|
|
244918
245288
|
SELECT $1, $2, $3, $4, $5,
|
|
244919
245289
|
COALESCE(MAX(c.seq), 0) + 1,
|
|
244920
|
-
$6, $7, $8::text, 0,
|
|
245290
|
+
$6, $7, $8::text, COALESCE($13::bigint, 0),
|
|
244921
245291
|
COALESCE(MAX(c.turn_version), 0) + 1,
|
|
244922
245292
|
$9, $10::jsonb, $11::jsonb, $12::timestamptz, $12::timestamptz,
|
|
245293
|
+
-- ord\uFF1A\u8D26\u672C\u9884\u6D3E\u4E86\u5C31\u7528\u5B83\uFF08\u5EFA\u884C\u5373\u6709\u53F7\u3001\u7B2C\u4E00\u5E27\u5C31\u80FD\u5E26\u4E0A wire\uFF09\uFF1B\u6CA1\u7ED9\u624D\u8D70\u5217 DEFAULT
|
|
245294
|
+
-- \u7684 nextval\u2014\u2014\u4E24\u6761\u8DEF\u53D6\u7684\u662F\u540C\u4E00\u6761\u8868\u7EA7\u5E8F\u5217\uFF0C\u8BED\u4E49\u76F8\u540C\uFF0C\u53EA\u5DEE\u62FF\u53F7\u53D1\u751F\u5728\u54EA\u4E00\u4FA7\u3002
|
|
245295
|
+
COALESCE($14::bigint, nextval('${this.s}.chat_items_ord_seq'::regclass)),
|
|
244923
245296
|
-- $12/$8 \u4E09\u5904\u90FD\u663E\u5F0F\u8F6C\u578B\uFF1A\u4E0D\u8F6C\u7684\u8BDD PG \u4F1A\u5728\u540C\u4E00\u6761\u8BED\u53E5\u91CC\u7ED9\u540C\u4E00\u4E2A\u53C2\u6570\u63A8\u51FA\u4E0D\u540C\u7C7B\u578B
|
|
244924
245297
|
-- \uFF08\u5217\u4F4D\u662F timestamptz/text\u3001CASE \u91CC\u662F unknown\uFF09\uFF0C\u76F4\u63A5\u62A5
|
|
244925
245298
|
-- inconsistent types deduced for parameter $12\u3002
|
|
@@ -244939,7 +245312,9 @@ var init_postgres_chat_items = __esm({
|
|
|
244939
245312
|
scrubPgString(input.providerItemKey ?? null),
|
|
244940
245313
|
payload,
|
|
244941
245314
|
metadata,
|
|
244942
|
-
at
|
|
245315
|
+
at,
|
|
245316
|
+
input.version ?? null,
|
|
245317
|
+
input.ord ?? null
|
|
244943
245318
|
]
|
|
244944
245319
|
);
|
|
244945
245320
|
return rowToItem(r.rows[0]);
|
|
@@ -244977,10 +245352,13 @@ var init_postgres_chat_items = __esm({
|
|
|
244977
245352
|
setExpr = `status = $3::text, completed_at = CASE WHEN $3 IN ('completed','failed','interrupted') THEN $2::timestamptz ELSE c.completed_at END`;
|
|
244978
245353
|
break;
|
|
244979
245354
|
}
|
|
245355
|
+
const versionParamIndex = args.length + 1;
|
|
245356
|
+
args.push(opts?.version ?? null);
|
|
244980
245357
|
const r = await this.pool.query(
|
|
244981
245358
|
`UPDATE ${this.s}.chat_items c
|
|
244982
245359
|
SET ${setExpr},
|
|
244983
|
-
version = c.version + 1
|
|
245360
|
+
version = CASE WHEN $${versionParamIndex}::bigint IS NULL THEN c.version + 1
|
|
245361
|
+
ELSE GREATEST(c.version, $${versionParamIndex}::bigint) END,
|
|
244984
245362
|
turn_version = ${bumpTurnVersion},
|
|
244985
245363
|
updated_at = $2
|
|
244986
245364
|
WHERE c.id = $1
|
|
@@ -245028,6 +245406,49 @@ var init_postgres_chat_items = __esm({
|
|
|
245028
245406
|
);
|
|
245029
245407
|
return r.rows.map((row) => rowToItem(row));
|
|
245030
245408
|
}
|
|
245409
|
+
/**
|
|
245410
|
+
* 一条会话的 item,按展示序升序(ADR-0510 D3:历史的唯一出口)。
|
|
245411
|
+
*
|
|
245412
|
+
* 排序键 `(ord IS NULL) DESC, ord DESC` 的意思是「取最新的一段」:带号的行按号从大到小、
|
|
245413
|
+
* 没号的存量行排在最后(= 最老)。截断之后再翻回升序返回。
|
|
245414
|
+
* 走索引 `chat_items_session_ord_idx (session_id, ord)`。
|
|
245415
|
+
*/
|
|
245416
|
+
async listSessionItems(sessionId, opts) {
|
|
245417
|
+
const limit = Math.max(1, Math.min(opts?.limit ?? 500, 2e3));
|
|
245418
|
+
const before = opts?.beforeOrd;
|
|
245419
|
+
const r = before === void 0 ? await this.pool.query(
|
|
245420
|
+
`SELECT * FROM ${this.s}.chat_items WHERE session_id = $1
|
|
245421
|
+
ORDER BY (ord IS NULL) ASC, ord DESC, seq DESC LIMIT $2`,
|
|
245422
|
+
[sessionId, limit]
|
|
245423
|
+
) : await this.pool.query(
|
|
245424
|
+
`SELECT * FROM ${this.s}.chat_items WHERE session_id = $1 AND ord IS NOT NULL AND ord < $2
|
|
245425
|
+
ORDER BY ord DESC LIMIT $3`,
|
|
245426
|
+
[sessionId, before, limit]
|
|
245427
|
+
);
|
|
245428
|
+
const rows = r.rows.map((row) => rowToItem(row));
|
|
245429
|
+
rows.reverse();
|
|
245430
|
+
return rows.sort((a, b2) => {
|
|
245431
|
+
if (a.ord === null && b2.ord === null) return a.seq - b2.seq;
|
|
245432
|
+
if (a.ord === null) return -1;
|
|
245433
|
+
if (b2.ord === null) return 1;
|
|
245434
|
+
return a.ord - b2.ord;
|
|
245435
|
+
});
|
|
245436
|
+
}
|
|
245437
|
+
async sessionVersionCursor(sessionId) {
|
|
245438
|
+
const r = await this.pool.query(
|
|
245439
|
+
`SELECT COALESCE(MAX(version), 0) AS v FROM ${this.s}.chat_items WHERE session_id = $1`,
|
|
245440
|
+
[sessionId]
|
|
245441
|
+
);
|
|
245442
|
+
return Number(r.rows[0]?.v ?? 0);
|
|
245443
|
+
}
|
|
245444
|
+
async reserveOrds(count2) {
|
|
245445
|
+
const n = Math.max(1, Math.min(Math.floor(count2), 1e3));
|
|
245446
|
+
const r = await this.pool.query(
|
|
245447
|
+
`SELECT nextval('${this.s}.chat_items_ord_seq'::regclass) AS ord FROM generate_series(1, $1)`,
|
|
245448
|
+
[n]
|
|
245449
|
+
);
|
|
245450
|
+
return r.rows.map((row) => Number(row.ord));
|
|
245451
|
+
}
|
|
245031
245452
|
async turnCursor(sessionId, turnId) {
|
|
245032
245453
|
const r = await this.pool.query(
|
|
245033
245454
|
`SELECT COALESCE(MAX(seq), 0) + 1 AS next_seq, COALESCE(MAX(turn_version), 0) + 1 AS next_turn_version
|
|
@@ -245364,8 +245785,14 @@ function planSessionOrd(sessionId, messages, items, turns, opts) {
|
|
|
245364
245785
|
}
|
|
245365
245786
|
async function listChatItemsSchemas(pool) {
|
|
245366
245787
|
const r = await pool.query(
|
|
245367
|
-
`SELECT n.nspname AS s
|
|
245368
|
-
|
|
245788
|
+
`SELECT n.nspname AS s
|
|
245789
|
+
FROM pg_class c
|
|
245790
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
245791
|
+
WHERE c.relname = 'chat_items' AND c.relkind = 'r'
|
|
245792
|
+
AND (SELECT count(*) FROM information_schema.columns col
|
|
245793
|
+
WHERE col.table_schema = n.nspname AND col.table_name = 'chat_items'
|
|
245794
|
+
AND col.column_name IN ('session_id', 'turn_id', 'seq', 'kind', 'payload')) = 5
|
|
245795
|
+
ORDER BY 1`
|
|
245369
245796
|
);
|
|
245370
245797
|
return r.rows.map((x2) => x2.s);
|
|
245371
245798
|
}
|
|
@@ -245959,9 +246386,10 @@ var init_postgres_nodes = __esm({
|
|
|
245959
246386
|
"../storage/src/postgres-nodes.ts"() {
|
|
245960
246387
|
"use strict";
|
|
245961
246388
|
import_node_crypto68 = require("node:crypto");
|
|
246389
|
+
init_pg_ident();
|
|
245962
246390
|
init_esm();
|
|
245963
246391
|
ident12 = (s2) => {
|
|
245964
|
-
|
|
246392
|
+
quoteIdent2(s2);
|
|
245965
246393
|
return s2;
|
|
245966
246394
|
};
|
|
245967
246395
|
PostgresNodeStore = class _PostgresNodeStore {
|
|
@@ -246226,8 +246654,9 @@ var init_postgres_model_prices = __esm({
|
|
|
246226
246654
|
"../storage/src/postgres-model-prices.ts"() {
|
|
246227
246655
|
"use strict";
|
|
246228
246656
|
init_esm();
|
|
246657
|
+
init_pg_ident();
|
|
246229
246658
|
ident13 = (s2) => {
|
|
246230
|
-
|
|
246659
|
+
quoteIdent2(s2);
|
|
246231
246660
|
return s2;
|
|
246232
246661
|
};
|
|
246233
246662
|
PostgresModelPriceStore = class _PostgresModelPriceStore {
|
|
@@ -246437,10 +246866,11 @@ var init_postgres_actor_memory = __esm({
|
|
|
246437
246866
|
"../storage/src/postgres-actor-memory.ts"() {
|
|
246438
246867
|
"use strict";
|
|
246439
246868
|
import_node_crypto69 = require("node:crypto");
|
|
246869
|
+
init_pg_ident();
|
|
246440
246870
|
init_src();
|
|
246441
246871
|
matchClause = (q2) => q2.requireMatch && q2.keywords.length > 0 ? " WHERE m > 0" : "";
|
|
246442
246872
|
ident14 = (s2) => {
|
|
246443
|
-
|
|
246873
|
+
quoteIdent2(s2);
|
|
246444
246874
|
return s2;
|
|
246445
246875
|
};
|
|
246446
246876
|
PostgresActorMemoryStore = class _PostgresActorMemoryStore {
|
|
@@ -246746,9 +247176,10 @@ var init_postgres_inbox_read = __esm({
|
|
|
246746
247176
|
"../storage/src/postgres-inbox-read.ts"() {
|
|
246747
247177
|
"use strict";
|
|
246748
247178
|
init_esm();
|
|
247179
|
+
init_pg_ident();
|
|
246749
247180
|
SEED_MARKER_ACTOR = "actor:system";
|
|
246750
247181
|
ident15 = (s2) => {
|
|
246751
|
-
|
|
247182
|
+
quoteIdent2(s2);
|
|
246752
247183
|
return s2;
|
|
246753
247184
|
};
|
|
246754
247185
|
PostgresReadMarkerStore = class _PostgresReadMarkerStore {
|
|
@@ -252000,10 +252431,11 @@ var init_postgres_knowledge = __esm({
|
|
|
252000
252431
|
"../storage/src/postgres-knowledge.ts"() {
|
|
252001
252432
|
"use strict";
|
|
252002
252433
|
init_esm();
|
|
252434
|
+
init_pg_ident();
|
|
252003
252435
|
init_pg_sanitize();
|
|
252004
252436
|
init_knowledge_snapshot_crypto();
|
|
252005
252437
|
ident17 = (value2) => {
|
|
252006
|
-
|
|
252438
|
+
quoteIdent2(value2);
|
|
252007
252439
|
return value2;
|
|
252008
252440
|
};
|
|
252009
252441
|
TABLES = [
|
|
@@ -255625,6 +256057,36 @@ async function startServe(opts) {
|
|
|
255625
256057
|
projectId2,
|
|
255626
256058
|
afterName
|
|
255627
256059
|
);
|
|
256060
|
+
},
|
|
256061
|
+
/**
|
|
256062
|
+
* 删项目后的跨域清理(发起人 2026-09-09 拍板的口径里,projects 域自己管不到的那两件):
|
|
256063
|
+
* ① 绑到该项目的会话 `project_id` 置空 —— 会话一条都不删,回到「不指定项目」。不清的话
|
|
256064
|
+
* 项目 chip 会按既定口径显示一串裸项目 id(`pages/chat/FRAME-DATA-MAP.md`)。
|
|
256065
|
+
* ② 该项目作用域下的变量/密钥删掉 —— 项目没了,`resolveProjectVariables` 再也解析不到它们,
|
|
256066
|
+
* 留着只是一堆谁也读不到的明文凭据。
|
|
256067
|
+
* 与改名钩子同款:`chatStoreFor` / `engineRouter` 的 const 都在本域构造之后,所以只能在
|
|
256068
|
+
* handler 体内(删除发生时)解析,不能在工厂里提前引用——那会撞 TDZ。
|
|
256069
|
+
*/
|
|
256070
|
+
onProjectDeletedFor: (companyId) => async ({ projectId: projectId2 }) => {
|
|
256071
|
+
const failures = [];
|
|
256072
|
+
try {
|
|
256073
|
+
const sessions = await chatStoreFor(companyId);
|
|
256074
|
+
if (sessions.clearProjectBinding) await sessions.clearProjectBinding(projectId2);
|
|
256075
|
+
else failures.push("\u4F1A\u8BDD store \u4E0D\u652F\u6301 clearProjectBinding\uFF0C\u4F1A\u8BDD\u7684\u9879\u76EE\u7ED1\u5B9A\u672A\u6E05");
|
|
256076
|
+
} catch (e) {
|
|
256077
|
+
failures.push(`\u6E05\u4F1A\u8BDD\u9879\u76EE\u7ED1\u5B9A\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
256078
|
+
}
|
|
256079
|
+
try {
|
|
256080
|
+
const registry2 = companyId && pgPool ? (await engineRouter.getEngine(companyId)).registry : registryStore;
|
|
256081
|
+
if (registry2.resolveProjectVariables) {
|
|
256082
|
+
for (const row of await registry2.resolveProjectVariables(projectId2)) {
|
|
256083
|
+
await registry2.deleteVariable(row.key, void 0, { projectId: projectId2 });
|
|
256084
|
+
}
|
|
256085
|
+
}
|
|
256086
|
+
} catch (e) {
|
|
256087
|
+
failures.push(`\u6E05\u9879\u76EE\u53D8\u91CF\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
256088
|
+
}
|
|
256089
|
+
if (failures.length) throw new Error(failures.join("\uFF1B"));
|
|
255628
256090
|
}
|
|
255629
256091
|
});
|
|
255630
256092
|
const currentArtifactProjection = async () => {
|
|
@@ -256611,6 +257073,16 @@ async function startServe(opts) {
|
|
|
256611
257073
|
if (!stores.artifacts) throw new Error(`[work-order-files] \u516C\u53F8 ${companyId} \u6CA1\u6709 artifacts store\uFF0C\u62D2\u7EDD\u56DE\u843D\u9ED8\u8BA4\u516C\u53F8`);
|
|
256612
257074
|
return stores.artifacts;
|
|
256613
257075
|
};
|
|
257076
|
+
const projectStateFor = async (companyId) => {
|
|
257077
|
+
if (!chatStoreRouter) return projectStateStore;
|
|
257078
|
+
const stores = await chatStoreRouter.open(companyId, "work-order-projects");
|
|
257079
|
+
if (!stores.projects) throw new Error(`[work-order-projects] \u516C\u53F8 ${companyId} \u6CA1\u6709 projects store\uFF0C\u62D2\u7EDD\u56DE\u843D\u9ED8\u8BA4\u516C\u53F8`);
|
|
257080
|
+
return stores.projects;
|
|
257081
|
+
};
|
|
257082
|
+
const resolveCompanyCodeRepo = makeCompanyCodeRepoResolver(async (companyId) => ({
|
|
257083
|
+
projects: await projectStateFor(companyId),
|
|
257084
|
+
bindings: await artifactStateFor(companyId)
|
|
257085
|
+
}));
|
|
256614
257086
|
if (pgDsn && pgPool && chatStoreRouter) {
|
|
256615
257087
|
const router = chatStoreRouter;
|
|
256616
257088
|
void (async () => {
|
|
@@ -256799,7 +257271,6 @@ async function startServe(opts) {
|
|
|
256799
257271
|
}
|
|
256800
257272
|
return token;
|
|
256801
257273
|
};
|
|
256802
|
-
let resolveCodeRepoRef;
|
|
256803
257274
|
let coordinatorRef = null;
|
|
256804
257275
|
let coordinatorSpawnProbe = null;
|
|
256805
257276
|
const dispatchTimers = /* @__PURE__ */ new Set();
|
|
@@ -256959,9 +257430,9 @@ async function startServe(opts) {
|
|
|
256959
257430
|
port: opts.port ?? 7320,
|
|
256960
257431
|
projectState: projectStateStore,
|
|
256961
257432
|
artifactState: artifactStateStore,
|
|
256962
|
-
//
|
|
257433
|
+
// 建单的项目校验、绑定与输入文件按当前公司落库;与项目/任务读侧使用同一个 router。
|
|
256963
257434
|
// 装了路由(有 PG)才给——dev 文件模式没有 schema 概念,沿用上面那个单实例。
|
|
256964
|
-
...chatStoreRouter ? { artifactStateFor } : {},
|
|
257435
|
+
...chatStoreRouter ? { artifactStateFor, projectStateFor } : {},
|
|
256965
257436
|
schema,
|
|
256966
257437
|
registry: registryStore,
|
|
256967
257438
|
// 会话级模型覆盖的候选清单:与 `GET /api/runtimes/:id/models` **同一份**实现
|
|
@@ -257031,7 +257502,7 @@ async function startServe(opts) {
|
|
|
257031
257502
|
});
|
|
257032
257503
|
return { plannerActor: planner.id, text: chunks.join("") };
|
|
257033
257504
|
},
|
|
257034
|
-
resolveCodeRepo:
|
|
257505
|
+
resolveCodeRepo: resolveCompanyCodeRepo,
|
|
257035
257506
|
// propose sha 校验兼镜像根:verifyExternalPinSha 在 ls-remote 未命中(祖先提交)时 `git fetch <remote> <sha>`
|
|
257036
257507
|
// 进此目录留档(校验即留档,见 git/collect.ts)。目录按需 `init --bare`。(历史:曾是合并器/CI 闸的 git-mirrors 根,已删。)
|
|
257037
257508
|
gitMirrorRoot: path29.join(opts.dir, "propose-mirrors"),
|
|
@@ -257108,8 +257579,8 @@ async function startServe(opts) {
|
|
|
257108
257579
|
collabDomain({
|
|
257109
257580
|
kernel: defaultCompanyKernel,
|
|
257110
257581
|
oplog,
|
|
257111
|
-
blobs,
|
|
257112
|
-
//
|
|
257582
|
+
blobs: engine.blobs,
|
|
257583
|
+
// 页头正文读写与内容接口同源:默认公司的事实柜。
|
|
257113
257584
|
// 资产柜:POST /api/workorders/:id/files 上传字节落这里(与 uploadChatAttachment 同一柜),
|
|
257114
257585
|
// 再让 artifacts.addWorkOrderFile 写行——手动上传不进 oplog / events,删了不伤重放。
|
|
257115
257586
|
assets,
|
|
@@ -259128,11 +259599,10 @@ async function startServe(opts) {
|
|
|
259128
259599
|
for (const w2 of projectsCfg?.workspaces ?? []) {
|
|
259129
259600
|
await artifactStateStore.upsertWorkspaceBinding({ workOrderId: w2.id, projectId: w2.projectId });
|
|
259130
259601
|
}
|
|
259131
|
-
const {
|
|
259602
|
+
const { resolveProject } = makeStoreCodeRepoResolver({
|
|
259132
259603
|
projects: projectStateStore,
|
|
259133
259604
|
bindings: artifactStateStore
|
|
259134
259605
|
});
|
|
259135
|
-
resolveCodeRepoRef = resolveCodeRepo;
|
|
259136
259606
|
if (opts.dispatch) {
|
|
259137
259607
|
const startDispatcher = (companyId, companyEngine) => {
|
|
259138
259608
|
if (dispatchers.has(companyId)) return;
|
|
@@ -259408,8 +259878,7 @@ async function startServe(opts) {
|
|
|
259408
259878
|
} : {},
|
|
259409
259879
|
schema: liveSchemaMap,
|
|
259410
259880
|
// 活视图:类型目录变更后由 rebuildLiveSchema 原地重填(Dispatcher 每轮现读 opts.schema)
|
|
259411
|
-
resolveCodeRepo,
|
|
259412
|
-
// §4/C:异步从权威源(store)解析;dispatcher 装配前 await
|
|
259881
|
+
resolveCodeRepo: (ws) => resolveCompanyCodeRepo(ws, companyId),
|
|
259413
259882
|
// #2 派单开关:工单"待派发"(控制面旁存标 held)时不派其活。held 集通常空、表极小,每次查可接受。
|
|
259414
259883
|
resolveDispatchHold: async (ws) => (await artifactStateStore.listWorkspaceDispatchHeld()).includes(ws),
|
|
259415
259884
|
// §D3 评审人连续性:锁定审核人不可用(熔断/离岗)→ 系统身份幂等升级,交管理者改派(不静默换人)。
|
|
@@ -260631,7 +261100,7 @@ ${nodeFault}` : "");
|
|
|
260631
261100
|
log: (m2) => console.log(m2),
|
|
260632
261101
|
batchLimit: envNum("OASIS_AUTOMATION_EVENT_BATCH", 200),
|
|
260633
261102
|
// 工单→项目绑定是合法薄旁存(B5),不是内核事实——列项目下的工单只认它。
|
|
260634
|
-
listProjectWorkspaces: async ({ projectId: projectId2 }) => (await
|
|
261103
|
+
listProjectWorkspaces: async ({ companyId, projectId: projectId2 }) => (await (await artifactStateFor(companyId)).listWorkspaceBindings()).filter((b2) => b2.projectId === projectId2).map((b2) => b2.workOrderId),
|
|
260635
261104
|
// 完成判据必须复用 D3 resolver(不复制 deriveStage),且与成员快照同一次读取。
|
|
260636
261105
|
// ensureTracked:内核跟踪集只在启动时装载一次、此后只随本进程写入增长——多实例部署下,
|
|
260637
261106
|
// 另一实例建出来的工单在这里会被读成 unknown(既不触发也不留痕,QA OBS-1 实测)。
|
|
@@ -260642,7 +261111,7 @@ ${nodeFault}` : "");
|
|
|
260642
261111
|
return makeAutomationWorkorderSnapshotResolver(engine2.kernel)(workspace);
|
|
260643
261112
|
},
|
|
260644
261113
|
// 触发时复查归因主体的项目读取权(与配置期同一口径)。
|
|
260645
|
-
projectReadable: async ({ projectId: projectId2 }) => await
|
|
261114
|
+
projectReadable: async ({ companyId, projectId: projectId2 }) => await (await projectStateFor(companyId)).getProject(projectId2) !== null
|
|
260646
261115
|
});
|
|
260647
261116
|
automationEventTimer = setInterval(() => {
|
|
260648
261117
|
void eventPoller.tick().catch((err) => console.error(`[project-event-poller] tick \u5931\u8D25: ${String(err)}`));
|
|
@@ -267871,7 +268340,7 @@ function shimScript() {
|
|
|
267871
268340
|
}
|
|
267872
268341
|
|
|
267873
268342
|
// src/index.ts
|
|
267874
|
-
var PKG_VERSION = true ? "2.2.
|
|
268343
|
+
var PKG_VERSION = true ? "2.2.2" : "dev";
|
|
267875
268344
|
var LOCAL_BIN = localBin();
|
|
267876
268345
|
var NPM_PREFIX = npmPrefix();
|
|
267877
268346
|
var INSTANCE = DEFAULT_INSTANCE;
|