@keyou007/agent-bot 0.1.24-alpha.1 → 0.1.24-alpha.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/CHANGELOG.md +7 -0
- package/README.md +6 -2
- package/README.zh.md +6 -2
- package/dist/cli/SelfUpdateRunner.js +2 -0
- package/dist/cli/SelfUpdateRunner.js.map +1 -1
- package/dist/codex/CodexEventMapper.js +13 -2
- package/dist/codex/CodexEventMapper.js.map +1 -1
- package/dist/codex/CodexRuntime.js +3 -3
- package/dist/codex/CodexRuntime.js.map +1 -1
- package/dist/feishu/CardRenderer.js +4 -0
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/feishu/FeishuMarkdown.js +70 -4
- package/dist/feishu/FeishuMarkdown.js.map +1 -1
- package/dist/local-files/TurnPreviewPage.js +4 -0
- package/dist/local-files/TurnPreviewPage.js.map +1 -1
- package/dist/presentation/TurnStateReducer.js +2 -2
- package/dist/presentation/TurnStateReducer.js.map +1 -1
- package/dist/proxy/ProxySessionController.js +1 -15
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/skills/agent-bot/SKILL.md +1 -1
- package/src/cli/SelfUpdateRunner.ts +2 -0
- package/src/codex/CodexEventMapper.ts +11 -2
- package/src/codex/CodexRuntime.ts +4 -4
- package/src/feishu/CardRenderer.ts +4 -0
- package/src/feishu/FeishuMarkdown.ts +69 -3
- package/src/local-files/TurnPreviewPage.ts +4 -0
- package/src/presentation/TurnStateReducer.ts +2 -2
- package/src/proxy/ProxySessionController.ts +1 -16
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keyou007/agent-bot",
|
|
3
|
-
"version": "0.1.24-alpha.
|
|
3
|
+
"version": "0.1.24-alpha.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@keyou007/agent-bot",
|
|
9
|
-
"version": "0.1.24-alpha.
|
|
9
|
+
"version": "0.1.24-alpha.2",
|
|
10
10
|
"license": "GPL-3.0-only",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@agentclientprotocol/sdk": "1.2.1",
|
package/package.json
CHANGED
|
@@ -125,7 +125,7 @@ agentbot task permissions [auto|confirm]
|
|
|
125
125
|
|
|
126
126
|
Omit the value to inspect the current setting and available choices. `agent` changes the default Agent for future tasks in that conversation. The other settings affect the specified task from its next request and become the saved defaults for that Agent.
|
|
127
127
|
|
|
128
|
-
A successful Provider switch confirms settings, not inference connectivity. Model-list fallback warnings appear on settings cards; inspect the Provider service when availability is unconfirmed. App Server errors and native retry notices
|
|
128
|
+
A successful Provider switch confirms settings, not inference connectivity. Model-list fallback warnings appear on settings cards; inspect the Provider service when availability is unconfirmed. App Server errors and native retry notices show the message and supplied `additionalDetails` as literal text on the current progress card and Preview without terminating the turn. Use Preview for long reasons; `Reconnecting` alone does not identify the cause when the Agent supplies no details, and old snapshots are not backfilled. Do not stop or restart a retrying task unless the user requests it.
|
|
129
129
|
|
|
130
130
|
Providers such as a Responses bridge may omit assistant message phases. Unphased text stays in the progress timeline; successful completion promotes only the last message not followed by a tool start. Do not diagnose progress text in the answer area as a CSS issue without checking message phases. Old snapshots are not automatically backfilled.
|
|
131
131
|
|
|
@@ -571,6 +571,7 @@ export function runNpmCommand(args: string[], cwd: string): CommandResult {
|
|
|
571
571
|
{
|
|
572
572
|
cwd,
|
|
573
573
|
env: process.env,
|
|
574
|
+
windowsHide: true,
|
|
574
575
|
encoding: "utf8",
|
|
575
576
|
maxBuffer: 32 * 1024 * 1024,
|
|
576
577
|
shell: process.platform === "win32",
|
|
@@ -610,6 +611,7 @@ function runNode(args: string[], cwd: string): CommandResult {
|
|
|
610
611
|
spawnSync(process.execPath, args, {
|
|
611
612
|
cwd,
|
|
612
613
|
env: process.env,
|
|
614
|
+
windowsHide: true,
|
|
613
615
|
encoding: "utf8",
|
|
614
616
|
maxBuffer: 32 * 1024 * 1024,
|
|
615
617
|
}),
|
|
@@ -73,7 +73,7 @@ export function mapCodexNotification(method: string, params: unknown): MappedCod
|
|
|
73
73
|
if (!threadId || !turnId) return undefined;
|
|
74
74
|
|
|
75
75
|
if (method === "error" && isRecord(params.error)) {
|
|
76
|
-
const message =
|
|
76
|
+
const message = formatCodexError(params.error);
|
|
77
77
|
if (!message || typeof params.willRetry !== "boolean") return undefined;
|
|
78
78
|
return { kind: "runtime_error", threadId, turnId, message, willRetry: params.willRetry };
|
|
79
79
|
}
|
|
@@ -200,7 +200,7 @@ export function mapCodexNotification(method: string, params: unknown): MappedCod
|
|
|
200
200
|
if (method === "turn/completed" && isRecord(params.turn)) {
|
|
201
201
|
const status = params.turn.status;
|
|
202
202
|
const mappedStatus = status === "interrupted" ? "cancelled" : status === "failed" ? "failed" : "completed";
|
|
203
|
-
const error =
|
|
203
|
+
const error = formatCodexError(params.turn.error);
|
|
204
204
|
return {
|
|
205
205
|
kind: "terminal",
|
|
206
206
|
threadId,
|
|
@@ -213,6 +213,15 @@ export function mapCodexNotification(method: string, params: unknown): MappedCod
|
|
|
213
213
|
return undefined;
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
+
export function formatCodexError(error: unknown): string | undefined {
|
|
217
|
+
if (!isRecord(error)) return undefined;
|
|
218
|
+
const message = stringValue(error.message)?.trim();
|
|
219
|
+
const details = stringValue(error.additionalDetails)?.trim();
|
|
220
|
+
if (!message) return details || undefined;
|
|
221
|
+
if (!details || message.includes(details)) return message;
|
|
222
|
+
return details.includes(message) ? details : `${message}\n\n${details}`;
|
|
223
|
+
}
|
|
224
|
+
|
|
216
225
|
function mapTool(
|
|
217
226
|
item: Record<string, unknown>,
|
|
218
227
|
phase: "started" | "updated",
|
|
@@ -29,7 +29,7 @@ import type {
|
|
|
29
29
|
import { appendGeneratedImageMarkdown } from "../utils/generatedImageMarkdown.js";
|
|
30
30
|
import { normalizeTaskTitle } from "../utils/taskTitle.js";
|
|
31
31
|
import { AppServerRequestError } from "./AppServerConnection.js";
|
|
32
|
-
import { mapCodexNotification } from "./CodexEventMapper.js";
|
|
32
|
+
import { formatCodexError, mapCodexNotification } from "./CodexEventMapper.js";
|
|
33
33
|
import { CodexLocalActivityDetector } from "./CodexLocalActivityDetector.js";
|
|
34
34
|
import { detectProjectlessWorkspace } from "./ProjectlessWorkspace.js";
|
|
35
35
|
import { threadWriterLockPath } from "./ThreadWriterProcess.js";
|
|
@@ -1460,7 +1460,7 @@ export class CodexRuntime implements AgentRuntime {
|
|
|
1460
1460
|
type: "turn_failed",
|
|
1461
1461
|
sessionId: session.localSessionId,
|
|
1462
1462
|
turnId: turn.id,
|
|
1463
|
-
message: turn.error
|
|
1463
|
+
message: formatCodexError(turn.error) ?? "App Server turn failed.",
|
|
1464
1464
|
});
|
|
1465
1465
|
return;
|
|
1466
1466
|
}
|
|
@@ -1639,7 +1639,7 @@ interface CodexTurnSnapshot {
|
|
|
1639
1639
|
status?: string;
|
|
1640
1640
|
savedPath?: string;
|
|
1641
1641
|
}>;
|
|
1642
|
-
error?: { message?: string } | null;
|
|
1642
|
+
error?: { message?: string; additionalDetails?: string | null } | null;
|
|
1643
1643
|
startedAt?: number | null;
|
|
1644
1644
|
durationMs?: number | null;
|
|
1645
1645
|
}
|
|
@@ -1798,7 +1798,7 @@ function remoteSessionSummary(thread: CodexThreadSnapshot): RemoteSessionSummary
|
|
|
1798
1798
|
finalResponse: lastTurn && lastTurn.status !== "inProgress"
|
|
1799
1799
|
? appendGeneratedImageMarkdown(extractFinalResponse(lastTurn), extractGeneratedImagePaths(lastTurn)) || undefined
|
|
1800
1800
|
: undefined,
|
|
1801
|
-
lastError: lastTurn?.error
|
|
1801
|
+
lastError: formatCodexError(lastTurn?.error),
|
|
1802
1802
|
lastTurnToolCount: toolCounts?.total,
|
|
1803
1803
|
lastTurnCompletedToolCount: toolCounts?.completed,
|
|
1804
1804
|
lastTurnFailedToolCount: toolCounts?.failed,
|
|
@@ -1847,6 +1847,10 @@ function renderActivity(
|
|
|
1847
1847
|
if (activity.kind === "assistant" || (activity.kind === "reasoning" && activity.id.startsWith("commentary:"))) {
|
|
1848
1848
|
const text = activity.text.trim();
|
|
1849
1849
|
if (!text) return [];
|
|
1850
|
+
if (activity.id.startsWith("commentary:runtime-error:")) {
|
|
1851
|
+
const chunks = fullAssistantText ? splitText(text, ACTIVITY_TEXT_CHUNK) : [text];
|
|
1852
|
+
return chunks.map((chunk) => markdown(codeBlock(chunk, fullAssistantText ? ACTIVITY_TEXT_CHUNK : MAX_LIVE_ASSISTANT_TEXT)));
|
|
1853
|
+
}
|
|
1850
1854
|
return fullAssistantText
|
|
1851
1855
|
? splitText(text, ACTIVITY_TEXT_CHUNK).map(markdown)
|
|
1852
1856
|
: [markdown(truncateText(text, MAX_LIVE_ASSISTANT_TEXT))];
|
|
@@ -54,11 +54,25 @@ function includeLocalFileLineReferences(
|
|
|
54
54
|
projectCwd?: string,
|
|
55
55
|
localFileUrl?: LocalFileUrlResolver,
|
|
56
56
|
): string {
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
const codeSpans = inlineCodeSpans(markdown);
|
|
58
|
+
return markdown.replace(MARKDOWN_LINK, (link, imageMarker: string, label: string, rawTarget: string, offset: number) => {
|
|
59
|
+
if (imageMarker || codeSpans.some(([start, end]) => offset >= start && offset < end)) return link;
|
|
59
60
|
|
|
60
61
|
const target = unwrapMarkdownTarget(rawTarget.trim());
|
|
61
|
-
if (!isLocalFileTarget(target))
|
|
62
|
+
if (!isLocalFileTarget(target)) {
|
|
63
|
+
const relative = resolveRelativeFileTarget(target, projectCwd);
|
|
64
|
+
if (!relative || isLocalImageTarget(relative.filePath)) return link;
|
|
65
|
+
const viewerUrl = localFileUrl?.(relative.filePath, relative.reference);
|
|
66
|
+
if (!viewerUrl) {
|
|
67
|
+
const displayPath = displayAbsolutePath(relative.filePath) + (relative.reference ?? "");
|
|
68
|
+
return label ? `${label}(${inlineCode(displayPath)})` : inlineCode(displayPath);
|
|
69
|
+
}
|
|
70
|
+
const url = new URL(viewerUrl);
|
|
71
|
+
if (relative.hash) url.hash = relative.hash;
|
|
72
|
+
const normalizedLabel = relative.reference && !label.endsWith(relative.reference)
|
|
73
|
+
? `${label}${relative.reference}` : label;
|
|
74
|
+
return `[${normalizedLabel}](${url.toString()})`;
|
|
75
|
+
}
|
|
62
76
|
if (isLocalImageTarget(target)) return link;
|
|
63
77
|
|
|
64
78
|
const reference = LOCAL_FILE_LINE_REFERENCE.exec(target)?.[1];
|
|
@@ -84,6 +98,58 @@ function includeLocalFileLineReferences(
|
|
|
84
98
|
});
|
|
85
99
|
}
|
|
86
100
|
|
|
101
|
+
interface RelativeFileTarget {
|
|
102
|
+
filePath: string;
|
|
103
|
+
reference?: string;
|
|
104
|
+
hash?: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function resolveRelativeFileTarget(target: string, projectCwd?: string): RelativeFileTarget | undefined {
|
|
108
|
+
if (!projectCwd || !target || /^(?:#|\?|\/\/)/u.test(target)) return undefined;
|
|
109
|
+
const pathApi = usesWindowsPaths(projectCwd) ? path.win32 : path.posix;
|
|
110
|
+
if (!pathApi.isAbsolute(projectCwd)) return undefined;
|
|
111
|
+
const suffixStart = target.search(/[?#]/u);
|
|
112
|
+
const pathTarget = suffixStart < 0 ? target : target.slice(0, suffixStart);
|
|
113
|
+
const lineSuffix = LOCAL_FILE_LINE_REFERENCE.exec(pathTarget)?.[1];
|
|
114
|
+
// A basename with a line suffix (report.md:12) is a file, not a URI scheme.
|
|
115
|
+
const fileWithLine = /^[^:/\\]+\.[^:/\\]+:\d+(?::\d+)?$/u.test(pathTarget);
|
|
116
|
+
if (!fileWithLine && /^[a-z][a-z\d+.-]*:/iu.test(target)) return undefined;
|
|
117
|
+
const encodedPath = lineSuffix ? pathTarget.slice(0, -lineSuffix.length) : pathTarget;
|
|
118
|
+
try {
|
|
119
|
+
const decodedPath = decodeURIComponent(encodedPath);
|
|
120
|
+
if (!decodedPath || /[\x00-\x1f\x7f]/u.test(decodedPath)
|
|
121
|
+
|| pathApi.isAbsolute(decodedPath) || /^[a-z]:/iu.test(decodedPath)) return undefined;
|
|
122
|
+
const filePath = pathApi.resolve(projectCwd, decodedPath);
|
|
123
|
+
const hashStart = target.indexOf("#");
|
|
124
|
+
const hash = hashStart < 0 ? undefined : new URL(target.slice(hashStart), "http://localhost/").hash;
|
|
125
|
+
const hashLine = /^#L(\d+)(?:C\d+)?(?:-L?\d+(?:C\d+)?)?$/u.exec(hash ?? "")?.[1];
|
|
126
|
+
const validHashLine = hashLine && Number.isSafeInteger(Number(hashLine)) && Number(hashLine) > 0;
|
|
127
|
+
return {
|
|
128
|
+
filePath,
|
|
129
|
+
reference: validHashLine ? `:${Number(hashLine)}` : hash ? undefined : lineSuffix,
|
|
130
|
+
hash: validHashLine ? `#L${Number(hashLine)}` : hash,
|
|
131
|
+
};
|
|
132
|
+
} catch {
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function inlineCodeSpans(markdown: string): Array<[number, number]> {
|
|
138
|
+
const runs = [...markdown.matchAll(/`+/gu)];
|
|
139
|
+
const spans: Array<[number, number]> = [];
|
|
140
|
+
for (let index = 0; index < runs.length; index += 1) {
|
|
141
|
+
const opener = runs[index]!;
|
|
142
|
+
const backslashes = /\\*$/u.exec(markdown.slice(0, opener.index))?.[0].length ?? 0;
|
|
143
|
+
if (backslashes % 2 !== 0) continue;
|
|
144
|
+
const closing = runs.findIndex((run, candidate) => candidate > index && run[0].length === opener[0].length);
|
|
145
|
+
if (closing < 0) continue;
|
|
146
|
+
const closer = runs[closing]!;
|
|
147
|
+
spans.push([opener.index, closer.index + closer[0].length]);
|
|
148
|
+
index = closing;
|
|
149
|
+
}
|
|
150
|
+
return spans;
|
|
151
|
+
}
|
|
152
|
+
|
|
87
153
|
function projectFilePathLabel(
|
|
88
154
|
target: string,
|
|
89
155
|
projectCwd: string,
|
|
@@ -362,6 +362,9 @@ function renderActivity(
|
|
|
362
362
|
): string {
|
|
363
363
|
switch (activity.kind) {
|
|
364
364
|
case "assistant":
|
|
365
|
+
if (activity.id.startsWith("commentary:runtime-error:")) {
|
|
366
|
+
return `<article class="activity message runtime-error" data-activity="${escapeAttribute(activity.id)}"><pre>${escapeHtml(activity.text)}</pre></article>`;
|
|
367
|
+
}
|
|
365
368
|
return renderMessageActivity(activity.id, activity.text, "commentary", renderText);
|
|
366
369
|
case "user":
|
|
367
370
|
return renderMessageActivity(activity.id, activity.text, "user", renderText);
|
|
@@ -813,6 +816,7 @@ summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px;
|
|
|
813
816
|
.tool-meta span { white-space: nowrap; }
|
|
814
817
|
.tool-header-timing { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
815
818
|
.tool-header-timing:empty, .tool-footer-timing:empty { display: none; }
|
|
819
|
+
.runtime-error pre { white-space: pre-wrap; overflow-wrap: anywhere; border-left: 2px solid var(--danger); }
|
|
816
820
|
.error-output, .error-result h2 { color: var(--danger); }
|
|
817
821
|
.muted, .empty { color: var(--muted); }
|
|
818
822
|
.notice, .empty { padding: 10px 0; }
|
|
@@ -179,7 +179,7 @@ export function reduceTurnEvent(state: TurnViewState, event: AgentEvent): TurnVi
|
|
|
179
179
|
const activityUpdate = upsertReasoningActivity(
|
|
180
180
|
warning ? activities.filter((activity) => activity.id !== (event.activityId ?? "progress")) : activities,
|
|
181
181
|
event.activityId ?? "progress",
|
|
182
|
-
warning ? bound(event.text) : event.text,
|
|
182
|
+
warning && !event.activityId?.startsWith("commentary:runtime-error:") ? bound(event.text) : event.text,
|
|
183
183
|
!warning && event.append === true,
|
|
184
184
|
event.activityId?.startsWith("commentary:") ? "assistant" : "reasoning",
|
|
185
185
|
);
|
|
@@ -252,7 +252,7 @@ export function reduceTurnEvent(state: TurnViewState, event: AgentEvent): TurnVi
|
|
|
252
252
|
approval: undefined,
|
|
253
253
|
completedAt,
|
|
254
254
|
durationMs: Math.max(0, completedAt - state.startedAt),
|
|
255
|
-
error:
|
|
255
|
+
error: event.message,
|
|
256
256
|
};
|
|
257
257
|
}
|
|
258
258
|
}
|
|
@@ -2158,7 +2158,7 @@ export class ProxySessionController {
|
|
|
2158
2158
|
await this.openExecutionSettings(contextKey, "model");
|
|
2159
2159
|
return;
|
|
2160
2160
|
case "provider":
|
|
2161
|
-
await this.
|
|
2161
|
+
await this.openExecutionSettings(contextKey, "provider");
|
|
2162
2162
|
return;
|
|
2163
2163
|
case "thinking":
|
|
2164
2164
|
await this.openExecutionSettings(contextKey, "thinking");
|
|
@@ -5179,21 +5179,6 @@ export class ProxySessionController {
|
|
|
5179
5179
|
await this.openExecutionSettings(contextKey, "agent");
|
|
5180
5180
|
}
|
|
5181
5181
|
|
|
5182
|
-
private async openProviderSettings(contextKey: string): Promise<void> {
|
|
5183
|
-
const loaded = await this.loadSession(this.requireCurrentSession(contextKey));
|
|
5184
|
-
const providers = loaded.runtime.kind === "codex" && loaded.runtime.listModelProviders
|
|
5185
|
-
? await this.modelProviderOptions(loaded)
|
|
5186
|
-
: [];
|
|
5187
|
-
const current = loaded.session.modelProvider?.trim()
|
|
5188
|
-
|| providers.find((provider) => provider.isDefault)?.id
|
|
5189
|
-
|| "运行时默认";
|
|
5190
|
-
if (providers.length <= 1) {
|
|
5191
|
-
await this.outbound.sendText(contextKey, `当前 Provider:${current}\n当前没有其他 Provider 可以切换。`);
|
|
5192
|
-
return;
|
|
5193
|
-
}
|
|
5194
|
-
await this.openExecutionSettings(contextKey, "provider", { sessionId: loaded.record.localSessionId });
|
|
5195
|
-
}
|
|
5196
|
-
|
|
5197
5182
|
private async selectProvider(
|
|
5198
5183
|
contextKey: string,
|
|
5199
5184
|
modelProvider: string,
|