@harness-mix/cli 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/README.md +469 -467
- package/docs/harness-management.md +1 -1
- package/docs/multi-agent-collaboration.md +3 -1
- package/docs/native-acp.md +138 -127
- package/output/native-build/desktop-controller.mjs +1 -1
- package/output/native-build/renderer-extension.js +172 -23
- package/package.json +13 -9
- package/scripts/acp-image-test.cjs +69 -0
- package/scripts/adapters-test.cjs +25 -0
- package/scripts/antigravity-adapter-test.cjs +647 -626
- package/scripts/codex-accounts-test.cjs +34 -0
- package/scripts/codex-adapter-test.cjs +238 -127
- package/scripts/collaboration-test.cjs +577 -262
- package/scripts/collaboration-ui-smoke.cjs +25 -3
- package/scripts/e2e-delegate.cjs +1 -1
- package/scripts/e2e-hermes-image.cjs +60 -0
- package/scripts/jsonl-stdin-test.cjs +40 -31
- package/scripts/kiro-cursor-adapters-test.cjs +124 -100
- package/scripts/native-acp-depth-test.cjs +30 -5
- package/scripts/native-update-apply-test.cjs +269 -215
- package/scripts/native-update.cjs +78 -0
- package/scripts/native-vendor-adapters-test.cjs +196 -154
- package/scripts/openclaw-adapter-test.cjs +121 -3
- package/scripts/openclaw-mcp-probe.cjs +100 -0
- package/scripts/openclaw-mcp-tool-probe.cjs +55 -0
- package/scripts/openclaw-thinking-probe.cjs +73 -0
- package/scripts/salvage-rollout-writes.cjs +72 -0
- package/scripts/storage-verification-test.cjs +11 -0
- package/scripts/zcode-adapter-test.cjs +329 -0
- package/scripts/zcode-live-probe.cjs +66 -0
- package/src/main/adapters/antigravity.js +1428 -1418
- package/src/main/adapters/claude.js +15 -7
- package/src/main/adapters/codex-app-server.js +29 -11
- package/src/main/adapters/codex.js +690 -649
- package/src/main/adapters/native-acp-command.js +51 -48
- package/src/main/adapters/native-acp.js +47 -12
- package/src/main/adapters/omp.js +7 -2
- package/src/main/adapters/openclaw.js +534 -343
- package/src/main/adapters/pi-family.js +35 -8
- package/src/main/adapters/qoder.js +12 -8
- package/src/main/adapters/zcode.js +925 -10
- package/src/main/host/collaboration-tools.js +1 -1
- package/src/main/host/collaboration.js +971 -714
- package/src/main/host/jsonl.js +130 -120
- package/src/main/host/runtime.js +9 -3
- package/src/main/host/verification-gates.js +14 -2
- package/src/main/native/codex-accounts.js +15 -4
- package/src/main/native/config.js +9 -9
- package/src/main/native/launcher.js +252 -237
- package/src/main/native/process-utils.js +157 -57
- package/src/main/native/protocol.js +1227 -1187
- package/src/main/native/update-state.js +123 -110
- package/src/main/native/updater.js +460 -394
- package/src/native-ui/desktop-control/dist/renderer-cdp-control-session.js +3 -1
- package/src/native-ui/desktop-control/dist/renderer-cdp-control-session.js.map +1 -1
- package/src/native-ui/desktop-control/dist/tsconfig.tsbuildinfo +1 -1
- package/src/native-ui/desktop-control/src/renderer-cdp-control-session.ts +358 -358
- package/src/native-ui/renderer-extension/dist/types/renderer-binding-probe.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-collab-cards.d.ts +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-collab-cards.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts +29 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-team-cards.d.ts +4 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-team-cards.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/tsconfig.tsbuildinfo +1 -1
- package/src/native-ui/renderer-extension/src/renderer-binding-probe.ts +3224 -3181
- package/src/native-ui/renderer-extension/src/renderer-collab-cards.ts +25 -0
- package/src/native-ui/renderer-extension/src/renderer-model-client.ts +27 -0
- package/src/native-ui/renderer-extension/src/renderer-team-cards.ts +93 -13
- package/src/native-ui/renderer-extension/src/settings/connections-page.ts +2 -2
- package/src/native-ui/renderer-extension/test/renderer-model-client.test.ts +47 -1
|
@@ -17027,9 +17027,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
17027
17027
|
"claude-code": { command: "npm install -g @anthropic-ai/claude-code" },
|
|
17028
17028
|
"deepseek-harness": { command: "pip install deepseek-harness" },
|
|
17029
17029
|
opencode: { command: "npm install -g opencode-ai" },
|
|
17030
|
-
grok: { command: "
|
|
17030
|
+
grok: { command: "powershell -NoProfile -ExecutionPolicy Bypass -Command iex (irm https://x.ai/cli/install.ps1)" },
|
|
17031
17031
|
omp: { command: "npm install -g @oh-my-pi/pi-coding-agent" },
|
|
17032
|
-
antigravity: { command: "
|
|
17032
|
+
antigravity: { command: "powershell -NoProfile -ExecutionPolicy Bypass -Command iex (irm https://antigravity.google/cli/install.ps1)" },
|
|
17033
17033
|
openclaw: { command: "npm install -g openclaw" },
|
|
17034
17034
|
hermes: { command: "pip install hermes-agent" },
|
|
17035
17035
|
cline: { command: "npm install -g cline" },
|
|
@@ -23131,6 +23131,10 @@ ${error51.stderrTail}`] : []
|
|
|
23131
23131
|
var THREAD_OWNERSHIP_LIST_METHOD = "harnessmix/thread/ownership/list";
|
|
23132
23132
|
var THREAD_USAGE_INSPECT_METHOD = "harnessmix/thread/usage/inspect";
|
|
23133
23133
|
var THREAD_TEAM_INSPECT_METHOD = "harnessmix/thread/team/inspect";
|
|
23134
|
+
var THREAD_TEAM_TASK_CANCEL_METHOD = "harnessmix/thread/team/task/cancel";
|
|
23135
|
+
var THREAD_TEAM_TASK_REASSIGN_METHOD = "harnessmix/thread/team/task/reassign";
|
|
23136
|
+
var THREAD_TEAM_MESSAGE_SEND_METHOD = "harnessmix/thread/team/message/send";
|
|
23137
|
+
var THREAD_COLLABORATION_CONTINUE_METHOD = "harnessmix/thread/collaboration/continue";
|
|
23134
23138
|
var THREAD_USAGE_UPDATED_METHOD = "harnessmix/thread/usage/updated";
|
|
23135
23139
|
var THREAD_TOKEN_USAGE_UPDATED_METHOD = "thread/tokenUsage/updated";
|
|
23136
23140
|
var UPDATE_CHECK_METHOD = "harnessmix/update/check";
|
|
@@ -23361,6 +23365,15 @@ ${error51.stderrTail}`] : []
|
|
|
23361
23365
|
const threadId = hostThreadIdSchema.parse(input.threadId);
|
|
23362
23366
|
return manager.sendRequest(THREAD_TEAM_INSPECT_METHOD, { threadId, ...input.teamId ? { teamId: input.teamId } : {} });
|
|
23363
23367
|
},
|
|
23368
|
+
async collaborationUserAction(input) {
|
|
23369
|
+
const threadId = hostThreadIdSchema.parse(input.threadId);
|
|
23370
|
+
const method = input.action === "task/cancel" ? THREAD_TEAM_TASK_CANCEL_METHOD : input.action === "task/reassign" ? THREAD_TEAM_TASK_REASSIGN_METHOD : input.action === "message/send" ? THREAD_TEAM_MESSAGE_SEND_METHOD : THREAD_COLLABORATION_CONTINUE_METHOD;
|
|
23371
|
+
const params = { threadId };
|
|
23372
|
+
for (const [key, value] of Object.entries(input)) {
|
|
23373
|
+
if (key !== "action" && key !== "threadId" && value !== void 0) params[key] = value;
|
|
23374
|
+
}
|
|
23375
|
+
return manager.sendRequest(method, params);
|
|
23376
|
+
},
|
|
23364
23377
|
subscribeThreadUsage(listener) {
|
|
23365
23378
|
const notifications = notificationTarget(source);
|
|
23366
23379
|
if (!notifications?.addNotificationCallback) {
|
|
@@ -24966,6 +24979,28 @@ ${error51.stderrTail}`] : []
|
|
|
24966
24979
|
const agent = payload.agent_type || "agent";
|
|
24967
24980
|
const icon2 = collaborationIcon(agent, agent, 18);
|
|
24968
24981
|
strip.append(icon2);
|
|
24982
|
+
if (payload.status === "interrupted" && payload.parent_thread_id && options.continueCollab) {
|
|
24983
|
+
const resumeBtn = document.createElement("button");
|
|
24984
|
+
resumeBtn.type = "button";
|
|
24985
|
+
resumeBtn.className = "harness-mix-collab-resume";
|
|
24986
|
+
resumeBtn.style.cssText = "display:inline-flex;align-items:center;gap:4px;padding:4px 10px;border-radius:6px;border:1px solid #c1702266;background:#c170221a;color:#c17022;font:inherit;cursor:pointer;font-size:12px;font-weight:500";
|
|
24987
|
+
resumeBtn.textContent = "\u25B6 \u6062\u590D\u6B64\u4EFB\u52A1";
|
|
24988
|
+
resumeBtn.title = "\u6062\u590D\u4E2D\u65AD\u7684\u59D4\u6D3E\uFF1A\u4E3B\u5BFC\u8005\u4F1A\u6536\u5230\u6062\u590D\u6307\u4EE4\uFF08\u4E0D\u91CD\u653E\u5DF2\u5B8C\u6210\u526F\u4F5C\u7528\uFF09";
|
|
24989
|
+
resumeBtn.addEventListener("click", async (e) => {
|
|
24990
|
+
e.preventDefault();
|
|
24991
|
+
e.stopPropagation();
|
|
24992
|
+
resumeBtn.disabled = true;
|
|
24993
|
+
try {
|
|
24994
|
+
await options.continueCollab(payload.parent_thread_id, payload.task_id);
|
|
24995
|
+
resumeBtn.textContent = "\u2713 \u5DF2\u4E0B\u53D1\u6062\u590D\u6307\u4EE4";
|
|
24996
|
+
} catch (err) {
|
|
24997
|
+
resumeBtn.disabled = false;
|
|
24998
|
+
resumeBtn.textContent = "\u25B6 \u6062\u590D\u5931\u8D25\uFF08\u91CD\u8BD5\uFF09";
|
|
24999
|
+
resumeBtn.title = `\u6062\u590D\u5931\u8D25\uFF1A${err?.message || err}`;
|
|
25000
|
+
}
|
|
25001
|
+
});
|
|
25002
|
+
strip.append(resumeBtn);
|
|
25003
|
+
}
|
|
24969
25004
|
if (payload.child_thread_id) {
|
|
24970
25005
|
const childId = payload.child_thread_id;
|
|
24971
25006
|
const jumpBtn = document.createElement("button");
|
|
@@ -25120,7 +25155,7 @@ ${error51.stderrTail}`] : []
|
|
|
25120
25155
|
}
|
|
25121
25156
|
var stateColor = (status) => ({ ready: "#8b8b8b", pending: "#8b8b8b", working: "#2878e3", in_progress: "#2878e3", active: "#2878e3", completed: "#1f9d68", blocked: "#c17022", failed: "#d14343", interrupted: "#c17022" })[status ?? ""] ?? "#8b8b8b";
|
|
25122
25157
|
var stateLabel = (status) => ({ ready: "\u5C31\u7EEA", pending: "\u5F85\u5F00\u59CB", working: "\u5DE5\u4F5C\u4E2D", in_progress: "\u8FDB\u884C\u4E2D", completed: "\u5DF2\u5B8C\u6210", blocked: "\u7B49\u5F85\u4F9D\u8D56", failed: "\u5931\u8D25", interrupted: "\u5DF2\u4E2D\u65AD", active: "\u534F\u4F5C\u4E2D" })[status ?? ""] ?? status ?? "\u672A\u77E5";
|
|
25123
|
-
var actionLabel = (action) => ({ team_created: "\u56E2\u961F\u5EFA\u7ACB", task_assigned: "\u4EFB\u52A1\u5206\u914D", task_updated: "\u4EFB\u52A1\u66F4\u65B0", message_sent: "\u56E2\u961F\u901A\u4FE1", task_started: "\u5F00\u59CB\u6267\u884C", member_session_ready: "\u4F1A\u8BDD\u5C31\u7EEA", task_settled: "\u4EFB\u52A1\u7ED3\u7B97", task_failed: "\u4EFB\u52A1\u5931\u8D25", task_cancelled: "\u4EFB\u52A1\u53D6\u6D88", task_interrupted: "\u4EFB\u52A1\u4E2D\u65AD", task_resumed: "\u6062\u590D\u6267\u884C", task_followup: "\u7EE7\u7EED\u6267\u884C" })[action ?? ""] ?? action ?? "\u5B9E\u65F6\u72B6\u6001";
|
|
25158
|
+
var actionLabel = (action) => ({ team_created: "\u56E2\u961F\u5EFA\u7ACB", task_assigned: "\u4EFB\u52A1\u5206\u914D", task_updated: "\u4EFB\u52A1\u66F4\u65B0", message_sent: "\u56E2\u961F\u901A\u4FE1", task_started: "\u5F00\u59CB\u6267\u884C", member_session_ready: "\u4F1A\u8BDD\u5C31\u7EEA", task_settled: "\u4EFB\u52A1\u7ED3\u7B97", task_failed: "\u4EFB\u52A1\u5931\u8D25", task_cancelled: "\u4EFB\u52A1\u53D6\u6D88", task_interrupted: "\u4EFB\u52A1\u4E2D\u65AD", task_resumed: "\u6062\u590D\u6267\u884C", task_followup: "\u7EE7\u7EED\u6267\u884C", task_reassigned: "\u4EFB\u52A1\u6539\u6D3E", task_retry: "\u81EA\u52A8\u91CD\u8BD5" })[action ?? ""] ?? action ?? "\u5B9E\u65F6\u72B6\u6001";
|
|
25124
25159
|
var memberPalette = ["#3b82f6", "#f59e0b", "#10b981", "#8b5cf6", "#f97316", "#ec4899", "#14b8a6", "#6366f1"];
|
|
25125
25160
|
var memberColor = (index) => memberPalette[index % memberPalette.length];
|
|
25126
25161
|
function el(tag, className, css) {
|
|
@@ -25182,6 +25217,41 @@ ${error51.stderrTail}`] : []
|
|
|
25182
25217
|
}
|
|
25183
25218
|
});
|
|
25184
25219
|
}
|
|
25220
|
+
function actionButton(label, title, onClick, tone) {
|
|
25221
|
+
const node = el("button", "harness-mix-team-action", `border:1px solid color-mix(in srgb,${tone ?? "currentColor"} 26%,transparent);border-radius:6px;background:color-mix(in srgb,${tone ?? "currentColor"} 7%,transparent);color:${tone ?? "inherit"};font:650 9px system-ui;padding:3px 8px;cursor:pointer;white-space:nowrap${tone ? "" : ";opacity:.85"}`);
|
|
25222
|
+
node.type = "button";
|
|
25223
|
+
node.textContent = label;
|
|
25224
|
+
node.title = title;
|
|
25225
|
+
node.addEventListener("click", (event) => {
|
|
25226
|
+
event.stopPropagation();
|
|
25227
|
+
event.preventDefault();
|
|
25228
|
+
onClick();
|
|
25229
|
+
});
|
|
25230
|
+
return node;
|
|
25231
|
+
}
|
|
25232
|
+
async function runUserAction(userAction, node, input) {
|
|
25233
|
+
node.classList.remove("harness-mix-team-open-failed");
|
|
25234
|
+
try {
|
|
25235
|
+
await userAction(input);
|
|
25236
|
+
} catch (error51) {
|
|
25237
|
+
console.warn("[TeamCards] \u770B\u677F\u64CD\u4F5C\u5931\u8D25:", error51);
|
|
25238
|
+
node.classList.add("harness-mix-team-open-failed");
|
|
25239
|
+
node.title = `\u64CD\u4F5C\u5931\u8D25\uFF1A${error51?.message ?? String(error51)}`;
|
|
25240
|
+
setTimeout(() => node.classList.remove("harness-mix-team-open-failed"), 1800);
|
|
25241
|
+
}
|
|
25242
|
+
}
|
|
25243
|
+
function continueButton(payload, userAction) {
|
|
25244
|
+
const busy = payload.lead?.display_status === "working";
|
|
25245
|
+
const node = actionButton("\u7EE7\u7EED\u534F\u4F5C", busy ? "\u4E3B\u5BFC\u8005\u56DE\u5408\u8FDB\u884C\u4E2D\uFF0C\u7ED3\u675F\u540E\u518D\u7EE7\u7EED" : "\u628A\u4E2D\u65AD\u7684\u59D4\u6D3E\u6062\u590D\u4E3A\u65B0\u7684\u4E3B\u5BFC\u8005\u56DE\u5408", () => {
|
|
25246
|
+
if (payload.lead_thread_id) void runUserAction(userAction, node, { action: "continue", threadId: payload.lead_thread_id });
|
|
25247
|
+
});
|
|
25248
|
+
if (busy) {
|
|
25249
|
+
node.disabled = true;
|
|
25250
|
+
node.style.opacity = ".45";
|
|
25251
|
+
node.style.cursor = "not-allowed";
|
|
25252
|
+
}
|
|
25253
|
+
return node;
|
|
25254
|
+
}
|
|
25185
25255
|
function metrics(payload) {
|
|
25186
25256
|
const node = el("div", "harness-mix-team-metrics", "display:flex;gap:18px;align-items:center");
|
|
25187
25257
|
const values = [[`${payload.tasks.filter((task) => task.status === "completed").length}/${payload.tasks.length}`, "\u5B8C\u6210", "#1f9d68"], [`${payload.tasks.filter((task) => task.status === "in_progress").length}`, "\u8FDB\u884C\u4E2D", "#2878e3"], [`${payload.tasks.filter((task) => task.status === "blocked").length}`, "\u7B49\u5F85", "#c17022"]];
|
|
@@ -25225,8 +25295,8 @@ ${error51.stderrTail}`] : []
|
|
|
25225
25295
|
dot.dataset.status = status ?? "";
|
|
25226
25296
|
return dot;
|
|
25227
25297
|
}
|
|
25228
|
-
var actionIcon = (action) => ({ team_created: "\u{1F3AC}", task_assigned: "\u{1F4CB}", task_updated: "\u{1F504}", task_started: "\u{1F680}", member_session_ready: "\u{1F50C}", task_settled: "\u2705", task_failed: "\u274C", task_cancelled: "\u26D4", task_interrupted: "\u23F8", task_resumed: "\u25B6\uFE0F", task_followup: "\u{1F4AC}" })[action ?? ""] ?? "\u2022";
|
|
25229
|
-
function renderBoard(payload, openThread, snapshots = []) {
|
|
25298
|
+
var actionIcon = (action) => ({ team_created: "\u{1F3AC}", task_assigned: "\u{1F4CB}", task_updated: "\u{1F504}", task_started: "\u{1F680}", member_session_ready: "\u{1F50C}", task_settled: "\u2705", task_failed: "\u274C", task_cancelled: "\u26D4", task_interrupted: "\u23F8", task_resumed: "\u25B6\uFE0F", task_followup: "\u{1F4AC}", task_reassigned: "\u{1F501}", task_retry: "\u267B\uFE0F" })[action ?? ""] ?? "\u2022";
|
|
25299
|
+
function renderBoard(payload, openThread, snapshots = [], userAction) {
|
|
25230
25300
|
const board = el("main", "harness-mix-team-board harness-mix-team-body", "height:100%;min-height:0;color:inherit");
|
|
25231
25301
|
const lead = payload.lead ?? { id: "lead", name: "Team Lead", role: "\u534F\u8C03\u4E0E\u9A8C\u6536", agent: "codex", display_status: "working" };
|
|
25232
25302
|
const tasksOf = (memberId) => payload.tasks.filter((task) => task.assignee === memberId);
|
|
@@ -25249,12 +25319,11 @@ ${error51.stderrTail}`] : []
|
|
|
25249
25319
|
};
|
|
25250
25320
|
const memberCard = (member, index) => {
|
|
25251
25321
|
const color = memberColor(index), assigned = tasksOf(member.id), done = assigned.filter((task) => task.status === "completed").length;
|
|
25252
|
-
const
|
|
25253
|
-
|
|
25254
|
-
|
|
25255
|
-
|
|
25256
|
-
|
|
25257
|
-
wireOpen(button, member.childId ?? member.child_thread_id, member.name, openThread);
|
|
25322
|
+
const card = el("div", "harness-mix-team-member", "position:relative;width:100%;box-sizing:border-box;border:1px solid color-mix(in srgb,currentColor 10%,transparent);background:color-mix(in srgb,Canvas 92%,transparent);box-shadow:0 2px 8px color-mix(in srgb,#000 6%,transparent);color:inherit;font:inherit;display:grid;grid-template-columns:40px minmax(0,1fr);align-items:center;gap:9px;padding:9px 10px;border-radius:12px;text-align:left;cursor:default");
|
|
25323
|
+
card.dataset.agent = member.agent;
|
|
25324
|
+
card.dataset.status = member.display_status ?? "";
|
|
25325
|
+
card.style.setProperty("--lane-color", color ?? "");
|
|
25326
|
+
wireOpen(card, member.childId ?? member.child_thread_id, member.name, openThread);
|
|
25258
25327
|
const avatar = el("span", "harness-mix-team-avatar", `position:relative;display:grid;place-items:center;width:40px;height:40px;flex:none;border-radius:11px;border:2px solid ${color};background:Canvas`);
|
|
25259
25328
|
avatar.dataset.status = member.display_status ?? "";
|
|
25260
25329
|
const dot = el("span", "harness-mix-team-status-dot", `position:absolute;right:-4px;bottom:-4px;width:11px;height:11px;box-sizing:border-box;border-radius:50%;background:${stateColor(member.display_status)};border:2.5px solid Canvas`);
|
|
@@ -25264,6 +25333,7 @@ ${error51.stderrTail}`] : []
|
|
|
25264
25333
|
copy.append(text("strong", member.name, "font-size:12px;font-weight:680;overflow:hidden;text-overflow:ellipsis;white-space:nowrap"));
|
|
25265
25334
|
const status = el("span", void 0, "display:flex;align-items:center;gap:5px;min-width:0");
|
|
25266
25335
|
status.append(text("span", stateLabel(member.display_status), `font-size:9px;color:${stateColor(member.display_status)};white-space:nowrap`), text("span", `${done}/${assigned.length}`, "font-size:9px;font-weight:650;opacity:.5;margin-left:auto"));
|
|
25336
|
+
if (member.unread) status.append(text("span", `\u{1F4AC}${member.unread}`, "font-size:8.5px;font-weight:700;color:#2878e3;background:color-mix(in srgb,#2878e3 12%,transparent);border-radius:99px;padding:1px 6px;flex:none"));
|
|
25267
25337
|
copy.append(status);
|
|
25268
25338
|
const progress = el("progress");
|
|
25269
25339
|
progress.max = Math.max(1, assigned.length);
|
|
@@ -25271,8 +25341,26 @@ ${error51.stderrTail}`] : []
|
|
|
25271
25341
|
progress.style.cssText = `width:100%;height:4px;margin:0;accent-color:${color}`;
|
|
25272
25342
|
progress.title = `${done}/${assigned.length} \u4E2A\u4EFB\u52A1\u5B8C\u6210`;
|
|
25273
25343
|
copy.append(progress);
|
|
25274
|
-
|
|
25275
|
-
|
|
25344
|
+
card.append(avatar, copy);
|
|
25345
|
+
if (userAction && payload.lead_thread_id) {
|
|
25346
|
+
const askRow = el("span", void 0, "position:absolute;top:5px;right:6px;display:flex;align-items:center;gap:4px;max-width:72%");
|
|
25347
|
+
const askButton = actionButton("\u8FFD\u95EE", `\u4EE5\u4E3B\u5BFC\u8005\u8EAB\u4EFD\u5411 ${member.name} \u53D1\u9001\u56E2\u961F\u6D88\u606F`, () => {
|
|
25348
|
+
const input = el("input", void 0, "font:600 10px system-ui;padding:3px 6px;border-radius:6px;border:1px solid color-mix(in srgb,currentColor 22%,transparent);color:inherit;background:Canvas;min-width:96px;flex:1");
|
|
25349
|
+
input.placeholder = `\u5411 ${member.name} \u8BF4\u2026`;
|
|
25350
|
+
const send = actionButton("\u53D1\u9001", "\u53D1\u9001\u56E2\u961F\u6D88\u606F", () => {
|
|
25351
|
+
const message = input.value.trim();
|
|
25352
|
+
if (message) void runUserAction(userAction, send, { action: "message/send", threadId: payload.lead_thread_id, teamId: payload.team_id, to: member.id, message });
|
|
25353
|
+
});
|
|
25354
|
+
input.addEventListener("keydown", (event) => {
|
|
25355
|
+
if (event.key === "Enter") send.click();
|
|
25356
|
+
});
|
|
25357
|
+
askRow.replaceChildren(input, send, actionButton("\xD7", "\u53D6\u6D88", () => askRow.replaceChildren(askButton)));
|
|
25358
|
+
input.focus();
|
|
25359
|
+
});
|
|
25360
|
+
askRow.append(askButton);
|
|
25361
|
+
card.append(askRow);
|
|
25362
|
+
}
|
|
25363
|
+
return card;
|
|
25276
25364
|
};
|
|
25277
25365
|
const laneOf = (member, index) => {
|
|
25278
25366
|
const color = memberColor(index), assigned = tasksOf(member.id), done = assigned.filter((task) => task.status === "completed").length;
|
|
@@ -25302,6 +25390,35 @@ ${error51.stderrTail}`] : []
|
|
|
25302
25390
|
title.title = task.title;
|
|
25303
25391
|
card.append(topRow, title);
|
|
25304
25392
|
if ((task.dependsOn?.length ?? 0) > 0) card.append(text("div", `\u4F9D\u8D56 ${task.dependsOn.length} \u9879\u4EFB\u52A1`, "font-size:8px;opacity:.45"));
|
|
25393
|
+
if (userAction && payload.lead_thread_id) {
|
|
25394
|
+
const assignee = payload.members.find((member2) => member2.id === task.assignee);
|
|
25395
|
+
const row = el("div", void 0, "display:flex;align-items:center;gap:5px;flex-wrap:wrap;min-width:0");
|
|
25396
|
+
const renderDefault = () => {
|
|
25397
|
+
row.replaceChildren();
|
|
25398
|
+
if (task.status === "in_progress") {
|
|
25399
|
+
row.append(actionButton("\u53D6\u6D88", "\u53D6\u6D88\u8BE5\u4EFB\u52A1\u5E76\u505C\u6B62\u6210\u5458\u4F1A\u8BDD", () => {
|
|
25400
|
+
if (window.confirm(`\u53D6\u6D88\u4EFB\u52A1\u300C${task.title}\u300D\uFF1F\u8FD0\u884C\u4E2D\u7684\u6210\u5458\u4F1A\u8BDD\u5C06\u88AB\u505C\u6B62\u3002`)) void runUserAction(userAction, row, { action: "task/cancel", threadId: payload.lead_thread_id, teamId: payload.team_id, taskId: task.id });
|
|
25401
|
+
}, "#d14343"));
|
|
25402
|
+
} else if (task.status === "failed" || task.status === "interrupted") {
|
|
25403
|
+
row.append(
|
|
25404
|
+
actionButton(task.status === "failed" ? "\u91CD\u8BD5" : "\u6062\u590D", `\u628A\u4EFB\u52A1\u91CD\u65B0\u6D3E\u7ED9 ${assignee?.name ?? "\u539F\u6210\u5458"}`, () => {
|
|
25405
|
+
void runUserAction(userAction, row, { action: "task/reassign", threadId: payload.lead_thread_id, teamId: payload.team_id, taskId: task.id, memberId: task.assignee });
|
|
25406
|
+
}, "#c17022"),
|
|
25407
|
+
actionButton("\u6539\u6D3E", "\u6539\u6D3E\u7ED9\u5176\u4ED6\u6210\u5458", () => {
|
|
25408
|
+
const select = el("select", void 0, "font:600 9px system-ui;padding:3px 4px;border-radius:6px;border:1px solid color-mix(in srgb,currentColor 22%,transparent);color:inherit;background:Canvas;max-width:120px;min-width:0");
|
|
25409
|
+
select.append(new Option("\u6539\u6D3E\u7ED9\u2026", ""));
|
|
25410
|
+
for (const candidate of payload.members) if (candidate.id !== task.assignee) select.append(new Option(candidate.name, candidate.id));
|
|
25411
|
+
const go = actionButton("\u786E\u5B9A", "\u6267\u884C\u6539\u6D3E", () => {
|
|
25412
|
+
if (select.value) void runUserAction(userAction, row, { action: "task/reassign", threadId: payload.lead_thread_id, teamId: payload.team_id, taskId: task.id, memberId: select.value });
|
|
25413
|
+
});
|
|
25414
|
+
row.replaceChildren(select, go, actionButton("\xD7", "\u53D6\u6D88\u6539\u6D3E", renderDefault));
|
|
25415
|
+
})
|
|
25416
|
+
);
|
|
25417
|
+
}
|
|
25418
|
+
};
|
|
25419
|
+
renderDefault();
|
|
25420
|
+
if (row.childElementCount) card.append(row);
|
|
25421
|
+
}
|
|
25305
25422
|
list.append(card);
|
|
25306
25423
|
}
|
|
25307
25424
|
if (!assigned.length) list.append(text("div", "\u7B49\u5F85\u4E3B\u5BFC\u8005\u5206\u914D\u4EFB\u52A1", "font-size:9px;opacity:.42;padding:10px 2px;text-align:center"));
|
|
@@ -25350,7 +25467,7 @@ ${error51.stderrTail}`] : []
|
|
|
25350
25467
|
board.append(left, feed);
|
|
25351
25468
|
return board;
|
|
25352
25469
|
}
|
|
25353
|
-
function renderSummary(payload, open, openThread) {
|
|
25470
|
+
function renderSummary(payload, open, openThread, userAction) {
|
|
25354
25471
|
const panel = el("section", "harness-mix-team-panel", 'box-sizing:border-box;border:1px solid color-mix(in srgb,currentColor 12%,transparent);border-radius:14px;background:color-mix(in srgb,Canvas 90%,transparent);box-shadow:0 8px 28px color-mix(in srgb,#000 7%,transparent);backdrop-filter:blur(18px);color:inherit;font:13px/1.4 system-ui,-apple-system,"Segoe UI",sans-serif;padding:12px 14px;display:grid;gap:10px;min-width:0');
|
|
25355
25472
|
panel.dataset.teamId = payload.team_id;
|
|
25356
25473
|
panel.dataset.teamStatus = payload.status;
|
|
@@ -25370,6 +25487,7 @@ ${error51.stderrTail}`] : []
|
|
|
25370
25487
|
button.textContent = "\u5C55\u5F00\u8BE6\u60C5";
|
|
25371
25488
|
button.addEventListener("click", open);
|
|
25372
25489
|
top.append(identity, metrics(payload), button);
|
|
25490
|
+
if (userAction && payload.lead_thread_id && payload.tasks.some((task) => task.status === "interrupted")) top.append(continueButton(payload, userAction));
|
|
25373
25491
|
const members = el("div", "harness-mix-team-summary-members harness-mix-team-scroll", "display:flex;align-items:center;gap:6px;min-width:0;overflow-x:auto;padding-bottom:1px");
|
|
25374
25492
|
payload.members.forEach((member, index) => {
|
|
25375
25493
|
const color = memberColor(index);
|
|
@@ -25386,6 +25504,7 @@ ${error51.stderrTail}`] : []
|
|
|
25386
25504
|
icon2.append(collaborationIcon(member.agent, member.name, 18), dot);
|
|
25387
25505
|
const label = el("span", void 0, "display:grid;min-width:0;line-height:1.2");
|
|
25388
25506
|
label.append(text("strong", member.name, "font-size:10px;font-weight:650;overflow:hidden;text-overflow:ellipsis;white-space:nowrap"), text("span", member.role, "font-size:8.5px;opacity:.52;overflow:hidden;text-overflow:ellipsis;white-space:nowrap"), text("span", stateLabel(member.display_status), `font-size:8.5px;color:${stateColor(member.display_status)}`));
|
|
25507
|
+
if (member.unread) label.append(text("span", `\u{1F4AC} ${member.unread} \u6761\u672A\u8BFB`, "font-size:8.5px;font-weight:700;color:#2878e3"));
|
|
25389
25508
|
chip.append(icon2, label);
|
|
25390
25509
|
members.append(chip);
|
|
25391
25510
|
});
|
|
@@ -25463,7 +25582,8 @@ ${error51.stderrTail}`] : []
|
|
|
25463
25582
|
collapse.textContent = "\u6536\u8D77\u8BE6\u60C5";
|
|
25464
25583
|
collapse.addEventListener("click", close);
|
|
25465
25584
|
timeline.append(event, range, live, play, collapse);
|
|
25466
|
-
|
|
25585
|
+
const headerActions = el("div", void 0, "display:flex;align-items:center;gap:6px;flex:none");
|
|
25586
|
+
header.append(title, statsWrap, progressWrap, timeline, headerActions);
|
|
25467
25587
|
const content = el("div", void 0, "min-height:0");
|
|
25468
25588
|
workbench.append(header, content);
|
|
25469
25589
|
source.after(workbench);
|
|
@@ -25478,13 +25598,14 @@ ${error51.stderrTail}`] : []
|
|
|
25478
25598
|
content.replaceChildren(renderBoard(visible, async (threadId) => {
|
|
25479
25599
|
close();
|
|
25480
25600
|
await options.openThread?.(threadId);
|
|
25481
|
-
}, feedSnapshots));
|
|
25601
|
+
}, feedSnapshots, options.userAction));
|
|
25482
25602
|
teamName.textContent = visible.name;
|
|
25483
25603
|
subtitle.textContent = visible.goal;
|
|
25484
25604
|
subtitle.title = visible.goal;
|
|
25485
25605
|
teamPill.textContent = stateLabel(visible.status);
|
|
25486
25606
|
teamPill.style.cssText = pillCss(visible.status);
|
|
25487
25607
|
statsWrap.replaceChildren(...headerStats(visible));
|
|
25608
|
+
headerActions.replaceChildren(...options.userAction && visible.tasks.some((task) => task.status === "interrupted") ? [continueButton(visible, options.userAction)] : []);
|
|
25488
25609
|
const completion = completionOf(visible);
|
|
25489
25610
|
progressBar.max = Math.max(1, completion.total);
|
|
25490
25611
|
progressBar.value = completion.done;
|
|
@@ -25550,7 +25671,7 @@ ${error51.stderrTail}`] : []
|
|
|
25550
25671
|
if ([...candidate.querySelectorAll(selector)].some((child) => parseTeamPayload(child.textContent ?? ""))) continue;
|
|
25551
25672
|
const signature = `${payload.updated_at ?? 0}:${payload.tasks.length}:${payload.messages.length}:${payload.members.map((member) => member.display_status).join(",")}`;
|
|
25552
25673
|
if (signatures.get(candidate) === signature) continue;
|
|
25553
|
-
const panel = renderSummary(payload, () => open(payload), options.openThread);
|
|
25674
|
+
const panel = renderSummary(payload, () => open(payload), options.openThread, options.userAction);
|
|
25554
25675
|
if (candidate.tagName === "PRE") {
|
|
25555
25676
|
if (!candidate.dataset.harnessMixTeamDisplay) candidate.dataset.harnessMixTeamDisplay = candidate.style.display || "__empty__";
|
|
25556
25677
|
candidate.style.display = "none";
|
|
@@ -25600,7 +25721,7 @@ ${error51.stderrTail}`] : []
|
|
|
25600
25721
|
if (activePanel?.isConnected && activeSignature === signature && activeThreadId === context.threadId) return;
|
|
25601
25722
|
const reopen = workbench?.dataset.teamId === payload.team_id && workbench.dataset.teamSource === "active-thread";
|
|
25602
25723
|
removeActivePanel();
|
|
25603
|
-
activePanel = renderSummary(payload, () => open(payload), options.openThread);
|
|
25724
|
+
activePanel = renderSummary(payload, () => open(payload), options.openThread, options.userAction);
|
|
25604
25725
|
activePanel.classList.add("harness-mix-team-launcher");
|
|
25605
25726
|
activePanel.dataset.teamSource = "active-thread";
|
|
25606
25727
|
activePanel.style.cssText += ";margin:8px 16px 0;flex:none;position:relative;z-index:11";
|
|
@@ -32795,6 +32916,10 @@ ${pet_market_default}`;
|
|
|
32795
32916
|
applyWorkspace: async (threadId, digest) => {
|
|
32796
32917
|
const client = modelClientForHost("local");
|
|
32797
32918
|
return client?.applyThreadWorkspace?.({ threadId, digest }) ?? { patch: "", digest: "" };
|
|
32919
|
+
},
|
|
32920
|
+
continueCollab: async (threadId, taskId) => {
|
|
32921
|
+
const client = modelClientForHost("local");
|
|
32922
|
+
return client?.collaborationUserAction?.({ action: "continue", threadId, ...taskId ? { taskId } : {} });
|
|
32798
32923
|
}
|
|
32799
32924
|
});
|
|
32800
32925
|
const teamCards = installTeamCards({
|
|
@@ -32802,6 +32927,10 @@ ${pet_market_default}`;
|
|
|
32802
32927
|
const client = modelClientForHost("local");
|
|
32803
32928
|
return client?.inspectThreadTeam?.({ threadId, ...teamId ? { teamId } : {} }) ?? null;
|
|
32804
32929
|
},
|
|
32930
|
+
userAction: async (input) => {
|
|
32931
|
+
const client = modelClientForHost("local");
|
|
32932
|
+
return client?.collaborationUserAction?.(input) ?? null;
|
|
32933
|
+
},
|
|
32805
32934
|
openThread: (threadId) => openRendererThread(hostThreadIdSchema.parse(threadId), { hostId: "local" }),
|
|
32806
32935
|
activeThread: () => {
|
|
32807
32936
|
const mounted = [...mountedByComposer.values()].find((candidate) => {
|
|
@@ -32991,14 +33120,15 @@ ${pet_market_default}`;
|
|
|
32991
33120
|
const refreshCommands = async (mounted) => {
|
|
32992
33121
|
const generation = ++mounted.commandRequestGeneration;
|
|
32993
33122
|
const agent = controller.get(mounted.composer).agent;
|
|
32994
|
-
const
|
|
33123
|
+
const threadId = threadIdFromComposerModelTarget(mounted.modelTarget);
|
|
33124
|
+
const hostId = threadId ? mounted.hostId : activeModelHostId();
|
|
32995
33125
|
const requestControl = modelControl;
|
|
32996
33126
|
const client = modelClientForHostFrom(requestControl, hostId);
|
|
32997
33127
|
mounted.control.harnessCommands.setCommands([]);
|
|
32998
33128
|
if (agent === "codex" || !client) return;
|
|
32999
33129
|
try {
|
|
33000
|
-
const catalog = await client.inspectHarnessCommands({ harnessId: externalHarnessIds[agent] });
|
|
33001
|
-
if (disposed || mountedByComposer.get(mounted.composer) !== mounted || mounted.commandRequestGeneration !== generation || requestControl !== modelControl ||
|
|
33130
|
+
const catalog = threadId ? await client.inspectThreadCommands({ threadId }) : await client.inspectHarnessCommands({ harnessId: externalHarnessIds[agent] });
|
|
33131
|
+
if (disposed || mountedByComposer.get(mounted.composer) !== mounted || mounted.commandRequestGeneration !== generation || requestControl !== modelControl || threadIdFromComposerModelTarget(mounted.modelTarget) !== threadId || (threadId ? mounted.hostId : activeModelHostId()) !== hostId || controller.get(mounted.composer).agent !== agent)
|
|
33002
33132
|
return;
|
|
33003
33133
|
mounted.control.harnessCommands.setCommands(
|
|
33004
33134
|
catalog.commands,
|
|
@@ -33358,7 +33488,8 @@ ${pet_market_default}`;
|
|
|
33358
33488
|
}
|
|
33359
33489
|
if (isDraft) mounted.hostId = requestHostId;
|
|
33360
33490
|
const availability = hostHarnessAvailabilityState(requestHostId).availability[agent];
|
|
33361
|
-
|
|
33491
|
+
const lockedOwnership = mounted.ownershipStatus === "ready" && state.phase === "locked";
|
|
33492
|
+
if (availability !== "ready" && !lockedOwnership) {
|
|
33362
33493
|
mounted.modelView = {
|
|
33363
33494
|
status: adapterStatus.state !== "ready" || availability === "checking" ? "waitingForAdapter" : "error",
|
|
33364
33495
|
thinkingSelectionSupported: false,
|
|
@@ -34654,7 +34785,25 @@ ${pet_market_default}`;
|
|
|
34654
34785
|
const selections = connectedComposers().map((mounted) => ({
|
|
34655
34786
|
composerId: mounted.composerId,
|
|
34656
34787
|
agent: controller.get(mounted.composer).agent,
|
|
34657
|
-
phase: controller.get(mounted.composer).phase
|
|
34788
|
+
phase: controller.get(mounted.composer).phase,
|
|
34789
|
+
// Diagnostic surface for live composer debugging: the raw external
|
|
34790
|
+
// model/permission views plus the controller-held selection refs.
|
|
34791
|
+
ownership: mounted.ownershipStatus,
|
|
34792
|
+
modelView: {
|
|
34793
|
+
status: mounted.modelView.status,
|
|
34794
|
+
...mounted.modelView.catalog ? { catalogModels: mounted.modelView.catalog.models.map((m) => m.ref.id.slice(0, 24)) } : {},
|
|
34795
|
+
...mounted.modelView.selected ? { selected: mounted.modelView.selected.id.slice(0, 24) } : {},
|
|
34796
|
+
...mounted.modelView.error ? { error: mounted.modelView.error.slice(0, 160) } : {}
|
|
34797
|
+
},
|
|
34798
|
+
...controller.get(mounted.composer).phase === "locked" && mounted.composerId ? {
|
|
34799
|
+
controllerModel: (() => {
|
|
34800
|
+
const agent = controller.get(mounted.composer).agent;
|
|
34801
|
+
if (agent === "codex") return void 0;
|
|
34802
|
+
const model = controller.modelForAgent(mounted.composer, agent);
|
|
34803
|
+
return model ? model.id.slice(0, 24) : void 0;
|
|
34804
|
+
})()
|
|
34805
|
+
} : {},
|
|
34806
|
+
permissionView: mounted.permissionModeView.status
|
|
34658
34807
|
}));
|
|
34659
34808
|
return {
|
|
34660
34809
|
version: 2,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@harness-mix/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"license": "(Apache-2.0 OR MIT)",
|
|
5
5
|
"description": "Harness Mix native Codex UI bridge for Codex, Pi, Claude Code, DeepSeek Harness, Antigravity, CodeBuddy, Kiro CLI, Cursor CLI and other native coding harnesses.",
|
|
6
6
|
"keywords": [
|
|
@@ -78,6 +78,7 @@
|
|
|
78
78
|
"test:codebuddy-migration": "node scripts/codebuddy-migration-test.cjs",
|
|
79
79
|
"e2e:native-acp": "node scripts/e2e-native-acp.cjs --live",
|
|
80
80
|
"test:native-acp-depth": "node scripts/native-acp-depth-test.cjs",
|
|
81
|
+
"test:acp-image": "node scripts/acp-image-test.cjs",
|
|
81
82
|
"test:kiro-cursor": "node scripts/kiro-cursor-adapters-test.cjs",
|
|
82
83
|
"test:docs": "node scripts/docs-command-test.cjs",
|
|
83
84
|
"typecheck:native-ui": "tsc -b src/native-ui/shared-contracts/tsconfig.json src/native-ui/desktop-control/tsconfig.json src/native-ui/renderer-extension/tsconfig.json --pretty false",
|
|
@@ -106,6 +107,7 @@
|
|
|
106
107
|
"e2e:native:pi": "node scripts/e2e-native-host.cjs --live --harness=pi",
|
|
107
108
|
"e2e:native:dsh": "node scripts/e2e-native-host.cjs --live --harness=deepseek-harness",
|
|
108
109
|
"e2e:native:claude": "node scripts/e2e-native-host.cjs --live --harness=claude-code",
|
|
110
|
+
"e2e:zcode": "node scripts/zcode-live-probe.cjs",
|
|
109
111
|
"start:codex": "node scripts/launch-codex.cjs",
|
|
110
112
|
"check": "node scripts/check.cjs",
|
|
111
113
|
"test:contracts": "node scripts/contracts-test.cjs",
|
|
@@ -123,7 +125,7 @@
|
|
|
123
125
|
"test:core-concurrency": "node scripts/core-concurrency-test.cjs",
|
|
124
126
|
"test:thread-title": "node scripts/thread-title-test.cjs",
|
|
125
127
|
"test:handoff": "node scripts/handoff-checkpoint-test.cjs && node scripts/switch-harness-test.cjs",
|
|
126
|
-
"test:core-all": "node scripts/contracts-test.cjs && node scripts/projector-test.cjs && node scripts/turn-manager-test.cjs && node scripts/sequence-test.cjs && node scripts/shadow-test.cjs && node scripts/architecture-test.cjs && node scripts/adapters-test.cjs && node scripts/dsh-adapter-test.cjs && node scripts/codex-adapter-test.cjs && node scripts/codex-accounts-test.cjs && node scripts/antigravity-adapter-test.cjs && node scripts/openclaw-adapter-test.cjs && node scripts/core-replay-test.cjs && node scripts/determinism-test.cjs && node scripts/core-runtime-test.cjs && node scripts/core-services-test.cjs && node scripts/native-file-change-test.cjs && node scripts/native-updater-test.cjs && node scripts/native-update-apply-test.cjs && node scripts/native-vendor-adapters-test.cjs && node scripts/core-concurrency-test.cjs && node scripts/concurrency-layers-test.cjs && node scripts/tool-concurrency-regression-test.cjs && node scripts/handoff-checkpoint-test.cjs && node scripts/switch-harness-test.cjs && node scripts/native-acp-depth-test.cjs && node scripts/thread-title-test.cjs && node scripts/native-job-object-test.cjs && node scripts/integrations-test.cjs && node scripts/storage-verification-test.cjs && node scripts/stuck-turn-test.cjs && node scripts/pi-cancel-race-test.cjs && node scripts/send-cancel-race-test.cjs && node scripts/delegation-await-test.cjs && node scripts/core-review-test.cjs && node scripts/jsonl-stdin-test.cjs && node scripts/send-pre-turn-cancel-test.cjs",
|
|
128
|
+
"test:core-all": "node scripts/contracts-test.cjs && node scripts/projector-test.cjs && node scripts/turn-manager-test.cjs && node scripts/sequence-test.cjs && node scripts/shadow-test.cjs && node scripts/architecture-test.cjs && node scripts/adapters-test.cjs && node scripts/dsh-adapter-test.cjs && node scripts/codex-adapter-test.cjs && node scripts/codex-accounts-test.cjs && node scripts/antigravity-adapter-test.cjs && node scripts/openclaw-adapter-test.cjs && node scripts/acp-image-test.cjs && node scripts/core-replay-test.cjs && node scripts/determinism-test.cjs && node scripts/core-runtime-test.cjs && node scripts/core-services-test.cjs && node scripts/native-file-change-test.cjs && node scripts/native-updater-test.cjs && node scripts/native-update-apply-test.cjs && node scripts/native-vendor-adapters-test.cjs && node scripts/core-concurrency-test.cjs && node scripts/concurrency-layers-test.cjs && node scripts/tool-concurrency-regression-test.cjs && node scripts/handoff-checkpoint-test.cjs && node scripts/switch-harness-test.cjs && node scripts/native-acp-depth-test.cjs && node scripts/thread-title-test.cjs && node scripts/native-job-object-test.cjs && node scripts/integrations-test.cjs && node scripts/storage-verification-test.cjs && node scripts/stuck-turn-test.cjs && node scripts/pi-cancel-race-test.cjs && node scripts/send-cancel-race-test.cjs && node scripts/delegation-await-test.cjs && node scripts/core-review-test.cjs && node scripts/zcode-adapter-test.cjs && node scripts/jsonl-stdin-test.cjs && node scripts/send-pre-turn-cancel-test.cjs && node scripts/collaboration-test.cjs && node scripts/collaboration-recovery-test.cjs",
|
|
127
129
|
"test:codex-accounts": "node scripts/codex-accounts-test.cjs",
|
|
128
130
|
"smoke:codex-accounts-ui": "electron scripts/codex-accounts-ui-smoke.cjs",
|
|
129
131
|
"test:transcript": "node scripts/transcript-test.cjs",
|
|
@@ -136,6 +138,7 @@
|
|
|
136
138
|
"e2e:dsh": "node scripts/e2e-dsh.cjs",
|
|
137
139
|
"e2e:openclaw": "node scripts/e2e-openclaw.cjs",
|
|
138
140
|
"e2e:openclaw:image": "node scripts/e2e-openclaw-image.cjs",
|
|
141
|
+
"e2e:hermes:image": "node scripts/e2e-hermes-image.cjs",
|
|
139
142
|
"e2e:codex": "node scripts/e2e-codex.cjs",
|
|
140
143
|
"e2e:delegate": "node scripts/e2e-delegate.cjs",
|
|
141
144
|
"test:determinism": "node scripts/determinism-test.cjs",
|
|
@@ -171,7 +174,8 @@
|
|
|
171
174
|
"test:delegation-await": "node scripts/delegation-await-test.cjs",
|
|
172
175
|
"test:core-review": "node scripts/core-review-test.cjs",
|
|
173
176
|
"test:jsonl-stdin": "node scripts/jsonl-stdin-test.cjs",
|
|
174
|
-
"test:send-pre-turn-cancel": "node scripts/send-pre-turn-cancel-test.cjs"
|
|
177
|
+
"test:send-pre-turn-cancel": "node scripts/send-pre-turn-cancel-test.cjs",
|
|
178
|
+
"test:zcode-adapter": "node scripts/zcode-adapter-test.cjs"
|
|
175
179
|
},
|
|
176
180
|
"devDependencies": {
|
|
177
181
|
"@types/node": "24.13.3",
|
|
@@ -187,11 +191,11 @@
|
|
|
187
191
|
"zod": "4.4.3"
|
|
188
192
|
},
|
|
189
193
|
"optionalDependencies": {
|
|
190
|
-
"@harness-mix/native-win32-x64": "0.2.
|
|
191
|
-
"@harness-mix/native-win32-arm64": "0.2.
|
|
192
|
-
"@harness-mix/native-darwin-x64": "0.2.
|
|
193
|
-
"@harness-mix/native-darwin-arm64": "0.2.
|
|
194
|
-
"@harness-mix/native-linux-x64": "0.2.
|
|
195
|
-
"@harness-mix/native-linux-arm64": "0.2.
|
|
194
|
+
"@harness-mix/native-win32-x64": "0.2.4",
|
|
195
|
+
"@harness-mix/native-win32-arm64": "0.2.4",
|
|
196
|
+
"@harness-mix/native-darwin-x64": "0.2.4",
|
|
197
|
+
"@harness-mix/native-darwin-arm64": "0.2.4",
|
|
198
|
+
"@harness-mix/native-linux-x64": "0.2.4",
|
|
199
|
+
"@harness-mix/native-linux-arm64": "0.2.4"
|
|
196
200
|
}
|
|
197
201
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const assert = require('node:assert/strict');
|
|
2
|
+
const { acpAdapter } = require('../src/main/adapters/acp');
|
|
3
|
+
const { randomUUID } = require('node:crypto');
|
|
4
|
+
|
|
5
|
+
// acpAdapter(Hermes 所在 ACP 家族工厂)图片附件路径单元测试:
|
|
6
|
+
// initialize 实测声明 image 支持 → session/prompt 携带 {type:'image',data,mimeType} 块;
|
|
7
|
+
// 声明不支持 → send 直接拒绝("原生 ACP 不支持图片"),不静默丢图。
|
|
8
|
+
// fixture 子进程模式仿 native-acp-depth-test.cjs。
|
|
9
|
+
|
|
10
|
+
if (process.argv.includes('--fixture')) {
|
|
11
|
+
const write = value => process.stdout.write(JSON.stringify({ jsonrpc: '2.0', ...value }) + '\n');
|
|
12
|
+
const noImages = process.argv.includes('--no-images');
|
|
13
|
+
let sid, sawImageBlock = null;
|
|
14
|
+
require('node:readline').createInterface({ input: process.stdin }).on('line', line => {
|
|
15
|
+
const r = JSON.parse(line);
|
|
16
|
+
let result = {};
|
|
17
|
+
if (r.method === 'initialize') result = { agentCapabilities: { loadSession: true, promptCapabilities: { image: !noImages } } };
|
|
18
|
+
if (r.method === 'session/new' || r.method === 'session/load') {
|
|
19
|
+
sid = randomUUID();
|
|
20
|
+
result = { sessionId: sid, configOptions: [{ id: 'model', currentValue: 'native-model', options: [{ value: 'native-model', name: 'Native' }] }] };
|
|
21
|
+
}
|
|
22
|
+
if (r.method === 'session/prompt') {
|
|
23
|
+
if (r.params.prompt[0]?.text === 'image') {
|
|
24
|
+
sawImageBlock = r.params.prompt[1];
|
|
25
|
+
// 形状断言在 fixture 内完成:不匹配直接以错误回包,让适配器侧 reject
|
|
26
|
+
const ok = JSON.stringify(sawImageBlock) === JSON.stringify({ type: 'image', data: 'AA==', mimeType: 'image/png' });
|
|
27
|
+
if (!ok) { write({ id: r.id, error: { message: `image block shape mismatch: ${JSON.stringify(sawImageBlock)}` } }); return; }
|
|
28
|
+
}
|
|
29
|
+
result = { stopReason: 'end_turn', userMessageId: 'fixture-user' };
|
|
30
|
+
}
|
|
31
|
+
if (r.id !== undefined) write({ id: r.id, result });
|
|
32
|
+
if (r.method === 'session/prompt' && sawImageBlock) process.stderr.write(`IMAGE_BLOCK_OK:${JSON.stringify(sawImageBlock)}\n`);
|
|
33
|
+
});
|
|
34
|
+
} else {
|
|
35
|
+
(async () => {
|
|
36
|
+
const build = () => acpAdapter({
|
|
37
|
+
id: 'hermes', name: 'Hermes', bin: 'hermes-fixture', executable: true, args: ['acp'],
|
|
38
|
+
images: true, fork: true, thinking: false, permissions: false, questions: false, compaction: false, usage: false, contextUsage: false,
|
|
39
|
+
resolveCommand: () => ({ command: process.execPath, args: [__filename, '--fixture'] }),
|
|
40
|
+
}).create();
|
|
41
|
+
|
|
42
|
+
// 1. 握手声明 image 支持 → 图片块按 ACP 形状进入 session/prompt
|
|
43
|
+
const adapter = build();
|
|
44
|
+
const session = await adapter.open({ thread: { cwd: process.cwd() }, emit: () => {} });
|
|
45
|
+
try {
|
|
46
|
+
assert.equal(session.state.agentCapabilities?.promptCapabilities?.image, true, '握手能力必须被采集');
|
|
47
|
+
await adapter.send(session, 'image', { emit: () => {} }, { images: [{ data: 'AA==', mime: 'image/png' }] });
|
|
48
|
+
} finally { await adapter.close(session); }
|
|
49
|
+
|
|
50
|
+
// 2. 纯文本回合不受影响
|
|
51
|
+
const textAdapter = build();
|
|
52
|
+
const textSession = await textAdapter.open({ thread: { cwd: process.cwd() }, emit: () => {} });
|
|
53
|
+
try { await textAdapter.send(textSession, 'plain', { emit: () => {} }); }
|
|
54
|
+
finally { await textAdapter.close(textSession); }
|
|
55
|
+
|
|
56
|
+
// 3. 旧版上游(initialize 不声明 image)→ send 拒绝而不是静默丢图
|
|
57
|
+
const old = acpAdapter({
|
|
58
|
+
id: 'hermes', name: 'Hermes', bin: 'hermes-fixture', executable: true, args: ['acp'],
|
|
59
|
+
images: true, fork: true, thinking: false, permissions: false, questions: false, compaction: false, usage: false, contextUsage: false,
|
|
60
|
+
resolveCommand: () => ({ command: process.execPath, args: [__filename, '--fixture', '--no-images'] }),
|
|
61
|
+
}).create();
|
|
62
|
+
const oldSession = await old.open({ thread: { cwd: process.cwd() }, emit: () => {} });
|
|
63
|
+
try {
|
|
64
|
+
await assert.rejects(old.send(oldSession, 'image', { emit: () => {} }, { images: [{ data: 'AA==', mime: 'image/png' }] }), /Hermes 原生 ACP 不支持图片/);
|
|
65
|
+
} finally { await old.close(oldSession); }
|
|
66
|
+
|
|
67
|
+
console.log('acp image: prompt block shape, handshake gating and loud rejection PASS');
|
|
68
|
+
})().catch(error => { console.error(error); process.exitCode = 1; });
|
|
69
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// PR 5 验收:对注册表中全部 Adapter 执行同一组契约测试(无 Electron、不启动原生进程)。
|
|
2
|
+
const assert = require('node:assert/strict');
|
|
2
3
|
const { buildAdapters } = require('../src/main/adapters');
|
|
3
4
|
const { runAdapterContractTests } = require('../src/main/harness-adapter/contract-test');
|
|
4
5
|
const { normalizeCapabilities } = require('../src/main/harness-adapter/manifest');
|
|
@@ -22,4 +23,28 @@ for (const adapter of adapters) {
|
|
|
22
23
|
});
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
// Pi 家族权限目录按成员原生面分开(诚实面):Pi=项目信任旗标,OMP=--approval-mode 三档。
|
|
27
|
+
// 实测依据:pi 0.84.2 `pi --help`(--approve/-a、--no-approve/-na);
|
|
28
|
+
// @oh-my-pi/pi-coding-agent 18.1.19 flag-tables.ts/settings-schema.ts(always-ask|write|yolo)。
|
|
29
|
+
{
|
|
30
|
+
const { PI_PERMISSION_MODES, OMP_APPROVAL_MODES, piPermissionLaunchArgs, ompPermissionLaunchArgs } = require('../src/main/adapters/pi-family');
|
|
31
|
+
test('[pi] 权限目录 = 项目信任三档(default/approve/no-approve),不携带 OMP 档', () => {
|
|
32
|
+
assert.deepEqual(PI_PERMISSION_MODES.map((m) => m.id), ['default', 'approve', 'no-approve']);
|
|
33
|
+
assert.deepEqual(piPermissionLaunchArgs('approve'), ['--approve']);
|
|
34
|
+
assert.deepEqual(piPermissionLaunchArgs('no-approve'), ['--no-approve']);
|
|
35
|
+
assert.deepEqual(piPermissionLaunchArgs('yolo'), [], 'Pi 不认 OMP 的档位 id');
|
|
36
|
+
});
|
|
37
|
+
test('[omp] 权限目录 = --approval-mode 三档(always-ask/write/yolo),不携带 Pi 旗标', () => {
|
|
38
|
+
assert.deepEqual(OMP_APPROVAL_MODES.map((m) => m.id), ['always-ask', 'write', 'yolo']);
|
|
39
|
+
for (const mode of ['always-ask', 'write', 'yolo']) assert.deepEqual(ompPermissionLaunchArgs(mode), ['--approval-mode', mode]);
|
|
40
|
+
assert.deepEqual(ompPermissionLaunchArgs('no-approve'), [], 'OMP 无 --no-approve 旗标,不虚构');
|
|
41
|
+
});
|
|
42
|
+
const omp = require('../src/main/adapters/omp');
|
|
43
|
+
const pi = require('../src/main/adapters/pi');
|
|
44
|
+
test('[pi/omp] 工厂注入各自的权限目录(describe 之外也可校验)', () => {
|
|
45
|
+
assert.deepEqual(pi.permissionModes.map((m) => m.id), ['default', 'approve', 'no-approve']);
|
|
46
|
+
assert.deepEqual(omp.permissionModes.map((m) => m.id), ['always-ask', 'write', 'yolo'], 'OMP 不再共用 Pi 的项目信任目录');
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
25
50
|
console.log(`adapters: ${passed} passed${failed ? `, ${failed} failed` : ''} (${adapters.length} adapters)`);
|