farming-code 2.2.29 → 2.2.30
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 +25 -66
- package/README.zh_cn.md +15 -66
- package/backend/acp-runtime.js +1 -15
- package/backend/acp-session-state.js +78 -88
- package/backend/agent-session-history.js +45 -12
- package/backend/codex-session-history.js +1 -1
- package/backend/codex-transcript.js +0 -202
- package/backend/config-manager.js +3 -19
- package/backend/farming-app-cli.js +1 -1
- package/backend/farming-cli.js +3 -57
- package/backend/index-html.js +21 -3
- package/backend/main-agent-skills.js +1 -18
- package/backend/server.js +6 -48
- package/backend/storage-layout.js +15 -0
- package/backend/update-service.js +55 -935
- package/config/farming.deploy.env.example +1 -1
- package/config/farming.install.env.example +1 -1
- package/dist/acp/codex-acp-1.1.4.mjs +387 -7
- package/dist/assets/App-Z1N7T0eM.js +695 -0
- package/dist/assets/{ProjectFilesSection-Cf2BEcAN.js → ProjectFilesSection-K3GHuQ4U.js} +2 -2
- package/dist/assets/code-dark-kLsQLvpf.css +1 -0
- package/dist/assets/{index-CQNaZc3l.js → index-D-zetSol.js} +2 -2
- package/dist/assets/main-DUTz-Rd2.css +1 -0
- package/dist/index.html +2 -1
- package/extensions/browser/backend/browser-resource-manager.js +27 -5
- package/extensions/browser/backend/browser-router.js +8 -0
- package/extensions/browser/backend/executable-discovery.js +26 -12
- package/extensions/browser/backend/managed-chromium-installer.js +593 -0
- package/extensions/browser/frontend/types.ts +8 -1
- package/extensions/browser/frontend/useBrowserResources.ts +8 -0
- package/index.html +1 -0
- package/package.json +1 -1
- package/backend/agent-memory-report.js +0 -583
- package/backend/bundle-update-helper.js +0 -113
- package/backend/codex-rollout-follower.js +0 -326
- package/dist/assets/App-Ex2oVVXO.js +0 -693
- package/dist/assets/code-dark-BEZTQwYZ.css +0 -1
- package/dist/assets/main-DK9Sy7-c.css +0 -1
|
@@ -11,7 +11,7 @@ FARMING_BASE_PATH=/farming
|
|
|
11
11
|
# 可选:为 demo 或共享机器隔离 server 进程的 HOME。
|
|
12
12
|
# FARMING_SERVER_HOME="${HOME}"
|
|
13
13
|
|
|
14
|
-
#
|
|
14
|
+
# 应用内更新只对 npm 安装开放;App Bundle 使用对应的手动部署流程。
|
|
15
15
|
|
|
16
16
|
# Farming 随发行包提供默认公共 npm 镜像候选。
|
|
17
17
|
# 可设置为其他 HTTPS Registry Origin,或设置为 off 关闭镜像候选。
|
|
@@ -23366,6 +23366,9 @@ function createAgentTextThoughtChunk(text, messageId, meta3) {
|
|
|
23366
23366
|
var LEGACY_SET_SESSION_MODEL_METHOD = "session/set_model";
|
|
23367
23367
|
var GOAL_CONTROL_METHOD = "_codex/session/goal_control";
|
|
23368
23368
|
var STEER_METHOD = "_codex/session/steer";
|
|
23369
|
+
var SUBAGENT_STATE_VERSION = 1;
|
|
23370
|
+
var MAX_SUBAGENT_THREADS = 128;
|
|
23371
|
+
var SUBAGENT_READ_CONCURRENCY = 8;
|
|
23369
23372
|
function isExtMethodRequest(request) {
|
|
23370
23373
|
return request.method === "authentication/status" || request.method === "authentication/logout" || request.method === LEGACY_SET_SESSION_MODEL_METHOD || request.method === GOAL_CONTROL_METHOD || request.method === STEER_METHOD;
|
|
23371
23374
|
}
|
|
@@ -26124,6 +26127,54 @@ var CodexAcpClient = class {
|
|
|
26124
26127
|
const response = await this.codexClient.threadGoalGet({ threadId: sessionId });
|
|
26125
26128
|
return response?.goal ?? null;
|
|
26126
26129
|
}
|
|
26130
|
+
onClientTransportEvent(callback) {
|
|
26131
|
+
this.codexClient.onClientTransportEvent(callback);
|
|
26132
|
+
}
|
|
26133
|
+
async listSubagentThreads(rootThreadId) {
|
|
26134
|
+
const threads = [];
|
|
26135
|
+
let cursor = null;
|
|
26136
|
+
do {
|
|
26137
|
+
const response = await this.codexClient.threadList({
|
|
26138
|
+
cursor,
|
|
26139
|
+
limit: Math.min(100, MAX_SUBAGENT_THREADS - threads.length),
|
|
26140
|
+
sortKey: "created_at",
|
|
26141
|
+
sortDirection: "asc",
|
|
26142
|
+
useStateDbOnly: true,
|
|
26143
|
+
ancestorThreadId: rootThreadId
|
|
26144
|
+
});
|
|
26145
|
+
threads.push(...response.data.slice(0, MAX_SUBAGENT_THREADS - threads.length));
|
|
26146
|
+
cursor = threads.length < MAX_SUBAGENT_THREADS ? response.nextCursor ?? null : null;
|
|
26147
|
+
} while (cursor);
|
|
26148
|
+
let nextIndex = 0;
|
|
26149
|
+
const resolved = new Array(threads.length);
|
|
26150
|
+
const worker = async () => {
|
|
26151
|
+
while (nextIndex < threads.length) {
|
|
26152
|
+
const index = nextIndex++;
|
|
26153
|
+
const thread = threads[index];
|
|
26154
|
+
const runtimeStatus = thread?.status?.type;
|
|
26155
|
+
if (runtimeStatus === "active" || runtimeStatus === "systemError") {
|
|
26156
|
+
resolved[index] = thread;
|
|
26157
|
+
continue;
|
|
26158
|
+
}
|
|
26159
|
+
try {
|
|
26160
|
+
const response = await this.codexClient.threadRead({
|
|
26161
|
+
threadId: thread.id,
|
|
26162
|
+
includeTurns: true
|
|
26163
|
+
});
|
|
26164
|
+
resolved[index] = response.thread;
|
|
26165
|
+
} catch {
|
|
26166
|
+
resolved[index] = thread;
|
|
26167
|
+
}
|
|
26168
|
+
}
|
|
26169
|
+
};
|
|
26170
|
+
await Promise.all(
|
|
26171
|
+
Array.from(
|
|
26172
|
+
{ length: Math.min(SUBAGENT_READ_CONCURRENCY, threads.length) },
|
|
26173
|
+
() => worker()
|
|
26174
|
+
)
|
|
26175
|
+
);
|
|
26176
|
+
return resolved.filter(Boolean);
|
|
26177
|
+
}
|
|
26127
26178
|
async setGoal(sessionId, objective, onTurnStarted) {
|
|
26128
26179
|
return await this.codexClient.runGoalSet({
|
|
26129
26180
|
threadId: sessionId,
|
|
@@ -27372,6 +27423,53 @@ function imageBlocks(images) {
|
|
|
27372
27423
|
return path6 ? [{ type: "text", text: `[@image](${path6})` }] : [];
|
|
27373
27424
|
});
|
|
27374
27425
|
}
|
|
27426
|
+
var MAX_HISTORY_IMAGE_BYTES = 5 * 1024 * 1024;
|
|
27427
|
+
var HISTORY_IMAGE_MIME_BY_EXT = /* @__PURE__ */ new Map([
|
|
27428
|
+
[".gif", "image/gif"],
|
|
27429
|
+
[".jpeg", "image/jpeg"],
|
|
27430
|
+
[".jpg", "image/jpeg"],
|
|
27431
|
+
[".png", "image/png"],
|
|
27432
|
+
[".svg", "image/svg+xml"],
|
|
27433
|
+
[".webp", "image/webp"]
|
|
27434
|
+
]);
|
|
27435
|
+
function historyImageBlockFromDataUrl(value) {
|
|
27436
|
+
const match = String(value ?? "").match(/^data:(image\/(?:gif|jpe?g|png|svg\+xml|webp));base64,([a-z0-9+/=]+)$/i);
|
|
27437
|
+
if (!match || Math.ceil(match[2].length * 3 / 4) > MAX_HISTORY_IMAGE_BYTES) {
|
|
27438
|
+
return null;
|
|
27439
|
+
}
|
|
27440
|
+
return {
|
|
27441
|
+
type: "image",
|
|
27442
|
+
mimeType: match[1].toLowerCase(),
|
|
27443
|
+
data: match[2]
|
|
27444
|
+
};
|
|
27445
|
+
}
|
|
27446
|
+
async function historyImageBlockFromLocalPath(value) {
|
|
27447
|
+
let filePath = String(value ?? "");
|
|
27448
|
+
if (filePath.startsWith("file://")) {
|
|
27449
|
+
try {
|
|
27450
|
+
filePath = fileURLToPath(filePath);
|
|
27451
|
+
} catch {
|
|
27452
|
+
return null;
|
|
27453
|
+
}
|
|
27454
|
+
}
|
|
27455
|
+
const mimeType = HISTORY_IMAGE_MIME_BY_EXT.get(path5.extname(filePath).toLowerCase());
|
|
27456
|
+
if (!mimeType) {
|
|
27457
|
+
return null;
|
|
27458
|
+
}
|
|
27459
|
+
try {
|
|
27460
|
+
const data = await readFile2(filePath);
|
|
27461
|
+
if (data.length === 0 || data.length > MAX_HISTORY_IMAGE_BYTES) {
|
|
27462
|
+
return null;
|
|
27463
|
+
}
|
|
27464
|
+
return {
|
|
27465
|
+
type: "image",
|
|
27466
|
+
mimeType,
|
|
27467
|
+
data: Buffer2.from(data).toString("base64")
|
|
27468
|
+
};
|
|
27469
|
+
} catch {
|
|
27470
|
+
return null;
|
|
27471
|
+
}
|
|
27472
|
+
}
|
|
27375
27473
|
function contentBlocksFromResponseContent(content) {
|
|
27376
27474
|
if (!Array.isArray(content)) {
|
|
27377
27475
|
return [];
|
|
@@ -28171,6 +28269,13 @@ var CodexAcpServer = class _CodexAcpServer {
|
|
|
28171
28269
|
closingSessions;
|
|
28172
28270
|
sessionGenerations;
|
|
28173
28271
|
sessionOpenGenerations;
|
|
28272
|
+
subagentSnapshots;
|
|
28273
|
+
subagentRootByThreadId;
|
|
28274
|
+
subagentRefreshTokens;
|
|
28275
|
+
subagentPublishQueues;
|
|
28276
|
+
subagentPublishDirty;
|
|
28277
|
+
subagentRecoveryTimers;
|
|
28278
|
+
subagentRecoveryAttempts;
|
|
28174
28279
|
constructor(connection, codexAcpClient, defaultAuthRequest, getExitCode, getRecentStderr) {
|
|
28175
28280
|
this.sessions = /* @__PURE__ */ new Map();
|
|
28176
28281
|
this.pendingMcpStartupSessions = /* @__PURE__ */ new Map();
|
|
@@ -28179,6 +28284,13 @@ var CodexAcpServer = class _CodexAcpServer {
|
|
|
28179
28284
|
this.closingSessions = /* @__PURE__ */ new Map();
|
|
28180
28285
|
this.sessionGenerations = /* @__PURE__ */ new Map();
|
|
28181
28286
|
this.sessionOpenGenerations = /* @__PURE__ */ new Map();
|
|
28287
|
+
this.subagentSnapshots = /* @__PURE__ */ new Map();
|
|
28288
|
+
this.subagentRootByThreadId = /* @__PURE__ */ new Map();
|
|
28289
|
+
this.subagentRefreshTokens = /* @__PURE__ */ new Map();
|
|
28290
|
+
this.subagentPublishQueues = /* @__PURE__ */ new Map();
|
|
28291
|
+
this.subagentPublishDirty = /* @__PURE__ */ new Set();
|
|
28292
|
+
this.subagentRecoveryTimers = /* @__PURE__ */ new Map();
|
|
28293
|
+
this.subagentRecoveryAttempts = /* @__PURE__ */ new Map();
|
|
28182
28294
|
this.connection = connection;
|
|
28183
28295
|
this.codexAcpClient = codexAcpClient;
|
|
28184
28296
|
this.defaultAuthRequest = defaultAuthRequest ?? null;
|
|
@@ -28194,6 +28306,9 @@ var CodexAcpServer = class _CodexAcpServer {
|
|
|
28194
28306
|
(operation) => this.runWithProcessCheck(operation),
|
|
28195
28307
|
() => this.refreshSessionsAuthState(null)
|
|
28196
28308
|
);
|
|
28309
|
+
this.codexAcpClient.onClientTransportEvent((event) => {
|
|
28310
|
+
this.handleSubagentTransportEvent(event);
|
|
28311
|
+
});
|
|
28197
28312
|
}
|
|
28198
28313
|
async initialize(_params) {
|
|
28199
28314
|
logger.log("Initialize request received");
|
|
@@ -28234,7 +28349,8 @@ var CodexAcpServer = class _CodexAcpServer {
|
|
|
28234
28349
|
},
|
|
28235
28350
|
_meta: {
|
|
28236
28351
|
codex: {
|
|
28237
|
-
steer: { method: STEER_METHOD, version: 1 }
|
|
28352
|
+
steer: { method: STEER_METHOD, version: 1 },
|
|
28353
|
+
subagents: { version: SUBAGENT_STATE_VERSION }
|
|
28238
28354
|
}
|
|
28239
28355
|
}
|
|
28240
28356
|
},
|
|
@@ -28513,6 +28629,7 @@ You have been logged out. Please try again.`);
|
|
|
28513
28629
|
thread
|
|
28514
28630
|
} = await this.getOrCreateSessionWithHistory(params);
|
|
28515
28631
|
await this.streamThreadHistory(sessionId, thread);
|
|
28632
|
+
this.refreshSubagentSnapshotAsync(sessionId);
|
|
28516
28633
|
logger.log("Session loaded", {
|
|
28517
28634
|
sessionId,
|
|
28518
28635
|
modelId: modelState.currentModelId,
|
|
@@ -28548,6 +28665,7 @@ You have been logged out. Please try again.`);
|
|
|
28548
28665
|
modelId: modelState.currentModelId,
|
|
28549
28666
|
availableModelCount: modelState.availableModels.length
|
|
28550
28667
|
});
|
|
28668
|
+
this.refreshSubagentSnapshotAsync(sessionId);
|
|
28551
28669
|
return {
|
|
28552
28670
|
models: modelState,
|
|
28553
28671
|
modes: modeState,
|
|
@@ -28589,6 +28707,7 @@ You have been logged out. Please try again.`);
|
|
|
28589
28707
|
await activePrompt.completion;
|
|
28590
28708
|
}
|
|
28591
28709
|
await this.runWithProcessCheck(() => this.codexAcpClient.closeSession(params.sessionId));
|
|
28710
|
+
this.clearSubagentSnapshot(params.sessionId);
|
|
28592
28711
|
logger.log("Session closed", { sessionId: params.sessionId });
|
|
28593
28712
|
} finally {
|
|
28594
28713
|
if (this.getSessionGeneration(params.sessionId) === closeGeneration) {
|
|
@@ -28601,6 +28720,256 @@ You have been logged out. Please try again.`);
|
|
|
28601
28720
|
}
|
|
28602
28721
|
return {};
|
|
28603
28722
|
}
|
|
28723
|
+
clearSubagentSnapshot(rootThreadId) {
|
|
28724
|
+
const snapshot = this.subagentSnapshots.get(rootThreadId);
|
|
28725
|
+
for (const threadId of snapshot?.agents.keys() ?? []) {
|
|
28726
|
+
if (this.subagentRootByThreadId.get(threadId) === rootThreadId) {
|
|
28727
|
+
this.subagentRootByThreadId.delete(threadId);
|
|
28728
|
+
}
|
|
28729
|
+
}
|
|
28730
|
+
this.subagentSnapshots.delete(rootThreadId);
|
|
28731
|
+
this.subagentRefreshTokens.delete(rootThreadId);
|
|
28732
|
+
this.subagentPublishQueues.delete(rootThreadId);
|
|
28733
|
+
this.subagentPublishDirty.delete(rootThreadId);
|
|
28734
|
+
const recoveryTimer = this.subagentRecoveryTimers.get(rootThreadId);
|
|
28735
|
+
if (recoveryTimer) clearTimeout(recoveryTimer);
|
|
28736
|
+
this.subagentRecoveryTimers.delete(rootThreadId);
|
|
28737
|
+
this.subagentRecoveryAttempts.delete(rootThreadId);
|
|
28738
|
+
}
|
|
28739
|
+
rootThreadIdForSubagent(threadId) {
|
|
28740
|
+
if (this.sessions.has(threadId)) return threadId;
|
|
28741
|
+
return this.subagentRootByThreadId.get(threadId) ?? null;
|
|
28742
|
+
}
|
|
28743
|
+
subagentStatusFromThread(thread) {
|
|
28744
|
+
const runtimeStatus = thread?.status?.type;
|
|
28745
|
+
if (runtimeStatus === "active") return "running";
|
|
28746
|
+
if (runtimeStatus === "systemError") return "errored";
|
|
28747
|
+
const lastTurn = Array.isArray(thread?.turns) ? thread.turns.at(-1) : null;
|
|
28748
|
+
switch (lastTurn?.status) {
|
|
28749
|
+
case "completed":
|
|
28750
|
+
return "completed";
|
|
28751
|
+
case "interrupted":
|
|
28752
|
+
return "interrupted";
|
|
28753
|
+
case "failed":
|
|
28754
|
+
return "errored";
|
|
28755
|
+
case "inProgress":
|
|
28756
|
+
return "running";
|
|
28757
|
+
default:
|
|
28758
|
+
return "pendingInit";
|
|
28759
|
+
}
|
|
28760
|
+
}
|
|
28761
|
+
subagentSnapshotEntry(thread) {
|
|
28762
|
+
return {
|
|
28763
|
+
threadId: String(thread.id),
|
|
28764
|
+
parentThreadId: typeof thread.parentThreadId === "string" ? thread.parentThreadId : null,
|
|
28765
|
+
name: typeof thread.agentNickname === "string" && thread.agentNickname.trim()
|
|
28766
|
+
? thread.agentNickname.trim()
|
|
28767
|
+
: typeof thread.agentRole === "string" && thread.agentRole.trim()
|
|
28768
|
+
? thread.agentRole.trim()
|
|
28769
|
+
: "",
|
|
28770
|
+
status: this.subagentStatusFromThread(thread)
|
|
28771
|
+
};
|
|
28772
|
+
}
|
|
28773
|
+
refreshSubagentSnapshotAsync(rootThreadId) {
|
|
28774
|
+
const refreshToken = this.subagentRefreshTokens.get(rootThreadId) ?? 0;
|
|
28775
|
+
void this.runWithProcessCheck(() => this.codexAcpClient.listSubagentThreads(rootThreadId)).then((threads) => {
|
|
28776
|
+
if (!this.sessions.has(rootThreadId)) return;
|
|
28777
|
+
if ((this.subagentRefreshTokens.get(rootThreadId) ?? 0) !== refreshToken) {
|
|
28778
|
+
this.refreshSubagentSnapshotAsync(rootThreadId);
|
|
28779
|
+
return;
|
|
28780
|
+
}
|
|
28781
|
+
const previous = this.subagentSnapshots.get(rootThreadId);
|
|
28782
|
+
const agents = new Map(
|
|
28783
|
+
threads.map((thread) => {
|
|
28784
|
+
const entry = this.subagentSnapshotEntry(thread);
|
|
28785
|
+
return [entry.threadId, entry];
|
|
28786
|
+
})
|
|
28787
|
+
);
|
|
28788
|
+
for (const threadId of previous?.agents.keys() ?? []) {
|
|
28789
|
+
if (!agents.has(threadId) && this.subagentRootByThreadId.get(threadId) === rootThreadId) {
|
|
28790
|
+
this.subagentRootByThreadId.delete(threadId);
|
|
28791
|
+
}
|
|
28792
|
+
}
|
|
28793
|
+
for (const threadId of agents.keys()) {
|
|
28794
|
+
this.subagentRootByThreadId.set(threadId, rootThreadId);
|
|
28795
|
+
}
|
|
28796
|
+
const snapshot = {
|
|
28797
|
+
revision: (previous?.revision ?? 0) + 1,
|
|
28798
|
+
agents
|
|
28799
|
+
};
|
|
28800
|
+
this.subagentSnapshots.set(rootThreadId, snapshot);
|
|
28801
|
+
this.publishSubagentUpdate(rootThreadId, "snapshot", [...agents.values()], snapshot.revision);
|
|
28802
|
+
}).catch((error51) => {
|
|
28803
|
+
logger.error(`Failed to reconcile Codex subagents for session ${rootThreadId}`, error51);
|
|
28804
|
+
});
|
|
28805
|
+
}
|
|
28806
|
+
publishSubagentUpdate(rootThreadId, kind, agents, revision) {
|
|
28807
|
+
if (!this.sessions.has(rootThreadId)) return;
|
|
28808
|
+
const run = async () => {
|
|
28809
|
+
const dirty = this.subagentPublishDirty.has(rootThreadId);
|
|
28810
|
+
const current = this.subagentSnapshots.get(rootThreadId);
|
|
28811
|
+
const updateKind = dirty ? "snapshot" : kind;
|
|
28812
|
+
const updateAgents = dirty && current ? [...current.agents.values()] : agents;
|
|
28813
|
+
const updateRevision = dirty && current ? current.revision : revision;
|
|
28814
|
+
const session = new ACPSessionConnection(this.connection, rootThreadId);
|
|
28815
|
+
await session.update({
|
|
28816
|
+
sessionUpdate: "session_info_update",
|
|
28817
|
+
_meta: {
|
|
28818
|
+
codex: {
|
|
28819
|
+
subagents: {
|
|
28820
|
+
version: SUBAGENT_STATE_VERSION,
|
|
28821
|
+
rootThreadId,
|
|
28822
|
+
revision: updateRevision,
|
|
28823
|
+
kind: updateKind,
|
|
28824
|
+
agents: updateAgents
|
|
28825
|
+
}
|
|
28826
|
+
}
|
|
28827
|
+
}
|
|
28828
|
+
});
|
|
28829
|
+
this.subagentPublishDirty.delete(rootThreadId);
|
|
28830
|
+
this.subagentRecoveryAttempts.delete(rootThreadId);
|
|
28831
|
+
const recoveryTimer = this.subagentRecoveryTimers.get(rootThreadId);
|
|
28832
|
+
if (recoveryTimer) clearTimeout(recoveryTimer);
|
|
28833
|
+
this.subagentRecoveryTimers.delete(rootThreadId);
|
|
28834
|
+
};
|
|
28835
|
+
const previous = this.subagentPublishQueues.get(rootThreadId);
|
|
28836
|
+
const next = (previous ? previous.then(run, run) : run()).catch((error51) => {
|
|
28837
|
+
logger.error(`Failed to publish Codex subagent state for session ${rootThreadId}`, error51);
|
|
28838
|
+
this.subagentPublishDirty.add(rootThreadId);
|
|
28839
|
+
this.scheduleSubagentRecovery(rootThreadId);
|
|
28840
|
+
});
|
|
28841
|
+
this.subagentPublishQueues.set(rootThreadId, next);
|
|
28842
|
+
void next.finally(() => {
|
|
28843
|
+
if (this.subagentPublishQueues.get(rootThreadId) === next) {
|
|
28844
|
+
this.subagentPublishQueues.delete(rootThreadId);
|
|
28845
|
+
}
|
|
28846
|
+
});
|
|
28847
|
+
}
|
|
28848
|
+
scheduleSubagentRecovery(rootThreadId) {
|
|
28849
|
+
if (!this.sessions.has(rootThreadId) || this.subagentRecoveryTimers.has(rootThreadId)) return;
|
|
28850
|
+
const attempt = this.subagentRecoveryAttempts.get(rootThreadId) ?? 0;
|
|
28851
|
+
if (attempt >= 3) return;
|
|
28852
|
+
const delays = [100, 500, 1e3];
|
|
28853
|
+
const timer = setTimeout(() => {
|
|
28854
|
+
this.subagentRecoveryTimers.delete(rootThreadId);
|
|
28855
|
+
if (!this.sessions.has(rootThreadId)) {
|
|
28856
|
+
this.subagentPublishDirty.delete(rootThreadId);
|
|
28857
|
+
this.subagentRecoveryAttempts.delete(rootThreadId);
|
|
28858
|
+
return;
|
|
28859
|
+
}
|
|
28860
|
+
if (!this.subagentPublishDirty.has(rootThreadId)) return;
|
|
28861
|
+
this.subagentRecoveryAttempts.set(rootThreadId, attempt + 1);
|
|
28862
|
+
const current = this.subagentSnapshots.get(rootThreadId);
|
|
28863
|
+
if (!current) return;
|
|
28864
|
+
this.publishSubagentUpdate(
|
|
28865
|
+
rootThreadId,
|
|
28866
|
+
"snapshot",
|
|
28867
|
+
[...current.agents.values()],
|
|
28868
|
+
current.revision
|
|
28869
|
+
);
|
|
28870
|
+
}, delays[attempt]);
|
|
28871
|
+
this.subagentRecoveryTimers.set(rootThreadId, timer);
|
|
28872
|
+
}
|
|
28873
|
+
updateSubagent(rootThreadId, entry) {
|
|
28874
|
+
if (!rootThreadId || !entry?.threadId || !this.sessions.has(rootThreadId)) return;
|
|
28875
|
+
const previous = this.subagentSnapshots.get(rootThreadId) ?? {
|
|
28876
|
+
revision: 0,
|
|
28877
|
+
agents: /* @__PURE__ */ new Map()
|
|
28878
|
+
};
|
|
28879
|
+
const existing = previous.agents.get(entry.threadId);
|
|
28880
|
+
const nextEntry = {
|
|
28881
|
+
threadId: entry.threadId,
|
|
28882
|
+
parentThreadId: entry.parentThreadId ?? existing?.parentThreadId ?? null,
|
|
28883
|
+
name: entry.name ?? existing?.name ?? "",
|
|
28884
|
+
status: entry.status ?? existing?.status ?? "pendingInit"
|
|
28885
|
+
};
|
|
28886
|
+
if (
|
|
28887
|
+
existing
|
|
28888
|
+
&& existing.parentThreadId === nextEntry.parentThreadId
|
|
28889
|
+
&& existing.name === nextEntry.name
|
|
28890
|
+
&& existing.status === nextEntry.status
|
|
28891
|
+
) {
|
|
28892
|
+
return;
|
|
28893
|
+
}
|
|
28894
|
+
const next = {
|
|
28895
|
+
revision: previous.revision + 1,
|
|
28896
|
+
agents: new Map(previous.agents)
|
|
28897
|
+
};
|
|
28898
|
+
next.agents.set(nextEntry.threadId, nextEntry);
|
|
28899
|
+
this.subagentSnapshots.set(rootThreadId, next);
|
|
28900
|
+
this.subagentRootByThreadId.set(nextEntry.threadId, rootThreadId);
|
|
28901
|
+
this.subagentRefreshTokens.set(
|
|
28902
|
+
rootThreadId,
|
|
28903
|
+
(this.subagentRefreshTokens.get(rootThreadId) ?? 0) + 1
|
|
28904
|
+
);
|
|
28905
|
+
this.publishSubagentUpdate(rootThreadId, "delta", [nextEntry], next.revision);
|
|
28906
|
+
}
|
|
28907
|
+
handleSubagentTransportEvent(event) {
|
|
28908
|
+
if (event?.eventType !== "notification") return;
|
|
28909
|
+
const params = event.params ?? {};
|
|
28910
|
+
const method = event.method;
|
|
28911
|
+
if (method === "thread/started") {
|
|
28912
|
+
const thread = params.thread;
|
|
28913
|
+
const parentThreadId = thread?.parentThreadId;
|
|
28914
|
+
const rootThreadId = typeof parentThreadId === "string"
|
|
28915
|
+
? this.rootThreadIdForSubagent(parentThreadId)
|
|
28916
|
+
: null;
|
|
28917
|
+
if (rootThreadId && thread?.id) {
|
|
28918
|
+
this.updateSubagent(rootThreadId, this.subagentSnapshotEntry(thread));
|
|
28919
|
+
}
|
|
28920
|
+
return;
|
|
28921
|
+
}
|
|
28922
|
+
if (
|
|
28923
|
+
(method === "item/started" || method === "item/completed")
|
|
28924
|
+
&& params.item?.type === "subAgentActivity"
|
|
28925
|
+
) {
|
|
28926
|
+
const parentThreadId = typeof params.threadId === "string" ? params.threadId : "";
|
|
28927
|
+
const rootThreadId = this.rootThreadIdForSubagent(parentThreadId);
|
|
28928
|
+
const childThreadId = params.item.threadId;
|
|
28929
|
+
if (rootThreadId && typeof childThreadId === "string") {
|
|
28930
|
+
this.updateSubagent(rootThreadId, {
|
|
28931
|
+
threadId: childThreadId,
|
|
28932
|
+
parentThreadId,
|
|
28933
|
+
status: "pendingInit"
|
|
28934
|
+
});
|
|
28935
|
+
}
|
|
28936
|
+
return;
|
|
28937
|
+
}
|
|
28938
|
+
const threadId = typeof params.threadId === "string" ? params.threadId : "";
|
|
28939
|
+
const rootThreadId = this.rootThreadIdForSubagent(threadId);
|
|
28940
|
+
if (!rootThreadId || rootThreadId === threadId) return;
|
|
28941
|
+
if (method === "turn/started") {
|
|
28942
|
+
this.updateSubagent(rootThreadId, { threadId, status: "running" });
|
|
28943
|
+
return;
|
|
28944
|
+
}
|
|
28945
|
+
if (method === "turn/completed") {
|
|
28946
|
+
const status = params.turn?.status;
|
|
28947
|
+
this.updateSubagent(rootThreadId, {
|
|
28948
|
+
threadId,
|
|
28949
|
+
status: status === "completed"
|
|
28950
|
+
? "completed"
|
|
28951
|
+
: status === "interrupted"
|
|
28952
|
+
? "interrupted"
|
|
28953
|
+
: status === "failed"
|
|
28954
|
+
? "errored"
|
|
28955
|
+
: "running"
|
|
28956
|
+
});
|
|
28957
|
+
return;
|
|
28958
|
+
}
|
|
28959
|
+
if (method === "thread/status/changed") {
|
|
28960
|
+
const status = params.status?.type;
|
|
28961
|
+
if (status === "active" || status === "systemError") {
|
|
28962
|
+
this.updateSubagent(rootThreadId, {
|
|
28963
|
+
threadId,
|
|
28964
|
+
status: status === "active" ? "running" : "errored"
|
|
28965
|
+
});
|
|
28966
|
+
}
|
|
28967
|
+
return;
|
|
28968
|
+
}
|
|
28969
|
+
if (method === "thread/closed") {
|
|
28970
|
+
this.updateSubagent(rootThreadId, { threadId, status: "shutdown" });
|
|
28971
|
+
}
|
|
28972
|
+
}
|
|
28604
28973
|
async deleteSession(params) {
|
|
28605
28974
|
logger.log("Deleting session...", { sessionId: params.sessionId });
|
|
28606
28975
|
const sessionId = params.sessionId;
|
|
@@ -29107,11 +29476,11 @@ You have been logged out. Please try again.`);
|
|
|
29107
29476
|
return [this.createPlanUpdate(item)];
|
|
29108
29477
|
}
|
|
29109
29478
|
}
|
|
29110
|
-
createUserMessageUpdates(item) {
|
|
29479
|
+
async createUserMessageUpdates(item) {
|
|
29111
29480
|
const updates = [];
|
|
29112
29481
|
const messageId = item.id;
|
|
29113
29482
|
for (const input of item.content) {
|
|
29114
|
-
const blocks = this.userInputToContentBlocks(input);
|
|
29483
|
+
const blocks = await this.userInputToContentBlocks(input);
|
|
29115
29484
|
for (const block of blocks) {
|
|
29116
29485
|
updates.push(createUserMessageChunk(block, messageId));
|
|
29117
29486
|
}
|
|
@@ -29155,15 +29524,26 @@ ${item.text}`
|
|
|
29155
29524
|
}
|
|
29156
29525
|
};
|
|
29157
29526
|
}
|
|
29158
|
-
userInputToContentBlocks(input) {
|
|
29527
|
+
async userInputToContentBlocks(input) {
|
|
29159
29528
|
switch (input.type) {
|
|
29160
29529
|
case "text":
|
|
29161
29530
|
return input.text.length > 0 ? [{ type: "text", text: input.text }] : [];
|
|
29162
|
-
case "image":
|
|
29163
|
-
|
|
29531
|
+
case "image": {
|
|
29532
|
+
const image = historyImageBlockFromDataUrl(input.url);
|
|
29533
|
+
return image ? [image] : [{
|
|
29534
|
+
type: "resource_link",
|
|
29535
|
+
uri: input.url,
|
|
29536
|
+
name: "image"
|
|
29537
|
+
}];
|
|
29538
|
+
}
|
|
29164
29539
|
case "localImage": {
|
|
29165
29540
|
const uri = input.path.startsWith("file://") ? input.path : `file://${input.path}`;
|
|
29166
|
-
|
|
29541
|
+
const image = await historyImageBlockFromLocalPath(input.path);
|
|
29542
|
+
return image ? [image] : [{
|
|
29543
|
+
type: "resource_link",
|
|
29544
|
+
uri,
|
|
29545
|
+
name: path5.basename(input.path) || "image"
|
|
29546
|
+
}];
|
|
29167
29547
|
}
|
|
29168
29548
|
case "skill":
|
|
29169
29549
|
return [{ type: "text", text: `skill:${input.name} (${input.path})` }];
|