oasis_test_v2 2.2.9 → 2.2.11
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 +725 -362
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -665,7 +665,20 @@ function isAgentAvatarPresetRef(ref2) {
|
|
|
665
665
|
const id = ref2.slice(AGENT_AVATAR_PRESET_PREFIX.length);
|
|
666
666
|
return AGENT_AVATAR_PRESET_IDS.includes(id);
|
|
667
667
|
}
|
|
668
|
-
|
|
668
|
+
function humanAvatarToneOf(seed) {
|
|
669
|
+
let hash2 = 2166136261;
|
|
670
|
+
for (const ch of seed.trim()) {
|
|
671
|
+
hash2 = Math.imul(hash2 ^ (ch.codePointAt(0) ?? 0), 16777619) >>> 0;
|
|
672
|
+
}
|
|
673
|
+
return hash2 % HUMAN_AVATAR_TONE_COUNT;
|
|
674
|
+
}
|
|
675
|
+
function humanAvatarPresetRef(tone) {
|
|
676
|
+
return `${HUMAN_AVATAR_PRESET_PREFIX}${tone}`;
|
|
677
|
+
}
|
|
678
|
+
function defaultHumanAvatarRef(actorId) {
|
|
679
|
+
return humanAvatarPresetRef(humanAvatarToneOf(actorId));
|
|
680
|
+
}
|
|
681
|
+
var AGENT_AVATAR_PRESET_PREFIX, AGENT_AVATAR_LEGACY_PRESET_IDS, AGENT_AVATAR_V2_PRESET_IDS, AGENT_AVATAR_PRESET_IDS, SYSTEM_AVATAR_REF, HUMAN_AVATAR_PRESET_PREFIX, HUMAN_AVATAR_TONE_COUNT;
|
|
669
682
|
var init_avatar = __esm({
|
|
670
683
|
"../contract/src/avatar.ts"() {
|
|
671
684
|
"use strict";
|
|
@@ -696,6 +709,8 @@ var init_avatar = __esm({
|
|
|
696
709
|
...AGENT_AVATAR_V2_PRESET_IDS
|
|
697
710
|
];
|
|
698
711
|
SYSTEM_AVATAR_REF = "preset:system";
|
|
712
|
+
HUMAN_AVATAR_PRESET_PREFIX = "preset:human:";
|
|
713
|
+
HUMAN_AVATAR_TONE_COUNT = 8;
|
|
699
714
|
}
|
|
700
715
|
});
|
|
701
716
|
|
|
@@ -905,7 +920,17 @@ var init_link_windows = __esm({
|
|
|
905
920
|
/** orphan-sweep 判「账上在跑、四张认领表都不认」为孤儿的宽限(`serve.ts`)。 */
|
|
906
921
|
orphanGraceMs: 12e4,
|
|
907
922
|
/** orphan-sweep 在 serve 冷启动后推迟判孤儿的窗口(`serve.ts`)。 */
|
|
908
|
-
orphanColdStartGraceMs: 18e4
|
|
923
|
+
orphanColdStartGraceMs: 18e4,
|
|
924
|
+
/**
|
|
925
|
+
* `/items` 读时把「账上还 running、本进程 live 注册表里却没有」的一轮**显示**成 `interrupted`
|
|
926
|
+
* 的宽限(ADR-0510 D6 表格第二行)。
|
|
927
|
+
*
|
|
928
|
+
* 取值必须 **≥ {@link LINK_WINDOWS.orphanGraceMs}**:这条判定只写 wire、不写账本,它的作用是
|
|
929
|
+
* 让页面别在权威扫描还没开始看的时候就一直转圈;但它也**不该比权威更早下结论**——早于
|
|
930
|
+
* `orphanGraceMs` 就等于拿时间当判据抢在核实前面说话(ADR-0166)。两个数写在同一处,
|
|
931
|
+
* 谁单边调参 `tests/adr-0300-link-windows.test.ts` 当场变红。
|
|
932
|
+
*/
|
|
933
|
+
chatTurnPhaseStaleAfterMs: 12e4
|
|
909
934
|
};
|
|
910
935
|
}
|
|
911
936
|
});
|
|
@@ -914,12 +939,16 @@ var init_link_windows = __esm({
|
|
|
914
939
|
function isSystemActorId(id) {
|
|
915
940
|
return typeof id === "string" && id.startsWith(SYSTEM_ACTOR_ID_PREFIX);
|
|
916
941
|
}
|
|
917
|
-
|
|
942
|
+
function isHumanActorId(id) {
|
|
943
|
+
return typeof id === "string" && id.startsWith(HUMAN_ACTOR_ID_PREFIX);
|
|
944
|
+
}
|
|
945
|
+
var SYSTEM_ACTOR_ID_PREFIX, SYSTEM_ACTOR_DISPLAY_NAME, HUMAN_ACTOR_ID_PREFIX, AGENT_PREFS_DEFAULTS, SkillOwnedByAnotherCompanyError;
|
|
918
946
|
var init_registry = __esm({
|
|
919
947
|
"../contract/src/registry.ts"() {
|
|
920
948
|
"use strict";
|
|
921
949
|
SYSTEM_ACTOR_ID_PREFIX = "actor:system";
|
|
922
950
|
SYSTEM_ACTOR_DISPLAY_NAME = "\u7CFB\u7EDF";
|
|
951
|
+
HUMAN_ACTOR_ID_PREFIX = "actor:human:";
|
|
923
952
|
AGENT_PREFS_DEFAULTS = { memoryEnabled: true };
|
|
924
953
|
SkillOwnedByAnotherCompanyError = class extends Error {
|
|
925
954
|
constructor(skillId, companyId) {
|
|
@@ -4637,8 +4666,6 @@ var init_kill = __esm({
|
|
|
4637
4666
|
function scan(state, ctx) {
|
|
4638
4667
|
if (state.workorder.dispatchPaused) return { events: [], terminated: false };
|
|
4639
4668
|
const events = [];
|
|
4640
|
-
const now = ctx.at;
|
|
4641
|
-
const policy = ctx.policy ?? CODE_DEFAULT_POLICY;
|
|
4642
4669
|
for (const node2 of state.liveNodes()) {
|
|
4643
4670
|
const lw = node2.latestWorkId ? state.work(node2.latestWorkId) : null;
|
|
4644
4671
|
if (!lw) {
|
|
@@ -4672,12 +4699,7 @@ function scan(state, ctx) {
|
|
|
4672
4699
|
continue;
|
|
4673
4700
|
}
|
|
4674
4701
|
if (wstate === "running") {
|
|
4675
|
-
|
|
4676
|
-
const elapsed = new Date(now).getTime() - new Date(start).getTime();
|
|
4677
|
-
const wcell = policy.resolve({ executor: classify(node2.assigneeActorId), scenario: "work" });
|
|
4678
|
-
if (wcell.timeoutAction.kind === "kill-rebuild" && elapsed > wcell.deadlineMs) {
|
|
4679
|
-
events.push({ kind: "work.timeout", workorderId: state.workorder.id, workId: lw.id });
|
|
4680
|
-
}
|
|
4702
|
+
checkRunningWorkTimeout(state, node2, lw, ctx, events);
|
|
4681
4703
|
continue;
|
|
4682
4704
|
}
|
|
4683
4705
|
const blockers = scanBlockersForNode(state, node2, lw);
|
|
@@ -4742,10 +4764,9 @@ function scan(state, ctx) {
|
|
|
4742
4764
|
workorderId: state.workorder.id,
|
|
4743
4765
|
nodeId: node2.id,
|
|
4744
4766
|
// ★ 合并写:保留 source ∈ {type, manual, model} 的原有行,覆写 source='closure' 的行。
|
|
4745
|
-
// `plan.update_review_requirements`
|
|
4767
|
+
// `plan.update_review_requirements` 的名单仍是**全量替换**,
|
|
4746
4768
|
// 所以必须把手动 / 类型 / 模型来源的行一并带上——漏一条即误删协调者手动指派的审核人。
|
|
4747
|
-
//
|
|
4748
|
-
// or 只含 closure 之外的组),本轮内 kill 无影响。
|
|
4769
|
+
// 总线为新事件写 preserveReviews:未变组的当前审核继续,只有新增 closure 组待派。
|
|
4749
4770
|
reviewers: mergedClosureRequirements(state, node2.id, closureActors)
|
|
4750
4771
|
});
|
|
4751
4772
|
continue;
|
|
@@ -4848,6 +4869,15 @@ function scan(state, ctx) {
|
|
|
4848
4869
|
const terminated = events.length === 0 && checkTerminated(state);
|
|
4849
4870
|
return { events, terminated };
|
|
4850
4871
|
}
|
|
4872
|
+
function checkRunningWorkTimeout(state, node2, work, ctx, events) {
|
|
4873
|
+
const start = work.lastActivityAt ?? work.startedAt ?? work.createdAt;
|
|
4874
|
+
const elapsed = new Date(ctx.at).getTime() - new Date(start).getTime();
|
|
4875
|
+
const policy = ctx.policy ?? CODE_DEFAULT_POLICY;
|
|
4876
|
+
const wcell = policy.resolve({ executor: classify(node2.assigneeActorId), scenario: "work" });
|
|
4877
|
+
if (wcell.timeoutAction.kind === "kill-rebuild" && elapsed > wcell.deadlineMs) {
|
|
4878
|
+
events.push({ kind: "work.timeout", workorderId: state.workorder.id, workId: work.id });
|
|
4879
|
+
}
|
|
4880
|
+
}
|
|
4851
4881
|
function openCommentBlockers(state, node2) {
|
|
4852
4882
|
if (!node2.assigneeActorId) return [];
|
|
4853
4883
|
return state.allIssues().filter((i) => i.kind === "comment" && i.resolvedAt === null && i.aboutNodeId === node2.id).map((i) => ({
|
|
@@ -4932,6 +4962,13 @@ function tryToRun(state, nodeId, ctx, events) {
|
|
|
4932
4962
|
const node2 = state.node(nodeId);
|
|
4933
4963
|
if (!node2) return;
|
|
4934
4964
|
if (!node2.assigneeActorId) return;
|
|
4965
|
+
const latest = (node2.latestWorkId ? state.work(node2.latestWorkId) : null) ?? null;
|
|
4966
|
+
if (unexemptedVetoBlockers(scanBlockersForNode(state, node2, latest), "dispatch").length > 0) {
|
|
4967
|
+
if (latest && workState(latest, hasOutput(latest, state.artifactsOf(latest.id).length)) === "running") {
|
|
4968
|
+
checkRunningWorkTimeout(state, node2, latest, ctx, events);
|
|
4969
|
+
}
|
|
4970
|
+
return;
|
|
4971
|
+
}
|
|
4935
4972
|
const inbound = state.edgesInto(nodeId);
|
|
4936
4973
|
for (const e of inbound) {
|
|
4937
4974
|
if (e.kind !== "data" || !e.required) continue;
|
|
@@ -5560,6 +5597,7 @@ CREATE TABLE IF NOT EXISTS ${q2}.work_artifacts (
|
|
|
5560
5597
|
content_kind text NOT NULL CHECK (content_kind IN ('inline','external','manifest')),
|
|
5561
5598
|
ordinal integer,
|
|
5562
5599
|
created_at timestamptz NOT NULL,
|
|
5600
|
+
content_edited_at timestamptz,
|
|
5563
5601
|
UNIQUE (work_id, logical_path)
|
|
5564
5602
|
);
|
|
5565
5603
|
CREATE INDEX IF NOT EXISTS work_artifacts_node_idx ON ${q2}.work_artifacts (node_id);
|
|
@@ -5725,6 +5763,9 @@ ALTER TABLE ${q2}.works ADD COLUMN IF NOT EXISTS proposal_reason text;
|
|
|
5725
5763
|
-- \u2605 \u8FD0\u884C\u6D3B\u8DC3\u5FC3\u8DF3\uFF1Av2 submit_output / work.conclude \u5199\u5165\uFF1Bscan \u8D85\u65F6\u4EE5\u5B83\u8D77\u7B97\uFF08work \u751F\u547D\u5468\u671F\u5EF6\u957F\u540E
|
|
5726
5764
|
-- 30min \u949F\u4E0D\u80FD\u518D\u7F69\u6B7B\u6574\u4E2A\u4F1A\u8BDD\uFF09\u3002\u5B58\u91CF\u4E3A NULL \u2192 \u56DE\u9000 started_at/created_at\uFF0C\u884C\u4E3A\u4E0D\u53D8\u3002
|
|
5727
5765
|
ALTER TABLE ${q2}.works ADD COLUMN IF NOT EXISTS last_activity_at timestamptz;
|
|
5766
|
+
-- \u4EA7\u7269\u6B63\u6587\u5C31\u5730\u6539\u5199\uFF08work.content_edited\uFF09\u7684\u65F6\u523B\u3002\u5C31\u5730\u6539\u5199\u4E0D\u65B0\u5F00 work\u3001\u4E0D\u94F8\u65B0\u7248\uFF0C
|
|
5767
|
+
-- \u6CA1\u6709\u8FD9\u4E00\u5217\uFF0C\u300C\u76F8\u5173\u6587\u4EF6\u300D\u8868\u7684\u65F6\u95F4\u5217\u5C31\u53EA\u80FD\u505C\u5728\u9A8C\u6536\u65F6\u523B\u3002\u5B58\u91CF\u884C\u4E3A NULL = \u4ECE\u672A\u88AB\u6539\u5199\u8FC7\u3002
|
|
5768
|
+
ALTER TABLE ${q2}.work_artifacts ADD COLUMN IF NOT EXISTS content_edited_at timestamptz;
|
|
5728
5769
|
-- \u2605 \u51BB\u7ED3\u6765\u4FE1\u96C6\u5408\uFF08\u8BA1\u5212 \xA76.5\uFF0C\u9636\u6BB5 E\uFF09\uFF1Awork.create \u6309 reasons \u7684 issue \u9879\u51BB\u7ED3\u7684 issue id \u6279\u6B21\uFF08jsonb \u6570\u7EC4\uFF09\u3002
|
|
5729
5770
|
-- reply \u5F52\u5C5E/\u9500\u8D26/\u5BA1\u8BA1\u53EA\u8BA4\u5B83\uFF0C\u4E0D\u9760\u65F6\u95F4\u7A97\u731C\u6D4B\u3002\u5B58\u91CF\u4E3A NULL\uFF08\u65E0\u6279\u6B21\u8BED\u4E49\uFF09\u3002
|
|
5730
5771
|
ALTER TABLE ${q2}.works ADD COLUMN IF NOT EXISTS frozen_issue_ids jsonb;
|
|
@@ -11388,7 +11429,11 @@ var init_store_postgres = __esm({
|
|
|
11388
11429
|
contentRef: a.content_ref,
|
|
11389
11430
|
contentKind: a.content_kind,
|
|
11390
11431
|
ordinal: a.ordinal,
|
|
11391
|
-
createdAt: iso(a.created_at)
|
|
11432
|
+
createdAt: iso(a.created_at),
|
|
11433
|
+
// 只在真被改写过时才带这个键:从未改写的行在内存态里是**没有这个字段**的,
|
|
11434
|
+
// PG 回读时补一个 `null` 会让「事件重放出来的态」和「从库里读出来的态」深比不相等
|
|
11435
|
+
// (packages/engine/tests/store-conformance.postgres.test.ts 就是钉这条一致性的)。
|
|
11436
|
+
...iso(a.content_edited_at) ? { contentEditedAt: iso(a.content_edited_at) } : {}
|
|
11392
11437
|
})),
|
|
11393
11438
|
workInputs: inputs.rows.map((i) => ({
|
|
11394
11439
|
workId: i.work_id,
|
|
@@ -11623,9 +11668,9 @@ var init_store_postgres = __esm({
|
|
|
11623
11668
|
for (const a of m2.rows) {
|
|
11624
11669
|
await this.c.query(
|
|
11625
11670
|
`INSERT INTO ${this.s}.work_artifacts
|
|
11626
|
-
(id,work_id,node_id,type,name,logical_path,content_ref,content_kind,ordinal,created_at)
|
|
11627
|
-
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10) ON CONFLICT (id) DO NOTHING`,
|
|
11628
|
-
[a.id, a.workId, a.nodeId, a.type, a.name, a.logicalPath, a.contentRef, a.contentKind, a.ordinal, a.createdAt]
|
|
11671
|
+
(id,work_id,node_id,type,name,logical_path,content_ref,content_kind,ordinal,created_at,content_edited_at)
|
|
11672
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11) ON CONFLICT (id) DO NOTHING`,
|
|
11673
|
+
[a.id, a.workId, a.nodeId, a.type, a.name, a.logicalPath, a.contentRef, a.contentKind, a.ordinal, a.createdAt, a.contentEditedAt ?? null]
|
|
11629
11674
|
);
|
|
11630
11675
|
}
|
|
11631
11676
|
break;
|
|
@@ -11974,7 +12019,7 @@ function markNodeForRetry(state, nodeId, at, resetRejectedBudget = false) {
|
|
|
11974
12019
|
}
|
|
11975
12020
|
}
|
|
11976
12021
|
}
|
|
11977
|
-
var planUpdateSpec, planAddNode, planDeleteNode, planAddEdge, planDeleteEdge, planAssignActor, planUpdateReviewRequirements, planNodeRetry, planUpdateFields, planApply;
|
|
12022
|
+
var planUpdateSpec, planAddNode, planDeleteNode, planAddEdge, planDeleteEdge, planAssignActor, reviewerKey, planUpdateReviewRequirements, planNodeRetry, planUpdateFields, planApply;
|
|
11978
12023
|
var init_plan = __esm({
|
|
11979
12024
|
"../engine/src/handlers/plan.ts"() {
|
|
11980
12025
|
"use strict";
|
|
@@ -12097,11 +12142,51 @@ var init_plan = __esm({
|
|
|
12097
12142
|
});
|
|
12098
12143
|
}
|
|
12099
12144
|
};
|
|
12145
|
+
reviewerKey = (r) => JSON.stringify([r.reviewerActorId, r.reviewGroup]);
|
|
12100
12146
|
planUpdateReviewRequirements = {
|
|
12101
12147
|
name: "plan/update-review-requirements",
|
|
12102
12148
|
kind: "plan.update_review_requirements",
|
|
12103
12149
|
apply(e, state, ctx) {
|
|
12104
|
-
|
|
12150
|
+
const node2 = state.node(e.nodeId);
|
|
12151
|
+
if (!node2) return;
|
|
12152
|
+
if (e.preserveReviews) {
|
|
12153
|
+
const previous3 = state.requirementsOf(e.nodeId);
|
|
12154
|
+
const previousByKey = new Map(previous3.map((r) => [reviewerKey(r), r]));
|
|
12155
|
+
const nextByKey = new Map(e.reviewers.map((r) => [reviewerKey(r), r]));
|
|
12156
|
+
const membershipChanged = previousByKey.size !== nextByKey.size || [...previousByKey.keys()].some((key) => !nextByKey.has(key));
|
|
12157
|
+
const currentByGroup = /* @__PURE__ */ new Map();
|
|
12158
|
+
for (const req of previous3) {
|
|
12159
|
+
const r = req.latestReviewId ? state.review(req.latestReviewId) : void 0;
|
|
12160
|
+
if (r && r.nodeId === e.nodeId && r.targetWorkId === node2.latestProposedWorkId && r.reviewGroup === req.reviewGroup && previousByKey.has(reviewerKey(r)) && nextByKey.has(reviewerKey(r))) {
|
|
12161
|
+
currentByGroup.set(req.reviewGroup, r.id);
|
|
12162
|
+
}
|
|
12163
|
+
}
|
|
12164
|
+
const next = [...nextByKey.values()].map((r) => {
|
|
12165
|
+
const old = previousByKey.get(reviewerKey(r));
|
|
12166
|
+
return {
|
|
12167
|
+
nodeId: e.nodeId,
|
|
12168
|
+
reviewerActorId: r.reviewerActorId,
|
|
12169
|
+
reviewGroup: r.reviewGroup,
|
|
12170
|
+
source: r.source,
|
|
12171
|
+
assignedBy: old?.assignedBy ?? ctx.actorId,
|
|
12172
|
+
// assignedAt 同时是历史判决回填的名单纪元。名单变更必须推进,避免拿缩减后的组集
|
|
12173
|
+
// 去补判旧 work;本轮有效票由 durable 指针保留,不靠时间戳继承(ADR 0533)。
|
|
12174
|
+
assignedAt: membershipChanged ? ctx.at : old?.assignedAt ?? ctx.at,
|
|
12175
|
+
latestReviewId: currentByGroup.get(r.reviewGroup) ?? null
|
|
12176
|
+
};
|
|
12177
|
+
});
|
|
12178
|
+
for (const r of state.allReviews()) {
|
|
12179
|
+
if (r.nodeId === e.nodeId && !nextByKey.has(reviewerKey(r)) && isReviewKillable(r)) {
|
|
12180
|
+
killReviewRow(state, r, ctx.at);
|
|
12181
|
+
}
|
|
12182
|
+
}
|
|
12183
|
+
const unchanged = previous3.length === next.length && next.every((r) => {
|
|
12184
|
+
const old = previousByKey.get(reviewerKey(r));
|
|
12185
|
+
return old?.source === r.source && old.latestReviewId === r.latestReviewId;
|
|
12186
|
+
});
|
|
12187
|
+
if (!unchanged) state.replaceRequirements(e.nodeId, next);
|
|
12188
|
+
return;
|
|
12189
|
+
}
|
|
12105
12190
|
killNodeReviews(state, e.nodeId, ctx.at);
|
|
12106
12191
|
state.replaceRequirements(e.nodeId, e.reviewers.map((r) => ({
|
|
12107
12192
|
nodeId: e.nodeId,
|
|
@@ -12112,6 +12197,9 @@ var init_plan = __esm({
|
|
|
12112
12197
|
assignedAt: ctx.at,
|
|
12113
12198
|
latestReviewId: null
|
|
12114
12199
|
})));
|
|
12200
|
+
},
|
|
12201
|
+
async effect(e, ctx) {
|
|
12202
|
+
if (e.preserveReviews) await ctx.cancelKilledSessions(e.workorderId, { nodeId: e.nodeId, retained: e.reviewers });
|
|
12115
12203
|
}
|
|
12116
12204
|
};
|
|
12117
12205
|
planNodeRetry = {
|
|
@@ -12626,7 +12714,7 @@ var init_work = __esm({
|
|
|
12626
12714
|
workContentEdited = {
|
|
12627
12715
|
name: "work/content-edited",
|
|
12628
12716
|
kind: "work.content_edited",
|
|
12629
|
-
apply(e, state) {
|
|
12717
|
+
apply(e, state, ctx) {
|
|
12630
12718
|
const work = state.work(e.workId);
|
|
12631
12719
|
if (!work) return;
|
|
12632
12720
|
const node2 = state.node(work.nodeId);
|
|
@@ -12634,7 +12722,7 @@ var init_work = __esm({
|
|
|
12634
12722
|
const artifacts = state.artifactsOf(e.workId);
|
|
12635
12723
|
const target = artifacts.find((a) => a.id === e.artifactId);
|
|
12636
12724
|
if (!target || target.contentRef !== e.previousRef || target.contentKind === "external") return;
|
|
12637
|
-
state.replaceArtifacts(e.workId, artifacts.map((a) => a.id === target.id ? { ...a, contentRef: e.contentRef } : a));
|
|
12725
|
+
state.replaceArtifacts(e.workId, artifacts.map((a) => a.id === target.id ? { ...a, contentRef: e.contentRef, contentEditedAt: ctx.at } : a));
|
|
12638
12726
|
}
|
|
12639
12727
|
};
|
|
12640
12728
|
workConclude = {
|
|
@@ -13199,6 +13287,8 @@ function versionCommandEvent(event) {
|
|
|
13199
13287
|
return { ...event, semanticsVersion: 3 };
|
|
13200
13288
|
}
|
|
13201
13289
|
switch (event.kind) {
|
|
13290
|
+
case "plan.update_review_requirements":
|
|
13291
|
+
return { ...event, preserveReviews: true };
|
|
13202
13292
|
case "work.accept":
|
|
13203
13293
|
case "work.kill":
|
|
13204
13294
|
case "work.handoff_recorded":
|
|
@@ -13764,7 +13854,7 @@ var init_bus = __esm({
|
|
|
13764
13854
|
// ★ 设计 §workorder.sealed/paused「按 work.kill 逻辑杀」:killAllWorks 只置状态,会话取消在这里做——
|
|
13765
13855
|
// apply 已把被杀 work/review 置 deadAt/cancelledAt,这里按 store 找它们仍挂着的会话 cancel
|
|
13766
13856
|
//(deadAt 且 endedAt 未置 = 刚被杀未退出的在途会话)。没会话 / 已退 → no-op。
|
|
13767
|
-
cancelKilledSessions: async () => {
|
|
13857
|
+
cancelKilledSessions: async (_workorderId, reviewScope) => {
|
|
13768
13858
|
let snap;
|
|
13769
13859
|
try {
|
|
13770
13860
|
snap = await this.store.transaction((tx) => tx.loadWorkorder(record8.workorderId));
|
|
@@ -13780,10 +13870,13 @@ var init_bus = __esm({
|
|
|
13780
13870
|
console.warn(`[bus] cancelKilledSessions ${what} ${id}: ${String(err)}`);
|
|
13781
13871
|
}
|
|
13782
13872
|
};
|
|
13783
|
-
for (const w2 of snap.works) {
|
|
13873
|
+
for (const w2 of reviewScope === void 0 ? snap.works : []) {
|
|
13784
13874
|
if (w2.deadAt && w2.endedAt === null) await cancel(w2.id, "work");
|
|
13785
13875
|
}
|
|
13786
13876
|
for (const r of snap.reviews) {
|
|
13877
|
+
if (reviewScope && (r.nodeId !== reviewScope.nodeId || reviewScope.retained.some(
|
|
13878
|
+
(req) => req.reviewerActorId === r.reviewerActorId && req.reviewGroup === r.reviewGroup
|
|
13879
|
+
))) continue;
|
|
13787
13880
|
if (r.cancelledAt && r.endedAt === null) await cancel(r.id, "review");
|
|
13788
13881
|
}
|
|
13789
13882
|
},
|
|
@@ -16236,6 +16329,10 @@ var init_dispatcher = __esm({
|
|
|
16236
16329
|
// 凭证是执行者属性,要人改(等待策略:熔断)
|
|
16237
16330
|
[NO_BINDING_REASON]: "A",
|
|
16238
16331
|
// 僵尸账号:只有人给它一个有效绑定才可能变
|
|
16332
|
+
// 绑到了**别家公司**的机器上(ADR-0164 §5 的租户闸)。与上一条同构:绑定本身就是错的,
|
|
16333
|
+
// 只有人改绑才可能变——而「改绑」正是 A 轴的第一条触发(executorSignature 含 nodeId),
|
|
16334
|
+
// 改完自动复位、不必再等一次人工干预。落 C 也不算错(安全默认),但会让人多做一步。
|
|
16335
|
+
"node-not-in-company": "A",
|
|
16239
16336
|
"clean": "B",
|
|
16240
16337
|
// 机器好好的、就是没交东西 → 换个东西给它做才有意义
|
|
16241
16338
|
"timeout": "B",
|
|
@@ -21158,16 +21255,18 @@ var init_kernel_bridge = __esm({
|
|
|
21158
21255
|
const artId = op.artifactId;
|
|
21159
21256
|
const opReviewers = op.reviewers;
|
|
21160
21257
|
const editReviewRoles = op.reviewRoles;
|
|
21258
|
+
const closureReviewers = (artifacts.get(artId)?.reviewers ?? []).filter((r) => r.source === "closure");
|
|
21161
21259
|
if (artId && (opReviewers || editReviewRoles !== void 0)) {
|
|
21162
21260
|
const art = artifacts.get(artId);
|
|
21163
21261
|
const effectiveRoles = editReviewRoles !== void 0 ? editReviewRoles === null ? void 0 : editReviewRoles : art?.reviewRoles;
|
|
21164
|
-
const effectiveExplicit = opReviewers ?? (art?.reviewers ?? []).map((r) => ({ actor: r.actor, source: r.source }));
|
|
21262
|
+
const effectiveExplicit = opReviewers ?? (art?.reviewers ?? []).filter((r) => r.source !== "closure").map((r) => ({ actor: r.actor, source: r.source }));
|
|
21165
21263
|
const { reviewers: nextReqs, staffingIssues } = resolveNodeReviewers({
|
|
21166
21264
|
typeName: art?.type ?? "",
|
|
21167
21265
|
typeDef: art ? this.schema.get(art.type) : void 0,
|
|
21168
21266
|
...effectiveRoles !== void 0 ? { reviewRoles: effectiveRoles } : {},
|
|
21169
21267
|
explicitReviewers: effectiveExplicit,
|
|
21170
|
-
roleIndex: this.roleIndex
|
|
21268
|
+
roleIndex: this.roleIndex,
|
|
21269
|
+
closureGate: closureReviewers.map((r) => r.actor)
|
|
21171
21270
|
});
|
|
21172
21271
|
for (const issue2 of staffingIssues) {
|
|
21173
21272
|
reviewerStaffing.push({ nodeId: artId, type: art?.type ?? "", role: issue2.role, from: issue2.from });
|
|
@@ -21226,7 +21325,7 @@ var init_kernel_bridge = __esm({
|
|
|
21226
21325
|
...opDescription !== void 0 ? { description: opDescription } : {},
|
|
21227
21326
|
...fields ? { fields: { ...previous3.fields, ...fields } } : {},
|
|
21228
21327
|
...editReviewRoles !== void 0 ? { reviewRoles: editReviewRoles ?? void 0 } : {},
|
|
21229
|
-
...opReviewers ? { reviewers: opReviewers } : {}
|
|
21328
|
+
...opReviewers ? { reviewers: [...opReviewers, ...closureReviewers] } : {}
|
|
21230
21329
|
});
|
|
21231
21330
|
applied++;
|
|
21232
21331
|
break;
|
|
@@ -28739,9 +28838,22 @@ var init_chat_item_ledger = __esm({
|
|
|
28739
28838
|
...deterministicId ? { deterministicId } : {}
|
|
28740
28839
|
});
|
|
28741
28840
|
}
|
|
28742
|
-
/**
|
|
28841
|
+
/**
|
|
28842
|
+
* 服务端合成的错误帧(session.done 抛 / exit 判失败)。不入正文,归 control。
|
|
28843
|
+
*
|
|
28844
|
+
* **返回 outcome 是为了让调用方把它推上 v3 流**(ADR-0510 D5/D6,2026-09-10 修)。
|
|
28845
|
+
* 在此之前这条行只落库、不发帧:客户端在这一轮里唯一收到的终态信号是 `emitV3TurnTerminal`
|
|
28846
|
+
* 发的 `turn_failed`,而那一帧不带任何正文、客户端的 `applyFrame` 又明说「不建 item、
|
|
28847
|
+
* 仅推进 frameSeq」——于是失败原因整条飞不到还开着的页面上,只有下一次 `/items` 快照
|
|
28848
|
+
* 才补得上。生产现场(v2.openfriday.ai,2026-09-10 05:39 那一轮):runtime 报
|
|
28849
|
+
* 「401 Authentication Fails」,库里三处都记着,页面上从头到尾是空的。
|
|
28850
|
+
*
|
|
28851
|
+
* 号取的是**建行时那两格钟**(`startedVersion` 给 wire 的 `started` 帧、`version` 给这一行
|
|
28852
|
+
* 本身的写),与 provider item 走 {@link ChatItemLedger.apply} 那条路逐字同构——
|
|
28853
|
+
* 快照与增量流因此还是同一套版本空间。
|
|
28854
|
+
*/
|
|
28743
28855
|
recordError(text5, extra) {
|
|
28744
|
-
this.insert({
|
|
28856
|
+
const id = this.insert({
|
|
28745
28857
|
kind: "control",
|
|
28746
28858
|
role: "assistant",
|
|
28747
28859
|
status: "failed",
|
|
@@ -28757,6 +28869,8 @@ var init_chat_item_ledger = __esm({
|
|
|
28757
28869
|
...extra?.detail ? { detail: extra.detail } : {}
|
|
28758
28870
|
}
|
|
28759
28871
|
});
|
|
28872
|
+
const row = this.rows.get(id);
|
|
28873
|
+
return { itemId: id, version: row.wireVersion, startedVersion: row.startedVersion, ord: row.ord };
|
|
28760
28874
|
}
|
|
28761
28875
|
/**
|
|
28762
28876
|
* 回灌重启前已经落库的那段正文(`chat-recovery` 重挂 / 迟到对账用)。
|
|
@@ -29272,6 +29386,8 @@ function runBackgroundAssistantChatTurn(deps) {
|
|
|
29272
29386
|
...assistantMsgId ? { messageId: assistantMsgId } : {},
|
|
29273
29387
|
log: log3
|
|
29274
29388
|
});
|
|
29389
|
+
const liveTurn = deps.liveChat && deps.liveHandle ? deps.liveChat.start(deps.chatSessionId, deps.liveHandle, { items: itemLedger }) : void 0;
|
|
29390
|
+
if (assistantMsgId) liveTurn?.setAssistantMessageId(assistantMsgId);
|
|
29275
29391
|
const normalizedSource = typeof deps.session.onNormalizedProviderEvent === "function" ? {
|
|
29276
29392
|
onNormalizedProviderEvent: deps.session.onNormalizedProviderEvent.bind(deps.session),
|
|
29277
29393
|
finishTurn: (signal) => deps.session.finishNormalizedTurn?.(signal)
|
|
@@ -29297,7 +29413,8 @@ function runBackgroundAssistantChatTurn(deps) {
|
|
|
29297
29413
|
let ckptDirty = false;
|
|
29298
29414
|
normalizedSource.onNormalizedProviderEvent((event) => {
|
|
29299
29415
|
try {
|
|
29300
|
-
|
|
29416
|
+
if (liveTurn) liveTurn.applyNormalizedEvent(event);
|
|
29417
|
+
else itemLedger.apply(event);
|
|
29301
29418
|
} catch {
|
|
29302
29419
|
}
|
|
29303
29420
|
ckptDirty = true;
|
|
@@ -29335,34 +29452,59 @@ function runBackgroundAssistantChatTurn(deps) {
|
|
|
29335
29452
|
}
|
|
29336
29453
|
const finalize = async (status) => {
|
|
29337
29454
|
if (ckptTimer) clearInterval(ckptTimer);
|
|
29338
|
-
|
|
29339
|
-
|
|
29340
|
-
|
|
29341
|
-
|
|
29342
|
-
|
|
29343
|
-
|
|
29344
|
-
|
|
29345
|
-
|
|
29346
|
-
|
|
29347
|
-
|
|
29348
|
-
|
|
29349
|
-
|
|
29350
|
-
|
|
29351
|
-
|
|
29352
|
-
|
|
29353
|
-
|
|
29354
|
-
|
|
29355
|
-
|
|
29356
|
-
|
|
29357
|
-
|
|
29358
|
-
|
|
29455
|
+
try {
|
|
29456
|
+
await checkpointChain.catch(() => void 0);
|
|
29457
|
+
normalizedSource.finishTurn(status === "done" ? "completed" : "failed");
|
|
29458
|
+
itemLedger.finish(status === "done" ? "completed" : "failed");
|
|
29459
|
+
await itemLedger.drain();
|
|
29460
|
+
const projectedContent = itemLedger.projectContent();
|
|
29461
|
+
const parts = collectedParts();
|
|
29462
|
+
await finalizeAssistantRow({
|
|
29463
|
+
chatStore: deps.chatStore,
|
|
29464
|
+
chatSessionId: deps.chatSessionId,
|
|
29465
|
+
assistantMsgId,
|
|
29466
|
+
content: projectedContent,
|
|
29467
|
+
parts,
|
|
29468
|
+
status,
|
|
29469
|
+
...runId ? { runId } : {},
|
|
29470
|
+
now
|
|
29471
|
+
});
|
|
29472
|
+
if (typeof deps.chatStore.updateSession === "function") {
|
|
29473
|
+
try {
|
|
29474
|
+
await deps.chatStore.updateSession(deps.chatSessionId, {
|
|
29475
|
+
runtimeSessionId: deps.session.nativeSessionId ?? null,
|
|
29476
|
+
touchedAt: now()
|
|
29477
|
+
});
|
|
29478
|
+
} catch {
|
|
29479
|
+
}
|
|
29480
|
+
}
|
|
29481
|
+
if (liveTurn) {
|
|
29482
|
+
if (projectedContent) liveTurn.emit({ type: "text", text: projectedContent });
|
|
29483
|
+
liveTurn.emitLive({
|
|
29484
|
+
protocolVersion: 2,
|
|
29485
|
+
streamId: `background:${deps.chatSessionId}`,
|
|
29486
|
+
turnId: deps.turnId ?? fallbackTurnId(runId),
|
|
29487
|
+
itemId: `background:${assistantMsgId ?? runId}`,
|
|
29488
|
+
itemType: "message",
|
|
29489
|
+
operation: "completed",
|
|
29490
|
+
payload: { role: "assistant", text: projectedContent }
|
|
29359
29491
|
});
|
|
29492
|
+
}
|
|
29493
|
+
try {
|
|
29494
|
+
deps.onSettled?.();
|
|
29360
29495
|
} catch {
|
|
29361
29496
|
}
|
|
29362
|
-
}
|
|
29363
|
-
|
|
29364
|
-
|
|
29365
|
-
|
|
29497
|
+
} finally {
|
|
29498
|
+
liveTurn?.emit({ type: "done" });
|
|
29499
|
+
liveTurn?.emitLive({
|
|
29500
|
+
protocolVersion: 2,
|
|
29501
|
+
streamId: `background:${deps.chatSessionId}`,
|
|
29502
|
+
turnId: deps.turnId ?? fallbackTurnId(runId),
|
|
29503
|
+
itemId: "background:terminal",
|
|
29504
|
+
itemType: "control",
|
|
29505
|
+
operation: status === "done" ? "turn_completed" : "turn_failed"
|
|
29506
|
+
});
|
|
29507
|
+
liveTurn?.finish(status);
|
|
29366
29508
|
}
|
|
29367
29509
|
};
|
|
29368
29510
|
void Promise.resolve(deps.session.done).then(() => finalize("done").catch(() => void 0)).catch((err) => {
|
|
@@ -142281,71 +142423,6 @@ var init_collect = __esm({
|
|
|
142281
142423
|
}
|
|
142282
142424
|
});
|
|
142283
142425
|
|
|
142284
|
-
// ../server/src/domains/projects/ephemeral-project.ts
|
|
142285
|
-
function isEphemeralProject(projectId2) {
|
|
142286
|
-
return projectId2.startsWith(EPHEMERAL_PROJECT_PREFIX);
|
|
142287
|
-
}
|
|
142288
|
-
function isUncategorizedProjectId(projectId2) {
|
|
142289
|
-
return projectId2 === UNCATEGORIZED_PROJECT_ID;
|
|
142290
|
-
}
|
|
142291
|
-
function ephemeralProjectIdFor(workOrderId) {
|
|
142292
|
-
return `${EPHEMERAL_PROJECT_PREFIX}${workOrderId.replace(/[^a-zA-Z0-9_-]+/g, "_")}`;
|
|
142293
|
-
}
|
|
142294
|
-
function ephemeralProjectNameFor(workOrderId, title) {
|
|
142295
|
-
return `\u4E34\u65F6\u9879\u76EE \xB7 ${title?.trim() || workOrderId}`;
|
|
142296
|
-
}
|
|
142297
|
-
async function ensureWorkorderProject(deps) {
|
|
142298
|
-
const { workOrderId, projectId: projectId2, title, createdBy } = deps;
|
|
142299
|
-
try {
|
|
142300
|
-
if (projectId2) {
|
|
142301
|
-
await deps.upsertBinding({ workOrderId, projectId: projectId2 });
|
|
142302
|
-
return projectId2;
|
|
142303
|
-
}
|
|
142304
|
-
const id = ephemeralProjectIdFor(workOrderId);
|
|
142305
|
-
await deps.createProject({
|
|
142306
|
-
id,
|
|
142307
|
-
name: ephemeralProjectNameFor(workOrderId, title),
|
|
142308
|
-
description: `\u5EFA\u5355\u65F6\u672A\u6307\u5B9A\u9879\u76EE\uFF0C\u7CFB\u7EDF\u81EA\u52A8\u4E3A\u5DE5\u5355 ${workOrderId} \u521B\u5EFA\u7684\u4E34\u65F6\u9879\u76EE\u3002\u53EF\u7528 \`PATCH /api/work-orders/${workOrderId}/project\` \u6539\u6302\u5230\u6B63\u5F0F\u9879\u76EE\u3002`,
|
|
142309
|
-
...createdBy ? { createdBy } : {}
|
|
142310
|
-
});
|
|
142311
|
-
await deps.upsertBinding({ workOrderId, projectId: id });
|
|
142312
|
-
return id;
|
|
142313
|
-
} catch (e) {
|
|
142314
|
-
deps.onWarn?.(`[project] \u5DE5\u5355 ${workOrderId} \u7ED1\u5B9A\u9879\u76EE\u5931\u8D25\uFF08\u4E0D\u963B\u65AD\u5EFA\u5355\uFF09\uFF1A${String(e)}`);
|
|
142315
|
-
return null;
|
|
142316
|
-
}
|
|
142317
|
-
}
|
|
142318
|
-
function makeEnsureProjectFromStore(store) {
|
|
142319
|
-
return async (input) => {
|
|
142320
|
-
const existing = await store.getProject(input.id);
|
|
142321
|
-
if (existing) return { id: existing.id };
|
|
142322
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
142323
|
-
const createdById = input.createdBy?.trim();
|
|
142324
|
-
const createdBy = createdById && (createdById.startsWith("actor:human:") || createdById.startsWith("actor:agent:")) ? { id: createdById, kind: projectActorKind(createdById), name: null, avatar: null } : void 0;
|
|
142325
|
-
await store.upsertProject({
|
|
142326
|
-
id: input.id,
|
|
142327
|
-
name: input.name,
|
|
142328
|
-
...input.description !== void 0 ? { description: input.description } : {},
|
|
142329
|
-
...createdBy ? { createdBy } : {},
|
|
142330
|
-
createdAt: now,
|
|
142331
|
-
updatedAt: now
|
|
142332
|
-
});
|
|
142333
|
-
return { id: input.id };
|
|
142334
|
-
};
|
|
142335
|
-
}
|
|
142336
|
-
var EPHEMERAL_PROJECT_PREFIX, UNCATEGORIZED_PROJECT_ID, UNCATEGORIZED_PROJECT_NAME, UNCATEGORIZED_PROJECT_DESCRIPTION, UNCATEGORIZED_UNKNOWN_TIME;
|
|
142337
|
-
var init_ephemeral_project = __esm({
|
|
142338
|
-
"../server/src/domains/projects/ephemeral-project.ts"() {
|
|
142339
|
-
"use strict";
|
|
142340
|
-
init_src();
|
|
142341
|
-
EPHEMERAL_PROJECT_PREFIX = "proj_tmp_";
|
|
142342
|
-
UNCATEGORIZED_PROJECT_ID = "proj:uncategorized";
|
|
142343
|
-
UNCATEGORIZED_PROJECT_NAME = "\u65E0\u5206\u7C7B\u9879\u76EE";
|
|
142344
|
-
UNCATEGORIZED_PROJECT_DESCRIPTION = "\u6240\u6709\u672A\u9009\u62E9\u9879\u76EE\u7684\u5BF9\u8BDD\u76F8\u5173\u6587\u4EF6\u548C\u4EFB\u52A1\u81EA\u52A8\u5E76\u5165\u8FD9\u91CC\uFF0C\u7EC4\u7EC7\u5185\u6240\u6709\u4EBA\u53EF\u89C1\u3002";
|
|
142345
|
-
UNCATEGORIZED_UNKNOWN_TIME = "1970-01-01T00:00:00.000Z";
|
|
142346
|
-
}
|
|
142347
|
-
});
|
|
142348
|
-
|
|
142349
142426
|
// ../server/src/domains/collab/timeline.ts
|
|
142350
142427
|
function buildNodeTimeline(artifactId, snap, runIdByTarget) {
|
|
142351
142428
|
if (!snap) return { artifactId, items: [] };
|
|
@@ -159766,8 +159843,9 @@ var init_live_chat = __esm({
|
|
|
159766
159843
|
DEFAULT_LIVE_COALESCE_MS = 24;
|
|
159767
159844
|
DEFAULT_LIVE_COALESCE_MAX_CHUNKS = 32;
|
|
159768
159845
|
DEFAULT_LIVE_COALESCE_MAX_CHARS = 64 * 1024;
|
|
159769
|
-
LiveChatRegistry = class {
|
|
159846
|
+
LiveChatRegistry = class _LiveChatRegistry {
|
|
159770
159847
|
turns = /* @__PURE__ */ new Map();
|
|
159848
|
+
turnStartWaiters = /* @__PURE__ */ new Map();
|
|
159771
159849
|
epochs = /* @__PURE__ */ new Map();
|
|
159772
159850
|
bufferMax;
|
|
159773
159851
|
graceMs;
|
|
@@ -159813,6 +159891,15 @@ var init_live_chat = __esm({
|
|
|
159813
159891
|
v3Items: /* @__PURE__ */ new Map()
|
|
159814
159892
|
};
|
|
159815
159893
|
this.turns.set(chatSessionId, turn);
|
|
159894
|
+
this.flushPendingUserItems(chatSessionId);
|
|
159895
|
+
const waiters = this.turnStartWaiters.get(chatSessionId);
|
|
159896
|
+
this.turnStartWaiters.delete(chatSessionId);
|
|
159897
|
+
for (const wake of waiters ?? []) {
|
|
159898
|
+
try {
|
|
159899
|
+
wake();
|
|
159900
|
+
} catch {
|
|
159901
|
+
}
|
|
159902
|
+
}
|
|
159816
159903
|
return {
|
|
159817
159904
|
runtimeSessionId: turn.runtimeSessionId,
|
|
159818
159905
|
epoch: turn.liveEpoch,
|
|
@@ -159835,6 +159922,7 @@ var init_live_chat = __esm({
|
|
|
159835
159922
|
},
|
|
159836
159923
|
emitLive: (event) => this.emitLive(turn, event),
|
|
159837
159924
|
applyNormalizedEvent: (event) => this.applyNormalizedEvent(turn, event),
|
|
159925
|
+
publishServerItem: (item) => this.publishServerItem(turn, item),
|
|
159838
159926
|
subscribe: (sub) => {
|
|
159839
159927
|
turn.subscribers.add(sub);
|
|
159840
159928
|
return () => {
|
|
@@ -159884,6 +159972,26 @@ var init_live_chat = __esm({
|
|
|
159884
159972
|
...opts.items ? { items: opts.items } : {}
|
|
159885
159973
|
};
|
|
159886
159974
|
}
|
|
159975
|
+
/** 只通知开轮,不承载内容。先检查再登记均为同步,避免空闲检查与订阅之间漏掉开轮。 */
|
|
159976
|
+
onNextTurn(chatSessionId, wake) {
|
|
159977
|
+
if (this.isRunning(chatSessionId)) {
|
|
159978
|
+
wake();
|
|
159979
|
+
return () => {
|
|
159980
|
+
};
|
|
159981
|
+
}
|
|
159982
|
+
let waiters = this.turnStartWaiters.get(chatSessionId);
|
|
159983
|
+
if (!waiters) {
|
|
159984
|
+
waiters = /* @__PURE__ */ new Set();
|
|
159985
|
+
this.turnStartWaiters.set(chatSessionId, waiters);
|
|
159986
|
+
}
|
|
159987
|
+
waiters.add(wake);
|
|
159988
|
+
return () => {
|
|
159989
|
+
waiters.delete(wake);
|
|
159990
|
+
if (waiters.size === 0 && this.turnStartWaiters.get(chatSessionId) === waiters) {
|
|
159991
|
+
this.turnStartWaiters.delete(chatSessionId);
|
|
159992
|
+
}
|
|
159993
|
+
};
|
|
159994
|
+
}
|
|
159887
159995
|
/**
|
|
159888
159996
|
* 归一层中间事件 → 带 `itemId` 的 item op(ADR D4 第 2 跳)+ v3 帧 dual-emit(S3)。
|
|
159889
159997
|
*
|
|
@@ -159981,6 +160089,45 @@ var init_live_chat = __esm({
|
|
|
159981
160089
|
};
|
|
159982
160090
|
this.publishV3Coalesced(turn, frame);
|
|
159983
160091
|
}
|
|
160092
|
+
/**
|
|
160093
|
+
* 服务端合成 item → 两帧(`started` + 终态),与 {@link emitV3} 给 provider item 发的那两帧
|
|
160094
|
+
* **逐字同构**:先用建行预留的那一格发 `started`(客户端以它为该 item 首次亮相、payload 首次成形),
|
|
160095
|
+
* 再用这一行本身的写号发 `completed` / `failed`。
|
|
160096
|
+
*
|
|
160097
|
+
* 为什么不学 {@link publishUserItem} 用一帧 `set_text`:那条路是给「一次性成形的**正文**」用的,
|
|
160098
|
+
* 落在客户端桶里 `status` 恒为 `streaming`——用户那条消息无所谓,失败收尾的 control 行却必须是
|
|
160099
|
+
* 终态,否则这一轮在客户端看来永远没关。
|
|
160100
|
+
*/
|
|
160101
|
+
publishServerItem(turn, item) {
|
|
160102
|
+
if (turn.status !== "running") return;
|
|
160103
|
+
const perItem = turn.v3Items.get(item.itemId) ?? { textOffset: 0, started: false };
|
|
160104
|
+
const ord = turn.items?.ordFor(item.itemId) ?? item.ord ?? void 0;
|
|
160105
|
+
const base = {
|
|
160106
|
+
protocolVersion: 3,
|
|
160107
|
+
streamId: turn.liveStreamId,
|
|
160108
|
+
turnId: this.wireTurnId(turn),
|
|
160109
|
+
itemId: item.itemId,
|
|
160110
|
+
itemType: item.itemType,
|
|
160111
|
+
...ord !== void 0 ? { ord } : {},
|
|
160112
|
+
offset: 0
|
|
160113
|
+
};
|
|
160114
|
+
if (!perItem.started && item.startedVersion !== void 0) {
|
|
160115
|
+
this.publishV3Coalesced(turn, {
|
|
160116
|
+
...base,
|
|
160117
|
+
operation: "started",
|
|
160118
|
+
itemVersion: item.startedVersion,
|
|
160119
|
+
payload: item.payload
|
|
160120
|
+
});
|
|
160121
|
+
perItem.started = true;
|
|
160122
|
+
}
|
|
160123
|
+
turn.v3Items.set(item.itemId, perItem);
|
|
160124
|
+
this.publishV3Coalesced(turn, {
|
|
160125
|
+
...base,
|
|
160126
|
+
operation: item.status === "failed" ? "failed" : "completed",
|
|
160127
|
+
itemVersion: item.version,
|
|
160128
|
+
payload: item.payload
|
|
160129
|
+
});
|
|
160130
|
+
}
|
|
159984
160131
|
assignV3Seq(turn, frame) {
|
|
159985
160132
|
return { ...frame, epoch: turn.liveEpoch, seq: ++turn.v3Seq };
|
|
159986
160133
|
}
|
|
@@ -160072,9 +160219,34 @@ var init_live_chat = __esm({
|
|
|
160072
160219
|
* (`server.ts` 里 `recordUserSubmission` 在 `liveChat.start` 之前)。拿不到就不发——
|
|
160073
160220
|
* 前端退回「等下一次快照」,与修复前一致,不会更坏。
|
|
160074
160221
|
*/
|
|
160222
|
+
/**
|
|
160223
|
+
* 「user item 已落库、但这条会话的 live 轮还没 start」时的暂存位(见 {@link publishUserItem})。
|
|
160224
|
+
*
|
|
160225
|
+
* 每会话只留最近几条:它的唯一用途是补发**当前这一次提交**,不是可靠队列。留太多等于把
|
|
160226
|
+
* 一条早就过期的 user 帧在下一轮开头推出去,那比丢了更糟(用户会看到上一轮的话冒出来)。
|
|
160227
|
+
*/
|
|
160228
|
+
pendingUserItems = /* @__PURE__ */ new Map();
|
|
160229
|
+
/** 单会话暂存上限。正常只会有 1 条(一次提交一条);给到 4 是为了并发提交时不至于丢。 */
|
|
160230
|
+
static PENDING_USER_ITEM_MAX = 4;
|
|
160231
|
+
bufferUserItem(chatSessionId, item) {
|
|
160232
|
+
const list2 = this.pendingUserItems.get(chatSessionId) ?? [];
|
|
160233
|
+
list2.push(item);
|
|
160234
|
+
while (list2.length > _LiveChatRegistry.PENDING_USER_ITEM_MAX) list2.shift();
|
|
160235
|
+
this.pendingUserItems.set(chatSessionId, list2);
|
|
160236
|
+
}
|
|
160237
|
+
/** `start` 之后立刻调:把暂存的 user item 补发出去。发不出去就丢掉,不再往下一轮带。 */
|
|
160238
|
+
flushPendingUserItems(chatSessionId) {
|
|
160239
|
+
const list2 = this.pendingUserItems.get(chatSessionId);
|
|
160240
|
+
if (!list2?.length) return;
|
|
160241
|
+
this.pendingUserItems.delete(chatSessionId);
|
|
160242
|
+
for (const item of list2) this.publishUserItem(chatSessionId, item);
|
|
160243
|
+
}
|
|
160075
160244
|
publishUserItem(chatSessionId, item) {
|
|
160076
160245
|
const turn = this.turns.get(chatSessionId);
|
|
160077
|
-
if (!turn)
|
|
160246
|
+
if (!turn) {
|
|
160247
|
+
this.bufferUserItem(chatSessionId, item);
|
|
160248
|
+
return false;
|
|
160249
|
+
}
|
|
160078
160250
|
const payload = { role: "user", text: item.text };
|
|
160079
160251
|
if (item.clientSubmitId) payload.clientSubmitId = item.clientSubmitId;
|
|
160080
160252
|
if (item.attachments?.length) payload.attachments = item.attachments;
|
|
@@ -160118,11 +160290,17 @@ var init_live_chat = __esm({
|
|
|
160118
160290
|
const frame = {
|
|
160119
160291
|
protocolVersion: 3,
|
|
160120
160292
|
streamId: turn.liveStreamId,
|
|
160121
|
-
|
|
160293
|
+
// 账本轮次 id,与 item 帧同一套(#1249 把 emitV3 改成了 wireTurnId,收尾帧当时漏了)。
|
|
160294
|
+
// 客户端此前把收尾帧整条丢弃(`applyFrame`:「不建 item,仅推进 frameSeq」),所以这个
|
|
160295
|
+
// 号是对是错都看不出来;相位一挂上去它就得对——否则相位会记到一个桶里根本不存在的 turnId 上。
|
|
160296
|
+
turnId: this.wireTurnId(turn),
|
|
160122
160297
|
itemId: `oasis-turn:${turn.liveEpoch}:${status}`,
|
|
160123
160298
|
itemType: "control",
|
|
160124
160299
|
operation,
|
|
160125
160300
|
itemVersion: 0,
|
|
160301
|
+
// 相位被携带,不被推导(ADR-0510 D6):这一轮到底是成了还是败了,服务端此刻知道,
|
|
160302
|
+
// 客户端替换即可,不用再去把一轮里每条 item 的 status 取或来猜。
|
|
160303
|
+
phase: status === "done" ? "succeeded" : "failed",
|
|
160126
160304
|
epoch: turn.liveEpoch,
|
|
160127
160305
|
seq: ++turn.v3Seq
|
|
160128
160306
|
};
|
|
@@ -160489,6 +160667,40 @@ var init_live_chat = __esm({
|
|
|
160489
160667
|
}
|
|
160490
160668
|
});
|
|
160491
160669
|
|
|
160670
|
+
// ../server/src/chat-turn-wait.ts
|
|
160671
|
+
function waitForChatTurn(res, liveChat, sessionId, startKeepalive, ready = false) {
|
|
160672
|
+
res.writeHead(200, {
|
|
160673
|
+
"content-type": "application/x-oasis-live-v3+ndjson; charset=utf-8",
|
|
160674
|
+
"cache-control": "no-cache, no-transform",
|
|
160675
|
+
"x-accel-buffering": "no",
|
|
160676
|
+
"access-control-expose-headers": "X-Chat-Session-Watch, X-Chat-Waiting, X-Can-Append",
|
|
160677
|
+
"x-chat-session-watch": "1",
|
|
160678
|
+
"x-chat-waiting": "1",
|
|
160679
|
+
"x-can-append": "0"
|
|
160680
|
+
});
|
|
160681
|
+
res.flushHeaders();
|
|
160682
|
+
const stopKeepalive = startKeepalive(res);
|
|
160683
|
+
let detach = () => {
|
|
160684
|
+
};
|
|
160685
|
+
const cleanup = () => {
|
|
160686
|
+
stopKeepalive();
|
|
160687
|
+
detach();
|
|
160688
|
+
};
|
|
160689
|
+
res.once("close", cleanup);
|
|
160690
|
+
const wake = () => {
|
|
160691
|
+
cleanup();
|
|
160692
|
+
if (!res.destroyed) res.end('{"type":"turn_available"}\n');
|
|
160693
|
+
};
|
|
160694
|
+
if (ready) wake();
|
|
160695
|
+
else detach = liveChat.onNextTurn(sessionId, wake);
|
|
160696
|
+
if (res.destroyed) cleanup();
|
|
160697
|
+
}
|
|
160698
|
+
var init_chat_turn_wait = __esm({
|
|
160699
|
+
"../server/src/chat-turn-wait.ts"() {
|
|
160700
|
+
"use strict";
|
|
160701
|
+
}
|
|
160702
|
+
});
|
|
160703
|
+
|
|
160492
160704
|
// ../server/src/chat-attachment-files.ts
|
|
160493
160705
|
function safeAttachmentName(raw) {
|
|
160494
160706
|
return (raw || "attachment").replace(/[^\w.\-]+/g, "_").replace(/^\.+/, "").slice(0, 80) || "attachment";
|
|
@@ -160686,13 +160898,24 @@ var init_closure_report = __esm({
|
|
|
160686
160898
|
});
|
|
160687
160899
|
|
|
160688
160900
|
// ../server/src/governance/workorder-drafts.ts
|
|
160689
|
-
var import_node_fs5, DRAFT_EDIT_LOCK_TTL_MS, WorkorderDraftStore, ident, seqNoOf, PostgresWorkorderDraftStore;
|
|
160901
|
+
var import_node_fs5, DRAFT_EDIT_LOCK_TTL_MS, WorkorderDraftTagMismatchError, WorkorderDraftStore, ident, seqNoOf, PostgresWorkorderDraftStore;
|
|
160690
160902
|
var init_workorder_drafts = __esm({
|
|
160691
160903
|
"../server/src/governance/workorder-drafts.ts"() {
|
|
160692
160904
|
"use strict";
|
|
160693
160905
|
import_node_fs5 = __toESM(require("node:fs"), 1);
|
|
160694
160906
|
init_closure_report();
|
|
160695
160907
|
DRAFT_EDIT_LOCK_TTL_MS = 12e4;
|
|
160908
|
+
WorkorderDraftTagMismatchError = class extends Error {
|
|
160909
|
+
constructor(draftId, expectedWorkspace, actualWorkspace) {
|
|
160910
|
+
super(
|
|
160911
|
+
actualWorkspace === void 0 ? `\u5EFA\u5355\u8349\u6848 ${draftId} \u4E0D\u5728\u672C store\uFF08\u5F88\u53EF\u80FD\u5EFA\u5728\u53E6\u4E00\u4E2A schema \u7684\u5E93\u91CC\uFF09\u2014\u2014\u62D2\u7EDD\u6253\u6807` : `\u5EFA\u5355\u8349\u6848 ${draftId} \u7684 workspace \u662F ${actualWorkspace}\uFF0C\u4E0E\u672C\u6B21\u5EFA\u5355\u7684 ${String(expectedWorkspace)} \u4E0D\u7B26\u2014\u2014\u62D2\u7EDD\u6253\u6807\uFF08\u8DE8\u5E93\u649E\u53F7\u4F1A\u6539\u7ED1\u522B\u4EBA\u7684\u8349\u6848\uFF09`
|
|
160912
|
+
);
|
|
160913
|
+
this.draftId = draftId;
|
|
160914
|
+
this.expectedWorkspace = expectedWorkspace;
|
|
160915
|
+
this.actualWorkspace = actualWorkspace;
|
|
160916
|
+
this.name = "WorkorderDraftTagMismatchError";
|
|
160917
|
+
}
|
|
160918
|
+
};
|
|
160696
160919
|
WorkorderDraftStore = class {
|
|
160697
160920
|
constructor(file) {
|
|
160698
160921
|
this.file = file;
|
|
@@ -160796,14 +161019,28 @@ var init_workorder_drafts = __esm({
|
|
|
160796
161019
|
}
|
|
160797
161020
|
/** 给草案打上 chat 会话标(建单经 chat 路时,/api/cmd 拿到 X-Chat-Session-Id 后调)。
|
|
160798
161021
|
* 同时(若拿得到)盖上产出它的那一轮 runId,供 friday 重进对话页把该版草案归位到那条助手消息。
|
|
160799
|
-
* sourceRunId 一旦盖上不再改(同一版草案就属于那一轮)。
|
|
160800
|
-
|
|
161022
|
+
* sourceRunId 一旦盖上不再改(同一版草案就属于那一轮)。
|
|
161023
|
+
*
|
|
161024
|
+
* **`expectedWorkspace` 是防跨库改绑的那道闸(2026-09-10 线上事故)**:草案 id 形如
|
|
161025
|
+
* `wodraft:<n>`,而**每个 schema 各有一台发号器**(共享库一台、每家公司各一台,见
|
|
161026
|
+
* `PostgresWorkorderDraftStore.open` 建的 `workorder_draft_seq`)。于是「共享库的 70 号」和
|
|
161027
|
+
* 「didi 库的 70 号」是两份毫不相干的草案。一旦建草案与打标落在**不同的 store**上
|
|
161028
|
+
* (公司上下文解析不出来 → 建落共享库;打标按公司解析 → 打进公司库),这里就会拿着同一个
|
|
161029
|
+
* 号在另一个库里翻出**别人的草案**,把它的 chatSessionId / sourceRunId 覆盖成本次会话——
|
|
161030
|
+
* 别人的建单卡就这样出现在你的对话里,而原归属被覆盖后再也找不回来。
|
|
161031
|
+
*
|
|
161032
|
+
* 所以调用方**必须**把「我刚建的那张单的 workspace」一起传进来:对不上就抛,绝不写。
|
|
161033
|
+
* 查不到 id 同理——旧实现在这里是 `if (d) {}` 的静默 no-op,那正是这次事故连一行日志都没留下的原因。
|
|
161034
|
+
* 抛出的是 {@link WorkorderDraftTagMismatchError},调用方该**记账后继续**(草案本身已经建好,
|
|
161035
|
+
* 不该因为打标失败把整个建单回执变成失败),见 `/api/cmd` 与 playbooks 两处调用点。 */
|
|
161036
|
+
async tagSession(draftId, chatSessionId, sourceRunId, expectedWorkspace) {
|
|
160801
161037
|
const d = this.drafts.get(draftId);
|
|
160802
|
-
if (d) {
|
|
160803
|
-
|
|
160804
|
-
if (sourceRunId && !d.sourceRunId) d.sourceRunId = sourceRunId;
|
|
160805
|
-
await this.persist({ kind: "upsert", draft: d });
|
|
161038
|
+
if (!d || expectedWorkspace !== void 0 && d.workspace !== expectedWorkspace) {
|
|
161039
|
+
throw new WorkorderDraftTagMismatchError(draftId, expectedWorkspace, d?.workspace);
|
|
160806
161040
|
}
|
|
161041
|
+
d.chatSessionId = chatSessionId;
|
|
161042
|
+
if (sourceRunId && !d.sourceRunId) d.sourceRunId = sourceRunId;
|
|
161043
|
+
await this.persist({ kind: "upsert", draft: d });
|
|
160807
161044
|
}
|
|
160808
161045
|
/** 草案被就地编辑后(editDraftPlan 改了 plan.ops)调用:bump `at` 让 friday 重载据此察觉,
|
|
160809
161046
|
* 并**落盘**——否则编辑过的草案在 apply 前进程重启就丢改动(草案落盘的初衷正是扛重启)。 */
|
|
@@ -161596,9 +161833,14 @@ var init_assistants = __esm({
|
|
|
161596
161833
|
await this.opts.actors.disableActor(agentId, by).catch(() => {
|
|
161597
161834
|
});
|
|
161598
161835
|
}
|
|
161836
|
+
/** 本服务的公司 scope;未启用公司维度(单租户装配/单测)时 undefined = 不过滤,行为不变。 */
|
|
161837
|
+
scope() {
|
|
161838
|
+
const companyId = this.opts.companyId();
|
|
161839
|
+
return companyId ? { companyId } : void 0;
|
|
161840
|
+
}
|
|
161599
161841
|
/* ---------- runtime 挑选(ADR §3.3.a):负载均衡 + 幂等 tie-break ---------- */
|
|
161600
161842
|
async pickAssistantRuntime() {
|
|
161601
|
-
const runtimes = (await this.opts.listRuntimes()).filter((r) => r.status === "online");
|
|
161843
|
+
const runtimes = (await this.opts.listRuntimes(void 0, this.scope())).filter((r) => r.status === "online");
|
|
161602
161844
|
if (runtimes.length === 0) return null;
|
|
161603
161845
|
let defaultKind = DEFAULT_ASSISTANT_RUNTIME_KIND;
|
|
161604
161846
|
for (const r of runtimes) {
|
|
@@ -161625,7 +161867,7 @@ var init_assistants = __esm({
|
|
|
161625
161867
|
if (!actor.roles.includes("assistant")) return false;
|
|
161626
161868
|
const binding = await this.opts.registry.getBinding(agentId);
|
|
161627
161869
|
if (!binding || binding.status !== "active") return false;
|
|
161628
|
-
const runtime = (await this.opts.listRuntimes(binding.nodeId)).find((r) => r.kind === binding.runtimeKind);
|
|
161870
|
+
const runtime = (await this.opts.listRuntimes(binding.nodeId, this.scope())).find((r) => r.kind === binding.runtimeKind);
|
|
161629
161871
|
if (!runtime || runtime.status !== "online") return false;
|
|
161630
161872
|
return true;
|
|
161631
161873
|
}
|
|
@@ -162356,14 +162598,25 @@ var init_planner = __esm({
|
|
|
162356
162598
|
function systemActorFallback(id) {
|
|
162357
162599
|
return isSystemActorId(id) ? { name: SYSTEM_ACTOR_DISPLAY_NAME, avatar: SYSTEM_AVATAR_REF } : void 0;
|
|
162358
162600
|
}
|
|
162601
|
+
function defaultAvatarRefOf(id, kind) {
|
|
162602
|
+
if (kind === "system" || isSystemActorId(id)) return SYSTEM_AVATAR_REF;
|
|
162603
|
+
if (kind === "human" || isHumanActorId(id)) return defaultHumanAvatarRef(id);
|
|
162604
|
+
return void 0;
|
|
162605
|
+
}
|
|
162359
162606
|
function actorRefFieldsOf(a) {
|
|
162360
|
-
const avatar = a.avatar ?? (a.kind
|
|
162607
|
+
const avatar = a.avatar ?? defaultAvatarRefOf(a.id, a.kind);
|
|
162361
162608
|
return {
|
|
162362
162609
|
name: a.name,
|
|
162363
162610
|
...a.roles[0] ? { role: a.roles[0] } : {},
|
|
162364
162611
|
...avatar ? { avatar } : {}
|
|
162365
162612
|
};
|
|
162366
162613
|
}
|
|
162614
|
+
function actorRefFallbackOf(id) {
|
|
162615
|
+
const system = systemActorFallback(id);
|
|
162616
|
+
if (system) return system;
|
|
162617
|
+
const avatar = defaultAvatarRefOf(id);
|
|
162618
|
+
return avatar ? { avatar } : void 0;
|
|
162619
|
+
}
|
|
162367
162620
|
function overviewRootIdOf(arts) {
|
|
162368
162621
|
const roots = arts.filter((a) => a.inputs.length === 0);
|
|
162369
162622
|
if (roots.length <= 1) return roots[0]?.id;
|
|
@@ -186647,12 +186900,13 @@ var init_governance_service = __esm({
|
|
|
186647
186900
|
const binding = await this.options.projects.getWorkspaceBinding(ctx.organizationId, workorderId);
|
|
186648
186901
|
if (!binding) {
|
|
186649
186902
|
if (claimedProjectId) throw new KnowledgeGovernanceError(409, "KNOWLEDGE_PROJECT_BINDING_MISSING", "\u5DE5\u5355\u6CA1\u6709\u53EF\u4FE1\u9879\u76EE\u7ED1\u5B9A");
|
|
186903
|
+
await this.requireOrganizationActor(ctx);
|
|
186650
186904
|
return void 0;
|
|
186651
186905
|
}
|
|
186652
186906
|
if (claimedProjectId && binding.projectId !== claimedProjectId) {
|
|
186653
186907
|
throw new KnowledgeGovernanceError(409, "KNOWLEDGE_PROJECT_MISMATCH", "\u8BF7\u6C42\u9879\u76EE\u4E0E\u5DE5\u5355\u7ED1\u5B9A\u4E0D\u4E00\u81F4");
|
|
186654
186908
|
}
|
|
186655
|
-
if (this.options.projects.
|
|
186909
|
+
if (this.options.projects.isProjectScopeUnsupported(ctx.organizationId, binding.projectId)) {
|
|
186656
186910
|
await this.requireOrganizationActor(ctx);
|
|
186657
186911
|
return void 0;
|
|
186658
186912
|
}
|
|
@@ -186860,8 +187114,8 @@ var init_governance_service = __esm({
|
|
|
186860
187114
|
const projectId2 = input.scopeType === "project" ? input.scopeId : void 0;
|
|
186861
187115
|
if (input.scopeType === "project") {
|
|
186862
187116
|
if (!projectId2) throw new KnowledgeGovernanceError(400, "KNOWLEDGE_PROJECT_REQUIRED", "\u9879\u76EE Space \u5FC5\u987B\u6307\u5B9A\u9879\u76EE");
|
|
186863
|
-
if (this.options.projects.
|
|
186864
|
-
throw new KnowledgeGovernanceError(409, "KNOWLEDGE_EPHEMERAL_PROJECT", "\
|
|
187117
|
+
if (this.options.projects.isProjectScopeUnsupported(ctx.organizationId, projectId2)) {
|
|
187118
|
+
throw new KnowledgeGovernanceError(409, "KNOWLEDGE_EPHEMERAL_PROJECT", "\u8BE5\u9879\u76EE\u4E0D\u652F\u6301\u72EC\u7ACB\u77E5\u8BC6 Space");
|
|
186865
187119
|
}
|
|
186866
187120
|
await this.authorizeProjectResource(ctx, projectId2, "configure");
|
|
186867
187121
|
} else if (input.scopeId) {
|
|
@@ -186987,7 +187241,7 @@ var init_governance_service = __esm({
|
|
|
186987
187241
|
async reconfigureKnowledgeTree(ctx, store, organizationSpace, connection, _config) {
|
|
186988
187242
|
const projectEntries = [];
|
|
186989
187243
|
for (const project of await this.options.projects.listProjects(ctx.organizationId)) {
|
|
186990
|
-
if (this.options.projects.
|
|
187244
|
+
if (this.options.projects.isProjectScopeUnsupported(ctx.organizationId, project.id)) continue;
|
|
186991
187245
|
const projectSpace = (await store.list(this.projectScope(ctx, project.id), "space")).filter((space) => space.status !== "archived").sort((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
|
|
186992
187246
|
if (projectSpace) projectEntries.push({ project, space: projectSpace });
|
|
186993
187247
|
}
|
|
@@ -187206,8 +187460,8 @@ var init_governance_service = __esm({
|
|
|
187206
187460
|
async resolveRunTarget(ctx, projectId2) {
|
|
187207
187461
|
await this.requireOrganizationActor(ctx);
|
|
187208
187462
|
if (projectId2) {
|
|
187209
|
-
if (this.options.projects.
|
|
187210
|
-
throw new KnowledgeGovernanceError(409, "KNOWLEDGE_EPHEMERAL_PROJECT", "\
|
|
187463
|
+
if (this.options.projects.isProjectScopeUnsupported(ctx.organizationId, projectId2)) {
|
|
187464
|
+
throw new KnowledgeGovernanceError(409, "KNOWLEDGE_EPHEMERAL_PROJECT", "\u8BE5\u9879\u76EE\u6CA1\u6709\u72EC\u7ACB\u9879\u76EE\u77E5\u8BC6\u5E93\uFF0C\u8BF7\u9009\u62E9\u7EC4\u7EC7\u77E5\u8BC6\u5E93");
|
|
187211
187465
|
}
|
|
187212
187466
|
await this.authorizeProjectResource(ctx, projectId2, "publish");
|
|
187213
187467
|
}
|
|
@@ -187423,7 +187677,7 @@ var init_governance_service = __esm({
|
|
|
187423
187677
|
if (actor.kind !== "system") await this.requireOrganizationAdmin(ctx);
|
|
187424
187678
|
const project = await this.options.projects.getProject(ctx.organizationId, projectId2);
|
|
187425
187679
|
if (!project) throw new KnowledgeGovernanceError(404, "KNOWLEDGE_PROJECT_NOT_FOUND", "\u9879\u76EE\u4E0D\u5B58\u5728");
|
|
187426
|
-
if (this.options.projects.
|
|
187680
|
+
if (this.options.projects.isProjectScopeUnsupported(ctx.organizationId, projectId2)) return null;
|
|
187427
187681
|
const store = await this.options.storeFor(ctx.organizationId);
|
|
187428
187682
|
const scope = this.projectScope(ctx, projectId2);
|
|
187429
187683
|
const existingSpaces = await store.list(scope, "space");
|
|
@@ -190123,7 +190377,7 @@ var init_governance_service = __esm({
|
|
|
190123
190377
|
if (event.claimedProjectId !== trusted.projectId) {
|
|
190124
190378
|
throw new KnowledgeGovernanceError(409, "KNOWLEDGE_PROJECT_MISMATCH", "\u8BF7\u6C42\u9879\u76EE\u4E0E\u53EF\u4FE1\u6765\u6E90\u5F52\u5C5E\u4E0D\u4E00\u81F4");
|
|
190125
190379
|
}
|
|
190126
|
-
const targetProjectId = trusted.projectId && !this.options.projects.
|
|
190380
|
+
const targetProjectId = trusted.projectId && !this.options.projects.isProjectScopeUnsupported(ctx.organizationId, trusted.projectId) ? trusted.projectId : void 0;
|
|
190127
190381
|
const discoveryActor = await this.requireActiveActor(ctx.actorId, ctx.organizationId);
|
|
190128
190382
|
if (discoveryActor.kind !== "system") await this.requireOrganizationActor(ctx);
|
|
190129
190383
|
if (targetProjectId) {
|
|
@@ -190389,7 +190643,7 @@ var init_governance_service = __esm({
|
|
|
190389
190643
|
runs.push(...await this.enqueueDiscoveryEvent(ctx, event, runtime ?? void 0));
|
|
190390
190644
|
} catch (error2) {
|
|
190391
190645
|
const store = await this.options.storeFor(ctx.organizationId);
|
|
190392
|
-
const taskProjectId = projectId2 && !this.options.projects.
|
|
190646
|
+
const taskProjectId = projectId2 && !this.options.projects.isProjectScopeUnsupported(ctx.organizationId, projectId2) ? projectId2 : void 0;
|
|
190393
190647
|
const scope = this.scopeFor(ctx, taskProjectId);
|
|
190394
190648
|
const task = this.governanceTask(ctx, "discovery_failed", event.sourceType, `${event.sourceId}:${event.sourceVersionId}`, {
|
|
190395
190649
|
eventType: event.eventType,
|
|
@@ -198960,8 +199214,11 @@ var init_github = __esm({
|
|
|
198960
199214
|
sessionEnv.set("GH_CONFIG_DIR", configDir);
|
|
198961
199215
|
const appSlug = credentials["app_slug"];
|
|
198962
199216
|
if (appSlug) sessionEnv.set("GH_APP_SLUG", appSlug);
|
|
198963
|
-
const
|
|
198964
|
-
const
|
|
199217
|
+
const rawBotEmail = credentials["app_bot_email"] ?? "";
|
|
199218
|
+
const rawBotName = credentials["app_bot_name"] ?? "";
|
|
199219
|
+
const staleBot = Boolean(appSlug && rawBotEmail && !rawBotEmail.includes(`+${appSlug}[bot]@`));
|
|
199220
|
+
const botEmail = staleBot ? "" : rawBotEmail;
|
|
199221
|
+
const botName = staleBot ? appSlug ? `${appSlug}[bot]` : "" : rawBotName;
|
|
198965
199222
|
const signName = botName || name;
|
|
198966
199223
|
sessionEnv.set("GIT_AUTHOR_NAME", signName);
|
|
198967
199224
|
sessionEnv.set("GIT_AUTHOR_EMAIL", botEmail);
|
|
@@ -201251,10 +201508,8 @@ async function adoptGithubApp(store, args, deps = {}) {
|
|
|
201251
201508
|
await put3("GITHUB_APP_TOKEN_EXPIRES_AT", String(minted.expiresAtMs), false);
|
|
201252
201509
|
if (args.slug) {
|
|
201253
201510
|
const bot = await fetchAppBotIdentity(args.slug, { token: minted.token }, deps);
|
|
201254
|
-
|
|
201255
|
-
|
|
201256
|
-
await put3("GITHUB_APP_BOT_NAME", bot.login, false);
|
|
201257
|
-
}
|
|
201511
|
+
await put3("GITHUB_APP_BOT_EMAIL", bot?.email ?? "", false);
|
|
201512
|
+
await put3("GITHUB_APP_BOT_NAME", bot?.login ?? `${args.slug}[bot]`, false);
|
|
201258
201513
|
}
|
|
201259
201514
|
return {
|
|
201260
201515
|
account: args.slug ? `${args.slug}[bot]` : args.appId,
|
|
@@ -201922,7 +202177,7 @@ async function draftActorResolver(registry2) {
|
|
|
201922
202177
|
const byId = new Map(actors.map((a) => [a.id, a]));
|
|
201923
202178
|
return (id) => {
|
|
201924
202179
|
const a = byId.get(id);
|
|
201925
|
-
return a ? actorRefFieldsOf(a) :
|
|
202180
|
+
return a ? actorRefFieldsOf(a) : actorRefFallbackOf(id);
|
|
201926
202181
|
};
|
|
201927
202182
|
}
|
|
201928
202183
|
async function draftRoleStaffingResolver(registry2) {
|
|
@@ -202412,20 +202667,6 @@ async function runCommand(kernel, blobs, oplog, engineStore, actor, command, arg
|
|
|
202412
202667
|
const project = await ctx.projectState.getProject(projectId2);
|
|
202413
202668
|
if (!project) throw new Error(`project not found: ${projectId2}`);
|
|
202414
202669
|
await bindingState.upsertWorkspaceBinding({ workOrderId: workspace, projectId: projectId2 });
|
|
202415
|
-
} else if (createdViaArg === "seed" && ctx.projectState && bindingState) {
|
|
202416
|
-
await ensureWorkorderProject({
|
|
202417
|
-
workOrderId: workspace,
|
|
202418
|
-
...optStr(args, "title") ? { title: optStr(args, "title") } : {},
|
|
202419
|
-
// ADR 0207「对话中创建则为对话人」:优先记**陪聊解析出的那个真人**
|
|
202420
|
-
// (`ctx.workorderInitiator.humanActorId`,与 brief.owner 同源),
|
|
202421
|
-
// 没有会话上下文时退到调用者本人——但**只在他是人时**;
|
|
202422
|
-
// agent 替人跑 spawn 不该把自己写成项目创建人,那种情况落 NULL,
|
|
202423
|
-
// 由读侧从工单 brief.owner 派生(service.resolveProjectCreator)。
|
|
202424
|
-
...ctx.workorderInitiator?.humanActorId ? { createdBy: ctx.workorderInitiator.humanActorId } : !isAgent(actor) ? { createdBy: actor } : {},
|
|
202425
|
-
createProject: makeEnsureProjectFromStore(ctx.projectState),
|
|
202426
|
-
upsertBinding: (b2) => bindingState.upsertWorkspaceBinding(b2),
|
|
202427
|
-
onWarn: (m2) => console.warn(m2)
|
|
202428
|
-
});
|
|
202429
202670
|
}
|
|
202430
202671
|
if (schemaForSeed) {
|
|
202431
202672
|
const title = optStr(args, "title") ?? optStr(args, "description") ?? workspace;
|
|
@@ -203154,18 +203395,27 @@ async function runView(kernel, oplog, engineStore, blobs, name, artifactId, para
|
|
|
203154
203395
|
);
|
|
203155
203396
|
const pendingGate = pendingConcludeAttempt(model, id);
|
|
203156
203397
|
const rejectionByRevision = /* @__PURE__ */ new Map();
|
|
203398
|
+
const contentEditedByRevision = /* @__PURE__ */ new Map();
|
|
203157
203399
|
if (engineStore) {
|
|
203158
203400
|
const snap = await engineStore.transaction((tx) => tx.loadWorkorder(artifact.workspace));
|
|
203159
203401
|
for (const w2 of snap?.works ?? []) {
|
|
203160
203402
|
if (w2.rejectedReason) rejectionByRevision.set(w2.id, w2.rejectedReason);
|
|
203161
203403
|
}
|
|
203404
|
+
for (const a of snap?.artifacts ?? []) {
|
|
203405
|
+
const at = a.contentEditedAt;
|
|
203406
|
+
if (!at) continue;
|
|
203407
|
+
const workId = a.workId;
|
|
203408
|
+
const prev = contentEditedByRevision.get(workId);
|
|
203409
|
+
if (!prev || at > prev) contentEditedByRevision.set(workId, at);
|
|
203410
|
+
}
|
|
203162
203411
|
}
|
|
203163
203412
|
const revisionViews = revisions.map((rev) => ({
|
|
203164
203413
|
...rev,
|
|
203165
203414
|
stage: revisionStage(artifact, rev),
|
|
203166
203415
|
approvals: (model.reviews.get(rev.id) ?? []).filter((v2) => v2.verdict === "approve").length,
|
|
203167
203416
|
required: pendingGate?.head === rev.id ? pendingGate.gate?.quorum ?? 0 : 0,
|
|
203168
|
-
...rejectionByRevision.has(rev.id) ? { rejection: { reason: rejectionByRevision.get(rev.id) } } : {}
|
|
203417
|
+
...rejectionByRevision.has(rev.id) ? { rejection: { reason: rejectionByRevision.get(rev.id) } } : {},
|
|
203418
|
+
...contentEditedByRevision.has(rev.id) ? { contentEditedAt: contentEditedByRevision.get(rev.id) } : {}
|
|
203169
203419
|
}));
|
|
203170
203420
|
return {
|
|
203171
203421
|
artifact,
|
|
@@ -203908,6 +204158,18 @@ async function startOasisServer(opts) {
|
|
|
203908
204158
|
chatStore: store,
|
|
203909
204159
|
chatSessionId: sessionId,
|
|
203910
204160
|
session: bgSession,
|
|
204161
|
+
liveChat,
|
|
204162
|
+
liveHandle: {
|
|
204163
|
+
runtimeSessionId: session.id,
|
|
204164
|
+
...session.runId ? { runId: session.runId } : {},
|
|
204165
|
+
kill: () => {
|
|
204166
|
+
void session.kill?.();
|
|
204167
|
+
},
|
|
204168
|
+
...session.appendInput ? { appendInput: (input) => session.appendInput(input) } : {},
|
|
204169
|
+
get canAppendInput() {
|
|
204170
|
+
return typeof session.appendInput === "function" && session.canAppendInput !== false;
|
|
204171
|
+
}
|
|
204172
|
+
},
|
|
203911
204173
|
...itemStore ? { itemStore } : {},
|
|
203912
204174
|
// 广播/委派回流带上 held turn id(B3);授权卡入口在别处,那条路本轮不接账本。
|
|
203913
204175
|
...turnId ? { turnId } : {}
|
|
@@ -204466,7 +204728,15 @@ async function startOasisServer(opts) {
|
|
|
204466
204728
|
if (body2.command === "spawn" && wodrafts) {
|
|
204467
204729
|
const draftId = result?.data?.draftId;
|
|
204468
204730
|
const chatSid = req.headers["x-chat-session-id"];
|
|
204469
|
-
if (draftId && typeof chatSid === "string")
|
|
204731
|
+
if (draftId && typeof chatSid === "string") {
|
|
204732
|
+
const draftWorkspace = result?.data?.workspace;
|
|
204733
|
+
await wodrafts.tagSession(draftId, chatSid, liveChat.runFor(chatSid), draftWorkspace).catch((err) => {
|
|
204734
|
+
console.error(
|
|
204735
|
+
`[workorder-draft] \u8349\u6848 ${draftId}\uFF08ws=${String(draftWorkspace)}\uFF09\u672A\u80FD\u5173\u8054\u4F1A\u8BDD ${chatSid}\uFF1A`,
|
|
204736
|
+
err
|
|
204737
|
+
);
|
|
204738
|
+
});
|
|
204739
|
+
}
|
|
204470
204740
|
}
|
|
204471
204741
|
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify(result));
|
|
204472
204742
|
} catch (err) {
|
|
@@ -205473,6 +205743,13 @@ async function startOasisServer(opts) {
|
|
|
205473
205743
|
};
|
|
205474
205744
|
const wantsV3 = url.searchParams.get("protocol") === "v3";
|
|
205475
205745
|
if (wantsV3) {
|
|
205746
|
+
const watchSession = url.searchParams.get("watch") === "1";
|
|
205747
|
+
const fromEpoch = url.searchParams.has("epoch") ? Number(url.searchParams.get("epoch")) : void 0;
|
|
205748
|
+
const cursor = liveChat.cursorFor(sid);
|
|
205749
|
+
if (watchSession && fromEpoch !== void 0 && cursor && cursor.epoch !== fromEpoch) {
|
|
205750
|
+
waitForChatTurn(res, liveChat, sid, startStreamKeepalive, true);
|
|
205751
|
+
return;
|
|
205752
|
+
}
|
|
205476
205753
|
const sub2 = (frame) => {
|
|
205477
205754
|
if (res.destroyed) return;
|
|
205478
205755
|
res.write(`${JSON.stringify(frame)}
|
|
@@ -205484,6 +205761,11 @@ async function startOasisServer(opts) {
|
|
|
205484
205761
|
await reopenRecoveringTurn();
|
|
205485
205762
|
att2 = liveChat.attachLiveV3(sid, fromSeq, sub2);
|
|
205486
205763
|
}
|
|
205764
|
+
if (watchSession && (!att2 || att2.status !== "running" && att2.replay.length === 0)) {
|
|
205765
|
+
att2?.detach();
|
|
205766
|
+
waitForChatTurn(res, liveChat, sid, startStreamKeepalive);
|
|
205767
|
+
return;
|
|
205768
|
+
}
|
|
205487
205769
|
if (!att2) {
|
|
205488
205770
|
res.writeHead(204).end();
|
|
205489
205771
|
return;
|
|
@@ -205491,7 +205773,8 @@ async function startOasisServer(opts) {
|
|
|
205491
205773
|
res.writeHead(200, {
|
|
205492
205774
|
"content-type": "application/x-oasis-live-v3+ndjson; charset=utf-8",
|
|
205493
205775
|
"transfer-encoding": "chunked",
|
|
205494
|
-
"access-control-expose-headers": "X-Session-Id, X-Replay-Gap, X-Live-Epoch, X-Can-Append",
|
|
205776
|
+
"access-control-expose-headers": "X-Session-Id, X-Replay-Gap, X-Live-Epoch, X-Can-Append, X-Chat-Session-Watch",
|
|
205777
|
+
...watchSession ? { "x-chat-session-watch": "1" } : {},
|
|
205495
205778
|
"x-session-id": cs.runtimeSessionId ?? "",
|
|
205496
205779
|
"x-live-epoch": String(att2.epoch),
|
|
205497
205780
|
"x-can-append": liveChat.canAppend(sid) ? "1" : "0",
|
|
@@ -206250,10 +206533,26 @@ ${composed}`;
|
|
|
206250
206533
|
...chatExit?.reason ? { reason: chatExit.reason } : {},
|
|
206251
206534
|
...chatExit?.detail ? { detail: chatExit.detail } : {}
|
|
206252
206535
|
});
|
|
206253
|
-
itemLedger.recordError(errText2, {
|
|
206536
|
+
const errorOutcome = itemLedger.recordError(errText2, {
|
|
206254
206537
|
...chatExit?.reason ? { reason: chatExit.reason } : {},
|
|
206255
206538
|
...chatExit?.detail ? { detail: chatExit.detail } : {}
|
|
206256
206539
|
});
|
|
206540
|
+
if (live) {
|
|
206541
|
+
live.publishServerItem({
|
|
206542
|
+
itemId: errorOutcome.itemId,
|
|
206543
|
+
itemType: "control",
|
|
206544
|
+
startedVersion: errorOutcome.startedVersion,
|
|
206545
|
+
version: errorOutcome.version,
|
|
206546
|
+
ord: errorOutcome.ord,
|
|
206547
|
+
status: "failed",
|
|
206548
|
+
payload: {
|
|
206549
|
+
code: "error",
|
|
206550
|
+
note: errText2,
|
|
206551
|
+
...chatExit?.reason ? { reason: chatExit.reason } : {},
|
|
206552
|
+
...chatExit?.detail ? { detail: chatExit.detail } : {}
|
|
206553
|
+
}
|
|
206554
|
+
});
|
|
206555
|
+
}
|
|
206257
206556
|
}
|
|
206258
206557
|
finished = true;
|
|
206259
206558
|
clearInterval(ckptTimer);
|
|
@@ -206858,15 +207157,36 @@ ${composed}`;
|
|
|
206858
207157
|
const installationId = await svc.revealResolvedVariable("GITHUB_APP_INSTALLATION_ID", actorId);
|
|
206859
207158
|
const effective = installationId ? await fetchInstallationPermissions({ appId, privateKeyPem, installationId }) : meta.permissions;
|
|
206860
207159
|
const missing = missingAppPermissions(effective);
|
|
207160
|
+
const requestedAll = Object.entries(meta.permissions).filter(([, level]) => Boolean(level)).map(([key, level]) => ({ key, level: String(level) })).sort((a, b2) => a.key.localeCompare(b2.key));
|
|
207161
|
+
const grantedKeys = new Set(
|
|
207162
|
+
Object.entries(effective).filter(([, level]) => Boolean(level)).map(([k2]) => k2)
|
|
207163
|
+
);
|
|
206861
207164
|
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({
|
|
206862
207165
|
configured: true,
|
|
206863
207166
|
slug: meta.slug,
|
|
206864
207167
|
ownerLogin: meta.ownerLogin,
|
|
207168
|
+
/** App 申请的全集(英文原名 + 级别)。界面用它回答「我配了多少项」。 */
|
|
207169
|
+
requested: requestedAll,
|
|
206865
207170
|
/** true=读的是这次安装真正被授予的权限;false=只拿到 App 申请的,可能偏乐观。 */
|
|
206866
207171
|
fromInstallation: Boolean(installationId),
|
|
206867
|
-
/**
|
|
206868
|
-
|
|
206869
|
-
|
|
207172
|
+
/**
|
|
207173
|
+
* App 申请了、但这次安装还没被授予——多半是权限升级还等着组织 owner 批准。
|
|
207174
|
+
* **在全集上做差**,不再只看本仓必需的那八项(否则人另外勾的永远报不出来)。
|
|
207175
|
+
*/
|
|
207176
|
+
awaitingApproval: requestedAll.filter((r) => !grantedKeys.has(r.key)).map((r) => r.key),
|
|
207177
|
+
/**
|
|
207178
|
+
* 这次安装**真正被授予**的全部权限,一律用 GitHub 的英文原名 + 级别
|
|
207179
|
+
* (2026-09-10 发起人:「所有权限都用英文原文,不区分是不是八项权限」)。
|
|
207180
|
+
*
|
|
207181
|
+
* 此前这里是 `GITHUB_APP_PERMISSION_LABELS.filter(...)` —— 那张表只有本仓必需的
|
|
207182
|
+
* 八项中文标签,于是这一格实际上是「必备清单打勾」,人在 GitHub 上另外勾的一个都不显示,
|
|
207183
|
+
* 看上去像没生效。中间还试过「八项中文 + 额外项英文」两段式,也被否了:
|
|
207184
|
+
* 同一个列表两套命名,反而要人先分辨「这条为什么是中文那条为什么是英文」。
|
|
207185
|
+
*
|
|
207186
|
+
* **不编中文名**:GitHub 的权限键是开放集合且还在增,写死对照表迟早漏;
|
|
207187
|
+
* 而英文原名与 GitHub 设置页上的字**逐字一致**,对着改权限时不用做心理翻译。
|
|
207188
|
+
*/
|
|
207189
|
+
granted: Object.entries(effective).filter(([, level]) => Boolean(level)).map(([key, level]) => ({ key, level: String(level) })).sort((a, b2) => a.key.localeCompare(b2.key)),
|
|
206870
207190
|
missing,
|
|
206871
207191
|
settingsUrl: meta.slug ? appPermissionSettingsUrl(meta) : null
|
|
206872
207192
|
}));
|
|
@@ -206942,7 +207262,8 @@ ${composed}`;
|
|
|
206942
207262
|
const perms = pending.permissions ?? {};
|
|
206943
207263
|
const missing = missingAppPermissions(perms);
|
|
206944
207264
|
return {
|
|
206945
|
-
|
|
207265
|
+
// 与 app/permissions 那一处同口径:英文原名 + 级别,不分是不是本仓必需的八项。
|
|
207266
|
+
granted: Object.entries(perms).filter(([, level]) => Boolean(level)).map(([key, level]) => ({ key, level: String(level) })).sort((a, b2) => a.key.localeCompare(b2.key)),
|
|
206946
207267
|
missing,
|
|
206947
207268
|
...pending.slug ? { settingsUrl: appPermissionSettingsUrl({
|
|
206948
207269
|
slug: pending.slug,
|
|
@@ -206985,10 +207306,8 @@ ${composed}`;
|
|
|
206985
207306
|
}
|
|
206986
207307
|
if (pending.slug) {
|
|
206987
207308
|
const bot = await fetchAppBotIdentity(pending.slug, { token: minted.token });
|
|
206988
|
-
|
|
206989
|
-
|
|
206990
|
-
await put3("GITHUB_APP_BOT_NAME", bot.login, false);
|
|
206991
|
-
}
|
|
207309
|
+
await put3("GITHUB_APP_BOT_EMAIL", bot?.email ?? "", false);
|
|
207310
|
+
await put3("GITHUB_APP_BOT_NAME", bot?.login ?? `${pending.slug}[bot]`, false);
|
|
206992
207311
|
}
|
|
206993
207312
|
pending.done = true;
|
|
206994
207313
|
delete pending.privateKeyPem;
|
|
@@ -207172,7 +207491,9 @@ ${composed}`;
|
|
|
207172
207491
|
const svc = await connectorActorsService();
|
|
207173
207492
|
if (svc) {
|
|
207174
207493
|
const connId = "github";
|
|
207175
|
-
|
|
207494
|
+
if (!actorIdParam) {
|
|
207495
|
+
await svc.upsertConnector({ id: connId, name: "GitHub", mode: "oauth", status: "connected", account: "github" });
|
|
207496
|
+
}
|
|
207176
207497
|
for (const [k2, v2] of [["GIT_AUTHOR_NAME", actorNameParam], ["GIT_AUTHOR_EMAIL", ""], ["GIT_COMMITTER_NAME", actorNameParam], ["GIT_COMMITTER_EMAIL", ""], ["EMAIL", ""]])
|
|
207177
207498
|
await svc.putVariable({ key: k2, value: v2, scope: "personal", actorId: actorIdParam, connectorId: connId, encrypted: false });
|
|
207178
207499
|
await svc.putVariable({ key: "GITHUB_TOKEN", value: p2.access_token, scope: "personal", actorId: actorIdParam, connectorId: connId, encrypted: true });
|
|
@@ -207307,7 +207628,6 @@ var init_server3 = __esm({
|
|
|
207307
207628
|
init_src7();
|
|
207308
207629
|
init_collect();
|
|
207309
207630
|
init_remote_util();
|
|
207310
|
-
init_ephemeral_project();
|
|
207311
207631
|
init_timeline();
|
|
207312
207632
|
init_node_state();
|
|
207313
207633
|
init_src5();
|
|
@@ -207316,6 +207636,7 @@ var init_server3 = __esm({
|
|
|
207316
207636
|
init_router();
|
|
207317
207637
|
init_infra_error();
|
|
207318
207638
|
init_live_chat();
|
|
207639
|
+
init_chat_turn_wait();
|
|
207319
207640
|
init_append_now();
|
|
207320
207641
|
init_workorder_drafts();
|
|
207321
207642
|
init_draft_edit();
|
|
@@ -210381,6 +210702,32 @@ var init_projection = __esm({
|
|
|
210381
210702
|
}
|
|
210382
210703
|
});
|
|
210383
210704
|
|
|
210705
|
+
// ../server/src/domains/projects/uncategorized-project.ts
|
|
210706
|
+
function isUncategorizedProjectId(projectId2) {
|
|
210707
|
+
return projectId2 === UNCATEGORIZED_PROJECT_ID;
|
|
210708
|
+
}
|
|
210709
|
+
async function bindWorkorderProject(deps) {
|
|
210710
|
+
const { workOrderId, projectId: projectId2 } = deps;
|
|
210711
|
+
if (!projectId2) return null;
|
|
210712
|
+
try {
|
|
210713
|
+
await deps.upsertBinding({ workOrderId, projectId: projectId2 });
|
|
210714
|
+
return projectId2;
|
|
210715
|
+
} catch (e) {
|
|
210716
|
+
deps.onWarn?.(`[project] \u5DE5\u5355 ${workOrderId} \u7ED1\u5B9A\u9879\u76EE\u5931\u8D25\uFF08\u4E0D\u963B\u65AD\u5EFA\u5355\uFF09\uFF1A${String(e)}`);
|
|
210717
|
+
return null;
|
|
210718
|
+
}
|
|
210719
|
+
}
|
|
210720
|
+
var UNCATEGORIZED_PROJECT_ID, UNCATEGORIZED_PROJECT_NAME, UNCATEGORIZED_PROJECT_DESCRIPTION, UNCATEGORIZED_UNKNOWN_TIME;
|
|
210721
|
+
var init_uncategorized_project = __esm({
|
|
210722
|
+
"../server/src/domains/projects/uncategorized-project.ts"() {
|
|
210723
|
+
"use strict";
|
|
210724
|
+
UNCATEGORIZED_PROJECT_ID = "proj:uncategorized";
|
|
210725
|
+
UNCATEGORIZED_PROJECT_NAME = "\u65E0\u5206\u7C7B\u9879\u76EE";
|
|
210726
|
+
UNCATEGORIZED_PROJECT_DESCRIPTION = "\u6240\u6709\u672A\u9009\u62E9\u9879\u76EE\u7684\u5BF9\u8BDD\u76F8\u5173\u6587\u4EF6\u548C\u4EFB\u52A1\u81EA\u52A8\u5E76\u5165\u8FD9\u91CC\uFF0C\u7EC4\u7EC7\u5185\u6240\u6709\u4EBA\u53EF\u89C1\u3002";
|
|
210727
|
+
UNCATEGORIZED_UNKNOWN_TIME = "1970-01-01T00:00:00.000Z";
|
|
210728
|
+
}
|
|
210729
|
+
});
|
|
210730
|
+
|
|
210384
210731
|
// ../server/src/domains/projects/service.ts
|
|
210385
210732
|
function sanitizeCreatorActorId(raw) {
|
|
210386
210733
|
const id = raw?.trim();
|
|
@@ -210642,6 +210989,10 @@ function buildRevisionDag(revisions) {
|
|
|
210642
210989
|
}))
|
|
210643
210990
|
};
|
|
210644
210991
|
}
|
|
210992
|
+
function laterOf(a, b2) {
|
|
210993
|
+
if (!b2) return a;
|
|
210994
|
+
return b2 > a ? b2 : a;
|
|
210995
|
+
}
|
|
210645
210996
|
var DEFAULT_AGENT_ACTOR, MemoryProjectStateStore, MemoryArtifactStateStore, ProjectsService;
|
|
210646
210997
|
var init_service4 = __esm({
|
|
210647
210998
|
"../server/src/domains/projects/service.ts"() {
|
|
@@ -210649,7 +211000,7 @@ var init_service4 = __esm({
|
|
|
210649
211000
|
init_src();
|
|
210650
211001
|
init_src5();
|
|
210651
211002
|
init_projection();
|
|
210652
|
-
|
|
211003
|
+
init_uncategorized_project();
|
|
210653
211004
|
init_workorders();
|
|
210654
211005
|
init_node_state();
|
|
210655
211006
|
DEFAULT_AGENT_ACTOR = "actor:agent:system";
|
|
@@ -210785,6 +211136,9 @@ var init_service4 = __esm({
|
|
|
210785
211136
|
async listWorkspaceBindings() {
|
|
210786
211137
|
return [...this.workspaceBindings.values()].map((binding) => ({ ...binding }));
|
|
210787
211138
|
}
|
|
211139
|
+
async deleteWorkspaceBinding(workOrderId) {
|
|
211140
|
+
this.workspaceBindings.delete(workOrderId);
|
|
211141
|
+
}
|
|
210788
211142
|
async setWorkspaceDispatchHold(workOrderId, held) {
|
|
210789
211143
|
if (held) this.dispatchHeld.add(workOrderId);
|
|
210790
211144
|
else this.dispatchHeld.delete(workOrderId);
|
|
@@ -210911,15 +211265,8 @@ var init_service4 = __esm({
|
|
|
210911
211265
|
return { project, artifacts, files };
|
|
210912
211266
|
}
|
|
210913
211267
|
async collectProjectFiles(projectId2) {
|
|
210914
|
-
if (
|
|
210915
|
-
|
|
210916
|
-
const ephemeralIds = stored.filter((p2) => isEphemeralProject(p2.id)).map((p2) => p2.id);
|
|
210917
|
-
const out = [];
|
|
210918
|
-
for (const id of ephemeralIds) {
|
|
210919
|
-
const files = await this.projects.listProjectFiles(id);
|
|
210920
|
-
out.push(...files.map((f2) => ({ ...f2, projectId: UNCATEGORIZED_PROJECT_ID })));
|
|
210921
|
-
}
|
|
210922
|
-
return out.sort((a, b2) => b2.uploadedAt.localeCompare(a.uploadedAt));
|
|
211268
|
+
if (isUncategorizedProjectId(projectId2)) return [];
|
|
211269
|
+
return this.projects.listProjectFiles(projectId2);
|
|
210923
211270
|
}
|
|
210924
211271
|
/**
|
|
210925
211272
|
* B2 投影同步:写命令落 oplog 后,从 oplog 重放重建 `this.artifacts` 文档层投影。
|
|
@@ -210976,23 +211323,18 @@ var init_service4 = __esm({
|
|
|
210976
211323
|
return project ? this.withProjectDerivedState(project) : null;
|
|
210977
211324
|
}
|
|
210978
211325
|
/**
|
|
210979
|
-
*
|
|
211326
|
+
* 「无分类项目」虚拟卡:把所有**没有项目绑定**的工单聚合成一张固定项目,供列表/详情/关联任务/相关文件消费。
|
|
210980
211327
|
* 存储里没有真行——这就是本卡与真项目最大的区别:
|
|
210981
211328
|
* - 写侧(成员、内容位置、上传文件)一律拒收(写到真项目 or 走真工单流程);
|
|
210982
|
-
* - 派生态(artifactSummary / workOrderCount
|
|
211329
|
+
* - 派生态(artifactSummary / workOrderCount)按「无绑定工单」算并集;
|
|
210983
211330
|
* - 帧上的「项目负责人:系统」由前端自选文案渲染,本层只给一个稳定的空 members 名单。
|
|
211331
|
+
*
|
|
211332
|
+
* ⚠ `workOrderCount` **走 listProjectWorkorders 同一条路**,不另写一份 bindings 过滤——
|
|
211333
|
+
* 两处各算各的正是「卡片计数与点进去的列表条数对不上」那类缺陷的来路。
|
|
210984
211334
|
*/
|
|
210985
211335
|
async buildUncategorizedProject() {
|
|
210986
|
-
const
|
|
210987
|
-
const
|
|
210988
|
-
stored.filter((project) => isEphemeralProject(project.id)).map((project) => project.id)
|
|
210989
|
-
);
|
|
210990
|
-
const bindings = await this.artifacts.listWorkspaceBindings();
|
|
210991
|
-
const workOrderCount = bindings.filter((b2) => ephemeralIds.has(b2.projectId)).length;
|
|
210992
|
-
const artifactList = await this.aggregateUncategorizedArtifacts(ephemeralIds, bindings);
|
|
210993
|
-
const ephemeralProjects = stored.filter((project) => ephemeralIds.has(project.id));
|
|
210994
|
-
const createdAts = ephemeralProjects.map((p2) => p2.createdAt).sort();
|
|
210995
|
-
const updatedAts = ephemeralProjects.map((p2) => p2.updatedAt).sort();
|
|
211336
|
+
const workorders = await this.listProjectWorkorders(UNCATEGORIZED_PROJECT_ID);
|
|
211337
|
+
const artifactList = await this.aggregateUncategorizedArtifacts();
|
|
210996
211338
|
return {
|
|
210997
211339
|
id: UNCATEGORIZED_PROJECT_ID,
|
|
210998
211340
|
name: UNCATEGORIZED_PROJECT_NAME,
|
|
@@ -211000,46 +211342,33 @@ var init_service4 = __esm({
|
|
|
211000
211342
|
memberCount: 0,
|
|
211001
211343
|
members: [],
|
|
211002
211344
|
artifactSummary: artifactList.summary,
|
|
211003
|
-
workOrderCount,
|
|
211345
|
+
workOrderCount: workorders.length,
|
|
211004
211346
|
// ADR 0205:本卡的「项目负责人:系统」是**前端按帧自选的文案**,后端不出 `"system"` 哨兵——
|
|
211005
211347
|
// 出 null 保持「取不到就 null」的域内红线,也不把一个不存在的 actor id 塞进契约。
|
|
211006
211348
|
createdBy: null,
|
|
211007
|
-
createdAt:
|
|
211008
|
-
updatedAt:
|
|
211349
|
+
createdAt: UNCATEGORIZED_UNKNOWN_TIME,
|
|
211350
|
+
updatedAt: UNCATEGORIZED_UNKNOWN_TIME
|
|
211009
211351
|
};
|
|
211010
211352
|
}
|
|
211011
|
-
async aggregateUncategorizedArtifacts(
|
|
211012
|
-
const
|
|
211013
|
-
const artifacts = [];
|
|
211014
|
-
for (const projectId2 of ephemeralIds) {
|
|
211015
|
-
const list2 = await this.listAllProjectArtifacts(projectId2);
|
|
211016
|
-
artifacts.push(...list2.map((a) => ({ ...a, projectId: UNCATEGORIZED_PROJECT_ID })));
|
|
211017
|
-
}
|
|
211353
|
+
async aggregateUncategorizedArtifacts() {
|
|
211354
|
+
const artifacts = await this.listAllProjectArtifacts(UNCATEGORIZED_PROJECT_ID);
|
|
211018
211355
|
artifacts.sort((a, b2) => b2.updatedAt.localeCompare(a.updatedAt));
|
|
211019
211356
|
return { artifacts, summary: summarizeArtifacts(artifacts) };
|
|
211020
211357
|
}
|
|
211021
211358
|
/**
|
|
211022
211359
|
* 项目列表(带派生态)。
|
|
211023
211360
|
*
|
|
211024
|
-
*
|
|
211025
|
-
*
|
|
211026
|
-
*
|
|
211027
|
-
*
|
|
211028
|
-
*
|
|
211029
|
-
* 期间别的请求从 pg 连接池取连接排不上号、撞 `connectionTimeoutMillis`(5s),
|
|
211030
|
-
* 前端看到的是「项目列表加载失败:timeout exceeded when trying to connect」——像数据库挂了,
|
|
211031
|
-
* 其实是被自己堵死的(实测并发时 /api/automations 从 0.24s 被拖到 7.1s,自动化页面因此空白)。
|
|
211032
|
-
*
|
|
211033
|
-
* 只对**真要返回的那些**算派生态。默认口径与路由一致(不含临时项目),排查用 `includeEphemeral`。
|
|
211361
|
+
* 库里的每一行都是**人建的真项目**,全部返回——临时项目(`proj_tmp_*`)退役后,这里不再有
|
|
211362
|
+
* "算完就扔"的过滤:旧版 dev 环境 294 个项目里 272 个是一单一个的临时项目,
|
|
211363
|
+
* `withProjectDerivedState` 对它们逐个跑 `buildWorkorderSummaries`(全量工单)+ 遍历全部 artifact,
|
|
211364
|
+
* 93% 的计算白算,还把单线程事件循环整块堵住 3~11 秒(并发时 /api/automations 从 0.24s 被拖到 7.1s,
|
|
211365
|
+
* 前端看到的是「项目列表加载失败:timeout」——像数据库挂了,其实是被自己堵死的)。
|
|
211034
211366
|
*/
|
|
211035
211367
|
async listProjects(opts) {
|
|
211036
211368
|
const stored = await this.projects.listProjects();
|
|
211037
|
-
const wanted = opts?.includeEphemeral === true ? stored : stored.filter((project) => !isEphemeralProject(project.id));
|
|
211038
211369
|
const creatorCache = /* @__PURE__ */ new Map();
|
|
211039
|
-
const derived = await Promise.all(
|
|
211370
|
+
const derived = await Promise.all(stored.map((project) => this.withProjectDerivedState(project, creatorCache)));
|
|
211040
211371
|
if (opts?.includeUncategorized === false) return derived;
|
|
211041
|
-
const wantUncategorized = opts?.includeUncategorized ?? !(opts?.includeEphemeral === true);
|
|
211042
|
-
if (!wantUncategorized) return derived;
|
|
211043
211372
|
const virtual = await this.buildUncategorizedProject();
|
|
211044
211373
|
return [virtual, ...derived];
|
|
211045
211374
|
}
|
|
@@ -211118,10 +211447,10 @@ var init_service4 = __esm({
|
|
|
211118
211447
|
* 项目创建人(ADR 0205)——**读时解析**,三条来路按优先级:
|
|
211119
211448
|
*
|
|
211120
211449
|
* 1. 库里落了 `created_by` → 按它解析 name/avatar;
|
|
211121
|
-
* 2.
|
|
211122
|
-
*
|
|
211123
|
-
*
|
|
211124
|
-
*
|
|
211450
|
+
* 2. 没落 → `null`(老的手建项目),前端诚实显示「—」。
|
|
211451
|
+
*
|
|
211452
|
+
* 旧版还有一档「临时项目从它绑定的那张工单 `brief.owner` 派生」——临时项目退役后没有这一档了
|
|
211453
|
+
* (见 uncategorized-project.ts 文件头)。
|
|
211125
211454
|
*
|
|
211126
211455
|
* **绝不拿别的字段顶替**(`members[0]`、项目名、updatedAt 都不行)——未指定成员时全组织都是成员,
|
|
211127
211456
|
* 按 actorId 排序等于随机点一个 agent 当负责人。这是本域红线,见 resolveActorName 的注释。
|
|
@@ -211129,9 +211458,7 @@ var init_service4 = __esm({
|
|
|
211129
211458
|
async resolveProjectCreator(project, workorders, cache) {
|
|
211130
211459
|
const stored = project.createdBy?.id;
|
|
211131
211460
|
if (stored) return this.resolveActorRef(stored, cache);
|
|
211132
|
-
|
|
211133
|
-
const ownerId = workorders.map((wo) => wo.owner?.id).find((id) => typeof id === "string" && id.startsWith("actor:"));
|
|
211134
|
-
return ownerId ? this.resolveActorRef(ownerId, cache) : null;
|
|
211461
|
+
return null;
|
|
211135
211462
|
}
|
|
211136
211463
|
/**
|
|
211137
211464
|
* actorId → {@link ProjectActorRef}:`kind` 按 id 前缀派生,`name` / `avatar` 走本公司名册现取。
|
|
@@ -211194,7 +211521,8 @@ var init_service4 = __esm({
|
|
|
211194
211521
|
*/
|
|
211195
211522
|
async reassignWorkorder(workOrderId, projectId2) {
|
|
211196
211523
|
if (isUncategorizedProjectId(projectId2)) {
|
|
211197
|
-
|
|
211524
|
+
await this.unlinkWorkorder(workOrderId);
|
|
211525
|
+
return { workOrderId, projectId: null };
|
|
211198
211526
|
}
|
|
211199
211527
|
const project = await this.projects.getProject(projectId2);
|
|
211200
211528
|
if (!project) throw new Error(`project not found: ${projectId2}`);
|
|
@@ -211203,77 +211531,46 @@ var init_service4 = __esm({
|
|
|
211203
211531
|
return binding;
|
|
211204
211532
|
}
|
|
211205
211533
|
/**
|
|
211206
|
-
*
|
|
211534
|
+
* 「取消任务关联」——**删掉工单的项目绑定行**,工单从此没有项目(读侧落进「无分类项目」桶)。
|
|
211207
211535
|
*
|
|
211208
|
-
*
|
|
211209
|
-
*
|
|
211210
|
-
*
|
|
211536
|
+
* 2026-09-10 之前这里是"回落到工单专属的临时项目 `proj_tmp_<slug>`",理由是「契约里每个工单
|
|
211537
|
+
* 都该属于某项目」。那套兜底已退役(见 uncategorized-project.ts 文件头):兜底容器让每张未挂靠
|
|
211538
|
+
* 工单变成一个只有它自己的假项目,代码仓/项目变量/记忆作用域全挂在一次性 id 上。
|
|
211211
211539
|
*
|
|
211212
|
-
*
|
|
211213
|
-
* 不需要预先存在(`ensureWorkorderProject` 后台建,也可能是本工单第一次被"解绑"——按需建)。
|
|
211540
|
+
* 幂等:没有绑定行时静默返回(store 的 delete 本身幂等)。
|
|
211214
211541
|
*/
|
|
211215
211542
|
async unlinkWorkorder(workOrderId) {
|
|
211216
211543
|
if (!workOrderId.trim()) throw new Error("work_order_id is required");
|
|
211217
|
-
|
|
211218
|
-
const existing = await this.artifacts.getWorkspaceBinding(workOrderId);
|
|
211219
|
-
if (!await this.projects.getProject(ephemeralId)) {
|
|
211220
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
211221
|
-
await this.projects.upsertProject({
|
|
211222
|
-
id: ephemeralId,
|
|
211223
|
-
name: `\u4E34\u65F6\u9879\u76EE \xB7 ${workOrderId}`,
|
|
211224
|
-
description: `\u5DE5\u5355 ${workOrderId} \u4ECE\u5176\u5B83\u9879\u76EE\u53D6\u6D88\u5173\u8054\u540E\u843D\u56DE\u7684\u4E34\u65F6\u9879\u76EE\u3002\u53EF\u7528 PATCH /api/work-orders/${workOrderId}/project \u6539\u6302\u5230\u6B63\u5F0F\u9879\u76EE\u3002`,
|
|
211225
|
-
createdAt: now,
|
|
211226
|
-
updatedAt: now
|
|
211227
|
-
});
|
|
211228
|
-
}
|
|
211229
|
-
if (existing?.projectId === ephemeralId) return existing;
|
|
211230
|
-
const binding = { workOrderId, projectId: ephemeralId };
|
|
211231
|
-
await this.artifacts.upsertWorkspaceBinding(binding);
|
|
211232
|
-
return binding;
|
|
211544
|
+
await this.artifacts.deleteWorkspaceBinding(workOrderId);
|
|
211233
211545
|
}
|
|
211234
211546
|
/**
|
|
211235
211547
|
* 删除项目(帧 `444:1792` 三点菜单第二项,口径由发起人 2026-09-09 拍板)。
|
|
211236
211548
|
*
|
|
211237
211549
|
* **只删项目这个壳**——项目主记录 + 成员名单 + 上传文件记录 + 内容位置。任务和会话一条都不删:
|
|
211238
211550
|
*
|
|
211239
|
-
* 1. 绑到本项目的工单**不管在跑还是没在跑**,一律 `unlinkWorkorder`
|
|
211240
|
-
*
|
|
211241
|
-
*
|
|
211242
|
-
*
|
|
211243
|
-
*
|
|
211244
|
-
*
|
|
211245
|
-
* 下一个节点就取不到代码仓——正是 `ephemeral-project.ts` 顶部那条五环事故链。
|
|
211551
|
+
* 1. 绑到本项目的工单**不管在跑还是没在跑**,一律 `unlinkWorkorder` 删掉绑定行 → 落进读侧的
|
|
211552
|
+
* 「无分类项目」。发起人明确不要「有任务在跑就不让删」这道闸,所以这里没有任何在跑判断。
|
|
211553
|
+
* 2. **代码仓不跟着搬**(发起人 2026-09-10 裁定)。`git/resolve.ts` 的 `resolveCodeRepo` 是
|
|
211554
|
+
* 派发时沿 `绑定 → 项目 → 内容位置` 实时解的,所以删完项目,这些任务下一个节点就取不到代码仓——
|
|
211555
|
+
* 这是**期望行为**:由执行中的数字员工发现并引导人给任务指定新项目,而不是由系统兜一个假容器
|
|
211556
|
+
* (旧版把 git 位置逐单复制到 `proj_tmp_*` 上,正是那套已退役的兜底)。
|
|
211246
211557
|
* 3. 会话的 `project_id` 置空、项目作用域的变量/密钥清理,走 {@link OnProjectDeleted} 钩子
|
|
211247
211558
|
* (本 service 对会话域与凭据域零耦合,同 `onProjectNameChanged` 的注入模式)。
|
|
211248
211559
|
*
|
|
211249
|
-
*
|
|
211250
|
-
* (工单的兜底归属,删掉等于把工单变成无绑定态——契约里 `Workspace.projectId` 必填)。
|
|
211560
|
+
* 拒绝 `proj:uncategorized`(读侧虚拟卡,存储里没有真行,删它没有意义)。
|
|
211251
211561
|
*/
|
|
211252
211562
|
async deleteProject(id) {
|
|
211253
211563
|
const projectId2 = id?.trim();
|
|
211254
211564
|
if (!projectId2) throw new Error("project id is required");
|
|
211255
211565
|
if (isUncategorizedProjectId(projectId2)) throw new Error(`project not deletable: ${projectId2}`);
|
|
211256
|
-
if (isEphemeralProject(projectId2)) throw new Error(`project not deletable: ${projectId2}`);
|
|
211257
211566
|
const project = await this.projects.getProject(projectId2);
|
|
211258
211567
|
if (!project) throw new Error(`project not found: ${projectId2}`);
|
|
211259
|
-
const gitLocations = (await this.projects.listContentLocations(projectId2)).filter((loc) => loc.kind === "external-home" && loc.externalHome?.system === "git");
|
|
211260
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
211261
211568
|
const bindings = await this.artifacts.listWorkspaceBindings();
|
|
211262
211569
|
const movedWorkOrderIds = [];
|
|
211263
211570
|
for (const binding of bindings) {
|
|
211264
211571
|
if (binding.projectId !== projectId2) continue;
|
|
211265
|
-
|
|
211572
|
+
await this.unlinkWorkorder(binding.workOrderId);
|
|
211266
211573
|
movedWorkOrderIds.push(binding.workOrderId);
|
|
211267
|
-
for (const loc of gitLocations) {
|
|
211268
|
-
await this.projects.addContentLocation({
|
|
211269
|
-
...loc,
|
|
211270
|
-
projectId: moved.projectId,
|
|
211271
|
-
// 派生 id:同一条位置搬到同一个临时项目永远算出同一个 id(addContentLocation 是
|
|
211272
|
-
// upsert),重跑不会堆出重复的仓库行。
|
|
211273
|
-
locationId: `pcl-${moved.projectId}-carried-${loc.locationId}`,
|
|
211274
|
-
updatedAt: now
|
|
211275
|
-
});
|
|
211276
|
-
}
|
|
211277
211574
|
}
|
|
211278
211575
|
await this.projects.deleteProject(projectId2);
|
|
211279
211576
|
if (this.onProjectDeleted) {
|
|
@@ -211283,7 +211580,7 @@ var init_service4 = __esm({
|
|
|
211283
211580
|
console.warn(`[projects] project-deleted follow-up failed for ${projectId2}: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
211284
211581
|
}
|
|
211285
211582
|
}
|
|
211286
|
-
return { projectId: projectId2, movedWorkOrderIds
|
|
211583
|
+
return { projectId: projectId2, movedWorkOrderIds };
|
|
211287
211584
|
}
|
|
211288
211585
|
/**
|
|
211289
211586
|
* 项目关联工单(帧 `1355:4918`):把内部私有的 `listProjectWorkorders` 暴露给路由层。
|
|
@@ -211498,7 +211795,11 @@ var init_service4 = __esm({
|
|
|
211498
211795
|
if (arts.length === 0) continue;
|
|
211499
211796
|
const work = worksById.get(acceptedId);
|
|
211500
211797
|
const folderPath = `wo:${workOrderId}/node:${node2.id}`;
|
|
211501
|
-
const
|
|
211798
|
+
const workUpdatedAt = work?.acceptedAt ?? work?.endedAt ?? work?.lastActivityAt ?? work?.createdAt ?? node2.updatedAt;
|
|
211799
|
+
const folderUpdatedAt = arts.reduce(
|
|
211800
|
+
(acc, a) => laterOf(acc, a.contentEditedAt),
|
|
211801
|
+
workUpdatedAt
|
|
211802
|
+
);
|
|
211502
211803
|
const creatorId = work?.assigneeActorId ?? null;
|
|
211503
211804
|
const creatorKind = creatorId ? creatorId.startsWith("actor:human:") ? "human" : "agent" : null;
|
|
211504
211805
|
const creatorName = creatorId ? this.resolveActorName(creatorId) : null;
|
|
@@ -211522,7 +211823,7 @@ var init_service4 = __esm({
|
|
|
211522
211823
|
for (const a of sortedArts) {
|
|
211523
211824
|
const artifactId = node2.id;
|
|
211524
211825
|
const artifactType = a.type ?? node2.type;
|
|
211525
|
-
const rowUpdatedAt =
|
|
211826
|
+
const rowUpdatedAt = laterOf(workUpdatedAt, a.contentEditedAt);
|
|
211526
211827
|
if (a.contentKind === "manifest" && a.contentRef?.trim()) {
|
|
211527
211828
|
const content3 = await this.readContentRefText(a.contentRef);
|
|
211528
211829
|
const files = content3 !== null ? tryParseManifestFiles(content3) : void 0;
|
|
@@ -211909,7 +212210,7 @@ var init_service4 = __esm({
|
|
|
211909
212210
|
if (!snap) return [];
|
|
211910
212211
|
const worksById = new Map(snap.works.map((w2) => [w2.id, w2]));
|
|
211911
212212
|
const binding = this.artifacts.getWorkspaceBinding ? await this.artifacts.getWorkspaceBinding(workOrderId).catch(() => null) : null;
|
|
211912
|
-
const projectId2 = binding?.projectId ??
|
|
212213
|
+
const projectId2 = binding?.projectId ?? UNCATEGORIZED_PROJECT_ID;
|
|
211913
212214
|
const out = [];
|
|
211914
212215
|
for (const node2 of snap.nodes) {
|
|
211915
212216
|
if (!node2.latestAcceptId) continue;
|
|
@@ -211918,6 +212219,7 @@ var init_service4 = __esm({
|
|
|
211918
212219
|
const arts = snap.artifacts.filter((a) => a.workId === acceptedId);
|
|
211919
212220
|
if (arts.length === 0) continue;
|
|
211920
212221
|
const work = worksById.get(acceptedId);
|
|
212222
|
+
const workUpdatedAt = work?.acceptedAt ?? work?.endedAt ?? work?.lastActivityAt ?? work?.createdAt ?? node2.updatedAt;
|
|
211921
212223
|
const documents = arts.slice().sort((a, b2) => a.ordinal - b2.ordinal).map((a) => ({
|
|
211922
212224
|
// collab 层 artifactId === engine nodeId(见 bridge-readmodel.ts:215)——预览端读的
|
|
211923
212225
|
// useArtifactView 也走这个 id。
|
|
@@ -211929,7 +212231,9 @@ var init_service4 = __esm({
|
|
|
211929
212231
|
contentRef: a.contentRef,
|
|
211930
212232
|
title: node2.title || a.name,
|
|
211931
212233
|
// 节点标题优先;否则用 engine artifact 的 "output" 兜底
|
|
211932
|
-
contentKind: engineContentKind(a.contentKind)
|
|
212234
|
+
contentKind: engineContentKind(a.contentKind),
|
|
212235
|
+
// 文档级时间:被人在预览里就地改写过就跳到改写时刻,否则就是这轮执行的时刻。
|
|
212236
|
+
updatedAt: laterOf(workUpdatedAt, a.contentEditedAt)
|
|
211933
212237
|
}));
|
|
211934
212238
|
out.push({
|
|
211935
212239
|
outputId: `engine:${acceptedId}`,
|
|
@@ -211940,7 +212244,9 @@ var init_service4 = __esm({
|
|
|
211940
212244
|
documents,
|
|
211941
212245
|
payload: {},
|
|
211942
212246
|
createdAt: work?.createdAt ?? node2.createdAt,
|
|
211943
|
-
|
|
212247
|
+
// 节点(= 文件夹行)的时间取「这轮执行的时刻」与「其下任一文档最后被就地改写的时刻」里更晚的那个:
|
|
212248
|
+
// 改过某个文件之后,文件夹不能还显示比自己的孩子更早的时间。
|
|
212249
|
+
updatedAt: documents.reduce((acc, d) => laterOf(acc, d.updatedAt), workUpdatedAt)
|
|
211944
212250
|
});
|
|
211945
212251
|
}
|
|
211946
212252
|
return out;
|
|
@@ -212034,20 +212340,8 @@ var init_service4 = __esm({
|
|
|
212034
212340
|
const filtered = artifacts.filter((artifact) => input.type === void 0 || artifact.type === input.type).filter((artifact) => input.status === void 0 || artifact.status === input.status).sort((a, b2) => b2.updatedAt.localeCompare(a.updatedAt));
|
|
212035
212341
|
return { artifacts: filtered, summary: summarizeArtifacts(filtered) };
|
|
212036
212342
|
}
|
|
212037
|
-
/**
|
|
212343
|
+
/** 内部:虚拟卡与真项目走同一条路——{@link listAllProjectArtifacts} 内部按「无绑定 / 绑到本项目」分流。 */
|
|
212038
212344
|
async collectProjectArtifacts(projectId2) {
|
|
212039
|
-
if (isUncategorizedProjectId(projectId2)) {
|
|
212040
|
-
const stored = await this.projects.listProjects();
|
|
212041
|
-
const ephemeralIds = new Set(
|
|
212042
|
-
stored.filter((project) => isEphemeralProject(project.id)).map((project) => project.id)
|
|
212043
|
-
);
|
|
212044
|
-
const artifacts = [];
|
|
212045
|
-
for (const id of ephemeralIds) {
|
|
212046
|
-
const list2 = await this.listAllProjectArtifacts(id);
|
|
212047
|
-
artifacts.push(...list2.map((a) => ({ ...a, projectId: UNCATEGORIZED_PROJECT_ID })));
|
|
212048
|
-
}
|
|
212049
|
-
return artifacts;
|
|
212050
|
-
}
|
|
212051
212345
|
return this.listAllProjectArtifacts(projectId2);
|
|
212052
212346
|
}
|
|
212053
212347
|
async listProjectWorkorders(projectId2) {
|
|
@@ -212056,17 +212350,19 @@ var init_service4 = __esm({
|
|
|
212056
212350
|
const uncategorized = isUncategorizedProjectId(projectId2);
|
|
212057
212351
|
return buildWorkorderSummaries(this.kernel.model, void 0, (workOrderId) => projectByWorkorder.get(workOrderId) ?? null, void 0, void 0, void 0, (role) => this.kernel.actorForRole(role)).filter((workorder) => {
|
|
212058
212352
|
const bound = workorder.projectId;
|
|
212059
|
-
if (uncategorized) return
|
|
212353
|
+
if (uncategorized) return bound == null;
|
|
212060
212354
|
return bound === projectId2;
|
|
212061
212355
|
});
|
|
212062
212356
|
}
|
|
212063
212357
|
async listAllProjectArtifacts(projectId2) {
|
|
212064
|
-
const
|
|
212358
|
+
const uncategorized = isUncategorizedProjectId(projectId2);
|
|
212359
|
+
const stored = uncategorized ? [] : await this.artifacts.listArtifacts({ projectId: projectId2 });
|
|
212065
212360
|
const byArtifact = new Map(stored.map((artifact) => [artifact.artifactId, artifact]));
|
|
212066
212361
|
const bindings = await this.artifacts.listWorkspaceBindings();
|
|
212067
212362
|
const projectByWorkorder = new Map(bindings.map((binding) => [binding.workOrderId, binding.projectId]));
|
|
212068
212363
|
for (const artifact of this.kernel.model.artifacts.values()) {
|
|
212069
|
-
|
|
212364
|
+
const bound = projectByWorkorder.get(artifact.workspace);
|
|
212365
|
+
if (uncategorized ? bound !== void 0 : bound !== projectId2) continue;
|
|
212070
212366
|
const descriptor = decodeDocumentDescription(artifact.description);
|
|
212071
212367
|
const info = nodeInfo(this.kernel.model, artifact);
|
|
212072
212368
|
const revisions = revisionsOf(this.kernel.model, artifact.id);
|
|
@@ -216731,8 +217027,18 @@ function wireRecoveredChatTurn(deps) {
|
|
|
216731
217027
|
clearInterval(ckptTimer);
|
|
216732
217028
|
const failed = exitFailed(info);
|
|
216733
217029
|
if (failed) {
|
|
216734
|
-
|
|
216735
|
-
|
|
217030
|
+
const errText2 = exitErrorText(info);
|
|
217031
|
+
live.emit({ type: "error", text: errText2 });
|
|
217032
|
+
const errorOutcome = itemLedger.recordError(errText2, { ...info.reason ? { reason: info.reason } : {} });
|
|
217033
|
+
live.publishServerItem({
|
|
217034
|
+
itemId: errorOutcome.itemId,
|
|
217035
|
+
itemType: "control",
|
|
217036
|
+
startedVersion: errorOutcome.startedVersion,
|
|
217037
|
+
version: errorOutcome.version,
|
|
217038
|
+
ord: errorOutcome.ord,
|
|
217039
|
+
status: "failed",
|
|
217040
|
+
payload: { code: "error", note: errText2, ...info.reason ? { reason: info.reason } : {} }
|
|
217041
|
+
});
|
|
216736
217042
|
}
|
|
216737
217043
|
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
216738
217044
|
const parts = collectedParts();
|
|
@@ -219630,13 +219936,6 @@ var init_memory = __esm({
|
|
|
219630
219936
|
async write(ctx, input) {
|
|
219631
219937
|
this.assertWritable(input, "agent");
|
|
219632
219938
|
const projectId2 = await this.projectOf(ctx);
|
|
219633
|
-
if (input.scope === "project" && projectId2 === null) {
|
|
219634
|
-
throw new ApiError(
|
|
219635
|
-
400,
|
|
219636
|
-
"MEMORY_NO_PROJECT",
|
|
219637
|
-
"\u672C\u8F6E\u89E3\u6790\u4E0D\u51FA\u6240\u5C5E\u9879\u76EE\uFF08\u975E\u6D3E\u53D1\u4F1A\u8BDD\u6216\u4EA7\u7269\u672A\u7ED1\u5B9A\u5DE5\u5355\uFF09\uFF0C\u65E0\u6CD5\u5199\u9879\u76EE\u7EA7\u8BB0\u5FC6\u2014\u2014\u6539\u7528 --scope actor \u5199\u8DE8\u9879\u76EE\u901A\u7528\u7684\u7ECF\u9A8C\u3002"
|
|
219638
|
-
);
|
|
219639
|
-
}
|
|
219640
219939
|
return this.writeResolved(ctx, input, input.scope === "project" ? projectId2 : null, "agent");
|
|
219641
219940
|
}
|
|
219642
219941
|
/** 控制台管理面写入:actorId/projectId 由受保护的管理路由明确给出,不复用 agent 自用入口。 */
|
|
@@ -220746,6 +221045,21 @@ var init_connector_skills = __esm({
|
|
|
220746
221045
|
}
|
|
220747
221046
|
});
|
|
220748
221047
|
|
|
221048
|
+
// ../server/src/domains/nodes/runtime-tenancy.ts
|
|
221049
|
+
function foreignNodeBlock(node2, expectedCompanyId) {
|
|
221050
|
+
if (!expectedCompanyId) return null;
|
|
221051
|
+
if (!node2) return null;
|
|
221052
|
+
const owner = node2.companyId;
|
|
221053
|
+
if (!owner) return null;
|
|
221054
|
+
if (owner === expectedCompanyId) return null;
|
|
221055
|
+
return `\u8FD0\u884C\u65F6\u8282\u70B9 ${node2.id} \u5C5E\u4E8E\u516C\u53F8 ${owner}\uFF0C\u4E0D\u5C5E\u4E8E ${expectedCompanyId}\u2014\u2014\u5458\u5DE5\u53EA\u80FD\u7ED1\u5B9A/\u6D3E\u53D1\u5230\u672C\u516C\u53F8\u7684\u8282\u70B9\uFF08ADR-0164 \xA75\uFF1A\u8DE8\u516C\u53F8\u5171\u4EAB\u540C\u4E00\u53F0\u7269\u7406\u673A\u7684\u6B63\u89E3\u662F\u5404\u81EA\u767B\u8BB0\u4E0D\u540C node id\uFF09\u3002`;
|
|
221056
|
+
}
|
|
221057
|
+
var init_runtime_tenancy = __esm({
|
|
221058
|
+
"../server/src/domains/nodes/runtime-tenancy.ts"() {
|
|
221059
|
+
"use strict";
|
|
221060
|
+
}
|
|
221061
|
+
});
|
|
221062
|
+
|
|
220749
221063
|
// ../server/src/domains/actors/routes.ts
|
|
220750
221064
|
async function asApiError(run) {
|
|
220751
221065
|
try {
|
|
@@ -221068,6 +221382,9 @@ function actorsDomain(opts) {
|
|
|
221068
221382
|
const { service } = await resolveCtx(req.auth.companyId);
|
|
221069
221383
|
const b2 = req.body;
|
|
221070
221384
|
if (!b2?.actorId || !b2.nodeId || !b2.runtimeKind) throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11 actorId / nodeId / runtimeKind");
|
|
221385
|
+
const targetNode = await opts.resolveNodeTenancy?.(b2.nodeId).catch(() => null) ?? null;
|
|
221386
|
+
const foreign = foreignNodeBlock(targetNode, req.auth.companyId);
|
|
221387
|
+
if (foreign) throw new ApiError(400, "NODE_NOT_IN_COMPANY", foreign);
|
|
221071
221388
|
const { clearedModel } = await service.bind({ actorId: b2.actorId, nodeId: b2.nodeId, runtimeKind: b2.runtimeKind, status: b2.status ?? "active" }, req.auth.actor);
|
|
221072
221389
|
return { status: 201, body: { ok: true, ...clearedModel ? { clearedModel } : {} } };
|
|
221073
221390
|
});
|
|
@@ -221877,6 +222194,7 @@ var init_routes3 = __esm({
|
|
|
221877
222194
|
init_image_upload();
|
|
221878
222195
|
init_skill_materializer();
|
|
221879
222196
|
init_connector_skills();
|
|
222197
|
+
init_runtime_tenancy();
|
|
221880
222198
|
CREDENTIAL_REVEAL_SOURCES = /* @__PURE__ */ new Set(["agent-cli", "agent-exec", "smoke-test", "oasis-internal", "console"]);
|
|
221881
222199
|
RevealRateLimiter = class {
|
|
221882
222200
|
constructor(perSession, perSessionPerKey, ttlMs = 2 * 60 * 6e4) {
|
|
@@ -222073,7 +222391,7 @@ function createActorsDomain(opts) {
|
|
|
222073
222391
|
return {
|
|
222074
222392
|
service: defaultCtx.service,
|
|
222075
222393
|
resolveCtx,
|
|
222076
|
-
register: actorsDomain({ resolveCtx, ...opts.trace ? { trace: opts.trace } : {}, ...opts.listBuiltinSkills ? { listBuiltinSkills: opts.listBuiltinSkills } : {}, ...opts.getBuiltinSkills ? { getBuiltinSkills: opts.getBuiltinSkills } : {}, ...opts.getConnectorSkills ? { getConnectorSkills: opts.getConnectorSkills } : {}, ...opts.refreshConnectorSkills ? { refreshConnectorSkills: opts.refreshConnectorSkills } : {}, ...opts.memory ? { memory: opts.memory } : {}, ...opts.resolveDispatchScope ? { resolveDispatchScope: opts.resolveDispatchScope } : {}, ...opts.projectExists ? { projectExists: opts.projectExists } : {}, ...opts.credentialAudit ? { credentialAudit: opts.credentialAudit } : {}, ...opts.readCredentialAudit ? { readCredentialAudit: opts.readCredentialAudit } : {}, ...opts.credentialRevealLimits ? { credentialRevealLimits: opts.credentialRevealLimits } : {}, ...opts.skillMarket ? { skillMarket: opts.skillMarket } : {} })
|
|
222394
|
+
register: actorsDomain({ resolveCtx, ...opts.trace ? { trace: opts.trace } : {}, ...opts.listBuiltinSkills ? { listBuiltinSkills: opts.listBuiltinSkills } : {}, ...opts.getBuiltinSkills ? { getBuiltinSkills: opts.getBuiltinSkills } : {}, ...opts.getConnectorSkills ? { getConnectorSkills: opts.getConnectorSkills } : {}, ...opts.refreshConnectorSkills ? { refreshConnectorSkills: opts.refreshConnectorSkills } : {}, ...opts.memory ? { memory: opts.memory } : {}, ...opts.resolveDispatchScope ? { resolveDispatchScope: opts.resolveDispatchScope } : {}, ...opts.projectExists ? { projectExists: opts.projectExists } : {}, ...opts.credentialAudit ? { credentialAudit: opts.credentialAudit } : {}, ...opts.readCredentialAudit ? { readCredentialAudit: opts.readCredentialAudit } : {}, ...opts.credentialRevealLimits ? { credentialRevealLimits: opts.credentialRevealLimits } : {}, ...opts.skillMarket ? { skillMarket: opts.skillMarket } : {}, ...opts.resolveNodeTenancy ? { resolveNodeTenancy: opts.resolveNodeTenancy } : {} })
|
|
222077
222395
|
};
|
|
222078
222396
|
}
|
|
222079
222397
|
var import_node_crypto49;
|
|
@@ -222275,12 +222593,9 @@ function projectsDomain(opts) {
|
|
|
222275
222593
|
}
|
|
222276
222594
|
});
|
|
222277
222595
|
router.get("/api/projects", async (req) => {
|
|
222278
|
-
const inc = req.query?.get("includeEphemeral");
|
|
222279
|
-
const includeEphemeral = inc === "1" || inc === "true";
|
|
222280
222596
|
const uncatRaw = req.query?.get("includeUncategorized");
|
|
222281
222597
|
const includeUncategorized = uncatRaw === null || uncatRaw === void 0 ? void 0 : !(uncatRaw === "0" || uncatRaw === "false");
|
|
222282
222598
|
const projects = await (await svc(req)).listProjects({
|
|
222283
|
-
includeEphemeral,
|
|
222284
222599
|
...includeUncategorized !== void 0 ? { includeUncategorized } : {}
|
|
222285
222600
|
});
|
|
222286
222601
|
const page = paginate(projects.map(toApiProject), req.query);
|
|
@@ -222318,8 +222633,7 @@ function projectsDomain(opts) {
|
|
|
222318
222633
|
body: {
|
|
222319
222634
|
ok: true,
|
|
222320
222635
|
project_id: result.projectId,
|
|
222321
|
-
moved_work_order_ids: result.movedWorkOrderIds
|
|
222322
|
-
carried_git_location_count: result.carriedGitLocationCount
|
|
222636
|
+
moved_work_order_ids: result.movedWorkOrderIds
|
|
222323
222637
|
}
|
|
222324
222638
|
};
|
|
222325
222639
|
} catch (err) {
|
|
@@ -222561,8 +222875,8 @@ ${errs.join("\n")}`);
|
|
|
222561
222875
|
const nulledPatch = "project_id" in b2 && b2.project_id === null || "projectId" in b2 && b2.projectId === null || b2.project_id === "null" || b2.project_id === "" || b2.projectId === "null" || b2.projectId === "";
|
|
222562
222876
|
if (nulledPatch) {
|
|
222563
222877
|
try {
|
|
222564
|
-
|
|
222565
|
-
return { status: 200, body: { work_order_id:
|
|
222878
|
+
await (await svc(req)).unlinkWorkorder(req.params.workOrderId);
|
|
222879
|
+
return { status: 200, body: { work_order_id: req.params.workOrderId, project_id: null } };
|
|
222566
222880
|
} catch (err) {
|
|
222567
222881
|
throw mapProjectError(err);
|
|
222568
222882
|
}
|
|
@@ -222578,8 +222892,8 @@ ${errs.join("\n")}`);
|
|
|
222578
222892
|
});
|
|
222579
222893
|
router.delete("/api/work-orders/:workOrderId/project", async (req) => {
|
|
222580
222894
|
try {
|
|
222581
|
-
|
|
222582
|
-
return { status: 200, body: { work_order_id:
|
|
222895
|
+
await (await svc(req)).unlinkWorkorder(req.params.workOrderId);
|
|
222896
|
+
return { status: 200, body: { work_order_id: req.params.workOrderId, project_id: null } };
|
|
222583
222897
|
} catch (err) {
|
|
222584
222898
|
throw mapProjectError(err);
|
|
222585
222899
|
}
|
|
@@ -223012,7 +223326,8 @@ function toApiNodeOutputDocument(doc) {
|
|
|
223012
223326
|
summary: doc.summary,
|
|
223013
223327
|
...doc.contentKind !== void 0 ? { content_kind: doc.contentKind } : {},
|
|
223014
223328
|
...doc.size !== void 0 ? { size: doc.size } : {},
|
|
223015
|
-
...doc.files !== void 0 ? { files: doc.files.map((f2) => ({ path: f2.path, ...f2.size !== void 0 ? { size: f2.size } : {} })) } : {}
|
|
223329
|
+
...doc.files !== void 0 ? { files: doc.files.map((f2) => ({ path: f2.path, ...f2.size !== void 0 ? { size: f2.size } : {} })) } : {},
|
|
223330
|
+
...doc.updatedAt !== void 0 ? { updated_at: doc.updatedAt } : {}
|
|
223016
223331
|
};
|
|
223017
223332
|
}
|
|
223018
223333
|
function toApiNodeOutputValidation(validation) {
|
|
@@ -223096,7 +223411,7 @@ var init_routes5 = __esm({
|
|
|
223096
223411
|
"use strict";
|
|
223097
223412
|
init_router();
|
|
223098
223413
|
init_src();
|
|
223099
|
-
|
|
223414
|
+
init_uncategorized_project();
|
|
223100
223415
|
}
|
|
223101
223416
|
});
|
|
223102
223417
|
|
|
@@ -228162,13 +228477,15 @@ async function buildChatPreview(chatStore, sessionId, who) {
|
|
|
228162
228477
|
if (lastAsst) lines.push({ label: who, text: clip2(lastAsst.content) });
|
|
228163
228478
|
return lines.length ? { lines } : void 0;
|
|
228164
228479
|
}
|
|
228165
|
-
async function buildChatInbox(chatStore, markers, me, resolveActor) {
|
|
228480
|
+
async function buildChatInbox(chatStore, markers, me, resolveActor, excludeChildSessions) {
|
|
228166
228481
|
const sessions = await chatStore.listSessions(me);
|
|
228167
228482
|
const byScope = new Map(markers.map((m2) => [m2.scope, m2]));
|
|
228168
228483
|
const unread = sessions.filter((s2) => s2.lastTurnQuality !== void 0 && isUnread(s2.touchedAt, byScope.get(chatScope(s2.id)))).sort((a, b2) => a.touchedAt < b2.touchedAt ? 1 : a.touchedAt > b2.touchedAt ? -1 : 0);
|
|
228484
|
+
const excluded = excludeChildSessions ? new Set(await excludeChildSessions(unread.map((s2) => s2.id)).catch(() => [])) : /* @__PURE__ */ new Set();
|
|
228169
228485
|
const out = [];
|
|
228170
228486
|
let previewsFetched = 0;
|
|
228171
228487
|
for (const s2 of unread) {
|
|
228488
|
+
if (excluded.has(s2.id)) continue;
|
|
228172
228489
|
const who = resolveActor?.(s2.aiActorId)?.name ?? "\u52A9\u7406";
|
|
228173
228490
|
const abnormal = s2.lastTurnQuality === "abnormal";
|
|
228174
228491
|
const preview2 = previewsFetched < PREVIEW_FETCH_CAP ? await buildChatPreview(chatStore, s2.id, who) : void 0;
|
|
@@ -229369,7 +229686,7 @@ async function buildResolver(registry2) {
|
|
|
229369
229686
|
const byId = new Map(actors.map((a) => [a.id, a]));
|
|
229370
229687
|
return (id) => {
|
|
229371
229688
|
const a = byId.get(id);
|
|
229372
|
-
return a ? actorRefFieldsOf(a) :
|
|
229689
|
+
return a ? actorRefFieldsOf(a) : actorRefFallbackOf(id);
|
|
229373
229690
|
};
|
|
229374
229691
|
}
|
|
229375
229692
|
async function buildProjectResolver(artifacts) {
|
|
@@ -229558,7 +229875,7 @@ function collabDomain(opts) {
|
|
|
229558
229875
|
if (projectFilter) {
|
|
229559
229876
|
items = items.filter((wo) => {
|
|
229560
229877
|
const bound = wo.projectId;
|
|
229561
|
-
if (isUncategorizedProjectId(projectFilter)) return
|
|
229878
|
+
if (isUncategorizedProjectId(projectFilter)) return bound == null;
|
|
229562
229879
|
return bound === projectFilter;
|
|
229563
229880
|
});
|
|
229564
229881
|
}
|
|
@@ -229964,7 +230281,8 @@ function collabDomain(opts) {
|
|
|
229964
230281
|
return void 0;
|
|
229965
230282
|
}
|
|
229966
230283
|
})();
|
|
229967
|
-
const
|
|
230284
|
+
const excludeChildSessions = opts.resolveDelegationChildIds ? (ids2) => opts.resolveDelegationChildIds(req.auth.companyId, ids2) : void 0;
|
|
230285
|
+
const chatItems = chatStore && myReadMarkers ? await buildChatInbox(chatStore, markers, me, resolveActor, excludeChildSessions) : [];
|
|
229968
230286
|
let waitingItems = [];
|
|
229969
230287
|
if (engineStore) {
|
|
229970
230288
|
const snaps = (await Promise.all(
|
|
@@ -230259,7 +230577,7 @@ var init_collab = __esm({
|
|
|
230259
230577
|
init_workorder_metrics();
|
|
230260
230578
|
init_chat_workorder_files();
|
|
230261
230579
|
init_organization_usage();
|
|
230262
|
-
|
|
230580
|
+
init_uncategorized_project();
|
|
230263
230581
|
init_tags();
|
|
230264
230582
|
init_activity2();
|
|
230265
230583
|
init_node_state();
|
|
@@ -230546,24 +230864,16 @@ function makeSeededWorkorderCreator(deps) {
|
|
|
230546
230864
|
const blobs = company?.blobs ?? deps.blobs;
|
|
230547
230865
|
const registry2 = company?.registry ?? deps.registry;
|
|
230548
230866
|
const artifactState = company ? company.artifactState : deps.artifactState;
|
|
230549
|
-
const
|
|
230867
|
+
const workorderDrafts = company ? company.workorderDrafts : deps.workorderDrafts;
|
|
230550
230868
|
const workspace = input.idempotencyKey ? workspaceForKey(input.idempotencyKey) : genWorkspace();
|
|
230551
230869
|
if (input.idempotencyKey) {
|
|
230552
230870
|
const existing = existingWorkorder(kernel, workspace);
|
|
230553
230871
|
if (existing) return existing;
|
|
230554
230872
|
}
|
|
230555
|
-
if (artifactState &&
|
|
230556
|
-
await
|
|
230873
|
+
if (artifactState && input.projectId) {
|
|
230874
|
+
await bindWorkorderProject({
|
|
230557
230875
|
workOrderId: workspace,
|
|
230558
|
-
|
|
230559
|
-
...input.title ? { title: input.title } : {},
|
|
230560
|
-
// ADR 0207:临时项目的创建人 = 建这张单的**真人**。`humanActorId` 是 B 路径 handler
|
|
230561
|
-
// 从 `x-chat-session-id` 反查填进来的「陪发起人聊出此单的人」,与 brief.owner 同源;
|
|
230562
|
-
// 没有它时只在 `input.actor` 本身是人时才用——剧本由 agent 触发时落 NULL,
|
|
230563
|
-
// 不把执行者写成负责人。
|
|
230564
|
-
...input.humanActorId ? { createdBy: input.humanActorId } : input.actor.startsWith("actor:human:") ? { createdBy: input.actor } : {},
|
|
230565
|
-
createProject: createProject ?? (async ({ id }) => ({ id })),
|
|
230566
|
-
// 上面的守卫保证走不到这个分支
|
|
230876
|
+
projectId: input.projectId,
|
|
230567
230877
|
upsertBinding: (b2) => artifactState.upsertWorkspaceBinding(b2),
|
|
230568
230878
|
...deps.onWarn ? { onWarn: deps.onWarn } : {}
|
|
230569
230879
|
});
|
|
@@ -230595,8 +230905,8 @@ function makeSeededWorkorderCreator(deps) {
|
|
|
230595
230905
|
if (!preview2.endState.ok) {
|
|
230596
230906
|
throw new Error(`\u5267\u672C\u5EFA\u5355\u7EC8\u6001\u6821\u9A8C\u4E0D\u8FC7\uFF1A${preview2.endState.violations.join("\uFF1B")}`);
|
|
230597
230907
|
}
|
|
230598
|
-
if (input.stage === true &&
|
|
230599
|
-
const draft = await
|
|
230908
|
+
if (input.stage === true && workorderDrafts) {
|
|
230909
|
+
const draft = await workorderDrafts.submit({
|
|
230600
230910
|
workspace,
|
|
230601
230911
|
plan: planned.plan,
|
|
230602
230912
|
rootArtifactId: planned.rootArtifactId,
|
|
@@ -230658,7 +230968,7 @@ var init_create_seeded_workorder = __esm({
|
|
|
230658
230968
|
"../server/src/domains/collab/create-seeded-workorder.ts"() {
|
|
230659
230969
|
"use strict";
|
|
230660
230970
|
import_node_crypto57 = require("node:crypto");
|
|
230661
|
-
|
|
230971
|
+
init_uncategorized_project();
|
|
230662
230972
|
init_planner();
|
|
230663
230973
|
enc2 = (s2) => new TextEncoder().encode(s2);
|
|
230664
230974
|
}
|
|
@@ -233772,12 +234082,20 @@ function createChatSessionsDomain(opts) {
|
|
|
233772
234082
|
const limitRaw = Number(req.query?.get("limit") ?? "0") || 0;
|
|
233773
234083
|
const limit = limitRaw > 0 ? Math.min(limitRaw, MAX_SNAPSHOT_ITEMS) : DEFAULT_SNAPSHOT_ITEMS;
|
|
233774
234084
|
const cursor = opts.liveChat?.cursorFor(chatSessionId) ?? null;
|
|
233775
|
-
|
|
233776
|
-
|
|
233777
|
-
|
|
233778
|
-
|
|
233779
|
-
|
|
233780
|
-
|
|
234085
|
+
const turns = await turnsFor(req);
|
|
234086
|
+
const activeTurnRow = turns ? await turns.activeTurn(chatSessionId).catch(() => null) : null;
|
|
234087
|
+
const turnId = cursor?.turnId ?? activeTurnRow?.id ?? null;
|
|
234088
|
+
const activeTurn = (() => {
|
|
234089
|
+
if (!activeTurnRow) return null;
|
|
234090
|
+
const settled = activeTurnRow.status !== "reserved" && activeTurnRow.status !== "running";
|
|
234091
|
+
if (settled) return { turnId: activeTurnRow.id, phase: activeTurnRow.status };
|
|
234092
|
+
if (opts.liveChat?.isRunning(chatSessionId)) {
|
|
234093
|
+
return { turnId: activeTurnRow.id, phase: "running" };
|
|
234094
|
+
}
|
|
234095
|
+
const startedAt = Date.parse(activeTurnRow.startedAt ?? activeTurnRow.reservedAt);
|
|
234096
|
+
const stale = Number.isFinite(startedAt) && Date.now() - startedAt >= LINK_WINDOWS.chatTurnPhaseStaleAfterMs;
|
|
234097
|
+
return { turnId: activeTurnRow.id, phase: stale ? "interrupted" : "running" };
|
|
234098
|
+
})();
|
|
233781
234099
|
const snapshotEntries = [];
|
|
233782
234100
|
let nextSinceVersion = sinceVersion;
|
|
233783
234101
|
let hasMoreBefore = false;
|
|
@@ -233809,6 +234127,7 @@ function createChatSessionsDomain(opts) {
|
|
|
233809
234127
|
nextSinceVersion,
|
|
233810
234128
|
frameCursor: cursor ? { epoch: cursor.epoch, seq: cursor.seq } : { epoch: 0, seq: 0 },
|
|
233811
234129
|
canAppend: cursor?.canAppend ?? false,
|
|
234130
|
+
activeTurn,
|
|
233812
234131
|
oldestOrd,
|
|
233813
234132
|
hasMoreBefore
|
|
233814
234133
|
};
|
|
@@ -233889,6 +234208,9 @@ function createChatSessionsDomain(opts) {
|
|
|
233889
234208
|
childSessionId: record8.childSessionId,
|
|
233890
234209
|
expertActorId: record8.expertActorId,
|
|
233891
234210
|
...expert?.name ? { expertName: expert.name } : {},
|
|
234211
|
+
/* 头像与名字同一本名册、同一次查询取出来。缺了它,委派卡与输入框上方状态条只能退回
|
|
234212
|
+
姓名首字的灰圆(发起人 2026-09-10 报的「灰底 D」)——那两处画的是「[头像] 名字 进行中」。 */
|
|
234213
|
+
...expert?.avatar ? { expertAvatar: expert.avatar } : {},
|
|
233892
234214
|
state: record8.state,
|
|
233893
234215
|
...record8.failureKind ? { failureKind: record8.failureKind } : {},
|
|
233894
234216
|
...record8.label ? { label: record8.label } : {},
|
|
@@ -237581,7 +237903,7 @@ function playbooksDomain(opts) {
|
|
|
237581
237903
|
const drafts = opts.resolveWorkorderDrafts ? await opts.resolveWorkorderDrafts(req.auth?.companyId) : opts.workorderDrafts;
|
|
237582
237904
|
if (result.draftId && chatSid && drafts) {
|
|
237583
237905
|
try {
|
|
237584
|
-
await drafts.tagSession(result.draftId, chatSid, opts.liveChat?.runFor(chatSid));
|
|
237906
|
+
await drafts.tagSession(result.draftId, chatSid, opts.liveChat?.runFor(chatSid), result.workspace);
|
|
237585
237907
|
} catch (err) {
|
|
237586
237908
|
console.warn(`[playbooks] tagSession(${result.draftId}) \u5931\u8D25\uFF0C\u4E0D\u963B\u585E\u5EFA\u5355\u54CD\u5E94:`, err);
|
|
237587
237909
|
}
|
|
@@ -238328,7 +238650,10 @@ var init_daemon_adapter = __esm({
|
|
|
238328
238650
|
} else if (msg.type === "session_normalized_event") {
|
|
238329
238651
|
this.confirmDelivery(msg.dispatchId, "started");
|
|
238330
238652
|
const entry = this.pending.get(msg.dispatchId);
|
|
238331
|
-
if (!entry)
|
|
238653
|
+
if (!entry) {
|
|
238654
|
+
if (this.stashEnabled) this.stashFrame(msg.dispatchId, { type: "normalized", event: msg.event });
|
|
238655
|
+
return;
|
|
238656
|
+
}
|
|
238332
238657
|
if (entry.normalizedCbs.length === 0) entry.normalizedBacklog.push(msg.event);
|
|
238333
238658
|
else for (const cb of entry.normalizedCbs) cb(msg.event);
|
|
238334
238659
|
} else if (msg.type === "session_output") {
|
|
@@ -238461,6 +238786,7 @@ var init_daemon_adapter = __esm({
|
|
|
238461
238786
|
let replayOutput;
|
|
238462
238787
|
let replayTelemetry;
|
|
238463
238788
|
let replayLiveEvent;
|
|
238789
|
+
let replayNormalized;
|
|
238464
238790
|
const flushReplay = () => {
|
|
238465
238791
|
while (stashed.frames.length > 0) {
|
|
238466
238792
|
const frame = stashed.frames[0];
|
|
@@ -238472,6 +238798,10 @@ var init_daemon_adapter = __esm({
|
|
|
238472
238798
|
if (!replayTelemetry) return;
|
|
238473
238799
|
stashed.frames.shift();
|
|
238474
238800
|
replayTelemetry(frame.event);
|
|
238801
|
+
} else if (frame.type === "normalized") {
|
|
238802
|
+
if (!replayNormalized) return;
|
|
238803
|
+
stashed.frames.shift();
|
|
238804
|
+
replayNormalized(frame.event);
|
|
238475
238805
|
} else {
|
|
238476
238806
|
if (!replayLiveEvent) return;
|
|
238477
238807
|
stashed.frames.shift();
|
|
@@ -238510,9 +238840,11 @@ var init_daemon_adapter = __esm({
|
|
|
238510
238840
|
},
|
|
238511
238841
|
...supportsNormalizedEvents ? {
|
|
238512
238842
|
onNormalizedProviderEvent(cb) {
|
|
238513
|
-
|
|
238843
|
+
replayNormalized ??= cb;
|
|
238844
|
+
flushReplay();
|
|
238514
238845
|
const backlog = entry.normalizedBacklog.splice(0, entry.normalizedBacklog.length);
|
|
238515
238846
|
for (const e of backlog) cb(e);
|
|
238847
|
+
entry.normalizedCbs.push(cb);
|
|
238516
238848
|
}
|
|
238517
238849
|
} : {},
|
|
238518
238850
|
// ADR-0093 对话追加(重挂句柄同 spawn 句柄)。
|
|
@@ -241995,6 +242327,9 @@ var init_postgres_projects = __esm({
|
|
|
241995
242327
|
const r = await this.pool.query(`SELECT * FROM ${this.s}.workspace_bindings`);
|
|
241996
242328
|
return r.rows.map(rowToWorkspaceBinding);
|
|
241997
242329
|
}
|
|
242330
|
+
async deleteWorkspaceBinding(workOrderId) {
|
|
242331
|
+
await this.pool.query(`DELETE FROM ${this.s}.workspace_bindings WHERE work_order_id = $1`, [workOrderId]);
|
|
242332
|
+
}
|
|
241998
242333
|
/* ---------- 任务输入文件(PRD §2.6.3 建单交接;薄旁存,非 oplog) ---------- */
|
|
241999
242334
|
async addWorkOrderFile(file) {
|
|
242000
242335
|
await this.pool.query(
|
|
@@ -255472,8 +255807,8 @@ var init_cutover_aware_workorder_drafts = __esm({
|
|
|
255472
255807
|
async snapshotForNewRun(d, runId) {
|
|
255473
255808
|
return (await this.pickWritable()).snapshotForNewRun(d, runId);
|
|
255474
255809
|
}
|
|
255475
|
-
async tagSession(id, s2, runId) {
|
|
255476
|
-
return (await this.pickWritable()).tagSession(id, s2, runId);
|
|
255810
|
+
async tagSession(id, s2, runId, expectedWorkspace) {
|
|
255811
|
+
return (await this.pickWritable()).tagSession(id, s2, runId, expectedWorkspace);
|
|
255477
255812
|
}
|
|
255478
255813
|
async markEdited(id) {
|
|
255479
255814
|
return (await this.pickWritable()).markEdited(id);
|
|
@@ -256444,6 +256779,7 @@ var init_src11 = __esm({
|
|
|
256444
256779
|
init_sender();
|
|
256445
256780
|
init_routes7();
|
|
256446
256781
|
init_runtime_drift();
|
|
256782
|
+
init_runtime_tenancy();
|
|
256447
256783
|
init_types4();
|
|
256448
256784
|
init_roles();
|
|
256449
256785
|
init_node_store();
|
|
@@ -256495,7 +256831,7 @@ var init_src11 = __esm({
|
|
|
256495
256831
|
init_connector_skills();
|
|
256496
256832
|
init_connector_tool_versions();
|
|
256497
256833
|
init_board_watch();
|
|
256498
|
-
|
|
256834
|
+
init_uncategorized_project();
|
|
256499
256835
|
init_run_settlement();
|
|
256500
256836
|
init_reconcile_terminal_runs();
|
|
256501
256837
|
init_run_settlement_wiring();
|
|
@@ -258007,6 +258343,9 @@ async function startServe(opts) {
|
|
|
258007
258343
|
let memStoreShared;
|
|
258008
258344
|
let isKnowledgeQueryAvailable = async (_actorId) => false;
|
|
258009
258345
|
const actors = createActorsDomain({
|
|
258346
|
+
// 租户闸(ADR-0164 §5):`POST /api/bindings` 据它拦「把本公司员工绑到别家公司的机器上」。
|
|
258347
|
+
// 闭包读当前 nodeStore(下方 PG 分支会重赋值),与 `listBindingModelIds` 同一写法。
|
|
258348
|
+
resolveNodeTenancy: async (nodeId) => nodeStore.getNode(nodeId),
|
|
258010
258349
|
store: registryStore,
|
|
258011
258350
|
oplog,
|
|
258012
258351
|
kernel: defaultCompanyKernel,
|
|
@@ -258075,7 +258414,10 @@ async function startServe(opts) {
|
|
|
258075
258414
|
prefsStore: humanPrefsStore,
|
|
258076
258415
|
registry: registryStore,
|
|
258077
258416
|
actors: actors.service,
|
|
258078
|
-
|
|
258417
|
+
// 默认公司那一份。**闭包求值**:`defaultCompanyId` 是 let,startup 后才被库里的真实 id 覆盖
|
|
258418
|
+
// (同 :1575 那句「必须是调用时求值」)。
|
|
258419
|
+
companyId: () => defaultCompanyId,
|
|
258420
|
+
listRuntimes: (nodeId, scope) => nodeStore.listRuntimes(nodeId, scope),
|
|
258079
258421
|
isPlatformAdmin: async () => false
|
|
258080
258422
|
// 占位;controlPlaneStore 就绪后由下方 setPlatformAdminChecker 注入真判据
|
|
258081
258423
|
});
|
|
@@ -258367,8 +258709,6 @@ async function startServe(opts) {
|
|
|
258367
258709
|
getSchema: () => schema,
|
|
258368
258710
|
registry: registryStore,
|
|
258369
258711
|
artifactState: artifactStateStore,
|
|
258370
|
-
// 决策 C:剧本建单没给 projectId 时,兜底建一个由工单派生的临时项目并绑上。
|
|
258371
|
-
createProject: makeEnsureProjectFromStore(projectStateStore),
|
|
258372
258712
|
onWarn: (m2) => console.warn(m2),
|
|
258373
258713
|
workorderDrafts,
|
|
258374
258714
|
buildDraftGraph: async (draft) => buildWorkorderDraftDetail(draft, await buildResolver(registryStore), buildTypeReviewerResolver(schema, await registryStore.listActors())).graph,
|
|
@@ -258390,8 +258730,12 @@ async function startServe(opts) {
|
|
|
258390
258730
|
blobs: engine2.blobs,
|
|
258391
258731
|
registry: engine2.registry ?? registryStore,
|
|
258392
258732
|
artifactState: stores.artifacts,
|
|
258393
|
-
//
|
|
258394
|
-
|
|
258733
|
+
// 草案 store 必须按公司拿(2026-09-10 事故):此前它是装配期捕获的共享库实例,
|
|
258734
|
+
// `stage: true` 建出来的草案落大通铺、读侧却按公司解析,本公司永远查不到自己的草案。
|
|
258735
|
+
// ⚠ 这个 helper 的 const 声明在本处之后,只能在**请求时**才求值的闭包里引用
|
|
258736
|
+
// (同上方 resolveChatSession 的写法),直接在装配期引用会撞 TDZ。
|
|
258737
|
+
// planner 阻断项旁账**不在本次范围**,理由见 create-seeded-workorder.ts 里那段注释。
|
|
258738
|
+
workorderDrafts: await workorderDraftsFor(companyId)
|
|
258395
258739
|
};
|
|
258396
258740
|
}
|
|
258397
258741
|
} : {}
|
|
@@ -259702,7 +260046,9 @@ async function startServe(opts) {
|
|
|
259702
260046
|
prefsStore: plane?.humanPrefs ?? humanPrefsStore,
|
|
259703
260047
|
registry: engine2.registry,
|
|
259704
260048
|
actors: ctx.service,
|
|
259705
|
-
|
|
260049
|
+
// 这一份服务的是**请求方的公司**——助理只在这家的在线 runtime 里挑机器(ADR-0164 §5)。
|
|
260050
|
+
companyId: () => cid,
|
|
260051
|
+
listRuntimes: (nodeId, scope) => nodeStore.listRuntimes(nodeId, scope),
|
|
259706
260052
|
isPlatformAdmin: async (caller) => {
|
|
259707
260053
|
const member = await controlPlaneStore.getMember(cid, caller);
|
|
259708
260054
|
return member?.role === "owner";
|
|
@@ -259796,6 +260142,14 @@ async function startServe(opts) {
|
|
|
259796
260142
|
// ADR-0086:chat 知会条目(与 readMarkers 成对)
|
|
259797
260143
|
// ADR「多租户数据面收口」§D2:知会条目按**当前公司**取。装了路由才给(dev 文件模式沿用单实例)。
|
|
259798
260144
|
...chatStoreRouter ? { resolveChatSession: chatStoreFor } : {},
|
|
260145
|
+
/* 委派子会话不进铃铛(发起人 2026-09-10):专家跑完回流到父会话,父会话那条已经通知过一次。
|
|
260146
|
+
判据与左侧会话列表那条路同一个读口(`listChildSessionIds`,见下面 chat-sessions 域的
|
|
260147
|
+
`delegations` 接线),不另造。灰度关着 → 回空集合 = 不滤,与接入前逐字一致。 */
|
|
260148
|
+
resolveDelegationChildIds: async (companyId, sessionIds) => {
|
|
260149
|
+
if (!chatDelegationEnabledFromEnv() || sessionIds.length === 0) return [];
|
|
260150
|
+
const ledger = await delegationStoreFor(companyId).catch(() => null);
|
|
260151
|
+
return ledger ? ledger.listChildSessionIds(sessionIds) : [];
|
|
260152
|
+
},
|
|
259799
260153
|
readMarkers: readMarkerStore,
|
|
259800
260154
|
// bundle=`inbox`:水位按当前公司取(与上面 resolveChatSession 成对)。
|
|
259801
260155
|
...pgPool ? { resolveReadMarkers: readMarkerStoreFor } : {},
|
|
@@ -260007,7 +260361,9 @@ async function startServe(opts) {
|
|
|
260007
260361
|
listProjects: (companyId) => companyId === defaultCompanyId ? projectStateStore.listProjects() : Promise.resolve([]),
|
|
260008
260362
|
listProjectMembers: async (companyId, projectId2) => companyId === defaultCompanyId ? (await projectStateStore.listProjectMembers(projectId2)).flatMap((member) => member.kind === "system" ? [] : [{ actorId: member.actorId, kind: member.kind, status: member.status === "active" ? "active" : "inactive" }]) : [],
|
|
260009
260363
|
getWorkspaceBinding: (companyId, workorderId) => companyId === defaultCompanyId ? artifactStateStore.getWorkspaceBinding(workorderId) : Promise.resolve(null),
|
|
260010
|
-
|
|
260364
|
+
// 非默认公司暂不支持项目级知识作用域(这里只有默认公司的 projectStateStore/名册可读),
|
|
260365
|
+
// 一律降级到组织作用域。旧版这个谓词还兼着"是不是 proj_tmp_ 临时项目",临时项目退役后只剩这一半。
|
|
260366
|
+
isProjectScopeUnsupported: (companyId) => companyId !== defaultCompanyId
|
|
260011
260367
|
},
|
|
260012
260368
|
actor: async (actorId, organizationId) => {
|
|
260013
260369
|
if (actorId === SYSTEM_ACTOR2) return { id: actorId, kind: "system", status: "active" };
|
|
@@ -260512,7 +260868,9 @@ async function startServe(opts) {
|
|
|
260512
260868
|
}
|
|
260513
260869
|
binding = currentBinding;
|
|
260514
260870
|
}
|
|
260515
|
-
const requireDispatchNode = (target) => {
|
|
260871
|
+
const requireDispatchNode = async (target) => {
|
|
260872
|
+
const foreign = foreignNodeBlock(await nodeStore.getNode(target.nodeId).catch(() => null), runCompanyId);
|
|
260873
|
+
if (foreign) throw new ApiError(409, "NODE_NOT_IN_COMPANY", foreign);
|
|
260516
260874
|
const connected = hub?.connectedDaemons().find((d) => d.daemonId === target.nodeId);
|
|
260517
260875
|
if (!connected) {
|
|
260518
260876
|
throw new ApiError(409, "NODE_OFFLINE", `\u7ED1\u5B9A\u8282\u70B9 ${target.nodeId} \u5F53\u524D\u4E0D\u5728\u7EBF`);
|
|
@@ -260522,7 +260880,7 @@ async function startServe(opts) {
|
|
|
260522
260880
|
}
|
|
260523
260881
|
return connected;
|
|
260524
260882
|
};
|
|
260525
|
-
let node2 = requireDispatchNode(binding);
|
|
260883
|
+
let node2 = await requireDispatchNode(binding);
|
|
260526
260884
|
if (!chatRemoteAdapter) {
|
|
260527
260885
|
throw new ApiError(503, "NODE_GATEWAY_NOT_READY", "node-gateway \u5C1A\u672A\u5C31\u7EEA");
|
|
260528
260886
|
}
|
|
@@ -260602,7 +260960,7 @@ async function startServe(opts) {
|
|
|
260602
260960
|
}
|
|
260603
260961
|
if (stableDispatch.target.nodeId !== binding.nodeId || stableDispatch.target.runtimeKind !== binding.runtimeKind) {
|
|
260604
260962
|
binding = { actorId, ...stableDispatch.target, status: "active" };
|
|
260605
|
-
node2 = requireDispatchNode(binding);
|
|
260963
|
+
node2 = await requireDispatchNode(binding);
|
|
260606
260964
|
requireNodeConnectors();
|
|
260607
260965
|
}
|
|
260608
260966
|
}
|
|
@@ -261955,6 +262313,8 @@ async function startServe(opts) {
|
|
|
261955
262313
|
checkDispatchable: async ({ actor, binding }) => {
|
|
261956
262314
|
try {
|
|
261957
262315
|
if (!binding.nodeId) return null;
|
|
262316
|
+
const foreign = foreignNodeBlock(await nodeStore.getNode(binding.nodeId).catch(() => null), companyId);
|
|
262317
|
+
if (foreign) return { reason: "node-not-in-company", errorMessage: foreign };
|
|
261958
262318
|
const drift = runtimeDriftBlock(await nodeStore.listRuntimes(binding.nodeId), binding.nodeId, binding.runtimeKind);
|
|
261959
262319
|
if (drift) return drift;
|
|
261960
262320
|
const health = healthOfNode(binding.nodeId);
|
|
@@ -269113,6 +269473,9 @@ ${res.warning}`);
|
|
|
269113
269473
|
...expect !== void 0 ? { expectedVersion: Number(expect) } : {}
|
|
269114
269474
|
});
|
|
269115
269475
|
println(`\u5DF2\u8BB0\u4E0B ${rec.memId}\uFF08v${rec.version}\uFF0C${rec.projectId === null ? "actor \u7EA7\xB7\u8DE8\u9879\u76EE\u901A\u7528" : `\u9879\u76EE ${rec.projectId}`}\uFF09`);
|
|
269476
|
+
if ((flags.get("scope") ?? "actor") === "project" && rec.projectId === null) {
|
|
269477
|
+
println(" \u24D8 \u672C\u8F6E\u89E3\u6790\u4E0D\u51FA\u6240\u5C5E\u9879\u76EE\uFF08\u8FD9\u5F20\u4EFB\u52A1\u6CA1\u6302\u9879\u76EE\uFF09\uFF0C\u5DF2\u6309 actor \u7EA7\u8BB0\u4E0B\u2014\u2014\u60F3\u8BA9\u5B83\u843D\u5230\u9879\u76EE\u540D\u4E0B\uFF0C\u5148\u7ED9\u4EFB\u52A1\u6307\u5B9A\u9879\u76EE\u3002");
|
|
269478
|
+
}
|
|
269116
269479
|
} catch (err) {
|
|
269117
269480
|
const body2 = err instanceof ApiRequestError ? err.body : void 0;
|
|
269118
269481
|
if (body2?.current) {
|
|
@@ -270671,7 +271034,7 @@ function shimScript() {
|
|
|
270671
271034
|
}
|
|
270672
271035
|
|
|
270673
271036
|
// src/index.ts
|
|
270674
|
-
var PKG_VERSION = true ? "2.2.
|
|
271037
|
+
var PKG_VERSION = true ? "2.2.11" : "dev";
|
|
270675
271038
|
var LOCAL_BIN = localBin();
|
|
270676
271039
|
var NPM_PREFIX = npmPrefix();
|
|
270677
271040
|
var INSTANCE = DEFAULT_INSTANCE;
|