replicas-engine 0.1.608 → 0.1.610
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 +5 -3
- package/dist/src/index.js +272 -156
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -134,6 +134,8 @@ Outgoing endpoints:
|
|
|
134
134
|
- `POST /v1/engine/claude/refresh-credentials`
|
|
135
135
|
- `POST /v1/engine/codex/refresh-credentials`
|
|
136
136
|
- `POST /v1/engine/infisical/refresh-token`
|
|
137
|
-
- `POST /v1/engine/chat-turn-
|
|
138
|
-
- `POST /v1/engine/skill-
|
|
139
|
-
- `POST /v1/engine/mcp-
|
|
137
|
+
- `POST /v1/engine/chat-turn-activity`
|
|
138
|
+
- `POST /v1/engine/skill-activity`
|
|
139
|
+
- `POST /v1/engine/mcp-activity`
|
|
140
|
+
|
|
141
|
+
The previous `chat-turn-usage`, `skill-usage`, and `mcp-usage` paths remain accepted for older engine versions.
|
package/dist/src/index.js
CHANGED
|
@@ -263,7 +263,7 @@ var AGENT_CREDENTIAL_METHODS_IN_ORDER = {
|
|
|
263
263
|
};
|
|
264
264
|
|
|
265
265
|
// ../shared/src/analytics/types.ts
|
|
266
|
-
function
|
|
266
|
+
function isAgentChatActivityRecord(value) {
|
|
267
267
|
return isRecord(value) && typeof value.chatId === "string" && typeof value.provider === "string" && isValidAgentProvider(value.provider) && typeof value.model === "string" && (value.credentialMethod === void 0 || isAuthMethod(value.credentialMethod)) && (value.credentialScope === void 0 || isCredentialScope(value.credentialScope)) && value.credentialMethod === void 0 === (value.credentialScope === void 0) && (value.senderUserId === void 0 || typeof value.senderUserId === "string") && typeof value.occurredAt === "string";
|
|
268
268
|
}
|
|
269
269
|
function isAuthMethod(value) {
|
|
@@ -272,22 +272,22 @@ function isAuthMethod(value) {
|
|
|
272
272
|
function isCredentialScope(value) {
|
|
273
273
|
return typeof value === "string" && CREDENTIAL_SCOPES.some((scope) => scope === value);
|
|
274
274
|
}
|
|
275
|
-
function
|
|
276
|
-
return
|
|
275
|
+
function isAgentChatTurnActivityRecord(value) {
|
|
276
|
+
return isAgentChatActivityRecord(value) && typeof value.turnId === "string" && typeof value.seconds === "number" && Number.isFinite(value.seconds) && value.seconds >= 0;
|
|
277
277
|
}
|
|
278
|
-
function
|
|
279
|
-
return
|
|
278
|
+
function isAgentChatActivityCallRecord(value) {
|
|
279
|
+
return isAgentChatActivityRecord(value) && typeof value.callId === "string" && value.callId.length > 0;
|
|
280
280
|
}
|
|
281
|
-
function
|
|
282
|
-
if (!
|
|
281
|
+
function isNamedAgentChatActivityRecord(value, field) {
|
|
282
|
+
if (!isAgentChatActivityCallRecord(value)) return false;
|
|
283
283
|
const name = value[field];
|
|
284
284
|
return typeof name === "string" && name.length > 0;
|
|
285
285
|
}
|
|
286
|
-
function
|
|
287
|
-
return
|
|
286
|
+
function isAgentChatSkillActivityRecord(value) {
|
|
287
|
+
return isNamedAgentChatActivityRecord(value, "skillName");
|
|
288
288
|
}
|
|
289
|
-
function
|
|
290
|
-
return
|
|
289
|
+
function isAgentChatMcpActivityRecord(value) {
|
|
290
|
+
return isNamedAgentChatActivityRecord(value, "mcpName");
|
|
291
291
|
}
|
|
292
292
|
|
|
293
293
|
// ../shared/src/aster.ts
|
|
@@ -7594,13 +7594,13 @@ import { mkdir as mkdir7 } from "fs/promises";
|
|
|
7594
7594
|
import { homedir as homedir9 } from "os";
|
|
7595
7595
|
import { join as join12 } from "path";
|
|
7596
7596
|
import { randomUUID } from "crypto";
|
|
7597
|
-
var
|
|
7598
|
-
var EVENTS_FILE = join12(
|
|
7597
|
+
var ENGINE_DIR2 = join12(homedir9(), ".replicas", "engine");
|
|
7598
|
+
var EVENTS_FILE = join12(ENGINE_DIR2, "events.jsonl");
|
|
7599
7599
|
var EventService = class {
|
|
7600
7600
|
subscribers = /* @__PURE__ */ new Map();
|
|
7601
7601
|
writer = new StreamWriter();
|
|
7602
7602
|
async initialize() {
|
|
7603
|
-
await mkdir7(
|
|
7603
|
+
await mkdir7(ENGINE_DIR2, { recursive: true });
|
|
7604
7604
|
this.writer.open(EVENTS_FILE);
|
|
7605
7605
|
}
|
|
7606
7606
|
subscribe(subscriber) {
|
|
@@ -10670,7 +10670,7 @@ var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
|
|
|
10670
10670
|
var MIN_CODEX_CLI_VERSION = "0.144.6";
|
|
10671
10671
|
var CODEX_UPGRADE_TIMEOUT_MS = 12e4;
|
|
10672
10672
|
var codexCliVersionEnsured = null;
|
|
10673
|
-
var ENGINE_PACKAGE_VERSION = "0.1.
|
|
10673
|
+
var ENGINE_PACKAGE_VERSION = "0.1.610";
|
|
10674
10674
|
var INITIALIZE_METHOD = "initialize";
|
|
10675
10675
|
var INITIALIZED_NOTIFICATION = "initialized";
|
|
10676
10676
|
var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
|
|
@@ -11599,14 +11599,14 @@ var TranscriptUpdateCoalescer = class {
|
|
|
11599
11599
|
// src/services/chat/history-paths.ts
|
|
11600
11600
|
import { homedir as homedir13 } from "os";
|
|
11601
11601
|
import { join as join17 } from "path";
|
|
11602
|
-
var
|
|
11603
|
-
var CHATS_FILE = join17(
|
|
11604
|
-
var CLAUDE_HISTORY_DIR = join17(
|
|
11605
|
-
var RELAY_HISTORY_DIR = join17(
|
|
11606
|
-
var CODEX_HISTORY_DIR = join17(
|
|
11607
|
-
var CURSOR_HISTORY_DIR = join17(
|
|
11608
|
-
var OPENCODE_HISTORY_DIR = join17(
|
|
11609
|
-
var PI_HISTORY_DIR = join17(
|
|
11602
|
+
var ENGINE_DIR3 = join17(homedir13(), ".replicas", "engine");
|
|
11603
|
+
var CHATS_FILE = join17(ENGINE_DIR3, "chats.json");
|
|
11604
|
+
var CLAUDE_HISTORY_DIR = join17(ENGINE_DIR3, "claude-histories");
|
|
11605
|
+
var RELAY_HISTORY_DIR = join17(ENGINE_DIR3, "relay-histories");
|
|
11606
|
+
var CODEX_HISTORY_DIR = join17(ENGINE_DIR3, "codex-histories");
|
|
11607
|
+
var CURSOR_HISTORY_DIR = join17(ENGINE_DIR3, "cursor-histories");
|
|
11608
|
+
var OPENCODE_HISTORY_DIR = join17(ENGINE_DIR3, "opencode-histories");
|
|
11609
|
+
var PI_HISTORY_DIR = join17(ENGINE_DIR3, "pi-histories");
|
|
11610
11610
|
var HISTORY_DIR_BY_PROVIDER = {
|
|
11611
11611
|
claude: CLAUDE_HISTORY_DIR,
|
|
11612
11612
|
relay: RELAY_HISTORY_DIR,
|
|
@@ -14966,34 +14966,93 @@ var RelayManager = class {
|
|
|
14966
14966
|
}
|
|
14967
14967
|
};
|
|
14968
14968
|
|
|
14969
|
-
// src/analytics/
|
|
14969
|
+
// src/analytics/agent/activity/agent-chat-activity-tracker-service.ts
|
|
14970
|
+
import {
|
|
14971
|
+
appendFile as appendFile3,
|
|
14972
|
+
mkdir as mkdir14,
|
|
14973
|
+
readFile as readFile12,
|
|
14974
|
+
readdir as readdir6,
|
|
14975
|
+
rename as rename2,
|
|
14976
|
+
unlink as unlink3
|
|
14977
|
+
} from "fs/promises";
|
|
14978
|
+
import { join as join22 } from "path";
|
|
14970
14979
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
14971
14980
|
|
|
14972
|
-
// src/analytics/
|
|
14973
|
-
|
|
14974
|
-
|
|
14981
|
+
// src/analytics/agent/activity/skill-mcp-call-extractor.ts
|
|
14982
|
+
var NON_MCP_SERVERS = /* @__PURE__ */ new Set(["claude", "cursor", "opencode", "pi", "custom", "dynamic"]);
|
|
14983
|
+
function mcpNameFromToolCall(message) {
|
|
14984
|
+
const parsedName = parseMcpToolName(message.tool);
|
|
14985
|
+
if (parsedName) return parsedName.server;
|
|
14986
|
+
if (message.tool.startsWith("mcp.")) {
|
|
14987
|
+
const [server, ...toolParts] = message.tool.slice("mcp.".length).split(".");
|
|
14988
|
+
if (server && toolParts.length > 0) return server;
|
|
14989
|
+
}
|
|
14990
|
+
return NON_MCP_SERVERS.has(message.server) ? null : message.server;
|
|
14991
|
+
}
|
|
14992
|
+
function skillCallsFromCommand(message) {
|
|
14993
|
+
const skillNames = message.skillNames ?? [];
|
|
14994
|
+
return skillNames.map((skillName, index) => ({
|
|
14995
|
+
kind: "skill",
|
|
14996
|
+
id: skillNames.length === 1 ? message.id : `${message.id}-${index}`,
|
|
14997
|
+
skillName,
|
|
14998
|
+
occurredAt: message.timestamp
|
|
14999
|
+
}));
|
|
15000
|
+
}
|
|
15001
|
+
function extractSkillMcpCalls(messages, provider) {
|
|
15002
|
+
return messages.flatMap((message) => {
|
|
15003
|
+
if (message.type === "skill") {
|
|
15004
|
+
if (provider === "relay") return [];
|
|
15005
|
+
return [{ kind: "skill", id: message.id, skillName: message.skillName, occurredAt: message.timestamp }];
|
|
15006
|
+
}
|
|
15007
|
+
if (message.type === "command") {
|
|
15008
|
+
return skillCallsFromCommand(message);
|
|
15009
|
+
}
|
|
15010
|
+
if (message.type !== "tool_call") return [];
|
|
15011
|
+
const mcpName = mcpNameFromToolCall(message);
|
|
15012
|
+
return mcpName ? [{ kind: "mcp", id: message.id, mcpName, occurredAt: message.timestamp }] : [];
|
|
15013
|
+
});
|
|
15014
|
+
}
|
|
15015
|
+
|
|
15016
|
+
// src/analytics/agent/activity/agent-chat-activity-tracker-service.ts
|
|
14975
15017
|
var MAX_FAILED_RECORDS = 500;
|
|
14976
15018
|
var FLUSH_DEBOUNCE_MS = 15e3;
|
|
14977
15019
|
var MAX_UPLOADED_SEGMENTS = 50;
|
|
14978
15020
|
var UPLOADED_SUFFIX = ".uploaded";
|
|
14979
|
-
var
|
|
15021
|
+
var MAX_TRACKED_MESSAGES = 500;
|
|
15022
|
+
var AgentChatActivityBuffer = class {
|
|
14980
15023
|
constructor(options) {
|
|
14981
15024
|
this.options = options;
|
|
14982
|
-
this.
|
|
14983
|
-
|
|
15025
|
+
this.storageNames = [
|
|
15026
|
+
options.storageName,
|
|
15027
|
+
...options.legacyStorageNames ?? []
|
|
15028
|
+
];
|
|
15029
|
+
this.liveFile = join22(ENGINE_DIR, `${options.storageName}.jsonl`);
|
|
15030
|
+
this.segmentFilePatterns = this.storageNames.map(
|
|
15031
|
+
(storageName) => new RegExp(`^${storageName}\\.(\\d+)\\.jsonl$`)
|
|
15032
|
+
);
|
|
14984
15033
|
}
|
|
14985
15034
|
options;
|
|
14986
15035
|
liveFile;
|
|
14987
|
-
|
|
15036
|
+
storageNames;
|
|
15037
|
+
segmentFilePatterns;
|
|
14988
15038
|
failedRecords = [];
|
|
14989
15039
|
pendingAppends = /* @__PURE__ */ new Set();
|
|
14990
15040
|
flushTimer = null;
|
|
14991
|
-
activeFlush = Promise.resolve({
|
|
15041
|
+
activeFlush = Promise.resolve({
|
|
15042
|
+
flushed: 0,
|
|
15043
|
+
failed: 0
|
|
15044
|
+
});
|
|
14992
15045
|
append(record) {
|
|
14993
|
-
const pending = mkdir14(
|
|
14994
|
-
|
|
14995
|
-
|
|
14996
|
-
|
|
15046
|
+
const pending = mkdir14(ENGINE_DIR, { recursive: true }).then(
|
|
15047
|
+
() => appendFile3(this.liveFile, `${JSON.stringify(record)}
|
|
15048
|
+
`, "utf-8")
|
|
15049
|
+
).catch((error) => {
|
|
15050
|
+
console.error(
|
|
15051
|
+
`[${this.options.storageName}] Append failed, will retry on flush:`,
|
|
15052
|
+
error
|
|
15053
|
+
);
|
|
15054
|
+
if (this.failedRecords.length < MAX_FAILED_RECORDS)
|
|
15055
|
+
this.failedRecords.push(record);
|
|
14997
15056
|
});
|
|
14998
15057
|
this.pendingAppends.add(pending);
|
|
14999
15058
|
void pending.finally(() => this.pendingAppends.delete(pending));
|
|
@@ -15003,7 +15062,10 @@ var AnalyticsBufferService = class {
|
|
|
15003
15062
|
this.flushTimer = setTimeout(() => {
|
|
15004
15063
|
this.flushTimer = null;
|
|
15005
15064
|
void this.flush().catch((error) => {
|
|
15006
|
-
console.error(
|
|
15065
|
+
console.error(
|
|
15066
|
+
`[${this.options.storageName}] Scheduled flush failed, retrying on the next record:`,
|
|
15067
|
+
error
|
|
15068
|
+
);
|
|
15007
15069
|
});
|
|
15008
15070
|
}, FLUSH_DEBOUNCE_MS);
|
|
15009
15071
|
this.flushTimer.unref?.();
|
|
@@ -15019,20 +15081,30 @@ var AnalyticsBufferService = class {
|
|
|
15019
15081
|
}
|
|
15020
15082
|
async runFlush() {
|
|
15021
15083
|
for (const record of this.failedRecords.splice(0)) this.append(record);
|
|
15022
|
-
while (this.pendingAppends.size > 0)
|
|
15023
|
-
|
|
15024
|
-
|
|
15025
|
-
|
|
15084
|
+
while (this.pendingAppends.size > 0)
|
|
15085
|
+
await Promise.allSettled([...this.pendingAppends]);
|
|
15086
|
+
for (const storageName of this.storageNames) {
|
|
15087
|
+
await rename2(
|
|
15088
|
+
join22(ENGINE_DIR, `${storageName}.jsonl`),
|
|
15089
|
+
join22(ENGINE_DIR, `${storageName}.${Date.now()}.jsonl`)
|
|
15090
|
+
).catch(() => {
|
|
15091
|
+
});
|
|
15092
|
+
}
|
|
15093
|
+
const entries = await readdir6(ENGINE_DIR).catch(() => []);
|
|
15026
15094
|
let flushed = 0;
|
|
15027
15095
|
let failed = 0;
|
|
15028
15096
|
for (const entry of entries) {
|
|
15029
|
-
if (!this.
|
|
15097
|
+
if (!this.segmentFilePatterns.some((pattern) => pattern.test(entry)))
|
|
15098
|
+
continue;
|
|
15030
15099
|
try {
|
|
15031
|
-
await this.uploadSegment(join22(
|
|
15100
|
+
await this.uploadSegment(join22(ENGINE_DIR, entry));
|
|
15032
15101
|
flushed++;
|
|
15033
15102
|
} catch (error) {
|
|
15034
15103
|
failed++;
|
|
15035
|
-
console.error(
|
|
15104
|
+
console.error(
|
|
15105
|
+
`[${this.options.storageName}] Segment upload failed, retained for retry:`,
|
|
15106
|
+
{ entry, error }
|
|
15107
|
+
);
|
|
15036
15108
|
}
|
|
15037
15109
|
}
|
|
15038
15110
|
const failedRecords = this.failedRecords.splice(0);
|
|
@@ -15042,13 +15114,22 @@ var AnalyticsBufferService = class {
|
|
|
15042
15114
|
flushed++;
|
|
15043
15115
|
} catch (error) {
|
|
15044
15116
|
failed++;
|
|
15045
|
-
this.failedRecords.unshift(
|
|
15046
|
-
|
|
15117
|
+
this.failedRecords.unshift(
|
|
15118
|
+
...failedRecords.slice(0, MAX_FAILED_RECORDS)
|
|
15119
|
+
);
|
|
15120
|
+
console.error(
|
|
15121
|
+
`[${this.options.storageName}] Failed records upload failed, retained for retry:`,
|
|
15122
|
+
error
|
|
15123
|
+
);
|
|
15047
15124
|
}
|
|
15048
15125
|
}
|
|
15049
|
-
const uploaded = entries.filter(
|
|
15126
|
+
const uploaded = entries.filter(
|
|
15127
|
+
(entry) => this.storageNames.some(
|
|
15128
|
+
(storageName) => entry.startsWith(`${storageName}.`)
|
|
15129
|
+
) && entry.endsWith(UPLOADED_SUFFIX)
|
|
15130
|
+
).sort();
|
|
15050
15131
|
for (const entry of uploaded.slice(0, -MAX_UPLOADED_SEGMENTS)) {
|
|
15051
|
-
await unlink3(join22(
|
|
15132
|
+
await unlink3(join22(ENGINE_DIR, entry)).catch(() => {
|
|
15052
15133
|
});
|
|
15053
15134
|
}
|
|
15054
15135
|
return { flushed, failed };
|
|
@@ -15067,66 +15148,40 @@ var AnalyticsBufferService = class {
|
|
|
15067
15148
|
});
|
|
15068
15149
|
}
|
|
15069
15150
|
};
|
|
15070
|
-
|
|
15071
|
-
|
|
15072
|
-
|
|
15073
|
-
|
|
15074
|
-
|
|
15075
|
-
if (parsedName) return parsedName.server;
|
|
15076
|
-
if (message.tool.startsWith("mcp.")) {
|
|
15077
|
-
const [server, ...toolParts] = message.tool.slice("mcp.".length).split(".");
|
|
15078
|
-
if (server && toolParts.length > 0) return server;
|
|
15151
|
+
var AgentChatActivityRecordTracker = class {
|
|
15152
|
+
pendingRecords = /* @__PURE__ */ new Map();
|
|
15153
|
+
buffer;
|
|
15154
|
+
constructor(options) {
|
|
15155
|
+
this.buffer = new AgentChatActivityBuffer(options);
|
|
15079
15156
|
}
|
|
15080
|
-
|
|
15081
|
-
|
|
15082
|
-
|
|
15083
|
-
|
|
15084
|
-
|
|
15085
|
-
|
|
15086
|
-
|
|
15087
|
-
|
|
15088
|
-
|
|
15089
|
-
|
|
15090
|
-
|
|
15091
|
-
|
|
15092
|
-
|
|
15093
|
-
|
|
15094
|
-
|
|
15095
|
-
|
|
15096
|
-
|
|
15097
|
-
if (message.type === "command") {
|
|
15098
|
-
return skillCallsFromCommand(message);
|
|
15099
|
-
}
|
|
15100
|
-
if (message.type !== "tool_call") return [];
|
|
15101
|
-
const mcpName = mcpNameFromToolCall(message);
|
|
15102
|
-
return mcpName ? [{ kind: "mcp", id: message.id, mcpName, occurredAt: message.timestamp }] : [];
|
|
15103
|
-
});
|
|
15104
|
-
}
|
|
15105
|
-
|
|
15106
|
-
// src/analytics/analytics.service.ts
|
|
15107
|
-
var MAX_TRACKED_MESSAGES = 500;
|
|
15108
|
-
async function uploadAnalyticsRecords(endpoint, body) {
|
|
15109
|
-
const response = await monolithRequest(endpoint, { body });
|
|
15110
|
-
if (!response.ok) throw new Error(`upload failed: ${response.status} ${await response.text()}`);
|
|
15111
|
-
}
|
|
15112
|
-
var AnalyticsService = class {
|
|
15157
|
+
track(chatId, record) {
|
|
15158
|
+
const records = this.pendingRecords.get(chatId) ?? [];
|
|
15159
|
+
records.push(record);
|
|
15160
|
+
this.pendingRecords.set(chatId, records);
|
|
15161
|
+
}
|
|
15162
|
+
noteTurnEnded(chatId) {
|
|
15163
|
+
const records = this.pendingRecords.get(chatId);
|
|
15164
|
+
if (!records) return;
|
|
15165
|
+
this.pendingRecords.delete(chatId);
|
|
15166
|
+
for (const record of records) this.buffer.append(record);
|
|
15167
|
+
this.buffer.scheduleFlush();
|
|
15168
|
+
}
|
|
15169
|
+
flush() {
|
|
15170
|
+
return this.buffer.flush();
|
|
15171
|
+
}
|
|
15172
|
+
};
|
|
15173
|
+
var AgentChatTurnActivityTracker = class {
|
|
15113
15174
|
pendingMessages = /* @__PURE__ */ new Map();
|
|
15114
15175
|
activeTurns = /* @__PURE__ */ new Map();
|
|
15115
|
-
turnBuffer
|
|
15116
|
-
|
|
15117
|
-
|
|
15118
|
-
|
|
15119
|
-
|
|
15120
|
-
|
|
15121
|
-
|
|
15122
|
-
|
|
15123
|
-
|
|
15124
|
-
});
|
|
15125
|
-
mcpBuffer = new AnalyticsBufferService({
|
|
15126
|
-
name: "mcp-usage",
|
|
15127
|
-
validate: isMcpUsageRecord,
|
|
15128
|
-
upload: (mcps) => uploadAnalyticsRecords("/v1/engine/mcp-usage", { mcps })
|
|
15129
|
-
});
|
|
15176
|
+
turnBuffer;
|
|
15177
|
+
constructor(uploadTurns) {
|
|
15178
|
+
this.turnBuffer = new AgentChatActivityBuffer({
|
|
15179
|
+
storageName: "chat-turn-activity",
|
|
15180
|
+
legacyStorageNames: ["turn-usage"],
|
|
15181
|
+
validate: isAgentChatTurnActivityRecord,
|
|
15182
|
+
upload: uploadTurns
|
|
15183
|
+
});
|
|
15184
|
+
}
|
|
15130
15185
|
noteMessageAccepted(messageId, request) {
|
|
15131
15186
|
this.pendingMessages.set(messageId, {
|
|
15132
15187
|
model: request.model,
|
|
@@ -15149,30 +15204,14 @@ var AnalyticsService = class {
|
|
|
15149
15204
|
senderUserId: attributes.senderUserId,
|
|
15150
15205
|
credentialMethod: credential?.method,
|
|
15151
15206
|
credentialScope: credential?.scope,
|
|
15152
|
-
skills: [],
|
|
15153
|
-
mcps: [],
|
|
15154
15207
|
seenCallIds: /* @__PURE__ */ new Set()
|
|
15155
15208
|
});
|
|
15156
15209
|
}
|
|
15157
|
-
|
|
15158
|
-
|
|
15159
|
-
|
|
15160
|
-
|
|
15161
|
-
|
|
15162
|
-
turn.seenCallIds.add(call.id);
|
|
15163
|
-
const common = {
|
|
15164
|
-
callId: call.id,
|
|
15165
|
-
chatId,
|
|
15166
|
-
provider: turn.provider,
|
|
15167
|
-
model: turn.model,
|
|
15168
|
-
senderUserId: turn.senderUserId,
|
|
15169
|
-
occurredAt: call.occurredAt,
|
|
15170
|
-
credentialMethod: turn.credentialMethod,
|
|
15171
|
-
credentialScope: turn.credentialScope
|
|
15172
|
-
};
|
|
15173
|
-
if (call.kind === "skill") turn.skills.push({ ...common, skillName: call.skillName });
|
|
15174
|
-
else turn.mcps.push({ ...common, mcpName: call.mcpName });
|
|
15175
|
-
}
|
|
15210
|
+
getActiveTurn(chatId) {
|
|
15211
|
+
return this.activeTurns.get(chatId);
|
|
15212
|
+
}
|
|
15213
|
+
getActiveChatIds() {
|
|
15214
|
+
return [...this.activeTurns.keys()];
|
|
15176
15215
|
}
|
|
15177
15216
|
noteTurnEnded(chatId) {
|
|
15178
15217
|
const turn = this.activeTurns.get(chatId);
|
|
@@ -15189,22 +15228,91 @@ var AnalyticsService = class {
|
|
|
15189
15228
|
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15190
15229
|
seconds: Math.round((Date.now() - turn.startedAtMs) / 1e3)
|
|
15191
15230
|
});
|
|
15192
|
-
for (const skill of turn.skills) this.skillBuffer.append(skill);
|
|
15193
|
-
for (const mcp of turn.mcps) this.mcpBuffer.append(mcp);
|
|
15194
15231
|
this.turnBuffer.scheduleFlush();
|
|
15195
|
-
|
|
15196
|
-
|
|
15232
|
+
}
|
|
15233
|
+
flush() {
|
|
15234
|
+
return this.turnBuffer.flush();
|
|
15235
|
+
}
|
|
15236
|
+
};
|
|
15237
|
+
async function uploadAgentChatActivity(endpoint, body) {
|
|
15238
|
+
const response = await monolithRequest(endpoint, { body });
|
|
15239
|
+
if (!response.ok)
|
|
15240
|
+
throw new Error(
|
|
15241
|
+
`upload failed: ${response.status} ${await response.text()}`
|
|
15242
|
+
);
|
|
15243
|
+
}
|
|
15244
|
+
var AgentChatActivityTrackerService = class {
|
|
15245
|
+
turnActivityTracker;
|
|
15246
|
+
skillActivityTracker;
|
|
15247
|
+
mcpActivityTracker;
|
|
15248
|
+
constructor() {
|
|
15249
|
+
this.turnActivityTracker = new AgentChatTurnActivityTracker(
|
|
15250
|
+
(turns) => uploadAgentChatActivity("/v1/engine/chat-turn-activity", { turns })
|
|
15251
|
+
);
|
|
15252
|
+
this.skillActivityTracker = new AgentChatActivityRecordTracker({
|
|
15253
|
+
storageName: "skill-activity",
|
|
15254
|
+
legacyStorageNames: ["skill-usage"],
|
|
15255
|
+
validate: isAgentChatSkillActivityRecord,
|
|
15256
|
+
upload: (skills) => uploadAgentChatActivity("/v1/engine/skill-activity", { skills })
|
|
15257
|
+
});
|
|
15258
|
+
this.mcpActivityTracker = new AgentChatActivityRecordTracker({
|
|
15259
|
+
storageName: "mcp-activity",
|
|
15260
|
+
legacyStorageNames: ["mcp-usage"],
|
|
15261
|
+
validate: isAgentChatMcpActivityRecord,
|
|
15262
|
+
upload: (mcps) => uploadAgentChatActivity("/v1/engine/mcp-activity", { mcps })
|
|
15263
|
+
});
|
|
15264
|
+
}
|
|
15265
|
+
noteMessageAccepted(messageId, request) {
|
|
15266
|
+
this.turnActivityTracker.noteMessageAccepted(messageId, request);
|
|
15267
|
+
}
|
|
15268
|
+
noteTurnStarted(chatId, messageId, provider) {
|
|
15269
|
+
this.turnActivityTracker.noteTurnStarted(chatId, messageId, provider);
|
|
15270
|
+
}
|
|
15271
|
+
noteSkillMcpCalls(chatId, messages) {
|
|
15272
|
+
const turn = this.turnActivityTracker.getActiveTurn(chatId);
|
|
15273
|
+
if (!turn) return;
|
|
15274
|
+
for (const call of extractSkillMcpCalls(messages, turn.provider)) {
|
|
15275
|
+
if (turn.seenCallIds.has(call.id)) continue;
|
|
15276
|
+
turn.seenCallIds.add(call.id);
|
|
15277
|
+
const activityRecord = {
|
|
15278
|
+
callId: call.id,
|
|
15279
|
+
chatId,
|
|
15280
|
+
provider: turn.provider,
|
|
15281
|
+
model: turn.model,
|
|
15282
|
+
senderUserId: turn.senderUserId,
|
|
15283
|
+
occurredAt: call.occurredAt,
|
|
15284
|
+
credentialMethod: turn.credentialMethod,
|
|
15285
|
+
credentialScope: turn.credentialScope
|
|
15286
|
+
};
|
|
15287
|
+
if (call.kind === "skill") {
|
|
15288
|
+
this.skillActivityTracker.track(chatId, {
|
|
15289
|
+
...activityRecord,
|
|
15290
|
+
skillName: call.skillName
|
|
15291
|
+
});
|
|
15292
|
+
} else {
|
|
15293
|
+
this.mcpActivityTracker.track(chatId, {
|
|
15294
|
+
...activityRecord,
|
|
15295
|
+
mcpName: call.mcpName
|
|
15296
|
+
});
|
|
15297
|
+
}
|
|
15298
|
+
}
|
|
15299
|
+
}
|
|
15300
|
+
noteTurnEnded(chatId) {
|
|
15301
|
+
this.skillActivityTracker.noteTurnEnded(chatId);
|
|
15302
|
+
this.mcpActivityTracker.noteTurnEnded(chatId);
|
|
15303
|
+
this.turnActivityTracker.noteTurnEnded(chatId);
|
|
15197
15304
|
}
|
|
15198
15305
|
async flush(finalizeInFlight = true) {
|
|
15199
15306
|
if (finalizeInFlight) {
|
|
15200
|
-
for (const chatId of this.
|
|
15307
|
+
for (const chatId of this.turnActivityTracker.getActiveChatIds())
|
|
15308
|
+
this.noteTurnEnded(chatId);
|
|
15201
15309
|
}
|
|
15202
|
-
const [
|
|
15203
|
-
this.
|
|
15204
|
-
this.
|
|
15205
|
-
this.
|
|
15310
|
+
const [turnActivity, skillActivity, mcpActivity] = await Promise.all([
|
|
15311
|
+
this.turnActivityTracker.flush(),
|
|
15312
|
+
this.skillActivityTracker.flush(),
|
|
15313
|
+
this.mcpActivityTracker.flush()
|
|
15206
15314
|
]);
|
|
15207
|
-
return {
|
|
15315
|
+
return { turnActivity, skillActivity, mcpActivity };
|
|
15208
15316
|
}
|
|
15209
15317
|
};
|
|
15210
15318
|
|
|
@@ -15490,7 +15598,7 @@ import { basename as basename2, join as join25 } from "path";
|
|
|
15490
15598
|
|
|
15491
15599
|
// src/services/chat/chat-senders.ts
|
|
15492
15600
|
import { join as join24 } from "path";
|
|
15493
|
-
var CHAT_SENDERS_DIR = join24(
|
|
15601
|
+
var CHAT_SENDERS_DIR = join24(ENGINE_DIR3, "chat-senders");
|
|
15494
15602
|
function chatMessageSendersFilePath(chatId) {
|
|
15495
15603
|
return join24(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
|
|
15496
15604
|
}
|
|
@@ -15508,9 +15616,9 @@ function parseChatMessageSendersJsonl(content) {
|
|
|
15508
15616
|
|
|
15509
15617
|
// src/services/upload-chat-transcripts.ts
|
|
15510
15618
|
var HISTORY_DIRS = [
|
|
15511
|
-
join25(
|
|
15512
|
-
join25(
|
|
15513
|
-
join25(
|
|
15619
|
+
join25(ENGINE_DIR3, "claude-histories"),
|
|
15620
|
+
join25(ENGINE_DIR3, "relay-histories"),
|
|
15621
|
+
join25(ENGINE_DIR3, "codex-histories")
|
|
15514
15622
|
];
|
|
15515
15623
|
async function putTranscript(uploadUrl, filePath, size) {
|
|
15516
15624
|
await new Promise((resolve5, reject) => {
|
|
@@ -15881,20 +15989,20 @@ function corruptChatsFilePath() {
|
|
|
15881
15989
|
return `${CHATS_FILE}.corrupt-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
15882
15990
|
}
|
|
15883
15991
|
var ChatService = class {
|
|
15884
|
-
constructor(workingDirectory,
|
|
15992
|
+
constructor(workingDirectory, agentChatActivityTrackerService2) {
|
|
15885
15993
|
this.workingDirectory = workingDirectory;
|
|
15886
|
-
this.
|
|
15994
|
+
this.agentChatActivityTrackerService = agentChatActivityTrackerService2;
|
|
15887
15995
|
keepAliveService.setActivityCheck(
|
|
15888
15996
|
() => [...this.chats.values()].some((chat) => !chat.persisted.deletedAt && hasActiveAgentWork(chat))
|
|
15889
15997
|
);
|
|
15890
15998
|
}
|
|
15891
15999
|
workingDirectory;
|
|
15892
|
-
|
|
16000
|
+
agentChatActivityTrackerService;
|
|
15893
16001
|
chats = /* @__PURE__ */ new Map();
|
|
15894
16002
|
persistInFlight = false;
|
|
15895
16003
|
persistQueued = false;
|
|
15896
16004
|
async initialize() {
|
|
15897
|
-
await mkdir15(
|
|
16005
|
+
await mkdir15(ENGINE_DIR3, { recursive: true });
|
|
15898
16006
|
await mkdir15(CLAUDE_HISTORY_DIR, { recursive: true });
|
|
15899
16007
|
await mkdir15(RELAY_HISTORY_DIR, { recursive: true });
|
|
15900
16008
|
await mkdir15(CODEX_HISTORY_DIR, { recursive: true });
|
|
@@ -16011,7 +16119,7 @@ var ChatService = class {
|
|
|
16011
16119
|
request.images
|
|
16012
16120
|
);
|
|
16013
16121
|
chat.pendingMessageIds.push(result.messageId);
|
|
16014
|
-
this.
|
|
16122
|
+
this.agentChatActivityTrackerService.noteMessageAccepted(result.messageId, request);
|
|
16015
16123
|
if (request.errorNotificationTarget) {
|
|
16016
16124
|
chat.errorNotificationTargets.set(result.messageId, request.errorNotificationTarget);
|
|
16017
16125
|
}
|
|
@@ -16067,7 +16175,7 @@ var ChatService = class {
|
|
|
16067
16175
|
async interrupt(chatId) {
|
|
16068
16176
|
const chat = this.requireChat(chatId);
|
|
16069
16177
|
const result = await chat.provider.interrupt();
|
|
16070
|
-
this.
|
|
16178
|
+
this.agentChatActivityTrackerService.noteTurnEnded(chatId);
|
|
16071
16179
|
chat.hasActiveTurn = false;
|
|
16072
16180
|
chat.activeMessageId = null;
|
|
16073
16181
|
chat.pendingMessageIds = [];
|
|
@@ -16089,7 +16197,7 @@ var ChatService = class {
|
|
|
16089
16197
|
return { interrupted: false, queue: [], goal: null };
|
|
16090
16198
|
}
|
|
16091
16199
|
const interruptResult = await chat.provider.interrupt();
|
|
16092
|
-
this.
|
|
16200
|
+
this.agentChatActivityTrackerService.noteTurnEnded(chatId);
|
|
16093
16201
|
chat.hasActiveTurn = false;
|
|
16094
16202
|
chat.activeMessageId = null;
|
|
16095
16203
|
chat.pendingMessageIds = [];
|
|
@@ -16176,7 +16284,7 @@ var ChatService = class {
|
|
|
16176
16284
|
messageId,
|
|
16177
16285
|
...chat.pendingMessageIds.filter((pendingMessageId) => pendingMessageId !== messageId && pendingMessageId !== interruptedMessageId)
|
|
16178
16286
|
];
|
|
16179
|
-
this.
|
|
16287
|
+
this.agentChatActivityTrackerService.noteTurnEnded(chatId);
|
|
16180
16288
|
chat.hasActiveTurn = false;
|
|
16181
16289
|
chat.activeMessageId = null;
|
|
16182
16290
|
if (interruptedMessageId) {
|
|
@@ -16310,14 +16418,22 @@ var ChatService = class {
|
|
|
16310
16418
|
const chatsById = new Map(
|
|
16311
16419
|
[...this.chats.entries()].map(([chatId, chat]) => [chatId, this.toSummary(chat)])
|
|
16312
16420
|
);
|
|
16313
|
-
const [chatTranscripts, canvas, repoState, engineLogs,
|
|
16421
|
+
const [chatTranscripts, canvas, repoState, engineLogs, chatActivity] = await Promise.all([
|
|
16314
16422
|
flushAllChatTranscripts(chatsById),
|
|
16315
16423
|
flushAllCanvasItems(),
|
|
16316
16424
|
flushRepoState(),
|
|
16317
16425
|
flushAllEngineLogs(),
|
|
16318
|
-
this.
|
|
16426
|
+
this.agentChatActivityTrackerService.flush()
|
|
16319
16427
|
]);
|
|
16320
|
-
return {
|
|
16428
|
+
return {
|
|
16429
|
+
chatTranscripts,
|
|
16430
|
+
canvas,
|
|
16431
|
+
repoState,
|
|
16432
|
+
engineLogs,
|
|
16433
|
+
turnActivity: chatActivity.turnActivity,
|
|
16434
|
+
skillActivity: chatActivity.skillActivity,
|
|
16435
|
+
mcpActivity: chatActivity.mcpActivity
|
|
16436
|
+
};
|
|
16321
16437
|
}
|
|
16322
16438
|
createRuntimeChat(persisted) {
|
|
16323
16439
|
const saveSession = async (sessionId) => {
|
|
@@ -16474,7 +16590,7 @@ var ChatService = class {
|
|
|
16474
16590
|
}
|
|
16475
16591
|
chat.hasActiveTurn = true;
|
|
16476
16592
|
chat.activeMessageId = messageId;
|
|
16477
|
-
this.
|
|
16593
|
+
this.agentChatActivityTrackerService.noteTurnStarted(chat.persisted.id, messageId, chat.persisted.provider);
|
|
16478
16594
|
chat.activeErrorNotificationTarget = chat.errorNotificationTargets.get(messageId) ?? null;
|
|
16479
16595
|
chat.errorNotificationTargets.delete(messageId);
|
|
16480
16596
|
this.publish({
|
|
@@ -16492,7 +16608,7 @@ var ChatService = class {
|
|
|
16492
16608
|
filter: false,
|
|
16493
16609
|
parentToolUseId: typeof event.payload.parent_tool_use_id === "string" ? event.payload.parent_tool_use_id : null
|
|
16494
16610
|
});
|
|
16495
|
-
this.
|
|
16611
|
+
this.agentChatActivityTrackerService.noteSkillMcpCalls(chatId, displayMessages);
|
|
16496
16612
|
}
|
|
16497
16613
|
const terminalErrors = terminalErrorsFromEvent(event, chat.persisted.provider, codexTranscript);
|
|
16498
16614
|
if (terminalErrors !== void 0) {
|
|
@@ -16556,7 +16672,7 @@ var ChatService = class {
|
|
|
16556
16672
|
}
|
|
16557
16673
|
chat.hasActiveTurn = false;
|
|
16558
16674
|
chat.activeMessageId = null;
|
|
16559
|
-
this.
|
|
16675
|
+
this.agentChatActivityTrackerService.noteTurnEnded(chatId);
|
|
16560
16676
|
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
16561
16677
|
this.publish({
|
|
16562
16678
|
type: "chat.turn.completed",
|
|
@@ -18592,8 +18708,8 @@ var authMiddleware = async (c, next) => {
|
|
|
18592
18708
|
}
|
|
18593
18709
|
await next();
|
|
18594
18710
|
};
|
|
18595
|
-
var
|
|
18596
|
-
var chatService = new ChatService(gitService.getWorkspaceRoot(),
|
|
18711
|
+
var agentChatActivityTrackerService = new AgentChatActivityTrackerService();
|
|
18712
|
+
var chatService = new ChatService(gitService.getWorkspaceRoot(), agentChatActivityTrackerService);
|
|
18597
18713
|
app.get("/health", async (c) => {
|
|
18598
18714
|
const requestedWaitMs = Number(c.req.query(ENGINE_HEALTH_WAIT_QUERY_PARAM));
|
|
18599
18715
|
const waitMs = Number.isFinite(requestedWaitMs) ? Math.min(Math.max(requestedWaitMs, 0), ENGINE_HEALTH_MAX_WAIT_MS) : 0;
|