@cursor/july 0.1.51 → 0.1.53
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/AGENTS.md +1 -0
- package/README.md +4 -2
- package/dist/bin/agent-serve.js +2 -0
- package/dist/channels/slack/init.d.ts +16 -3
- package/dist/channels/slack/init.d.ts.map +1 -1
- package/dist/channels/slack/init.js +92 -18
- package/dist/channels/slack/manifest.d.ts +8 -0
- package/dist/channels/slack/manifest.d.ts.map +1 -1
- package/dist/channels/slack/manifest.js +17 -0
- package/dist/channels/slack/setup.d.ts +26 -1
- package/dist/channels/slack/setup.d.ts.map +1 -1
- package/dist/channels/slack/setup.js +107 -25
- package/dist/docs/404.html +3 -3
- package/dist/docs/ab.html +6 -6
- package/dist/docs/assets/{app.C51TX1Bw.js → app.DAKcDclS.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.C4uxzYEU.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.BB9HCJPq.js → VPLocalSearchBox.BNIaCSfG.js} +2 -2
- package/dist/docs/assets/chunks/{theme.Dp-0SZcu.js → theme.B47GzJjc.js} +2 -2
- package/dist/docs/assets/{guides_slack.md.D2nTRu9M.js → guides_slack.md.DP4H75WP.js} +8 -6
- package/dist/docs/assets/guides_slack.md.DP4H75WP.lean.js +1 -0
- package/dist/docs/assets/{reference_cli.md.CGKfz_Yj.js → reference_cli.md.-V6J7Cl8.js} +7 -3
- package/dist/docs/assets/{reference_cli.md.CGKfz_Yj.lean.js → reference_cli.md.-V6J7Cl8.lean.js} +1 -1
- package/dist/docs/assets/{style.CK3hwt6q.css → style.kTsvp4pE.css} +1 -1
- package/dist/docs/assets/{troubleshooting.md.C6n3wCr6.js → troubleshooting.md.CuVztCKs.js} +1 -1
- package/dist/docs/building-with-agents.html +6 -6
- package/dist/docs/concepts.html +6 -6
- package/dist/docs/deployment.html +6 -6
- package/dist/docs/evals.html +6 -6
- package/dist/docs/example-agents/approval-buddy.html +6 -6
- package/dist/docs/example-agents/benny.html +6 -6
- package/dist/docs/example-agents/bugbot.html +6 -6
- package/dist/docs/example-agents/codebase-wiki.html +6 -6
- package/dist/docs/example-agents/codeowners-review.html +6 -6
- package/dist/docs/example-agents/concierge.html +6 -6
- package/dist/docs/example-agents/fsd.html +6 -6
- package/dist/docs/example-agents/index.html +6 -6
- package/dist/docs/example-agents/knowledge-base.html +6 -6
- package/dist/docs/example-agents/oncall.html +6 -6
- package/dist/docs/example-agents/security-reviewer.html +6 -6
- package/dist/docs/example-agents/slack-agent.html +6 -6
- package/dist/docs/example-agents/weather-agent.html +6 -6
- package/dist/docs/guides/agent-to-agent.html +6 -6
- package/dist/docs/guides/cloud-runtime.html +6 -6
- package/dist/docs/guides/github.html +6 -6
- package/dist/docs/guides/human-in-the-loop.html +6 -6
- package/dist/docs/guides/mcp-oauth.html +6 -6
- package/dist/docs/guides/opentelemetry.html +6 -6
- package/dist/docs/guides/slack.html +14 -12
- package/dist/docs/guides/webhooks.html +6 -6
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +6 -6
- package/dist/docs/index.html +6 -6
- package/dist/docs/quickstart.html +6 -6
- package/dist/docs/reference/agent-config.html +6 -6
- package/dist/docs/reference/artifacts.html +6 -6
- package/dist/docs/reference/channels.html +6 -6
- package/dist/docs/reference/cli.html +13 -9
- package/dist/docs/reference/connections.html +6 -6
- package/dist/docs/reference/hooks.html +6 -6
- package/dist/docs/reference/http-api.html +6 -6
- package/dist/docs/reference/instructions.html +6 -6
- package/dist/docs/reference/playground.html +5 -5
- package/dist/docs/reference/project-layout.html +6 -6
- package/dist/docs/reference/prompt.html +6 -6
- package/dist/docs/reference/schedules.html +6 -6
- package/dist/docs/reference/sessions.html +6 -6
- package/dist/docs/reference/skills.html +6 -6
- package/dist/docs/reference/subagents.html +6 -6
- package/dist/docs/reference/tools.html +6 -6
- package/dist/docs/scaffolding-agents.html +5 -5
- package/dist/docs/storage.html +6 -6
- package/dist/docs/templates/triage.html +6 -6
- package/dist/docs/troubleshooting.html +6 -6
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +8 -18
- package/dist/internal/cli-slack-manual.d.ts +20 -0
- package/dist/internal/cli-slack-manual.d.ts.map +1 -0
- package/dist/internal/cli-slack-manual.js +106 -0
- package/dist/internal/cli-slack.d.ts +5 -2
- package/dist/internal/cli-slack.d.ts.map +1 -1
- package/dist/internal/cli-slack.js +18 -69
- package/dist/internal/deploy-client.d.ts +7 -10
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +1 -3
- package/dist/internal/deploy-source.d.ts +1 -1
- package/dist/internal/deploy-source.js +1 -1
- package/dist/internal/event-log.d.ts +37 -17
- package/dist/internal/event-log.d.ts.map +1 -1
- package/dist/internal/event-log.js +189 -55
- package/dist/internal/process-memory-telemetry.d.ts +3 -4
- package/dist/internal/process-memory-telemetry.d.ts.map +1 -1
- package/dist/internal/process-memory-telemetry.js +4 -17
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +2 -2
- package/dist/playground/assets/{index-BKMz70wx.js → index-BHAPd9KT.js} +31 -31
- package/dist/playground/assets/index-DpKH0HcT.css +1 -0
- package/dist/playground/index.html +2 -2
- package/docs/guides/slack.md +52 -7
- package/docs/reference/cli.md +29 -6
- package/docs/troubleshooting.md +1 -1
- package/package.json +1 -1
- package/skills/create-agent/SKILL.md +1 -0
- package/skills/setup-slack/SKILL.md +99 -7
- package/src/bin/agent-serve.ts +2 -0
- package/src/channels/slack/init.ts +130 -20
- package/src/channels/slack/manifest.ts +27 -0
- package/src/channels/slack/setup.ts +124 -23
- package/src/internal/cli-deploy.ts +8 -22
- package/src/internal/cli-slack-manual.ts +127 -0
- package/src/internal/cli-slack.ts +18 -70
- package/src/internal/deploy-client.ts +8 -13
- package/src/internal/deploy-source.ts +1 -1
- package/src/internal/event-log.ts +167 -52
- package/src/internal/process-memory-telemetry.ts +5 -24
- package/src/internal/session-engine.ts +4 -2
- package/dist/docs/assets/chunks/@localSearchIndexroot.D2DpXdZN.js +0 -1
- package/dist/docs/assets/guides_slack.md.D2nTRu9M.lean.js +0 -1
- package/dist/playground/assets/index-BmMrulSc.css +0 -1
- /package/dist/docs/assets/{troubleshooting.md.C6n3wCr6.lean.js → troubleshooting.md.CuVztCKs.lean.js} +0 -0
|
@@ -1,20 +1,83 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Durable, replayable per-session event log.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* Durable, replayable per-session event log. `events.ndjson` is the source
|
|
3
|
+
* of truth. The process keeps `nextIndex`, live subscribers, and a short
|
|
4
|
+
* unflushed tail — not a cache of the full transcript. Replay reads the
|
|
5
|
+
* file on demand; live streams subscribe first, then catch up by index.
|
|
6
|
+
*
|
|
7
|
+
* Hosted engines mint thousands of sessions (one per PR under
|
|
8
|
+
* agentic-owners). Retaining every streamed tool output in RAM for the
|
|
9
|
+
* process lifetime is what OOM'd those engines at the ~4 GB heap cap.
|
|
6
10
|
*/
|
|
7
11
|
|
|
8
|
-
import { appendFile, mkdir,
|
|
12
|
+
import { appendFile, type FileHandle, mkdir, open } from "node:fs/promises";
|
|
9
13
|
import { dirname } from "node:path";
|
|
14
|
+
import { createInterface } from "node:readline";
|
|
10
15
|
import type { SessionEvent, SessionEventPayload } from "../types.js";
|
|
11
16
|
|
|
12
17
|
export type AppendableEvent = SessionEventPayload & { turnId?: string };
|
|
13
18
|
|
|
19
|
+
function isEnoent(error: unknown): boolean {
|
|
20
|
+
return (
|
|
21
|
+
typeof error === "object" &&
|
|
22
|
+
error !== null &&
|
|
23
|
+
"code" in error &&
|
|
24
|
+
(error as { code: unknown }).code === "ENOENT"
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function parseEventLine(line: string): SessionEvent | undefined {
|
|
29
|
+
const trimmed = line.trim();
|
|
30
|
+
if (trimmed === "") {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
return JSON.parse(trimmed) as SessionEvent;
|
|
35
|
+
} catch {
|
|
36
|
+
// Skip torn trailing writes from a crashed process.
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function readNdjsonEvents(
|
|
42
|
+
filePath: string,
|
|
43
|
+
onEvent: (event: SessionEvent) => void
|
|
44
|
+
): Promise<void> {
|
|
45
|
+
let fh: FileHandle;
|
|
46
|
+
try {
|
|
47
|
+
fh = await open(filePath, "r");
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (isEnoent(error)) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
const stream = fh.createReadStream({ encoding: "utf8" });
|
|
55
|
+
const rl = createInterface({ input: stream, crlfDelay: Infinity });
|
|
56
|
+
try {
|
|
57
|
+
for await (const line of rl) {
|
|
58
|
+
const event = parseEventLine(line);
|
|
59
|
+
if (event !== undefined) {
|
|
60
|
+
onEvent(event);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
} finally {
|
|
64
|
+
rl.close();
|
|
65
|
+
stream.destroy();
|
|
66
|
+
await fh.close().catch(() => {});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
14
70
|
export class SessionEventLog {
|
|
15
|
-
private
|
|
71
|
+
private nextIndex = 0;
|
|
16
72
|
private readonly listeners = new Set<(event: SessionEvent) => void>();
|
|
17
73
|
private persistChain: Promise<void> = Promise.resolve();
|
|
74
|
+
/** Appended events whose `appendFile` has not finished. */
|
|
75
|
+
private readonly inflight: SessionEvent[] = [];
|
|
76
|
+
/**
|
|
77
|
+
* In-process patches (cloud cost backfill) that are not rewritten to
|
|
78
|
+
* `events.ndjson`. Applied on read.
|
|
79
|
+
*/
|
|
80
|
+
private readonly replacements = new Map<number, SessionEvent>();
|
|
18
81
|
|
|
19
82
|
private constructor(
|
|
20
83
|
private readonly sessionId: string,
|
|
@@ -32,59 +95,52 @@ export class SessionEventLog {
|
|
|
32
95
|
args.filePath,
|
|
33
96
|
args.onPersistError ?? (() => {})
|
|
34
97
|
);
|
|
35
|
-
await log.
|
|
98
|
+
await log.readNextIndex();
|
|
36
99
|
return log;
|
|
37
100
|
}
|
|
38
101
|
|
|
39
|
-
private async
|
|
40
|
-
let
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
for (const line of raw.split("\n")) {
|
|
47
|
-
if (line.trim() === "") {
|
|
48
|
-
continue;
|
|
102
|
+
private async readNextIndex(): Promise<void> {
|
|
103
|
+
let next = 0;
|
|
104
|
+
await readNdjsonEvents(this.filePath, (event) => {
|
|
105
|
+
if (typeof event.index === "number" && event.index >= next) {
|
|
106
|
+
next = event.index + 1;
|
|
49
107
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
} catch {
|
|
53
|
-
// Skip torn trailing writes from a crashed process.
|
|
54
|
-
}
|
|
55
|
-
}
|
|
108
|
+
});
|
|
109
|
+
this.nextIndex = next;
|
|
56
110
|
}
|
|
57
111
|
|
|
58
112
|
get count(): number {
|
|
59
|
-
return this.
|
|
113
|
+
return this.nextIndex;
|
|
60
114
|
}
|
|
61
115
|
|
|
62
116
|
/**
|
|
63
|
-
* In-process residency vs recorded length.
|
|
64
|
-
* (`
|
|
65
|
-
*
|
|
66
|
-
* the unflushed tail.
|
|
117
|
+
* In-process residency vs recorded length. `eventsTotal` is the durable
|
|
118
|
+
* count (`nextIndex`). `eventsRetained` is the unflushed tail — the
|
|
119
|
+
* transcript is not cached after `appendFile` lands.
|
|
67
120
|
*/
|
|
68
121
|
memoryStats(): { eventsRetained: number; eventsTotal: number } {
|
|
69
|
-
|
|
70
|
-
|
|
122
|
+
return {
|
|
123
|
+
eventsRetained: this.inflight.length,
|
|
124
|
+
eventsTotal: this.nextIndex,
|
|
125
|
+
};
|
|
71
126
|
}
|
|
72
127
|
|
|
73
128
|
/**
|
|
74
|
-
* Assign the next index
|
|
75
|
-
*
|
|
76
|
-
*
|
|
129
|
+
* Assign the next index and fan out to live subscribers. Ordering is
|
|
130
|
+
* synchronous; file persistence is chained in the background and awaited
|
|
131
|
+
* by {@link flush}. The event is not retained after the write lands.
|
|
77
132
|
*/
|
|
78
133
|
append(payload: AppendableEvent): SessionEvent {
|
|
79
134
|
const { turnId, ...rest } = payload;
|
|
80
135
|
const event = {
|
|
81
|
-
index: this.
|
|
136
|
+
index: this.nextIndex,
|
|
82
137
|
sessionId: this.sessionId,
|
|
83
138
|
...(turnId === undefined ? {} : { turnId }),
|
|
84
139
|
at: new Date().toISOString(),
|
|
85
140
|
...rest,
|
|
86
141
|
} as SessionEvent;
|
|
87
|
-
this.
|
|
142
|
+
this.nextIndex += 1;
|
|
143
|
+
this.inflight.push(event);
|
|
88
144
|
for (const listener of [...this.listeners]) {
|
|
89
145
|
try {
|
|
90
146
|
listener(event);
|
|
@@ -93,35 +149,67 @@ export class SessionEventLog {
|
|
|
93
149
|
}
|
|
94
150
|
}
|
|
95
151
|
const line = `${JSON.stringify(event)}\n`;
|
|
152
|
+
const index = event.index;
|
|
96
153
|
this.persistChain = this.persistChain
|
|
97
154
|
.then(() => appendFile(this.filePath, line, "utf8"))
|
|
155
|
+
.then(() => {
|
|
156
|
+
this.dropInflight(index);
|
|
157
|
+
})
|
|
98
158
|
.catch((error) => this.onPersistError(error));
|
|
99
159
|
return event;
|
|
100
160
|
}
|
|
101
161
|
|
|
162
|
+
private dropInflight(index: number): void {
|
|
163
|
+
if (this.inflight[0]?.index === index) {
|
|
164
|
+
this.inflight.shift();
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const i = this.inflight.findIndex((event) => event.index === index);
|
|
168
|
+
if (i >= 0) {
|
|
169
|
+
this.inflight.splice(i, 1);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
102
173
|
/** Wait for pending file writes (used on close). */
|
|
103
174
|
async flush(): Promise<void> {
|
|
104
175
|
await this.persistChain;
|
|
105
176
|
}
|
|
106
177
|
|
|
107
|
-
|
|
108
|
-
|
|
178
|
+
/**
|
|
179
|
+
* Read recorded events from `startIndex`. Does not retain the result on
|
|
180
|
+
* the log; callers own the returned array.
|
|
181
|
+
*/
|
|
182
|
+
async snapshot(startIndex = 0): Promise<SessionEvent[]> {
|
|
183
|
+
const byIndex = new Map<number, SessionEvent>();
|
|
184
|
+
await readNdjsonEvents(this.filePath, (event) => {
|
|
185
|
+
if (event.index >= startIndex) {
|
|
186
|
+
byIndex.set(event.index, event);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
for (const event of this.inflight) {
|
|
190
|
+
if (event.index >= startIndex) {
|
|
191
|
+
byIndex.set(event.index, event);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
for (const [index, event] of this.replacements) {
|
|
195
|
+
if (index >= startIndex) {
|
|
196
|
+
byIndex.set(index, event);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return [...byIndex.values()].sort((a, b) => a.index - b.index);
|
|
109
200
|
}
|
|
110
201
|
|
|
111
202
|
/**
|
|
112
|
-
* Replace one already-recorded event in
|
|
113
|
-
* onto historical `turn.completed` rows without rewriting
|
|
114
|
-
* Live subscribers are not notified — patch before
|
|
203
|
+
* Replace one already-recorded event in this process. Used to backfill
|
|
204
|
+
* usage/cost onto historical `turn.completed` rows without rewriting
|
|
205
|
+
* `events.ndjson`. Live subscribers are not notified — patch before
|
|
206
|
+
* opening a replay stream.
|
|
115
207
|
*/
|
|
116
208
|
replace(index: number, event: SessionEvent): boolean {
|
|
117
|
-
if (index < 0 || index >= this.
|
|
209
|
+
if (index < 0 || index >= this.nextIndex || event.index !== index) {
|
|
118
210
|
return false;
|
|
119
211
|
}
|
|
120
|
-
|
|
121
|
-
if (current === undefined || current.index !== index) {
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
124
|
-
this.events[index] = event;
|
|
212
|
+
this.replacements.set(index, event);
|
|
125
213
|
return true;
|
|
126
214
|
}
|
|
127
215
|
|
|
@@ -135,12 +223,16 @@ export class SessionEventLog {
|
|
|
135
223
|
/**
|
|
136
224
|
* NDJSON stream: replays recorded events from `startIndex`, then follows
|
|
137
225
|
* live events until the consumer cancels.
|
|
226
|
+
*
|
|
227
|
+
* Subscribe first (buffer live events), then read the file, then drain
|
|
228
|
+
* the buffer by index so an async file read cannot drop or duplicate
|
|
229
|
+
* events that arrive during catch-up.
|
|
138
230
|
*/
|
|
139
231
|
stream(startIndex = 0): ReadableStream<Uint8Array> {
|
|
140
232
|
const encoder = new TextEncoder();
|
|
141
233
|
let unsubscribe: (() => void) | undefined;
|
|
142
234
|
return new ReadableStream<Uint8Array>({
|
|
143
|
-
start: (controller) => {
|
|
235
|
+
start: async (controller) => {
|
|
144
236
|
const push = (event: SessionEvent): void => {
|
|
145
237
|
try {
|
|
146
238
|
controller.enqueue(encoder.encode(`${JSON.stringify(event)}\n`));
|
|
@@ -148,15 +240,38 @@ export class SessionEventLog {
|
|
|
148
240
|
unsubscribe?.();
|
|
149
241
|
}
|
|
150
242
|
};
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
push(event);
|
|
154
|
-
}
|
|
243
|
+
let following = false;
|
|
244
|
+
const buffered: SessionEvent[] = [];
|
|
155
245
|
unsubscribe = this.subscribe((event) => {
|
|
156
|
-
if (event.index
|
|
246
|
+
if (event.index < startIndex) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (following) {
|
|
157
250
|
push(event);
|
|
251
|
+
} else {
|
|
252
|
+
buffered.push(event);
|
|
158
253
|
}
|
|
159
254
|
});
|
|
255
|
+
try {
|
|
256
|
+
const prefix = await this.snapshot(startIndex);
|
|
257
|
+
let maxSeen = startIndex - 1;
|
|
258
|
+
for (const event of prefix) {
|
|
259
|
+
push(event);
|
|
260
|
+
if (event.index > maxSeen) {
|
|
261
|
+
maxSeen = event.index;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
following = true;
|
|
265
|
+
for (const event of buffered) {
|
|
266
|
+
if (event.index > maxSeen) {
|
|
267
|
+
push(event);
|
|
268
|
+
maxSeen = event.index;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
} catch (error) {
|
|
272
|
+
unsubscribe?.();
|
|
273
|
+
controller.error(error);
|
|
274
|
+
}
|
|
160
275
|
},
|
|
161
276
|
cancel: () => {
|
|
162
277
|
unsubscribe?.();
|
|
@@ -8,9 +8,8 @@
|
|
|
8
8
|
* An optional in-process sampler (`AGENT_SDK_MEMORY_METRICS=1`) still logs
|
|
9
9
|
* and, if a Datadog key happens to be in the pod, posts directly.
|
|
10
10
|
*
|
|
11
|
-
* `event_log.
|
|
12
|
-
*
|
|
13
|
-
* are equal today; a later consume-and-drop change should split them.
|
|
11
|
+
* Event-log residency is `event_log.resident_sessions` (open session
|
|
12
|
+
* map). Transcripts live on disk; unflushed tails are not a scrape gauge.
|
|
14
13
|
*/
|
|
15
14
|
|
|
16
15
|
import { getHeapStatistics } from "node:v8";
|
|
@@ -102,7 +101,7 @@ export function sampleProcessMemory(
|
|
|
102
101
|
|
|
103
102
|
export type MemoryHealthFields = {
|
|
104
103
|
memory: ProcessMemorySample & { heapUsedRatio: number };
|
|
105
|
-
eventLog: EventLogMemoryStats
|
|
104
|
+
eventLog: Pick<EventLogMemoryStats, "residentSessions">;
|
|
106
105
|
};
|
|
107
106
|
|
|
108
107
|
export function heapUsedRatio(memory: ProcessMemorySample): number {
|
|
@@ -132,7 +131,7 @@ export function buildMemoryHealthFields(
|
|
|
132
131
|
): MemoryHealthFields {
|
|
133
132
|
return {
|
|
134
133
|
memory: { ...sample, heapUsedRatio: heapUsedRatio(sample) },
|
|
135
|
-
eventLog,
|
|
134
|
+
eventLog: { residentSessions: eventLog.residentSessions },
|
|
136
135
|
};
|
|
137
136
|
}
|
|
138
137
|
|
|
@@ -167,9 +166,7 @@ export function formatMemoryLogLine(args: {
|
|
|
167
166
|
` heap_limit_mb=${mb(args.memory.heapLimitBytes)}` +
|
|
168
167
|
` heap_used_ratio=${ratio.toFixed(3)}` +
|
|
169
168
|
` rss_mb=${mb(args.memory.rssBytes)}` +
|
|
170
|
-
` event_log.sessions=${args.eventLog.residentSessions}`
|
|
171
|
-
` event_log.retained=${args.eventLog.eventsRetained}` +
|
|
172
|
-
` event_log.total=${args.eventLog.eventsTotal}`
|
|
169
|
+
` event_log.sessions=${args.eventLog.residentSessions}`
|
|
173
170
|
);
|
|
174
171
|
}
|
|
175
172
|
|
|
@@ -209,14 +206,6 @@ export function buildDatadogGaugeSeries(args: {
|
|
|
209
206
|
name: `${EVENT_LOG_METRIC_PREFIX}.resident_sessions`,
|
|
210
207
|
value: args.eventLog.residentSessions,
|
|
211
208
|
},
|
|
212
|
-
{
|
|
213
|
-
name: `${EVENT_LOG_METRIC_PREFIX}.events_retained`,
|
|
214
|
-
value: args.eventLog.eventsRetained,
|
|
215
|
-
},
|
|
216
|
-
{
|
|
217
|
-
name: `${EVENT_LOG_METRIC_PREFIX}.events_total`,
|
|
218
|
-
value: args.eventLog.eventsTotal,
|
|
219
|
-
},
|
|
220
209
|
];
|
|
221
210
|
return points.map((point) => ({
|
|
222
211
|
metric: point.name,
|
|
@@ -300,14 +289,6 @@ function recordOtel(
|
|
|
300
289
|
memory.rssBytes,
|
|
301
290
|
attributes
|
|
302
291
|
);
|
|
303
|
-
histogram(meter, `${EVENT_LOG_METRIC_PREFIX}.events_retained`).record(
|
|
304
|
-
eventLog.eventsRetained,
|
|
305
|
-
attributes
|
|
306
|
-
);
|
|
307
|
-
histogram(meter, `${EVENT_LOG_METRIC_PREFIX}.events_total`).record(
|
|
308
|
-
eventLog.eventsTotal,
|
|
309
|
-
attributes
|
|
310
|
-
);
|
|
311
292
|
histogram(meter, `${EVENT_LOG_METRIC_PREFIX}.resident_sessions`).record(
|
|
312
293
|
eventLog.residentSessions,
|
|
313
294
|
attributes
|
|
@@ -3014,7 +3014,7 @@ export class SessionEngine {
|
|
|
3014
3014
|
|
|
3015
3015
|
private async runCloudTurnCostBackfill(sessionId: string): Promise<void> {
|
|
3016
3016
|
const log = await this.logs.get(sessionId);
|
|
3017
|
-
const events = log.snapshot(0);
|
|
3017
|
+
const events = await log.snapshot(0);
|
|
3018
3018
|
if (!events.some((event) => isUnpricedCompletedTurn(event))) {
|
|
3019
3019
|
if (
|
|
3020
3020
|
!this.isTurnRunning(sessionId) &&
|
|
@@ -3049,7 +3049,9 @@ export class SessionEngine {
|
|
|
3049
3049
|
}
|
|
3050
3050
|
log.replace(i, next);
|
|
3051
3051
|
}
|
|
3052
|
-
if (
|
|
3052
|
+
if (
|
|
3053
|
+
!(await log.snapshot(0)).some((event) => isUnpricedCompletedTurn(event))
|
|
3054
|
+
) {
|
|
3053
3055
|
this.cloudCostBackfillDone.add(sessionId);
|
|
3054
3056
|
}
|
|
3055
3057
|
}
|