oasis_test_v2 2.2.0 → 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 +696 -200
- 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),
|
|
@@ -206701,7 +206779,9 @@ var init_memory_registry_store = __esm({
|
|
|
206701
206779
|
return [...this.runtimeModelOverrides.values()].filter((o) => o.runtimeId === runtimeId).map((o) => ({ ...o }));
|
|
206702
206780
|
}
|
|
206703
206781
|
async upsertConnector(c) {
|
|
206704
|
-
this.connectors.
|
|
206782
|
+
const prev = this.connectors.get(c.id);
|
|
206783
|
+
const keptBy = c.connectedBy ?? prev?.connectedBy;
|
|
206784
|
+
this.connectors.set(c.id, { ...c, ...keptBy ? { connectedBy: keptBy } : {} });
|
|
206705
206785
|
}
|
|
206706
206786
|
async listConnectors() {
|
|
206707
206787
|
return [...this.connectors.values()].map((c) => ({ ...c }));
|
|
@@ -206760,7 +206840,12 @@ var init_memory_registry_store = __esm({
|
|
|
206760
206840
|
async putVariable(v2) {
|
|
206761
206841
|
const mk = this._varKey(v2.key, v2.actorId, v2.projectId);
|
|
206762
206842
|
const ex = this.variables.get(mk);
|
|
206763
|
-
|
|
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
|
+
});
|
|
206764
206849
|
}
|
|
206765
206850
|
async touchVariableUsage(key, actorId, projectId2, when) {
|
|
206766
206851
|
const mk = this._varKey(key, actorId, projectId2);
|
|
@@ -209261,6 +209346,9 @@ function foldSingleNodeTasks(items) {
|
|
|
209261
209346
|
}
|
|
209262
209347
|
return folded;
|
|
209263
209348
|
}
|
|
209349
|
+
function engineContentKind(k2) {
|
|
209350
|
+
return k2 === "manifest" ? "manifest" : k2 === "external" ? "external-pin" : "inline-blob";
|
|
209351
|
+
}
|
|
209264
209352
|
function namedError(name, message) {
|
|
209265
209353
|
const err = new Error(message);
|
|
209266
209354
|
err.name = name;
|
|
@@ -209482,6 +209570,13 @@ var init_service4 = __esm({
|
|
|
209482
209570
|
async listProjects() {
|
|
209483
209571
|
return [...this.projects.values()].map((project) => ({ ...project }));
|
|
209484
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
|
+
}
|
|
209485
209580
|
async replaceProjectMembers(projectId2, members) {
|
|
209486
209581
|
this.members.set(projectId2, new Map(members.map((member) => [member.actorId, cloneProjectMember(member)])));
|
|
209487
209582
|
}
|
|
@@ -209627,6 +209722,7 @@ var init_service4 = __esm({
|
|
|
209627
209722
|
}
|
|
209628
209723
|
projectCreatedHandler;
|
|
209629
209724
|
onProjectNameChanged;
|
|
209725
|
+
onProjectDeleted;
|
|
209630
209726
|
/**
|
|
209631
209727
|
* Cross-domain follow-up for project-scoped resources. Project persistence stays authoritative:
|
|
209632
209728
|
* a failed follow-up is reported but never turns an already-created project into a false 500.
|
|
@@ -209641,6 +209737,10 @@ var init_service4 = __esm({
|
|
|
209641
209737
|
setOnProjectNameChanged(handler) {
|
|
209642
209738
|
this.onProjectNameChanged = handler;
|
|
209643
209739
|
}
|
|
209740
|
+
/** 删项目后的跨域清理钩子(会话解绑 + 项目变量清理)。装配层注入;缺省 = 无副作用。 */
|
|
209741
|
+
setOnProjectDeleted(handler) {
|
|
209742
|
+
this.onProjectDeleted = handler;
|
|
209743
|
+
}
|
|
209644
209744
|
/** 工单→项目绑定查询(content-location add 的项目匹配守卫用)。 */
|
|
209645
209745
|
async getWorkspaceBinding(workOrderId) {
|
|
209646
209746
|
return this.artifacts.getWorkspaceBinding(workOrderId);
|
|
@@ -210029,6 +210129,60 @@ var init_service4 = __esm({
|
|
|
210029
210129
|
await this.artifacts.upsertWorkspaceBinding(binding);
|
|
210030
210130
|
return binding;
|
|
210031
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
|
+
}
|
|
210032
210186
|
/**
|
|
210033
210187
|
* 项目关联工单(帧 `1355:4918`):把内部私有的 `listProjectWorkorders` 暴露给路由层。
|
|
210034
210188
|
* `projectId="proj:uncategorized"` 时聚合所有 `proj_tmp_*` 绑定的工单。
|
|
@@ -210138,6 +210292,9 @@ var init_service4 = __esm({
|
|
|
210138
210292
|
size: file?.size ?? null,
|
|
210139
210293
|
taskId,
|
|
210140
210294
|
taskTitle,
|
|
210295
|
+
// 内容形态/引用只挂在**整份产物**那一行;装箱单摊出来的成员行有自己的后缀,
|
|
210296
|
+
// 按后缀走图标即可,挂上 manifest 反而会把 `Dockerfile` 这种无后缀成员画成压缩包。
|
|
210297
|
+
...file ? {} : { contentKind: revision.contentKind, contentRef: revision.contentRef },
|
|
210141
210298
|
creatorKind: creatorId ? creatorId.startsWith("actor:human:") ? "human" : "agent" : null,
|
|
210142
210299
|
creatorId,
|
|
210143
210300
|
creatorName: creatorId ? this.resolveActorName(creatorId) : null,
|
|
@@ -210292,6 +210449,8 @@ var init_service4 = __esm({
|
|
|
210292
210449
|
depth: 2,
|
|
210293
210450
|
type: artifactType || "",
|
|
210294
210451
|
size: null,
|
|
210452
|
+
contentKind: engineContentKind(a.contentKind),
|
|
210453
|
+
...a.contentRef ? { contentRef: a.contentRef } : {},
|
|
210295
210454
|
taskId: workOrderId,
|
|
210296
210455
|
taskTitle,
|
|
210297
210456
|
creatorKind,
|
|
@@ -210643,7 +210802,6 @@ var init_service4 = __esm({
|
|
|
210643
210802
|
const worksById = new Map(snap.works.map((w2) => [w2.id, w2]));
|
|
210644
210803
|
const binding = this.artifacts.getWorkspaceBinding ? await this.artifacts.getWorkspaceBinding(workOrderId).catch(() => null) : null;
|
|
210645
210804
|
const projectId2 = binding?.projectId ?? ephemeralProjectIdFor(workOrderId);
|
|
210646
|
-
const mapContentKind = (k2) => k2 === "manifest" ? "manifest" : k2 === "external" ? "external-pin" : "inline-blob";
|
|
210647
210805
|
const out = [];
|
|
210648
210806
|
for (const node2 of snap.nodes) {
|
|
210649
210807
|
if (!node2.latestAcceptId) continue;
|
|
@@ -210663,7 +210821,7 @@ var init_service4 = __esm({
|
|
|
210663
210821
|
contentRef: a.contentRef,
|
|
210664
210822
|
title: node2.title || a.name,
|
|
210665
210823
|
// 节点标题优先;否则用 engine artifact 的 "output" 兜底
|
|
210666
|
-
contentKind:
|
|
210824
|
+
contentKind: engineContentKind(a.contentKind)
|
|
210667
210825
|
}));
|
|
210668
210826
|
out.push({
|
|
210669
210827
|
outputId: `engine:${acceptedId}`,
|
|
@@ -217497,6 +217655,20 @@ ${input.description}
|
|
|
217497
217655
|
upsertConnector(c) {
|
|
217498
217656
|
return this.opts.store.upsertConnector(c);
|
|
217499
217657
|
}
|
|
217658
|
+
/**
|
|
217659
|
+
* 记下「配置人」(子 PRD《组织页》§3.3:连接器详情展示配置人)。
|
|
217660
|
+
*
|
|
217661
|
+
* **只动这一格**:读回整条再原样写回、只覆盖 `connectedBy`,不碰 status / account / verifiedAt
|
|
217662
|
+
* ——那几样是授权流程刚写进去的,调用方(控制台)手上的未必是最新值,整条覆盖会把它们写旧。
|
|
217663
|
+
* 连接器不存在时抛,不悄悄建一条空记录。
|
|
217664
|
+
*/
|
|
217665
|
+
async setConnectorConfiguredBy(connectorId, actorId) {
|
|
217666
|
+
const current = (await this.opts.store.listConnectors()).find((c) => c.id === connectorId);
|
|
217667
|
+
if (!current) throw new Error(`connector not found: ${connectorId}`);
|
|
217668
|
+
const next = { ...current, connectedBy: actorId };
|
|
217669
|
+
await this.opts.store.upsertConnector(next);
|
|
217670
|
+
return next;
|
|
217671
|
+
}
|
|
217500
217672
|
listConnectors() {
|
|
217501
217673
|
return this.opts.store.listConnectors();
|
|
217502
217674
|
}
|
|
@@ -217702,6 +217874,8 @@ ${input.description}
|
|
|
217702
217874
|
}
|
|
217703
217875
|
await this.opts.store.putVariable({
|
|
217704
217876
|
key: v2.key,
|
|
217877
|
+
// 展示名(子 PRD《密钥管理》§2)。没传就不传下去——存储层按「不动」处理,不抹既有名字。
|
|
217878
|
+
...v2.name !== void 0 && v2.name !== "" ? { name: v2.name } : {},
|
|
217705
217879
|
scope: v2.scope,
|
|
217706
217880
|
...v2.actorId !== void 0 ? { actorId: v2.actorId } : {},
|
|
217707
217881
|
...v2.projectId !== void 0 ? { projectId: v2.projectId } : {},
|
|
@@ -217770,6 +217944,7 @@ ${input.description}
|
|
|
217770
217944
|
const isPlain = r.valueEncrypted.startsWith("plain:");
|
|
217771
217945
|
return {
|
|
217772
217946
|
key: r.key,
|
|
217947
|
+
...r.name !== void 0 ? { name: r.name } : {},
|
|
217773
217948
|
scope: r.scope,
|
|
217774
217949
|
...r.actorId !== void 0 ? { actorId: r.actorId } : {},
|
|
217775
217950
|
...r.projectId !== void 0 ? { projectId: r.projectId } : {},
|
|
@@ -219512,6 +219687,13 @@ function actorsDomain(opts) {
|
|
|
219512
219687
|
});
|
|
219513
219688
|
return { status: 201, body: { ok: true } };
|
|
219514
219689
|
});
|
|
219690
|
+
router.post("/api/connectors/:id/configured-by", async (req) => {
|
|
219691
|
+
const { service } = await resolveCtx(req.auth.companyId);
|
|
219692
|
+
const conn = await service.setConnectorConfiguredBy(req.params.id, req.auth.actor).catch((e) => {
|
|
219693
|
+
throw new ApiError(404, "NOT_FOUND", e.message);
|
|
219694
|
+
});
|
|
219695
|
+
return { status: 200, body: conn };
|
|
219696
|
+
});
|
|
219515
219697
|
router.get("/api/actors/:id/connectors", async (req) => {
|
|
219516
219698
|
const { service } = await resolveCtx(req.auth.companyId);
|
|
219517
219699
|
return { status: 200, body: await service.actorConnectorState(req.params.id) };
|
|
@@ -219590,6 +219772,8 @@ function actorsDomain(opts) {
|
|
|
219590
219772
|
key: b2.key,
|
|
219591
219773
|
value: b2.value,
|
|
219592
219774
|
scope,
|
|
219775
|
+
// 展示名(子 PRD《密钥管理》§2「名称」);不传 = 不改既有名字。
|
|
219776
|
+
...typeof b2.name === "string" && b2.name.trim() ? { name: b2.name.trim() } : {},
|
|
219593
219777
|
...scope === "project" ? { projectId: b2.projectId } : {},
|
|
219594
219778
|
...b2.deliveryMode !== void 0 ? { deliveryMode: b2.deliveryMode } : {},
|
|
219595
219779
|
...b2.connectorId !== void 0 ? { connectorId: b2.connectorId } : {},
|
|
@@ -219661,6 +219845,8 @@ function actorsDomain(opts) {
|
|
|
219661
219845
|
value: b2.value,
|
|
219662
219846
|
actorId,
|
|
219663
219847
|
scope: "personal",
|
|
219848
|
+
// 展示名(同上)。组织页「添加密钥 → 新建」走的就是这条端点。
|
|
219849
|
+
...typeof b2.name === "string" && b2.name.trim() ? { name: b2.name.trim() } : {},
|
|
219664
219850
|
...b2.deliveryMode !== void 0 ? { deliveryMode: b2.deliveryMode } : {},
|
|
219665
219851
|
...b2.connectorId !== void 0 ? { connectorId: b2.connectorId } : {},
|
|
219666
219852
|
...b2.overrides !== void 0 ? { overrides: b2.overrides } : {},
|
|
@@ -220717,6 +220903,22 @@ function projectsDomain(opts) {
|
|
|
220717
220903
|
throw mapProjectError(err);
|
|
220718
220904
|
}
|
|
220719
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
|
+
});
|
|
220720
220922
|
router.get("/api/projects/:id/members", async (req) => {
|
|
220721
220923
|
try {
|
|
220722
220924
|
const members = await (await svc(req)).listProjectMembers(req.params.id);
|
|
@@ -221139,6 +221341,9 @@ function mapProjectError(err) {
|
|
|
221139
221341
|
if (err instanceof Error && err.message.startsWith("project not found:")) {
|
|
221140
221342
|
return new ApiError(404, "NOT_FOUND", err.message);
|
|
221141
221343
|
}
|
|
221344
|
+
if (err instanceof Error && err.message.startsWith("project not deletable:")) {
|
|
221345
|
+
return new ApiError(400, "PROJECT_NOT_DELETABLE", err.message);
|
|
221346
|
+
}
|
|
221142
221347
|
if (err instanceof Error && err.message.startsWith("missing artifact revision registration:")) {
|
|
221143
221348
|
return new ApiError(400, "MISSING_ARTIFACT_REVISION", err.message);
|
|
221144
221349
|
}
|
|
@@ -221446,6 +221651,10 @@ function toApiProjectFilesView(view) {
|
|
|
221446
221651
|
file_path: item.filePath ?? null,
|
|
221447
221652
|
depth: item.depth,
|
|
221448
221653
|
type: item.type,
|
|
221654
|
+
// 内容形态 + 内容引用:前端名称列给**没有后缀**的行选图标用(正文→文本、git 提交→代码、
|
|
221655
|
+
// 其它外部引用→链接)。交付物整份产物行才有,上传文件与文件夹为 null。
|
|
221656
|
+
content_kind: item.contentKind ?? null,
|
|
221657
|
+
content_ref: item.contentRef ?? null,
|
|
221449
221658
|
size: item.size,
|
|
221450
221659
|
task_id: item.taskId,
|
|
221451
221660
|
task_title: item.taskTitle,
|
|
@@ -221501,6 +221710,10 @@ function createProjectsDomain(opts) {
|
|
|
221501
221710
|
const handler = opts.onProjectNameChangedFor(companyId);
|
|
221502
221711
|
if (handler) svc.setOnProjectNameChanged(handler);
|
|
221503
221712
|
}
|
|
221713
|
+
if (opts.onProjectDeletedFor) {
|
|
221714
|
+
const handler = opts.onProjectDeletedFor(companyId);
|
|
221715
|
+
if (handler) svc.setOnProjectDeleted(handler);
|
|
221716
|
+
}
|
|
221504
221717
|
return svc;
|
|
221505
221718
|
};
|
|
221506
221719
|
const service = build(void 0, void 0);
|
|
@@ -222326,8 +222539,8 @@ var init_offboarding = __esm({
|
|
|
222326
222539
|
return { ownedAgents, inFlight };
|
|
222327
222540
|
}
|
|
222328
222541
|
/**
|
|
222329
|
-
* 现在**能不能直接删**这个成员。DELETE
|
|
222330
|
-
*
|
|
222542
|
+
* 现在**能不能直接删**这个成员。DELETE 与 commit 共用它,不会两处各判一套。
|
|
222543
|
+
* 交接删掉之后这里只剩两条:不是成员(NOT_FOUND)、公司最后一位 owner(LAST_OWNER)。
|
|
222331
222544
|
*/
|
|
222332
222545
|
async blockersForRemoval(companyId, accountId) {
|
|
222333
222546
|
const blockers = [];
|
|
@@ -222510,6 +222723,15 @@ function companiesDomain(opts) {
|
|
|
222510
222723
|
if (!agentCredentials) throw new ApiError(501, "NOT_IMPLEMENTED", "\u672C\u90E8\u7F72\u672A\u63A5\u5165 agent \u51ED\u8BC1");
|
|
222511
222724
|
return agentCredentials;
|
|
222512
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
|
+
};
|
|
222513
222735
|
const requireIfMatch = (req) => {
|
|
222514
222736
|
const raw = req.raw.headers["if-match"];
|
|
222515
222737
|
const text5 = (typeof raw === "string" ? raw : Array.isArray(raw) ? raw[0] : void 0)?.replace(/^"|"$/g, "");
|
|
@@ -222625,6 +222847,7 @@ function companiesDomain(opts) {
|
|
|
222625
222847
|
throw new ApiError(400, "BAD_REQUEST", "displayName \u987B\u4E3A\u975E\u7A7A\u5B57\u7B26\u4E32");
|
|
222626
222848
|
}
|
|
222627
222849
|
member = await service.setMemberDisplayName(cid, aid, b2.displayName).catch(mapErr);
|
|
222850
|
+
await syncActorNameToDisplayName(member, req.auth.actor);
|
|
222628
222851
|
}
|
|
222629
222852
|
return { status: 200, body: member };
|
|
222630
222853
|
});
|
|
@@ -224675,6 +224898,8 @@ function buildWorkorderActivity(input) {
|
|
|
224675
224898
|
const groupKey = (nodeId) => `${nodeId}#${groupIndexOf.get(nodeId) ?? 0}`;
|
|
224676
224899
|
const reviewCards = /* @__PURE__ */ new Map();
|
|
224677
224900
|
const issueCards = /* @__PURE__ */ new Map();
|
|
224901
|
+
const workOfIssue = /* @__PURE__ */ new Map();
|
|
224902
|
+
const plainCommentIssueIds = /* @__PURE__ */ new Set();
|
|
224678
224903
|
const acceptCards = /* @__PURE__ */ new Map();
|
|
224679
224904
|
const standalone = [];
|
|
224680
224905
|
const touch = (d, rec) => {
|
|
@@ -224732,10 +224957,22 @@ function buildWorkorderActivity(input) {
|
|
|
224732
224957
|
const executorId = w2?.assigneeActorId ?? rec.actorId;
|
|
224733
224958
|
if (isPendingActor(executorId)) break;
|
|
224734
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
|
+
}
|
|
224735
224971
|
const reworkReason = str4(reasons.find((r) => r.kind === "rework")?.reason);
|
|
224736
224972
|
const key = groupKey(nodeId);
|
|
224737
224973
|
const existingGroup = groupCards.get(key);
|
|
224738
224974
|
if (existingGroup) {
|
|
224975
|
+
if (!existingGroup.traceWorkIds.includes(workId)) existingGroup.traceWorkIds.push(workId);
|
|
224739
224976
|
touch(existingGroup, rec);
|
|
224740
224977
|
existingGroup.executorId = executorId;
|
|
224741
224978
|
existingGroup.phase = "running";
|
|
@@ -224748,6 +224985,7 @@ function buildWorkorderActivity(input) {
|
|
|
224748
224985
|
}
|
|
224749
224986
|
const fresh = {
|
|
224750
224987
|
id: `work:${workId}`,
|
|
224988
|
+
traceWorkIds: [workId],
|
|
224751
224989
|
seq: rec.seq,
|
|
224752
224990
|
at: rec.createdAt,
|
|
224753
224991
|
updatedAt: rec.createdAt,
|
|
@@ -224790,8 +225028,9 @@ function buildWorkorderActivity(input) {
|
|
|
224790
225028
|
}
|
|
224791
225029
|
break;
|
|
224792
225030
|
}
|
|
225031
|
+
const engineVerdict = workById.get(workId)?.status;
|
|
224793
225032
|
touch(d, rec);
|
|
224794
|
-
if (ev.outcome === "failed") {
|
|
225033
|
+
if (ev.outcome === "failed" && engineVerdict !== "success") {
|
|
224795
225034
|
d.phase = "stuck";
|
|
224796
225035
|
d.stuckReason = "failed";
|
|
224797
225036
|
d.status = ACTIVITY_COPY.failed(nameOf(d.executorId));
|
|
@@ -225023,6 +225262,7 @@ function buildWorkorderActivity(input) {
|
|
|
225023
225262
|
});
|
|
225024
225263
|
break;
|
|
225025
225264
|
}
|
|
225265
|
+
if (!isGap && !isEscalation && !isBackpressure) plainCommentIssueIds.add(issueId);
|
|
225026
225266
|
const nodeAssignee = nodeId ? nodeById.get(nodeId)?.assigneeActorId : void 0;
|
|
225027
225267
|
const commentCounterpart = nodeAssignee ?? ownerId;
|
|
225028
225268
|
const commentTargetId = commentCounterpart && commentCounterpart !== authorId ? commentCounterpart : void 0;
|
|
@@ -225111,7 +225351,10 @@ function buildWorkorderActivity(input) {
|
|
|
225111
225351
|
d.phase = "done";
|
|
225112
225352
|
d.actions = [];
|
|
225113
225353
|
d.executorId = resolverId;
|
|
225114
|
-
|
|
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));
|
|
225115
225358
|
break;
|
|
225116
225359
|
}
|
|
225117
225360
|
case "plan.node_retry": {
|
|
@@ -225166,12 +225409,37 @@ function buildWorkorderActivity(input) {
|
|
|
225166
225409
|
...issueCards.values(),
|
|
225167
225410
|
...acceptCards.values()
|
|
225168
225411
|
].sort((a, b2) => a.updatedAt.localeCompare(b2.updatedAt) || a.seq - b2.seq || a.id.localeCompare(b2.id));
|
|
225169
|
-
const
|
|
225170
|
-
const
|
|
225171
|
-
|
|
225172
|
-
|
|
225173
|
-
if (
|
|
225174
|
-
|
|
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;
|
|
225175
225443
|
};
|
|
225176
225444
|
const cards = drafts.map((d) => ({
|
|
225177
225445
|
id: d.id,
|
|
@@ -225186,7 +225454,8 @@ function buildWorkorderActivity(input) {
|
|
|
225186
225454
|
...d.detail ? { detail: d.detail } : {},
|
|
225187
225455
|
artifacts: d.artifacts,
|
|
225188
225456
|
actions: d.actions,
|
|
225189
|
-
|
|
225457
|
+
...d.traceWorkIds?.length ? { traceWorkIds: d.traceWorkIds, traceAvailable: true } : {},
|
|
225458
|
+
.../* @__PURE__ */ ((r) => r ? { runId: r, traceAvailable: true } : {})(runIdOfCard(d))
|
|
225190
225459
|
}));
|
|
225191
225460
|
return { workorderId, cards, truncated };
|
|
225192
225461
|
}
|
|
@@ -225195,6 +225464,7 @@ var init_activity2 = __esm({
|
|
|
225195
225464
|
"../server/src/domains/collab/activity.ts"() {
|
|
225196
225465
|
"use strict";
|
|
225197
225466
|
init_src();
|
|
225467
|
+
init_src4();
|
|
225198
225468
|
init_workorder_manager();
|
|
225199
225469
|
ACTIVITY_EVENT_KINDS = [
|
|
225200
225470
|
"plan.changed",
|
|
@@ -225324,6 +225594,14 @@ var init_activity2 = __esm({
|
|
|
225324
225594
|
* 留言 / 回压收口——**同样一句索引**。收口 note 与开口正文同源、同样能长到几千字:
|
|
225325
225595
|
* 全库实测 `ws:wo-56bba3c1` 那条收口后小状态仍是 **8096 字**。全文去留言线看。
|
|
225326
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`,
|
|
225327
225605
|
commentResolved: (by, node2) => `${by} \u5DF2\u5904\u7406\u300A${node2}\u300B\u7684\u7559\u8A00\u3002`,
|
|
225328
225606
|
issueResolved: (note) => note
|
|
225329
225607
|
};
|
|
@@ -225347,6 +225625,125 @@ function targetOf(cardId) {
|
|
|
225347
225625
|
return null;
|
|
225348
225626
|
}
|
|
225349
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) {
|
|
225350
225747
|
const { workorderId, cardId, snap } = input;
|
|
225351
225748
|
const ref2 = input.ref ?? ((id) => ({ id }));
|
|
225352
225749
|
const empty2 = { workorderId, cardId, attempts: [] };
|
|
@@ -225359,26 +225756,6 @@ function buildWorkorderActivityTrace(input) {
|
|
|
225359
225756
|
const nodeId = work?.nodeId ?? review?.nodeId;
|
|
225360
225757
|
if (!nodeId) return empty2;
|
|
225361
225758
|
const node2 = nodeById.get(nodeId);
|
|
225362
|
-
const nodesWithInEdges = new Set(snap.edges.map((e) => e.toNodeId));
|
|
225363
|
-
const isRootBrief = node2?.type === "brief" && !nodesWithInEdges.has(nodeId);
|
|
225364
|
-
const artifactsOfWork = (workId) => {
|
|
225365
|
-
const list2 = [];
|
|
225366
|
-
for (const a of snap.artifacts) {
|
|
225367
|
-
if (a.workId !== workId) continue;
|
|
225368
|
-
const artifactNodeId = a.nodeId;
|
|
225369
|
-
const files = a.contentKind === "manifest" ? input.manifestFiles?.get(a.contentRef) : void 0;
|
|
225370
|
-
if (files && files.length > 0) {
|
|
225371
|
-
for (const path41 of files) {
|
|
225372
|
-
list2.push({ id: a.id, name: path41.split("/").pop() || path41, path: path41, nodeId: artifactNodeId });
|
|
225373
|
-
}
|
|
225374
|
-
} else if (isRootBrief) {
|
|
225375
|
-
list2.push({ id: a.id, name: "\u4EFB\u52A1\u76EE\u6807", path: "\u4EFB\u52A1\u76EE\u6807.md", nodeId: artifactNodeId });
|
|
225376
|
-
} else {
|
|
225377
|
-
list2.push({ id: a.id, name: a.name, nodeId: artifactNodeId });
|
|
225378
|
-
}
|
|
225379
|
-
}
|
|
225380
|
-
return list2;
|
|
225381
|
-
};
|
|
225382
225759
|
const managerId = resolveWorkorderManager(snap) ?? snap.workorder.ownerActorId;
|
|
225383
225760
|
const startedAt = work?.createdAt ?? review?.createdAt;
|
|
225384
225761
|
const spec = node2?.spec?.trim();
|
|
@@ -225388,18 +225765,18 @@ function buildWorkorderActivityTrace(input) {
|
|
|
225388
225765
|
nodeTitle: node2?.title || nodeId.split(":").slice(2).join(":") || nodeId,
|
|
225389
225766
|
...spec ? { spec } : {}
|
|
225390
225767
|
};
|
|
225391
|
-
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));
|
|
225392
225769
|
const executorId = work?.assigneeActorId ?? review?.reviewerActorId;
|
|
225393
225770
|
const conclusion = work?.conclusion?.trim() || review?.note?.trim() || void 0;
|
|
225394
225771
|
const attempts = rows.map((d, i) => {
|
|
225395
225772
|
const isLast = i === rows.length - 1;
|
|
225396
|
-
const actorId =
|
|
225773
|
+
const actorId = d.actorId ?? executorId;
|
|
225397
225774
|
return {
|
|
225398
225775
|
runId: d.id,
|
|
225399
225776
|
at: d.createdAt,
|
|
225400
225777
|
actor: ref2(actorId),
|
|
225401
225778
|
...isLast && conclusion ? { conclusion } : {},
|
|
225402
|
-
artifacts: isLast && work ? artifactsOfWork(work
|
|
225779
|
+
artifacts: isLast && work ? artifactsOfWork(work, input) : []
|
|
225403
225780
|
};
|
|
225404
225781
|
});
|
|
225405
225782
|
return { workorderId, cardId, briefing, attempts };
|
|
@@ -225407,6 +225784,8 @@ function buildWorkorderActivityTrace(input) {
|
|
|
225407
225784
|
var init_activity_trace = __esm({
|
|
225408
225785
|
"../server/src/domains/collab/activity-trace.ts"() {
|
|
225409
225786
|
"use strict";
|
|
225787
|
+
init_src4();
|
|
225788
|
+
init_activity2();
|
|
225410
225789
|
init_workorder_manager();
|
|
225411
225790
|
}
|
|
225412
225791
|
});
|
|
@@ -227260,10 +227639,7 @@ function collabDomain(opts) {
|
|
|
227260
227639
|
const hit = cache.get(companyId);
|
|
227261
227640
|
if (hit) return hit;
|
|
227262
227641
|
const e = await opts.resolveEngine(companyId);
|
|
227263
|
-
|
|
227264
|
-
cache.set(companyId, defaultCtx);
|
|
227265
|
-
return defaultCtx;
|
|
227266
|
-
}
|
|
227642
|
+
const artifacts = opts.resolveArtifacts ? await opts.resolveArtifacts(companyId) : e.kernel === opts.kernel ? opts.artifacts : void 0;
|
|
227267
227643
|
const ctx = {
|
|
227268
227644
|
kernel: e.kernel,
|
|
227269
227645
|
oplog: e.oplog,
|
|
@@ -227273,7 +227649,7 @@ function collabDomain(opts) {
|
|
|
227273
227649
|
// ★ 产物旁存也要按公司取(见 resolveArtifacts 的注释):漏了这一行,非默认公司的
|
|
227274
227650
|
// `/api/workorders/:id/files` 与 node_output 清单恒空——写侧写对了、读侧根本没接线。
|
|
227275
227651
|
// 抛出来就抛出来:宁可 500 说"这家公司的数据面解析不了",也不能回落默认公司的库。
|
|
227276
|
-
...
|
|
227652
|
+
...artifacts ? { artifacts } : {}
|
|
227277
227653
|
};
|
|
227278
227654
|
cache.set(companyId, ctx);
|
|
227279
227655
|
return ctx;
|
|
@@ -227527,7 +227903,10 @@ function collabDomain(opts) {
|
|
|
227527
227903
|
...extra?.avatar ? { avatar: extra.avatar } : {}
|
|
227528
227904
|
};
|
|
227529
227905
|
};
|
|
227530
|
-
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, []];
|
|
227531
227910
|
const manifestFiles = snap ? await resolveActivityManifestFiles(snap.artifacts, blobs) : void 0;
|
|
227532
227911
|
const dispatches = await listDispatchRows(workorderId, opts.dispatchesOfWorkorder);
|
|
227533
227912
|
return {
|
|
@@ -227536,6 +227915,7 @@ function collabDomain(opts) {
|
|
|
227536
227915
|
workorderId,
|
|
227537
227916
|
cardId,
|
|
227538
227917
|
snap,
|
|
227918
|
+
events,
|
|
227539
227919
|
ref: ref2,
|
|
227540
227920
|
...dispatches ? { dispatches } : {},
|
|
227541
227921
|
...manifestFiles ? { manifestFiles } : {}
|
|
@@ -231085,42 +231465,17 @@ function mapChatItemToSnapshotEntry(row) {
|
|
|
231085
231465
|
itemType,
|
|
231086
231466
|
role,
|
|
231087
231467
|
status,
|
|
231088
|
-
|
|
231468
|
+
// ADR-0510 附录 A:版本轴 = 会话逻辑时钟(`chat_items.version`)。
|
|
231469
|
+
// 此前这里填的是 `turnVersion`(轮级 resync 游标),而增量流填的是进程内每 item 计数——
|
|
231470
|
+
// 两套编号空间,一轮中途拉快照就把随后每一帧判 stale,进 resync 死循环。
|
|
231471
|
+
itemVersion: row.version,
|
|
231089
231472
|
seq: row.seq,
|
|
231473
|
+
ord: row.ord,
|
|
231090
231474
|
messageId: row.messageId,
|
|
231091
231475
|
updatedAt: row.updatedAt,
|
|
231092
231476
|
payload: row.payload
|
|
231093
231477
|
};
|
|
231094
231478
|
}
|
|
231095
|
-
function synthesizeMessageItem(msg, seq) {
|
|
231096
|
-
const attachments = Array.isArray(msg.attachments) ? msg.attachments.map((a) => ({
|
|
231097
|
-
name: a.name,
|
|
231098
|
-
...a.blobRef !== void 0 ? { blobRef: a.blobRef } : {},
|
|
231099
|
-
...a.contentType !== void 0 ? { contentType: a.contentType } : {}
|
|
231100
|
-
})) : void 0;
|
|
231101
|
-
const clientSubmitId = msg.role === "user" ? msg.clientSubmitId ?? clientSubmitIdFromMessageKey(msg.messageKey) : void 0;
|
|
231102
|
-
const role = msg.role === "user" || msg.role === "system" ? msg.role : "assistant";
|
|
231103
|
-
const status = msg.status === "running" ? "streaming" : msg.status === "error" ? "failed" : "completed";
|
|
231104
|
-
return {
|
|
231105
|
-
// ADR-0510 D1:有 csid 就用**与账本 user item、前端乐观桶位同名**的身份;没有(历史行 / 老客户端)
|
|
231106
|
-
// 才退回合成 id。三方同名,桶按 itemId 合并才闭环——否则同一条 user 消息画两个气泡。
|
|
231107
|
-
itemId: clientSubmitId ? optimisticUserItemId(clientSubmitId) : `chat-message:${msg.id}`,
|
|
231108
|
-
turnId: msg.runId ?? `chat-message-turn:${msg.id}`,
|
|
231109
|
-
itemType: "message",
|
|
231110
|
-
role,
|
|
231111
|
-
status,
|
|
231112
|
-
itemVersion: 0,
|
|
231113
|
-
seq,
|
|
231114
|
-
messageId: msg.id,
|
|
231115
|
-
updatedAt: msg.completedAt ?? msg.createdAt,
|
|
231116
|
-
payload: {
|
|
231117
|
-
role,
|
|
231118
|
-
text: msg.content ?? "",
|
|
231119
|
-
...attachments?.length ? { attachments } : {},
|
|
231120
|
-
...clientSubmitId ? { clientSubmitId } : {}
|
|
231121
|
-
}
|
|
231122
|
-
};
|
|
231123
|
-
}
|
|
231124
231479
|
function throwWorkdirError(code2) {
|
|
231125
231480
|
switch (code2) {
|
|
231126
231481
|
case "PATH_ESCAPE":
|
|
@@ -231482,57 +231837,50 @@ function createChatSessionsDomain(opts) {
|
|
|
231482
231837
|
if (!session || session.humanActorId !== req.auth.actor) throw new ApiError(404, "NOT_FOUND", "session not found");
|
|
231483
231838
|
const chatSessionId = req.params.id;
|
|
231484
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;
|
|
231485
231844
|
const cursor = opts.liveChat?.cursorFor(chatSessionId) ?? null;
|
|
231486
231845
|
let turnId = cursor?.turnId ?? null;
|
|
231487
|
-
let inflightRunId = opts.liveChat?.runFor(chatSessionId) ?? null;
|
|
231488
231846
|
if (!turnId) {
|
|
231489
231847
|
const turns = await turnsFor(req);
|
|
231490
231848
|
const active = turns ? await turns.activeTurn(chatSessionId).catch(() => null) : null;
|
|
231491
231849
|
turnId = active?.id ?? null;
|
|
231492
|
-
inflightRunId ??= active?.assistantRunId ?? null;
|
|
231493
231850
|
}
|
|
231494
231851
|
const snapshotEntries = [];
|
|
231495
231852
|
let nextSinceVersion = sinceVersion;
|
|
231496
|
-
|
|
231497
|
-
|
|
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;
|
|
231498
231868
|
for (const row of rows) {
|
|
231499
231869
|
snapshotEntries.push(mapChatItemToSnapshotEntry(row));
|
|
231500
231870
|
if (row.turnVersion > nextSinceVersion) nextSinceVersion = row.turnVersion;
|
|
231501
231871
|
}
|
|
231502
231872
|
}
|
|
231503
|
-
const
|
|
231504
|
-
const HISTORY_TURNS = 12;
|
|
231505
|
-
const historicalEntries = [];
|
|
231506
|
-
if (sinceVersion === 0) {
|
|
231507
|
-
const listRecentTurns2 = store2.listRecentTurns;
|
|
231508
|
-
const messages = listRecentTurns2 ? (await listRecentTurns2.call(store2, chatSessionId, HISTORY_TURNS).catch(() => ({ messages: [], hasMoreBefore: false }))).messages : windowMessagesByTurns(await store2.listMessages(chatSessionId).catch(() => []), HISTORY_TURNS).messages;
|
|
231509
|
-
let historySeq = 0;
|
|
231510
|
-
for (const msg of messages) {
|
|
231511
|
-
if (!msg.id) continue;
|
|
231512
|
-
const rows = await items.listItemsByMessage(msg.id).catch(() => []);
|
|
231513
|
-
const historical = turnId ? rows.filter((row) => row.turnId !== turnId) : rows;
|
|
231514
|
-
if (rows.length > 0) {
|
|
231515
|
-
for (const row of [...historical].sort((a, b2) => a.seq - b2.seq)) {
|
|
231516
|
-
historySeq += 1;
|
|
231517
|
-
historicalEntries.push({ ...mapChatItemToSnapshotEntry(row), seq: historySeq });
|
|
231518
|
-
}
|
|
231519
|
-
continue;
|
|
231520
|
-
}
|
|
231521
|
-
if (msg.role === "assistant" && msg.status === "running" && inflightRunId && msg.runId === inflightRunId) continue;
|
|
231522
|
-
const synthesized = synthesizeMessageItem(msg, historySeq + 1);
|
|
231523
|
-
if (activeTurnItemIds.has(synthesized.itemId)) continue;
|
|
231524
|
-
historySeq += 1;
|
|
231525
|
-
historicalEntries.push(synthesized);
|
|
231526
|
-
}
|
|
231527
|
-
}
|
|
231873
|
+
const oldestOrd = snapshotEntries.find((e) => e.ord !== null)?.ord ?? null;
|
|
231528
231874
|
const body2 = {
|
|
231529
231875
|
chatSessionId,
|
|
231530
231876
|
turnId,
|
|
231531
|
-
//
|
|
231532
|
-
items:
|
|
231877
|
+
// 已按 ord 升序——服务端给什么顺序,客户端就按 ord 排出什么顺序,两边同一把尺子。
|
|
231878
|
+
items: snapshotEntries,
|
|
231533
231879
|
nextSinceVersion,
|
|
231534
231880
|
frameCursor: cursor ? { epoch: cursor.epoch, seq: cursor.seq } : { epoch: 0, seq: 0 },
|
|
231535
|
-
canAppend: cursor?.canAppend ?? false
|
|
231881
|
+
canAppend: cursor?.canAppend ?? false,
|
|
231882
|
+
oldestOrd,
|
|
231883
|
+
hasMoreBefore
|
|
231536
231884
|
};
|
|
231537
231885
|
return { status: 200, body: body2 };
|
|
231538
231886
|
});
|
|
@@ -231885,7 +232233,7 @@ function createChatSessionsDomain(opts) {
|
|
|
231885
232233
|
});
|
|
231886
232234
|
};
|
|
231887
232235
|
}
|
|
231888
|
-
var import_node_crypto59, WORKDIR_READ_MAX_BYTES;
|
|
232236
|
+
var import_node_crypto59, DEFAULT_SNAPSHOT_ITEMS, MAX_SNAPSHOT_ITEMS, WORKDIR_READ_MAX_BYTES;
|
|
231889
232237
|
var init_chat_sessions = __esm({
|
|
231890
232238
|
"../server/src/domains/chat-sessions/index.ts"() {
|
|
231891
232239
|
"use strict";
|
|
@@ -231906,6 +232254,8 @@ var init_chat_sessions = __esm({
|
|
|
231906
232254
|
init_workorder_terminal();
|
|
231907
232255
|
init_session_workdir();
|
|
231908
232256
|
init_project_bundle();
|
|
232257
|
+
DEFAULT_SNAPSHOT_ITEMS = 400;
|
|
232258
|
+
MAX_SNAPSHOT_ITEMS = 1e3;
|
|
231909
232259
|
WORKDIR_READ_MAX_BYTES = 2 * 1024 * 1024;
|
|
231910
232260
|
}
|
|
231911
232261
|
});
|
|
@@ -236568,6 +236918,9 @@ function makeStoreCodeRepoResolver(deps) {
|
|
|
236568
236918
|
}
|
|
236569
236919
|
};
|
|
236570
236920
|
}
|
|
236921
|
+
function makeCompanyCodeRepoResolver(storesFor) {
|
|
236922
|
+
return async (ws, companyId) => makeStoreCodeRepoResolver(await storesFor(companyId)).resolveCodeRepo(ws);
|
|
236923
|
+
}
|
|
236571
236924
|
var init_resolve = __esm({
|
|
236572
236925
|
"../server/src/git/resolve.ts"() {
|
|
236573
236926
|
"use strict";
|
|
@@ -237518,16 +237871,28 @@ var init_fused_signal_router = __esm({
|
|
|
237518
237871
|
}
|
|
237519
237872
|
});
|
|
237520
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
|
+
|
|
237521
237885
|
// ../storage/src/postgres.ts
|
|
237522
237886
|
var import_node_crypto67, ident3, isUniqueViolation, PostgresOplogStore, PostgresBlobStore;
|
|
237523
237887
|
var init_postgres = __esm({
|
|
237524
237888
|
"../storage/src/postgres.ts"() {
|
|
237525
237889
|
"use strict";
|
|
237526
237890
|
import_node_crypto67 = require("node:crypto");
|
|
237891
|
+
init_pg_ident();
|
|
237527
237892
|
init_esm();
|
|
237528
237893
|
init_src();
|
|
237529
237894
|
ident3 = (s2) => {
|
|
237530
|
-
|
|
237895
|
+
quoteIdent2(s2);
|
|
237531
237896
|
return s2;
|
|
237532
237897
|
};
|
|
237533
237898
|
isUniqueViolation = (err) => typeof err === "object" && err !== null && err.code === "23505";
|
|
@@ -237749,7 +238114,9 @@ function rowToVariable(row) {
|
|
|
237749
238114
|
updatedAt: new Date(row.updated_at).toISOString(),
|
|
237750
238115
|
// ADR 凭据保险库补章 §D2.1:到期/取用时间戳(text 列存 ISO 字符串,原样带出;null/空 = undefined)。
|
|
237751
238116
|
...row.expires_at ? { expiresAt: row.expires_at } : {},
|
|
237752
|
-
...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 } : {}
|
|
237753
238120
|
};
|
|
237754
238121
|
}
|
|
237755
238122
|
var ident4, PostgresRegistryStore, rowToActor, rowToConfig, rowToAgentPrefs;
|
|
@@ -237757,9 +238124,10 @@ var init_postgres_registry = __esm({
|
|
|
237757
238124
|
"../storage/src/postgres-registry.ts"() {
|
|
237758
238125
|
"use strict";
|
|
237759
238126
|
init_esm();
|
|
238127
|
+
init_pg_ident();
|
|
237760
238128
|
init_src();
|
|
237761
238129
|
ident4 = (s2) => {
|
|
237762
|
-
|
|
238130
|
+
quoteIdent2(s2);
|
|
237763
238131
|
return s2;
|
|
237764
238132
|
};
|
|
237765
238133
|
PostgresRegistryStore = class _PostgresRegistryStore {
|
|
@@ -237854,6 +238222,7 @@ var init_postgres_registry = __esm({
|
|
|
237854
238222
|
account text,
|
|
237855
238223
|
verified_at timestamptz
|
|
237856
238224
|
)`);
|
|
238225
|
+
await pool.query(`ALTER TABLE "${s2}".connectors ADD COLUMN IF NOT EXISTS connected_by text`);
|
|
237857
238226
|
await pool.query(`
|
|
237858
238227
|
CREATE TABLE IF NOT EXISTS "${s2}".actor_connector_connections (
|
|
237859
238228
|
actor_id text NOT NULL,
|
|
@@ -237914,6 +238283,7 @@ var init_postgres_registry = __esm({
|
|
|
237914
238283
|
await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS project_id text`);
|
|
237915
238284
|
await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS environment text`);
|
|
237916
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`);
|
|
237917
238287
|
await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS expires_at text`);
|
|
237918
238288
|
await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS last_used_at text`);
|
|
237919
238289
|
await pool.query(`DROP INDEX IF EXISTS "${s2}"."${s2}_variables_key_actor_uq"`);
|
|
@@ -238224,9 +238594,11 @@ var init_postgres_registry = __esm({
|
|
|
238224
238594
|
/* ---------- 连接器 ---------- */
|
|
238225
238595
|
async upsertConnector(c) {
|
|
238226
238596
|
await this.pool.query(
|
|
238227
|
-
`INSERT INTO ${this.s}.connectors (id, name, mode, status, account, verified_at
|
|
238228
|
-
|
|
238229
|
-
|
|
238597
|
+
`INSERT INTO ${this.s}.connectors (id, name, mode, status, account, verified_at, connected_by)
|
|
238598
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7)
|
|
238599
|
+
ON CONFLICT (id) DO UPDATE SET name=$2, mode=$3, status=$4, account=$5, verified_at=$6,
|
|
238600
|
+
connected_by=COALESCE($7, ${this.s}.connectors.connected_by)`,
|
|
238601
|
+
[c.id, c.name, c.mode, c.status, c.account ?? null, c.verifiedAt ?? null, c.connectedBy ?? null]
|
|
238230
238602
|
);
|
|
238231
238603
|
}
|
|
238232
238604
|
async listConnectors() {
|
|
@@ -238237,7 +238609,8 @@ var init_postgres_registry = __esm({
|
|
|
238237
238609
|
mode: row.mode,
|
|
238238
238610
|
status: row.status,
|
|
238239
238611
|
...row.account !== null ? { account: row.account } : {},
|
|
238240
|
-
...row.verified_at !== null ? { verifiedAt: new Date(row.verified_at).toISOString() } : {}
|
|
238612
|
+
...row.verified_at !== null ? { verifiedAt: new Date(row.verified_at).toISOString() } : {},
|
|
238613
|
+
...row.connected_by ? { connectedBy: row.connected_by } : {}
|
|
238241
238614
|
}));
|
|
238242
238615
|
}
|
|
238243
238616
|
async deleteConnector(id) {
|
|
@@ -238409,10 +238782,13 @@ var init_postgres_registry = __esm({
|
|
|
238409
238782
|
/* ---------- 变量 ---------- */
|
|
238410
238783
|
async putVariable(v2) {
|
|
238411
238784
|
await this.pool.query(
|
|
238412
|
-
`
|
|
238413
|
-
|
|
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)
|
|
238414
238789
|
ON CONFLICT (key, COALESCE(actor_id, ''), COALESCE(project_id, '')) DO UPDATE
|
|
238415
|
-
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)`,
|
|
238416
238792
|
[
|
|
238417
238793
|
v2.key,
|
|
238418
238794
|
v2.scope,
|
|
@@ -238423,7 +238799,8 @@ var init_postgres_registry = __esm({
|
|
|
238423
238799
|
v2.deliveryMode ?? null,
|
|
238424
238800
|
v2.valueEncrypted,
|
|
238425
238801
|
v2.updatedAt,
|
|
238426
|
-
v2.expiresAt ?? null
|
|
238802
|
+
v2.expiresAt ?? null,
|
|
238803
|
+
v2.name ?? null
|
|
238427
238804
|
]
|
|
238428
238805
|
);
|
|
238429
238806
|
}
|
|
@@ -238519,17 +238896,6 @@ var init_postgres_registry = __esm({
|
|
|
238519
238896
|
}
|
|
238520
238897
|
});
|
|
238521
238898
|
|
|
238522
|
-
// ../storage/src/pg-ident.ts
|
|
238523
|
-
function quoteIdent2(name) {
|
|
238524
|
-
if (!/^[A-Za-z0-9_][A-Za-z0-9_.-]*$/.test(name)) throw new Error(`invalid schema name: ${name}`);
|
|
238525
|
-
return `"${name}"`;
|
|
238526
|
-
}
|
|
238527
|
-
var init_pg_ident = __esm({
|
|
238528
|
-
"../storage/src/pg-ident.ts"() {
|
|
238529
|
-
"use strict";
|
|
238530
|
-
}
|
|
238531
|
-
});
|
|
238532
|
-
|
|
238533
238899
|
// ../storage/src/postgres-assistant-store.ts
|
|
238534
238900
|
var ASSISTANT_LOCK_NAMESPACE, rowToBinding, rowToHistory, PostgresAssistantBindStore, PostgresHumanPrefsStore;
|
|
238535
238901
|
var init_postgres_assistant_store = __esm({
|
|
@@ -238838,8 +239204,9 @@ var init_postgres_channels = __esm({
|
|
|
238838
239204
|
"../storage/src/postgres-channels.ts"() {
|
|
238839
239205
|
"use strict";
|
|
238840
239206
|
init_esm();
|
|
239207
|
+
init_pg_ident();
|
|
238841
239208
|
ident5 = (s2) => {
|
|
238842
|
-
|
|
239209
|
+
quoteIdent2(s2);
|
|
238843
239210
|
return s2;
|
|
238844
239211
|
};
|
|
238845
239212
|
PostgresChannelStore = class _PostgresChannelStore {
|
|
@@ -239113,6 +239480,24 @@ var init_postgres_projects = __esm({
|
|
|
239113
239480
|
const r = await this.pool.query(`SELECT * FROM ${this.s}.projects ORDER BY created_at`);
|
|
239114
239481
|
return r.rows.map(rowToProject);
|
|
239115
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
|
+
}
|
|
239116
239501
|
async replaceProjectMembers(projectId2, members) {
|
|
239117
239502
|
const client = await this.pool.connect();
|
|
239118
239503
|
try {
|
|
@@ -239907,8 +240292,9 @@ var init_postgres_control_plane = __esm({
|
|
|
239907
240292
|
"../storage/src/postgres-control-plane.ts"() {
|
|
239908
240293
|
"use strict";
|
|
239909
240294
|
init_esm();
|
|
240295
|
+
init_pg_ident();
|
|
239910
240296
|
ident7 = (s2) => {
|
|
239911
|
-
|
|
240297
|
+
quoteIdent2(s2);
|
|
239912
240298
|
return s2;
|
|
239913
240299
|
};
|
|
239914
240300
|
PostgresControlPlaneStore = class _PostgresControlPlaneStore {
|
|
@@ -240833,11 +241219,12 @@ var ident8, mergeContinuityRefs3, PostgresTraceStore, rowToRun, terminalHandoffO
|
|
|
240833
241219
|
var init_postgres_trace = __esm({
|
|
240834
241220
|
"../storage/src/postgres-trace.ts"() {
|
|
240835
241221
|
"use strict";
|
|
241222
|
+
init_pg_ident();
|
|
240836
241223
|
init_esm();
|
|
240837
241224
|
init_src();
|
|
240838
241225
|
init_pg_sanitize();
|
|
240839
241226
|
ident8 = (s2) => {
|
|
240840
|
-
|
|
241227
|
+
quoteIdent2(s2);
|
|
240841
241228
|
return s2;
|
|
240842
241229
|
};
|
|
240843
241230
|
mergeContinuityRefs3 = (...lists) => {
|
|
@@ -242822,10 +243209,11 @@ var init_postgres_automations = __esm({
|
|
|
242822
243209
|
"../storage/src/postgres-automations.ts"() {
|
|
242823
243210
|
"use strict";
|
|
242824
243211
|
init_esm();
|
|
243212
|
+
init_pg_ident();
|
|
242825
243213
|
init_postgres_chat_turns();
|
|
242826
243214
|
init_src();
|
|
242827
243215
|
ident10 = (s2) => {
|
|
242828
|
-
|
|
243216
|
+
quoteIdent2(s2);
|
|
242829
243217
|
return s2;
|
|
242830
243218
|
};
|
|
242831
243219
|
genId = (prefix) => `${prefix}_${Math.random().toString(36).slice(2, 12)}`;
|
|
@@ -244064,6 +244452,15 @@ var init_postgres_chat_sessions = __esm({
|
|
|
244064
244452
|
args
|
|
244065
244453
|
);
|
|
244066
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
|
+
}
|
|
244067
244464
|
async deleteSession(id) {
|
|
244068
244465
|
if (!await this.scopedSessionExists(id)) return;
|
|
244069
244466
|
await this.pool.query(`DELETE FROM ${this.s}.chat_messages WHERE session_id = $1`, [id]);
|
|
@@ -244887,12 +245284,15 @@ var init_postgres_chat_items = __esm({
|
|
|
244887
245284
|
const r = await this.pool.query(
|
|
244888
245285
|
`INSERT INTO ${this.s}.chat_items
|
|
244889
245286
|
(id, session_id, message_id, turn_id, run_id, seq, kind, role, status,
|
|
244890
|
-
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)
|
|
244891
245288
|
SELECT $1, $2, $3, $4, $5,
|
|
244892
245289
|
COALESCE(MAX(c.seq), 0) + 1,
|
|
244893
|
-
$6, $7, $8::text, 0,
|
|
245290
|
+
$6, $7, $8::text, COALESCE($13::bigint, 0),
|
|
244894
245291
|
COALESCE(MAX(c.turn_version), 0) + 1,
|
|
244895
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)),
|
|
244896
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
|
|
244897
245297
|
-- \uFF08\u5217\u4F4D\u662F timestamptz/text\u3001CASE \u91CC\u662F unknown\uFF09\uFF0C\u76F4\u63A5\u62A5
|
|
244898
245298
|
-- inconsistent types deduced for parameter $12\u3002
|
|
@@ -244912,7 +245312,9 @@ var init_postgres_chat_items = __esm({
|
|
|
244912
245312
|
scrubPgString(input.providerItemKey ?? null),
|
|
244913
245313
|
payload,
|
|
244914
245314
|
metadata,
|
|
244915
|
-
at
|
|
245315
|
+
at,
|
|
245316
|
+
input.version ?? null,
|
|
245317
|
+
input.ord ?? null
|
|
244916
245318
|
]
|
|
244917
245319
|
);
|
|
244918
245320
|
return rowToItem(r.rows[0]);
|
|
@@ -244950,10 +245352,13 @@ var init_postgres_chat_items = __esm({
|
|
|
244950
245352
|
setExpr = `status = $3::text, completed_at = CASE WHEN $3 IN ('completed','failed','interrupted') THEN $2::timestamptz ELSE c.completed_at END`;
|
|
244951
245353
|
break;
|
|
244952
245354
|
}
|
|
245355
|
+
const versionParamIndex = args.length + 1;
|
|
245356
|
+
args.push(opts?.version ?? null);
|
|
244953
245357
|
const r = await this.pool.query(
|
|
244954
245358
|
`UPDATE ${this.s}.chat_items c
|
|
244955
245359
|
SET ${setExpr},
|
|
244956
|
-
version = c.version + 1
|
|
245360
|
+
version = CASE WHEN $${versionParamIndex}::bigint IS NULL THEN c.version + 1
|
|
245361
|
+
ELSE GREATEST(c.version, $${versionParamIndex}::bigint) END,
|
|
244957
245362
|
turn_version = ${bumpTurnVersion},
|
|
244958
245363
|
updated_at = $2
|
|
244959
245364
|
WHERE c.id = $1
|
|
@@ -245001,6 +245406,49 @@ var init_postgres_chat_items = __esm({
|
|
|
245001
245406
|
);
|
|
245002
245407
|
return r.rows.map((row) => rowToItem(row));
|
|
245003
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
|
+
}
|
|
245004
245452
|
async turnCursor(sessionId, turnId) {
|
|
245005
245453
|
const r = await this.pool.query(
|
|
245006
245454
|
`SELECT COALESCE(MAX(seq), 0) + 1 AS next_seq, COALESCE(MAX(turn_version), 0) + 1 AS next_turn_version
|
|
@@ -245337,8 +245785,14 @@ function planSessionOrd(sessionId, messages, items, turns, opts) {
|
|
|
245337
245785
|
}
|
|
245338
245786
|
async function listChatItemsSchemas(pool) {
|
|
245339
245787
|
const r = await pool.query(
|
|
245340
|
-
`SELECT n.nspname AS s
|
|
245341
|
-
|
|
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`
|
|
245342
245796
|
);
|
|
245343
245797
|
return r.rows.map((x2) => x2.s);
|
|
245344
245798
|
}
|
|
@@ -245932,9 +246386,10 @@ var init_postgres_nodes = __esm({
|
|
|
245932
246386
|
"../storage/src/postgres-nodes.ts"() {
|
|
245933
246387
|
"use strict";
|
|
245934
246388
|
import_node_crypto68 = require("node:crypto");
|
|
246389
|
+
init_pg_ident();
|
|
245935
246390
|
init_esm();
|
|
245936
246391
|
ident12 = (s2) => {
|
|
245937
|
-
|
|
246392
|
+
quoteIdent2(s2);
|
|
245938
246393
|
return s2;
|
|
245939
246394
|
};
|
|
245940
246395
|
PostgresNodeStore = class _PostgresNodeStore {
|
|
@@ -246199,8 +246654,9 @@ var init_postgres_model_prices = __esm({
|
|
|
246199
246654
|
"../storage/src/postgres-model-prices.ts"() {
|
|
246200
246655
|
"use strict";
|
|
246201
246656
|
init_esm();
|
|
246657
|
+
init_pg_ident();
|
|
246202
246658
|
ident13 = (s2) => {
|
|
246203
|
-
|
|
246659
|
+
quoteIdent2(s2);
|
|
246204
246660
|
return s2;
|
|
246205
246661
|
};
|
|
246206
246662
|
PostgresModelPriceStore = class _PostgresModelPriceStore {
|
|
@@ -246410,10 +246866,11 @@ var init_postgres_actor_memory = __esm({
|
|
|
246410
246866
|
"../storage/src/postgres-actor-memory.ts"() {
|
|
246411
246867
|
"use strict";
|
|
246412
246868
|
import_node_crypto69 = require("node:crypto");
|
|
246869
|
+
init_pg_ident();
|
|
246413
246870
|
init_src();
|
|
246414
246871
|
matchClause = (q2) => q2.requireMatch && q2.keywords.length > 0 ? " WHERE m > 0" : "";
|
|
246415
246872
|
ident14 = (s2) => {
|
|
246416
|
-
|
|
246873
|
+
quoteIdent2(s2);
|
|
246417
246874
|
return s2;
|
|
246418
246875
|
};
|
|
246419
246876
|
PostgresActorMemoryStore = class _PostgresActorMemoryStore {
|
|
@@ -246719,9 +247176,10 @@ var init_postgres_inbox_read = __esm({
|
|
|
246719
247176
|
"../storage/src/postgres-inbox-read.ts"() {
|
|
246720
247177
|
"use strict";
|
|
246721
247178
|
init_esm();
|
|
247179
|
+
init_pg_ident();
|
|
246722
247180
|
SEED_MARKER_ACTOR = "actor:system";
|
|
246723
247181
|
ident15 = (s2) => {
|
|
246724
|
-
|
|
247182
|
+
quoteIdent2(s2);
|
|
246725
247183
|
return s2;
|
|
246726
247184
|
};
|
|
246727
247185
|
PostgresReadMarkerStore = class _PostgresReadMarkerStore {
|
|
@@ -251973,10 +252431,11 @@ var init_postgres_knowledge = __esm({
|
|
|
251973
252431
|
"../storage/src/postgres-knowledge.ts"() {
|
|
251974
252432
|
"use strict";
|
|
251975
252433
|
init_esm();
|
|
252434
|
+
init_pg_ident();
|
|
251976
252435
|
init_pg_sanitize();
|
|
251977
252436
|
init_knowledge_snapshot_crypto();
|
|
251978
252437
|
ident17 = (value2) => {
|
|
251979
|
-
|
|
252438
|
+
quoteIdent2(value2);
|
|
251980
252439
|
return value2;
|
|
251981
252440
|
};
|
|
251982
252441
|
TABLES = [
|
|
@@ -255598,6 +256057,36 @@ async function startServe(opts) {
|
|
|
255598
256057
|
projectId2,
|
|
255599
256058
|
afterName
|
|
255600
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"));
|
|
255601
256090
|
}
|
|
255602
256091
|
});
|
|
255603
256092
|
const currentArtifactProjection = async () => {
|
|
@@ -256584,6 +257073,16 @@ async function startServe(opts) {
|
|
|
256584
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`);
|
|
256585
257074
|
return stores.artifacts;
|
|
256586
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
|
+
}));
|
|
256587
257086
|
if (pgDsn && pgPool && chatStoreRouter) {
|
|
256588
257087
|
const router = chatStoreRouter;
|
|
256589
257088
|
void (async () => {
|
|
@@ -256772,7 +257271,6 @@ async function startServe(opts) {
|
|
|
256772
257271
|
}
|
|
256773
257272
|
return token;
|
|
256774
257273
|
};
|
|
256775
|
-
let resolveCodeRepoRef;
|
|
256776
257274
|
let coordinatorRef = null;
|
|
256777
257275
|
let coordinatorSpawnProbe = null;
|
|
256778
257276
|
const dispatchTimers = /* @__PURE__ */ new Set();
|
|
@@ -256932,9 +257430,9 @@ async function startServe(opts) {
|
|
|
256932
257430
|
port: opts.port ?? 7320,
|
|
256933
257431
|
projectState: projectStateStore,
|
|
256934
257432
|
artifactState: artifactStateStore,
|
|
256935
|
-
//
|
|
257433
|
+
// 建单的项目校验、绑定与输入文件按当前公司落库;与项目/任务读侧使用同一个 router。
|
|
256936
257434
|
// 装了路由(有 PG)才给——dev 文件模式没有 schema 概念,沿用上面那个单实例。
|
|
256937
|
-
...chatStoreRouter ? { artifactStateFor } : {},
|
|
257435
|
+
...chatStoreRouter ? { artifactStateFor, projectStateFor } : {},
|
|
256938
257436
|
schema,
|
|
256939
257437
|
registry: registryStore,
|
|
256940
257438
|
// 会话级模型覆盖的候选清单:与 `GET /api/runtimes/:id/models` **同一份**实现
|
|
@@ -257004,7 +257502,7 @@ async function startServe(opts) {
|
|
|
257004
257502
|
});
|
|
257005
257503
|
return { plannerActor: planner.id, text: chunks.join("") };
|
|
257006
257504
|
},
|
|
257007
|
-
resolveCodeRepo:
|
|
257505
|
+
resolveCodeRepo: resolveCompanyCodeRepo,
|
|
257008
257506
|
// propose sha 校验兼镜像根:verifyExternalPinSha 在 ls-remote 未命中(祖先提交)时 `git fetch <remote> <sha>`
|
|
257009
257507
|
// 进此目录留档(校验即留档,见 git/collect.ts)。目录按需 `init --bare`。(历史:曾是合并器/CI 闸的 git-mirrors 根,已删。)
|
|
257010
257508
|
gitMirrorRoot: path29.join(opts.dir, "propose-mirrors"),
|
|
@@ -257081,8 +257579,8 @@ async function startServe(opts) {
|
|
|
257081
257579
|
collabDomain({
|
|
257082
257580
|
kernel: defaultCompanyKernel,
|
|
257083
257581
|
oplog,
|
|
257084
|
-
blobs,
|
|
257085
|
-
//
|
|
257582
|
+
blobs: engine.blobs,
|
|
257583
|
+
// 页头正文读写与内容接口同源:默认公司的事实柜。
|
|
257086
257584
|
// 资产柜:POST /api/workorders/:id/files 上传字节落这里(与 uploadChatAttachment 同一柜),
|
|
257087
257585
|
// 再让 artifacts.addWorkOrderFile 写行——手动上传不进 oplog / events,删了不伤重放。
|
|
257088
257586
|
assets,
|
|
@@ -259101,11 +259599,10 @@ async function startServe(opts) {
|
|
|
259101
259599
|
for (const w2 of projectsCfg?.workspaces ?? []) {
|
|
259102
259600
|
await artifactStateStore.upsertWorkspaceBinding({ workOrderId: w2.id, projectId: w2.projectId });
|
|
259103
259601
|
}
|
|
259104
|
-
const {
|
|
259602
|
+
const { resolveProject } = makeStoreCodeRepoResolver({
|
|
259105
259603
|
projects: projectStateStore,
|
|
259106
259604
|
bindings: artifactStateStore
|
|
259107
259605
|
});
|
|
259108
|
-
resolveCodeRepoRef = resolveCodeRepo;
|
|
259109
259606
|
if (opts.dispatch) {
|
|
259110
259607
|
const startDispatcher = (companyId, companyEngine) => {
|
|
259111
259608
|
if (dispatchers.has(companyId)) return;
|
|
@@ -259381,8 +259878,7 @@ async function startServe(opts) {
|
|
|
259381
259878
|
} : {},
|
|
259382
259879
|
schema: liveSchemaMap,
|
|
259383
259880
|
// 活视图:类型目录变更后由 rebuildLiveSchema 原地重填(Dispatcher 每轮现读 opts.schema)
|
|
259384
|
-
resolveCodeRepo,
|
|
259385
|
-
// §4/C:异步从权威源(store)解析;dispatcher 装配前 await
|
|
259881
|
+
resolveCodeRepo: (ws) => resolveCompanyCodeRepo(ws, companyId),
|
|
259386
259882
|
// #2 派单开关:工单"待派发"(控制面旁存标 held)时不派其活。held 集通常空、表极小,每次查可接受。
|
|
259387
259883
|
resolveDispatchHold: async (ws) => (await artifactStateStore.listWorkspaceDispatchHeld()).includes(ws),
|
|
259388
259884
|
// §D3 评审人连续性:锁定审核人不可用(熔断/离岗)→ 系统身份幂等升级,交管理者改派(不静默换人)。
|
|
@@ -260604,7 +261100,7 @@ ${nodeFault}` : "");
|
|
|
260604
261100
|
log: (m2) => console.log(m2),
|
|
260605
261101
|
batchLimit: envNum("OASIS_AUTOMATION_EVENT_BATCH", 200),
|
|
260606
261102
|
// 工单→项目绑定是合法薄旁存(B5),不是内核事实——列项目下的工单只认它。
|
|
260607
|
-
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),
|
|
260608
261104
|
// 完成判据必须复用 D3 resolver(不复制 deriveStage),且与成员快照同一次读取。
|
|
260609
261105
|
// ensureTracked:内核跟踪集只在启动时装载一次、此后只随本进程写入增长——多实例部署下,
|
|
260610
261106
|
// 另一实例建出来的工单在这里会被读成 unknown(既不触发也不留痕,QA OBS-1 实测)。
|
|
@@ -260615,7 +261111,7 @@ ${nodeFault}` : "");
|
|
|
260615
261111
|
return makeAutomationWorkorderSnapshotResolver(engine2.kernel)(workspace);
|
|
260616
261112
|
},
|
|
260617
261113
|
// 触发时复查归因主体的项目读取权(与配置期同一口径)。
|
|
260618
|
-
projectReadable: async ({ projectId: projectId2 }) => await
|
|
261114
|
+
projectReadable: async ({ companyId, projectId: projectId2 }) => await (await projectStateFor(companyId)).getProject(projectId2) !== null
|
|
260619
261115
|
});
|
|
260620
261116
|
automationEventTimer = setInterval(() => {
|
|
260621
261117
|
void eventPoller.tick().catch((err) => console.error(`[project-event-poller] tick \u5931\u8D25: ${String(err)}`));
|
|
@@ -267844,7 +268340,7 @@ function shimScript() {
|
|
|
267844
268340
|
}
|
|
267845
268341
|
|
|
267846
268342
|
// src/index.ts
|
|
267847
|
-
var PKG_VERSION = true ? "2.2.
|
|
268343
|
+
var PKG_VERSION = true ? "2.2.2" : "dev";
|
|
267848
268344
|
var LOCAL_BIN = localBin();
|
|
267849
268345
|
var NPM_PREFIX = npmPrefix();
|
|
267850
268346
|
var INSTANCE = DEFAULT_INSTANCE;
|