@openclaw/copilot 2026.7.1 → 2026.7.2-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
@@ -14,13 +14,11 @@ The harness claims the canonical subscription `github-copilot` provider plus
14
14
  custom BYOK provider entries that the Copilot SDK can represent. Manifest-owned
15
15
  native provider ids stay with their owning runtimes. The harness is opt-in only:
16
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
+ entry; `auto` never picks it. OpenClaw remains the default embedded runtime.
18
18
 
19
19
  See [GitHub Copilot agent runtime](../../docs/plugins/copilot.md) for
20
20
  configuration, the doctor contract, transcript mirroring, compaction, side
21
21
  questions, replay, and the supported-surface contract.
22
- See [qa/copilot-capabilities.md](../../qa/copilot-capabilities.md)
23
- for the SDK capability inventory the harness is pinned to.
24
22
 
25
23
  ## Package
26
24
 
@@ -1,29 +1,12 @@
1
- import { a as resolveCopilotAuth, i as createCopilotByokAuth, n as resolveCopilotProvider, r as createCopilotByokProxy } from "./harness-BQhGzc1Z.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";
1
+ import { a as resolveCopilotAuth, i as createCopilotByokAuth, n as resolveCopilotProvider, r as createCopilotByokProxy } from "./harness-DeWR8_cC.js";
2
+ import { applyEmbeddedAttemptToolsAllow, awaitAgentEndSideEffects, buildAgentHarnessUserInputAnswers, buildAgentHookContextChannelFields, buildEmbeddedAttemptToolRunContext, clearActiveEmbeddedRun, deliverAgentHarnessUserInputPrompt, detectAndLoadAgentHarnessPromptImages, embeddedAgentLog, extractToolErrorMessage, getModelProviderRequestTransport, getPluginToolMeta, isHostScopedAgentToolActive, 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";
6
- import { publishSessionTranscriptUpdateByIdentity, withSessionTranscriptWriteLock } from "openclaw/plugin-sdk/session-transcript-runtime";
6
+ import { withSessionTranscriptWriteLock } from "openclaw/plugin-sdk/session-transcript-runtime";
7
7
  import { createAgentHarnessTaskRuntime } from "openclaw/plugin-sdk/agent-harness-task-runtime";
8
+ import { convertMcpCallToolResult } from "@github/copilot-sdk";
8
9
  import { createAgentHarnessToolSurfaceRuntime } from "openclaw/plugin-sdk/agent-harness-tool-runtime";
9
- //#region extensions/copilot/src/compaction-bridge.ts
10
- /**
11
- * Shape an `InfiniteSessionConfig` for `SessionConfig.infiniteSessions`.
12
- * Returns `undefined` when no fields were supplied so callers can
13
- * spread conditionally and let the SDK apply its own defaults
14
- * (`enabled: true`, background 0.80, buffer 0.95). Any explicitly-set
15
- * value (including `enabled: false` to disable infinite sessions) is
16
- * preserved.
17
- */
18
- function createInfiniteSessionConfig(options) {
19
- if (!options) return;
20
- const result = {};
21
- if (options.enabled !== void 0) result.enabled = options.enabled;
22
- if (options.backgroundCompactionThreshold !== void 0) result.backgroundCompactionThreshold = options.backgroundCompactionThreshold;
23
- if (options.bufferExhaustionThreshold !== void 0) result.bufferExhaustionThreshold = options.bufferExhaustionThreshold;
24
- return Object.keys(result).length > 0 ? result : void 0;
25
- }
26
- //#endregion
27
10
  //#region extensions/copilot/src/dual-write-transcripts.ts
28
11
  /**
29
12
  * Mirrors the AgentMessages produced by the copilot agent runtime into the
@@ -98,9 +81,8 @@ function buildMirrorDedupeIdentity(message) {
98
81
  async function mirrorCopilotTranscript(params) {
99
82
  const messages = params.messages.filter((message) => message.role === "user" || message.role === "assistant" || message.role === "toolResult");
100
83
  if (messages.length === 0) return;
101
- const transcriptTarget = resolveCopilotMirrorTranscriptTarget(params);
102
- if (await withSessionTranscriptWriteLock({
103
- ...transcriptTarget,
84
+ await withCopilotMirrorTranscriptWriteLock({
85
+ ...resolveCopilotMirrorTranscriptTarget(params),
104
86
  config: params.config
105
87
  }, async (transcript) => {
106
88
  let didAppendMessage = false;
@@ -130,22 +112,24 @@ async function mirrorCopilotTranscript(params) {
130
112
  didAppendMessage = true;
131
113
  if (idempotencyKey) existingIdempotencyKeys.add(idempotencyKey);
132
114
  }
115
+ if (didAppendMessage) await transcript.publishUpdate(params.sessionKey ? { sessionKey: params.sessionKey } : void 0);
133
116
  return didAppendMessage;
134
- })) await publishSessionTranscriptUpdateByIdentity({
135
- ...transcriptTarget,
136
- update: params.sessionKey ? { sessionKey: params.sessionKey } : void 0
137
117
  });
138
118
  }
139
119
  function resolveCopilotMirrorTranscriptTarget(params) {
140
- const sessionFile = params.sessionFile.trim();
141
- if (!sessionFile) throw new Error("Copilot transcript mirror requires a sessionFile target");
120
+ const sessionKey = params.sessionKey?.trim();
121
+ const storePath = params.storePath?.trim();
122
+ if (!sessionKey || !storePath) throw new Error("Copilot transcript mirror requires a runtime session identity");
142
123
  return {
143
124
  ...params.agentId ? { agentId: params.agentId } : {},
144
- sessionFile,
145
125
  sessionId: params.sessionId,
146
- sessionKey: params.sessionKey ?? ""
126
+ sessionKey,
127
+ storePath
147
128
  };
148
129
  }
130
+ function withCopilotMirrorTranscriptWriteLock(params, run) {
131
+ return withSessionTranscriptWriteLock(params, run);
132
+ }
149
133
  function readTranscriptIdempotencyKeys(events) {
150
134
  const keys = /* @__PURE__ */ new Set();
151
135
  for (const event of events) {
@@ -223,7 +207,7 @@ function attachEventBridge(session, options) {
223
207
  let usage;
224
208
  let streamError;
225
209
  const toolMetas = [];
226
- const toolNamesByCallId = /* @__PURE__ */ new Map();
210
+ const toolMetaIndexByCallId = /* @__PURE__ */ new Map();
227
211
  let startedCount = 0;
228
212
  let completedCount = 0;
229
213
  let activeCompactionCount = 0;
@@ -244,7 +228,7 @@ function attachEventBridge(session, options) {
244
228
  const unsubscribeFns = [];
245
229
  registerListener(session, unsubscribeFns, "assistant.message_delta", (event) => {
246
230
  if (!isRootSessionEvent(event)) return;
247
- const messageId = readString$2(event.data.messageId) ?? "assistant-message";
231
+ const messageId = readString$1(event.data.messageId) ?? "assistant-message";
248
232
  const delta = event.data.deltaContent;
249
233
  if (!delta) return;
250
234
  const entry = ensureMessageAccumulator(messagesById, messageOrder, messageId);
@@ -267,7 +251,7 @@ function attachEventBridge(session, options) {
267
251
  });
268
252
  registerListener(session, unsubscribeFns, "assistant.reasoning_delta", (event) => {
269
253
  if (!isRootSessionEvent(event)) return;
270
- const reasoningId = readString$2(event.data.reasoningId) ?? "assistant-reasoning";
254
+ const reasoningId = readString$1(event.data.reasoningId) ?? "assistant-reasoning";
271
255
  const delta = event.data.deltaContent;
272
256
  if (!delta) return;
273
257
  if (!reasoningById.has(reasoningId)) {
@@ -288,17 +272,19 @@ function attachEventBridge(session, options) {
288
272
  });
289
273
  registerListener(session, unsubscribeFns, "tool.execution_start", (event) => {
290
274
  if (isRootSessionEvent(event)) startedCount += 1;
291
- toolNamesByCallId.set(event.data.toolCallId, event.data.toolName);
275
+ toolMetaIndexByCallId.set(event.data.toolCallId, toolMetas.length);
292
276
  toolMetas.push({ toolName: event.data.toolName });
293
277
  });
294
278
  registerListener(session, unsubscribeFns, "tool.execution_complete", (event) => {
295
279
  if (isRootSessionEvent(event)) completedCount += 1;
296
- const toolName = toolNamesByCallId.get(event.data.toolCallId);
280
+ const toolMetaIndex = toolMetaIndexByCallId.get(event.data.toolCallId);
281
+ const toolName = toolMetaIndex === void 0 ? void 0 : toolMetas[toolMetaIndex]?.toolName;
297
282
  const meta = event.data.success ? event.data.result?.detailedContent ?? event.data.result?.content : event.data.error?.message;
298
- if (toolName) toolMetas.push({
299
- meta,
300
- toolName
301
- });
283
+ if (toolName && toolMetaIndex !== void 0) toolMetas[toolMetaIndex] = {
284
+ ...meta ? { meta } : {},
285
+ toolName,
286
+ ...event.data.success ? {} : { isError: true }
287
+ };
302
288
  });
303
289
  registerListener(session, unsubscribeFns, "session.plan_changed", (event) => {
304
290
  enqueueAgentEvent({
@@ -313,7 +299,10 @@ function attachEventBridge(session, options) {
313
299
  });
314
300
  });
315
301
  registerListener(session, unsubscribeFns, "exit_plan_mode.requested", (event) => {
316
- const steps = splitPlanText(event.data.planContent);
302
+ const steps = splitPlanText(event.data.planContent).map((step) => ({
303
+ step,
304
+ status: "pending"
305
+ }));
317
306
  enqueueAgentEvent({
318
307
  stream: "plan",
319
308
  data: {
@@ -364,6 +353,9 @@ function attachEventBridge(session, options) {
364
353
  enqueueCompactionCallback(options.onCompactionStart);
365
354
  });
366
355
  registerListener(session, unsubscribeFns, "session.compaction_complete", (event) => {
356
+ if (event.data.success) try {
357
+ options.onContextCompacted?.();
358
+ } catch {}
367
359
  if (!isRootCompactionEvent(event)) return;
368
360
  activeCompactionCount = Math.max(0, activeCompactionCount - 1);
369
361
  enqueueCompactionCallback(() => options.onCompactionComplete?.({
@@ -553,7 +545,7 @@ function joinReasoning(order, reasoningById) {
553
545
  function splitPlanText(text) {
554
546
  return (text ?? "").split(/\r?\n/).map((line) => line.trim().replace(/^[-*]\s+/, "")).filter((line) => line.length > 0);
555
547
  }
556
- function readString$2(value) {
548
+ function readString$1(value) {
557
549
  return typeof value === "string" && value.length > 0 ? value : void 0;
558
550
  }
559
551
  function registerListener(session, unsubscribeFns, eventType, handler) {
@@ -1057,7 +1049,8 @@ async function createCopilotToolBridge(input) {
1057
1049
  abortSignal: input.abortSignal,
1058
1050
  beforeExecute: input.beforeExecute,
1059
1051
  onAgentToolResult: input.attemptParams?.onAgentToolResult,
1060
- onToolCompleted: input.onToolCompleted
1052
+ onToolCompleted: input.onToolCompleted,
1053
+ observeToolTerminal: input.attemptParams?.observeToolTerminal
1061
1054
  })),
1062
1055
  sourceTools: filteredTools
1063
1056
  };
@@ -1113,9 +1106,12 @@ function buildOpenClawCodingToolsOptions(input, toolPlan, toolSurfaceRuntime) {
1113
1106
  elevated: a.bashElevated
1114
1107
  },
1115
1108
  messageProvider: a.messageProvider ?? a.messageChannel,
1109
+ chatType: a.chatType,
1116
1110
  agentAccountId: a.agentAccountId,
1117
1111
  messageTo: a.messageTo,
1118
1112
  messageThreadId: a.messageThreadId,
1113
+ nativeChannelId: a.chatId,
1114
+ messageActionTurnCapability: a.messageActionTurnCapability,
1119
1115
  groupId: a.groupId,
1120
1116
  groupChannel: a.groupChannel,
1121
1117
  groupSpace: a.groupSpace,
@@ -1164,6 +1160,7 @@ function buildOpenClawCodingToolsOptions(input, toolPlan, toolSurfaceRuntime) {
1164
1160
  enableHeartbeatTool: a.enableHeartbeatTool,
1165
1161
  forceHeartbeatTool: a.forceHeartbeatTool,
1166
1162
  authProfileStore: a.toolAuthProfileStore ?? a.authProfileStore,
1163
+ computerContextEpoch: input.computerContextEpoch,
1167
1164
  onToolOutcome: a.onToolOutcome,
1168
1165
  onYield: (message) => {
1169
1166
  try {
@@ -1199,8 +1196,16 @@ function convertOpenClawToolToSdkTool(sourceTool, ctx) {
1199
1196
  console.warn("[copilot-tool-bridge] onToolCompleted handler threw; continuing", error);
1200
1197
  }
1201
1198
  };
1202
- const failureResult = (executedArgs, invocation, startedAt, message, error) => {
1199
+ const failureResult = (executedArgs, invocation, startedAt, message, error, executionStarted) => {
1203
1200
  const errorMessage = toError$1(error).message;
1201
+ ctx.observeToolTerminal?.({
1202
+ toolCallId: invocation.toolCallId,
1203
+ toolName: sourceTool.name,
1204
+ arguments: executedArgs,
1205
+ executionStarted,
1206
+ outcome: "failure",
1207
+ failure: { error: errorMessage }
1208
+ });
1204
1209
  notifyToolResult(sanitizeToolResult({
1205
1210
  content: [{
1206
1211
  type: "text",
@@ -1224,7 +1229,7 @@ function convertOpenClawToolToSdkTool(sourceTool, ctx) {
1224
1229
  const startedAt = Date.now();
1225
1230
  if (ctx.abortSignal?.aborted) {
1226
1231
  const error = /* @__PURE__ */ new Error("[copilot-tool-bridge] aborted before execution");
1227
- return failureResult(args, invocation, startedAt, error.message, error);
1232
+ return failureResult(args, invocation, startedAt, error.message, error, false);
1228
1233
  }
1229
1234
  try {
1230
1235
  await ctx.beforeExecute?.({
@@ -1235,24 +1240,32 @@ function convertOpenClawToolToSdkTool(sourceTool, ctx) {
1235
1240
  toolName: sourceTool.name
1236
1241
  });
1237
1242
  } catch (error) {
1238
- return failureResult(args, invocation, startedAt, `[copilot-tool-bridge] beforeExecute failed for tool '${sourceTool.name}': ${toError$1(error).message}`, error);
1243
+ return failureResult(args, invocation, startedAt, `[copilot-tool-bridge] beforeExecute failed for tool '${sourceTool.name}': ${toError$1(error).message}`, error, false);
1239
1244
  }
1240
1245
  let preparedArgs;
1241
1246
  try {
1242
1247
  preparedArgs = sourceTool.prepareArguments ? sourceTool.prepareArguments(args) : args;
1243
1248
  } catch (error) {
1244
- return failureResult(args, invocation, startedAt, `[copilot-tool-bridge] prepareArguments failed for tool '${sourceTool.name}': ${toError$1(error).message}`, error);
1249
+ return failureResult(args, invocation, startedAt, `[copilot-tool-bridge] prepareArguments failed for tool '${sourceTool.name}': ${toError$1(error).message}`, error, false);
1245
1250
  }
1246
1251
  let result;
1247
1252
  try {
1248
1253
  result = await sourceTool.execute(invocation.toolCallId, preparedArgs, ctx.abortSignal, void 0);
1249
1254
  } catch (error) {
1250
- return failureResult(preparedArgs, invocation, startedAt, `[copilot-tool-bridge] tool '${sourceTool.name}' failed: ${toError$1(error).message}`, error);
1255
+ return failureResult(preparedArgs, invocation, startedAt, `[copilot-tool-bridge] tool '${sourceTool.name}' failed: ${toError$1(error).message}`, error, true);
1251
1256
  }
1252
- const sdkResult = agentToolResultToSdk(result);
1257
+ const sdkResult = convertMcpCallToolResult({ content: result.content });
1253
1258
  const sanitizedResult = sanitizeToolResult(result);
1254
1259
  const resultIsError = sdkResult.resultType === "failure" || isToolResultError(sanitizedResult);
1255
1260
  const resultError = resultIsError ? extractToolErrorMessage(sanitizedResult) : void 0;
1261
+ ctx.observeToolTerminal?.({
1262
+ toolCallId: invocation.toolCallId,
1263
+ toolName: sourceTool.name,
1264
+ arguments: preparedArgs,
1265
+ executionStarted: true,
1266
+ outcome: resultIsError ? "failure" : "success",
1267
+ ...resultIsError ? { failure: { error: resultError ?? "tool returned an error" } } : {}
1268
+ });
1256
1269
  notifyToolResult(sanitizedResult, resultIsError);
1257
1270
  notifyToolCompleted({
1258
1271
  toolName: sourceTool.name,
@@ -1282,11 +1295,24 @@ async function executeCatalogTool(input, params) {
1282
1295
  const sourceTool = params.tool;
1283
1296
  const startedAt = Date.now();
1284
1297
  let preparedArgs = params.input;
1298
+ let executionStarted = false;
1299
+ let terminalObserved = false;
1285
1300
  try {
1286
1301
  preparedArgs = sourceTool.prepareArguments ? sourceTool.prepareArguments(params.input) : params.input;
1302
+ executionStarted = true;
1287
1303
  const result = await sourceTool.execute(params.toolCallId, preparedArgs, params.signal ?? input.abortSignal, params.onUpdate);
1288
1304
  const sanitizedResult = sanitizeToolResult(result);
1289
1305
  const isError = isToolResultError(sanitizedResult);
1306
+ const error = isError ? extractToolErrorMessage(sanitizedResult) ?? "tool returned an error" : void 0;
1307
+ terminalObserved = true;
1308
+ input.attemptParams?.observeToolTerminal?.({
1309
+ toolCallId: params.toolCallId,
1310
+ toolName: params.toolName,
1311
+ arguments: preparedArgs,
1312
+ executionStarted,
1313
+ outcome: isError ? "failure" : "success",
1314
+ ...error ? { failure: { error } } : {}
1315
+ });
1290
1316
  input.attemptParams?.onAgentToolResult?.({
1291
1317
  toolName: params.toolName,
1292
1318
  result: sanitizedResult,
@@ -1297,12 +1323,20 @@ async function executeCatalogTool(input, params) {
1297
1323
  toolCallId: params.toolCallId,
1298
1324
  args: toToolStartArgs(preparedArgs),
1299
1325
  result: sanitizedResult,
1300
- ...isError ? { error: extractToolErrorMessage(sanitizedResult) ?? "tool returned an error" } : {},
1326
+ ...error ? { error } : {},
1301
1327
  startedAt
1302
1328
  });
1303
1329
  return result;
1304
1330
  } catch (error) {
1305
1331
  const message = toError$1(error).message;
1332
+ if (!terminalObserved) input.attemptParams?.observeToolTerminal?.({
1333
+ toolCallId: params.toolCallId,
1334
+ toolName: params.toolName,
1335
+ arguments: preparedArgs,
1336
+ executionStarted,
1337
+ outcome: "failure",
1338
+ failure: { error: message }
1339
+ });
1306
1340
  const failure = sanitizeToolResult({
1307
1341
  content: [{
1308
1342
  type: "text",
@@ -1331,51 +1365,6 @@ async function executeCatalogTool(input, params) {
1331
1365
  function toToolStartArgs(args) {
1332
1366
  return args && typeof args === "object" && !Array.isArray(args) ? args : { value: args };
1333
1367
  }
1334
- function agentToolResultToSdk(result) {
1335
- const content = result?.content;
1336
- if (content == null) return createSuccessResult("");
1337
- if (!Array.isArray(content)) return createUnsupportedContentFailure(typeof content);
1338
- const textParts = [];
1339
- const binaryResults = [];
1340
- for (const block of content) {
1341
- if (!block || typeof block !== "object") return createUnsupportedContentFailure(typeof block);
1342
- const kind = readString$1(block.type);
1343
- if (kind === "text") {
1344
- const text = readString$1(block.text, { allowEmpty: true });
1345
- if (text === void 0) return createUnsupportedContentFailure(kind);
1346
- textParts.push(text);
1347
- continue;
1348
- }
1349
- if (kind === "image") {
1350
- const base64Data = readString$1(block.data);
1351
- const mimeType = readString$1(block.mimeType);
1352
- if (!base64Data || !mimeType) return createUnsupportedContentFailure(kind);
1353
- binaryResults.push({
1354
- base64Data,
1355
- data: base64Data,
1356
- mimeType,
1357
- type: "image"
1358
- });
1359
- continue;
1360
- }
1361
- return createUnsupportedContentFailure(kind ?? typeof block);
1362
- }
1363
- return {
1364
- ...binaryResults.length > 0 ? { binaryResultsForLlm: binaryResults } : {},
1365
- resultType: "success",
1366
- textResultForLlm: textParts.join("\n")
1367
- };
1368
- }
1369
- function createUnsupportedContentFailure(kind) {
1370
- const message = `[copilot-tool-bridge] unsupported AgentToolResult content shape: ${kind}`;
1371
- return createFailureResult(message, new Error(message));
1372
- }
1373
- function createSuccessResult(textResultForLlm) {
1374
- return {
1375
- resultType: "success",
1376
- textResultForLlm
1377
- };
1378
- }
1379
1368
  function createFailureResult(message, error) {
1380
1369
  return {
1381
1370
  error: toError$1(error).message,
@@ -1449,10 +1438,6 @@ function findDuplicateToolNames(sourceTools) {
1449
1438
  }
1450
1439
  return [...counts.entries()].filter(([, count]) => count > 1).map(([name]) => name).toSorted();
1451
1440
  }
1452
- function readString$1(value, options = {}) {
1453
- if (typeof value !== "string") return;
1454
- if (options.allowEmpty || value.length > 0) return value;
1455
- }
1456
1441
  function toError$1(error) {
1457
1442
  return error instanceof Error ? error : new Error(String(error));
1458
1443
  }
@@ -1622,9 +1607,9 @@ async function resolveCopilotWorkspaceBootstrapContext(params) {
1622
1607
  * (`src/agents/pi-embedded-runner/run/attempt.ts:603`). Files whose
1623
1608
  * resolved relative path escapes the source workspace (parent
1624
1609
  * traversal or absolute) are left untouched so we never pretend a
1625
- * file lives inside the sandbox when it does not. Exported for unit
1626
- * tests; intentionally local to the Copilot extension (codex keeps
1627
- * similar helpers extension-local rather than importing from PI).
1610
+ * file lives inside the sandbox when it does not. Intentionally local
1611
+ * to the Copilot extension (codex keeps similar helpers extension-local
1612
+ * rather than importing from PI).
1628
1613
  */
1629
1614
  function remapCopilotBootstrapContextFiles(params) {
1630
1615
  if (params.sourceWorkspaceDir === params.targetWorkspaceDir) return params.files;
@@ -1801,6 +1786,7 @@ async function runCopilotAttempt(params, deps) {
1801
1786
  const attemptStartedAt = now();
1802
1787
  const input = params;
1803
1788
  const createToolBridge = deps.createToolBridge ?? createCopilotToolBridge;
1789
+ const ringZeroSystemAgentRun = (deps.isHostScopedToolActive?.("openclaw") ?? isHostScopedAgentToolActive("openclaw")) && isSystemAgentOnlyToolAllowlist(input.toolsAllow);
1804
1790
  const messages = getMessagesSnapshotInput(input);
1805
1791
  const modelRef = resolveModelRef(input);
1806
1792
  const resolvedWorkspaceForSandbox = readResolvedAttemptPath(input.workspaceDir) ?? readResolvedAttemptPath(input.cwd);
@@ -1880,6 +1866,13 @@ async function runCopilotAttempt(params, deps) {
1880
1866
  let downgradedFromResume = false;
1881
1867
  let resumeFailureRecovered = false;
1882
1868
  let yieldDetected = false;
1869
+ let lastToolError;
1870
+ const hostObserveToolTerminal = input.observeToolTerminal;
1871
+ const observeToolTerminal = hostObserveToolTerminal ? (observation) => {
1872
+ const terminal = hostObserveToolTerminal(observation);
1873
+ lastToolError = terminal.lastToolError;
1874
+ return terminal;
1875
+ } : void 0;
1883
1876
  const markExternalAbort = () => {
1884
1877
  abortRequested = true;
1885
1878
  externalAbort = true;
@@ -1963,6 +1956,7 @@ async function runCopilotAttempt(params, deps) {
1963
1956
  const cleanupByokProxy = byokProxy?.close;
1964
1957
  const sessionProvider = byokProxy?.provider ?? poolAcquire.provider;
1965
1958
  const sessionRef = { current: void 0 };
1959
+ const computerContextEpoch = { value: 0 };
1966
1960
  try {
1967
1961
  let sdkTools;
1968
1962
  try {
@@ -1979,7 +1973,11 @@ async function runCopilotAttempt(params, deps) {
1979
1973
  sandbox,
1980
1974
  spawnWorkspaceDir: sandboxAwareSpawnWorkspaceDir,
1981
1975
  abortSignal: params.abortSignal,
1982
- attemptParams: input,
1976
+ attemptParams: observeToolTerminal ? {
1977
+ ...input,
1978
+ observeToolTerminal
1979
+ } : input,
1980
+ computerContextEpoch,
1983
1981
  sessionRef,
1984
1982
  onYieldDetected: () => {
1985
1983
  yieldDetected = true;
@@ -2054,8 +2052,14 @@ async function runCopilotAttempt(params, deps) {
2054
2052
  signal: params.abortSignal
2055
2053
  });
2056
2054
  userInputBridgeRef = userInputBridge;
2057
- 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);
2058
- 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;
2055
+ const sessionConfig = createSessionConfig(attemptInput, modelRef.id, sdkTools, poolAcquire.auth, sessionProvider, promptBuild.developerInstructions || void 0, effectiveWorkspaceDir, effectiveCwd, userInputBridge.onUserInputRequest, {
2056
+ hooksBridgeOptions: hasNativePromptHook ? { onUserPromptSubmitted: ({ additionalContext, prompt }) => emitLlmInput(prompt, additionalContext) } : void 0,
2057
+ includeAskUser: !ringZeroSystemAgentRun
2058
+ });
2059
+ const compactionSessionConfig = byokProxy ? createSessionConfig(attemptInput, modelRef.id, sdkTools, poolAcquire.auth, poolAcquire.provider, promptBuild.developerInstructions || void 0, effectiveWorkspaceDir, effectiveCwd, userInputBridge.onUserInputRequest, {
2060
+ hooksBridgeOptions: hasNativePromptHook ? { onUserPromptSubmitted: ({ additionalContext, prompt }) => emitLlmInput(prompt, additionalContext) } : void 0,
2061
+ includeAskUser: !ringZeroSystemAgentRun
2062
+ }) : sessionConfig;
2059
2063
  const replayDecision = decideReplayAction({
2060
2064
  sdkSessionId: input.initialReplayState?.sdkSessionId,
2061
2065
  replayInvalid: input.initialReplayState?.replayInvalid
@@ -2088,6 +2092,11 @@ async function runCopilotAttempt(params, deps) {
2088
2092
  onAssistantDelta: input.onAssistantDelta,
2089
2093
  onAgentEvent: input.onAgentEvent,
2090
2094
  onNativeSubagentEvent: (event) => nativeSubagentTaskMirror?.handleEvent(event),
2095
+ onContextCompacted: () => {
2096
+ computerContextEpoch.value += 1;
2097
+ delete computerContextEpoch.frameToolCallId;
2098
+ delete computerContextEpoch.frameImageIdentity;
2099
+ },
2091
2100
  onCompactionStart: async () => {
2092
2101
  const sessionFile = readString(input.sessionFile);
2093
2102
  if (!sessionFile) return;
@@ -2245,20 +2254,21 @@ async function runCopilotAttempt(params, deps) {
2245
2254
  ...syntheticUser ? [syntheticUser] : [],
2246
2255
  ...taggedLastAssistant ? [taggedLastAssistant] : []
2247
2256
  ];
2248
- const sessionFileForMirror = readString(input.sessionFile);
2249
2257
  const openClawSessionIdForMirror = readString(input.sessionId);
2258
+ const openClawSessionKeyForMirror = readString(input.sessionKey);
2259
+ const openClawStorePathForMirror = readString(input.sessionTarget?.storePath);
2250
2260
  const mirrorScopeSessionId = sessionIdUsed ?? openClawSessionIdForMirror;
2251
- if (sessionFileForMirror && openClawSessionIdForMirror && messagesSnapshot.length > 0) {
2261
+ if (openClawSessionIdForMirror && openClawSessionKeyForMirror && openClawStorePathForMirror && messagesSnapshot.length > 0) {
2252
2262
  const taggedMessages = messagesSnapshot.map((message, index) => {
2253
2263
  if (message.role !== "user" && message.role !== "assistant" && message.role !== "toolResult") return message;
2254
2264
  if (hasMirrorIdentity(message)) return message;
2255
2265
  return attachCopilotMirrorIdentity(message, `${sdkSessionId ?? mirrorScopeSessionId ?? "attempt"}:${message.role}:${index}`);
2256
2266
  });
2257
2267
  await dualWriteCopilotTranscriptBestEffort({
2258
- sessionFile: sessionFileForMirror,
2259
2268
  sessionId: openClawSessionIdForMirror,
2260
- sessionKey: readString(input.sessionKey),
2269
+ sessionKey: openClawSessionKeyForMirror,
2261
2270
  agentId: readString(input.agentId),
2271
+ storePath: openClawStorePathForMirror,
2262
2272
  messages: taggedMessages,
2263
2273
  idempotencyScope: mirrorScopeSessionId ? `copilot:${mirrorScopeSessionId}` : void 0,
2264
2274
  config: input.config
@@ -2278,6 +2288,7 @@ async function runCopilotAttempt(params, deps) {
2278
2288
  startedCount: snap?.startedCount ?? 0
2279
2289
  },
2280
2290
  lastAssistant,
2291
+ lastToolError,
2281
2292
  messagesSnapshot,
2282
2293
  now,
2283
2294
  promptError,
@@ -2343,6 +2354,7 @@ function createResult(params, state) {
2343
2354
  startedCount: 0
2344
2355
  },
2345
2356
  lastAssistant: state.lastAssistant,
2357
+ ...state.lastToolError ? { lastToolError: state.lastToolError } : {},
2346
2358
  messagesSnapshot: state.messagesSnapshot,
2347
2359
  messagingToolSentMediaUrls: [],
2348
2360
  messagingToolSentTargets: [],
@@ -2364,10 +2376,9 @@ function createPromptError(code, message, cause) {
2364
2376
  if (cause !== void 0) error.cause = cause;
2365
2377
  return error;
2366
2378
  }
2367
- function createSessionConfig(params, sdkModelId, sdkTools, resolvedAuth, resolvedProvider, systemMessageContent, effectiveWorkspaceDir, effectiveCwd, onUserInputRequest, hooksBridgeOptions) {
2379
+ function createSessionConfig(params, sdkModelId, sdkTools, resolvedAuth, resolvedProvider, systemMessageContent, effectiveWorkspaceDir, effectiveCwd, onUserInputRequest, options) {
2368
2380
  const permissionPolicy = params.permissionPolicy ?? rejectAllPolicy;
2369
- const hooks = createHooksBridge(params.hooksConfig, hooksBridgeOptions);
2370
- const infiniteSessions = createInfiniteSessionConfig(params.infiniteSessionConfig);
2381
+ const hooks = createHooksBridge(params.hooksConfig, options.hooksBridgeOptions);
2371
2382
  return {
2372
2383
  model: sdkModelId,
2373
2384
  onPermissionRequest: createPermissionBridge(permissionPolicy),
@@ -2375,10 +2386,10 @@ function createSessionConfig(params, sdkModelId, sdkTools, resolvedAuth, resolve
2375
2386
  ...resolvedProvider.provider ? { provider: resolvedProvider.provider } : {},
2376
2387
  ...hooks ? { hooks } : {},
2377
2388
  ...typeof params.enableSessionTelemetry === "boolean" ? { enableSessionTelemetry: params.enableSessionTelemetry } : {},
2378
- ...infiniteSessions ? { infiniteSessions } : {},
2389
+ ...params.infiniteSessionConfig ? { infiniteSessions: params.infiniteSessionConfig } : {},
2379
2390
  reasoningEffort: params.reasoningEffort,
2380
2391
  tools: sdkTools,
2381
- availableTools: buildCopilotAvailableTools(sdkTools),
2392
+ availableTools: buildCopilotAvailableTools(sdkTools, options.includeAskUser),
2382
2393
  workingDirectory: effectiveCwd ?? effectiveWorkspaceDir ?? readResolvedAttemptPath(params.workspaceDir),
2383
2394
  ...effectiveWorkspaceDir && effectiveCwd && effectiveCwd !== effectiveWorkspaceDir ? { instructionDirectories: [effectiveWorkspaceDir] } : {},
2384
2395
  ...resolvedAuth.authMode === "gitHubToken" && resolvedAuth.gitHubToken ? { gitHubToken: resolvedAuth.gitHubToken } : {},
@@ -2388,8 +2399,13 @@ function createSessionConfig(params, sdkModelId, sdkTools, resolvedAuth, resolve
2388
2399
  } } : {}
2389
2400
  };
2390
2401
  }
2391
- function buildCopilotAvailableTools(sdkTools) {
2392
- return [.../* @__PURE__ */ new Set([...sdkTools.map((tool) => tool.name), ...COPILOT_ASK_USER_AVAILABLE_TOOLS])];
2402
+ function buildCopilotAvailableTools(sdkTools, includeAskUser) {
2403
+ const availableTools = sdkTools.map((tool) => tool.name);
2404
+ if (includeAskUser) availableTools.push(...COPILOT_ASK_USER_AVAILABLE_TOOLS);
2405
+ return [...new Set(availableTools)];
2406
+ }
2407
+ function isSystemAgentOnlyToolAllowlist(toolsAllow) {
2408
+ return toolsAllow?.length === 1 && toolsAllow[0]?.trim().toLowerCase() === "openclaw";
2393
2409
  }
2394
2410
  async function createMessageOptions(params, context) {
2395
2411
  const attachments = createPromptImageAttachments(await resolvePromptImages(params, context));
@@ -10,8 +10,7 @@ function normalizeCompatibilityConfig({ cfg }) {
10
10
  * Session-state ownership claim for the copilot agent runtime.
11
11
  *
12
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).
13
+ * - providerIds: The subscription Copilot providers.
15
14
  * - runtimeIds: Our harness id (matches harness.ts `id` field).
16
15
  * - cliSessionKeys: Session keys this harness writes; doctor uses
17
16
  * this when pruning stale CLI session state.
@@ -481,7 +481,7 @@ function resolveCopilotProvider(params) {
481
481
  };
482
482
  }
483
483
  function isCopilotByokUnsupportedProviderError(error) {
484
- 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");
484
+ return error instanceof Error && (error.message === COPILOT_BYOK_PROVIDER_ERROR || error.message === COPILOT_BYOK_TRANSPORT_POLICY_ERROR || error.message === COPILOT_BYOK_ENDPOINT_POLICY_ERROR);
485
485
  }
486
486
  function supportsCopilotByokProviderShape(model) {
487
487
  if (!readString(model.baseUrl) || hasUnsupportedTransportPolicy(model)) return false;
@@ -894,7 +894,7 @@ function createCopilotAgentHarness(options) {
894
894
  async runAttempt(params) {
895
895
  const attemptPromise = (async () => {
896
896
  if (disposed) throw new Error("[copilot] harness has been disposed; cannot start new attempts");
897
- const { resolvePoolAcquire, runCopilotAttempt } = await import("./attempt-D6MSc00N.js");
897
+ const { resolvePoolAcquire, runCopilotAttempt } = await import("./attempt-Bwgye68K.js");
898
898
  if (disposed) throw new Error("[copilot] harness was disposed while starting an attempt");
899
899
  const pool = await getPool();
900
900
  if (disposed) throw new Error("[copilot] harness was disposed while starting an attempt");
@@ -1010,7 +1010,7 @@ function createCopilotAgentHarness(options) {
1010
1010
  };
1011
1011
  const tracked = trackedSessions.get(openclawSessionId);
1012
1012
  const currentCompactKey = computeSessionCompactKey(params);
1013
- const { resolvePoolAcquire } = await import("./attempt-D6MSc00N.js");
1013
+ const { resolvePoolAcquire } = await import("./attempt-Bwgye68K.js");
1014
1014
  let resolvedPoolAcquire;
1015
1015
  let currentAuth;
1016
1016
  try {
package/dist/harness.js CHANGED
@@ -1,2 +1,2 @@
1
- import { t as createCopilotAgentHarness } from "./harness-BQhGzc1Z.js";
1
+ import { t as createCopilotAgentHarness } from "./harness-DeWR8_cC.js";
2
2
  export { createCopilotAgentHarness };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as createCopilotAgentHarness } from "./harness-BQhGzc1Z.js";
1
+ import { t as createCopilotAgentHarness } from "./harness-DeWR8_cC.js";
2
2
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
3
3
  import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
4
4
  //#region extensions/copilot/index.ts
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@openclaw/copilot",
3
- "version": "2026.7.1",
3
+ "version": "2026.7.2-beta.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/copilot",
9
- "version": "2026.7.1",
9
+ "version": "2026.7.2-beta.2",
10
10
  "dependencies": {
11
11
  "@github/copilot-sdk": "1.0.5"
12
12
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/copilot",
3
- "version": "2026.7.1",
3
+ "version": "2026.7.2-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",
@@ -11,7 +11,6 @@
11
11
  "@github/copilot-sdk": "1.0.5"
12
12
  },
13
13
  "devDependencies": {
14
- "@github/copilot": "1.0.68",
15
14
  "@openclaw/plugin-sdk": "workspace:*"
16
15
  },
17
16
  "openclaw": {
@@ -25,10 +24,10 @@
25
24
  "minHostVersion": ">=2026.5.28"
26
25
  },
27
26
  "compat": {
28
- "pluginApi": ">=2026.7.1"
27
+ "pluginApi": ">=2026.7.2-beta.2"
29
28
  },
30
29
  "build": {
31
- "openclawVersion": "2026.7.1",
30
+ "openclawVersion": "2026.7.2-beta.2",
32
31
  "bundledDist": false
33
32
  },
34
33
  "release": {
@@ -47,7 +46,7 @@
47
46
  "README.md"
48
47
  ],
49
48
  "peerDependencies": {
50
- "openclaw": ">=2026.7.1"
49
+ "openclaw": ">=2026.7.2-beta.2"
51
50
  },
52
51
  "peerDependenciesMeta": {
53
52
  "openclaw": {