@uncensoredcode/openbridge 0.1.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 +117 -0
- package/bin/openbridge.js +10 -0
- package/package.json +85 -0
- package/packages/cli/dist/args.d.ts +30 -0
- package/packages/cli/dist/args.js +160 -0
- package/packages/cli/dist/cli.d.ts +2 -0
- package/packages/cli/dist/cli.js +9 -0
- package/packages/cli/dist/index.d.ts +26 -0
- package/packages/cli/dist/index.js +76 -0
- package/packages/runtime/dist/assistant-protocol.d.ts +34 -0
- package/packages/runtime/dist/assistant-protocol.js +121 -0
- package/packages/runtime/dist/execution/in-process.d.ts +14 -0
- package/packages/runtime/dist/execution/in-process.js +45 -0
- package/packages/runtime/dist/execution/types.d.ts +49 -0
- package/packages/runtime/dist/execution/types.js +20 -0
- package/packages/runtime/dist/index.d.ts +86 -0
- package/packages/runtime/dist/index.js +60 -0
- package/packages/runtime/dist/normalizers/index.d.ts +6 -0
- package/packages/runtime/dist/normalizers/index.js +12 -0
- package/packages/runtime/dist/normalizers/legacy-packet.d.ts +6 -0
- package/packages/runtime/dist/normalizers/legacy-packet.js +131 -0
- package/packages/runtime/dist/output-sanitizer.d.ts +23 -0
- package/packages/runtime/dist/output-sanitizer.js +78 -0
- package/packages/runtime/dist/packet-extractor.d.ts +17 -0
- package/packages/runtime/dist/packet-extractor.js +43 -0
- package/packages/runtime/dist/packet-normalizer.d.ts +21 -0
- package/packages/runtime/dist/packet-normalizer.js +47 -0
- package/packages/runtime/dist/prompt-compiler.d.ts +28 -0
- package/packages/runtime/dist/prompt-compiler.js +301 -0
- package/packages/runtime/dist/protocol.d.ts +44 -0
- package/packages/runtime/dist/protocol.js +165 -0
- package/packages/runtime/dist/provider-failure.d.ts +52 -0
- package/packages/runtime/dist/provider-failure.js +236 -0
- package/packages/runtime/dist/provider.d.ts +40 -0
- package/packages/runtime/dist/provider.js +1 -0
- package/packages/runtime/dist/runtime.d.ts +86 -0
- package/packages/runtime/dist/runtime.js +462 -0
- package/packages/runtime/dist/session-bound-provider.d.ts +52 -0
- package/packages/runtime/dist/session-bound-provider.js +366 -0
- package/packages/runtime/dist/tool-name-aliases.d.ts +5 -0
- package/packages/runtime/dist/tool-name-aliases.js +13 -0
- package/packages/runtime/dist/tools/bash.d.ts +9 -0
- package/packages/runtime/dist/tools/bash.js +157 -0
- package/packages/runtime/dist/tools/edit.d.ts +9 -0
- package/packages/runtime/dist/tools/edit.js +94 -0
- package/packages/runtime/dist/tools/index.d.ts +39 -0
- package/packages/runtime/dist/tools/index.js +27 -0
- package/packages/runtime/dist/tools/list-dir.d.ts +9 -0
- package/packages/runtime/dist/tools/list-dir.js +127 -0
- package/packages/runtime/dist/tools/read.d.ts +9 -0
- package/packages/runtime/dist/tools/read.js +56 -0
- package/packages/runtime/dist/tools/registry.d.ts +15 -0
- package/packages/runtime/dist/tools/registry.js +38 -0
- package/packages/runtime/dist/tools/runtime-path.d.ts +7 -0
- package/packages/runtime/dist/tools/runtime-path.js +22 -0
- package/packages/runtime/dist/tools/search-files.d.ts +9 -0
- package/packages/runtime/dist/tools/search-files.js +149 -0
- package/packages/runtime/dist/tools/text-file.d.ts +32 -0
- package/packages/runtime/dist/tools/text-file.js +101 -0
- package/packages/runtime/dist/tools/workspace-path.d.ts +17 -0
- package/packages/runtime/dist/tools/workspace-path.js +70 -0
- package/packages/runtime/dist/tools/write.d.ts +9 -0
- package/packages/runtime/dist/tools/write.js +59 -0
- package/packages/server/dist/bridge/bridge-model-catalog.d.ts +56 -0
- package/packages/server/dist/bridge/bridge-model-catalog.js +100 -0
- package/packages/server/dist/bridge/bridge-runtime-service.d.ts +61 -0
- package/packages/server/dist/bridge/bridge-runtime-service.js +1386 -0
- package/packages/server/dist/bridge/chat-completions/chat-completion-service.d.ts +127 -0
- package/packages/server/dist/bridge/chat-completions/chat-completion-service.js +1026 -0
- package/packages/server/dist/bridge/index.d.ts +335 -0
- package/packages/server/dist/bridge/index.js +45 -0
- package/packages/server/dist/bridge/live-provider-extraction-canary.d.ts +69 -0
- package/packages/server/dist/bridge/live-provider-extraction-canary.js +186 -0
- package/packages/server/dist/bridge/providers/generic-provider-transport.d.ts +53 -0
- package/packages/server/dist/bridge/providers/generic-provider-transport.js +973 -0
- package/packages/server/dist/bridge/providers/provider-session-resolver.d.ts +17 -0
- package/packages/server/dist/bridge/providers/provider-session-resolver.js +95 -0
- package/packages/server/dist/bridge/providers/provider-streams.d.ts +80 -0
- package/packages/server/dist/bridge/providers/provider-streams.js +844 -0
- package/packages/server/dist/bridge/providers/provider-transport-profile.d.ts +194 -0
- package/packages/server/dist/bridge/providers/provider-transport-profile.js +198 -0
- package/packages/server/dist/bridge/providers/web-provider-transport.d.ts +30 -0
- package/packages/server/dist/bridge/providers/web-provider-transport.js +151 -0
- package/packages/server/dist/bridge/state/file-bridge-state-store.d.ts +36 -0
- package/packages/server/dist/bridge/state/file-bridge-state-store.js +164 -0
- package/packages/server/dist/bridge/stores/local-session-package-store.d.ts +23 -0
- package/packages/server/dist/bridge/stores/local-session-package-store.js +548 -0
- package/packages/server/dist/bridge/stores/provider-store.d.ts +94 -0
- package/packages/server/dist/bridge/stores/provider-store.js +143 -0
- package/packages/server/dist/bridge/stores/session-backed-provider-store.d.ts +7 -0
- package/packages/server/dist/bridge/stores/session-backed-provider-store.js +26 -0
- package/packages/server/dist/bridge/stores/session-package-store.d.ts +286 -0
- package/packages/server/dist/bridge/stores/session-package-store.js +1527 -0
- package/packages/server/dist/bridge/stores/session-store.d.ts +120 -0
- package/packages/server/dist/bridge/stores/session-store.js +139 -0
- package/packages/server/dist/cli/index.d.ts +9 -0
- package/packages/server/dist/cli/index.js +6 -0
- package/packages/server/dist/cli/main.d.ts +2 -0
- package/packages/server/dist/cli/main.js +9 -0
- package/packages/server/dist/cli/run-bridge-server-cli.d.ts +54 -0
- package/packages/server/dist/cli/run-bridge-server-cli.js +371 -0
- package/packages/server/dist/client/bridge-api-client.d.ts +61 -0
- package/packages/server/dist/client/bridge-api-client.js +267 -0
- package/packages/server/dist/client/index.d.ts +11 -0
- package/packages/server/dist/client/index.js +11 -0
- package/packages/server/dist/config/bridge-server-config.d.ts +52 -0
- package/packages/server/dist/config/bridge-server-config.js +118 -0
- package/packages/server/dist/config/index.d.ts +20 -0
- package/packages/server/dist/config/index.js +8 -0
- package/packages/server/dist/http/bridge-api-route-context.d.ts +14 -0
- package/packages/server/dist/http/bridge-api-route-context.js +1 -0
- package/packages/server/dist/http/create-bridge-api-server.d.ts +72 -0
- package/packages/server/dist/http/create-bridge-api-server.js +225 -0
- package/packages/server/dist/http/index.d.ts +5 -0
- package/packages/server/dist/http/index.js +5 -0
- package/packages/server/dist/http/parse-request.d.ts +6 -0
- package/packages/server/dist/http/parse-request.js +27 -0
- package/packages/server/dist/http/register-bridge-api-routes.d.ts +7 -0
- package/packages/server/dist/http/register-bridge-api-routes.js +17 -0
- package/packages/server/dist/http/routes/admin-routes.d.ts +7 -0
- package/packages/server/dist/http/routes/admin-routes.js +135 -0
- package/packages/server/dist/http/routes/chat-completions-route.d.ts +7 -0
- package/packages/server/dist/http/routes/chat-completions-route.js +49 -0
- package/packages/server/dist/http/routes/health-routes.d.ts +6 -0
- package/packages/server/dist/http/routes/health-routes.js +7 -0
- package/packages/server/dist/http/routes/message-routes.d.ts +7 -0
- package/packages/server/dist/http/routes/message-routes.js +7 -0
- package/packages/server/dist/index.d.ts +85 -0
- package/packages/server/dist/index.js +28 -0
- package/packages/server/dist/security/bridge-auth.d.ts +9 -0
- package/packages/server/dist/security/bridge-auth.js +41 -0
- package/packages/server/dist/security/cors-policy.d.ts +5 -0
- package/packages/server/dist/security/cors-policy.js +34 -0
- package/packages/server/dist/security/index.d.ts +16 -0
- package/packages/server/dist/security/index.js +12 -0
- package/packages/server/dist/security/redact-sensitive-values.d.ts +19 -0
- package/packages/server/dist/security/redact-sensitive-values.js +67 -0
- package/packages/server/dist/shared/api-schema.d.ts +133 -0
- package/packages/server/dist/shared/api-schema.js +1 -0
- package/packages/server/dist/shared/bridge-api-error.d.ts +17 -0
- package/packages/server/dist/shared/bridge-api-error.js +19 -0
- package/packages/server/dist/shared/index.d.ts +7 -0
- package/packages/server/dist/shared/index.js +7 -0
- package/packages/server/dist/shared/output.d.ts +5 -0
- package/packages/server/dist/shared/output.js +14 -0
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
import { assistantProtocolModule } from "./assistant-protocol.js";
|
|
2
|
+
import { packetExtractorModule } from "./packet-extractor.js";
|
|
3
|
+
import { packetNormalizerModule } from "./packet-normalizer.js";
|
|
4
|
+
import { protocolModule } from "./protocol.js";
|
|
5
|
+
import { providerFailureModule } from "./provider-failure.js";
|
|
6
|
+
import { toolNameAliasesModule } from "./tool-name-aliases.js";
|
|
7
|
+
const { parseAssistantResponse, validateAssistantResponse } = assistantProtocolModule;
|
|
8
|
+
const { extractPacketCandidate } = packetExtractorModule;
|
|
9
|
+
const { normalizeProviderPacket } = packetNormalizerModule;
|
|
10
|
+
const { parseZcPacket, serializeToolResult } = protocolModule;
|
|
11
|
+
const { normalizeProviderToolName } = toolNameAliasesModule;
|
|
12
|
+
const { formatProviderFailureMessage, isProviderFailure, serializeProviderFailure, withProviderRecovery } = providerFailureModule;
|
|
13
|
+
const DEFAULT_MAX_STEPS = 8;
|
|
14
|
+
async function runBridgeRuntime(input) {
|
|
15
|
+
const maxSteps = input.config?.maxSteps ?? DEFAULT_MAX_STEPS;
|
|
16
|
+
const conversation = {
|
|
17
|
+
sessionHistory: input.sessionHistory ?? [],
|
|
18
|
+
entries: [
|
|
19
|
+
{
|
|
20
|
+
type: "user_message",
|
|
21
|
+
content: input.userMessage
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
};
|
|
25
|
+
const availableTools = await input.toolExecutor.getAvailableTools();
|
|
26
|
+
for (let step = 1; step <= maxSteps; step += 1) {
|
|
27
|
+
let rawText;
|
|
28
|
+
const providerStartedAt = Date.now();
|
|
29
|
+
try {
|
|
30
|
+
rawText = await input.provider.completeTurn({
|
|
31
|
+
conversation,
|
|
32
|
+
availableTools
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (isProviderFailure(error)) {
|
|
37
|
+
const serialized = serializeProviderFailure(withProviderRecovery(error, error.recovery, {
|
|
38
|
+
displayMessage: formatProviderFailureMessage(error)
|
|
39
|
+
}));
|
|
40
|
+
return emitOutcome({
|
|
41
|
+
mode: "fail",
|
|
42
|
+
message: serialized.message,
|
|
43
|
+
steps: step,
|
|
44
|
+
conversation,
|
|
45
|
+
failure: {
|
|
46
|
+
source: "provider",
|
|
47
|
+
provider: serialized
|
|
48
|
+
}
|
|
49
|
+
}, input.config);
|
|
50
|
+
}
|
|
51
|
+
return emitOutcome({
|
|
52
|
+
mode: "fail",
|
|
53
|
+
message: `Provider adapter failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
54
|
+
steps: step,
|
|
55
|
+
conversation,
|
|
56
|
+
failure: {
|
|
57
|
+
source: "provider",
|
|
58
|
+
provider: {
|
|
59
|
+
kind: "permanent",
|
|
60
|
+
code: "transport_error",
|
|
61
|
+
message: `Provider adapter failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
62
|
+
retryable: false,
|
|
63
|
+
sessionResetEligible: false,
|
|
64
|
+
emptyOutput: false,
|
|
65
|
+
recovery: {
|
|
66
|
+
softRetryCount: 0,
|
|
67
|
+
sessionResetCount: 0
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}, input.config);
|
|
72
|
+
}
|
|
73
|
+
input.config?.onEvent?.({
|
|
74
|
+
type: "provider_response",
|
|
75
|
+
step,
|
|
76
|
+
rawText,
|
|
77
|
+
durationMs: Date.now() - providerStartedAt
|
|
78
|
+
});
|
|
79
|
+
let response;
|
|
80
|
+
try {
|
|
81
|
+
response = parseAndValidateProviderResponse(input.provider.id, rawText, availableTools);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
const validationError = error instanceof Error ? error.message : String(error);
|
|
85
|
+
input.config?.onEvent?.({
|
|
86
|
+
type: "main_response_invalid",
|
|
87
|
+
step,
|
|
88
|
+
error: validationError,
|
|
89
|
+
rawTextLength: rawText.length
|
|
90
|
+
});
|
|
91
|
+
input.config?.onEvent?.({
|
|
92
|
+
type: "packet_parse_failed",
|
|
93
|
+
step,
|
|
94
|
+
error: validationError
|
|
95
|
+
});
|
|
96
|
+
input.config?.onEvent?.({
|
|
97
|
+
type: "repair_attempted",
|
|
98
|
+
step
|
|
99
|
+
});
|
|
100
|
+
const repaired = await attemptResponseRepair({
|
|
101
|
+
provider: input.provider,
|
|
102
|
+
conversation,
|
|
103
|
+
availableTools,
|
|
104
|
+
invalidResponse: rawText,
|
|
105
|
+
validationError
|
|
106
|
+
});
|
|
107
|
+
if (!repaired.ok) {
|
|
108
|
+
input.config?.onEvent?.({
|
|
109
|
+
type: "repair_failed",
|
|
110
|
+
step,
|
|
111
|
+
reason: repaired.failure.source === "provider" ? "provider_failure" : "protocol_invalid",
|
|
112
|
+
error: repaired.failure.source === "protocol" ? repaired.failure.message : undefined,
|
|
113
|
+
providerFailure: repaired.failure.source === "provider"
|
|
114
|
+
? {
|
|
115
|
+
kind: repaired.failure.provider.kind,
|
|
116
|
+
code: repaired.failure.provider.code,
|
|
117
|
+
message: repaired.failure.provider.message
|
|
118
|
+
}
|
|
119
|
+
: undefined
|
|
120
|
+
});
|
|
121
|
+
if (repaired.failure.source === "provider") {
|
|
122
|
+
return emitOutcome({
|
|
123
|
+
mode: "fail",
|
|
124
|
+
message: repaired.failure.provider.message,
|
|
125
|
+
steps: step,
|
|
126
|
+
conversation,
|
|
127
|
+
failure: repaired.failure
|
|
128
|
+
}, input.config);
|
|
129
|
+
}
|
|
130
|
+
return emitOutcome({
|
|
131
|
+
mode: "fail",
|
|
132
|
+
message: repaired.failure.message,
|
|
133
|
+
steps: step,
|
|
134
|
+
conversation,
|
|
135
|
+
failure: repaired.failure
|
|
136
|
+
}, input.config);
|
|
137
|
+
}
|
|
138
|
+
rawText = repaired.rawText;
|
|
139
|
+
try {
|
|
140
|
+
response = parseAndValidateProviderResponse(input.provider.id, rawText, availableTools);
|
|
141
|
+
}
|
|
142
|
+
catch (repairError) {
|
|
143
|
+
const repairValidationError = repairError instanceof Error ? repairError.message : String(repairError);
|
|
144
|
+
input.config?.onEvent?.({
|
|
145
|
+
type: "packet_parse_failed",
|
|
146
|
+
step,
|
|
147
|
+
error: repairValidationError
|
|
148
|
+
});
|
|
149
|
+
input.config?.onEvent?.({
|
|
150
|
+
type: "repair_failed",
|
|
151
|
+
step,
|
|
152
|
+
reason: "protocol_invalid",
|
|
153
|
+
error: `Invalid assistant response: ${repairValidationError}`
|
|
154
|
+
});
|
|
155
|
+
return emitOutcome({
|
|
156
|
+
mode: "fail",
|
|
157
|
+
message: `Invalid assistant response: ${repairValidationError}`,
|
|
158
|
+
steps: step,
|
|
159
|
+
conversation,
|
|
160
|
+
failure: {
|
|
161
|
+
source: "protocol",
|
|
162
|
+
code: "malformed_provider_packet",
|
|
163
|
+
message: `Invalid assistant response: ${repairValidationError}`
|
|
164
|
+
}
|
|
165
|
+
}, input.config);
|
|
166
|
+
}
|
|
167
|
+
input.config?.onEvent?.({
|
|
168
|
+
type: "repair_valid",
|
|
169
|
+
step,
|
|
170
|
+
mode: getParsedResponseMode(response),
|
|
171
|
+
rawTextLength: rawText.length
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
input.config?.onEvent?.({
|
|
175
|
+
type: "packet_parsed",
|
|
176
|
+
step,
|
|
177
|
+
mode: getParsedResponseMode(response)
|
|
178
|
+
});
|
|
179
|
+
conversation.entries.push({
|
|
180
|
+
type: "provider_packet",
|
|
181
|
+
rawText,
|
|
182
|
+
packet: response
|
|
183
|
+
});
|
|
184
|
+
if (isAssistantToolResponse(response) || isZcToolRequest(response)) {
|
|
185
|
+
const toolCall = isZcToolRequest(response)
|
|
186
|
+
? {
|
|
187
|
+
id: response.toolCall.id,
|
|
188
|
+
name: response.toolCall.name,
|
|
189
|
+
args: response.toolCall.args
|
|
190
|
+
}
|
|
191
|
+
: {
|
|
192
|
+
id: `call_${step}`,
|
|
193
|
+
name: response.toolCall.name,
|
|
194
|
+
args: response.toolCall.arguments
|
|
195
|
+
};
|
|
196
|
+
const toolStartedAt = Date.now();
|
|
197
|
+
const execution = await input.toolExecutor.executeTool({
|
|
198
|
+
call: toolCall
|
|
199
|
+
});
|
|
200
|
+
const toolResult = {
|
|
201
|
+
id: toolCall.id,
|
|
202
|
+
name: toolCall.name,
|
|
203
|
+
ok: execution.ok,
|
|
204
|
+
payload: execution.payload
|
|
205
|
+
};
|
|
206
|
+
const serializedToolResult = serializeToolResult(toolResult);
|
|
207
|
+
conversation.entries.push({
|
|
208
|
+
type: "tool_result",
|
|
209
|
+
rawText: serializedToolResult,
|
|
210
|
+
result: toolResult
|
|
211
|
+
});
|
|
212
|
+
input.config?.onEvent?.({
|
|
213
|
+
type: "tool_result",
|
|
214
|
+
step,
|
|
215
|
+
rawText: serializedToolResult,
|
|
216
|
+
result: toolResult,
|
|
217
|
+
durationMs: Date.now() - toolStartedAt
|
|
218
|
+
});
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
switch (getParsedResponseMode(response)) {
|
|
222
|
+
case "final":
|
|
223
|
+
return emitOutcome({
|
|
224
|
+
mode: "final",
|
|
225
|
+
message: extractParsedResponseMessage(response),
|
|
226
|
+
steps: step,
|
|
227
|
+
conversation
|
|
228
|
+
}, input.config);
|
|
229
|
+
case "ask_user":
|
|
230
|
+
return emitOutcome({
|
|
231
|
+
mode: "ask_user",
|
|
232
|
+
message: extractParsedResponseMessage(response),
|
|
233
|
+
steps: step,
|
|
234
|
+
conversation
|
|
235
|
+
}, input.config);
|
|
236
|
+
case "fail":
|
|
237
|
+
return emitOutcome({
|
|
238
|
+
mode: "fail",
|
|
239
|
+
message: extractParsedResponseMessage(response),
|
|
240
|
+
steps: step,
|
|
241
|
+
conversation
|
|
242
|
+
}, input.config);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return emitOutcome({
|
|
246
|
+
mode: "fail",
|
|
247
|
+
message: `Runtime exceeded max steps (${maxSteps}).`,
|
|
248
|
+
steps: maxSteps,
|
|
249
|
+
conversation,
|
|
250
|
+
failure: {
|
|
251
|
+
source: "runtime",
|
|
252
|
+
code: "max_steps_exhausted",
|
|
253
|
+
message: `Runtime exceeded max steps (${maxSteps}).`
|
|
254
|
+
}
|
|
255
|
+
}, input.config);
|
|
256
|
+
}
|
|
257
|
+
function repairLeadingAssistantBlock(content) {
|
|
258
|
+
const trimmed = content.trim();
|
|
259
|
+
const recovered = recoverLatestFinalBlock(trimmed);
|
|
260
|
+
if (recovered) {
|
|
261
|
+
return recovered;
|
|
262
|
+
}
|
|
263
|
+
return trimmed;
|
|
264
|
+
}
|
|
265
|
+
function recoverLatestFinalBlock(content) {
|
|
266
|
+
return recoverLatestTaggedBlock(content, "final")?.block ?? null;
|
|
267
|
+
}
|
|
268
|
+
function recoverLatestTaggedBlock(content, tag) {
|
|
269
|
+
const closeTag = `</${tag}>`;
|
|
270
|
+
const closeIndex = content.lastIndexOf(closeTag);
|
|
271
|
+
if (closeIndex < 0) {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
const marker = new RegExp(`(?:<)?${tag}>`, "gi");
|
|
275
|
+
let latestMatch = null;
|
|
276
|
+
for (const match of content.matchAll(marker)) {
|
|
277
|
+
const index = match.index ?? -1;
|
|
278
|
+
if (index < 0 || index >= closeIndex) {
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
latestMatch = {
|
|
282
|
+
index,
|
|
283
|
+
markerLength: match[0].length
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
if (!latestMatch) {
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
const innerContent = content.slice(latestMatch.index + latestMatch.markerLength, closeIndex);
|
|
290
|
+
return {
|
|
291
|
+
index: latestMatch.index,
|
|
292
|
+
block: `<${tag}>${innerContent}${closeTag}`
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
function parseAndValidateProviderResponse(providerId, rawText, availableTools) {
|
|
296
|
+
const normalizedAssistantResponse = repairLeadingAssistantBlock(rawText);
|
|
297
|
+
try {
|
|
298
|
+
return validateAssistantResponse(normalizeAssistantToolNames(parseAssistantResponse(normalizedAssistantResponse)), availableTools);
|
|
299
|
+
}
|
|
300
|
+
catch (assistantError) {
|
|
301
|
+
const extraction = extractPacketCandidate(rawText);
|
|
302
|
+
if (!extraction.ok) {
|
|
303
|
+
throw assistantError;
|
|
304
|
+
}
|
|
305
|
+
const normalization = normalizeProviderPacket(providerId, extraction.packetText);
|
|
306
|
+
if (!normalization.ok) {
|
|
307
|
+
throw assistantError;
|
|
308
|
+
}
|
|
309
|
+
const packet = normalizeZcPacketToolNames(parseZcPacket(normalization.canonicalPacket));
|
|
310
|
+
validateZcPacketResponse(packet, availableTools);
|
|
311
|
+
return packet;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
function normalizeAssistantToolNames(response) {
|
|
315
|
+
if (response.type !== "tool") {
|
|
316
|
+
return response;
|
|
317
|
+
}
|
|
318
|
+
return {
|
|
319
|
+
...response,
|
|
320
|
+
toolCall: {
|
|
321
|
+
...response.toolCall,
|
|
322
|
+
name: normalizeProviderToolName(response.toolCall.name)
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
function normalizeZcPacketToolNames(packet) {
|
|
327
|
+
if (packet.mode !== "tool_request") {
|
|
328
|
+
return packet;
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
...packet,
|
|
332
|
+
toolCall: {
|
|
333
|
+
...packet.toolCall,
|
|
334
|
+
name: normalizeProviderToolName(packet.toolCall.name)
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
function validateZcPacketResponse(packet, availableTools) {
|
|
339
|
+
if (packet.mode !== "tool_request") {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
const tool = availableTools.find((candidate) => candidate.name === packet.toolCall.name);
|
|
343
|
+
if (!tool) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
const argumentsRecord = packet.toolCall.args;
|
|
347
|
+
const propertyNames = Object.keys(tool.inputSchema.properties);
|
|
348
|
+
const allowedProperties = new Set(propertyNames);
|
|
349
|
+
for (const name of Object.keys(argumentsRecord)) {
|
|
350
|
+
if (!allowedProperties.has(name)) {
|
|
351
|
+
throw new Error(`Tool "${tool.name}" received unknown argument "${name}".`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
for (const requiredName of tool.inputSchema.required) {
|
|
355
|
+
if (!(requiredName in argumentsRecord)) {
|
|
356
|
+
throw new Error(`Tool "${tool.name}" is missing required argument "${requiredName}".`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
for (const propertyName of propertyNames) {
|
|
360
|
+
if (!(propertyName in argumentsRecord)) {
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
const property = tool.inputSchema.properties[propertyName];
|
|
364
|
+
const value = argumentsRecord[propertyName];
|
|
365
|
+
if (property.type === "string" && typeof value !== "string") {
|
|
366
|
+
throw new Error(`Tool "${tool.name}" argument "${propertyName}" must be a string.`);
|
|
367
|
+
}
|
|
368
|
+
if (property.type === "boolean" && typeof value !== "boolean") {
|
|
369
|
+
throw new Error(`Tool "${tool.name}" argument "${propertyName}" must be a boolean.`);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
function isAssistantToolResponse(response) {
|
|
374
|
+
return "type" in response && response.type === "tool";
|
|
375
|
+
}
|
|
376
|
+
function isAssistantFinalResponse(response) {
|
|
377
|
+
return "type" in response && response.type === "final";
|
|
378
|
+
}
|
|
379
|
+
function isZcToolRequest(response) {
|
|
380
|
+
return "mode" in response && response.mode === "tool_request";
|
|
381
|
+
}
|
|
382
|
+
function getParsedResponseMode(response) {
|
|
383
|
+
if ("type" in response) {
|
|
384
|
+
return response.type;
|
|
385
|
+
}
|
|
386
|
+
return response.mode === "tool_request" ? "tool" : response.mode;
|
|
387
|
+
}
|
|
388
|
+
function extractParsedResponseMessage(response) {
|
|
389
|
+
if (isAssistantFinalResponse(response)) {
|
|
390
|
+
return response.message;
|
|
391
|
+
}
|
|
392
|
+
if (isAssistantToolResponse(response) || isZcToolRequest(response)) {
|
|
393
|
+
throw new Error("tool_request packets do not carry assistant text.");
|
|
394
|
+
}
|
|
395
|
+
return response.message;
|
|
396
|
+
}
|
|
397
|
+
function emitOutcome(outcome, config) {
|
|
398
|
+
config?.onEvent?.({
|
|
399
|
+
type: "outcome",
|
|
400
|
+
outcome
|
|
401
|
+
});
|
|
402
|
+
return outcome;
|
|
403
|
+
}
|
|
404
|
+
async function attemptResponseRepair(input) {
|
|
405
|
+
if (typeof input.provider.repairInvalidResponse !== "function") {
|
|
406
|
+
return {
|
|
407
|
+
ok: false,
|
|
408
|
+
failure: {
|
|
409
|
+
source: "protocol",
|
|
410
|
+
code: "malformed_provider_packet",
|
|
411
|
+
message: `Invalid assistant response: ${input.validationError}`
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
try {
|
|
416
|
+
const rawText = await input.provider.repairInvalidResponse({
|
|
417
|
+
conversation: input.conversation,
|
|
418
|
+
availableTools: input.availableTools,
|
|
419
|
+
invalidResponse: input.invalidResponse,
|
|
420
|
+
validationError: input.validationError
|
|
421
|
+
});
|
|
422
|
+
return {
|
|
423
|
+
ok: true,
|
|
424
|
+
rawText
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
catch (error) {
|
|
428
|
+
if (isProviderFailure(error)) {
|
|
429
|
+
const serialized = serializeProviderFailure(withProviderRecovery(error, error.recovery, {
|
|
430
|
+
displayMessage: formatProviderFailureMessage(error)
|
|
431
|
+
}));
|
|
432
|
+
return {
|
|
433
|
+
ok: false,
|
|
434
|
+
failure: {
|
|
435
|
+
source: "provider",
|
|
436
|
+
provider: serialized
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
return {
|
|
441
|
+
ok: false,
|
|
442
|
+
failure: {
|
|
443
|
+
source: "provider",
|
|
444
|
+
provider: {
|
|
445
|
+
kind: "permanent",
|
|
446
|
+
code: "transport_error",
|
|
447
|
+
message: `Provider repair adapter failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
448
|
+
retryable: false,
|
|
449
|
+
sessionResetEligible: false,
|
|
450
|
+
emptyOutput: false,
|
|
451
|
+
recovery: {
|
|
452
|
+
softRetryCount: 0,
|
|
453
|
+
sessionResetCount: 0
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
export const runtimeModule = {
|
|
461
|
+
runBridgeRuntime
|
|
462
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { CompiledProviderMessage } from "./prompt-compiler.ts";
|
|
2
|
+
import type { ProviderAdapter, ProviderTurnInput, RepairInvalidResponseInput } from "./provider.ts";
|
|
3
|
+
type UpstreamConversationBinding = {
|
|
4
|
+
conversationId: string;
|
|
5
|
+
parentId: string;
|
|
6
|
+
runtimePlannerPrimed?: boolean;
|
|
7
|
+
};
|
|
8
|
+
interface SessionBindingStore {
|
|
9
|
+
loadBinding(providerId: string, sessionId: string): Promise<UpstreamConversationBinding | null>;
|
|
10
|
+
saveBinding(providerId: string, sessionId: string, binding: UpstreamConversationBinding): Promise<void>;
|
|
11
|
+
clearBinding(providerId: string, sessionId: string): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
type ProviderTransportRequest = {
|
|
14
|
+
lane: "main" | "repair";
|
|
15
|
+
providerId: string;
|
|
16
|
+
modelId: string;
|
|
17
|
+
sessionId: string;
|
|
18
|
+
requestId: string;
|
|
19
|
+
attempt: number;
|
|
20
|
+
continuation: boolean;
|
|
21
|
+
toolFollowUp: boolean;
|
|
22
|
+
providerSessionReused: boolean;
|
|
23
|
+
messages: CompiledProviderMessage[];
|
|
24
|
+
upstreamBinding: Pick<UpstreamConversationBinding, "conversationId" | "parentId"> | null;
|
|
25
|
+
};
|
|
26
|
+
type ProviderTransportResponse = {
|
|
27
|
+
content: string;
|
|
28
|
+
upstreamBinding?: Pick<UpstreamConversationBinding, "conversationId" | "parentId"> | null;
|
|
29
|
+
};
|
|
30
|
+
interface ProviderTransport {
|
|
31
|
+
completeChat(request: ProviderTransportRequest): Promise<ProviderTransportResponse>;
|
|
32
|
+
}
|
|
33
|
+
type SessionBoundProviderAdapterOptions = {
|
|
34
|
+
providerId: string;
|
|
35
|
+
modelId: string;
|
|
36
|
+
transport: ProviderTransport;
|
|
37
|
+
sessionBindingStore?: SessionBindingStore;
|
|
38
|
+
sessionId?: string;
|
|
39
|
+
bridgeRequestId?: string;
|
|
40
|
+
onTraceEvent?: (type: string, detail: unknown) => void;
|
|
41
|
+
};
|
|
42
|
+
declare class SessionBoundProviderAdapter implements ProviderAdapter {
|
|
43
|
+
#private;
|
|
44
|
+
readonly id: string;
|
|
45
|
+
constructor(options: SessionBoundProviderAdapterOptions);
|
|
46
|
+
completeTurn(input: ProviderTurnInput): Promise<string>;
|
|
47
|
+
repairInvalidResponse(input: RepairInvalidResponseInput): Promise<string>;
|
|
48
|
+
}
|
|
49
|
+
export declare const sessionBoundProviderModule: {
|
|
50
|
+
SessionBoundProviderAdapter: typeof SessionBoundProviderAdapter;
|
|
51
|
+
};
|
|
52
|
+
export type { ProviderTransport, ProviderTransportRequest, ProviderTransportResponse, SessionBindingStore, SessionBoundProviderAdapter, SessionBoundProviderAdapterOptions, UpstreamConversationBinding };
|