oasis_test_v2 2.2.85 → 2.2.87

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 +740 -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) => {
@@ -3086,8 +2744,7 @@ async function fetchBoardTurns(base, fetchImpl, boardKey) {
3086
2744
  turns.sort((a, b2) => (b2.startedAt ?? Number.NEGATIVE_INFINITY) - (a.startedAt ?? Number.NEGATIVE_INFINITY) || 0);
3087
2745
  return turns;
3088
2746
  }
3089
- async function fetchBoardStatus(base, fetchImpl, artifactId) {
3090
- const boardKey = deriveBoardKey(artifactId);
2747
+ async function fetchBoardStatus(base, fetchImpl, boardKey) {
3091
2748
  let boardExists = true;
3092
2749
  let artifacts = [];
3093
2750
  try {
@@ -3131,7 +2788,7 @@ async function fetchBoardStatus(base, fetchImpl, artifactId) {
3131
2788
  const turns = boardExists ? await fetchBoardTurns(base, fetchImpl, boardKey) : [];
3132
2789
  return { boardKey, boardExists, runs, turns, artifacts };
3133
2790
  }
3134
- async function waitBoardSettled(base, fetchImpl, artifactId, opts = {}) {
2791
+ async function waitBoardSettled(base, fetchImpl, boardKey, opts = {}) {
3135
2792
  const interval = opts.intervalMs ?? 2e4;
3136
2793
  const timeout = opts.timeoutMs ?? 100 * 6e4;
3137
2794
  const sleep6 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
@@ -3140,7 +2797,7 @@ async function waitBoardSettled(base, fetchImpl, artifactId, opts = {}) {
3140
2797
  let lastKey = null;
3141
2798
  let waited = false;
3142
2799
  for (; ; ) {
3143
- const st = await fetchBoardStatus(base, fetchImpl, artifactId);
2800
+ const st = await fetchBoardStatus(base, fetchImpl, boardKey);
3144
2801
  const elapsedMs = now() - started;
3145
2802
  if (!st.boardExists) return { status: st, settled: true, waited, live: [], elapsedMs };
3146
2803
  const live = liveBoardRuns(st);
@@ -3201,7 +2858,7 @@ async function harvestBoard(base, fetchImpl, publicBase, workRoot, boardKey, run
3201
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));
3202
2859
  const entry = htmls[0]?.path;
3203
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");
3204
- const dir = fs2.mkdtempSync(path2.join(workRoot, "od-deliverable-"));
2861
+ const dir = fs.mkdtempSync(path.join(workRoot, "od-deliverable-"));
3205
2862
  const fetchInline = async (rel) => {
3206
2863
  const res = await fetchImpl(`${base}/api/projects/${encodeURIComponent(boardKey)}/export/${encodeURIComponent(rel)}?inline=1`);
3207
2864
  if (!res.ok) throw new Error(`od export ${rel} \u2192 ${res.status}`);
@@ -3212,16 +2869,16 @@ async function harvestBoard(base, fetchImpl, publicBase, workRoot, boardKey, run
3212
2869
  if (!res.ok) throw new Error(`od raw ${rel} \u2192 ${res.status}`);
3213
2870
  return await res.text();
3214
2871
  };
3215
- fs2.writeFileSync(path2.join(dir, "index.html"), await fetchInline(entry));
2872
+ fs.writeFileSync(path.join(dir, "index.html"), await fetchInline(entry));
3216
2873
  for (const f2 of listing.files ?? []) {
3217
2874
  if (f2.path.endsWith(".md") && !f2.path.includes("/")) {
3218
2875
  try {
3219
- fs2.writeFileSync(path2.join(dir, f2.path), await fetchRaw(f2.path));
2876
+ fs.writeFileSync(path.join(dir, f2.path), await fetchRaw(f2.path));
3220
2877
  } catch {
3221
2878
  }
3222
2879
  }
3223
2880
  }
3224
- fs2.writeFileSync(path2.join(dir, BOARD_LINK_FILE), formatBoardLink({ publicBase, boardKey, entry, runId }));
2881
+ fs.writeFileSync(path.join(dir, BOARD_LINK_FILE), formatBoardLink({ publicBase, boardKey, entry, runId }));
3225
2882
  return { dir, entry };
3226
2883
  }
3227
2884
  function formatBoardLink(args) {
@@ -3242,7 +2899,7 @@ function parseBoardLinkSourceRun(text7) {
3242
2899
  }
3243
2900
  async function startDesignChatTurn(opts) {
3244
2901
  const fetchImpl = withOdToken(opts.fetchImpl ?? fetch, opts.token);
3245
- const boardKey = deriveBoardKey(opts.artifactId);
2902
+ const boardKey = opts.boardKey;
3246
2903
  const { conversationId } = await ensureProject(opts.base, fetchImpl, boardKey, opts.title);
3247
2904
  if (opts.contextFiles) await materializeContext(opts.base, fetchImpl, boardKey, opts.contextFiles);
3248
2905
  const userMessageId = await putVisibleUserTurn(opts.base, fetchImpl, { boardKey, conversationId, content: opts.message });
@@ -3269,13 +2926,14 @@ async function runDesignChatTurn(opts) {
3269
2926
  const { artifacts, fileCount } = await fetchResultPackage(opts.base, fetchImpl, runId);
3270
2927
  return { boardKey, conversationId, runId, userMessageId, artifacts, fileCount };
3271
2928
  }
3272
- var import_node_crypto2, fs2, path2, OdHttpError, TASK_CONTEXT_DIR, TERMINAL_TURN_STATUS, isTerminalRunStatus, BOARD_LINK_FILE, SOURCE_RUN_LABEL, UNKNOWN_SOURCE_RUN;
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;
3273
2930
  var init_driver = __esm({
3274
2931
  "../adapters/src/open-design/driver.ts"() {
3275
2932
  "use strict";
3276
2933
  import_node_crypto2 = require("node:crypto");
3277
- fs2 = __toESM(require("node:fs"), 1);
3278
- 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}$/;
3279
2937
  OdHttpError = class extends Error {
3280
2938
  constructor(message, status) {
3281
2939
  super(message);
@@ -3328,20 +2986,20 @@ function materializeBundle(dir, bundle) {
3328
2986
  for (const [rel, entry] of Object.entries(bundle.restoreFiles ?? {})) {
3329
2987
  try {
3330
2988
  const parts = rel.split("/");
3331
- 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 === "..")) {
3332
2990
  throw new Error("\u975E\u6CD5\u76F8\u5BF9\u8DEF\u5F84");
3333
2991
  }
3334
- let parent = fs3.realpathSync(dir);
2992
+ let parent = fs2.realpathSync(dir);
3335
2993
  for (const component of parts.slice(0, -1)) {
3336
- parent = path3.join(parent, component);
3337
- if (!fs3.existsSync(parent)) fs3.mkdirSync(parent);
3338
- 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);
3339
2997
  if (st.isSymbolicLink() || !st.isDirectory()) throw new Error("\u6062\u590D\u8DEF\u5F84\u5305\u542B\u975E\u76EE\u5F55\u6216\u8F6F\u94FE\u63A5");
3340
2998
  }
3341
- const file = path3.join(parent, parts.at(-1));
2999
+ const file = path2.join(parent, parts.at(-1));
3342
3000
  let exists = false;
3343
3001
  try {
3344
- const st = fs3.lstatSync(file);
3002
+ const st = fs2.lstatSync(file);
3345
3003
  if (!st.isFile() || st.isSymbolicLink()) throw new Error("\u6062\u590D\u76EE\u6807\u4E0D\u662F\u666E\u901A\u6587\u4EF6");
3346
3004
  exists = true;
3347
3005
  } catch (error2) {
@@ -3351,37 +3009,37 @@ function materializeBundle(dir, bundle) {
3351
3009
  if (exists && !entry.overwrite) continue;
3352
3010
  const bytes2 = Buffer.from(entry.contentBase64, "base64");
3353
3011
  if ((0, import_node_crypto3.createHash)("sha256").update(bytes2).digest("hex") !== entry.sha256) throw new Error("\u5185\u5BB9\u6821\u9A8C\u5931\u8D25");
3354
- 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)()}`);
3355
3013
  try {
3356
- fs3.writeFileSync(temp, bytes2, { flag: "wx", mode: 384 });
3357
- fs3.renameSync(temp, file);
3014
+ fs2.writeFileSync(temp, bytes2, { flag: "wx", mode: 384 });
3015
+ fs2.renameSync(temp, file);
3358
3016
  } finally {
3359
- fs3.rmSync(temp, { force: true });
3017
+ fs2.rmSync(temp, { force: true });
3360
3018
  }
3361
3019
  } catch (error2) {
3362
3020
  throw new BundleRestoreError(rel, error2 instanceof Error ? error2.message : String(error2));
3363
3021
  }
3364
3022
  }
3365
3023
  for (const [rel, content3] of Object.entries(bundle.files)) {
3366
- const file = path3.join(dir, rel);
3367
- fs3.mkdirSync(path3.dirname(file), { recursive: true });
3368
- fs3.writeFileSync(file, content3);
3024
+ const file = path2.join(dir, rel);
3025
+ fs2.mkdirSync(path2.dirname(file), { recursive: true });
3026
+ fs2.writeFileSync(file, content3);
3369
3027
  }
3370
3028
  for (const [rel, b64] of Object.entries(bundle.binaryFiles ?? {})) {
3371
- const file = path3.join(dir, rel);
3372
- fs3.mkdirSync(path3.dirname(file), { recursive: true });
3373
- 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"));
3374
3032
  }
3375
3033
  const task = bundle.files["TASK.md"];
3376
3034
  if (!task) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
3377
3035
  return task;
3378
3036
  }
3379
- var fs3, path3, import_node_crypto3, BundleRestoreError;
3037
+ var fs2, path2, import_node_crypto3, BundleRestoreError;
3380
3038
  var init_materialize_bundle = __esm({
3381
3039
  "../adapters/src/_core/materialize-bundle.ts"() {
3382
3040
  "use strict";
3383
- fs3 = __toESM(require("node:fs"), 1);
3384
- path3 = __toESM(require("node:path"), 1);
3041
+ fs2 = __toESM(require("node:fs"), 1);
3042
+ path2 = __toESM(require("node:path"), 1);
3385
3043
  import_node_crypto3 = require("node:crypto");
3386
3044
  BundleRestoreError = class extends Error {
3387
3045
  constructor(filePath, detail) {
@@ -3625,7 +3283,7 @@ function containerEnv(job, extraEarly, extraLate) {
3625
3283
  ...extraLate
3626
3284
  };
3627
3285
  if (job.wrapperPaths?.length) {
3628
- 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}`;
3629
3287
  }
3630
3288
  env["OASIS_SERVER"] = job.server.url;
3631
3289
  env["OASIS_TOKEN"] = job.actorToken;
@@ -3634,7 +3292,7 @@ function containerEnv(job, extraEarly, extraLate) {
3634
3292
  }
3635
3293
  function commonArgs(p2, name) {
3636
3294
  const ee = p2.executionEnv;
3637
- const binMount = `/usr/local/bin/${p2.binName ?? path5.basename(p2.binHostPath)}`;
3295
+ const binMount = `/usr/local/bin/${p2.binName ?? path4.basename(p2.binHostPath)}`;
3638
3296
  const args = [
3639
3297
  // 不用 --userns=keep-id:rootless 默认映射已是「容器内 root = 宿主调用用户」,bind 挂载上的
3640
3298
  // 文件属主内外天然一致(agent 写的文件宿主侧可读可删);keep-id 反而在部分主机的 overlay
@@ -3653,7 +3311,7 @@ function commonArgs(p2, name) {
3653
3311
  `${p2.homeHostDir}:${CONTAINER_HOME}`
3654
3312
  ];
3655
3313
  for (const dir of /* @__PURE__ */ new Set([
3656
- ...(p2.job.wrapperPaths ?? []).map((w2) => path5.dirname(w2)),
3314
+ ...(p2.job.wrapperPaths ?? []).map((w2) => path4.dirname(w2)),
3657
3315
  ...p2.extraRoBindDirs ?? []
3658
3316
  ])) {
3659
3317
  args.push("-v", `${dir}:${dir}:ro`);
@@ -3677,7 +3335,7 @@ function droppedEnvNames(env) {
3677
3335
  }
3678
3336
  function wrapForContainer(p2) {
3679
3337
  const ee = p2.executionEnv;
3680
- 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)}`;
3681
3339
  const eeHash = executionEnvHash(p2);
3682
3340
  if (ee.persist && p2.job.workdirKey) {
3683
3341
  const name2 = persistContainerName(p2.job.workdirKey);
@@ -3704,8 +3362,8 @@ function wrapForContainer(p2) {
3704
3362
  droppedEnvNames: droppedEnvNames(p2.env)
3705
3363
  };
3706
3364
  }
3707
- const name = ephemeralContainerName(p2.job, path5.basename(p2.cwd));
3708
- 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");
3709
3367
  return {
3710
3368
  bin: "podman",
3711
3369
  args: [
@@ -3735,12 +3393,12 @@ function wrapForContainer(p2) {
3735
3393
  function killContainerArgs(containerName) {
3736
3394
  return ["kill", containerName];
3737
3395
  }
3738
- 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;
3739
3397
  var init_container_wrap = __esm({
3740
3398
  "../adapters/src/_core/container-wrap.ts"() {
3741
3399
  "use strict";
3742
3400
  import_node_crypto5 = require("node:crypto");
3743
- path5 = __toESM(require("node:path"), 1);
3401
+ path4 = __toESM(require("node:path"), 1);
3744
3402
  init_session_paths();
3745
3403
  CONTAINER_DEFAULT_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
3746
3404
  CONTAINER_HOME = "/oasis-home";
@@ -3752,7 +3410,7 @@ var init_container_wrap = __esm({
3752
3410
  function podmanAvailable() {
3753
3411
  if (podmanProbe === void 0) {
3754
3412
  try {
3755
- 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;
3756
3414
  } catch {
3757
3415
  podmanProbe = false;
3758
3416
  }
@@ -3760,19 +3418,19 @@ function podmanAvailable() {
3760
3418
  return podmanProbe;
3761
3419
  }
3762
3420
  function resolveBinHostPath(bin) {
3763
- if (bin.includes(path6.sep)) {
3421
+ if (bin.includes(path5.sep)) {
3764
3422
  try {
3765
- return fs5.realpathSync(path6.resolve(bin));
3423
+ return fs4.realpathSync(path5.resolve(bin));
3766
3424
  } catch {
3767
- return path6.resolve(bin);
3425
+ return path5.resolve(bin);
3768
3426
  }
3769
3427
  }
3770
- for (const dir of (process.env["PATH"] ?? "").split(path6.delimiter)) {
3428
+ for (const dir of (process.env["PATH"] ?? "").split(path5.delimiter)) {
3771
3429
  if (!dir) continue;
3772
- const candidate = path6.join(dir, bin);
3430
+ const candidate = path5.join(dir, bin);
3773
3431
  try {
3774
- fs5.accessSync(candidate, fs5.constants.X_OK);
3775
- return fs5.realpathSync(candidate);
3432
+ fs4.accessSync(candidate, fs4.constants.X_OK);
3433
+ return fs4.realpathSync(candidate);
3776
3434
  } catch {
3777
3435
  }
3778
3436
  }
@@ -3780,25 +3438,25 @@ function resolveBinHostPath(bin) {
3780
3438
  }
3781
3439
  function resolveBinPackageDir(binHostPath) {
3782
3440
  if (!/\.(js|mjs|cjs)$/i.test(binHostPath)) return null;
3783
- let dir = path6.dirname(binHostPath);
3441
+ let dir = path5.dirname(binHostPath);
3784
3442
  for (let i = 0; i < 8; i++) {
3785
- if (fs5.existsSync(path6.join(dir, "package.json"))) {
3786
- 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;
3787
3445
  return dir;
3788
3446
  }
3789
- const parent = path6.dirname(dir);
3447
+ const parent = path5.dirname(dir);
3790
3448
  if (parent === dir) break;
3791
3449
  dir = parent;
3792
3450
  }
3793
3451
  return null;
3794
3452
  }
3795
3453
  function containerHomeHostDir(workRoot) {
3796
- const dir = path6.join(resolveWorkRoot(workRoot), "container-home");
3797
- fs5.mkdirSync(dir, { recursive: true });
3454
+ const dir = path5.join(resolveWorkRoot(workRoot), "container-home");
3455
+ fs4.mkdirSync(dir, { recursive: true });
3798
3456
  return dir;
3799
3457
  }
3800
3458
  async function ensureImage(image) {
3801
- 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;
3802
3460
  if (!exists) {
3803
3461
  try {
3804
3462
  await execFileP(PODMAN, ["pull", image], { timeout: 6e5 });
@@ -3816,7 +3474,7 @@ async function ensureImage(image) {
3816
3474
  async function ensurePersistContainer(plan) {
3817
3475
  if (plan.mode !== "exec" || !plan.ensureArgs) return;
3818
3476
  const name = plan.containerName;
3819
- 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;
3820
3478
  if (exists) {
3821
3479
  const { stdout: label2 } = await execFileP(
3822
3480
  PODMAN,
@@ -3835,7 +3493,7 @@ async function ensurePersistContainer(plan) {
3835
3493
  }
3836
3494
  function killContainerBestEffort(containerName, _mode) {
3837
3495
  try {
3838
- (0, import_node_child_process2.execFile)(PODMAN, killContainerArgs(containerName), { timeout: 3e4 }, () => {
3496
+ (0, import_node_child_process.execFile)(PODMAN, killContainerArgs(containerName), { timeout: 3e4 }, () => {
3839
3497
  });
3840
3498
  } catch {
3841
3499
  }
@@ -3856,8 +3514,8 @@ async function reapEphemeralContainers() {
3856
3514
  }
3857
3515
  function removePersistContainerForDir(workdir) {
3858
3516
  if (!podmanAvailable()) return;
3859
- const name = `oasis-wd-${path6.basename(workdir)}`;
3860
- (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 }, () => {
3861
3519
  });
3862
3520
  }
3863
3521
  async function maybeContainerize(params) {
@@ -3870,12 +3528,12 @@ async function maybeContainerize(params) {
3870
3528
  const env = containerEnv(params.job, params.extraEnvEarly, params.extraEnvLate);
3871
3529
  const extraRoBindDirs = [...new Set((params.job.wrapperPaths ?? []).flatMap((w2) => {
3872
3530
  try {
3873
- const target = fs5.realpathSync(w2);
3874
- const targetDir = path6.dirname(target);
3875
- 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");
3876
3534
  const dirs = [targetDir];
3877
- if (fs5.existsSync(baseSibling)) dirs.push(baseSibling);
3878
- 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);
3879
3537
  } catch {
3880
3538
  return [];
3881
3539
  }
@@ -3884,7 +3542,7 @@ async function maybeContainerize(params) {
3884
3542
  const binPackageDir = resolveBinPackageDir(binHostPath);
3885
3543
  const plan = wrapForContainer({
3886
3544
  binHostPath,
3887
- binName: path6.basename(params.bin),
3545
+ binName: path5.basename(params.bin),
3888
3546
  // realpath 前的名字("claude")——argv[0] 语义要保住
3889
3547
  ...binPackageDir ? { binPackageDir } : {},
3890
3548
  args: params.args,
@@ -3899,22 +3557,22 @@ async function maybeContainerize(params) {
3899
3557
  if (plan.droppedEnvNames.length > 0) {
3900
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(", ")}`);
3901
3559
  }
3902
- if (plan.cidFile) fs5.rmSync(plan.cidFile, { force: true });
3560
+ if (plan.cidFile) fs4.rmSync(plan.cidFile, { force: true });
3903
3561
  await ensurePersistContainer(plan);
3904
3562
  return { bin: plan.bin, args: plan.args, env, containerName: plan.containerName, mode: plan.mode, imageDigest };
3905
3563
  }
3906
- 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;
3907
3565
  var init_container_runtime = __esm({
3908
3566
  "../adapters/src/_core/container-runtime.ts"() {
3909
3567
  "use strict";
3910
- import_node_child_process2 = require("node:child_process");
3911
- fs5 = __toESM(require("node:fs"), 1);
3568
+ import_node_child_process = require("node:child_process");
3569
+ fs4 = __toESM(require("node:fs"), 1);
3912
3570
  os2 = __toESM(require("node:os"), 1);
3913
- path6 = __toESM(require("node:path"), 1);
3571
+ path5 = __toESM(require("node:path"), 1);
3914
3572
  import_node_util = require("node:util");
3915
3573
  init_container_wrap();
3916
3574
  init_session_paths();
3917
- execFileP = (0, import_node_util.promisify)(import_node_child_process2.execFile);
3575
+ execFileP = (0, import_node_util.promisify)(import_node_child_process.execFile);
3918
3576
  PODMAN = "podman";
3919
3577
  }
3920
3578
  });
@@ -4118,16 +3776,16 @@ var init_model_report = __esm({
4118
3776
 
4119
3777
  // ../adapters/src/_core/transcript.ts
4120
3778
  function openTranscript(file) {
4121
- const out = fs6.createWriteStream(file);
3779
+ const out = fs5.createWriteStream(file);
4122
3780
  out.on("error", () => {
4123
3781
  });
4124
3782
  return out;
4125
3783
  }
4126
- var fs6;
3784
+ var fs5;
4127
3785
  var init_transcript = __esm({
4128
3786
  "../adapters/src/_core/transcript.ts"() {
4129
3787
  "use strict";
4130
- fs6 = __toESM(require("node:fs"), 1);
3788
+ fs5 = __toESM(require("node:fs"), 1);
4131
3789
  }
4132
3790
  });
4133
3791
 
@@ -4235,10 +3893,10 @@ function byModelOf(calls) {
4235
3893
  }
4236
3894
  function readSubagentTranscriptCalls(outputFile) {
4237
3895
  try {
4238
- const stat4 = fs7.statSync(outputFile);
3896
+ const stat4 = fs6.statSync(outputFile);
4239
3897
  if (!stat4.isFile() || stat4.size > SUBAGENT_TRANSCRIPT_MAX_BYTES) return void 0;
4240
3898
  const calls = /* @__PURE__ */ new Map();
4241
- for (const line2 of fs7.readFileSync(outputFile, "utf8").split("\n")) {
3899
+ for (const line2 of fs6.readFileSync(outputFile, "utf8").split("\n")) {
4242
3900
  if (!line2.includes('"assistant"') || !line2.includes('"usage"')) continue;
4243
3901
  let parsed;
4244
3902
  try {
@@ -4493,14 +4151,14 @@ function newClaudeUsageAcc() {
4493
4151
  };
4494
4152
  }
4495
4153
  function findClaudeSessionFile(configDir, cwd, runtimeSessionId) {
4496
- const root = path7.join(configDir, "projects");
4154
+ const root = path6.join(configDir, "projects");
4497
4155
  const name = `${runtimeSessionId}.jsonl`;
4498
- const direct = path7.join(root, cwd.replace(/[^a-zA-Z0-9]/g, "-"), name);
4499
- 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;
4500
4158
  try {
4501
- for (const slug6 of fs7.readdirSync(root)) {
4502
- const candidate = path7.join(root, slug6, name);
4503
- 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;
4504
4162
  }
4505
4163
  } catch {
4506
4164
  }
@@ -4508,15 +4166,15 @@ function findClaudeSessionFile(configDir, cwd, runtimeSessionId) {
4508
4166
  }
4509
4167
  function readClaudeRestoredCost(sessionFile) {
4510
4168
  try {
4511
- const stat4 = fs7.statSync(sessionFile);
4169
+ const stat4 = fs6.statSync(sessionFile);
4512
4170
  if (!stat4.isFile() || stat4.size === 0) return null;
4513
4171
  const length = Math.min(stat4.size, RESTORED_COST_TAIL_BYTES);
4514
4172
  const buf = Buffer.alloc(length);
4515
- const fd = fs7.openSync(sessionFile, "r");
4173
+ const fd = fs6.openSync(sessionFile, "r");
4516
4174
  try {
4517
- fs7.readSync(fd, buf, 0, length, stat4.size - length);
4175
+ fs6.readSync(fd, buf, 0, length, stat4.size - length);
4518
4176
  } finally {
4519
- fs7.closeSync(fd);
4177
+ fs6.closeSync(fd);
4520
4178
  }
4521
4179
  const lines = buf.toString("utf8").split("\n");
4522
4180
  for (let i = lines.length - 1; i >= 0; i--) {
@@ -4740,15 +4398,15 @@ function classifyExit(result) {
4740
4398
  if (status === 429 || status === 529) return "rate-limit";
4741
4399
  return "error";
4742
4400
  }
4743
- 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;
4744
4402
  var init_claude_code = __esm({
4745
4403
  "../adapters/src/claude-code/index.ts"() {
4746
4404
  "use strict";
4747
- import_node_child_process3 = require("node:child_process");
4405
+ import_node_child_process2 = require("node:child_process");
4748
4406
  import_node_crypto6 = require("node:crypto");
4749
- fs7 = __toESM(require("node:fs"), 1);
4407
+ fs6 = __toESM(require("node:fs"), 1);
4750
4408
  os3 = __toESM(require("node:os"), 1);
4751
- path7 = __toESM(require("node:path"), 1);
4409
+ path6 = __toESM(require("node:path"), 1);
4752
4410
  readline = __toESM(require("node:readline"), 1);
4753
4411
  init_sync_skills();
4754
4412
  init_materialize_bundle();
@@ -4852,7 +4510,7 @@ var init_claude_code = __esm({
4852
4510
  runtimeKind: "claude"
4853
4511
  }
4854
4512
  });
4855
- fs7.mkdirSync(dir, { recursive: true });
4513
+ fs6.mkdirSync(dir, { recursive: true });
4856
4514
  await runSyncSkills(this.opts.syncSkills, {
4857
4515
  actorId: job.actor,
4858
4516
  actorToken: job.actorToken,
@@ -4893,7 +4551,7 @@ var init_claude_code = __esm({
4893
4551
  { runtime: "claude-code", actor: job.actor, ...job.dispatchId ? { dispatchId: job.dispatchId } : {} }
4894
4552
  )
4895
4553
  ];
4896
- 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");
4897
4555
  const resumedSessionFile = job.runtimeSessionId && job.resumeRuntimeSession ? findClaudeSessionFile(claudeConfigDir, dir, job.runtimeSessionId) : void 0;
4898
4556
  const restoredCost = resumedSessionFile ? readClaudeRestoredCost(resumedSessionFile) : null;
4899
4557
  const otelEnv = this.resolveOtelEnv(job, id);
@@ -4911,11 +4569,11 @@ var init_claude_code = __esm({
4911
4569
  ...otelEnv
4912
4570
  }
4913
4571
  });
4914
- 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, {
4915
4573
  cwd: dir,
4916
4574
  env: scrubLeakyEnv(process.env),
4917
4575
  stdio: ["pipe", "pipe", "pipe"]
4918
- }) : (0, import_node_child_process3.spawn)(this.opts.claudeBin ?? "claude", args, {
4576
+ }) : (0, import_node_child_process2.spawn)(this.opts.claudeBin ?? "claude", args, {
4919
4577
  cwd: dir,
4920
4578
  env: {
4921
4579
  // 凭证泄漏防护:剔除 pnpm/npm 脚本注入的变量——npm_lifecycle_script 等会把 serve 的启动命令
@@ -4926,7 +4584,7 @@ var init_claude_code = __esm({
4926
4584
  ...job.env,
4927
4585
  // BUG-4 缓冲:抬高单响应输出 token 上限(临时缓冲,根治靠分解)
4928
4586
  ...this.opts.maxOutputTokens ? { CLAUDE_CODE_MAX_OUTPUT_TOKENS: String(this.opts.maxOutputTokens) } : {},
4929
- ...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"] ?? ""}` } : {},
4930
4588
  OASIS_SERVER: job.server.url,
4931
4589
  OASIS_TOKEN: job.actorToken,
4932
4590
  // OTel 遥测(默认关;resolveOtelEnv 决定开不开,runId 进 resource 属性自然归位到本次 run)。
@@ -5006,7 +4664,7 @@ var init_claude_code = __esm({
5006
4664
  if (promptViaStdin) child.stdin.write(prompt2);
5007
4665
  child.stdin.end();
5008
4666
  }
5009
- let transcriptRef = path7.join(dir, "transcript.txt");
4667
+ let transcriptRef = path6.join(dir, "transcript.txt");
5010
4668
  const out = openTranscript(transcriptRef);
5011
4669
  diag = (event, extra) => {
5012
4670
  try {
@@ -5147,14 +4805,14 @@ var init_claude_code = __esm({
5147
4805
  touchMetaExit(dir, { at: (/* @__PURE__ */ new Date()).toISOString(), ...reason ? { reason } : {}, runId: id });
5148
4806
  if (this.opts.cleanupWorkdir && isOneShot) {
5149
4807
  try {
5150
- const keepDir = path7.join(resolveWorkRoot(this.opts.workRoot), "oasis-transcripts");
5151
- fs7.mkdirSync(keepDir, { recursive: true });
5152
- 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`);
5153
4811
  const src = transcriptRef;
5154
4812
  out.end(() => {
5155
4813
  try {
5156
- fs7.renameSync(src, kept);
5157
- fs7.rmSync(dir, { recursive: true, force: true });
4814
+ fs6.renameSync(src, kept);
4815
+ fs6.rmSync(dir, { recursive: true, force: true });
5158
4816
  } catch {
5159
4817
  }
5160
4818
  });
@@ -5500,7 +5158,7 @@ var init_resilient = __esm({
5500
5158
 
5501
5159
  // ../adapters/src/_core/skill-cache.ts
5502
5160
  function defaultCacheRoot() {
5503
- return path8.join(os4.homedir(), ".oasis", "skill-cache");
5161
+ return path7.join(os4.homedir(), ".oasis", "skill-cache");
5504
5162
  }
5505
5163
  function sessionSkillsSubdir(runtimeKind) {
5506
5164
  switch (runtimeKind) {
@@ -5521,7 +5179,7 @@ async function syncActorSkills(opts) {
5521
5179
  });
5522
5180
  const now = opts.now ?? (() => /* @__PURE__ */ new Date());
5523
5181
  const actorDir = pathForActor(cacheRoot, opts.actorId);
5524
- const manifestPath = path8.join(actorDir, "manifest.json");
5182
+ const manifestPath = path7.join(actorDir, "manifest.json");
5525
5183
  await fsp.mkdir(actorDir, { recursive: true, mode: 493 });
5526
5184
  const release = await acquireLock(cacheRoot, opts.actorId, now, log3);
5527
5185
  try {
@@ -5542,7 +5200,7 @@ async function syncActorSkills(opts) {
5542
5200
  for (const [id, entry] of Object.entries(manifest.skills)) {
5543
5201
  if (cloudIds.has(id)) continue;
5544
5202
  try {
5545
- 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 });
5546
5204
  } catch (err) {
5547
5205
  log3("warn", `\u5220\u9664\u5B64\u513F\u6280\u80FD\u76EE\u5F55\u5931\u8D25 ${entry.dir}`, err);
5548
5206
  }
@@ -5554,7 +5212,7 @@ async function syncActorSkills(opts) {
5554
5212
  const forceClearForSession = /* @__PURE__ */ new Set();
5555
5213
  for (const cloud of cloudMetadata) {
5556
5214
  const variantSlug = variantSlugFor(cloud.contentHash);
5557
- const variantDir = path8.join(actorDir, cloud.dir, variantSlug);
5215
+ const variantDir = path7.join(actorDir, cloud.dir, variantSlug);
5558
5216
  const cached3 = manifest.skills[cloud.id];
5559
5217
  const alreadyMaterialized = await dirLooksValid(variantDir, cloud.fileList);
5560
5218
  let actualFiles = null;
@@ -5576,7 +5234,7 @@ async function syncActorSkills(opts) {
5576
5234
  fileList: cloud.fileList
5577
5235
  };
5578
5236
  if (cached3 && cached3.dir !== cloud.dir) {
5579
- 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(() => {
5580
5238
  });
5581
5239
  }
5582
5240
  desiredForSession.set(cloud.dir, { variantDir, variantSlug });
@@ -5638,7 +5296,7 @@ async function syncActorSkills(opts) {
5638
5296
  fileList: cloud.fileList
5639
5297
  };
5640
5298
  if (cached3 && cached3.dir !== cloud.dir) {
5641
- 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(() => {
5642
5300
  });
5643
5301
  }
5644
5302
  desiredForSession.set(cloud.dir, { variantDir, variantSlug });
@@ -5668,10 +5326,10 @@ async function syncActorSkills(opts) {
5668
5326
  }
5669
5327
  }
5670
5328
  function pathForActor(cacheRoot, actorId) {
5671
- return path8.join(cacheRoot, "actors", encodeURIComponent(actorId));
5329
+ return path7.join(cacheRoot, "actors", encodeURIComponent(actorId));
5672
5330
  }
5673
5331
  function lockPathFor(cacheRoot, actorId) {
5674
- return path8.join(cacheRoot, "locks", `${encodeURIComponent(actorId)}.lock`);
5332
+ return path7.join(cacheRoot, "locks", `${encodeURIComponent(actorId)}.lock`);
5675
5333
  }
5676
5334
  function aggregateSkillContentHash(files) {
5677
5335
  const sorted = [...files].sort((a, b2) => a.path.localeCompare(b2.path));
@@ -5693,7 +5351,7 @@ function variantSlugFor(contentHash) {
5693
5351
  }
5694
5352
  async function acquireLock(cacheRoot, actorId, now, log3) {
5695
5353
  const lockPath = lockPathFor(cacheRoot, actorId);
5696
- await fsp.mkdir(path8.dirname(lockPath), { recursive: true, mode: 493 });
5354
+ await fsp.mkdir(path7.dirname(lockPath), { recursive: true, mode: 493 });
5697
5355
  const started = now().getTime();
5698
5356
  while (true) {
5699
5357
  try {
@@ -5759,28 +5417,28 @@ async function readManifest(manifestPath, actorId, log3) {
5759
5417
  };
5760
5418
  }
5761
5419
  async function writeManifest(manifestPath, manifest) {
5762
- const dir = path8.dirname(manifestPath);
5420
+ const dir = path7.dirname(manifestPath);
5763
5421
  await fsp.mkdir(dir, { recursive: true });
5764
5422
  const tmp = `${manifestPath}.${process.pid}.${Date.now()}.tmp`;
5765
5423
  await fsp.writeFile(tmp, JSON.stringify(manifest, null, 2), { mode: 420 });
5766
5424
  await fsp.rename(tmp, manifestPath);
5767
5425
  }
5768
5426
  async function materializeVariantDir(actorDir, targetDir, variantSlug, files) {
5769
- const parentPath = path8.join(actorDir, targetDir);
5427
+ const parentPath = path7.join(actorDir, targetDir);
5770
5428
  await fsp.mkdir(parentPath, { recursive: true, mode: 493 });
5771
- const targetPath = path8.join(parentPath, variantSlug);
5772
- const tmpPath = path8.join(parentPath, `${variantSlug}.new.${process.pid}.${Date.now()}`);
5773
- 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()}`);
5774
5432
  await fsp.rm(tmpPath, { recursive: true, force: true });
5775
5433
  await fsp.mkdir(tmpPath, { recursive: true, mode: 493 });
5776
5434
  for (const f2 of files) {
5777
5435
  if (!f2.content) continue;
5778
- const dest = path8.join(tmpPath, f2.path);
5779
- const rel = path8.relative(tmpPath, dest);
5780
- 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)) {
5781
5439
  throw new Error(`SkillFile.path \u4E0D\u5408\u6CD5: ${f2.path}`);
5782
5440
  }
5783
- await fsp.mkdir(path8.dirname(dest), { recursive: true });
5441
+ await fsp.mkdir(path7.dirname(dest), { recursive: true });
5784
5442
  await fsp.writeFile(dest, f2.content, { mode: 420 });
5785
5443
  }
5786
5444
  let hadOld = false;
@@ -5798,7 +5456,7 @@ async function dirLooksValid(dir, expectFileList) {
5798
5456
  if (expectFileList.length === 0) return false;
5799
5457
  try {
5800
5458
  for (const rel of expectFileList) {
5801
- await fsp.stat(path8.join(dir, rel));
5459
+ await fsp.stat(path7.join(dir, rel));
5802
5460
  }
5803
5461
  return true;
5804
5462
  } catch {
@@ -5810,7 +5468,7 @@ async function hashMaterializedDir(dir, fileList) {
5810
5468
  try {
5811
5469
  const files = [];
5812
5470
  for (const rel of fileList) {
5813
- const content3 = await fsp.readFile(path8.join(dir, rel), "utf8");
5471
+ const content3 = await fsp.readFile(path7.join(dir, rel), "utf8");
5814
5472
  files.push({ path: rel, content: content3 });
5815
5473
  }
5816
5474
  return aggregateSkillContentHash(files);
@@ -5822,10 +5480,10 @@ async function listMaterializedFiles(dir) {
5822
5480
  try {
5823
5481
  const out = [];
5824
5482
  const walk = async (sub) => {
5825
- const abs = sub ? path8.join(dir, sub) : dir;
5483
+ const abs = sub ? path7.join(dir, sub) : dir;
5826
5484
  const entries = await fsp.readdir(abs, { withFileTypes: true });
5827
5485
  for (const e of entries) {
5828
- const rel = sub ? path8.join(sub, e.name) : e.name;
5486
+ const rel = sub ? path7.join(sub, e.name) : e.name;
5829
5487
  if (e.isDirectory()) {
5830
5488
  await walk(rel);
5831
5489
  } else {
@@ -5841,14 +5499,14 @@ async function listMaterializedFiles(dir) {
5841
5499
  }
5842
5500
  async function syncSessionSymlinks(sessionDir, runtimeKind, desired, pending, forceClear, log3) {
5843
5501
  const subdir = sessionSkillsSubdir(runtimeKind);
5844
- const skillsDir = path8.join(sessionDir, subdir);
5502
+ const skillsDir = path7.join(sessionDir, subdir);
5845
5503
  await fsp.mkdir(skillsDir, { recursive: true, mode: 493 });
5846
5504
  for (const [name, { variantDir }] of desired) {
5847
- const link = path8.join(skillsDir, name);
5505
+ const link = path7.join(skillsDir, name);
5848
5506
  let ok3 = false;
5849
5507
  try {
5850
5508
  const current = await fsp.readlink(link);
5851
- ok3 = path8.resolve(skillsDir, current) === variantDir;
5509
+ ok3 = path7.resolve(skillsDir, current) === variantDir;
5852
5510
  } catch {
5853
5511
  }
5854
5512
  if (!ok3) {
@@ -5875,22 +5533,22 @@ async function syncSessionSymlinks(sessionDir, runtimeKind, desired, pending, fo
5875
5533
  }
5876
5534
  }
5877
5535
  try {
5878
- await fsp.rm(path8.join(skillsDir, name), { recursive: true, force: true });
5536
+ await fsp.rm(path7.join(skillsDir, name), { recursive: true, force: true });
5879
5537
  } catch (err) {
5880
5538
  log3("warn", `\u6E05\u5B64\u513F\u5931\u8D25 ${name}`, err);
5881
5539
  }
5882
5540
  }
5883
5541
  }
5884
5542
  async function verifyPendingLink(skillsDir, name, pend, log3) {
5885
- const link = path8.join(skillsDir, name);
5543
+ const link = path7.join(skillsDir, name);
5886
5544
  let resolved;
5887
5545
  try {
5888
5546
  const current = await fsp.readlink(link);
5889
- resolved = path8.resolve(skillsDir, current);
5547
+ resolved = path7.resolve(skillsDir, current);
5890
5548
  } catch {
5891
5549
  return false;
5892
5550
  }
5893
- if (path8.basename(resolved) !== pend.wantedVariantSlug) return false;
5551
+ if (path7.basename(resolved) !== pend.wantedVariantSlug) return false;
5894
5552
  const actual = await listMaterializedFiles(resolved);
5895
5553
  const expected = [...pend.fileList].sort();
5896
5554
  const setsMatch = actual !== null && actual.length === expected.length && actual.every((p2, i) => p2 === expected[i]);
@@ -5905,14 +5563,14 @@ async function verifyPendingLink(skillsDir, name, pend, log3) {
5905
5563
  }
5906
5564
  return true;
5907
5565
  }
5908
- 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;
5909
5567
  var init_skill_cache = __esm({
5910
5568
  "../adapters/src/_core/skill-cache.ts"() {
5911
5569
  "use strict";
5912
5570
  import_node_crypto7 = require("node:crypto");
5913
5571
  fsp = __toESM(require("node:fs/promises"), 1);
5914
5572
  os4 = __toESM(require("node:os"), 1);
5915
- path8 = __toESM(require("node:path"), 1);
5573
+ path7 = __toESM(require("node:path"), 1);
5916
5574
  MANIFEST_SCHEMA_VERSION = 2;
5917
5575
  STALE_LOCK_MS = 6e4;
5918
5576
  LOCK_ACQUIRE_TIMEOUT_MS = 3e4;
@@ -5967,7 +5625,7 @@ var init_skill_cache_http = __esm({
5967
5625
  function resolveOnPath(binary) {
5968
5626
  try {
5969
5627
  const cmd = process.platform === "win32" ? "where" : "which";
5970
- 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"] });
5971
5629
  const first = out.split("\n")[0]?.trim();
5972
5630
  return first || void 0;
5973
5631
  } catch {
@@ -5976,7 +5634,7 @@ function resolveOnPath(binary) {
5976
5634
  }
5977
5635
  function verifyCursorAcpIdentity(binary) {
5978
5636
  try {
5979
- (0, import_node_child_process4.execFileSync)(binary, ["acp", "--help"], {
5637
+ (0, import_node_child_process3.execFileSync)(binary, ["acp", "--help"], {
5980
5638
  stdio: ["ignore", "ignore", "ignore"],
5981
5639
  timeout: ACP_HELP_TIMEOUT_MS
5982
5640
  });
@@ -5992,11 +5650,11 @@ function resolveCursorBinary() {
5992
5650
  if (agent && verifyCursorAcpIdentity(agent)) return agent;
5993
5651
  return void 0;
5994
5652
  }
5995
- 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;
5996
5654
  var init_binary = __esm({
5997
5655
  "../adapters/src/cursor/binary.ts"() {
5998
5656
  "use strict";
5999
- import_node_child_process4 = require("node:child_process");
5657
+ import_node_child_process3 = require("node:child_process");
6000
5658
  DEFAULT_CURSOR_BIN = "cursor-agent";
6001
5659
  ACP_HELP_TIMEOUT_MS = 1500;
6002
5660
  }
@@ -6204,7 +5862,7 @@ async function probeCodexAppServer(bin, timeoutMs = 15e3) {
6204
5862
  }
6205
5863
  let child;
6206
5864
  try {
6207
- 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"], {
6208
5866
  cwd: dir,
6209
5867
  stdio: ["pipe", "pipe", "ignore"],
6210
5868
  env: { ...process.env }
@@ -6384,7 +6042,7 @@ async function discoverPiModels(bin) {
6384
6042
  async function discoverPiModelsRPC(bin) {
6385
6043
  const child = (() => {
6386
6044
  try {
6387
- 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"], {
6388
6046
  stdio: ["pipe", "pipe", "ignore"],
6389
6047
  env: { ...process.env }
6390
6048
  });
@@ -6461,7 +6119,7 @@ async function runCapture(bin, args, timeoutMs = 15e3) {
6461
6119
  return await new Promise((resolve12) => {
6462
6120
  let child;
6463
6121
  try {
6464
- 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 } });
6465
6123
  } catch {
6466
6124
  resolve12(void 0);
6467
6125
  return;
@@ -6492,7 +6150,7 @@ async function runCapture(bin, args, timeoutMs = 15e3) {
6492
6150
  async function discoverACPModels(bin, provider, args = ["acp"], timeoutMs = 12e3) {
6493
6151
  const child = (() => {
6494
6152
  try {
6495
- return (0, import_node_child_process5.spawn)(bin, args, {
6153
+ return (0, import_node_child_process4.spawn)(bin, args, {
6496
6154
  stdio: ["pipe", "pipe", "ignore"],
6497
6155
  env: { ...process.env, ...provider === "hermes" ? { HERMES_YOLO_MODE: "1" } : {} }
6498
6156
  });
@@ -6663,11 +6321,11 @@ function parseOpenclawAgentsText(output) {
6663
6321
  function isSafeIdentifier(value2) {
6664
6322
  return /^[A-Za-z][A-Za-z0-9._/-]*$/.test(value2);
6665
6323
  }
6666
- 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;
6667
6325
  var init_models = __esm({
6668
6326
  "../adapters/src/_core/models.ts"() {
6669
6327
  "use strict";
6670
- import_node_child_process5 = require("node:child_process");
6328
+ import_node_child_process4 = require("node:child_process");
6671
6329
  import_node_fs2 = require("node:fs");
6672
6330
  import_node_os2 = require("node:os");
6673
6331
  import_node_path3 = require("node:path");
@@ -6725,16 +6383,16 @@ async function runProtocolSession(job, cfg) {
6725
6383
  ...cfg.env,
6726
6384
  ...job.env,
6727
6385
  ...cfg.yoloEnv ?? {},
6728
- ...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"] ?? ""}` } : {},
6729
6387
  OASIS_SERVER: job.server.url,
6730
6388
  OASIS_TOKEN: job.actorToken,
6731
6389
  ...cfg.rootBypassRuntime ? rootBypassEnv(cfg.rootBypassRuntime) : {}
6732
6390
  };
6733
- 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"] });
6734
6392
  child.stdin.on("error", () => {
6735
6393
  });
6736
6394
  if (child.pid !== void 0 && job.workdirKey) lockWorkdir(dir, child.pid, "session", job.workdirKey);
6737
- const transcriptRef = path9.join(dir, "transcript.txt");
6395
+ const transcriptRef = path8.join(dir, "transcript.txt");
6738
6396
  const transcript = openTranscript(transcriptRef);
6739
6397
  const outputCbs = [];
6740
6398
  const telemetryCbs = [];
@@ -6956,13 +6614,13 @@ async function runProtocolSession(job, cfg) {
6956
6614
  }
6957
6615
  };
6958
6616
  }
6959
- var import_node_child_process6, import_node_crypto8, path9, ProtocolCommandRejected, ProtocolSessionClosing;
6617
+ var import_node_child_process5, import_node_crypto8, path8, ProtocolCommandRejected, ProtocolSessionClosing;
6960
6618
  var init_protocol_session = __esm({
6961
6619
  "../adapters/src/_core/protocol-session.ts"() {
6962
6620
  "use strict";
6963
- import_node_child_process6 = require("node:child_process");
6621
+ import_node_child_process5 = require("node:child_process");
6964
6622
  import_node_crypto8 = require("node:crypto");
6965
- path9 = __toESM(require("node:path"), 1);
6623
+ path8 = __toESM(require("node:path"), 1);
6966
6624
  init_claude_code();
6967
6625
  init_usage();
6968
6626
  init_container_runtime();
@@ -7119,12 +6777,12 @@ function pickCodexReportedModel(sources) {
7119
6777
  function codexSessionsRoot() {
7120
6778
  const candidates = [];
7121
6779
  const ch = process.env["CODEX_HOME"];
7122
- if (ch) candidates.push(path10.join(ch, "sessions"));
6780
+ if (ch) candidates.push(path9.join(ch, "sessions"));
7123
6781
  const home2 = process.env["HOME"] || os5.homedir();
7124
- if (home2) candidates.push(path10.join(home2, ".codex", "sessions"));
6782
+ if (home2) candidates.push(path9.join(home2, ".codex", "sessions"));
7125
6783
  for (const c of candidates) {
7126
6784
  try {
7127
- if (fs8.statSync(c).isDirectory()) return c;
6785
+ if (fs7.statSync(c).isDirectory()) return c;
7128
6786
  } catch {
7129
6787
  }
7130
6788
  }
@@ -7136,17 +6794,17 @@ function listRecentRollouts(root, sinceMs) {
7136
6794
  const walk = (d) => {
7137
6795
  let ents;
7138
6796
  try {
7139
- ents = fs8.readdirSync(d, { withFileTypes: true });
6797
+ ents = fs7.readdirSync(d, { withFileTypes: true });
7140
6798
  } catch {
7141
6799
  return;
7142
6800
  }
7143
6801
  for (const ent of ents) {
7144
- const p2 = path10.join(d, ent.name);
6802
+ const p2 = path9.join(d, ent.name);
7145
6803
  if (ent.isDirectory()) walk(p2);
7146
6804
  else if (ent.isFile() && ent.name.startsWith("rollout-") && ent.name.endsWith(".jsonl")) {
7147
6805
  let m2 = 0;
7148
6806
  try {
7149
- m2 = fs8.statSync(p2).mtimeMs;
6807
+ m2 = fs7.statSync(p2).mtimeMs;
7150
6808
  } catch {
7151
6809
  }
7152
6810
  if (m2 >= margin) found2.push({ f: p2, m: m2 });
@@ -7162,12 +6820,12 @@ function findRolloutsBySessionId(root, sessionId) {
7162
6820
  const walk = (d) => {
7163
6821
  let ents;
7164
6822
  try {
7165
- ents = fs8.readdirSync(d, { withFileTypes: true });
6823
+ ents = fs7.readdirSync(d, { withFileTypes: true });
7166
6824
  } catch {
7167
6825
  return;
7168
6826
  }
7169
6827
  for (const ent of ents) {
7170
- const p2 = path10.join(d, ent.name);
6828
+ const p2 = path9.join(d, ent.name);
7171
6829
  if (ent.isDirectory()) walk(p2);
7172
6830
  else if (ent.isFile() && ent.name.startsWith("rollout-") && ent.name.endsWith(suffix)) found2.push(p2);
7173
6831
  }
@@ -7197,7 +6855,7 @@ function locateCodexRollout(workdir, sinceMs, sessionsRoot = codexSessionsRoot()
7197
6855
  const files = listRecentRollouts(sessionsRoot, sinceMs);
7198
6856
  const readLines = (f2) => {
7199
6857
  try {
7200
- return fs8.readFileSync(f2, "utf8").split("\n");
6858
+ return fs7.readFileSync(f2, "utf8").split("\n");
7201
6859
  } catch {
7202
6860
  return null;
7203
6861
  }
@@ -7443,16 +7101,16 @@ function codexTurnErrorMessage(value2) {
7443
7101
  const detail = typeof error2?.additionalDetails === "string" ? error2.additionalDetails.trim() : "";
7444
7102
  return [summary, detail].filter(Boolean).join("\n").slice(0, 4e3);
7445
7103
  }
7446
- 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;
7447
7105
  var init_codex = __esm({
7448
7106
  "../adapters/src/codex/index.ts"() {
7449
7107
  "use strict";
7450
- import_node_child_process7 = require("node:child_process");
7108
+ import_node_child_process6 = require("node:child_process");
7451
7109
  init_container_runtime();
7452
7110
  import_node_crypto9 = require("node:crypto");
7453
- fs8 = __toESM(require("node:fs"), 1);
7111
+ fs7 = __toESM(require("node:fs"), 1);
7454
7112
  os5 = __toESM(require("node:os"), 1);
7455
- path10 = __toESM(require("node:path"), 1);
7113
+ path9 = __toESM(require("node:path"), 1);
7456
7114
  readline2 = __toESM(require("node:readline"), 1);
7457
7115
  init_sync_skills();
7458
7116
  init_materialize_bundle();
@@ -7490,9 +7148,9 @@ var init_codex = __esm({
7490
7148
  rest = "";
7491
7149
  /** 读新追加的整行逐行回调;文件被截短时先回调 onReset 再从头读。读失败抛给调用方(调用方吞)。 */
7492
7150
  read(onLine, onReset) {
7493
- const fd = fs8.openSync(this.file, "r");
7151
+ const fd = fs7.openSync(this.file, "r");
7494
7152
  try {
7495
- const size = fs8.fstatSync(fd).size;
7153
+ const size = fs7.fstatSync(fd).size;
7496
7154
  if (size < this.offset) {
7497
7155
  this.offset = 0;
7498
7156
  this.rest = "";
@@ -7500,7 +7158,7 @@ var init_codex = __esm({
7500
7158
  }
7501
7159
  if (size > this.offset) {
7502
7160
  const buf = Buffer.alloc(size - this.offset);
7503
- const n = fs8.readSync(fd, buf, 0, buf.length, this.offset);
7161
+ const n = fs7.readSync(fd, buf, 0, buf.length, this.offset);
7504
7162
  this.offset += n;
7505
7163
  const text7 = this.rest + buf.subarray(0, n).toString("utf8");
7506
7164
  const lines = text7.split("\n");
@@ -7508,7 +7166,7 @@ var init_codex = __esm({
7508
7166
  for (const line2 of lines) onLine(line2);
7509
7167
  }
7510
7168
  } finally {
7511
- fs8.closeSync(fd);
7169
+ fs7.closeSync(fd);
7512
7170
  }
7513
7171
  }
7514
7172
  };
@@ -7771,7 +7429,7 @@ ${task}` : task;
7771
7429
  let fromRollout;
7772
7430
  if (state.rolloutPath) {
7773
7431
  try {
7774
- fromRollout = parseCodexModel(fs8.readFileSync(state.rolloutPath, "utf8").split("\n"));
7432
+ fromRollout = parseCodexModel(fs7.readFileSync(state.rolloutPath, "utf8").split("\n"));
7775
7433
  } catch {
7776
7434
  }
7777
7435
  }
@@ -7923,19 +7581,19 @@ ${task}` : task;
7923
7581
  ...this.opts.workRoot !== void 0 ? { workRoot: this.opts.workRoot } : {},
7924
7582
  ...this.opts.env !== void 0 ? { extraEnvEarly: this.opts.env } : {}
7925
7583
  });
7926
- 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, {
7927
7585
  cwd: dir,
7928
7586
  env: scrubLeakyEnv(process.env),
7929
7587
  // stdin 的开合口径与宿主分支一致:codex 只在走 stdin 时才该收到管道(见下方长注释)。
7930
7588
  // 容器包装本身带 `-i`,podman 会把这条管道透传给容器内进程。
7931
7589
  stdio: [promptViaStdin ? "pipe" : "ignore", "pipe", "pipe"]
7932
- }) : (0, import_node_child_process7.spawn)(this.opts.codexBin ?? "codex", args, {
7590
+ }) : (0, import_node_child_process6.spawn)(this.opts.codexBin ?? "codex", args, {
7933
7591
  cwd: dir,
7934
7592
  env: {
7935
7593
  ...scrubLeakyEnv(process.env),
7936
7594
  ...this.opts.env,
7937
7595
  ...job.env,
7938
- ...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"] ?? ""}` } : {},
7939
7597
  OASIS_SERVER: job.server.url,
7940
7598
  OASIS_TOKEN: job.actorToken
7941
7599
  },
@@ -7952,7 +7610,7 @@ ${task}` : task;
7952
7610
  child.stdin.end(prompt2);
7953
7611
  }
7954
7612
  if (child.pid !== void 0 && job.workdirKey) lockWorkdir(dir, child.pid, "session", job.workdirKey);
7955
- let transcriptRef = path10.join(dir, "transcript.txt");
7613
+ let transcriptRef = path9.join(dir, "transcript.txt");
7956
7614
  const out = openTranscript(transcriptRef);
7957
7615
  const outputCbs = [];
7958
7616
  const telemetryCbs = [];
@@ -8054,14 +7712,14 @@ ${task}` : task;
8054
7712
  }
8055
7713
  if (this.opts.cleanupWorkdir && !job.workdirKey) {
8056
7714
  try {
8057
- const keepDir = path10.join(resolveWorkRoot(this.opts.workRoot), "oasis-transcripts");
8058
- fs8.mkdirSync(keepDir, { recursive: true });
8059
- 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`);
8060
7718
  const src = transcriptRef;
8061
7719
  out.end(() => {
8062
7720
  try {
8063
- fs8.renameSync(src, kept);
8064
- fs8.rmSync(dir, { recursive: true, force: true });
7721
+ fs7.renameSync(src, kept);
7722
+ fs7.rmSync(dir, { recursive: true, force: true });
8065
7723
  } catch {
8066
7724
  }
8067
7725
  });
@@ -8579,10 +8237,10 @@ function buildReportedUsage(acc, model, inputTokensIncludeCacheRead) {
8579
8237
  }
8580
8238
  async function runACPSession(job, cfg) {
8581
8239
  if (job.executionEnv) {
8582
- 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`);
8583
8241
  }
8584
8242
  const slug6 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
8585
- 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";
8586
8244
  const id = `${binTag}-${slug6}-${(0, import_node_crypto10.randomUUID)().slice(0, 8)}`;
8587
8245
  const dir = job.workdirKey ? prepareWorkdir({
8588
8246
  workRoot: cfg.workRoot,
@@ -8616,7 +8274,7 @@ async function runACPSession(job, cfg) {
8616
8274
  });
8617
8275
  }
8618
8276
  const task = materializeBundle(dir, job.bundle);
8619
- 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(
8620
8278
  job.extraArgs,
8621
8279
  cfg.extraArgs,
8622
8280
  cfg.blockedFlags,
@@ -8628,9 +8286,9 @@ async function runACPSession(job, cfg) {
8628
8286
  ...cfg.env,
8629
8287
  ...job.env,
8630
8288
  ...cfg.yoloEnv ?? {},
8631
- ...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"] ?? ""}` } : {},
8632
8290
  // 每会话技能目录绝对路径 → env(供不扫 cwd 的 runtime 经 config external_dirs 发现,见 injectSkillsDirEnv)。
8633
- ...cfg.injectSkillsDirEnv ? { [cfg.injectSkillsDirEnv]: path11.join(dir, sessionSkillsSubdir(cfg.runtimeKind ?? "")) } : {},
8291
+ ...cfg.injectSkillsDirEnv ? { [cfg.injectSkillsDirEnv]: path10.join(dir, sessionSkillsSubdir(cfg.runtimeKind ?? "")) } : {},
8634
8292
  OASIS_SERVER: job.server.url,
8635
8293
  OASIS_TOKEN: job.actorToken,
8636
8294
  ...cfg.rootBypassRuntime ? rootBypassEnv(cfg.rootBypassRuntime) : {}
@@ -8640,7 +8298,7 @@ async function runACPSession(job, cfg) {
8640
8298
  child.stdin.on("error", () => {
8641
8299
  });
8642
8300
  if (child.pid !== void 0 && job.workdirKey) lockWorkdir(dir, child.pid, "session", job.workdirKey);
8643
- let transcriptRef = path11.join(dir, "transcript.txt");
8301
+ let transcriptRef = path10.join(dir, "transcript.txt");
8644
8302
  const transcriptOut = openTranscript(transcriptRef);
8645
8303
  const outputCbs = [];
8646
8304
  const telemetryCbs = [];
@@ -8742,14 +8400,14 @@ ${note}` : note };
8742
8400
  }
8743
8401
  if (cfg.cleanupWorkdir && !job.workdirKey) {
8744
8402
  try {
8745
- const keepDir = path11.join(resolveWorkRoot(cfg.workRoot), "oasis-transcripts");
8746
- fs9.mkdirSync(keepDir, { recursive: true });
8747
- 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`);
8748
8406
  const src = transcriptRef;
8749
8407
  transcriptOut.end(() => {
8750
8408
  try {
8751
- fs9.renameSync(src, kept);
8752
- fs9.rmSync(dir, { recursive: true, force: true });
8409
+ fs8.renameSync(src, kept);
8410
+ fs8.rmSync(dir, { recursive: true, force: true });
8753
8411
  } catch {
8754
8412
  }
8755
8413
  });
@@ -9099,14 +8757,14 @@ ${task}` : task;
9099
8757
  return run;
9100
8758
  }
9101
8759
  }
9102
- 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;
9103
8761
  var init_acp = __esm({
9104
8762
  "../adapters/src/_core/acp.ts"() {
9105
8763
  "use strict";
9106
- import_node_child_process8 = require("node:child_process");
8764
+ import_node_child_process7 = require("node:child_process");
9107
8765
  import_node_crypto10 = require("node:crypto");
9108
- fs9 = __toESM(require("node:fs"), 1);
9109
- path11 = __toESM(require("node:path"), 1);
8766
+ fs8 = __toESM(require("node:fs"), 1);
8767
+ path10 = __toESM(require("node:path"), 1);
9110
8768
  readline3 = __toESM(require("node:readline"), 1);
9111
8769
  init_sync_skills();
9112
8770
  init_session_paths();
@@ -9708,10 +9366,10 @@ function cursorAcpSessionStorePath(sessionId, env = process.env) {
9708
9366
  if (!sessionId) return void 0;
9709
9367
  const home2 = env["CURSOR_HOME"] || env["HOME"] || os6.homedir();
9710
9368
  if (!home2) return void 0;
9711
- const base = env["CURSOR_HOME"] ? home2 : path12.join(home2, ".cursor");
9712
- 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");
9713
9371
  try {
9714
- return fs10.existsSync(file) ? file : void 0;
9372
+ return fs9.existsSync(file) ? file : void 0;
9715
9373
  } catch {
9716
9374
  return void 0;
9717
9375
  }
@@ -9846,13 +9504,13 @@ async function readCursorRoutedModel(sessionId, window3) {
9846
9504
  }
9847
9505
  }
9848
9506
  }
9849
- 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;
9850
9508
  var init_local_store = __esm({
9851
9509
  "../adapters/src/cursor/local-store.ts"() {
9852
9510
  "use strict";
9853
- fs10 = __toESM(require("node:fs"), 1);
9511
+ fs9 = __toESM(require("node:fs"), 1);
9854
9512
  os6 = __toESM(require("node:os"), 1);
9855
- path12 = __toESM(require("node:path"), 1);
9513
+ path11 = __toESM(require("node:path"), 1);
9856
9514
  MAX_TABLES = 24;
9857
9515
  MAX_ROWS_PER_TABLE = 200;
9858
9516
  TURN_CLOCK_TOLERANCE_MS = 1e3;
@@ -10127,12 +9785,12 @@ function normalizePi(line2) {
10127
9785
  function piReportedModel(state, requested) {
10128
9786
  return pickReportedModel({ runtime: [state.model, state.startupModel], requested });
10129
9787
  }
10130
- var fs11, path13, PI_SESSION_FILE, PI_BLOCKED_FLAGS, PiAdapter;
9788
+ var fs10, path12, PI_SESSION_FILE, PI_BLOCKED_FLAGS, PiAdapter;
10131
9789
  var init_pi = __esm({
10132
9790
  "../adapters/src/pi/index.ts"() {
10133
9791
  "use strict";
10134
- fs11 = __toESM(require("node:fs"), 1);
10135
- path13 = __toESM(require("node:path"), 1);
9792
+ fs10 = __toESM(require("node:fs"), 1);
9793
+ path12 = __toESM(require("node:path"), 1);
10136
9794
  init_protocol_session();
10137
9795
  init_usage();
10138
9796
  init_model_report();
@@ -10159,9 +9817,9 @@ var init_pi = __esm({
10159
9817
  buildArgs(job2, dir) {
10160
9818
  const model = job2.model ?? opts.model;
10161
9819
  if (!job2.workdirKey) return ["--mode", "rpc", "--no-session", ...model ? ["--model", model] : []];
10162
- const sessionFile = path13.join(dir, PI_SESSION_FILE);
9820
+ const sessionFile = path12.join(dir, PI_SESSION_FILE);
10163
9821
  try {
10164
- if (!fs11.existsSync(sessionFile)) fs11.writeFileSync(sessionFile, "");
9822
+ if (!fs10.existsSync(sessionFile)) fs10.writeFileSync(sessionFile, "");
10165
9823
  } catch {
10166
9824
  return ["--mode", "rpc", "--no-session"];
10167
9825
  }
@@ -10268,7 +9926,7 @@ function pdeathsigAvailable(probe = defaultProbe) {
10268
9926
  function defaultProbe() {
10269
9927
  if (os7.platform() !== "linux") return false;
10270
9928
  try {
10271
- 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();
10272
9930
  return help.includes("--pdeathsig");
10273
9931
  } catch {
10274
9932
  return false;
@@ -10280,11 +9938,11 @@ function pdeathsigWrap(bin, args) {
10280
9938
  function shouldBindRuntimePdeathsig() {
10281
9939
  return process.env["OASIS_BIND_RUNTIME_PDEATHSIG"] === "1" && pdeathsigAvailable();
10282
9940
  }
10283
- var import_node_child_process9, os7, cachedAvailable;
9941
+ var import_node_child_process8, os7, cachedAvailable;
10284
9942
  var init_pdeathsig = __esm({
10285
9943
  "../adapters/src/_core/pdeathsig.ts"() {
10286
9944
  "use strict";
10287
- import_node_child_process9 = require("node:child_process");
9945
+ import_node_child_process8 = require("node:child_process");
10288
9946
  os7 = __toESM(require("node:os"), 1);
10289
9947
  }
10290
9948
  });
@@ -10332,7 +9990,7 @@ function buildEnv(job, cfg) {
10332
9990
  ...scrubLeakyEnv(process.env),
10333
9991
  ...cfg.env,
10334
9992
  ...job.env,
10335
- ...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"] ?? ""}` } : {},
10336
9994
  OASIS_SERVER: job.server.url,
10337
9995
  OASIS_TOKEN: job.actorToken,
10338
9996
  // root 下放行 bypassPermissions(仅 claude 家族 runtime 声明;见 _core/root-bypass.ts)。
@@ -10357,14 +10015,14 @@ function makeFinish(id, cfg, transcriptRefPtr, transcriptOut, dir, workdirKey, r
10357
10015
  }
10358
10016
  if (cfg.cleanupWorkdir && !workdirKey) {
10359
10017
  try {
10360
- const keepDir = path14.join(resolveWorkRoot(cfg.workRoot), "oasis-transcripts");
10361
- fs12.mkdirSync(keepDir, { recursive: true });
10362
- 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`);
10363
10021
  const src = transcriptRefPtr.value;
10364
10022
  transcriptOut.end(() => {
10365
10023
  try {
10366
- fs12.renameSync(src, kept);
10367
- fs12.rmSync(dir, { recursive: true, force: true });
10024
+ fs11.renameSync(src, kept);
10025
+ fs11.rmSync(dir, { recursive: true, force: true });
10368
10026
  } catch {
10369
10027
  }
10370
10028
  });
@@ -10395,17 +10053,17 @@ async function runStreamJson(job, cfg) {
10395
10053
  ...cfg.env !== void 0 ? { extraEnvEarly: cfg.env } : {}
10396
10054
  });
10397
10055
  const direct = shouldBindRuntimePdeathsig() ? pdeathsigWrap(cfg.bin, args) : { bin: cfg.bin, args };
10398
- 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, {
10399
10057
  cwd: dir,
10400
10058
  env: scrubLeakyEnv(process.env),
10401
10059
  stdio: ["ignore", "pipe", "pipe"]
10402
- }) : (0, import_node_child_process10.spawn)(direct.bin, direct.args, {
10060
+ }) : (0, import_node_child_process9.spawn)(direct.bin, direct.args, {
10403
10061
  cwd: dir,
10404
10062
  env: buildEnv(job, cfg),
10405
10063
  stdio: ["ignore", "pipe", "pipe"]
10406
10064
  });
10407
10065
  if (child.pid !== void 0 && job.workdirKey) lockWorkdir(dir, child.pid, "session", job.workdirKey);
10408
- const transcriptRefPtr = { value: path14.join(dir, "transcript.txt") };
10066
+ const transcriptRefPtr = { value: path13.join(dir, "transcript.txt") };
10409
10067
  const transcriptOut = openTranscript(transcriptRefPtr.value);
10410
10068
  const outputCbs = [];
10411
10069
  const telemetryCbs = [];
@@ -10505,17 +10163,17 @@ async function runOneShotText(job, cfg) {
10505
10163
  ...cfg.env !== void 0 ? { extraEnvEarly: cfg.env } : {}
10506
10164
  });
10507
10165
  const direct = shouldBindRuntimePdeathsig() ? pdeathsigWrap(cfg.bin, args) : { bin: cfg.bin, args };
10508
- 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, {
10509
10167
  cwd: dir,
10510
10168
  env: scrubLeakyEnv(process.env),
10511
10169
  stdio: ["ignore", "pipe", "pipe"]
10512
- }) : (0, import_node_child_process10.spawn)(direct.bin, direct.args, {
10170
+ }) : (0, import_node_child_process9.spawn)(direct.bin, direct.args, {
10513
10171
  cwd: dir,
10514
10172
  env: buildEnv(job, cfg),
10515
10173
  stdio: ["ignore", "pipe", "pipe"]
10516
10174
  });
10517
10175
  if (child.pid !== void 0 && job.workdirKey) lockWorkdir(dir, child.pid, "session", job.workdirKey);
10518
- const transcriptRefPtr = { value: path14.join(dir, "transcript.txt") };
10176
+ const transcriptRefPtr = { value: path13.join(dir, "transcript.txt") };
10519
10177
  const transcriptOut = openTranscript(transcriptRefPtr.value);
10520
10178
  const outputCbs = [];
10521
10179
  const exitCbs = [];
@@ -10588,14 +10246,14 @@ async function runOneShotText(job, cfg) {
10588
10246
  }
10589
10247
  });
10590
10248
  }
10591
- var import_node_child_process10, import_node_crypto11, fs12, path14, readline4;
10249
+ var import_node_child_process9, import_node_crypto11, fs11, path13, readline4;
10592
10250
  var init_subprocess = __esm({
10593
10251
  "../adapters/src/_core/subprocess.ts"() {
10594
10252
  "use strict";
10595
- import_node_child_process10 = require("node:child_process");
10253
+ import_node_child_process9 = require("node:child_process");
10596
10254
  import_node_crypto11 = require("node:crypto");
10597
- fs12 = __toESM(require("node:fs"), 1);
10598
- path14 = __toESM(require("node:path"), 1);
10255
+ fs11 = __toESM(require("node:fs"), 1);
10256
+ path13 = __toESM(require("node:path"), 1);
10599
10257
  readline4 = __toESM(require("node:readline"), 1);
10600
10258
  init_claude_code();
10601
10259
  init_container_runtime();
@@ -11469,6 +11127,398 @@ var init_src2 = __esm({
11469
11127
  }
11470
11128
  });
11471
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
+
11472
11522
  // ../server/src/domains/actors/model-pricing.ts
11473
11523
  function lookupInMap(map, prefixKeys, candidates) {
11474
11524
  for (const candidate of candidates) {
@@ -141210,7 +141260,7 @@ ${m2.spec.split("\n").map((l) => ` > ${l}`).join("\n")}`);
141210
141260
  // 队列从 N 变 N+1、随后 conclude 撞 queue not quiescent(本文件「怎么提交」那段注释记的卡死形状)。
141211
141261
  ...args.action === "review" || integrating || queueTaskDue ? [] : [
141212
141262
  ``,
141213
- `### \u4EA4\u4ED8\u65F6\u7684\u975E\u963B\u585E\u4FE1\u606F`,
141263
+ `### \u4EA4\u4ED8\u65F6\u5199\u7ED9\u4E0B\u6E38\u7684\u6CE8\u610F\u4E8B\u9879`,
141214
141264
  `\u4E0B\u9762\u51E0\u9879\u662F\u4EA4\u4ED8\u547D\u4EE4\uFF08\`oasis propose\`\uFF09\u7684\u53C2\u6570\uFF0C\u76F4\u63A5\u52A0\u5728\u90A3\u6761\u547D\u4EE4\u540E\u9762\uFF0C\u548C \`--reason\` \u5199\u5728\u4E00\u8D77\uFF1B\`oasis conclude\` \u53EA\u6536 \`--note\`\uFF0C\u5199\u5728 conclude \u4E0A\u7684\u5176\u5B83\u53C2\u6570\u4E0D\u4F1A\u88AB\u8BB0\u5F55\u3002`,
141215
141265
  `\`--reason\` \u5199\u660E\u76F8\u5BF9\u4E0A\u4E00\u7248\u6539\u4E86\u4EC0\u4E48\u3001\u4E3A\u4EC0\u4E48\u3001\u53EF\u80FD\u5F71\u54CD\u4EC0\u4E48\uFF1B\u9996\u6B21\u7248\u672C\u8BF4\u660E\u672C\u7248\u5B8C\u6210\u601D\u8DEF\u548C\u5185\u5BB9\u3002`,
141216
141266
  `\u53EA\u6709\u5F53\u524D\u7248\u672C\u5DF2\u7ECF\u8FBE\u5230\u4EA4\u4ED8\u6807\u51C6\u65F6\uFF0C\u624D\u53EF\u4EE5\u8BB0\u5F55\uFF08\u6BCF\u9879\u53EF\u4EE5\u5199\u591A\u6761\uFF0C\u7528\u82F1\u6587\u5206\u53F7 \`;\` \u9694\u5F00\uFF1B\u4EE5\u4E0B\u67D0\u4E00\u9879\u5982\u679C\u6CA1\u6709\u5C31\u4E0D\u5199\uFF0C\u540C\u65F6\u8BE5\u53C2\u6570\u4E5F\u4E0D\u5199\uFF09\uFF1A`,
@@ -143252,9 +143302,12 @@ ${supplemental.taskAppend.trim()}
143252
143302
  bundle.files["TASK.md"] += [
143253
143303
  "",
143254
143304
  "## \u6267\u884C\u6062\u590D\uFF08\u4EC5\u5F53\u524D\u8282\u70B9\uFF09",
143255
- "\u672C\u8F6E\u662F\u540C\u4E00\u8282\u70B9\u4E0A\u4E00\u6B21 Attempt \u4E2D\u65AD\u540E\u7684\u7EE7\u7EED\u6267\u884C\u3002\u5F00\u5DE5\u524D\u5148\u8BFB `execution/RESUME.md`\uFF0C",
143256
- "\u6838\u5BF9\u6700\u65B0 Checkpoint\u3001\u5DF2\u5B8C\u6210\u6B65\u9AA4\u3001durable outputs\u3001ExternalEffects \u4E0E\u672A\u51B3\u6B65\u9AA4\uFF1B",
143257
- "\u4ECE\u5C1A\u672A\u5B8C\u6210\u7684\u90E8\u5206\u7EE7\u7EED\uFF0C\u907F\u514D\u91CD\u590D\u5DF2\u7ECF\u786E\u8BA4\u5B8C\u6210\u7684\u6B65\u9AA4\u6216\u5916\u90E8\u526F\u4F5C\u7528\u3002",
143305
+ // 2026-09-24(语菲定稿):进度记录可能比实际慢一步(K-04),换了执行机器时工作目录是新的、
143306
+ // 上一趟写的文件都不在(K-05 第三趟)——两种情况都以工作目录里实际有的文件为准。
143307
+ "\u672C\u8F6E\u662F\u540C\u4E00\u8282\u70B9\u4E0A\u4E00\u6B21\u6267\u884C\u4E2D\u65AD\u540E\u7684\u7EE7\u7EED\u3002\u5F00\u5DE5\u524D\u5148\u8BFB `execution/RESUME.md`\uFF0C\u91CC\u9762\u662F\u4E0A\u4E00\u6B21\u6700\u540E\u8BB0\u4E0B\u7684\u8FDB\u5EA6\uFF1B",
143308
+ "\u518D\u67E5\u770B\u5DE5\u4F5C\u76EE\u5F55\u91CC\u5B9E\u9645\u5DF2\u6709\u7684\u6587\u4EF6\uFF0C\u4EE5\u5B9E\u9645\u60C5\u51B5\u4E3A\u51C6\uFF1A",
143309
+ "- \u8FDB\u5EA6\u91CC\u8BF4\u5DF2\u7ECF\u5199\u597D\u7684\u6587\u4EF6\uFF0C\u5DE5\u4F5C\u76EE\u5F55\u91CC\u8FD8\u5728\u7684\uFF0C\u76F4\u63A5\u63A5\u7740\u7528\uFF1B",
143310
+ "- \u5DE5\u4F5C\u76EE\u5F55\u91CC\u6CA1\u6709\u7684\uFF08\u6BD4\u5982\u8FD9\u6B21\u6362\u4E86\u6267\u884C\u673A\u5668\uFF0C\u5DE5\u4F5C\u76EE\u5F55\u662F\u65B0\u7684\uFF09\uFF0C\u8981\u91CD\u65B0\u5199\u3002",
143258
143311
  "\u8FD9\u4EFD\u6062\u590D\u6750\u6599\u4E0D\u662F\u4E0A\u6E38\u8282\u70B9\u7684\u4E1A\u52A1\u4EA4\u63A5\uFF1B\u4E0A\u6E38\u4EA4\u63A5\u4ECD\u4EE5 `inputs/*/_HANDOFF.md` \u4E3A\u51C6\u3002",
143259
143312
  ""
143260
143313
  ].join("\n");
@@ -213845,6 +213898,16 @@ async function startOasisServer(opts) {
213845
213898
  res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ data: data2 }));
213846
213899
  return;
213847
213900
  }
213901
+ if (viewName === "design-board") {
213902
+ const artifactId = url.searchParams.get("artifact");
213903
+ if (!artifactId) throw new Error("\u8BE5\u89C6\u56FE\u9700\u8981 artifact \u53C2\u6570");
213904
+ 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");
213905
+ const artifact = engine.kernel.model.artifacts.get(artifactId);
213906
+ if (!artifact) throw new Error(`artifact \u4E0D\u5B58\u5728: ${artifactId}`);
213907
+ const board = await resolveDesignBoard({ companyId: currentCompanyId, model: engine.kernel.model, blobs: engine.blobs, artifact });
213908
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ data: { companyId: currentCompanyId, ...board } }));
213909
+ return;
213910
+ }
213848
213911
  const customView = opts.views?.[viewName];
213849
213912
  if (customView) {
213850
213913
  res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ data: await customView() }));
@@ -216277,6 +216340,7 @@ var init_server3 = __esm({
216277
216340
  init_chat_session();
216278
216341
  init_chat_system_message();
216279
216342
  init_sealed_node_receipt();
216343
+ init_board_key();
216280
216344
  init_session_runtime_target();
216281
216345
  init_src();
216282
216346
  init_src();
@@ -249291,8 +249355,10 @@ function executionContinuityDomain(service) {
249291
249355
  router.post("/api/execution-continuity/attempts/:id/checkpoints", async (req) => {
249292
249356
  const attemptId = resolveAttemptWriter(req, req.params.id);
249293
249357
  const b2 = req.body ?? {};
249358
+ if (b2.pendingSteps === void 0) b2.pendingSteps = [];
249359
+ if (b2.recoverability === void 0) b2.recoverability = "partial";
249294
249360
  if (!isStringArray(b2.pendingSteps) || !b2.recoverability || !["full", "partial", "none"].includes(b2.recoverability) || b2.durableRefs !== void 0 && !isRefArray(b2.durableRefs) || b2.completedStepIds !== void 0 && !isStringArray(b2.completedStepIds) || b2.trigger !== void 0 && !AGENT_CHECKPOINT_TRIGGERS.includes(b2.trigger)) {
249295
- throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11 pendingSteps \u6216 recoverability(full|partial|none)");
249361
+ throw new ApiError(400, "BAD_REQUEST", "pendingSteps \u987B\u4E3A\u5B57\u7B26\u4E32\u6570\u7EC4\uFF1Brecoverability \u53EA\u80FD\u662F full|partial|none");
249296
249362
  }
249297
249363
  const checkpoint = await service.checkpoint({
249298
249364
  attemptId,
@@ -249412,6 +249478,9 @@ function mergeContinuityRefs2(primary, extra) {
249412
249478
  }
249413
249479
  return out;
249414
249480
  }
249481
+ function stripRecoverability(text7) {
249482
+ return text7.replace(/\s*recoverability=\w+/g, "");
249483
+ }
249415
249484
  function renderResumePack(pack) {
249416
249485
  const lines = [
249417
249486
  "# ResumePack",
@@ -249425,7 +249494,7 @@ function renderResumePack(pack) {
249425
249494
  lines.push("", "## Previous attempt");
249426
249495
  if (!pack.previousAttempt) lines.push("- This is the first attempt.");
249427
249496
  else {
249428
- lines.push(`- Attempt ${pack.previousAttempt.attemptNo ?? "?"}: ${pack.previousHandoff?.summary || pack.previousAttempt.status}`);
249497
+ lines.push(`- Attempt ${pack.previousAttempt.attemptNo ?? "?"}: ${stripRecoverability(pack.previousHandoff?.summary || pack.previousAttempt.status)}`);
249429
249498
  const cp = pack.latestCheckpoint;
249430
249499
  const recoverySource = pack.recoverySourceAttempt ?? (cp ? pack.previousAttempt : null);
249431
249500
  if (cp && recoverySource && recoverySource.id !== pack.previousAttempt.id) {
@@ -249433,7 +249502,6 @@ function renderResumePack(pack) {
249433
249502
  }
249434
249503
  const done = cp?.completedStepIds ?? [];
249435
249504
  const durable = (cp?.durableRefs.length ? cp.durableRefs : pack.previousHandoff?.changedOutputs) ?? [];
249436
- if (cp) lines.push(`- Recoverability: ${cp.recoverability}`);
249437
249505
  if (done.length > 0) lines.push(`- \u4E0A\u4E00\u8D9F\u5DF2\u5B8C\u6210\u7684\u6B65\u9AA4\uFF1A${done.join("\u3001")}`);
249438
249506
  for (const ref2 of durable) {
249439
249507
  lines.push(` - durable: ${renderRefId(ref2)}${ref2.version ? `@${ref2.version}` : ""}${ref2.label ? ` \u2014 ${ref2.label}` : ""}`);
@@ -249446,20 +249514,6 @@ function renderResumePack(pack) {
249446
249514
  );
249447
249515
  }
249448
249516
  }
249449
- lines.push("", "## External effects");
249450
- const effectSummary = pack.externalEffectSummary;
249451
- if (effectSummary.total === 0) lines.push("- None recorded");
249452
- else {
249453
- const totals = effectSummary.totalByStatus;
249454
- const included = effectSummary.includedByStatus;
249455
- lines.push(
249456
- `- ${effectSummary.total} total; showing all ${included.pending} pending, latest ${included.succeeded} of ${totals.succeeded} succeeded, and latest ${included.failed} of ${totals.failed} retryable failed. Older records remain available through the history query.`
249457
- );
249458
- }
249459
- for (const effect of pack.externalEffects) {
249460
- const action = effect.status === "succeeded" ? "do not repeat" : effect.status === "pending" ? "execution uncertain; reconcile before retry" : "failed; retry allowed";
249461
- lines.push(`- [${effect.status}/${effect.source}] ${effect.kind} \u2192 ${effect.target} (${action}; key ${effect.idempotencyKey})`);
249462
- }
249463
249517
  const snapshot = Buffer.from(JSON.stringify(pack), "utf8").toString("base64url");
249464
249518
  lines.push("", `${RESUME_SNAPSHOT_PREFIX}${snapshot}${RESUME_SNAPSHOT_SUFFIX}`);
249465
249519
  return `${lines.join("\n")}
@@ -249593,7 +249647,7 @@ function renderCoordinatorView(view, taskBackground) {
249593
249647
  );
249594
249648
  if (source.recoveryCheckpoint) {
249595
249649
  lines.push(
249596
- ` - Recovery checkpoint: #${source.recoveryCheckpoint.seq} (${source.recoveryCheckpoint.recoverability}) from Attempt ${source.recoveredFromAttempt.attemptNo ?? "?"}`
249650
+ ` - Recovery checkpoint: #${source.recoveryCheckpoint.seq} from Attempt ${source.recoveredFromAttempt.attemptNo ?? "?"}`
249597
249651
  );
249598
249652
  for (const step of source.recoveryCheckpoint.pendingSteps) lines.push(` - Recovery pending: ${step}`);
249599
249653
  }
@@ -249603,14 +249657,14 @@ function renderCoordinatorView(view, taskBackground) {
249603
249657
  lines.push(` - Recovery Agent handoff (${handoffFrom}): ${source.recoveryHandoff.summary}`);
249604
249658
  } else if (source.recoveryHandoff.producedBy === "system_reconstructed") {
249605
249659
  lines.push(
249606
- ` - Recovery system reconstruction (${handoffFrom}, not an Agent conclusion): ${source.recoveryHandoff.summary}`
249660
+ ` - Recovery system reconstruction (${handoffFrom}, not an Agent conclusion): ${stripRecoverability(source.recoveryHandoff.summary)}`
249607
249661
  );
249608
249662
  }
249609
249663
  }
249610
249664
  for (const risk of source.recoveryHandoff?.risks ?? []) lines.push(` - Recovery risk: ${risk}`);
249611
249665
  }
249612
249666
  if (node2.progress) {
249613
- lines.push(`- Latest checkpoint: ${node2.progress.recoverability} at ${node2.progress.lastCheckpointAt}`);
249667
+ lines.push(`- Latest checkpoint at ${node2.progress.lastCheckpointAt}`);
249614
249668
  for (const step of node2.progress.pendingSteps) lines.push(` - Pending: ${step}`);
249615
249669
  }
249616
249670
  if (node2.handoffProducer === "agent" && node2.summary.trim() && !sameCoordinatorText(node2.summary, node2.conclusion)) {
@@ -277634,6 +277688,7 @@ var init_board_watch = __esm({
277634
277688
  init_src();
277635
277689
  init_src7();
277636
277690
  init_src2();
277691
+ init_board_key();
277637
277692
  RUN_MARKER_PREFIX = "[od-board-watch run:";
277638
277693
  boardWatchRunMarker = (roundKey) => `${RUN_MARKER_PREFIX}${roundKey}]`;
277639
277694
  parseRunMarker = (body2) => {
@@ -277674,6 +277729,12 @@ var init_board_watch = __esm({
277674
277729
  revisionSourceRun = /* @__PURE__ */ new Map();
277675
277730
  /** 因 owner 在做而压着没写的 `<公司>\0<artifactId>`(只为日志只打一次)。 */
277676
277731
  deferredLogged = /* @__PURE__ */ new Set();
277732
+ /** 公司 → (revisionId → board.link 正文):解析画板用。revision id 跨公司可能同名,所以按公司分开。 */
277733
+ boardLinks = /* @__PURE__ */ new Map();
277734
+ /** `<公司>\0<artifactId>` → 上次解析画板失败的原文(同样的错只记一次)。 */
277735
+ boardResolveError = /* @__PURE__ */ new Map();
277736
+ /** 已经报过的串板(`<boardKey>\0<公司,…>`),同一组只报一次。 */
277737
+ sharedBoardLogged = /* @__PURE__ */ new Set();
277677
277738
  async odGet(path47) {
277678
277739
  const fetchImpl = this.deps.fetchImpl ?? fetch;
277679
277740
  try {
@@ -277730,12 +277791,13 @@ var init_board_watch = __esm({
277730
277791
  async tick(now = Date.now()) {
277731
277792
  const { log: log3 } = this.deps;
277732
277793
  const denied = [];
277794
+ const boardUsers = /* @__PURE__ */ new Map();
277733
277795
  const { listCompanies, kernelFor } = this.deps;
277734
277796
  if (listCompanies && kernelFor) {
277735
277797
  for (const companyId of await listCompanies()) {
277736
277798
  try {
277737
277799
  const blobs = this.deps.blobsFor ? await this.deps.blobsFor(companyId) : void 0;
277738
- await this.tickCompany(companyId, await kernelFor(companyId), now, denied, blobs);
277800
+ await this.tickCompany(companyId, await kernelFor(companyId), now, denied, blobs, boardUsers);
277739
277801
  this.companyLastError.delete(companyId);
277740
277802
  } catch (err) {
277741
277803
  const msg = String(err);
@@ -277745,8 +277807,9 @@ var init_board_watch = __esm({
277745
277807
  }
277746
277808
  }
277747
277809
  } else {
277748
- await this.tickCompany("", this.deps.kernel, now, denied, this.deps.blobs);
277810
+ await this.tickCompany("", this.deps.kernel, now, denied, this.deps.blobs, boardUsers);
277749
277811
  }
277812
+ this.logSharedBoards(boardUsers);
277750
277813
  if (denied.length > 0) {
277751
277814
  const codes = [...new Set(denied.map((d) => d.status))].join("/");
277752
277815
  const eg = denied[0];
@@ -277756,8 +277819,8 @@ var init_board_watch = __esm({
277756
277819
  }
277757
277820
  }
277758
277821
  /** 巡一家公司(company 为空串 = 单 kernel 模式);被拒的板记进 denied,由 tick 统一汇总成一行。 */
277759
- async tickCompany(company, kernel, now, denied, blobs) {
277760
- const { boardKeyOf, log: log3 } = this.deps;
277822
+ async tickCompany(company, kernel, now, denied, blobs, boardUsers) {
277823
+ const { log: log3 } = this.deps;
277761
277824
  const graceMs = this.deps.firstLookGraceMs ?? 10 * 6e4;
277762
277825
  const maxPerTick = this.deps.maxLettersPerTick ?? 2;
277763
277826
  const actor = this.deps.actor ?? "actor:system";
@@ -277771,8 +277834,12 @@ var init_board_watch = __esm({
277771
277834
  }
277772
277835
  if (a.type !== "design") continue;
277773
277836
  if (lifecycleOf(model, a.id) !== "active") continue;
277774
- const boardKey = boardKeyOf(a.id);
277775
277837
  const pinKey = `${company}\0${a.id}`;
277838
+ const boardKey = await this.boardOf(company, model, blobs, a, tag);
277839
+ if (boardKey === null) continue;
277840
+ const users = boardUsers.get(boardKey) ?? [];
277841
+ users.push({ company, artifactId: a.id });
277842
+ boardUsers.set(boardKey, users);
277776
277843
  const watchLetters = annotationsOf(model, a.id).filter((x2) => x2.author === actor && x2.body.includes("[od-board-watch "));
277777
277844
  const lettered = new Set(watchLetters.map((x2) => parseRunMarker(x2.body)).filter((s2) => s2 !== null));
277778
277845
  const recordedSince = watchLetters.reduce((mn, x2) => {
@@ -277837,6 +277904,43 @@ var init_board_watch = __esm({
277837
277904
  }
277838
277905
  }
277839
277906
  }
277907
+ /** 这个产物实际用的画板(与 CLI 同一个解析函数);解析不了(没有公司、装箱单读不到)→ 记一行日志、本轮跳过它。 */
277908
+ async boardOf(company, model, blobs, a, tag) {
277909
+ const errKey = `${company}\0${a.id}`;
277910
+ let cache = this.boardLinks.get(company);
277911
+ if (!cache) this.boardLinks.set(company, cache = /* @__PURE__ */ new Map());
277912
+ try {
277913
+ const { boardKey } = await resolveDesignBoard(
277914
+ { companyId: company || (this.deps.companyId ?? ""), model, blobs, artifact: a },
277915
+ { deriveKey: this.deps.boardKeyOf, cache }
277916
+ );
277917
+ this.boardResolveError.delete(errKey);
277918
+ return boardKey;
277919
+ } catch (err) {
277920
+ const msg = String(err);
277921
+ if (this.boardResolveError.get(errKey) !== msg) {
277922
+ this.boardResolveError.set(errKey, msg);
277923
+ this.deps.log?.(`[board-watch] ${tag}${a.id} \u89E3\u6790\u4E0D\u51FA\u753B\u677F\uFF0C\u672C\u8F6E\u8DF3\u8FC7\uFF1A${msg}`);
277924
+ }
277925
+ return null;
277926
+ }
277927
+ }
277928
+ /**
277929
+ * 同一块板这一轮同时是不止一家公司的 design 画板 → 记一行(同一组只记一次)。只会出现在旧规则遗留的存量板上:
277930
+ * 两家同名产物都已经交过稿、board.link 都指向同一块只由 artifactId 起名的旧板。不自动处理,交给人看。
277931
+ */
277932
+ logSharedBoards(boardUsers) {
277933
+ for (const [boardKey, users] of boardUsers) {
277934
+ const companies = [...new Set(users.map((u) => u.company))].sort();
277935
+ if (companies.length < 2) continue;
277936
+ const key2 = `${boardKey}\0${companies.join(",")}`;
277937
+ if (this.sharedBoardLogged.has(key2)) continue;
277938
+ this.sharedBoardLogged.add(key2);
277939
+ this.deps.log?.(
277940
+ `[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`
277941
+ );
277942
+ }
277943
+ }
277840
277944
  /**
277841
277945
  * 当前 head 与最近一版装箱单(两者可能不同:head 在跑空壳/被打回时回落)各自 board.link 里的快照来源 runId。
277842
277946
  * 读不到(非装箱单、没有 board.link、blob 缺失、占位 status-pull)的不算。
@@ -278974,6 +279078,7 @@ __export(src_exports2, {
278974
279078
  knowledgeSearchTokenStream: () => knowledgeSearchTokenStream,
278975
279079
  knowledgeSynthesisPrompt: () => knowledgeSynthesisPrompt,
278976
279080
  knowledgeTerms: () => knowledgeTerms,
279081
+ latestBoardLink: () => latestBoardLink,
278977
279082
  letterBackfillDoneScope: () => letterBackfillDoneScope,
278978
279083
  linkChatAttachmentsToWorkOrder: () => linkChatAttachmentsToWorkOrder,
278979
279084
  listActiveCoordinatorAgents: () => listActiveCoordinatorAgents,
@@ -279105,6 +279210,7 @@ __export(src_exports2, {
279105
279210
  resolveChatItemsTurnId: () => resolveChatItemsTurnId,
279106
279211
  resolveCoordinatorProvisionScope: () => resolveCoordinatorProvisionScope,
279107
279212
  resolveDefaultCompanyConfig: () => resolveDefaultCompanyConfig,
279213
+ resolveDesignBoard: () => resolveDesignBoard,
279108
279214
  resolveDispatchScope: () => resolveDispatchScope,
279109
279215
  resolveListenTarget: () => resolveListenTarget,
279110
279216
  resolveManagerBinding: () => resolveManagerBinding,
@@ -279322,6 +279428,7 @@ var init_src11 = __esm({
279322
279428
  init_connector_tool_versions();
279323
279429
  init_agent_manage_guard();
279324
279430
  init_board_watch();
279431
+ init_board_key();
279325
279432
  init_uncategorized_project();
279326
279433
  init_run_settlement();
279327
279434
  init_reconcile_terminal_runs();
@@ -292843,16 +292950,16 @@ var COMMAND_DECLS = {
292843
292950
  },
292844
292951
  "continuity-checkpoint": {
292845
292952
  group: "\u56FE\u673A\u5236\uFF08\xA76/\xA77\uFF09",
292846
- usage: "oasis continuity-checkpoint --pending <list> --recoverability full|partial|none [--trigger plan_completed|stage_completed] [--refs <json-array>] [--completed <list>]",
292953
+ usage: "oasis continuity-checkpoint [--completed <list>] [--pending <list>] [--recoverability full|partial|none] [--trigger plan_completed|stage_completed] [--refs <json-array>]",
292847
292954
  description: "\u4E3A\u5F53\u524D\u6D3E\u53D1 attempt \u8FFD\u52A0\u53EF\u6062\u590D\u68C0\u67E5\u70B9\uFF08\u81EA\u52A8\u4FDD\u7559\u6700\u65B0\u4E94\u4E2A\uFF09\u3002",
292848
292955
  flags: [
292849
- { name: "pending", desc: `\u5F85\u5B8C\u6210\u6B65\u9AA4\u3002JSON \u6570\u7EC4 '["a","b"]' \u6216\u5206\u53F7\u5206\u9694 "a; b" \u90FD\u6536`, required: true },
292850
- { name: "recoverability", desc: "\u6062\u590D\u7A0B\u5EA6\uFF08full | partial | none\uFF09", required: true },
292956
+ { name: "pending", desc: '\u8FD8\u6CA1\u505A\u7684\u6B65\u9AA4\u3002\u5206\u53F7\u5206\u9694 "a; b" \u6216 JSON \u6570\u7EC4\u90FD\u6536\uFF1B\u4E0D\u5199\u6216\u5199\u7A7A = \u6CA1\u6709\u5269\u4F59' },
292957
+ { name: "recoverability", desc: "\u6062\u590D\u7A0B\u5EA6\uFF08full | partial | none\uFF09\uFF0C\u53EF\u9009\uFF1B\u4E0D\u5199\u6309 partial \u8BB0" },
292851
292958
  { name: "refs", desc: "\u5DF2\u6301\u4E45\u5316\u4EA7\u51FA\u5F15\u7528 JSON array" },
292852
- { name: "completed", desc: "\u5DF2\u5B8C\u6210\u7684\u7A33\u5B9A\u6B65\u9AA4 id\u3002JSON \u6570\u7EC4\u6216\u5206\u53F7\u5206\u9694\u90FD\u6536" },
292959
+ { name: "completed", desc: "\u5230\u73B0\u5728\u4E3A\u6B62\u786E\u5B9E\u505A\u5B8C\u7684\u6B65\u9AA4\u3002\u5206\u53F7\u5206\u9694\u6216 JSON \u6570\u7EC4\u90FD\u6536" },
292853
292960
  { name: "trigger", desc: "\u7ED3\u6784\u5316\u8FB9\u754C\uFF08plan_completed | stage_completed\uFF1B\u7701\u7565\u4E3A\u666E\u901A agent checkpoint\uFF09" }
292854
292961
  ],
292855
- examples: [`oasis continuity-checkpoint --pending '["run tests"]' --recoverability full --refs '[{"kind":"file","id":"src/x.ts"}]'`]
292962
+ examples: ['oasis continuity-checkpoint --completed "\u67E5\u5B8C\u5317\u4EAC\u3001\u4E0A\u6D77\u653F\u7B56\uFF0C\u7ED3\u8BBA\u8BB0\u5728 notes.md; \u5199\u597D deliverable/beijing.md" --pending "\u5199 deliverable/shanghai.md; \u5199\u603B\u7ED3.md"']
292856
292963
  },
292857
292964
  "continuity-effect": {
292858
292965
  group: "\u56FE\u673A\u5236\uFF08\xA76/\xA77\uFF09",
@@ -294696,7 +294803,7 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback) {
294696
294803
  }
294697
294804
  }
294698
294805
  }
294699
- var CLI_VERSION = "2.2.85" ? "2.2.85" : "dev";
294806
+ var CLI_VERSION = "2.2.87" ? "2.2.87" : "dev";
294700
294807
  function apiHeaders(token, company) {
294701
294808
  const headers = {
294702
294809
  authorization: `Bearer ${token}`,
@@ -295641,9 +295748,10 @@ Trace: ${run.traceRunId}`);
295641
295748
  break;
295642
295749
  }
295643
295750
  case "continuity-checkpoint": {
295751
+ const pendingRaw = flags.get("pending");
295644
295752
  const result = await api.post("/api/execution-continuity/attempts/current/checkpoints", {
295645
- pendingSteps: parseStringArrayFlag(need(flags, "pending"), "pending"),
295646
- recoverability: need(flags, "recoverability"),
295753
+ pendingSteps: pendingRaw === void 0 || pendingRaw.trim() === "" ? [] : parseStringArrayFlag(pendingRaw, "pending"),
295754
+ ...flags.has("recoverability") ? { recoverability: flags.get("recoverability") } : {},
295647
295755
  durableRefs: flags.has("refs") ? parseArrayFlag(flags.get("refs"), "refs") : [],
295648
295756
  completedStepIds: flags.has("completed") ? parseStringArrayFlag(flags.get("completed"), "completed") : [],
295649
295757
  ...flags.has("trigger") ? { trigger: flags.get("trigger") } : {}
@@ -296435,6 +296543,16 @@ ${res.warning}`);
296435
296543
  }
296436
296544
  case "design": {
296437
296545
  const sub = positional[0];
296546
+ const resolveBoard = async (artifactId) => {
296547
+ let b2;
296548
+ try {
296549
+ b2 = await api.view("design-board", artifactId);
296550
+ } catch (err) {
296551
+ 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`);
296552
+ }
296553
+ 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`);
296554
+ return { boardKey: b2.boardKey, note: b2.source === "board-link" ? "\uFF08\u6CBF\u7528\u5DF2\u4EA4\u7A3F\u8BB0\u5F55\u91CC\u7684\u753B\u677F\uFF09" : "" };
296555
+ };
296438
296556
  if (sub === "chat") {
296439
296557
  const artifactId = needPos(positional, 1, "oasis design chat <artifactId> --message <t>");
296440
296558
  const message = flags.get("message") ?? (["DESIGN_BRIEF.md", "TASK.md"].map((f2) => fs44.existsSync(f2) ? fs44.readFileSync(f2, "utf8") : "").find((s2) => s2.trim()) ?? "");
@@ -296452,24 +296570,26 @@ ${res.warning}`);
296452
296570
  }
296453
296571
  }
296454
296572
  }
296573
+ const board = await resolveBoard(artifactId);
296455
296574
  const baseTurnOpts = {
296456
296575
  base: odBase,
296457
296576
  ...od.token ? { token: od.token } : {},
296458
296577
  agentId: flags.get("agent") ?? "claude",
296459
296578
  artifactId,
296579
+ boardKey: board.boardKey,
296460
296580
  title: flags.get("title") ?? artifactId,
296461
296581
  message,
296462
296582
  ...flags.get("system") !== void 0 ? { systemPrompt: flags.get("system") } : {},
296463
296583
  ...isContinue ? {} : { contextFiles }
296464
296584
  };
296465
296585
  if (flags.get("async") === "true") {
296466
- println(`[design] \u753B\u677F ${deriveBoardKey(artifactId)} \u2190 ${artifactId}\uFF1B${isContinue ? "\u7EED\u8F6E" : "\u9996\u8F6E"}\uFF08--async\uFF09\u2192 od \u5F15\u64CE ${odLabel} \u2026`);
296586
+ 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`);
296467
296587
  const started = await startDesignChatTurn(baseTurnOpts);
296468
296588
  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`);
296469
296589
  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`);
296470
296590
  break;
296471
296591
  }
296472
- println(`[design] \u753B\u677F ${deriveBoardKey(artifactId)} \u2190 ${artifactId}\uFF1B${isContinue ? "\u7EED\u8F6E" : "\u9996\u8F6E"}\u5BF9\u8BDD\u9A71\u52A8 od \u5F15\u64CE ${odLabel} \u2026`);
296592
+ 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`);
296473
296593
  const turn = await runDesignChatTurn({
296474
296594
  ...baseTurnOpts,
296475
296595
  onLine: (line2) => println(`[od] ${line2.length > 500 ? line2.slice(0, 500) + "\u2026" : line2}`)
@@ -296484,13 +296604,14 @@ ${res.warning}`);
296484
296604
  const od = resolveOpenDesignEndpoint(flags.get("od-url"));
296485
296605
  const odBase = od.base;
296486
296606
  const odFetch = withOdToken(fetch, od.token);
296607
+ const { boardKey } = await resolveBoard(artifactId);
296487
296608
  let st;
296488
296609
  let waitedForRun = false;
296489
296610
  if (flags.get("wait") === "true") {
296490
296611
  const timeoutMin = positiveNumberFlag(flags, "wait-timeout", 100);
296491
296612
  const intervalSec = positiveNumberFlag(flags, "wait-interval", 20);
296492
296613
  const fmt = (ms) => `${Math.floor(ms / 6e4)} \u5206 ${Math.floor(ms % 6e4 / 1e3)} \u79D2`;
296493
- const waited = await waitBoardSettled(odBase, odFetch, artifactId, {
296614
+ const waited = await waitBoardSettled(odBase, odFetch, boardKey, {
296494
296615
  timeoutMs: timeoutMin * 6e4,
296495
296616
  intervalMs: intervalSec * 1e3,
296496
296617
  // 每次轮询打一行撑 liveness;状态没变只打短的一行,别刷屏
@@ -296511,7 +296632,7 @@ ${res.warning}`);
296511
296632
  }
296512
296633
  if (waitedForRun) println(`[design] \u753B\u677F\u4E0A\u7684\u8F6E\u6B21\u90FD\u5DF2\u7ED3\u675F\uFF08\u7B49\u4E86 ${fmt(waited.elapsedMs)}\uFF09\u3002`);
296513
296634
  } else {
296514
- st = await fetchBoardStatus(odBase, odFetch, artifactId);
296635
+ st = await fetchBoardStatus(odBase, odFetch, boardKey);
296515
296636
  }
296516
296637
  if (!st.boardExists) {
296517
296638
  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`);
@@ -298652,7 +298773,7 @@ function shimScript() {
298652
298773
  }
298653
298774
 
298654
298775
  // src/index.ts
298655
- var PKG_VERSION = true ? "2.2.85" : "dev";
298776
+ var PKG_VERSION = true ? "2.2.87" : "dev";
298656
298777
  var LOCAL_BIN = localBin();
298657
298778
  var NPM_PREFIX = npmPrefix();
298658
298779
  var INSTANCE = DEFAULT_INSTANCE;