@blade-hq/agent-client 2610.0.0-beta.3 → 2610.0.0-beta.31
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 +69 -4
- package/dist/blade-client.d.ts +2 -2
- package/dist/index.d.ts +9 -2
- package/dist/index.js +559 -75
- package/dist/index.js.map +1 -1
- package/dist/platform-endpoints.d.ts +14 -0
- package/dist/resources/computers.d.ts +63 -0
- package/dist/resources/models.d.ts +2 -0
- package/dist/resources/sessions.d.ts +3 -1
- package/dist/schemas/context.d.ts +49 -0
- package/dist/schemas/event.d.ts +9 -0
- package/dist/schemas/message-utils.d.ts +5 -1
- package/dist/schemas/message.d.ts +5 -0
- package/dist/schemas/projection.d.ts +14 -1
- package/dist/schemas/session.d.ts +3 -1
- package/dist/session/agent-session.d.ts +3 -1
- package/dist/session/state.d.ts +4 -1
- package/dist/shared/projection/builder.d.ts +2 -0
- package/dist/shared/projection/context.d.ts +3 -0
- package/dist/shared/projection/history.d.ts +1 -0
- package/dist/shared/projection/index.d.ts +1 -0
- package/dist/types/socket-events.d.ts +13 -4
- package/package.json +1 -1
- package/public-api.md +214 -5
package/dist/index.js
CHANGED
|
@@ -352,8 +352,7 @@ var HeadlessResource = class {
|
|
|
352
352
|
socket.on("system:error", onSystemError);
|
|
353
353
|
socket.emit("session:subscribe", {
|
|
354
354
|
session_id,
|
|
355
|
-
event_format: "raw"
|
|
356
|
-
stream_tokens: this.client.options.streamTokens !== false
|
|
355
|
+
event_format: "raw"
|
|
357
356
|
});
|
|
358
357
|
socket.emit(
|
|
359
358
|
"chat:send",
|
|
@@ -425,6 +424,50 @@ function ensureSocketConnected(socket, timeoutMs, detail) {
|
|
|
425
424
|
});
|
|
426
425
|
}
|
|
427
426
|
|
|
427
|
+
// src/resources/computers.ts
|
|
428
|
+
function computerState(computer) {
|
|
429
|
+
if (computer.is_primary) return "primary";
|
|
430
|
+
if (computer.enabled) return "enabled";
|
|
431
|
+
return computer.online ? "idle" : "offline";
|
|
432
|
+
}
|
|
433
|
+
function canToggleComputer(computer) {
|
|
434
|
+
return !computer.is_primary;
|
|
435
|
+
}
|
|
436
|
+
function sortComputers(computers) {
|
|
437
|
+
return [...computers].sort((a, b) => {
|
|
438
|
+
if (a.enabled !== b.enabled) return a.enabled ? -1 : 1;
|
|
439
|
+
if (a.online !== b.online) return a.online ? -1 : 1;
|
|
440
|
+
return a.label.localeCompare(b.label);
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
var ComputersResource = class {
|
|
444
|
+
constructor(client) {
|
|
445
|
+
this.client = client;
|
|
446
|
+
}
|
|
447
|
+
client;
|
|
448
|
+
/**
|
|
449
|
+
* 列出当前用户的全部电脑,带上"在不在线"和"这次会话能不能用"。
|
|
450
|
+
* 服务端只查库,电脑离线时同样返回,所以拿得到"它离线了"这个结论。
|
|
451
|
+
*/
|
|
452
|
+
list(sessionId) {
|
|
453
|
+
return this.client.json(
|
|
454
|
+
"GET",
|
|
455
|
+
`/api/sessions/${encodeURIComponent(sessionId)}/computers`
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* 启动或停止一台电脑。只改这次会话的可用范围,不会碰远程机器,
|
|
460
|
+
* 电脑离线时同样能登记,等它上线就直接可用。
|
|
461
|
+
*/
|
|
462
|
+
setEnabled(sessionId, computer, enabled) {
|
|
463
|
+
return this.client.json(
|
|
464
|
+
"PUT",
|
|
465
|
+
`/api/sessions/${encodeURIComponent(sessionId)}/computers/${encodeURIComponent(computer)}/enabled`,
|
|
466
|
+
{ enabled }
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
|
|
428
471
|
// src/resources/models.ts
|
|
429
472
|
var ModelsResource = class {
|
|
430
473
|
constructor(client) {
|
|
@@ -473,6 +516,7 @@ var SessionInfo = type({
|
|
|
473
516
|
"disable_tools?": "string[]",
|
|
474
517
|
"runtime_type?": "string | null",
|
|
475
518
|
"daemon_id?": "string | null",
|
|
519
|
+
"agent_runtime_id?": "string | null",
|
|
476
520
|
"workspace_path?": "string | null",
|
|
477
521
|
"match?": "unknown"
|
|
478
522
|
});
|
|
@@ -1023,6 +1067,126 @@ function toolStatusFromResult(result) {
|
|
|
1023
1067
|
return inferToolStatus(text);
|
|
1024
1068
|
}
|
|
1025
1069
|
|
|
1070
|
+
// src/schemas/context.ts
|
|
1071
|
+
var BUILTIN_CONTEXT_TITLES = {
|
|
1072
|
+
"agent-instructions": "\u9879\u76EE\u8BF4\u660E",
|
|
1073
|
+
"skill-catalog": "\u53EF\u7528\u80FD\u529B",
|
|
1074
|
+
"runtime-context": "\u5F53\u524D\u5DE5\u4F5C\u73AF\u5883",
|
|
1075
|
+
"workspace-context": "\u5F53\u524D\u5DE5\u4F5C\u533A\u73AF\u5883"
|
|
1076
|
+
};
|
|
1077
|
+
var BUILTIN_CONTEXT_DETAILS = {
|
|
1078
|
+
"agent-instructions": "\u8FD9\u4E9B\u8BF4\u660E\u4F1A\u5E2E\u52A9\u667A\u80FD\u4F53\u7406\u89E3\u5F53\u524D\u9879\u76EE\u548C\u5DE5\u4F5C\u8981\u6C42\u3002",
|
|
1079
|
+
"skill-catalog": "\u8FD9\u4E9B\u80FD\u529B\u4F1A\u5E2E\u52A9\u667A\u80FD\u4F53\u9009\u62E9\u5408\u9002\u7684\u65B9\u5F0F\u5B8C\u6210\u4EFB\u52A1\u3002",
|
|
1080
|
+
"runtime-context": "\u8FD9\u4E9B\u4FE1\u606F\u4F1A\u5E2E\u52A9\u667A\u80FD\u4F53\u4E86\u89E3\u5F53\u524D\u6587\u4EF6\u5939\u548C\u8FD0\u884C\u6761\u4EF6\u3002",
|
|
1081
|
+
"workspace-context": "\u8FD9\u4E9B\u4FE1\u606F\u4F1A\u5E2E\u52A9\u667A\u80FD\u4F53\u4E86\u89E3\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u3001\u6587\u4EF6\u548C\u8FD0\u884C\u73AF\u5883\u3002"
|
|
1082
|
+
};
|
|
1083
|
+
function nonEmptyString(value) {
|
|
1084
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
1085
|
+
}
|
|
1086
|
+
function contextProjectionData(fields) {
|
|
1087
|
+
const contextKind = nonEmptyString(fields.context_kind);
|
|
1088
|
+
const contextKey = nonEmptyString(fields.context_key);
|
|
1089
|
+
const contextName = nonEmptyString(fields.context_name);
|
|
1090
|
+
const contextRevision = nonEmptyString(fields.context_revision);
|
|
1091
|
+
if (!contextKind || !contextKey || !contextName || !contextRevision || typeof fields.context_order !== "number" || !Number.isFinite(fields.context_order) || !fields.context_action) {
|
|
1092
|
+
return null;
|
|
1093
|
+
}
|
|
1094
|
+
return {
|
|
1095
|
+
context_kind: contextKind,
|
|
1096
|
+
context_key: contextKey,
|
|
1097
|
+
context_name: contextName,
|
|
1098
|
+
context_order: fields.context_order,
|
|
1099
|
+
context_action: fields.context_action,
|
|
1100
|
+
context_revision: contextRevision,
|
|
1101
|
+
display_title: nonEmptyString(fields.display_title),
|
|
1102
|
+
display_summary: nonEmptyString(fields.display_summary),
|
|
1103
|
+
sources: Array.isArray(fields.sources) ? fields.sources : []
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
function getContextDisplayState(context) {
|
|
1107
|
+
const title = nonEmptyString(context.display_title) ?? BUILTIN_CONTEXT_TITLES[context.context_kind] ?? "\u5BF9\u8BDD\u80CC\u666F";
|
|
1108
|
+
const isRemoved = context.context_action === "removed";
|
|
1109
|
+
const summary = nonEmptyString(context.display_summary) ?? (isRemoved ? "\u5DF2\u505C\u6B62\u7528\u4E8E\u540E\u7EED\u5BF9\u8BDD" : context.context_action === "retained" ? "\u7EE7\u7EED\u7528\u4E8E\u540E\u7EED\u5BF9\u8BDD" : context.context_kind === "agent-instructions" ? "\u5DF2\u66F4\u65B0\u672C\u6B21\u5BF9\u8BDD\u4F7F\u7528\u7684\u9879\u76EE\u8BF4\u660E" : context.context_kind === "skill-catalog" ? "\u5DF2\u66F4\u65B0\u672C\u6B21\u53EF\u7528\u80FD\u529B" : context.context_kind === "runtime-context" ? "\u5DF2\u66F4\u65B0\u672C\u6B21\u5DE5\u4F5C\u73AF\u5883" : context.context_kind === "workspace-context" ? "\u5DF2\u66F4\u65B0\u672C\u6B21\u5DE5\u4F5C\u533A\u73AF\u5883" : "\u5DF2\u66F4\u65B0\u672C\u6B21\u5BF9\u8BDD\u80CC\u666F");
|
|
1110
|
+
const detail = isRemoved ? `${title}\u5DF2\u4E0D\u518D\u7528\u4E8E\u540E\u7EED\u5BF9\u8BDD\u3002` : BUILTIN_CONTEXT_DETAILS[context.context_kind] ?? "\u8FD9\u4E9B\u4FE1\u606F\u4F1A\u5E2E\u52A9\u667A\u80FD\u4F53\u7406\u89E3\u5F53\u524D\u4EFB\u52A1\u3002";
|
|
1111
|
+
return { title, summary, detail, isRemoved };
|
|
1112
|
+
}
|
|
1113
|
+
function groupAdjacentContextRuns(kinds) {
|
|
1114
|
+
const runs = [];
|
|
1115
|
+
let current = null;
|
|
1116
|
+
kinds.forEach((kind, index) => {
|
|
1117
|
+
if (kind === null) {
|
|
1118
|
+
current = null;
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
if (current === null) {
|
|
1122
|
+
current = [];
|
|
1123
|
+
runs.push(current);
|
|
1124
|
+
}
|
|
1125
|
+
current.push(index);
|
|
1126
|
+
});
|
|
1127
|
+
return runs;
|
|
1128
|
+
}
|
|
1129
|
+
function getContextGroupDisplayState(contexts) {
|
|
1130
|
+
const count = contexts.length;
|
|
1131
|
+
if (count === 1) {
|
|
1132
|
+
const single = getContextDisplayState(contexts[0]);
|
|
1133
|
+
return { title: single.title, summary: single.summary, count };
|
|
1134
|
+
}
|
|
1135
|
+
const removed = contexts.filter(
|
|
1136
|
+
(context) => context.context_action === "removed"
|
|
1137
|
+
).length;
|
|
1138
|
+
const updated = count - removed;
|
|
1139
|
+
const parts = [];
|
|
1140
|
+
if (updated > 0) parts.push(`${updated} \u9879\u5DF2\u66F4\u65B0`);
|
|
1141
|
+
if (removed > 0) parts.push(`${removed} \u9879\u5DF2\u505C\u7528`);
|
|
1142
|
+
return {
|
|
1143
|
+
title: "\u5BF9\u8BDD\u80CC\u666F",
|
|
1144
|
+
summary: parts.join(" \xB7 ") || `${count} \u9879`,
|
|
1145
|
+
count
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
// src/shared/projection/context.ts
|
|
1150
|
+
function isRecord2(value) {
|
|
1151
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1152
|
+
}
|
|
1153
|
+
function buildContextTurn(entry, sequence) {
|
|
1154
|
+
if (entry.kind !== "context" || !isRecord2(entry.source) || !isRecord2(entry.data)) {
|
|
1155
|
+
return null;
|
|
1156
|
+
}
|
|
1157
|
+
const display = isRecord2(entry.data.display) ? entry.data.display : {};
|
|
1158
|
+
const message = isRecord2(entry.message) ? entry.message : {};
|
|
1159
|
+
const context = contextProjectionData({
|
|
1160
|
+
context_kind: typeof entry.source.kind === "string" ? entry.source.kind : null,
|
|
1161
|
+
context_key: typeof entry.source.key === "string" ? entry.source.key : null,
|
|
1162
|
+
context_name: typeof entry.data.contribution_name === "string" ? entry.data.contribution_name : null,
|
|
1163
|
+
context_order: typeof entry.data.order === "number" ? entry.data.order : null,
|
|
1164
|
+
context_action: entry.data.action === "published" || entry.data.action === "retained" || entry.data.action === "removed" ? entry.data.action : null,
|
|
1165
|
+
context_revision: typeof entry.data.revision === "string" ? entry.data.revision : null,
|
|
1166
|
+
display_title: typeof display.title === "string" ? display.title : null,
|
|
1167
|
+
display_summary: typeof display.summary === "string" ? display.summary : null,
|
|
1168
|
+
sources: Array.isArray(display.sources) ? display.sources.filter(isRecord2) : []
|
|
1169
|
+
});
|
|
1170
|
+
if (!context) return null;
|
|
1171
|
+
const turnId = typeof entry.id === "string" && entry.id ? entry.id : `context-${sequence}`;
|
|
1172
|
+
return {
|
|
1173
|
+
id: turnId,
|
|
1174
|
+
sequence,
|
|
1175
|
+
turn_id: turnId,
|
|
1176
|
+
loop_id: typeof entry.loop_name === "string" && entry.loop_name ? entry.loop_name : "root",
|
|
1177
|
+
kind: "context",
|
|
1178
|
+
role: "system",
|
|
1179
|
+
status: "completed",
|
|
1180
|
+
blocks: [],
|
|
1181
|
+
tool_calls: [],
|
|
1182
|
+
model: null,
|
|
1183
|
+
usage: null,
|
|
1184
|
+
duration_ms: 0,
|
|
1185
|
+
context_content: Object.hasOwn(message, "content") ? message.content : null,
|
|
1186
|
+
...context
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1026
1190
|
// src/shared/projection/builder.ts
|
|
1027
1191
|
function hasChatRunEvent(events) {
|
|
1028
1192
|
return events.some(
|
|
@@ -1058,7 +1222,8 @@ function completedResponseText(content) {
|
|
|
1058
1222
|
).filter((part) => part.type === "text").map((part) => String(part.text ?? "")).join("");
|
|
1059
1223
|
}
|
|
1060
1224
|
function replaceTextContent(state, content) {
|
|
1061
|
-
|
|
1225
|
+
const currentContent = state.blocks.filter((block) => block.type === "text").map((block) => String(block.content ?? "")).join("");
|
|
1226
|
+
if (currentContent === content) return;
|
|
1062
1227
|
const replacement = { type: "text", content };
|
|
1063
1228
|
const blocks = [];
|
|
1064
1229
|
let inserted = false;
|
|
@@ -1078,6 +1243,31 @@ function replaceTextContent(state, content) {
|
|
|
1078
1243
|
}
|
|
1079
1244
|
state.blocks = blocks;
|
|
1080
1245
|
}
|
|
1246
|
+
function applyResponseBlockOrder(state, value) {
|
|
1247
|
+
if (!Array.isArray(value)) return false;
|
|
1248
|
+
const ordered = [];
|
|
1249
|
+
for (const item of value) {
|
|
1250
|
+
if (typeof item !== "object" || item === null || Array.isArray(item)) continue;
|
|
1251
|
+
const block = item;
|
|
1252
|
+
if (block.type === "text" || block.type === "thinking") {
|
|
1253
|
+
const content = String(block[block.type] ?? "");
|
|
1254
|
+
if (content) ordered.push({ type: block.type, content });
|
|
1255
|
+
continue;
|
|
1256
|
+
}
|
|
1257
|
+
if (block.type !== "tool_call") continue;
|
|
1258
|
+
const toolCallId = String(block.id ?? "");
|
|
1259
|
+
const toolBlock = state.blocks.find(
|
|
1260
|
+
(candidate) => candidate.type === "tool_use" && candidate.tool_call_id === toolCallId
|
|
1261
|
+
);
|
|
1262
|
+
if (toolBlock) ordered.push(toolBlock);
|
|
1263
|
+
}
|
|
1264
|
+
if (ordered.length === 0 && value.length > 0) return false;
|
|
1265
|
+
const unrelatedBlocks = state.blocks.filter(
|
|
1266
|
+
(block) => block.type !== "text" && block.type !== "thinking" && block.type !== "tool_use"
|
|
1267
|
+
);
|
|
1268
|
+
state.blocks = [...ordered, ...unrelatedBlocks];
|
|
1269
|
+
return true;
|
|
1270
|
+
}
|
|
1081
1271
|
var ClientProjectionBuilder = class {
|
|
1082
1272
|
activeTurns = /* @__PURE__ */ new Map();
|
|
1083
1273
|
lastTurnIds = /* @__PURE__ */ new Map();
|
|
@@ -1117,6 +1307,8 @@ var ClientProjectionBuilder = class {
|
|
|
1117
1307
|
return this.onMemoryInject(loopId, payload);
|
|
1118
1308
|
case "memory:inject:none":
|
|
1119
1309
|
return null;
|
|
1310
|
+
case "context:entry":
|
|
1311
|
+
return this.onContextEntry(payload);
|
|
1120
1312
|
case "loop:turn":
|
|
1121
1313
|
return this.onLoopTurn(loopId, payload);
|
|
1122
1314
|
case "mode:change":
|
|
@@ -1165,6 +1357,8 @@ var ClientProjectionBuilder = class {
|
|
|
1165
1357
|
return this.syncPatch(state);
|
|
1166
1358
|
case "llm:tool_call:created":
|
|
1167
1359
|
return this.onToolCallCreated(state, payload);
|
|
1360
|
+
case "llm:tool_call:arguments:delta":
|
|
1361
|
+
return this.onToolCallArgumentsDelta(state, payload);
|
|
1168
1362
|
case "llm:response:done":
|
|
1169
1363
|
return this.onResponseDone(state, loopId, payload);
|
|
1170
1364
|
case "tool:result:delta":
|
|
@@ -1232,6 +1426,11 @@ var ClientProjectionBuilder = class {
|
|
|
1232
1426
|
}
|
|
1233
1427
|
return null;
|
|
1234
1428
|
}
|
|
1429
|
+
onContextEntry(payload) {
|
|
1430
|
+
if (!isRecord3(payload.entry)) return null;
|
|
1431
|
+
const turn = buildContextTurn(payload.entry, this.nextSeq());
|
|
1432
|
+
return turn ? [{ kind: "upsert", turn }] : null;
|
|
1433
|
+
}
|
|
1235
1434
|
onLoopTurn(loopId, payload) {
|
|
1236
1435
|
let turnId = String(payload.turn_id ?? this.nextTurnId(`turn:${loopId}`));
|
|
1237
1436
|
let memoryRefs = null;
|
|
@@ -1288,11 +1487,11 @@ var ClientProjectionBuilder = class {
|
|
|
1288
1487
|
loopId,
|
|
1289
1488
|
toolCallId: optStr(payload.tool_call_id) ?? void 0,
|
|
1290
1489
|
toolName: toolNameFromPayload(payload) ?? void 0,
|
|
1291
|
-
workspacePath:
|
|
1490
|
+
workspacePath: isRecord3(payload.workspace_change) ? optStr(payload.workspace_change.to) ?? void 0 : void 0
|
|
1292
1491
|
}
|
|
1293
1492
|
];
|
|
1294
1493
|
const change = payload.workspace_change;
|
|
1295
|
-
if (
|
|
1494
|
+
if (isRecord3(change)) {
|
|
1296
1495
|
const source = optStr(change.source) ?? "";
|
|
1297
1496
|
const projectName = optStr(change.project_name) ?? "";
|
|
1298
1497
|
const fromPath = optStr(change.from) ?? "";
|
|
@@ -1430,13 +1629,7 @@ var ClientProjectionBuilder = class {
|
|
|
1430
1629
|
}
|
|
1431
1630
|
onToolCallCreated(state, payload) {
|
|
1432
1631
|
if ("arguments_delta" in payload) {
|
|
1433
|
-
|
|
1434
|
-
const toolCallId2 = String(payload.id ?? "");
|
|
1435
|
-
if (delta && toolCallId2) {
|
|
1436
|
-
appendToolCallArguments(state, toolCallId2, delta);
|
|
1437
|
-
return this.syncPatch(state);
|
|
1438
|
-
}
|
|
1439
|
-
return null;
|
|
1632
|
+
return this.onToolCallArgumentsDelta(state, payload);
|
|
1440
1633
|
}
|
|
1441
1634
|
const toolCallId = String(payload.id ?? "");
|
|
1442
1635
|
const toolName = toolNameFromPayload(payload);
|
|
@@ -1445,6 +1638,13 @@ var ClientProjectionBuilder = class {
|
|
|
1445
1638
|
upsertToolCall(state, toolCallId, toolName, displayName, args);
|
|
1446
1639
|
return this.syncPatch(state);
|
|
1447
1640
|
}
|
|
1641
|
+
onToolCallArgumentsDelta(state, payload) {
|
|
1642
|
+
const delta = String(payload.arguments_delta ?? "");
|
|
1643
|
+
const toolCallId = String(payload.id ?? "");
|
|
1644
|
+
if (!delta || !toolCallId) return null;
|
|
1645
|
+
appendToolCallArguments(state, toolCallId, delta);
|
|
1646
|
+
return this.syncPatch(state);
|
|
1647
|
+
}
|
|
1448
1648
|
onResponseDone(state, loopId, payload) {
|
|
1449
1649
|
state.responseDone = true;
|
|
1450
1650
|
const model = optStr(payload.model);
|
|
@@ -1452,9 +1652,6 @@ var ClientProjectionBuilder = class {
|
|
|
1452
1652
|
if (typeof payload.usage === "object" && payload.usage !== null) {
|
|
1453
1653
|
state.usage = { ...payload.usage };
|
|
1454
1654
|
}
|
|
1455
|
-
if ("content" in payload) {
|
|
1456
|
-
replaceTextContent(state, completedResponseText(payload.content));
|
|
1457
|
-
}
|
|
1458
1655
|
const toolCalls = payload.tool_calls;
|
|
1459
1656
|
if (Array.isArray(toolCalls)) {
|
|
1460
1657
|
for (const raw of toolCalls) {
|
|
@@ -1472,7 +1669,13 @@ var ClientProjectionBuilder = class {
|
|
|
1472
1669
|
);
|
|
1473
1670
|
}
|
|
1474
1671
|
}
|
|
1475
|
-
if (state.
|
|
1672
|
+
if (!applyResponseBlockOrder(state, payload.response_blocks) && "content" in payload) {
|
|
1673
|
+
replaceTextContent(state, completedResponseText(payload.content));
|
|
1674
|
+
}
|
|
1675
|
+
const isNativeProviderResponse = typeof payload.provider === "string" && payload.provider !== "";
|
|
1676
|
+
if (state.toolCalls.length === 0 || isNativeProviderResponse && state.toolCalls.every(
|
|
1677
|
+
(toolCall) => toolCall.status !== "pending" && toolCall.status !== "awaiting_answer"
|
|
1678
|
+
)) {
|
|
1476
1679
|
return this.finalize(loopId, "completed");
|
|
1477
1680
|
}
|
|
1478
1681
|
return this.syncPatch(state);
|
|
@@ -1486,7 +1689,8 @@ var ClientProjectionBuilder = class {
|
|
|
1486
1689
|
const durationMs = typeof rawDuration === "number" && rawDuration >= 0 ? Math.round(rawDuration) : null;
|
|
1487
1690
|
const sl = sourceLoopFor(state.loopId, this.loopDescriptions);
|
|
1488
1691
|
applyToolResult(state, toolCallId, content, status, durationMs, sl);
|
|
1489
|
-
|
|
1692
|
+
const isNativeProviderResult = typeof payload.provider === "string" && payload.provider !== "";
|
|
1693
|
+
if (!isNativeProviderResult && this.shouldFinalizeAfterToolResults(state)) {
|
|
1490
1694
|
return this.finalize(loopId, "completed");
|
|
1491
1695
|
}
|
|
1492
1696
|
return this.syncPatch(state);
|
|
@@ -1527,6 +1731,11 @@ var ClientProjectionBuilder = class {
|
|
|
1527
1731
|
const toolName = String(payload.tool_name ?? "");
|
|
1528
1732
|
const displayName = this.displayNameResolver(toolName);
|
|
1529
1733
|
upsertToolCall(state, toolCallId, toolName, displayName, "");
|
|
1734
|
+
const relatedToolCallId = String(payload.related_tool_call_id ?? "");
|
|
1735
|
+
if (relatedToolCallId && relatedToolCallId !== toolCallId) {
|
|
1736
|
+
const related = state.toolCalls.find((toolCall) => toolCall.id === relatedToolCallId);
|
|
1737
|
+
if (related) related.status = "awaiting_answer";
|
|
1738
|
+
}
|
|
1530
1739
|
return this.syncPatch(state);
|
|
1531
1740
|
}
|
|
1532
1741
|
onChildPause(state, loopId, payload) {
|
|
@@ -1754,7 +1963,7 @@ var ClientProjectionBuilder = class {
|
|
|
1754
1963
|
return `${prefix}:${this.syntheticCounter}:${hex}`;
|
|
1755
1964
|
}
|
|
1756
1965
|
};
|
|
1757
|
-
function
|
|
1966
|
+
function isRecord3(value) {
|
|
1758
1967
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1759
1968
|
}
|
|
1760
1969
|
function optStr(value) {
|
|
@@ -1783,6 +1992,7 @@ function projectHistory(entries) {
|
|
|
1783
1992
|
const latestAssistantByLoop = /* @__PURE__ */ new Map();
|
|
1784
1993
|
const pendingChildPauses = /* @__PURE__ */ new Map();
|
|
1785
1994
|
const answeredToolCallIds = /* @__PURE__ */ new Set();
|
|
1995
|
+
const nativeToolTurns = /* @__PURE__ */ new Set();
|
|
1786
1996
|
let latestRootMessage = null;
|
|
1787
1997
|
let latestRootAssistant = null;
|
|
1788
1998
|
let sequence = 0;
|
|
@@ -1790,7 +2000,7 @@ function projectHistory(entries) {
|
|
|
1790
2000
|
const kind = String(entry.kind ?? "");
|
|
1791
2001
|
const loopId = String(entry.loop_name ?? "root");
|
|
1792
2002
|
const id = String(entry.id ?? `history-${sequence + 1}`);
|
|
1793
|
-
if (kind === "post_chat_followup" &&
|
|
2003
|
+
if (kind === "post_chat_followup" && isRecord4(entry.data)) {
|
|
1794
2004
|
const assistantEntryId = String(entry.data.assistant_entry_id ?? "");
|
|
1795
2005
|
if (latestRootMessage?.role === "assistant" && latestRootMessage.turn_id === assistantEntryId) {
|
|
1796
2006
|
const followup = normalizePostChatFollowup(entry.data, assistantEntryId);
|
|
@@ -1798,7 +2008,15 @@ function projectHistory(entries) {
|
|
|
1798
2008
|
}
|
|
1799
2009
|
continue;
|
|
1800
2010
|
}
|
|
1801
|
-
if (kind === "
|
|
2011
|
+
if (kind === "context") {
|
|
2012
|
+
const turn = buildContextTurn(entry, sequence + 1);
|
|
2013
|
+
if (turn) {
|
|
2014
|
+
sequence += 1;
|
|
2015
|
+
turns.push(turn);
|
|
2016
|
+
}
|
|
2017
|
+
continue;
|
|
2018
|
+
}
|
|
2019
|
+
if (kind === "message" && isRecord4(entry.message)) {
|
|
1802
2020
|
const message = entry.message;
|
|
1803
2021
|
const role = String(message.role ?? "");
|
|
1804
2022
|
if (role === "tool") {
|
|
@@ -1830,14 +2048,35 @@ function projectHistory(entries) {
|
|
|
1830
2048
|
blocks,
|
|
1831
2049
|
tool_calls: toolCalls,
|
|
1832
2050
|
model: stringOrNull(message.model),
|
|
1833
|
-
usage:
|
|
2051
|
+
usage: isRecord4(message._usage) ? { ...message._usage } : null,
|
|
1834
2052
|
duration_ms: numberOrZero(message._duration_ms),
|
|
1835
|
-
started_at: typeof entry.timestamp === "string" ? entry.timestamp : void 0
|
|
2053
|
+
started_at: typeof entry.timestamp === "string" ? entry.timestamp : void 0,
|
|
2054
|
+
// fork 子会话的父锚点:历史里落在消息级 _parent_fork_tool_call_id。丢掉它,
|
|
2055
|
+
// 刷新后工作流步骤 fork 就配不回 workflow:run 工具调用(普通 Agent fork 有
|
|
2056
|
+
// root Agent 工具调用兜底配对,工作流 fork 没有,子智能体卡会整体消失)。
|
|
2057
|
+
parent_fork_tool_call_id: stringOrNull(message._parent_fork_tool_call_id)
|
|
1836
2058
|
};
|
|
2059
|
+
const isNativeToolEntry = role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.some(
|
|
2060
|
+
(toolCall) => isRecord4(toolCall) && typeof toolCall.provider === "string"
|
|
2061
|
+
);
|
|
2062
|
+
const previousTurn = turns[turns.length - 1];
|
|
2063
|
+
if (role === "assistant" && previousTurn?.role === "assistant" && previousTurn.loop_id === loopId && nativeToolTurns.has(previousTurn)) {
|
|
2064
|
+
previousTurn.blocks.push(...turn.blocks);
|
|
2065
|
+
previousTurn.tool_calls.push(...turn.tool_calls);
|
|
2066
|
+
previousTurn.status = turn.status;
|
|
2067
|
+
previousTurn.model = turn.model ?? previousTurn.model;
|
|
2068
|
+
previousTurn.usage = turn.usage ?? previousTurn.usage;
|
|
2069
|
+
previousTurn.duration_ms = turn.duration_ms || previousTurn.duration_ms;
|
|
2070
|
+
if (turn.started_at) previousTurn.started_at = turn.started_at;
|
|
2071
|
+
if (loopId === "root") latestRootMessage = previousTurn;
|
|
2072
|
+
latestAssistantByLoop.set(loopId, previousTurn);
|
|
2073
|
+
continue;
|
|
2074
|
+
}
|
|
1837
2075
|
if (loopId === "root" && (role === "user" || role === "assistant")) {
|
|
1838
2076
|
latestRootMessage = turn;
|
|
1839
2077
|
}
|
|
1840
2078
|
turns.push(turn);
|
|
2079
|
+
if (isNativeToolEntry) nativeToolTurns.add(turn);
|
|
1841
2080
|
if (role === "assistant") latestAssistantByLoop.set(loopId, turn);
|
|
1842
2081
|
if (loopId === "root") {
|
|
1843
2082
|
if (role === "assistant") latestRootAssistant = turn;
|
|
@@ -1846,10 +2085,25 @@ function projectHistory(entries) {
|
|
|
1846
2085
|
for (const toolCall of toolCalls) toolOwners.set(toolCall.id, turn);
|
|
1847
2086
|
continue;
|
|
1848
2087
|
}
|
|
2088
|
+
if (kind === "tool_result") {
|
|
2089
|
+
const data = entry.data ?? {};
|
|
2090
|
+
const toolCallId = String(data.tool_call_id ?? "");
|
|
2091
|
+
const owner = toolOwners.get(toolCallId) ?? findLatestAssistant(turns, loopId);
|
|
2092
|
+
if (owner && toolCallId) {
|
|
2093
|
+
const rawContent = data.content;
|
|
2094
|
+
const content = typeof rawContent === "string" ? rawContent : Array.isArray(rawContent) ? rawContent : String(rawContent ?? "");
|
|
2095
|
+
const status = data.is_error === true ? "error" : inferToolStatus(typeof content === "string" ? content : JSON.stringify(content));
|
|
2096
|
+
setToolCallStatus(owner, toolCallId, status, content, null);
|
|
2097
|
+
if (!owner.blocks.some((block) => block.type === "tool_result" && block.tool_call_id === toolCallId)) {
|
|
2098
|
+
owner.blocks.push({ type: "tool_result", content, tool_call_id: toolCallId });
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
continue;
|
|
2102
|
+
}
|
|
1849
2103
|
if (kind === "mode_change") {
|
|
1850
2104
|
turns.push(markerTurn(id, ++sequence, loopId, "mode_change", entry.data ?? {}));
|
|
1851
2105
|
} else if (kind === "workspace_change") {
|
|
1852
|
-
const change =
|
|
2106
|
+
const change = isRecord4(entry.data) ? entry.data : {};
|
|
1853
2107
|
const source = stringOrNull(change.source) ?? "";
|
|
1854
2108
|
const projectName = stringOrNull(change.project_name) ?? "";
|
|
1855
2109
|
const fromPath = stringOrNull(change.from) ?? "";
|
|
@@ -1890,7 +2144,7 @@ function projectHistory(entries) {
|
|
|
1890
2144
|
} else if (kind === "compaction" || kind === "tool_result_archive") {
|
|
1891
2145
|
const data = entry.data ?? {};
|
|
1892
2146
|
const compactionId = String(data.compaction_id ?? entry.id ?? `history-${sequence + 1}`);
|
|
1893
|
-
let content =
|
|
2147
|
+
let content = isRecord4(entry.message) ? entry.message.content : void 0;
|
|
1894
2148
|
if (kind === "tool_result_archive") {
|
|
1895
2149
|
const archivedCount = Array.isArray(data.archived_files) ? data.archived_files.length : 0;
|
|
1896
2150
|
content = `<compaction-summary>
|
|
@@ -1905,11 +2159,11 @@ function projectHistory(entries) {
|
|
|
1905
2159
|
);
|
|
1906
2160
|
} else if (kind === "child_pause") {
|
|
1907
2161
|
const data = entry.data ?? {};
|
|
1908
|
-
const sourceLoop =
|
|
2162
|
+
const sourceLoop = isRecord4(data.source_loop) ? data.source_loop : {};
|
|
1909
2163
|
const childLoopId = String(data.child_loop_name ?? sourceLoop.name ?? "");
|
|
1910
2164
|
const childToolCallId = String(data.child_pause_tool_call_id ?? "");
|
|
1911
2165
|
const parentToolCallId = String(data.parent_fork_tool_call_id ?? "");
|
|
1912
|
-
const pauseToolData =
|
|
2166
|
+
const pauseToolData = isRecord4(data.pause_tool_data) ? data.pause_tool_data : {};
|
|
1913
2167
|
const description = String(sourceLoop.description ?? "");
|
|
1914
2168
|
applyAskUserPauseToTurn(
|
|
1915
2169
|
latestAssistantByLoop.get(childLoopId),
|
|
@@ -1997,15 +2251,15 @@ function parseToolArguments(value) {
|
|
|
1997
2251
|
if (!value) return {};
|
|
1998
2252
|
try {
|
|
1999
2253
|
const parsed = JSON.parse(value);
|
|
2000
|
-
return
|
|
2254
|
+
return isRecord4(parsed) ? parsed : {};
|
|
2001
2255
|
} catch {
|
|
2002
2256
|
return {};
|
|
2003
2257
|
}
|
|
2004
2258
|
}
|
|
2005
2259
|
function buildToolCalls(value) {
|
|
2006
2260
|
if (!Array.isArray(value)) return [];
|
|
2007
|
-
return value.filter(
|
|
2008
|
-
const fn =
|
|
2261
|
+
return value.filter(isRecord4).map((raw) => {
|
|
2262
|
+
const fn = isRecord4(raw.function) ? raw.function : {};
|
|
2009
2263
|
const name = String(fn.name ?? "");
|
|
2010
2264
|
return {
|
|
2011
2265
|
id: String(raw.id ?? ""),
|
|
@@ -2021,23 +2275,40 @@ function buildBlocks(message, toolCalls) {
|
|
|
2021
2275
|
const displayContent = message.role === "user" ? message._display_content : void 0;
|
|
2022
2276
|
const stored = message._blocks;
|
|
2023
2277
|
const blocks = [];
|
|
2278
|
+
const storedToolCallIds = /* @__PURE__ */ new Set();
|
|
2024
2279
|
if (displayContent !== void 0) {
|
|
2025
2280
|
blocks.push({ type: "text", content: displayContent });
|
|
2026
2281
|
} else if (Array.isArray(stored)) {
|
|
2027
2282
|
for (const raw of stored) {
|
|
2028
|
-
if (!
|
|
2283
|
+
if (!isRecord4(raw)) continue;
|
|
2029
2284
|
const type3 = String(raw.type ?? "");
|
|
2030
2285
|
if (type3 === "thinking") blocks.push({ type: type3, content: raw.thinking ?? raw.content ?? "" });
|
|
2031
2286
|
if (type3 === "text") blocks.push({ type: type3, content: raw.text ?? raw.content ?? "" });
|
|
2287
|
+
if (type3 === "tool_call") {
|
|
2288
|
+
const toolCallId = String(raw.id ?? "");
|
|
2289
|
+
const toolCall = toolCalls.find((candidate) => candidate.id === toolCallId);
|
|
2290
|
+
if (!toolCall) continue;
|
|
2291
|
+
storedToolCallIds.add(toolCallId);
|
|
2292
|
+
blocks.push({
|
|
2293
|
+
type: "tool_use",
|
|
2294
|
+
content: toolCall.arguments,
|
|
2295
|
+
tool_call_id: toolCall.id,
|
|
2296
|
+
tool_name: toolCall.tool_name,
|
|
2297
|
+
display_name: toolCall.display_name
|
|
2298
|
+
});
|
|
2299
|
+
}
|
|
2032
2300
|
}
|
|
2033
2301
|
} else if (message.content !== void 0 && message.content !== "") {
|
|
2034
2302
|
blocks.push({ type: "text", content: message.content });
|
|
2035
2303
|
}
|
|
2036
2304
|
for (const toolCall of toolCalls) {
|
|
2305
|
+
if (storedToolCallIds.has(toolCall.id)) continue;
|
|
2037
2306
|
blocks.push({
|
|
2038
2307
|
type: "tool_use",
|
|
2039
|
-
content:
|
|
2040
|
-
tool_call_id: toolCall.id
|
|
2308
|
+
content: toolCall.arguments,
|
|
2309
|
+
tool_call_id: toolCall.id,
|
|
2310
|
+
tool_name: toolCall.tool_name,
|
|
2311
|
+
display_name: toolCall.display_name
|
|
2041
2312
|
});
|
|
2042
2313
|
}
|
|
2043
2314
|
return blocks;
|
|
@@ -2058,7 +2329,7 @@ function markerTurn(id, sequence, loopId, type3, content, toolCallId = null) {
|
|
|
2058
2329
|
duration_ms: 0
|
|
2059
2330
|
};
|
|
2060
2331
|
}
|
|
2061
|
-
function
|
|
2332
|
+
function isRecord4(value) {
|
|
2062
2333
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2063
2334
|
}
|
|
2064
2335
|
function stringOrNull(value) {
|
|
@@ -2094,7 +2365,8 @@ function toCreateSessionPayload(request) {
|
|
|
2094
2365
|
solution_snapshot_session_id: request.solution_snapshot_session_id ?? null,
|
|
2095
2366
|
compaction_ratio: request.compaction_ratio ?? null,
|
|
2096
2367
|
...request.runtime_type ? { runtime_type: request.runtime_type } : {},
|
|
2097
|
-
...request.daemon_id ? { daemon_id: request.daemon_id } : {}
|
|
2368
|
+
...request.daemon_id ? { daemon_id: request.daemon_id } : {},
|
|
2369
|
+
...request.agent_runtime_id ? { agent_runtime_id: request.agent_runtime_id } : {}
|
|
2098
2370
|
};
|
|
2099
2371
|
}
|
|
2100
2372
|
var builtinSolutionIds = /* @__PURE__ */ new Set([
|
|
@@ -2708,6 +2980,36 @@ var LEGACY_FORK_CONTEXT_MARKERS = [
|
|
|
2708
2980
|
"\u53EF\u901A\u8FC7 `read_file` \u8BFB\u53D6\u3002",
|
|
2709
2981
|
"# \u4F60\u7684\u4EFB\u52A1"
|
|
2710
2982
|
];
|
|
2983
|
+
var INTERNAL_CHAT_ERROR_PATTERNS = [
|
|
2984
|
+
/\b(?:ENOENT|EACCES|EPERM)\b/i,
|
|
2985
|
+
/\[(?:Errno -?\d+|WinError \d+)\]/i,
|
|
2986
|
+
/\b(?:ECONNRESET|ECONNREFUSED|ETIMEDOUT|EHOSTUNREACH|ENETUNREACH|ENOTFOUND|EAI_AGAIN)\b/i,
|
|
2987
|
+
/\b(?:Connection refused|Connection reset by peer|All connection attempts failed|getaddrinfo failed)\b/i,
|
|
2988
|
+
/(?:^|[\s("'`])\/(?:root|home|Users|tmp|var|opt|etc)\//,
|
|
2989
|
+
/[A-Za-z]:\\(?:Users|Windows|Program Files|AppData)\\/i,
|
|
2990
|
+
/(?:^|\n)Traceback \(most recent call last\):/,
|
|
2991
|
+
/(?:^|\n)\s*at\s+\S+(?:\s+\(|:\d+:\d+)/,
|
|
2992
|
+
/(?:^|\n)File "[^"]+", line \d+/
|
|
2993
|
+
];
|
|
2994
|
+
var SAFE_CHAT_ERROR_PREFIXES = [
|
|
2995
|
+
"\u6C99\u76D2\u8FD0\u884C\u73AF\u5883\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
|
|
2996
|
+
"\u6A21\u578B\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u68C0\u67E5\u6A21\u578B\u914D\u7F6E\u540E\u91CD\u8BD5\u3002"
|
|
2997
|
+
];
|
|
2998
|
+
var GENERIC_CHAT_ERROR = "\u6D88\u606F\u53D1\u9001\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002";
|
|
2999
|
+
function chatErrorForDisplay(message, developer = false) {
|
|
3000
|
+
const normalized = message.trim();
|
|
3001
|
+
if (!developer) {
|
|
3002
|
+
const safePrefix = SAFE_CHAT_ERROR_PREFIXES.find((prefix) => normalized.startsWith(prefix));
|
|
3003
|
+
if (safePrefix) return safePrefix;
|
|
3004
|
+
}
|
|
3005
|
+
if (!developer && /no source matches this model/i.test(normalized)) {
|
|
3006
|
+
return "\u5F53\u524D\u6A21\u578B\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u6216\u91CD\u65B0\u9009\u62E9\u6A21\u578B\u3002";
|
|
3007
|
+
}
|
|
3008
|
+
if (!developer && INTERNAL_CHAT_ERROR_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3009
|
+
return GENERIC_CHAT_ERROR;
|
|
3010
|
+
}
|
|
3011
|
+
return normalized || GENERIC_CHAT_ERROR;
|
|
3012
|
+
}
|
|
2711
3013
|
function normalizeMessageContent(content) {
|
|
2712
3014
|
if (typeof content === "string") return content;
|
|
2713
3015
|
if (Array.isArray(content)) return content;
|
|
@@ -2727,10 +3029,10 @@ function isLegacyForkContextText(text) {
|
|
|
2727
3029
|
const normalized = text.trim();
|
|
2728
3030
|
return LEGACY_FORK_CONTEXT_MARKERS.every((marker) => normalized.includes(marker));
|
|
2729
3031
|
}
|
|
2730
|
-
function isHiddenInternalMessage(message) {
|
|
3032
|
+
function isHiddenInternalMessage(message, options = {}) {
|
|
2731
3033
|
if (message.role !== "user") return false;
|
|
2732
3034
|
const text = getTextContent(normalizeMessageContent(message.content));
|
|
2733
|
-
return isSystemReminderText(text) || isSystemNotificationText(text) || isLegacyForkContextText(text);
|
|
3035
|
+
return isSystemReminderText(text) || isSystemNotificationText(text) || (options.includeLegacyForkContext ?? true) && isLegacyForkContextText(text);
|
|
2734
3036
|
}
|
|
2735
3037
|
function buildMessageContent(text, attachments) {
|
|
2736
3038
|
if (attachments.length === 0) return text;
|
|
@@ -2903,10 +3205,10 @@ function inferLoopStatusFromMessages(messages) {
|
|
|
2903
3205
|
}
|
|
2904
3206
|
function inferLoopStatusFromTurns(turns, messages) {
|
|
2905
3207
|
const latestAgentNotification = [...turns].reverse().flatMap((turn) => turn.blocks).find((block) => {
|
|
2906
|
-
if (block.type !== "system_notification" || !
|
|
3208
|
+
if (block.type !== "system_notification" || !isRecord5(block.content)) return false;
|
|
2907
3209
|
return block.content.notification_type === "agent:start" || block.content.notification_type === "agent:end";
|
|
2908
3210
|
});
|
|
2909
|
-
if (latestAgentNotification?.type === "system_notification" &&
|
|
3211
|
+
if (latestAgentNotification?.type === "system_notification" && isRecord5(latestAgentNotification.content)) {
|
|
2910
3212
|
const notificationType = latestAgentNotification.content.notification_type;
|
|
2911
3213
|
const status = latestAgentNotification.content.status;
|
|
2912
3214
|
if (notificationType === "agent:start" || status === "running") return "running";
|
|
@@ -2915,17 +3217,47 @@ function inferLoopStatusFromTurns(turns, messages) {
|
|
|
2915
3217
|
}
|
|
2916
3218
|
return inferLoopStatusFromMessages(messages);
|
|
2917
3219
|
}
|
|
2918
|
-
function
|
|
3220
|
+
function isRecord5(value) {
|
|
2919
3221
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2920
3222
|
}
|
|
3223
|
+
function toSelectionMap(value) {
|
|
3224
|
+
if (!isRecord5(value)) return {};
|
|
3225
|
+
const entries = Object.entries(value).map(([questionKey, optionIndexes]) => {
|
|
3226
|
+
if (!Array.isArray(optionIndexes)) return null;
|
|
3227
|
+
const parsedIndexes = optionIndexes.map((item) => typeof item === "number" ? item : Number(item)).filter((item) => Number.isInteger(item));
|
|
3228
|
+
return [Number(questionKey), parsedIndexes];
|
|
3229
|
+
}).filter((entry) => entry !== null);
|
|
3230
|
+
return Object.fromEntries(entries);
|
|
3231
|
+
}
|
|
3232
|
+
function toCustomMap(value) {
|
|
3233
|
+
if (!isRecord5(value)) return {};
|
|
3234
|
+
const entries = Object.entries(value).filter(([, text]) => typeof text === "string").map(([questionKey, text]) => [Number(questionKey), text]);
|
|
3235
|
+
return Object.fromEntries(entries);
|
|
3236
|
+
}
|
|
3237
|
+
function extractAskAnswers(turns) {
|
|
3238
|
+
const answers = {};
|
|
3239
|
+
for (const turn of turns) {
|
|
3240
|
+
for (const block of turn.blocks) {
|
|
3241
|
+
if (block.type !== "ask_user_answer" || typeof block.tool_call_id !== "string") continue;
|
|
3242
|
+
if (!isRecord5(block.content)) continue;
|
|
3243
|
+
const note = typeof block.content.note === "string" ? block.content.note.trim() : "";
|
|
3244
|
+
answers[block.tool_call_id] = {
|
|
3245
|
+
selections: toSelectionMap(block.content.selections),
|
|
3246
|
+
custom: toCustomMap(block.content.custom),
|
|
3247
|
+
...note ? { note } : {}
|
|
3248
|
+
};
|
|
3249
|
+
}
|
|
3250
|
+
}
|
|
3251
|
+
return answers;
|
|
3252
|
+
}
|
|
2921
3253
|
function parentForkToolCallIdFromTurn(turn) {
|
|
2922
3254
|
if (typeof turn.parent_fork_tool_call_id === "string" && turn.parent_fork_tool_call_id.length > 0) {
|
|
2923
3255
|
return turn.parent_fork_tool_call_id;
|
|
2924
3256
|
}
|
|
2925
3257
|
for (const block of turn.blocks) {
|
|
2926
|
-
if (block.type !== "system_notification" || !
|
|
3258
|
+
if (block.type !== "system_notification" || !isRecord5(block.content)) continue;
|
|
2927
3259
|
const metadata = block.content.metadata;
|
|
2928
|
-
if (!
|
|
3260
|
+
if (!isRecord5(metadata)) continue;
|
|
2929
3261
|
const parentId = metadata.parent_fork_tool_call_id;
|
|
2930
3262
|
if (typeof parentId === "string" && parentId.length > 0) return parentId;
|
|
2931
3263
|
}
|
|
@@ -2942,16 +3274,16 @@ function buildMessageContent2(turn) {
|
|
|
2942
3274
|
}
|
|
2943
3275
|
function workspaceNotificationContent(turn) {
|
|
2944
3276
|
const block = turn.blocks.find(
|
|
2945
|
-
(candidate) => candidate.type === "system_notification" &&
|
|
3277
|
+
(candidate) => candidate.type === "system_notification" && isRecord5(candidate.content) && candidate.content.notification_type === "workspace_change"
|
|
2946
3278
|
);
|
|
2947
|
-
if (!block || !
|
|
3279
|
+
if (!block || !isRecord5(block.content)) return "";
|
|
2948
3280
|
const title = typeof block.content.title === "string" ? block.content.title.trim() : "";
|
|
2949
3281
|
const detail = typeof block.content.detail === "string" ? block.content.detail.trim() : "";
|
|
2950
3282
|
return [title ? `**${title}**` : "", detail].filter(Boolean).join("\n\n");
|
|
2951
3283
|
}
|
|
2952
3284
|
function askUserAnswerContent(turn) {
|
|
2953
3285
|
const answerBlock = turn.blocks.find((block) => block.type === "ask_user_answer");
|
|
2954
|
-
if (!answerBlock || !
|
|
3286
|
+
if (!answerBlock || !isRecord5(answerBlock.content)) return null;
|
|
2955
3287
|
const answer = answerBlock.content.answer;
|
|
2956
3288
|
return typeof answer === "string" && answer.trim().length > 0 ? answer : null;
|
|
2957
3289
|
}
|
|
@@ -2971,6 +3303,19 @@ function extractModeFromBlocks(blocks) {
|
|
|
2971
3303
|
return null;
|
|
2972
3304
|
}
|
|
2973
3305
|
function projectionToMessage(turn) {
|
|
3306
|
+
if (turn.kind === "context") {
|
|
3307
|
+
const context = contextProjectionData(turn);
|
|
3308
|
+
if (!context) return null;
|
|
3309
|
+
return {
|
|
3310
|
+
role: "assistant",
|
|
3311
|
+
content: "",
|
|
3312
|
+
kind: "context",
|
|
3313
|
+
context,
|
|
3314
|
+
loop_name: turn.loop_id,
|
|
3315
|
+
entry_id: turn.turn_id,
|
|
3316
|
+
status: turn.status
|
|
3317
|
+
};
|
|
3318
|
+
}
|
|
2974
3319
|
if (turn.kind === "compaction" && turn.compaction_id) {
|
|
2975
3320
|
return {
|
|
2976
3321
|
role: "assistant",
|
|
@@ -3140,6 +3485,25 @@ function orderTurns(turns) {
|
|
|
3140
3485
|
}
|
|
3141
3486
|
return turns;
|
|
3142
3487
|
}
|
|
3488
|
+
function reconcileOptimisticUserTurns(currentTurns, incomingTurns) {
|
|
3489
|
+
const currentById = new Map(currentTurns.map((turn) => [turn.turn_id, turn]));
|
|
3490
|
+
const optimisticUserTurn = currentTurns.find(
|
|
3491
|
+
(turn) => turn.role === "user" && (turn.kind ?? "message") === "message" && turn.turn_id.startsWith("local-user-")
|
|
3492
|
+
);
|
|
3493
|
+
let optimisticUserMatched = false;
|
|
3494
|
+
return incomingTurns.map((turn) => {
|
|
3495
|
+
const current = currentById.get(turn.turn_id);
|
|
3496
|
+
if (current) {
|
|
3497
|
+
const currentRenderId = current.client_render_id;
|
|
3498
|
+
return turn.client_render_id || !currentRenderId ? turn : { ...turn, client_render_id: currentRenderId };
|
|
3499
|
+
}
|
|
3500
|
+
if (!optimisticUserMatched && optimisticUserTurn && turn.role === "user" && (turn.kind ?? "message") === "message" && !turn.turn_id.startsWith("local-user-")) {
|
|
3501
|
+
optimisticUserMatched = true;
|
|
3502
|
+
return { ...turn, client_render_id: optimisticUserTurn.turn_id };
|
|
3503
|
+
}
|
|
3504
|
+
return turn;
|
|
3505
|
+
});
|
|
3506
|
+
}
|
|
3143
3507
|
function areAgentLoopsEqual(left, right) {
|
|
3144
3508
|
if (!left) return Object.keys(right).length === 0;
|
|
3145
3509
|
const leftEntries = Object.entries(left);
|
|
@@ -3151,7 +3515,7 @@ function areAgentLoopsEqual(left, right) {
|
|
|
3151
3515
|
});
|
|
3152
3516
|
}
|
|
3153
3517
|
function withTurns(state, turns) {
|
|
3154
|
-
const orderedTurns = orderTurns(turns);
|
|
3518
|
+
const orderedTurns = orderTurns(reconcileOptimisticUserTurns(state.turns, turns));
|
|
3155
3519
|
const { messages, agentLoops, activeCompaction } = materialize(orderedTurns);
|
|
3156
3520
|
const isWaitingForInput = orderedTurns.some(
|
|
3157
3521
|
(turn) => turn.tool_calls.some((toolCall) => toolCall.status === "awaiting_answer")
|
|
@@ -3161,14 +3525,31 @@ function withTurns(state, turns) {
|
|
|
3161
3525
|
const preservedErrors = lastTurnId ? state.messages.filter(
|
|
3162
3526
|
(m) => m.role === "error" && typeof m.entry_id === "string" && m.entry_id.startsWith(`${ERROR_ANCHOR_PREFIX}${lastTurnId}:`)
|
|
3163
3527
|
) : [];
|
|
3164
|
-
const
|
|
3528
|
+
const turnById = new Map(orderedTurns.map((turn) => [turn.turn_id, turn]));
|
|
3529
|
+
const messagesWithRenderIds = messages.map((message) => {
|
|
3530
|
+
if (!message.entry_id) return message;
|
|
3531
|
+
const renderId = turnById.get(message.entry_id)?.client_render_id;
|
|
3532
|
+
return renderId ? { ...message, render_id: renderId } : message;
|
|
3533
|
+
});
|
|
3534
|
+
const mergedMessages = preservedErrors.length > 0 ? [...messagesWithRenderIds, ...preservedErrors] : messagesWithRenderIds;
|
|
3165
3535
|
const latestMode = [...orderedTurns].reverse().map((turn) => extractModeFromBlocks(turn.blocks)).find((mode) => mode !== null);
|
|
3536
|
+
const askAnswersFromHistory = extractAskAnswers(orderedTurns);
|
|
3537
|
+
const presentToolCallIds = new Set(
|
|
3538
|
+
orderedTurns.flatMap((turn) => turn.tool_calls.map((toolCall) => toolCall.id))
|
|
3539
|
+
);
|
|
3540
|
+
const askAnswers = { ...askAnswersFromHistory };
|
|
3541
|
+
for (const [toolCallId, answer] of Object.entries(state.askAnswers)) {
|
|
3542
|
+
if (!(toolCallId in askAnswers) && presentToolCallIds.has(toolCallId)) {
|
|
3543
|
+
askAnswers[toolCallId] = answer;
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3166
3546
|
return {
|
|
3167
3547
|
...state,
|
|
3168
3548
|
turns: orderedTurns,
|
|
3169
3549
|
messages: mergedMessages,
|
|
3170
3550
|
agentLoops: stableAgentLoops,
|
|
3171
3551
|
activeCompaction,
|
|
3552
|
+
askAnswers,
|
|
3172
3553
|
mode: latestMode ?? state.mode,
|
|
3173
3554
|
// 历史恢复时 chat:end(paused) 可能已不在 socket replay 缓存中;
|
|
3174
3555
|
// awaiting_answer 是投影里的持久权威状态,必须据此恢复问答交互。
|
|
@@ -3196,14 +3577,17 @@ function addUserMessage(state, content) {
|
|
|
3196
3577
|
}
|
|
3197
3578
|
function upsertTurn(state, turn) {
|
|
3198
3579
|
let existing = [...state.turns];
|
|
3199
|
-
|
|
3200
|
-
|
|
3580
|
+
const [nextTurn] = reconcileOptimisticUserTurns(existing, [turn]);
|
|
3581
|
+
if (nextTurn.client_render_id && turn.role === "user") {
|
|
3582
|
+
existing = existing.filter(
|
|
3583
|
+
(item) => !(item.role === "user" && item.turn_id.startsWith("local-user-"))
|
|
3584
|
+
);
|
|
3201
3585
|
}
|
|
3202
|
-
const index = existing.findIndex((item) => item.turn_id ===
|
|
3586
|
+
const index = existing.findIndex((item) => item.turn_id === nextTurn.turn_id);
|
|
3203
3587
|
if (index >= 0) {
|
|
3204
|
-
existing[index] =
|
|
3588
|
+
existing[index] = nextTurn;
|
|
3205
3589
|
} else {
|
|
3206
|
-
existing.push(
|
|
3590
|
+
existing.push(nextTurn);
|
|
3207
3591
|
}
|
|
3208
3592
|
return withTurns(state, existing);
|
|
3209
3593
|
}
|
|
@@ -3255,18 +3639,18 @@ function markStreamingTurns(state, turnStatus, toolCallStatus) {
|
|
|
3255
3639
|
// src/session/agent-session.ts
|
|
3256
3640
|
var OOM_MESSAGE = "\u6C99\u76D2\u5185\u5B58\u4F7F\u7528\u8D85\u51FA\u9650\u5236\uFF0C\u5DF2\u81EA\u52A8\u91CD\u542F\u3002\u5982\u679C\u7ECF\u5E38\u89E6\u53D1\uFF0C\u53EF\u4EE5\u8054\u7CFB\u7BA1\u7406\u5458\u8C03\u6574\u989D\u5EA6\u3002";
|
|
3257
3641
|
var OOM_KEYWORDS_RE = /(?:\b(?:exit_code|ExitedWith|exited with)|退出码)\D*(?<!\d)137(?!\d)/i;
|
|
3258
|
-
function
|
|
3642
|
+
function isRecord6(value) {
|
|
3259
3643
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3260
3644
|
}
|
|
3261
3645
|
function isOomText(value) {
|
|
3262
3646
|
return typeof value === "string" && OOM_KEYWORDS_RE.test(value);
|
|
3263
3647
|
}
|
|
3264
3648
|
function parseJsonRecord(value) {
|
|
3265
|
-
if (
|
|
3649
|
+
if (isRecord6(value)) return value;
|
|
3266
3650
|
if (typeof value !== "string") return null;
|
|
3267
3651
|
try {
|
|
3268
3652
|
const parsed = JSON.parse(value);
|
|
3269
|
-
return
|
|
3653
|
+
return isRecord6(parsed) ? parsed : null;
|
|
3270
3654
|
} catch {
|
|
3271
3655
|
return null;
|
|
3272
3656
|
}
|
|
@@ -3313,7 +3697,7 @@ function hasChatRunEvent2(events) {
|
|
|
3313
3697
|
);
|
|
3314
3698
|
}
|
|
3315
3699
|
function isUiMetaLike(value) {
|
|
3316
|
-
return
|
|
3700
|
+
return isRecord6(value) && ("resourceHTML" in value || "resourceUri" in value || "resourceURI" in value);
|
|
3317
3701
|
}
|
|
3318
3702
|
var AgentSession = class _AgentSession {
|
|
3319
3703
|
sessionId;
|
|
@@ -3335,6 +3719,7 @@ var AgentSession = class _AgentSession {
|
|
|
3335
3719
|
replayQueue = Promise.resolve();
|
|
3336
3720
|
pendingReplayMessage = null;
|
|
3337
3721
|
pendingReplayMode;
|
|
3722
|
+
pendingReplayKbIds;
|
|
3338
3723
|
recentChatEndAt = 0;
|
|
3339
3724
|
disposed = false;
|
|
3340
3725
|
getAppContext = null;
|
|
@@ -3359,7 +3744,6 @@ var AgentSession = class _AgentSession {
|
|
|
3359
3744
|
const session = new _AgentSession(
|
|
3360
3745
|
sessionId,
|
|
3361
3746
|
{
|
|
3362
|
-
streamTokens: false,
|
|
3363
3747
|
emitEvent: readonlyError,
|
|
3364
3748
|
emitWithAck: async () => readonlyError(),
|
|
3365
3749
|
ensureConnected: readonlyError,
|
|
@@ -3464,7 +3848,7 @@ var AgentSession = class _AgentSession {
|
|
|
3464
3848
|
if (this.getAppContext) {
|
|
3465
3849
|
try {
|
|
3466
3850
|
const rawContext = await this.getAppContext();
|
|
3467
|
-
if (!
|
|
3851
|
+
if (!isRecord6(rawContext)) {
|
|
3468
3852
|
throw new Error("getContext \u5FC5\u987B\u8FD4\u56DE JSON \u5BF9\u8C61");
|
|
3469
3853
|
}
|
|
3470
3854
|
const serialized = JSON.stringify(rawContext);
|
|
@@ -3472,7 +3856,7 @@ var AgentSession = class _AgentSession {
|
|
|
3472
3856
|
throw new Error("getContext \u5FC5\u987B\u8FD4\u56DE\u53EF\u5E8F\u5217\u5316\u7684 JSON \u5BF9\u8C61");
|
|
3473
3857
|
}
|
|
3474
3858
|
const normalized = JSON.parse(serialized);
|
|
3475
|
-
if (!
|
|
3859
|
+
if (!isRecord6(normalized)) {
|
|
3476
3860
|
throw new Error("getContext \u5FC5\u987B\u8FD4\u56DE JSON \u5BF9\u8C61");
|
|
3477
3861
|
}
|
|
3478
3862
|
appContext = normalized;
|
|
@@ -3486,13 +3870,11 @@ var AgentSession = class _AgentSession {
|
|
|
3486
3870
|
let optimisticTurnId = null;
|
|
3487
3871
|
let optimisticAskAnswer;
|
|
3488
3872
|
let previousAskAnswer;
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
});
|
|
3495
|
-
}
|
|
3873
|
+
this.update((state) => {
|
|
3874
|
+
const next = addUserMessage(state, content);
|
|
3875
|
+
optimisticTurnId = next.turns.at(-1)?.turn_id ?? null;
|
|
3876
|
+
return next;
|
|
3877
|
+
});
|
|
3496
3878
|
this.update((s) => ({ ...s, isStreaming: true }));
|
|
3497
3879
|
if (askUserAnswer && typeof askUserAnswer.tool_call_id === "string") {
|
|
3498
3880
|
const { tool_call_id, ...rest } = askUserAnswer;
|
|
@@ -3506,6 +3888,7 @@ var AgentSession = class _AgentSession {
|
|
|
3506
3888
|
if (!options.replayDecision) {
|
|
3507
3889
|
this.pendingReplayMessage = content;
|
|
3508
3890
|
this.pendingReplayMode = options.mode;
|
|
3891
|
+
this.pendingReplayKbIds = options.kbIds;
|
|
3509
3892
|
}
|
|
3510
3893
|
const payload = {
|
|
3511
3894
|
session_id: this.sessionId,
|
|
@@ -3516,7 +3899,8 @@ var AgentSession = class _AgentSession {
|
|
|
3516
3899
|
thinking_override: options.thinkingOverride,
|
|
3517
3900
|
whatif: options.whatif,
|
|
3518
3901
|
replay_decision: options.replayDecision,
|
|
3519
|
-
app_context: appContext
|
|
3902
|
+
app_context: appContext,
|
|
3903
|
+
kb_ids: options.kbIds && options.kbIds.length > 0 ? options.kbIds : void 0
|
|
3520
3904
|
};
|
|
3521
3905
|
try {
|
|
3522
3906
|
await this.ensureJoined();
|
|
@@ -3552,6 +3936,7 @@ var AgentSession = class _AgentSession {
|
|
|
3552
3936
|
rollbackOptimisticSend(optimisticTurnId, askUserAnswer, optimisticAskAnswer, previousAskAnswer, message) {
|
|
3553
3937
|
this.pendingReplayMessage = null;
|
|
3554
3938
|
this.pendingReplayMode = void 0;
|
|
3939
|
+
this.pendingReplayKbIds = void 0;
|
|
3555
3940
|
this.update((current) => {
|
|
3556
3941
|
let next = optimisticTurnId ? withTurns(
|
|
3557
3942
|
current,
|
|
@@ -3733,7 +4118,7 @@ var AgentSession = class _AgentSession {
|
|
|
3733
4118
|
_handleTurnEvents(events) {
|
|
3734
4119
|
if (!events.length) return;
|
|
3735
4120
|
const terminalChatEndStatus = extractTerminalChatEndStatus(events);
|
|
3736
|
-
if (!terminalChatEndStatus && hasChatRunEvent2(events)) {
|
|
4121
|
+
if (!terminalChatEndStatus && hasChatRunEvent2(events) && this.state.status !== "waiting_for_input") {
|
|
3737
4122
|
this.markRunningIfIdle();
|
|
3738
4123
|
}
|
|
3739
4124
|
if (this.state.turns.length) {
|
|
@@ -3805,8 +4190,10 @@ var AgentSession = class _AgentSession {
|
|
|
3805
4190
|
_handleReplayMismatch(data) {
|
|
3806
4191
|
const message = this.pendingReplayMessage ?? data.actual_message ?? "";
|
|
3807
4192
|
const mode = this.pendingReplayMode;
|
|
4193
|
+
const kbIds = this.pendingReplayKbIds;
|
|
3808
4194
|
this.pendingReplayMessage = null;
|
|
3809
4195
|
this.pendingReplayMode = void 0;
|
|
4196
|
+
this.pendingReplayKbIds = void 0;
|
|
3810
4197
|
const respond = (decision) => {
|
|
3811
4198
|
void this.runtime.emitWithAck(
|
|
3812
4199
|
"chat:send",
|
|
@@ -3814,7 +4201,8 @@ var AgentSession = class _AgentSession {
|
|
|
3814
4201
|
session_id: this.sessionId,
|
|
3815
4202
|
message,
|
|
3816
4203
|
mode,
|
|
3817
|
-
replay_decision: decision
|
|
4204
|
+
replay_decision: decision,
|
|
4205
|
+
kb_ids: kbIds && kbIds.length > 0 ? kbIds : void 0
|
|
3818
4206
|
},
|
|
3819
4207
|
3e4
|
|
3820
4208
|
).then((response) => {
|
|
@@ -3855,9 +4243,9 @@ var AgentSession = class _AgentSession {
|
|
|
3855
4243
|
detail,
|
|
3856
4244
|
status,
|
|
3857
4245
|
loopId,
|
|
3858
|
-
metadata:
|
|
4246
|
+
metadata: isRecord6(notification.metadata) ? notification.metadata : void 0
|
|
3859
4247
|
});
|
|
3860
|
-
if (notificationType === "bg:started" &&
|
|
4248
|
+
if (notificationType === "bg:started" && isRecord6(notification.metadata)) {
|
|
3861
4249
|
const taskId = typeof notification.metadata.task_id === "string" ? notification.metadata.task_id : "";
|
|
3862
4250
|
if (taskId) {
|
|
3863
4251
|
this.emitter.emit("backgroundTask", {
|
|
@@ -3987,7 +4375,7 @@ var AgentSession = class _AgentSession {
|
|
|
3987
4375
|
title: ui.title ?? "\u5DE5\u5177\u9884\u89C8"
|
|
3988
4376
|
});
|
|
3989
4377
|
}
|
|
3990
|
-
if (block.type === "system_notification" &&
|
|
4378
|
+
if (block.type === "system_notification" && isRecord6(block.content)) {
|
|
3991
4379
|
this._handleSystemNotification(turn.loop_id, block.content);
|
|
3992
4380
|
}
|
|
3993
4381
|
}
|
|
@@ -4093,8 +4481,7 @@ var AgentSession = class _AgentSession {
|
|
|
4093
4481
|
{
|
|
4094
4482
|
session_id: this.sessionId,
|
|
4095
4483
|
event_format: "raw",
|
|
4096
|
-
resume: true
|
|
4097
|
-
stream_tokens: this.runtime.streamTokens
|
|
4484
|
+
resume: true
|
|
4098
4485
|
},
|
|
4099
4486
|
5e3
|
|
4100
4487
|
).then((response) => {
|
|
@@ -4231,7 +4618,6 @@ var SessionHub = class {
|
|
|
4231
4618
|
}
|
|
4232
4619
|
runtimeFor() {
|
|
4233
4620
|
return {
|
|
4234
|
-
streamTokens: this.client.options?.streamTokens !== false,
|
|
4235
4621
|
emitEvent: (event, data) => {
|
|
4236
4622
|
;
|
|
4237
4623
|
this.client.socket().emit(event, data);
|
|
@@ -4471,7 +4857,7 @@ function resolveAuthToken(options) {
|
|
|
4471
4857
|
|
|
4472
4858
|
// src/version.ts
|
|
4473
4859
|
var SDK_NAME = "agent-client";
|
|
4474
|
-
var SDK_VERSION = true ? "2610.0.0-beta.
|
|
4860
|
+
var SDK_VERSION = true ? "2610.0.0-beta.31" : "1.1.1";
|
|
4475
4861
|
|
|
4476
4862
|
// src/socket.ts
|
|
4477
4863
|
function withSdkIdentity(auth) {
|
|
@@ -4499,6 +4885,7 @@ var BladeClient = class {
|
|
|
4499
4885
|
options;
|
|
4500
4886
|
auth;
|
|
4501
4887
|
headless;
|
|
4888
|
+
computers;
|
|
4502
4889
|
models;
|
|
4503
4890
|
sessions;
|
|
4504
4891
|
/** 实时会话中枢:client.sessions.connect() 内部使用,一般不直接访问。 */
|
|
@@ -4510,6 +4897,7 @@ var BladeClient = class {
|
|
|
4510
4897
|
};
|
|
4511
4898
|
this.auth = new AuthResource(this);
|
|
4512
4899
|
this.headless = new HeadlessResource(this);
|
|
4900
|
+
this.computers = new ComputersResource(this);
|
|
4513
4901
|
this.models = new ModelsResource(this);
|
|
4514
4902
|
this.sessions = new SessionsResource(this);
|
|
4515
4903
|
this.hub = new SessionHub(this);
|
|
@@ -4864,6 +5252,89 @@ function parseXhrHeaders(rawHeaders) {
|
|
|
4864
5252
|
return headers;
|
|
4865
5253
|
}
|
|
4866
5254
|
|
|
5255
|
+
// src/platform-endpoints.ts
|
|
5256
|
+
var PLATFORM_SERVICE_NAMES = [
|
|
5257
|
+
"os",
|
|
5258
|
+
"hub",
|
|
5259
|
+
"agent",
|
|
5260
|
+
"oauth",
|
|
5261
|
+
"llmGateway",
|
|
5262
|
+
"gitea",
|
|
5263
|
+
"tile"
|
|
5264
|
+
];
|
|
5265
|
+
var EMPTY_PLATFORM_ENDPOINTS = Object.freeze({
|
|
5266
|
+
services: Object.freeze({})
|
|
5267
|
+
});
|
|
5268
|
+
function defaultDocumentBaseUrl() {
|
|
5269
|
+
if (typeof document === "undefined") return void 0;
|
|
5270
|
+
if (document.querySelector("base[href]")) return document.baseURI;
|
|
5271
|
+
if (typeof location !== "undefined" && location.origin) return `${location.origin}/`;
|
|
5272
|
+
return document.baseURI;
|
|
5273
|
+
}
|
|
5274
|
+
function isRecord7(value) {
|
|
5275
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5276
|
+
}
|
|
5277
|
+
function normalizeServiceUrl(value) {
|
|
5278
|
+
if (typeof value !== "string" || !value.trim()) return null;
|
|
5279
|
+
if (value.includes("?") || value.includes("#")) return null;
|
|
5280
|
+
try {
|
|
5281
|
+
const url = new URL(value.trim());
|
|
5282
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return null;
|
|
5283
|
+
if (url.username || url.password || url.search || url.hash) return null;
|
|
5284
|
+
return url.toString().replace(/\/$/, "");
|
|
5285
|
+
} catch {
|
|
5286
|
+
return null;
|
|
5287
|
+
}
|
|
5288
|
+
}
|
|
5289
|
+
function parsePlatformEndpoints(value) {
|
|
5290
|
+
if (!isRecord7(value) || !isRecord7(value.services)) return null;
|
|
5291
|
+
const services = {};
|
|
5292
|
+
for (const name of PLATFORM_SERVICE_NAMES) {
|
|
5293
|
+
if (!(name in value.services)) continue;
|
|
5294
|
+
const url = normalizeServiceUrl(value.services[name]);
|
|
5295
|
+
if (!url) return null;
|
|
5296
|
+
if (name === "agent" && new URL(url).pathname !== "/") return null;
|
|
5297
|
+
services[name] = url;
|
|
5298
|
+
}
|
|
5299
|
+
return { services };
|
|
5300
|
+
}
|
|
5301
|
+
async function loadPlatformEndpoints(options = {}) {
|
|
5302
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
5303
|
+
const baseUrl = options.baseUrl ?? defaultDocumentBaseUrl();
|
|
5304
|
+
if (!fetchImpl || !baseUrl) return EMPTY_PLATFORM_ENDPOINTS;
|
|
5305
|
+
const controller = new AbortController();
|
|
5306
|
+
const timeout = globalThis.setTimeout(() => controller.abort(), options.timeoutMs ?? 3e3);
|
|
5307
|
+
try {
|
|
5308
|
+
const response = await fetchImpl(new URL("config.json", baseUrl), {
|
|
5309
|
+
cache: "no-store",
|
|
5310
|
+
credentials: "same-origin",
|
|
5311
|
+
headers: { Accept: "application/json" },
|
|
5312
|
+
signal: controller.signal
|
|
5313
|
+
});
|
|
5314
|
+
if (!response.ok) return EMPTY_PLATFORM_ENDPOINTS;
|
|
5315
|
+
return parsePlatformEndpoints(await response.json()) ?? EMPTY_PLATFORM_ENDPOINTS;
|
|
5316
|
+
} catch {
|
|
5317
|
+
return EMPTY_PLATFORM_ENDPOINTS;
|
|
5318
|
+
} finally {
|
|
5319
|
+
globalThis.clearTimeout(timeout);
|
|
5320
|
+
}
|
|
5321
|
+
}
|
|
5322
|
+
function resolveServiceUrl(endpoints, name, path = "") {
|
|
5323
|
+
const baseUrl = endpoints.services[name];
|
|
5324
|
+
if (!baseUrl) return null;
|
|
5325
|
+
if (!path) return baseUrl;
|
|
5326
|
+
const pathReference = path.trimStart();
|
|
5327
|
+
if (path.includes("\\") || pathReference.startsWith("//") || /^[a-z][a-z0-9+.-]*:/i.test(pathReference)) {
|
|
5328
|
+
return null;
|
|
5329
|
+
}
|
|
5330
|
+
const base = new URL(`${baseUrl}/`);
|
|
5331
|
+
const target = new URL(path.replace(/^\/+/, ""), base);
|
|
5332
|
+
if (target.origin !== base.origin || !target.pathname.startsWith(base.pathname)) {
|
|
5333
|
+
return null;
|
|
5334
|
+
}
|
|
5335
|
+
return target.toString();
|
|
5336
|
+
}
|
|
5337
|
+
|
|
4867
5338
|
// src/commands/protocol.ts
|
|
4868
5339
|
function isCommandEnvelope(value) {
|
|
4869
5340
|
if (typeof value !== "object" || value === null) return false;
|
|
@@ -4967,7 +5438,9 @@ export {
|
|
|
4967
5438
|
BladeApiError,
|
|
4968
5439
|
BladeClient,
|
|
4969
5440
|
ClientProjectionBuilder,
|
|
5441
|
+
ComputersResource,
|
|
4970
5442
|
DEFAULT_REPLAY_SPEED,
|
|
5443
|
+
EMPTY_PLATFORM_ENDPOINTS,
|
|
4971
5444
|
LayoutType,
|
|
4972
5445
|
ModelsResource,
|
|
4973
5446
|
SDK_NAME,
|
|
@@ -4980,20 +5453,31 @@ export {
|
|
|
4980
5453
|
TaskStatus,
|
|
4981
5454
|
acceptedPostChatFollowupCompletesLatestRun,
|
|
4982
5455
|
buildMessageContent,
|
|
5456
|
+
canToggleComputer,
|
|
5457
|
+
chatErrorForDisplay,
|
|
5458
|
+
computerState,
|
|
4983
5459
|
connectEmbedded,
|
|
4984
5460
|
contentPreview,
|
|
5461
|
+
contextProjectionData,
|
|
4985
5462
|
createInitialSessionState,
|
|
4986
5463
|
createSocket,
|
|
4987
5464
|
extractTextAttachments,
|
|
5465
|
+
getContextDisplayState,
|
|
5466
|
+
getContextGroupDisplayState,
|
|
4988
5467
|
getFileParts,
|
|
4989
5468
|
getImageParts,
|
|
4990
5469
|
getTextContent,
|
|
5470
|
+
groupAdjacentContextRuns,
|
|
4991
5471
|
groupMessagesByLoop,
|
|
4992
5472
|
isCommandEnvelope,
|
|
4993
5473
|
isHiddenInternalMessage,
|
|
4994
5474
|
isInboundEnvelope,
|
|
4995
5475
|
latestPostChatFollowup,
|
|
5476
|
+
loadPlatformEndpoints,
|
|
4996
5477
|
normalizeMessageContent,
|
|
5478
|
+
reconcileOptimisticUserTurns,
|
|
5479
|
+
resolveServiceUrl,
|
|
5480
|
+
sortComputers,
|
|
4997
5481
|
toReplaySnapshot,
|
|
4998
5482
|
transformSlashCommand
|
|
4999
5483
|
};
|