cogpit-memory 0.1.10 → 0.3.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/LICENSE +21 -0
- package/README.md +34 -25
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +3090 -1211
- package/dist/commands/search.d.ts +1 -1
- package/dist/commands/sessions.d.ts +2 -4
- package/dist/index.d.ts +0 -1
- package/dist/index.js +3062 -1264
- package/dist/lib/agent-descriptors.d.ts +387 -0
- package/dist/lib/agentEnvelope.d.ts +24 -0
- package/dist/lib/agents.d.ts +112 -0
- package/dist/lib/claude.d.ts +49 -0
- package/dist/lib/codex-exec.d.ts +39 -0
- package/dist/lib/codex-tool-normalization.d.ts +31 -4
- package/dist/lib/codex.d.ts +29 -1
- package/dist/lib/copilot.d.ts +51 -0
- package/dist/lib/dirs.d.ts +9 -0
- package/dist/lib/helpers.d.ts +0 -6
- package/dist/lib/interactiveState.d.ts +16 -2
- package/dist/lib/messageTypeGuards.d.ts +10 -1
- package/dist/lib/metadata.d.ts +16 -14
- package/dist/lib/parser.d.ts +6 -4
- package/dist/lib/search-index.d.ts +7 -67
- package/dist/lib/sessionStats.d.ts +6 -1
- package/dist/lib/sessionStatus.d.ts +12 -27
- package/dist/lib/stores.d.ts +47 -0
- package/dist/lib/turnBuilder.d.ts +55 -0
- package/dist/lib/turnContent.d.ts +2 -0
- package/dist/lib/types.d.ts +200 -10
- package/package.json +22 -3
- package/skill/SKILL.md +59 -49
- package/dist/lib/pricingTiers.d.ts +0 -30
- package/dist/lib/token-costs.d.ts +0 -48
package/dist/lib/codex.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ParseSessionOptions, ParsedSession } from "./types";
|
|
1
|
+
import type { ParseSessionOptions, ParsedSession, RawRecord, SessionStatusInfo } from "./types";
|
|
2
2
|
export { extractApplyPatchInputs, findFailedNestedPatchCallIds, parseApplyPatch, parseCodexToolPatches, } from "./codex-patches";
|
|
3
3
|
export { parseCustomToolOutput } from "./codex-tool-normalization";
|
|
4
4
|
interface CodexMetadata {
|
|
@@ -26,5 +26,33 @@ interface CodexMetadata {
|
|
|
26
26
|
agentPath: string;
|
|
27
27
|
}
|
|
28
28
|
export declare function isCodexSessionText(jsonlText: string): boolean;
|
|
29
|
+
/** True when already-parsed raw records came from this CLI. */
|
|
30
|
+
export declare function isCodexRawRecords(records: readonly {
|
|
31
|
+
type?: unknown;
|
|
32
|
+
}[]): boolean;
|
|
29
33
|
export declare function extractCodexMetadataFromLines(lines: string[]): CodexMetadata;
|
|
30
34
|
export declare function parseCodexSession(jsonlText: string, options?: ParseSessionOptions): ParsedSession;
|
|
35
|
+
/**
|
|
36
|
+
* Incrementally extend a parsed session with newly written lines.
|
|
37
|
+
*
|
|
38
|
+
* Rollout records carry cross-turn state (patch folding, collaboration agent
|
|
39
|
+
* lifecycles, token roll-ups), so the whole transcript is re-parsed rather than
|
|
40
|
+
* only its tail.
|
|
41
|
+
*/
|
|
42
|
+
export declare function appendCodexSession(existing: ParsedSession, newJsonlText: string): ParsedSession;
|
|
43
|
+
/** Derive status by walking backward through a rollout's records. */
|
|
44
|
+
export declare function deriveCodexSessionStatus(rawMessages: readonly RawRecord[]): SessionStatusInfo;
|
|
45
|
+
/** A branch keeps the `session_meta` record, renamed and pointed back at its origin. */
|
|
46
|
+
export declare function brandCodexBranch(firstRecord: Record<string, unknown>, sessionId: string, turnIndex: number | null): {
|
|
47
|
+
record: Record<string, unknown>;
|
|
48
|
+
originalId: string;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Indexes at which a turn starts.
|
|
52
|
+
*
|
|
53
|
+
* A rollout writes the `turn_context` record that configures a turn *before*
|
|
54
|
+
* the user message that opens it, so the boundary reported is that context
|
|
55
|
+
* record: cutting at the user message alone would strand a context line
|
|
56
|
+
* belonging to a turn that no longer exists.
|
|
57
|
+
*/
|
|
58
|
+
export declare function codexTurnBoundaries(records: readonly Record<string, unknown>[]): number[];
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ParseSessionOptions, ParsedSession, RawRecord, SessionStatusInfo } from "./types";
|
|
2
|
+
interface CopilotMetadata {
|
|
3
|
+
sessionId: string;
|
|
4
|
+
version: string;
|
|
5
|
+
gitBranch: string;
|
|
6
|
+
cwd: string;
|
|
7
|
+
model: string;
|
|
8
|
+
slug: string;
|
|
9
|
+
name: string;
|
|
10
|
+
firstUserMessage: string;
|
|
11
|
+
lastUserMessage: string;
|
|
12
|
+
timestamp: string;
|
|
13
|
+
lastTimestamp: string;
|
|
14
|
+
turnCount: number;
|
|
15
|
+
isSubagent: false;
|
|
16
|
+
parentSessionId: null;
|
|
17
|
+
}
|
|
18
|
+
export declare function isCopilotSessionText(jsonlText: string): boolean;
|
|
19
|
+
/** True when already-parsed raw records came from this CLI. */
|
|
20
|
+
export declare function isCopilotRawRecords(records: readonly {
|
|
21
|
+
type?: unknown;
|
|
22
|
+
}[]): boolean;
|
|
23
|
+
export declare function extractCopilotMetadataFromLines(lines: string[]): CopilotMetadata;
|
|
24
|
+
export declare function parseCopilotSession(jsonlText: string, options?: ParseSessionOptions): ParsedSession;
|
|
25
|
+
/**
|
|
26
|
+
* Incrementally extend a parsed session with newly written lines.
|
|
27
|
+
*
|
|
28
|
+
* Copilot's durable events carry cross-turn state (permission requests, usage
|
|
29
|
+
* roll-ups, sub-agent lifecycles), so the whole transcript is re-parsed rather
|
|
30
|
+
* than only its tail.
|
|
31
|
+
*/
|
|
32
|
+
export declare function appendCopilotSession(existing: ParsedSession, newJsonlText: string): ParsedSession;
|
|
33
|
+
/** True when a record `type` belongs to this CLI's durable event stream. */
|
|
34
|
+
export declare function isCopilotEventType(type: unknown): type is string;
|
|
35
|
+
/** Derive status from Copilot CLI's durable session events. */
|
|
36
|
+
export declare function deriveCopilotSessionStatus(rawMessages: readonly RawRecord[]): SessionStatusInfo;
|
|
37
|
+
/**
|
|
38
|
+
* Copilot branches through its own fork RPC (`capabilities.nativeFork`), so a
|
|
39
|
+
* copied-and-cut transcript is never made and there is no header shape to
|
|
40
|
+
* invent for one.
|
|
41
|
+
*/
|
|
42
|
+
export declare function brandCopilotBranch(): never;
|
|
43
|
+
/**
|
|
44
|
+
* Indexes of the `user.message` events that open a turn.
|
|
45
|
+
*
|
|
46
|
+
* Sub-agent events carry an `agentId` and belong to the turn already in
|
|
47
|
+
* flight. An event with no durable `id` cannot be addressed by the CLI's fork
|
|
48
|
+
* and rewind RPCs, so it cannot be a boundary either.
|
|
49
|
+
*/
|
|
50
|
+
export declare function copilotTurnBoundaries(records: readonly Record<string, unknown>[]): number[];
|
|
51
|
+
export {};
|
package/dist/lib/dirs.d.ts
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
|
+
import { type AgentKind } from "./agent-descriptors";
|
|
2
|
+
/**
|
|
3
|
+
* Where each agent CLI keeps its state, honouring the environment variable it
|
|
4
|
+
* publishes for that. `$CODEX_HOME` used to be ignored here while
|
|
5
|
+
* `$COPILOT_HOME` was honoured, so a redirected Codex install was invisible to
|
|
6
|
+
* the whole package.
|
|
7
|
+
*/
|
|
8
|
+
export declare function agentHomeDir(kind: AgentKind, env?: NodeJS.ProcessEnv, homeDirectory?: string): string;
|
|
1
9
|
export declare const dirs: {
|
|
2
10
|
PROJECTS_DIR: string;
|
|
3
11
|
TEAMS_DIR: string;
|
|
4
12
|
TASKS_DIR: string;
|
|
5
13
|
CODEX_SESSIONS_DIR: string;
|
|
14
|
+
COPILOT_SESSIONS_DIR: string;
|
|
6
15
|
};
|
|
7
16
|
/** Default database path for the FTS5 search index. */
|
|
8
17
|
export declare const DEFAULT_DB_PATH: string;
|
package/dist/lib/helpers.d.ts
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
/** Find the JSONL file path for a session by searching all project directories. */
|
|
2
|
-
export declare function findJsonlPath(sessionId: string): Promise<string | null>;
|
|
3
1
|
export declare function matchSubagentToMember(leadSessionId: string, subagentFileName: string, members: Array<{
|
|
4
2
|
name: string;
|
|
5
3
|
agentType: string;
|
|
6
4
|
prompt?: string;
|
|
7
5
|
}>): Promise<string | null>;
|
|
8
|
-
export declare function projectDirToReadableName(dirName: string): {
|
|
9
|
-
path: string;
|
|
10
|
-
shortName: string;
|
|
11
|
-
};
|
|
@@ -8,6 +8,12 @@ export interface PlanApprovalState {
|
|
|
8
8
|
tool: string;
|
|
9
9
|
prompt: string;
|
|
10
10
|
}>;
|
|
11
|
+
provider?: "copilot";
|
|
12
|
+
requestId?: string;
|
|
13
|
+
summary?: string;
|
|
14
|
+
planContent?: string;
|
|
15
|
+
actions?: string[];
|
|
16
|
+
recommendedAction?: string;
|
|
11
17
|
}
|
|
12
18
|
export interface UserQuestionState {
|
|
13
19
|
type: "question";
|
|
@@ -24,12 +30,20 @@ export interface UserQuestionState {
|
|
|
24
30
|
}
|
|
25
31
|
export type PendingInteraction = PlanApprovalState | UserQuestionState | null;
|
|
26
32
|
/**
|
|
27
|
-
* Check if the previous turn's last tool call is the same interactive tool
|
|
28
|
-
*
|
|
33
|
+
* Check if the previous turn's last tool call is the same interactive tool that
|
|
34
|
+
* *errored* -- indicates a stuck re-call loop we should suppress.
|
|
35
|
+
*
|
|
36
|
+
* An unanswered (result === null) prompt in the previous turn is NOT a loop:
|
|
37
|
+
* it just means the user replied with a message instead of answering, and the
|
|
38
|
+
* agent is now asking again. Treating that as stuck suppressed the current
|
|
39
|
+
* prompt entirely, leaving the turn blocked with no way to answer it.
|
|
29
40
|
*/
|
|
30
41
|
export declare function isStuckInteractiveLoop(turns: Turn[], toolName: string): boolean;
|
|
31
42
|
/**
|
|
32
43
|
* Detect if the session is waiting for user interaction (plan approval or
|
|
33
44
|
* AskUserQuestion). Returns the interaction state or null.
|
|
45
|
+
*
|
|
46
|
+
* A blocking prompt wins over an async question asked earlier in the same turn:
|
|
47
|
+
* it is the one holding the agent up.
|
|
34
48
|
*/
|
|
35
49
|
export declare function detectPendingInteraction(session: ParsedSession): PendingInteraction;
|
|
@@ -5,13 +5,22 @@
|
|
|
5
5
|
* dependencies. Extracted here as a dependency-free leaf module (imports only
|
|
6
6
|
* from ./types) so both files can share a single source of truth.
|
|
7
7
|
*/
|
|
8
|
-
import type { RawMessage, UserMessage, AssistantMessage, ProgressMessage, SystemMessage, SummaryMessage, QueueOperationMessage } from "./types";
|
|
8
|
+
import type { RawMessage, UserMessage, AssistantMessage, ProgressMessage, SystemMessage, SummaryMessage, QueueOperationMessage, AttachmentMessage } from "./types";
|
|
9
9
|
export declare function isUserMessage(msg: RawMessage): msg is UserMessage;
|
|
10
10
|
export declare function isAssistantMessage(msg: RawMessage): msg is AssistantMessage;
|
|
11
11
|
export declare function isProgressMessage(msg: RawMessage): msg is ProgressMessage;
|
|
12
12
|
export declare function isSystemMessage(msg: RawMessage): msg is SystemMessage;
|
|
13
13
|
export declare function isSummaryMessage(msg: RawMessage): msg is SummaryMessage;
|
|
14
14
|
export declare function isQueueOperationMessage(msg: RawMessage): msg is QueueOperationMessage;
|
|
15
|
+
export declare function isAttachmentMessage(msg: RawMessage): msg is AttachmentMessage;
|
|
16
|
+
/**
|
|
17
|
+
* The synthetic user message Claude Code writes after compacting, carrying the
|
|
18
|
+
* summary text. Flagged `isVisibleInTranscriptOnly` too — it is transcript
|
|
19
|
+
* furniture, never something the user typed.
|
|
20
|
+
*/
|
|
21
|
+
export declare function isCompactSummaryMessage(msg: RawMessage): msg is UserMessage & {
|
|
22
|
+
isCompactSummary: true;
|
|
23
|
+
};
|
|
15
24
|
export declare function isCompactBoundary(msg: RawMessage): msg is SystemMessage & {
|
|
16
25
|
subtype: "compact_boundary";
|
|
17
26
|
};
|
package/dist/lib/metadata.d.ts
CHANGED
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
import { type SessionStatusInfo } from "./sessionStatus";
|
|
2
|
+
/**
|
|
3
|
+
* Header metadata for one transcript, whichever agent wrote it.
|
|
4
|
+
*
|
|
5
|
+
* Which agent that is comes from the shared format registry, in its own
|
|
6
|
+
* detection order — the same code the app and the parser use, rather than a
|
|
7
|
+
* hand-rolled sniff that recognised a narrower set of Codex records.
|
|
8
|
+
*/
|
|
2
9
|
export declare function getSessionMeta(filePath: string): Promise<{
|
|
10
|
+
lineCount: number;
|
|
11
|
+
branchedFrom: {
|
|
12
|
+
sessionId: string;
|
|
13
|
+
turnIndex?: number | null;
|
|
14
|
+
} | undefined;
|
|
15
|
+
firstUserMessage: string;
|
|
16
|
+
lastUserMessage: string;
|
|
17
|
+
timestamp: string;
|
|
18
|
+
turnCount: number;
|
|
3
19
|
sessionId: string;
|
|
4
20
|
version: string;
|
|
5
21
|
gitBranch: string;
|
|
6
22
|
model: string;
|
|
7
23
|
slug: string;
|
|
8
24
|
cwd: string;
|
|
9
|
-
firstUserMessage: string;
|
|
10
|
-
lastUserMessage: string;
|
|
11
|
-
timestamp: string;
|
|
12
|
-
turnCount: number;
|
|
13
|
-
lineCount: number;
|
|
14
|
-
branchedFrom: {
|
|
15
|
-
sessionId: string;
|
|
16
|
-
turnIndex?: number | null;
|
|
17
|
-
} | undefined;
|
|
18
25
|
}>;
|
|
19
26
|
/**
|
|
20
27
|
* Read backward through a session JSONL to derive agent status.
|
|
@@ -24,8 +31,3 @@ export declare function getSessionMeta(filePath: string): Promise<{
|
|
|
24
31
|
* deriveSessionStatus() function as the client side.
|
|
25
32
|
*/
|
|
26
33
|
export declare function getSessionStatus(filePath: string): Promise<SessionStatusInfo>;
|
|
27
|
-
/**
|
|
28
|
-
* Search all user messages in a session file for a query string.
|
|
29
|
-
* Returns the first matching message snippet, or null if no match.
|
|
30
|
-
*/
|
|
31
|
-
export declare function searchSessionMessages(filePath: string, query: string): Promise<string | null>;
|
package/dist/lib/parser.d.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ImageBlock, ParseSessionOptions, ParsedSession, UserContent } from "./types";
|
|
2
2
|
export type { PendingInteraction } from "./interactiveState";
|
|
3
3
|
export type { ParseSessionOptions } from "./types";
|
|
4
4
|
export { detectPendingInteraction } from "./interactiveState";
|
|
5
5
|
export declare function parseSession(jsonlText: string, options?: ParseSessionOptions): ParsedSession;
|
|
6
6
|
/**
|
|
7
7
|
* Incrementally append new JSONL lines to an existing parsed session.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
*
|
|
9
|
+
* Dispatch is on raw-record shape, never on `existing.agentKind`: that field is
|
|
10
|
+
* optional on `ParsedSession` and this is the append hot path. When the records
|
|
11
|
+
* carry no discriminator — an empty window, or one paged in above the loaded
|
|
12
|
+
* range — the newly arrived text decides instead.
|
|
10
13
|
*/
|
|
11
14
|
export declare function parseSessionAppend(existing: ParsedSession, newJsonlText: string): ParsedSession;
|
|
12
15
|
export declare function getUserMessageText(content: UserContent | null): string;
|
|
13
16
|
export declare function getUserMessageImages(content: UserContent | null): ImageBlock[];
|
|
14
|
-
export declare function getToolColor(toolName: string): string;
|
|
@@ -6,7 +6,6 @@ export interface IndexStats {
|
|
|
6
6
|
indexedSessions: number;
|
|
7
7
|
indexedSubagents: number;
|
|
8
8
|
totalRows: number;
|
|
9
|
-
watcherRunning: boolean;
|
|
10
9
|
lastFullBuild: string | null;
|
|
11
10
|
lastUpdate: string | null;
|
|
12
11
|
}
|
|
@@ -20,12 +19,8 @@ export interface SearchHit {
|
|
|
20
19
|
export declare class SearchIndex {
|
|
21
20
|
private db;
|
|
22
21
|
private dbPath;
|
|
23
|
-
projectsDir: string | null;
|
|
24
|
-
private _watcherRunning;
|
|
25
22
|
private _lastFullBuild;
|
|
26
23
|
private _lastUpdate;
|
|
27
|
-
private watcher;
|
|
28
|
-
private debounceTimers;
|
|
29
24
|
constructor(dbPath: string);
|
|
30
25
|
private initSchema;
|
|
31
26
|
getStats(): IndexStats;
|
|
@@ -45,6 +40,7 @@ export declare class SearchIndex {
|
|
|
45
40
|
isSubagent?: boolean;
|
|
46
41
|
parentSessionId?: string | null;
|
|
47
42
|
}): void;
|
|
43
|
+
pruneMissingFiles(): number;
|
|
48
44
|
/**
|
|
49
45
|
* Query the FTS5 index and return structured search results.
|
|
50
46
|
*
|
|
@@ -59,6 +55,7 @@ export declare class SearchIndex {
|
|
|
59
55
|
search(query: string, opts?: {
|
|
60
56
|
limit?: number;
|
|
61
57
|
sessionId?: string;
|
|
58
|
+
excludeSessionId?: string;
|
|
62
59
|
maxAgeMs?: number;
|
|
63
60
|
caseSensitive?: boolean;
|
|
64
61
|
}): SearchHit[];
|
|
@@ -68,32 +65,20 @@ export declare class SearchIndex {
|
|
|
68
65
|
*/
|
|
69
66
|
countMatches(query: string, opts?: {
|
|
70
67
|
sessionId?: string;
|
|
68
|
+
excludeSessionId?: string;
|
|
71
69
|
maxAgeMs?: number;
|
|
72
70
|
}): {
|
|
73
71
|
totalHits: number;
|
|
74
72
|
sessionsSearched: number;
|
|
75
73
|
};
|
|
76
74
|
/**
|
|
77
|
-
* Clear all indexed data and re-index every
|
|
78
|
-
* Structure: projectsDir/{projectName}/{sessionId}.jsonl
|
|
79
|
-
* Subagents: projectsDir/{projectName}/{sessionId}/subagents/agent-{id}.jsonl
|
|
75
|
+
* Clear all indexed data and re-index every transcript, from every agent.
|
|
80
76
|
*
|
|
81
77
|
* Optimized: discovers all files first, then processes them in a single
|
|
82
78
|
* SQLite transaction with pre-prepared statements. This avoids the overhead
|
|
83
79
|
* of 3000+ individual transactions (each forcing a disk sync).
|
|
84
|
-
*
|
|
85
|
-
* Stores `projectsDir` as a class field so `rebuild()` can reuse it.
|
|
86
80
|
*/
|
|
87
|
-
buildFull(
|
|
88
|
-
/**
|
|
89
|
-
* Incrementally re-index only files whose mtime has changed since last index.
|
|
90
|
-
* New files (not in indexed_files) are always indexed.
|
|
91
|
-
*
|
|
92
|
-
* WARNING: This walks ALL files under projectsDir and stats each one.
|
|
93
|
-
* On large session stores (3000+ files) this can take minutes.
|
|
94
|
-
* Prefer `updateRecent()` for CLI search paths.
|
|
95
|
-
*/
|
|
96
|
-
updateStale(projectsDir: string): void;
|
|
81
|
+
buildFull(): void;
|
|
97
82
|
/**
|
|
98
83
|
* Lightweight incremental update for CLI search paths.
|
|
99
84
|
*
|
|
@@ -102,53 +87,8 @@ export declare class SearchIndex {
|
|
|
102
87
|
* Caps re-indexing to `maxFiles` to prevent blocking on large backlogs
|
|
103
88
|
* (run `index rebuild` for a full catch-up).
|
|
104
89
|
*/
|
|
105
|
-
updateRecent(
|
|
106
|
-
/**
|
|
107
|
-
* Re-run buildFull using the previously stored projectsDir.
|
|
108
|
-
* No-op if projectsDir was never set.
|
|
109
|
-
*/
|
|
110
|
-
rebuild(): void;
|
|
111
|
-
/**
|
|
112
|
-
* Walk the projects directory tree and invoke `callback` for every JSONL file.
|
|
113
|
-
*
|
|
114
|
-
* Directory structure:
|
|
115
|
-
* projectsDir/
|
|
116
|
-
* {projectName}/
|
|
117
|
-
* {sessionId}.jsonl <- session file
|
|
118
|
-
* {sessionId}/subagents/
|
|
119
|
-
* agent-{agentId}.jsonl <- subagent file (recursive)
|
|
120
|
-
*
|
|
121
|
-
* Skips the "memory" directory.
|
|
122
|
-
*/
|
|
90
|
+
updateRecent(maxFiles?: number): void;
|
|
91
|
+
/** Every transcript from every agent, tagged with the session it belongs to. */
|
|
123
92
|
private discoverFiles;
|
|
124
|
-
/**
|
|
125
|
-
* Recursively discover subagent JSONL files under the subagents directory
|
|
126
|
-
* that corresponds to `parentPath`.
|
|
127
|
-
*
|
|
128
|
-
* For a parent at `/path/to/session-1.jsonl`, looks for subagents at
|
|
129
|
-
* `/path/to/session-1/subagents/agent-*.jsonl`.
|
|
130
|
-
*
|
|
131
|
-
* Recurses up to `maxDepth` levels (default 4).
|
|
132
|
-
*/
|
|
133
|
-
private discoverSubagents;
|
|
134
|
-
/**
|
|
135
|
-
* Start watching `projectsDir` for JSONL file changes.
|
|
136
|
-
* Runs `updateStale()` immediately for an initial sync, then sets up
|
|
137
|
-
* `fs.watch` with `{ recursive: true }` (macOS-compatible) to detect
|
|
138
|
-
* subsequent file changes and trigger debounced re-indexing.
|
|
139
|
-
*/
|
|
140
|
-
startWatching(projectsDir: string): void;
|
|
141
|
-
/**
|
|
142
|
-
* Stop the file watcher and clear any pending debounce timers.
|
|
143
|
-
* Safe to call even when not watching (no-op).
|
|
144
|
-
*/
|
|
145
|
-
stopWatching(): void;
|
|
146
|
-
/**
|
|
147
|
-
* Private helper: debounce re-indexing of a single file.
|
|
148
|
-
* Waits 2 seconds after the last change event for a given file path
|
|
149
|
-
* before actually calling `indexFile()`. This coalesces rapid writes
|
|
150
|
-
* (e.g. streaming JSONL appends) into a single index operation.
|
|
151
|
-
*/
|
|
152
|
-
private debouncedReindex;
|
|
153
93
|
close(): void;
|
|
154
94
|
}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Session statistics computation — token usage aggregation and tool call tallying.
|
|
3
|
+
*
|
|
4
|
+
* Token totals are the raw figures the transcript reports. Costs are not
|
|
5
|
+
* computed here: pricing needs the live LiteLLM rate table, which callers
|
|
6
|
+
* apply via shared/usageCost/pricing.ts.
|
|
3
7
|
*/
|
|
4
|
-
import type { Turn, SessionStats } from "./types";
|
|
8
|
+
import type { Turn, SessionStats, TokenUsage } from "./types";
|
|
5
9
|
export declare function createEmptySessionStats(turnCount: number): SessionStats;
|
|
6
10
|
export declare function computeStats(turns: Turn[]): SessionStats;
|
|
11
|
+
export declare function mergeTokenUsage(existing: TokenUsage | null, incoming: TokenUsage): TokenUsage;
|
|
@@ -1,36 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* Walks backward from the last message to determine if the agent
|
|
4
|
-
* is idle, thinking, calling tools, etc.
|
|
2
|
+
* Session status: what the agent is doing right now, and how to label it.
|
|
5
3
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Status is always derived from raw transcript records, never stored as app
|
|
5
|
+
* state. Each format derives its own — this file only routes to the format the
|
|
6
|
+
* records were written in, and holds the presentation labels.
|
|
8
7
|
*/
|
|
9
|
-
|
|
10
|
-
export
|
|
11
|
-
status: SessionStatus;
|
|
12
|
-
/** Name of the tool currently being used (if status is tool_use) */
|
|
13
|
-
toolName?: string;
|
|
14
|
-
/** Number of pending queue items (user messages waiting to be processed) */
|
|
15
|
-
pendingQueue?: number;
|
|
16
|
-
/** Why the session terminated (from Claude Code's terminal_reason). Only set for non-normal endings. */
|
|
17
|
-
terminalReason?: string;
|
|
18
|
-
}
|
|
8
|
+
import type { RawRecord, SessionStatus, SessionStatusInfo } from "./types";
|
|
9
|
+
export type { SessionStatus, SessionStatusInfo } from "./types";
|
|
19
10
|
/**
|
|
20
|
-
* Derive session status from raw JSONL
|
|
11
|
+
* Derive session status from raw JSONL record objects.
|
|
21
12
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* to `deriveCodexSessionStatus`. All other sessions are treated as Claude Code
|
|
26
|
-
* format. If a third provider is added, add a detection branch here and in
|
|
27
|
-
* `src/lib/providers/registry.ts`.
|
|
13
|
+
* The format is resolved from the records themselves, in `AGENT_KINDS` order,
|
|
14
|
+
* so a transcript that carries no positive discriminator lands on the terminal
|
|
15
|
+
* format — the same fallback the parser uses.
|
|
28
16
|
*/
|
|
29
|
-
export declare function deriveSessionStatus(rawMessages:
|
|
30
|
-
type: string;
|
|
31
|
-
[key: string]: unknown;
|
|
32
|
-
}>): SessionStatusInfo;
|
|
17
|
+
export declare function deriveSessionStatus(rawMessages: RawRecord[]): SessionStatusInfo;
|
|
33
18
|
/** Human-readable label for terminal_reason values from Claude Code. */
|
|
34
19
|
export declare function getTerminalReasonLabel(reason: string): string;
|
|
35
20
|
/** Human-readable label for a session status. Returns null for "idle". */
|
|
36
|
-
export declare function getStatusLabel(status: SessionStatus | undefined, toolName?: string, terminalReason?: string): string | null;
|
|
21
|
+
export declare function getStatusLabel(status: SessionStatus | undefined, toolName?: string, terminalReason?: string, pendingAgents?: number): string | null;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type AgentDescriptor, type AgentKind } from "./agent-descriptors";
|
|
2
|
+
/** One transcript on disk, plus what an index needs to file it under. */
|
|
3
|
+
export interface SessionFile {
|
|
4
|
+
path: string;
|
|
5
|
+
mtimeMs: number;
|
|
6
|
+
sessionId: string;
|
|
7
|
+
isSubagent: boolean;
|
|
8
|
+
parentSessionId: string | null;
|
|
9
|
+
}
|
|
10
|
+
/** Where a transcript belongs, without reading it. */
|
|
11
|
+
export type SessionFileIdentity = Omit<SessionFile, "path" | "mtimeMs">;
|
|
12
|
+
export interface AgentSessionStore {
|
|
13
|
+
readonly kind: AgentKind;
|
|
14
|
+
readonly descriptor: AgentDescriptor;
|
|
15
|
+
/** Absolute root this agent's transcripts live under. */
|
|
16
|
+
root(): string;
|
|
17
|
+
/** True when an absolute path is inside this agent's root. */
|
|
18
|
+
ownsPath(filePath: string): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Which session a transcript belongs to. Never `basename(file, ".jsonl")`:
|
|
21
|
+
* that is Copilot's literal `events`, and it loses a sub-agent's parent.
|
|
22
|
+
*/
|
|
23
|
+
identify(filePath: string, root?: string): SessionFileIdentity;
|
|
24
|
+
/** Transcripts modified at or after `cutoffMs`. */
|
|
25
|
+
list(cutoffMs: number, root?: string): SessionFile[];
|
|
26
|
+
/** Locate a session by id, or null when this agent does not have it. */
|
|
27
|
+
find(sessionId: string): string | null;
|
|
28
|
+
}
|
|
29
|
+
export declare function storeFor(kind: AgentKind): AgentSessionStore;
|
|
30
|
+
/** The store a path under no known root is read with: the agent that owns every unprefixed project. */
|
|
31
|
+
export declare function defaultStore(): AgentSessionStore;
|
|
32
|
+
/** Every store, in registry order. */
|
|
33
|
+
export declare function allStores(): readonly AgentSessionStore[];
|
|
34
|
+
/** The store whose root contains `filePath`, or null when none does. */
|
|
35
|
+
export declare function storeForPath(filePath: string): AgentSessionStore | null;
|
|
36
|
+
/**
|
|
37
|
+
* Transcripts from every agent, newest first. One walk per root, so no caller
|
|
38
|
+
* can accidentally cover a different set of agents from its neighbours.
|
|
39
|
+
*/
|
|
40
|
+
export declare function listAllSessionFiles(cutoffMs: number): SessionFile[];
|
|
41
|
+
/**
|
|
42
|
+
* Absolute path of a session's transcript, searched across every agent.
|
|
43
|
+
*
|
|
44
|
+
* The id arrives from a CLI argument and is joined straight onto a root, so it
|
|
45
|
+
* is rejected outright unless it is a single path segment.
|
|
46
|
+
*/
|
|
47
|
+
export declare function findSessionFile(sessionId: string): Promise<string | null>;
|
|
@@ -2,4 +2,59 @@
|
|
|
2
2
|
* Turn builder — state machine that converts raw JSONL messages into Turn objects.
|
|
3
3
|
*/
|
|
4
4
|
import type { RawMessage, Turn } from "./types";
|
|
5
|
+
/**
|
|
6
|
+
* Attach each peer message to the `SendMessage` that answered it.
|
|
7
|
+
*
|
|
8
|
+
* Walks blocks in chronological order holding the still-unanswered messages per
|
|
9
|
+
* sender, so a reply claims the oldest outstanding message from that sender and
|
|
10
|
+
* pairing only ever runs forward in time. A `SendMessage` sent *before* any
|
|
11
|
+
* inbound message from that sender is an instruction, not a reply.
|
|
12
|
+
*
|
|
13
|
+
* The join is on the sender name, which is the only key available: the block
|
|
14
|
+
* carries no task id, because `origin.senderTaskId` names the sending agent's
|
|
15
|
+
* task rather than the message. `SendMessage.input.to` uses the same names that
|
|
16
|
+
* arrive in `origin.from`, so the join holds.
|
|
17
|
+
*
|
|
18
|
+
* A pure recompute: every existing pairing is cleared before the walk, so the
|
|
19
|
+
* answer depends only on the list handed in. Callers that assemble a transcript
|
|
20
|
+
* from several `buildTurns` calls — the page stitcher and the incremental
|
|
21
|
+
* append — re-run it over the joined list and get what a single parse of that
|
|
22
|
+
* list would have produced. Turns holding no peer message come back by
|
|
23
|
+
* reference, so a re-run costs nothing for the rest of the transcript.
|
|
24
|
+
*/
|
|
25
|
+
export declare function pairAgentMessageReplies(turns: readonly Turn[]): Turn[];
|
|
26
|
+
/**
|
|
27
|
+
* Raw-message index where each turn `buildTurns` would produce begins, so
|
|
28
|
+
* `starts[i]` is the index that produces `turns[i]`.
|
|
29
|
+
*
|
|
30
|
+
* Incremental appends need both a rebuild cut point and the number of turns
|
|
31
|
+
* that precede it. Deriving them separately is how an append drifts out of
|
|
32
|
+
* sync with a full re-parse, so both come from this one pass.
|
|
33
|
+
*
|
|
34
|
+
* Mirrors the turn-creation rule of `buildTurns` below — a parser test asserts
|
|
35
|
+
* the two agree, so keep them in lockstep.
|
|
36
|
+
*
|
|
37
|
+
* This is a cheap forward scan on the append hot path, which is the only reason
|
|
38
|
+
* it restates the rule instead of calling `buildTurnsWithStarts`. It is
|
|
39
|
+
* deliberately NOT used to cut a transcript: undo and branching go through
|
|
40
|
+
* `AgentFormat.turnBoundaries`, which is derived from the real turn walk,
|
|
41
|
+
* because a boundary that disagrees with the parser truncates history. Widen
|
|
42
|
+
* this only alongside `buildTurns`, never on its own.
|
|
43
|
+
*/
|
|
44
|
+
export declare function findTurnStartIndices(messages: RawMessage[]): number[];
|
|
45
|
+
export interface BuiltTurns {
|
|
46
|
+
turns: Turn[];
|
|
47
|
+
/** Index, into `messages`, of the record that opened each turn. */
|
|
48
|
+
turnStartIndices: number[];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Build turns and report where each one started.
|
|
52
|
+
*
|
|
53
|
+
* Turn boundaries used to be derived by a separate predicate that restated this
|
|
54
|
+
* function's turn rule; the two drifted, and because boundaries decide where a
|
|
55
|
+
* transcript gets physically cut, the disagreement silently truncated history.
|
|
56
|
+
* Both now come from this one walk.
|
|
57
|
+
*/
|
|
58
|
+
export declare function buildTurnsWithStarts(messages: RawMessage[]): BuiltTurns;
|
|
59
|
+
/** Turns only — the common case. */
|
|
5
60
|
export declare function buildTurns(messages: RawMessage[]): Turn[];
|