oasis_test_v2 2.2.84 → 2.2.86

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.
Files changed (2) hide show
  1. package/dist/index.js +1008 -619
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -2524,368 +2524,26 @@ var init_sealed_node_receipt = __esm({
2524
2524
  }
2525
2525
  });
2526
2526
 
2527
- // ../server/src/domains/chat-sessions/session-runtime-target.ts
2528
- function sessionRuntimeOverride(session) {
2529
- const nodeId = session?.overrideNodeId?.trim();
2530
- const runtimeKind = session?.overrideRuntimeKind?.trim();
2531
- if (!nodeId || !runtimeKind) return null;
2532
- return { nodeId, runtimeKind };
2533
- }
2534
- function chatSessionInitialRuntime(input) {
2535
- const trimmed = (v2) => typeof v2 === "string" && v2.trim() ? v2.trim() : null;
2536
- const binding = input.binding?.nodeId && input.binding?.runtimeKind ? { nodeId: input.binding.nodeId, runtimeKind: input.binding.runtimeKind } : null;
2537
- const requestedOverride = input.requested?.override;
2538
- const picked = requestedOverride?.nodeId && requestedOverride?.runtimeKind ? { nodeId: requestedOverride.nodeId, runtimeKind: requestedOverride.runtimeKind } : null;
2539
- const target = picked ?? binding;
2540
- const followsAgentTarget = !picked || !!binding && picked.nodeId === binding.nodeId && picked.runtimeKind === binding.runtimeKind;
2541
- const requestedModel = input.requested?.model;
2542
- const model = requestedModel !== void 0 ? trimmed(requestedModel) : followsAgentTarget ? trimmed(input.agentModel) : null;
2543
- return {
2544
- overrideNodeId: target?.nodeId ?? null,
2545
- overrideRuntimeKind: target?.runtimeKind ?? null,
2546
- model
2547
- };
2548
- }
2549
- function sessionRuntimeOverrideChangesTarget(session, binding) {
2550
- const override = sessionRuntimeOverride(session);
2551
- if (!override) return false;
2552
- if (!binding) return true;
2553
- return override.nodeId !== binding.nodeId || override.runtimeKind !== binding.runtimeKind;
2554
- }
2555
- async function resolveSessionRuntimeTarget(input) {
2556
- const { probe } = input;
2557
- const override = sessionRuntimeOverride(input.session);
2558
- if (!override) {
2559
- return probe.binding ? { binding: probe.binding, source: "actor_binding" } : null;
2560
- }
2561
- const kinds = await probe.availableRuntimeKinds(override.nodeId);
2562
- const nodeName = (await probe.nodeDisplayName?.(override.nodeId))?.trim() || override.nodeId;
2563
- if (kinds === null) {
2564
- throw new SessionRuntimeOverrideError(
2565
- "CHAT_SESSION_NODE_UNAVAILABLE",
2566
- `\u8FD9\u6761\u4F1A\u8BDD\u9009\u5B9A\u7684\u8282\u70B9\u300C${nodeName}\u300D\u5F53\u524D\u4E0D\u53EF\u7528\uFF08\u53EF\u80FD\u5DF2\u79BB\u7EBF\u3001\u5DF2\u5220\u9664\uFF0C\u6216\u4E0D\u5C5E\u4E8E\u5F53\u524D\u516C\u53F8\uFF09\u3002\u8BF7\u786E\u8BA4\u90A3\u53F0\u673A\u5668\u5F00\u7740\u3001\u8282\u70B9\u7A0B\u5E8F\u5728\u8FD0\u884C\u540E\u518D\u53D1\uFF1B\u6216${CLEAR_SESSION_RUNTIME_HINT}`
2567
- );
2568
- }
2569
- if (!kinds.includes(override.runtimeKind)) {
2570
- throw new SessionRuntimeOverrideError(
2571
- "CHAT_SESSION_RUNTIME_UNAVAILABLE",
2572
- `\u8282\u70B9\u300C${nodeName}\u300D\u4E0A\u6CA1\u6709\u53EF\u7528\u7684\u8FD0\u884C\u65F6 ${override.runtimeKind}\uFF08\u53EF\u80FD\u5DF2\u5378\u8F7D\u3001\u8FD8\u6CA1\u88C5\u597D\uFF0C\u6216\u672A\u4E0A\u62A5\uFF09\u3002\u8BF7\u5728\u90A3\u53F0\u673A\u5668\u4E0A\u88C5\u597D\u540E\u518D\u53D1\uFF1B\u6216${CLEAR_SESSION_RUNTIME_HINT}`
2573
- );
2574
- }
2575
- if (!probe.binding) {
2576
- throw new SessionRuntimeOverrideError(
2577
- "ACTOR_BINDING_MISSING",
2578
- `\u8FD9\u4F4D Agent \u8FD8\u6CA1\u6709\u7ED1\u5B9A\u8FD0\u884C\u65F6\uFF0C\u9009\u5B9A\u8282\u70B9\u4E5F\u6D3E\u4E0D\u51FA\u53BB\u3002\u8BF7\u5148\u7ED9\u4ED6\u7ED1\u5B9A\u4E00\u4E2A\u53EF\u7528\u7684\u8FD0\u884C\u65F6\uFF1B\u6216${CLEAR_SESSION_RUNTIME_HINT}`
2579
- );
2580
- }
2581
- return {
2582
- binding: { actorId: probe.binding.actorId, nodeId: override.nodeId, runtimeKind: override.runtimeKind, status: probe.binding.status },
2583
- source: "session_override"
2584
- };
2585
- }
2586
- function canAnnounceRuntimeSwitch(prior) {
2587
- const nodeId = prior?.runtimeId?.trim();
2588
- const kind = prior?.runtimeKind?.trim();
2589
- return Boolean(nodeId && kind && nodeId !== "unknown");
2590
- }
2591
- function runtimeSwitchedNotice(target) {
2592
- const name = target.nodeName?.trim();
2593
- const where = name ? `${name} \xB7 ${target.runtimeKind}` : target.runtimeKind;
2594
- return `\u8FD0\u884C\u65F6\u5DF2\u5207\u5230 ${where}\uFF0C\u672C\u8F6E\u8D77\u5728\u65B0\u7684\u8FD0\u884C\u65F6\u4F1A\u8BDD\u4E2D\u7EE7\u7EED\uFF0C\u5DE5\u4F5C\u533A\u4E0E\u4E0A\u4E0B\u6587\u4E0D\u6CBF\u7528\u3002`;
2595
- }
2596
- var SessionRuntimeOverrideError, CLEAR_SESSION_RUNTIME_HINT;
2597
- var init_session_runtime_target = __esm({
2598
- "../server/src/domains/chat-sessions/session-runtime-target.ts"() {
2599
- "use strict";
2600
- SessionRuntimeOverrideError = class extends Error {
2601
- constructor(code2, message, status = 409) {
2602
- super(message);
2603
- this.code = code2;
2604
- this.status = status;
2605
- this.name = "SessionRuntimeOverrideError";
2606
- }
2607
- };
2608
- CLEAR_SESSION_RUNTIME_HINT = "\u5728\u8F93\u5165\u6846\u5E95\u90E8\u7684\u8FD0\u884C\u65F6\u9009\u62E9\u91CC\u6362\u4E00\u53F0\u53EF\u7528\u7684\u670D\u52A1\u5668\u6216\u8FD0\u884C\u65F6\u5373\u53EF\u7EE7\u7EED\uFF08\u5458\u5DE5\u81EA\u5DF1\u7684\u90A3\u4E00\u5957\u6807\u7740\u300C\u5F53\u524D\u5458\u5DE5\u6240\u5728\u670D\u52A1\u5668\u300D\u300C\u5F53\u524D\u5458\u5DE5\u8FD0\u884C\u65F6\u300D\uFF09\u3002";
2609
- }
2610
- });
2611
-
2612
- // ../server/src/artifact-content.ts
2613
- async function currentOutput(kernel, artifactId) {
2614
- await kernel.refreshModel();
2615
- const artifact = kernel.model.artifacts.get(artifactId);
2616
- if (!artifact) throw new ApiError(404, "NOT_FOUND", "\u6587\u4EF6\u4E0D\u5B58\u5728");
2617
- const snap = await kernel.getStore().transaction((tx) => tx.loadWorkorder(artifact.workspace));
2618
- const node2 = snap?.nodes.find((n) => n.id === artifactId);
2619
- const latest = snap?.works.find((w2) => w2.id === node2?.latestWorkId);
2620
- const workId = latest && (latest.status === "failed" || latest.status === "dead") && node2?.latestProposedWorkId ? node2.latestProposedWorkId : node2?.latestWorkId;
2621
- const output = snap?.artifacts.filter((a) => a.workId === workId).sort((a, b2) => a.ordinal - b2.ordinal)[0];
2622
- if (!node2 || !output) throw new ApiError(404, "NOT_FOUND", "\u8FD9\u4E2A\u8282\u70B9\u5C1A\u672A\u4EA4\u4ED8\u6587\u4EF6");
2623
- return { node: node2, output };
2624
- }
2625
- function looksLikeFileName(name) {
2626
- return /^\.[a-z]{1,8}[0-9]?$/.test(fileExtensionOf(name));
2627
- }
2628
- function decodeText(bytes2) {
2629
- if (bytes2.length > currentFileLimits().textEditBytes || bytes2.includes(0)) return null;
2630
- try {
2631
- return new TextDecoder("utf-8", { fatal: true }).decode(bytes2);
2632
- } catch {
2633
- return null;
2634
- }
2635
- }
2636
- async function readEditable(kernel, blobs, artifactId, path47) {
2637
- const { node: node2, output } = await currentOutput(kernel, artifactId);
2638
- let text7 = null;
2639
- if (output.contentKind === "manifest" && path47 !== null) {
2640
- const manifest = parseManifest(await blobs.get(output.contentRef));
2641
- const entry = manifest.entries.find((e) => e.path === path47);
2642
- if (!entry) throw new ApiError(404, "NOT_FOUND", "\u5305\u5185\u6587\u4EF6\u4E0D\u5B58\u5728");
2643
- if (isEditableFileName(entry.path)) text7 = decodeText(await blobs.get(entry.hash));
2644
- } else if (output.contentKind === "inline") {
2645
- const name = node2.title || output.name;
2646
- if (!looksLikeFileName(name) || isEditableFileName(name)) text7 = decodeText(await blobs.get(output.contentRef));
2647
- }
2648
- const view = {
2649
- artifactId,
2650
- revisionId: output.workId,
2651
- contentRef: output.contentRef,
2652
- path: path47,
2653
- editable: text7 !== null,
2654
- text: text7
2655
- };
2656
- return { view, node: node2, output };
2657
- }
2658
- async function readArtifactEditableContent(kernel, blobs, artifactId, path47) {
2659
- return (await readEditable(kernel, blobs, artifactId, path47)).view;
2660
- }
2661
- async function saveArtifactContent(kernel, blobs, actor, input) {
2662
- if (!input || typeof input.artifactId !== "string" || typeof input.revisionId !== "string" || typeof input.contentRef !== "string" || typeof input.text !== "string" || input.path != null && typeof input.path !== "string") {
2663
- throw new ApiError(400, "BAD_REQUEST", "\u4FDD\u5B58\u53C2\u6570\u4E0D\u5B8C\u6574");
2664
- }
2665
- const bytes2 = new TextEncoder().encode(input.text);
2666
- if (bytes2.length > currentFileLimits().textEditBytes || bytes2.includes(0)) {
2667
- throw new ApiError(400, "BAD_REQUEST", `\u4EC5\u652F\u6301\u4E0D\u8D85\u8FC7 ${currentFileLimits().textEditBytes / 1024 / 1024} MiB \u7684 UTF-8 \u6587\u672C`);
2668
- }
2669
- const path47 = input.path ?? null;
2670
- const { view, node: node2, output } = await readEditable(kernel, blobs, input.artifactId, path47);
2671
- if (view.revisionId !== input.revisionId || view.contentRef !== input.contentRef) {
2672
- throw new ApiError(409, "CONTENT_CONFLICT", "\u5185\u5BB9\u5DF2\u88AB\u66F4\u65B0\uFF0C\u8BF7\u91CD\u65B0\u6253\u5F00\u6587\u4EF6\u540E\u518D\u4FDD\u5B58");
2673
- }
2674
- if (!view.editable) throw new ApiError(400, "NOT_EDITABLE", "\u6B64\u6587\u4EF6\u4E0D\u652F\u6301\u5728\u7EBF\u7F16\u8F91");
2675
- let contentRef = await blobs.put(bytes2);
2676
- if (output.contentKind === "manifest") {
2677
- const manifest = parseManifest(await blobs.get(output.contentRef));
2678
- contentRef = await blobs.put(serializeManifest(manifest.entries.map((e) => e.path === path47 ? { ...e, hash: contentRef, size: bytes2.length } : e)));
2679
- }
2680
- try {
2681
- await kernel.editArtifactContent({
2682
- artifactId: input.artifactId,
2683
- actor,
2684
- workId: output.workId,
2685
- outputId: output.id,
2686
- expectedLatestWorkId: node2.latestWorkId,
2687
- previousRef: output.contentRef,
2688
- contentRef,
2689
- path: path47
2690
- });
2691
- } catch (error2) {
2692
- if (error2.code === "content-conflict") {
2693
- throw new ApiError(409, "CONTENT_CONFLICT", "\u5185\u5BB9\u5DF2\u88AB\u66F4\u65B0\uFF0C\u8BF7\u91CD\u65B0\u6253\u5F00\u6587\u4EF6\u540E\u518D\u4FDD\u5B58");
2694
- }
2695
- throw error2;
2696
- }
2697
- return { ...view, contentRef, text: input.text };
2698
- }
2699
- async function readArtifactFile(kernel, blobs, artifactId, path47, expectedRef, revisionId = null) {
2700
- await kernel.refreshModel();
2701
- const artifact = kernel.model.artifacts.get(artifactId);
2702
- const selectedRevision = revisionId ?? artifact?.currentRev;
2703
- const output = selectedRevision ? kernel.model.revisions.get(selectedRevision) : void 0;
2704
- if (!artifact || !output || output.artifactId !== artifactId) {
2705
- throw new ApiError(404, "NOT_FOUND", revisionId !== null ? "\u8BE5\u4EA7\u7269\u7248\u672C\u4E0D\u5B58\u5728" : "\u6B64\u4EA7\u7269\u6CA1\u6709\u53EF\u4E0B\u8F7D\u7684\u539F\u6587\u4EF6");
2706
- }
2707
- if (expectedRef && output.contentRef !== expectedRef) throw new ApiError(409, "CONTENT_CONFLICT", "\u6587\u4EF6\u5DF2\u66F4\u65B0\uFF0C\u8BF7\u91CD\u65B0\u6253\u5F00");
2708
- let hash3 = output.contentRef;
2709
- let name = artifact.title || artifactId;
2710
- if (output.contentKind === "manifest") {
2711
- if (!path47) throw new ApiError(400, "PATH_REQUIRED", "\u8BF7\u9009\u62E9\u6587\u4EF6\u5939\u4E2D\u7684\u5177\u4F53\u6587\u4EF6");
2712
- const entry = parseManifest(await blobs.get(output.contentRef)).entries.find((e) => e.path === path47);
2713
- if (!entry) throw new ApiError(404, "NOT_FOUND", "\u5305\u5185\u6587\u4EF6\u4E0D\u5B58\u5728");
2714
- hash3 = entry.hash;
2715
- name = entry.path;
2716
- if (entry.size !== void 0 && entry.size > currentFileReadMaxBytes()) throw new ApiError(413, "FILE_TOO_LARGE", `\u6587\u4EF6\u8D85\u8FC7 ${currentFileReadMaxBytes() / 1024 / 1024} MiB \u8BFB\u53D6\u9650\u5236`);
2717
- } else if (output.contentKind !== "inline-blob") {
2718
- throw new ApiError(404, "NOT_FOUND", "\u6B64\u4EA7\u7269\u6CA1\u6709\u53EF\u4E0B\u8F7D\u7684\u539F\u6587\u4EF6");
2719
- }
2720
- const meta = await blobs.meta?.(hash3);
2721
- if (meta && meta.size > currentFileReadMaxBytes()) throw new ApiError(413, "FILE_TOO_LARGE", `\u6587\u4EF6\u8D85\u8FC7 ${currentFileReadMaxBytes() / 1024 / 1024} MiB \u8BFB\u53D6\u9650\u5236`);
2722
- try {
2723
- return { bytes: await blobs.get(hash3), name, contentType: meta?.contentType };
2724
- } catch {
2725
- throw new ApiError(404, "NOT_FOUND", "\u5F53\u524D\u516C\u53F8\u4E2D\u627E\u4E0D\u5230\u8BE5\u6587\u4EF6\u6B63\u6587");
2726
- }
2727
- }
2728
- var init_artifact_content2 = __esm({
2729
- "../server/src/artifact-content.ts"() {
2730
- "use strict";
2731
- init_file_limits2();
2732
- init_src();
2733
- init_router();
2734
- }
2735
- });
2736
-
2737
- // ../server/src/file-http.ts
2738
- async function serveFileRequest(req, res, url, engine) {
2739
- try {
2740
- if (req.method !== "GET" || url.pathname !== "/api/files/content") throw new ApiError(404, "NOT_FOUND", "\u6587\u4EF6\u63A5\u53E3\u4E0D\u5B58\u5728");
2741
- const { bytes: bytes2, name, contentType } = await readArtifactFile(
2742
- engine.kernel,
2743
- engine.blobs,
2744
- url.searchParams.get("artifact") ?? "",
2745
- url.searchParams.get("path"),
2746
- url.searchParams.get("ref"),
2747
- url.searchParams.get("revisionId")
2748
- );
2749
- if (bytes2.length > currentFileReadMaxBytes()) throw new ApiError(413, "FILE_TOO_LARGE", `\u6587\u4EF6\u8D85\u8FC7 ${currentFileReadMaxBytes() / 1024 / 1024} MiB \u8BFB\u53D6\u9650\u5236`);
2750
- res.writeHead(200, {
2751
- "content-type": contentType ?? sniffContentType(bytes2) ?? "application/octet-stream",
2752
- "content-length": String(bytes2.length),
2753
- "cache-control": "private, no-store",
2754
- "x-content-type-options": "nosniff",
2755
- "content-disposition": `attachment; filename*=UTF-8''${encodeURIComponent(name.split(/[\\/]/).pop() || "file").replace(/['()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`)}`
2756
- }).end(Buffer.from(bytes2));
2757
- } catch (error2) {
2758
- if (res.destroyed) return;
2759
- const status = error2 instanceof ApiError ? error2.status : 500;
2760
- if (!req.complete) {
2761
- res.setHeader("connection", "close");
2762
- req.resume();
2763
- }
2764
- res.writeHead(status, { "content-type": "application/json", "cache-control": "no-store" }).end(JSON.stringify({
2765
- error: {
2766
- code: error2 instanceof ApiError ? error2.code : "FILE_READ_FAILED",
2767
- message: error2 instanceof ApiError ? error2.message : "\u6587\u4EF6\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5"
2768
- }
2769
- }));
2770
- }
2771
- }
2772
- var init_file_http = __esm({
2773
- "../server/src/file-http.ts"() {
2774
- "use strict";
2775
- init_src();
2776
- init_artifact_content2();
2777
- init_router();
2778
- init_file_limits2();
2779
- }
2780
- });
2781
-
2782
- // ../server/src/build-info.ts
2783
- function findRepoRoot() {
2784
- let dir;
2785
- try {
2786
- dir = path.dirname((0, import_node_url.fileURLToPath)(__esm_import_meta_url));
2787
- } catch {
2788
- return null;
2789
- }
2790
- for (let i = 0; i < 12; i++) {
2791
- if (fs.existsSync(path.join(dir, ".git"))) return dir;
2792
- const parent = path.dirname(dir);
2793
- if (parent === dir) break;
2794
- dir = parent;
2795
- }
2796
- return null;
2797
- }
2798
- function git(root, args) {
2799
- try {
2800
- const out = (0, import_node_child_process.execFileSync)("git", args, {
2801
- cwd: root,
2802
- encoding: "utf8",
2803
- timeout: 3e3,
2804
- stdio: ["ignore", "pipe", "ignore"]
2805
- });
2806
- const v2 = out.trim();
2807
- return v2 || null;
2808
- } catch {
2809
- return null;
2810
- }
2811
- }
2812
- function getBuildInfo() {
2813
- if (cached) return cached;
2814
- const envSha = process.env["OASIS_GIT_SHA"]?.trim();
2815
- const envBranch = process.env["OASIS_GIT_BRANCH"]?.trim();
2816
- let gitSha = envSha || null;
2817
- let gitBranch = envBranch || null;
2818
- if (!gitSha || !gitBranch) {
2819
- const root = findRepoRoot();
2820
- if (root) {
2821
- if (!gitSha) gitSha = git(root, ["rev-parse", "HEAD"]);
2822
- if (!gitBranch) {
2823
- const b2 = git(root, ["rev-parse", "--abbrev-ref", "HEAD"]);
2824
- gitBranch = b2 === "HEAD" ? null : b2;
2825
- }
2826
- }
2827
- }
2828
- cached = { gitSha, gitBranch, startedAt: STARTED_AT };
2829
- return cached;
2830
- }
2831
- function resetBuildInfoCacheForTest() {
2832
- cached = void 0;
2833
- }
2834
- var import_node_child_process, fs, path, import_node_url, STARTED_AT, cached;
2835
- var init_build_info = __esm({
2836
- "../server/src/build-info.ts"() {
2837
- "use strict";
2838
- import_node_child_process = require("node:child_process");
2839
- fs = __toESM(require("node:fs"), 1);
2840
- path = __toESM(require("node:path"), 1);
2841
- import_node_url = require("node:url");
2842
- STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
2843
- }
2844
- });
2845
-
2846
- // ../server/src/command-policy.ts
2847
- function classifyCommand(command2) {
2848
- return COMMAND_POLICY[command2] ?? { risk: "direct" };
2849
- }
2850
- var shortId, COMMAND_POLICY;
2851
- var init_command_policy = __esm({
2852
- "../server/src/command-policy.ts"() {
2853
- "use strict";
2854
- shortId = (v2) => typeof v2 === "string" ? v2.replace(/^artifact:[^:]+:/, "") : String(v2 ?? "?");
2855
- COMMAND_POLICY = {
2856
- forceConclude: {
2857
- risk: "authorize",
2858
- idempotent: false,
2859
- effect: (a) => `\u5F3A\u5236\u6536\u53E3\u8282\u70B9 ${shortId(a.artifactId)}\uFF08\u7ED5\u8FC7\u8BC4\u5BA1\u95F8\u76F4\u63A5\u76D6\u91CC\u7A0B\u7891\uFF09`
2860
- },
2861
- promote: {
2862
- risk: "authorize",
2863
- idempotent: true,
2864
- effect: (a) => `\u628A\u8282\u70B9 ${shortId(a.artifactId)} \u8F6C\u4E3A\u6301\u4E45\uFF08temp \u2192 persistent\uFF09`
2865
- },
2866
- resolveEscalation: {
2867
- risk: "authorize",
2868
- idempotent: true,
2869
- // 重复关同一条是幂等的(kernel.resolveEscalation 内部按 escalationId 去重)
2870
- // 「关错=静默停摆」的唯一防线是人在卡上一眼看出「关的是哪一条」——escalationId 与 reason 摘要必须上卡。
2871
- // a 里 escalationId/artifactId 由 server.ts 起草时冻进 commandArgs(已四级解析),保证是真值、非 `?`。
2872
- effect: (a) => typeof a.gapId === "string" ? `\u91C7\u7528\u65B9\u6848\uFF1A${String(a.reason ?? "")}\uFF1B\u89E3\u51B3\u7F3A\u53E3 ${a.gapId}\uFF1B\u5173\u95ED\u5173\u8054\u4E0A\u62A5 ${String(a.escalationId)}\uFF1B\u7EE7\u7EED\u6267\u884C\uFF1A\u662F\uFF08\u4ECD\u987B\u6EE1\u8DB3\u5176\u4ED6\u963B\u585E\u6761\u4EF6\uFF09` : `\u5173\u95ED\u4E0A\u62A5 ${shortId(a.escalationId)}\uFF08\u8282\u70B9 ${shortId(a.artifactId)}${typeof a.reason === "string" ? `\uFF0C\u7406\u7531\uFF1A${a.reason}` : ""}\uFF09`
2873
- },
2874
- gc: {
2875
- risk: "forbidden",
2876
- forbiddenReason: "gc \u662F\u56DE\u6536\u5B64\u513F blob \u7684\u57FA\u7840\u8BBE\u65BD\u7EF4\u62A4\uFF0C\u7531 serve \u8FDB\u7A0B/\u4EBA\u7C7B operator \u6267\u884C\uFF0C\u4E0D\u5C5E\u4E8E\u7BA1\u7406\u8005\u80FD\u529B\u8303\u56F4\u3002"
2877
- }
2878
- };
2879
- }
2880
- });
2881
-
2882
2527
  // ../adapters/src/open-design/driver.ts
2883
- function deriveBoardKey(artifactId) {
2528
+ function boardKeyOf(artifactId, fingerprintInput) {
2884
2529
  const tail = artifactId.split(":").pop() ?? artifactId;
2885
2530
  const ascii = tail.replace(/[^a-zA-Z0-9-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 24);
2886
- const sha8 = (0, import_node_crypto2.createHash)("sha256").update(artifactId).digest("hex").slice(0, 8);
2531
+ const sha8 = (0, import_node_crypto2.createHash)("sha256").update(fingerprintInput).digest("hex").slice(0, 8);
2887
2532
  return ascii ? `od-${ascii}-${sha8}` : `od-${sha8}`;
2888
2533
  }
2534
+ function deriveBoardKey(companyId, artifactId) {
2535
+ const company = companyId.trim();
2536
+ if (company.length === 0) throw new Error(`\u6D3E\u751F\u753B\u677F\u540D\u9700\u8981\u516C\u53F8 id\uFF08${artifactId}\uFF09\u2014\u2014\u62FF\u4E0D\u5230\u516C\u53F8\u65F6\u4E0D\u80FD\u56DE\u843D\u5230\u4E0D\u5E26\u516C\u53F8\u7684\u65E7\u540D\u5B57`);
2537
+ return boardKeyOf(artifactId, `${company}\0${artifactId}`);
2538
+ }
2539
+ function parseBoardLinkBoardKey(text7) {
2540
+ for (const line2 of text7.split(/\r?\n/)) {
2541
+ const m2 = /\/projects\/([^/\s?#]+)\s*$/.exec(line2.trim());
2542
+ if (!m2) continue;
2543
+ return BOARD_KEY_RE.test(m2[1]) ? m2[1] : null;
2544
+ }
2545
+ return null;
2546
+ }
2889
2547
  function withOdToken(fetchImpl, token) {
2890
2548
  if (!token) return fetchImpl;
2891
2549
  return (input, init) => {
@@ -2977,6 +2635,7 @@ async function fetchResultPackage(base, fetchImpl, runId) {
2977
2635
  function toEpochMs(v2) {
2978
2636
  if (typeof v2 === "number" && Number.isFinite(v2)) return v2;
2979
2637
  if (typeof v2 === "string" && v2.length > 0) {
2638
+ if (/^\d+(\.\d+)?$/.test(v2.trim())) return Number(v2.trim());
2980
2639
  const ms = Date.parse(v2);
2981
2640
  return Number.isNaN(ms) ? void 0 : ms;
2982
2641
  }
@@ -2993,8 +2652,99 @@ function latestSucceededRun(runs) {
2993
2652
  }
2994
2653
  return best;
2995
2654
  }
2996
- async function fetchBoardStatus(base, fetchImpl, artifactId) {
2997
- const boardKey = deriveBoardKey(artifactId);
2655
+ function liveBoardRuns(st) {
2656
+ const out = [];
2657
+ const known = /* @__PURE__ */ new Set();
2658
+ for (const t of st.turns ?? []) {
2659
+ if (t.runId !== null) known.add(t.runId);
2660
+ if (isTerminalRunStatus(t.status)) continue;
2661
+ out.push({ key: t.runId ?? `msg:${t.messageId ?? "?"}`, status: t.status, ...t.agentId ? { agentId: t.agentId } : {}, source: "conversation" });
2662
+ }
2663
+ for (const r of st.runs) {
2664
+ if (known.has(r.runId) || r.runId.length === 0) continue;
2665
+ if (r.status !== "running" && r.status !== "queued") continue;
2666
+ out.push({ key: r.runId, status: r.status, ...r.agentId ? { agentId: r.agentId } : {}, source: "registry" });
2667
+ }
2668
+ return out;
2669
+ }
2670
+ function latestEndedRun(st) {
2671
+ let best;
2672
+ for (const t of st.turns ?? []) {
2673
+ if (!isTerminalRunStatus(t.status)) continue;
2674
+ if (best === void 0 || (t.endedAt ?? Number.NEGATIVE_INFINITY) > (best.endedAt ?? Number.NEGATIVE_INFINITY)) best = t;
2675
+ }
2676
+ if (best) return { runId: best.runId, status: best.status, ...best.agentId ? { agentId: best.agentId } : {}, ...best.error ? { error: best.error } : {} };
2677
+ const r = st.runs.find((x2) => isTerminalRunStatus(x2.status));
2678
+ return r ? { runId: r.runId, status: r.status, ...r.agentId ? { agentId: r.agentId } : {}, ...r.error ? { error: r.error } : {} } : void 0;
2679
+ }
2680
+ function snapshotSourceRunId(st) {
2681
+ let best;
2682
+ for (const t of st.turns ?? []) {
2683
+ if (t.status !== "succeeded" || t.runId === null) continue;
2684
+ if (best === void 0 || (t.endedAt ?? Number.NEGATIVE_INFINITY) > (best.endedAt ?? Number.NEGATIVE_INFINITY)) best = t;
2685
+ }
2686
+ return best?.runId ?? latestSucceededRun(st.runs)?.runId;
2687
+ }
2688
+ function nonEmptyStr(v2) {
2689
+ return typeof v2 === "string" && v2.trim().length > 0 ? v2 : void 0;
2690
+ }
2691
+ function turnError(m2, status) {
2692
+ const events = Array.isArray(m2["events"]) ? m2["events"] : [];
2693
+ for (let i = events.length - 1; i >= 0; i--) {
2694
+ const e = events[i];
2695
+ if (!e || typeof e !== "object") continue;
2696
+ const ev = e;
2697
+ if (ev["kind"] === "status" && ev["label"] === "error" && nonEmptyStr(ev["detail"])) return ev["detail"];
2698
+ }
2699
+ return status !== "succeeded" && isTerminalRunStatus(status) ? nonEmptyStr(m2["content"]) : void 0;
2700
+ }
2701
+ async function fetchBoardTurns(base, fetchImpl, boardKey) {
2702
+ const convPath = `/api/projects/${encodeURIComponent(boardKey)}/conversations`;
2703
+ const guard = (err) => {
2704
+ if (err instanceof OdHttpError && (err.status === 401 || err.status === 403)) throw err;
2705
+ return null;
2706
+ };
2707
+ let convs;
2708
+ try {
2709
+ convs = (await odApi(base, fetchImpl, "GET", convPath)).conversations ?? [];
2710
+ } catch (err) {
2711
+ if (err instanceof OdHttpError && err.status === 404) return [];
2712
+ return guard(err);
2713
+ }
2714
+ const turns = [];
2715
+ for (const c of convs) {
2716
+ const cid = nonEmptyStr(c["id"]);
2717
+ if (cid === void 0) continue;
2718
+ let msgs;
2719
+ try {
2720
+ msgs = (await odApi(base, fetchImpl, "GET", `${convPath}/${encodeURIComponent(cid)}/messages`)).messages ?? [];
2721
+ } catch (err) {
2722
+ if (err instanceof OdHttpError && err.status === 404) continue;
2723
+ return guard(err);
2724
+ }
2725
+ for (const m2 of msgs) {
2726
+ if (!m2 || typeof m2 !== "object" || m2["role"] !== "assistant") continue;
2727
+ const status = nonEmptyStr(m2["runStatus"]);
2728
+ if (status === void 0) continue;
2729
+ const runId = nonEmptyStr(m2["runId"]) ?? null;
2730
+ const messageId = nonEmptyStr(m2["id"]) ?? null;
2731
+ if (runId === null && messageId === null) continue;
2732
+ const turn = { conversationId: cid, runId, messageId, status };
2733
+ const agentId = nonEmptyStr(m2["agentId"]);
2734
+ if (agentId) turn.agentId = agentId;
2735
+ const error2 = turnError(m2, status);
2736
+ if (error2) turn.error = error2;
2737
+ const startedAt = toEpochMs(m2["startedAt"]) ?? toEpochMs(m2["createdAt"]);
2738
+ if (startedAt !== void 0) turn.startedAt = startedAt;
2739
+ const endedAt = toEpochMs(m2["endedAt"]);
2740
+ if (endedAt !== void 0) turn.endedAt = endedAt;
2741
+ turns.push(turn);
2742
+ }
2743
+ }
2744
+ turns.sort((a, b2) => (b2.startedAt ?? Number.NEGATIVE_INFINITY) - (a.startedAt ?? Number.NEGATIVE_INFINITY) || 0);
2745
+ return turns;
2746
+ }
2747
+ async function fetchBoardStatus(base, fetchImpl, boardKey) {
2998
2748
  let boardExists = true;
2999
2749
  let artifacts = [];
3000
2750
  try {
@@ -3035,7 +2785,31 @@ async function fetchBoardStatus(base, fetchImpl, artifactId) {
3035
2785
  } catch {
3036
2786
  }
3037
2787
  }
3038
- return { boardKey, boardExists, runs, artifacts };
2788
+ const turns = boardExists ? await fetchBoardTurns(base, fetchImpl, boardKey) : [];
2789
+ return { boardKey, boardExists, runs, turns, artifacts };
2790
+ }
2791
+ async function waitBoardSettled(base, fetchImpl, boardKey, opts = {}) {
2792
+ const interval = opts.intervalMs ?? 2e4;
2793
+ const timeout = opts.timeoutMs ?? 100 * 6e4;
2794
+ const sleep6 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
2795
+ const now = opts.now ?? Date.now;
2796
+ const started = now();
2797
+ let lastKey = null;
2798
+ let waited = false;
2799
+ for (; ; ) {
2800
+ const st = await fetchBoardStatus(base, fetchImpl, boardKey);
2801
+ const elapsedMs = now() - started;
2802
+ if (!st.boardExists) return { status: st, settled: true, waited, live: [], elapsedMs };
2803
+ const live = liveBoardRuns(st);
2804
+ const unreadable = st.turns === null;
2805
+ if (live.length === 0 && !unreadable) return { status: st, settled: true, waited, live, elapsedMs };
2806
+ const key2 = unreadable && live.length === 0 ? "?" : live.map((r) => `${r.key}=${r.status}`).sort().join(",");
2807
+ opts.onPoll?.({ live, elapsedMs, changed: key2 !== lastKey, unreadable });
2808
+ lastKey = key2;
2809
+ if (elapsedMs >= timeout) return { status: st, settled: false, waited: true, live, elapsedMs };
2810
+ waited = true;
2811
+ await sleep6(Math.min(interval, Math.max(0, timeout - elapsedMs)));
2812
+ }
3039
2813
  }
3040
2814
  async function pumpDesignEvents(base, fetchImpl, runId, onLine) {
3041
2815
  const res = await fetchImpl(`${base}/api/runs/${runId}/events`);
@@ -3084,7 +2858,7 @@ async function harvestBoard(base, fetchImpl, publicBase, workRoot, boardKey, run
3084
2858
  const htmls = (listing.files ?? []).filter((f2) => f2.kind === "html" && !f2.path.startsWith("task-context/")).filter((f2) => (f2.artifactManifest?.status ?? "complete") === "complete").sort((a, b2) => (b2.mtime ?? 0) - (a.mtime ?? 0));
3085
2859
  const entry = htmls[0]?.path;
3086
2860
  if (!entry) throw new Error("\u753B\u677F\u9879\u76EE\u65E0\u5DF2\u5B8C\u6210\u7684 HTML \u5165\u53E3\u7A3F\u2014\u2014\u8BBE\u8BA1\u4F1A\u8BDD\u672A\u4EA7\u51FA\u53EF\u4EA4\u4ED8\u754C\u9762");
3087
- const dir = fs2.mkdtempSync(path2.join(workRoot, "od-deliverable-"));
2861
+ const dir = fs.mkdtempSync(path.join(workRoot, "od-deliverable-"));
3088
2862
  const fetchInline = async (rel) => {
3089
2863
  const res = await fetchImpl(`${base}/api/projects/${encodeURIComponent(boardKey)}/export/${encodeURIComponent(rel)}?inline=1`);
3090
2864
  if (!res.ok) throw new Error(`od export ${rel} \u2192 ${res.status}`);
@@ -3095,28 +2869,37 @@ async function harvestBoard(base, fetchImpl, publicBase, workRoot, boardKey, run
3095
2869
  if (!res.ok) throw new Error(`od raw ${rel} \u2192 ${res.status}`);
3096
2870
  return await res.text();
3097
2871
  };
3098
- fs2.writeFileSync(path2.join(dir, "index.html"), await fetchInline(entry));
2872
+ fs.writeFileSync(path.join(dir, "index.html"), await fetchInline(entry));
3099
2873
  for (const f2 of listing.files ?? []) {
3100
2874
  if (f2.path.endsWith(".md") && !f2.path.includes("/")) {
3101
2875
  try {
3102
- fs2.writeFileSync(path2.join(dir, f2.path), await fetchRaw(f2.path));
2876
+ fs.writeFileSync(path.join(dir, f2.path), await fetchRaw(f2.path));
3103
2877
  } catch {
3104
2878
  }
3105
2879
  }
3106
2880
  }
3107
- fs2.writeFileSync(
3108
- path2.join(dir, "board.link"),
3109
- `${publicBase}/projects/${boardKey}
3110
- \u5165\u53E3\u7A3F\uFF1A${entry}
3111
- \u5FEB\u7167\u6765\u6E90 run\uFF1A${runId}
3112
- \uFF08\u6D3B\u753B\u677F\u2014\u2014\u7EE7\u7EED\u8FED\u4EE3\u8BF7\u56DE\u5230\u8BE5\u9879\u76EE\uFF1B\u672C\u88C5\u7BB1\u5355\u4E3A\u8BE5\u6B21\u4EA4\u4ED8\u7684\u51BB\u7ED3\u5FEB\u7167\uFF09
3113
- `
3114
- );
2881
+ fs.writeFileSync(path.join(dir, BOARD_LINK_FILE), formatBoardLink({ publicBase, boardKey, entry, runId }));
3115
2882
  return { dir, entry };
3116
2883
  }
2884
+ function formatBoardLink(args) {
2885
+ return `${args.publicBase}/projects/${args.boardKey}
2886
+ \u5165\u53E3\u7A3F\uFF1A${args.entry}
2887
+ ${SOURCE_RUN_LABEL}${args.runId}
2888
+ \uFF08\u6D3B\u753B\u677F\u2014\u2014\u7EE7\u7EED\u8FED\u4EE3\u8BF7\u56DE\u5230\u8BE5\u9879\u76EE\uFF1B\u672C\u88C5\u7BB1\u5355\u4E3A\u8BE5\u6B21\u4EA4\u4ED8\u7684\u51BB\u7ED3\u5FEB\u7167\uFF09
2889
+ `;
2890
+ }
2891
+ function parseBoardLinkSourceRun(text7) {
2892
+ for (const line2 of text7.split(/\r?\n/)) {
2893
+ const t = line2.trim();
2894
+ if (!t.startsWith(SOURCE_RUN_LABEL)) continue;
2895
+ const id = t.slice(SOURCE_RUN_LABEL.length).trim();
2896
+ return id.length > 0 && id !== UNKNOWN_SOURCE_RUN ? id : null;
2897
+ }
2898
+ return null;
2899
+ }
3117
2900
  async function startDesignChatTurn(opts) {
3118
2901
  const fetchImpl = withOdToken(opts.fetchImpl ?? fetch, opts.token);
3119
- const boardKey = deriveBoardKey(opts.artifactId);
2902
+ const boardKey = opts.boardKey;
3120
2903
  const { conversationId } = await ensureProject(opts.base, fetchImpl, boardKey, opts.title);
3121
2904
  if (opts.contextFiles) await materializeContext(opts.base, fetchImpl, boardKey, opts.contextFiles);
3122
2905
  const userMessageId = await putVisibleUserTurn(opts.base, fetchImpl, { boardKey, conversationId, content: opts.message });
@@ -3143,13 +2926,14 @@ async function runDesignChatTurn(opts) {
3143
2926
  const { artifacts, fileCount } = await fetchResultPackage(opts.base, fetchImpl, runId);
3144
2927
  return { boardKey, conversationId, runId, userMessageId, artifacts, fileCount };
3145
2928
  }
3146
- var import_node_crypto2, fs2, path2, OdHttpError, TASK_CONTEXT_DIR;
2929
+ var import_node_crypto2, fs, path, BOARD_KEY_RE, OdHttpError, TASK_CONTEXT_DIR, TERMINAL_TURN_STATUS, isTerminalRunStatus, BOARD_LINK_FILE, SOURCE_RUN_LABEL, UNKNOWN_SOURCE_RUN;
3147
2930
  var init_driver = __esm({
3148
2931
  "../adapters/src/open-design/driver.ts"() {
3149
2932
  "use strict";
3150
2933
  import_node_crypto2 = require("node:crypto");
3151
- fs2 = __toESM(require("node:fs"), 1);
3152
- path2 = __toESM(require("node:path"), 1);
2934
+ fs = __toESM(require("node:fs"), 1);
2935
+ path = __toESM(require("node:path"), 1);
2936
+ BOARD_KEY_RE = /^[A-Za-z0-9-]{1,64}$/;
3153
2937
  OdHttpError = class extends Error {
3154
2938
  constructor(message, status) {
3155
2939
  super(message);
@@ -3157,6 +2941,11 @@ var init_driver = __esm({
3157
2941
  }
3158
2942
  };
3159
2943
  TASK_CONTEXT_DIR = "task-context/";
2944
+ TERMINAL_TURN_STATUS = /* @__PURE__ */ new Set(["succeeded", "failed", "canceled", "cancelled"]);
2945
+ isTerminalRunStatus = (s2) => TERMINAL_TURN_STATUS.has(s2);
2946
+ BOARD_LINK_FILE = "board.link";
2947
+ SOURCE_RUN_LABEL = "\u5FEB\u7167\u6765\u6E90 run\uFF1A";
2948
+ UNKNOWN_SOURCE_RUN = "status-pull";
3160
2949
  }
3161
2950
  });
3162
2951
 
@@ -3197,20 +2986,20 @@ function materializeBundle(dir, bundle) {
3197
2986
  for (const [rel, entry] of Object.entries(bundle.restoreFiles ?? {})) {
3198
2987
  try {
3199
2988
  const parts = rel.split("/");
3200
- if (!rel || path3.isAbsolute(rel) || rel.includes("\\") || parts.some((p2) => !p2 || p2 === "." || p2 === "..")) {
2989
+ if (!rel || path2.isAbsolute(rel) || rel.includes("\\") || parts.some((p2) => !p2 || p2 === "." || p2 === "..")) {
3201
2990
  throw new Error("\u975E\u6CD5\u76F8\u5BF9\u8DEF\u5F84");
3202
2991
  }
3203
- let parent = fs3.realpathSync(dir);
2992
+ let parent = fs2.realpathSync(dir);
3204
2993
  for (const component of parts.slice(0, -1)) {
3205
- parent = path3.join(parent, component);
3206
- if (!fs3.existsSync(parent)) fs3.mkdirSync(parent);
3207
- const st = fs3.lstatSync(parent);
2994
+ parent = path2.join(parent, component);
2995
+ if (!fs2.existsSync(parent)) fs2.mkdirSync(parent);
2996
+ const st = fs2.lstatSync(parent);
3208
2997
  if (st.isSymbolicLink() || !st.isDirectory()) throw new Error("\u6062\u590D\u8DEF\u5F84\u5305\u542B\u975E\u76EE\u5F55\u6216\u8F6F\u94FE\u63A5");
3209
2998
  }
3210
- const file = path3.join(parent, parts.at(-1));
2999
+ const file = path2.join(parent, parts.at(-1));
3211
3000
  let exists = false;
3212
3001
  try {
3213
- const st = fs3.lstatSync(file);
3002
+ const st = fs2.lstatSync(file);
3214
3003
  if (!st.isFile() || st.isSymbolicLink()) throw new Error("\u6062\u590D\u76EE\u6807\u4E0D\u662F\u666E\u901A\u6587\u4EF6");
3215
3004
  exists = true;
3216
3005
  } catch (error2) {
@@ -3220,37 +3009,37 @@ function materializeBundle(dir, bundle) {
3220
3009
  if (exists && !entry.overwrite) continue;
3221
3010
  const bytes2 = Buffer.from(entry.contentBase64, "base64");
3222
3011
  if ((0, import_node_crypto3.createHash)("sha256").update(bytes2).digest("hex") !== entry.sha256) throw new Error("\u5185\u5BB9\u6821\u9A8C\u5931\u8D25");
3223
- const temp = path3.join(parent, `.oasis-restore-${(0, import_node_crypto3.randomUUID)()}`);
3012
+ const temp = path2.join(parent, `.oasis-restore-${(0, import_node_crypto3.randomUUID)()}`);
3224
3013
  try {
3225
- fs3.writeFileSync(temp, bytes2, { flag: "wx", mode: 384 });
3226
- fs3.renameSync(temp, file);
3014
+ fs2.writeFileSync(temp, bytes2, { flag: "wx", mode: 384 });
3015
+ fs2.renameSync(temp, file);
3227
3016
  } finally {
3228
- fs3.rmSync(temp, { force: true });
3017
+ fs2.rmSync(temp, { force: true });
3229
3018
  }
3230
3019
  } catch (error2) {
3231
3020
  throw new BundleRestoreError(rel, error2 instanceof Error ? error2.message : String(error2));
3232
3021
  }
3233
3022
  }
3234
3023
  for (const [rel, content3] of Object.entries(bundle.files)) {
3235
- const file = path3.join(dir, rel);
3236
- fs3.mkdirSync(path3.dirname(file), { recursive: true });
3237
- fs3.writeFileSync(file, content3);
3024
+ const file = path2.join(dir, rel);
3025
+ fs2.mkdirSync(path2.dirname(file), { recursive: true });
3026
+ fs2.writeFileSync(file, content3);
3238
3027
  }
3239
3028
  for (const [rel, b64] of Object.entries(bundle.binaryFiles ?? {})) {
3240
- const file = path3.join(dir, rel);
3241
- fs3.mkdirSync(path3.dirname(file), { recursive: true });
3242
- fs3.writeFileSync(file, Buffer.from(b64, "base64"));
3029
+ const file = path2.join(dir, rel);
3030
+ fs2.mkdirSync(path2.dirname(file), { recursive: true });
3031
+ fs2.writeFileSync(file, Buffer.from(b64, "base64"));
3243
3032
  }
3244
3033
  const task = bundle.files["TASK.md"];
3245
3034
  if (!task) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
3246
3035
  return task;
3247
3036
  }
3248
- var fs3, path3, import_node_crypto3, BundleRestoreError;
3037
+ var fs2, path2, import_node_crypto3, BundleRestoreError;
3249
3038
  var init_materialize_bundle = __esm({
3250
3039
  "../adapters/src/_core/materialize-bundle.ts"() {
3251
3040
  "use strict";
3252
- fs3 = __toESM(require("node:fs"), 1);
3253
- path3 = __toESM(require("node:path"), 1);
3041
+ fs2 = __toESM(require("node:fs"), 1);
3042
+ path2 = __toESM(require("node:path"), 1);
3254
3043
  import_node_crypto3 = require("node:crypto");
3255
3044
  BundleRestoreError = class extends Error {
3256
3045
  constructor(filePath, detail) {
@@ -3494,7 +3283,7 @@ function containerEnv(job, extraEarly, extraLate) {
3494
3283
  ...extraLate
3495
3284
  };
3496
3285
  if (job.wrapperPaths?.length) {
3497
- env["PATH"] = `${job.wrapperPaths.map((p2) => path5.dirname(p2)).join(path5.delimiter)}${path5.delimiter}${CONTAINER_DEFAULT_PATH}`;
3286
+ env["PATH"] = `${job.wrapperPaths.map((p2) => path4.dirname(p2)).join(path4.delimiter)}${path4.delimiter}${CONTAINER_DEFAULT_PATH}`;
3498
3287
  }
3499
3288
  env["OASIS_SERVER"] = job.server.url;
3500
3289
  env["OASIS_TOKEN"] = job.actorToken;
@@ -3503,7 +3292,7 @@ function containerEnv(job, extraEarly, extraLate) {
3503
3292
  }
3504
3293
  function commonArgs(p2, name) {
3505
3294
  const ee = p2.executionEnv;
3506
- const binMount = `/usr/local/bin/${p2.binName ?? path5.basename(p2.binHostPath)}`;
3295
+ const binMount = `/usr/local/bin/${p2.binName ?? path4.basename(p2.binHostPath)}`;
3507
3296
  const args = [
3508
3297
  // 不用 --userns=keep-id:rootless 默认映射已是「容器内 root = 宿主调用用户」,bind 挂载上的
3509
3298
  // 文件属主内外天然一致(agent 写的文件宿主侧可读可删);keep-id 反而在部分主机的 overlay
@@ -3522,7 +3311,7 @@ function commonArgs(p2, name) {
3522
3311
  `${p2.homeHostDir}:${CONTAINER_HOME}`
3523
3312
  ];
3524
3313
  for (const dir of /* @__PURE__ */ new Set([
3525
- ...(p2.job.wrapperPaths ?? []).map((w2) => path5.dirname(w2)),
3314
+ ...(p2.job.wrapperPaths ?? []).map((w2) => path4.dirname(w2)),
3526
3315
  ...p2.extraRoBindDirs ?? []
3527
3316
  ])) {
3528
3317
  args.push("-v", `${dir}:${dir}:ro`);
@@ -3546,7 +3335,7 @@ function droppedEnvNames(env) {
3546
3335
  }
3547
3336
  function wrapForContainer(p2) {
3548
3337
  const ee = p2.executionEnv;
3549
- const binInContainer = p2.binPackageDir ? p2.binHostPath : `/usr/local/bin/${p2.binName ?? path5.basename(p2.binHostPath)}`;
3338
+ const binInContainer = p2.binPackageDir ? p2.binHostPath : `/usr/local/bin/${p2.binName ?? path4.basename(p2.binHostPath)}`;
3550
3339
  const eeHash = executionEnvHash(p2);
3551
3340
  if (ee.persist && p2.job.workdirKey) {
3552
3341
  const name2 = persistContainerName(p2.job.workdirKey);
@@ -3573,8 +3362,8 @@ function wrapForContainer(p2) {
3573
3362
  droppedEnvNames: droppedEnvNames(p2.env)
3574
3363
  };
3575
3364
  }
3576
- const name = ephemeralContainerName(p2.job, path5.basename(p2.cwd));
3577
- const cidFile = path5.join(p2.cwd, ".oasis-container-cid");
3365
+ const name = ephemeralContainerName(p2.job, path4.basename(p2.cwd));
3366
+ const cidFile = path4.join(p2.cwd, ".oasis-container-cid");
3578
3367
  return {
3579
3368
  bin: "podman",
3580
3369
  args: [
@@ -3604,12 +3393,12 @@ function wrapForContainer(p2) {
3604
3393
  function killContainerArgs(containerName) {
3605
3394
  return ["kill", containerName];
3606
3395
  }
3607
- var import_node_crypto5, path5, CONTAINER_DEFAULT_PATH, CONTAINER_HOME, EE_HASH_LABEL;
3396
+ var import_node_crypto5, path4, CONTAINER_DEFAULT_PATH, CONTAINER_HOME, EE_HASH_LABEL;
3608
3397
  var init_container_wrap = __esm({
3609
3398
  "../adapters/src/_core/container-wrap.ts"() {
3610
3399
  "use strict";
3611
3400
  import_node_crypto5 = require("node:crypto");
3612
- path5 = __toESM(require("node:path"), 1);
3401
+ path4 = __toESM(require("node:path"), 1);
3613
3402
  init_session_paths();
3614
3403
  CONTAINER_DEFAULT_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
3615
3404
  CONTAINER_HOME = "/oasis-home";
@@ -3621,7 +3410,7 @@ var init_container_wrap = __esm({
3621
3410
  function podmanAvailable() {
3622
3411
  if (podmanProbe === void 0) {
3623
3412
  try {
3624
- podmanProbe = (0, import_node_child_process2.spawnSync)(PODMAN, ["--version"], { timeout: 1e4 }).status === 0;
3413
+ podmanProbe = (0, import_node_child_process.spawnSync)(PODMAN, ["--version"], { timeout: 1e4 }).status === 0;
3625
3414
  } catch {
3626
3415
  podmanProbe = false;
3627
3416
  }
@@ -3629,19 +3418,19 @@ function podmanAvailable() {
3629
3418
  return podmanProbe;
3630
3419
  }
3631
3420
  function resolveBinHostPath(bin) {
3632
- if (bin.includes(path6.sep)) {
3421
+ if (bin.includes(path5.sep)) {
3633
3422
  try {
3634
- return fs5.realpathSync(path6.resolve(bin));
3423
+ return fs4.realpathSync(path5.resolve(bin));
3635
3424
  } catch {
3636
- return path6.resolve(bin);
3425
+ return path5.resolve(bin);
3637
3426
  }
3638
3427
  }
3639
- for (const dir of (process.env["PATH"] ?? "").split(path6.delimiter)) {
3428
+ for (const dir of (process.env["PATH"] ?? "").split(path5.delimiter)) {
3640
3429
  if (!dir) continue;
3641
- const candidate = path6.join(dir, bin);
3430
+ const candidate = path5.join(dir, bin);
3642
3431
  try {
3643
- fs5.accessSync(candidate, fs5.constants.X_OK);
3644
- return fs5.realpathSync(candidate);
3432
+ fs4.accessSync(candidate, fs4.constants.X_OK);
3433
+ return fs4.realpathSync(candidate);
3645
3434
  } catch {
3646
3435
  }
3647
3436
  }
@@ -3649,25 +3438,25 @@ function resolveBinHostPath(bin) {
3649
3438
  }
3650
3439
  function resolveBinPackageDir(binHostPath) {
3651
3440
  if (!/\.(js|mjs|cjs)$/i.test(binHostPath)) return null;
3652
- let dir = path6.dirname(binHostPath);
3441
+ let dir = path5.dirname(binHostPath);
3653
3442
  for (let i = 0; i < 8; i++) {
3654
- if (fs5.existsSync(path6.join(dir, "package.json"))) {
3655
- if (dir === "/" || dir === os2.tmpdir() || dir.split(path6.sep).length <= 2) return null;
3443
+ if (fs4.existsSync(path5.join(dir, "package.json"))) {
3444
+ if (dir === "/" || dir === os2.tmpdir() || dir.split(path5.sep).length <= 2) return null;
3656
3445
  return dir;
3657
3446
  }
3658
- const parent = path6.dirname(dir);
3447
+ const parent = path5.dirname(dir);
3659
3448
  if (parent === dir) break;
3660
3449
  dir = parent;
3661
3450
  }
3662
3451
  return null;
3663
3452
  }
3664
3453
  function containerHomeHostDir(workRoot) {
3665
- const dir = path6.join(resolveWorkRoot(workRoot), "container-home");
3666
- fs5.mkdirSync(dir, { recursive: true });
3454
+ const dir = path5.join(resolveWorkRoot(workRoot), "container-home");
3455
+ fs4.mkdirSync(dir, { recursive: true });
3667
3456
  return dir;
3668
3457
  }
3669
3458
  async function ensureImage(image) {
3670
- const exists = (0, import_node_child_process2.spawnSync)(PODMAN, ["image", "exists", image], { timeout: 15e3 }).status === 0;
3459
+ const exists = (0, import_node_child_process.spawnSync)(PODMAN, ["image", "exists", image], { timeout: 15e3 }).status === 0;
3671
3460
  if (!exists) {
3672
3461
  try {
3673
3462
  await execFileP(PODMAN, ["pull", image], { timeout: 6e5 });
@@ -3685,7 +3474,7 @@ async function ensureImage(image) {
3685
3474
  async function ensurePersistContainer(plan) {
3686
3475
  if (plan.mode !== "exec" || !plan.ensureArgs) return;
3687
3476
  const name = plan.containerName;
3688
- const exists = (0, import_node_child_process2.spawnSync)(PODMAN, ["container", "exists", name], { timeout: 15e3 }).status === 0;
3477
+ const exists = (0, import_node_child_process.spawnSync)(PODMAN, ["container", "exists", name], { timeout: 15e3 }).status === 0;
3689
3478
  if (exists) {
3690
3479
  const { stdout: label2 } = await execFileP(
3691
3480
  PODMAN,
@@ -3704,7 +3493,7 @@ async function ensurePersistContainer(plan) {
3704
3493
  }
3705
3494
  function killContainerBestEffort(containerName, _mode) {
3706
3495
  try {
3707
- (0, import_node_child_process2.execFile)(PODMAN, killContainerArgs(containerName), { timeout: 3e4 }, () => {
3496
+ (0, import_node_child_process.execFile)(PODMAN, killContainerArgs(containerName), { timeout: 3e4 }, () => {
3708
3497
  });
3709
3498
  } catch {
3710
3499
  }
@@ -3725,8 +3514,8 @@ async function reapEphemeralContainers() {
3725
3514
  }
3726
3515
  function removePersistContainerForDir(workdir) {
3727
3516
  if (!podmanAvailable()) return;
3728
- const name = `oasis-wd-${path6.basename(workdir)}`;
3729
- (0, import_node_child_process2.execFile)(PODMAN, ["rm", "-f", name], { timeout: 3e4 }, () => {
3517
+ const name = `oasis-wd-${path5.basename(workdir)}`;
3518
+ (0, import_node_child_process.execFile)(PODMAN, ["rm", "-f", name], { timeout: 3e4 }, () => {
3730
3519
  });
3731
3520
  }
3732
3521
  async function maybeContainerize(params) {
@@ -3739,12 +3528,12 @@ async function maybeContainerize(params) {
3739
3528
  const env = containerEnv(params.job, params.extraEnvEarly, params.extraEnvLate);
3740
3529
  const extraRoBindDirs = [...new Set((params.job.wrapperPaths ?? []).flatMap((w2) => {
3741
3530
  try {
3742
- const target = fs5.realpathSync(w2);
3743
- const targetDir = path6.dirname(target);
3744
- const baseSibling = path6.join(path6.dirname(targetDir), "_base");
3531
+ const target = fs4.realpathSync(w2);
3532
+ const targetDir = path5.dirname(target);
3533
+ const baseSibling = path5.join(path5.dirname(targetDir), "_base");
3745
3534
  const dirs = [targetDir];
3746
- if (fs5.existsSync(baseSibling)) dirs.push(baseSibling);
3747
- return dirs.filter((d) => d !== "/" && d !== os2.tmpdir() && d.split(path6.sep).length > 2);
3535
+ if (fs4.existsSync(baseSibling)) dirs.push(baseSibling);
3536
+ return dirs.filter((d) => d !== "/" && d !== os2.tmpdir() && d.split(path5.sep).length > 2);
3748
3537
  } catch {
3749
3538
  return [];
3750
3539
  }
@@ -3753,7 +3542,7 @@ async function maybeContainerize(params) {
3753
3542
  const binPackageDir = resolveBinPackageDir(binHostPath);
3754
3543
  const plan = wrapForContainer({
3755
3544
  binHostPath,
3756
- binName: path6.basename(params.bin),
3545
+ binName: path5.basename(params.bin),
3757
3546
  // realpath 前的名字("claude")——argv[0] 语义要保住
3758
3547
  ...binPackageDir ? { binPackageDir } : {},
3759
3548
  args: params.args,
@@ -3768,22 +3557,22 @@ async function maybeContainerize(params) {
3768
3557
  if (plan.droppedEnvNames.length > 0) {
3769
3558
  console.warn(`[container] \u8FD9\u4E9B\u53D8\u91CF\u540D\u653E\u4E0D\u8FDB\u5BB9\u5668\uFF08\u5F00\u5934\u7A7A\u767D / \u542B = / \u7A7A\u5B57\u7B26\uFF09\uFF0C\u672C\u8F6E\u672A\u6CE8\u5165\uFF1A${plan.droppedEnvNames.map((k2) => JSON.stringify(k2)).join(", ")}`);
3770
3559
  }
3771
- if (plan.cidFile) fs5.rmSync(plan.cidFile, { force: true });
3560
+ if (plan.cidFile) fs4.rmSync(plan.cidFile, { force: true });
3772
3561
  await ensurePersistContainer(plan);
3773
3562
  return { bin: plan.bin, args: plan.args, env, containerName: plan.containerName, mode: plan.mode, imageDigest };
3774
3563
  }
3775
- var import_node_child_process2, fs5, os2, path6, import_node_util, execFileP, PODMAN, podmanProbe;
3564
+ var import_node_child_process, fs4, os2, path5, import_node_util, execFileP, PODMAN, podmanProbe;
3776
3565
  var init_container_runtime = __esm({
3777
3566
  "../adapters/src/_core/container-runtime.ts"() {
3778
3567
  "use strict";
3779
- import_node_child_process2 = require("node:child_process");
3780
- fs5 = __toESM(require("node:fs"), 1);
3568
+ import_node_child_process = require("node:child_process");
3569
+ fs4 = __toESM(require("node:fs"), 1);
3781
3570
  os2 = __toESM(require("node:os"), 1);
3782
- path6 = __toESM(require("node:path"), 1);
3571
+ path5 = __toESM(require("node:path"), 1);
3783
3572
  import_node_util = require("node:util");
3784
3573
  init_container_wrap();
3785
3574
  init_session_paths();
3786
- execFileP = (0, import_node_util.promisify)(import_node_child_process2.execFile);
3575
+ execFileP = (0, import_node_util.promisify)(import_node_child_process.execFile);
3787
3576
  PODMAN = "podman";
3788
3577
  }
3789
3578
  });
@@ -3987,16 +3776,16 @@ var init_model_report = __esm({
3987
3776
 
3988
3777
  // ../adapters/src/_core/transcript.ts
3989
3778
  function openTranscript(file) {
3990
- const out = fs6.createWriteStream(file);
3779
+ const out = fs5.createWriteStream(file);
3991
3780
  out.on("error", () => {
3992
3781
  });
3993
3782
  return out;
3994
3783
  }
3995
- var fs6;
3784
+ var fs5;
3996
3785
  var init_transcript = __esm({
3997
3786
  "../adapters/src/_core/transcript.ts"() {
3998
3787
  "use strict";
3999
- fs6 = __toESM(require("node:fs"), 1);
3788
+ fs5 = __toESM(require("node:fs"), 1);
4000
3789
  }
4001
3790
  });
4002
3791
 
@@ -4104,10 +3893,10 @@ function byModelOf(calls) {
4104
3893
  }
4105
3894
  function readSubagentTranscriptCalls(outputFile) {
4106
3895
  try {
4107
- const stat4 = fs7.statSync(outputFile);
3896
+ const stat4 = fs6.statSync(outputFile);
4108
3897
  if (!stat4.isFile() || stat4.size > SUBAGENT_TRANSCRIPT_MAX_BYTES) return void 0;
4109
3898
  const calls = /* @__PURE__ */ new Map();
4110
- for (const line2 of fs7.readFileSync(outputFile, "utf8").split("\n")) {
3899
+ for (const line2 of fs6.readFileSync(outputFile, "utf8").split("\n")) {
4111
3900
  if (!line2.includes('"assistant"') || !line2.includes('"usage"')) continue;
4112
3901
  let parsed;
4113
3902
  try {
@@ -4362,14 +4151,14 @@ function newClaudeUsageAcc() {
4362
4151
  };
4363
4152
  }
4364
4153
  function findClaudeSessionFile(configDir, cwd, runtimeSessionId) {
4365
- const root = path7.join(configDir, "projects");
4154
+ const root = path6.join(configDir, "projects");
4366
4155
  const name = `${runtimeSessionId}.jsonl`;
4367
- const direct = path7.join(root, cwd.replace(/[^a-zA-Z0-9]/g, "-"), name);
4368
- if (fs7.existsSync(direct)) return direct;
4156
+ const direct = path6.join(root, cwd.replace(/[^a-zA-Z0-9]/g, "-"), name);
4157
+ if (fs6.existsSync(direct)) return direct;
4369
4158
  try {
4370
- for (const slug6 of fs7.readdirSync(root)) {
4371
- const candidate = path7.join(root, slug6, name);
4372
- if (fs7.existsSync(candidate)) return candidate;
4159
+ for (const slug6 of fs6.readdirSync(root)) {
4160
+ const candidate = path6.join(root, slug6, name);
4161
+ if (fs6.existsSync(candidate)) return candidate;
4373
4162
  }
4374
4163
  } catch {
4375
4164
  }
@@ -4377,15 +4166,15 @@ function findClaudeSessionFile(configDir, cwd, runtimeSessionId) {
4377
4166
  }
4378
4167
  function readClaudeRestoredCost(sessionFile) {
4379
4168
  try {
4380
- const stat4 = fs7.statSync(sessionFile);
4169
+ const stat4 = fs6.statSync(sessionFile);
4381
4170
  if (!stat4.isFile() || stat4.size === 0) return null;
4382
4171
  const length = Math.min(stat4.size, RESTORED_COST_TAIL_BYTES);
4383
4172
  const buf = Buffer.alloc(length);
4384
- const fd = fs7.openSync(sessionFile, "r");
4173
+ const fd = fs6.openSync(sessionFile, "r");
4385
4174
  try {
4386
- fs7.readSync(fd, buf, 0, length, stat4.size - length);
4175
+ fs6.readSync(fd, buf, 0, length, stat4.size - length);
4387
4176
  } finally {
4388
- fs7.closeSync(fd);
4177
+ fs6.closeSync(fd);
4389
4178
  }
4390
4179
  const lines = buf.toString("utf8").split("\n");
4391
4180
  for (let i = lines.length - 1; i >= 0; i--) {
@@ -4609,15 +4398,15 @@ function classifyExit(result) {
4609
4398
  if (status === 429 || status === 529) return "rate-limit";
4610
4399
  return "error";
4611
4400
  }
4612
- var import_node_child_process3, import_node_crypto6, fs7, os3, path7, readline, CLAUDE_BLOCKED_FLAGS, SUBAGENT_TRANSCRIPT_MAX_BYTES, SUBAGENT_TERMINAL, RESTORED_COST_TAIL_BYTES, ONE_SHOT_UNSAFE_TOOLS, ClaudeCodeAdapter;
4401
+ var import_node_child_process2, import_node_crypto6, fs6, os3, path6, readline, CLAUDE_BLOCKED_FLAGS, SUBAGENT_TRANSCRIPT_MAX_BYTES, SUBAGENT_TERMINAL, RESTORED_COST_TAIL_BYTES, ONE_SHOT_UNSAFE_TOOLS, ClaudeCodeAdapter;
4613
4402
  var init_claude_code = __esm({
4614
4403
  "../adapters/src/claude-code/index.ts"() {
4615
4404
  "use strict";
4616
- import_node_child_process3 = require("node:child_process");
4405
+ import_node_child_process2 = require("node:child_process");
4617
4406
  import_node_crypto6 = require("node:crypto");
4618
- fs7 = __toESM(require("node:fs"), 1);
4407
+ fs6 = __toESM(require("node:fs"), 1);
4619
4408
  os3 = __toESM(require("node:os"), 1);
4620
- path7 = __toESM(require("node:path"), 1);
4409
+ path6 = __toESM(require("node:path"), 1);
4621
4410
  readline = __toESM(require("node:readline"), 1);
4622
4411
  init_sync_skills();
4623
4412
  init_materialize_bundle();
@@ -4721,7 +4510,7 @@ var init_claude_code = __esm({
4721
4510
  runtimeKind: "claude"
4722
4511
  }
4723
4512
  });
4724
- fs7.mkdirSync(dir, { recursive: true });
4513
+ fs6.mkdirSync(dir, { recursive: true });
4725
4514
  await runSyncSkills(this.opts.syncSkills, {
4726
4515
  actorId: job.actor,
4727
4516
  actorToken: job.actorToken,
@@ -4762,7 +4551,7 @@ var init_claude_code = __esm({
4762
4551
  { runtime: "claude-code", actor: job.actor, ...job.dispatchId ? { dispatchId: job.dispatchId } : {} }
4763
4552
  )
4764
4553
  ];
4765
- const claudeConfigDir = job.env?.["CLAUDE_CONFIG_DIR"] || this.opts.env?.["CLAUDE_CONFIG_DIR"] || process.env["CLAUDE_CONFIG_DIR"] || path7.join(os3.homedir(), ".claude");
4554
+ const claudeConfigDir = job.env?.["CLAUDE_CONFIG_DIR"] || this.opts.env?.["CLAUDE_CONFIG_DIR"] || process.env["CLAUDE_CONFIG_DIR"] || path6.join(os3.homedir(), ".claude");
4766
4555
  const resumedSessionFile = job.runtimeSessionId && job.resumeRuntimeSession ? findClaudeSessionFile(claudeConfigDir, dir, job.runtimeSessionId) : void 0;
4767
4556
  const restoredCost = resumedSessionFile ? readClaudeRestoredCost(resumedSessionFile) : null;
4768
4557
  const otelEnv = this.resolveOtelEnv(job, id);
@@ -4780,11 +4569,11 @@ var init_claude_code = __esm({
4780
4569
  ...otelEnv
4781
4570
  }
4782
4571
  });
4783
- const child = containerized ? (0, import_node_child_process3.spawn)(containerized.bin, containerized.args, {
4572
+ const child = containerized ? (0, import_node_child_process2.spawn)(containerized.bin, containerized.args, {
4784
4573
  cwd: dir,
4785
4574
  env: scrubLeakyEnv(process.env),
4786
4575
  stdio: ["pipe", "pipe", "pipe"]
4787
- }) : (0, import_node_child_process3.spawn)(this.opts.claudeBin ?? "claude", args, {
4576
+ }) : (0, import_node_child_process2.spawn)(this.opts.claudeBin ?? "claude", args, {
4788
4577
  cwd: dir,
4789
4578
  env: {
4790
4579
  // 凭证泄漏防护:剔除 pnpm/npm 脚本注入的变量——npm_lifecycle_script 等会把 serve 的启动命令
@@ -4795,7 +4584,7 @@ var init_claude_code = __esm({
4795
4584
  ...job.env,
4796
4585
  // BUG-4 缓冲:抬高单响应输出 token 上限(临时缓冲,根治靠分解)
4797
4586
  ...this.opts.maxOutputTokens ? { CLAUDE_CODE_MAX_OUTPUT_TOKENS: String(this.opts.maxOutputTokens) } : {},
4798
- ...job.wrapperPaths?.length ? { PATH: `${job.wrapperPaths.map((p2) => path7.dirname(p2)).join(path7.delimiter)}${path7.delimiter}${job.env?.["PATH"] ?? process.env["PATH"] ?? ""}` } : {},
4587
+ ...job.wrapperPaths?.length ? { PATH: `${job.wrapperPaths.map((p2) => path6.dirname(p2)).join(path6.delimiter)}${path6.delimiter}${job.env?.["PATH"] ?? process.env["PATH"] ?? ""}` } : {},
4799
4588
  OASIS_SERVER: job.server.url,
4800
4589
  OASIS_TOKEN: job.actorToken,
4801
4590
  // OTel 遥测(默认关;resolveOtelEnv 决定开不开,runId 进 resource 属性自然归位到本次 run)。
@@ -4875,7 +4664,7 @@ var init_claude_code = __esm({
4875
4664
  if (promptViaStdin) child.stdin.write(prompt2);
4876
4665
  child.stdin.end();
4877
4666
  }
4878
- let transcriptRef = path7.join(dir, "transcript.txt");
4667
+ let transcriptRef = path6.join(dir, "transcript.txt");
4879
4668
  const out = openTranscript(transcriptRef);
4880
4669
  diag = (event, extra) => {
4881
4670
  try {
@@ -5016,14 +4805,14 @@ var init_claude_code = __esm({
5016
4805
  touchMetaExit(dir, { at: (/* @__PURE__ */ new Date()).toISOString(), ...reason ? { reason } : {}, runId: id });
5017
4806
  if (this.opts.cleanupWorkdir && isOneShot) {
5018
4807
  try {
5019
- const keepDir = path7.join(resolveWorkRoot(this.opts.workRoot), "oasis-transcripts");
5020
- fs7.mkdirSync(keepDir, { recursive: true });
5021
- const kept = path7.join(keepDir, `${id}.txt`);
4808
+ const keepDir = path6.join(resolveWorkRoot(this.opts.workRoot), "oasis-transcripts");
4809
+ fs6.mkdirSync(keepDir, { recursive: true });
4810
+ const kept = path6.join(keepDir, `${id}.txt`);
5022
4811
  const src = transcriptRef;
5023
4812
  out.end(() => {
5024
4813
  try {
5025
- fs7.renameSync(src, kept);
5026
- fs7.rmSync(dir, { recursive: true, force: true });
4814
+ fs6.renameSync(src, kept);
4815
+ fs6.rmSync(dir, { recursive: true, force: true });
5027
4816
  } catch {
5028
4817
  }
5029
4818
  });
@@ -5369,7 +5158,7 @@ var init_resilient = __esm({
5369
5158
 
5370
5159
  // ../adapters/src/_core/skill-cache.ts
5371
5160
  function defaultCacheRoot() {
5372
- return path8.join(os4.homedir(), ".oasis", "skill-cache");
5161
+ return path7.join(os4.homedir(), ".oasis", "skill-cache");
5373
5162
  }
5374
5163
  function sessionSkillsSubdir(runtimeKind) {
5375
5164
  switch (runtimeKind) {
@@ -5390,7 +5179,7 @@ async function syncActorSkills(opts) {
5390
5179
  });
5391
5180
  const now = opts.now ?? (() => /* @__PURE__ */ new Date());
5392
5181
  const actorDir = pathForActor(cacheRoot, opts.actorId);
5393
- const manifestPath = path8.join(actorDir, "manifest.json");
5182
+ const manifestPath = path7.join(actorDir, "manifest.json");
5394
5183
  await fsp.mkdir(actorDir, { recursive: true, mode: 493 });
5395
5184
  const release = await acquireLock(cacheRoot, opts.actorId, now, log3);
5396
5185
  try {
@@ -5411,7 +5200,7 @@ async function syncActorSkills(opts) {
5411
5200
  for (const [id, entry] of Object.entries(manifest.skills)) {
5412
5201
  if (cloudIds.has(id)) continue;
5413
5202
  try {
5414
- await fsp.rm(path8.join(actorDir, entry.dir), { recursive: true, force: true });
5203
+ await fsp.rm(path7.join(actorDir, entry.dir), { recursive: true, force: true });
5415
5204
  } catch (err) {
5416
5205
  log3("warn", `\u5220\u9664\u5B64\u513F\u6280\u80FD\u76EE\u5F55\u5931\u8D25 ${entry.dir}`, err);
5417
5206
  }
@@ -5423,7 +5212,7 @@ async function syncActorSkills(opts) {
5423
5212
  const forceClearForSession = /* @__PURE__ */ new Set();
5424
5213
  for (const cloud of cloudMetadata) {
5425
5214
  const variantSlug = variantSlugFor(cloud.contentHash);
5426
- const variantDir = path8.join(actorDir, cloud.dir, variantSlug);
5215
+ const variantDir = path7.join(actorDir, cloud.dir, variantSlug);
5427
5216
  const cached3 = manifest.skills[cloud.id];
5428
5217
  const alreadyMaterialized = await dirLooksValid(variantDir, cloud.fileList);
5429
5218
  let actualFiles = null;
@@ -5445,7 +5234,7 @@ async function syncActorSkills(opts) {
5445
5234
  fileList: cloud.fileList
5446
5235
  };
5447
5236
  if (cached3 && cached3.dir !== cloud.dir) {
5448
- await fsp.rm(path8.join(actorDir, cached3.dir), { recursive: true, force: true }).catch(() => {
5237
+ await fsp.rm(path7.join(actorDir, cached3.dir), { recursive: true, force: true }).catch(() => {
5449
5238
  });
5450
5239
  }
5451
5240
  desiredForSession.set(cloud.dir, { variantDir, variantSlug });
@@ -5507,7 +5296,7 @@ async function syncActorSkills(opts) {
5507
5296
  fileList: cloud.fileList
5508
5297
  };
5509
5298
  if (cached3 && cached3.dir !== cloud.dir) {
5510
- await fsp.rm(path8.join(actorDir, cached3.dir), { recursive: true, force: true }).catch(() => {
5299
+ await fsp.rm(path7.join(actorDir, cached3.dir), { recursive: true, force: true }).catch(() => {
5511
5300
  });
5512
5301
  }
5513
5302
  desiredForSession.set(cloud.dir, { variantDir, variantSlug });
@@ -5537,10 +5326,10 @@ async function syncActorSkills(opts) {
5537
5326
  }
5538
5327
  }
5539
5328
  function pathForActor(cacheRoot, actorId) {
5540
- return path8.join(cacheRoot, "actors", encodeURIComponent(actorId));
5329
+ return path7.join(cacheRoot, "actors", encodeURIComponent(actorId));
5541
5330
  }
5542
5331
  function lockPathFor(cacheRoot, actorId) {
5543
- return path8.join(cacheRoot, "locks", `${encodeURIComponent(actorId)}.lock`);
5332
+ return path7.join(cacheRoot, "locks", `${encodeURIComponent(actorId)}.lock`);
5544
5333
  }
5545
5334
  function aggregateSkillContentHash(files) {
5546
5335
  const sorted = [...files].sort((a, b2) => a.path.localeCompare(b2.path));
@@ -5562,7 +5351,7 @@ function variantSlugFor(contentHash) {
5562
5351
  }
5563
5352
  async function acquireLock(cacheRoot, actorId, now, log3) {
5564
5353
  const lockPath = lockPathFor(cacheRoot, actorId);
5565
- await fsp.mkdir(path8.dirname(lockPath), { recursive: true, mode: 493 });
5354
+ await fsp.mkdir(path7.dirname(lockPath), { recursive: true, mode: 493 });
5566
5355
  const started = now().getTime();
5567
5356
  while (true) {
5568
5357
  try {
@@ -5628,28 +5417,28 @@ async function readManifest(manifestPath, actorId, log3) {
5628
5417
  };
5629
5418
  }
5630
5419
  async function writeManifest(manifestPath, manifest) {
5631
- const dir = path8.dirname(manifestPath);
5420
+ const dir = path7.dirname(manifestPath);
5632
5421
  await fsp.mkdir(dir, { recursive: true });
5633
5422
  const tmp = `${manifestPath}.${process.pid}.${Date.now()}.tmp`;
5634
5423
  await fsp.writeFile(tmp, JSON.stringify(manifest, null, 2), { mode: 420 });
5635
5424
  await fsp.rename(tmp, manifestPath);
5636
5425
  }
5637
5426
  async function materializeVariantDir(actorDir, targetDir, variantSlug, files) {
5638
- const parentPath = path8.join(actorDir, targetDir);
5427
+ const parentPath = path7.join(actorDir, targetDir);
5639
5428
  await fsp.mkdir(parentPath, { recursive: true, mode: 493 });
5640
- const targetPath = path8.join(parentPath, variantSlug);
5641
- const tmpPath = path8.join(parentPath, `${variantSlug}.new.${process.pid}.${Date.now()}`);
5642
- const oldBackup = path8.join(parentPath, `${variantSlug}.old.${process.pid}.${Date.now()}`);
5429
+ const targetPath = path7.join(parentPath, variantSlug);
5430
+ const tmpPath = path7.join(parentPath, `${variantSlug}.new.${process.pid}.${Date.now()}`);
5431
+ const oldBackup = path7.join(parentPath, `${variantSlug}.old.${process.pid}.${Date.now()}`);
5643
5432
  await fsp.rm(tmpPath, { recursive: true, force: true });
5644
5433
  await fsp.mkdir(tmpPath, { recursive: true, mode: 493 });
5645
5434
  for (const f2 of files) {
5646
5435
  if (!f2.content) continue;
5647
- const dest = path8.join(tmpPath, f2.path);
5648
- const rel = path8.relative(tmpPath, dest);
5649
- if (rel.startsWith("..") || path8.isAbsolute(rel)) {
5436
+ const dest = path7.join(tmpPath, f2.path);
5437
+ const rel = path7.relative(tmpPath, dest);
5438
+ if (rel.startsWith("..") || path7.isAbsolute(rel)) {
5650
5439
  throw new Error(`SkillFile.path \u4E0D\u5408\u6CD5: ${f2.path}`);
5651
5440
  }
5652
- await fsp.mkdir(path8.dirname(dest), { recursive: true });
5441
+ await fsp.mkdir(path7.dirname(dest), { recursive: true });
5653
5442
  await fsp.writeFile(dest, f2.content, { mode: 420 });
5654
5443
  }
5655
5444
  let hadOld = false;
@@ -5667,7 +5456,7 @@ async function dirLooksValid(dir, expectFileList) {
5667
5456
  if (expectFileList.length === 0) return false;
5668
5457
  try {
5669
5458
  for (const rel of expectFileList) {
5670
- await fsp.stat(path8.join(dir, rel));
5459
+ await fsp.stat(path7.join(dir, rel));
5671
5460
  }
5672
5461
  return true;
5673
5462
  } catch {
@@ -5679,7 +5468,7 @@ async function hashMaterializedDir(dir, fileList) {
5679
5468
  try {
5680
5469
  const files = [];
5681
5470
  for (const rel of fileList) {
5682
- const content3 = await fsp.readFile(path8.join(dir, rel), "utf8");
5471
+ const content3 = await fsp.readFile(path7.join(dir, rel), "utf8");
5683
5472
  files.push({ path: rel, content: content3 });
5684
5473
  }
5685
5474
  return aggregateSkillContentHash(files);
@@ -5691,10 +5480,10 @@ async function listMaterializedFiles(dir) {
5691
5480
  try {
5692
5481
  const out = [];
5693
5482
  const walk = async (sub) => {
5694
- const abs = sub ? path8.join(dir, sub) : dir;
5483
+ const abs = sub ? path7.join(dir, sub) : dir;
5695
5484
  const entries = await fsp.readdir(abs, { withFileTypes: true });
5696
5485
  for (const e of entries) {
5697
- const rel = sub ? path8.join(sub, e.name) : e.name;
5486
+ const rel = sub ? path7.join(sub, e.name) : e.name;
5698
5487
  if (e.isDirectory()) {
5699
5488
  await walk(rel);
5700
5489
  } else {
@@ -5710,14 +5499,14 @@ async function listMaterializedFiles(dir) {
5710
5499
  }
5711
5500
  async function syncSessionSymlinks(sessionDir, runtimeKind, desired, pending, forceClear, log3) {
5712
5501
  const subdir = sessionSkillsSubdir(runtimeKind);
5713
- const skillsDir = path8.join(sessionDir, subdir);
5502
+ const skillsDir = path7.join(sessionDir, subdir);
5714
5503
  await fsp.mkdir(skillsDir, { recursive: true, mode: 493 });
5715
5504
  for (const [name, { variantDir }] of desired) {
5716
- const link = path8.join(skillsDir, name);
5505
+ const link = path7.join(skillsDir, name);
5717
5506
  let ok3 = false;
5718
5507
  try {
5719
5508
  const current = await fsp.readlink(link);
5720
- ok3 = path8.resolve(skillsDir, current) === variantDir;
5509
+ ok3 = path7.resolve(skillsDir, current) === variantDir;
5721
5510
  } catch {
5722
5511
  }
5723
5512
  if (!ok3) {
@@ -5744,22 +5533,22 @@ async function syncSessionSymlinks(sessionDir, runtimeKind, desired, pending, fo
5744
5533
  }
5745
5534
  }
5746
5535
  try {
5747
- await fsp.rm(path8.join(skillsDir, name), { recursive: true, force: true });
5536
+ await fsp.rm(path7.join(skillsDir, name), { recursive: true, force: true });
5748
5537
  } catch (err) {
5749
5538
  log3("warn", `\u6E05\u5B64\u513F\u5931\u8D25 ${name}`, err);
5750
5539
  }
5751
5540
  }
5752
5541
  }
5753
5542
  async function verifyPendingLink(skillsDir, name, pend, log3) {
5754
- const link = path8.join(skillsDir, name);
5543
+ const link = path7.join(skillsDir, name);
5755
5544
  let resolved;
5756
5545
  try {
5757
5546
  const current = await fsp.readlink(link);
5758
- resolved = path8.resolve(skillsDir, current);
5547
+ resolved = path7.resolve(skillsDir, current);
5759
5548
  } catch {
5760
5549
  return false;
5761
5550
  }
5762
- if (path8.basename(resolved) !== pend.wantedVariantSlug) return false;
5551
+ if (path7.basename(resolved) !== pend.wantedVariantSlug) return false;
5763
5552
  const actual = await listMaterializedFiles(resolved);
5764
5553
  const expected = [...pend.fileList].sort();
5765
5554
  const setsMatch = actual !== null && actual.length === expected.length && actual.every((p2, i) => p2 === expected[i]);
@@ -5774,14 +5563,14 @@ async function verifyPendingLink(skillsDir, name, pend, log3) {
5774
5563
  }
5775
5564
  return true;
5776
5565
  }
5777
- var import_node_crypto7, fsp, os4, path8, MANIFEST_SCHEMA_VERSION, STALE_LOCK_MS, LOCK_ACQUIRE_TIMEOUT_MS, LOCK_POLL_MS;
5566
+ var import_node_crypto7, fsp, os4, path7, MANIFEST_SCHEMA_VERSION, STALE_LOCK_MS, LOCK_ACQUIRE_TIMEOUT_MS, LOCK_POLL_MS;
5778
5567
  var init_skill_cache = __esm({
5779
5568
  "../adapters/src/_core/skill-cache.ts"() {
5780
5569
  "use strict";
5781
5570
  import_node_crypto7 = require("node:crypto");
5782
5571
  fsp = __toESM(require("node:fs/promises"), 1);
5783
5572
  os4 = __toESM(require("node:os"), 1);
5784
- path8 = __toESM(require("node:path"), 1);
5573
+ path7 = __toESM(require("node:path"), 1);
5785
5574
  MANIFEST_SCHEMA_VERSION = 2;
5786
5575
  STALE_LOCK_MS = 6e4;
5787
5576
  LOCK_ACQUIRE_TIMEOUT_MS = 3e4;
@@ -5836,7 +5625,7 @@ var init_skill_cache_http = __esm({
5836
5625
  function resolveOnPath(binary) {
5837
5626
  try {
5838
5627
  const cmd = process.platform === "win32" ? "where" : "which";
5839
- const out = (0, import_node_child_process4.execFileSync)(cmd, [binary], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
5628
+ const out = (0, import_node_child_process3.execFileSync)(cmd, [binary], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
5840
5629
  const first = out.split("\n")[0]?.trim();
5841
5630
  return first || void 0;
5842
5631
  } catch {
@@ -5845,7 +5634,7 @@ function resolveOnPath(binary) {
5845
5634
  }
5846
5635
  function verifyCursorAcpIdentity(binary) {
5847
5636
  try {
5848
- (0, import_node_child_process4.execFileSync)(binary, ["acp", "--help"], {
5637
+ (0, import_node_child_process3.execFileSync)(binary, ["acp", "--help"], {
5849
5638
  stdio: ["ignore", "ignore", "ignore"],
5850
5639
  timeout: ACP_HELP_TIMEOUT_MS
5851
5640
  });
@@ -5861,11 +5650,11 @@ function resolveCursorBinary() {
5861
5650
  if (agent && verifyCursorAcpIdentity(agent)) return agent;
5862
5651
  return void 0;
5863
5652
  }
5864
- var import_node_child_process4, DEFAULT_CURSOR_BIN, ACP_HELP_TIMEOUT_MS;
5653
+ var import_node_child_process3, DEFAULT_CURSOR_BIN, ACP_HELP_TIMEOUT_MS;
5865
5654
  var init_binary = __esm({
5866
5655
  "../adapters/src/cursor/binary.ts"() {
5867
5656
  "use strict";
5868
- import_node_child_process4 = require("node:child_process");
5657
+ import_node_child_process3 = require("node:child_process");
5869
5658
  DEFAULT_CURSOR_BIN = "cursor-agent";
5870
5659
  ACP_HELP_TIMEOUT_MS = 1500;
5871
5660
  }
@@ -6073,7 +5862,7 @@ async function probeCodexAppServer(bin, timeoutMs = 15e3) {
6073
5862
  }
6074
5863
  let child;
6075
5864
  try {
6076
- child = (0, import_node_child_process5.spawn)(bin, ["app-server", "--stdio", "-c", "allow_login_shell=false"], {
5865
+ child = (0, import_node_child_process4.spawn)(bin, ["app-server", "--stdio", "-c", "allow_login_shell=false"], {
6077
5866
  cwd: dir,
6078
5867
  stdio: ["pipe", "pipe", "ignore"],
6079
5868
  env: { ...process.env }
@@ -6253,7 +6042,7 @@ async function discoverPiModels(bin) {
6253
6042
  async function discoverPiModelsRPC(bin) {
6254
6043
  const child = (() => {
6255
6044
  try {
6256
- return (0, import_node_child_process5.spawn)(bin, ["--mode", "rpc", "--no-session", "--no-skills", "--no-prompt-templates", "--no-context-files"], {
6045
+ return (0, import_node_child_process4.spawn)(bin, ["--mode", "rpc", "--no-session", "--no-skills", "--no-prompt-templates", "--no-context-files"], {
6257
6046
  stdio: ["pipe", "pipe", "ignore"],
6258
6047
  env: { ...process.env }
6259
6048
  });
@@ -6330,7 +6119,7 @@ async function runCapture(bin, args, timeoutMs = 15e3) {
6330
6119
  return await new Promise((resolve12) => {
6331
6120
  let child;
6332
6121
  try {
6333
- child = (0, import_node_child_process5.spawn)(bin, args, { stdio: ["ignore", "pipe", "ignore"], env: { ...process.env } });
6122
+ child = (0, import_node_child_process4.spawn)(bin, args, { stdio: ["ignore", "pipe", "ignore"], env: { ...process.env } });
6334
6123
  } catch {
6335
6124
  resolve12(void 0);
6336
6125
  return;
@@ -6361,7 +6150,7 @@ async function runCapture(bin, args, timeoutMs = 15e3) {
6361
6150
  async function discoverACPModels(bin, provider, args = ["acp"], timeoutMs = 12e3) {
6362
6151
  const child = (() => {
6363
6152
  try {
6364
- return (0, import_node_child_process5.spawn)(bin, args, {
6153
+ return (0, import_node_child_process4.spawn)(bin, args, {
6365
6154
  stdio: ["pipe", "pipe", "ignore"],
6366
6155
  env: { ...process.env, ...provider === "hermes" ? { HERMES_YOLO_MODE: "1" } : {} }
6367
6156
  });
@@ -6532,11 +6321,11 @@ function parseOpenclawAgentsText(output) {
6532
6321
  function isSafeIdentifier(value2) {
6533
6322
  return /^[A-Za-z][A-Za-z0-9._/-]*$/.test(value2);
6534
6323
  }
6535
- var import_node_child_process5, import_node_fs2, import_node_os2, import_node_path3, modelCache, CACHE_TTL_MS;
6324
+ var import_node_child_process4, import_node_fs2, import_node_os2, import_node_path3, modelCache, CACHE_TTL_MS;
6536
6325
  var init_models = __esm({
6537
6326
  "../adapters/src/_core/models.ts"() {
6538
6327
  "use strict";
6539
- import_node_child_process5 = require("node:child_process");
6328
+ import_node_child_process4 = require("node:child_process");
6540
6329
  import_node_fs2 = require("node:fs");
6541
6330
  import_node_os2 = require("node:os");
6542
6331
  import_node_path3 = require("node:path");
@@ -6594,16 +6383,16 @@ async function runProtocolSession(job, cfg) {
6594
6383
  ...cfg.env,
6595
6384
  ...job.env,
6596
6385
  ...cfg.yoloEnv ?? {},
6597
- ...job.wrapperPaths?.length ? { PATH: `${job.wrapperPaths.map((p2) => path9.dirname(p2)).join(path9.delimiter)}${path9.delimiter}${job.env?.["PATH"] ?? process.env["PATH"] ?? ""}` } : {},
6386
+ ...job.wrapperPaths?.length ? { PATH: `${job.wrapperPaths.map((p2) => path8.dirname(p2)).join(path8.delimiter)}${path8.delimiter}${job.env?.["PATH"] ?? process.env["PATH"] ?? ""}` } : {},
6598
6387
  OASIS_SERVER: job.server.url,
6599
6388
  OASIS_TOKEN: job.actorToken,
6600
6389
  ...cfg.rootBypassRuntime ? rootBypassEnv(cfg.rootBypassRuntime) : {}
6601
6390
  };
6602
- const child = containerized ? (0, import_node_child_process6.spawn)(containerized.bin, containerized.args, { cwd: dir, env: scrubLeakyEnv(process.env), stdio: ["pipe", "pipe", "pipe"] }) : (0, import_node_child_process6.spawn)(cfg.bin, args, { cwd: dir, env, stdio: ["pipe", "pipe", "pipe"] });
6391
+ const child = containerized ? (0, import_node_child_process5.spawn)(containerized.bin, containerized.args, { cwd: dir, env: scrubLeakyEnv(process.env), stdio: ["pipe", "pipe", "pipe"] }) : (0, import_node_child_process5.spawn)(cfg.bin, args, { cwd: dir, env, stdio: ["pipe", "pipe", "pipe"] });
6603
6392
  child.stdin.on("error", () => {
6604
6393
  });
6605
6394
  if (child.pid !== void 0 && job.workdirKey) lockWorkdir(dir, child.pid, "session", job.workdirKey);
6606
- const transcriptRef = path9.join(dir, "transcript.txt");
6395
+ const transcriptRef = path8.join(dir, "transcript.txt");
6607
6396
  const transcript = openTranscript(transcriptRef);
6608
6397
  const outputCbs = [];
6609
6398
  const telemetryCbs = [];
@@ -6825,13 +6614,13 @@ async function runProtocolSession(job, cfg) {
6825
6614
  }
6826
6615
  };
6827
6616
  }
6828
- var import_node_child_process6, import_node_crypto8, path9, ProtocolCommandRejected, ProtocolSessionClosing;
6617
+ var import_node_child_process5, import_node_crypto8, path8, ProtocolCommandRejected, ProtocolSessionClosing;
6829
6618
  var init_protocol_session = __esm({
6830
6619
  "../adapters/src/_core/protocol-session.ts"() {
6831
6620
  "use strict";
6832
- import_node_child_process6 = require("node:child_process");
6621
+ import_node_child_process5 = require("node:child_process");
6833
6622
  import_node_crypto8 = require("node:crypto");
6834
- path9 = __toESM(require("node:path"), 1);
6623
+ path8 = __toESM(require("node:path"), 1);
6835
6624
  init_claude_code();
6836
6625
  init_usage();
6837
6626
  init_container_runtime();
@@ -6988,12 +6777,12 @@ function pickCodexReportedModel(sources) {
6988
6777
  function codexSessionsRoot() {
6989
6778
  const candidates = [];
6990
6779
  const ch = process.env["CODEX_HOME"];
6991
- if (ch) candidates.push(path10.join(ch, "sessions"));
6780
+ if (ch) candidates.push(path9.join(ch, "sessions"));
6992
6781
  const home2 = process.env["HOME"] || os5.homedir();
6993
- if (home2) candidates.push(path10.join(home2, ".codex", "sessions"));
6782
+ if (home2) candidates.push(path9.join(home2, ".codex", "sessions"));
6994
6783
  for (const c of candidates) {
6995
6784
  try {
6996
- if (fs8.statSync(c).isDirectory()) return c;
6785
+ if (fs7.statSync(c).isDirectory()) return c;
6997
6786
  } catch {
6998
6787
  }
6999
6788
  }
@@ -7005,17 +6794,17 @@ function listRecentRollouts(root, sinceMs) {
7005
6794
  const walk = (d) => {
7006
6795
  let ents;
7007
6796
  try {
7008
- ents = fs8.readdirSync(d, { withFileTypes: true });
6797
+ ents = fs7.readdirSync(d, { withFileTypes: true });
7009
6798
  } catch {
7010
6799
  return;
7011
6800
  }
7012
6801
  for (const ent of ents) {
7013
- const p2 = path10.join(d, ent.name);
6802
+ const p2 = path9.join(d, ent.name);
7014
6803
  if (ent.isDirectory()) walk(p2);
7015
6804
  else if (ent.isFile() && ent.name.startsWith("rollout-") && ent.name.endsWith(".jsonl")) {
7016
6805
  let m2 = 0;
7017
6806
  try {
7018
- m2 = fs8.statSync(p2).mtimeMs;
6807
+ m2 = fs7.statSync(p2).mtimeMs;
7019
6808
  } catch {
7020
6809
  }
7021
6810
  if (m2 >= margin) found2.push({ f: p2, m: m2 });
@@ -7031,12 +6820,12 @@ function findRolloutsBySessionId(root, sessionId) {
7031
6820
  const walk = (d) => {
7032
6821
  let ents;
7033
6822
  try {
7034
- ents = fs8.readdirSync(d, { withFileTypes: true });
6823
+ ents = fs7.readdirSync(d, { withFileTypes: true });
7035
6824
  } catch {
7036
6825
  return;
7037
6826
  }
7038
6827
  for (const ent of ents) {
7039
- const p2 = path10.join(d, ent.name);
6828
+ const p2 = path9.join(d, ent.name);
7040
6829
  if (ent.isDirectory()) walk(p2);
7041
6830
  else if (ent.isFile() && ent.name.startsWith("rollout-") && ent.name.endsWith(suffix)) found2.push(p2);
7042
6831
  }
@@ -7066,7 +6855,7 @@ function locateCodexRollout(workdir, sinceMs, sessionsRoot = codexSessionsRoot()
7066
6855
  const files = listRecentRollouts(sessionsRoot, sinceMs);
7067
6856
  const readLines = (f2) => {
7068
6857
  try {
7069
- return fs8.readFileSync(f2, "utf8").split("\n");
6858
+ return fs7.readFileSync(f2, "utf8").split("\n");
7070
6859
  } catch {
7071
6860
  return null;
7072
6861
  }
@@ -7312,16 +7101,16 @@ function codexTurnErrorMessage(value2) {
7312
7101
  const detail = typeof error2?.additionalDetails === "string" ? error2.additionalDetails.trim() : "";
7313
7102
  return [summary, detail].filter(Boolean).join("\n").slice(0, 4e3);
7314
7103
  }
7315
- var import_node_child_process7, import_node_crypto9, fs8, os5, path10, readline2, CODEX_BLOCKED_FLAGS, CODEX_ARGV_PROMPT_MAX_BYTES, CodexRolloutLineTail, CodexRolloutUsageTail, CodexRolloutModelTail, CODEX_APP_SERVER_BLOCKED_FLAGS, CodexAdapter;
7104
+ var import_node_child_process6, import_node_crypto9, fs7, os5, path9, readline2, CODEX_BLOCKED_FLAGS, CODEX_ARGV_PROMPT_MAX_BYTES, CodexRolloutLineTail, CodexRolloutUsageTail, CodexRolloutModelTail, CODEX_APP_SERVER_BLOCKED_FLAGS, CodexAdapter;
7316
7105
  var init_codex = __esm({
7317
7106
  "../adapters/src/codex/index.ts"() {
7318
7107
  "use strict";
7319
- import_node_child_process7 = require("node:child_process");
7108
+ import_node_child_process6 = require("node:child_process");
7320
7109
  init_container_runtime();
7321
7110
  import_node_crypto9 = require("node:crypto");
7322
- fs8 = __toESM(require("node:fs"), 1);
7111
+ fs7 = __toESM(require("node:fs"), 1);
7323
7112
  os5 = __toESM(require("node:os"), 1);
7324
- path10 = __toESM(require("node:path"), 1);
7113
+ path9 = __toESM(require("node:path"), 1);
7325
7114
  readline2 = __toESM(require("node:readline"), 1);
7326
7115
  init_sync_skills();
7327
7116
  init_materialize_bundle();
@@ -7359,9 +7148,9 @@ var init_codex = __esm({
7359
7148
  rest = "";
7360
7149
  /** 读新追加的整行逐行回调;文件被截短时先回调 onReset 再从头读。读失败抛给调用方(调用方吞)。 */
7361
7150
  read(onLine, onReset) {
7362
- const fd = fs8.openSync(this.file, "r");
7151
+ const fd = fs7.openSync(this.file, "r");
7363
7152
  try {
7364
- const size = fs8.fstatSync(fd).size;
7153
+ const size = fs7.fstatSync(fd).size;
7365
7154
  if (size < this.offset) {
7366
7155
  this.offset = 0;
7367
7156
  this.rest = "";
@@ -7369,7 +7158,7 @@ var init_codex = __esm({
7369
7158
  }
7370
7159
  if (size > this.offset) {
7371
7160
  const buf = Buffer.alloc(size - this.offset);
7372
- const n = fs8.readSync(fd, buf, 0, buf.length, this.offset);
7161
+ const n = fs7.readSync(fd, buf, 0, buf.length, this.offset);
7373
7162
  this.offset += n;
7374
7163
  const text7 = this.rest + buf.subarray(0, n).toString("utf8");
7375
7164
  const lines = text7.split("\n");
@@ -7377,7 +7166,7 @@ var init_codex = __esm({
7377
7166
  for (const line2 of lines) onLine(line2);
7378
7167
  }
7379
7168
  } finally {
7380
- fs8.closeSync(fd);
7169
+ fs7.closeSync(fd);
7381
7170
  }
7382
7171
  }
7383
7172
  };
@@ -7640,7 +7429,7 @@ ${task}` : task;
7640
7429
  let fromRollout;
7641
7430
  if (state.rolloutPath) {
7642
7431
  try {
7643
- fromRollout = parseCodexModel(fs8.readFileSync(state.rolloutPath, "utf8").split("\n"));
7432
+ fromRollout = parseCodexModel(fs7.readFileSync(state.rolloutPath, "utf8").split("\n"));
7644
7433
  } catch {
7645
7434
  }
7646
7435
  }
@@ -7792,19 +7581,19 @@ ${task}` : task;
7792
7581
  ...this.opts.workRoot !== void 0 ? { workRoot: this.opts.workRoot } : {},
7793
7582
  ...this.opts.env !== void 0 ? { extraEnvEarly: this.opts.env } : {}
7794
7583
  });
7795
- const child = containerized ? (0, import_node_child_process7.spawn)(containerized.bin, containerized.args, {
7584
+ const child = containerized ? (0, import_node_child_process6.spawn)(containerized.bin, containerized.args, {
7796
7585
  cwd: dir,
7797
7586
  env: scrubLeakyEnv(process.env),
7798
7587
  // stdin 的开合口径与宿主分支一致:codex 只在走 stdin 时才该收到管道(见下方长注释)。
7799
7588
  // 容器包装本身带 `-i`,podman 会把这条管道透传给容器内进程。
7800
7589
  stdio: [promptViaStdin ? "pipe" : "ignore", "pipe", "pipe"]
7801
- }) : (0, import_node_child_process7.spawn)(this.opts.codexBin ?? "codex", args, {
7590
+ }) : (0, import_node_child_process6.spawn)(this.opts.codexBin ?? "codex", args, {
7802
7591
  cwd: dir,
7803
7592
  env: {
7804
7593
  ...scrubLeakyEnv(process.env),
7805
7594
  ...this.opts.env,
7806
7595
  ...job.env,
7807
- ...job.wrapperPaths?.length ? { PATH: `${job.wrapperPaths.map((p2) => path10.dirname(p2)).join(path10.delimiter)}${path10.delimiter}${job.env?.["PATH"] ?? process.env["PATH"] ?? ""}` } : {},
7596
+ ...job.wrapperPaths?.length ? { PATH: `${job.wrapperPaths.map((p2) => path9.dirname(p2)).join(path9.delimiter)}${path9.delimiter}${job.env?.["PATH"] ?? process.env["PATH"] ?? ""}` } : {},
7808
7597
  OASIS_SERVER: job.server.url,
7809
7598
  OASIS_TOKEN: job.actorToken
7810
7599
  },
@@ -7821,7 +7610,7 @@ ${task}` : task;
7821
7610
  child.stdin.end(prompt2);
7822
7611
  }
7823
7612
  if (child.pid !== void 0 && job.workdirKey) lockWorkdir(dir, child.pid, "session", job.workdirKey);
7824
- let transcriptRef = path10.join(dir, "transcript.txt");
7613
+ let transcriptRef = path9.join(dir, "transcript.txt");
7825
7614
  const out = openTranscript(transcriptRef);
7826
7615
  const outputCbs = [];
7827
7616
  const telemetryCbs = [];
@@ -7923,14 +7712,14 @@ ${task}` : task;
7923
7712
  }
7924
7713
  if (this.opts.cleanupWorkdir && !job.workdirKey) {
7925
7714
  try {
7926
- const keepDir = path10.join(resolveWorkRoot(this.opts.workRoot), "oasis-transcripts");
7927
- fs8.mkdirSync(keepDir, { recursive: true });
7928
- const kept = path10.join(keepDir, `${id}.txt`);
7715
+ const keepDir = path9.join(resolveWorkRoot(this.opts.workRoot), "oasis-transcripts");
7716
+ fs7.mkdirSync(keepDir, { recursive: true });
7717
+ const kept = path9.join(keepDir, `${id}.txt`);
7929
7718
  const src = transcriptRef;
7930
7719
  out.end(() => {
7931
7720
  try {
7932
- fs8.renameSync(src, kept);
7933
- fs8.rmSync(dir, { recursive: true, force: true });
7721
+ fs7.renameSync(src, kept);
7722
+ fs7.rmSync(dir, { recursive: true, force: true });
7934
7723
  } catch {
7935
7724
  }
7936
7725
  });
@@ -8448,10 +8237,10 @@ function buildReportedUsage(acc, model, inputTokensIncludeCacheRead) {
8448
8237
  }
8449
8238
  async function runACPSession(job, cfg) {
8450
8239
  if (job.executionEnv) {
8451
- throw new Error(`executionEnv\uFF1A${path11.basename(cfg.bin)}\uFF08ACP runtime\uFF09\u5C1A\u672A\u652F\u6301\u5BB9\u5668\u5316\u6267\u884C\uFF08ADR-0147\uFF09\uFF0C\u8BF7\u6539\u6D3E claude-code \u5458\u5DE5\u6216\u53BB\u6389\u8BE5\u8282\u70B9\u7684 executionEnv \u58F0\u660E`);
8240
+ throw new Error(`executionEnv\uFF1A${path10.basename(cfg.bin)}\uFF08ACP runtime\uFF09\u5C1A\u672A\u652F\u6301\u5BB9\u5668\u5316\u6267\u884C\uFF08ADR-0147\uFF09\uFF0C\u8BF7\u6539\u6D3E claude-code \u5458\u5DE5\u6216\u53BB\u6389\u8BE5\u8282\u70B9\u7684 executionEnv \u58F0\u660E`);
8452
8241
  }
8453
8242
  const slug6 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
8454
- const binTag = path11.basename(cfg.bin).replace(/[^a-zA-Z0-9]/g, "").slice(0, 12) || "acp";
8243
+ const binTag = path10.basename(cfg.bin).replace(/[^a-zA-Z0-9]/g, "").slice(0, 12) || "acp";
8455
8244
  const id = `${binTag}-${slug6}-${(0, import_node_crypto10.randomUUID)().slice(0, 8)}`;
8456
8245
  const dir = job.workdirKey ? prepareWorkdir({
8457
8246
  workRoot: cfg.workRoot,
@@ -8485,7 +8274,7 @@ async function runACPSession(job, cfg) {
8485
8274
  });
8486
8275
  }
8487
8276
  const task = materializeBundle(dir, job.bundle);
8488
- const child = (0, import_node_child_process8.spawn)(cfg.bin, [...cfg.args ?? [], ...prepareExtraArgs(
8277
+ const child = (0, import_node_child_process7.spawn)(cfg.bin, [...cfg.args ?? [], ...prepareExtraArgs(
8489
8278
  job.extraArgs,
8490
8279
  cfg.extraArgs,
8491
8280
  cfg.blockedFlags,
@@ -8497,9 +8286,9 @@ async function runACPSession(job, cfg) {
8497
8286
  ...cfg.env,
8498
8287
  ...job.env,
8499
8288
  ...cfg.yoloEnv ?? {},
8500
- ...job.wrapperPaths?.length ? { PATH: `${job.wrapperPaths.map((p2) => path11.dirname(p2)).join(path11.delimiter)}${path11.delimiter}${job.env?.["PATH"] ?? process.env["PATH"] ?? ""}` } : {},
8289
+ ...job.wrapperPaths?.length ? { PATH: `${job.wrapperPaths.map((p2) => path10.dirname(p2)).join(path10.delimiter)}${path10.delimiter}${job.env?.["PATH"] ?? process.env["PATH"] ?? ""}` } : {},
8501
8290
  // 每会话技能目录绝对路径 → env(供不扫 cwd 的 runtime 经 config external_dirs 发现,见 injectSkillsDirEnv)。
8502
- ...cfg.injectSkillsDirEnv ? { [cfg.injectSkillsDirEnv]: path11.join(dir, sessionSkillsSubdir(cfg.runtimeKind ?? "")) } : {},
8291
+ ...cfg.injectSkillsDirEnv ? { [cfg.injectSkillsDirEnv]: path10.join(dir, sessionSkillsSubdir(cfg.runtimeKind ?? "")) } : {},
8503
8292
  OASIS_SERVER: job.server.url,
8504
8293
  OASIS_TOKEN: job.actorToken,
8505
8294
  ...cfg.rootBypassRuntime ? rootBypassEnv(cfg.rootBypassRuntime) : {}
@@ -8509,7 +8298,7 @@ async function runACPSession(job, cfg) {
8509
8298
  child.stdin.on("error", () => {
8510
8299
  });
8511
8300
  if (child.pid !== void 0 && job.workdirKey) lockWorkdir(dir, child.pid, "session", job.workdirKey);
8512
- let transcriptRef = path11.join(dir, "transcript.txt");
8301
+ let transcriptRef = path10.join(dir, "transcript.txt");
8513
8302
  const transcriptOut = openTranscript(transcriptRef);
8514
8303
  const outputCbs = [];
8515
8304
  const telemetryCbs = [];
@@ -8611,14 +8400,14 @@ ${note}` : note };
8611
8400
  }
8612
8401
  if (cfg.cleanupWorkdir && !job.workdirKey) {
8613
8402
  try {
8614
- const keepDir = path11.join(resolveWorkRoot(cfg.workRoot), "oasis-transcripts");
8615
- fs9.mkdirSync(keepDir, { recursive: true });
8616
- const kept = path11.join(keepDir, `${id}.txt`);
8403
+ const keepDir = path10.join(resolveWorkRoot(cfg.workRoot), "oasis-transcripts");
8404
+ fs8.mkdirSync(keepDir, { recursive: true });
8405
+ const kept = path10.join(keepDir, `${id}.txt`);
8617
8406
  const src = transcriptRef;
8618
8407
  transcriptOut.end(() => {
8619
8408
  try {
8620
- fs9.renameSync(src, kept);
8621
- fs9.rmSync(dir, { recursive: true, force: true });
8409
+ fs8.renameSync(src, kept);
8410
+ fs8.rmSync(dir, { recursive: true, force: true });
8622
8411
  } catch {
8623
8412
  }
8624
8413
  });
@@ -8968,14 +8757,14 @@ ${task}` : task;
8968
8757
  return run;
8969
8758
  }
8970
8759
  }
8971
- var import_node_child_process8, import_node_crypto10, fs9, path11, readline3, PROMPT_TURN_MESSAGE_MAX, PROMPT_TURN_STDERR_MAX_LINES, PROMPT_FAILURE_STDERR_SETTLE_MS, ACPClient, LEGACY_APPROVE_OPTION_ID;
8760
+ var import_node_child_process7, import_node_crypto10, fs8, path10, readline3, PROMPT_TURN_MESSAGE_MAX, PROMPT_TURN_STDERR_MAX_LINES, PROMPT_FAILURE_STDERR_SETTLE_MS, ACPClient, LEGACY_APPROVE_OPTION_ID;
8972
8761
  var init_acp = __esm({
8973
8762
  "../adapters/src/_core/acp.ts"() {
8974
8763
  "use strict";
8975
- import_node_child_process8 = require("node:child_process");
8764
+ import_node_child_process7 = require("node:child_process");
8976
8765
  import_node_crypto10 = require("node:crypto");
8977
- fs9 = __toESM(require("node:fs"), 1);
8978
- path11 = __toESM(require("node:path"), 1);
8766
+ fs8 = __toESM(require("node:fs"), 1);
8767
+ path10 = __toESM(require("node:path"), 1);
8979
8768
  readline3 = __toESM(require("node:readline"), 1);
8980
8769
  init_sync_skills();
8981
8770
  init_session_paths();
@@ -9577,10 +9366,10 @@ function cursorAcpSessionStorePath(sessionId, env = process.env) {
9577
9366
  if (!sessionId) return void 0;
9578
9367
  const home2 = env["CURSOR_HOME"] || env["HOME"] || os6.homedir();
9579
9368
  if (!home2) return void 0;
9580
- const base = env["CURSOR_HOME"] ? home2 : path12.join(home2, ".cursor");
9581
- const file = path12.join(base, "acp-sessions", sessionId, "store.db");
9369
+ const base = env["CURSOR_HOME"] ? home2 : path11.join(home2, ".cursor");
9370
+ const file = path11.join(base, "acp-sessions", sessionId, "store.db");
9582
9371
  try {
9583
- return fs10.existsSync(file) ? file : void 0;
9372
+ return fs9.existsSync(file) ? file : void 0;
9584
9373
  } catch {
9585
9374
  return void 0;
9586
9375
  }
@@ -9715,13 +9504,13 @@ async function readCursorRoutedModel(sessionId, window3) {
9715
9504
  }
9716
9505
  }
9717
9506
  }
9718
- var fs10, os6, path12, MAX_TABLES, MAX_ROWS_PER_TABLE, TURN_CLOCK_TOLERANCE_MS, TIME_KEY_TAILS;
9507
+ var fs9, os6, path11, MAX_TABLES, MAX_ROWS_PER_TABLE, TURN_CLOCK_TOLERANCE_MS, TIME_KEY_TAILS;
9719
9508
  var init_local_store = __esm({
9720
9509
  "../adapters/src/cursor/local-store.ts"() {
9721
9510
  "use strict";
9722
- fs10 = __toESM(require("node:fs"), 1);
9511
+ fs9 = __toESM(require("node:fs"), 1);
9723
9512
  os6 = __toESM(require("node:os"), 1);
9724
- path12 = __toESM(require("node:path"), 1);
9513
+ path11 = __toESM(require("node:path"), 1);
9725
9514
  MAX_TABLES = 24;
9726
9515
  MAX_ROWS_PER_TABLE = 200;
9727
9516
  TURN_CLOCK_TOLERANCE_MS = 1e3;
@@ -9996,12 +9785,12 @@ function normalizePi(line2) {
9996
9785
  function piReportedModel(state, requested) {
9997
9786
  return pickReportedModel({ runtime: [state.model, state.startupModel], requested });
9998
9787
  }
9999
- var fs11, path13, PI_SESSION_FILE, PI_BLOCKED_FLAGS, PiAdapter;
9788
+ var fs10, path12, PI_SESSION_FILE, PI_BLOCKED_FLAGS, PiAdapter;
10000
9789
  var init_pi = __esm({
10001
9790
  "../adapters/src/pi/index.ts"() {
10002
9791
  "use strict";
10003
- fs11 = __toESM(require("node:fs"), 1);
10004
- path13 = __toESM(require("node:path"), 1);
9792
+ fs10 = __toESM(require("node:fs"), 1);
9793
+ path12 = __toESM(require("node:path"), 1);
10005
9794
  init_protocol_session();
10006
9795
  init_usage();
10007
9796
  init_model_report();
@@ -10028,9 +9817,9 @@ var init_pi = __esm({
10028
9817
  buildArgs(job2, dir) {
10029
9818
  const model = job2.model ?? opts.model;
10030
9819
  if (!job2.workdirKey) return ["--mode", "rpc", "--no-session", ...model ? ["--model", model] : []];
10031
- const sessionFile = path13.join(dir, PI_SESSION_FILE);
9820
+ const sessionFile = path12.join(dir, PI_SESSION_FILE);
10032
9821
  try {
10033
- if (!fs11.existsSync(sessionFile)) fs11.writeFileSync(sessionFile, "");
9822
+ if (!fs10.existsSync(sessionFile)) fs10.writeFileSync(sessionFile, "");
10034
9823
  } catch {
10035
9824
  return ["--mode", "rpc", "--no-session"];
10036
9825
  }
@@ -10137,7 +9926,7 @@ function pdeathsigAvailable(probe = defaultProbe) {
10137
9926
  function defaultProbe() {
10138
9927
  if (os7.platform() !== "linux") return false;
10139
9928
  try {
10140
- const help = (0, import_node_child_process9.execFileSync)("setpriv", ["--help"], { stdio: ["ignore", "pipe", "ignore"], timeout: 2e3 }).toString();
9929
+ const help = (0, import_node_child_process8.execFileSync)("setpriv", ["--help"], { stdio: ["ignore", "pipe", "ignore"], timeout: 2e3 }).toString();
10141
9930
  return help.includes("--pdeathsig");
10142
9931
  } catch {
10143
9932
  return false;
@@ -10149,11 +9938,11 @@ function pdeathsigWrap(bin, args) {
10149
9938
  function shouldBindRuntimePdeathsig() {
10150
9939
  return process.env["OASIS_BIND_RUNTIME_PDEATHSIG"] === "1" && pdeathsigAvailable();
10151
9940
  }
10152
- var import_node_child_process9, os7, cachedAvailable;
9941
+ var import_node_child_process8, os7, cachedAvailable;
10153
9942
  var init_pdeathsig = __esm({
10154
9943
  "../adapters/src/_core/pdeathsig.ts"() {
10155
9944
  "use strict";
10156
- import_node_child_process9 = require("node:child_process");
9945
+ import_node_child_process8 = require("node:child_process");
10157
9946
  os7 = __toESM(require("node:os"), 1);
10158
9947
  }
10159
9948
  });
@@ -10201,7 +9990,7 @@ function buildEnv(job, cfg) {
10201
9990
  ...scrubLeakyEnv(process.env),
10202
9991
  ...cfg.env,
10203
9992
  ...job.env,
10204
- ...job.wrapperPaths?.length ? { PATH: `${job.wrapperPaths.map((p2) => path14.dirname(p2)).join(path14.delimiter)}${path14.delimiter}${job.env?.["PATH"] ?? process.env["PATH"] ?? ""}` } : {},
9993
+ ...job.wrapperPaths?.length ? { PATH: `${job.wrapperPaths.map((p2) => path13.dirname(p2)).join(path13.delimiter)}${path13.delimiter}${job.env?.["PATH"] ?? process.env["PATH"] ?? ""}` } : {},
10205
9994
  OASIS_SERVER: job.server.url,
10206
9995
  OASIS_TOKEN: job.actorToken,
10207
9996
  // root 下放行 bypassPermissions(仅 claude 家族 runtime 声明;见 _core/root-bypass.ts)。
@@ -10226,14 +10015,14 @@ function makeFinish(id, cfg, transcriptRefPtr, transcriptOut, dir, workdirKey, r
10226
10015
  }
10227
10016
  if (cfg.cleanupWorkdir && !workdirKey) {
10228
10017
  try {
10229
- const keepDir = path14.join(resolveWorkRoot(cfg.workRoot), "oasis-transcripts");
10230
- fs12.mkdirSync(keepDir, { recursive: true });
10231
- const kept = path14.join(keepDir, `${id}.txt`);
10018
+ const keepDir = path13.join(resolveWorkRoot(cfg.workRoot), "oasis-transcripts");
10019
+ fs11.mkdirSync(keepDir, { recursive: true });
10020
+ const kept = path13.join(keepDir, `${id}.txt`);
10232
10021
  const src = transcriptRefPtr.value;
10233
10022
  transcriptOut.end(() => {
10234
10023
  try {
10235
- fs12.renameSync(src, kept);
10236
- fs12.rmSync(dir, { recursive: true, force: true });
10024
+ fs11.renameSync(src, kept);
10025
+ fs11.rmSync(dir, { recursive: true, force: true });
10237
10026
  } catch {
10238
10027
  }
10239
10028
  });
@@ -10264,17 +10053,17 @@ async function runStreamJson(job, cfg) {
10264
10053
  ...cfg.env !== void 0 ? { extraEnvEarly: cfg.env } : {}
10265
10054
  });
10266
10055
  const direct = shouldBindRuntimePdeathsig() ? pdeathsigWrap(cfg.bin, args) : { bin: cfg.bin, args };
10267
- const child = containerized ? (0, import_node_child_process10.spawn)(containerized.bin, containerized.args, {
10056
+ const child = containerized ? (0, import_node_child_process9.spawn)(containerized.bin, containerized.args, {
10268
10057
  cwd: dir,
10269
10058
  env: scrubLeakyEnv(process.env),
10270
10059
  stdio: ["ignore", "pipe", "pipe"]
10271
- }) : (0, import_node_child_process10.spawn)(direct.bin, direct.args, {
10060
+ }) : (0, import_node_child_process9.spawn)(direct.bin, direct.args, {
10272
10061
  cwd: dir,
10273
10062
  env: buildEnv(job, cfg),
10274
10063
  stdio: ["ignore", "pipe", "pipe"]
10275
10064
  });
10276
10065
  if (child.pid !== void 0 && job.workdirKey) lockWorkdir(dir, child.pid, "session", job.workdirKey);
10277
- const transcriptRefPtr = { value: path14.join(dir, "transcript.txt") };
10066
+ const transcriptRefPtr = { value: path13.join(dir, "transcript.txt") };
10278
10067
  const transcriptOut = openTranscript(transcriptRefPtr.value);
10279
10068
  const outputCbs = [];
10280
10069
  const telemetryCbs = [];
@@ -10374,17 +10163,17 @@ async function runOneShotText(job, cfg) {
10374
10163
  ...cfg.env !== void 0 ? { extraEnvEarly: cfg.env } : {}
10375
10164
  });
10376
10165
  const direct = shouldBindRuntimePdeathsig() ? pdeathsigWrap(cfg.bin, args) : { bin: cfg.bin, args };
10377
- const child = containerized ? (0, import_node_child_process10.spawn)(containerized.bin, containerized.args, {
10166
+ const child = containerized ? (0, import_node_child_process9.spawn)(containerized.bin, containerized.args, {
10378
10167
  cwd: dir,
10379
10168
  env: scrubLeakyEnv(process.env),
10380
10169
  stdio: ["ignore", "pipe", "pipe"]
10381
- }) : (0, import_node_child_process10.spawn)(direct.bin, direct.args, {
10170
+ }) : (0, import_node_child_process9.spawn)(direct.bin, direct.args, {
10382
10171
  cwd: dir,
10383
10172
  env: buildEnv(job, cfg),
10384
10173
  stdio: ["ignore", "pipe", "pipe"]
10385
10174
  });
10386
10175
  if (child.pid !== void 0 && job.workdirKey) lockWorkdir(dir, child.pid, "session", job.workdirKey);
10387
- const transcriptRefPtr = { value: path14.join(dir, "transcript.txt") };
10176
+ const transcriptRefPtr = { value: path13.join(dir, "transcript.txt") };
10388
10177
  const transcriptOut = openTranscript(transcriptRefPtr.value);
10389
10178
  const outputCbs = [];
10390
10179
  const exitCbs = [];
@@ -10457,14 +10246,14 @@ async function runOneShotText(job, cfg) {
10457
10246
  }
10458
10247
  });
10459
10248
  }
10460
- var import_node_child_process10, import_node_crypto11, fs12, path14, readline4;
10249
+ var import_node_child_process9, import_node_crypto11, fs11, path13, readline4;
10461
10250
  var init_subprocess = __esm({
10462
10251
  "../adapters/src/_core/subprocess.ts"() {
10463
10252
  "use strict";
10464
- import_node_child_process10 = require("node:child_process");
10253
+ import_node_child_process9 = require("node:child_process");
10465
10254
  import_node_crypto11 = require("node:crypto");
10466
- fs12 = __toESM(require("node:fs"), 1);
10467
- path14 = __toESM(require("node:path"), 1);
10255
+ fs11 = __toESM(require("node:fs"), 1);
10256
+ path13 = __toESM(require("node:path"), 1);
10468
10257
  readline4 = __toESM(require("node:readline"), 1);
10469
10258
  init_claude_code();
10470
10259
  init_container_runtime();
@@ -11338,6 +11127,398 @@ var init_src2 = __esm({
11338
11127
  }
11339
11128
  });
11340
11129
 
11130
+ // ../server/src/design/board-key.ts
11131
+ async function boardLinkOf(blobs, rev, cache) {
11132
+ if (rev.contentKind !== "manifest" || rev.contentRef.length === 0) return null;
11133
+ const hit = cache?.get(rev.id);
11134
+ if (hit !== void 0) return hit;
11135
+ const manifest = parseManifest(await blobs.get(rev.contentRef));
11136
+ const entry = manifest.entries.find((e) => e.path === BOARD_LINK_FILE);
11137
+ const text7 = entry ? new TextDecoder().decode(await blobs.get(entry.hash)) : null;
11138
+ cache?.set(rev.id, text7);
11139
+ return text7;
11140
+ }
11141
+ async function latestBoardLink(model, blobs, a, cache) {
11142
+ const head = a.currentRev ? model.revisions.get(a.currentRev) : void 0;
11143
+ const rest = [...model.revisions.values()].filter((r) => r.artifactId === a.id && r.id !== head?.id && r.contentKind === "manifest").sort((x2, y) => y.createdAt.localeCompare(x2.createdAt));
11144
+ for (const rev of head ? [head, ...rest] : rest) {
11145
+ const text7 = await boardLinkOf(blobs, rev, cache);
11146
+ if (text7 !== null) return text7;
11147
+ }
11148
+ return null;
11149
+ }
11150
+ async function resolveDesignBoard(args, deps = {}) {
11151
+ if (args.companyId.trim().length === 0) {
11152
+ throw new Error(`\u62FF\u4E0D\u5230 ${args.artifact.id} \u6240\u5728\u7684\u516C\u53F8\uFF0C\u4E0D\u80FD\u786E\u5B9A\u753B\u677F\u2014\u2014\u4E0D\u56DE\u843D\u5230\u4E0D\u5E26\u516C\u53F8\u7684\u65E7\u753B\u677F\u540D`);
11153
+ }
11154
+ const boardLink = args.blobs ? await latestBoardLink(args.model, args.blobs, args.artifact, deps.cache) : null;
11155
+ const fromLink = boardLink !== null ? parseBoardLinkBoardKey(boardLink) : null;
11156
+ if (fromLink !== null) return { boardKey: fromLink, source: "board-link" };
11157
+ return { boardKey: (deps.deriveKey ?? deriveBoardKey)(args.companyId, args.artifact.id), source: "derived" };
11158
+ }
11159
+ var init_board_key = __esm({
11160
+ "../server/src/design/board-key.ts"() {
11161
+ "use strict";
11162
+ init_src();
11163
+ init_src2();
11164
+ }
11165
+ });
11166
+
11167
+ // ../server/src/domains/chat-sessions/session-runtime-target.ts
11168
+ function sessionRuntimeOverride(session) {
11169
+ const nodeId = session?.overrideNodeId?.trim();
11170
+ const runtimeKind = session?.overrideRuntimeKind?.trim();
11171
+ if (!nodeId || !runtimeKind) return null;
11172
+ return { nodeId, runtimeKind };
11173
+ }
11174
+ function chatSessionInitialRuntime(input) {
11175
+ const trimmed = (v2) => typeof v2 === "string" && v2.trim() ? v2.trim() : null;
11176
+ const binding = input.binding?.nodeId && input.binding?.runtimeKind ? { nodeId: input.binding.nodeId, runtimeKind: input.binding.runtimeKind } : null;
11177
+ const requestedOverride = input.requested?.override;
11178
+ const picked = requestedOverride?.nodeId && requestedOverride?.runtimeKind ? { nodeId: requestedOverride.nodeId, runtimeKind: requestedOverride.runtimeKind } : null;
11179
+ const target = picked ?? binding;
11180
+ const followsAgentTarget = !picked || !!binding && picked.nodeId === binding.nodeId && picked.runtimeKind === binding.runtimeKind;
11181
+ const requestedModel = input.requested?.model;
11182
+ const model = requestedModel !== void 0 ? trimmed(requestedModel) : followsAgentTarget ? trimmed(input.agentModel) : null;
11183
+ return {
11184
+ overrideNodeId: target?.nodeId ?? null,
11185
+ overrideRuntimeKind: target?.runtimeKind ?? null,
11186
+ model
11187
+ };
11188
+ }
11189
+ function sessionRuntimeOverrideChangesTarget(session, binding) {
11190
+ const override = sessionRuntimeOverride(session);
11191
+ if (!override) return false;
11192
+ if (!binding) return true;
11193
+ return override.nodeId !== binding.nodeId || override.runtimeKind !== binding.runtimeKind;
11194
+ }
11195
+ async function resolveSessionRuntimeTarget(input) {
11196
+ const { probe } = input;
11197
+ const override = sessionRuntimeOverride(input.session);
11198
+ if (!override) {
11199
+ return probe.binding ? { binding: probe.binding, source: "actor_binding" } : null;
11200
+ }
11201
+ const kinds = await probe.availableRuntimeKinds(override.nodeId);
11202
+ const nodeName = (await probe.nodeDisplayName?.(override.nodeId))?.trim() || override.nodeId;
11203
+ if (kinds === null) {
11204
+ throw new SessionRuntimeOverrideError(
11205
+ "CHAT_SESSION_NODE_UNAVAILABLE",
11206
+ `\u8FD9\u6761\u4F1A\u8BDD\u9009\u5B9A\u7684\u8282\u70B9\u300C${nodeName}\u300D\u5F53\u524D\u4E0D\u53EF\u7528\uFF08\u53EF\u80FD\u5DF2\u79BB\u7EBF\u3001\u5DF2\u5220\u9664\uFF0C\u6216\u4E0D\u5C5E\u4E8E\u5F53\u524D\u516C\u53F8\uFF09\u3002\u8BF7\u786E\u8BA4\u90A3\u53F0\u673A\u5668\u5F00\u7740\u3001\u8282\u70B9\u7A0B\u5E8F\u5728\u8FD0\u884C\u540E\u518D\u53D1\uFF1B\u6216${CLEAR_SESSION_RUNTIME_HINT}`
11207
+ );
11208
+ }
11209
+ if (!kinds.includes(override.runtimeKind)) {
11210
+ throw new SessionRuntimeOverrideError(
11211
+ "CHAT_SESSION_RUNTIME_UNAVAILABLE",
11212
+ `\u8282\u70B9\u300C${nodeName}\u300D\u4E0A\u6CA1\u6709\u53EF\u7528\u7684\u8FD0\u884C\u65F6 ${override.runtimeKind}\uFF08\u53EF\u80FD\u5DF2\u5378\u8F7D\u3001\u8FD8\u6CA1\u88C5\u597D\uFF0C\u6216\u672A\u4E0A\u62A5\uFF09\u3002\u8BF7\u5728\u90A3\u53F0\u673A\u5668\u4E0A\u88C5\u597D\u540E\u518D\u53D1\uFF1B\u6216${CLEAR_SESSION_RUNTIME_HINT}`
11213
+ );
11214
+ }
11215
+ if (!probe.binding) {
11216
+ throw new SessionRuntimeOverrideError(
11217
+ "ACTOR_BINDING_MISSING",
11218
+ `\u8FD9\u4F4D Agent \u8FD8\u6CA1\u6709\u7ED1\u5B9A\u8FD0\u884C\u65F6\uFF0C\u9009\u5B9A\u8282\u70B9\u4E5F\u6D3E\u4E0D\u51FA\u53BB\u3002\u8BF7\u5148\u7ED9\u4ED6\u7ED1\u5B9A\u4E00\u4E2A\u53EF\u7528\u7684\u8FD0\u884C\u65F6\uFF1B\u6216${CLEAR_SESSION_RUNTIME_HINT}`
11219
+ );
11220
+ }
11221
+ return {
11222
+ binding: { actorId: probe.binding.actorId, nodeId: override.nodeId, runtimeKind: override.runtimeKind, status: probe.binding.status },
11223
+ source: "session_override"
11224
+ };
11225
+ }
11226
+ function canAnnounceRuntimeSwitch(prior) {
11227
+ const nodeId = prior?.runtimeId?.trim();
11228
+ const kind = prior?.runtimeKind?.trim();
11229
+ return Boolean(nodeId && kind && nodeId !== "unknown");
11230
+ }
11231
+ function runtimeSwitchedNotice(target) {
11232
+ const name = target.nodeName?.trim();
11233
+ const where = name ? `${name} \xB7 ${target.runtimeKind}` : target.runtimeKind;
11234
+ return `\u8FD0\u884C\u65F6\u5DF2\u5207\u5230 ${where}\uFF0C\u672C\u8F6E\u8D77\u5728\u65B0\u7684\u8FD0\u884C\u65F6\u4F1A\u8BDD\u4E2D\u7EE7\u7EED\uFF0C\u5DE5\u4F5C\u533A\u4E0E\u4E0A\u4E0B\u6587\u4E0D\u6CBF\u7528\u3002`;
11235
+ }
11236
+ var SessionRuntimeOverrideError, CLEAR_SESSION_RUNTIME_HINT;
11237
+ var init_session_runtime_target = __esm({
11238
+ "../server/src/domains/chat-sessions/session-runtime-target.ts"() {
11239
+ "use strict";
11240
+ SessionRuntimeOverrideError = class extends Error {
11241
+ constructor(code2, message, status = 409) {
11242
+ super(message);
11243
+ this.code = code2;
11244
+ this.status = status;
11245
+ this.name = "SessionRuntimeOverrideError";
11246
+ }
11247
+ };
11248
+ CLEAR_SESSION_RUNTIME_HINT = "\u5728\u8F93\u5165\u6846\u5E95\u90E8\u7684\u8FD0\u884C\u65F6\u9009\u62E9\u91CC\u6362\u4E00\u53F0\u53EF\u7528\u7684\u670D\u52A1\u5668\u6216\u8FD0\u884C\u65F6\u5373\u53EF\u7EE7\u7EED\uFF08\u5458\u5DE5\u81EA\u5DF1\u7684\u90A3\u4E00\u5957\u6807\u7740\u300C\u5F53\u524D\u5458\u5DE5\u6240\u5728\u670D\u52A1\u5668\u300D\u300C\u5F53\u524D\u5458\u5DE5\u8FD0\u884C\u65F6\u300D\uFF09\u3002";
11249
+ }
11250
+ });
11251
+
11252
+ // ../server/src/artifact-content.ts
11253
+ async function currentOutput(kernel, artifactId) {
11254
+ await kernel.refreshModel();
11255
+ const artifact = kernel.model.artifacts.get(artifactId);
11256
+ if (!artifact) throw new ApiError(404, "NOT_FOUND", "\u6587\u4EF6\u4E0D\u5B58\u5728");
11257
+ const snap = await kernel.getStore().transaction((tx) => tx.loadWorkorder(artifact.workspace));
11258
+ const node2 = snap?.nodes.find((n) => n.id === artifactId);
11259
+ const latest = snap?.works.find((w2) => w2.id === node2?.latestWorkId);
11260
+ const workId = latest && (latest.status === "failed" || latest.status === "dead") && node2?.latestProposedWorkId ? node2.latestProposedWorkId : node2?.latestWorkId;
11261
+ const output = snap?.artifacts.filter((a) => a.workId === workId).sort((a, b2) => a.ordinal - b2.ordinal)[0];
11262
+ if (!node2 || !output) throw new ApiError(404, "NOT_FOUND", "\u8FD9\u4E2A\u8282\u70B9\u5C1A\u672A\u4EA4\u4ED8\u6587\u4EF6");
11263
+ return { node: node2, output };
11264
+ }
11265
+ function looksLikeFileName(name) {
11266
+ return /^\.[a-z]{1,8}[0-9]?$/.test(fileExtensionOf(name));
11267
+ }
11268
+ function decodeText(bytes2) {
11269
+ if (bytes2.length > currentFileLimits().textEditBytes || bytes2.includes(0)) return null;
11270
+ try {
11271
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes2);
11272
+ } catch {
11273
+ return null;
11274
+ }
11275
+ }
11276
+ async function readEditable(kernel, blobs, artifactId, path47) {
11277
+ const { node: node2, output } = await currentOutput(kernel, artifactId);
11278
+ let text7 = null;
11279
+ if (output.contentKind === "manifest" && path47 !== null) {
11280
+ const manifest = parseManifest(await blobs.get(output.contentRef));
11281
+ const entry = manifest.entries.find((e) => e.path === path47);
11282
+ if (!entry) throw new ApiError(404, "NOT_FOUND", "\u5305\u5185\u6587\u4EF6\u4E0D\u5B58\u5728");
11283
+ if (isEditableFileName(entry.path)) text7 = decodeText(await blobs.get(entry.hash));
11284
+ } else if (output.contentKind === "inline") {
11285
+ const name = node2.title || output.name;
11286
+ if (!looksLikeFileName(name) || isEditableFileName(name)) text7 = decodeText(await blobs.get(output.contentRef));
11287
+ }
11288
+ const view = {
11289
+ artifactId,
11290
+ revisionId: output.workId,
11291
+ contentRef: output.contentRef,
11292
+ path: path47,
11293
+ editable: text7 !== null,
11294
+ text: text7
11295
+ };
11296
+ return { view, node: node2, output };
11297
+ }
11298
+ async function readArtifactEditableContent(kernel, blobs, artifactId, path47) {
11299
+ return (await readEditable(kernel, blobs, artifactId, path47)).view;
11300
+ }
11301
+ async function saveArtifactContent(kernel, blobs, actor, input) {
11302
+ if (!input || typeof input.artifactId !== "string" || typeof input.revisionId !== "string" || typeof input.contentRef !== "string" || typeof input.text !== "string" || input.path != null && typeof input.path !== "string") {
11303
+ throw new ApiError(400, "BAD_REQUEST", "\u4FDD\u5B58\u53C2\u6570\u4E0D\u5B8C\u6574");
11304
+ }
11305
+ const bytes2 = new TextEncoder().encode(input.text);
11306
+ if (bytes2.length > currentFileLimits().textEditBytes || bytes2.includes(0)) {
11307
+ throw new ApiError(400, "BAD_REQUEST", `\u4EC5\u652F\u6301\u4E0D\u8D85\u8FC7 ${currentFileLimits().textEditBytes / 1024 / 1024} MiB \u7684 UTF-8 \u6587\u672C`);
11308
+ }
11309
+ const path47 = input.path ?? null;
11310
+ const { view, node: node2, output } = await readEditable(kernel, blobs, input.artifactId, path47);
11311
+ if (view.revisionId !== input.revisionId || view.contentRef !== input.contentRef) {
11312
+ throw new ApiError(409, "CONTENT_CONFLICT", "\u5185\u5BB9\u5DF2\u88AB\u66F4\u65B0\uFF0C\u8BF7\u91CD\u65B0\u6253\u5F00\u6587\u4EF6\u540E\u518D\u4FDD\u5B58");
11313
+ }
11314
+ if (!view.editable) throw new ApiError(400, "NOT_EDITABLE", "\u6B64\u6587\u4EF6\u4E0D\u652F\u6301\u5728\u7EBF\u7F16\u8F91");
11315
+ let contentRef = await blobs.put(bytes2);
11316
+ if (output.contentKind === "manifest") {
11317
+ const manifest = parseManifest(await blobs.get(output.contentRef));
11318
+ contentRef = await blobs.put(serializeManifest(manifest.entries.map((e) => e.path === path47 ? { ...e, hash: contentRef, size: bytes2.length } : e)));
11319
+ }
11320
+ try {
11321
+ await kernel.editArtifactContent({
11322
+ artifactId: input.artifactId,
11323
+ actor,
11324
+ workId: output.workId,
11325
+ outputId: output.id,
11326
+ expectedLatestWorkId: node2.latestWorkId,
11327
+ previousRef: output.contentRef,
11328
+ contentRef,
11329
+ path: path47
11330
+ });
11331
+ } catch (error2) {
11332
+ if (error2.code === "content-conflict") {
11333
+ throw new ApiError(409, "CONTENT_CONFLICT", "\u5185\u5BB9\u5DF2\u88AB\u66F4\u65B0\uFF0C\u8BF7\u91CD\u65B0\u6253\u5F00\u6587\u4EF6\u540E\u518D\u4FDD\u5B58");
11334
+ }
11335
+ throw error2;
11336
+ }
11337
+ return { ...view, contentRef, text: input.text };
11338
+ }
11339
+ async function readArtifactFile(kernel, blobs, artifactId, path47, expectedRef, revisionId = null) {
11340
+ await kernel.refreshModel();
11341
+ const artifact = kernel.model.artifacts.get(artifactId);
11342
+ const selectedRevision = revisionId ?? artifact?.currentRev;
11343
+ const output = selectedRevision ? kernel.model.revisions.get(selectedRevision) : void 0;
11344
+ if (!artifact || !output || output.artifactId !== artifactId) {
11345
+ throw new ApiError(404, "NOT_FOUND", revisionId !== null ? "\u8BE5\u4EA7\u7269\u7248\u672C\u4E0D\u5B58\u5728" : "\u6B64\u4EA7\u7269\u6CA1\u6709\u53EF\u4E0B\u8F7D\u7684\u539F\u6587\u4EF6");
11346
+ }
11347
+ if (expectedRef && output.contentRef !== expectedRef) throw new ApiError(409, "CONTENT_CONFLICT", "\u6587\u4EF6\u5DF2\u66F4\u65B0\uFF0C\u8BF7\u91CD\u65B0\u6253\u5F00");
11348
+ let hash3 = output.contentRef;
11349
+ let name = artifact.title || artifactId;
11350
+ if (output.contentKind === "manifest") {
11351
+ if (!path47) throw new ApiError(400, "PATH_REQUIRED", "\u8BF7\u9009\u62E9\u6587\u4EF6\u5939\u4E2D\u7684\u5177\u4F53\u6587\u4EF6");
11352
+ const entry = parseManifest(await blobs.get(output.contentRef)).entries.find((e) => e.path === path47);
11353
+ if (!entry) throw new ApiError(404, "NOT_FOUND", "\u5305\u5185\u6587\u4EF6\u4E0D\u5B58\u5728");
11354
+ hash3 = entry.hash;
11355
+ name = entry.path;
11356
+ if (entry.size !== void 0 && entry.size > currentFileReadMaxBytes()) throw new ApiError(413, "FILE_TOO_LARGE", `\u6587\u4EF6\u8D85\u8FC7 ${currentFileReadMaxBytes() / 1024 / 1024} MiB \u8BFB\u53D6\u9650\u5236`);
11357
+ } else if (output.contentKind !== "inline-blob") {
11358
+ throw new ApiError(404, "NOT_FOUND", "\u6B64\u4EA7\u7269\u6CA1\u6709\u53EF\u4E0B\u8F7D\u7684\u539F\u6587\u4EF6");
11359
+ }
11360
+ const meta = await blobs.meta?.(hash3);
11361
+ if (meta && meta.size > currentFileReadMaxBytes()) throw new ApiError(413, "FILE_TOO_LARGE", `\u6587\u4EF6\u8D85\u8FC7 ${currentFileReadMaxBytes() / 1024 / 1024} MiB \u8BFB\u53D6\u9650\u5236`);
11362
+ try {
11363
+ return { bytes: await blobs.get(hash3), name, contentType: meta?.contentType };
11364
+ } catch {
11365
+ throw new ApiError(404, "NOT_FOUND", "\u5F53\u524D\u516C\u53F8\u4E2D\u627E\u4E0D\u5230\u8BE5\u6587\u4EF6\u6B63\u6587");
11366
+ }
11367
+ }
11368
+ var init_artifact_content2 = __esm({
11369
+ "../server/src/artifact-content.ts"() {
11370
+ "use strict";
11371
+ init_file_limits2();
11372
+ init_src();
11373
+ init_router();
11374
+ }
11375
+ });
11376
+
11377
+ // ../server/src/file-http.ts
11378
+ async function serveFileRequest(req, res, url, engine) {
11379
+ try {
11380
+ if (req.method !== "GET" || url.pathname !== "/api/files/content") throw new ApiError(404, "NOT_FOUND", "\u6587\u4EF6\u63A5\u53E3\u4E0D\u5B58\u5728");
11381
+ const { bytes: bytes2, name, contentType } = await readArtifactFile(
11382
+ engine.kernel,
11383
+ engine.blobs,
11384
+ url.searchParams.get("artifact") ?? "",
11385
+ url.searchParams.get("path"),
11386
+ url.searchParams.get("ref"),
11387
+ url.searchParams.get("revisionId")
11388
+ );
11389
+ if (bytes2.length > currentFileReadMaxBytes()) throw new ApiError(413, "FILE_TOO_LARGE", `\u6587\u4EF6\u8D85\u8FC7 ${currentFileReadMaxBytes() / 1024 / 1024} MiB \u8BFB\u53D6\u9650\u5236`);
11390
+ res.writeHead(200, {
11391
+ "content-type": contentType ?? sniffContentType(bytes2) ?? "application/octet-stream",
11392
+ "content-length": String(bytes2.length),
11393
+ "cache-control": "private, no-store",
11394
+ "x-content-type-options": "nosniff",
11395
+ "content-disposition": `attachment; filename*=UTF-8''${encodeURIComponent(name.split(/[\\/]/).pop() || "file").replace(/['()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`)}`
11396
+ }).end(Buffer.from(bytes2));
11397
+ } catch (error2) {
11398
+ if (res.destroyed) return;
11399
+ const status = error2 instanceof ApiError ? error2.status : 500;
11400
+ if (!req.complete) {
11401
+ res.setHeader("connection", "close");
11402
+ req.resume();
11403
+ }
11404
+ res.writeHead(status, { "content-type": "application/json", "cache-control": "no-store" }).end(JSON.stringify({
11405
+ error: {
11406
+ code: error2 instanceof ApiError ? error2.code : "FILE_READ_FAILED",
11407
+ message: error2 instanceof ApiError ? error2.message : "\u6587\u4EF6\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5"
11408
+ }
11409
+ }));
11410
+ }
11411
+ }
11412
+ var init_file_http = __esm({
11413
+ "../server/src/file-http.ts"() {
11414
+ "use strict";
11415
+ init_src();
11416
+ init_artifact_content2();
11417
+ init_router();
11418
+ init_file_limits2();
11419
+ }
11420
+ });
11421
+
11422
+ // ../server/src/build-info.ts
11423
+ function findRepoRoot() {
11424
+ let dir;
11425
+ try {
11426
+ dir = path15.dirname((0, import_node_url.fileURLToPath)(__esm_import_meta_url));
11427
+ } catch {
11428
+ return null;
11429
+ }
11430
+ for (let i = 0; i < 12; i++) {
11431
+ if (fs13.existsSync(path15.join(dir, ".git"))) return dir;
11432
+ const parent = path15.dirname(dir);
11433
+ if (parent === dir) break;
11434
+ dir = parent;
11435
+ }
11436
+ return null;
11437
+ }
11438
+ function git(root, args) {
11439
+ try {
11440
+ const out = (0, import_node_child_process10.execFileSync)("git", args, {
11441
+ cwd: root,
11442
+ encoding: "utf8",
11443
+ timeout: 3e3,
11444
+ stdio: ["ignore", "pipe", "ignore"]
11445
+ });
11446
+ const v2 = out.trim();
11447
+ return v2 || null;
11448
+ } catch {
11449
+ return null;
11450
+ }
11451
+ }
11452
+ function getBuildInfo() {
11453
+ if (cached) return cached;
11454
+ const envSha = process.env["OASIS_GIT_SHA"]?.trim();
11455
+ const envBranch = process.env["OASIS_GIT_BRANCH"]?.trim();
11456
+ let gitSha = envSha || null;
11457
+ let gitBranch = envBranch || null;
11458
+ if (!gitSha || !gitBranch) {
11459
+ const root = findRepoRoot();
11460
+ if (root) {
11461
+ if (!gitSha) gitSha = git(root, ["rev-parse", "HEAD"]);
11462
+ if (!gitBranch) {
11463
+ const b2 = git(root, ["rev-parse", "--abbrev-ref", "HEAD"]);
11464
+ gitBranch = b2 === "HEAD" ? null : b2;
11465
+ }
11466
+ }
11467
+ }
11468
+ cached = { gitSha, gitBranch, startedAt: STARTED_AT };
11469
+ return cached;
11470
+ }
11471
+ function resetBuildInfoCacheForTest() {
11472
+ cached = void 0;
11473
+ }
11474
+ var import_node_child_process10, fs13, path15, import_node_url, STARTED_AT, cached;
11475
+ var init_build_info = __esm({
11476
+ "../server/src/build-info.ts"() {
11477
+ "use strict";
11478
+ import_node_child_process10 = require("node:child_process");
11479
+ fs13 = __toESM(require("node:fs"), 1);
11480
+ path15 = __toESM(require("node:path"), 1);
11481
+ import_node_url = require("node:url");
11482
+ STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
11483
+ }
11484
+ });
11485
+
11486
+ // ../server/src/command-policy.ts
11487
+ function classifyCommand(command2) {
11488
+ return COMMAND_POLICY[command2] ?? { risk: "direct" };
11489
+ }
11490
+ var shortId, COMMAND_POLICY;
11491
+ var init_command_policy = __esm({
11492
+ "../server/src/command-policy.ts"() {
11493
+ "use strict";
11494
+ shortId = (v2) => typeof v2 === "string" ? v2.replace(/^artifact:[^:]+:/, "") : String(v2 ?? "?");
11495
+ COMMAND_POLICY = {
11496
+ forceConclude: {
11497
+ risk: "authorize",
11498
+ idempotent: false,
11499
+ effect: (a) => `\u5F3A\u5236\u6536\u53E3\u8282\u70B9 ${shortId(a.artifactId)}\uFF08\u7ED5\u8FC7\u8BC4\u5BA1\u95F8\u76F4\u63A5\u76D6\u91CC\u7A0B\u7891\uFF09`
11500
+ },
11501
+ promote: {
11502
+ risk: "authorize",
11503
+ idempotent: true,
11504
+ effect: (a) => `\u628A\u8282\u70B9 ${shortId(a.artifactId)} \u8F6C\u4E3A\u6301\u4E45\uFF08temp \u2192 persistent\uFF09`
11505
+ },
11506
+ resolveEscalation: {
11507
+ risk: "authorize",
11508
+ idempotent: true,
11509
+ // 重复关同一条是幂等的(kernel.resolveEscalation 内部按 escalationId 去重)
11510
+ // 「关错=静默停摆」的唯一防线是人在卡上一眼看出「关的是哪一条」——escalationId 与 reason 摘要必须上卡。
11511
+ // a 里 escalationId/artifactId 由 server.ts 起草时冻进 commandArgs(已四级解析),保证是真值、非 `?`。
11512
+ effect: (a) => typeof a.gapId === "string" ? `\u91C7\u7528\u65B9\u6848\uFF1A${String(a.reason ?? "")}\uFF1B\u89E3\u51B3\u7F3A\u53E3 ${a.gapId}\uFF1B\u5173\u95ED\u5173\u8054\u4E0A\u62A5 ${String(a.escalationId)}\uFF1B\u7EE7\u7EED\u6267\u884C\uFF1A\u662F\uFF08\u4ECD\u987B\u6EE1\u8DB3\u5176\u4ED6\u963B\u585E\u6761\u4EF6\uFF09` : `\u5173\u95ED\u4E0A\u62A5 ${shortId(a.escalationId)}\uFF08\u8282\u70B9 ${shortId(a.artifactId)}${typeof a.reason === "string" ? `\uFF0C\u7406\u7531\uFF1A${a.reason}` : ""}\uFF09`
11513
+ },
11514
+ gc: {
11515
+ risk: "forbidden",
11516
+ forbiddenReason: "gc \u662F\u56DE\u6536\u5B64\u513F blob \u7684\u57FA\u7840\u8BBE\u65BD\u7EF4\u62A4\uFF0C\u7531 serve \u8FDB\u7A0B/\u4EBA\u7C7B operator \u6267\u884C\uFF0C\u4E0D\u5C5E\u4E8E\u7BA1\u7406\u8005\u80FD\u529B\u8303\u56F4\u3002"
11517
+ }
11518
+ };
11519
+ }
11520
+ });
11521
+
11341
11522
  // ../server/src/domains/actors/model-pricing.ts
11342
11523
  function lookupInMap(map, prefixKeys, candidates) {
11343
11524
  for (const candidate of candidates) {
@@ -141016,14 +141197,19 @@ ${m2.spec.split("\n").map((l) => ` > ${l}`).join("\n")}`);
141016
141197
  })() : canvasMode ? [
141017
141198
  // 内核只出「你产什么 + 内核权威锚点」——**设计工作流散文全在你的人设(identity/AGENTS.md「产出流程」)**,
141018
141199
  // 那是单一真源、改它=改配置不发版(提案 design-conversational-driving 治本,2026-07-17)。这里不重复正文,
141019
- // 只钉住内核唯一不能漂的两件事:真实 artifactId、缺前置的逃生口 gap。
141200
+ // 只钉住内核不能漂的几件事:真实 artifactId、缺前置的逃生口 gap,以及「下单后在会话里 status --wait 等结果」
141201
+ //(2026-09-24 加:它直接关系引擎的零产出判定——下单即退出会被判零产出→协议续唤→连败熔断;人设各公司
141202
+ // 各一份、未必同步更新,这条放内核才保证每个设计员工都拿得到)。
141020
141203
  `\u4F60\u8D1F\u8D23 **design \u4EA7\u7269 \`${artifactId}\`**\uFF0C\u8FD0\u884C\u5728**\u901A\u7528\u8FD0\u884C\u65F6**\u4E0A\u2014\u2014\u4F60\u662F\u7532\u65B9\u8BBE\u8BA1\u8D1F\u8D23\u4EBA\uFF0C\u591A\u8F6E\u5BF9\u8BDD\u9A71\u52A8**\u534F\u4F5C\u753B\u677F\u5F15\u64CE**\u6253\u78E8\u754C\u9762\u7A3F\u3002`,
141021
141204
  ``,
141022
141205
  `**\u5B8C\u6574\u5DE5\u4F5C\u6D41\uFF08\u600E\u4E48\u53D6\u8D27/\u8D77\u8F6E/\u8BFB\u4EA7\u7269/\u4EA4\u7A3F/\u522B\u624B\u6539\u677F\u6587\u4EF6\u2026\uFF09\u89C1\u4F60\u7684\u4EBA\u8BBE \`identity/AGENTS.md\` \u7684\u300C\u4EA7\u51FA\u6D41\u7A0B\uFF08design \u4EA7\u7269\uFF09\u300D**\u2014\u2014\u628A\u91CC\u9762 \`<artifactId>\` \u90FD\u6362\u6210\u672C\u4EA7\u7269 \`${artifactId}\`\u3002`,
141023
141206
  ``,
141024
- `\u5185\u6838\u53EA\u5728\u6B64\u9489\u4E24\u6761\u4E0D\u53EF\u6F02\u7684\uFF1A`,
141207
+ `\u5185\u6838\u53EA\u5728\u6B64\u9489\u51E0\u6761\u4E0D\u53EF\u6F02\u7684\uFF1A`,
141025
141208
  `- **\u4EA4\u7A3F\u8D70 \`oasis propose ${artifactId} --from-dir <\u76EE\u5F55> --reason "\u2026"\`**\uFF08proposed \u624D\u8FDB\u7248\u672C\u53F2\u3001\u4E0B\u6E38\u624D\u62FF\u5F97\u5230\uFF1B\u4E0D propose = head \u4ECD \u2205\uFF09\uFF1B`,
141026
- `- \u7F3A**\u8FD8\u4E0D\u5B58\u5728\u7684\u4E0A\u6E38\u524D\u7F6E** \u2192 \`oasis gap ${artifactId} --desc "<\u7F3A\u4EC0\u4E48>"\` \u4EA4\u4E0A\u53BB\uFF1B\u5DF2\u505A\u5B8C\u7684\u90E8\u5206\u7167\u5E38 \`propose\`\uFF0C\u4F46\u8FD9\u4E00\u8F6E \`conclude\` \u4F1A\u88AB\u62D2\uFF0C\u63D0\u4EA4\u5B8C\u5373\u6536\u5DE5\u3002`
141209
+ `- \u7F3A**\u8FD8\u4E0D\u5B58\u5728\u7684\u4E0A\u6E38\u524D\u7F6E** \u2192 \`oasis gap ${artifactId} --desc "<\u7F3A\u4EC0\u4E48>"\` \u4EA4\u4E0A\u53BB\uFF1B\u5DF2\u505A\u5B8C\u7684\u90E8\u5206\u7167\u5E38 \`propose\`\uFF0C\u4F46\u8FD9\u4E00\u8F6E \`conclude\` \u4F1A\u88AB\u62D2\uFF0C\u63D0\u4EA4\u5B8C\u5373\u6536\u5DE5\u3002`,
141210
+ `- **\u5728\u672C\u4F1A\u8BDD\u91CC\u7B49\u753B\u677F\u51FA\u7ED3\u679C**\uFF08\u540C\u5F00\u53D1\u7B49 CI\uFF09\uFF1A\u5F00\u5DE5\u5148 \`oasis design status ${artifactId}\`\uFF08\u6062\u590D\u4F18\u5148\uFF0C\u677F\u4E0A\u5DF2\u6709\u8D27/\u5DF2\u6709\u5728\u8DD1\u7684\u8F6E\u6B21\u5C31\u522B\u91CD\u4E0B\uFF09\uFF1B\u4E0B\u5355\u7528 \`oasis design chat ${artifactId} \u2026 --async\`\uFF0C\u7136\u540E**\u7ACB\u523B** \`oasis design status ${artifactId} --wait --pull\` \u963B\u585E\u7B49\u8FD9\u4E00\u8F6E\u8DD1\u5B8C\u5E76\u62C9\u8D27\uFF08\u6700\u957F\u7EA6 100 \u5206\u949F\uFF1B\u963B\u585E\u671F\u95F4\u4E0D\u8017 token\uFF09\u3002\u6210\u529F \u2192 \u6838\u5BF9 \u2192 propose\uFF1B\u5931\u8D25 \u2192 \u6309\u5B83\u7ED9\u7684\u539F\u56E0\u6362\u5F15\u64CE\uFF08\`--agent\`\uFF09\u6216\u8865\u6E05\u9700\u6C42\u540E\u91CD\u4E0B\uFF0C\u518D \`--wait\`\u3002`,
141211
+ ` - **\u4E0D\u8981\u4E0B\u5355\u540E\u76F4\u63A5\u7ED3\u675F\u4F1A\u8BDD**\u2014\u2014\u6CA1\u4EA4\u4E1C\u897F\u5C31\u6536\u5DE5\u4F1A\u88AB\u5224\u96F6\u4EA7\u51FA\uFF1B**\u4E5F\u4E0D\u8981\u81EA\u5DF1\u9694\u51E0\u5206\u949F\u624B\u52A8 status \u8F6E\u8BE2**\u2014\u2014\u6BCF\u67E5\u4E00\u6B21\u90FD\u8981\u63A8\u7406\u4E00\u6B21\u3002\u547D\u4EE4\u82E5\u88AB\u6267\u884C\u5DE5\u5177\u7684\u5355\u6761\u8D85\u65F6\u6253\u65AD\u6216\u8D85\u65F6\u9000\u51FA\uFF0C\u539F\u6837\u518D\u8DD1\u4E00\u6B21 \`--wait\` \u63A5\u7740\u7B49\uFF08\u5B83\u7B49\u7684\u662F\u753B\u677F\uFF0C\u4E0D\u4F1A\u91CD\u590D\u4E0B\u5355\uFF09\u3002`,
141212
+ `- \u88AB\u753B\u677F\u5DE1\u68C0\u7684\u4FE1\u5524\u9192\u65F6\uFF1A\u5148\u770B\u5F53\u524D\u7248\u672C\u88C5\u7BB1\u5355 board.link \u91CC\u7684\u300C\u5FEB\u7167\u6765\u6E90 run\u300D\u662F\u4E0D\u662F\u5C31\u662F\u4FE1\u91CC\u90A3\u4E00\u8F6E\u2014\u2014\u662F\u5C31 \`oasis resolve <\u4FE1 id>\`\uFF0C\u522B\u91CD\u590D propose\u3002`
141027
141213
  ] : (() => {
141028
141214
  const codeRepo = args.codeRepo ?? null;
141029
141215
  const repos = typeDef?.contentType === "code" ? codeRepo?.repos : void 0;
@@ -213709,6 +213895,16 @@ async function startOasisServer(opts) {
213709
213895
  res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ data: data2 }));
213710
213896
  return;
213711
213897
  }
213898
+ if (viewName === "design-board") {
213899
+ const artifactId = url.searchParams.get("artifact");
213900
+ if (!artifactId) throw new Error("\u8BE5\u89C6\u56FE\u9700\u8981 artifact \u53C2\u6570");
213901
+ if (!currentCompanyId) throw new Error("\u62FF\u4E0D\u5230\u5F53\u524D\u516C\u53F8\uFF08\u4F1A\u8BDD token \u6CA1\u7B7E\u516C\u53F8\u3001\u8BF7\u6C42\u4E5F\u6CA1\u6307\u5B9A\u516C\u53F8\uFF09\uFF0C\u4E0D\u80FD\u786E\u5B9A\u753B\u677F\u2014\u2014\u4E0D\u56DE\u843D\u5230\u4E0D\u5E26\u516C\u53F8\u7684\u65E7\u753B\u677F\u540D");
213902
+ const artifact = engine.kernel.model.artifacts.get(artifactId);
213903
+ if (!artifact) throw new Error(`artifact \u4E0D\u5B58\u5728: ${artifactId}`);
213904
+ const board = await resolveDesignBoard({ companyId: currentCompanyId, model: engine.kernel.model, blobs: engine.blobs, artifact });
213905
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ data: { companyId: currentCompanyId, ...board } }));
213906
+ return;
213907
+ }
213712
213908
  const customView = opts.views?.[viewName];
213713
213909
  if (customView) {
213714
213910
  res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ data: await customView() }));
@@ -216141,6 +216337,7 @@ var init_server3 = __esm({
216141
216337
  init_chat_session();
216142
216338
  init_chat_system_message();
216143
216339
  init_sealed_node_receipt();
216340
+ init_board_key();
216144
216341
  init_session_runtime_target();
216145
216342
  init_src();
216146
216343
  init_src();
@@ -277486,11 +277683,19 @@ function messageError(m2, status) {
277486
277683
  }
277487
277684
  return { error: null, errorCode: null };
277488
277685
  }
277686
+ function ownerIsWorking(model, a) {
277687
+ if (a.latestWorkState !== "running") return false;
277688
+ const latest = [...model.revisions.values()].filter((r) => r.artifactId === a.id && r.lane !== "reply").sort((x2, y) => y.createdAt.localeCompare(x2.createdAt))[0];
277689
+ return latest !== void 0 && latest.author === a.owner;
277690
+ }
277489
277691
  var RUN_MARKER_PREFIX, boardWatchRunMarker, parseRunMarker, SINCE_MARKER_PREFIX, boardWatchSinceMarker, parseSinceMarker, TERMINAL_RUN_STATUS, MAX_REASON_CHARS, MAX_LISTED_FILES, BoardWatcher;
277490
277692
  var init_board_watch = __esm({
277491
277693
  "../server/src/design/board-watch.ts"() {
277492
277694
  "use strict";
277695
+ init_src();
277493
277696
  init_src7();
277697
+ init_src2();
277698
+ init_board_key();
277494
277699
  RUN_MARKER_PREFIX = "[od-board-watch run:";
277495
277700
  boardWatchRunMarker = (roundKey) => `${RUN_MARKER_PREFIX}${roundKey}]`;
277496
277701
  parseRunMarker = (body2) => {
@@ -277523,6 +277728,20 @@ var init_board_watch = __esm({
277523
277728
  seenConvAt = /* @__PURE__ */ new Map();
277524
277729
  /** 公司 → 上次记下的失败原文(同样的错只记一次;成功一轮就清掉)。 */
277525
277730
  companyLastError = /* @__PURE__ */ new Map();
277731
+ /** `<公司>\0<artifactId>\0<轮次键>` → 这一轮已被取走(head 来自它或更晚的一轮),不再重判。只在内存,不写假信。 */
277732
+ takenRounds = /* @__PURE__ */ new Set();
277733
+ /** `<公司>\0<boardKey>` → (runId → endedAt):判断 head 的快照来源 run 是不是「更晚的一轮」用。 */
277734
+ roundEndedAt = /* @__PURE__ */ new Map();
277735
+ /** `<公司>\0<revisionId>` → 该版 board.link 的快照来源 runId(null = 没有/读不到)。revision 内容不可变,缓存安全。 */
277736
+ revisionSourceRun = /* @__PURE__ */ new Map();
277737
+ /** 因 owner 在做而压着没写的 `<公司>\0<artifactId>`(只为日志只打一次)。 */
277738
+ deferredLogged = /* @__PURE__ */ new Set();
277739
+ /** 公司 → (revisionId → board.link 正文):解析画板用。revision id 跨公司可能同名,所以按公司分开。 */
277740
+ boardLinks = /* @__PURE__ */ new Map();
277741
+ /** `<公司>\0<artifactId>` → 上次解析画板失败的原文(同样的错只记一次)。 */
277742
+ boardResolveError = /* @__PURE__ */ new Map();
277743
+ /** 已经报过的串板(`<boardKey>\0<公司,…>`),同一组只报一次。 */
277744
+ sharedBoardLogged = /* @__PURE__ */ new Set();
277526
277745
  async odGet(path47) {
277527
277746
  const fetchImpl = this.deps.fetchImpl ?? fetch;
277528
277747
  try {
@@ -277579,11 +277798,13 @@ var init_board_watch = __esm({
277579
277798
  async tick(now = Date.now()) {
277580
277799
  const { log: log3 } = this.deps;
277581
277800
  const denied = [];
277801
+ const boardUsers = /* @__PURE__ */ new Map();
277582
277802
  const { listCompanies, kernelFor } = this.deps;
277583
277803
  if (listCompanies && kernelFor) {
277584
277804
  for (const companyId of await listCompanies()) {
277585
277805
  try {
277586
- await this.tickCompany(companyId, await kernelFor(companyId), now, denied);
277806
+ const blobs = this.deps.blobsFor ? await this.deps.blobsFor(companyId) : void 0;
277807
+ await this.tickCompany(companyId, await kernelFor(companyId), now, denied, blobs, boardUsers);
277587
277808
  this.companyLastError.delete(companyId);
277588
277809
  } catch (err) {
277589
277810
  const msg = String(err);
@@ -277593,8 +277814,9 @@ var init_board_watch = __esm({
277593
277814
  }
277594
277815
  }
277595
277816
  } else {
277596
- await this.tickCompany("", this.deps.kernel, now, denied);
277817
+ await this.tickCompany("", this.deps.kernel, now, denied, this.deps.blobs, boardUsers);
277597
277818
  }
277819
+ this.logSharedBoards(boardUsers);
277598
277820
  if (denied.length > 0) {
277599
277821
  const codes = [...new Set(denied.map((d) => d.status))].join("/");
277600
277822
  const eg = denied[0];
@@ -277604,8 +277826,8 @@ var init_board_watch = __esm({
277604
277826
  }
277605
277827
  }
277606
277828
  /** 巡一家公司(company 为空串 = 单 kernel 模式);被拒的板记进 denied,由 tick 统一汇总成一行。 */
277607
- async tickCompany(company, kernel, now, denied) {
277608
- const { boardKeyOf, log: log3 } = this.deps;
277829
+ async tickCompany(company, kernel, now, denied, blobs, boardUsers) {
277830
+ const { log: log3 } = this.deps;
277609
277831
  const graceMs = this.deps.firstLookGraceMs ?? 10 * 6e4;
277610
277832
  const maxPerTick = this.deps.maxLettersPerTick ?? 2;
277611
277833
  const actor = this.deps.actor ?? "actor:system";
@@ -277619,8 +277841,12 @@ var init_board_watch = __esm({
277619
277841
  }
277620
277842
  if (a.type !== "design") continue;
277621
277843
  if (lifecycleOf(model, a.id) !== "active") continue;
277622
- const boardKey = boardKeyOf(a.id);
277623
277844
  const pinKey = `${company}\0${a.id}`;
277845
+ const boardKey = await this.boardOf(company, model, blobs, a, tag);
277846
+ if (boardKey === null) continue;
277847
+ const users = boardUsers.get(boardKey) ?? [];
277848
+ users.push({ company, artifactId: a.id });
277849
+ boardUsers.set(boardKey, users);
277624
277850
  const watchLetters = annotationsOf(model, a.id).filter((x2) => x2.author === actor && x2.body.includes("[od-board-watch "));
277625
277851
  const lettered = new Set(watchLetters.map((x2) => parseRunMarker(x2.body)).filter((s2) => s2 !== null));
277626
277852
  const recordedSince = watchLetters.reduce((mn, x2) => {
@@ -277639,12 +277865,40 @@ var init_board_watch = __esm({
277639
277865
  continue;
277640
277866
  }
277641
277867
  if (recordedSince === null && !this.pinnedSince.has(pinKey)) this.pinnedSince.set(pinKey, since);
277642
- const fresh = read3.value.rounds.filter((r) => r.endedAt > since && !lettered.has(r.key)).sort((x2, y) => x2.endedAt - y.endedAt);
277868
+ const endedAtOf = this.roundEndedAt.get(`${company}\0${boardKey}`) ?? /* @__PURE__ */ new Map();
277869
+ this.roundEndedAt.set(`${company}\0${boardKey}`, endedAtOf);
277870
+ for (const r of read3.value.rounds) if (r.runId !== null) endedAtOf.set(r.runId, r.endedAt);
277871
+ const takenKey = (r) => `${company}\0${a.id}\0${r.key}`;
277872
+ const fresh = read3.value.rounds.filter((r) => r.endedAt > since && !lettered.has(r.key) && !this.takenRounds.has(takenKey(r))).sort((x2, y) => x2.endedAt - y.endedAt);
277873
+ const deferKey = `${company}\0${a.id}`;
277874
+ if (fresh.length > 0 && ownerIsWorking(model, a)) {
277875
+ if (!this.deferredLogged.has(deferKey)) {
277876
+ this.deferredLogged.add(deferKey);
277877
+ log3?.(`[board-watch] ${tag}${a.id} \u7684 owner \u6B63\u5728\u6267\u884C\u8FD9\u4E2A\u8282\u70B9\uFF0C\u677F ${boardKey} \u4E0A ${fresh.length} \u8F6E\u7ED3\u675F\u7684\u5BF9\u8BDD\u5148\u4E0D\u5199\u4FE1\uFF0C\u7B49\u5B83\u8FD9\u8F6E work \u7ED3\u675F\u518D\u770B`);
277878
+ }
277879
+ continue;
277880
+ }
277881
+ this.deferredLogged.delete(deferKey);
277882
+ const sourceRuns = fresh.length > 0 && blobs ? await this.headSourceRuns(company, blobs, model, a, log3, tag) : [];
277883
+ const takenBy = (r) => {
277884
+ for (const src of sourceRuns) {
277885
+ if (r.runId !== null && src === r.runId) return src;
277886
+ const srcEnded = endedAtOf.get(src);
277887
+ if (srcEnded !== void 0 && srcEnded >= r.endedAt) return src;
277888
+ }
277889
+ return null;
277890
+ };
277643
277891
  const seen = /* @__PURE__ */ new Set();
277644
277892
  for (const r of fresh) {
277645
277893
  if (written >= maxPerTick) break;
277646
277894
  if (seen.has(r.key)) continue;
277647
277895
  seen.add(r.key);
277896
+ const src = takenBy(r);
277897
+ if (src !== null) {
277898
+ this.takenRounds.add(takenKey(r));
277899
+ log3?.(`[board-watch] ${tag}${a.id} \u2190 \u677F ${boardKey} \u4E00\u8F6E\u5BF9\u8BDD\u7ED3\u675F\uFF08${r.key.slice(0, 12)} ${r.status}\uFF09\uFF0C\u5F53\u524D\u7248\u672C\u7684\u5FEB\u7167\u6765\u6E90 run ${src} \u5DF2\u8986\u76D6\u5B83 \u2192 \u4E0D\u5199\u4FE1`);
277900
+ continue;
277901
+ }
277648
277902
  const ok3 = r.status === "succeeded";
277649
277903
  const reason = ok3 ? null : r.error ?? (r.runId !== null ? await this.runErrorFromRegistry(boardKey, r.runId) : null);
277650
277904
  await kernel.annotate({ artifactId: a.id, actor, body: this.letterBody(a.id, boardKey, r, since, reason), kind: "comment" });
@@ -277657,6 +277911,73 @@ var init_board_watch = __esm({
277657
277911
  }
277658
277912
  }
277659
277913
  }
277914
+ /** 这个产物实际用的画板(与 CLI 同一个解析函数);解析不了(没有公司、装箱单读不到)→ 记一行日志、本轮跳过它。 */
277915
+ async boardOf(company, model, blobs, a, tag) {
277916
+ const errKey = `${company}\0${a.id}`;
277917
+ let cache = this.boardLinks.get(company);
277918
+ if (!cache) this.boardLinks.set(company, cache = /* @__PURE__ */ new Map());
277919
+ try {
277920
+ const { boardKey } = await resolveDesignBoard(
277921
+ { companyId: company || (this.deps.companyId ?? ""), model, blobs, artifact: a },
277922
+ { deriveKey: this.deps.boardKeyOf, cache }
277923
+ );
277924
+ this.boardResolveError.delete(errKey);
277925
+ return boardKey;
277926
+ } catch (err) {
277927
+ const msg = String(err);
277928
+ if (this.boardResolveError.get(errKey) !== msg) {
277929
+ this.boardResolveError.set(errKey, msg);
277930
+ this.deps.log?.(`[board-watch] ${tag}${a.id} \u89E3\u6790\u4E0D\u51FA\u753B\u677F\uFF0C\u672C\u8F6E\u8DF3\u8FC7\uFF1A${msg}`);
277931
+ }
277932
+ return null;
277933
+ }
277934
+ }
277935
+ /**
277936
+ * 同一块板这一轮同时是不止一家公司的 design 画板 → 记一行(同一组只记一次)。只会出现在旧规则遗留的存量板上:
277937
+ * 两家同名产物都已经交过稿、board.link 都指向同一块只由 artifactId 起名的旧板。不自动处理,交给人看。
277938
+ */
277939
+ logSharedBoards(boardUsers) {
277940
+ for (const [boardKey, users] of boardUsers) {
277941
+ const companies = [...new Set(users.map((u) => u.company))].sort();
277942
+ if (companies.length < 2) continue;
277943
+ const key2 = `${boardKey}\0${companies.join(",")}`;
277944
+ if (this.sharedBoardLogged.has(key2)) continue;
277945
+ this.sharedBoardLogged.add(key2);
277946
+ this.deps.log?.(
277947
+ `[board-watch] \u4E32\u677F\uFF1A\u753B\u677F ${boardKey} \u540C\u65F6\u662F ${users.map((u) => `[${u.company}] ${u.artifactId}`).join("\u3001")} \u7684\u753B\u677F\uFF08\u65E7\u547D\u540D\u89C4\u5219\u53EA\u6309\u4EA7\u7269 id \u8D77\u540D\u3001\u4E24\u5BB6\u90FD\u5DF2\u5728\u8FD9\u5757\u677F\u4E0A\u4EA4\u8FC7\u7A3F\uFF09\u3002\u4E24\u5BB6\u4E92\u76F8\u770B\u5F97\u5230\u5BF9\u65B9\u7684\u6750\u6599\u548C\u7A3F\u5B50\uFF0C\u9700\u8981\u4EBA\u5904\u7406\uFF1B\u5DE1\u68C0\u4E0D\u81EA\u52A8\u6539`
277948
+ );
277949
+ }
277950
+ }
277951
+ /**
277952
+ * 当前 head 与最近一版装箱单(两者可能不同:head 在跑空壳/被打回时回落)各自 board.link 里的快照来源 runId。
277953
+ * 读不到(非装箱单、没有 board.link、blob 缺失、占位 status-pull)的不算。
277954
+ */
277955
+ async headSourceRuns(company, blobs, model, a, log3, tag) {
277956
+ const candidates = [];
277957
+ const head = a.currentRev ? model.revisions.get(a.currentRev) : void 0;
277958
+ if (head) candidates.push(head);
277959
+ const latestManifest = [...model.revisions.values()].filter((r) => r.artifactId === a.id && r.contentKind === "manifest" && r.contentRef.length > 0).sort((x2, y) => y.createdAt.localeCompare(x2.createdAt))[0];
277960
+ if (latestManifest && latestManifest.id !== head?.id) candidates.push(latestManifest);
277961
+ const out = [];
277962
+ for (const rev of candidates) {
277963
+ if (rev.contentKind !== "manifest" || rev.contentRef.length === 0) continue;
277964
+ const cacheKey = `${company}\0${rev.id}`;
277965
+ let src = this.revisionSourceRun.get(cacheKey);
277966
+ if (src === void 0) {
277967
+ try {
277968
+ const manifest = parseManifest(await blobs.get(rev.contentRef));
277969
+ const entry = manifest.entries.find((e) => e.path === BOARD_LINK_FILE);
277970
+ src = entry ? parseBoardLinkSourceRun(new TextDecoder().decode(await blobs.get(entry.hash))) : null;
277971
+ } catch (err) {
277972
+ log3?.(`[board-watch] ${tag}\u8BFB ${a.id} \u7248\u672C ${rev.id} \u7684 board.link \u5931\u8D25\uFF08${String(err)}\uFF09\uFF0C\u6309\u300C\u4E0D\u77E5\u9053\u6765\u81EA\u54EA\u4E00\u8F6E\u300D\u5904\u7406`);
277973
+ src = null;
277974
+ }
277975
+ this.revisionSourceRun.set(cacheKey, src);
277976
+ }
277977
+ if (src !== null) out.push(src);
277978
+ }
277979
+ return out;
277980
+ }
277660
277981
  letterBody(artifactId, boardKey, r, since, reason) {
277661
277982
  const marker = `${boardWatchRunMarker(r.key)} ${boardWatchSinceMarker(since)}`;
277662
277983
  const round = `${r.runId !== null ? `run ${r.runId.slice(0, 8)}` : `\u6D88\u606F ${r.key.slice(4, 12)}`}\uFF0C\u5F15\u64CE=${r.agentId ?? "?"}\uFF0C\u7ED3\u675F\u4E8E ${new Date(r.endedAt).toISOString()}`;
@@ -277667,7 +277988,8 @@ var init_board_watch = __esm({
277667
277988
  `\u753B\u677F ${boardKey} \u4E0A\u6709\u4E00\u8F6E\u5BF9\u8BDD\u8DD1\u5B8C\u4E86\uFF08${round}\uFF09\uFF0C\u8FD9\u4E00\u8F6E\u4EA7\u51FA\uFF1A`,
277668
277989
  files,
277669
277990
  ``,
277670
- `\u53D6\u8D27\u6838\u5BF9\uFF1A\`oasis design status ${artifactId} --pull\` \u2192 \u5BF9\u7740 DESIGN_BRIEF.md \u4E0E inputs/ \u9010\u5B57\u6BB5\u6838\uFF08\u6570\u636E\u5BF9\u4E0D\u5BF9\u3001\u72B6\u6001\u5168\u4E0D\u5168\u3001\u6709\u6CA1\u6709\u7F16\u9020\uFF09\u3002\u591F\u597D\u5C31 \`oasis propose ${artifactId} --from-dir <\u76EE\u5F55> --reason "<\u8FD9\u7248\u505A\u4E86\u4EC0\u4E48>"\`\uFF1B\u8981\u6539\u5C31\u56DE\u677F\u4E0A\u518D\u6765\u4E00\u8F6E \`oasis design chat ${artifactId} --turn continue --message "<\u8981\u6539\u4EC0\u4E48>" --async\`\uFF08\u26D4 \u522B\u628A --pull \u4E0B\u6765\u7684\u6587\u4EF6\u5728\u672C\u5730\u624B\u6539\u540E propose\uFF09\u3002\u8FD9\u4E00\u8F6E\u82E5\u662F\u4EBA\u76F4\u63A5\u5728\u677F\u4E0A\u804A\u51FA\u6765\u7684\u3001\u5185\u5BB9\u4E0D\u5FC5\u6536\u5F55\uFF0C\u56DE\u5E16\u8BF4\u660E\u540E\u6309 wontfix \u5904\u7406\u5373\u53EF\uFF08\u540C\u4E00\u8F6E\u4E0D\u4F1A\u518D\u6765\u4FE1\uFF09\u3002`
277991
+ `\u5148\u770B\u8FD9\u4E00\u8F6E\u662F\u4E0D\u662F\u5DF2\u7ECF\u4EA4\u8FC7\uFF1A\u5F53\u524D\u7248\u672C\u88C5\u7BB1\u5355\u91CC board.link \u7684\u300C\u5FEB\u7167\u6765\u6E90 run\u300D\u5982\u679C\u5C31\u662F ${r.runId ?? "\u8FD9\u4E00\u8F6E"}\uFF0C\u8BF4\u660E\u5DF2\u53D6\u8D70\uFF0C\`oasis resolve <\u672C\u4FE1 id> --as resolved\` \u5373\u53EF\uFF0C\u522B\u91CD\u590D propose\u3002`,
277992
+ `\u6CA1\u4EA4\u8FC7\u5C31\u53D6\u8D27\u6838\u5BF9\uFF1A\`oasis design status ${artifactId} --wait --pull\`\uFF08\u677F\u4E0A\u8FD8\u6709\u5728\u8DD1\u7684\u8F6E\u6B21\u4F1A\u5148\u7B49\u5B83\u8DD1\u5B8C\uFF09\u2192 \u5BF9\u7740 DESIGN_BRIEF.md \u4E0E inputs/ \u9010\u5B57\u6BB5\u6838\uFF08\u6570\u636E\u5BF9\u4E0D\u5BF9\u3001\u72B6\u6001\u5168\u4E0D\u5168\u3001\u6709\u6CA1\u6709\u7F16\u9020\uFF09\u3002\u591F\u597D\u5C31 \`oasis propose ${artifactId} --from-dir <\u76EE\u5F55> --reason "<\u8FD9\u7248\u505A\u4E86\u4EC0\u4E48>"\`\uFF1B\u8981\u6539\u5C31\u56DE\u677F\u4E0A\u518D\u6765\u4E00\u8F6E \`oasis design chat ${artifactId} --turn continue --message "<\u8981\u6539\u4EC0\u4E48>" --async\`\uFF0C\u968F\u540E \`oasis design status ${artifactId} --wait --pull\` \u7B49\u7ED3\u679C\uFF08\u26D4 \u522B\u628A --pull \u4E0B\u6765\u7684\u6587\u4EF6\u5728\u672C\u5730\u624B\u6539\u540E propose\uFF09\u3002\u8FD9\u4E00\u8F6E\u82E5\u662F\u4EBA\u76F4\u63A5\u5728\u677F\u4E0A\u804A\u51FA\u6765\u7684\u3001\u5185\u5BB9\u4E0D\u5FC5\u6536\u5F55\uFF0C\u56DE\u5E16\u8BF4\u660E\u540E\u6309 wontfix \u5904\u7406\u5373\u53EF\uFF08\u540C\u4E00\u8F6E\u4E0D\u4F1A\u518D\u6765\u4FE1\uFF09\u3002`
277671
277993
  ].join("\n");
277672
277994
  }
277673
277995
  const quoted = reason === null ? null : reason.length > MAX_REASON_CHARS ? `${reason.slice(0, MAX_REASON_CHARS)}\u2026` : reason;
@@ -277676,7 +277998,7 @@ var init_board_watch = __esm({
277676
277998
  `\u753B\u677F ${boardKey} \u4E0A\u6709\u4E00\u8F6E\u5BF9\u8BDD**\u6CA1\u8DD1\u6210**\uFF08\u7EC8\u6001=${r.status}\uFF0C${round}\uFF09\u3002`,
277677
277999
  quoted !== null ? `od \u8BB0\u7684\u539F\u56E0\uFF08\u539F\u6587\uFF09\uFF1A${quoted}${r.errorCode !== null ? `\uFF08\u9519\u8BEF\u7801 ${r.errorCode}\uFF09` : ""}` : `od \u6CA1\u6709\u8BB0\u5F55\u8FD9\u4E00\u8F6E\u7684\u5931\u8D25\u539F\u56E0\u3002`,
277678
278000
  ``,
277679
- `\u5148 \`oasis design status ${artifactId}\` \u770B\u677F\u4E0A\u6709\u6CA1\u6709\u53EF\u7528\u7684\u4E0A\u4E00\u7248\uFF1A\u6709\u5C31\u7167\u5E38\u53D6\u8D27\u6838\u5BF9\u3002\u8981\u91CD\u8DD1\u5C31\u81EA\u5DF1\u5224\u65AD\uFF1A\u50CF\u662F\u989D\u5EA6/\u9650\u6D41\u5C31\u7B49\u6062\u590D\u540E\u518D\u6765\uFF0C\u6216\u6362\u4E2A\u5F15\u64CE \`oasis design chat ${artifactId} --agent <\u5F15\u64CE> --message "<\u2026>" --async\`\uFF1B\u50CF\u662F\u9700\u6C42/\u6570\u636E\u95EE\u9898\u5C31\u628A\u5B83\u4EEC\u9489\u5F97\u66F4\u6B7B\u540E\u91CD\u8D77\u4E00\u8F6E\uFF1B\u4ECD\u4E0D\u884C\u7528 \`oasis gap ${artifactId} --desc "<\u5361\u5728\u54EA>"\` \u5192\u6CE1\u7ED9\u4EBA\u3002`
278001
+ `\u5148 \`oasis design status ${artifactId}\` \u770B\u677F\u4E0A\u6709\u6CA1\u6709\u53EF\u7528\u7684\u4E0A\u4E00\u7248\uFF1A\u6709\u5C31\u7167\u5E38\u53D6\u8D27\u6838\u5BF9\u3002\u8981\u91CD\u8DD1\u5C31\u81EA\u5DF1\u5224\u65AD\uFF1A\u50CF\u662F\u989D\u5EA6/\u9650\u6D41\u5C31\u7B49\u6062\u590D\u540E\u518D\u6765\uFF0C\u6216\u6362\u4E2A\u5F15\u64CE \`oasis design chat ${artifactId} --turn continue --agent <\u5F15\u64CE> --message "<\u2026>" --async\`\uFF1B\u50CF\u662F\u9700\u6C42/\u6570\u636E\u95EE\u9898\u5C31\u628A\u5B83\u4EEC\u9489\u5F97\u66F4\u6B7B\u540E\u91CD\u8D77\u4E00\u8F6E\u3002\u91CD\u4E0B\u4E4B\u540E\u7ACB\u523B \`oasis design status ${artifactId} --wait --pull\` \u5728\u4F1A\u8BDD\u91CC\u7B49\u7ED3\u679C\uFF0C\u522B\u4E0B\u5355\u5C31\u6536\u5DE5\u3002\u4ECD\u4E0D\u884C\u7528 \`oasis gap ${artifactId} --desc "<\u5361\u5728\u54EA>"\` \u5192\u6CE1\u7ED9\u4EBA\u3002`
277680
278002
  ].join("\n");
277681
278003
  }
277682
278004
  };
@@ -278763,6 +279085,7 @@ __export(src_exports2, {
278763
279085
  knowledgeSearchTokenStream: () => knowledgeSearchTokenStream,
278764
279086
  knowledgeSynthesisPrompt: () => knowledgeSynthesisPrompt,
278765
279087
  knowledgeTerms: () => knowledgeTerms,
279088
+ latestBoardLink: () => latestBoardLink,
278766
279089
  letterBackfillDoneScope: () => letterBackfillDoneScope,
278767
279090
  linkChatAttachmentsToWorkOrder: () => linkChatAttachmentsToWorkOrder,
278768
279091
  listActiveCoordinatorAgents: () => listActiveCoordinatorAgents,
@@ -278809,6 +279132,7 @@ __export(src_exports2, {
278809
279132
  openNewEngine: () => openNewEngine,
278810
279133
  operatorCrossCompanyRead: () => operatorCrossCompanyRead,
278811
279134
  outputsPartProvider: () => outputsPartProvider,
279135
+ ownerIsWorking: () => ownerIsWorking,
278812
279136
  packMatches: () => packMatches,
278813
279137
  parentConversationTaskLines: () => parentConversationTaskLines,
278814
279138
  parseConnectorSkillId: () => parseConnectorSkillId,
@@ -278893,6 +279217,7 @@ __export(src_exports2, {
278893
279217
  resolveChatItemsTurnId: () => resolveChatItemsTurnId,
278894
279218
  resolveCoordinatorProvisionScope: () => resolveCoordinatorProvisionScope,
278895
279219
  resolveDefaultCompanyConfig: () => resolveDefaultCompanyConfig,
279220
+ resolveDesignBoard: () => resolveDesignBoard,
278896
279221
  resolveDispatchScope: () => resolveDispatchScope,
278897
279222
  resolveListenTarget: () => resolveListenTarget,
278898
279223
  resolveManagerBinding: () => resolveManagerBinding,
@@ -279110,6 +279435,7 @@ var init_src11 = __esm({
279110
279435
  init_connector_tool_versions();
279111
279436
  init_agent_manage_guard();
279112
279437
  init_board_watch();
279438
+ init_board_key();
279113
279439
  init_uncategorized_project();
279114
279440
  init_run_settlement();
279115
279441
  init_reconcile_terminal_runs();
@@ -289309,6 +289635,8 @@ ${nodeFault}` : "");
289309
289635
  return [defaultCompanyId, ...active.filter((id) => id !== defaultCompanyId)];
289310
289636
  },
289311
289637
  kernelFor: async (companyId) => (await getEngine(companyId)).kernel,
289638
+ // 读 head 装箱单里的 board.link(「快照来源 run」)判断这一轮是否已被取走,已取走就不写信
289639
+ blobsFor: async (companyId) => (await getEngine(companyId)).blobs,
289312
289640
  odBase,
289313
289641
  ...odToken ? { odToken } : {},
289314
289642
  boardKeyOf: deriveBoardKey,
@@ -292568,29 +292896,32 @@ var COMMAND_DECLS = {
292568
292896
  flags: [
292569
292897
  { name: "message", desc: "\u6D88\u606F\u5185\u5BB9", required: true },
292570
292898
  { name: "turn", desc: "\u5BF9\u8BDD\u8F6E\u6B21\uFF08first | continue\uFF09" },
292571
- { name: "async", desc: "\u5F02\u6B65\u6A21\u5F0F\uFF08\u8D77\u5B8C\u5373\u8FD4\uFF09" },
292899
+ { name: "async", desc: "\u4E0B\u5355\u5373\u8FD4\uFF1B\u968F\u540E\u7528 design status --wait --pull \u963B\u585E\u7B49\u7ED3\u679C" },
292572
292900
  { name: "system", desc: "\u7CFB\u7EDF\u63D0\u793A" }
292573
292901
  ],
292574
292902
  examples: ['oasis design chat artifact:design:xyz --message "\u628A\u6309\u94AE\u6539\u6210\u84DD\u8272" --turn continue']
292575
292903
  },
292576
292904
  "design status": {
292577
292905
  group: "\u8BBE\u8BA1\u7A3F",
292578
- usage: "oasis design status <artifactId> [--pull]",
292579
- description: "\u67E5\u753B\u677F\u73B0\u72B6\uFF08\u6062\u590D\u4F18\u5148\uFF09\u3002\u88AB\u6D3E\u53D1\u5148\u8DD1\u5B83\u2014\u2014\u5F15\u64CE\u5728\u8DD1\u5C31\u522B\u8D77\u65B0\u8F6E\u3002",
292906
+ usage: "oasis design status <artifactId> [--pull] [--wait [--wait-timeout <\u5206\u949F>]]",
292907
+ description: "\u67E5\u753B\u677F\u73B0\u72B6\uFF08\u6062\u590D\u4F18\u5148\uFF09\u3002\u88AB\u6D3E\u53D1\u5148\u8DD1\u5B83\u2014\u2014\u5F15\u64CE\u5728\u8DD1\u5C31\u522B\u8D77\u65B0\u8F6E\u3002--wait\uFF1A\u677F\u4E0A\u6709\u5728\u8DD1\u7684\u8F6E\u6B21\u5C31\u963B\u585E\u7B49\u5230\u5168\u90E8\u7ED3\u675F\uFF08\u4E0D\u7BA1\u662F\u8C01\u8D77\u7684\uFF09\uFF0C\u518D\u6253\u5370\u7ED3\u679C\uFF1B\u8D85\u65F6\u975E\u96F6\u9000\u51FA\u3002",
292580
292908
  positional: [
292581
292909
  { name: "<artifactId>", required: true, desc: "\u4EA7\u7269 id" }
292582
292910
  ],
292583
292911
  flags: [
292584
- { name: "pull", desc: "\u62C9\u53D6\u6700\u65B0\u753B\u677F\u5185\u5BB9\u5230\u672C\u5730" }
292912
+ { name: "pull", desc: "\u62C9\u53D6\u6700\u65B0\u753B\u677F\u5185\u5BB9\u5230\u672C\u5730\uFF08\u5E26 --wait \u65F6\uFF1A\u7B49\u5B8C\u4E14\u6700\u8FD1\u4E00\u8F6E\u6210\u529F\u624D\u62C9\uFF09" },
292913
+ { name: "wait", desc: "\u677F\u4E0A\u6709\u5728\u8DD1\u7684\u8F6E\u6B21\u5C31\u963B\u585E\u7B49\u5B83\u4EEC\u7ED3\u675F\uFF08\u540C gh pr checks --watch\uFF09", boolean: true },
292914
+ { name: "wait-timeout", desc: "--wait \u6700\u591A\u7B49\u51E0\u5206\u949F\uFF08\u9ED8\u8BA4 100\uFF09" },
292915
+ { name: "wait-interval", desc: "--wait \u8F6E\u8BE2\u95F4\u9694\u79D2\u6570\uFF08\u9ED8\u8BA4 20\uFF09" }
292585
292916
  ],
292586
- examples: ["oasis design status artifact:design:xyz --pull"]
292917
+ examples: ["oasis design status artifact:design:xyz --wait --pull", "oasis design status artifact:design:xyz --pull"]
292587
292918
  },
292588
292919
  design: {
292589
292920
  group: "\u8BBE\u8BA1\u7A3F",
292590
292921
  usage: "oasis design <chat|status> <artifactId> ...",
292591
292922
  description: "\u8BBE\u8BA1\u4EA7\u7269\u76F8\u5173\u64CD\u4F5C\u3002\u8FDE\u54EA\u4E2A open-design\uFF1A\u5458\u5DE5\u5F00\u4E86 open-design \u8FDE\u63A5\u5668\u5C31\u8FDE\u5B83\u767B\u8BB0\u7684\u5730\u5740\u5E76\u5E26\u4EE4\u724C\uFF08\u4EFB\u610F\u8282\u70B9\u53EF\u7528\uFF09\uFF0C\u5426\u5219 $OASIS_OPEN_DESIGN_URL\uFF0C\u518D\u5426\u5219\u672C\u673A\u3002",
292592
292923
  subCommands: ["design chat", "design status"],
292593
- examples: ['oasis design chat artifact:design:xyz --message "\u8BBE\u8BA1\u767B\u5F55\u9875" --async']
292924
+ examples: ['oasis design chat artifact:design:xyz --message "\u8BBE\u8BA1\u767B\u5F55\u9875" --async', "oasis design status artifact:design:xyz --wait --pull"]
292594
292925
  },
292595
292926
  // —— 图机制(§6/§7) ——
292596
292927
  annotate: {
@@ -294268,6 +294599,15 @@ function needPos(positional, i, usage) {
294268
294599
  if (v2 === void 0) throw new Error(`\u7528\u6CD5: ${usage}`);
294269
294600
  return v2;
294270
294601
  }
294602
+ function positiveNumberFlag(flags, name, dflt) {
294603
+ const raw = flags.get(name);
294604
+ if (raw === void 0) return dflt;
294605
+ const n = Number(raw);
294606
+ if (!Number.isFinite(n) || n <= 0) throw new Error(`--${name} \u8981\u4E00\u4E2A\u6B63\u6570\uFF08\u6536\u5230\uFF1A${raw}\uFF09`);
294607
+ return n;
294608
+ }
294609
+ var shortRunKey = (key2) => key2.startsWith("msg:") ? `\u6D88\u606F ${key2.slice(4, 12)}` : `run ${key2.slice(0, 8)}`;
294610
+ var clipReason = (reason) => reason.length > 300 ? `${reason.slice(0, 300)}\u2026` : reason;
294271
294611
  function semicolonList(raw, field) {
294272
294612
  if (raw === void 0) return {};
294273
294613
  const items = raw.split(";").map((x2) => x2.trim()).filter(Boolean);
@@ -294470,7 +294810,7 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback) {
294470
294810
  }
294471
294811
  }
294472
294812
  }
294473
- var CLI_VERSION = "2.2.84" ? "2.2.84" : "dev";
294813
+ var CLI_VERSION = "2.2.86" ? "2.2.86" : "dev";
294474
294814
  function apiHeaders(token, company) {
294475
294815
  const headers = {
294476
294816
  authorization: `Bearer ${token}`,
@@ -296209,6 +296549,16 @@ ${res.warning}`);
296209
296549
  }
296210
296550
  case "design": {
296211
296551
  const sub = positional[0];
296552
+ const resolveBoard = async (artifactId) => {
296553
+ let b2;
296554
+ try {
296555
+ b2 = await api.view("design-board", artifactId);
296556
+ } catch (err) {
296557
+ throw new Error(`\u62FF\u4E0D\u5230 ${artifactId} \u7684\u753B\u677F\uFF08\u753B\u677F\u540D\u8981\u5E26\u5F53\u524D\u516C\u53F8\uFF09\uFF1A${err instanceof Error ? err.message : String(err)}\u3002\u4E0D\u56DE\u843D\u5230\u4E0D\u5E26\u516C\u53F8\u7684\u65E7\u753B\u677F\u540D`);
296558
+ }
296559
+ if (!b2?.companyId || !b2.boardKey) throw new Error(`\u62FF\u4E0D\u5230 ${artifactId} \u7684\u753B\u677F\uFF1A\u670D\u52A1\u7AEF\u6CA1\u7ED9\u51FA\u516C\u53F8\u6216\u753B\u677F\u540D\u3002\u4E0D\u56DE\u843D\u5230\u4E0D\u5E26\u516C\u53F8\u7684\u65E7\u753B\u677F\u540D`);
296560
+ return { boardKey: b2.boardKey, note: b2.source === "board-link" ? "\uFF08\u6CBF\u7528\u5DF2\u4EA4\u7A3F\u8BB0\u5F55\u91CC\u7684\u753B\u677F\uFF09" : "" };
296561
+ };
296212
296562
  if (sub === "chat") {
296213
296563
  const artifactId = needPos(positional, 1, "oasis design chat <artifactId> --message <t>");
296214
296564
  const message = flags.get("message") ?? (["DESIGN_BRIEF.md", "TASK.md"].map((f2) => fs44.existsSync(f2) ? fs44.readFileSync(f2, "utf8") : "").find((s2) => s2.trim()) ?? "");
@@ -296226,24 +296576,26 @@ ${res.warning}`);
296226
296576
  }
296227
296577
  }
296228
296578
  }
296579
+ const board = await resolveBoard(artifactId);
296229
296580
  const baseTurnOpts = {
296230
296581
  base: odBase,
296231
296582
  ...od.token ? { token: od.token } : {},
296232
296583
  agentId: flags.get("agent") ?? "claude",
296233
296584
  artifactId,
296585
+ boardKey: board.boardKey,
296234
296586
  title: flags.get("title") ?? artifactId,
296235
296587
  message,
296236
296588
  ...flags.get("system") !== void 0 ? { systemPrompt: flags.get("system") } : {},
296237
296589
  ...isContinue ? {} : { contextFiles }
296238
296590
  };
296239
296591
  if (flags.get("async") === "true") {
296240
- println(`[design] \u753B\u677F ${deriveBoardKey(artifactId)} \u2190 ${artifactId}\uFF1B${isContinue ? "\u7EED\u8F6E" : "\u9996\u8F6E"}\uFF08--async\uFF09\u2192 od \u5F15\u64CE ${odLabel} \u2026`);
296592
+ println(`[design] \u753B\u677F ${board.boardKey}${board.note} \u2190 ${artifactId}\uFF1B${isContinue ? "\u7EED\u8F6E" : "\u9996\u8F6E"}\uFF08--async\uFF09\u2192 od \u5F15\u64CE ${odLabel} \u2026`);
296241
296593
  const started = await startDesignChatTurn(baseTurnOpts);
296242
296594
  println(`[design] \u5DF2\u4E0B\u5355\uFF1Arun ${started.runId.slice(0, 8)}\uFF08board ${started.boardKey}, \u53EF\u89C1 user \u56DE\u5408 ${started.userMessageId.slice(0, 8)}\uFF09\u3002\u5F15\u64CE\u901A\u5E38\u8981 13\u201344 \u5206\u949F\u3002`);
296243
- println(`[design] \u4E4B\u540E\u7528 oasis design status ${artifactId} \u67E5\u8FDB\u5EA6/\u53D6\u8D27\uFF08--pull \u62C9\u4EA7\u7269\u5230\u672C\u5730\u8BFB\uFF09\uFF1B\u4E0D\u5FC5\u5B88\u7740\u7B49\u3002`);
296595
+ println(`[design] \u4E0B\u4E00\u6B65\u7ACB\u523B\u6267\u884C\uFF1Aoasis design status ${artifactId} --wait --pull\uFF08\u963B\u585E\u7B49\u8FD9\u4E00\u8F6E\u8DD1\u5B8C\u5E76\u62C9\u8D27\uFF0C\u6700\u957F\u7B49 100 \u5206\u949F\uFF09\u3002\u522B\u4E0B\u5355\u540E\u76F4\u63A5\u7ED3\u675F\u4F1A\u8BDD\uFF0C\u4E5F\u522B\u81EA\u5DF1\u9694\u51E0\u5206\u949F\u624B\u52A8 status\u3002`);
296244
296596
  break;
296245
296597
  }
296246
- println(`[design] \u753B\u677F ${deriveBoardKey(artifactId)} \u2190 ${artifactId}\uFF1B${isContinue ? "\u7EED\u8F6E" : "\u9996\u8F6E"}\u5BF9\u8BDD\u9A71\u52A8 od \u5F15\u64CE ${odLabel} \u2026`);
296598
+ println(`[design] \u753B\u677F ${board.boardKey}${board.note} \u2190 ${artifactId}\uFF1B${isContinue ? "\u7EED\u8F6E" : "\u9996\u8F6E"}\u5BF9\u8BDD\u9A71\u52A8 od \u5F15\u64CE ${odLabel} \u2026`);
296247
296599
  const turn = await runDesignChatTurn({
296248
296600
  ...baseTurnOpts,
296249
296601
  onLine: (line2) => println(`[od] ${line2.length > 500 ? line2.slice(0, 500) + "\u2026" : line2}`)
@@ -296254,46 +296606,83 @@ ${res.warning}`);
296254
296606
  break;
296255
296607
  }
296256
296608
  if (sub === "status") {
296257
- const artifactId = needPos(positional, 1, "oasis design status <artifactId> [--pull]");
296609
+ const artifactId = needPos(positional, 1, "oasis design status <artifactId> [--pull] [--wait]");
296258
296610
  const od = resolveOpenDesignEndpoint(flags.get("od-url"));
296259
296611
  const odBase = od.base;
296260
296612
  const odFetch = withOdToken(fetch, od.token);
296261
- const st = await fetchBoardStatus(odBase, odFetch, artifactId);
296613
+ const { boardKey } = await resolveBoard(artifactId);
296614
+ let st;
296615
+ let waitedForRun = false;
296616
+ if (flags.get("wait") === "true") {
296617
+ const timeoutMin = positiveNumberFlag(flags, "wait-timeout", 100);
296618
+ const intervalSec = positiveNumberFlag(flags, "wait-interval", 20);
296619
+ const fmt = (ms) => `${Math.floor(ms / 6e4)} \u5206 ${Math.floor(ms % 6e4 / 1e3)} \u79D2`;
296620
+ const waited = await waitBoardSettled(odBase, odFetch, boardKey, {
296621
+ timeoutMs: timeoutMin * 6e4,
296622
+ intervalMs: intervalSec * 1e3,
296623
+ // 每次轮询打一行撑 liveness;状态没变只打短的一行,别刷屏
296624
+ onPoll: ({ live: live2, elapsedMs, changed, unreadable }) => {
296625
+ if (!changed) return println(`[design] \u2026\u4ECD\u5728\u7B49\uFF08\u5DF2\u7B49 ${fmt(elapsedMs)}\uFF09`);
296626
+ if (live2.length === 0 && unreadable) return println(`[design] \u8FD9\u6B21\u6CA1\u8BFB\u5230\u753B\u677F\u5BF9\u8BDD\u8BB0\u5F55\uFF08od \u6682\u65F6\u4E0D\u901A\uFF1F\uFF09\uFF0C\u63A5\u7740\u7B49\uFF08\u5DF2\u7B49 ${fmt(elapsedMs)}\uFF09`);
296627
+ const runs = live2.map((r) => `${shortRunKey(r.key)} \u72B6\u6001=${r.status}${r.agentId ? ` \u5F15\u64CE=${r.agentId}` : ""}`).join("\uFF1B");
296628
+ println(`[design] \u7B49\u753B\u677F\u8FD9\u4E00\u8F6E\u8DD1\u5B8C\uFF1A${runs}\uFF08\u5DF2\u7B49 ${fmt(elapsedMs)}\uFF0C\u4E0A\u9650 ${timeoutMin} \u5206\u949F\uFF09`);
296629
+ }
296630
+ });
296631
+ st = waited.status;
296632
+ waitedForRun = waited.waited;
296633
+ if (!waited.settled) {
296634
+ const runs = waited.live.map((r) => `${shortRunKey(r.key)} \u72B6\u6001=${r.status}`).join("\u3001") || "\u5BF9\u8BDD\u8BB0\u5F55\u8BFB\u4E0D\u5230";
296635
+ println(`[design] \u7B49\u4E86 ${fmt(waited.elapsedMs)}\uFF0C\u753B\u677F\u4E0A\u8FD9\u4E00\u8F6E\u8FD8\u6CA1\u7ED3\u675F\uFF08${runs}\uFF09\u2014\u2014\u5F15\u64CE\u8FD8\u5728\u8DD1\uFF0C\u53EF\u7A0D\u540E\u518D oasis design status ${artifactId} --wait${flags.get("pull") === "true" ? " --pull" : ""}\u3002\u522B\u53E6\u8D77\u65B0\u8F6E\u3002`);
296636
+ process.exitCode = 2;
296637
+ break;
296638
+ }
296639
+ if (waitedForRun) println(`[design] \u753B\u677F\u4E0A\u7684\u8F6E\u6B21\u90FD\u5DF2\u7ED3\u675F\uFF08\u7B49\u4E86 ${fmt(waited.elapsedMs)}\uFF09\u3002`);
296640
+ } else {
296641
+ st = await fetchBoardStatus(odBase, odFetch, boardKey);
296642
+ }
296262
296643
  if (!st.boardExists) {
296263
- println(`[design] \u753B\u677F ${st.boardKey} \u5C1A\u4E0D\u5B58\u5728\u2014\u2014\u8FD8\u6CA1\u8D77\u8FC7\u4EFB\u4F55\u4E00\u8F6E\u3002\u5F00\u5DE5\uFF1Aoasis design chat ${artifactId} --message "<\u8BBE\u8BA1\u9700\u6C42+\u771F\u5B9E\u6570\u636E>" --async`);
296644
+ println(`[design] \u753B\u677F ${st.boardKey} \u5C1A\u4E0D\u5B58\u5728\u2014\u2014\u8FD8\u6CA1\u8D77\u8FC7\u4EFB\u4F55\u4E00\u8F6E\u3002\u5F00\u5DE5\uFF1Aoasis design chat ${artifactId} --message "<\u8BBE\u8BA1\u9700\u6C42+\u771F\u5B9E\u6570\u636E>" --async\uFF0C\u968F\u540E oasis design status ${artifactId} --wait --pull`);
296264
296645
  break;
296265
296646
  }
296266
- const live = st.runs.filter((r) => r.status === "running" || r.status === "queued");
296647
+ const live = liveBoardRuns(st);
296267
296648
  if (live.length > 0) {
296268
- for (const r of live) println(`[design] \u5F15\u64CE\u5728\u8DD1\uFF1Arun ${r.runId.slice(0, 8)} \u72B6\u6001=${r.status}\u2014\u2014\u522B\u518D\u8D77\u65B0\u8F6E\uFF1B\u8FC7\u51E0\u5206\u949F\u518D status\uFF0C\u6216\u672C\u8F6E\u6536\u5DE5\u7B49\u91CD\u6D3E\u3002`);
296649
+ for (const r of live) println(`[design] \u5F15\u64CE\u5728\u8DD1\uFF1A${shortRunKey(r.key)} \u72B6\u6001=${r.status}${r.agentId ? ` \u5F15\u64CE=${r.agentId}` : ""}\u2014\u2014\u522B\u518D\u8D77\u65B0\u8F6E\uFF1B\u7528 oasis design status ${artifactId} --wait --pull \u963B\u585E\u7B49\u5B83\u8DD1\u5B8C\u5E76\u62C9\u8D27\u3002`);
296269
296650
  } else if (st.runs.length > 0) {
296270
296651
  for (const r of st.runs.slice(0, 3)) {
296271
296652
  println(`[design] \u8FD1\u671F run\uFF1A${r.runId.slice(0, 8)} \u7EC8\u6001=${r.status}${r.agentId ? ` \u5F15\u64CE=${r.agentId}` : ""}`);
296272
- if (r.status === "failed" && r.error) {
296273
- const reason = r.error.length > 300 ? `${r.error.slice(0, 300)}\u2026` : r.error;
296274
- println(`[design] \u5931\u8D25\u539F\u56E0\uFF08od \u539F\u6587\uFF09\uFF1A${reason}`);
296275
- }
296653
+ if (r.status === "failed" && r.error) println(`[design] \u5931\u8D25\u539F\u56E0\uFF08od \u539F\u6587\uFF09\uFF1A${clipReason(r.error)}`);
296654
+ }
296655
+ } else if ((st.turns ?? []).length > 0) {
296656
+ for (const t of (st.turns ?? []).slice(0, 3)) {
296657
+ println(`[design] \u8FD1\u671F run\uFF08\u5BF9\u8BDD\u8BB0\u5F55\uFF09\uFF1A${(t.runId ?? t.messageId ?? "?").slice(0, 8)} \u7EC8\u6001=${t.status}${t.agentId ? ` \u5F15\u64CE=${t.agentId}` : ""}`);
296658
+ if (t.status !== "succeeded" && t.error) println(`[design] \u5931\u8D25\u539F\u56E0\uFF08od \u539F\u6587\uFF09\uFF1A${clipReason(t.error)}`);
296276
296659
  }
296277
296660
  } else {
296278
296661
  println(`[design] \u65E0\u5728\u4E16 run \u8BB0\u5F55\uFF08od \u91CD\u542F\u540E\u4E3A\u7A7A\uFF0C\u5C5E\u6B63\u5E38\uFF09\u2014\u2014\u770B\u4E0B\u9762\u677F\u4E0A\u4EA7\u7269\u5373\u6301\u4E45\u771F\u76F8\u3002`);
296279
296662
  }
296663
+ const last = latestEndedRun(st);
296664
+ const lastFailed = waitedForRun && last !== void 0 && last.status !== "succeeded";
296665
+ if (lastFailed) {
296666
+ println(`[design] \u521A\u7ED3\u675F\u7684\u8FD9\u4E00\u8F6E\u6CA1\u8DD1\u6210\uFF08\u7EC8\u6001=${last.status}${last.agentId ? ` \u5F15\u64CE=${last.agentId}` : ""}\uFF09${last.error ? `\uFF1A${clipReason(last.error)}` : "\uFF0Cod \u6CA1\u8BB0\u539F\u56E0"}\u3002`);
296667
+ println(`[design] \u50CF\u989D\u5EA6/\u9650\u6D41/\u5F15\u64CE\u6545\u969C \u2192 \u6362\u5F15\u64CE\u91CD\u4E0B\uFF1Aoasis design chat ${artifactId} --turn continue --agent <\u53E6\u4E00\u4E2A\u5F15\u64CE> --message "<\u2026>" --async\uFF1B\u50CF\u9700\u6C42/\u6570\u636E\u4E0D\u6E05 \u2192 \u8865\u6E05\u695A\u540E\u91CD\u4E0B\uFF1B\u4E0B\u5B8C\u7167\u65E7 oasis design status ${artifactId} --wait --pull\u3002`);
296668
+ }
296280
296669
  if (st.artifacts.length === 0) {
296281
- println(`[design] \u677F\u4E0A\u8FD8\u6CA1\u6709\u4EA4\u4ED8\u7269\u3002${live.length > 0 ? "\u7B49\u5F15\u64CE\u8DD1\u5B8C\u518D\u6765\u3002" : `\u8D77\u4E00\u8F6E\uFF1Aoasis design chat ${artifactId} --message "<\u2026>" --async`}`);
296670
+ println(`[design] \u677F\u4E0A\u8FD8\u6CA1\u6709\u4EA4\u4ED8\u7269\u3002${live.length > 0 ? `\u7B49\u5F15\u64CE\u8DD1\u5B8C\uFF1Aoasis design status ${artifactId} --wait --pull` : `\u8D77\u4E00\u8F6E\uFF1Aoasis design chat ${artifactId} --message "<\u2026>" --async\uFF0C\u968F\u540E oasis design status ${artifactId} --wait --pull`}`);
296282
296671
  break;
296283
296672
  }
296284
296673
  println(`[design] \u677F\u4E0A\u4EA4\u4ED8\u7269\uFF08\u6301\u4E45\uFF0C\u65B0\u2192\u65E7\uFF09\uFF1A`);
296285
296674
  for (const a of st.artifacts) println(` - ${a.path}\uFF08${a.kind ?? "?"}\uFF09status=${a.status ?? "?"}${a.mtime ? ` \xB7 ${new Date(a.mtime).toISOString()}` : ""}`);
296286
- if (flags.get("pull") === "true") {
296675
+ if (flags.get("pull") === "true" && !lastFailed) {
296287
296676
  const odPublic = (flags.get("od-public-url") ?? process.env["OASIS_OPEN_DESIGN_PUBLIC_URL"] ?? "https://design.open-friday.com").replace(/\/$/, "");
296288
- const { dir: dir2, entry } = await harvestBoard(odBase, odFetch, odPublic, os13.tmpdir(), st.boardKey, latestSucceededRun(st.runs)?.runId ?? "status-pull");
296677
+ const { dir: dir2, entry } = await harvestBoard(odBase, odFetch, odPublic, os13.tmpdir(), st.boardKey, snapshotSourceRunId(st) ?? "status-pull");
296289
296678
  println(`[design] \u5DF2\u62C9\u5230\u672C\u5730\uFF1A${dir2}\uFF08\u5165\u53E3 ${entry}\uFF09`);
296290
296679
  println(`[design] \u8BFB\u5B8C\u6EE1\u610F\u5373\u4EA4\u7A3F\uFF1Aoasis propose ${artifactId} --from-dir ${dir2} --reason "<\u8FD9\u7248\u505A\u4E86\u4EC0\u4E48>"`);
296291
- } else {
296680
+ } else if (!lastFailed) {
296292
296681
  println(`[design] \u53D6\u8D27\uFF1Aoasis design status ${artifactId} --pull\uFF08\u62C9\u5230\u672C\u5730\u76EE\u5F55\u8BFB\uFF0C\u8BFB\u5B8C propose --from-dir \u4EA4\u7A3F\uFF09`);
296293
296682
  }
296294
296683
  break;
296295
296684
  }
296296
- throw new Error("\u7528\u6CD5\uFF1Aoasis design chat <artifactId> --message <t> [--turn first|continue] [--async]\uFF1Boasis design status <artifactId> [--pull]");
296685
+ throw new Error("\u7528\u6CD5\uFF1Aoasis design chat <artifactId> --message <t> [--turn first|continue] [--async]\uFF1Boasis design status <artifactId> [--pull] [--wait [--wait-timeout <\u5206\u949F>]]");
296297
296686
  }
296298
296687
  case "merge": {
296299
296688
  const artifactId = needPos(positional, 0, "oasis merge <artifactId> [revisionId]");
@@ -298390,7 +298779,7 @@ function shimScript() {
298390
298779
  }
298391
298780
 
298392
298781
  // src/index.ts
298393
- var PKG_VERSION = true ? "2.2.84" : "dev";
298782
+ var PKG_VERSION = true ? "2.2.86" : "dev";
298394
298783
  var LOCAL_BIN = localBin();
298395
298784
  var NPM_PREFIX = npmPrefix();
298396
298785
  var INSTANCE = DEFAULT_INSTANCE;