oasis_test_v2 2.2.9 → 2.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +210 -41
- 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) {
|
|
@@ -5560,6 +5589,7 @@ CREATE TABLE IF NOT EXISTS ${q2}.work_artifacts (
|
|
|
5560
5589
|
content_kind text NOT NULL CHECK (content_kind IN ('inline','external','manifest')),
|
|
5561
5590
|
ordinal integer,
|
|
5562
5591
|
created_at timestamptz NOT NULL,
|
|
5592
|
+
content_edited_at timestamptz,
|
|
5563
5593
|
UNIQUE (work_id, logical_path)
|
|
5564
5594
|
);
|
|
5565
5595
|
CREATE INDEX IF NOT EXISTS work_artifacts_node_idx ON ${q2}.work_artifacts (node_id);
|
|
@@ -5725,6 +5755,9 @@ ALTER TABLE ${q2}.works ADD COLUMN IF NOT EXISTS proposal_reason text;
|
|
|
5725
5755
|
-- \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
5756
|
-- 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
5757
|
ALTER TABLE ${q2}.works ADD COLUMN IF NOT EXISTS last_activity_at timestamptz;
|
|
5758
|
+
-- \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
|
|
5759
|
+
-- \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
|
|
5760
|
+
ALTER TABLE ${q2}.work_artifacts ADD COLUMN IF NOT EXISTS content_edited_at timestamptz;
|
|
5728
5761
|
-- \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
5762
|
-- 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
5763
|
ALTER TABLE ${q2}.works ADD COLUMN IF NOT EXISTS frozen_issue_ids jsonb;
|
|
@@ -11388,7 +11421,11 @@ var init_store_postgres = __esm({
|
|
|
11388
11421
|
contentRef: a.content_ref,
|
|
11389
11422
|
contentKind: a.content_kind,
|
|
11390
11423
|
ordinal: a.ordinal,
|
|
11391
|
-
createdAt: iso(a.created_at)
|
|
11424
|
+
createdAt: iso(a.created_at),
|
|
11425
|
+
// 只在真被改写过时才带这个键:从未改写的行在内存态里是**没有这个字段**的,
|
|
11426
|
+
// PG 回读时补一个 `null` 会让「事件重放出来的态」和「从库里读出来的态」深比不相等
|
|
11427
|
+
// (packages/engine/tests/store-conformance.postgres.test.ts 就是钉这条一致性的)。
|
|
11428
|
+
...iso(a.content_edited_at) ? { contentEditedAt: iso(a.content_edited_at) } : {}
|
|
11392
11429
|
})),
|
|
11393
11430
|
workInputs: inputs.rows.map((i) => ({
|
|
11394
11431
|
workId: i.work_id,
|
|
@@ -11623,9 +11660,9 @@ var init_store_postgres = __esm({
|
|
|
11623
11660
|
for (const a of m2.rows) {
|
|
11624
11661
|
await this.c.query(
|
|
11625
11662
|
`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]
|
|
11663
|
+
(id,work_id,node_id,type,name,logical_path,content_ref,content_kind,ordinal,created_at,content_edited_at)
|
|
11664
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11) ON CONFLICT (id) DO NOTHING`,
|
|
11665
|
+
[a.id, a.workId, a.nodeId, a.type, a.name, a.logicalPath, a.contentRef, a.contentKind, a.ordinal, a.createdAt, a.contentEditedAt ?? null]
|
|
11629
11666
|
);
|
|
11630
11667
|
}
|
|
11631
11668
|
break;
|
|
@@ -12626,7 +12663,7 @@ var init_work = __esm({
|
|
|
12626
12663
|
workContentEdited = {
|
|
12627
12664
|
name: "work/content-edited",
|
|
12628
12665
|
kind: "work.content_edited",
|
|
12629
|
-
apply(e, state) {
|
|
12666
|
+
apply(e, state, ctx) {
|
|
12630
12667
|
const work = state.work(e.workId);
|
|
12631
12668
|
if (!work) return;
|
|
12632
12669
|
const node2 = state.node(work.nodeId);
|
|
@@ -12634,7 +12671,7 @@ var init_work = __esm({
|
|
|
12634
12671
|
const artifacts = state.artifactsOf(e.workId);
|
|
12635
12672
|
const target = artifacts.find((a) => a.id === e.artifactId);
|
|
12636
12673
|
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));
|
|
12674
|
+
state.replaceArtifacts(e.workId, artifacts.map((a) => a.id === target.id ? { ...a, contentRef: e.contentRef, contentEditedAt: ctx.at } : a));
|
|
12638
12675
|
}
|
|
12639
12676
|
};
|
|
12640
12677
|
workConclude = {
|
|
@@ -28739,9 +28776,22 @@ var init_chat_item_ledger = __esm({
|
|
|
28739
28776
|
...deterministicId ? { deterministicId } : {}
|
|
28740
28777
|
});
|
|
28741
28778
|
}
|
|
28742
|
-
/**
|
|
28779
|
+
/**
|
|
28780
|
+
* 服务端合成的错误帧(session.done 抛 / exit 判失败)。不入正文,归 control。
|
|
28781
|
+
*
|
|
28782
|
+
* **返回 outcome 是为了让调用方把它推上 v3 流**(ADR-0510 D5/D6,2026-09-10 修)。
|
|
28783
|
+
* 在此之前这条行只落库、不发帧:客户端在这一轮里唯一收到的终态信号是 `emitV3TurnTerminal`
|
|
28784
|
+
* 发的 `turn_failed`,而那一帧不带任何正文、客户端的 `applyFrame` 又明说「不建 item、
|
|
28785
|
+
* 仅推进 frameSeq」——于是失败原因整条飞不到还开着的页面上,只有下一次 `/items` 快照
|
|
28786
|
+
* 才补得上。生产现场(v2.openfriday.ai,2026-09-10 05:39 那一轮):runtime 报
|
|
28787
|
+
* 「401 Authentication Fails」,库里三处都记着,页面上从头到尾是空的。
|
|
28788
|
+
*
|
|
28789
|
+
* 号取的是**建行时那两格钟**(`startedVersion` 给 wire 的 `started` 帧、`version` 给这一行
|
|
28790
|
+
* 本身的写),与 provider item 走 {@link ChatItemLedger.apply} 那条路逐字同构——
|
|
28791
|
+
* 快照与增量流因此还是同一套版本空间。
|
|
28792
|
+
*/
|
|
28743
28793
|
recordError(text5, extra) {
|
|
28744
|
-
this.insert({
|
|
28794
|
+
const id = this.insert({
|
|
28745
28795
|
kind: "control",
|
|
28746
28796
|
role: "assistant",
|
|
28747
28797
|
status: "failed",
|
|
@@ -28757,6 +28807,8 @@ var init_chat_item_ledger = __esm({
|
|
|
28757
28807
|
...extra?.detail ? { detail: extra.detail } : {}
|
|
28758
28808
|
}
|
|
28759
28809
|
});
|
|
28810
|
+
const row = this.rows.get(id);
|
|
28811
|
+
return { itemId: id, version: row.wireVersion, startedVersion: row.startedVersion, ord: row.ord };
|
|
28760
28812
|
}
|
|
28761
28813
|
/**
|
|
28762
28814
|
* 回灌重启前已经落库的那段正文(`chat-recovery` 重挂 / 迟到对账用)。
|
|
@@ -159835,6 +159887,7 @@ var init_live_chat = __esm({
|
|
|
159835
159887
|
},
|
|
159836
159888
|
emitLive: (event) => this.emitLive(turn, event),
|
|
159837
159889
|
applyNormalizedEvent: (event) => this.applyNormalizedEvent(turn, event),
|
|
159890
|
+
publishServerItem: (item) => this.publishServerItem(turn, item),
|
|
159838
159891
|
subscribe: (sub) => {
|
|
159839
159892
|
turn.subscribers.add(sub);
|
|
159840
159893
|
return () => {
|
|
@@ -159981,6 +160034,45 @@ var init_live_chat = __esm({
|
|
|
159981
160034
|
};
|
|
159982
160035
|
this.publishV3Coalesced(turn, frame);
|
|
159983
160036
|
}
|
|
160037
|
+
/**
|
|
160038
|
+
* 服务端合成 item → 两帧(`started` + 终态),与 {@link emitV3} 给 provider item 发的那两帧
|
|
160039
|
+
* **逐字同构**:先用建行预留的那一格发 `started`(客户端以它为该 item 首次亮相、payload 首次成形),
|
|
160040
|
+
* 再用这一行本身的写号发 `completed` / `failed`。
|
|
160041
|
+
*
|
|
160042
|
+
* 为什么不学 {@link publishUserItem} 用一帧 `set_text`:那条路是给「一次性成形的**正文**」用的,
|
|
160043
|
+
* 落在客户端桶里 `status` 恒为 `streaming`——用户那条消息无所谓,失败收尾的 control 行却必须是
|
|
160044
|
+
* 终态,否则这一轮在客户端看来永远没关。
|
|
160045
|
+
*/
|
|
160046
|
+
publishServerItem(turn, item) {
|
|
160047
|
+
if (turn.status !== "running") return;
|
|
160048
|
+
const perItem = turn.v3Items.get(item.itemId) ?? { textOffset: 0, started: false };
|
|
160049
|
+
const ord = turn.items?.ordFor(item.itemId) ?? item.ord ?? void 0;
|
|
160050
|
+
const base = {
|
|
160051
|
+
protocolVersion: 3,
|
|
160052
|
+
streamId: turn.liveStreamId,
|
|
160053
|
+
turnId: this.wireTurnId(turn),
|
|
160054
|
+
itemId: item.itemId,
|
|
160055
|
+
itemType: item.itemType,
|
|
160056
|
+
...ord !== void 0 ? { ord } : {},
|
|
160057
|
+
offset: 0
|
|
160058
|
+
};
|
|
160059
|
+
if (!perItem.started && item.startedVersion !== void 0) {
|
|
160060
|
+
this.publishV3Coalesced(turn, {
|
|
160061
|
+
...base,
|
|
160062
|
+
operation: "started",
|
|
160063
|
+
itemVersion: item.startedVersion,
|
|
160064
|
+
payload: item.payload
|
|
160065
|
+
});
|
|
160066
|
+
perItem.started = true;
|
|
160067
|
+
}
|
|
160068
|
+
turn.v3Items.set(item.itemId, perItem);
|
|
160069
|
+
this.publishV3Coalesced(turn, {
|
|
160070
|
+
...base,
|
|
160071
|
+
operation: item.status === "failed" ? "failed" : "completed",
|
|
160072
|
+
itemVersion: item.version,
|
|
160073
|
+
payload: item.payload
|
|
160074
|
+
});
|
|
160075
|
+
}
|
|
159984
160076
|
assignV3Seq(turn, frame) {
|
|
159985
160077
|
return { ...frame, epoch: turn.liveEpoch, seq: ++turn.v3Seq };
|
|
159986
160078
|
}
|
|
@@ -160118,11 +160210,17 @@ var init_live_chat = __esm({
|
|
|
160118
160210
|
const frame = {
|
|
160119
160211
|
protocolVersion: 3,
|
|
160120
160212
|
streamId: turn.liveStreamId,
|
|
160121
|
-
|
|
160213
|
+
// 账本轮次 id,与 item 帧同一套(#1249 把 emitV3 改成了 wireTurnId,收尾帧当时漏了)。
|
|
160214
|
+
// 客户端此前把收尾帧整条丢弃(`applyFrame`:「不建 item,仅推进 frameSeq」),所以这个
|
|
160215
|
+
// 号是对是错都看不出来;相位一挂上去它就得对——否则相位会记到一个桶里根本不存在的 turnId 上。
|
|
160216
|
+
turnId: this.wireTurnId(turn),
|
|
160122
160217
|
itemId: `oasis-turn:${turn.liveEpoch}:${status}`,
|
|
160123
160218
|
itemType: "control",
|
|
160124
160219
|
operation,
|
|
160125
160220
|
itemVersion: 0,
|
|
160221
|
+
// 相位被携带,不被推导(ADR-0510 D6):这一轮到底是成了还是败了,服务端此刻知道,
|
|
160222
|
+
// 客户端替换即可,不用再去把一轮里每条 item 的 status 取或来猜。
|
|
160223
|
+
phase: status === "done" ? "succeeded" : "failed",
|
|
160126
160224
|
epoch: turn.liveEpoch,
|
|
160127
160225
|
seq: ++turn.v3Seq
|
|
160128
160226
|
};
|
|
@@ -162356,14 +162454,25 @@ var init_planner = __esm({
|
|
|
162356
162454
|
function systemActorFallback(id) {
|
|
162357
162455
|
return isSystemActorId(id) ? { name: SYSTEM_ACTOR_DISPLAY_NAME, avatar: SYSTEM_AVATAR_REF } : void 0;
|
|
162358
162456
|
}
|
|
162457
|
+
function defaultAvatarRefOf(id, kind) {
|
|
162458
|
+
if (kind === "system" || isSystemActorId(id)) return SYSTEM_AVATAR_REF;
|
|
162459
|
+
if (kind === "human" || isHumanActorId(id)) return defaultHumanAvatarRef(id);
|
|
162460
|
+
return void 0;
|
|
162461
|
+
}
|
|
162359
162462
|
function actorRefFieldsOf(a) {
|
|
162360
|
-
const avatar = a.avatar ?? (a.kind
|
|
162463
|
+
const avatar = a.avatar ?? defaultAvatarRefOf(a.id, a.kind);
|
|
162361
162464
|
return {
|
|
162362
162465
|
name: a.name,
|
|
162363
162466
|
...a.roles[0] ? { role: a.roles[0] } : {},
|
|
162364
162467
|
...avatar ? { avatar } : {}
|
|
162365
162468
|
};
|
|
162366
162469
|
}
|
|
162470
|
+
function actorRefFallbackOf(id) {
|
|
162471
|
+
const system = systemActorFallback(id);
|
|
162472
|
+
if (system) return system;
|
|
162473
|
+
const avatar = defaultAvatarRefOf(id);
|
|
162474
|
+
return avatar ? { avatar } : void 0;
|
|
162475
|
+
}
|
|
162367
162476
|
function overviewRootIdOf(arts) {
|
|
162368
162477
|
const roots = arts.filter((a) => a.inputs.length === 0);
|
|
162369
162478
|
if (roots.length <= 1) return roots[0]?.id;
|
|
@@ -198960,8 +199069,11 @@ var init_github = __esm({
|
|
|
198960
199069
|
sessionEnv.set("GH_CONFIG_DIR", configDir);
|
|
198961
199070
|
const appSlug = credentials["app_slug"];
|
|
198962
199071
|
if (appSlug) sessionEnv.set("GH_APP_SLUG", appSlug);
|
|
198963
|
-
const
|
|
198964
|
-
const
|
|
199072
|
+
const rawBotEmail = credentials["app_bot_email"] ?? "";
|
|
199073
|
+
const rawBotName = credentials["app_bot_name"] ?? "";
|
|
199074
|
+
const staleBot = Boolean(appSlug && rawBotEmail && !rawBotEmail.includes(`+${appSlug}[bot]@`));
|
|
199075
|
+
const botEmail = staleBot ? "" : rawBotEmail;
|
|
199076
|
+
const botName = staleBot ? appSlug ? `${appSlug}[bot]` : "" : rawBotName;
|
|
198965
199077
|
const signName = botName || name;
|
|
198966
199078
|
sessionEnv.set("GIT_AUTHOR_NAME", signName);
|
|
198967
199079
|
sessionEnv.set("GIT_AUTHOR_EMAIL", botEmail);
|
|
@@ -201251,10 +201363,8 @@ async function adoptGithubApp(store, args, deps = {}) {
|
|
|
201251
201363
|
await put3("GITHUB_APP_TOKEN_EXPIRES_AT", String(minted.expiresAtMs), false);
|
|
201252
201364
|
if (args.slug) {
|
|
201253
201365
|
const bot = await fetchAppBotIdentity(args.slug, { token: minted.token }, deps);
|
|
201254
|
-
|
|
201255
|
-
|
|
201256
|
-
await put3("GITHUB_APP_BOT_NAME", bot.login, false);
|
|
201257
|
-
}
|
|
201366
|
+
await put3("GITHUB_APP_BOT_EMAIL", bot?.email ?? "", false);
|
|
201367
|
+
await put3("GITHUB_APP_BOT_NAME", bot?.login ?? `${args.slug}[bot]`, false);
|
|
201258
201368
|
}
|
|
201259
201369
|
return {
|
|
201260
201370
|
account: args.slug ? `${args.slug}[bot]` : args.appId,
|
|
@@ -201922,7 +202032,7 @@ async function draftActorResolver(registry2) {
|
|
|
201922
202032
|
const byId = new Map(actors.map((a) => [a.id, a]));
|
|
201923
202033
|
return (id) => {
|
|
201924
202034
|
const a = byId.get(id);
|
|
201925
|
-
return a ? actorRefFieldsOf(a) :
|
|
202035
|
+
return a ? actorRefFieldsOf(a) : actorRefFallbackOf(id);
|
|
201926
202036
|
};
|
|
201927
202037
|
}
|
|
201928
202038
|
async function draftRoleStaffingResolver(registry2) {
|
|
@@ -203154,18 +203264,27 @@ async function runView(kernel, oplog, engineStore, blobs, name, artifactId, para
|
|
|
203154
203264
|
);
|
|
203155
203265
|
const pendingGate = pendingConcludeAttempt(model, id);
|
|
203156
203266
|
const rejectionByRevision = /* @__PURE__ */ new Map();
|
|
203267
|
+
const contentEditedByRevision = /* @__PURE__ */ new Map();
|
|
203157
203268
|
if (engineStore) {
|
|
203158
203269
|
const snap = await engineStore.transaction((tx) => tx.loadWorkorder(artifact.workspace));
|
|
203159
203270
|
for (const w2 of snap?.works ?? []) {
|
|
203160
203271
|
if (w2.rejectedReason) rejectionByRevision.set(w2.id, w2.rejectedReason);
|
|
203161
203272
|
}
|
|
203273
|
+
for (const a of snap?.artifacts ?? []) {
|
|
203274
|
+
const at = a.contentEditedAt;
|
|
203275
|
+
if (!at) continue;
|
|
203276
|
+
const workId = a.workId;
|
|
203277
|
+
const prev = contentEditedByRevision.get(workId);
|
|
203278
|
+
if (!prev || at > prev) contentEditedByRevision.set(workId, at);
|
|
203279
|
+
}
|
|
203162
203280
|
}
|
|
203163
203281
|
const revisionViews = revisions.map((rev) => ({
|
|
203164
203282
|
...rev,
|
|
203165
203283
|
stage: revisionStage(artifact, rev),
|
|
203166
203284
|
approvals: (model.reviews.get(rev.id) ?? []).filter((v2) => v2.verdict === "approve").length,
|
|
203167
203285
|
required: pendingGate?.head === rev.id ? pendingGate.gate?.quorum ?? 0 : 0,
|
|
203168
|
-
...rejectionByRevision.has(rev.id) ? { rejection: { reason: rejectionByRevision.get(rev.id) } } : {}
|
|
203286
|
+
...rejectionByRevision.has(rev.id) ? { rejection: { reason: rejectionByRevision.get(rev.id) } } : {},
|
|
203287
|
+
...contentEditedByRevision.has(rev.id) ? { contentEditedAt: contentEditedByRevision.get(rev.id) } : {}
|
|
203169
203288
|
}));
|
|
203170
203289
|
return {
|
|
203171
203290
|
artifact,
|
|
@@ -206250,10 +206369,26 @@ ${composed}`;
|
|
|
206250
206369
|
...chatExit?.reason ? { reason: chatExit.reason } : {},
|
|
206251
206370
|
...chatExit?.detail ? { detail: chatExit.detail } : {}
|
|
206252
206371
|
});
|
|
206253
|
-
itemLedger.recordError(errText2, {
|
|
206372
|
+
const errorOutcome = itemLedger.recordError(errText2, {
|
|
206254
206373
|
...chatExit?.reason ? { reason: chatExit.reason } : {},
|
|
206255
206374
|
...chatExit?.detail ? { detail: chatExit.detail } : {}
|
|
206256
206375
|
});
|
|
206376
|
+
if (live) {
|
|
206377
|
+
live.publishServerItem({
|
|
206378
|
+
itemId: errorOutcome.itemId,
|
|
206379
|
+
itemType: "control",
|
|
206380
|
+
startedVersion: errorOutcome.startedVersion,
|
|
206381
|
+
version: errorOutcome.version,
|
|
206382
|
+
ord: errorOutcome.ord,
|
|
206383
|
+
status: "failed",
|
|
206384
|
+
payload: {
|
|
206385
|
+
code: "error",
|
|
206386
|
+
note: errText2,
|
|
206387
|
+
...chatExit?.reason ? { reason: chatExit.reason } : {},
|
|
206388
|
+
...chatExit?.detail ? { detail: chatExit.detail } : {}
|
|
206389
|
+
}
|
|
206390
|
+
});
|
|
206391
|
+
}
|
|
206257
206392
|
}
|
|
206258
206393
|
finished = true;
|
|
206259
206394
|
clearInterval(ckptTimer);
|
|
@@ -206866,7 +207001,19 @@ ${composed}`;
|
|
|
206866
207001
|
fromInstallation: Boolean(installationId),
|
|
206867
207002
|
/** App 申请了、但这次安装还没被授予——多半是权限升级还等着组织 owner 批准。 */
|
|
206868
207003
|
awaitingApproval: missingAppPermissions(effective).filter((m2) => meta.permissions[m2.key]).map((m2) => m2.key),
|
|
206869
|
-
|
|
207004
|
+
/**
|
|
207005
|
+
* 这次安装**真正被授予**的全部权限,一律用 GitHub 的英文原名 + 级别
|
|
207006
|
+
* (2026-09-10 发起人:「所有权限都用英文原文,不区分是不是八项权限」)。
|
|
207007
|
+
*
|
|
207008
|
+
* 此前这里是 `GITHUB_APP_PERMISSION_LABELS.filter(...)` —— 那张表只有本仓必需的
|
|
207009
|
+
* 八项中文标签,于是这一格实际上是「必备清单打勾」,人在 GitHub 上另外勾的一个都不显示,
|
|
207010
|
+
* 看上去像没生效。中间还试过「八项中文 + 额外项英文」两段式,也被否了:
|
|
207011
|
+
* 同一个列表两套命名,反而要人先分辨「这条为什么是中文那条为什么是英文」。
|
|
207012
|
+
*
|
|
207013
|
+
* **不编中文名**:GitHub 的权限键是开放集合且还在增,写死对照表迟早漏;
|
|
207014
|
+
* 而英文原名与 GitHub 设置页上的字**逐字一致**,对着改权限时不用做心理翻译。
|
|
207015
|
+
*/
|
|
207016
|
+
granted: Object.entries(effective).filter(([, level]) => Boolean(level)).map(([key, level]) => ({ key, level: String(level) })).sort((a, b2) => a.key.localeCompare(b2.key)),
|
|
206870
207017
|
missing,
|
|
206871
207018
|
settingsUrl: meta.slug ? appPermissionSettingsUrl(meta) : null
|
|
206872
207019
|
}));
|
|
@@ -206942,7 +207089,8 @@ ${composed}`;
|
|
|
206942
207089
|
const perms = pending.permissions ?? {};
|
|
206943
207090
|
const missing = missingAppPermissions(perms);
|
|
206944
207091
|
return {
|
|
206945
|
-
|
|
207092
|
+
// 与 app/permissions 那一处同口径:英文原名 + 级别,不分是不是本仓必需的八项。
|
|
207093
|
+
granted: Object.entries(perms).filter(([, level]) => Boolean(level)).map(([key, level]) => ({ key, level: String(level) })).sort((a, b2) => a.key.localeCompare(b2.key)),
|
|
206946
207094
|
missing,
|
|
206947
207095
|
...pending.slug ? { settingsUrl: appPermissionSettingsUrl({
|
|
206948
207096
|
slug: pending.slug,
|
|
@@ -206985,10 +207133,8 @@ ${composed}`;
|
|
|
206985
207133
|
}
|
|
206986
207134
|
if (pending.slug) {
|
|
206987
207135
|
const bot = await fetchAppBotIdentity(pending.slug, { token: minted.token });
|
|
206988
|
-
|
|
206989
|
-
|
|
206990
|
-
await put3("GITHUB_APP_BOT_NAME", bot.login, false);
|
|
206991
|
-
}
|
|
207136
|
+
await put3("GITHUB_APP_BOT_EMAIL", bot?.email ?? "", false);
|
|
207137
|
+
await put3("GITHUB_APP_BOT_NAME", bot?.login ?? `${pending.slug}[bot]`, false);
|
|
206992
207138
|
}
|
|
206993
207139
|
pending.done = true;
|
|
206994
207140
|
delete pending.privateKeyPem;
|
|
@@ -210642,6 +210788,10 @@ function buildRevisionDag(revisions) {
|
|
|
210642
210788
|
}))
|
|
210643
210789
|
};
|
|
210644
210790
|
}
|
|
210791
|
+
function laterOf(a, b2) {
|
|
210792
|
+
if (!b2) return a;
|
|
210793
|
+
return b2 > a ? b2 : a;
|
|
210794
|
+
}
|
|
210645
210795
|
var DEFAULT_AGENT_ACTOR, MemoryProjectStateStore, MemoryArtifactStateStore, ProjectsService;
|
|
210646
210796
|
var init_service4 = __esm({
|
|
210647
210797
|
"../server/src/domains/projects/service.ts"() {
|
|
@@ -211498,7 +211648,11 @@ var init_service4 = __esm({
|
|
|
211498
211648
|
if (arts.length === 0) continue;
|
|
211499
211649
|
const work = worksById.get(acceptedId);
|
|
211500
211650
|
const folderPath = `wo:${workOrderId}/node:${node2.id}`;
|
|
211501
|
-
const
|
|
211651
|
+
const workUpdatedAt = work?.acceptedAt ?? work?.endedAt ?? work?.lastActivityAt ?? work?.createdAt ?? node2.updatedAt;
|
|
211652
|
+
const folderUpdatedAt = arts.reduce(
|
|
211653
|
+
(acc, a) => laterOf(acc, a.contentEditedAt),
|
|
211654
|
+
workUpdatedAt
|
|
211655
|
+
);
|
|
211502
211656
|
const creatorId = work?.assigneeActorId ?? null;
|
|
211503
211657
|
const creatorKind = creatorId ? creatorId.startsWith("actor:human:") ? "human" : "agent" : null;
|
|
211504
211658
|
const creatorName = creatorId ? this.resolveActorName(creatorId) : null;
|
|
@@ -211522,7 +211676,7 @@ var init_service4 = __esm({
|
|
|
211522
211676
|
for (const a of sortedArts) {
|
|
211523
211677
|
const artifactId = node2.id;
|
|
211524
211678
|
const artifactType = a.type ?? node2.type;
|
|
211525
|
-
const rowUpdatedAt =
|
|
211679
|
+
const rowUpdatedAt = laterOf(workUpdatedAt, a.contentEditedAt);
|
|
211526
211680
|
if (a.contentKind === "manifest" && a.contentRef?.trim()) {
|
|
211527
211681
|
const content3 = await this.readContentRefText(a.contentRef);
|
|
211528
211682
|
const files = content3 !== null ? tryParseManifestFiles(content3) : void 0;
|
|
@@ -211918,6 +212072,7 @@ var init_service4 = __esm({
|
|
|
211918
212072
|
const arts = snap.artifacts.filter((a) => a.workId === acceptedId);
|
|
211919
212073
|
if (arts.length === 0) continue;
|
|
211920
212074
|
const work = worksById.get(acceptedId);
|
|
212075
|
+
const workUpdatedAt = work?.acceptedAt ?? work?.endedAt ?? work?.lastActivityAt ?? work?.createdAt ?? node2.updatedAt;
|
|
211921
212076
|
const documents = arts.slice().sort((a, b2) => a.ordinal - b2.ordinal).map((a) => ({
|
|
211922
212077
|
// collab 层 artifactId === engine nodeId(见 bridge-readmodel.ts:215)——预览端读的
|
|
211923
212078
|
// useArtifactView 也走这个 id。
|
|
@@ -211929,7 +212084,9 @@ var init_service4 = __esm({
|
|
|
211929
212084
|
contentRef: a.contentRef,
|
|
211930
212085
|
title: node2.title || a.name,
|
|
211931
212086
|
// 节点标题优先;否则用 engine artifact 的 "output" 兜底
|
|
211932
|
-
contentKind: engineContentKind(a.contentKind)
|
|
212087
|
+
contentKind: engineContentKind(a.contentKind),
|
|
212088
|
+
// 文档级时间:被人在预览里就地改写过就跳到改写时刻,否则就是这轮执行的时刻。
|
|
212089
|
+
updatedAt: laterOf(workUpdatedAt, a.contentEditedAt)
|
|
211933
212090
|
}));
|
|
211934
212091
|
out.push({
|
|
211935
212092
|
outputId: `engine:${acceptedId}`,
|
|
@@ -211940,7 +212097,9 @@ var init_service4 = __esm({
|
|
|
211940
212097
|
documents,
|
|
211941
212098
|
payload: {},
|
|
211942
212099
|
createdAt: work?.createdAt ?? node2.createdAt,
|
|
211943
|
-
|
|
212100
|
+
// 节点(= 文件夹行)的时间取「这轮执行的时刻」与「其下任一文档最后被就地改写的时刻」里更晚的那个:
|
|
212101
|
+
// 改过某个文件之后,文件夹不能还显示比自己的孩子更早的时间。
|
|
212102
|
+
updatedAt: documents.reduce((acc, d) => laterOf(acc, d.updatedAt), workUpdatedAt)
|
|
211944
212103
|
});
|
|
211945
212104
|
}
|
|
211946
212105
|
return out;
|
|
@@ -223012,7 +223171,8 @@ function toApiNodeOutputDocument(doc) {
|
|
|
223012
223171
|
summary: doc.summary,
|
|
223013
223172
|
...doc.contentKind !== void 0 ? { content_kind: doc.contentKind } : {},
|
|
223014
223173
|
...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 } : {} })) } : {}
|
|
223174
|
+
...doc.files !== void 0 ? { files: doc.files.map((f2) => ({ path: f2.path, ...f2.size !== void 0 ? { size: f2.size } : {} })) } : {},
|
|
223175
|
+
...doc.updatedAt !== void 0 ? { updated_at: doc.updatedAt } : {}
|
|
223016
223176
|
};
|
|
223017
223177
|
}
|
|
223018
223178
|
function toApiNodeOutputValidation(validation) {
|
|
@@ -229369,7 +229529,7 @@ async function buildResolver(registry2) {
|
|
|
229369
229529
|
const byId = new Map(actors.map((a) => [a.id, a]));
|
|
229370
229530
|
return (id) => {
|
|
229371
229531
|
const a = byId.get(id);
|
|
229372
|
-
return a ? actorRefFieldsOf(a) :
|
|
229532
|
+
return a ? actorRefFieldsOf(a) : actorRefFallbackOf(id);
|
|
229373
229533
|
};
|
|
229374
229534
|
}
|
|
229375
229535
|
async function buildProjectResolver(artifacts) {
|
|
@@ -233772,12 +233932,20 @@ function createChatSessionsDomain(opts) {
|
|
|
233772
233932
|
const limitRaw = Number(req.query?.get("limit") ?? "0") || 0;
|
|
233773
233933
|
const limit = limitRaw > 0 ? Math.min(limitRaw, MAX_SNAPSHOT_ITEMS) : DEFAULT_SNAPSHOT_ITEMS;
|
|
233774
233934
|
const cursor = opts.liveChat?.cursorFor(chatSessionId) ?? null;
|
|
233775
|
-
|
|
233776
|
-
|
|
233777
|
-
|
|
233778
|
-
|
|
233779
|
-
|
|
233780
|
-
|
|
233935
|
+
const turns = await turnsFor(req);
|
|
233936
|
+
const activeTurnRow = turns ? await turns.activeTurn(chatSessionId).catch(() => null) : null;
|
|
233937
|
+
const turnId = cursor?.turnId ?? activeTurnRow?.id ?? null;
|
|
233938
|
+
const activeTurn = (() => {
|
|
233939
|
+
if (!activeTurnRow) return null;
|
|
233940
|
+
const settled = activeTurnRow.status !== "reserved" && activeTurnRow.status !== "running";
|
|
233941
|
+
if (settled) return { turnId: activeTurnRow.id, phase: activeTurnRow.status };
|
|
233942
|
+
if (opts.liveChat?.isRunning(chatSessionId)) {
|
|
233943
|
+
return { turnId: activeTurnRow.id, phase: "running" };
|
|
233944
|
+
}
|
|
233945
|
+
const startedAt = Date.parse(activeTurnRow.startedAt ?? activeTurnRow.reservedAt);
|
|
233946
|
+
const stale = Number.isFinite(startedAt) && Date.now() - startedAt >= LINK_WINDOWS.chatTurnPhaseStaleAfterMs;
|
|
233947
|
+
return { turnId: activeTurnRow.id, phase: stale ? "interrupted" : "running" };
|
|
233948
|
+
})();
|
|
233781
233949
|
const snapshotEntries = [];
|
|
233782
233950
|
let nextSinceVersion = sinceVersion;
|
|
233783
233951
|
let hasMoreBefore = false;
|
|
@@ -233809,6 +233977,7 @@ function createChatSessionsDomain(opts) {
|
|
|
233809
233977
|
nextSinceVersion,
|
|
233810
233978
|
frameCursor: cursor ? { epoch: cursor.epoch, seq: cursor.seq } : { epoch: 0, seq: 0 },
|
|
233811
233979
|
canAppend: cursor?.canAppend ?? false,
|
|
233980
|
+
activeTurn,
|
|
233812
233981
|
oldestOrd,
|
|
233813
233982
|
hasMoreBefore
|
|
233814
233983
|
};
|
|
@@ -270671,7 +270840,7 @@ function shimScript() {
|
|
|
270671
270840
|
}
|
|
270672
270841
|
|
|
270673
270842
|
// src/index.ts
|
|
270674
|
-
var PKG_VERSION = true ? "2.2.
|
|
270843
|
+
var PKG_VERSION = true ? "2.2.10" : "dev";
|
|
270675
270844
|
var LOCAL_BIN = localBin();
|
|
270676
270845
|
var NPM_PREFIX = npmPrefix();
|
|
270677
270846
|
var INSTANCE = DEFAULT_INSTANCE;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oasis_test_v2",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.10",
|
|
4
4
|
"description": "Oasis node daemon + CLI — background daemon, auto-start, full server CLI",
|
|
5
5
|
"bin": {
|
|
6
6
|
"oasis": "./dist/index.js"
|
|
@@ -26,6 +26,6 @@
|
|
|
26
26
|
"node": ">=20"
|
|
27
27
|
},
|
|
28
28
|
"oasisRelease": {
|
|
29
|
-
"sourceHead": "
|
|
29
|
+
"sourceHead": "3eac5f26307c5d0cf750e4a7edc6a5f60bdbe63d"
|
|
30
30
|
}
|
|
31
31
|
}
|