claude-code-rust 0.6.0 → 0.7.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/README.md +5 -5
- package/agent-sdk/dist/bridge/agents.js +75 -0
- package/agent-sdk/dist/bridge/commands.js +42 -11
- package/agent-sdk/dist/bridge/error_classification.js +55 -0
- package/agent-sdk/dist/bridge/events.js +83 -0
- package/agent-sdk/dist/bridge/history.js +0 -17
- package/agent-sdk/dist/bridge/message_handlers.js +428 -0
- package/agent-sdk/dist/bridge/permissions.js +15 -3
- package/agent-sdk/dist/bridge/session_lifecycle.js +368 -0
- package/agent-sdk/dist/bridge/shared.js +49 -0
- package/agent-sdk/dist/bridge/state_parsing.js +66 -0
- package/agent-sdk/dist/bridge/tool_calls.js +168 -0
- package/agent-sdk/dist/bridge/user_interaction.js +175 -0
- package/agent-sdk/dist/bridge.js +21 -1323
- package/agent-sdk/dist/bridge.test.js +113 -55
- package/bin/claude-rs.js +1 -1
- package/package.json +5 -6
- package/scripts/postinstall.js +2 -2
- package/agent-sdk/README.md +0 -13
- package/agent-sdk/dist/bridge/usage.js +0 -95
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { asRecordOrNull } from "./shared.js";
|
|
2
|
+
import { writeEvent, emitSessionUpdate } from "./events.js";
|
|
3
|
+
import { setToolCallStatus } from "./tool_calls.js";
|
|
4
|
+
export const ASK_USER_QUESTION_TOOL_NAME = "AskUserQuestion";
|
|
5
|
+
export const QUESTION_CHOICE_KIND = "question_choice";
|
|
6
|
+
export const EXIT_PLAN_MODE_TOOL_NAME = "ExitPlanMode";
|
|
7
|
+
export const PLAN_APPROVE_KIND = "plan_approve";
|
|
8
|
+
export const PLAN_REJECT_KIND = "plan_reject";
|
|
9
|
+
export async function requestExitPlanModeApproval(session, toolUseId, inputData, baseToolCall) {
|
|
10
|
+
const options = [
|
|
11
|
+
{
|
|
12
|
+
option_id: "approve",
|
|
13
|
+
name: "Approve",
|
|
14
|
+
description: "Approve the plan and continue",
|
|
15
|
+
kind: PLAN_APPROVE_KIND,
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
option_id: "reject",
|
|
19
|
+
name: "Reject",
|
|
20
|
+
description: "Reject the plan",
|
|
21
|
+
kind: PLAN_REJECT_KIND,
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
const request = {
|
|
25
|
+
tool_call: baseToolCall,
|
|
26
|
+
options,
|
|
27
|
+
};
|
|
28
|
+
const outcome = await new Promise((resolve) => {
|
|
29
|
+
session.pendingPermissions.set(toolUseId, {
|
|
30
|
+
onOutcome: resolve,
|
|
31
|
+
toolName: EXIT_PLAN_MODE_TOOL_NAME,
|
|
32
|
+
inputData,
|
|
33
|
+
});
|
|
34
|
+
writeEvent({ event: "permission_request", session_id: session.sessionId, request });
|
|
35
|
+
});
|
|
36
|
+
if (outcome.outcome !== "selected" || outcome.option_id === "reject") {
|
|
37
|
+
setToolCallStatus(session, toolUseId, "failed", "Plan rejected");
|
|
38
|
+
return { behavior: "deny", message: "Plan rejected", toolUseID: toolUseId };
|
|
39
|
+
}
|
|
40
|
+
return { behavior: "allow", updatedInput: inputData, toolUseID: toolUseId };
|
|
41
|
+
}
|
|
42
|
+
export function parseAskUserQuestionPrompts(inputData) {
|
|
43
|
+
const rawQuestions = Array.isArray(inputData.questions) ? inputData.questions : [];
|
|
44
|
+
const prompts = [];
|
|
45
|
+
for (const rawQuestion of rawQuestions) {
|
|
46
|
+
const questionRecord = asRecordOrNull(rawQuestion);
|
|
47
|
+
if (!questionRecord) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const question = typeof questionRecord.question === "string" ? questionRecord.question.trim() : "";
|
|
51
|
+
if (!question) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const headerRaw = typeof questionRecord.header === "string" ? questionRecord.header.trim() : "";
|
|
55
|
+
const header = headerRaw || `Q${prompts.length + 1}`;
|
|
56
|
+
const multiSelect = Boolean(questionRecord.multiSelect);
|
|
57
|
+
const rawOptions = Array.isArray(questionRecord.options) ? questionRecord.options : [];
|
|
58
|
+
const options = [];
|
|
59
|
+
for (const rawOption of rawOptions) {
|
|
60
|
+
const optionRecord = asRecordOrNull(rawOption);
|
|
61
|
+
if (!optionRecord) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const label = typeof optionRecord.label === "string" ? optionRecord.label.trim() : "";
|
|
65
|
+
const description = typeof optionRecord.description === "string" ? optionRecord.description.trim() : "";
|
|
66
|
+
if (!label) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
options.push({ label, description });
|
|
70
|
+
}
|
|
71
|
+
if (options.length < 2) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
prompts.push({ question, header, multiSelect, options });
|
|
75
|
+
}
|
|
76
|
+
return prompts;
|
|
77
|
+
}
|
|
78
|
+
function askUserQuestionOptions(prompt) {
|
|
79
|
+
return prompt.options.map((option, index) => ({
|
|
80
|
+
option_id: `question_${index}`,
|
|
81
|
+
name: option.label,
|
|
82
|
+
description: option.description,
|
|
83
|
+
kind: QUESTION_CHOICE_KIND,
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
function askUserQuestionPromptToolCall(base, prompt, index, total) {
|
|
87
|
+
return {
|
|
88
|
+
...base,
|
|
89
|
+
title: prompt.question,
|
|
90
|
+
raw_input: {
|
|
91
|
+
questions: [
|
|
92
|
+
{
|
|
93
|
+
question: prompt.question,
|
|
94
|
+
header: prompt.header,
|
|
95
|
+
multiSelect: prompt.multiSelect,
|
|
96
|
+
options: prompt.options,
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
question_index: index,
|
|
100
|
+
total_questions: total,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function askUserQuestionTranscript(answers) {
|
|
105
|
+
return answers.map((entry) => `${entry.header}: ${entry.answer}\n ${entry.question}`).join("\n");
|
|
106
|
+
}
|
|
107
|
+
export async function requestAskUserQuestionAnswers(session, toolUseId, toolName, inputData, baseToolCall) {
|
|
108
|
+
const prompts = parseAskUserQuestionPrompts(inputData);
|
|
109
|
+
if (prompts.length === 0) {
|
|
110
|
+
return { behavior: "allow", updatedInput: inputData, toolUseID: toolUseId };
|
|
111
|
+
}
|
|
112
|
+
const answers = {};
|
|
113
|
+
const transcript = [];
|
|
114
|
+
for (const [index, prompt] of prompts.entries()) {
|
|
115
|
+
const promptToolCall = askUserQuestionPromptToolCall(baseToolCall, prompt, index, prompts.length);
|
|
116
|
+
const fields = {
|
|
117
|
+
title: promptToolCall.title,
|
|
118
|
+
status: "in_progress",
|
|
119
|
+
raw_input: promptToolCall.raw_input,
|
|
120
|
+
};
|
|
121
|
+
emitSessionUpdate(session.sessionId, {
|
|
122
|
+
type: "tool_call_update",
|
|
123
|
+
tool_call_update: { tool_call_id: toolUseId, fields },
|
|
124
|
+
});
|
|
125
|
+
const tracked = session.toolCalls.get(toolUseId);
|
|
126
|
+
if (tracked) {
|
|
127
|
+
tracked.title = promptToolCall.title;
|
|
128
|
+
tracked.status = "in_progress";
|
|
129
|
+
tracked.raw_input = promptToolCall.raw_input;
|
|
130
|
+
}
|
|
131
|
+
const request = {
|
|
132
|
+
tool_call: promptToolCall,
|
|
133
|
+
options: askUserQuestionOptions(prompt),
|
|
134
|
+
};
|
|
135
|
+
const outcome = await new Promise((resolve) => {
|
|
136
|
+
session.pendingPermissions.set(toolUseId, {
|
|
137
|
+
onOutcome: resolve,
|
|
138
|
+
toolName,
|
|
139
|
+
inputData,
|
|
140
|
+
});
|
|
141
|
+
writeEvent({ event: "permission_request", session_id: session.sessionId, request });
|
|
142
|
+
});
|
|
143
|
+
if (outcome.outcome !== "selected") {
|
|
144
|
+
setToolCallStatus(session, toolUseId, "failed", "Question cancelled");
|
|
145
|
+
return { behavior: "deny", message: "Question cancelled", toolUseID: toolUseId };
|
|
146
|
+
}
|
|
147
|
+
const selected = request.options.find((option) => option.option_id === outcome.option_id);
|
|
148
|
+
if (!selected) {
|
|
149
|
+
setToolCallStatus(session, toolUseId, "failed", "Question answer was invalid");
|
|
150
|
+
return { behavior: "deny", message: "Question answer was invalid", toolUseID: toolUseId };
|
|
151
|
+
}
|
|
152
|
+
answers[prompt.question] = selected.name;
|
|
153
|
+
transcript.push({ header: prompt.header, question: prompt.question, answer: selected.name });
|
|
154
|
+
const summary = askUserQuestionTranscript(transcript);
|
|
155
|
+
const progressFields = {
|
|
156
|
+
status: index + 1 >= prompts.length ? "completed" : "in_progress",
|
|
157
|
+
raw_output: summary,
|
|
158
|
+
content: [{ type: "content", content: { type: "text", text: summary } }],
|
|
159
|
+
};
|
|
160
|
+
emitSessionUpdate(session.sessionId, {
|
|
161
|
+
type: "tool_call_update",
|
|
162
|
+
tool_call_update: { tool_call_id: toolUseId, fields: progressFields },
|
|
163
|
+
});
|
|
164
|
+
if (tracked) {
|
|
165
|
+
tracked.status = progressFields.status ?? tracked.status;
|
|
166
|
+
tracked.raw_output = summary;
|
|
167
|
+
tracked.content = progressFields.content ?? tracked.content;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
behavior: "allow",
|
|
172
|
+
updatedInput: { ...inputData, answers },
|
|
173
|
+
toolUseID: toolUseId,
|
|
174
|
+
};
|
|
175
|
+
}
|