@code-yeongyu/senpi 2026.5.29-4 → 2026.6.2
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/CHANGELOG.md +41 -1
- package/README.md +8 -2
- package/dist/cli/args.d.ts +1 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +13 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +9 -1
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +5 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +18 -2
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +3 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +9 -3
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +4 -2
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +13 -1
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +7 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +2 -0
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +29 -1
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +1 -0
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/provider-attribution.d.ts +4 -0
- package/dist/core/provider-attribution.d.ts.map +1 -0
- package/dist/core/provider-attribution.js +73 -0
- package/dist/core/provider-attribution.js.map +1 -0
- package/dist/core/provider-display-names.d.ts.map +1 -1
- package/dist/core/provider-display-names.js +1 -0
- package/dist/core/provider-display-names.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +8 -34
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +92 -68
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +7 -10
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +5 -7
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +6 -7
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts +5 -2
- package/dist/core/tools/render-utils.d.ts.map +1 -1
- package/dist/core/tools/render-utils.js +17 -1
- package/dist/core/tools/render-utils.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +5 -6
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +8 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +25 -1
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +3 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +39 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +1 -0
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +1 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/docs/extensions.md +35 -11
- package/docs/providers.md +2 -0
- package/docs/quickstart.md +1 -0
- package/docs/rpc.md +3 -2
- package/docs/session-format.md +1 -1
- package/docs/sessions.md +8 -0
- package/docs/settings.md +1 -1
- package/docs/terminal-setup.md +2 -0
- package/docs/tui.md +12 -3
- package/docs/usage.md +6 -1
- package/examples/extensions/custom-header.ts +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/index.ts +53 -2
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/doom-overlay/index.ts +1 -1
- package/examples/extensions/handoff.ts +1 -1
- package/examples/extensions/interactive-shell.ts +1 -1
- package/examples/extensions/overlay-qa-tests.ts +152 -81
- package/examples/extensions/qna.ts +1 -1
- package/examples/extensions/question.ts +1 -1
- package/examples/extensions/questionnaire.ts +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/snake.ts +1 -1
- package/examples/extensions/space-invaders.ts +1 -1
- package/examples/extensions/summarize.ts +1 -1
- package/examples/extensions/tic-tac-toe.ts +1 -1
- package/examples/extensions/todo.ts +1 -1
- package/examples/extensions/tools.ts +5 -0
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { randomBytes, randomUUID } from "crypto";
|
|
2
|
-
import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readdirSync,
|
|
3
|
-
import { readdir,
|
|
2
|
+
import { appendFileSync, closeSync, createReadStream, existsSync, mkdirSync, openSync, readdirSync, readSync, statSync, writeFileSync, } from "fs";
|
|
3
|
+
import { readdir, stat } from "fs/promises";
|
|
4
4
|
import { join, resolve } from "path";
|
|
5
|
+
import { createInterface } from "readline";
|
|
6
|
+
import { StringDecoder } from "string_decoder";
|
|
5
7
|
import { getAgentDir as getDefaultAgentDir, getSessionsDir } from "../config.js";
|
|
6
8
|
import { normalizePath, resolvePath } from "../utils/paths.js";
|
|
7
9
|
// Fork change: inlined UUIDv7 (upstream uses the `uuid` npm package). Keeps this
|
|
@@ -247,24 +249,52 @@ export function getDefaultSessionDir(cwd, agentDir = getDefaultAgentDir()) {
|
|
|
247
249
|
}
|
|
248
250
|
return sessionDir;
|
|
249
251
|
}
|
|
252
|
+
const SESSION_READ_BUFFER_SIZE = 1024 * 1024;
|
|
253
|
+
function parseSessionEntryLine(line) {
|
|
254
|
+
if (!line.trim())
|
|
255
|
+
return null;
|
|
256
|
+
try {
|
|
257
|
+
return JSON.parse(line);
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
// Skip malformed lines
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
250
264
|
/** Exported for testing */
|
|
251
265
|
export function loadEntriesFromFile(filePath) {
|
|
252
266
|
const resolvedFilePath = normalizePath(filePath);
|
|
253
267
|
if (!existsSync(resolvedFilePath))
|
|
254
268
|
return [];
|
|
255
|
-
const content = readFileSync(resolvedFilePath, "utf8");
|
|
256
269
|
const entries = [];
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
270
|
+
const fd = openSync(resolvedFilePath, "r");
|
|
271
|
+
try {
|
|
272
|
+
const decoder = new StringDecoder("utf8");
|
|
273
|
+
const buffer = Buffer.allocUnsafe(SESSION_READ_BUFFER_SIZE);
|
|
274
|
+
let pending = "";
|
|
275
|
+
while (true) {
|
|
276
|
+
const bytesRead = readSync(fd, buffer, 0, buffer.length, null);
|
|
277
|
+
if (bytesRead === 0)
|
|
278
|
+
break;
|
|
279
|
+
pending += decoder.write(buffer.subarray(0, bytesRead));
|
|
280
|
+
let lineStart = 0;
|
|
281
|
+
let newlineIndex = pending.indexOf("\n", lineStart);
|
|
282
|
+
while (newlineIndex !== -1) {
|
|
283
|
+
const entry = parseSessionEntryLine(pending.slice(lineStart, newlineIndex));
|
|
284
|
+
if (entry)
|
|
285
|
+
entries.push(entry);
|
|
286
|
+
lineStart = newlineIndex + 1;
|
|
287
|
+
newlineIndex = pending.indexOf("\n", lineStart);
|
|
288
|
+
}
|
|
289
|
+
pending = pending.slice(lineStart);
|
|
267
290
|
}
|
|
291
|
+
pending += decoder.end();
|
|
292
|
+
const finalEntry = parseSessionEntryLine(pending);
|
|
293
|
+
if (finalEntry)
|
|
294
|
+
entries.push(finalEntry);
|
|
295
|
+
}
|
|
296
|
+
finally {
|
|
297
|
+
closeSync(fd);
|
|
268
298
|
}
|
|
269
299
|
// Validate session header
|
|
270
300
|
if (entries.length === 0)
|
|
@@ -333,73 +363,53 @@ function extractTextContent(message) {
|
|
|
333
363
|
.map((block) => block.text)
|
|
334
364
|
.join(" ");
|
|
335
365
|
}
|
|
336
|
-
function
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
continue;
|
|
346
|
-
const msgTimestamp = message.timestamp;
|
|
347
|
-
if (typeof msgTimestamp === "number") {
|
|
348
|
-
lastActivityTime = Math.max(lastActivityTime ?? 0, msgTimestamp);
|
|
349
|
-
continue;
|
|
350
|
-
}
|
|
351
|
-
const entryTimestamp = entry.timestamp;
|
|
352
|
-
if (typeof entryTimestamp === "string") {
|
|
353
|
-
const t = new Date(entryTimestamp).getTime();
|
|
354
|
-
if (!Number.isNaN(t)) {
|
|
355
|
-
lastActivityTime = Math.max(lastActivityTime ?? 0, t);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
return lastActivityTime;
|
|
360
|
-
}
|
|
361
|
-
function getSessionModifiedDate(entries, header, statsMtime) {
|
|
362
|
-
const lastActivityTime = getLastActivityTime(entries);
|
|
363
|
-
if (typeof lastActivityTime === "number" && lastActivityTime > 0) {
|
|
364
|
-
return new Date(lastActivityTime);
|
|
366
|
+
function getMessageActivityTime(entry) {
|
|
367
|
+
const message = entry.message;
|
|
368
|
+
if (!isMessageWithContent(message))
|
|
369
|
+
return undefined;
|
|
370
|
+
if (message.role !== "user" && message.role !== "assistant")
|
|
371
|
+
return undefined;
|
|
372
|
+
const msgTimestamp = message.timestamp;
|
|
373
|
+
if (typeof msgTimestamp === "number") {
|
|
374
|
+
return msgTimestamp;
|
|
365
375
|
}
|
|
366
|
-
const
|
|
367
|
-
return
|
|
376
|
+
const t = new Date(entry.timestamp).getTime();
|
|
377
|
+
return Number.isNaN(t) ? undefined : t;
|
|
368
378
|
}
|
|
369
379
|
async function buildSessionInfo(filePath) {
|
|
370
380
|
try {
|
|
371
|
-
const content = await readFile(filePath, "utf8");
|
|
372
|
-
const entries = [];
|
|
373
|
-
const lines = content.trim().split("\n");
|
|
374
|
-
for (const line of lines) {
|
|
375
|
-
if (!line.trim())
|
|
376
|
-
continue;
|
|
377
|
-
try {
|
|
378
|
-
entries.push(JSON.parse(line));
|
|
379
|
-
}
|
|
380
|
-
catch {
|
|
381
|
-
// Skip malformed lines
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
if (entries.length === 0)
|
|
385
|
-
return null;
|
|
386
|
-
const header = entries[0];
|
|
387
|
-
if (header.type !== "session")
|
|
388
|
-
return null;
|
|
389
381
|
const stats = await stat(filePath);
|
|
382
|
+
let header = null;
|
|
390
383
|
let messageCount = 0;
|
|
391
384
|
let firstMessage = "";
|
|
392
385
|
const allMessages = [];
|
|
393
386
|
let name;
|
|
394
|
-
|
|
387
|
+
let lastActivityTime;
|
|
388
|
+
const rl = createInterface({
|
|
389
|
+
input: createReadStream(filePath, { encoding: "utf8" }),
|
|
390
|
+
crlfDelay: Infinity,
|
|
391
|
+
});
|
|
392
|
+
for await (const line of rl) {
|
|
393
|
+
const entry = parseSessionEntryLine(line);
|
|
394
|
+
if (!entry)
|
|
395
|
+
continue;
|
|
396
|
+
if (!header) {
|
|
397
|
+
if (entry.type !== "session")
|
|
398
|
+
return null;
|
|
399
|
+
header = entry;
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
395
402
|
// Extract session name (use latest, including explicit clears)
|
|
396
403
|
if (entry.type === "session_info") {
|
|
397
|
-
|
|
398
|
-
name = infoEntry.name?.trim() || undefined;
|
|
404
|
+
name = entry.name?.trim() || undefined;
|
|
399
405
|
}
|
|
400
406
|
if (entry.type !== "message")
|
|
401
407
|
continue;
|
|
402
408
|
messageCount++;
|
|
409
|
+
const activityTime = getMessageActivityTime(entry);
|
|
410
|
+
if (typeof activityTime === "number") {
|
|
411
|
+
lastActivityTime = Math.max(lastActivityTime ?? 0, activityTime);
|
|
412
|
+
}
|
|
403
413
|
const message = entry.message;
|
|
404
414
|
if (!isMessageWithContent(message))
|
|
405
415
|
continue;
|
|
@@ -413,9 +423,16 @@ async function buildSessionInfo(filePath) {
|
|
|
413
423
|
firstMessage = textContent;
|
|
414
424
|
}
|
|
415
425
|
}
|
|
426
|
+
if (!header)
|
|
427
|
+
return null;
|
|
416
428
|
const cwd = typeof header.cwd === "string" ? header.cwd : "";
|
|
417
429
|
const parentSessionPath = header.parentSession;
|
|
418
|
-
const
|
|
430
|
+
const headerTime = typeof header.timestamp === "string" ? new Date(header.timestamp).getTime() : NaN;
|
|
431
|
+
const modified = typeof lastActivityTime === "number" && lastActivityTime > 0
|
|
432
|
+
? new Date(lastActivityTime)
|
|
433
|
+
: !Number.isNaN(headerTime)
|
|
434
|
+
? new Date(headerTime)
|
|
435
|
+
: stats.mtime;
|
|
419
436
|
return {
|
|
420
437
|
path: filePath,
|
|
421
438
|
id: header.id,
|
|
@@ -608,8 +625,15 @@ export class SessionManager {
|
|
|
608
625
|
_rewriteFile() {
|
|
609
626
|
if (!this.persist || !this.sessionFile)
|
|
610
627
|
return;
|
|
611
|
-
const
|
|
612
|
-
|
|
628
|
+
const fd = openSync(this.sessionFile, "w");
|
|
629
|
+
try {
|
|
630
|
+
for (const entry of this.fileEntries) {
|
|
631
|
+
writeFileSync(fd, `${JSON.stringify(entry)}\n`);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
finally {
|
|
635
|
+
closeSync(fd);
|
|
636
|
+
}
|
|
613
637
|
}
|
|
614
638
|
isPersisted() {
|
|
615
639
|
return this.persist;
|