oasis_test 0.1.36 → 0.1.38
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 +485 -96
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -184,6 +184,7 @@ function fold(model, op) {
|
|
|
184
184
|
anchor: { target: op.target, ...p2.anchorSpan !== void 0 ? { span: p2.anchorSpan } : {} },
|
|
185
185
|
author: op.actor,
|
|
186
186
|
body: p2.body,
|
|
187
|
+
...p2.attachments !== void 0 && p2.attachments.length > 0 ? { attachments: p2.attachments.map((a) => ({ ...a })) } : {},
|
|
187
188
|
state: "open",
|
|
188
189
|
kind: p2.kind ?? defaultKind,
|
|
189
190
|
stagedResolution: null,
|
|
@@ -1858,17 +1859,17 @@ var init_kernel = __esm({
|
|
|
1858
1859
|
const artifact = this.artifactOrThrow(args.artifactId);
|
|
1859
1860
|
this.enforceAcl("propose", this.schema.get(artifact.type), args.actor, artifact.owner);
|
|
1860
1861
|
const proposingPart = args.part ?? null;
|
|
1861
|
-
if (unresolvedGapsOf(this.model, args.artifactId).some((g2) => g2.part === proposingPart)) {
|
|
1862
|
+
if (proposingPart !== null && unresolvedGapsOf(this.model, args.artifactId).some((g2) => g2.part === proposingPart)) {
|
|
1862
1863
|
throw new KernelError(
|
|
1863
|
-
|
|
1864
|
+
`\u5931\u8D25\uFF1A\u8FD9\u4E2A\u90E8\u4EF6\uFF08${args.part}\uFF09\u8FD8\u6709\u672A\u89E3\u51B3\u7684 gap\uFF0C\u4E0D\u80FD\u63D0\u4EA4\u3002\u53EF\u4EE5\u76F4\u63A5\u6536\u5DE5\u2014\u2014gap \u6709\u7ED3\u679C\u540E\u7CFB\u7EDF\u4F1A\u91CD\u65B0\u5524\u8D77\u4F60\u7EED\u505A\uFF0C\u4E0D\u7528\u7B49\u5728\u8FD9\u91CC\u3002`
|
|
1864
1865
|
);
|
|
1865
1866
|
}
|
|
1866
|
-
const proposingHasOpenBackpressure = [...this.model.annotations.values()].some(
|
|
1867
|
+
const proposingHasOpenBackpressure = proposingPart !== null && [...this.model.annotations.values()].some(
|
|
1867
1868
|
(a) => a.raisedFrom === args.artifactId && a.state === "open" && a.raisedFromPart === proposingPart
|
|
1868
1869
|
);
|
|
1869
1870
|
if (proposingHasOpenBackpressure) {
|
|
1870
1871
|
throw new KernelError(
|
|
1871
|
-
|
|
1872
|
+
`\u5931\u8D25\uFF1A\u8FD9\u4E2A\u90E8\u4EF6\uFF08${args.part}\uFF09\u8FD8\u6709\u672A\u7B54\u590D\u7684\u56DE\u538B\uFF08\u4F60\u5411\u4E0A\u6E38\u63D0\u7684\u7591\u95EE\u8FD8\u6CA1\u56DE\uFF09\uFF0C\u4E0D\u80FD\u63D0\u4EA4\u3002\u53EF\u4EE5\u76F4\u63A5\u6536\u5DE5\u2014\u2014\u4E0A\u6E38\u7B54\u590D\u540E\u7CFB\u7EDF\u4F1A\u91CD\u65B0\u5524\u8D77\u4F60\u7EED\u505A\u3002`
|
|
1872
1873
|
);
|
|
1873
1874
|
}
|
|
1874
1875
|
const base = args.base === void 0 ? artifact.currentRev : args.base;
|
|
@@ -1892,6 +1893,14 @@ var init_kernel = __esm({
|
|
|
1892
1893
|
`part "${args.part}" \u4E0D\u662F ${args.artifactId} \u58F0\u660E\u7684\u90E8\u4EF6\uFF08\u6709\uFF1A${(artifact.parts ?? []).map((p2) => p2.name).join("\u3001") || "\u65E0"}\uFF09`
|
|
1893
1894
|
);
|
|
1894
1895
|
}
|
|
1896
|
+
const alreadyProduced = revisionsOf(this.model, args.artifactId).some(
|
|
1897
|
+
(r) => r.part === args.part && r.state !== "rejected"
|
|
1898
|
+
);
|
|
1899
|
+
if (alreadyProduced) {
|
|
1900
|
+
throw new KernelError(
|
|
1901
|
+
`\u5931\u8D25\uFF1A\u8FD9\u4E2A\u90E8\u4EF6\uFF08${args.part}\uFF09\u5DF2\u7ECF\u63D0\u4EA4\u8FC7\u4E00\u7248\u4E86\u2014\u2014\u4E00\u90E8\u4EF6\u4E00\u7248\u3001\u4EA4\u4E86\u5373\u7EC8\u7ED3\uFF0C\u4E0D\u80FD\u518D\u4EA4\u3002\u53EF\u4EE5\u76F4\u63A5\u6536\u5DE5\u3002`
|
|
1902
|
+
);
|
|
1903
|
+
}
|
|
1895
1904
|
}
|
|
1896
1905
|
if (args.rebasedFrom !== void 0) {
|
|
1897
1906
|
const old = this.model.revisions.get(args.rebasedFrom);
|
|
@@ -2081,6 +2090,12 @@ var init_kernel = __esm({
|
|
|
2081
2090
|
`\u8FD8\u6709\u672A\u7B54\u590D\u7684\u56DE\u538B annotation\uFF08\u81EA\u63D0 ${openSelfAnno.length} \u6761\uFF09\uFF0C\u4E0D\u80FD conclude\u2014\u2014\u7B49\u4E0A\u6E38\u56DE\u5E94\u3001\u90A3\u6761 revision \u7EED\u5B8C\u518D\u6536\u53E3\uFF08\u7EBF2 \u2462\uFF09`
|
|
2082
2091
|
);
|
|
2083
2092
|
}
|
|
2093
|
+
const openReceived = openBackpressureOf(this.model, args.artifactId);
|
|
2094
|
+
if (openReceived.length > 0) {
|
|
2095
|
+
throw new KernelError(
|
|
2096
|
+
`\u5931\u8D25\uFF1A\u8FD8\u6709 ${openReceived.length} \u6761\u4E0B\u6E38\u56DE\u538B\u6CA1\u7B54\u590D\uFF08\u4E0B\u6E38\u660E\u786E\u6253\u56DE\u4E86\u5F53\u524D\u7248\u672C\uFF09\uFF0C\u4E0D\u80FD conclude\u2014\u2014\u5148\u6309\u56DE\u538B\u6539\u5185\u5BB9\u3001resolve \u56DE\u5E94\u4E0B\u6E38\uFF0C\u518D\u6536\u53E3\uFF08\u4E24\u5C42\u9501\u70B9 \xA76.3\uFF09\u3002`
|
|
2097
|
+
);
|
|
2098
|
+
}
|
|
2084
2099
|
const unproduced = (artifact.parts ?? []).filter(
|
|
2085
2100
|
(p2) => !revisionsOf(this.model, args.artifactId).some((r) => r.part === p2.name && r.state !== "rejected")
|
|
2086
2101
|
);
|
|
@@ -2262,6 +2277,7 @@ var init_kernel = __esm({
|
|
|
2262
2277
|
const payload = {
|
|
2263
2278
|
annotationId,
|
|
2264
2279
|
body: args.body,
|
|
2280
|
+
...args.attachments !== void 0 && args.attachments.length > 0 ? { attachments: args.attachments } : {},
|
|
2265
2281
|
// 信箱语义(v3.1 ①a):命令层一律盖章——新批注都是"寄给 owner 的信";存量旧 op 无此字段不参与唤醒
|
|
2266
2282
|
mail: true,
|
|
2267
2283
|
...args.span !== void 0 ? { anchorSpan: args.span } : {},
|
|
@@ -2577,7 +2593,7 @@ var init_kernel = __esm({
|
|
|
2577
2593
|
* 派生里程碑带 forced(by, reason),消费者可见。
|
|
2578
2594
|
*/
|
|
2579
2595
|
async forceConclude(args) {
|
|
2580
|
-
this.bounceHeadMovers(args.artifactId);
|
|
2596
|
+
this.bounceHeadMovers(args.artifactId, { allowPendingGate: true });
|
|
2581
2597
|
this.ensureActive(args.artifactId);
|
|
2582
2598
|
const artifact = this.artifactOrThrow(args.artifactId);
|
|
2583
2599
|
if (artifact.currentRev === null) {
|
|
@@ -3029,10 +3045,10 @@ ${res.candidates.map((c) => ` - ${c.type}:${c.title ?? "(\u65E0title)"} \u2192
|
|
|
3029
3045
|
return this.blobs;
|
|
3030
3046
|
}
|
|
3031
3047
|
/** 推进 head / 改变队列结局的命令:两档冻结都拦;收尾 gate 待审中 = 派生的 merge 档冻结(§5.7)。 */
|
|
3032
|
-
bounceHeadMovers(artifactId) {
|
|
3048
|
+
bounceHeadMovers(artifactId, opts) {
|
|
3033
3049
|
const scope = this.frozen.get(artifactId);
|
|
3034
3050
|
if (scope) throw new FrozenError(artifactId, scope);
|
|
3035
|
-
if (pendingConcludeAttempt(this.model, artifactId)) throw new FrozenError(artifactId, "merge");
|
|
3051
|
+
if (!opts?.allowPendingGate && pendingConcludeAttempt(this.model, artifactId)) throw new FrozenError(artifactId, "merge");
|
|
3036
3052
|
}
|
|
3037
3053
|
/** propose(emit)类:只有 write 档(§11.7 干预)才拦——merge 档下照常入队。 */
|
|
3038
3054
|
bounceWrites(artifactId) {
|
|
@@ -3757,31 +3773,6 @@ function partHasOpenBackpressure(model, id, part) {
|
|
|
3757
3773
|
function partProduced(model, artifactId, part) {
|
|
3758
3774
|
return revisionsOf(model, artifactId).some((r) => r.part === part && r.state !== "rejected");
|
|
3759
3775
|
}
|
|
3760
|
-
function partLastRevSeq(model, artifactId, part) {
|
|
3761
|
-
let max = null;
|
|
3762
|
-
for (const r of revisionsOf(model, artifactId)) {
|
|
3763
|
-
if (r.part !== part || r.state === "rejected") continue;
|
|
3764
|
-
const seq = model.proposeSeq.get(r.id);
|
|
3765
|
-
if (seq !== void 0 && (max === null || seq > max)) max = seq;
|
|
3766
|
-
}
|
|
3767
|
-
return max;
|
|
3768
|
-
}
|
|
3769
|
-
function partReworkNeeded(model, artifactId, part) {
|
|
3770
|
-
const mySeq = partLastRevSeq(model, artifactId, part);
|
|
3771
|
-
if (mySeq === null) return false;
|
|
3772
|
-
for (const a of pendingBackpressureResponsesOf(model, artifactId)) {
|
|
3773
|
-
if (a.raisedFromPart === part) return true;
|
|
3774
|
-
}
|
|
3775
|
-
for (const g2 of model.gaps.get(artifactId) ?? []) {
|
|
3776
|
-
if (g2.part === part && g2.resolved && g2.resolvedAtSeq !== void 0 && g2.resolvedAtSeq > mySeq) return true;
|
|
3777
|
-
}
|
|
3778
|
-
const spec = (model.artifacts.get(artifactId)?.parts ?? []).find((p2) => p2.name === part);
|
|
3779
|
-
for (const dep of spec?.deps ?? []) {
|
|
3780
|
-
const depSeq = partLastRevSeq(model, artifactId, dep);
|
|
3781
|
-
if (depSeq !== null && depSeq > mySeq) return true;
|
|
3782
|
-
}
|
|
3783
|
-
return false;
|
|
3784
|
-
}
|
|
3785
3776
|
function computeProduceJobs(model) {
|
|
3786
3777
|
const out = [];
|
|
3787
3778
|
for (const artifact of model.artifacts.values()) {
|
|
@@ -3801,7 +3792,7 @@ function computeProduceJobs(model) {
|
|
|
3801
3792
|
if (partHasOpenBackpressure(model, artifact.id, null)) continue;
|
|
3802
3793
|
for (const part of parts) {
|
|
3803
3794
|
if (gaps.some((g2) => g2.part === part.name) || partHasOpenBackpressure(model, artifact.id, part.name)) continue;
|
|
3804
|
-
if (partProduced(model, artifact.id, part.name)
|
|
3795
|
+
if (partProduced(model, artifact.id, part.name)) continue;
|
|
3805
3796
|
const deps = part.deps ?? [];
|
|
3806
3797
|
if (!deps.every((d) => partProduced(model, artifact.id, d))) continue;
|
|
3807
3798
|
out.push({ artifactId: artifact.id, part: part.name });
|
|
@@ -3817,7 +3808,6 @@ function computeIntegrateJobs(model) {
|
|
|
3817
3808
|
if (!isProducible(model, artifact.id)) continue;
|
|
3818
3809
|
if (openBackpressureOf(model, artifact.id).length > 0) continue;
|
|
3819
3810
|
if (!parts.every((p2) => partProduced(model, artifact.id, p2.name))) continue;
|
|
3820
|
-
if (parts.some((p2) => partReworkNeeded(model, artifact.id, p2.name))) continue;
|
|
3821
3811
|
if (queueOf(model, artifact.id).length === 0) continue;
|
|
3822
3812
|
out.push(artifact.id);
|
|
3823
3813
|
}
|
|
@@ -21687,6 +21677,121 @@ var init_router = __esm({
|
|
|
21687
21677
|
}
|
|
21688
21678
|
});
|
|
21689
21679
|
|
|
21680
|
+
// ../server/src/live-chat.ts
|
|
21681
|
+
var DEFAULT_BUFFER_MAX, DEFAULT_GRACE_MS, LiveChatRegistry;
|
|
21682
|
+
var init_live_chat = __esm({
|
|
21683
|
+
"../server/src/live-chat.ts"() {
|
|
21684
|
+
"use strict";
|
|
21685
|
+
DEFAULT_BUFFER_MAX = 4e3;
|
|
21686
|
+
DEFAULT_GRACE_MS = 6e4;
|
|
21687
|
+
LiveChatRegistry = class {
|
|
21688
|
+
turns = /* @__PURE__ */ new Map();
|
|
21689
|
+
bufferMax;
|
|
21690
|
+
graceMs;
|
|
21691
|
+
constructor(opts = {}) {
|
|
21692
|
+
this.bufferMax = opts.bufferMax ?? DEFAULT_BUFFER_MAX;
|
|
21693
|
+
this.graceMs = opts.graceMs ?? DEFAULT_GRACE_MS;
|
|
21694
|
+
}
|
|
21695
|
+
/**
|
|
21696
|
+
* 开始一轮。若该会话已有驻留的旧轮(上一轮已 done 但还在 grace 期),先清掉。
|
|
21697
|
+
* 同一会话不允许两轮 running 并存——上层保证一个 chatSessionId 一次只发一轮。
|
|
21698
|
+
*/
|
|
21699
|
+
start(chatSessionId, handle) {
|
|
21700
|
+
const prev = this.turns.get(chatSessionId);
|
|
21701
|
+
if (prev?.evictTimer) clearTimeout(prev.evictTimer);
|
|
21702
|
+
const turn = {
|
|
21703
|
+
chatSessionId,
|
|
21704
|
+
runtimeSessionId: handle.runtimeSessionId,
|
|
21705
|
+
...handle.runId ? { runId: handle.runId } : {},
|
|
21706
|
+
seq: 0,
|
|
21707
|
+
buffer: [],
|
|
21708
|
+
minBufferedSeq: 0,
|
|
21709
|
+
subscribers: /* @__PURE__ */ new Set(),
|
|
21710
|
+
status: "running",
|
|
21711
|
+
handle
|
|
21712
|
+
};
|
|
21713
|
+
this.turns.set(chatSessionId, turn);
|
|
21714
|
+
return {
|
|
21715
|
+
runtimeSessionId: turn.runtimeSessionId,
|
|
21716
|
+
emit: (part) => {
|
|
21717
|
+
const withSeq = { ...part, seq: ++turn.seq };
|
|
21718
|
+
turn.buffer.push(withSeq);
|
|
21719
|
+
if (turn.minBufferedSeq === 0) turn.minBufferedSeq = withSeq.seq;
|
|
21720
|
+
if (turn.buffer.length > this.bufferMax) {
|
|
21721
|
+
const dropped = turn.buffer.splice(0, turn.buffer.length - this.bufferMax);
|
|
21722
|
+
const last = dropped[dropped.length - 1];
|
|
21723
|
+
if (last) turn.minBufferedSeq = last.seq + 1;
|
|
21724
|
+
}
|
|
21725
|
+
for (const sub of turn.subscribers) {
|
|
21726
|
+
try {
|
|
21727
|
+
sub(withSeq);
|
|
21728
|
+
} catch {
|
|
21729
|
+
}
|
|
21730
|
+
}
|
|
21731
|
+
return withSeq;
|
|
21732
|
+
},
|
|
21733
|
+
subscribe: (sub) => {
|
|
21734
|
+
turn.subscribers.add(sub);
|
|
21735
|
+
return () => {
|
|
21736
|
+
turn.subscribers.delete(sub);
|
|
21737
|
+
};
|
|
21738
|
+
},
|
|
21739
|
+
finish: (status) => {
|
|
21740
|
+
turn.status = status;
|
|
21741
|
+
turn.subscribers.clear();
|
|
21742
|
+
turn.evictTimer = setTimeout(() => {
|
|
21743
|
+
if (this.turns.get(chatSessionId) === turn) this.turns.delete(chatSessionId);
|
|
21744
|
+
}, this.graceMs);
|
|
21745
|
+
turn.evictTimer.unref?.();
|
|
21746
|
+
},
|
|
21747
|
+
bufferedParts: () => turn.buffer.slice()
|
|
21748
|
+
};
|
|
21749
|
+
}
|
|
21750
|
+
/**
|
|
21751
|
+
* 观众接入运行中的一轮。无该会话的 live 轮时返回 null(前端回退 getChatSessionDetail 读 DB 最终态)。
|
|
21752
|
+
* 有则返回需补发的历史 part + 当前状态 + detach。live part 后续经 sub 推送。
|
|
21753
|
+
*/
|
|
21754
|
+
attach(chatSessionId, fromSeq, sub) {
|
|
21755
|
+
const turn = this.turns.get(chatSessionId);
|
|
21756
|
+
if (!turn) return null;
|
|
21757
|
+
const gap = turn.minBufferedSeq > 0 && fromSeq + 1 < turn.minBufferedSeq;
|
|
21758
|
+
const replay = turn.buffer.filter((p2) => p2.seq > fromSeq);
|
|
21759
|
+
if (turn.status === "running") {
|
|
21760
|
+
turn.subscribers.add(sub);
|
|
21761
|
+
}
|
|
21762
|
+
return {
|
|
21763
|
+
replay,
|
|
21764
|
+
status: turn.status,
|
|
21765
|
+
gap,
|
|
21766
|
+
detach: () => {
|
|
21767
|
+
turn.subscribers.delete(sub);
|
|
21768
|
+
}
|
|
21769
|
+
};
|
|
21770
|
+
}
|
|
21771
|
+
/** 显式停止:kill agent 这一轮。返回是否命中一个 live 轮。 */
|
|
21772
|
+
stop(chatSessionId) {
|
|
21773
|
+
const turn = this.turns.get(chatSessionId);
|
|
21774
|
+
if (!turn) return false;
|
|
21775
|
+
if (turn.status === "running") {
|
|
21776
|
+
try {
|
|
21777
|
+
turn.handle.kill();
|
|
21778
|
+
} catch {
|
|
21779
|
+
}
|
|
21780
|
+
}
|
|
21781
|
+
return true;
|
|
21782
|
+
}
|
|
21783
|
+
/** 该会话当前是否有一轮 running(供上层判断是否要挡并发发送)。 */
|
|
21784
|
+
isRunning(chatSessionId) {
|
|
21785
|
+
return this.turns.get(chatSessionId)?.status === "running";
|
|
21786
|
+
}
|
|
21787
|
+
/** 测试/排障用:当前驻留的会话数。 */
|
|
21788
|
+
size() {
|
|
21789
|
+
return this.turns.size;
|
|
21790
|
+
}
|
|
21791
|
+
};
|
|
21792
|
+
}
|
|
21793
|
+
});
|
|
21794
|
+
|
|
21690
21795
|
// ../server/src/governance/workorder-drafts.ts
|
|
21691
21796
|
var import_node_fs, WorkorderDraftStore;
|
|
21692
21797
|
var init_workorder_drafts = __esm({
|
|
@@ -23315,6 +23420,17 @@ function truncateOutput(text) {
|
|
|
23315
23420
|
return text.length > CHAT_OUTPUT_LIMIT ? `${text.slice(0, CHAT_OUTPUT_LIMIT)}
|
|
23316
23421
|
...[truncated]` : text;
|
|
23317
23422
|
}
|
|
23423
|
+
function startStreamKeepalive(res, ms = 15e3) {
|
|
23424
|
+
const timer = setInterval(() => {
|
|
23425
|
+
if (res.destroyed || !res.writable) return;
|
|
23426
|
+
try {
|
|
23427
|
+
res.write("\n");
|
|
23428
|
+
} catch {
|
|
23429
|
+
}
|
|
23430
|
+
}, ms);
|
|
23431
|
+
timer.unref?.();
|
|
23432
|
+
return () => clearInterval(timer);
|
|
23433
|
+
}
|
|
23318
23434
|
function chatPartFromTrajectoryEvent(event) {
|
|
23319
23435
|
const payload = asObj2(event.payload);
|
|
23320
23436
|
if (event.kind === "message") {
|
|
@@ -23418,6 +23534,16 @@ function optStr(args, key) {
|
|
|
23418
23534
|
if (typeof v2 !== "string") throw new Error(`\u53C2\u6570 ${key} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);
|
|
23419
23535
|
return v2;
|
|
23420
23536
|
}
|
|
23537
|
+
function attachmentArgs(args) {
|
|
23538
|
+
const raw = args["attachments"];
|
|
23539
|
+
if (!Array.isArray(raw)) return void 0;
|
|
23540
|
+
return raw.filter((a) => typeof a === "object" && a !== null).filter((a) => typeof a.name === "string" && (typeof a.text === "string" || typeof a.blobRef === "string")).slice(0, 5).map((a) => ({
|
|
23541
|
+
name: a.name,
|
|
23542
|
+
...typeof a.text === "string" ? { text: a.text } : {},
|
|
23543
|
+
...typeof a.blobRef === "string" ? { blobRef: a.blobRef } : {},
|
|
23544
|
+
...typeof a.contentType === "string" ? { contentType: a.contentType } : {}
|
|
23545
|
+
}));
|
|
23546
|
+
}
|
|
23421
23547
|
function hasOpenHumanChangeRequest2(kernel, artifactId) {
|
|
23422
23548
|
return hasOpenHumanChangeRequest(kernel.model, artifactId);
|
|
23423
23549
|
}
|
|
@@ -23802,7 +23928,7 @@ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
|
|
|
23802
23928
|
});
|
|
23803
23929
|
const parts = kernel.model.artifacts.get(artifactId)?.parts ?? [];
|
|
23804
23930
|
let landed = false;
|
|
23805
|
-
if (!holdForReview && !requiresHumanReview && qBefore === 0 && parts.length <= 1) {
|
|
23931
|
+
if (!holdForReview && !requiresHumanReview && qBefore === 0 && (revision.part === void 0 || parts.length <= 1)) {
|
|
23806
23932
|
try {
|
|
23807
23933
|
const r = await kernel.advanceQueue(artifactId, actor);
|
|
23808
23934
|
landed = r.landed.some((x2) => x2.id === revision.id);
|
|
@@ -23963,6 +24089,7 @@ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
|
|
|
23963
24089
|
}
|
|
23964
24090
|
case "annotate": {
|
|
23965
24091
|
const raisedFrom = optStr(args, "raisedFrom");
|
|
24092
|
+
const attachments = attachmentArgs(args);
|
|
23966
24093
|
const mentions = Array.isArray(args["mentions"]) ? args["mentions"].filter((x2) => typeof x2 === "string" && x2.startsWith("actor:")) : void 0;
|
|
23967
24094
|
if (mentions !== void 0 && mentions.length > 0 && isAgent(actor)) {
|
|
23968
24095
|
throw new Error("@ \u6743\u53EA\u5C5E\u4E8E\u4EBA\uFF1Aagent \u627E agent \u7528\u56DE\u538B\uFF08annotate --from\uFF09\u6216\u5199\u4FE1\u5230\u5B83\u7684\u4EA7\u7269\uFF1B\u66FF\u4EBA @ \u9700\u5728 1:1 \u4F1A\u8BDD\u4E2D\uFF08\u7F72\u540D\u4EE3\u7F72\uFF09\u3002");
|
|
@@ -23974,6 +24101,7 @@ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
|
|
|
23974
24101
|
...hand !== void 0 ? { hand } : {},
|
|
23975
24102
|
// 署名(§四):1:1 会话中作者=对话的人,agent 手留审计
|
|
23976
24103
|
...mentions !== void 0 && mentions.length > 0 ? { mentions } : {},
|
|
24104
|
+
...attachments !== void 0 && attachments.length > 0 ? { attachments } : {},
|
|
23977
24105
|
...optStr(args, "revisionId") !== void 0 ? { revisionId: optStr(args, "revisionId") } : {},
|
|
23978
24106
|
...raisedFrom !== void 0 ? { raisedFrom } : {},
|
|
23979
24107
|
...optStr(args, "raisedFromPart") !== void 0 ? { raisedFromPart: optStr(args, "raisedFromPart") } : {},
|
|
@@ -24311,6 +24439,7 @@ async function startOasisServer(opts) {
|
|
|
24311
24439
|
const resolveEngine = opts.resolveEngine ?? (async () => ({ kernel: opts.kernel, oplog: opts.oplog, blobs: opts.blobs, ...opts.registry ? { registry: opts.registry } : {} }));
|
|
24312
24440
|
const domainRouter = new Router();
|
|
24313
24441
|
for (const register of opts.domains ?? []) register(domainRouter);
|
|
24442
|
+
const liveChat = new LiveChatRegistry();
|
|
24314
24443
|
const httpServer = http.createServer((req, res) => {
|
|
24315
24444
|
void (async () => {
|
|
24316
24445
|
try {
|
|
@@ -24937,6 +25066,61 @@ async function startOasisServer(opts) {
|
|
|
24937
25066
|
}
|
|
24938
25067
|
return;
|
|
24939
25068
|
}
|
|
25069
|
+
if (req.method === "GET" && /^\/api\/chat-sessions\/[^/]+\/stream$/.test(url.pathname)) {
|
|
25070
|
+
const sid = decodeURIComponent(url.pathname.split("/")[3] ?? "");
|
|
25071
|
+
const cs = opts.chatSession ? await opts.chatSession.getSession(sid).catch(() => null) : null;
|
|
25072
|
+
if (!cs || cs.humanActorId !== actor) {
|
|
25073
|
+
res.writeHead(404, { "content-type": "application/json" }).end(JSON.stringify({ error: "CHAT_SESSION_NOT_FOUND" }));
|
|
25074
|
+
return;
|
|
25075
|
+
}
|
|
25076
|
+
const fromSeq = Math.max(0, Number(url.searchParams.get("fromSeq") ?? "0") || 0);
|
|
25077
|
+
const sub = (part) => {
|
|
25078
|
+
if (res.destroyed) return;
|
|
25079
|
+
res.write(`${JSON.stringify(part)}
|
|
25080
|
+
`);
|
|
25081
|
+
if (part.type === "done") res.end();
|
|
25082
|
+
};
|
|
25083
|
+
const att = liveChat.attach(sid, fromSeq, sub);
|
|
25084
|
+
if (!att) {
|
|
25085
|
+
res.writeHead(204).end();
|
|
25086
|
+
return;
|
|
25087
|
+
}
|
|
25088
|
+
res.writeHead(200, {
|
|
25089
|
+
"content-type": "application/x-ndjson; charset=utf-8",
|
|
25090
|
+
"transfer-encoding": "chunked",
|
|
25091
|
+
"access-control-expose-headers": "X-Session-Id, X-Replay-Gap",
|
|
25092
|
+
"x-session-id": cs.runtimeSessionId ?? "",
|
|
25093
|
+
...att.gap ? { "x-replay-gap": "1" } : {}
|
|
25094
|
+
});
|
|
25095
|
+
res.flushHeaders();
|
|
25096
|
+
const stopKa = startStreamKeepalive(res);
|
|
25097
|
+
for (const part of att.replay) {
|
|
25098
|
+
if (!res.destroyed) res.write(`${JSON.stringify(part)}
|
|
25099
|
+
`);
|
|
25100
|
+
}
|
|
25101
|
+
if (att.status !== "running") {
|
|
25102
|
+
att.detach();
|
|
25103
|
+
stopKa();
|
|
25104
|
+
res.end();
|
|
25105
|
+
return;
|
|
25106
|
+
}
|
|
25107
|
+
res.on("close", () => {
|
|
25108
|
+
stopKa();
|
|
25109
|
+
att.detach();
|
|
25110
|
+
});
|
|
25111
|
+
return;
|
|
25112
|
+
}
|
|
25113
|
+
if (req.method === "POST" && /^\/api\/chat-sessions\/[^/]+\/stop$/.test(url.pathname)) {
|
|
25114
|
+
const sid = decodeURIComponent(url.pathname.split("/")[3] ?? "");
|
|
25115
|
+
const cs = opts.chatSession ? await opts.chatSession.getSession(sid).catch(() => null) : null;
|
|
25116
|
+
if (!cs || cs.humanActorId !== actor) {
|
|
25117
|
+
res.writeHead(404, { "content-type": "application/json" }).end(JSON.stringify({ error: "CHAT_SESSION_NOT_FOUND" }));
|
|
25118
|
+
return;
|
|
25119
|
+
}
|
|
25120
|
+
const stopped = liveChat.stop(sid);
|
|
25121
|
+
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ stopped }));
|
|
25122
|
+
return;
|
|
25123
|
+
}
|
|
24940
25124
|
if (url.pathname === "/api/chat" && req.method === "POST") {
|
|
24941
25125
|
try {
|
|
24942
25126
|
const body = JSON.parse(rawBody);
|
|
@@ -24980,23 +25164,52 @@ async function startOasisServer(opts) {
|
|
|
24980
25164
|
...persistAttachments.length ? { attachments: persistAttachments } : {}
|
|
24981
25165
|
}).catch(() => void 0);
|
|
24982
25166
|
}
|
|
24983
|
-
let
|
|
24984
|
-
|
|
24985
|
-
const streamedParts = [];
|
|
24986
|
-
const finalizeAssistant = async () => {
|
|
24987
|
-
if (persisted || !chatStore || !persistTarget) return;
|
|
24988
|
-
persisted = true;
|
|
24989
|
-
if (!assistantText && !session.runId && streamedParts.length === 0) return;
|
|
25167
|
+
let assistantMsgId;
|
|
25168
|
+
if (chatStore && persistTarget) {
|
|
24990
25169
|
const { randomUUID: randomUUID19 } = await import("node:crypto");
|
|
25170
|
+
const id = randomUUID19();
|
|
24991
25171
|
await chatStore.appendMessage({
|
|
24992
|
-
id
|
|
25172
|
+
id,
|
|
24993
25173
|
sessionId: persistTarget.id,
|
|
24994
25174
|
role: "assistant",
|
|
24995
|
-
content:
|
|
25175
|
+
content: "",
|
|
24996
25176
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
24997
|
-
|
|
24998
|
-
...
|
|
25177
|
+
status: "running",
|
|
25178
|
+
...session.runId ? { runId: session.runId } : {}
|
|
25179
|
+
}).then(() => {
|
|
25180
|
+
assistantMsgId = id;
|
|
24999
25181
|
}).catch(() => void 0);
|
|
25182
|
+
}
|
|
25183
|
+
let assistantText = "";
|
|
25184
|
+
let persisted = false;
|
|
25185
|
+
let live = null;
|
|
25186
|
+
const localParts = [];
|
|
25187
|
+
const collectedParts = () => (live ? live.bufferedParts() : localParts).filter((p2) => p2.type !== "done");
|
|
25188
|
+
const finalizeAssistant = async (status = "done") => {
|
|
25189
|
+
if (persisted || !chatStore || !persistTarget) return;
|
|
25190
|
+
persisted = true;
|
|
25191
|
+
const parts = collectedParts();
|
|
25192
|
+
if (assistantMsgId) {
|
|
25193
|
+
await chatStore.updateMessage(assistantMsgId, {
|
|
25194
|
+
content: assistantText,
|
|
25195
|
+
status,
|
|
25196
|
+
...parts.length ? { parts } : {},
|
|
25197
|
+
...session.runId ? { runId: session.runId } : {}
|
|
25198
|
+
}).catch(() => void 0);
|
|
25199
|
+
} else {
|
|
25200
|
+
if (!assistantText && !session.runId && parts.length === 0) return;
|
|
25201
|
+
const { randomUUID: randomUUID19 } = await import("node:crypto");
|
|
25202
|
+
await chatStore.appendMessage({
|
|
25203
|
+
id: randomUUID19(),
|
|
25204
|
+
sessionId: persistTarget.id,
|
|
25205
|
+
role: "assistant",
|
|
25206
|
+
content: assistantText,
|
|
25207
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25208
|
+
status,
|
|
25209
|
+
...session.runId ? { runId: session.runId } : {},
|
|
25210
|
+
...parts.length ? { parts } : {}
|
|
25211
|
+
}).catch(() => void 0);
|
|
25212
|
+
}
|
|
25000
25213
|
await chatStore.updateSession(persistTarget.id, {
|
|
25001
25214
|
// 优先落 runtime-native id(codex rollout / ACP session/new 返回),让下一轮 resume
|
|
25002
25215
|
// 能命中 runtime 认可的 id;claude-code 等接受任意外部 id 的仍回退 session.id 兼容原行为。
|
|
@@ -25005,21 +25218,40 @@ async function startOasisServer(opts) {
|
|
|
25005
25218
|
}).catch(() => void 0);
|
|
25006
25219
|
};
|
|
25007
25220
|
let finished = false;
|
|
25221
|
+
let detachViewer;
|
|
25222
|
+
let stopKeepalive;
|
|
25008
25223
|
res.on("close", () => {
|
|
25009
|
-
|
|
25224
|
+
stopKeepalive?.();
|
|
25225
|
+
if (finished) return;
|
|
25226
|
+
if (live) {
|
|
25227
|
+
detachViewer?.();
|
|
25228
|
+
} else {
|
|
25010
25229
|
void session.kill?.();
|
|
25011
25230
|
void finalizeAssistant();
|
|
25012
25231
|
}
|
|
25013
25232
|
});
|
|
25014
25233
|
if (typedParts) {
|
|
25015
|
-
|
|
25016
|
-
|
|
25017
|
-
|
|
25018
|
-
|
|
25019
|
-
|
|
25020
|
-
|
|
25234
|
+
live = body.chatSessionId ? liveChat.start(body.chatSessionId, {
|
|
25235
|
+
runtimeSessionId: session.id,
|
|
25236
|
+
...session.runId ? { runId: session.runId } : {},
|
|
25237
|
+
kill: () => {
|
|
25238
|
+
void session.kill?.();
|
|
25239
|
+
}
|
|
25240
|
+
}) : null;
|
|
25241
|
+
let localSeq = 0;
|
|
25242
|
+
let ckptDirty = false;
|
|
25243
|
+
const writeToRes = (part) => {
|
|
25244
|
+
if (!res.destroyed) res.write(`${JSON.stringify(part)}
|
|
25021
25245
|
`);
|
|
25022
25246
|
};
|
|
25247
|
+
const emit = (part) => {
|
|
25248
|
+
ckptDirty = true;
|
|
25249
|
+
if (live) return live.emit(part);
|
|
25250
|
+
const withSeq = { ...part, seq: ++localSeq };
|
|
25251
|
+
if (part.type !== "done") localParts.push(withSeq);
|
|
25252
|
+
writeToRes(withSeq);
|
|
25253
|
+
return withSeq;
|
|
25254
|
+
};
|
|
25023
25255
|
res.writeHead(200, {
|
|
25024
25256
|
"content-type": "application/x-ndjson; charset=utf-8",
|
|
25025
25257
|
"transfer-encoding": "chunked",
|
|
@@ -25027,25 +25259,40 @@ async function startOasisServer(opts) {
|
|
|
25027
25259
|
"x-session-id": session.id,
|
|
25028
25260
|
...session.runId ? { "x-run-id": session.runId } : {}
|
|
25029
25261
|
});
|
|
25262
|
+
res.flushHeaders();
|
|
25263
|
+
stopKeepalive = startStreamKeepalive(res);
|
|
25264
|
+
const ckptTimer = setInterval(() => {
|
|
25265
|
+
if (!ckptDirty || !assistantMsgId || !chatStore) return;
|
|
25266
|
+
ckptDirty = false;
|
|
25267
|
+
const parts = collectedParts();
|
|
25268
|
+
void chatStore.updateMessage(assistantMsgId, { content: assistantText, status: "running", ...parts.length ? { parts } : {} }).catch(() => void 0);
|
|
25269
|
+
}, 2e3);
|
|
25270
|
+
ckptTimer.unref?.();
|
|
25271
|
+
if (live) detachViewer = live.subscribe(writeToRes);
|
|
25030
25272
|
let sawTextOutput = false;
|
|
25031
25273
|
session.onOutput((chunk) => {
|
|
25032
25274
|
sawTextOutput = true;
|
|
25033
25275
|
assistantText += chunk;
|
|
25034
|
-
|
|
25276
|
+
emit({ type: "text", text: chunk });
|
|
25035
25277
|
});
|
|
25036
25278
|
session.onTelemetry?.((event) => {
|
|
25037
25279
|
if (event.kind === "message" && sawTextOutput) return;
|
|
25038
25280
|
const part = chatPartFromTrajectoryEvent(event);
|
|
25039
|
-
if (part)
|
|
25281
|
+
if (part) emit(part);
|
|
25040
25282
|
});
|
|
25283
|
+
let errored = false;
|
|
25041
25284
|
try {
|
|
25042
25285
|
await session.done;
|
|
25043
25286
|
} catch (err) {
|
|
25044
|
-
|
|
25287
|
+
errored = true;
|
|
25288
|
+
emit({ type: "error", text: err instanceof Error ? err.message : String(err) });
|
|
25045
25289
|
}
|
|
25046
25290
|
finished = true;
|
|
25047
|
-
|
|
25048
|
-
|
|
25291
|
+
clearInterval(ckptTimer);
|
|
25292
|
+
await finalizeAssistant(errored ? "error" : "done");
|
|
25293
|
+
emit({ type: "done", sessionId: session.id });
|
|
25294
|
+
live?.finish(errored ? "error" : "done");
|
|
25295
|
+
stopKeepalive?.();
|
|
25049
25296
|
res.end();
|
|
25050
25297
|
return;
|
|
25051
25298
|
}
|
|
@@ -25481,6 +25728,7 @@ var init_server3 = __esm({
|
|
|
25481
25728
|
init_src2();
|
|
25482
25729
|
init_mcp2();
|
|
25483
25730
|
init_router();
|
|
25731
|
+
init_live_chat();
|
|
25484
25732
|
init_workorder_drafts();
|
|
25485
25733
|
init_draft_edit();
|
|
25486
25734
|
init_workorder_detail();
|
|
@@ -28684,6 +28932,17 @@ var init_dev_store = __esm({
|
|
|
28684
28932
|
this.messages.set(m2.sessionId, list);
|
|
28685
28933
|
return record5;
|
|
28686
28934
|
}
|
|
28935
|
+
async updateMessage(id, patch) {
|
|
28936
|
+
for (const list of this.messages.values()) {
|
|
28937
|
+
const item = list.find((x2) => x2.id === id);
|
|
28938
|
+
if (!item) continue;
|
|
28939
|
+
if (patch.content !== void 0) item.content = patch.content;
|
|
28940
|
+
if (patch.parts !== void 0) item.parts = patch.parts;
|
|
28941
|
+
if (patch.status !== void 0) item.status = patch.status;
|
|
28942
|
+
if (patch.runId !== void 0) item.runId = patch.runId;
|
|
28943
|
+
return;
|
|
28944
|
+
}
|
|
28945
|
+
}
|
|
28687
28946
|
async listMessages(sessionId, limit) {
|
|
28688
28947
|
const list = (this.messages.get(sessionId) ?? []).sort((a, b2) => a.seq - b2.seq);
|
|
28689
28948
|
return limit ? list.slice(-limit) : list;
|
|
@@ -28732,6 +28991,10 @@ var init_dev_store = __esm({
|
|
|
28732
28991
|
this.save();
|
|
28733
28992
|
return record5;
|
|
28734
28993
|
}
|
|
28994
|
+
async updateMessage(id, patch) {
|
|
28995
|
+
await super.updateMessage(id, patch);
|
|
28996
|
+
this.save();
|
|
28997
|
+
}
|
|
28735
28998
|
async linkWorkOrder(sessionId, workOrderId) {
|
|
28736
28999
|
await super.linkWorkOrder(sessionId, workOrderId);
|
|
28737
29000
|
this.save();
|
|
@@ -37714,6 +37977,31 @@ function codexSessionsRoot() {
|
|
|
37714
37977
|
}
|
|
37715
37978
|
return "";
|
|
37716
37979
|
}
|
|
37980
|
+
function codexRolloutExists(sessionId, root = codexSessionsRoot()) {
|
|
37981
|
+
if (!sessionId || !root) return false;
|
|
37982
|
+
const suffix = `-${sessionId}.jsonl`;
|
|
37983
|
+
let hit = false;
|
|
37984
|
+
const walk = (d) => {
|
|
37985
|
+
if (hit) return;
|
|
37986
|
+
let ents;
|
|
37987
|
+
try {
|
|
37988
|
+
ents = fs8.readdirSync(d, { withFileTypes: true });
|
|
37989
|
+
} catch {
|
|
37990
|
+
return;
|
|
37991
|
+
}
|
|
37992
|
+
for (const ent of ents) {
|
|
37993
|
+
if (hit) return;
|
|
37994
|
+
const p2 = path8.join(d, ent.name);
|
|
37995
|
+
if (ent.isDirectory()) walk(p2);
|
|
37996
|
+
else if (ent.isFile() && ent.name.startsWith("rollout-") && ent.name.endsWith(suffix)) {
|
|
37997
|
+
hit = true;
|
|
37998
|
+
return;
|
|
37999
|
+
}
|
|
38000
|
+
}
|
|
38001
|
+
};
|
|
38002
|
+
walk(root);
|
|
38003
|
+
return hit;
|
|
38004
|
+
}
|
|
37717
38005
|
function listRecentRollouts(root, sinceMs) {
|
|
37718
38006
|
const margin = sinceMs - 6e4;
|
|
37719
38007
|
const found = [];
|
|
@@ -37964,14 +38252,17 @@ var init_codex = __esm({
|
|
|
37964
38252
|
const task = job.bundle.files["TASK.md"];
|
|
37965
38253
|
if (!task) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
|
|
37966
38254
|
const prompt = job.systemPrompt ? ["# System Prompt", job.systemPrompt, "", "# Task", task].join("\n") : task;
|
|
37967
|
-
const
|
|
38255
|
+
const isResume = Boolean(
|
|
38256
|
+
job.resumeRuntimeSession && job.runtimeSessionId && codexRolloutExists(job.runtimeSessionId)
|
|
38257
|
+
);
|
|
37968
38258
|
const args = [
|
|
37969
|
-
|
|
38259
|
+
"exec",
|
|
38260
|
+
"-C",
|
|
38261
|
+
dir,
|
|
38262
|
+
...isResume ? ["resume", job.runtimeSessionId] : [],
|
|
37970
38263
|
"--json",
|
|
37971
38264
|
"--dangerously-bypass-approvals-and-sandbox",
|
|
37972
38265
|
"--skip-git-repo-check",
|
|
37973
|
-
"-C",
|
|
37974
|
-
dir,
|
|
37975
38266
|
...job.model ?? this.opts.model ? ["--model", job.model ?? this.opts.model] : [],
|
|
37976
38267
|
// 打开 reasoning 摘要通道:默认 auto,让 codex 产出 reasoning item(→ thought → 前端思考块)。
|
|
37977
38268
|
...this.opts.reasoningSummary !== "none" ? ["-c", `model_reasoning_summary=${this.opts.reasoningSummary ?? "auto"}`] : [],
|
|
@@ -38577,6 +38868,9 @@ function parseArgsJSON(argsText) {
|
|
|
38577
38868
|
function extractSessionID(result) {
|
|
38578
38869
|
return result?.sessionId ?? "";
|
|
38579
38870
|
}
|
|
38871
|
+
function extractStopReason(result) {
|
|
38872
|
+
return result?.stopReason ?? "";
|
|
38873
|
+
}
|
|
38580
38874
|
async function runACPSession(job, cfg) {
|
|
38581
38875
|
const slug6 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
|
|
38582
38876
|
const binTag = path9.basename(cfg.bin).replace(/[^a-zA-Z0-9]/g, "").slice(0, 12) || "acp";
|
|
@@ -38611,8 +38905,11 @@ async function runACPSession(job, cfg) {
|
|
|
38611
38905
|
let killedByUs = false;
|
|
38612
38906
|
let actualModel;
|
|
38613
38907
|
let capturedSessionId;
|
|
38908
|
+
let usageClient;
|
|
38614
38909
|
const finish = (info) => {
|
|
38615
38910
|
if (exited) return;
|
|
38911
|
+
const usage = info.usage ?? (usageClient ? buildUsage(usageClient.usage, estimateCost(usageClient.usage, actualModel)) : void 0);
|
|
38912
|
+
if (usage && !info.usage) info = { ...info, usage };
|
|
38616
38913
|
if (actualModel && !info.model) info = { ...info, model: actualModel };
|
|
38617
38914
|
if (capturedSessionId && !info.runtimeSessionId) info = { ...info, runtimeSessionId: capturedSessionId };
|
|
38618
38915
|
if (cfg.cleanupWorkdir) {
|
|
@@ -38660,6 +38957,7 @@ async function runACPSession(job, cfg) {
|
|
|
38660
38957
|
},
|
|
38661
38958
|
() => streamingCurrentTurn
|
|
38662
38959
|
);
|
|
38960
|
+
usageClient = client;
|
|
38663
38961
|
const rl = readline3.createInterface({ input: child.stdout });
|
|
38664
38962
|
const readerDone = new Promise((resolve4) => {
|
|
38665
38963
|
rl.on("line", (line) => {
|
|
@@ -38677,7 +38975,9 @@ async function runACPSession(job, cfg) {
|
|
|
38677
38975
|
});
|
|
38678
38976
|
child.on("exit", (code) => {
|
|
38679
38977
|
clearTimeout(timer);
|
|
38680
|
-
|
|
38978
|
+
void readerDone.then(() => {
|
|
38979
|
+
finish({ code: killedByUs ? null : code, reason: killedByUs ? "timeout" : code === 0 ? "clean" : "error", transcriptRef });
|
|
38980
|
+
});
|
|
38681
38981
|
});
|
|
38682
38982
|
void (async () => {
|
|
38683
38983
|
try {
|
|
@@ -38688,6 +38988,7 @@ async function runACPSession(job, cfg) {
|
|
|
38688
38988
|
});
|
|
38689
38989
|
const resolvedModel = job.model ?? cfg.model;
|
|
38690
38990
|
let sessionId = "";
|
|
38991
|
+
let ambiguousResume = false;
|
|
38691
38992
|
if (job.resumeRuntimeSession && job.runtimeSessionId) {
|
|
38692
38993
|
const method = cfg.resumeMethod ?? "session/load";
|
|
38693
38994
|
try {
|
|
@@ -38697,7 +38998,11 @@ async function runACPSession(job, cfg) {
|
|
|
38697
38998
|
mcpServers: [],
|
|
38698
38999
|
...resolvedModel ? { model: resolvedModel } : {}
|
|
38699
39000
|
});
|
|
38700
|
-
sessionId = extractSessionID(resumeRes)
|
|
39001
|
+
sessionId = extractSessionID(resumeRes);
|
|
39002
|
+
if (!sessionId) {
|
|
39003
|
+
sessionId = job.runtimeSessionId;
|
|
39004
|
+
ambiguousResume = true;
|
|
39005
|
+
}
|
|
38701
39006
|
} catch {
|
|
38702
39007
|
sessionId = "";
|
|
38703
39008
|
}
|
|
@@ -38722,10 +39027,24 @@ async function runACPSession(job, cfg) {
|
|
|
38722
39027
|
|
|
38723
39028
|
${task}` : task;
|
|
38724
39029
|
streamingCurrentTurn = true;
|
|
38725
|
-
await client.request("session/prompt", {
|
|
39030
|
+
let promptResult = await client.request("session/prompt", {
|
|
38726
39031
|
sessionId,
|
|
38727
39032
|
prompt: [{ type: "text", text: userText }]
|
|
38728
39033
|
});
|
|
39034
|
+
if (ambiguousResume && extractStopReason(promptResult) === "refusal" && !hasUsage(client.usage)) {
|
|
39035
|
+
const sessionRes = await client.request("session/new", {
|
|
39036
|
+
cwd: dir,
|
|
39037
|
+
mcpServers: [],
|
|
39038
|
+
...resolvedModel ? { model: resolvedModel } : {}
|
|
39039
|
+
});
|
|
39040
|
+
sessionId = extractSessionID(sessionRes);
|
|
39041
|
+
if (!sessionId) throw new Error("session/new returned no sessionId");
|
|
39042
|
+
capturedSessionId = sessionId;
|
|
39043
|
+
promptResult = await client.request("session/prompt", {
|
|
39044
|
+
sessionId,
|
|
39045
|
+
prompt: [{ type: "text", text: userText }]
|
|
39046
|
+
});
|
|
39047
|
+
}
|
|
38729
39048
|
const usage = buildUsage(client.usage, estimateCost(client.usage, actualModel));
|
|
38730
39049
|
if (!exited) finish({ code: 0, reason: "clean", transcriptRef, usage, model: actualModel });
|
|
38731
39050
|
child.kill("SIGTERM");
|
|
@@ -39975,39 +40294,47 @@ function nodesDomain(deps) {
|
|
|
39975
40294
|
if (msg.type !== "hello") return;
|
|
39976
40295
|
const d = hub.connectedDaemons().find((c) => c.daemonId === daemonId);
|
|
39977
40296
|
if (!d) return;
|
|
39978
|
-
|
|
39979
|
-
|
|
39980
|
-
|
|
39981
|
-
|
|
39982
|
-
|
|
39983
|
-
nodeVersion: d.meta.nodeVersion ?? "unknown",
|
|
39984
|
-
status: "online",
|
|
39985
|
-
// revives a previously soft-deleted node
|
|
39986
|
-
lastSeenAt: now(),
|
|
39987
|
-
enrolledAt: existing?.enrolledAt ?? now(),
|
|
39988
|
-
...d.meta.nodeName ? { name: d.meta.nodeName } : existing?.name ? { name: existing.name } : {}
|
|
39989
|
-
});
|
|
39990
|
-
for (const cap of msg.meta.runtimes) {
|
|
39991
|
-
await nodeStore.addRuntimeIfAbsent({
|
|
39992
|
-
id: `runtime:${daemonId}:${cap.kind}`,
|
|
39993
|
-
nodeId: daemonId,
|
|
39994
|
-
kind: cap.kind,
|
|
40297
|
+
try {
|
|
40298
|
+
if (msg.meta.runtimes) {
|
|
40299
|
+
const existing = await nodeStore.getNode(daemonId);
|
|
40300
|
+
await nodeStore.upsertNode({
|
|
40301
|
+
id: daemonId,
|
|
39995
40302
|
hostname: d.meta.hostname,
|
|
39996
|
-
|
|
39997
|
-
...cap.version ? { version: cap.version } : {},
|
|
40303
|
+
nodeVersion: d.meta.nodeVersion ?? "unknown",
|
|
39998
40304
|
status: "online",
|
|
39999
|
-
|
|
40305
|
+
// revives a previously soft-deleted node
|
|
40306
|
+
lastSeenAt: now(),
|
|
40307
|
+
enrolledAt: existing?.enrolledAt ?? now(),
|
|
40308
|
+
...d.meta.nodeName ? { name: d.meta.nodeName } : existing?.name ? { name: existing.name } : {}
|
|
40000
40309
|
});
|
|
40310
|
+
for (const cap of msg.meta.runtimes) {
|
|
40311
|
+
await nodeStore.addRuntimeIfAbsent({
|
|
40312
|
+
id: `runtime:${daemonId}:${cap.kind}`,
|
|
40313
|
+
nodeId: daemonId,
|
|
40314
|
+
kind: cap.kind,
|
|
40315
|
+
hostname: d.meta.hostname,
|
|
40316
|
+
...cap.binary ? { binary: cap.binary } : {},
|
|
40317
|
+
...cap.version ? { version: cap.version } : {},
|
|
40318
|
+
status: "online",
|
|
40319
|
+
lastSeenAt: now()
|
|
40320
|
+
});
|
|
40321
|
+
}
|
|
40322
|
+
await nodeStore.setNodeRuntimesOnline(daemonId, true);
|
|
40323
|
+
} else {
|
|
40324
|
+
await nodeStore.setNodeOnline(daemonId, true);
|
|
40325
|
+
await nodeStore.setNodeRuntimesOnline(daemonId, true);
|
|
40001
40326
|
}
|
|
40002
|
-
|
|
40003
|
-
|
|
40004
|
-
await nodeStore.setNodeOnline(daemonId, true);
|
|
40005
|
-
await nodeStore.setNodeRuntimesOnline(daemonId, true);
|
|
40327
|
+
} catch (err) {
|
|
40328
|
+
console.warn(`[nodes] hello \u5904\u7406\u5931\u8D25(node=${daemonId})\uFF1A${err instanceof Error ? err.message : String(err)}`);
|
|
40006
40329
|
}
|
|
40007
40330
|
});
|
|
40008
40331
|
hub.onDaemonDisconnected = async (daemonId) => {
|
|
40009
|
-
|
|
40010
|
-
|
|
40332
|
+
try {
|
|
40333
|
+
await nodeStore.setNodeOnline(daemonId, false);
|
|
40334
|
+
await nodeStore.setNodeRuntimesOnline(daemonId, false);
|
|
40335
|
+
} catch (err) {
|
|
40336
|
+
console.warn(`[nodes] disconnect \u5904\u7406\u5931\u8D25(node=${daemonId})\uFF1A${err instanceof Error ? err.message : String(err)}`);
|
|
40337
|
+
}
|
|
40011
40338
|
};
|
|
40012
40339
|
}
|
|
40013
40340
|
return (router) => {
|
|
@@ -40821,6 +41148,39 @@ function collabDomain(opts) {
|
|
|
40821
41148
|
if (draft) return { status: 200, body: buildWorkorderDraftDetail(draft, resolve4) };
|
|
40822
41149
|
return { status: 404, body: { error: { code: "not_found", message: `\u5DE5\u5355\u4E0D\u5B58\u5728: ${req.params.id}` } } };
|
|
40823
41150
|
});
|
|
41151
|
+
router.post("/api/workorders/:id/hold", async (req) => {
|
|
41152
|
+
const { kernel, artifacts } = await resolveCtx(req.auth.companyId);
|
|
41153
|
+
const ws = req.params.id;
|
|
41154
|
+
const arts = [...kernel.model.artifacts.values()].filter((a) => a.workspace === ws);
|
|
41155
|
+
if (arts.length === 0) {
|
|
41156
|
+
return { status: 404, body: { error: { code: "not_found", message: `\u5DE5\u5355\u4E0D\u5B58\u5728: ${ws}` } } };
|
|
41157
|
+
}
|
|
41158
|
+
if (!artifacts) {
|
|
41159
|
+
return { status: 503, body: { error: { code: "no_store", message: "\u63A7\u5236\u9762\u65C1\u5B58\u672A\u63A5\u5165\uFF0C\u65E0\u6CD5\u6682\u505C" } } };
|
|
41160
|
+
}
|
|
41161
|
+
const held = (await artifacts.listWorkspaceDispatchHeld()).includes(ws);
|
|
41162
|
+
if (held) return { status: 200, body: { held: false } };
|
|
41163
|
+
await artifacts.setWorkspaceDispatchHold(ws, true);
|
|
41164
|
+
return { status: 200, body: { held: true } };
|
|
41165
|
+
});
|
|
41166
|
+
router.post("/api/workorders/:id/cancel", async (req) => {
|
|
41167
|
+
if (!isHumanActor(req.auth.actor)) {
|
|
41168
|
+
return { status: 403, body: { error: { code: "forbidden", message: "\u53D6\u6D88\u5DE5\u5355\u662F\u9500\u6BC1\u6027\u6CBB\u7406\u64CD\u4F5C\uFF0C\u4EC5\u4EBA\u7C7B\u53EF\u6267\u884C" } } };
|
|
41169
|
+
}
|
|
41170
|
+
const { kernel } = await resolveCtx(req.auth.companyId);
|
|
41171
|
+
const ws = req.params.id;
|
|
41172
|
+
const active = [...kernel.model.artifacts.values()].filter((a) => a.workspace === ws && lifecycleOf(kernel.model, a.id) === "active");
|
|
41173
|
+
const all = [...kernel.model.artifacts.values()].filter((a) => a.workspace === ws);
|
|
41174
|
+
if (all.length === 0) {
|
|
41175
|
+
return { status: 404, body: { error: { code: "not_found", message: `\u5DE5\u5355\u4E0D\u5B58\u5728: ${ws}` } } };
|
|
41176
|
+
}
|
|
41177
|
+
let cancelled = 0;
|
|
41178
|
+
for (const a of active) {
|
|
41179
|
+
await kernel.seal({ artifactId: a.id, actor: req.auth.actor, reason: "cancelled", note: "\u5DE5\u5355\u53D6\u6D88\uFF08\u6279\u91CF\uFF09" });
|
|
41180
|
+
cancelled += 1;
|
|
41181
|
+
}
|
|
41182
|
+
return { status: 200, body: { cancelled } };
|
|
41183
|
+
});
|
|
40824
41184
|
router.post("/api/workorders/:id/dispatch", async (req) => {
|
|
40825
41185
|
const { kernel, artifacts } = await resolveCtx(req.auth.companyId);
|
|
40826
41186
|
const ws = req.params.id;
|
|
@@ -40891,6 +41251,7 @@ function collabDomain(opts) {
|
|
|
40891
41251
|
var init_collab = __esm({
|
|
40892
41252
|
"../server/src/domains/collab/index.ts"() {
|
|
40893
41253
|
"use strict";
|
|
41254
|
+
init_src2();
|
|
40894
41255
|
init_workorders();
|
|
40895
41256
|
init_workorder_detail();
|
|
40896
41257
|
init_inbox();
|
|
@@ -53957,6 +54318,7 @@ var PostgresChatSessionStore = class _PostgresChatSessionStore {
|
|
|
53957
54318
|
await pool.query(`ALTER TABLE "${s2}".chat_messages ADD COLUMN IF NOT EXISTS run_id text`);
|
|
53958
54319
|
await pool.query(`ALTER TABLE "${s2}".chat_messages ADD COLUMN IF NOT EXISTS parts jsonb`);
|
|
53959
54320
|
await pool.query(`ALTER TABLE "${s2}".chat_messages ADD COLUMN IF NOT EXISTS attachments jsonb`);
|
|
54321
|
+
await pool.query(`ALTER TABLE "${s2}".chat_messages ADD COLUMN IF NOT EXISTS status text`);
|
|
53960
54322
|
await pool.query(`ALTER TABLE "${s2}".chat_sessions ADD COLUMN IF NOT EXISTS workspace text`);
|
|
53961
54323
|
await pool.query(`
|
|
53962
54324
|
WITH ranked AS (
|
|
@@ -54022,11 +54384,11 @@ var PostgresChatSessionStore = class _PostgresChatSessionStore {
|
|
|
54022
54384
|
async appendMessage(m2) {
|
|
54023
54385
|
const insert = async () => {
|
|
54024
54386
|
const r = await this.pool.query(
|
|
54025
|
-
`INSERT INTO ${this.s}.chat_messages (id, session_id, role, content, seq, created_at, run_id, parts, attachments)
|
|
54026
|
-
SELECT $1,$2,$3,$4, COALESCE(MAX(seq),0)+1, $5,$6,$7,$8
|
|
54387
|
+
`INSERT INTO ${this.s}.chat_messages (id, session_id, role, content, seq, created_at, run_id, parts, attachments, status)
|
|
54388
|
+
SELECT $1,$2,$3,$4, COALESCE(MAX(seq),0)+1, $5,$6,$7,$8,$9
|
|
54027
54389
|
FROM ${this.s}.chat_messages WHERE session_id = $2
|
|
54028
54390
|
RETURNING seq`,
|
|
54029
|
-
[m2.id, m2.sessionId, m2.role, m2.content, m2.createdAt, m2.runId ?? null, m2.parts !== void 0 ? JSON.stringify(m2.parts) : null, m2.attachments !== void 0 ? JSON.stringify(m2.attachments) : null]
|
|
54391
|
+
[m2.id, m2.sessionId, m2.role, m2.content, m2.createdAt, m2.runId ?? null, m2.parts !== void 0 ? JSON.stringify(m2.parts) : null, m2.attachments !== void 0 ? JSON.stringify(m2.attachments) : null, m2.status ?? null]
|
|
54030
54392
|
);
|
|
54031
54393
|
return Number(r.rows[0].seq);
|
|
54032
54394
|
};
|
|
@@ -54040,6 +54402,29 @@ var PostgresChatSessionStore = class _PostgresChatSessionStore {
|
|
|
54040
54402
|
}
|
|
54041
54403
|
}
|
|
54042
54404
|
}
|
|
54405
|
+
async updateMessage(id, patch) {
|
|
54406
|
+
const sets = [];
|
|
54407
|
+
const args = [];
|
|
54408
|
+
if (patch.content !== void 0) {
|
|
54409
|
+
args.push(patch.content);
|
|
54410
|
+
sets.push(`content = $${args.length}`);
|
|
54411
|
+
}
|
|
54412
|
+
if (patch.parts !== void 0) {
|
|
54413
|
+
args.push(JSON.stringify(patch.parts));
|
|
54414
|
+
sets.push(`parts = $${args.length}`);
|
|
54415
|
+
}
|
|
54416
|
+
if (patch.status !== void 0) {
|
|
54417
|
+
args.push(patch.status);
|
|
54418
|
+
sets.push(`status = $${args.length}`);
|
|
54419
|
+
}
|
|
54420
|
+
if (patch.runId !== void 0) {
|
|
54421
|
+
args.push(patch.runId);
|
|
54422
|
+
sets.push(`run_id = $${args.length}`);
|
|
54423
|
+
}
|
|
54424
|
+
if (sets.length === 0) return;
|
|
54425
|
+
args.push(id);
|
|
54426
|
+
await this.pool.query(`UPDATE ${this.s}.chat_messages SET ${sets.join(", ")} WHERE id = $${args.length}`, args);
|
|
54427
|
+
}
|
|
54043
54428
|
async listMessages(sessionId, limit) {
|
|
54044
54429
|
const limitClause = limit ? `LIMIT ${limit}` : "";
|
|
54045
54430
|
const r = await this.pool.query(
|
|
@@ -54083,7 +54468,8 @@ var rowToMessage = (row) => ({
|
|
|
54083
54468
|
createdAt: new Date(row.created_at).toISOString(),
|
|
54084
54469
|
...row.run_id != null ? { runId: row.run_id } : {},
|
|
54085
54470
|
...row.parts != null ? { parts: row.parts } : {},
|
|
54086
|
-
...row.attachments != null ? { attachments: row.attachments } : {}
|
|
54471
|
+
...row.attachments != null ? { attachments: row.attachments } : {},
|
|
54472
|
+
...row.status != null ? { status: row.status } : {}
|
|
54087
54473
|
});
|
|
54088
54474
|
|
|
54089
54475
|
// ../storage/src/postgres-nodes.ts
|
|
@@ -54787,6 +55173,9 @@ async function startServe(opts) {
|
|
|
54787
55173
|
return service.listSkillFiles(skillId);
|
|
54788
55174
|
}
|
|
54789
55175
|
});
|
|
55176
|
+
process.on("unhandledRejection", (reason) => {
|
|
55177
|
+
console.error(`[serve] unhandledRejection\uFF08\u5DF2\u515C\u5E95\uFF0C\u4E0D\u5D29\u8FDB\u7A0B\uFF09\uFF1A${reason instanceof Error ? reason.stack ?? reason.message : String(reason)}`);
|
|
55178
|
+
});
|
|
54790
55179
|
const server = await startOasisServer({
|
|
54791
55180
|
kernel: engine.kernel,
|
|
54792
55181
|
blobs: engine.blobs,
|
|
@@ -57730,7 +58119,7 @@ ${res.warning}`);
|
|
|
57730
58119
|
}
|
|
57731
58120
|
|
|
57732
58121
|
// src/index.ts
|
|
57733
|
-
var PKG_VERSION = true ? "0.1.
|
|
58122
|
+
var PKG_VERSION = true ? "0.1.38" : "dev";
|
|
57734
58123
|
var OASIS_DIR = path18.join(os10.homedir(), ".oasis");
|
|
57735
58124
|
var CONFIG_FILE = path18.join(OASIS_DIR, "node-config.json");
|
|
57736
58125
|
var PID_FILE = path18.join(OASIS_DIR, "node.pid");
|