cogpit-memory 0.3.0 → 0.4.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.
@@ -21,7 +21,7 @@ export { AGENT_KINDS } from "./types";
21
21
  * point: a fourth CLI adds one row here instead of a new arm in 40 conditionals.
22
22
  */
23
23
  export interface AgentCapabilities {
24
- /** Git worktrees, which Cogpit stores under the Claude projects tree. */
24
+ /** Launch a session in a worktree managed by its CLI. */
25
25
  readonly worktrees: boolean;
26
26
  /** MCP server configuration can be passed at spawn time. */
27
27
  readonly mcp: boolean;
@@ -59,8 +59,18 @@ export interface AgentCapabilities {
59
59
  * exist only for models whose live catalog entry advertises them.
60
60
  */
61
61
  readonly reasoningEffort: boolean;
62
+ readonly configurableContextWindow: boolean;
62
63
  /** A faster/priority service tier can be requested. */
63
64
  readonly fastTier: boolean;
65
+ /**
66
+ * The CLI can carry a session past a spent session allowance by continuing at
67
+ * lower priority, spending the weekly allowance to do it. The mode is reached
68
+ * only from the CLI's own terminal UI — no flag, no setting, and nothing on
69
+ * the programmatic interface Cogpit drives — so the hand-off is all Cogpit
70
+ * can offer. Never true for an agent billed per request, which has no
71
+ * allowance to run out of in the first place.
72
+ */
73
+ readonly lowPriorityInTerminal: boolean;
64
74
  /**
65
75
  * Images may be attached when the selected model's catalog entry says nothing
66
76
  * about its input modalities. When false, vision has to be advertised.
@@ -85,6 +95,7 @@ export interface AgentCapabilities {
85
95
  readonly modelFallbackNotices: boolean;
86
96
  /** Whether an unattended "auto" permission mode can be offered. */
87
97
  readonly autoPermissionMode: "never" | "per-model" | "always";
98
+ readonly toolPermissionRules: boolean;
88
99
  /** Whether composer settings take effect immediately or on the next turn. */
89
100
  readonly settingsApply: "live" | "next-turn";
90
101
  /**
@@ -107,6 +118,8 @@ export interface AgentDirNameCodec {
107
118
  readonly lossy: boolean;
108
119
  /** True when `dirName` belongs to this agent. */
109
120
  owns(dirName: string | null | undefined): boolean;
121
+ /** True when `dirName` is a name `encode` produces, rather than one this agent merely owns. */
122
+ recognizes(dirName: string): boolean;
110
123
  /** Encode a project path as this agent's dirName. */
111
124
  encode(cwd: string): string;
112
125
  /** Decode one of this agent's dirNames back to a project path. */
@@ -125,6 +138,22 @@ export interface AgentSessionFileCodec {
125
138
  urlId(fileName: string): string;
126
139
  /** Exact inverse of `urlId`. */
127
140
  fileNameFromUrlId(urlId: string): string;
141
+ /**
142
+ * The top-level session a transcript belongs to, so a sub-agent or workflow
143
+ * transcript answers with the session that spawned it. Takes the file's
144
+ * canonical path — symlinks resolved, `/`-separated — relative to the
145
+ * project directory where the agent has one on disk, to the sessions root
146
+ * otherwise; `AgentStore.transcriptRoot` produces exactly that. Null for a
147
+ * path that could step outside it or names no session.
148
+ */
149
+ transcriptRoot(relativePath: string): TranscriptRoot | null;
150
+ }
151
+ /** The top-level session a transcript belongs to. */
152
+ export interface TranscriptRoot {
153
+ /** Lowercased. */
154
+ rootSessionId: string;
155
+ /** False for a sub-agent's or workflow's transcript filed under that session. */
156
+ isRootTranscript: boolean;
128
157
  }
129
158
  export interface AgentResumeCodec {
130
159
  /** Copy-pasteable shell command that reopens the session in a terminal. */
@@ -184,6 +213,7 @@ export interface AgentCli {
184
213
  readonly homeDirName: string;
185
214
  /** Environment variable overriding that directory, if the CLI has one. */
186
215
  readonly homeEnvVar: string | null;
216
+ readonly sdkHomeEnvVar?: string;
187
217
  /**
188
218
  * Path inside the home whose existence proves the CLI has been used. Empty
189
219
  * when the home directory itself is the evidence.
@@ -195,6 +225,7 @@ export interface AgentCli {
195
225
  * that configuration is missing; the rest sit at a fixed, env-overridable
196
226
  * location and can be found on a first run.
197
227
  */
228
+ readonly requiresConfiguration?: boolean;
198
229
  readonly homeIsDiscoverable: boolean;
199
230
  readonly process: AgentProcessMatch;
200
231
  }
@@ -211,6 +242,8 @@ export interface AgentConfigFile {
211
242
  readonly scopes: readonly AgentConfigScope[];
212
243
  /** Tree label; defaults to `path`. */
213
244
  readonly label?: string;
245
+ /** Read only when none of the agent's other instruction files exist at that scope. */
246
+ readonly fallback?: true;
214
247
  }
215
248
  /**
216
249
  * Where a CLI reads its instructions, settings and extensions from. Drives the
@@ -329,6 +362,8 @@ export interface AgentDescriptor {
329
362
  readonly binName: string;
330
363
  readonly dirName: AgentDirNameCodec;
331
364
  readonly sessionFile: AgentSessionFileCodec;
365
+ /** Folder inside a project where the CLI cuts its worktrees; null when they live elsewhere. */
366
+ readonly projectWorktreeDir: string | null;
332
367
  /**
333
368
  * Whether header metadata is complete after reading only the first bytes of a
334
369
  * transcript. False when an exact line count or the last user message matters
@@ -385,3 +420,6 @@ export declare function projectDirNameFor(kind: AgentKind, cwd: string, knownDir
385
420
  export declare function sessionIdFromFileName(kind: AgentKind, fileName: string): string | null;
386
421
  /** True when `value` is a bare session UUID. */
387
422
  export declare function isSessionUuid(value: string): boolean;
423
+ /** Canonical access identity, preserving the instance token's case. */
424
+ export declare function canonicalSessionId(value: string): string | null;
425
+ export declare const PERSISTED_AGENT_HOME_FIELD = "claudeDir";
@@ -42,12 +42,13 @@ export interface AgentFormat {
42
42
  /** Status derived from raw records. */
43
43
  status(records: readonly RawRecord[]): SessionStatusInfo;
44
44
  /**
45
- * The first record of a transcript, rewritten for a branch: it now names
46
- * `sessionId` and records the session and turn it was cut from. Returns the
47
- * id the record carried before, which is what the branch reports as its
48
- * origin.
45
+ * The first record of a transcript, rewritten for a branch cut at
46
+ * `branchedAt`: it now names `sessionId` and records the session and turn
47
+ * it was cut from, and when, where the copied lines cannot otherwise be
48
+ * told from the branch's own. Returns the id the record carried before,
49
+ * which is what the branch reports as its origin.
49
50
  */
50
- brandBranch(firstRecord: Record<string, unknown>, sessionId: string, turnIndex: number | null): {
51
+ brandBranch(firstRecord: Record<string, unknown>, sessionId: string, turnIndex: number | null, branchedAt: number): {
51
52
  record: Record<string, unknown>;
52
53
  originalId: string;
53
54
  };
@@ -94,15 +95,25 @@ export declare function cutLineForTurnCount(format: AgentFormat, lines: readonly
94
95
  */
95
96
  export declare function cutLineAfterTurnIndex(format: AgentFormat, lines: readonly string[], turnIndex: number): number | null;
96
97
  /**
97
- * The line to cut at to keep everything through the turn containing the record
98
- * whose `uuid` is `turnUuid`. Correct even when the caller has only a tail of
99
- * the transcript loaded, so its turn indexes do not match file order.
98
+ * Each turn's first line paired with the id the parser gives that turn, in file
99
+ * order. Null when the two do not pair up, which would mean the ids cannot be
100
+ * trusted to name these lines.
100
101
  *
101
- * `"keep-all"` means the uuid was found in the last turn (nothing to remove);
102
- * null means it was not found at all, and the caller should fall back to an
103
- * index-based cut.
102
+ * This is what lets a caller holding only the loaded tail of a transcript name
103
+ * a turn: its window index says nothing about file order, but its id does.
104
104
  */
105
- export declare function cutLineAfterUuid(format: AgentFormat, lines: readonly string[], turnUuid: string): number | "keep-all" | null;
105
+ export declare function turnStartsWithIds(format: AgentFormat, lines: readonly string[]): {
106
+ line: number;
107
+ id: string;
108
+ }[] | null;
109
+ /**
110
+ * The line to cut at to keep everything through the turn whose id is `turnId`.
111
+ *
112
+ * `"keep-all"` means it is the last turn (nothing to remove); null means no
113
+ * turn has that id — an agent may leave a turn unlabelled — and the caller
114
+ * should fall back to an index-based cut.
115
+ */
116
+ export declare function cutLineAfterTurnId(format: AgentFormat, lines: readonly string[], turnId: string): number | "keep-all" | null;
106
117
  /**
107
118
  * The tool calls a recorded call actually ran. Every format records one call
108
119
  * per tool use except Codex code mode, which persists a whole script of them as
@@ -25,6 +25,13 @@ interface CodexMetadata {
25
25
  /** Canonical collaboration path for the agent that owns this rollout. */
26
26
  agentPath: string;
27
27
  }
28
+ /**
29
+ * The session a rollout was forked or spawned from, read from its
30
+ * `session_meta` payload. Forks name it in `forked_from_id`; spawned threads
31
+ * and reviews in `parent_thread_id`, and some spawned threads only under
32
+ * `source.subagent.thread_spawn`.
33
+ */
34
+ export declare function codexParentId(payload: Record<string, unknown>): string | null;
28
35
  export declare function isCodexSessionText(jsonlText: string): boolean;
29
36
  /** True when already-parsed raw records came from this CLI. */
30
37
  export declare function isCodexRawRecords(records: readonly {
@@ -42,8 +49,12 @@ export declare function parseCodexSession(jsonlText: string, options?: ParseSess
42
49
  export declare function appendCodexSession(existing: ParsedSession, newJsonlText: string): ParsedSession;
43
50
  /** Derive status by walking backward through a rollout's records. */
44
51
  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): {
52
+ /**
53
+ * A branch keeps the `session_meta` record, renamed and pointed back at its
54
+ * origin. The copied lines keep their origin's timestamps, so `at` is what
55
+ * tells them from the branch's own.
56
+ */
57
+ export declare function brandCodexBranch(firstRecord: Record<string, unknown>, sessionId: string, turnIndex: number | null, branchedAt: number): {
47
58
  record: Record<string, unknown>;
48
59
  originalId: string;
49
60
  };
@@ -12,6 +12,7 @@ export declare const dirs: {
12
12
  TASKS_DIR: string;
13
13
  CODEX_SESSIONS_DIR: string;
14
14
  COPILOT_SESSIONS_DIR: string;
15
+ ACP_PROJECTS_DIR: string;
15
16
  };
16
17
  /** Default database path for the FTS5 search index. */
17
18
  export declare const DEFAULT_DB_PATH: string;
@@ -0,0 +1,12 @@
1
+ import type { ParsedSession } from "./types";
2
+ export declare function instanceSessionId(instanceId: string, nativeId: string): string;
3
+ export declare function splitInstanceSessionId(id: string): {
4
+ instanceId: string;
5
+ nativeId: string;
6
+ };
7
+ export declare function instanceDirName(instanceId: string, nativeDirName: string): string;
8
+ export declare function splitInstanceDirName(dirName: string | null | undefined): {
9
+ instanceId: string;
10
+ nativeDirName: string;
11
+ };
12
+ export declare function scopeParsedSession(session: ParsedSession, instanceId: string): ParsedSession;
@@ -0,0 +1 @@
1
+ export declare function parseTranscript(content: string, filePath: string): import("./types").ParsedSession;
@@ -11,6 +11,7 @@ export interface SessionFile {
11
11
  export type SessionFileIdentity = Omit<SessionFile, "path" | "mtimeMs">;
12
12
  export interface AgentSessionStore {
13
13
  readonly kind: AgentKind;
14
+ readonly instanceId: string;
14
15
  readonly descriptor: AgentDescriptor;
15
16
  /** Absolute root this agent's transcripts live under. */
16
17
  root(): string;
@@ -0,0 +1,12 @@
1
+ import type { ToolCall, ToolFileDiff } from "./types";
2
+ export declare function toolResultMetadata(name: string, value: unknown): Pick<ToolCall, "awaitingReview" | "fileDiffs" | "additionalFileDiffs">;
3
+ export declare function formatToolFileDiff(file: ToolFileDiff): string;
4
+ export declare function toolDiffEdits(files: readonly ToolFileDiff[]): Array<{
5
+ filePath: string;
6
+ oldString: string;
7
+ newString: string;
8
+ diffLineCounts: {
9
+ add: number;
10
+ del: number;
11
+ };
12
+ }>;
@@ -1,5 +1,5 @@
1
1
  /** The agent CLIs Cogpit can drive. */
2
- export type AgentKind = "claude" | "codex" | "copilot";
2
+ export type AgentKind = "claude" | "codex" | "copilot" | "acp";
3
3
  /**
4
4
  * Every agent kind, in transcript-detection order. Codex and Copilot carry
5
5
  * positive discriminators in their records; Claude is the terminal fallback and
@@ -126,7 +126,10 @@ export interface UserMessage extends BaseMessage {
126
126
  thinkingMetadata?: {
127
127
  maxThinkingTokens: number;
128
128
  };
129
- toolUseResult?: AgentToolUseResult;
129
+ toolUseResult?: Partial<AgentToolUseResult> & {
130
+ staged?: boolean;
131
+ bashEditDiff?: unknown;
132
+ };
130
133
  sourceToolAssistantUUID?: string;
131
134
  }
132
135
  export interface TokenUsage {
@@ -200,7 +203,7 @@ export interface AgentProgressData {
200
203
  prompt: string;
201
204
  agentId: string;
202
205
  }
203
- export type HookEventName = "PreToolUse" | "PostToolUse" | "PostToolUseFailure" | "UserPromptSubmit" | "SessionStart" | "SessionEnd" | "Stop" | "StopFailure" | "SubagentStop" | "PreCompact" | "PostCompact" | "PermissionDenied" | "TaskCreated" | "WorktreeCreate" | "CwdChanged" | "FileChanged" | "Elicitation" | "ElicitationResult" | "Notification";
206
+ export type HookEventName = "PreToolUse" | "PostToolUse" | "PostToolUseFailure" | "UserPromptSubmit" | "SessionStart" | "SessionEnd" | "Stop" | "StopFailure" | "SubagentStop" | "PreCompact" | "PostCompact" | "PreModelSwitch" | "PostModelSwitch" | "PermissionDenied" | "TaskCreated" | "WorktreeCreate" | "CwdChanged" | "FileChanged" | "Elicitation" | "ElicitationResult" | "Notification";
204
207
  export interface HookProgressData {
205
208
  type: "hook_progress";
206
209
  /** Event name (newer SDK: hook_event_name; older SDK: hookEvent) */
@@ -346,6 +349,16 @@ export interface AgentSettingMessage extends BaseMessage {
346
349
  agentSetting?: string;
347
350
  }
348
351
  export type RawMessage = UserMessage | AssistantMessage | ProgressMessage | SystemMessage | FileHistorySnapshotMessage | SummaryMessage | QueueOperationMessage | AttachmentMessage;
352
+ export interface ToolFileDiff {
353
+ filePath: string;
354
+ hunks: Array<{
355
+ oldStart: number;
356
+ oldLines: number;
357
+ newStart: number;
358
+ newLines: number;
359
+ lines: string[];
360
+ }>;
361
+ }
349
362
  export interface ToolCall {
350
363
  id: string;
351
364
  name: string;
@@ -354,6 +367,14 @@ export interface ToolCall {
354
367
  /** Binary images returned by a tool, when the provider persists them. */
355
368
  resultImages?: ImageBlock[];
356
369
  isError: boolean;
370
+ awaitingReview?: boolean;
371
+ fileDiffs?: ToolFileDiff[];
372
+ additionalFileDiffs?: number;
373
+ /** Counts from one structured file diff, before combining it with later edits. */
374
+ diffLineCounts?: {
375
+ add: number;
376
+ del: number;
377
+ };
357
378
  timestamp: string;
358
379
  /**
359
380
  * A question whose tool result is only an acceptance receipt. Codex's
@@ -468,6 +489,17 @@ export type TurnContentBlock = {
468
489
  kind: "task_notification";
469
490
  content: string;
470
491
  timestamp?: string;
492
+ }
493
+ /**
494
+ * Output of a slash command that ran locally and answered the user directly
495
+ * (`/usage`, `/context`, `/recap`), recorded as a `local_command` system
496
+ * record. `content` is the raw record text, `<local-command-stdout>` tags
497
+ * included, left for the renderers like `task_notification`.
498
+ */
499
+ | {
500
+ kind: "local_command";
501
+ content: string;
502
+ timestamp?: string;
471
503
  };
472
504
  export interface Turn {
473
505
  id: string;
@@ -553,6 +585,7 @@ export interface ParsedSession {
553
585
  * of truth — the session's dirName is (see `shared/session/agents.ts`).
554
586
  */
555
587
  agentKind?: AgentKind;
588
+ instanceId?: string;
556
589
  }
557
590
  export interface ArchivedToolCall {
558
591
  type: "Edit" | "Write";
@@ -564,6 +597,8 @@ export interface ArchivedToolCall {
564
597
  }
565
598
  export interface ArchivedTurn {
566
599
  index: number;
600
+ /** The archived turn's `Turn.id`. Absent on archives written before ids were kept. */
601
+ id?: string;
567
602
  userMessage: string | null;
568
603
  toolCalls: ArchivedToolCall[];
569
604
  thinkingBlocks: string[];
@@ -574,7 +609,14 @@ export interface ArchivedTurn {
574
609
  export interface Branch {
575
610
  id: string;
576
611
  createdAt: string;
612
+ /**
613
+ * Position of the fork turn among the turns loaded when this was written.
614
+ * Only a long session's tail is loaded, so it is re-resolved from
615
+ * `branchPointTurnId` on every read and trusted as stored only without one.
616
+ */
577
617
  branchPointTurnIndex: number;
618
+ /** `Turn.id` of the last turn kept before the fork; null when none was kept. */
619
+ branchPointTurnId?: string | null;
578
620
  label: string;
579
621
  turns: ArchivedTurn[];
580
622
  jsonlLines: string[];
@@ -583,8 +625,6 @@ export interface Branch {
583
625
  }
584
626
  export interface UndoState {
585
627
  sessionId: string;
586
- currentTurnIndex: number;
587
- totalTurns: number;
588
628
  branches: Branch[];
589
629
  activeBranchId: string | null;
590
630
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cogpit-memory",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "CLI tool for Claude Code, Codex, and GitHub Copilot CLI session introspection",
5
5
  "license": "MIT",
6
6
  "repository": {
package/skill/SKILL.md CHANGED
@@ -153,7 +153,7 @@ Response shape:
153
153
  ```
154
154
 
155
155
  Key details:
156
- - `contentBlocks` kinds: `thinking`, `text`, `tool_calls`, `sub_agent`, `background_agent`, `queued_prompt`, `hook_event`, `plan_mode`, `recap`, `agent_message`
156
+ - `contentBlocks` kinds: `thinking`, `text`, `tool_calls`, `sub_agent`, `background_agent`, `queued_prompt`, `hook_event`, `plan_mode`, `recap`, `agent_message`, `task_notification`, `local_command`
157
157
  - Tool call `result` is truncated at 10,000 chars -- check `resultTruncated: true`
158
158
  - Tool call `result` may be `null` if the tool hasn't returned yet
159
159
  - Sub-agent blocks show prompt + result text. When the provider writes a separate sub-agent transcript, use Layer 3 for the full conversation
@@ -353,3 +353,11 @@ cogpit-memory index rebuild
353
353
  | Index rebuild | `cogpit-memory index rebuild` |
354
354
 
355
355
  **Default to Layer 1 only. Drill into Layer 2/3 only when you have a specific reason.**
356
+
357
+ ## Provider account identities
358
+
359
+ Sessions from configured Cogpit provider accounts use account-qualified IDs. Pass
360
+ the full ID to context and search commands. Cogpit supplies
361
+ `COGPIT_ORCHESTRATION_ROOT` to discover these profiles; set it to the app data root
362
+ when running the memory CLI independently. Inside a provider worker, introspection
363
+ uses that account’s own transcript store. ACP transcripts are supported.