@openclaw/copilot 2026.5.28
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 +28 -0
- package/dist/attempt-DMegR4ua.js +1667 -0
- package/dist/doctor-contract-api.js +30 -0
- package/dist/harness-Blgz_qk3.js +473 -0
- package/dist/harness.js +2 -0
- package/dist/index.js +33 -0
- package/dist/runtime-C2rPhifm.js +341 -0
- package/npm-shrinkwrap.json +160 -0
- package/openclaw.plugin.json +39 -0
- package/package.json +57 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region extensions/copilot/doctor-contract-api.ts
|
|
2
|
+
const legacyConfigRules = [];
|
|
3
|
+
function normalizeCompatibilityConfig({ cfg }) {
|
|
4
|
+
return {
|
|
5
|
+
config: cfg,
|
|
6
|
+
changes: []
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Session-state ownership claim for the copilot agent runtime.
|
|
11
|
+
*
|
|
12
|
+
* - id / label: Identify the extension in doctor output.
|
|
13
|
+
* - providerIds: The subscription Copilot providers (kept in sync
|
|
14
|
+
* with `SUPPORTED_PROVIDERS` in attempt.ts).
|
|
15
|
+
* - runtimeIds: Our harness id (matches harness.ts `id` field).
|
|
16
|
+
* - cliSessionKeys: Session keys this harness writes; doctor uses
|
|
17
|
+
* this when pruning stale CLI session state.
|
|
18
|
+
* - authProfilePrefixes: Conventional prefix for any auth profile
|
|
19
|
+
* created/consumed by this extension.
|
|
20
|
+
*/
|
|
21
|
+
const sessionRouteStateOwners = [{
|
|
22
|
+
id: "copilot",
|
|
23
|
+
label: "GitHub Copilot agent runtime",
|
|
24
|
+
providerIds: ["github-copilot"],
|
|
25
|
+
runtimeIds: ["copilot"],
|
|
26
|
+
cliSessionKeys: ["copilot"],
|
|
27
|
+
authProfilePrefixes: ["github-copilot:"]
|
|
28
|
+
}];
|
|
29
|
+
//#endregion
|
|
30
|
+
export { legacyConfigRules, normalizeCompatibilityConfig, sessionRouteStateOwners };
|
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
5
|
+
//#region extensions/copilot/src/auth-bridge.ts
|
|
6
|
+
/**
|
|
7
|
+
* Pure functional auth resolver for the copilot agent runtime.
|
|
8
|
+
*
|
|
9
|
+
* Scope:
|
|
10
|
+
*
|
|
11
|
+
* - Consumes the resolved auth signals that core's harness contract
|
|
12
|
+
* already carries on `EmbeddedRunAttemptParams` (=
|
|
13
|
+
* `AgentHarnessAttemptParams`): `resolvedApiKey`, `authProfileId`,
|
|
14
|
+
* `authProfileIdSource`. Core resolves these from the agent's
|
|
15
|
+
* `AuthProfileStore` via `provider-usage.auth.ts:resolveProviderAuths`
|
|
16
|
+
* before invoking the harness, so the harness does not re-perform
|
|
17
|
+
* the lookup (and could not, due to the package boundary in
|
|
18
|
+
* `tsconfig.package-boundary.base.json`).
|
|
19
|
+
* - Reads optional explicit overrides from the harness attempt params
|
|
20
|
+
* (`auth.useLoggedInUser`, `auth.gitHubToken`) for direct CLI / test
|
|
21
|
+
* use cases.
|
|
22
|
+
* - Falls back to OPENCLAW_GITHUB_TOKEN, COPILOT_GITHUB_TOKEN,
|
|
23
|
+
* GH_TOKEN, or GITHUB_TOKEN env vars (in that precedence) when
|
|
24
|
+
* no contract-resolved token is given; synthesises a stable,
|
|
25
|
+
* non-reversible pool fingerprint so token rotation busts the
|
|
26
|
+
* client pool cleanly.
|
|
27
|
+
* - Computes a per-agent `copilotHome` default
|
|
28
|
+
* (`<openClawHome>/.openclaw/agents/<agentId>/copilot`, or
|
|
29
|
+
* `<agentDir>/copilot` when an agent directory is supplied) that
|
|
30
|
+
* respects `OPENCLAW_HOME` for the home directory root.
|
|
31
|
+
* - Defaults to `useLoggedInUser` when no token signal is available.
|
|
32
|
+
*
|
|
33
|
+
* Precedence (highest to lowest):
|
|
34
|
+
* 1. `auth.useLoggedInUser === true` (explicit user opt-in)
|
|
35
|
+
* 2. `auth.gitHubToken` (explicit override; requires
|
|
36
|
+
* `profileId` + `profileVersion`)
|
|
37
|
+
* 3. `resolvedApiKey` + `authProfileId` from the contract (core's
|
|
38
|
+
* AuthProfileStore-resolved token — the production main path for
|
|
39
|
+
* a configured `github-copilot` auth profile)
|
|
40
|
+
* 4. OPENCLAW_GITHUB_TOKEN, then COPILOT_GITHUB_TOKEN, then
|
|
41
|
+
* GH_TOKEN, then GITHUB_TOKEN env vars (mirrors the
|
|
42
|
+
* shipped `github-copilot` provider precedence so headless
|
|
43
|
+
* users who already follow the documented
|
|
44
|
+
* COPILOT_GITHUB_TOKEN / GH_TOKEN setup get the token they
|
|
45
|
+
* configured rather than silently falling through to the
|
|
46
|
+
* logged-in CLI user.)
|
|
47
|
+
* 5. `useLoggedInUser` (default)
|
|
48
|
+
*/
|
|
49
|
+
const COPILOT_TOKEN_PROFILE_ERROR = "[copilot-attempt] gitHubToken auth requires profileId+profileVersion (pool keying safety; per Q5/Q1 decisions)";
|
|
50
|
+
const COPILOT_DEFAULT_AGENT_ID = "copilot";
|
|
51
|
+
/**
|
|
52
|
+
* Resolve copilot auth + copilotHome.
|
|
53
|
+
*
|
|
54
|
+
* Synchronous because we intentionally do not perform any I/O or
|
|
55
|
+
* cross-package credential lookups here (see file header for rationale).
|
|
56
|
+
*
|
|
57
|
+
* Throws if `gitHubToken` is supplied via `params.auth.gitHubToken`
|
|
58
|
+
* WITHOUT both `profileId` and `profileVersion` (the existing invariant
|
|
59
|
+
* from attempt.ts; preserves pool-key safety per Q5/Q1).
|
|
60
|
+
*/
|
|
61
|
+
function resolveCopilotAuth(input) {
|
|
62
|
+
const env = input.env ?? process.env;
|
|
63
|
+
const homeDir = input.homeDir ?? homedir;
|
|
64
|
+
const agentId = sanitizeAgentId(input.agentId);
|
|
65
|
+
const copilotHome = resolveCopilotHome({
|
|
66
|
+
explicit: readString(input.copilotHome),
|
|
67
|
+
agentDir: readString(input.agentDir),
|
|
68
|
+
workspaceDir: readString(input.workspaceDir),
|
|
69
|
+
agentId,
|
|
70
|
+
env,
|
|
71
|
+
homeDir
|
|
72
|
+
});
|
|
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);
|
|
76
|
+
if (input.auth?.useLoggedInUser === true) return {
|
|
77
|
+
authMode: "useLoggedInUser",
|
|
78
|
+
copilotHome,
|
|
79
|
+
agentId
|
|
80
|
+
};
|
|
81
|
+
if (explicitToken) {
|
|
82
|
+
if (!explicitProfileId || !explicitProfileVersion) throw new Error(COPILOT_TOKEN_PROFILE_ERROR);
|
|
83
|
+
return {
|
|
84
|
+
authMode: "gitHubToken",
|
|
85
|
+
gitHubToken: explicitToken,
|
|
86
|
+
authProfileId: explicitProfileId,
|
|
87
|
+
authProfileVersion: explicitProfileVersion,
|
|
88
|
+
copilotHome,
|
|
89
|
+
agentId
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
const contractToken = readString(input.resolvedApiKey);
|
|
93
|
+
if (contractToken) return {
|
|
94
|
+
authMode: "gitHubToken",
|
|
95
|
+
gitHubToken: contractToken,
|
|
96
|
+
authProfileId: readString(input.authProfileId) ?? "pi:resolved",
|
|
97
|
+
authProfileVersion: tokenFingerprint(contractToken),
|
|
98
|
+
copilotHome,
|
|
99
|
+
agentId
|
|
100
|
+
};
|
|
101
|
+
const envFallback = readEnvTokenFallback(env);
|
|
102
|
+
if (envFallback) return {
|
|
103
|
+
authMode: "gitHubToken",
|
|
104
|
+
gitHubToken: envFallback.token,
|
|
105
|
+
authProfileId: envFallback.profileId,
|
|
106
|
+
authProfileVersion: envFallback.profileVersion,
|
|
107
|
+
copilotHome,
|
|
108
|
+
agentId
|
|
109
|
+
};
|
|
110
|
+
return {
|
|
111
|
+
authMode: "useLoggedInUser",
|
|
112
|
+
copilotHome,
|
|
113
|
+
agentId
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Validate + sanitise an agent id for use in filesystem paths and pool
|
|
118
|
+
* keys.
|
|
119
|
+
*
|
|
120
|
+
* Mirrors the shape constraints documented by core's `normalizeAgentId`
|
|
121
|
+
* / `isValidAgentId` in `src/routing/session-key.ts` (alnum + `-_`,
|
|
122
|
+
* starts with alnum, lowercase, <=64 chars). We re-implement here
|
|
123
|
+
* because the package boundary prevents importing from `src/`. Any
|
|
124
|
+
* caller that passes an invalid id falls back to the shared default
|
|
125
|
+
* (`COPILOT_DEFAULT_AGENT_ID`) rather than throwing - the harness's
|
|
126
|
+
* job is to keep running with a safe default, not to validate config.
|
|
127
|
+
*/
|
|
128
|
+
function sanitizeAgentId(value) {
|
|
129
|
+
const trimmed = (value ?? "").trim().toLowerCase();
|
|
130
|
+
if (!trimmed) return COPILOT_DEFAULT_AGENT_ID;
|
|
131
|
+
if (!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(trimmed)) return COPILOT_DEFAULT_AGENT_ID;
|
|
132
|
+
return trimmed;
|
|
133
|
+
}
|
|
134
|
+
function resolveCopilotHome(args) {
|
|
135
|
+
if (args.explicit) return resolve(args.explicit);
|
|
136
|
+
if (args.agentDir) return resolve(join(args.agentDir, "copilot"));
|
|
137
|
+
const openClawHome = readString(args.env.OPENCLAW_HOME);
|
|
138
|
+
return resolve(join(openClawHome ? resolve(openClawHome) : safeHomeDir(args.homeDir), ".openclaw", "agents", args.agentId, "copilot"));
|
|
139
|
+
}
|
|
140
|
+
function safeHomeDir(homeDir) {
|
|
141
|
+
try {
|
|
142
|
+
const value = homeDir();
|
|
143
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
144
|
+
} catch {}
|
|
145
|
+
return process.cwd();
|
|
146
|
+
}
|
|
147
|
+
function readEnvTokenFallback(env) {
|
|
148
|
+
const candidates = [
|
|
149
|
+
{
|
|
150
|
+
name: "OPENCLAW_GITHUB_TOKEN",
|
|
151
|
+
value: readString(env.OPENCLAW_GITHUB_TOKEN)
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: "COPILOT_GITHUB_TOKEN",
|
|
155
|
+
value: readString(env.COPILOT_GITHUB_TOKEN)
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: "GH_TOKEN",
|
|
159
|
+
value: readString(env.GH_TOKEN)
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: "GITHUB_TOKEN",
|
|
163
|
+
value: readString(env.GITHUB_TOKEN)
|
|
164
|
+
}
|
|
165
|
+
];
|
|
166
|
+
for (const { name, value } of candidates) if (value) return {
|
|
167
|
+
token: value,
|
|
168
|
+
profileId: `env:${name}`,
|
|
169
|
+
profileVersion: tokenFingerprint(value)
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Non-reversible 12-hex-char fingerprint of a token, prefixed with
|
|
174
|
+
* `sha256:` for forward-compat. Used as the pool-key profileVersion when
|
|
175
|
+
* a token comes from env: rotation -> different fingerprint -> pool
|
|
176
|
+
* entry invalidated cleanly. 48 bits of entropy is sufficient
|
|
177
|
+
* collision resistance for a per-agent client pool; never log the
|
|
178
|
+
* fingerprint alongside an account id.
|
|
179
|
+
*/
|
|
180
|
+
function tokenFingerprint(token) {
|
|
181
|
+
return `sha256:${createHash("sha256").update(token).digest("hex").slice(0, 12)}`;
|
|
182
|
+
}
|
|
183
|
+
function readString(value) {
|
|
184
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
185
|
+
}
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region extensions/copilot/src/compaction-bridge.ts
|
|
188
|
+
/**
|
|
189
|
+
* Shape an `InfiniteSessionConfig` for `SessionConfig.infiniteSessions`.
|
|
190
|
+
* Returns `undefined` when no fields were supplied so callers can
|
|
191
|
+
* spread conditionally and let the SDK apply its own defaults
|
|
192
|
+
* (`enabled: true`, background 0.80, buffer 0.95). Any explicitly-set
|
|
193
|
+
* value (including `enabled: false` to disable infinite sessions) is
|
|
194
|
+
* preserved.
|
|
195
|
+
*/
|
|
196
|
+
function createInfiniteSessionConfig(options) {
|
|
197
|
+
if (!options) return;
|
|
198
|
+
const result = {};
|
|
199
|
+
if (options.enabled !== void 0) result.enabled = options.enabled;
|
|
200
|
+
if (options.backgroundCompactionThreshold !== void 0) result.backgroundCompactionThreshold = options.backgroundCompactionThreshold;
|
|
201
|
+
if (options.bufferExhaustionThreshold !== void 0) result.bufferExhaustionThreshold = options.bufferExhaustionThreshold;
|
|
202
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
203
|
+
}
|
|
204
|
+
function compactJsonValue(input) {
|
|
205
|
+
const out = {};
|
|
206
|
+
for (const [key, value] of Object.entries(input)) if (value !== void 0) out[key] = value;
|
|
207
|
+
return out;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Write an OpenClaw-shaped compaction marker JSON file under
|
|
211
|
+
* `<workspaceDir>/<subdir>/openclaw-compaction-<sessionId>-<ts>.json`.
|
|
212
|
+
*
|
|
213
|
+
* Returns the resolved file path and the marker payload that was
|
|
214
|
+
* written. Throws if the workspaceDir or sessionId is missing/empty
|
|
215
|
+
* (the caller should not invoke this without those — the harness
|
|
216
|
+
* `compact()` must validate first).
|
|
217
|
+
*/
|
|
218
|
+
async function writeOpenClawCompactionMarker(input, options = {}) {
|
|
219
|
+
if (!input.workspaceDir || typeof input.workspaceDir !== "string") throw new Error("[copilot:compaction-bridge] workspaceDir is required to write a marker");
|
|
220
|
+
if (!input.sessionId || typeof input.sessionId !== "string") throw new Error("[copilot:compaction-bridge] sessionId is required to write a marker");
|
|
221
|
+
const now = options.now ?? Date.now;
|
|
222
|
+
const fs = options.fs ?? {
|
|
223
|
+
mkdir,
|
|
224
|
+
writeFile
|
|
225
|
+
};
|
|
226
|
+
const subdir = options.subdir ?? "files";
|
|
227
|
+
const ts = now();
|
|
228
|
+
const filename = `openclaw-compaction-${ts}-${input.sessionId.replace(/[^a-zA-Z0-9._-]/g, "_")}.json`;
|
|
229
|
+
const dirPath = join(input.workspaceDir, subdir);
|
|
230
|
+
const filePath = join(dirPath, filename);
|
|
231
|
+
const marker = compactJsonValue({
|
|
232
|
+
version: 1,
|
|
233
|
+
source: "copilot-harness",
|
|
234
|
+
sessionId: input.sessionId,
|
|
235
|
+
ts,
|
|
236
|
+
compacted: false,
|
|
237
|
+
trigger: input.trigger,
|
|
238
|
+
force: input.force,
|
|
239
|
+
sdkSessionId: input.sdkSessionId,
|
|
240
|
+
currentTokenCount: input.currentTokenCount,
|
|
241
|
+
reason: input.reason
|
|
242
|
+
});
|
|
243
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
244
|
+
await fs.writeFile(filePath, `${JSON.stringify(marker, null, 2)}\n`, "utf8");
|
|
245
|
+
return {
|
|
246
|
+
path: filePath,
|
|
247
|
+
marker
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
//#endregion
|
|
251
|
+
//#region extensions/copilot/harness.ts
|
|
252
|
+
const COPILOT_PROVIDER_IDS = new Set(["github-copilot"]);
|
|
253
|
+
function normalizeBinding(value) {
|
|
254
|
+
if (!value || value.schemaVersion !== 1 || typeof value.sdkSessionId !== "string" || value.sdkSessionId.trim() === "" || typeof value.compatKey !== "string" || value.compatKey.trim() === "" || typeof value.updatedAt !== "number" || !Number.isFinite(value.updatedAt)) return;
|
|
255
|
+
return {
|
|
256
|
+
schemaVersion: 1,
|
|
257
|
+
sdkSessionId: value.sdkSessionId.trim(),
|
|
258
|
+
compatKey: value.compatKey,
|
|
259
|
+
updatedAt: value.updatedAt
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
function lookupStoredBinding(store, key) {
|
|
263
|
+
try {
|
|
264
|
+
return normalizeBinding(store?.lookup(key));
|
|
265
|
+
} catch {
|
|
266
|
+
try {
|
|
267
|
+
store?.delete(key);
|
|
268
|
+
} catch {}
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
function registerStoredBinding(store, key, binding) {
|
|
273
|
+
try {
|
|
274
|
+
store?.register(key, binding);
|
|
275
|
+
return true;
|
|
276
|
+
} catch {
|
|
277
|
+
try {
|
|
278
|
+
store?.delete(key);
|
|
279
|
+
} catch {}
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function deleteStoredBinding(store, key) {
|
|
284
|
+
try {
|
|
285
|
+
store?.delete(key);
|
|
286
|
+
return true;
|
|
287
|
+
} catch {
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function computeSessionCompatKey(params) {
|
|
292
|
+
const p = params;
|
|
293
|
+
const modelObj = p.model && typeof p.model === "object" ? p.model : { id: typeof p.model === "string" ? p.model : void 0 };
|
|
294
|
+
let authParts;
|
|
295
|
+
let resolvedAgentId = "";
|
|
296
|
+
let resolvedCopilotHome = "";
|
|
297
|
+
try {
|
|
298
|
+
const resolved = resolveCopilotAuth({
|
|
299
|
+
agentId: typeof p.agentId === "string" ? p.agentId : void 0,
|
|
300
|
+
agentDir: typeof p.agentDir === "string" ? p.agentDir : void 0,
|
|
301
|
+
workspaceDir: typeof p.workspaceDir === "string" ? p.workspaceDir : void 0,
|
|
302
|
+
copilotHome: typeof p.copilotHome === "string" ? p.copilotHome : void 0,
|
|
303
|
+
auth: p.auth,
|
|
304
|
+
resolvedApiKey: typeof p.resolvedApiKey === "string" ? p.resolvedApiKey : void 0,
|
|
305
|
+
authProfileId: typeof p.authProfileId === "string" ? p.authProfileId : void 0,
|
|
306
|
+
profileVersion: typeof p.profileVersion === "string" ? p.profileVersion : void 0
|
|
307
|
+
});
|
|
308
|
+
resolvedAgentId = resolved.agentId;
|
|
309
|
+
resolvedCopilotHome = resolved.copilotHome;
|
|
310
|
+
authParts = [
|
|
311
|
+
`auth.mode=${resolved.authMode}`,
|
|
312
|
+
`auth.profileId=${resolved.authProfileId ?? ""}`,
|
|
313
|
+
`auth.profileVersion=${resolved.authProfileVersion ?? ""}`
|
|
314
|
+
];
|
|
315
|
+
} catch {
|
|
316
|
+
authParts = ["auth=unresolvable"];
|
|
317
|
+
}
|
|
318
|
+
return [
|
|
319
|
+
`provider=${modelObj.provider ?? ""}`,
|
|
320
|
+
`model=${modelObj.id ?? ""}`,
|
|
321
|
+
`api=${modelObj.api ?? ""}`,
|
|
322
|
+
`cwd=${p.cwd ?? p.workspaceDir ?? ""}`,
|
|
323
|
+
`agentId=${resolvedAgentId}`,
|
|
324
|
+
`agentDir=${p.agentDir ?? ""}`,
|
|
325
|
+
`copilotHome=${p.copilotHome ?? ""}`,
|
|
326
|
+
`resolvedCopilotHome=${resolvedCopilotHome}`,
|
|
327
|
+
...authParts
|
|
328
|
+
].join("|");
|
|
329
|
+
}
|
|
330
|
+
function createCopilotAgentHarness(options) {
|
|
331
|
+
let poolPromise;
|
|
332
|
+
let createdPool;
|
|
333
|
+
let disposed = false;
|
|
334
|
+
let disposePromise;
|
|
335
|
+
const inFlight = /* @__PURE__ */ new Set();
|
|
336
|
+
const trackedSessions = /* @__PURE__ */ new Map();
|
|
337
|
+
const resetBlockedStoredSessions = /* @__PURE__ */ new Set();
|
|
338
|
+
async function getPool() {
|
|
339
|
+
if (options?.pool) return options.pool;
|
|
340
|
+
if (!poolPromise) poolPromise = (async () => {
|
|
341
|
+
const { createCopilotClientPool } = await import("./runtime-C2rPhifm.js");
|
|
342
|
+
createdPool = createCopilotClientPool(options?.poolOptions);
|
|
343
|
+
return createdPool;
|
|
344
|
+
})();
|
|
345
|
+
return poolPromise;
|
|
346
|
+
}
|
|
347
|
+
return {
|
|
348
|
+
id: options?.id ?? "copilot",
|
|
349
|
+
label: options?.label ?? "GitHub Copilot agent runtime",
|
|
350
|
+
supports(ctx) {
|
|
351
|
+
if (String(ctx.requestedRuntime ?? "").trim().toLowerCase() !== "copilot") return {
|
|
352
|
+
supported: false,
|
|
353
|
+
reason: "copilot is opt-in only"
|
|
354
|
+
};
|
|
355
|
+
const provider = ctx.provider.trim().toLowerCase();
|
|
356
|
+
if (!COPILOT_PROVIDER_IDS.has(provider)) return {
|
|
357
|
+
supported: false,
|
|
358
|
+
reason: `provider is not one of: ${[...COPILOT_PROVIDER_IDS].toSorted().join(", ")}`
|
|
359
|
+
};
|
|
360
|
+
return {
|
|
361
|
+
supported: true,
|
|
362
|
+
priority: 100
|
|
363
|
+
};
|
|
364
|
+
},
|
|
365
|
+
async runAttempt(params) {
|
|
366
|
+
const attemptPromise = (async () => {
|
|
367
|
+
if (disposed) throw new Error("[copilot] harness has been disposed; cannot start new attempts");
|
|
368
|
+
const { runCopilotAttempt } = await import("./attempt-DMegR4ua.js");
|
|
369
|
+
if (disposed) throw new Error("[copilot] harness was disposed while starting an attempt");
|
|
370
|
+
const pool = await getPool();
|
|
371
|
+
if (disposed) throw new Error("[copilot] harness was disposed while starting an attempt");
|
|
372
|
+
const openclawSessionId = typeof params.sessionId === "string" ? params.sessionId : void 0;
|
|
373
|
+
const currentCompatKey = computeSessionCompatKey(params);
|
|
374
|
+
const tracked = openclawSessionId ? trackedSessions.get(openclawSessionId) : void 0;
|
|
375
|
+
const stored = openclawSessionId ? resetBlockedStoredSessions.has(openclawSessionId) ? void 0 : lookupStoredBinding(options?.sessionStore, openclawSessionId) : void 0;
|
|
376
|
+
const resumableSessionId = tracked && tracked.compatKey === currentCompatKey ? tracked.sdkSessionId : !tracked && stored && stored.compatKey === currentCompatKey ? stored.sdkSessionId : void 0;
|
|
377
|
+
return runCopilotAttempt(resumableSessionId ? {
|
|
378
|
+
...params,
|
|
379
|
+
initialReplayState: {
|
|
380
|
+
...params.initialReplayState,
|
|
381
|
+
sdkSessionId: resumableSessionId
|
|
382
|
+
}
|
|
383
|
+
} : params, {
|
|
384
|
+
pool,
|
|
385
|
+
onSessionEstablished: openclawSessionId ? ({ sdkSessionId, pooledClient }) => {
|
|
386
|
+
trackedSessions.set(openclawSessionId, {
|
|
387
|
+
sdkSessionId,
|
|
388
|
+
client: pooledClient.client,
|
|
389
|
+
compatKey: currentCompatKey
|
|
390
|
+
});
|
|
391
|
+
if (registerStoredBinding(options?.sessionStore, openclawSessionId, {
|
|
392
|
+
schemaVersion: 1,
|
|
393
|
+
sdkSessionId,
|
|
394
|
+
compatKey: currentCompatKey,
|
|
395
|
+
updatedAt: Date.now()
|
|
396
|
+
})) resetBlockedStoredSessions.delete(openclawSessionId);
|
|
397
|
+
} : void 0
|
|
398
|
+
});
|
|
399
|
+
})();
|
|
400
|
+
inFlight.add(attemptPromise);
|
|
401
|
+
try {
|
|
402
|
+
return await attemptPromise;
|
|
403
|
+
} finally {
|
|
404
|
+
inFlight.delete(attemptPromise);
|
|
405
|
+
}
|
|
406
|
+
},
|
|
407
|
+
async reset(params) {
|
|
408
|
+
const openclawSessionId = typeof params.sessionId === "string" ? params.sessionId : void 0;
|
|
409
|
+
if (!openclawSessionId) return;
|
|
410
|
+
const tracked = trackedSessions.get(openclawSessionId);
|
|
411
|
+
if (deleteStoredBinding(options?.sessionStore, openclawSessionId)) resetBlockedStoredSessions.delete(openclawSessionId);
|
|
412
|
+
else resetBlockedStoredSessions.add(openclawSessionId);
|
|
413
|
+
if (!tracked) return;
|
|
414
|
+
trackedSessions.delete(openclawSessionId);
|
|
415
|
+
try {
|
|
416
|
+
await tracked.client.deleteSession(tracked.sdkSessionId);
|
|
417
|
+
} catch {}
|
|
418
|
+
},
|
|
419
|
+
async compact(params) {
|
|
420
|
+
const openclawSessionId = typeof params.sessionId === "string" ? params.sessionId : void 0;
|
|
421
|
+
const workspaceDir = typeof params.workspaceDir === "string" ? params.workspaceDir : void 0;
|
|
422
|
+
if (!openclawSessionId || !workspaceDir) return {
|
|
423
|
+
ok: false,
|
|
424
|
+
compacted: false,
|
|
425
|
+
reason: "missing-required-params"
|
|
426
|
+
};
|
|
427
|
+
const tracked = trackedSessions.get(openclawSessionId);
|
|
428
|
+
const reason = params.force ? "force-requested-but-sdk-has-no-synchronous-compact-api" : "deferred-to-sdk-infinite-sessions";
|
|
429
|
+
try {
|
|
430
|
+
await writeOpenClawCompactionMarker({
|
|
431
|
+
sessionId: openclawSessionId,
|
|
432
|
+
workspaceDir,
|
|
433
|
+
trigger: params.trigger,
|
|
434
|
+
currentTokenCount: params.currentTokenCount,
|
|
435
|
+
sdkSessionId: tracked?.sdkSessionId,
|
|
436
|
+
force: params.force,
|
|
437
|
+
reason
|
|
438
|
+
});
|
|
439
|
+
} catch (err) {
|
|
440
|
+
return {
|
|
441
|
+
ok: false,
|
|
442
|
+
compacted: false,
|
|
443
|
+
reason: "marker-write-failed",
|
|
444
|
+
failure: {
|
|
445
|
+
reason: "marker-write-failed",
|
|
446
|
+
rawError: err instanceof Error ? err.message : String(err)
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
return {
|
|
451
|
+
ok: true,
|
|
452
|
+
compacted: false,
|
|
453
|
+
reason
|
|
454
|
+
};
|
|
455
|
+
},
|
|
456
|
+
async dispose() {
|
|
457
|
+
if (disposePromise) return disposePromise;
|
|
458
|
+
disposed = true;
|
|
459
|
+
disposePromise = (async () => {
|
|
460
|
+
if (inFlight.size > 0) await Promise.allSettled(inFlight);
|
|
461
|
+
trackedSessions.clear();
|
|
462
|
+
resetBlockedStoredSessions.clear();
|
|
463
|
+
if (createdPool) {
|
|
464
|
+
const errors = await createdPool.dispose();
|
|
465
|
+
if (errors.length > 0) throw new AggregateError(errors, "[copilot] pool disposal errors");
|
|
466
|
+
}
|
|
467
|
+
})();
|
|
468
|
+
return disposePromise;
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
//#endregion
|
|
473
|
+
export { createInfiniteSessionConfig as n, resolveCopilotAuth as r, createCopilotAgentHarness as t };
|
package/dist/harness.js
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { t as createCopilotAgentHarness } from "./harness-Blgz_qk3.js";
|
|
2
|
+
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
3
|
+
//#region extensions/copilot/index.ts
|
|
4
|
+
function isRecord(value) {
|
|
5
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
|
+
}
|
|
7
|
+
function readPoolOptions(pluginConfig) {
|
|
8
|
+
if (!isRecord(pluginConfig)) return;
|
|
9
|
+
const pool = pluginConfig.pool;
|
|
10
|
+
if (!isRecord(pool)) return;
|
|
11
|
+
const idleTtlMs = pool.idleTtlMs;
|
|
12
|
+
if (typeof idleTtlMs !== "number" || !Number.isFinite(idleTtlMs) || idleTtlMs < 1) return;
|
|
13
|
+
return { idleTtlMs };
|
|
14
|
+
}
|
|
15
|
+
var copilot_default = definePluginEntry({
|
|
16
|
+
id: "copilot",
|
|
17
|
+
name: "GitHub Copilot agent runtime",
|
|
18
|
+
description: "Registers the GitHub Copilot agent runtime.",
|
|
19
|
+
register(api) {
|
|
20
|
+
const poolOptions = readPoolOptions(api.pluginConfig);
|
|
21
|
+
const sessionStore = api.runtime.state.openSyncKeyedStore({
|
|
22
|
+
namespace: "sdk-sessions",
|
|
23
|
+
maxEntries: 5e3,
|
|
24
|
+
defaultTtlMs: 2160 * 60 * 60 * 1e3
|
|
25
|
+
});
|
|
26
|
+
api.registerAgentHarness(createCopilotAgentHarness({
|
|
27
|
+
...poolOptions ? { poolOptions } : {},
|
|
28
|
+
sessionStore
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
//#endregion
|
|
33
|
+
export { copilot_default as default };
|