@1agents/session-reader 0.6.1 → 0.7.0
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 +42 -0
- package/cordis.patch.yml +4 -0
- package/dist/bin/1session.js +26 -1
- package/dist/src/dsh/adapter.d.ts +18 -0
- package/dist/src/dsh/adapter.js +198 -0
- package/dist/src/dsh/client.d.ts +2 -0
- package/dist/src/dsh/client.js +18997 -0
- package/dist/src/dsh/index.d.ts +2 -0
- package/dist/src/dsh/index.js +2 -0
- package/dist/src/dsh/plugin.d.ts +3 -0
- package/dist/src/dsh/plugin.js +392 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +2 -0
- package/dist/src/resolver.d.ts +7 -0
- package/dist/src/resolver.js +13 -8
- package/dist/src/serve/http.js +13 -1
- package/dist/src/store/nul.d.ts +12 -0
- package/dist/src/store/nul.js +52 -0
- package/dist/src/store/read.d.ts +6 -2
- package/dist/src/store/read.js +18 -15
- package/dist/src/store/schema.d.ts +1 -1
- package/dist/src/store/schema.js +1 -1
- package/dist/src/store/write.js +3 -2
- package/dist/src/web/api.d.ts +72 -0
- package/dist/src/web/api.js +126 -0
- package/dist/src/web/app.d.ts +1 -0
- package/dist/src/web/app.js +18975 -0
- package/dist/src/web/server.d.ts +20 -0
- package/dist/src/web/server.js +126 -0
- package/package.json +25 -4
- package/skills/1session/SKILL.md +46 -8
- package/skills/1session/evals/evals.json +43 -1
- package/skills/1session/references/cli.md +16 -0
- package/skills/1session/references/install.md +195 -0
package/dist/src/store/schema.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* the behaviour — never derive them from a git hash, `dist` ships without git.
|
|
8
8
|
*/
|
|
9
9
|
/** DDL layout. A bump drops and rebuilds the whole database. */
|
|
10
|
-
export const SCHEMA_VERSION =
|
|
10
|
+
export const SCHEMA_VERSION = 2;
|
|
11
11
|
/** L1 semantics — anything in `parsers/` that changes normalized events. */
|
|
12
12
|
export const PARSER_VERSION = 3;
|
|
13
13
|
/** L2 rules — `writes.ts` / `ledger.ts`. Re-derives facts from stored events. */
|
package/dist/src/store/write.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { encodeText } from './nul.js';
|
|
1
2
|
import fs from 'node:fs';
|
|
2
3
|
import crypto from 'node:crypto';
|
|
3
4
|
import { PARSER_VERSION } from './schema.js';
|
|
@@ -51,7 +52,7 @@ export function writeSession(db, session, fingerprint, turnCount) {
|
|
|
51
52
|
id, provider, native_id, source_path, workspace, title, started_at, ended_at,
|
|
52
53
|
event_count, turn_count, source_size, source_mtime_ms, head_hash, aux_fingerprint,
|
|
53
54
|
parser_version, extractor_version, edge_version, indexed_at, artifacts_json, stats_json
|
|
54
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 0, ?, ?, ?)`).run(id, session.ref.provider, session.ref.id, session.ref.path, session.ref.workspace ?? null, session.ref.title
|
|
55
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 0, ?, ?, ?)`).run(id, session.ref.provider, session.ref.id, session.ref.path, session.ref.workspace ?? null, encodeText(session.ref.title), session.ref.createdAt ?? null, session.ref.updatedAt ?? null, session.turns.length, turnCount, fingerprint.sourceSize, fingerprint.sourceMtimeMs, fingerprint.headHash, fingerprint.auxFingerprint ?? null, PARSER_VERSION, new Date().toISOString(), JSON.stringify(session.artifacts), JSON.stringify(session.stats));
|
|
55
56
|
const insert = db.prepare(`INSERT INTO events (
|
|
56
57
|
session_id, idx, kind, text, tool_name, tool_args_json, tool_result, is_error,
|
|
57
58
|
ts, source_index, exit_code, pid, duration_ms, provider_truncated
|
|
@@ -60,7 +61,7 @@ export function writeSession(db, session, fingerprint, turnCount) {
|
|
|
60
61
|
// 0.08% and silently dropped search hits from long build logs — exactly
|
|
61
62
|
// the kind of "looks complete, isn't" answer this store exists to remove.
|
|
62
63
|
for (const event of session.turns) {
|
|
63
|
-
insert.run(id, event.index, event.kind, event.text
|
|
64
|
+
insert.run(id, event.index, event.kind, encodeText(event.text), event.toolName ?? null, event.toolArgs ? JSON.stringify(event.toolArgs) : null, encodeText(event.toolResult), event.isError === undefined ? null : event.isError ? 1 : 0, event.timestamp ?? null, event.sourceIndex ?? null, event.exitCode ?? null, event.processId ?? null, event.durationMs ?? null, event.truncated === undefined ? null : event.truncated ? 1 : 0);
|
|
64
65
|
}
|
|
65
66
|
db.exec('COMMIT');
|
|
66
67
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { ServerResponse } from 'node:http';
|
|
2
|
+
import type { NormalizedSession } from '../types.js';
|
|
3
|
+
export interface SessionApiOptions {
|
|
4
|
+
/** Used when the client asks for scope=cwd but does not send ?workspace=. */
|
|
5
|
+
fallbackCwd: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function resolveListWorkspace(scope: string | null, workspace: string | null, fallbackCwd: string): string | undefined;
|
|
8
|
+
/** Chat-preview turns. Built only after the user clicks a session. */
|
|
9
|
+
export declare function previewTurns(normalized: NormalizedSession): {
|
|
10
|
+
no: number;
|
|
11
|
+
turn: number;
|
|
12
|
+
status: import("../types.js").TurnStatus;
|
|
13
|
+
prompt: string;
|
|
14
|
+
userPrompt: string;
|
|
15
|
+
outcome: string;
|
|
16
|
+
assistantReply: string;
|
|
17
|
+
thinking: string;
|
|
18
|
+
thinkingBlocks: string[];
|
|
19
|
+
toolCalls: any[];
|
|
20
|
+
files: string[];
|
|
21
|
+
commands: number;
|
|
22
|
+
errors: number;
|
|
23
|
+
startedAt: string | undefined;
|
|
24
|
+
endedAt: string | undefined;
|
|
25
|
+
durationMs: number | undefined;
|
|
26
|
+
}[];
|
|
27
|
+
export declare function sessionPreview(sessionId: string): Promise<{
|
|
28
|
+
ref: import("../types.js").SessionRef;
|
|
29
|
+
stats: import("../types.js").ProviderStats;
|
|
30
|
+
overview: import("../types.js").SessionOverview;
|
|
31
|
+
turns: {
|
|
32
|
+
no: number;
|
|
33
|
+
turn: number;
|
|
34
|
+
status: import("../types.js").TurnStatus;
|
|
35
|
+
prompt: string;
|
|
36
|
+
userPrompt: string;
|
|
37
|
+
outcome: string;
|
|
38
|
+
assistantReply: string;
|
|
39
|
+
thinking: string;
|
|
40
|
+
thinkingBlocks: string[];
|
|
41
|
+
toolCalls: any[];
|
|
42
|
+
files: string[];
|
|
43
|
+
commands: number;
|
|
44
|
+
errors: number;
|
|
45
|
+
startedAt: string | undefined;
|
|
46
|
+
endedAt: string | undefined;
|
|
47
|
+
durationMs: number | undefined;
|
|
48
|
+
}[];
|
|
49
|
+
files: {
|
|
50
|
+
displayPath: string;
|
|
51
|
+
path: string;
|
|
52
|
+
host?: string;
|
|
53
|
+
operation: string;
|
|
54
|
+
turn: number;
|
|
55
|
+
eventIndex: number;
|
|
56
|
+
timestamp?: string;
|
|
57
|
+
provenance: import("../types.js").Provenance;
|
|
58
|
+
extractor: string;
|
|
59
|
+
group: import("../types.js").FileGroup;
|
|
60
|
+
}[];
|
|
61
|
+
}>;
|
|
62
|
+
export declare function listSessionsForUi(options: {
|
|
63
|
+
limit?: number;
|
|
64
|
+
provider?: string;
|
|
65
|
+
since?: string;
|
|
66
|
+
workspace?: string;
|
|
67
|
+
}): Promise<import("../types.js").SessionRef[]>;
|
|
68
|
+
/**
|
|
69
|
+
* Shared JSON API used by the DSH plugin and `1session web`.
|
|
70
|
+
* Path is already stripped of `/api/session-reader`. Returns false if unmatched.
|
|
71
|
+
*/
|
|
72
|
+
export declare function handleSessionApi(pathname: string, url: URL, res: ServerResponse, options: SessionApiOptions): Promise<boolean>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { buildOverview, displayPath, fileLedger, listRecentSessions, loadSession, searchSessions, summarizeTurns, } from '../index.js';
|
|
2
|
+
export function resolveListWorkspace(scope, workspace, fallbackCwd) {
|
|
3
|
+
if (scope === 'global')
|
|
4
|
+
return undefined;
|
|
5
|
+
if (workspace)
|
|
6
|
+
return workspace;
|
|
7
|
+
if (scope && scope !== 'cwd')
|
|
8
|
+
return scope;
|
|
9
|
+
return fallbackCwd;
|
|
10
|
+
}
|
|
11
|
+
/** Chat-preview turns. Built only after the user clicks a session. */
|
|
12
|
+
export function previewTurns(normalized) {
|
|
13
|
+
return summarizeTurns(normalized).map((ts) => {
|
|
14
|
+
const evs = (normalized.turns || []).slice(ts.events[0], ts.events[1] + 1);
|
|
15
|
+
const thinkingEvents = evs.filter((e) => e.kind === 'thinking');
|
|
16
|
+
const assistantEvents = evs.filter((e) => e.kind === 'assistant');
|
|
17
|
+
const toolCalls = [];
|
|
18
|
+
for (let i = 0; i < evs.length; i++) {
|
|
19
|
+
const e = evs[i];
|
|
20
|
+
if (e.kind === 'tool_call') {
|
|
21
|
+
const result = evs.slice(i + 1).find((r) => r.kind === 'tool_result' && (r.toolName === e.toolName || !r.toolName));
|
|
22
|
+
toolCalls.push({
|
|
23
|
+
id: e.id,
|
|
24
|
+
toolName: e.toolName,
|
|
25
|
+
args: e.toolArgs,
|
|
26
|
+
result: result?.toolResult,
|
|
27
|
+
exitCode: result?.exitCode,
|
|
28
|
+
isError: result?.isError,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
no: ts.no,
|
|
34
|
+
turn: ts.no,
|
|
35
|
+
status: ts.status,
|
|
36
|
+
prompt: ts.prompt,
|
|
37
|
+
userPrompt: ts.prompt,
|
|
38
|
+
outcome: ts.outcome,
|
|
39
|
+
assistantReply: assistantEvents.map((a) => a.text).filter(Boolean).join('\n\n') || ts.outcome,
|
|
40
|
+
thinking: thinkingEvents.map((t) => t.text).filter(Boolean).join('\n\n'),
|
|
41
|
+
thinkingBlocks: thinkingEvents.map((t) => t.text).filter(Boolean),
|
|
42
|
+
toolCalls,
|
|
43
|
+
files: ts.files,
|
|
44
|
+
commands: ts.commands,
|
|
45
|
+
errors: ts.errors,
|
|
46
|
+
startedAt: ts.startedAt,
|
|
47
|
+
endedAt: ts.endedAt,
|
|
48
|
+
durationMs: ts.durationMs,
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
export async function sessionPreview(sessionId) {
|
|
53
|
+
const normalized = await loadSession(sessionId, { useIndex: true });
|
|
54
|
+
const workspace = normalized.ref.workspace;
|
|
55
|
+
return {
|
|
56
|
+
ref: normalized.ref,
|
|
57
|
+
stats: normalized.stats,
|
|
58
|
+
overview: buildOverview(normalized),
|
|
59
|
+
turns: previewTurns(normalized),
|
|
60
|
+
files: fileLedger(normalized).map((record) => ({
|
|
61
|
+
...record,
|
|
62
|
+
displayPath: displayPath(record, workspace),
|
|
63
|
+
})),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export async function listSessionsForUi(options) {
|
|
67
|
+
const limit = options.limit ?? 50;
|
|
68
|
+
return listRecentSessions({
|
|
69
|
+
limit,
|
|
70
|
+
provider: options.provider,
|
|
71
|
+
since: options.since,
|
|
72
|
+
workspace: options.workspace,
|
|
73
|
+
useIndex: false,
|
|
74
|
+
scan: Math.min(Math.max(limit * 4, 80), 200),
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
function sendJson(res, status, body) {
|
|
78
|
+
const payload = JSON.stringify(body);
|
|
79
|
+
res.statusCode = status;
|
|
80
|
+
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
81
|
+
res.end(payload);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Shared JSON API used by the DSH plugin and `1session web`.
|
|
85
|
+
* Path is already stripped of `/api/session-reader`. Returns false if unmatched.
|
|
86
|
+
*/
|
|
87
|
+
export async function handleSessionApi(pathname, url, res, options) {
|
|
88
|
+
if (pathname === '/sessions' || pathname === '' || pathname === '/') {
|
|
89
|
+
const limit = url.searchParams.get('limit') ? Number(url.searchParams.get('limit')) : 50;
|
|
90
|
+
const scope = url.searchParams.get('scope');
|
|
91
|
+
const provider = url.searchParams.get('provider') || undefined;
|
|
92
|
+
const since = url.searchParams.get('since') ?? undefined;
|
|
93
|
+
const workspace = resolveListWorkspace(scope, url.searchParams.get('workspace'), options.fallbackCwd);
|
|
94
|
+
const sessions = await listSessionsForUi({ limit, provider, since, workspace });
|
|
95
|
+
sendJson(res, 200, { sessions });
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
if (pathname === '/search') {
|
|
99
|
+
const query = url.searchParams.get('q') ?? '';
|
|
100
|
+
const scope = url.searchParams.get('scope');
|
|
101
|
+
const since = url.searchParams.get('since') ?? undefined;
|
|
102
|
+
const provider = url.searchParams.get('provider') ?? undefined;
|
|
103
|
+
const limit = url.searchParams.get('limit') ? Number(url.searchParams.get('limit')) : 20;
|
|
104
|
+
const workspace = resolveListWorkspace(scope, url.searchParams.get('workspace'), options.fallbackCwd);
|
|
105
|
+
const hits = await searchSessions(query, { workspace, since, provider, limit });
|
|
106
|
+
sendJson(res, 200, { hits });
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
const matchSession = pathname.match(/^\/session\/([^/]+)$/);
|
|
110
|
+
if (matchSession) {
|
|
111
|
+
const sessionId = decodeURIComponent(matchSession[1]);
|
|
112
|
+
try {
|
|
113
|
+
sendJson(res, 200, await sessionPreview(sessionId));
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
117
|
+
if (/session not found/i.test(msg)) {
|
|
118
|
+
sendJson(res, 404, { error: `Session not found: ${sessionId}` });
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|