doer-agent 0.5.3 → 0.5.5

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.
@@ -97,12 +97,12 @@ function truncateSessionRpcString(value) {
97
97
  const omittedChars = value.length - SESSION_RPC_MAX_STRING_CHARS;
98
98
  return `${value.slice(0, SESSION_RPC_MAX_STRING_CHARS)}\n[truncated ${omittedChars} chars for session RPC]`;
99
99
  }
100
- function sanitizeSessionRpcPayload(value) {
100
+ function sanitizeSessionRpcPayload(value, options = {}) {
101
101
  if (typeof value === "string") {
102
- return truncateSessionRpcString(value);
102
+ return options.truncateStrings ? truncateSessionRpcString(value) : value;
103
103
  }
104
104
  if (Array.isArray(value)) {
105
- return value.map((entry) => sanitizeSessionRpcPayload(entry));
105
+ return value.map((entry) => sanitizeSessionRpcPayload(entry, options));
106
106
  }
107
107
  if (!isObjectRecord(value)) {
108
108
  return value;
@@ -113,37 +113,31 @@ function sanitizeSessionRpcPayload(value) {
113
113
  sanitized[key] = buildInlineBlobMarker(entry);
114
114
  continue;
115
115
  }
116
- sanitized[key] = sanitizeSessionRpcPayload(entry);
116
+ sanitized[key] = sanitizeSessionRpcPayload(entry, options);
117
117
  }
118
118
  return sanitized;
119
119
  }
120
120
  function sanitizeSessionRpcRawLine(line) {
121
121
  const trimmed = line.trim();
122
122
  if (!trimmed.startsWith("{")) {
123
- return truncateSessionRpcString(line);
123
+ return line;
124
124
  }
125
125
  try {
126
126
  const parsed = JSON.parse(line);
127
127
  if (!isObjectRecord(parsed)) {
128
- return truncateSessionRpcString(line);
128
+ return line;
129
129
  }
130
130
  if (parsed.type === "compacted" || parsed.type === "turn_context" || parsed.type === "session_meta") {
131
131
  return JSON.stringify({
132
132
  ...parsed,
133
- payload: sanitizeSessionRpcPayload(parsed.payload),
133
+ payload: sanitizeSessionRpcPayload(parsed.payload, { truncateStrings: true }),
134
134
  });
135
135
  }
136
136
  if (!isObjectRecord(parsed.payload)) {
137
- return JSON.stringify({
138
- ...parsed,
139
- payload: sanitizeSessionRpcPayload(parsed.payload),
140
- });
137
+ return line;
141
138
  }
142
139
  if (parsed.type !== "response_item") {
143
- return JSON.stringify({
144
- ...parsed,
145
- payload: sanitizeSessionRpcPayload(parsed.payload),
146
- });
140
+ return line;
147
141
  }
148
142
  const payloadType = typeof parsed.payload.type === "string" ? parsed.payload.type : "";
149
143
  if (payloadType === "message" || payloadType === "reasoning") {
@@ -162,7 +156,7 @@ function sanitizeSessionRpcRawLine(line) {
162
156
  });
163
157
  }
164
158
  catch {
165
- return truncateSessionRpcString(line);
159
+ return line;
166
160
  }
167
161
  }
168
162
  function toTrimmedStringOrNull(value) {
@@ -181,6 +175,10 @@ function pickSessionString(...values) {
181
175
  }
182
176
  return null;
183
177
  }
178
+ function toSortableTimestampMs(value) {
179
+ const parsed = Date.parse(value);
180
+ return Number.isFinite(parsed) ? parsed : 0;
181
+ }
184
182
  export async function collectSessionJsonlFiles(workspaceRoot) {
185
183
  const out = [];
186
184
  const stack = [getSessionsRootPath(workspaceRoot)];
@@ -239,8 +237,14 @@ async function readFirstLine(fileHandle, fileSize) {
239
237
  }
240
238
  return raw.trim();
241
239
  }
242
- function extractLastAgentMessage(candidateLines) {
243
- let fallback = null;
240
+ function normalizeSessionSummaryMessage(value) {
241
+ const message = toTrimmedStringOrNull(value);
242
+ if (!message || message.toLowerCase() === "empty") {
243
+ return null;
244
+ }
245
+ return message;
246
+ }
247
+ function extractLastSessionMessage(candidateLines) {
244
248
  for (const line of candidateLines) {
245
249
  const trimmed = line.trim();
246
250
  if (!trimmed) {
@@ -251,18 +255,13 @@ function extractLastAgentMessage(candidateLines) {
251
255
  if (parsed.type !== "event_msg" || !isObjectRecord(parsed.payload)) {
252
256
  continue;
253
257
  }
254
- if (parsed.payload.type === "agent_message" && typeof parsed.payload.message === "string" && parsed.payload.message.trim()) {
255
- return {
256
- message: parsed.payload.message.trim(),
257
- updatedAt: toTrimmedStringOrNull(parsed.timestamp),
258
- };
258
+ if (parsed.payload.type !== "agent_message" && parsed.payload.type !== "user_message") {
259
+ continue;
259
260
  }
260
- if (!fallback &&
261
- parsed.payload.type === "task_complete" &&
262
- typeof parsed.payload.last_agent_message === "string" &&
263
- parsed.payload.last_agent_message.trim()) {
264
- fallback = {
265
- message: parsed.payload.last_agent_message.trim(),
261
+ const message = normalizeSessionSummaryMessage(parsed.payload.message);
262
+ if (message) {
263
+ return {
264
+ message,
266
265
  updatedAt: toTrimmedStringOrNull(parsed.timestamp),
267
266
  };
268
267
  }
@@ -271,9 +270,9 @@ function extractLastAgentMessage(candidateLines) {
271
270
  // ignore malformed lines
272
271
  }
273
272
  }
274
- return fallback;
273
+ return null;
275
274
  }
276
- async function readLastAgentMessage(fileHandle, fileSize) {
275
+ async function readLastSessionMessage(fileHandle, fileSize) {
277
276
  const chunkBytes = 16_384;
278
277
  const maxScanBytes = 131_072;
279
278
  if (fileSize <= 0) {
@@ -294,12 +293,12 @@ async function readLastAgentMessage(fileHandle, fileSize) {
294
293
  const merged = buffer.toString("utf8", 0, bytesRead) + carry;
295
294
  const lines = merged.split(/\r?\n/);
296
295
  carry = lines.shift() || "";
297
- const found = extractLastAgentMessage(lines.reverse());
296
+ const found = extractLastSessionMessage(lines.reverse());
298
297
  if (found) {
299
298
  return found;
300
299
  }
301
300
  }
302
- return extractLastAgentMessage([carry]);
301
+ return extractLastSessionMessage([carry]);
303
302
  }
304
303
  function normalizeSessionMeta(rawMeta, filePath, mtimeMs) {
305
304
  const baseName = path.basename(filePath, path.extname(filePath));
@@ -321,7 +320,7 @@ async function readSessionSummary(filePath, mtimeMs) {
321
320
  fileHandle = await open(filePath, "r");
322
321
  const entryStat = await fileHandle.stat();
323
322
  const firstLine = await readFirstLine(fileHandle, entryStat.size);
324
- const tailSummary = await readLastAgentMessage(fileHandle, entryStat.size);
323
+ const tailSummary = await readLastSessionMessage(fileHandle, entryStat.size);
325
324
  let normalized = normalizeSessionMeta({}, filePath, mtimeMs);
326
325
  if (firstLine) {
327
326
  try {
@@ -371,7 +370,7 @@ async function listAgentSessions(workspaceRoot) {
371
370
  const files = await collectSessionJsonlFiles(workspaceRoot);
372
371
  files.sort((a, b) => b.mtimeMs - a.mtimeMs || a.filePath.localeCompare(b.filePath));
373
372
  const sessions = await Promise.all(files.slice(0, 10).map((file) => readSessionSummary(file.filePath, file.mtimeMs)));
374
- return sessions.sort((a, b) => Date.parse(b.updatedAt) - Date.parse(a.updatedAt) || b.filePath.localeCompare(a.filePath));
373
+ return sessions.sort((a, b) => toSortableTimestampMs(b.updatedAt) - toSortableTimestampMs(a.updatedAt) || b.filePath.localeCompare(a.filePath));
375
374
  }
376
375
  async function readSessionLineIndex(workspaceRoot, filePath) {
377
376
  const resolvedFile = resolveSessionFilePath(workspaceRoot, filePath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doer-agent",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "description": "Reverse-polling agent runtime for doer",
5
5
  "type": "module",
6
6
  "main": "dist/agent.js",