clay-server 5.2.0-beta.5 → 5.2.0-beta.6
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/lib/claude-hook-installer.js +1 -267
- package/lib/claude-jsonl-watcher.js +1 -204
- package/lib/clay-skill-install.js +1 -33
- package/lib/cli-sessions.js +1 -300
- package/lib/codex-defaults.js +1 -28
- package/lib/context-usage.js +3 -2
- package/lib/daemon.js +5 -55
- package/lib/default-ai-runtime.js +4 -8
- package/lib/email-mcp-server.js +1 -1
- package/lib/kiro-defaults.js +1 -22
- package/lib/mcp-proxy.js +2 -32
- package/lib/mcp-query-bridge.js +1 -1
- package/lib/mcp-source-adapters.js +1 -91
- package/lib/mcp-source-context.js +1 -42
- package/lib/mcp-source-discovery.js +1 -169
- package/lib/mcp-source-plugins.js +1 -68
- package/lib/mcp-source-restrictions.js +1 -47
- package/lib/mcp-tool-policy.js +1 -26
- package/lib/os-user-diagnostics.js +2 -4
- package/lib/os-users.js +4 -65
- package/lib/project-connection.js +8 -7
- package/lib/project-http.js +4 -4
- package/lib/project-mcp.js +2 -2
- package/lib/project-pair-result-delivery.js +2 -2
- package/lib/project-pair-usage.js +12 -13
- package/lib/project-pending-message-queue.js +3 -1
- package/lib/project-session-history-actions.js +5 -3
- package/lib/project-session-pair.js +1 -1
- package/lib/project-session-runtime-actions.js +2 -1
- package/lib/project-session-spawn.js +2 -3
- package/lib/project-session-vendor.js +2 -1
- package/lib/project-sessions.js +18 -26
- package/lib/project-user-message.js +1 -0
- package/lib/project-vendor-login.js +6 -36
- package/lib/public/app.js +1 -0
- package/lib/public/css/messages.css +6 -2
- package/lib/public/modules/app-favicon.js +11 -2
- package/lib/public/modules/app-header.js +4 -2
- package/lib/public/modules/app-loop-ui.js +2 -0
- package/lib/public/modules/app-messages.js +36 -5
- package/lib/public/modules/app-notifications.js +1 -2
- package/lib/public/modules/app-panels.js +61 -123
- package/lib/public/modules/app-rate-limit.js +7 -27
- package/lib/public/modules/chat-render-runtime.js +3 -0
- package/lib/public/modules/mcp-connection-view.js +1 -1
- package/lib/public/modules/permission-control.js +5 -4
- package/lib/public/modules/scheduler.js +2 -0
- package/lib/public/modules/session-create-form.js +10 -10
- package/lib/public/modules/settings-defaults.js +27 -28
- package/lib/public/modules/tools.js +2 -1
- package/lib/public/modules/tui-grab.js +1 -2
- package/lib/public/modules/vendor-login-browser.js +5 -3
- package/lib/public/modules/vendor-login.js +2 -2
- package/lib/public/modules/vendor-priority.js +15 -1
- package/lib/public/muse-avatar.svg +4 -0
- package/lib/safe-bash-commands.js +3 -45
- package/lib/sdk-bridge.js +127 -395
- package/lib/sdk-foreground-admission.js +16 -0
- package/lib/sdk-mcp-descriptors.js +2 -37
- package/lib/sdk-message-processor.js +50 -17
- package/lib/sdk-quota-snapshot.js +1 -1
- package/lib/sdk-response-recovery.js +1 -155
- package/lib/sdk-worker.js +2 -677
- package/lib/server.js +1 -1
- package/lib/session-fork-history.js +21 -0
- package/lib/session-hygiene.js +3 -35
- package/lib/session-permission-mode.js +4 -3
- package/lib/session-skill-scope.js +11 -0
- package/lib/sessions.js +17 -89
- package/lib/tool-llm.js +1 -1
- package/lib/tui-transcript-index.js +1 -132
- package/lib/vendor-login-browser.js +1 -11
- package/lib/yoke/acp-agent-profiles.js +5 -3
- package/lib/yoke/adapters/antigravity.js +3 -14
- package/lib/yoke/adapters/claude-query-contract.js +9 -0
- package/lib/yoke/adapters/claude.js +7 -42
- package/lib/yoke/adapters/codex-query-contract.js +24 -0
- package/lib/yoke/adapters/codex.js +1 -0
- package/lib/yoke/adapters/kiro.js +3 -1
- package/lib/yoke/adapters/muse-query-contract.js +7 -0
- package/lib/yoke/adapters/muse.js +122 -0
- package/lib/yoke/auth-events.js +17 -0
- package/lib/yoke/claude-hook-installer.js +267 -0
- package/lib/yoke/claude-jsonl-watcher.js +204 -0
- package/lib/yoke/claude-maintenance.js +55 -0
- package/lib/yoke/claude-permission-patterns.js +53 -0
- package/lib/yoke/claude-response-recovery.js +155 -0
- package/lib/yoke/claude-transcript-hygiene.js +34 -0
- package/lib/yoke/clay-skill-install.js +33 -0
- package/lib/yoke/cli-sessions.js +300 -0
- package/lib/yoke/codex-app-server.js +7 -12
- package/lib/yoke/configuration.js +13 -0
- package/lib/yoke/discovery.js +70 -0
- package/lib/yoke/errors.js +8 -0
- package/lib/yoke/host-environment.js +7 -0
- package/lib/yoke/index.js +9 -51
- package/lib/yoke/interface.js +116 -0
- package/lib/yoke/kiro-acp-server.js +5 -5
- package/lib/yoke/legacy-sessions.js +89 -0
- package/lib/yoke/login-protocol.js +25 -0
- package/lib/yoke/maintenance.js +12 -0
- package/lib/yoke/mcp-proxy.js +32 -0
- package/lib/yoke/mcp-source-adapters.js +91 -0
- package/lib/yoke/mcp-source-context.js +42 -0
- package/lib/yoke/mcp-source-discovery.js +169 -0
- package/lib/yoke/mcp-source-plugins.js +68 -0
- package/lib/yoke/mcp-source-restrictions.js +47 -0
- package/lib/yoke/mcp-tool-policy-normalize.js +28 -0
- package/lib/yoke/model-preferences.js +6 -0
- package/lib/yoke/muse-artifacts.js +36 -0
- package/lib/yoke/muse-controls.js +50 -0
- package/lib/yoke/muse-input.js +64 -0
- package/lib/yoke/muse-items.js +157 -0
- package/lib/yoke/muse-policy.js +52 -0
- package/lib/yoke/muse-query.js +428 -0
- package/lib/yoke/muse-runtime.js +211 -0
- package/lib/yoke/muse-services.js +79 -0
- package/lib/yoke/muse-skills.js +79 -0
- package/lib/yoke/muse-turns.js +71 -0
- package/lib/yoke/options/acp.js +2 -0
- package/lib/yoke/options/antigravity.js +2 -0
- package/lib/yoke/options/claude.js +21 -0
- package/lib/yoke/options/codex-defaults.js +29 -0
- package/lib/yoke/options/codex-settings.js +21 -0
- package/lib/yoke/options/codex.js +6 -0
- package/lib/yoke/options/kiro-defaults.js +22 -0
- package/lib/yoke/options/kiro.js +5 -0
- package/lib/yoke/options/muse.js +5 -0
- package/lib/yoke/provider-contract.js +5 -0
- package/lib/yoke/providers/antigravity-executable.js +8 -0
- package/lib/yoke/providers/claude-executable.js +40 -0
- package/lib/yoke/providers/claude-model-descriptions.js +14 -0
- package/lib/yoke/providers/claude-process.js +97 -0
- package/lib/yoke/providers/claude-provision.js +75 -0
- package/lib/yoke/providers/codex-errors.js +8 -0
- package/lib/yoke/providers/muse-executable.js +17 -0
- package/lib/yoke/provisioning.js +6 -0
- package/lib/yoke/query-options.js +10 -0
- package/lib/yoke/response-recovery.js +41 -0
- package/lib/yoke/sdk-mcp-descriptors.js +37 -0
- package/lib/yoke/sdk-server-factory.js +7 -0
- package/lib/yoke/session-history.js +8 -0
- package/lib/yoke/session-tool-transport.js +31 -13
- package/lib/yoke/settings.js +23 -0
- package/lib/yoke/source-locations.js +4 -0
- package/lib/yoke/title-query.js +18 -7
- package/lib/yoke/tui-transcript-index.js +132 -0
- package/lib/yoke/usage.js +48 -0
- package/lib/yoke/user-input.js +13 -0
- package/lib/yoke/vendor-registry.js +81 -1
- package/package.json +3 -2
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
function itemInput(item) {
|
|
2
|
+
if (!item || typeof item.args !== "string") return {};
|
|
3
|
+
try {
|
|
4
|
+
return JSON.parse(item.args);
|
|
5
|
+
} catch (e) {
|
|
6
|
+
return {
|
|
7
|
+
rawArgs: item.args
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function toolName(item) {
|
|
13
|
+
var name = String(item && (item.tool || item.toolName) || "Tool").toLowerCase();
|
|
14
|
+
if (name.indexOf("shell") !== -1 || name.indexOf("command") !== -1) return "Bash";
|
|
15
|
+
if (name.indexOf("read") !== -1) return "Read";
|
|
16
|
+
if (name.indexOf("write") !== -1 || name.indexOf("create") !== -1) return "Write";
|
|
17
|
+
if (name.indexOf("edit") !== -1 || name.indexOf("delete") !== -1) return "Edit";
|
|
18
|
+
if (name.indexOf("search") !== -1 || name.indexOf("grep") !== -1) return "Grep";
|
|
19
|
+
return item && (item.tool || item.toolName) || "Tool";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function createRenderer(state, push) {
|
|
23
|
+
var streamedItems = {};
|
|
24
|
+
var openBlocks = {};
|
|
25
|
+
var notices = {};
|
|
26
|
+
function closeBlocks(turnId) {
|
|
27
|
+
Object.keys(openBlocks).forEach(function(id) {
|
|
28
|
+
if (turnId && openBlocks[id] !== turnId) return;
|
|
29
|
+
push({
|
|
30
|
+
yokeType: "block_stop",
|
|
31
|
+
blockId: id
|
|
32
|
+
});
|
|
33
|
+
delete openBlocks[id];
|
|
34
|
+
});
|
|
35
|
+
if (!turnId) { state.thinkingBlocks = {}; state.textBlocks = {}; state.toolBlocks = {}; }
|
|
36
|
+
}
|
|
37
|
+
function emitNotice(item, id) {
|
|
38
|
+
if (item.status === "inProgress" || item.kind === "userMessage") return;
|
|
39
|
+
var failed = ["failed", "rejected", "timedOut"].includes(item.status)
|
|
40
|
+
|| (item.kind === "compaction" && item.outcome === "failed")
|
|
41
|
+
|| (item.kind === "hookRun" && ["failed", "blocked", "timedOut"].includes(item.runStatus))
|
|
42
|
+
|| (item.kind === "subagent" && item.result && item.result.errorKind);
|
|
43
|
+
var text = "";
|
|
44
|
+
if (failed) {
|
|
45
|
+
var labels = { reminderChild: "A background task", hookRun: "A hook", compaction: "Context maintenance", sideChat: "A side conversation", subagent: "A task", workflow: "A workflow", userShell: "A command", webSearch: "A search" };
|
|
46
|
+
var label = labels[item.kind] || "A Muse operation";
|
|
47
|
+
// Unknown kinds have no established human-facing fields. Do not dump them.
|
|
48
|
+
var detail = labels[item.kind] ? (item.failureReason || item.reason || item.message || "") : "";
|
|
49
|
+
if (item.kind === "subagent" && !detail && item.result) detail = item.result.summary || "";
|
|
50
|
+
text = label + " failed" + (detail ? ": " + detail : ".");
|
|
51
|
+
} else if (item.status === "completed") {
|
|
52
|
+
// Only documented action results belong in chat. Reminder/hook/compaction/
|
|
53
|
+
// side-chat bookkeeping and unknown protocol fallback text stay silent.
|
|
54
|
+
if (item.kind === "subagent" && item.result) text = item.result.text || item.result.summary || "";
|
|
55
|
+
else if (item.kind === "workflow") text = item.finalSummary || item.message || "";
|
|
56
|
+
else if (item.kind === "userShell") text = item.visibleOutput || "";
|
|
57
|
+
else if (item.kind === "webSearch") text = item.fallbackText || "";
|
|
58
|
+
}
|
|
59
|
+
var key = id + (failed ? ":failure" : ":summary");
|
|
60
|
+
if (typeof text !== "string" || !text.trim() || notices[key]) return;
|
|
61
|
+
notices[key] = true;
|
|
62
|
+
push({ yokeType: "informational", level: failed ? "warning" : "info", content: text });
|
|
63
|
+
}
|
|
64
|
+
function emitItem(item) {
|
|
65
|
+
if (!item || state.stopped) return;
|
|
66
|
+
var id = item.itemId || "muse-item";
|
|
67
|
+
if (item.kind === "agentMessage" || item.kind === "reasoning") {
|
|
68
|
+
var previous = state.textBlocks[id] || "";
|
|
69
|
+
var value = item.kind === "reasoning" && Array.isArray(item.summary) ? item.summary.join("\n") : item.text || "";
|
|
70
|
+
var blockId = state.thinkingBlocks[id] || id;
|
|
71
|
+
if (item.kind === "reasoning") state.thinkingBlocks[id] = blockId;
|
|
72
|
+
if (!previous && value) {
|
|
73
|
+
openBlocks[blockId] = item.turnId;
|
|
74
|
+
push({
|
|
75
|
+
yokeType: item.kind === "reasoning" ? "thinking_start" : "text_start",
|
|
76
|
+
blockId: blockId
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
if (value.length > previous.length) push({
|
|
80
|
+
yokeType: item.kind === "reasoning" ? "thinking_delta" : "text_delta",
|
|
81
|
+
blockId: blockId,
|
|
82
|
+
text: value.substring(previous.length)
|
|
83
|
+
});
|
|
84
|
+
if (value.length >= previous.length) state.textBlocks[id] = value;
|
|
85
|
+
if (item.status !== "inProgress" && openBlocks[blockId]) {
|
|
86
|
+
push({
|
|
87
|
+
yokeType: "block_stop",
|
|
88
|
+
blockId: blockId
|
|
89
|
+
});
|
|
90
|
+
delete openBlocks[blockId];
|
|
91
|
+
}
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (item.kind === "toolCall") {
|
|
95
|
+
if (!state.toolBlocks[id]) {
|
|
96
|
+
state.toolBlocks[id] = true;
|
|
97
|
+
push({
|
|
98
|
+
yokeType: "tool_start",
|
|
99
|
+
blockId: id,
|
|
100
|
+
toolId: id,
|
|
101
|
+
toolName: toolName(item)
|
|
102
|
+
});
|
|
103
|
+
push({
|
|
104
|
+
yokeType: "tool_executing",
|
|
105
|
+
blockId: id,
|
|
106
|
+
toolId: id,
|
|
107
|
+
toolName: toolName(item),
|
|
108
|
+
input: itemInput(item)
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
if (item.status && item.status !== "inProgress" && state.toolBlocks[id] !== "completed") {
|
|
112
|
+
state.toolBlocks[id] = "completed";
|
|
113
|
+
push({
|
|
114
|
+
yokeType: "tool_result",
|
|
115
|
+
blockId: id,
|
|
116
|
+
toolId: id,
|
|
117
|
+
content: item.visibleOutput || item.message || item.failureReason || "",
|
|
118
|
+
isError: item.status !== "completed"
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
emitNotice(item, id);
|
|
124
|
+
}
|
|
125
|
+
function renderFrame(frame) {
|
|
126
|
+
var params = frame.params || {};
|
|
127
|
+
var item = params.item;
|
|
128
|
+
var owner = item && item.turnId || streamedItems[params.itemId] && streamedItems[params.itemId].turnId;
|
|
129
|
+
if (!(state.liveTurnIds && state.liveTurnIds.has(owner) || state.activeTurn && owner === state.activeTurn.turnId)) return false;
|
|
130
|
+
if (item) streamedItems[item.itemId] = Object.assign({}, item); else if (frame.method === "item/delta") {
|
|
131
|
+
item = streamedItems[params.itemId];
|
|
132
|
+
if (item) {
|
|
133
|
+
var field = params.field || "text";
|
|
134
|
+
if (field.indexOf("summary.") === 0) {
|
|
135
|
+
var index = Number(field.split(".")[1]);
|
|
136
|
+
item.summary = (item.summary || []).slice();
|
|
137
|
+
item.summary[index] = (item.summary[index] || "") + params.delta;
|
|
138
|
+
} else if (field === "text") item.text = (item.text || "") + params.delta; else if (field === "output") item.visibleOutput = (item.visibleOutput || "") + params.delta;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (item) emitItem(item);
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
emitItem: emitItem,
|
|
146
|
+
renderFrame: renderFrame,
|
|
147
|
+
closeBlocks: closeBlocks,
|
|
148
|
+
hasItem: function(id) {
|
|
149
|
+
return !!streamedItems[id];
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
module.exports = {
|
|
155
|
+
createRenderer: createRenderer,
|
|
156
|
+
itemInput: itemInput
|
|
157
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
var itemInput = require("./muse-items").itemInput;
|
|
2
|
+
|
|
3
|
+
function normalizeMcpServers(servers) {
|
|
4
|
+
var config = {};
|
|
5
|
+
if (servers == null) return config;
|
|
6
|
+
if (!Array.isArray(servers)) throw new Error("Invalid Muse session MCP servers");
|
|
7
|
+
for (var i = 0; i < servers.length; i++) {
|
|
8
|
+
var server = servers[i] || {};
|
|
9
|
+
if (!server.name || !server.command || typeof server.name !== "string" || typeof server.command !== "string" || !Array.isArray(server.args || [])) throw new Error("Invalid Muse session MCP server");
|
|
10
|
+
if (["__proto__", "constructor", "prototype"].includes(server.name) || Object.prototype.hasOwnProperty.call(config, server.name)) throw new Error("Invalid or duplicate Muse MCP server name");
|
|
11
|
+
var env = {};
|
|
12
|
+
var entries = Array.isArray(server.env) ? server.env : [];
|
|
13
|
+
for (var j = 0; j < entries.length; j++) if (entries[j] && entries[j].name) env[entries[j].name] = String(entries[j].value == null ? "" : entries[j].value);
|
|
14
|
+
config[server.name] = {
|
|
15
|
+
transport: "stdio",
|
|
16
|
+
command: server.command,
|
|
17
|
+
args: server.args || [],
|
|
18
|
+
env: env,
|
|
19
|
+
mode: "required"
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
return config;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function chooseApprovalDecision(request, canUseTool, signal) {
|
|
26
|
+
var choices = Array.isArray(request && request.availableChoices) ? request.availableChoices : [];
|
|
27
|
+
if (!choices.length || typeof canUseTool !== "function") return Promise.reject(new Error("Muse approval cannot be decided safely"));
|
|
28
|
+
if (signal && signal.aborted) return Promise.reject(new Error("Muse approval cancelled"));
|
|
29
|
+
return Promise.resolve(canUseTool(request.toolName || "Tool", itemInput({
|
|
30
|
+
args: request.rawArgs
|
|
31
|
+
}), {
|
|
32
|
+
request: request,
|
|
33
|
+
signal: signal
|
|
34
|
+
})).then(function(result) {
|
|
35
|
+
if (signal && signal.aborted) throw new Error("Muse approval cancelled");
|
|
36
|
+
var allowed = result && result.behavior === "allow";
|
|
37
|
+
if (!allowed && !(result && result.behavior === "deny")) throw new Error("Muse approval returned an unsupported decision");
|
|
38
|
+
for (var i = 0; i < choices.length; i++) if (choices[i].scope === "once" && (choices[i].decision === (allowed ? "approved" : "denied") || !allowed && choices[i].decision === "abort")) {
|
|
39
|
+
var decision = {
|
|
40
|
+
choiceId: choices[i].choiceId
|
|
41
|
+
};
|
|
42
|
+
if (choices[i].acceptsFeedback && result.message) decision.feedback = result.message;
|
|
43
|
+
return decision;
|
|
44
|
+
}
|
|
45
|
+
throw new Error("Muse approval has no matching safe choice");
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = {
|
|
50
|
+
normalizeMcpServers: normalizeMcpServers,
|
|
51
|
+
chooseApprovalDecision: chooseApprovalDecision
|
|
52
|
+
};
|
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
var queryAbort = require("./query-abort");
|
|
2
|
+
|
|
3
|
+
var runtimeTools = require("./muse-runtime");
|
|
4
|
+
|
|
5
|
+
var policy = require("./muse-policy");
|
|
6
|
+
|
|
7
|
+
function createMuseQueryHandle(adapter, queryOpts) {
|
|
8
|
+
var state = {
|
|
9
|
+
client: null,
|
|
10
|
+
connection: null,
|
|
11
|
+
session: null,
|
|
12
|
+
sessionId: queryOpts.resumeSessionId || null,
|
|
13
|
+
model: queryOpts.model || null,
|
|
14
|
+
effort: queryOpts.effort || null,
|
|
15
|
+
toolPolicy: "ask",
|
|
16
|
+
messages: [],
|
|
17
|
+
messageSequence: 0,
|
|
18
|
+
active: false,
|
|
19
|
+
ended: false,
|
|
20
|
+
inputEnded: false,
|
|
21
|
+
stopped: false,
|
|
22
|
+
events: [],
|
|
23
|
+
waiter: null,
|
|
24
|
+
textBlocks: {},
|
|
25
|
+
thinkingBlocks: {},
|
|
26
|
+
toolBlocks: {},
|
|
27
|
+
activeTurn: null,
|
|
28
|
+
liveTurnIds: new Set(),
|
|
29
|
+
abortController: queryOpts.abortController || new AbortController(),
|
|
30
|
+
started: false
|
|
31
|
+
};
|
|
32
|
+
var removeAbort = function() {};
|
|
33
|
+
var handle;
|
|
34
|
+
var failure = null;
|
|
35
|
+
var cleanup = null;
|
|
36
|
+
var runtime = null;
|
|
37
|
+
var itemFrames = [];
|
|
38
|
+
var skillDelivery = require("./muse-skills").create(Object.assign({}, queryOpts, { cwd: queryOpts.cwd || adapter.cwd }), function() { return runtime; }, push, assertLive);
|
|
39
|
+
var emitArtifact = require("./muse-artifacts").create(function() { return runtime; }, push);
|
|
40
|
+
var renderer = require("./muse-items").createRenderer(state, push);
|
|
41
|
+
var turns = require("./muse-turns").create({ state: state, renderer: renderer, emitArtifact: emitArtifact,
|
|
42
|
+
runtime: function() { return runtime; }, ready: flushFrames, push: push, outcomeEvent: outcomeEvent, fail: fail, close: function() { return handle.close(); } });
|
|
43
|
+
function push(event) {
|
|
44
|
+
if (state.stopped || state.ended) return;
|
|
45
|
+
if (state.waiter) {
|
|
46
|
+
var resolve = state.waiter;
|
|
47
|
+
state.waiter = null;
|
|
48
|
+
resolve({
|
|
49
|
+
value: event,
|
|
50
|
+
done: false
|
|
51
|
+
});
|
|
52
|
+
} else state.events.push(event);
|
|
53
|
+
}
|
|
54
|
+
function finish() {
|
|
55
|
+
if (state.waiter) {
|
|
56
|
+
var resolve = state.waiter;
|
|
57
|
+
state.waiter = null;
|
|
58
|
+
resolve({
|
|
59
|
+
value: undefined,
|
|
60
|
+
done: true
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function assertLive() {
|
|
65
|
+
if (state.stopped || state.ended || adapter.shuttingDown) throw new Error("Muse Code query was stopped");
|
|
66
|
+
}
|
|
67
|
+
function activateTurn(turn) {
|
|
68
|
+
state.activeTurn = turn;
|
|
69
|
+
}
|
|
70
|
+
function flushFrames() {
|
|
71
|
+
var frames = itemFrames.splice(0);
|
|
72
|
+
for (var i = 0; i < frames.length; i++) renderer.renderFrame(frames[i]);
|
|
73
|
+
}
|
|
74
|
+
function chooseApproval(request) {
|
|
75
|
+
if (state.stopped || state.abortController.signal.aborted) return Promise.reject(new Error("Muse approval cancelled"));
|
|
76
|
+
return runtime.wait(policy.chooseApprovalDecision(request, queryOpts.canUseTool, state.abortController.signal));
|
|
77
|
+
}
|
|
78
|
+
function fail(error) {
|
|
79
|
+
if (state.stopped || failure) return;
|
|
80
|
+
failure = error;
|
|
81
|
+
state.messages = [];
|
|
82
|
+
push(runtimeTools.isAuthError(error) ? {
|
|
83
|
+
yokeType: "auth_required",
|
|
84
|
+
vendor: "muse"
|
|
85
|
+
} : {
|
|
86
|
+
yokeType: "error",
|
|
87
|
+
text: error.message || String(error)
|
|
88
|
+
});
|
|
89
|
+
handle.close().catch(function(closeError) {
|
|
90
|
+
adapter.lastCloseError = closeError;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
async function selectModel(model) {
|
|
94
|
+
if (!model) return null;
|
|
95
|
+
if (!adapter.catalog.some(function(row) {
|
|
96
|
+
return row.value === model;
|
|
97
|
+
})) {
|
|
98
|
+
var result = await runtime.waitRequest(state.connection.request("model/list", {}));
|
|
99
|
+
adapter.catalog = runtimeTools.normalizeCatalog(result);
|
|
100
|
+
}
|
|
101
|
+
assertLive();
|
|
102
|
+
return runtimeTools.modelSelection(adapter.catalog, model);
|
|
103
|
+
}
|
|
104
|
+
function validateEffort(effort) {
|
|
105
|
+
var vocabulary = [ "none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra" ];
|
|
106
|
+
if (!vocabulary.includes(effort)) throw new Error("Unsupported Muse reasoning effort: " + effort);
|
|
107
|
+
var row = adapter.catalog.find(function(model) {
|
|
108
|
+
return model.value === state.model;
|
|
109
|
+
});
|
|
110
|
+
if (row && !row.effortUnknown && !row.supportedEffortLevels.includes(effort)) {
|
|
111
|
+
throw new Error("Muse model does not support reasoning effort: " + effort);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async function ensureSession() {
|
|
115
|
+
assertLive();
|
|
116
|
+
if (state.session) return state.session;
|
|
117
|
+
var sdk = await adapter.loadSdk();
|
|
118
|
+
assertLive();
|
|
119
|
+
runtime = runtimeTools.createRuntime(adapter, sdk, {
|
|
120
|
+
command: adapter.binaryPath,
|
|
121
|
+
args: [ "serve" ],
|
|
122
|
+
cwd: queryOpts.cwd || adapter.cwd,
|
|
123
|
+
userInputDialogs: queryOpts.userInputMode !== "fallback" && typeof queryOpts.onUserInputRequest === "function",
|
|
124
|
+
env: Object.assign({}, process.env, queryOpts.env || {})
|
|
125
|
+
});
|
|
126
|
+
runtime.fail = fail;
|
|
127
|
+
runtime.onUserInput = require("./muse-input").createRouter(runtime, queryOpts, state.abortController.signal);
|
|
128
|
+
runtime.onFrame = function(frame) {
|
|
129
|
+
turns.onFrame(frame);
|
|
130
|
+
if (frame.method.indexOf("item/") === 0) {
|
|
131
|
+
if (!renderer.renderFrame(frame)) itemFrames.push(frame);
|
|
132
|
+
}
|
|
133
|
+
if (frame.method === "session/modelChanged") {
|
|
134
|
+
var previousModel = state.model;
|
|
135
|
+
state.model = frame.params.modelId;
|
|
136
|
+
if (typeof queryOpts.onModelSwitch === "function" && previousModel && previousModel !== state.model) queryOpts.onModelSwitch({ fromModel: previousModel, toModel: state.model, source: frame.params.source });
|
|
137
|
+
}
|
|
138
|
+
if (frame.method === "userInput/settled") runtime.onUserInput.settle(frame.params.userInputId);
|
|
139
|
+
if (frame.method === "userInput/requested") runtime.onUserInput(frame.params).catch(fail);
|
|
140
|
+
if (frame.method === "usage/changed") {
|
|
141
|
+
var quota = require("./muse-services").quotaEvent(frame.params);
|
|
142
|
+
if (quota) push(quota);
|
|
143
|
+
}
|
|
144
|
+
if (frame.method === "session/contextUsage") state.contextUsage = frame.params;
|
|
145
|
+
if (frame.method === "approval/requested" && queryOpts.resumeSessionId && !state.started) {
|
|
146
|
+
state.resumedTurnId = state.resumedTurnId || frame.params.turnId;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
var spawned = await runtime.initialize();
|
|
150
|
+
assertLive();
|
|
151
|
+
state.connection = spawned.connection;
|
|
152
|
+
var museOpts = queryOpts.adapterOptions && queryOpts.adapterOptions.MUSE || {};
|
|
153
|
+
var mcpServers = policy.normalizeMcpServers(museOpts.mcpServers);
|
|
154
|
+
var hasMcp = Object.keys(mcpServers).length > 0;
|
|
155
|
+
var config = hasMcp ? {
|
|
156
|
+
mcpServers: mcpServers
|
|
157
|
+
} : undefined;
|
|
158
|
+
if (hasMcp && !(spawned.initializeResult.grantedCapabilities || []).includes("sessionMcp")) {
|
|
159
|
+
throw new Error("Muse host does not support the required sessionMcp capability");
|
|
160
|
+
}
|
|
161
|
+
var selection = await selectModel(state.model);
|
|
162
|
+
if (state.effort) validateEffort(state.effort);
|
|
163
|
+
var verb = state.sessionId ? "session/resume" : "session/start";
|
|
164
|
+
var params = state.sessionId ? {
|
|
165
|
+
sessionId: state.sessionId,
|
|
166
|
+
config: config,
|
|
167
|
+
history: "inline"
|
|
168
|
+
} : {
|
|
169
|
+
workspaceRoot: queryOpts.cwd || adapter.cwd,
|
|
170
|
+
config: config,
|
|
171
|
+
approvalMode: museOpts.readOnly ? "denyUnmatched" : "onRequest"
|
|
172
|
+
};
|
|
173
|
+
if (selection && verb === "session/start") {
|
|
174
|
+
params.modelId = selection.modelId;
|
|
175
|
+
if (selection.providerId) params.providerId = selection.providerId;
|
|
176
|
+
}
|
|
177
|
+
var result = await runtime.waitRequest(state.connection.command(verb, params));
|
|
178
|
+
assertLive();
|
|
179
|
+
state.sessionId = result.session.sessionId;
|
|
180
|
+
var opening = {
|
|
181
|
+
verb: verb,
|
|
182
|
+
result: result
|
|
183
|
+
};
|
|
184
|
+
state.session = new sdk.Session({
|
|
185
|
+
connection: state.connection,
|
|
186
|
+
durability: sdk.readSessionDurability(spawned.initializeResult),
|
|
187
|
+
opening: opening,
|
|
188
|
+
sessionId: state.sessionId
|
|
189
|
+
});
|
|
190
|
+
runtime.session = state.session;
|
|
191
|
+
state.session.onApproval(chooseApproval);
|
|
192
|
+
state.session.onApprovalError(function(problem) {
|
|
193
|
+
fail(problem.error || new Error("Muse approval failed: " + problem.kind));
|
|
194
|
+
});
|
|
195
|
+
state.session.onGapError(function(problem) {
|
|
196
|
+
fail(problem.error || new Error("Muse view gap could not be filled"));
|
|
197
|
+
});
|
|
198
|
+
runtimeTools.seedHistory(state.session, opening, runtime.onFrame);
|
|
199
|
+
state.resumedTurnId = state.session.fold.activeTurnId;
|
|
200
|
+
state.contextUsage = result.history && result.history.snapshot && result.history.snapshot.state.contextUsage || null;
|
|
201
|
+
runtime.opening = false;
|
|
202
|
+
var held = runtime.held.splice(0);
|
|
203
|
+
for (var i = 0; i < held.length; i++) runtime.apply(held[i]);
|
|
204
|
+
assertLive();
|
|
205
|
+
if (runtime.dead) throw new Error("Muse host exited while opening the session");
|
|
206
|
+
if (verb === "session/resume") {
|
|
207
|
+
await runtime.waitRequest(state.connection.command("session/setApprovalMode", {
|
|
208
|
+
sessionId: state.sessionId,
|
|
209
|
+
mode: museOpts.readOnly ? "denyUnmatched" : "onRequest"
|
|
210
|
+
}));
|
|
211
|
+
if (selection) await runtime.waitRequest(state.connection.command("session/setModel", {
|
|
212
|
+
sessionId: state.sessionId,
|
|
213
|
+
model: selection
|
|
214
|
+
}));
|
|
215
|
+
}
|
|
216
|
+
assertLive();
|
|
217
|
+
if (verb === "session/resume") {
|
|
218
|
+
state.resumedTurnId = state.resumedTurnId || result.session.activeTurnId || state.session.fold.activeTurnId;
|
|
219
|
+
if ((result.pendingRequests || []).length) {
|
|
220
|
+
var pending = await runtime.waitRequest(state.connection.request("approval/listPending", {
|
|
221
|
+
sessionId: state.sessionId
|
|
222
|
+
}));
|
|
223
|
+
for (var promptIndex = 0; promptIndex < (pending.userInputs || []).length; promptIndex++) {
|
|
224
|
+
var promptRequest = pending.userInputs[promptIndex];
|
|
225
|
+
state.resumedTurnId = state.resumedTurnId || promptRequest.turnId;
|
|
226
|
+
runtime.onUserInput(promptRequest).catch(fail);
|
|
227
|
+
}
|
|
228
|
+
for (var pendingIndex = 0; pendingIndex < (pending.approvals || []).length; pendingIndex++) {
|
|
229
|
+
var approval = pending.approvals[pendingIndex];
|
|
230
|
+
state.resumedTurnId = state.resumedTurnId || approval.turnId;
|
|
231
|
+
runtime.apply({
|
|
232
|
+
method: "approval/requested",
|
|
233
|
+
params: approval
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
assertLive();
|
|
239
|
+
push({
|
|
240
|
+
yokeType: "session_started",
|
|
241
|
+
sessionId: state.sessionId
|
|
242
|
+
});
|
|
243
|
+
return state.session;
|
|
244
|
+
}
|
|
245
|
+
function outcomeEvent(outcome) {
|
|
246
|
+
if (!outcome || outcome.kind === "unqueued" || outcome.kind === "completed" && outcome.params && (outcome.params.terminal === "cancelled" || outcome.params.terminal === "cancelledByUser")) return {
|
|
247
|
+
yokeType: "interrupted"
|
|
248
|
+
};
|
|
249
|
+
if (outcome.kind === "terminalUnknown") return {
|
|
250
|
+
yokeType: "error",
|
|
251
|
+
text: "Muse host exited before the turn outcome was known"
|
|
252
|
+
};
|
|
253
|
+
var params = outcome.params || {};
|
|
254
|
+
if (params.terminal === "failed") return params.error && params.error.kind === "authRequired" ? {
|
|
255
|
+
yokeType: "auth_required",
|
|
256
|
+
vendor: "muse"
|
|
257
|
+
} : {
|
|
258
|
+
yokeType: "error",
|
|
259
|
+
text: params.error && params.error.message || params.reason || "Muse Code turn failed."
|
|
260
|
+
};
|
|
261
|
+
if (params.terminal !== "completed") return {
|
|
262
|
+
yokeType: "error",
|
|
263
|
+
text: "Unknown Muse turn outcome: " + params.terminal
|
|
264
|
+
};
|
|
265
|
+
return {
|
|
266
|
+
yokeType: "result",
|
|
267
|
+
messageType: "assistant",
|
|
268
|
+
sessionId: state.sessionId,
|
|
269
|
+
usage: params.usage ? {
|
|
270
|
+
input_tokens: params.usage.inputTokens,
|
|
271
|
+
output_tokens: params.usage.outputTokens,
|
|
272
|
+
cache_read_input_tokens: params.usage.cacheReadTokens == null ? params.usage.cachedTokens : params.usage.cacheReadTokens,
|
|
273
|
+
cache_creation_input_tokens: params.usage.cacheWriteTokens || 0
|
|
274
|
+
} : null,
|
|
275
|
+
cost: null,
|
|
276
|
+
duration: params.durationMs || null
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
async function runMessage(text, images, skill, sequence) {
|
|
280
|
+
state.active = true;
|
|
281
|
+
try {
|
|
282
|
+
var session = await ensureSession();
|
|
283
|
+
assertLive();
|
|
284
|
+
if (state.resumedTurnId) {
|
|
285
|
+
var resumed = session.turn(state.resumedTurnId);
|
|
286
|
+
state.resumedTurnId = null;
|
|
287
|
+
activateTurn(resumed);
|
|
288
|
+
var resumedBoundary = await turns.follow(resumed, false);
|
|
289
|
+
assertLive();
|
|
290
|
+
if (resumedBoundary.kind === "failed" || resumedBoundary.event && resumedBoundary.event.yokeType !== "result") return;
|
|
291
|
+
push({ yokeType: "runtime_specific", vendor: "muse",
|
|
292
|
+
eventType: resumedBoundary.kind === "foreground" ? "resumed_foreground_completed" : "resumed_turn_completed", raw: resumedBoundary.outcome });
|
|
293
|
+
}
|
|
294
|
+
var prepared = await skillDelivery.prepare(text || "", skill);
|
|
295
|
+
assertLive();
|
|
296
|
+
skill = prepared.skill;
|
|
297
|
+
var prompt = prepared.text;
|
|
298
|
+
if (!state.started) {
|
|
299
|
+
state.started = true;
|
|
300
|
+
prompt = [ queryOpts.systemPrompt, queryOpts.appendSystemPrompt, prompt ].filter(function(part) {
|
|
301
|
+
return !!part;
|
|
302
|
+
}).join("\n\n");
|
|
303
|
+
}
|
|
304
|
+
state.activeTurn = null;
|
|
305
|
+
if (prepared.scoped) skillDelivery.check();
|
|
306
|
+
push({ yokeType: "turn_activity", phase: "waiting" });
|
|
307
|
+
var turn = await runtime.wait(session.sendUserTurn({
|
|
308
|
+
input: require("./muse-input").inputParts(prompt, images).concat(skill ? [{ type: "skill", selector: skill.selector, arguments: skill.arguments }] : []),
|
|
309
|
+
reasoningEffort: state.effort || undefined
|
|
310
|
+
}));
|
|
311
|
+
assertLive();
|
|
312
|
+
activateTurn(turn);
|
|
313
|
+
await turns.follow(turn, true, sequence);
|
|
314
|
+
} catch (e) {
|
|
315
|
+
fail(e);
|
|
316
|
+
} finally {
|
|
317
|
+
state.active = false;
|
|
318
|
+
state.activeTurn = null;
|
|
319
|
+
if (state.messages.length && !state.stopped) {
|
|
320
|
+
var next = state.messages.shift();
|
|
321
|
+
runMessage(next.text, next.images, next.skill, next.sequence).catch(function(error) {
|
|
322
|
+
adapter.lastCloseError = error;
|
|
323
|
+
});
|
|
324
|
+
} else if (state.inputEnded && !turns.pending()) await handle.close();
|
|
325
|
+
else if (!state.stopped && turns.background()) push({ yokeType: "foreground_available" });
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
handle = {
|
|
329
|
+
[Symbol.asyncIterator]: function() {
|
|
330
|
+
return {
|
|
331
|
+
next: function() {
|
|
332
|
+
if (state.events.length) return Promise.resolve({
|
|
333
|
+
value: state.events.shift(),
|
|
334
|
+
done: false
|
|
335
|
+
});
|
|
336
|
+
if (state.ended) return Promise.resolve({
|
|
337
|
+
value: undefined,
|
|
338
|
+
done: true
|
|
339
|
+
});
|
|
340
|
+
return new Promise(function(resolve) {
|
|
341
|
+
state.waiter = resolve;
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
},
|
|
346
|
+
pushMessage: function(text, images, skill) {
|
|
347
|
+
if (state.stopped || state.ended || state.inputEnded) return false;
|
|
348
|
+
state.messages.push({
|
|
349
|
+
text: text, sequence: ++state.messageSequence,
|
|
350
|
+
images: images, skill: skill
|
|
351
|
+
});
|
|
352
|
+
if (!state.active) {
|
|
353
|
+
var next = state.messages.shift();
|
|
354
|
+
runMessage(next.text, next.images, next.skill, next.sequence).catch(function(error) {
|
|
355
|
+
adapter.lastCloseError = error;
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
return true;
|
|
359
|
+
},
|
|
360
|
+
// Clay's callback remains the authority even when its UI permits all tools.
|
|
361
|
+
setToolPolicy: async function(value) {
|
|
362
|
+
assertLive();
|
|
363
|
+
if (["ask", "allow-all"].indexOf(value) < 0) throw new Error("Unsupported Muse tool policy: " + value);
|
|
364
|
+
state.toolPolicy = value;
|
|
365
|
+
return { policy: value, nativeApprovalMode: "onRequest", authority: "canUseTool" };
|
|
366
|
+
},
|
|
367
|
+
stopTask: async function(taskId) {
|
|
368
|
+
assertLive();
|
|
369
|
+
if (!taskId) return handle.abort();
|
|
370
|
+
if (!state.connection || !state.session) throw new Error("Muse task stop requires an opened session");
|
|
371
|
+
await runtime.waitRequest(state.connection.command("task/stop", {
|
|
372
|
+
sessionId: state.sessionId,
|
|
373
|
+
taskId: taskId
|
|
374
|
+
}));
|
|
375
|
+
},
|
|
376
|
+
getContextUsage: function() {
|
|
377
|
+
var context = state.contextUsage;
|
|
378
|
+
return Promise.resolve(context ? {
|
|
379
|
+
input_tokens: context.usedTokens,
|
|
380
|
+
contextWindow: context.windowTokens
|
|
381
|
+
} : null);
|
|
382
|
+
},
|
|
383
|
+
endInput: function() {
|
|
384
|
+
state.inputEnded = true;
|
|
385
|
+
if (!state.active && !state.messages.length && !turns.pending()) handle.close().catch(function(error) {
|
|
386
|
+
adapter.lastCloseError = error;
|
|
387
|
+
});
|
|
388
|
+
},
|
|
389
|
+
abort: function() {
|
|
390
|
+
if (state.ended || state.stopped) return cleanup;
|
|
391
|
+
state.events = [];
|
|
392
|
+
var liveTurns = new Set(state.liveTurnIds);
|
|
393
|
+
if (state.activeTurn) liveTurns.add(state.activeTurn.turnId);
|
|
394
|
+
if (state.connection) liveTurns.forEach(function(turnId) {
|
|
395
|
+
state.connection.command("turn/cancel", { sessionId: state.sessionId, turnId: turnId }).catch(function(error) {
|
|
396
|
+
adapter.lastCancelError = error;
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
push({
|
|
400
|
+
yokeType: "interrupted"
|
|
401
|
+
});
|
|
402
|
+
return handle.close();
|
|
403
|
+
},
|
|
404
|
+
close: function() {
|
|
405
|
+
if (cleanup) return cleanup;
|
|
406
|
+
state.stopped = true;
|
|
407
|
+
state.inputEnded = true;
|
|
408
|
+
state.messages = [];
|
|
409
|
+
state.ended = true;
|
|
410
|
+
removeAbort();
|
|
411
|
+
state.abortController.abort();
|
|
412
|
+
finish();
|
|
413
|
+
adapter.handles.delete(handle);
|
|
414
|
+
cleanup = runtime ? runtime.close() : Promise.resolve();
|
|
415
|
+
return cleanup;
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
Object.assign(handle, require("./muse-controls").create({ state: state, assertLive: assertLive, selectModel: selectModel, validateEffort: validateEffort, skills: skillDelivery, runtime: function() { return runtime; }, handle: handle }));
|
|
419
|
+
adapter.handles.add(handle);
|
|
420
|
+
removeAbort = queryAbort.bindAbort(state.abortController, function() {
|
|
421
|
+
handle.abort();
|
|
422
|
+
});
|
|
423
|
+
return handle;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
module.exports = {
|
|
427
|
+
createMuseQueryHandle: createMuseQueryHandle
|
|
428
|
+
};
|