cogpit-memory 0.2.1 → 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.
- package/README.md +36 -27
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +3669 -1929
- package/dist/commands/search.d.ts +1 -1
- package/dist/commands/sessions.d.ts +2 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +3247 -1579
- package/dist/lib/agent-descriptors.d.ts +425 -0
- package/dist/lib/agents.d.ts +123 -0
- package/dist/lib/claude.d.ts +49 -0
- package/dist/lib/codex-exec.d.ts +12 -0
- package/dist/lib/codex-tool-normalization.d.ts +25 -4
- package/dist/lib/codex.d.ts +40 -1
- package/dist/lib/copilot.d.ts +51 -0
- package/dist/lib/dirs.d.ts +10 -0
- package/dist/lib/helpers.d.ts +0 -20
- package/dist/lib/instances.d.ts +12 -0
- package/dist/lib/interactiveState.d.ts +9 -0
- package/dist/lib/metadata.d.ts +16 -14
- package/dist/lib/parser.d.ts +6 -4
- package/dist/lib/read-session.d.ts +1 -0
- package/dist/lib/search-index.d.ts +7 -67
- package/dist/lib/sessionStats.d.ts +2 -1
- package/dist/lib/sessionStatus.d.ts +11 -32
- package/dist/lib/stores.d.ts +48 -0
- package/dist/lib/toolResults.d.ts +12 -0
- package/dist/lib/turnBuilder.d.ts +22 -0
- package/dist/lib/turnContent.d.ts +2 -0
- package/dist/lib/types.d.ts +85 -7
- package/package.json +3 -2
- package/skill/SKILL.md +67 -49
- package/dist/lib/token-costs.d.ts +0 -27
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static facts about each agent CLI: how it names project directories and
|
|
3
|
+
* session files, and which product capabilities it supports.
|
|
4
|
+
*
|
|
5
|
+
* This module is deliberately parser-free. It imports only `./types`, so a
|
|
6
|
+
* caller that just needs a dirName codec — `server/sessionPaths.ts`, the
|
|
7
|
+
* cogpit-memory CLI, a renderer route guard — does not drag the 40 KB Codex and
|
|
8
|
+
* Copilot transcript parsers along with it. The parsing side of an agent lives
|
|
9
|
+
* behind `AgentFormat` in `./agents`.
|
|
10
|
+
*
|
|
11
|
+
* Everything here is data or pure string math. Anything needing `node:fs` is an
|
|
12
|
+
* `AgentStore` and lives in `server/agents/`, because `shared/` is bundled into
|
|
13
|
+
* the renderer and must stay free of node builtins.
|
|
14
|
+
*/
|
|
15
|
+
import { type AgentKind } from "./types";
|
|
16
|
+
export type { AgentKind } from "./types";
|
|
17
|
+
export { AGENT_KINDS } from "./types";
|
|
18
|
+
/**
|
|
19
|
+
* What a given agent can actually do. Every flag names a *capability*, never an
|
|
20
|
+
* agent — `capabilities.worktrees`, never `kind === "claude"`. That is the whole
|
|
21
|
+
* point: a fourth CLI adds one row here instead of a new arm in 40 conditionals.
|
|
22
|
+
*/
|
|
23
|
+
export interface AgentCapabilities {
|
|
24
|
+
/** Launch a session in a worktree managed by its CLI. */
|
|
25
|
+
readonly worktrees: boolean;
|
|
26
|
+
/** MCP server configuration can be passed at spawn time. */
|
|
27
|
+
readonly mcp: boolean;
|
|
28
|
+
/** `/slash` command discovery and expansion. */
|
|
29
|
+
readonly slashCommands: boolean;
|
|
30
|
+
/** The Workflow tool writes run state Cogpit can read back. */
|
|
31
|
+
readonly workflows: boolean;
|
|
32
|
+
/** Agent teams (multi-session teammates). */
|
|
33
|
+
readonly agentTeams: boolean;
|
|
34
|
+
/** Sub-agent transcripts are written as separate readable files. */
|
|
35
|
+
readonly subagentTranscripts: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Whether a sub-agent can itself own sub-agents that are addressed under its
|
|
38
|
+
* own session id. When false, every descendant is filed under the root
|
|
39
|
+
* session, so listing the root's children already yields the whole tree.
|
|
40
|
+
*/
|
|
41
|
+
readonly nestedSubagents: boolean;
|
|
42
|
+
/** Sessions can be published as read-only shares. */
|
|
43
|
+
readonly sharing: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* How a long-running goal is tracked: read back from the transcript and set
|
|
46
|
+
* with a slash command, through the CLI's own goal API, or not at all.
|
|
47
|
+
*/
|
|
48
|
+
readonly goals: "transcript" | "thread-api" | false;
|
|
49
|
+
/** Transcript-rewinding undo is available. */
|
|
50
|
+
readonly undo: boolean;
|
|
51
|
+
/** Undo history is persisted, so redo and branches work. */
|
|
52
|
+
readonly redo: boolean;
|
|
53
|
+
/** The CLI exposes its own rewind RPC (used instead of replaying edits). */
|
|
54
|
+
readonly nativeRewind: boolean;
|
|
55
|
+
/** The CLI checkpoints files itself, so undo can restore them natively. */
|
|
56
|
+
readonly fileCheckpoints: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* A reasoning-effort ladder applies by default. When false, effort levels
|
|
59
|
+
* exist only for models whose live catalog entry advertises them.
|
|
60
|
+
*/
|
|
61
|
+
readonly reasoningEffort: boolean;
|
|
62
|
+
readonly configurableContextWindow: boolean;
|
|
63
|
+
/** A faster/priority service tier can be requested. */
|
|
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;
|
|
74
|
+
/**
|
|
75
|
+
* Images may be attached when the selected model's catalog entry says nothing
|
|
76
|
+
* about its input modalities. When false, vision has to be advertised.
|
|
77
|
+
*/
|
|
78
|
+
readonly imageInput: boolean;
|
|
79
|
+
/** A message can be delivered into an already-running turn. */
|
|
80
|
+
readonly midTurnSteering: boolean;
|
|
81
|
+
/** The whole session can be shut down from the composer. */
|
|
82
|
+
readonly stopSession: boolean;
|
|
83
|
+
/** Maximum-effort orchestration mode, pinned to the top effort level. */
|
|
84
|
+
readonly ultracode: boolean;
|
|
85
|
+
/** Remaining context can be computed from the transcript. */
|
|
86
|
+
readonly contextWindow: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* A session may be driven by a process Cogpit did not start, in which case it
|
|
89
|
+
* is view-only.
|
|
90
|
+
*/
|
|
91
|
+
readonly externalProcesses: boolean;
|
|
92
|
+
/** Several logins are managed by a switcher tool Cogpit can drive. */
|
|
93
|
+
readonly accountSwitching: boolean;
|
|
94
|
+
/** The transcript records model substitutions the composer surfaces. */
|
|
95
|
+
readonly modelFallbackNotices: boolean;
|
|
96
|
+
/** Whether an unattended "auto" permission mode can be offered. */
|
|
97
|
+
readonly autoPermissionMode: "never" | "per-model" | "always";
|
|
98
|
+
readonly toolPermissionRules: boolean;
|
|
99
|
+
/** Whether composer settings take effect immediately or on the next turn. */
|
|
100
|
+
readonly settingsApply: "live" | "next-turn";
|
|
101
|
+
/**
|
|
102
|
+
* Where turn liveness is read for listings and notifications: the transcript
|
|
103
|
+
* tail, or the runtime's own turn state for a CLI whose transcript lags it.
|
|
104
|
+
*/
|
|
105
|
+
readonly turnLiveness: "transcript" | "runtime";
|
|
106
|
+
/** Partial assistant output is published on the stream bus while a turn runs. */
|
|
107
|
+
readonly tokenStreaming: boolean;
|
|
108
|
+
/** Branching is a fork RPC on the CLI rather than a copied and cut transcript. */
|
|
109
|
+
readonly nativeFork: boolean;
|
|
110
|
+
/** A title can be given to a session when it is created. */
|
|
111
|
+
readonly namedSessions: boolean;
|
|
112
|
+
}
|
|
113
|
+
export interface AgentDirNameCodec {
|
|
114
|
+
/**
|
|
115
|
+
* True when `decode(encode(cwd))` can differ from `cwd`, so a caller holding
|
|
116
|
+
* the real dirName must pass it along rather than recompute it.
|
|
117
|
+
*/
|
|
118
|
+
readonly lossy: boolean;
|
|
119
|
+
/** True when `dirName` belongs to this agent. */
|
|
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;
|
|
123
|
+
/** Encode a project path as this agent's dirName. */
|
|
124
|
+
encode(cwd: string): string;
|
|
125
|
+
/** Decode one of this agent's dirNames back to a project path. */
|
|
126
|
+
decode(dirName: string): string | null;
|
|
127
|
+
}
|
|
128
|
+
export interface AgentSessionFileCodec {
|
|
129
|
+
/** Path of a session's transcript, relative to this agent's sessions root. */
|
|
130
|
+
name(sessionId: string, at?: Date): string;
|
|
131
|
+
/** Inverse of `name`: recover the session id, or null if unrecognised. */
|
|
132
|
+
sessionId(fileName: string): string | null;
|
|
133
|
+
/**
|
|
134
|
+
* The identifier a URL carries for this transcript. Agents whose on-disk name
|
|
135
|
+
* is rebuildable from the id shorten to it; the rest keep the relative path,
|
|
136
|
+
* because a date-nested rollout cannot be recovered from its uuid alone.
|
|
137
|
+
*/
|
|
138
|
+
urlId(fileName: string): string;
|
|
139
|
+
/** Exact inverse of `urlId`. */
|
|
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;
|
|
157
|
+
}
|
|
158
|
+
export interface AgentResumeCodec {
|
|
159
|
+
/** Copy-pasteable shell command that reopens the session in a terminal. */
|
|
160
|
+
command(sessionId: string, cwd?: string): string;
|
|
161
|
+
/** argv handed to `binName` when Cogpit spawns the resume itself. */
|
|
162
|
+
args(sessionId: string): readonly string[];
|
|
163
|
+
}
|
|
164
|
+
export interface AgentHomebrewFormula {
|
|
165
|
+
readonly name: string;
|
|
166
|
+
/** Casks ship a prebuilt app and need `brew upgrade --cask`. */
|
|
167
|
+
readonly cask: boolean;
|
|
168
|
+
}
|
|
169
|
+
export interface AgentSelfUpdate {
|
|
170
|
+
/** argv appended to `binName` to make the CLI update itself. */
|
|
171
|
+
readonly args: readonly string[];
|
|
172
|
+
/**
|
|
173
|
+
* Whether a resolved binary path belongs to a self-managing install.
|
|
174
|
+
*
|
|
175
|
+
* Paths arrive lowercased and forward-slashed, because the caller normalises
|
|
176
|
+
* every candidate before any predicate runs — these are written against that
|
|
177
|
+
* form and comparing a raw Windows path here would never match.
|
|
178
|
+
*/
|
|
179
|
+
matches(normalizedPath: string): boolean;
|
|
180
|
+
}
|
|
181
|
+
/** How a CLI shows up in the machine's process listing. */
|
|
182
|
+
export interface AgentProcessMatch {
|
|
183
|
+
/**
|
|
184
|
+
* `"command"`: any command line naming the binary is this CLI, launchers and
|
|
185
|
+
* helpers included. `"executable"`: only a process whose executable is the
|
|
186
|
+
* binary, for a name too common to match loosely.
|
|
187
|
+
*/
|
|
188
|
+
readonly by: "command" | "executable";
|
|
189
|
+
/** The session a resumed process was started with, read from its command line. */
|
|
190
|
+
sessionIdFromCommand(command: string): string | null;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Everything Cogpit needs to find, version and upgrade one CLI, plus where that
|
|
194
|
+
* CLI keeps its own state. Cogpit drives whatever the user installed, so all of
|
|
195
|
+
* this is discovery, never assumption — the one exception being a CLI that
|
|
196
|
+
* Cogpit's own dependencies happen to ship a copy of.
|
|
197
|
+
*/
|
|
198
|
+
export interface AgentCli {
|
|
199
|
+
/** npm package publishing this CLI. */
|
|
200
|
+
readonly packageName: string;
|
|
201
|
+
readonly homebrew: AgentHomebrewFormula | null;
|
|
202
|
+
readonly wingetId: string | null;
|
|
203
|
+
/** argv that makes the CLI print its version. */
|
|
204
|
+
readonly versionArgs: readonly string[];
|
|
205
|
+
readonly selfUpdate: AgentSelfUpdate | null;
|
|
206
|
+
/**
|
|
207
|
+
* Whether an SDK in Cogpit's dependency tree vendors its own copy of this
|
|
208
|
+
* CLI. That is the only case where there is more than one binary to choose
|
|
209
|
+
* from, so it is what enables the executable picker.
|
|
210
|
+
*/
|
|
211
|
+
readonly bundledBySdk: boolean;
|
|
212
|
+
/** Directory holding the CLI's own state, relative to the user's home. */
|
|
213
|
+
readonly homeDirName: string;
|
|
214
|
+
/** Environment variable overriding that directory, if the CLI has one. */
|
|
215
|
+
readonly homeEnvVar: string | null;
|
|
216
|
+
readonly sdkHomeEnvVar?: string;
|
|
217
|
+
/**
|
|
218
|
+
* Path inside the home whose existence proves the CLI has been used. Empty
|
|
219
|
+
* when the home directory itself is the evidence.
|
|
220
|
+
*/
|
|
221
|
+
readonly installMarker: string;
|
|
222
|
+
/**
|
|
223
|
+
* Whether Cogpit can locate this home on its own. Claude's is a user-chosen
|
|
224
|
+
* path stored in Cogpit's own configuration, so it cannot be discovered when
|
|
225
|
+
* that configuration is missing; the rest sit at a fixed, env-overridable
|
|
226
|
+
* location and can be found on a first run.
|
|
227
|
+
*/
|
|
228
|
+
readonly requiresConfiguration?: boolean;
|
|
229
|
+
readonly homeIsDiscoverable: boolean;
|
|
230
|
+
readonly process: AgentProcessMatch;
|
|
231
|
+
}
|
|
232
|
+
export type AgentConfigScope = "global" | "project";
|
|
233
|
+
export interface AgentConfigFile {
|
|
234
|
+
/**
|
|
235
|
+
* `"scope"` places the file beside the config root — in the user's home
|
|
236
|
+
* directory or at the project root — and `"root"` places it inside it.
|
|
237
|
+
*/
|
|
238
|
+
readonly in: "scope" | "root";
|
|
239
|
+
/** Path relative to whichever of those two directories `in` names. */
|
|
240
|
+
readonly path: string;
|
|
241
|
+
/** Scopes at which this file exists. */
|
|
242
|
+
readonly scopes: readonly AgentConfigScope[];
|
|
243
|
+
/** Tree label; defaults to `path`. */
|
|
244
|
+
readonly label?: string;
|
|
245
|
+
/** Read only when none of the agent's other instruction files exist at that scope. */
|
|
246
|
+
readonly fallback?: true;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Where a CLI reads its instructions, settings and extensions from. Drives the
|
|
250
|
+
* config browser, so an agent missing a row here is an agent the user cannot
|
|
251
|
+
* configure through Cogpit at all.
|
|
252
|
+
*/
|
|
253
|
+
export interface AgentConfigLayout {
|
|
254
|
+
/** Directory the CLI reads config from, under the home or a project root. */
|
|
255
|
+
readonly rootDirName: string;
|
|
256
|
+
readonly instructions: readonly AgentConfigFile[];
|
|
257
|
+
readonly settings: readonly AgentConfigFile[];
|
|
258
|
+
/** Directories inside the config root; null when the CLI has no such concept. */
|
|
259
|
+
readonly skillsDir: string | null;
|
|
260
|
+
readonly agentsDir: string | null;
|
|
261
|
+
/** Codex calls these "prompts". */
|
|
262
|
+
readonly commandsDir: string | null;
|
|
263
|
+
/** Global-only theme directory, if the CLI has one. */
|
|
264
|
+
readonly themesDir: string | null;
|
|
265
|
+
/** Global-only installed-plugin tree, if the CLI has one. */
|
|
266
|
+
readonly pluginsDir: string | null;
|
|
267
|
+
/**
|
|
268
|
+
* Marker directory holding `plugin.json` inside a plugin the CLI loads from a
|
|
269
|
+
* path, and so the shape Cogpit writes its own local plugin in. Null for a
|
|
270
|
+
* CLI that has no plugins and reads `skillsDir` alone.
|
|
271
|
+
*/
|
|
272
|
+
readonly pluginManifestDir: string | null;
|
|
273
|
+
}
|
|
274
|
+
export interface AgentContextWindow {
|
|
275
|
+
/** Window assumed for a model with no entry in `limits`. */
|
|
276
|
+
readonly defaultLimit: number;
|
|
277
|
+
/** Model-id substrings with a different window; first match wins. */
|
|
278
|
+
readonly limits: readonly {
|
|
279
|
+
readonly match: string;
|
|
280
|
+
readonly limit: number;
|
|
281
|
+
}[];
|
|
282
|
+
/**
|
|
283
|
+
* Headroom the CLI reserves before it auto-compacts. Zero for a CLI that
|
|
284
|
+
* decides its own compaction point, so applying another's reserve would
|
|
285
|
+
* report a session as nearly full while it still has room.
|
|
286
|
+
*/
|
|
287
|
+
readonly compactBuffer: number;
|
|
288
|
+
/** Model-id marker requesting an extended window, if the CLI has one. */
|
|
289
|
+
readonly extendedContext: {
|
|
290
|
+
readonly marker: string;
|
|
291
|
+
readonly limit: number;
|
|
292
|
+
} | null;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Cogpit's access picker, as it crosses the wire. Every field is optional
|
|
296
|
+
* because it arrives from a client that may be older than the server; each
|
|
297
|
+
* agent maps it onto its own policy vocabulary.
|
|
298
|
+
*/
|
|
299
|
+
export interface PermissionsConfig {
|
|
300
|
+
mode?: string;
|
|
301
|
+
allowedTools?: string[];
|
|
302
|
+
disallowedTools?: string[];
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* argv Cogpit appends to `binName` when it launches a turn. Each function
|
|
306
|
+
* returns an empty array when the setting was not chosen, so call sites spread
|
|
307
|
+
* them unconditionally.
|
|
308
|
+
*/
|
|
309
|
+
export interface AgentLaunchArgs {
|
|
310
|
+
/** Translate the access picker into this CLI's own policy flags. */
|
|
311
|
+
permissions(permissions?: PermissionsConfig): string[];
|
|
312
|
+
model(model?: string): string[];
|
|
313
|
+
effort(effort?: string): string[];
|
|
314
|
+
fastTier(enabled?: boolean): string[];
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* The wire values a "fast mode" request carries.
|
|
318
|
+
*
|
|
319
|
+
* An agent Cogpit drives two ways can want two different spellings of the same
|
|
320
|
+
* intent, and they are recorded separately rather than merged, because only the
|
|
321
|
+
* CLI itself can say which is right.
|
|
322
|
+
*/
|
|
323
|
+
export interface AgentServiceTier {
|
|
324
|
+
/** `serviceTier` sent over a persistent RPC/app-server connection. */
|
|
325
|
+
readonly appServerValue: string;
|
|
326
|
+
/** `service_tier` written into the CLI's own config when it is spawned. */
|
|
327
|
+
readonly cliConfigValue: string;
|
|
328
|
+
}
|
|
329
|
+
export interface EffortOption {
|
|
330
|
+
value: string;
|
|
331
|
+
label: string;
|
|
332
|
+
description?: string;
|
|
333
|
+
}
|
|
334
|
+
export interface ServiceTierOption {
|
|
335
|
+
value: string;
|
|
336
|
+
label: string;
|
|
337
|
+
description?: string;
|
|
338
|
+
}
|
|
339
|
+
/** One row of a model picker, and one entry of the `/api/models` catalog. */
|
|
340
|
+
export interface ModelOption {
|
|
341
|
+
value: string;
|
|
342
|
+
label: string;
|
|
343
|
+
description?: string;
|
|
344
|
+
/** Canonical wire model id this option resolves to (`""` is the default). */
|
|
345
|
+
resolvedModel?: string;
|
|
346
|
+
isDefault?: boolean;
|
|
347
|
+
defaultReasoningEffort?: string;
|
|
348
|
+
supportedReasoningEfforts?: EffortOption[];
|
|
349
|
+
inputModalities?: string[];
|
|
350
|
+
supportsPersonality?: boolean;
|
|
351
|
+
serviceTiers?: ServiceTierOption[];
|
|
352
|
+
availabilityMessage?: string;
|
|
353
|
+
supportsEffort?: boolean;
|
|
354
|
+
supportsAdaptiveThinking?: boolean;
|
|
355
|
+
supportsAutoMode?: boolean;
|
|
356
|
+
}
|
|
357
|
+
export interface AgentDescriptor {
|
|
358
|
+
readonly kind: AgentKind;
|
|
359
|
+
/** Human-facing product name. */
|
|
360
|
+
readonly displayName: string;
|
|
361
|
+
/** Executable name, as invoked on PATH. */
|
|
362
|
+
readonly binName: string;
|
|
363
|
+
readonly dirName: AgentDirNameCodec;
|
|
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;
|
|
367
|
+
/**
|
|
368
|
+
* Whether header metadata is complete after reading only the first bytes of a
|
|
369
|
+
* transcript. False when an exact line count or the last user message matters
|
|
370
|
+
* and nothing short of a full read can produce them, which is what makes a
|
|
371
|
+
* partial head read a false economy for that agent.
|
|
372
|
+
*/
|
|
373
|
+
readonly metadataFromHead: boolean;
|
|
374
|
+
readonly resume: AgentResumeCodec;
|
|
375
|
+
readonly launchArgs: AgentLaunchArgs;
|
|
376
|
+
/** Null for an agent with no faster tier, or one that asks for it some other way. */
|
|
377
|
+
readonly serviceTier: AgentServiceTier | null;
|
|
378
|
+
readonly cli: AgentCli;
|
|
379
|
+
readonly config: AgentConfigLayout;
|
|
380
|
+
readonly contextWindow: AgentContextWindow;
|
|
381
|
+
readonly capabilities: AgentCapabilities;
|
|
382
|
+
}
|
|
383
|
+
/** True when a process listing's command line belongs to this CLI. */
|
|
384
|
+
export declare function matchesAgentProcess(descriptor: AgentDescriptor, command: string): boolean;
|
|
385
|
+
/** The `Win32_Process` name filter that lists this CLI, for `Get-CimInstance`. */
|
|
386
|
+
export declare function windowsProcessNameFilter(descriptor: AgentDescriptor): string;
|
|
387
|
+
export declare const CODEX_DIR_PREFIX = "codex__";
|
|
388
|
+
export declare const COPILOT_DIR_PREFIX = "copilot__";
|
|
389
|
+
export declare function descriptorFor(kind: AgentKind): AgentDescriptor;
|
|
390
|
+
export declare function allDescriptors(): readonly AgentDescriptor[];
|
|
391
|
+
/**
|
|
392
|
+
* The one agent with a given property, for code that only makes sense while
|
|
393
|
+
* exactly one has it — the configured home, the slash-command palette. Fails
|
|
394
|
+
* loudly on zero or several rather than silently picking the first, so a
|
|
395
|
+
* capability a second agent gains is noticed instead of misrouted.
|
|
396
|
+
*/
|
|
397
|
+
export declare function soleDescriptorWhere(predicate: (descriptor: AgentDescriptor) => boolean, what: string): AgentDescriptor;
|
|
398
|
+
/**
|
|
399
|
+
* Resolve the agent that owns a project dirName.
|
|
400
|
+
*
|
|
401
|
+
* This is Cogpit's **source of truth** for "which agent is this session?". It
|
|
402
|
+
* beats content sniffing because it is known before a single byte is read, it
|
|
403
|
+
* is what the URL carries, and it is what every write path (spawn, undo,
|
|
404
|
+
* branch) already keys on. `ParsedSession.agentKind` is the parser's own
|
|
405
|
+
* finding and is used to cross-check, never to override.
|
|
406
|
+
*
|
|
407
|
+
* Falls back to Claude for a null/unknown dirName, matching the historical
|
|
408
|
+
* behaviour every caller depends on.
|
|
409
|
+
*/
|
|
410
|
+
export declare function descriptorForDirName(dirName: string | null | undefined): AgentDescriptor;
|
|
411
|
+
/** Convenience wrapper: the agent kind owning a project dirName. */
|
|
412
|
+
export declare function agentKindForDirName(dirName: string | null | undefined): AgentKind;
|
|
413
|
+
/**
|
|
414
|
+
* Encode a project path for a given agent. An agent whose encoding is lossy
|
|
415
|
+
* cannot recover its canonical dirName from the path, so a caller that already
|
|
416
|
+
* holds it passes it as `knownDirName` rather than let it be re-derived.
|
|
417
|
+
*/
|
|
418
|
+
export declare function projectDirNameFor(kind: AgentKind, cwd: string, knownDirName?: string): string;
|
|
419
|
+
/** True when `fileName` looks like a session transcript for `kind`. */
|
|
420
|
+
export declare function sessionIdFromFileName(kind: AgentKind, fileName: string): string | null;
|
|
421
|
+
/** True when `value` is a bare session UUID. */
|
|
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";
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { type CodexExecCall } from "./codex-exec";
|
|
2
|
+
import { type AgentKind } from "./types";
|
|
3
|
+
import type { ParseSessionOptions, ParsedSession, RawRecord, SessionStatusInfo } from "./types";
|
|
4
|
+
import type { AgentDescriptor } from "./agent-descriptors";
|
|
5
|
+
export type { AgentCapabilities, AgentDescriptor, AgentDirNameCodec, AgentKind, AgentSessionFileCodec, } from "./agent-descriptors";
|
|
6
|
+
export { AGENT_KINDS, CODEX_DIR_PREFIX, COPILOT_DIR_PREFIX, agentKindForDirName, allDescriptors, descriptorFor, descriptorForDirName, isSessionUuid, projectDirNameFor, sessionIdFromFileName, } from "./agent-descriptors";
|
|
7
|
+
/**
|
|
8
|
+
* Header metadata every format can report from the first lines of a transcript.
|
|
9
|
+
*
|
|
10
|
+
* Individual formats return supersets of this (turn counts, first/last user
|
|
11
|
+
* message, sub-agent parentage); the registry narrows them to the fields all
|
|
12
|
+
* three agree on.
|
|
13
|
+
*/
|
|
14
|
+
export interface AgentSessionMetadata {
|
|
15
|
+
sessionId: string;
|
|
16
|
+
version: string;
|
|
17
|
+
gitBranch: string;
|
|
18
|
+
cwd: string;
|
|
19
|
+
slug: string;
|
|
20
|
+
name: string;
|
|
21
|
+
model: string;
|
|
22
|
+
branchedFrom?: {
|
|
23
|
+
sessionId: string;
|
|
24
|
+
turnIndex?: number | null;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export interface AgentFormat {
|
|
28
|
+
readonly kind: AgentKind;
|
|
29
|
+
readonly descriptor: AgentDescriptor;
|
|
30
|
+
/** True when this JSONL text was produced by this CLI. */
|
|
31
|
+
detectsText(jsonlText: string): boolean;
|
|
32
|
+
/** True when these already-parsed raw records came from this CLI. */
|
|
33
|
+
detectsRecords(records: readonly {
|
|
34
|
+
type?: unknown;
|
|
35
|
+
}[]): boolean;
|
|
36
|
+
/** Full parse into the shared session model. */
|
|
37
|
+
parse(jsonlText: string, options?: ParseSessionOptions): ParsedSession;
|
|
38
|
+
/** Incremental append of newly written lines. */
|
|
39
|
+
append(existing: ParsedSession, newJsonlText: string): ParsedSession;
|
|
40
|
+
/** Cheap header metadata read from already-split transcript lines. */
|
|
41
|
+
metadataFromLines(lines: readonly string[]): AgentSessionMetadata;
|
|
42
|
+
/** Status derived from raw records. */
|
|
43
|
+
status(records: readonly RawRecord[]): SessionStatusInfo;
|
|
44
|
+
/**
|
|
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.
|
|
50
|
+
*/
|
|
51
|
+
brandBranch(firstRecord: Record<string, unknown>, sessionId: string, turnIndex: number | null, branchedAt: number): {
|
|
52
|
+
record: Record<string, unknown>;
|
|
53
|
+
originalId: string;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Indexes of the records at which a turn starts, in file order.
|
|
57
|
+
*
|
|
58
|
+
* The **one** implementation of turn-boundary detection. Undo cuts a
|
|
59
|
+
* transcript by computing `keepLines` on the client and having the server
|
|
60
|
+
* verify and apply it; if the two sides scanned for boundaries separately
|
|
61
|
+
* and ever disagreed by one line, the result would be a silently corrupted
|
|
62
|
+
* transcript rather than an error. They cannot disagree while they both call
|
|
63
|
+
* this.
|
|
64
|
+
*/
|
|
65
|
+
turnBoundaries(records: readonly Record<string, unknown>[]): number[];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The kind that claims any transcript nothing else did. Detection order makes
|
|
69
|
+
* it the last entry of `AGENT_KINDS`; callers that need to know whether
|
|
70
|
+
* detection actually matched compare against this.
|
|
71
|
+
*/
|
|
72
|
+
export declare const TERMINAL_AGENT_KIND: AgentKind;
|
|
73
|
+
export declare function formatFor(kind: AgentKind): AgentFormat;
|
|
74
|
+
export declare function formatForText(jsonlText: string): AgentFormat;
|
|
75
|
+
export declare function formatForRecords(records: readonly {
|
|
76
|
+
type?: unknown;
|
|
77
|
+
}[]): AgentFormat;
|
|
78
|
+
/**
|
|
79
|
+
* Boundary indexes for raw JSONL lines rather than parsed records.
|
|
80
|
+
*
|
|
81
|
+
* A malformed line stands in as an empty record so indexes keep matching the
|
|
82
|
+
* caller's line array — every consumer here slices that array by the returned
|
|
83
|
+
* index, so dropping a line would shift the cut.
|
|
84
|
+
*/
|
|
85
|
+
export declare function turnBoundaryLines(format: AgentFormat, lines: readonly string[]): number[];
|
|
86
|
+
/**
|
|
87
|
+
* The line to cut at to keep exactly `keepTurnCount` turns — i.e. where the
|
|
88
|
+
* turn after the last kept one begins. `lines.length` when the transcript has
|
|
89
|
+
* no more turns than that, meaning nothing is removed.
|
|
90
|
+
*/
|
|
91
|
+
export declare function cutLineForTurnCount(format: AgentFormat, lines: readonly string[], keepTurnCount: number): number;
|
|
92
|
+
/**
|
|
93
|
+
* The line to cut at to keep everything through turn `turnIndex`, or null when
|
|
94
|
+
* that turn is the last one and there is nothing to remove.
|
|
95
|
+
*/
|
|
96
|
+
export declare function cutLineAfterTurnIndex(format: AgentFormat, lines: readonly string[], turnIndex: number): number | null;
|
|
97
|
+
/**
|
|
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.
|
|
101
|
+
*
|
|
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
|
+
*/
|
|
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;
|
|
117
|
+
/**
|
|
118
|
+
* The tool calls a recorded call actually ran. Every format records one call
|
|
119
|
+
* per tool use except Codex code mode, which persists a whole script of them as
|
|
120
|
+
* a single `exec` call, so readers that care about what ran — rather than how
|
|
121
|
+
* it is presented — expand a call here instead of learning that grammar.
|
|
122
|
+
*/
|
|
123
|
+
export declare function expandToolScript<T extends CodexExecCall>(call: T): CodexExecCall[];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ParseSessionOptions, ParsedSession, RawRecord, SessionStatusInfo } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Unconditionally true: this is the terminal arm of transcript detection.
|
|
4
|
+
*
|
|
5
|
+
* Detection ORDER is the contract. `AGENT_KINDS` ends with this format, so a
|
|
6
|
+
* transcript only reaches it after every positively-discriminated format has
|
|
7
|
+
* declined it. Spelling the fallback out as "not the others" instead would make
|
|
8
|
+
* each new CLI edit this function and re-introduce the mutual references the
|
|
9
|
+
* registry exists to remove.
|
|
10
|
+
*/
|
|
11
|
+
export declare function isClaudeSessionText(): boolean;
|
|
12
|
+
export interface ClaudeMetadata {
|
|
13
|
+
sessionId: string;
|
|
14
|
+
version: string;
|
|
15
|
+
gitBranch: string;
|
|
16
|
+
cwd: string;
|
|
17
|
+
slug: string;
|
|
18
|
+
name: string;
|
|
19
|
+
model: string;
|
|
20
|
+
branchedFrom?: {
|
|
21
|
+
sessionId: string;
|
|
22
|
+
turnIndex?: number | null;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/** Cheap header metadata read from already-split transcript lines. */
|
|
26
|
+
export declare function extractClaudeMetadataFromLines(lines: readonly string[]): ClaudeMetadata;
|
|
27
|
+
export declare function parseClaudeSession(jsonlText: string, options?: ParseSessionOptions): ParsedSession;
|
|
28
|
+
/**
|
|
29
|
+
* Incrementally append new JSONL lines to an existing parsed session.
|
|
30
|
+
* Avoids re-parsing all turns from scratch — only re-processes the last
|
|
31
|
+
* (potentially incomplete) turn and any new messages.
|
|
32
|
+
*/
|
|
33
|
+
export declare function appendClaudeSession(existing: ParsedSession, newJsonlText: string): ParsedSession;
|
|
34
|
+
/** Derive status by walking backward through Claude Code's JSONL records. */
|
|
35
|
+
export declare function deriveClaudeSessionStatus(rawMessages: readonly RawRecord[]): SessionStatusInfo;
|
|
36
|
+
/** A branch keeps the header record, renamed and pointed back at its origin. */
|
|
37
|
+
export declare function brandClaudeBranch(firstRecord: Record<string, unknown>, sessionId: string, turnIndex: number | null): {
|
|
38
|
+
record: Record<string, unknown>;
|
|
39
|
+
originalId: string;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Indexes of the records that start a turn.
|
|
43
|
+
*
|
|
44
|
+
* A user record that carries *any* `tool_result` block is a continuation of the
|
|
45
|
+
* turn in flight, not a new one — the CLI writes tool results back as user
|
|
46
|
+
* records. This predicate is the one the client and the server both cut on, so
|
|
47
|
+
* an off-by-one here writes a corrupted transcript rather than failing loudly.
|
|
48
|
+
*/
|
|
49
|
+
export declare function claudeTurnBoundaries(records: readonly Record<string, unknown>[]): number[];
|
package/dist/lib/codex-exec.d.ts
CHANGED
|
@@ -7,6 +7,10 @@ export interface CodexExecInvocation {
|
|
|
7
7
|
argumentSource: string;
|
|
8
8
|
startIndex: number;
|
|
9
9
|
}
|
|
10
|
+
export interface CodexExecCall {
|
|
11
|
+
name: string;
|
|
12
|
+
input: Record<string, unknown>;
|
|
13
|
+
}
|
|
10
14
|
export declare function readJsStringLiteral(source: string, startIndex: number): JsStringLiteral | null;
|
|
11
15
|
/**
|
|
12
16
|
* Extract actual tool invocations from a Codex code-mode `exec` script.
|
|
@@ -25,3 +29,11 @@ export declare function extractJsStringPropertyValues(source: string, property:
|
|
|
25
29
|
export declare function extractJsNumberPropertyValues(source: string, property: string): number[];
|
|
26
30
|
/** Count top-level entries in an array literal, or one for a non-empty value. */
|
|
27
31
|
export declare function countJsCollectionEntries(source: string): number;
|
|
32
|
+
/** Codex code mode wraps a whole orchestration script in one `exec` tool call. */
|
|
33
|
+
export declare function isCodexExecCall<T extends CodexExecCall>(call: T): call is T & {
|
|
34
|
+
input: {
|
|
35
|
+
raw: string;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
/** Decode literal call inputs for presentation; expressions remain available as source. */
|
|
39
|
+
export declare function getCodexExecCalls(input: Record<string, unknown>): CodexExecCall[];
|
|
@@ -1,14 +1,35 @@
|
|
|
1
|
+
import type { ImageBlock } from "./types";
|
|
1
2
|
/**
|
|
2
3
|
* Normalize a Codex function name to its canonical collaboration-tool name.
|
|
3
4
|
* Current collaboration tools carry a namespace separately, but older and
|
|
4
5
|
* transitional rollouts also encoded it in the function name itself.
|
|
5
6
|
*/
|
|
6
7
|
export declare function normalizeFunctionName(rawName: string): string;
|
|
7
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Read the exit code Codex's shell wrapper prints into its own output. Nothing
|
|
10
|
+
* else in the text decides failure: a command that prints source files, HTML or
|
|
11
|
+
* logs carries words like "error" that say nothing about how it ended.
|
|
12
|
+
*/
|
|
13
|
+
export declare function hasFailedExit(output: string | null): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Names Codex gives the tool that asks the user a question, both the blocking
|
|
16
|
+
* form and the `_async` one that returns a receipt and keeps working.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isCodexQuestionTool(name: string): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Normalize Codex request_user_input input to AskUserQuestion format.
|
|
21
|
+
*
|
|
22
|
+
* Codex writes a question as `{ title, options: string[] | null }`, where the
|
|
23
|
+
* options are bare labels rather than the `{ label, description }` objects the
|
|
24
|
+
* question card renders.
|
|
25
|
+
*/
|
|
26
|
+
export declare function normalizeQuestions(input: Record<string, unknown>): Record<string, unknown>;
|
|
8
27
|
/** Normalize Codex update_plan input to TodoWrite format. */
|
|
9
28
|
export declare function normalizePlanToTodos(input: Record<string, unknown>): Record<string, unknown>;
|
|
10
|
-
|
|
11
|
-
export declare function parseCustomToolOutput(output: unknown): {
|
|
29
|
+
export interface CodexToolOutput {
|
|
12
30
|
text: string;
|
|
13
31
|
isError: boolean;
|
|
14
|
-
|
|
32
|
+
images?: ImageBlock[];
|
|
33
|
+
}
|
|
34
|
+
/** Read function, custom-tool, and MCP output without losing structured images. */
|
|
35
|
+
export declare function parseCustomToolOutput(output: unknown): CodexToolOutput;
|