@agentdevjs/viewer 0.1.0 → 0.1.1
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/dist/{chunk-OPBQEOJC.js → chunk-OVQA4A62.js} +150 -29
- package/dist/chunk-OVQA4A62.js.map +1 -0
- package/dist/cli/server.js +1 -1
- package/dist/cli/server.js.map +1 -1
- package/dist/cli/viewer.js +1 -1
- package/dist/cli/viewer.js.map +1 -1
- package/dist/index.d.ts +34 -3
- package/dist/index.js +1 -1
- package/package.json +59 -59
- package/dist/chunk-OPBQEOJC.js.map +0 -1
|
@@ -6844,9 +6844,9 @@ var ViewerWorker = class _ViewerWorker {
|
|
|
6844
6844
|
reject(err);
|
|
6845
6845
|
}
|
|
6846
6846
|
});
|
|
6847
|
-
this.server.listen(this.port, async () => {
|
|
6848
|
-
const url = `http://
|
|
6849
|
-
console.log(`[Viewer Worker] ${url}`);
|
|
6847
|
+
this.server.listen(this.port, "127.0.0.1", async () => {
|
|
6848
|
+
const url = `http://127.0.0.1:${this.port}`;
|
|
6849
|
+
console.log(`[Viewer Worker] ${url} (loopback only)`);
|
|
6850
6850
|
console.log(`[Viewer Worker] MCP endpoint: ${url}/mcp`);
|
|
6851
6851
|
if (this.openBrowser) {
|
|
6852
6852
|
try {
|
|
@@ -7037,7 +7037,7 @@ var ViewerWorker = class _ViewerWorker {
|
|
|
7037
7037
|
}
|
|
7038
7038
|
const msgMatch = url.match(/^\/api\/agents\/([^/]+)\/messages$/);
|
|
7039
7039
|
if (msgMatch && req.method === "GET") {
|
|
7040
|
-
this.handleGetAgentMessages(req, res, msgMatch[1]);
|
|
7040
|
+
this.handleGetAgentMessages(req, res, msgMatch[1], urlObj.searchParams);
|
|
7041
7041
|
return;
|
|
7042
7042
|
}
|
|
7043
7043
|
const toolsMatch = url.match(/^\/api\/agents\/([^/]+)\/tools$/);
|
|
@@ -7197,20 +7197,46 @@ var ViewerWorker = class _ViewerWorker {
|
|
|
7197
7197
|
}
|
|
7198
7198
|
}
|
|
7199
7199
|
}
|
|
7200
|
-
} catch
|
|
7200
|
+
} catch {
|
|
7201
7201
|
}
|
|
7202
7202
|
return null;
|
|
7203
7203
|
}
|
|
7204
7204
|
/**
|
|
7205
7205
|
* GET /api/agents/:id/messages - 获取指定 Agent 的消息
|
|
7206
|
+
*
|
|
7207
|
+
* 增量取数(ADR-0012):?since=<n> 返回自下标 n 起的尾部切片(附 baseCount);
|
|
7208
|
+
* ?tail=1 返回最后一条。无参数路径的响应形状与历史完全一致(全量数组)。
|
|
7206
7209
|
*/
|
|
7207
|
-
handleGetAgentMessages(req, res, agentId) {
|
|
7210
|
+
handleGetAgentMessages(req, res, agentId, searchParams) {
|
|
7208
7211
|
const session = this.agentSessions.get(agentId);
|
|
7209
7212
|
if (!session) {
|
|
7210
7213
|
res.writeHead(404);
|
|
7211
7214
|
res.end(JSON.stringify({ error: "Agent not found" }));
|
|
7212
7215
|
return;
|
|
7213
7216
|
}
|
|
7217
|
+
const since = searchParams?.get("since");
|
|
7218
|
+
if (since !== null && since !== void 0) {
|
|
7219
|
+
const n = Number.parseInt(since, 10);
|
|
7220
|
+
if (Number.isNaN(n) || n < 0) {
|
|
7221
|
+
res.writeHead(400, { "Content-Type": "application/json; charset=utf-8" });
|
|
7222
|
+
res.end(JSON.stringify({ error: "Invalid since parameter" }));
|
|
7223
|
+
return;
|
|
7224
|
+
}
|
|
7225
|
+
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
7226
|
+
res.end(JSON.stringify({
|
|
7227
|
+
messages: session.messages.slice(n),
|
|
7228
|
+
baseCount: n
|
|
7229
|
+
}));
|
|
7230
|
+
return;
|
|
7231
|
+
}
|
|
7232
|
+
if (searchParams?.get("tail") === "1") {
|
|
7233
|
+
const last = session.messages.length > 0 ? session.messages[session.messages.length - 1] : null;
|
|
7234
|
+
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
7235
|
+
res.end(JSON.stringify({
|
|
7236
|
+
messages: last ? [last] : []
|
|
7237
|
+
}));
|
|
7238
|
+
return;
|
|
7239
|
+
}
|
|
7214
7240
|
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
7215
7241
|
res.end(JSON.stringify({
|
|
7216
7242
|
agentId,
|
|
@@ -7252,7 +7278,9 @@ var ViewerWorker = class _ViewerWorker {
|
|
|
7252
7278
|
return;
|
|
7253
7279
|
}
|
|
7254
7280
|
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
7255
|
-
|
|
7281
|
+
const overview = this.getMergedOverview(session);
|
|
7282
|
+
const probe = this.getMessagesProbe(session);
|
|
7283
|
+
res.end(JSON.stringify(probe ? { ...overview, _messagesProbe: probe } : overview));
|
|
7256
7284
|
}
|
|
7257
7285
|
handleGetAgentTodoPlan(req, res, agentId) {
|
|
7258
7286
|
const session = this.agentSessions.get(agentId);
|
|
@@ -7438,7 +7466,7 @@ var ViewerWorker = class _ViewerWorker {
|
|
|
7438
7466
|
delete session.inputLease;
|
|
7439
7467
|
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
7440
7468
|
res.end(JSON.stringify({ success: true }));
|
|
7441
|
-
} catch
|
|
7469
|
+
} catch {
|
|
7442
7470
|
res.writeHead(400);
|
|
7443
7471
|
res.end(JSON.stringify({ error: "Invalid request" }));
|
|
7444
7472
|
}
|
|
@@ -7759,17 +7787,35 @@ var ViewerWorker = class _ViewerWorker {
|
|
|
7759
7787
|
}
|
|
7760
7788
|
/**
|
|
7761
7789
|
* 应用内存限制
|
|
7790
|
+
*
|
|
7791
|
+
* 字节限制基于增量缓存 _totalBytes(推送时维护,修剪时扣减):未超限
|
|
7792
|
+
* 零序列化开销;未初始化(旧路径未走过推送)时先全量求和建立基线。
|
|
7793
|
+
* 切点语义与原实现一致:超出 MAX_BYTES 时保留首个超限条目之后的尾部。
|
|
7762
7794
|
*/
|
|
7763
7795
|
enforceMemoryLimits(session) {
|
|
7796
|
+
const store = session;
|
|
7764
7797
|
while (session.messages.length > this.MAX_MESSAGES) {
|
|
7765
|
-
session.messages.shift();
|
|
7798
|
+
const removed = session.messages.shift();
|
|
7799
|
+
if (typeof store._totalBytes === "number") {
|
|
7800
|
+
store._totalBytes -= JSON.stringify(removed).length;
|
|
7801
|
+
}
|
|
7766
7802
|
}
|
|
7767
|
-
|
|
7768
|
-
|
|
7769
|
-
|
|
7770
|
-
|
|
7771
|
-
|
|
7772
|
-
|
|
7803
|
+
if (typeof store._totalBytes !== "number") {
|
|
7804
|
+
let total = 0;
|
|
7805
|
+
for (let i = 0; i < session.messages.length; i++) {
|
|
7806
|
+
total += JSON.stringify(session.messages[i]).length;
|
|
7807
|
+
}
|
|
7808
|
+
store._totalBytes = total;
|
|
7809
|
+
}
|
|
7810
|
+
if (store._totalBytes > this.MAX_BYTES) {
|
|
7811
|
+
let byteSize = 0;
|
|
7812
|
+
for (let i = 0; i < session.messages.length; i++) {
|
|
7813
|
+
byteSize += JSON.stringify(session.messages[i]).length;
|
|
7814
|
+
if (byteSize > this.MAX_BYTES) {
|
|
7815
|
+
session.messages = session.messages.slice(i + 1);
|
|
7816
|
+
store._totalBytes -= byteSize;
|
|
7817
|
+
break;
|
|
7818
|
+
}
|
|
7773
7819
|
}
|
|
7774
7820
|
}
|
|
7775
7821
|
}
|
|
@@ -7778,7 +7824,7 @@ var ViewerWorker = class _ViewerWorker {
|
|
|
7778
7824
|
* 处理注册 Agent
|
|
7779
7825
|
*/
|
|
7780
7826
|
handleRegisterAgent(msg, clientId) {
|
|
7781
|
-
const { agentId, name,
|
|
7827
|
+
const { agentId, name, projectRoot, templateMounts, templateEntries, hookInspector, overview, activeInputRequest, inputPolicy } = msg;
|
|
7782
7828
|
const session = this.getOrCreateSession(agentId, name);
|
|
7783
7829
|
if (inputPolicy === "none") {
|
|
7784
7830
|
session.inputPolicy = "none";
|
|
@@ -7887,18 +7933,33 @@ var ViewerWorker = class _ViewerWorker {
|
|
|
7887
7933
|
/**
|
|
7888
7934
|
* 处理推送消息(带去重优化)
|
|
7889
7935
|
*
|
|
7890
|
-
*
|
|
7936
|
+
* 只有在消息真正变化时才更新会话并触发前端更新。推送时刻新旧数组都在手上,
|
|
7937
|
+
* 顺带完成变更分类(ADR-0012)并增量维护总字节缓存;分类为 rewrite 时
|
|
7938
|
+
* 照常更新 session.messages——修正"中段变化但 count 与末条签名均不变"
|
|
7939
|
+
* 被静默丢弃的盲区。
|
|
7891
7940
|
*/
|
|
7892
7941
|
handlePushMessages(msg) {
|
|
7893
7942
|
const { agentId, messages } = msg;
|
|
7894
7943
|
const session = this.agentSessions.get(agentId);
|
|
7895
7944
|
if (!session) return;
|
|
7896
|
-
const
|
|
7897
|
-
|
|
7945
|
+
const oldMessages = session.messages;
|
|
7946
|
+
const change = this.classifyMessagesChange(oldMessages, messages);
|
|
7947
|
+
if (change) {
|
|
7898
7948
|
session.messages = messages;
|
|
7949
|
+
const store = session;
|
|
7950
|
+
store._messagesChangeSeq = (typeof store._messagesChangeSeq === "number" ? store._messagesChangeSeq : 0) + 1;
|
|
7951
|
+
this.updateTotalBytes(session, oldMessages, messages, change.changeKind);
|
|
7899
7952
|
session._lastMessageSig = this.getLastMessageSignature(messages);
|
|
7900
7953
|
this.updateSessionActivity(agentId);
|
|
7954
|
+
const preTrimCount = messages.length;
|
|
7901
7955
|
this.enforceMemoryLimits(session);
|
|
7956
|
+
const trimmed = session.messages.length < preTrimCount;
|
|
7957
|
+
store._messagesChange = {
|
|
7958
|
+
changeKind: trimmed ? "rewrite" : change.changeKind,
|
|
7959
|
+
sinceIndex: trimmed ? 0 : change.sinceIndex,
|
|
7960
|
+
fakeFullBytes: store._totalBytes
|
|
7961
|
+
};
|
|
7962
|
+
} else if (typeof session._totalBytes !== "number") {
|
|
7902
7963
|
}
|
|
7903
7964
|
}
|
|
7904
7965
|
createEmptyOverview() {
|
|
@@ -8031,18 +8092,78 @@ var ViewerWorker = class _ViewerWorker {
|
|
|
8031
8092
|
};
|
|
8032
8093
|
}
|
|
8033
8094
|
/**
|
|
8034
|
-
*
|
|
8095
|
+
* 变更分类(ADR-0012,推送时刻新旧数组都在手上):
|
|
8096
|
+
* - append:新数组以旧数组为前缀且变长(sinceIndex = 旧数组长度)
|
|
8097
|
+
* - tail:条数不变,仅最后一条不同(流式输出)
|
|
8098
|
+
* - rewrite:其余(中段替换 / 条数减少,含 count 与末条签名均不变的盲区)
|
|
8099
|
+
* 返回 null 表示未变化。
|
|
8035
8100
|
*/
|
|
8036
|
-
|
|
8037
|
-
|
|
8038
|
-
|
|
8101
|
+
classifyMessagesChange(oldMessages, newMessages) {
|
|
8102
|
+
const oldLen = oldMessages.length;
|
|
8103
|
+
const newLen = newMessages.length;
|
|
8104
|
+
if (newLen > oldLen && this.isSameMessagePrefix(oldMessages, newMessages, oldLen)) {
|
|
8105
|
+
return { changeKind: "append", sinceIndex: oldLen };
|
|
8106
|
+
}
|
|
8107
|
+
if (newLen === oldLen) {
|
|
8108
|
+
if (newLen === 0) return null;
|
|
8109
|
+
const lastSigChanged = this.getLastMessageSignature(newMessages) !== this.getLastMessageSignature(oldMessages);
|
|
8110
|
+
const prefixLen = lastSigChanged ? newLen - 1 : newLen;
|
|
8111
|
+
if (this.isSameMessagePrefix(oldMessages, newMessages, prefixLen)) {
|
|
8112
|
+
return lastSigChanged ? { changeKind: "tail", sinceIndex: newLen - 1 } : null;
|
|
8113
|
+
}
|
|
8114
|
+
return { changeKind: "rewrite", sinceIndex: 0 };
|
|
8039
8115
|
}
|
|
8040
|
-
|
|
8041
|
-
|
|
8116
|
+
return { changeKind: "rewrite", sinceIndex: 0 };
|
|
8117
|
+
}
|
|
8118
|
+
/**
|
|
8119
|
+
* 判断两数组前 len 条是否逐条一致:先比引用(运行时对未变化条目通常
|
|
8120
|
+
* 复用同一对象),不一致再退回 JSON 全等(消息对象可能被重建)。
|
|
8121
|
+
*/
|
|
8122
|
+
isSameMessagePrefix(oldMessages, newMessages, len) {
|
|
8123
|
+
for (let i = 0; i < len; i++) {
|
|
8124
|
+
if (oldMessages[i] === newMessages[i]) continue;
|
|
8125
|
+
if (JSON.stringify(oldMessages[i]) !== JSON.stringify(newMessages[i])) return false;
|
|
8126
|
+
}
|
|
8127
|
+
return true;
|
|
8128
|
+
}
|
|
8129
|
+
/**
|
|
8130
|
+
* 消息探测数据(/overview HTTP 组装用,ADR-0012)。count 取当前消息数,
|
|
8131
|
+
* 其余为最近一次推送记录下的分类与假想全量字节;未走过推送路径
|
|
8132
|
+
* (_totalBytes 未初始化)时返回 null,调用方据此整体省略 _messagesProbe。
|
|
8133
|
+
*/
|
|
8134
|
+
getMessagesProbe(session) {
|
|
8135
|
+
const change = session._messagesChange;
|
|
8136
|
+
if (!change || typeof session._totalBytes !== "number") return null;
|
|
8137
|
+
return {
|
|
8138
|
+
seq: session._messagesChangeSeq || 0,
|
|
8139
|
+
count: session.messages.length,
|
|
8140
|
+
changeKind: change.changeKind,
|
|
8141
|
+
sinceIndex: change.sinceIndex,
|
|
8142
|
+
fakeFullBytes: change.fakeFullBytes
|
|
8143
|
+
};
|
|
8144
|
+
}
|
|
8145
|
+
/**
|
|
8146
|
+
* 增量维护 session 总字节缓存(_totalBytes = 全量响应体假想字节数):
|
|
8147
|
+
* append 只序列化新增条目,tail 只序列化末条新旧两份;rewrite(rollback /
|
|
8148
|
+
* compact 等低频路径)与未初始化基线时全量重建。
|
|
8149
|
+
*/
|
|
8150
|
+
updateTotalBytes(session, oldMessages, newMessages, changeKind) {
|
|
8151
|
+
const store = session;
|
|
8152
|
+
if (typeof store._totalBytes !== "number" || changeKind === "rewrite") {
|
|
8153
|
+
let total = 0;
|
|
8154
|
+
for (let i = 0; i < newMessages.length; i++) {
|
|
8155
|
+
total += JSON.stringify(newMessages[i]).length;
|
|
8156
|
+
}
|
|
8157
|
+
store._totalBytes = total;
|
|
8158
|
+
return;
|
|
8159
|
+
}
|
|
8160
|
+
if (changeKind === "append") {
|
|
8161
|
+
for (let i = oldMessages.length; i < newMessages.length; i++) {
|
|
8162
|
+
store._totalBytes += JSON.stringify(newMessages[i]).length;
|
|
8163
|
+
}
|
|
8164
|
+
return;
|
|
8042
8165
|
}
|
|
8043
|
-
|
|
8044
|
-
const oldSig = session._lastMessageSig;
|
|
8045
|
-
return newSig !== oldSig;
|
|
8166
|
+
store._totalBytes += JSON.stringify(newMessages[newMessages.length - 1]).length - JSON.stringify(oldMessages[oldMessages.length - 1]).length;
|
|
8046
8167
|
}
|
|
8047
8168
|
/**
|
|
8048
8169
|
* 获取最后一条消息的签名(用于变化检测)
|
|
@@ -8565,4 +8686,4 @@ export {
|
|
|
8565
8686
|
generateViewerHtml,
|
|
8566
8687
|
ViewerWorker
|
|
8567
8688
|
};
|
|
8568
|
-
//# sourceMappingURL=chunk-
|
|
8689
|
+
//# sourceMappingURL=chunk-OVQA4A62.js.map
|