oasis_test 0.1.53 → 0.1.54
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 -57
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -46,6 +46,7 @@ function emptyReadModel() {
|
|
|
46
46
|
annotations: /* @__PURE__ */ new Map(),
|
|
47
47
|
annotationSeq: /* @__PURE__ */ new Map(),
|
|
48
48
|
seals: /* @__PURE__ */ new Map(),
|
|
49
|
+
holds: /* @__PURE__ */ new Map(),
|
|
49
50
|
lastOpAt: /* @__PURE__ */ new Map(),
|
|
50
51
|
pinMeta: /* @__PURE__ */ new Map(),
|
|
51
52
|
gaps: /* @__PURE__ */ new Map(),
|
|
@@ -316,13 +317,28 @@ function fold(model, op) {
|
|
|
316
317
|
gap.resolved = true;
|
|
317
318
|
gap.resolvedAtSeq = op.seq;
|
|
318
319
|
if (p2.note !== void 0) gap.note = p2.note;
|
|
319
|
-
for (const e of model.escalations.get(op.artifactId) ?? [])
|
|
320
|
+
for (const e of model.escalations.get(op.artifactId) ?? []) {
|
|
321
|
+
if (e.resolved) continue;
|
|
322
|
+
if (e.gapId !== void 0) {
|
|
323
|
+
if (e.gapId === p2.gapId) e.resolved = true;
|
|
324
|
+
} else if (e.part === gap.part) {
|
|
325
|
+
e.resolved = true;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
320
328
|
break;
|
|
321
329
|
}
|
|
322
330
|
case "escalate": {
|
|
323
331
|
const p2 = op.payload;
|
|
324
332
|
const list = model.escalations.get(op.artifactId) ?? [];
|
|
325
|
-
list.push({
|
|
333
|
+
list.push({
|
|
334
|
+
part: p2.part ?? null,
|
|
335
|
+
reason: p2.reason,
|
|
336
|
+
by: op.actor,
|
|
337
|
+
...op.hand !== void 0 ? { hand: op.hand } : {},
|
|
338
|
+
at: op.timestamp,
|
|
339
|
+
resolved: false,
|
|
340
|
+
...p2.gapId !== void 0 ? { gapId: p2.gapId } : {}
|
|
341
|
+
});
|
|
326
342
|
model.escalations.set(op.artifactId, list);
|
|
327
343
|
break;
|
|
328
344
|
}
|
|
@@ -418,6 +434,21 @@ function fold(model, op) {
|
|
|
418
434
|
model.seals.delete(op.artifactId);
|
|
419
435
|
break;
|
|
420
436
|
}
|
|
437
|
+
case "hold": {
|
|
438
|
+
const p2 = op.payload;
|
|
439
|
+
if (!model.artifacts.has(op.artifactId)) throw new CorruptLogError(op, "hold on missing artifact");
|
|
440
|
+
model.holds.set(op.artifactId, {
|
|
441
|
+
source: p2.source,
|
|
442
|
+
by: op.actor,
|
|
443
|
+
at: op.timestamp,
|
|
444
|
+
...p2.reason !== void 0 ? { reason: p2.reason } : {}
|
|
445
|
+
});
|
|
446
|
+
break;
|
|
447
|
+
}
|
|
448
|
+
case "release": {
|
|
449
|
+
model.holds.delete(op.artifactId);
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
421
452
|
case "unlink_input": {
|
|
422
453
|
const p2 = op.payload;
|
|
423
454
|
const artifact = model.artifacts.get(op.artifactId);
|
|
@@ -499,6 +530,12 @@ function lifecycleOf(model, id) {
|
|
|
499
530
|
const seal = model.seals.get(id);
|
|
500
531
|
return seal ? `sealed:${seal.reason}` : "active";
|
|
501
532
|
}
|
|
533
|
+
function isHeld(model, id) {
|
|
534
|
+
return model.holds.has(id);
|
|
535
|
+
}
|
|
536
|
+
function holdOf(model, id) {
|
|
537
|
+
return model.holds.get(id) ?? null;
|
|
538
|
+
}
|
|
502
539
|
function annotationsOf(model, id) {
|
|
503
540
|
const revisionIds = new Set(revisionsOf(model, id).map((r) => r.id));
|
|
504
541
|
return [...model.annotations.values()].filter(
|
|
@@ -565,7 +602,14 @@ function findGap(model, gapId) {
|
|
|
565
602
|
return null;
|
|
566
603
|
}
|
|
567
604
|
function unresolvedEscalationsOf(model, id) {
|
|
568
|
-
return (model.escalations.get(id) ?? []).filter((e) => !e.resolved).map((e) => ({
|
|
605
|
+
return (model.escalations.get(id) ?? []).filter((e) => !e.resolved).map((e) => ({
|
|
606
|
+
part: e.part,
|
|
607
|
+
reason: e.reason,
|
|
608
|
+
by: e.by,
|
|
609
|
+
...e.hand !== void 0 ? { hand: e.hand } : {},
|
|
610
|
+
at: e.at,
|
|
611
|
+
...e.gapId !== void 0 ? { gapId: e.gapId } : {}
|
|
612
|
+
}));
|
|
569
613
|
}
|
|
570
614
|
function listPendingReviews(model, workspace) {
|
|
571
615
|
const all = [...model.pendingReviews.values()];
|
|
@@ -606,6 +650,16 @@ function stuckDiagnosis(model, id, opts) {
|
|
|
606
650
|
if (!artifact || lifecycle !== "active") {
|
|
607
651
|
return { id, lifecycle, staleMs, causes, needsCoordinator: false };
|
|
608
652
|
}
|
|
653
|
+
if (isHeld(model, id)) {
|
|
654
|
+
const h = holdOf(model, id);
|
|
655
|
+
causes.push({
|
|
656
|
+
category: "held",
|
|
657
|
+
source: "oplog",
|
|
658
|
+
detail: `\u8282\u70B9\u5DF2\u6682\u505C\u6D3E\u53D1\uFF08${h?.source ?? "?"}${h?.reason ? `\uFF1A${h.reason}` : ""}\uFF09\u2014\u2014\u5F85\u89E3\u963B`,
|
|
659
|
+
handler: "human"
|
|
660
|
+
});
|
|
661
|
+
return { id, lifecycle, staleMs, causes, needsCoordinator: false };
|
|
662
|
+
}
|
|
609
663
|
if (artifact.owner.startsWith("pending:role:")) {
|
|
610
664
|
causes.push({
|
|
611
665
|
category: "owner-pending",
|
|
@@ -2454,19 +2508,28 @@ var init_kernel = __esm({
|
|
|
2454
2508
|
*/
|
|
2455
2509
|
async escalate(args) {
|
|
2456
2510
|
this.artifactOrThrow(args.artifactId);
|
|
2457
|
-
if (!isHumanActor(args.actor)) {
|
|
2511
|
+
if (!isHumanActor(args.actor) && args.actor !== SYSTEM_ACTOR) {
|
|
2458
2512
|
const ws = this.model.artifacts.get(args.artifactId)?.workspace;
|
|
2459
2513
|
const brief = [...this.model.artifacts.values()].find((a) => a.type === "brief" && a.workspace === ws);
|
|
2460
2514
|
const manager = brief?.fields?.["manager"];
|
|
2461
2515
|
if (manager !== args.actor) {
|
|
2462
2516
|
throw new KernelError(
|
|
2463
|
-
`escalate \u4EC5\u9650\u672C\u5DE5\u5355\u7BA1\u7406\u8005\uFF08brief.fields.manager\uFF09\
|
|
2517
|
+
`escalate \u4EC5\u9650\u672C\u5DE5\u5355\u7BA1\u7406\u8005\uFF08brief.fields.manager\uFF09\u3001\u4EBA\u7C7B\u6216\u7CFB\u7EDF\u8C03\u7528\u2014\u2014${args.actor} \u4E0D\u662F\u3002\u4F60\u82E5\u662F producer/owner \u5361\u4F4F\u4E86\uFF1A\u7528 \`oasis gap\` \u4E0A\u62A5\u9700\u8981\uFF0C\u7531\u7BA1\u7406\u8005\u5206\u8BCA\uFF08\u51B3\u7B56 0030 D4\uFF09\u3002`
|
|
2464
2518
|
);
|
|
2465
2519
|
}
|
|
2466
2520
|
}
|
|
2521
|
+
let part = args.part;
|
|
2522
|
+
let gapId = args.gapId;
|
|
2523
|
+
if (gapId) {
|
|
2524
|
+
const found = findGap(this.model, gapId);
|
|
2525
|
+
if (found && found.artifactId === args.artifactId) {
|
|
2526
|
+
if (part === void 0 && found.gap.part) part = found.gap.part;
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2467
2529
|
await this.commit(args.artifactId, args.actor, "escalate", args.artifactId, {
|
|
2468
2530
|
reason: args.reason,
|
|
2469
|
-
...
|
|
2531
|
+
...part !== void 0 ? { part } : {},
|
|
2532
|
+
...gapId !== void 0 ? { gapId } : {}
|
|
2470
2533
|
}, void 0, args.hand);
|
|
2471
2534
|
}
|
|
2472
2535
|
/** 决策 0027 D1:人显式关闭本 artifact 上的 escalate(off-graph 解决兜底)。 */
|
|
@@ -2619,6 +2682,43 @@ var init_kernel = __esm({
|
|
|
2619
2682
|
const payload = { persistence: "persistent" };
|
|
2620
2683
|
await this.commit(args.artifactId, args.actor, "promote", args.artifactId, payload);
|
|
2621
2684
|
}
|
|
2685
|
+
/**
|
|
2686
|
+
* 节点级派发暂停(node-hold,本批):落一条 hold op——**只挡 agent 派发、不锁内容**(≠ seal/freeze,
|
|
2687
|
+
* 见 docs/notes/artifact-seal-lifecycle.md)。用于自动升级冻结(source:"sla")与人/管理者显式暂停。
|
|
2688
|
+
* 不校验 lifecycle:sealed 节点本就不派、held 于它无意义但无害;主用途是冻 active 节点。幂等——重复 hold 覆盖来源。
|
|
2689
|
+
*/
|
|
2690
|
+
async hold(args) {
|
|
2691
|
+
this.artifactOrThrow(args.artifactId);
|
|
2692
|
+
this.assertHoldAdmission(args.artifactId, args.actor);
|
|
2693
|
+
const payload = {
|
|
2694
|
+
source: args.source,
|
|
2695
|
+
...args.reason !== void 0 ? { reason: args.reason } : {}
|
|
2696
|
+
};
|
|
2697
|
+
await this.commit(args.artifactId, args.actor, "hold", args.artifactId, payload);
|
|
2698
|
+
}
|
|
2699
|
+
/** 解阻(node-hold):撤销 hold,节点恢复可派(投影下一拍重推当下欠的活)。空解(未 held)拦下,避免日志噪声。 */
|
|
2700
|
+
async release(args) {
|
|
2701
|
+
this.artifactOrThrow(args.artifactId);
|
|
2702
|
+
this.assertHoldAdmission(args.artifactId, args.actor);
|
|
2703
|
+
if (!isHeld(this.model, args.artifactId)) throw new KernelError(`not held: ${args.artifactId}`);
|
|
2704
|
+
const payload = { ...args.reason !== void 0 ? { reason: args.reason } : {} };
|
|
2705
|
+
await this.commit(args.artifactId, args.actor, "release", args.artifactId, payload);
|
|
2706
|
+
}
|
|
2707
|
+
/**
|
|
2708
|
+
* hold/release 准入闸(红线 6:管控在服务端收紧):人类 / 本工单管理者 / `actor:system`(自动升级)可调;
|
|
2709
|
+
* 普通 producer/owner agent 不可——防 agent 把自己节点暂停了逃避派发(同 escalate 的机制闸精神,kernel.escalate)。
|
|
2710
|
+
*/
|
|
2711
|
+
assertHoldAdmission(artifactId, actor) {
|
|
2712
|
+
if (isHumanActor(actor)) return;
|
|
2713
|
+
if (actor === SYSTEM_ACTOR) return;
|
|
2714
|
+
const ws = this.model.artifacts.get(artifactId)?.workspace;
|
|
2715
|
+
const brief = [...this.model.artifacts.values()].find((a) => a.type === "brief" && a.workspace === ws);
|
|
2716
|
+
const manager = brief?.fields?.["manager"];
|
|
2717
|
+
if (manager === actor) return;
|
|
2718
|
+
throw new KernelError(
|
|
2719
|
+
`hold/release \u4EC5\u9650\u4EBA\u7C7B\u3001\u672C\u5DE5\u5355\u7BA1\u7406\u8005\uFF08brief.fields.manager\uFF09\u6216\u7CFB\u7EDF\u8C03\u7528\u2014\u2014${actor} \u4E0D\u662F\u3002`
|
|
2720
|
+
);
|
|
2721
|
+
}
|
|
2622
2722
|
// ── 治理 op(§11.3):force/override 单点豁免可直发;结构 op 以 Intervention 为载体 ──────
|
|
2623
2723
|
/** 治理解封:sealed → active(lifecycle = 最后一条 seal/reopen,§4.4)。frozen 解冻、cancelled 复活都走它 */
|
|
2624
2724
|
async reopen(args) {
|
|
@@ -4269,6 +4369,7 @@ var init_dispatcher = __esm({
|
|
|
4269
4369
|
const { kernel } = this.opts;
|
|
4270
4370
|
const jobWorkspace = kernel.model.artifacts.get(spec.artifactId)?.workspace;
|
|
4271
4371
|
if (jobWorkspace && await this.opts.resolveDispatchHold?.(jobWorkspace)) return 0;
|
|
4372
|
+
if (isHeld(kernel.model, spec.artifactId)) return 0;
|
|
4272
4373
|
this.assertSpawnAttemptCurrent(jobKey, attempt);
|
|
4273
4374
|
const seqNow = kernel.model.lastSeq.get(spec.artifactId) ?? 0;
|
|
4274
4375
|
const strike = this.strikes.get(jobKey);
|
|
@@ -24554,7 +24655,8 @@ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
|
|
|
24554
24655
|
artifactId: str(args, "artifactId"),
|
|
24555
24656
|
actor,
|
|
24556
24657
|
reason: str(args, "reason"),
|
|
24557
|
-
...optStr(args, "part") !== void 0 ? { part: optStr(args, "part") } : {}
|
|
24658
|
+
...optStr(args, "part") !== void 0 ? { part: optStr(args, "part") } : {},
|
|
24659
|
+
...optStr(args, "gapId") !== void 0 ? { gapId: optStr(args, "gapId") } : {}
|
|
24558
24660
|
});
|
|
24559
24661
|
return { message: '\u5DF2\u4E0A\u62A5\uFF08escalate\uFF09\uFF1A\u7CFB\u7EDF\u5C06\u5347\u7EA7\u5230\u8D1F\u8D23\u4EBA\u6536\u4EF6\u7BB1\u3002\u4F60\u53EF\u4EE5\u6536\u5DE5\u4E86\uFF1B\u4E4B\u540E\u53EF\u80FD\u5728\u5BF9\u8BDD\u91CC\u88AB ta \u95EE\u5230\u3002\u26A0\uFE0F \u82E5\u8FD9\u6B21\u4E0A\u62A5\u5173\u8054\u67D0\u6761 gap\uFF0C**\u4E0D\u8981\u5BF9\u5B83\u8C03 resolve-gap**\u2014\u2014\u90A3\u8868\u793A"\u9700\u6C42\u5DF2\u6EE1\u8DB3\u3001\u8BA9 owner \u590D\u5DE5"\uFF0C\u73B0\u5728\u8FD8\u6CA1\u6EE1\u8DB3\uFF1Bgap \u4FDD\u6301 open \u8282\u70B9\u624D\u4F1A\u7EE7\u7EED\u7B49\u5F85\u3002' };
|
|
24560
24662
|
}
|
|
@@ -24610,6 +24712,23 @@ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
|
|
|
24610
24712
|
await kernel.reopen({ artifactId: str(args, "artifactId"), actor, note: optStr(args, "note") });
|
|
24611
24713
|
return { message: `reopened ${str(args, "artifactId")}\uFF08lifecycle \u2192 active\uFF09` };
|
|
24612
24714
|
}
|
|
24715
|
+
case "hold": {
|
|
24716
|
+
await kernel.hold({
|
|
24717
|
+
artifactId: str(args, "artifactId"),
|
|
24718
|
+
actor,
|
|
24719
|
+
source: optStr(args, "source") ?? "human",
|
|
24720
|
+
...optStr(args, "reason") !== void 0 ? { reason: optStr(args, "reason") } : {}
|
|
24721
|
+
});
|
|
24722
|
+
return { message: `\u8282\u70B9\u5DF2\u6682\u505C\u6D3E\u53D1\uFF08hold\uFF09\uFF1A${str(args, "artifactId")}\u3002\u6062\u590D\u7528 release\u3002` };
|
|
24723
|
+
}
|
|
24724
|
+
case "release": {
|
|
24725
|
+
await kernel.release({
|
|
24726
|
+
artifactId: str(args, "artifactId"),
|
|
24727
|
+
actor,
|
|
24728
|
+
...optStr(args, "reason") !== void 0 ? { reason: optStr(args, "reason") } : {}
|
|
24729
|
+
});
|
|
24730
|
+
return { message: `\u8282\u70B9\u5DF2\u89E3\u963B\uFF08release\uFF09\uFF1A${str(args, "artifactId")}\u2014\u2014\u6295\u5F71\u4E0B\u4E00\u62CD\u4F1A\u91CD\u63A8\u8BE5\u8282\u70B9\u5F53\u4E0B\u6B20\u7684\u6D3B\u3002` };
|
|
24731
|
+
}
|
|
24613
24732
|
case "forceConclude": {
|
|
24614
24733
|
await kernel.forceConclude({
|
|
24615
24734
|
artifactId: str(args, "artifactId"),
|
|
@@ -26675,6 +26794,15 @@ var init_memory_trace_store = __esm({
|
|
|
26675
26794
|
const nextCursor = items.length > limit ? page[page.length - 1]?.id ?? null : null;
|
|
26676
26795
|
return { items: page.map((r) => this.decorate(structuredClone(r))), nextCursor };
|
|
26677
26796
|
}
|
|
26797
|
+
async listUsageRuns(filter) {
|
|
26798
|
+
return [...this.runs.values()].filter((run) => run.usage !== null && run.startedAt >= filter.from && run.startedAt <= filter.to).map((run) => ({
|
|
26799
|
+
actorId: run.actorId,
|
|
26800
|
+
artifactId: run.artifactId,
|
|
26801
|
+
startedAt: run.startedAt,
|
|
26802
|
+
effectiveModel: run.effectiveModel,
|
|
26803
|
+
usage: structuredClone(run.usage)
|
|
26804
|
+
}));
|
|
26805
|
+
}
|
|
26678
26806
|
async appendEvent(runId, event) {
|
|
26679
26807
|
return (await this.appendEvents(runId, [event]))[0];
|
|
26680
26808
|
}
|
|
@@ -39704,6 +39832,8 @@ async function runACPSession(job, cfg) {
|
|
|
39704
39832
|
...job.env,
|
|
39705
39833
|
...cfg.yoloEnv ?? {},
|
|
39706
39834
|
...job.wrapperPaths?.length ? { PATH: `${job.wrapperPaths.map((p2) => path11.dirname(p2)).join(path11.delimiter)}${path11.delimiter}${job.env?.["PATH"] ?? process.env["PATH"] ?? ""}` } : {},
|
|
39835
|
+
// 每会话技能目录绝对路径 → env(供不扫 cwd 的 runtime 经 config external_dirs 发现,见 injectSkillsDirEnv)。
|
|
39836
|
+
...cfg.injectSkillsDirEnv ? { [cfg.injectSkillsDirEnv]: path11.join(dir, sessionSkillsSubdir(cfg.runtimeKind ?? "")) } : {},
|
|
39707
39837
|
OASIS_SERVER: job.server.url,
|
|
39708
39838
|
OASIS_TOKEN: job.actorToken
|
|
39709
39839
|
},
|
|
@@ -39946,6 +40076,7 @@ var init_acp = __esm({
|
|
|
39946
40076
|
path11 = __toESM(require("node:path"), 1);
|
|
39947
40077
|
readline3 = __toESM(require("node:readline"), 1);
|
|
39948
40078
|
init_sync_skills();
|
|
40079
|
+
init_skill_cache();
|
|
39949
40080
|
init_claude_code();
|
|
39950
40081
|
init_usage();
|
|
39951
40082
|
init_pricing();
|
|
@@ -40218,6 +40349,11 @@ var init_hermes = __esm({
|
|
|
40218
40349
|
},
|
|
40219
40350
|
extraArgs: this.opts.extraArgs,
|
|
40220
40351
|
runtimeKind: "hermes",
|
|
40352
|
+
// hermes 不扫 cwd 技能目录(只认 $HERMES_HOME/skills + config external_dirs)。把本会话
|
|
40353
|
+
// 技能目录绝对路径喂到 OASIS_HERMES_SKILLS_DIR;node enroll 已把 config.yaml 的
|
|
40354
|
+
// skills.external_dirs 配成该 env → hermes 每 session 发现各自技能。见
|
|
40355
|
+
// packages/adapters/docs/modules/07-skills-delivery.md 与 connect-script.ts。
|
|
40356
|
+
injectSkillsDirEnv: "OASIS_HERMES_SKILLS_DIR",
|
|
40221
40357
|
...this.opts.syncSkills ? { syncSkills: this.opts.syncSkills } : {}
|
|
40222
40358
|
});
|
|
40223
40359
|
}
|
|
@@ -41036,6 +41172,45 @@ if command -v hermes &>/dev/null; then
|
|
|
41036
41172
|
"$HERMES_PIP" install 'agent-client-protocol>=0.9.0,<1.0'
|
|
41037
41173
|
fi
|
|
41038
41174
|
fi
|
|
41175
|
+
# Oasis \u6BCF\u4F1A\u8BDD\u6280\u80FD\uFF1Ahermes \u4E0D\u626B cwd\uFF0C\u53EA\u8BA4 $HERMES_HOME/skills + config skills.external_dirs\u3002
|
|
41176
|
+
# \u5E42\u7B49\u5730\u628A config.yaml \u7684 external_dirs \u914D\u6210\u5B57\u9762\u91CF $OASIS_HERMES_SKILLS_DIR\uFF08\u7531 hermes \u8FD0\u884C\u65F6
|
|
41177
|
+
# \u6309 env \u5C55\u5F00\uFF1Badapter \u6BCF\u4F1A\u8BDD\u628A\u8BE5 env \u6307\u5411\u672C workdir \u7684\u6280\u80FD\u76EE\u5F55 \u2192 \u6BCF session \u5404\u81EA\u53D1\u73B0\uFF0C\u8BFB\u4FA7\u9694\u79BB\uFF09\u3002
|
|
41178
|
+
HERMES_HOME_DIR="$HERMES_HOME"
|
|
41179
|
+
if [ -z "$HERMES_HOME_DIR" ]; then HERMES_HOME_DIR="$HOME/.hermes"; fi
|
|
41180
|
+
"$HERMES_PY" - "$HERMES_HOME_DIR/config.yaml" <<'PYEOF' || true
|
|
41181
|
+
import sys, os
|
|
41182
|
+
try:
|
|
41183
|
+
import yaml
|
|
41184
|
+
except Exception:
|
|
41185
|
+
sys.exit(0)
|
|
41186
|
+
p = sys.argv[1]
|
|
41187
|
+
data = {}
|
|
41188
|
+
if os.path.exists(p):
|
|
41189
|
+
try:
|
|
41190
|
+
with open(p) as f:
|
|
41191
|
+
data = yaml.safe_load(f) or {}
|
|
41192
|
+
except Exception:
|
|
41193
|
+
sys.exit(0)
|
|
41194
|
+
if not isinstance(data, dict):
|
|
41195
|
+
sys.exit(0)
|
|
41196
|
+
skills = data.get("skills")
|
|
41197
|
+
if not isinstance(skills, dict):
|
|
41198
|
+
skills = {}
|
|
41199
|
+
data["skills"] = skills
|
|
41200
|
+
ext = skills.get("external_dirs")
|
|
41201
|
+
if not isinstance(ext, list):
|
|
41202
|
+
ext = [] if not ext else [ext]
|
|
41203
|
+
skills["external_dirs"] = ext
|
|
41204
|
+
token = "$OASIS_HERMES_SKILLS_DIR"
|
|
41205
|
+
if token not in ext:
|
|
41206
|
+
ext.append(token)
|
|
41207
|
+
os.makedirs(os.path.dirname(p), exist_ok=True)
|
|
41208
|
+
tmp = p + ".oasis.tmp"
|
|
41209
|
+
with open(tmp, "w") as f:
|
|
41210
|
+
yaml.safe_dump(data, f, sort_keys=False, allow_unicode=True)
|
|
41211
|
+
os.replace(tmp, p)
|
|
41212
|
+
print("-> hermes config.yaml: skills.external_dirs += $OASIS_HERMES_SKILLS_DIR")
|
|
41213
|
+
PYEOF
|
|
41039
41214
|
fi
|
|
41040
41215
|
|
|
41041
41216
|
OASIS_DIR="$(pwd)"
|
|
@@ -41743,8 +41918,18 @@ var init_node_store = __esm({
|
|
|
41743
41918
|
});
|
|
41744
41919
|
|
|
41745
41920
|
// ../server/src/domains/collab/inbox.ts
|
|
41746
|
-
function buildInbox(model, me, leadOf) {
|
|
41921
|
+
function buildInbox(model, me, leadOf, resolveActor) {
|
|
41747
41922
|
const out = [];
|
|
41923
|
+
const gapItems = [];
|
|
41924
|
+
const actorRef = (id) => {
|
|
41925
|
+
const extra = resolveActor?.(id);
|
|
41926
|
+
return {
|
|
41927
|
+
id,
|
|
41928
|
+
...extra?.name ? { name: extra.name } : {},
|
|
41929
|
+
...extra?.role ? { role: extra.role } : {},
|
|
41930
|
+
...extra?.avatar ? { avatar: extra.avatar } : {}
|
|
41931
|
+
};
|
|
41932
|
+
};
|
|
41748
41933
|
const creatorOf = /* @__PURE__ */ new Map();
|
|
41749
41934
|
for (const a of model.artifacts.values()) {
|
|
41750
41935
|
if (a.type === "brief") creatorOf.set(a.workspace, a.owner);
|
|
@@ -41765,44 +41950,53 @@ function buildInbox(model, me, leadOf) {
|
|
|
41765
41950
|
summary: `\u5DE5\u5355\u7F3A\u4EBA\u624B\uFF1A${pendingRole[1]} \u89D2\u8272\u65E0\u5458\u5DE5\uFF0C\u300C${nodeLabel(a)}\u300D\u5F85\u6D3E\u2014\u2014\u8BF7\u8865\u4EBA\u6216 assign`
|
|
41766
41951
|
});
|
|
41767
41952
|
}
|
|
41768
|
-
const gapEscalationSeen = /* @__PURE__ */ new Set();
|
|
41769
41953
|
for (const a of model.artifacts.values()) {
|
|
41770
|
-
const escs = unresolvedEscalationsOf(model, a.id);
|
|
41771
|
-
if (escs.length === 0) continue;
|
|
41772
41954
|
const isOwnerRecipient = creatorOf.get(a.workspace) === me;
|
|
41773
41955
|
const isLeadRecipient = !!leadOf && leadOf(a.owner) === me;
|
|
41774
41956
|
if (!isOwnerRecipient && !isLeadRecipient) continue;
|
|
41775
|
-
const key = `${a.workspace}:${a.id}`;
|
|
41776
|
-
if (gapEscalationSeen.has(key)) continue;
|
|
41777
|
-
gapEscalationSeen.add(key);
|
|
41778
|
-
const who = isOwnerRecipient ? "\uFF08\u4F60\u662F\u5DE5\u5355\u5F52\u5C5E\u4EBA\uFF09" : "\uFF08\u4F60\u662F\u5361\u4F4F\u8282\u70B9 owner \u7684\u4E0A\u7EA7\uFF09";
|
|
41779
|
-
out.push({
|
|
41780
|
-
kind: "gap-escalation",
|
|
41781
|
-
artifactId: a.id,
|
|
41782
|
-
workspace: a.workspace,
|
|
41783
|
-
since: escs.at(-1).at,
|
|
41784
|
-
actionRequired: true,
|
|
41785
|
-
summary: `\u300C${nodeLabel(a)}\u300D\u88AB\u4E0A\u62A5\u9700\u4F60\u4ECB\u5165${who}\uFF1A${escs.at(-1).reason.slice(0, 100)}${escs.length > 1 ? `\uFF08\u5171 ${escs.length} \u6761\uFF09` : ""}`
|
|
41786
|
-
});
|
|
41787
|
-
}
|
|
41788
|
-
for (const a of model.artifacts.values()) {
|
|
41789
41957
|
const gaps = unresolvedGapsOf(model, a.id);
|
|
41790
|
-
|
|
41791
|
-
const isOwnerRecipient = creatorOf.get(a.workspace) === me;
|
|
41792
|
-
const isLeadRecipient = !!leadOf && leadOf(a.owner) === me;
|
|
41793
|
-
if (!isOwnerRecipient && !isLeadRecipient) continue;
|
|
41794
|
-
const key = `${a.workspace}:${a.id}`;
|
|
41795
|
-
if (gapEscalationSeen.has(key)) continue;
|
|
41796
|
-
gapEscalationSeen.add(key);
|
|
41958
|
+
const escalations = unresolvedEscalationsOf(model, a.id);
|
|
41797
41959
|
const who = isOwnerRecipient ? "\uFF08\u4F60\u662F\u5DE5\u5355\u5F52\u5C5E\u4EBA\uFF09" : "\uFF08\u4F60\u662F\u5361\u4F4F\u8282\u70B9 owner \u7684\u4E0A\u7EA7\uFF09";
|
|
41798
|
-
|
|
41799
|
-
|
|
41800
|
-
|
|
41801
|
-
|
|
41802
|
-
|
|
41803
|
-
|
|
41804
|
-
|
|
41805
|
-
|
|
41960
|
+
for (const gap of gaps) {
|
|
41961
|
+
const byGapId = escalations.filter((entry) => entry.gapId === gap.gapId);
|
|
41962
|
+
const samePartGaps = gaps.filter((candidate) => candidate.part === gap.part);
|
|
41963
|
+
const samePartEscalations = escalations.filter(
|
|
41964
|
+
(entry) => entry.gapId === void 0 && entry.part === gap.part
|
|
41965
|
+
);
|
|
41966
|
+
const partlessEscalations = escalations.filter(
|
|
41967
|
+
(entry) => entry.gapId === void 0 && entry.part === null
|
|
41968
|
+
);
|
|
41969
|
+
const exactEscalation = byGapId.at(-1) ?? (samePartGaps.length === 1 ? samePartEscalations.at(-1) : void 0) ?? (gaps.length === 1 ? partlessEscalations.at(-1) : void 0);
|
|
41970
|
+
gapItems.push({
|
|
41971
|
+
kind: "gap-escalation",
|
|
41972
|
+
artifactId: a.id,
|
|
41973
|
+
workspace: a.workspace,
|
|
41974
|
+
since: exactEscalation?.at ?? gap.lastReportedAt ?? model.lastOpAt.get(a.id) ?? "",
|
|
41975
|
+
actionRequired: true,
|
|
41976
|
+
summary: exactEscalation?.reason ?? gap.description,
|
|
41977
|
+
gap: {
|
|
41978
|
+
gapId: gap.gapId,
|
|
41979
|
+
// 契约:reporter 永远是 GapView.by(原始上报人);诊断者另见 diagnosis 文案,不覆盖署名。
|
|
41980
|
+
reporter: actorRef(gap.by),
|
|
41981
|
+
artifactLabel: nodeLabel(a),
|
|
41982
|
+
problem: gap.description,
|
|
41983
|
+
...exactEscalation ? { diagnosis: exactEscalation.reason } : {},
|
|
41984
|
+
priority: exactEscalation ? "decision_required" : "blocked",
|
|
41985
|
+
reportCount: gap.reportCount ?? 1
|
|
41986
|
+
}
|
|
41987
|
+
});
|
|
41988
|
+
}
|
|
41989
|
+
if (gaps.length === 0 && escalations.length > 0) {
|
|
41990
|
+
const last = escalations.at(-1);
|
|
41991
|
+
gapItems.push({
|
|
41992
|
+
kind: "gap-escalation",
|
|
41993
|
+
artifactId: a.id,
|
|
41994
|
+
workspace: a.workspace,
|
|
41995
|
+
since: last.at,
|
|
41996
|
+
actionRequired: true,
|
|
41997
|
+
summary: `\u300C${nodeLabel(a)}\u300D\u88AB\u4E0A\u62A5\u9700\u4F60\u4ECB\u5165${who}\uFF1A${last.reason.slice(0, 100)}${escalations.length > 1 ? `\uFF08\u5171 ${escalations.length} \u6761\uFF09` : ""}`
|
|
41998
|
+
});
|
|
41999
|
+
}
|
|
41806
42000
|
}
|
|
41807
42001
|
for (const r of listPendingReviews(model)) {
|
|
41808
42002
|
if (r.origin) continue;
|
|
@@ -41910,7 +42104,10 @@ function buildInbox(model, me, leadOf) {
|
|
|
41910
42104
|
if (gate) continue;
|
|
41911
42105
|
out.push({ kind: "assigned", artifactId: a.id, workspace: ws, since: model.lastOpAt.get(a.id) ?? "", actionRequired: true, summary: `\u5728\u9014\u5F85\u529E\uFF1A${label}` });
|
|
41912
42106
|
}
|
|
41913
|
-
|
|
42107
|
+
const rank = (item) => item.gap?.priority === "decision_required" ? 0 : 1;
|
|
42108
|
+
gapItems.sort((a, b2) => rank(a) - rank(b2) || a.since.localeCompare(b2.since));
|
|
42109
|
+
out.sort((x2, y) => x2.since < y.since ? 1 : x2.since > y.since ? -1 : 0);
|
|
42110
|
+
return [...gapItems, ...out];
|
|
41914
42111
|
}
|
|
41915
42112
|
var init_inbox = __esm({
|
|
41916
42113
|
"../server/src/domains/collab/inbox.ts"() {
|
|
@@ -41970,7 +42167,9 @@ var init_audit = __esm({
|
|
|
41970
42167
|
reopen: "\u91CD\u5F00",
|
|
41971
42168
|
seal: "\u5C01\u5B58",
|
|
41972
42169
|
link_input: "\u63A5\u4F9D\u8D56",
|
|
41973
|
-
report_gap: "\u7559\u7F3A\u53E3"
|
|
42170
|
+
report_gap: "\u7559\u7F3A\u53E3",
|
|
42171
|
+
hold: "\u6682\u505C\u6D3E\u53D1",
|
|
42172
|
+
release: "\u89E3\u963B"
|
|
41974
42173
|
};
|
|
41975
42174
|
}
|
|
41976
42175
|
});
|
|
@@ -42021,6 +42220,139 @@ var init_dashboard = __esm({
|
|
|
42021
42220
|
}
|
|
42022
42221
|
});
|
|
42023
42222
|
|
|
42223
|
+
// ../server/src/domains/collab/organization-usage.ts
|
|
42224
|
+
function emptyUsage() {
|
|
42225
|
+
return {
|
|
42226
|
+
input: 0,
|
|
42227
|
+
cache: 0,
|
|
42228
|
+
output: 0,
|
|
42229
|
+
tokens: 0,
|
|
42230
|
+
costUsdMicros: 0,
|
|
42231
|
+
costParts: { input: 0, cache: 0, output: 0 }
|
|
42232
|
+
};
|
|
42233
|
+
}
|
|
42234
|
+
function usageOf(run) {
|
|
42235
|
+
if (!run.effectiveModel?.trim()) return null;
|
|
42236
|
+
const input = Math.max(run.usage.inputTokens ?? 0, 0);
|
|
42237
|
+
const cache = Math.max(run.usage.cacheReadTokens ?? 0, 0) + Math.max(run.usage.cacheCreationTokens ?? 0, 0);
|
|
42238
|
+
const output = Math.max(run.usage.outputTokens ?? 0, 0);
|
|
42239
|
+
const tokens = input + cache + output;
|
|
42240
|
+
const costUsdMicros = Math.max(run.usage.costUsdMicros ?? 0, 0);
|
|
42241
|
+
const denominator = tokens || 1;
|
|
42242
|
+
const inputCost = Math.round(costUsdMicros * input / denominator);
|
|
42243
|
+
const cacheCost = Math.round(costUsdMicros * cache / denominator);
|
|
42244
|
+
return {
|
|
42245
|
+
input,
|
|
42246
|
+
cache,
|
|
42247
|
+
output,
|
|
42248
|
+
tokens,
|
|
42249
|
+
costUsdMicros,
|
|
42250
|
+
costParts: {
|
|
42251
|
+
input: inputCost,
|
|
42252
|
+
cache: cacheCost,
|
|
42253
|
+
output: costUsdMicros - inputCost - cacheCost
|
|
42254
|
+
}
|
|
42255
|
+
};
|
|
42256
|
+
}
|
|
42257
|
+
function addUsage(target, addition) {
|
|
42258
|
+
target.input += addition.input;
|
|
42259
|
+
target.cache += addition.cache;
|
|
42260
|
+
target.output += addition.output;
|
|
42261
|
+
target.tokens += addition.tokens;
|
|
42262
|
+
target.costUsdMicros += addition.costUsdMicros;
|
|
42263
|
+
target.costParts.input += addition.costParts.input;
|
|
42264
|
+
target.costParts.cache += addition.costParts.cache;
|
|
42265
|
+
target.costParts.output += addition.costParts.output;
|
|
42266
|
+
}
|
|
42267
|
+
function bucketFormatter(timeZone, kind) {
|
|
42268
|
+
return new Intl.DateTimeFormat("en-CA", {
|
|
42269
|
+
timeZone,
|
|
42270
|
+
year: "numeric",
|
|
42271
|
+
month: "2-digit",
|
|
42272
|
+
day: "2-digit",
|
|
42273
|
+
...kind === "hour" ? { hour: "2-digit", hourCycle: "h23" } : {}
|
|
42274
|
+
});
|
|
42275
|
+
}
|
|
42276
|
+
function bucketKey(formatter, value, kind) {
|
|
42277
|
+
const date3 = new Date(value);
|
|
42278
|
+
if (!Number.isFinite(date3.getTime())) return null;
|
|
42279
|
+
const parts = new Map(formatter.formatToParts(date3).map((part) => [part.type, part.value]));
|
|
42280
|
+
const day = `${parts.get("year")}-${parts.get("month")}-${parts.get("day")}`;
|
|
42281
|
+
return kind === "hour" ? `${day}T${parts.get("hour")}` : day;
|
|
42282
|
+
}
|
|
42283
|
+
function buildOrganizationUsageSummary(input) {
|
|
42284
|
+
const formatter = bucketFormatter(input.timeZone, input.bucketKind);
|
|
42285
|
+
const workorders = buildWorkorderSummaries(input.model);
|
|
42286
|
+
const workorderById = new Map(workorders.map((workorder) => [workorder.id, workorder]));
|
|
42287
|
+
const workspaceByArtifact = new Map([...input.model.artifacts.values()].map((artifact) => [artifact.id, artifact.workspace]));
|
|
42288
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
42289
|
+
const rows = /* @__PURE__ */ new Map();
|
|
42290
|
+
const total = emptyUsage();
|
|
42291
|
+
let unpricedRunCount = 0;
|
|
42292
|
+
for (const run of input.runs) {
|
|
42293
|
+
const usage = usageOf(run);
|
|
42294
|
+
if (!usage) {
|
|
42295
|
+
unpricedRunCount += 1;
|
|
42296
|
+
continue;
|
|
42297
|
+
}
|
|
42298
|
+
addUsage(total, usage);
|
|
42299
|
+
const key = bucketKey(formatter, run.startedAt, input.bucketKind);
|
|
42300
|
+
if (key) {
|
|
42301
|
+
const bucket = buckets.get(key) ?? { key, ...emptyUsage() };
|
|
42302
|
+
addUsage(bucket, usage);
|
|
42303
|
+
buckets.set(key, bucket);
|
|
42304
|
+
}
|
|
42305
|
+
const workspace = run.artifactId ? workspaceByArtifact.get(run.artifactId) : void 0;
|
|
42306
|
+
const workorder = workspace ? workorderById.get(workspace) : void 0;
|
|
42307
|
+
const rowId = workorder?.id ?? UNASSIGNED_ID;
|
|
42308
|
+
let row = rows.get(rowId);
|
|
42309
|
+
if (!row) {
|
|
42310
|
+
row = {
|
|
42311
|
+
id: rowId,
|
|
42312
|
+
title: workorder?.title ?? "\u672A\u5173\u8054\u5DE5\u5355",
|
|
42313
|
+
stage: workorder?.stage ?? "unassigned",
|
|
42314
|
+
usage: emptyUsage(),
|
|
42315
|
+
employees: [],
|
|
42316
|
+
share: 0,
|
|
42317
|
+
...!workorder ? { unassigned: true } : {}
|
|
42318
|
+
};
|
|
42319
|
+
rows.set(rowId, row);
|
|
42320
|
+
}
|
|
42321
|
+
addUsage(row.usage, usage);
|
|
42322
|
+
let employee = row.employees.find((item) => item.actorId === run.actorId);
|
|
42323
|
+
if (!employee) {
|
|
42324
|
+
employee = { actorId: run.actorId, usage: emptyUsage(), share: 0 };
|
|
42325
|
+
row.employees.push(employee);
|
|
42326
|
+
}
|
|
42327
|
+
addUsage(employee.usage, usage);
|
|
42328
|
+
}
|
|
42329
|
+
const workorderRows = [...rows.values()].map((row) => {
|
|
42330
|
+
row.share = total.tokens > 0 ? row.usage.tokens / total.tokens : 0;
|
|
42331
|
+
row.employees = row.employees.map((employee) => ({
|
|
42332
|
+
...employee,
|
|
42333
|
+
share: row.usage.tokens > 0 ? employee.usage.tokens / row.usage.tokens : 0
|
|
42334
|
+
})).sort((a, b2) => b2.usage.tokens - a.usage.tokens);
|
|
42335
|
+
return row;
|
|
42336
|
+
}).sort((a, b2) => {
|
|
42337
|
+
if (a.unassigned !== b2.unassigned) return a.unassigned ? 1 : -1;
|
|
42338
|
+
return b2.usage.tokens - a.usage.tokens;
|
|
42339
|
+
});
|
|
42340
|
+
return {
|
|
42341
|
+
total,
|
|
42342
|
+
buckets: [...buckets.values()].sort((a, b2) => a.key.localeCompare(b2.key)),
|
|
42343
|
+
workorders: workorderRows,
|
|
42344
|
+
unpricedRunCount
|
|
42345
|
+
};
|
|
42346
|
+
}
|
|
42347
|
+
var UNASSIGNED_ID;
|
|
42348
|
+
var init_organization_usage = __esm({
|
|
42349
|
+
"../server/src/domains/collab/organization-usage.ts"() {
|
|
42350
|
+
"use strict";
|
|
42351
|
+
init_workorders();
|
|
42352
|
+
UNASSIGNED_ID = "__unassigned__";
|
|
42353
|
+
}
|
|
42354
|
+
});
|
|
42355
|
+
|
|
42024
42356
|
// ../server/src/domains/collab/index.ts
|
|
42025
42357
|
async function resolveLead(registry2, me) {
|
|
42026
42358
|
const rec = await registry2.getActor(me);
|
|
@@ -42098,6 +42430,34 @@ function collabDomain(opts) {
|
|
|
42098
42430
|
return ctx;
|
|
42099
42431
|
}
|
|
42100
42432
|
return (router) => {
|
|
42433
|
+
router.get("/api/organization/usage-summary", async (req) => {
|
|
42434
|
+
if (!opts.trace) {
|
|
42435
|
+
return { status: 503, body: { error: { code: "no_trace_store", message: "\u8FD0\u884C\u7528\u91CF\u6570\u636E\u6E90\u672A\u63A5\u5165" } } };
|
|
42436
|
+
}
|
|
42437
|
+
const from = req.query.get("from");
|
|
42438
|
+
const to = req.query.get("to");
|
|
42439
|
+
const bucket = req.query.get("bucket");
|
|
42440
|
+
const timeZone = req.query.get("timeZone") ?? "UTC";
|
|
42441
|
+
if (!from || !to || bucket !== "hour" && bucket !== "day") {
|
|
42442
|
+
return { status: 400, body: { error: { code: "bad_request", message: "from / to / bucket(hour|day) \u5FC5\u586B" } } };
|
|
42443
|
+
}
|
|
42444
|
+
const fromMs = Date.parse(from);
|
|
42445
|
+
const toMs = Date.parse(to);
|
|
42446
|
+
if (!Number.isFinite(fromMs) || !Number.isFinite(toMs) || fromMs > toMs) {
|
|
42447
|
+
return { status: 400, body: { error: { code: "bad_request", message: "from / to \u65F6\u95F4\u8303\u56F4\u65E0\u6548" } } };
|
|
42448
|
+
}
|
|
42449
|
+
try {
|
|
42450
|
+
new Intl.DateTimeFormat("en", { timeZone }).format();
|
|
42451
|
+
} catch {
|
|
42452
|
+
return { status: 400, body: { error: { code: "bad_request", message: "timeZone \u65E0\u6548" } } };
|
|
42453
|
+
}
|
|
42454
|
+
const { kernel } = await resolveCtx(req.auth.companyId);
|
|
42455
|
+
const runs = await opts.trace.listUsageRuns({ from, to });
|
|
42456
|
+
return {
|
|
42457
|
+
status: 200,
|
|
42458
|
+
body: buildOrganizationUsageSummary({ model: kernel.model, runs, bucketKind: bucket, timeZone })
|
|
42459
|
+
};
|
|
42460
|
+
});
|
|
42101
42461
|
router.get("/api/workorders", async (req) => {
|
|
42102
42462
|
const { kernel, registry: registry2, artifacts } = await resolveCtx(req.auth.companyId);
|
|
42103
42463
|
const resolve5 = await buildResolver(registry2);
|
|
@@ -42165,8 +42525,13 @@ function collabDomain(opts) {
|
|
|
42165
42525
|
});
|
|
42166
42526
|
router.get("/api/inbox", async (req) => {
|
|
42167
42527
|
const { kernel, registry: registry2 } = await resolveCtx(req.auth.companyId);
|
|
42168
|
-
const leadOf = await
|
|
42169
|
-
|
|
42528
|
+
const [leadOf, resolveActor] = await Promise.all([
|
|
42529
|
+
buildLeadResolver(registry2),
|
|
42530
|
+
// 决策 0026 (c):gap 升级也发卡住节点 owner 的人类上级
|
|
42531
|
+
buildResolver(registry2)
|
|
42532
|
+
// 决策 0042:gap reporter 名册 join
|
|
42533
|
+
]);
|
|
42534
|
+
return { status: 200, body: { items: buildInbox(kernel.model, req.auth.actor, leadOf, resolveActor) } };
|
|
42170
42535
|
});
|
|
42171
42536
|
router.get("/api/audit", async (req) => {
|
|
42172
42537
|
const { oplog } = await resolveCtx(req.auth.companyId);
|
|
@@ -42227,12 +42592,14 @@ var init_collab = __esm({
|
|
|
42227
42592
|
init_inbox();
|
|
42228
42593
|
init_audit();
|
|
42229
42594
|
init_dashboard();
|
|
42595
|
+
init_organization_usage();
|
|
42230
42596
|
init_node_state();
|
|
42231
42597
|
init_workorders();
|
|
42232
42598
|
init_workorder_detail();
|
|
42233
42599
|
init_inbox();
|
|
42234
42600
|
init_audit();
|
|
42235
42601
|
init_dashboard();
|
|
42602
|
+
init_organization_usage();
|
|
42236
42603
|
init_planner();
|
|
42237
42604
|
}
|
|
42238
42605
|
});
|
|
@@ -42753,6 +43120,17 @@ var init_service4 = __esm({
|
|
|
42753
43120
|
const page = await this.store.listRuns(filter);
|
|
42754
43121
|
return { ...page, items: page.items.map((run) => this.withDisplayCost(run)) };
|
|
42755
43122
|
}
|
|
43123
|
+
/** 组织汇总专用瘦读:只取归属、时间和 usage,并统一补齐展示成本。 */
|
|
43124
|
+
async listUsageRuns(filter) {
|
|
43125
|
+
const rows = await this.store.listUsageRuns(filter);
|
|
43126
|
+
return rows.map((row) => ({
|
|
43127
|
+
...row,
|
|
43128
|
+
usage: {
|
|
43129
|
+
...row.usage,
|
|
43130
|
+
costUsdMicros: displayCostUsdMicros(row.effectiveModel, row.usage)
|
|
43131
|
+
}
|
|
43132
|
+
}));
|
|
43133
|
+
}
|
|
42756
43134
|
/** 按维度查聚合用量(per-session/per-agent plan §4)。store 未实现聚合时返回空。
|
|
42757
43135
|
* 读侧补全展示成本:adapter 未上报 cost 时按价格快照估算(与员工周统计同口径)。 */
|
|
42758
43136
|
async queryUsageStats(filter) {
|
|
@@ -45586,7 +45964,7 @@ var init_worker = __esm({
|
|
|
45586
45964
|
\u3010\u600E\u4E48\u5E72\u3011\u963B\u585E\u662F**\u75C7\u72B6**\u4E0D\u662F\u75C5\u3002\u7CFB\u7EDF\u5DF2\u5148\u673A\u68B0\u5206\u8BCA\u3001\u628A**\u80FD\u5B9A\u4F4D\u7684**\uFF08\u8D44\u6E90\u6392\u961F / \u8FD0\u884C\u65F6\u7194\u65AD / \u4EBA\u6CA1\u63A5 / \u7B49\u4E00\u4E2A\u5065\u5EB7\u5728\u4EA7\u7684\u4E0A\u6E38\uFF09\u8DEF\u7531\u7ED9\u4E86\u5BF9\u53E3\u5904\u7F6E\u8005\uFF1B\u8D70\u5230\u4F60\u8FD9\u513F\u7684\u662F\u9700\u8981\u4F60\u5224\u65AD\u7684\u90A3\u7C7B\u3002
|
|
45587
45965
|
\u2460 \u5148\u8BFB\u4E0B\u65B9"\u75C5\u5386"\u6BB5 + \u5FC5\u8981\u65F6 \`oasis status/content <id>\` \u6CBF\u4F9D\u8D56\u94FE\u6478\u6E05\u8BED\u4E49\u6839\uFF1B
|
|
45588
45966
|
\u2461 \u51FA**\u6700\u7701\u4E8B\u3001\u6700\u6CBB\u672C**\u7684\u4E00\u624B\uFF1A\u80FD\u8865\u4E0A\u4E0B\u6587\u5C31\u522B\u6539\u56FE\u3001\u80FD\u8FDE\u65E2\u6709\u5C31\u522B\u65B0\u5EFA\u3001\u80FD\u81EA\u5DF1\u89E3\u51B3\u5C31\u522B\u60CA\u52A8\u4EBA\uFF1B\u9500\u6BC1\u6027\u6539\u56FE\uFF08\u65AD\u8FB9/\u5E9F\u8282\u70B9/\u6539\u6D3E\uFF09apply \u65F6\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u8F6C\u6210\u5F85\u4EBA\u786E\u8BA4\u7684\u63D0\u6848\uFF0C\u4F60 stage \u597D\u8BB2\u6E05\u5F71\u54CD\u5373\u53EF\u3002
|
|
45589
|
-
\u2462 **\u6536\u5C3E\u5FC5\u987B\u663E\u5F0F**\u2014\u2014\u628A\u8FD9\u6761\u7EBF\u63A8\u52A8\u4E86\u3001\u6216\u5224\u65AD\u8BE5\u7531 owner \u7EE7\u7EED \u2192 \`oasis resolve-gap --gap <id> --note "<\u4F60\u505A\u4E86\u4EC0\u4E48 / owner \u8BE5\u600E\u4E48\u63A5>"\`\uFF08**\u4E0D\u53D1\u8FD9\u53E5 gap \u4E0D\u89E3\u3001owner \u4E0D\u9192**\uFF09\uFF1B\u591F\u4E0D\u7740 / \u8981\u4EBA\u5B9A \u2192 \`oasis escalate <\u8282\u70B9> --reason "<\u6839\u56E0 + \u8981\u4EBA\u505A\u4EC0\u4E48>"\` \u4E0A\u62A5\u3002**\u7EDD\u4E0D\u7A7A\u624B\u9000\u51FA**\uFF1A\u4EC0\u4E48\u90FD\u4E0D\u505A\u5730\u6536\u5DE5 = \u628A\u8282\u70B9\u6C38\u4E45\u667E\u6B7B\uFF0C\u6700\u7CDF\u3002`;
|
|
45967
|
+
\u2462 **\u6536\u5C3E\u5FC5\u987B\u663E\u5F0F**\u2014\u2014\u628A\u8FD9\u6761\u7EBF\u63A8\u52A8\u4E86\u3001\u6216\u5224\u65AD\u8BE5\u7531 owner \u7EE7\u7EED \u2192 \`oasis resolve-gap --gap <id> --note "<\u4F60\u505A\u4E86\u4EC0\u4E48 / owner \u8BE5\u600E\u4E48\u63A5>"\`\uFF08**\u4E0D\u53D1\u8FD9\u53E5 gap \u4E0D\u89E3\u3001owner \u4E0D\u9192**\uFF09\uFF1B\u591F\u4E0D\u7740 / \u8981\u4EBA\u5B9A \u2192 \`oasis escalate <\u8282\u70B9> --reason "<\u6839\u56E0 + \u8981\u4EBA\u505A\u4EC0\u4E48>" --gap <gapId> [--part <part>]\` \u4E0A\u62A5\uFF08**\u5FC5\u987B\u5E26 --gap**\uFF0C\u6709 part \u65F6\u4E00\u5E76\u5E26\u4E0A\uFF09\u3002**\u7EDD\u4E0D\u7A7A\u624B\u9000\u51FA**\uFF1A\u4EC0\u4E48\u90FD\u4E0D\u505A\u5730\u6536\u5DE5 = \u628A\u8282\u70B9\u6C38\u4E45\u667E\u6B7B\uFF0C\u6700\u7CDF\u3002`;
|
|
45590
45968
|
CONVERSATIONAL_ETHOS = `\u4F60\u662F\u8FD9\u4E2A\u5DE5\u5355\u7684**\u7BA1\u7406\u8005**\uFF0C\u6B63\u5728\u548C\u5DE5\u5355\u53D1\u8D77\u4EBA\u5BF9\u8BDD\u3001\u4E00\u8D77\u628A\u5B83\u5F80\u524D\u63A8\u3002\u4F60\u7684\u624B\u6BB5\u5F88\u5BBD\uFF1A\u5E2E\u5404\u8282\u70B9 owner \u626B\u6E05\u969C\u788D\u3001\u7B54\u7591\u3001\u8865\u4E0A\u4E0B\u6587\u3001**\u81EA\u5DF1\u8DD1\u5206\u6790 / \u62C9\u6570\u636E / \u5199\u8BF4\u660E**\u3001\u534F\u8C03\u8DE8\u8282\u70B9\u3001\u6309\u9700\u6539\u56FE\uFF08\u8FDE\u8FB9 / \u65B0\u5EFA / \u53BB\u91CD / \u6539\u6D3E / \u6539\u6807\u9898\u4E0E brief\uFF09\u3001\u4E3A\u53D1\u8D77\u4EBA\u89E3\u7B54\u6216\u4EE3\u529E\u5DE5\u5355\u8303\u56F4\u5185\u7684\u4E8B\u52A1\u3002**\u4F60\u6709\u4F1A\u8BDD\u5185\u7684\u5168\u90E8\u80FD\u529B\uFF08Bash / \u6587\u4EF6 / \u4EFB\u610F CLI\uFF09\uFF0C\u653E\u5F00\u7528\u3002\u552F\u4E00\u786C\u8FB9\u754C\uFF1A\u4E0D\u66FF owner \u5199\u8282\u70B9\u7684\u4EA7\u7269\u6B63\u6587**\uFF08\u8865\u4E0A\u4E0B\u6587\u3001\u7ED9\u53C2\u8003\u90FD\u884C\uFF0C\u522B\u4EE3\u7B14\uFF09\u3002
|
|
45591
45969
|
- **\u5148\u8BCA\u65AD\u3001\u7ED9\u65B9\u6848\u3001\u95EE ta**\uFF1A\u770B\u6E05\u5361\u5728\u54EA\uFF0C\u7ED9 1\u20132 \u4E2A\u53EF\u884C\u505A\u6CD5\u3001\u8BB2\u6E05\u5404\u81EA\u5F71\u54CD\uFF0C\u95EE ta \u91C7\u7528\u54EA\u4E2A\u2014\u2014\u522B\u81EA\u4F5C\u4E3B\u5F20\u95F7\u5934\u6539\u3002\u660E\u663E\u4E14\u4F4E\u98CE\u9669\u7684\u53EF\u4EE5\u5148\u505A\u4E00\u7248\u518D\u8BF4\u3002
|
|
45592
45970
|
- **\u6539\u52A8\u5206\u4E24\u7C7B**\uFF08\u6388\u6743\u5355\u4F4D\u662F diff\uFF09\uFF1A\u52A0\u8FB9 / \u65B0\u589E\u8282\u70B9 / \u6539\u6807\u9898 brief \u8FD9\u7C7B**\u53EF\u9006\u7684**\uFF08B\uFF09\uFF0C\`stage\`\u2192\`apply\` \u76F4\u63A5\u843D\u3001\u505A\u5B8C\u628A\u52A8\u4E86\u4EC0\u4E48\u544A\u8BC9 ta\uFF1B\u65AD\u8FB9 / \u5E9F\u8282\u70B9 / \u6539\u6D3E / \u64A4\u90E8\u4EF6\u8FD9\u7C7B**\u9500\u6BC1\u6027\u7684**\uFF08C\uFF09\uFF0C\u4F60 \`apply\` \u65F6\u7CFB\u7EDF\u4F1A**\u81EA\u52A8\u8F6C\u6210\u4E00\u4EFD\u5F85\u786E\u8BA4\u63D0\u6848**\u653E\u5230 ta \u9762\u524D\u7684\u300C\u5F85\u786E\u8BA4\u53D8\u66F4\u300D\u5361\u2014\u2014\u4F60 **stage \u597D\u3001\u628A\u5F71\u54CD\u8BB2\u6E05\u695A\u5C31\u884C\uFF0C\u522B\u81EA\u5DF1\u786C\u843D**\uFF0C\u7531 ta \u5728\u5361\u4E0A\u786E\u8BA4\u6216\u9A73\u56DE\u3002
|
|
@@ -45613,7 +45991,7 @@ var init_worker = __esm({
|
|
|
45613
45991
|
|
|
45614
45992
|
\u3010\u6536\u5C3E gap / \u4E0A\u62A5\u5361\u70B9\u3011\uFF08\u4E0D\u8D70\u6682\u5B58\u7F13\u51B2\uFF0C\u76F4\u63A5\u751F\u6548\uFF09
|
|
45615
45993
|
- \`oasis resolve-gap --gap <gapId> --note "<\u4F60\u505A\u4E86\u4EC0\u4E48 / owner \u8BE5\u600E\u4E48\u7EE7\u7EED>"\` \u2014\u2014 **\u4FEE\u5B8C\u4E00\u6761 gap \u5FC5\u987B\u663E\u5F0F\u6536\u5C3E**\uFF1A\u8FDE\u8FB9 / \u5EFA\u8282\u70B9**\u4E0D\u4F1A\u81EA\u52A8\u89E3 gap**\uFF0C\u4E0D\u53D1\u8FD9\u53E5\u62A5 gap \u7684 owner \u9192\u4E0D\u8FC7\u6765\u3002\`--gap\` = \u90A3\u6761 gap \u7684 id\uFF08reconcile \u4EFB\u52A1\u91CC\u4F1A\u7ED9\u4F60\uFF09\uFF1B\`--note\` \u539F\u6837\u7ED9 owner \u770B\uFF0C\u5199\u4EBA\u8BDD\u3002
|
|
45616
|
-
- \`oasis escalate <id> --reason "<\u7F3A\u4EC0\u4E48\u80FD\u529B / \u8981\u4EBA\u66FF\u4F60\u5B9A\u6216\u505A\u4EC0\u4E48>"\` \u2014\u2014 **\u641E\u4E0D\u5B9A / \u62FF\u4E0D\u51C6 / \u6839\u56E0\u4E0D\u5728\u56FE\u4E0A\u5C31\u4E0A\u62A5\u53D1\u8D77\u4EBA**\uFF08\u522B\u6C89\u9ED8\u9000\u51FA\uFF09\u3002\`<id>\` = \u5361\u4F4F\u7684\u8282\u70B9\uFF1B\`--reason\` \u8BF4\u6E05\u4F60\u5224\u65AD\u7684\u6839\u56E0\u548C\u9700\u8981\u4EBA\u505A\u4EC0\u4E48\u3002\u7CFB\u7EDF\u4F1A\u767B\u8BB0\u672C\u6B21\u4F1A\u8BDD\uFF0C\u4EBA\u56DE\u8BDD\u65F6\u4F60\u88AB**\u63A5\u7740\u5524\u8D77\u3001\u5E26\u4E0A\u4E0B\u6587\u7EE7\u7EED\u89E3**\u3002
|
|
45994
|
+
- \`oasis escalate <id> --reason "<\u7F3A\u4EC0\u4E48\u80FD\u529B / \u8981\u4EBA\u66FF\u4F60\u5B9A\u6216\u505A\u4EC0\u4E48>" --gap <gapId> [--part <part>]\` \u2014\u2014 **\u641E\u4E0D\u5B9A / \u62FF\u4E0D\u51C6 / \u6839\u56E0\u4E0D\u5728\u56FE\u4E0A\u5C31\u4E0A\u62A5\u53D1\u8D77\u4EBA**\uFF08\u522B\u6C89\u9ED8\u9000\u51FA\uFF09\u3002\`<id>\` = \u5361\u4F4F\u7684\u8282\u70B9\uFF1B\`--reason\` \u8BF4\u6E05\u4F60\u5224\u65AD\u7684\u6839\u56E0\u548C\u9700\u8981\u4EBA\u505A\u4EC0\u4E48\u3002\u7CFB\u7EDF\u4F1A\u767B\u8BB0\u672C\u6B21\u4F1A\u8BDD\uFF0C\u4EBA\u56DE\u8BDD\u65F6\u4F60\u88AB**\u63A5\u7740\u5524\u8D77\u3001\u5E26\u4E0A\u4E0B\u6587\u7EE7\u7EED\u89E3**\u3002
|
|
45617
45995
|
|
|
45618
45996
|
\u3010\u8BFB\u56FE\u3011\uFF08\u90FD\u8BFB**\u771F\u56FE**\uFF0C\u4E0E\u522B\u7684 agent \u540C\u8BED\u4E49\uFF0C\u4E0D\u53D7\u4F60\u6682\u5B58\u5F71\u54CD\uFF09
|
|
45619
45997
|
- \`oasis graph\` \u2014\u2014 \u5168\u56FE\u4F9D\u8D56\u8FB9\uFF08\u8C01\u8FDE\u8C01\uFF09\uFF1B\`oasis ls\` \u2014\u2014 \u5168\u4EA7\u7269\u5217\u8868\uFF08id / type / owner / head / \u961F\u5217 / \u72B6\u6001\u6807\uFF09\u3002\u5148\u7528\u5B83\u4EEC\u5EFA\u7ACB\u5168\u5C40\u56FE\u666F\u3002
|
|
@@ -45779,7 +46157,7 @@ ${SHARED_GRAPH_BODY}`;
|
|
|
45779
46157
|
const out = [];
|
|
45780
46158
|
for (const artifact of this.deps.kernel.model.artifacts.values()) {
|
|
45781
46159
|
for (const g2 of unresolvedGapsOf(this.deps.kernel.model, artifact.id)) {
|
|
45782
|
-
out.push({ artifactId: artifact.id, gapId: g2.gapId, description: g2.description });
|
|
46160
|
+
out.push({ artifactId: artifact.id, gapId: g2.gapId, description: g2.description, part: g2.part });
|
|
45783
46161
|
}
|
|
45784
46162
|
}
|
|
45785
46163
|
return out;
|
|
@@ -45814,6 +46192,10 @@ ${SHARED_GRAPH_BODY}`;
|
|
|
45814
46192
|
}
|
|
45815
46193
|
return lines.length > 0 ? lines.join("\n") : "\uFF08\u56FE\u91CC\u6682\u65E0\u5176\u5B83\u4EA7\u7269\uFF09";
|
|
45816
46194
|
}
|
|
46195
|
+
escalateCmd(gap, reasonPlaceholder) {
|
|
46196
|
+
const partFlag = gap.part ? ` --part ${gap.part}` : "";
|
|
46197
|
+
return `oasis escalate ${gap.artifactId} --reason "${reasonPlaceholder}" --gap ${gap.gapId}${partFlag}`;
|
|
46198
|
+
}
|
|
45817
46199
|
async reconcile(gap) {
|
|
45818
46200
|
const gapper = this.deps.kernel.model.artifacts.get(gap.artifactId);
|
|
45819
46201
|
const brief = gapper?.title ?? gapper?.description ?? "";
|
|
@@ -45826,7 +46208,7 @@ ${SHARED_GRAPH_BODY}`;
|
|
|
45826
46208
|
`## \u8981\u4F60\u505A\u4EC0\u4E48\uFF08\u5148\u5206\u8BCA\uFF09`,
|
|
45827
46209
|
`\u8FD9\u4E2A\u9700\u6C42**\u53EF\u80FD\u662F\u4EFB\u4F55\u963B\u585E**\u2014\u2014\u7F3A\u4E0A\u6E38\u8F93\u5165 / \u7F3A\u4FE1\u606F / \u8981\u4EBA\u51B3\u7B56 / \u7F3A\u80FD\u529B\uFF08\u5982\u67D0\u4E2A\u5DE5\u5177\u88C5\u4E0D\u4E0A\uFF09\u3002\u4F60\u5206\u8BCA\uFF1A`,
|
|
45828
46210
|
`- **\u56FE\u4E0A\u80FD\u4FEE\u7684**\uFF08\u7F3A\u4E0A\u6E38/\u8BE5\u8FDE\u6CA1\u8FDE\uFF09\u2192 \u5224\u65AD\u8FDE\u65E2\u6709\uFF08\u53BB\u91CD\uFF0C\u4F18\u5148\uFF09\u8FD8\u662F\u65B0\u5EFA\u2014\u2014\u6BD4\u5BF9\u4E0B\u9762\u73B0\u6709\u4EA7\u7269\u7684 brief\uFF1B\u62FF\u4E0D\u51C6\u5C31 \`oasis content <\u5019\u9009id>\` \u8BFB\u6B63\u6587\u3002`,
|
|
45829
|
-
`- **\u56FE\u4E0A\u4FEE\u4E0D\u4E86\u7684**\uFF08\u8981\u4EBA\u62CD\u677F / \u7F3A\u73AF\u5883\u80FD\u529B\uFF09\u2192
|
|
46211
|
+
`- **\u56FE\u4E0A\u4FEE\u4E0D\u4E86\u7684**\uFF08\u8981\u4EBA\u62CD\u677F / \u7F3A\u73AF\u5883\u80FD\u529B\uFF09\u2192 \`${this.escalateCmd(gap, "<\u8981\u4EBA\u5B9A/\u505A\u4EC0\u4E48>")}\`\uFF0C\u7CFB\u7EDF\u4F1A\u5347\u7EA7\u7ED9\u53D1\u8D77\u4EBA\u3001\u4F60\u4E4B\u540E\u53EF\u80FD\u5728\u5BF9\u8BDD\u91CC\u88AB ta \u95EE\u5230\uFF0C\u7136\u540E\u6536\u5DE5\u3002`,
|
|
45830
46212
|
``,
|
|
45831
46213
|
`## \u73B0\u6709\u4EA7\u7269\uFF08id / type / \u662F\u5426\u5DF2\u4EA4\u4ED8 / brief / \u4F9D\u8D56\uFF09`,
|
|
45832
46214
|
this.structDigest(gap.artifactId),
|
|
@@ -45840,11 +46222,11 @@ ${SHARED_GRAPH_BODY}`;
|
|
|
45840
46222
|
`- \u786E\u65E0\u5339\u914D\u3001\u65B0\u9700\u6C42 \u2192 \`oasis spawn --type <\u7C7B\u578B> --title "<\u77ED\u540D>" --brief "${gap.description}"\`\uFF08\u4E0D\u7528\u7ED9 id\uFF09+ \`oasis link ${gap.artifactId} --to <\u7C7B\u578B>:<\u77ED\u540D>\`\uFF08\u6309 type:title \u5F15\u521A\u5EFA\u7684\uFF09\u2192 apply\u3002`,
|
|
45841
46223
|
`- **\u4FEE\u5B8C\u624D\u6536\u5C3E**\uFF1A\`oasis resolve-gap --gap ${gap.gapId} --note "<\u4F60\u505A\u4E86\u4EC0\u4E48 / owner \u8BE5\u600E\u4E48\u7EE7\u7EED>"\`\u2014\u2014**\u8FDE\u8FB9\u4E0D\u4F1A\u81EA\u52A8\u89E3 gap**\uFF0C\u4E0D\u53D1\u8FD9\u53E5 owner \u9192\u4E0D\u8FC7\u6765\uFF1Bnote \u4F1A\u539F\u6837\u7ED9 owner \u770B\uFF0C\u5199\u4EBA\u8BDD\u3002`,
|
|
45842
46224
|
`**B. \u8981\u4EBA**\uFF08\u51B3\u7B56/\u80FD\u529B\u95EE\u9898\uFF0C\u56FE\u4E0A\u4FEE\u4E0D\u4E86\uFF09\uFF1A`,
|
|
45843
|
-
`-
|
|
46225
|
+
`- \`${this.escalateCmd(gap, "<\u8981\u4EBA\u5B9A/\u505A\u4EC0\u4E48>")}\` \u540E**\u76F4\u63A5\u6536\u5DE5**\u3002`,
|
|
45844
46226
|
`- \u26A0\uFE0F **\u7EDD\u4E0D\u8981\u5BF9\u8FD9\u6761 gap \u8C03 resolve-gap**\u2014\u2014resolve-gap \u7684\u542B\u4E49\u662F"**\u9700\u6C42\u5DF2\u6EE1\u8DB3\u3001\u8BA9 owner \u590D\u5DE5**"\uFF0C\u4E0D\u662F"\u6211\u5206\u8BCA\u5B8C\u4E86"\u7684\u767B\u8BB0\u3002\u73B0\u5728\u9700\u6C42\u8FD8\u6CA1\u6EE1\u8DB3\uFF1Agap \u4FDD\u6301 open \u624D\u662F"\u8282\u70B9\u7EE7\u7EED\u7B49\u5F85"\u7684\u6B63\u786E\u72B6\u6001\uFF1B\u63D0\u524D resolve \u4F1A\u8BA9 owner \u5728\u5565\u90FD\u6CA1\u53D8\u7684\u60C5\u51B5\u4E0B\u88AB\u53EB\u9192\u767D\u8DD1\u3001\u8FD8\u4F1A\u628A\u4F60\u7684\u4E0A\u62A5\u4ECE\u4EBA\u7684\u6536\u4EF6\u7BB1\u91CC\u62B9\u6389\u3002\u4EBA\u7B54\u590D\u540E\uFF08\u591A\u534A\u5728\u5BF9\u8BDD\u91CC\u627E\u4F60\uFF09\u4F60\u518D\u4FEE\u56FE\u3001\u90A3\u65F6\u624D resolve-gap\u3002`,
|
|
45845
46227
|
`**\u5176\u5B83**\uFF1A`,
|
|
45846
46228
|
`- \u987A\u624B\u53D1\u73B0\u4E24\u4E2A\u4EA7\u7269\u91CD\u590D \u2192 **\u5148\u628A\u89E3 gap \u90A3\u7B14\uFF08\u542B resolve-gap\uFF09\u5355\u72EC\u6536\u6389**\uFF0C\u518D\u53E6\u8D77\u4E00\u7B14\uFF1A\`oasis edit\` \u6269\u4FDD\u7559\u4EF6 brief + \`oasis link\` \u6539\u8FDE\u6D88\u8D39\u8005 + \`oasis cancel\` \u5E9F\u5F03\u4EF6\uFF0C\u4E00\u8D77\u6682\u5B58\u518D apply\u3002`,
|
|
45847
|
-
`- **\u62FF\u4E0D\u51C6\u4FEE\u6CD5 \u2192 \u4E00\u5F8B\u627E\u4EBA\uFF0C\u7EDD\u4E0D\u6C89\u9ED8\u9000\u51FA**\uFF1A
|
|
46229
|
+
`- **\u62FF\u4E0D\u51C6\u4FEE\u6CD5 \u2192 \u4E00\u5F8B\u627E\u4EBA\uFF0C\u7EDD\u4E0D\u6C89\u9ED8\u9000\u51FA**\uFF1A\`${this.escalateCmd(gap, "<\u4F60\u5361\u5728\u54EA\u3001\u9700\u8981\u4EBA\u66FF\u4F60\u5B9A/\u505A\u4EC0\u4E48>")}\` \u628A\u7591\u8651\u4E0A\u62A5\u7ED9\u53D1\u8D77\u4EBA\u2014\u2014\u7CFB\u7EDF\u4F1A\u767B\u8BB0\u672C\u6B21\u4F1A\u8BDD\uFF0C\u4EBA\u56DE\u8BDD\u65F6\u4F60\u88AB\u63A5\u7740\u5524\u8D77\u3001\u5E26\u7740\u4E0A\u4E0B\u6587\u7EE7\u7EED\u89E3\u3002**\u6CA1\u6709"\u8FD8\u4E0D\u5230\u8981\u4EBA\u7684\u7A0B\u5EA6"\u8FD9\u4E00\u6863\uFF1A\u6539\u4E0D\u52A8\u53C8\u62FF\u4E0D\u51C6\uFF0C\u5C31\u662F\u8981\u4EBA\u3002\u4EC0\u4E48\u90FD\u4E0D\u505A\u5730\u6536\u5DE5 = \u628A\u8282\u70B9\u6C38\u4E45\u667E\u6B7B\uFF0C\u6700\u7CDF\u3002**`
|
|
45848
46230
|
].join("\n");
|
|
45849
46231
|
const subjectKey = `gap:${gap.gapId}`;
|
|
45850
46232
|
const emptyCount = this.emptyHandedGap.get(subjectKey) ?? 0;
|
|
@@ -45854,7 +46236,7 @@ ${SHARED_GRAPH_BODY}`;
|
|
|
45854
46236
|
`## \u26A0\uFE0F \u63A5\u4E0A\u4E00\u8F6E\u2014\u2014\u4F60\u4E0A\u6B21\u88AB\u5524\u8D77\u5374**\u7A7A\u624B\u9000\u51FA\u4E86**\uFF08\u65E2\u6CA1 resolve-gap \u4E5F\u6CA1 escalate\uFF09`,
|
|
45855
46237
|
`\u8FD9\u6761\u7EBF\u8FD8\u5361\u7740\u3002\u8FD9\u4E00\u8F6E\u4F60**\u5FC5\u987B**\u4E8C\u9009\u4E00\u6536\u53E3\uFF0C\u4E0D\u8BB8\u518D\u7A7A\u624B\u9000\u51FA\uFF1A`,
|
|
45856
46238
|
`- \u5DF2\u628A\u8FD9\u6761\u7EBF\u63A8\u52A8 / \u5224\u65AD\u8BE5 owner \u63A5\u624B \u2192 \`oasis resolve-gap --gap ${gap.gapId} --note "<\u4F60\u505A\u4E86\u4EC0\u4E48 / owner \u600E\u4E48\u63A5>"\``,
|
|
45857
|
-
`- \u591F\u4E0D\u7740 / \u8981\u4EBA\u62CD\u677F \u2192
|
|
46239
|
+
`- \u591F\u4E0D\u7740 / \u8981\u4EBA\u62CD\u677F \u2192 \`${this.escalateCmd(gap, "<\u6839\u56E0 + \u8981\u4EBA\u505A\u4EC0\u4E48>")}\``,
|
|
45858
46240
|
`\u4E0D\u53D1\u8FD9\u4E24\u53E5\u4E4B\u4E00\uFF0Cgap \u6C38\u8FDC\u89E3\u4E0D\u5F00\u3001owner \u6C38\u8FDC\u9192\u4E0D\u8FC7\u6765\u3002**\u8FD9\u662F\u6700\u540E\u4E00\u6B21\u673A\u4F1A\u2014\u2014\u518D\u7A7A\u624B\uFF0C\u7CFB\u7EDF\u4F1A\u66FF\u4F60\u4E0A\u62A5\u7ED9\u4EBA\u3002**`
|
|
45859
46241
|
].join("\n");
|
|
45860
46242
|
const resumeId = emptyCount > 0 ? this.emptyHandedSession.get(subjectKey) : void 0;
|
|
@@ -45877,7 +46259,9 @@ ${SHARED_GRAPH_BODY}`;
|
|
|
45877
46259
|
await this.deps.kernel.escalate({
|
|
45878
46260
|
artifactId: gap.artifactId,
|
|
45879
46261
|
actor: run.managerActorId,
|
|
45880
|
-
reason: `\u7BA1\u7406\u8005\u88AB\u7CFB\u7EDF\u5524\u8D77 ${n} \u8F6E\u4ECD\u672A\u6536\u53E3\uFF08\u65E2\u672A resolve-gap \u4E5F\u672A escalate\uFF09\u2014\u2014\u7CFB\u7EDF\u4EE3\u4E3A\u4E0A\u62A5\uFF0C\u8BF7\u4F60\u5904\u7406\u8FD9\u6761\u5361\u70B9\uFF1A${gap.description}
|
|
46262
|
+
reason: `\u7BA1\u7406\u8005\u88AB\u7CFB\u7EDF\u5524\u8D77 ${n} \u8F6E\u4ECD\u672A\u6536\u53E3\uFF08\u65E2\u672A resolve-gap \u4E5F\u672A escalate\uFF09\u2014\u2014\u7CFB\u7EDF\u4EE3\u4E3A\u4E0A\u62A5\uFF0C\u8BF7\u4F60\u5904\u7406\u8FD9\u6761\u5361\u70B9\uFF1A${gap.description}`,
|
|
46263
|
+
gapId: gap.gapId,
|
|
46264
|
+
...gap.part ? { part: gap.part } : {}
|
|
45881
46265
|
}).catch((e) => this.deps.log?.(`[coordinator] ${gap.artifactId} \u81EA\u52A8 escalate \u5931\u8D25\uFF1A${String(e)}`));
|
|
45882
46266
|
this.deps.log?.(`[coordinator] ${gap.artifactId} \u7BA1\u7406\u8005\u8FDE\u7EED ${n} \u8F6E\u7A7A\u624B \u2192 \u7CFB\u7EDF\u81EA\u52A8 escalate \u4EA4\u53D1\u8D77\u4EBA`);
|
|
45883
46267
|
this.emptyHandedGap.delete(subjectKey);
|
|
@@ -54496,6 +54880,22 @@ var PostgresTraceStore = class _PostgresTraceStore {
|
|
|
54496
54880
|
const nextCursor = hasMore ? page[page.length - 1]?.id ?? null : null;
|
|
54497
54881
|
return { items: page, nextCursor };
|
|
54498
54882
|
}
|
|
54883
|
+
async listUsageRuns(filter) {
|
|
54884
|
+
const r = await this.pool.query(
|
|
54885
|
+
`SELECT actor_id, artifact_id, started_at, effective_model, usage
|
|
54886
|
+
FROM ${this.s}.agent_runs
|
|
54887
|
+
WHERE usage IS NOT NULL AND started_at >= $1 AND started_at <= $2
|
|
54888
|
+
ORDER BY started_at`,
|
|
54889
|
+
[filter.from, filter.to]
|
|
54890
|
+
);
|
|
54891
|
+
return r.rows.map((row) => ({
|
|
54892
|
+
actorId: row.actor_id,
|
|
54893
|
+
artifactId: row.artifact_id ?? null,
|
|
54894
|
+
startedAt: row.started_at,
|
|
54895
|
+
effectiveModel: row.effective_model ?? null,
|
|
54896
|
+
usage: row.usage
|
|
54897
|
+
}));
|
|
54898
|
+
}
|
|
54499
54899
|
/* ---------- 事件流(单写者 seq + 幂等吸收)---------- */
|
|
54500
54900
|
async appendEvent(runId, event) {
|
|
54501
54901
|
return (await this.appendEvents(runId, [event]))[0];
|
|
@@ -56414,6 +56814,7 @@ async function startServe(opts) {
|
|
|
56414
56814
|
collabDomain({
|
|
56415
56815
|
kernel,
|
|
56416
56816
|
oplog,
|
|
56817
|
+
trace: trace.service,
|
|
56417
56818
|
registry: registryStore,
|
|
56418
56819
|
artifacts: artifactStateStore,
|
|
56419
56820
|
dispatchJournal: () => journalRing.slice(-200),
|
|
@@ -57088,8 +57489,17 @@ ${detail}` : genericLine));
|
|
|
57088
57489
|
if (f2.action !== "produce") continue;
|
|
57089
57490
|
const cur = kernel.model.artifacts.get(f2.artifactId);
|
|
57090
57491
|
if (!cur) continue;
|
|
57091
|
-
if (now - Date.parse(f2.fusedAt)
|
|
57092
|
-
|
|
57492
|
+
if (now - Date.parse(f2.fusedAt) <= agentMs) continue;
|
|
57493
|
+
if (unresolvedEscalationsOf(kernel.model, f2.artifactId).length > 0) continue;
|
|
57494
|
+
if (isHeld(kernel.model, f2.artifactId)) continue;
|
|
57495
|
+
const reason = `agent \u8FDE\u8D25\u7194\u65AD\u540E ${Math.round(agentMs / 6e4)} \u5206\u949F\u65E0\u4EBA\u5904\u7406\uFF08\u8FDE\u8D25 ${f2.consecutiveFailures} \u6B21\uFF09`;
|
|
57496
|
+
try {
|
|
57497
|
+
await kernel.escalate({ artifactId: f2.artifactId, actor: SYSTEM_ACTOR, reason });
|
|
57498
|
+
await kernel.hold({ artifactId: f2.artifactId, actor: SYSTEM_ACTOR, source: "sla", reason });
|
|
57499
|
+
journal({ kind: "escalated", at: (/* @__PURE__ */ new Date()).toISOString(), artifactId: f2.artifactId, from: cur.owner, to: cur.owner, reason });
|
|
57500
|
+
console.log(`[sla] ${f2.artifactId} \u7194\u65AD\u5347\u7EA7\uFF1A\u843D escalate + hold\uFF08owner \u4FDD\u6301 ${cur.owner}\uFF0C\u4E0D\u593A\uFF09`);
|
|
57501
|
+
} catch (err) {
|
|
57502
|
+
console.error(`[sla] \u7194\u65AD\u5347\u7EA7\u5931\u8D25 ${f2.artifactId}: ${String(err)}`);
|
|
57093
57503
|
}
|
|
57094
57504
|
}
|
|
57095
57505
|
})().catch((err) => console.error(`[sla] \u626B\u63CF\u5931\u8D25: ${String(err)}`));
|
|
@@ -58861,9 +59271,10 @@ ${res.warning}`);
|
|
|
58861
59271
|
}
|
|
58862
59272
|
case "escalate": {
|
|
58863
59273
|
const { message } = await api.cmd("escalate", {
|
|
58864
|
-
artifactId: needPos(positional, 0, "oasis escalate <artifactId> --reason t [--part p]"),
|
|
59274
|
+
artifactId: needPos(positional, 0, "oasis escalate <artifactId> --reason t [--gap id] [--part p]"),
|
|
58865
59275
|
reason: need(flags, "reason"),
|
|
58866
|
-
...flags.get("part") !== void 0 ? { part: flags.get("part") } : {}
|
|
59276
|
+
...flags.get("part") !== void 0 ? { part: flags.get("part") } : {},
|
|
59277
|
+
...flags.get("gap") !== void 0 ? { gapId: flags.get("gap") } : {}
|
|
58867
59278
|
});
|
|
58868
59279
|
println(message);
|
|
58869
59280
|
break;
|
|
@@ -58876,6 +59287,23 @@ ${res.warning}`);
|
|
|
58876
59287
|
println(message);
|
|
58877
59288
|
break;
|
|
58878
59289
|
}
|
|
59290
|
+
case "hold": {
|
|
59291
|
+
const { message } = await api.cmd("hold", {
|
|
59292
|
+
artifactId: needPos(positional, 0, "oasis hold <artifactId> [--reason t]"),
|
|
59293
|
+
...flags.get("reason") !== void 0 ? { reason: flags.get("reason") } : {},
|
|
59294
|
+
...flags.get("source") !== void 0 ? { source: flags.get("source") } : {}
|
|
59295
|
+
});
|
|
59296
|
+
println(message);
|
|
59297
|
+
break;
|
|
59298
|
+
}
|
|
59299
|
+
case "release": {
|
|
59300
|
+
const { message } = await api.cmd("release", {
|
|
59301
|
+
artifactId: needPos(positional, 0, "oasis release <artifactId> [--reason t]"),
|
|
59302
|
+
...flags.get("reason") !== void 0 ? { reason: flags.get("reason") } : {}
|
|
59303
|
+
});
|
|
59304
|
+
println(message);
|
|
59305
|
+
break;
|
|
59306
|
+
}
|
|
58879
59307
|
case "add-revision": {
|
|
58880
59308
|
const { message } = await api.cmd("addRevision", {
|
|
58881
59309
|
artifactId: needPos(positional, 0, "oasis add-revision <artifactId> --desc <\u8981\u5B83\u5B8C\u6210\u4EC0\u4E48> [--role <\u8C01\u6765\u505A>]"),
|
|
@@ -59328,7 +59756,7 @@ ${res.warning}`);
|
|
|
59328
59756
|
}
|
|
59329
59757
|
|
|
59330
59758
|
// src/index.ts
|
|
59331
|
-
var PKG_VERSION = true ? "0.1.
|
|
59759
|
+
var PKG_VERSION = true ? "0.1.54" : "dev";
|
|
59332
59760
|
var OASIS_DIR = path18.join(os9.homedir(), ".oasis");
|
|
59333
59761
|
var CONFIG_FILE = path18.join(OASIS_DIR, "node-config.json");
|
|
59334
59762
|
var PID_FILE = path18.join(OASIS_DIR, "node.pid");
|