@ai-sdk/harness-opencode 1.0.102 → 1.0.103
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 +8 -0
- package/dist/bridge/index.mjs +154 -13
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.d.ts +683 -1
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/bridge/create-emit-stream-event.ts +7 -3
- package/src/bridge/index.ts +92 -3
- package/src/bridge/question-tool.ts +90 -0
- package/src/opencode-harness.ts +9 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
HarnessV1QuestionsToolInput,
|
|
3
|
+
HarnessV1QuestionsToolOutput,
|
|
4
|
+
} from '@ai-sdk/harness';
|
|
5
|
+
import type { QuestionInfo } from '@opencode-ai/sdk/v2';
|
|
6
|
+
|
|
7
|
+
export type OpenCodeQuestionRequest = {
|
|
8
|
+
id: string;
|
|
9
|
+
sessionID: string;
|
|
10
|
+
questions: QuestionInfo[];
|
|
11
|
+
tool?: { messageID: string; callID: string };
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function openCodeQuestionKey(
|
|
15
|
+
nativeRequest: OpenCodeQuestionRequest,
|
|
16
|
+
): string {
|
|
17
|
+
return JSON.stringify(nativeRequest.questions);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function toHarnessQuestionsInput(
|
|
21
|
+
nativeRequest: OpenCodeQuestionRequest,
|
|
22
|
+
): HarnessV1QuestionsToolInput {
|
|
23
|
+
return {
|
|
24
|
+
allowPartialAnswers: true,
|
|
25
|
+
questions: nativeRequest.questions.map((question, questionIndex) => ({
|
|
26
|
+
id: `question-${questionIndex + 1}`,
|
|
27
|
+
question: question.question,
|
|
28
|
+
header: question.header,
|
|
29
|
+
options: question.options.map((option, optionIndex) => ({
|
|
30
|
+
id: `option-${optionIndex + 1}`,
|
|
31
|
+
label: option.label,
|
|
32
|
+
description: option.description,
|
|
33
|
+
})),
|
|
34
|
+
...(question.multiple !== undefined
|
|
35
|
+
? { allowMultiple: question.multiple }
|
|
36
|
+
: {}),
|
|
37
|
+
...(question.custom !== undefined
|
|
38
|
+
? { allowFreeForm: question.custom }
|
|
39
|
+
: {}),
|
|
40
|
+
})),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function toOpenCodeQuestionResponse(input: {
|
|
45
|
+
nativeRequest: OpenCodeQuestionRequest;
|
|
46
|
+
output: HarnessV1QuestionsToolOutput;
|
|
47
|
+
}): { action: 'reply'; answers: string[][] } | { action: 'reject' } {
|
|
48
|
+
if (
|
|
49
|
+
input.output.action === 'declined' ||
|
|
50
|
+
input.output.action === 'cancelled'
|
|
51
|
+
) {
|
|
52
|
+
return { action: 'reject' };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const answers = input.nativeRequest.questions.map(
|
|
56
|
+
(nativeQuestion, questionIndex) => {
|
|
57
|
+
const answer =
|
|
58
|
+
input.output.action === 'answered' ||
|
|
59
|
+
input.output.action === 'partially-answered'
|
|
60
|
+
? input.output.answers[`question-${questionIndex + 1}`]
|
|
61
|
+
: undefined;
|
|
62
|
+
if (answer == null) return [];
|
|
63
|
+
const selectedLabels = answer.optionIds.flatMap(optionId => {
|
|
64
|
+
const index = positionalIdIndex({
|
|
65
|
+
id: optionId,
|
|
66
|
+
prefix: 'option-',
|
|
67
|
+
});
|
|
68
|
+
const option =
|
|
69
|
+
index == null ? undefined : nativeQuestion.options[index];
|
|
70
|
+
return option == null ? [] : [option.label];
|
|
71
|
+
});
|
|
72
|
+
return answer.freeform === undefined
|
|
73
|
+
? selectedLabels
|
|
74
|
+
: [...selectedLabels, answer.freeform];
|
|
75
|
+
},
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
return { action: 'reply', answers };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function positionalIdIndex(input: {
|
|
82
|
+
id: string;
|
|
83
|
+
prefix: string;
|
|
84
|
+
}): number | undefined {
|
|
85
|
+
if (!input.id.startsWith(input.prefix)) return undefined;
|
|
86
|
+
const oneBasedIndex = Number(input.id.slice(input.prefix.length));
|
|
87
|
+
return Number.isInteger(oneBasedIndex) && oneBasedIndex > 0
|
|
88
|
+
? oneBasedIndex - 1
|
|
89
|
+
: undefined;
|
|
90
|
+
}
|
package/src/opencode-harness.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import {
|
|
3
3
|
commonTool,
|
|
4
|
+
HARNESS_V1_BUILTIN_TOOLS,
|
|
4
5
|
HarnessCapabilityUnsupportedError,
|
|
5
6
|
harnessV1DiagnosticFromBridgeFrame,
|
|
6
7
|
type HarnessV1,
|
|
@@ -123,6 +124,11 @@ export type OpenCodeHarnessSettings = {
|
|
|
123
124
|
const optionalStringRecord = z.record(z.string(), z.unknown()).optional();
|
|
124
125
|
|
|
125
126
|
const OPENCODE_BUILTIN_TOOLS = {
|
|
127
|
+
askUserQuestions: {
|
|
128
|
+
...HARNESS_V1_BUILTIN_TOOLS.askUserQuestions,
|
|
129
|
+
nativeName: 'question',
|
|
130
|
+
toolUseKind: 'readonly',
|
|
131
|
+
},
|
|
126
132
|
read: commonTool('read', {
|
|
127
133
|
nativeName: 'view',
|
|
128
134
|
toolUseKind: 'readonly',
|
|
@@ -954,6 +960,9 @@ function createSession({
|
|
|
954
960
|
toolCallId: input.toolCallId,
|
|
955
961
|
output: input.output,
|
|
956
962
|
isError: input.isError,
|
|
963
|
+
...(input.toolResult !== undefined
|
|
964
|
+
? { toolResult: input.toolResult }
|
|
965
|
+
: {}),
|
|
957
966
|
});
|
|
958
967
|
},
|
|
959
968
|
submitToolApproval: async input => {
|