@openclaw/copilot 2026.6.11 → 2026.7.1-beta.2
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
CHANGED
|
@@ -10,10 +10,11 @@ openclaw plugins install @openclaw/copilot
|
|
|
10
10
|
|
|
11
11
|
Restart the Gateway after installing or updating the plugin.
|
|
12
12
|
|
|
13
|
-
The harness claims the canonical subscription `github-copilot` provider
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
The harness claims the canonical subscription `github-copilot` provider plus
|
|
14
|
+
custom BYOK provider entries that the Copilot SDK can represent. Manifest-owned
|
|
15
|
+
native provider ids stay with their owning runtimes. The harness is opt-in only:
|
|
16
|
+
selection requires explicit `agentRuntime.id: "copilot"` on a model or provider
|
|
17
|
+
entry; `auto` never picks it. PI remains the default embedded runtime.
|
|
17
18
|
|
|
18
19
|
See [GitHub Copilot agent runtime](../../docs/plugins/copilot.md) for
|
|
19
20
|
configuration, the doctor contract, transcript mirroring, compaction, side
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { applyEmbeddedAttemptToolsAllow, awaitAgentEndSideEffects, buildAgentHarnessUserInputAnswers, buildAgentHookContextChannelFields, buildEmbeddedAttemptToolRunContext, clearActiveEmbeddedRun, deliverAgentHarnessUserInputPrompt, detectAndLoadAgentHarnessPromptImages, embeddedAgentLog, extractToolErrorMessage, getPluginToolMeta, isSubagentSessionKey, isToolResultError, resolveAgentHarnessBeforePromptBuildResult, resolveAttemptFsWorkspaceOnly, resolveAttemptSpawnWorkspaceDir, resolveBootstrapContextForRun, resolveCompactionTimeoutMs, resolveEmbeddedAttemptToolConstructionPlan, resolveModelAuthMode, resolveSandboxContext, resolveSessionAgentIds, resolveUserPath, runAgentEndSideEffects, runAgentHarnessAfterCompactionHook, runAgentHarnessAfterToolCallHook, runAgentHarnessBeforeCompactionHook, runAgentHarnessBeforeMessageWriteHook, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, sanitizeToolResult, setActiveEmbeddedRun } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
1
|
+
import { a as resolveCopilotAuth, i as createCopilotByokAuth, n as resolveCopilotProvider, r as createCopilotByokProxy } from "./harness-B9xB4LpE.js";
|
|
2
|
+
import { applyEmbeddedAttemptToolsAllow, awaitAgentEndSideEffects, buildAgentHarnessUserInputAnswers, buildAgentHookContextChannelFields, buildEmbeddedAttemptToolRunContext, clearActiveEmbeddedRun, deliverAgentHarnessUserInputPrompt, detectAndLoadAgentHarnessPromptImages, embeddedAgentLog, extractToolErrorMessage, getModelProviderRequestTransport, getPluginToolMeta, isSubagentSessionKey, isToolResultError, resolveAgentHarnessBeforePromptBuildResult, resolveAttemptFsWorkspaceOnly, resolveAttemptSpawnWorkspaceDir, resolveBootstrapContextForRun, resolveCompactionTimeoutMs, resolveEmbeddedAttemptToolConstructionPlan, resolveModelAuthMode, resolveSandboxContext, resolveSessionAgentIds, resolveUserPath, runAgentEndSideEffects, runAgentHarnessAfterCompactionHook, runAgentHarnessAfterToolCallHook, runAgentHarnessBeforeCompactionHook, runAgentHarnessBeforeMessageWriteHook, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, sanitizeToolResult, setActiveEmbeddedRun } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import fsp from "node:fs/promises";
|
|
@@ -242,6 +242,7 @@ function attachEventBridge(session, options) {
|
|
|
242
242
|
let detached = false;
|
|
243
243
|
const unsubscribeFns = [];
|
|
244
244
|
registerListener(session, unsubscribeFns, "assistant.message_delta", (event) => {
|
|
245
|
+
if (!isRootSessionEvent(event)) return;
|
|
245
246
|
const messageId = readString$2(event.data.messageId) ?? "assistant-message";
|
|
246
247
|
const delta = event.data.deltaContent;
|
|
247
248
|
if (!delta) return;
|
|
@@ -264,6 +265,7 @@ function attachEventBridge(session, options) {
|
|
|
264
265
|
deltaChain.catch(() => void 0);
|
|
265
266
|
});
|
|
266
267
|
registerListener(session, unsubscribeFns, "assistant.reasoning_delta", (event) => {
|
|
268
|
+
if (!isRootSessionEvent(event)) return;
|
|
267
269
|
const reasoningId = readString$2(event.data.reasoningId) ?? "assistant-reasoning";
|
|
268
270
|
const delta = event.data.deltaContent;
|
|
269
271
|
if (!delta) return;
|
|
@@ -274,20 +276,22 @@ function attachEventBridge(session, options) {
|
|
|
274
276
|
reasoningById.set(reasoningId, `${reasoningById.get(reasoningId) ?? ""}${delta}`);
|
|
275
277
|
});
|
|
276
278
|
registerListener(session, unsubscribeFns, "assistant.message", (event) => {
|
|
279
|
+
if (!isRootSessionEvent(event)) return;
|
|
277
280
|
lastAssistantEvent = event;
|
|
278
281
|
const entry = ensureMessageAccumulator(messagesById, messageOrder, event.data.messageId);
|
|
279
282
|
if (typeof event.data.content === "string" && event.data.content.length >= entry.text.length) entry.text = event.data.content;
|
|
280
283
|
});
|
|
281
284
|
registerListener(session, unsubscribeFns, "assistant.usage", (event) => {
|
|
285
|
+
if (!isRootSessionEvent(event)) return;
|
|
282
286
|
usage = normalizeCopilotUsage(event.data);
|
|
283
287
|
});
|
|
284
288
|
registerListener(session, unsubscribeFns, "tool.execution_start", (event) => {
|
|
285
|
-
startedCount += 1;
|
|
289
|
+
if (isRootSessionEvent(event)) startedCount += 1;
|
|
286
290
|
toolNamesByCallId.set(event.data.toolCallId, event.data.toolName);
|
|
287
291
|
toolMetas.push({ toolName: event.data.toolName });
|
|
288
292
|
});
|
|
289
293
|
registerListener(session, unsubscribeFns, "tool.execution_complete", (event) => {
|
|
290
|
-
completedCount += 1;
|
|
294
|
+
if (isRootSessionEvent(event)) completedCount += 1;
|
|
291
295
|
const toolName = toolNamesByCallId.get(event.data.toolCallId);
|
|
292
296
|
const meta = event.data.success ? event.data.result?.detailedContent ?? event.data.result?.content : event.data.error?.message;
|
|
293
297
|
if (toolName) toolMetas.push({
|
|
@@ -324,6 +328,22 @@ function attachEventBridge(session, options) {
|
|
|
324
328
|
}
|
|
325
329
|
});
|
|
326
330
|
});
|
|
331
|
+
registerListener(session, unsubscribeFns, "exit_plan_mode.completed", (event) => {
|
|
332
|
+
enqueueAgentEvent({
|
|
333
|
+
stream: "plan",
|
|
334
|
+
data: {
|
|
335
|
+
phase: "update",
|
|
336
|
+
title: "Plan decision",
|
|
337
|
+
source: "copilot-sdk",
|
|
338
|
+
requestId: event.data.requestId,
|
|
339
|
+
...event.data.approved !== void 0 ? { approved: event.data.approved } : {},
|
|
340
|
+
...event.data.autoApproveEdits !== void 0 ? { autoApproveEdits: event.data.autoApproveEdits } : {},
|
|
341
|
+
...event.data.feedback ? { feedback: event.data.feedback } : {},
|
|
342
|
+
...event.data.selectedAction ? { selectedAction: event.data.selectedAction } : {},
|
|
343
|
+
...event.agentId ? { agentId: event.agentId } : {}
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
});
|
|
327
347
|
registerListener(session, unsubscribeFns, "subagent.started", (event) => {
|
|
328
348
|
forwardNativeSubagentEvent(event);
|
|
329
349
|
});
|
|
@@ -520,9 +540,12 @@ function finalizeAssistantTexts(messageOrder, messagesById, event) {
|
|
|
520
540
|
function isAssistantMessageEvent(event) {
|
|
521
541
|
return event?.type === "assistant.message";
|
|
522
542
|
}
|
|
523
|
-
function
|
|
543
|
+
function isRootSessionEvent(event) {
|
|
524
544
|
return event.agentId === void 0;
|
|
525
545
|
}
|
|
546
|
+
function isRootCompactionEvent(event) {
|
|
547
|
+
return isRootSessionEvent(event);
|
|
548
|
+
}
|
|
526
549
|
function joinReasoning(order, reasoningById) {
|
|
527
550
|
return order.map((reasoningId) => reasoningById.get(reasoningId) ?? "").join("");
|
|
528
551
|
}
|
|
@@ -964,7 +987,7 @@ function supportsModelTools(modelProvider) {
|
|
|
964
987
|
return SUPPORTED_TOOL_PROVIDERS.has(modelProvider);
|
|
965
988
|
}
|
|
966
989
|
async function createCopilotToolBridge(input) {
|
|
967
|
-
if (!supportsModelTools(input.modelProvider)) return {
|
|
990
|
+
if (!input.allowModelTools && !supportsModelTools(input.modelProvider)) return {
|
|
968
991
|
sdkTools: [],
|
|
969
992
|
sourceTools: []
|
|
970
993
|
};
|
|
@@ -1671,7 +1694,6 @@ function readResolvedWorkspacePath(value) {
|
|
|
1671
1694
|
}
|
|
1672
1695
|
//#endregion
|
|
1673
1696
|
//#region extensions/copilot/src/attempt.ts
|
|
1674
|
-
const SUPPORTED_PROVIDERS = new Set(["github-copilot"]);
|
|
1675
1697
|
const BACKGROUND_COMPACTION_CANCEL_TIMEOUT_MS = 5e3;
|
|
1676
1698
|
const COPILOT_ASK_USER_AVAILABLE_TOOLS = ["builtin:ask_user"];
|
|
1677
1699
|
async function runCopilotAgentEndHook(params, hookParams) {
|
|
@@ -1735,6 +1757,7 @@ function deferBackgroundCompactionCleanup(params) {
|
|
|
1735
1757
|
await params.session.disconnect();
|
|
1736
1758
|
} catch {}
|
|
1737
1759
|
params.cleanupToolBridge?.();
|
|
1760
|
+
await params.cleanupByokProxy?.();
|
|
1738
1761
|
if (outcome !== "completed" && params.sdkSessionId) try {
|
|
1739
1762
|
await params.handle.client.deleteSession(params.sdkSessionId);
|
|
1740
1763
|
} catch {}
|
|
@@ -1812,13 +1835,21 @@ async function runCopilotAttempt(params, deps) {
|
|
|
1812
1835
|
sdkSessionId: void 0,
|
|
1813
1836
|
sessionIdUsed: input.sessionId
|
|
1814
1837
|
}));
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
})
|
|
1838
|
+
try {
|
|
1839
|
+
resolveCopilotProvider({
|
|
1840
|
+
model: modelRef,
|
|
1841
|
+
resolvedApiKey: readString(params.resolvedApiKey),
|
|
1842
|
+
authProfileId: readString(params.authProfileId)
|
|
1843
|
+
});
|
|
1844
|
+
} catch (error) {
|
|
1845
|
+
return finishAttempt(createResult(input, {
|
|
1846
|
+
messagesSnapshot: messages,
|
|
1847
|
+
now,
|
|
1848
|
+
promptError: createPromptError("model_not_supported", toError(error).message, error),
|
|
1849
|
+
sdkSessionId: void 0,
|
|
1850
|
+
sessionIdUsed: input.sessionId
|
|
1851
|
+
}));
|
|
1852
|
+
}
|
|
1822
1853
|
let abortRequested = false;
|
|
1823
1854
|
let aborted = false;
|
|
1824
1855
|
let externalAbort = false;
|
|
@@ -1913,11 +1944,26 @@ async function runCopilotAttempt(params, deps) {
|
|
|
1913
1944
|
resolvedWorkspace: resolvedWorkspaceForSandbox
|
|
1914
1945
|
}) : void 0;
|
|
1915
1946
|
const poolAcquire = resolvePoolAcquire(input);
|
|
1947
|
+
let byokProxy;
|
|
1948
|
+
try {
|
|
1949
|
+
byokProxy = await createCopilotByokProxy(poolAcquire.provider);
|
|
1950
|
+
} catch (error) {
|
|
1951
|
+
return finishAttempt(createResult(input, {
|
|
1952
|
+
messagesSnapshot: messages,
|
|
1953
|
+
now,
|
|
1954
|
+
promptError: createPromptError("model_not_supported", toError(error).message, error),
|
|
1955
|
+
sdkSessionId: void 0,
|
|
1956
|
+
sessionIdUsed: input.sessionId
|
|
1957
|
+
}));
|
|
1958
|
+
}
|
|
1959
|
+
const cleanupByokProxy = byokProxy?.close;
|
|
1960
|
+
const sessionProvider = byokProxy?.provider ?? poolAcquire.provider;
|
|
1916
1961
|
const sessionRef = { current: void 0 };
|
|
1917
1962
|
try {
|
|
1918
1963
|
let sdkTools;
|
|
1919
1964
|
try {
|
|
1920
1965
|
const toolBridge = await createToolBridge({
|
|
1966
|
+
allowModelTools: poolAcquire.provider.mode === "byok",
|
|
1921
1967
|
modelProvider: modelRef.provider,
|
|
1922
1968
|
modelId: modelRef.id,
|
|
1923
1969
|
agentId: readString(params.agentId) ?? "copilot",
|
|
@@ -1974,6 +2020,7 @@ async function runCopilotAttempt(params, deps) {
|
|
|
1974
2020
|
developerInstructions: originalDeveloperInstructions,
|
|
1975
2021
|
messages,
|
|
1976
2022
|
ctx: hookContext,
|
|
2023
|
+
bootstrapContextRunKind: input.bootstrapContextRunKind,
|
|
1977
2024
|
..."beforeAgentStartResult" in input ? { beforeAgentStartResult: input.beforeAgentStartResult } : {}
|
|
1978
2025
|
});
|
|
1979
2026
|
const attemptInput = promptBuild.prompt === input.prompt ? input : {
|
|
@@ -2003,7 +2050,8 @@ async function runCopilotAttempt(params, deps) {
|
|
|
2003
2050
|
signal: params.abortSignal
|
|
2004
2051
|
});
|
|
2005
2052
|
userInputBridgeRef = userInputBridge;
|
|
2006
|
-
const sessionConfig = createSessionConfig(attemptInput, modelRef.id, sdkTools, poolAcquire.auth, promptBuild.developerInstructions || void 0, effectiveWorkspaceDir, effectiveCwd, userInputBridge.onUserInputRequest, hasNativePromptHook ? { onUserPromptSubmitted: ({ additionalContext, prompt }) => emitLlmInput(prompt, additionalContext) } : void 0);
|
|
2053
|
+
const sessionConfig = createSessionConfig(attemptInput, modelRef.id, sdkTools, poolAcquire.auth, sessionProvider, promptBuild.developerInstructions || void 0, effectiveWorkspaceDir, effectiveCwd, userInputBridge.onUserInputRequest, hasNativePromptHook ? { onUserPromptSubmitted: ({ additionalContext, prompt }) => emitLlmInput(prompt, additionalContext) } : void 0);
|
|
2054
|
+
const compactionSessionConfig = byokProxy ? createSessionConfig(attemptInput, modelRef.id, sdkTools, poolAcquire.auth, poolAcquire.provider, promptBuild.developerInstructions || void 0, effectiveWorkspaceDir, effectiveCwd, userInputBridge.onUserInputRequest, hasNativePromptHook ? { onUserPromptSubmitted: ({ additionalContext, prompt }) => emitLlmInput(prompt, additionalContext) } : void 0) : sessionConfig;
|
|
2007
2055
|
const replayDecision = decideReplayAction({
|
|
2008
2056
|
sdkSessionId: input.initialReplayState?.sdkSessionId,
|
|
2009
2057
|
replayInvalid: input.initialReplayState?.replayInvalid
|
|
@@ -2026,6 +2074,7 @@ async function runCopilotAttempt(params, deps) {
|
|
|
2026
2074
|
sessionIdUsed = sdkSessionId ?? input.sessionId;
|
|
2027
2075
|
if (sdkSessionId && deps.onSessionEstablished) try {
|
|
2028
2076
|
deps.onSessionEstablished({
|
|
2077
|
+
compactionSessionConfig,
|
|
2029
2078
|
sdkSessionId,
|
|
2030
2079
|
pooledClient: handle,
|
|
2031
2080
|
sessionConfig
|
|
@@ -2078,6 +2127,7 @@ async function runCopilotAttempt(params, deps) {
|
|
|
2078
2127
|
const messageOptions = await createMessageOptions(attemptInput, {
|
|
2079
2128
|
effectiveCwd,
|
|
2080
2129
|
effectiveWorkspaceDir,
|
|
2130
|
+
provider: poolAcquire.provider,
|
|
2081
2131
|
sandbox,
|
|
2082
2132
|
workspaceOnly: effectiveFsWorkspaceOnly
|
|
2083
2133
|
});
|
|
@@ -2121,6 +2171,7 @@ async function runCopilotAttempt(params, deps) {
|
|
|
2121
2171
|
awaitSessionIdle: !bridge.hasObservedSessionIdle(),
|
|
2122
2172
|
bridge,
|
|
2123
2173
|
cleanupToolBridge,
|
|
2174
|
+
cleanupByokProxy,
|
|
2124
2175
|
finalizeNativeSubagents: () => nativeSubagentTaskMirror?.finalizeActiveRuns(),
|
|
2125
2176
|
handle,
|
|
2126
2177
|
pool: deps.pool,
|
|
@@ -2144,6 +2195,7 @@ async function runCopilotAttempt(params, deps) {
|
|
|
2144
2195
|
await bridge?.awaitAgentEventChain();
|
|
2145
2196
|
nativeSubagentTaskMirror?.finalizeActiveRuns();
|
|
2146
2197
|
cleanupToolBridge?.();
|
|
2198
|
+
await cleanupByokProxy?.();
|
|
2147
2199
|
bridge?.detach();
|
|
2148
2200
|
params.abortSignal?.removeEventListener("abort", onAbort);
|
|
2149
2201
|
if (session) try {
|
|
@@ -2299,7 +2351,7 @@ function createPromptError(code, message, cause) {
|
|
|
2299
2351
|
if (cause !== void 0) error.cause = cause;
|
|
2300
2352
|
return error;
|
|
2301
2353
|
}
|
|
2302
|
-
function createSessionConfig(params, sdkModelId, sdkTools, resolvedAuth, systemMessageContent, effectiveWorkspaceDir, effectiveCwd, onUserInputRequest, hooksBridgeOptions) {
|
|
2354
|
+
function createSessionConfig(params, sdkModelId, sdkTools, resolvedAuth, resolvedProvider, systemMessageContent, effectiveWorkspaceDir, effectiveCwd, onUserInputRequest, hooksBridgeOptions) {
|
|
2303
2355
|
const permissionPolicy = params.permissionPolicy ?? rejectAllPolicy;
|
|
2304
2356
|
const hooks = createHooksBridge(params.hooksConfig, hooksBridgeOptions);
|
|
2305
2357
|
const infiniteSessions = createInfiniteSessionConfig(params.infiniteSessionConfig);
|
|
@@ -2307,6 +2359,7 @@ function createSessionConfig(params, sdkModelId, sdkTools, resolvedAuth, systemM
|
|
|
2307
2359
|
model: sdkModelId,
|
|
2308
2360
|
onPermissionRequest: createPermissionBridge(permissionPolicy),
|
|
2309
2361
|
onUserInputRequest,
|
|
2362
|
+
...resolvedProvider.provider ? { provider: resolvedProvider.provider } : {},
|
|
2310
2363
|
...hooks ? { hooks } : {},
|
|
2311
2364
|
...typeof params.enableSessionTelemetry === "boolean" ? { enableSessionTelemetry: params.enableSessionTelemetry } : {},
|
|
2312
2365
|
...infiniteSessions ? { infiniteSessions } : {},
|
|
@@ -2327,10 +2380,16 @@ function buildCopilotAvailableTools(sdkTools) {
|
|
|
2327
2380
|
}
|
|
2328
2381
|
async function createMessageOptions(params, context) {
|
|
2329
2382
|
const attachments = createPromptImageAttachments(await resolvePromptImages(params, context));
|
|
2330
|
-
|
|
2383
|
+
const requestHeaders = resolveProviderRequestHeaders(context.provider);
|
|
2384
|
+
return {
|
|
2331
2385
|
prompt: params.prompt,
|
|
2332
|
-
attachments
|
|
2333
|
-
|
|
2386
|
+
...attachments.length > 0 ? { attachments } : {},
|
|
2387
|
+
...requestHeaders ? { requestHeaders } : {}
|
|
2388
|
+
};
|
|
2389
|
+
}
|
|
2390
|
+
function resolveProviderRequestHeaders(provider) {
|
|
2391
|
+
const headers = provider.provider?.headers;
|
|
2392
|
+
return headers && Object.keys(headers).length > 0 ? { ...headers } : void 0;
|
|
2334
2393
|
}
|
|
2335
2394
|
function createPromptImageAttachments(images) {
|
|
2336
2395
|
return images.flatMap((image, index) => {
|
|
@@ -2373,7 +2432,7 @@ function createSystemMessageContent(params, workspaceBootstrapInstructions) {
|
|
|
2373
2432
|
if (bootstrap) sections.push(bootstrap);
|
|
2374
2433
|
const extraSystemPrompt = readString(params.extraSystemPrompt)?.trim();
|
|
2375
2434
|
if (extraSystemPrompt && !isRawCopilotModelRun(params)) {
|
|
2376
|
-
const contextHeader = params.promptMode === "minimal" ? "## Subagent Context" : "##
|
|
2435
|
+
const contextHeader = params.promptMode === "minimal" ? "## Subagent Context" : "## Conversation Context";
|
|
2377
2436
|
sections.push(`${contextHeader}\n${extraSystemPrompt}`);
|
|
2378
2437
|
}
|
|
2379
2438
|
return sections.length > 0 ? sections.join("\n\n") : void 0;
|
|
@@ -2416,19 +2475,47 @@ function readResolvedAttemptPath(value) {
|
|
|
2416
2475
|
return resolveUserPath(raw);
|
|
2417
2476
|
}
|
|
2418
2477
|
function resolveModelRef(params) {
|
|
2419
|
-
const rawModel = params.model;
|
|
2420
|
-
if (rawModel && typeof rawModel === "object")
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2478
|
+
const rawModel = params.runtimeModel ?? params.model;
|
|
2479
|
+
if (rawModel && typeof rawModel === "object") {
|
|
2480
|
+
const model = rawModel;
|
|
2481
|
+
const requestTransport = getModelProviderRequestTransport(rawModel);
|
|
2482
|
+
const rawRequest = model.request;
|
|
2483
|
+
return {
|
|
2484
|
+
api: readString(model.api),
|
|
2485
|
+
id: readString(model.id) ?? readString(params.modelId) ?? "unknown-model",
|
|
2486
|
+
provider: readString(model.provider) ?? readString(params.provider) ?? "unknown-provider",
|
|
2487
|
+
baseUrl: readString(model.baseUrl),
|
|
2488
|
+
azureApiVersion: readString(model.azureApiVersion ?? model.params?.azureApiVersion),
|
|
2489
|
+
headers: model.headers,
|
|
2490
|
+
authHeader: model.authHeader,
|
|
2491
|
+
requestAuthMode: readString(requestTransport?.auth?.mode ?? rawRequest?.auth?.mode),
|
|
2492
|
+
requestProxy: requestTransport?.proxy ?? rawRequest?.proxy,
|
|
2493
|
+
requestTls: requestTransport?.tls ?? rawRequest?.tls,
|
|
2494
|
+
requestAllowPrivateNetwork: requestTransport?.allowPrivateNetwork ?? rawRequest?.allowPrivateNetwork,
|
|
2495
|
+
contextTokens: model.contextTokens,
|
|
2496
|
+
contextWindow: model.contextWindow,
|
|
2497
|
+
maxTokens: model.maxTokens
|
|
2498
|
+
};
|
|
2499
|
+
}
|
|
2425
2500
|
return {
|
|
2426
2501
|
id: readString(typeof rawModel === "string" ? rawModel : void 0) ?? readString(params.modelId) ?? "unknown-model",
|
|
2427
2502
|
provider: readString(params.provider) ?? "unknown-provider"
|
|
2428
2503
|
};
|
|
2429
2504
|
}
|
|
2430
2505
|
function resolvePoolAcquire(params) {
|
|
2431
|
-
const
|
|
2506
|
+
const provider = resolveCopilotProvider({
|
|
2507
|
+
model: resolveModelRef(params),
|
|
2508
|
+
resolvedApiKey: readString(params.resolvedApiKey),
|
|
2509
|
+
authProfileId: readString(params.authProfileId)
|
|
2510
|
+
});
|
|
2511
|
+
const auth = provider.mode === "byok" ? createCopilotByokAuth({
|
|
2512
|
+
agentId: readString(params.agentId),
|
|
2513
|
+
agentDir: readString(params.agentDir),
|
|
2514
|
+
workspaceDir: readString(params.workspaceDir),
|
|
2515
|
+
copilotHome: readString(params.copilotHome),
|
|
2516
|
+
authProfileId: provider.authProfileId,
|
|
2517
|
+
authProfileVersion: provider.authProfileVersion
|
|
2518
|
+
}) : resolveCopilotAuth({
|
|
2432
2519
|
agentId: readString(params.agentId),
|
|
2433
2520
|
agentDir: readString(params.agentDir),
|
|
2434
2521
|
workspaceDir: readString(params.workspaceDir),
|
|
@@ -2440,20 +2527,21 @@ function resolvePoolAcquire(params) {
|
|
|
2440
2527
|
});
|
|
2441
2528
|
return {
|
|
2442
2529
|
key: {
|
|
2443
|
-
agentId:
|
|
2444
|
-
authMode:
|
|
2445
|
-
...
|
|
2446
|
-
authProfileId:
|
|
2447
|
-
authProfileVersion:
|
|
2530
|
+
agentId: auth.agentId,
|
|
2531
|
+
authMode: auth.authMode,
|
|
2532
|
+
...auth.authMode === "gitHubToken" || auth.authMode === "byok" ? {
|
|
2533
|
+
authProfileId: auth.authProfileId,
|
|
2534
|
+
authProfileVersion: auth.authProfileVersion
|
|
2448
2535
|
} : {},
|
|
2449
|
-
copilotHome:
|
|
2536
|
+
copilotHome: auth.copilotHome
|
|
2450
2537
|
},
|
|
2451
2538
|
options: {
|
|
2452
|
-
copilotHome:
|
|
2453
|
-
|
|
2454
|
-
useLoggedInUser:
|
|
2539
|
+
copilotHome: auth.copilotHome,
|
|
2540
|
+
...auth.authMode === "gitHubToken" && auth.gitHubToken ? { gitHubToken: auth.gitHubToken } : {},
|
|
2541
|
+
useLoggedInUser: auth.authMode === "useLoggedInUser"
|
|
2455
2542
|
},
|
|
2456
|
-
auth
|
|
2543
|
+
auth,
|
|
2544
|
+
provider
|
|
2457
2545
|
};
|
|
2458
2546
|
}
|
|
2459
2547
|
function toError(error) {
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import { buildAgentHookContextChannelFields, compactWithSafetyTimeout, resolveCompactionTimeoutMs, runAgentHarnessAfterCompactionHook, runAgentHarnessBeforeCompactionHook } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
2
|
-
import { createHash } from "node:crypto";
|
|
1
|
+
import { buildAgentHookContextChannelFields, compactWithSafetyTimeout, getModelProviderRequestTransport, resolveCompactionTimeoutMs, runAgentHarnessAfterCompactionHook, runAgentHarnessBeforeCompactionHook } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
2
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
|
+
import { createServer } from "node:http";
|
|
6
|
+
import { Readable } from "node:stream";
|
|
7
|
+
import { finished } from "node:stream/promises";
|
|
8
|
+
import { fetchWithSsrFGuard, isBlockedHostnameOrIp } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
9
|
+
import { isNonSecretApiKeyMarker } from "openclaw/plugin-sdk/provider-auth";
|
|
5
10
|
//#region extensions/copilot/src/auth-bridge.ts
|
|
6
11
|
/**
|
|
7
12
|
* Pure functional auth resolver for the copilot agent runtime.
|
|
@@ -48,6 +53,22 @@ import { join, resolve } from "node:path";
|
|
|
48
53
|
*/
|
|
49
54
|
const COPILOT_TOKEN_PROFILE_ERROR = "[copilot-attempt] gitHubToken auth requires profileId+profileVersion (pool keying safety; per Q5/Q1 decisions)";
|
|
50
55
|
const COPILOT_DEFAULT_AGENT_ID = "copilot";
|
|
56
|
+
function createCopilotByokAuth(input) {
|
|
57
|
+
return {
|
|
58
|
+
...resolveCopilotAuth({
|
|
59
|
+
agentId: input.agentId,
|
|
60
|
+
agentDir: input.agentDir,
|
|
61
|
+
workspaceDir: input.workspaceDir,
|
|
62
|
+
copilotHome: input.copilotHome,
|
|
63
|
+
env: input.env,
|
|
64
|
+
homeDir: input.homeDir,
|
|
65
|
+
auth: { useLoggedInUser: true }
|
|
66
|
+
}),
|
|
67
|
+
authMode: "byok",
|
|
68
|
+
authProfileId: input.authProfileId?.trim() || "byok:resolved",
|
|
69
|
+
authProfileVersion: input.authProfileVersion?.trim() || "byok:unfingerprinted"
|
|
70
|
+
};
|
|
71
|
+
}
|
|
51
72
|
/**
|
|
52
73
|
* Resolve copilot auth + copilotHome.
|
|
53
74
|
*
|
|
@@ -63,16 +84,16 @@ function resolveCopilotAuth(input) {
|
|
|
63
84
|
const homeDir = input.homeDir ?? homedir;
|
|
64
85
|
const agentId = sanitizeAgentId(input.agentId);
|
|
65
86
|
const copilotHome = resolveCopilotHome({
|
|
66
|
-
explicit: readString(input.copilotHome),
|
|
67
|
-
agentDir: readString(input.agentDir),
|
|
68
|
-
workspaceDir: readString(input.workspaceDir),
|
|
87
|
+
explicit: readString$1(input.copilotHome),
|
|
88
|
+
agentDir: readString$1(input.agentDir),
|
|
89
|
+
workspaceDir: readString$1(input.workspaceDir),
|
|
69
90
|
agentId,
|
|
70
91
|
env,
|
|
71
92
|
homeDir
|
|
72
93
|
});
|
|
73
|
-
const explicitToken = readString(input.auth?.gitHubToken);
|
|
74
|
-
const explicitProfileId = readString(input.auth?.profileId) ?? readString(input.authProfileId);
|
|
75
|
-
const explicitProfileVersion = readString(input.auth?.profileVersion) ?? readString(input.profileVersion);
|
|
94
|
+
const explicitToken = readString$1(input.auth?.gitHubToken);
|
|
95
|
+
const explicitProfileId = readString$1(input.auth?.profileId) ?? readString$1(input.authProfileId);
|
|
96
|
+
const explicitProfileVersion = readString$1(input.auth?.profileVersion) ?? readString$1(input.profileVersion);
|
|
76
97
|
if (input.auth?.useLoggedInUser === true) return {
|
|
77
98
|
authMode: "useLoggedInUser",
|
|
78
99
|
copilotHome,
|
|
@@ -89,11 +110,11 @@ function resolveCopilotAuth(input) {
|
|
|
89
110
|
agentId
|
|
90
111
|
};
|
|
91
112
|
}
|
|
92
|
-
const contractToken = readString(input.resolvedApiKey);
|
|
113
|
+
const contractToken = readString$1(input.resolvedApiKey);
|
|
93
114
|
if (contractToken) return {
|
|
94
115
|
authMode: "gitHubToken",
|
|
95
116
|
gitHubToken: contractToken,
|
|
96
|
-
authProfileId: readString(input.authProfileId) ?? "pi:resolved",
|
|
117
|
+
authProfileId: readString$1(input.authProfileId) ?? "pi:resolved",
|
|
97
118
|
authProfileVersion: tokenFingerprint(contractToken),
|
|
98
119
|
copilotHome,
|
|
99
120
|
agentId
|
|
@@ -134,7 +155,7 @@ function sanitizeAgentId(value) {
|
|
|
134
155
|
function resolveCopilotHome(args) {
|
|
135
156
|
if (args.explicit) return resolve(args.explicit);
|
|
136
157
|
if (args.agentDir) return resolve(join(args.agentDir, "copilot"));
|
|
137
|
-
const openClawHome = readString(args.env.OPENCLAW_HOME);
|
|
158
|
+
const openClawHome = readString$1(args.env.OPENCLAW_HOME);
|
|
138
159
|
return resolve(join(openClawHome ? resolve(openClawHome) : safeHomeDir(args.homeDir), ".openclaw", "agents", args.agentId, "copilot"));
|
|
139
160
|
}
|
|
140
161
|
function safeHomeDir(homeDir) {
|
|
@@ -148,19 +169,19 @@ function readEnvTokenFallback(env) {
|
|
|
148
169
|
const candidates = [
|
|
149
170
|
{
|
|
150
171
|
name: "OPENCLAW_GITHUB_TOKEN",
|
|
151
|
-
value: readString(env.OPENCLAW_GITHUB_TOKEN)
|
|
172
|
+
value: readString$1(env.OPENCLAW_GITHUB_TOKEN)
|
|
152
173
|
},
|
|
153
174
|
{
|
|
154
175
|
name: "COPILOT_GITHUB_TOKEN",
|
|
155
|
-
value: readString(env.COPILOT_GITHUB_TOKEN)
|
|
176
|
+
value: readString$1(env.COPILOT_GITHUB_TOKEN)
|
|
156
177
|
},
|
|
157
178
|
{
|
|
158
179
|
name: "GH_TOKEN",
|
|
159
|
-
value: readString(env.GH_TOKEN)
|
|
180
|
+
value: readString$1(env.GH_TOKEN)
|
|
160
181
|
},
|
|
161
182
|
{
|
|
162
183
|
name: "GITHUB_TOKEN",
|
|
163
|
-
value: readString(env.GITHUB_TOKEN)
|
|
184
|
+
value: readString$1(env.GITHUB_TOKEN)
|
|
164
185
|
}
|
|
165
186
|
];
|
|
166
187
|
for (const { name, value } of candidates) if (value) return {
|
|
@@ -180,15 +201,382 @@ function readEnvTokenFallback(env) {
|
|
|
180
201
|
function tokenFingerprint(token) {
|
|
181
202
|
return `sha256:${createHash("sha256").update(token).digest("hex").slice(0, 12)}`;
|
|
182
203
|
}
|
|
183
|
-
function readString(value) {
|
|
204
|
+
function readString$1(value) {
|
|
184
205
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
185
206
|
}
|
|
186
207
|
//#endregion
|
|
208
|
+
//#region extensions/copilot/src/byok-proxy.ts
|
|
209
|
+
const LOOPBACK_HOST = "127.0.0.1";
|
|
210
|
+
async function createCopilotByokProxy(resolvedProvider) {
|
|
211
|
+
if (resolvedProvider.mode !== "byok") return;
|
|
212
|
+
const providerConfig = resolvedProvider.provider;
|
|
213
|
+
if (!providerConfig?.baseUrl) throw new Error("[copilot-attempt] BYOK requires a provider baseUrl");
|
|
214
|
+
const targetBaseUrl = new URL(providerConfig.baseUrl);
|
|
215
|
+
const nonce = randomBytes(12).toString("hex");
|
|
216
|
+
const targetPathPrefix = trimTrailingSlash(targetBaseUrl.pathname);
|
|
217
|
+
const proxyPathPrefix = `/${nonce}${targetPathPrefix}`;
|
|
218
|
+
const acceptsAzureSdkPaths = providerConfig.type === "azure";
|
|
219
|
+
const activeFetches = /* @__PURE__ */ new Set();
|
|
220
|
+
const server = createServer((req, res) => {
|
|
221
|
+
handleProxyRequest(req, res, {
|
|
222
|
+
acceptsAzureSdkPaths,
|
|
223
|
+
activeFetches,
|
|
224
|
+
proxyPathPrefix,
|
|
225
|
+
targetBaseUrl,
|
|
226
|
+
targetPathPrefix
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
await new Promise((resolve, reject) => {
|
|
230
|
+
server.once("error", reject);
|
|
231
|
+
server.listen(0, LOOPBACK_HOST, () => {
|
|
232
|
+
server.off("error", reject);
|
|
233
|
+
resolve();
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
const address = server.address();
|
|
237
|
+
if (!address || typeof address === "string") {
|
|
238
|
+
server.close();
|
|
239
|
+
throw new Error("[copilot-attempt] failed to start BYOK network proxy");
|
|
240
|
+
}
|
|
241
|
+
const proxyBaseUrl = `http://${LOOPBACK_HOST}:${address.port}${proxyPathPrefix}`;
|
|
242
|
+
const sdkBaseUrl = acceptsAzureSdkPaths ? `http://${LOOPBACK_HOST}:${address.port}` : proxyBaseUrl;
|
|
243
|
+
return {
|
|
244
|
+
provider: {
|
|
245
|
+
...resolvedProvider,
|
|
246
|
+
provider: {
|
|
247
|
+
...providerConfig,
|
|
248
|
+
baseUrl: sdkBaseUrl
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
close: async () => {
|
|
252
|
+
for (const controller of activeFetches) controller.abort();
|
|
253
|
+
await new Promise((resolve) => {
|
|
254
|
+
server.close(() => resolve());
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
async function handleProxyRequest(req, res, params) {
|
|
260
|
+
let guarded;
|
|
261
|
+
const upstreamAbort = new AbortController();
|
|
262
|
+
params.activeFetches.add(upstreamAbort);
|
|
263
|
+
const abortUpstream = () => upstreamAbort.abort();
|
|
264
|
+
req.on("aborted", abortUpstream);
|
|
265
|
+
res.on("close", () => {
|
|
266
|
+
if (!res.writableEnded) abortUpstream();
|
|
267
|
+
});
|
|
268
|
+
try {
|
|
269
|
+
const url = resolveTargetUrl(req, params);
|
|
270
|
+
if (!url) {
|
|
271
|
+
res.writeHead(404);
|
|
272
|
+
res.end("Not found");
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const body = req.method === "GET" || req.method === "HEAD" ? void 0 : await readBody(req);
|
|
276
|
+
guarded = await fetchWithSsrFGuard({
|
|
277
|
+
url: url.toString(),
|
|
278
|
+
init: {
|
|
279
|
+
method: req.method,
|
|
280
|
+
headers: normalizeProxyRequestHeaders(req.headers),
|
|
281
|
+
signal: upstreamAbort.signal,
|
|
282
|
+
...body ? { body: toFetchBody(body) } : {}
|
|
283
|
+
},
|
|
284
|
+
auditContext: "copilot-byok-provider",
|
|
285
|
+
requireHttps: true
|
|
286
|
+
});
|
|
287
|
+
res.writeHead(guarded.response.status, guarded.response.statusText, normalizeProxyResponseHeaders(guarded.response.headers));
|
|
288
|
+
if (!guarded.response.body) {
|
|
289
|
+
res.end();
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
await finished(Readable.fromWeb(guarded.response.body).pipe(res));
|
|
293
|
+
} catch (error) {
|
|
294
|
+
if (res.destroyed || res.writableEnded) return;
|
|
295
|
+
if (res.headersSent) {
|
|
296
|
+
res.destroy(error instanceof Error ? error : void 0);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
res.writeHead(502);
|
|
300
|
+
res.end(error instanceof Error ? error.message : "BYOK provider proxy failed");
|
|
301
|
+
} finally {
|
|
302
|
+
req.off("aborted", abortUpstream);
|
|
303
|
+
params.activeFetches.delete(upstreamAbort);
|
|
304
|
+
await guarded?.release().catch(() => void 0);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
function resolveTargetUrl(req, params) {
|
|
308
|
+
const incomingUrl = new URL(req.url ?? "/", `http://${LOOPBACK_HOST}`);
|
|
309
|
+
if (incomingUrl.pathname !== params.proxyPathPrefix && !incomingUrl.pathname.startsWith(`${params.proxyPathPrefix}/`)) return params.acceptsAzureSdkPaths && isAzureSdkProxyPath(incomingUrl.pathname) ? resolveDirectTargetUrl(incomingUrl, params.targetBaseUrl) : void 0;
|
|
310
|
+
const suffix = incomingUrl.pathname.slice(params.proxyPathPrefix.length);
|
|
311
|
+
const targetUrl = new URL(params.targetBaseUrl);
|
|
312
|
+
targetUrl.pathname = `${params.targetPathPrefix}${suffix}` || "/";
|
|
313
|
+
for (const [key, value] of incomingUrl.searchParams) targetUrl.searchParams.append(key, value);
|
|
314
|
+
return targetUrl;
|
|
315
|
+
}
|
|
316
|
+
function resolveDirectTargetUrl(incomingUrl, targetBaseUrl) {
|
|
317
|
+
const targetUrl = new URL(targetBaseUrl);
|
|
318
|
+
targetUrl.pathname = incomingUrl.pathname;
|
|
319
|
+
for (const [key, value] of incomingUrl.searchParams) targetUrl.searchParams.append(key, value);
|
|
320
|
+
return targetUrl;
|
|
321
|
+
}
|
|
322
|
+
function isAzureSdkProxyPath(pathname) {
|
|
323
|
+
return pathname === "/openai" || pathname.startsWith("/openai/");
|
|
324
|
+
}
|
|
325
|
+
async function readBody(req) {
|
|
326
|
+
const chunks = [];
|
|
327
|
+
for await (const chunk of req) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
328
|
+
return chunks.length > 0 ? Buffer.concat(chunks) : void 0;
|
|
329
|
+
}
|
|
330
|
+
function toFetchBody(body) {
|
|
331
|
+
const copy = new Uint8Array(body.byteLength);
|
|
332
|
+
copy.set(body);
|
|
333
|
+
return copy;
|
|
334
|
+
}
|
|
335
|
+
function normalizeProxyRequestHeaders(headers) {
|
|
336
|
+
const out = {};
|
|
337
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
338
|
+
if (isHopByHopHeader(key) || key.toLowerCase() === "accept-encoding") continue;
|
|
339
|
+
const normalized = normalizeHeaderValue(value);
|
|
340
|
+
if (normalized !== void 0) out[key] = normalized;
|
|
341
|
+
}
|
|
342
|
+
out["accept-encoding"] = "identity";
|
|
343
|
+
return out;
|
|
344
|
+
}
|
|
345
|
+
function normalizeProxyResponseHeaders(headers) {
|
|
346
|
+
const out = {};
|
|
347
|
+
headers.forEach((value, key) => {
|
|
348
|
+
if (!isHopByHopHeader(key) && !isContentEncodingHeader(key)) out[key] = value;
|
|
349
|
+
});
|
|
350
|
+
return out;
|
|
351
|
+
}
|
|
352
|
+
function normalizeHeaderValue(value) {
|
|
353
|
+
if (value === void 0) return;
|
|
354
|
+
return Array.isArray(value) ? value.join(", ") : String(value);
|
|
355
|
+
}
|
|
356
|
+
function isHopByHopHeader(key) {
|
|
357
|
+
switch (key.toLowerCase()) {
|
|
358
|
+
case "connection":
|
|
359
|
+
case "host":
|
|
360
|
+
case "keep-alive":
|
|
361
|
+
case "proxy-authenticate":
|
|
362
|
+
case "proxy-authorization":
|
|
363
|
+
case "te":
|
|
364
|
+
case "trailer":
|
|
365
|
+
case "transfer-encoding":
|
|
366
|
+
case "upgrade": return true;
|
|
367
|
+
default: return false;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
function isContentEncodingHeader(key) {
|
|
371
|
+
switch (key.toLowerCase()) {
|
|
372
|
+
case "content-encoding":
|
|
373
|
+
case "content-length": return true;
|
|
374
|
+
default: return false;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
function trimTrailingSlash(pathname) {
|
|
378
|
+
const trimmed = pathname.replace(/\/+$/, "");
|
|
379
|
+
return trimmed === "" ? "" : trimmed;
|
|
380
|
+
}
|
|
381
|
+
//#endregion
|
|
382
|
+
//#region extensions/copilot/src/provider-bridge.ts
|
|
383
|
+
const COPILOT_BYOK_PROVIDER_ERROR = "[copilot-attempt] BYOK requires an OpenAI-compatible or Anthropic model api and a non-empty baseUrl";
|
|
384
|
+
const COPILOT_BYOK_TRANSPORT_POLICY_ERROR = "[copilot-attempt] BYOK does not support OpenClaw provider request proxy, TLS, or private-network policy overrides";
|
|
385
|
+
const COPILOT_BYOK_ENDPOINT_POLICY_ERROR = "[copilot-attempt] BYOK endpoint is blocked by OpenClaw SSRF policy";
|
|
386
|
+
const CREDENTIAL_QUERY_PARAM_NAMES = new Set([
|
|
387
|
+
"accesstoken",
|
|
388
|
+
"appsecret",
|
|
389
|
+
"auth",
|
|
390
|
+
"authtoken",
|
|
391
|
+
"apikey",
|
|
392
|
+
"authorization",
|
|
393
|
+
"clientsecret",
|
|
394
|
+
"code",
|
|
395
|
+
"credential",
|
|
396
|
+
"hooktoken",
|
|
397
|
+
"idtoken",
|
|
398
|
+
"jwt",
|
|
399
|
+
"key",
|
|
400
|
+
"pass",
|
|
401
|
+
"passwd",
|
|
402
|
+
"password",
|
|
403
|
+
"privatekey",
|
|
404
|
+
"refreshtoken",
|
|
405
|
+
"secret",
|
|
406
|
+
"session",
|
|
407
|
+
"sig",
|
|
408
|
+
"signature",
|
|
409
|
+
"token",
|
|
410
|
+
"xapikey",
|
|
411
|
+
"xaccesstoken",
|
|
412
|
+
"xamzsecuritytoken",
|
|
413
|
+
"xamzsignature",
|
|
414
|
+
"xauthtoken"
|
|
415
|
+
]);
|
|
416
|
+
const QUERY_PARAM_NAME_SEPARATOR_RE = /[\p{C}\p{Z}\u115F\u1160\u3164\uFFA0+]/gu;
|
|
417
|
+
/**
|
|
418
|
+
* Maps OpenClaw's prepared model facts into the Copilot SDK's session-level
|
|
419
|
+
* provider contract. The SDK owns the wire request; OpenClaw only supplies
|
|
420
|
+
* the already-resolved endpoint, model, headers, and credential.
|
|
421
|
+
*/
|
|
422
|
+
function resolveCopilotProvider(params) {
|
|
423
|
+
if (params.model.provider.trim().toLowerCase() === "github-copilot") return { mode: "github-copilot" };
|
|
424
|
+
const baseUrl = readString(params.model.baseUrl);
|
|
425
|
+
if (!baseUrl) throw new Error(COPILOT_BYOK_PROVIDER_ERROR);
|
|
426
|
+
assertByokEndpointAllowed(baseUrl);
|
|
427
|
+
if (hasUnsupportedTransportPolicy(params.model)) throw new Error(COPILOT_BYOK_TRANSPORT_POLICY_ERROR);
|
|
428
|
+
const api = readString(params.model.api)?.toLowerCase() ?? "openai-responses";
|
|
429
|
+
const provider = resolveProviderType(api, baseUrl, params.model.azureApiVersion);
|
|
430
|
+
const resolvedApiKey = resolveProviderCredential(params.resolvedApiKey);
|
|
431
|
+
const headers = resolveProviderHeaders(params.model.headers);
|
|
432
|
+
const requestAuthMode = readString(params.model.requestAuthMode)?.toLowerCase();
|
|
433
|
+
const usePreparedRequestAuth = requestAuthMode !== void 0 && requestAuthMode !== "provider-default";
|
|
434
|
+
return {
|
|
435
|
+
mode: "byok",
|
|
436
|
+
provider: {
|
|
437
|
+
type: provider.type,
|
|
438
|
+
...provider.wireApi ? { wireApi: provider.wireApi } : {},
|
|
439
|
+
baseUrl: provider.baseUrl,
|
|
440
|
+
modelId: params.model.id,
|
|
441
|
+
wireModel: params.model.id,
|
|
442
|
+
...resolvedApiKey && !usePreparedRequestAuth ? params.model.authHeader ? { bearerToken: resolvedApiKey } : { apiKey: resolvedApiKey } : {},
|
|
443
|
+
...headers ? { headers } : {},
|
|
444
|
+
...provider.azure ? { azure: provider.azure } : {},
|
|
445
|
+
...params.model.contextTokens ?? params.model.contextWindow ? { maxPromptTokens: params.model.contextTokens ?? params.model.contextWindow } : {},
|
|
446
|
+
...params.model.maxTokens ? { maxOutputTokens: params.model.maxTokens } : {}
|
|
447
|
+
},
|
|
448
|
+
authProfileId: params.authProfileId?.trim() || `byok:${params.model.provider}`,
|
|
449
|
+
authProfileVersion: tokenFingerprint(stableSerialize({
|
|
450
|
+
api,
|
|
451
|
+
baseUrl: provider.baseUrl,
|
|
452
|
+
azureApiVersion: provider.azure?.apiVersion,
|
|
453
|
+
headers,
|
|
454
|
+
authHeader: params.model.authHeader,
|
|
455
|
+
requestAuthMode: params.model.requestAuthMode,
|
|
456
|
+
apiKey: resolvedApiKey,
|
|
457
|
+
modelId: params.model.id,
|
|
458
|
+
maxPromptTokens: params.model.contextTokens ?? params.model.contextWindow,
|
|
459
|
+
maxOutputTokens: params.model.maxTokens
|
|
460
|
+
}))
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
function isCopilotByokUnsupportedProviderError(error) {
|
|
464
|
+
return error instanceof Error && (error.message === "[copilot-attempt] BYOK requires an OpenAI-compatible or Anthropic model api and a non-empty baseUrl" || error.message === "[copilot-attempt] BYOK does not support OpenClaw provider request proxy, TLS, or private-network policy overrides" || error.message === "[copilot-attempt] BYOK endpoint is blocked by OpenClaw SSRF policy");
|
|
465
|
+
}
|
|
466
|
+
function supportsCopilotByokProviderShape(model) {
|
|
467
|
+
if (!readString(model.baseUrl) || hasUnsupportedTransportPolicy(model)) return false;
|
|
468
|
+
try {
|
|
469
|
+
resolveProviderType(readString(model.api)?.toLowerCase() ?? "openai-responses", readString(model.baseUrl), void 0);
|
|
470
|
+
assertByokEndpointHostAllowed(readString(model.baseUrl));
|
|
471
|
+
return true;
|
|
472
|
+
} catch {
|
|
473
|
+
return false;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
function hasUnsupportedTransportPolicy(model) {
|
|
477
|
+
return model.requestProxy !== void 0 || model.requestTls !== void 0 || model.requestAllowPrivateNetwork !== void 0;
|
|
478
|
+
}
|
|
479
|
+
function assertByokEndpointHostAllowed(baseUrl) {
|
|
480
|
+
let url;
|
|
481
|
+
try {
|
|
482
|
+
url = new URL(baseUrl);
|
|
483
|
+
} catch {
|
|
484
|
+
throw new Error(COPILOT_BYOK_PROVIDER_ERROR);
|
|
485
|
+
}
|
|
486
|
+
if (url.protocol !== "https:") throw new Error(COPILOT_BYOK_ENDPOINT_POLICY_ERROR);
|
|
487
|
+
if (url.username || url.password) throw new Error(COPILOT_BYOK_ENDPOINT_POLICY_ERROR);
|
|
488
|
+
for (const key of url.searchParams.keys()) if (CREDENTIAL_QUERY_PARAM_NAMES.has(normalizeCredentialQueryParamName(key))) throw new Error(COPILOT_BYOK_ENDPOINT_POLICY_ERROR);
|
|
489
|
+
if (isBlockedHostnameOrIp(url.hostname.toLowerCase().replace(/\.+$/, ""))) throw new Error(COPILOT_BYOK_ENDPOINT_POLICY_ERROR);
|
|
490
|
+
}
|
|
491
|
+
function normalizeCredentialQueryParamName(name) {
|
|
492
|
+
const stripped = name.replace(QUERY_PARAM_NAME_SEPARATOR_RE, "");
|
|
493
|
+
try {
|
|
494
|
+
return decodeURIComponent(stripped).replace(QUERY_PARAM_NAME_SEPARATOR_RE, "").toLowerCase().replace(/[-_]/g, "");
|
|
495
|
+
} catch {
|
|
496
|
+
return stripped.toLowerCase().replace(/[-_]/g, "");
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
function assertByokEndpointAllowed(baseUrl) {
|
|
500
|
+
assertByokEndpointHostAllowed(baseUrl);
|
|
501
|
+
}
|
|
502
|
+
function resolveProviderType(api, baseUrl, azureApiVersion) {
|
|
503
|
+
switch (api) {
|
|
504
|
+
case "anthropic-messages": return {
|
|
505
|
+
type: "anthropic",
|
|
506
|
+
baseUrl
|
|
507
|
+
};
|
|
508
|
+
case "azure-openai-responses": return resolveAzureProviderType(baseUrl, azureApiVersion);
|
|
509
|
+
case "openai-responses": return {
|
|
510
|
+
type: "openai",
|
|
511
|
+
wireApi: "responses",
|
|
512
|
+
baseUrl
|
|
513
|
+
};
|
|
514
|
+
case "openai-completions":
|
|
515
|
+
case "ollama": return {
|
|
516
|
+
type: "openai",
|
|
517
|
+
wireApi: "completions",
|
|
518
|
+
baseUrl
|
|
519
|
+
};
|
|
520
|
+
default: throw new Error(COPILOT_BYOK_PROVIDER_ERROR);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
function resolveAzureProviderType(baseUrl, apiVersion) {
|
|
524
|
+
let url;
|
|
525
|
+
try {
|
|
526
|
+
url = new URL(baseUrl);
|
|
527
|
+
} catch {
|
|
528
|
+
throw new Error(COPILOT_BYOK_PROVIDER_ERROR);
|
|
529
|
+
}
|
|
530
|
+
if (isOpenAICompatibleAzureResponsesBaseUrl(url)) return {
|
|
531
|
+
type: "openai",
|
|
532
|
+
wireApi: "responses",
|
|
533
|
+
baseUrl
|
|
534
|
+
};
|
|
535
|
+
if (!isTraditionalAzureOpenAIHost(url.hostname)) throw new Error(COPILOT_BYOK_PROVIDER_ERROR);
|
|
536
|
+
url.pathname = "";
|
|
537
|
+
url.search = "";
|
|
538
|
+
url.hash = "";
|
|
539
|
+
const resolvedApiVersion = readString(apiVersion);
|
|
540
|
+
return {
|
|
541
|
+
type: "azure",
|
|
542
|
+
wireApi: "responses",
|
|
543
|
+
baseUrl: url.toString().replace(/\/+$/, ""),
|
|
544
|
+
...resolvedApiVersion ? { azure: { apiVersion: resolvedApiVersion } } : {}
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
function isTraditionalAzureOpenAIHost(hostname) {
|
|
548
|
+
return hostname.endsWith(".openai.azure.com") || hostname.endsWith(".cognitiveservices.azure.com");
|
|
549
|
+
}
|
|
550
|
+
function isOpenAICompatibleAzureResponsesBaseUrl(url) {
|
|
551
|
+
if (isTraditionalAzureOpenAIHost(url.hostname)) return false;
|
|
552
|
+
const hostname = url.hostname.toLowerCase();
|
|
553
|
+
if (!(hostname.endsWith(".services.ai.azure.com") || hostname.endsWith(".api.cognitive.microsoft.com"))) return false;
|
|
554
|
+
const normalizedPath = url.pathname.replace(/\/+$/, "");
|
|
555
|
+
return normalizedPath === "/openai/v1" || normalizedPath.endsWith("/openai/v1");
|
|
556
|
+
}
|
|
557
|
+
function stableSerialize(value) {
|
|
558
|
+
if (Array.isArray(value)) return `[${value.map(stableSerialize).join(",")}]`;
|
|
559
|
+
if (value && typeof value === "object") return `{${Object.entries(value).toSorted(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => `${JSON.stringify(key)}:${stableSerialize(entry)}`).join(",")}}`;
|
|
560
|
+
return JSON.stringify(value) ?? "null";
|
|
561
|
+
}
|
|
562
|
+
function readString(value) {
|
|
563
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
564
|
+
}
|
|
565
|
+
function resolveProviderCredential(value) {
|
|
566
|
+
const credential = readString(value);
|
|
567
|
+
return credential && !isNonSecretApiKeyMarker(credential) ? credential : void 0;
|
|
568
|
+
}
|
|
569
|
+
function resolveProviderHeaders(headers) {
|
|
570
|
+
if (!headers) return;
|
|
571
|
+
const resolved = Object.fromEntries(Object.entries(headers).filter(([, value]) => typeof value === "string"));
|
|
572
|
+
return Object.keys(resolved).length > 0 ? resolved : void 0;
|
|
573
|
+
}
|
|
574
|
+
//#endregion
|
|
187
575
|
//#region extensions/copilot/harness.ts
|
|
188
576
|
const COPILOT_PROVIDER_IDS = new Set(["github-copilot"]);
|
|
189
577
|
function sessionAuthFields(auth) {
|
|
190
|
-
return auth.authMode === "gitHubToken" ? {
|
|
191
|
-
authMode:
|
|
578
|
+
return auth.authMode === "gitHubToken" || auth.authMode === "byok" ? {
|
|
579
|
+
authMode: auth.authMode,
|
|
192
580
|
authProfileId: auth.authProfileId,
|
|
193
581
|
authProfileVersion: auth.authProfileVersion
|
|
194
582
|
} : { authMode: "useLoggedInUser" };
|
|
@@ -196,17 +584,17 @@ function sessionAuthFields(auth) {
|
|
|
196
584
|
function sessionAuthMatches(stored, current) {
|
|
197
585
|
if (stored.authMode !== current.authMode) return false;
|
|
198
586
|
if (stored.authMode === "useLoggedInUser") return true;
|
|
199
|
-
return current.authMode ===
|
|
587
|
+
return current.authMode === stored.authMode && stored.authProfileId === current.authProfileId && stored.authProfileVersion === current.authProfileVersion;
|
|
200
588
|
}
|
|
201
589
|
function normalizeBinding(value) {
|
|
202
|
-
if (!value || value.schemaVersion !== 2 || typeof value.sdkSessionId !== "string" || value.sdkSessionId.trim() === "" || typeof value.compatKey !== "string" || value.compatKey.trim() === "" || typeof value.compactKey !== "string" || value.compactKey.trim() === "" || value.authMode !== "gitHubToken" && value.authMode !== "useLoggedInUser" || value.authMode === "gitHubToken" && (typeof value.authProfileId !== "string" || value.authProfileId.trim() === "" || typeof value.authProfileVersion !== "string" || value.authProfileVersion.trim() === "") || typeof value.updatedAt !== "number" || !Number.isFinite(value.updatedAt)) return;
|
|
590
|
+
if (!value || value.schemaVersion !== 2 || typeof value.sdkSessionId !== "string" || value.sdkSessionId.trim() === "" || typeof value.compatKey !== "string" || value.compatKey.trim() === "" || typeof value.compactKey !== "string" || value.compactKey.trim() === "" || value.authMode !== "gitHubToken" && value.authMode !== "byok" && value.authMode !== "useLoggedInUser" || (value.authMode === "gitHubToken" || value.authMode === "byok") && (typeof value.authProfileId !== "string" || value.authProfileId.trim() === "" || typeof value.authProfileVersion !== "string" || value.authProfileVersion.trim() === "") || typeof value.updatedAt !== "number" || !Number.isFinite(value.updatedAt)) return;
|
|
203
591
|
return {
|
|
204
592
|
schemaVersion: 2,
|
|
205
593
|
sdkSessionId: value.sdkSessionId.trim(),
|
|
206
594
|
compatKey: value.compatKey,
|
|
207
595
|
compactKey: value.compactKey,
|
|
208
596
|
authMode: value.authMode,
|
|
209
|
-
...value.authMode === "gitHubToken" ? {
|
|
597
|
+
...value.authMode === "gitHubToken" || value.authMode === "byok" ? {
|
|
210
598
|
authProfileId: value.authProfileId,
|
|
211
599
|
authProfileVersion: value.authProfileVersion
|
|
212
600
|
} : {},
|
|
@@ -290,23 +678,61 @@ function readAgentIdFromSessionKey(sessionKey) {
|
|
|
290
678
|
}
|
|
291
679
|
function computeSessionKey(params, options) {
|
|
292
680
|
const p = params;
|
|
293
|
-
const modelObj = p.model && typeof p.model === "object" ? p.model : { id: typeof p.model === "string" ? p.model : void 0 };
|
|
681
|
+
const modelObj = p.model && typeof p.model === "object" ? p.model : p.runtimeModel && typeof p.runtimeModel === "object" ? p.runtimeModel : { id: typeof p.model === "string" ? p.model : void 0 };
|
|
294
682
|
const provider = modelObj.provider ?? (typeof p.provider === "string" ? p.provider : "");
|
|
295
683
|
const modelId = modelObj.id ?? (typeof p.modelId === "string" ? p.modelId : void 0) ?? (typeof p.model === "string" ? p.model : "");
|
|
684
|
+
const requestTransport = p.model && typeof p.model === "object" ? getModelProviderRequestTransport(p.model) : void 0;
|
|
685
|
+
const requestAuthMode = readSessionString(requestTransport?.auth?.mode ?? modelObj.request?.auth?.mode);
|
|
686
|
+
const azureApiVersion = readSessionString(modelObj.azureApiVersion ?? modelObj.params?.azureApiVersion);
|
|
296
687
|
let authParts;
|
|
297
688
|
let resolvedAgentId = "";
|
|
298
689
|
let resolvedCopilotHome = "";
|
|
299
690
|
try {
|
|
300
|
-
const resolved = resolveCopilotAuth({
|
|
691
|
+
const resolved = !options.includeAuth ? resolveCopilotAuth({
|
|
301
692
|
agentId: typeof p.agentId === "string" ? p.agentId : readAgentIdFromSessionKey(p.sessionKey),
|
|
302
693
|
agentDir: typeof p.agentDir === "string" ? p.agentDir : void 0,
|
|
303
694
|
workspaceDir: typeof p.workspaceDir === "string" ? p.workspaceDir : void 0,
|
|
304
695
|
copilotHome: typeof p.copilotHome === "string" ? p.copilotHome : void 0,
|
|
305
|
-
auth:
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
696
|
+
auth: { useLoggedInUser: true }
|
|
697
|
+
}) : (() => {
|
|
698
|
+
const modelProvider = resolveCopilotProvider({
|
|
699
|
+
model: {
|
|
700
|
+
api: modelObj.api,
|
|
701
|
+
id: modelId,
|
|
702
|
+
provider,
|
|
703
|
+
baseUrl: modelObj.baseUrl,
|
|
704
|
+
azureApiVersion,
|
|
705
|
+
headers: modelObj.headers,
|
|
706
|
+
authHeader: modelObj.authHeader,
|
|
707
|
+
requestAuthMode,
|
|
708
|
+
requestProxy: requestTransport?.proxy ?? modelObj.request?.proxy,
|
|
709
|
+
requestTls: requestTransport?.tls ?? modelObj.request?.tls,
|
|
710
|
+
requestAllowPrivateNetwork: requestTransport?.allowPrivateNetwork ?? modelObj.request?.allowPrivateNetwork,
|
|
711
|
+
contextTokens: modelObj.contextTokens,
|
|
712
|
+
contextWindow: modelObj.contextWindow,
|
|
713
|
+
maxTokens: modelObj.maxTokens
|
|
714
|
+
},
|
|
715
|
+
resolvedApiKey: typeof p.resolvedApiKey === "string" ? p.resolvedApiKey : void 0,
|
|
716
|
+
authProfileId: typeof p.authProfileId === "string" ? p.authProfileId : void 0
|
|
717
|
+
});
|
|
718
|
+
return modelProvider.mode === "byok" ? createCopilotByokAuth({
|
|
719
|
+
agentId: typeof p.agentId === "string" ? p.agentId : readAgentIdFromSessionKey(p.sessionKey),
|
|
720
|
+
agentDir: typeof p.agentDir === "string" ? p.agentDir : void 0,
|
|
721
|
+
workspaceDir: typeof p.workspaceDir === "string" ? p.workspaceDir : void 0,
|
|
722
|
+
copilotHome: typeof p.copilotHome === "string" ? p.copilotHome : void 0,
|
|
723
|
+
authProfileId: modelProvider.authProfileId,
|
|
724
|
+
authProfileVersion: modelProvider.authProfileVersion
|
|
725
|
+
}) : resolveCopilotAuth({
|
|
726
|
+
agentId: typeof p.agentId === "string" ? p.agentId : readAgentIdFromSessionKey(p.sessionKey),
|
|
727
|
+
agentDir: typeof p.agentDir === "string" ? p.agentDir : void 0,
|
|
728
|
+
workspaceDir: typeof p.workspaceDir === "string" ? p.workspaceDir : void 0,
|
|
729
|
+
copilotHome: typeof p.copilotHome === "string" ? p.copilotHome : void 0,
|
|
730
|
+
auth: p.auth,
|
|
731
|
+
resolvedApiKey: typeof p.resolvedApiKey === "string" ? p.resolvedApiKey : void 0,
|
|
732
|
+
authProfileId: typeof p.authProfileId === "string" ? p.authProfileId : void 0,
|
|
733
|
+
profileVersion: typeof p.profileVersion === "string" ? p.profileVersion : void 0
|
|
734
|
+
});
|
|
735
|
+
})();
|
|
310
736
|
resolvedAgentId = resolved.agentId;
|
|
311
737
|
resolvedCopilotHome = resolved.copilotHome;
|
|
312
738
|
authParts = [
|
|
@@ -314,6 +740,7 @@ function computeSessionKey(params, options) {
|
|
|
314
740
|
`auth.profileId=${resolved.authProfileId ?? ""}`,
|
|
315
741
|
`auth.profileVersion=${resolved.authProfileVersion ?? ""}`
|
|
316
742
|
];
|
|
743
|
+
if (!options.includeAuth) authParts = [];
|
|
317
744
|
} catch {
|
|
318
745
|
authParts = ["auth=unresolvable"];
|
|
319
746
|
}
|
|
@@ -321,6 +748,7 @@ function computeSessionKey(params, options) {
|
|
|
321
748
|
`provider=${provider}`,
|
|
322
749
|
`model=${modelId}`,
|
|
323
750
|
...options.includeApi ? [`api=${modelObj.api ?? ""}`] : [],
|
|
751
|
+
...options.includeApi ? [`baseUrlFingerprint=${fingerprintSessionValue(modelObj.baseUrl)}`] : [],
|
|
324
752
|
`cwd=${p.cwd ?? p.workspaceDir ?? ""}`,
|
|
325
753
|
`agentId=${resolvedAgentId}`,
|
|
326
754
|
`agentDir=${p.agentDir ?? ""}`,
|
|
@@ -329,6 +757,12 @@ function computeSessionKey(params, options) {
|
|
|
329
757
|
...options.includeAuth ? authParts : []
|
|
330
758
|
].join("|");
|
|
331
759
|
}
|
|
760
|
+
function readSessionString(value) {
|
|
761
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
762
|
+
}
|
|
763
|
+
function fingerprintSessionValue(value) {
|
|
764
|
+
return typeof value === "string" && value ? tokenFingerprint(value) : "";
|
|
765
|
+
}
|
|
332
766
|
function computeSessionCompatKey(params) {
|
|
333
767
|
return computeSessionKey(params, {
|
|
334
768
|
includeApi: true,
|
|
@@ -409,10 +843,29 @@ function createCopilotAgentHarness(options) {
|
|
|
409
843
|
reason: "copilot is opt-in only"
|
|
410
844
|
};
|
|
411
845
|
const provider = ctx.provider.trim().toLowerCase();
|
|
412
|
-
if (!
|
|
846
|
+
if (!provider) return {
|
|
847
|
+
supported: false,
|
|
848
|
+
reason: "provider is required"
|
|
849
|
+
};
|
|
850
|
+
if (COPILOT_PROVIDER_IDS.has(provider)) return {
|
|
851
|
+
supported: true,
|
|
852
|
+
priority: 100
|
|
853
|
+
};
|
|
854
|
+
const providerOwnerPluginIds = ctx.providerOwnerPluginIds;
|
|
855
|
+
if (ctx.providerOwnerStatus !== "unowned" || !providerOwnerPluginIds || providerOwnerPluginIds.length > 0) return {
|
|
413
856
|
supported: false,
|
|
414
857
|
reason: `provider is not one of: ${[...COPILOT_PROVIDER_IDS].toSorted().join(", ")}`
|
|
415
858
|
};
|
|
859
|
+
if (!supportsCopilotByokProviderShape({
|
|
860
|
+
api: ctx.modelProvider?.api,
|
|
861
|
+
baseUrl: ctx.modelProvider?.baseUrl,
|
|
862
|
+
requestProxy: ctx.modelProvider?.request?.proxy,
|
|
863
|
+
requestTls: ctx.modelProvider?.request?.tls,
|
|
864
|
+
requestAllowPrivateNetwork: ctx.modelProvider?.request?.allowPrivateNetwork
|
|
865
|
+
})) return {
|
|
866
|
+
supported: false,
|
|
867
|
+
reason: "provider is not a supported Copilot BYOK model (requires supported api, baseUrl, and no request transport policy overrides)"
|
|
868
|
+
};
|
|
416
869
|
return {
|
|
417
870
|
supported: true,
|
|
418
871
|
priority: 100
|
|
@@ -421,11 +874,17 @@ function createCopilotAgentHarness(options) {
|
|
|
421
874
|
async runAttempt(params) {
|
|
422
875
|
const attemptPromise = (async () => {
|
|
423
876
|
if (disposed) throw new Error("[copilot] harness has been disposed; cannot start new attempts");
|
|
424
|
-
const { resolvePoolAcquire, runCopilotAttempt } = await import("./attempt-
|
|
877
|
+
const { resolvePoolAcquire, runCopilotAttempt } = await import("./attempt-DIieBZcc.js");
|
|
425
878
|
if (disposed) throw new Error("[copilot] harness was disposed while starting an attempt");
|
|
426
|
-
const poolAcquire = resolvePoolAcquire(params);
|
|
427
879
|
const pool = await getPool();
|
|
428
880
|
if (disposed) throw new Error("[copilot] harness was disposed while starting an attempt");
|
|
881
|
+
let poolAcquire;
|
|
882
|
+
try {
|
|
883
|
+
poolAcquire = resolvePoolAcquire(params);
|
|
884
|
+
} catch (error) {
|
|
885
|
+
if (isCopilotByokUnsupportedProviderError(error)) return runCopilotAttempt(params, { pool });
|
|
886
|
+
throw error;
|
|
887
|
+
}
|
|
429
888
|
const openclawSessionId = typeof params.sessionId === "string" ? params.sessionId : void 0;
|
|
430
889
|
const currentCompatKey = computeSessionCompatKey(params);
|
|
431
890
|
const currentCompactKey = computeSessionCompactKey(params);
|
|
@@ -442,7 +901,7 @@ function createCopilotAgentHarness(options) {
|
|
|
442
901
|
}
|
|
443
902
|
} : params, {
|
|
444
903
|
pool,
|
|
445
|
-
onSessionEstablished: openclawSessionId ? ({ sdkSessionId, pooledClient, sessionConfig }) => {
|
|
904
|
+
onSessionEstablished: openclawSessionId ? ({ compactionSessionConfig, sdkSessionId, pooledClient, sessionConfig }) => {
|
|
446
905
|
trackedSessions.set(openclawSessionId, {
|
|
447
906
|
sdkSessionId,
|
|
448
907
|
client: pooledClient.client,
|
|
@@ -450,7 +909,7 @@ function createCopilotAgentHarness(options) {
|
|
|
450
909
|
compatKey: currentCompatKey,
|
|
451
910
|
compactKey: currentCompactKey,
|
|
452
911
|
poolKey: pooledClient.key,
|
|
453
|
-
sessionConfig,
|
|
912
|
+
sessionConfig: compactionSessionConfig ?? sessionConfig,
|
|
454
913
|
...sessionAuthFields(poolAcquire.auth)
|
|
455
914
|
});
|
|
456
915
|
registerStoredBinding(options?.sessionStore, openclawSessionId, {
|
|
@@ -531,9 +990,21 @@ function createCopilotAgentHarness(options) {
|
|
|
531
990
|
};
|
|
532
991
|
const tracked = trackedSessions.get(openclawSessionId);
|
|
533
992
|
const currentCompactKey = computeSessionCompactKey(params);
|
|
534
|
-
const { resolvePoolAcquire } = await import("./attempt-
|
|
535
|
-
|
|
536
|
-
|
|
993
|
+
const { resolvePoolAcquire } = await import("./attempt-DIieBZcc.js");
|
|
994
|
+
let resolvedPoolAcquire;
|
|
995
|
+
let currentAuth;
|
|
996
|
+
try {
|
|
997
|
+
resolvedPoolAcquire = resolvePoolAcquire(params);
|
|
998
|
+
} catch (error) {
|
|
999
|
+
if (isCopilotByokUnsupportedProviderError(error)) return {
|
|
1000
|
+
ok: false,
|
|
1001
|
+
compacted: false,
|
|
1002
|
+
reason: "missing_thread_binding",
|
|
1003
|
+
failure: { reason: "missing_thread_binding" }
|
|
1004
|
+
};
|
|
1005
|
+
throw error;
|
|
1006
|
+
}
|
|
1007
|
+
if (!currentAuth) currentAuth = sessionAuthFields(resolvedPoolAcquire.auth);
|
|
537
1008
|
const compatibleTracked = tracked?.compactKey === currentCompactKey && sessionAuthMatches(tracked, currentAuth) ? tracked : void 0;
|
|
538
1009
|
if (!compatibleTracked) return {
|
|
539
1010
|
ok: false,
|
|
@@ -541,20 +1012,30 @@ function createCopilotAgentHarness(options) {
|
|
|
541
1012
|
reason: "missing_thread_binding",
|
|
542
1013
|
failure: { reason: "missing_thread_binding" }
|
|
543
1014
|
};
|
|
544
|
-
const poolAcquire =
|
|
1015
|
+
const poolAcquire = {
|
|
545
1016
|
key: compatibleTracked.poolKey,
|
|
546
1017
|
options: compatibleTracked.clientOptions
|
|
547
|
-
}
|
|
1018
|
+
};
|
|
548
1019
|
let compactResult;
|
|
549
1020
|
let handle;
|
|
550
1021
|
let pool;
|
|
551
1022
|
let activeSdkSession;
|
|
1023
|
+
let cleanupByokProxy;
|
|
552
1024
|
const hookContext = buildCopilotCompactionHookContext(params);
|
|
553
1025
|
try {
|
|
554
1026
|
throwIfAborted(params.abortSignal);
|
|
555
1027
|
pool = await getPool();
|
|
556
1028
|
handle = await pool.acquire(poolAcquire.key, poolAcquire.options);
|
|
557
1029
|
const client = handle.client;
|
|
1030
|
+
const byokProxy = compatibleTracked.authMode === "byok" && compatibleTracked.sessionConfig.provider ? await createCopilotByokProxy({
|
|
1031
|
+
mode: "byok",
|
|
1032
|
+
provider: compatibleTracked.sessionConfig.provider
|
|
1033
|
+
}) : void 0;
|
|
1034
|
+
cleanupByokProxy = byokProxy?.close;
|
|
1035
|
+
const sessionConfig = byokProxy?.provider.provider ? {
|
|
1036
|
+
...compatibleTracked.sessionConfig,
|
|
1037
|
+
provider: byokProxy.provider.provider
|
|
1038
|
+
} : compatibleTracked.sessionConfig;
|
|
558
1039
|
await runAgentHarnessBeforeCompactionHook({
|
|
559
1040
|
sessionFile: params.sessionFile,
|
|
560
1041
|
ctx: hookContext
|
|
@@ -563,11 +1044,11 @@ function createCopilotAgentHarness(options) {
|
|
|
563
1044
|
abortSignal,
|
|
564
1045
|
client,
|
|
565
1046
|
customInstructions: params.customInstructions,
|
|
566
|
-
gitHubToken: compatibleTracked?.clientOptions.gitHubToken ?? (resolvedPoolAcquire
|
|
1047
|
+
gitHubToken: compatibleTracked?.clientOptions.gitHubToken ?? (resolvedPoolAcquire?.auth.authMode === "gitHubToken" ? resolvedPoolAcquire.auth.gitHubToken : void 0),
|
|
567
1048
|
onSession: (session) => {
|
|
568
1049
|
activeSdkSession = session;
|
|
569
1050
|
},
|
|
570
|
-
sessionConfig
|
|
1051
|
+
sessionConfig,
|
|
571
1052
|
sdkSessionId: compatibleTracked.sdkSessionId
|
|
572
1053
|
}), resolveCompactionTimeoutMs(params.config), {
|
|
573
1054
|
abortSignal: params.abortSignal,
|
|
@@ -598,6 +1079,7 @@ function createCopilotAgentHarness(options) {
|
|
|
598
1079
|
}
|
|
599
1080
|
};
|
|
600
1081
|
} finally {
|
|
1082
|
+
await cleanupByokProxy?.();
|
|
601
1083
|
if (pool && handle) try {
|
|
602
1084
|
await pool.release(handle);
|
|
603
1085
|
} catch {}
|
|
@@ -648,4 +1130,4 @@ function createCopilotAgentHarness(options) {
|
|
|
648
1130
|
};
|
|
649
1131
|
}
|
|
650
1132
|
//#endregion
|
|
651
|
-
export { resolveCopilotAuth as n, createCopilotAgentHarness as t };
|
|
1133
|
+
export { resolveCopilotAuth as a, createCopilotByokAuth as i, resolveCopilotProvider as n, createCopilotByokProxy as r, createCopilotAgentHarness as t };
|
package/dist/harness.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as createCopilotAgentHarness } from "./harness-
|
|
1
|
+
import { t as createCopilotAgentHarness } from "./harness-B9xB4LpE.js";
|
|
2
2
|
export { createCopilotAgentHarness };
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { t as createCopilotAgentHarness } from "./harness-
|
|
1
|
+
import { t as createCopilotAgentHarness } from "./harness-B9xB4LpE.js";
|
|
2
2
|
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
3
|
+
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
4
|
//#region extensions/copilot/index.ts
|
|
4
|
-
function isRecord(value) {
|
|
5
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
|
-
}
|
|
7
5
|
function readPoolOptions(pluginConfig) {
|
|
8
6
|
if (!isRecord(pluginConfig)) return;
|
|
9
7
|
const pool = pluginConfig.pool;
|
|
@@ -17,6 +15,7 @@ var copilot_default = definePluginEntry({
|
|
|
17
15
|
name: "GitHub Copilot agent runtime",
|
|
18
16
|
description: "Registers the GitHub Copilot agent runtime.",
|
|
19
17
|
register(api) {
|
|
18
|
+
if (api.registrationMode !== "full") return;
|
|
20
19
|
const poolOptions = readPoolOptions(api.pluginConfig);
|
|
21
20
|
const sessionStore = api.runtime.state.openSyncKeyedStore({
|
|
22
21
|
namespace: "sdk-sessions",
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/copilot",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.7.1-beta.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@openclaw/copilot",
|
|
9
|
-
"version": "2026.
|
|
9
|
+
"version": "2026.7.1-beta.2",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@github/copilot-sdk": "1.0.0-beta.9"
|
|
12
12
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/copilot",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.7.1-beta.2",
|
|
4
4
|
"description": "OpenClaw GitHub Copilot agent runtime plugin (registers a `github-copilot` AgentHarness backed by @github/copilot-sdk over JSON-RPC to the GitHub Copilot CLI)",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
"minHostVersion": ">=2026.5.28"
|
|
26
26
|
},
|
|
27
27
|
"compat": {
|
|
28
|
-
"pluginApi": ">=2026.
|
|
28
|
+
"pluginApi": ">=2026.7.1-beta.2"
|
|
29
29
|
},
|
|
30
30
|
"build": {
|
|
31
|
-
"openclawVersion": "2026.
|
|
31
|
+
"openclawVersion": "2026.7.1-beta.2",
|
|
32
32
|
"bundledDist": false
|
|
33
33
|
},
|
|
34
34
|
"release": {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"README.md"
|
|
48
48
|
],
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"openclaw": ">=2026.
|
|
50
|
+
"openclaw": ">=2026.7.1-beta.2"
|
|
51
51
|
},
|
|
52
52
|
"peerDependenciesMeta": {
|
|
53
53
|
"openclaw": {
|