@lmctl-ai/lmctl 0.1.342 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +116 -0
- package/README.md +11 -51
- package/data/price-aliases.json +1 -0
- package/data/price-data.json +1 -1
- package/dist/cli/batch.d.ts +2 -0
- package/dist/cli/busy-evidence.d.ts +27 -0
- package/dist/cli/chat-cycle.d.ts +59 -0
- package/dist/cli/chat-exec.d.ts +83 -0
- package/dist/cli/chat-policy.d.ts +22 -0
- package/dist/cli/chat.d.ts +23 -0
- package/dist/cli/code-resolver.d.ts +8 -0
- package/dist/cli/common.d.ts +54 -0
- package/dist/cli/context.d.ts +12 -0
- package/dist/cli/cost.d.ts +2 -0
- package/dist/cli/export.d.ts +2 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +1062 -1400
- package/dist/cli/infra.d.ts +78 -0
- package/dist/cli/ls.d.ts +2 -0
- package/dist/cli/mailbox-cli.d.ts +6 -0
- package/dist/cli/managed.d.ts +62 -0
- package/dist/cli/mcp-install.d.ts +23 -0
- package/dist/cli/mcp.d.ts +2 -0
- package/dist/cli/paths.d.ts +50 -0
- package/dist/cli/plan-wizard.d.ts +42 -0
- package/dist/cli/plan.d.ts +20 -0
- package/dist/cli/queue-delivery.d.ts +107 -0
- package/dist/cli/ratelimit.d.ts +3 -0
- package/dist/cli/remote-fetch.d.ts +7 -0
- package/dist/cli/run-queries.d.ts +82 -0
- package/dist/cli/running.d.ts +5 -0
- package/dist/cli/schema.sql +20 -1
- package/dist/cli/script.d.ts +6 -0
- package/dist/cli/script.js +2097 -0
- package/dist/cli/self-identity.d.ts +25 -0
- package/dist/cli/session-idle.d.ts +118 -0
- package/dist/cli/status.d.ts +22 -0
- package/dist/cli/tail.d.ts +18 -0
- package/dist/cli/target-resolver.d.ts +139 -0
- package/dist/cli/team-status.d.ts +91 -0
- package/dist/cli/teamfile-compat.d.ts +71 -0
- package/dist/cli/teamfile-store.d.ts +18 -0
- package/dist/cli/teamfile-template.d.ts +40 -0
- package/dist/cli/template-env.d.ts +25 -0
- package/dist/cli/terminal-lock.d.ts +25 -0
- package/dist/cli/terminal.d.ts +23 -0
- package/dist/cli/time-filter.d.ts +10 -0
- package/dist/cli/update-notifier.d.ts +11 -0
- package/dist/cli/version-util.d.ts +2 -0
- package/dist/config/seed_templates.d.ts +1 -0
- package/dist/db/backfill-event-log.d.ts +26 -0
- package/dist/db/delegation.d.ts +37 -0
- package/dist/db/escalation_vocab.d.ts +45 -0
- package/dist/db/event-log.d.ts +40 -0
- package/dist/db/index.d.ts +1213 -0
- package/dist/db/inflight-acquire.d.ts +70 -0
- package/dist/db/invocation-event-projections.d.ts +65 -0
- package/dist/db/ledger-identity.test.d.ts +1 -0
- package/dist/db/live-event-projections.d.ts +92 -0
- package/dist/db/migrate.d.ts +51 -0
- package/dist/db/open-readonly.d.ts +2 -0
- package/dist/db/process-liveness.d.ts +37 -0
- package/dist/db/sqlite.d.ts +47 -0
- package/dist/db/sqlite.test.d.ts +1 -0
- package/dist/db/terminal-lock.d.ts +14 -0
- package/dist/db/tracked-invocation-reconcile.d.ts +52 -0
- package/dist/db/tracked-invocation.d.ts +116 -0
- package/dist/mcp/dispatch.d.ts +45 -0
- package/dist/mcp/mediation.d.ts +128 -0
- package/dist/mcp/path-warning.d.ts +7 -0
- package/dist/mcp/server.d.ts +63 -0
- package/dist/mcp/tools.d.ts +24 -0
- package/dist/providers/adapter.d.ts +265 -0
- package/dist/providers/adapters/agy-adapter.d.ts +17 -0
- package/dist/providers/adapters/claude-adapter.d.ts +20 -0
- package/dist/providers/adapters/codex-adapter.d.ts +22 -0
- package/dist/providers/adapters/copilot-adapter.d.ts +16 -0
- package/dist/providers/adapters/dsh-adapter.d.ts +89 -0
- package/dist/providers/adapters/gemini-adapter.d.ts +44 -0
- package/dist/providers/adapters/hermes-adapter.d.ts +71 -0
- package/dist/providers/adapters/index.d.ts +35 -0
- package/dist/providers/adapters/kimi-adapter.d.ts +16 -0
- package/dist/providers/adapters/lmplayer-adapter.d.ts +14 -0
- package/dist/providers/adapters/opencode-adapter.d.ts +11 -0
- package/dist/providers/adapters/pi-adapter.d.ts +17 -0
- package/dist/providers/adapters/print-turn.d.ts +29 -0
- package/dist/providers/adapters/qwen-adapter.d.ts +11 -0
- package/dist/providers/agy.d.ts +32 -0
- package/dist/providers/base-adapter.d.ts +107 -0
- package/dist/providers/base.d.ts +12 -0
- package/dist/providers/binary-check.d.ts +102 -0
- package/dist/providers/bundle.d.ts +118 -0
- package/dist/providers/claude-mock.d.ts +90 -0
- package/dist/providers/claude.d.ts +21 -0
- package/dist/providers/codex-config.d.ts +15 -0
- package/dist/providers/codex.d.ts +25 -0
- package/dist/providers/config/model-selection-lint.d.ts +45 -0
- package/dist/providers/config/team-config.d.ts +183 -0
- package/dist/providers/config/types.d.ts +146 -0
- package/dist/providers/config/user-config.d.ts +88 -0
- package/dist/providers/copilot.d.ts +21 -0
- package/dist/providers/debug.d.ts +128 -0
- package/dist/providers/dsh-sidecar.d.ts +65 -0
- package/dist/providers/dsh-zstd.d.ts +27 -0
- package/dist/providers/dsh.d.ts +37 -0
- package/dist/providers/frontmatter.d.ts +46 -0
- package/dist/providers/gemini-session-reader.d.ts +151 -0
- package/dist/providers/gemini.d.ts +39 -0
- package/dist/providers/hermes.d.ts +90 -0
- package/dist/providers/index.d.ts +421 -0
- package/dist/providers/kimi.d.ts +37 -0
- package/dist/providers/model-catalog.d.ts +141 -0
- package/dist/providers/one-shot-timeout.d.ts +1 -0
- package/dist/providers/opencode-config.d.ts +62 -0
- package/dist/providers/opencode.d.ts +40 -0
- package/dist/providers/parsers/acp.d.ts +47 -0
- package/dist/providers/parsers/codex-app-server.d.ts +60 -0
- package/dist/providers/parsers/stream-json.d.ts +57 -0
- package/dist/providers/passive-readers/agy.d.ts +155 -0
- package/dist/providers/passive-readers/copilot.d.ts +173 -0
- package/dist/providers/passive-readers/dsh.d.ts +34 -0
- package/dist/providers/passive-readers/kimi.d.ts +44 -0
- package/dist/providers/passive-readers/opencode.d.ts +89 -0
- package/dist/providers/passive-readers/qwen.d.ts +151 -0
- package/dist/providers/passive-readers/shared.d.ts +138 -0
- package/dist/providers/passive-session-reader.d.ts +188 -0
- package/dist/providers/path-utils.d.ts +1 -0
- package/dist/providers/pi.d.ts +32 -0
- package/dist/providers/pricing.d.ts +107 -0
- package/dist/providers/qwen.d.ts +22 -0
- package/dist/providers/ratelimit-readings.d.ts +32 -0
- package/dist/providers/registry.d.ts +31 -0
- package/dist/providers/resume.d.ts +43 -0
- package/dist/providers/seed-instruction.d.ts +3 -0
- package/dist/providers/self-session-env.d.ts +21 -0
- package/dist/providers/session-limit.d.ts +9 -0
- package/dist/providers/strip-ansi.d.ts +5 -0
- package/dist/providers/transport-errors.d.ts +94 -0
- package/dist/providers/transport-lifecycle.d.ts +63 -0
- package/dist/providers/transport-wrappers.d.ts +110 -0
- package/dist/providers/transports/acp-profiles/config-option-step.d.ts +24 -0
- package/dist/providers/transports/acp-profiles/copilot.d.ts +12 -0
- package/dist/providers/transports/acp-profiles/gemini.d.ts +22 -0
- package/dist/providers/transports/acp-profiles/hermes.d.ts +27 -0
- package/dist/providers/transports/acp-profiles/index.d.ts +19 -0
- package/dist/providers/transports/acp-profiles/kimi.d.ts +24 -0
- package/dist/providers/transports/acp-profiles/opencode.d.ts +26 -0
- package/dist/providers/transports/acp-profiles/types.d.ts +258 -0
- package/dist/providers/transports/acp-rpc.d.ts +18 -0
- package/dist/providers/transports/acp.d.ts +215 -0
- package/dist/providers/transports/codex-app-server.d.ts +190 -0
- package/dist/providers/transports/hold-open.d.ts +120 -0
- package/dist/providers/transports/manager.d.ts +21 -0
- package/dist/providers/transports/stream-json.d.ts +162 -0
- package/dist/providers/transports/types.d.ts +56 -0
- package/dist/providers/types.d.ts +154 -0
- package/dist/providers/yolo-contract.d.ts +131 -0
- package/dist/script/adapter.d.ts +101 -0
- package/dist/script/batch-script.d.ts +7 -0
- package/dist/script/index.d.ts +3 -0
- package/dist/script/lmscript.d.ts +550 -0
- package/dist/shared/policy.d.ts +74 -0
- package/dist/shared-lib/db.d.ts +25 -0
- package/dist/shared-lib/passive-session-reader.d.ts +2 -0
- package/dist/shared-lib/provider-parse.d.ts +6 -0
- package/dist/templates/basedir.d.ts +52 -0
- package/dist/uri/lmctl_uri.d.ts +38 -0
- package/durable-memory/model_selection.md +108 -25
- package/examples/README.md +103 -0
- package/examples/anonymous-panel.lms +3 -0
- package/examples/batch-prompts.txt +9 -0
- package/examples/batchscript.lms +67 -0
- package/examples/n8n/agent-lib.lms +56 -0
- package/examples/n8n/git-lib.lms +93 -0
- package/examples/n8n/mock-support.db +0 -0
- package/examples/n8n/multi-agent-review.lms +107 -0
- package/examples/n8n/news-digest.lms +159 -0
- package/examples/n8n/rag-search.lms +163 -0
- package/examples/n8n/support-agent.lms +88 -0
- package/examples/n8n/ticket-triage.lms +156 -0
- package/package.json +13 -17
- package/dist/cli/side_effect_classifier.json +0 -19
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Passive (read-only, bounded, certified) reader for OpenCode sessions.
|
|
3
|
+
*
|
|
4
|
+
* ## Relationship to lmplayer (the investigated finding, from real stores)
|
|
5
|
+
*
|
|
6
|
+
* lmplayer IS an opencode fork/wrapper, and the two share the opencode SQLite
|
|
7
|
+
* schema EXACTLY: `message(id,session_id,time_created,time_updated,data)`,
|
|
8
|
+
* `part(id,message_id,session_id,time_created,time_updated,data)`,
|
|
9
|
+
* `session(id,project_id,…)`, with the per-row `data` column holding JSON. Verified
|
|
10
|
+
* on this box: an opencode assistant `message.data` is
|
|
11
|
+
* `{parentID, role:"assistant", tokens:{input,output,reasoning,cache:{read,write}},
|
|
12
|
+
* modelID, providerID, time:{created,completed}}` and a tool `part.data` is
|
|
13
|
+
* `{type:"tool", tool, callID, state:{status,input,output,metadata:{display}}}` —
|
|
14
|
+
* byte-for-byte the shapes the dispatcher's lmplayer reader already handles.
|
|
15
|
+
*
|
|
16
|
+
* What DIFFERS is ONLY the store location and the label:
|
|
17
|
+
* - opencode canonical store: `$XDG_DATA_HOME/opencode/opencode.db` (large, live)
|
|
18
|
+
* and `…/opencode/opencode-local.db`, plus `$XDG_CONFIG_HOME/opencode/opencode.db`.
|
|
19
|
+
* - lmplayer store: `$XDG_DATA_HOME/lmplayer/opencode*.db` (with extra
|
|
20
|
+
* lmplayer-only `session_job*` tables — the source of the
|
|
21
|
+
* `session-job-notification` synthetic part type, filtered here too).
|
|
22
|
+
*
|
|
23
|
+
* So this reader reuses the lmplayer extraction verbatim but labels every source
|
|
24
|
+
* `provider:"opencode"` — NEVER "lmplayer", even though the SQLite shape is
|
|
25
|
+
* identical (lmbi's explicit "no cross-provider mislabeling" requirement). The
|
|
26
|
+
* label flows from `source.provider`, which discovery sets to `"opencode"`.
|
|
27
|
+
*
|
|
28
|
+
* LANDMINE for wiring: the `OPENCODE_DB` env var currently routes the dispatcher's
|
|
29
|
+
* LMPLAYER default paths — pointing it at the opencode store would label opencode
|
|
30
|
+
* data as `lmplayer`. This reader deliberately does NOT read that env; an explicit
|
|
31
|
+
* store override goes through `OpencodePassiveReaderOptions.dbPaths`.
|
|
32
|
+
*
|
|
33
|
+
* De-duplicate the mirrored helpers (parseApplyPatch / classifyTool / firstPath /
|
|
34
|
+
* writeInputBytes / sqliteRevision / the row reader) against the dispatcher's own
|
|
35
|
+
* copies at wiring time.
|
|
36
|
+
*/
|
|
37
|
+
import type { ReadLimits, Snapshot, Source, SourceState } from "../passive-session-reader.js";
|
|
38
|
+
export declare const OPENCODE_PROVIDER = "opencode";
|
|
39
|
+
/** opencode's own canonical stores (NOT lmplayer's, NOT the OPENCODE_DB env). */
|
|
40
|
+
export declare function defaultOpencodeDbPaths(): string[];
|
|
41
|
+
interface OpencodeMessageRow extends Record<string, unknown> {
|
|
42
|
+
id: string;
|
|
43
|
+
time_created: number;
|
|
44
|
+
time_updated: number;
|
|
45
|
+
data: string;
|
|
46
|
+
}
|
|
47
|
+
interface OpencodePartRow extends Record<string, unknown> {
|
|
48
|
+
id: string;
|
|
49
|
+
message_id: string;
|
|
50
|
+
time_created: number;
|
|
51
|
+
time_updated: number;
|
|
52
|
+
data: string;
|
|
53
|
+
}
|
|
54
|
+
/** Extract per-response metadata from an opencode session's rows. Verbatim
|
|
55
|
+
* lmplayer logic, labeled through `source.provider` (= "opencode"). */
|
|
56
|
+
export declare function readOpencodeRows(source: Source, revision: string, rows: {
|
|
57
|
+
messages: OpencodeMessageRow[];
|
|
58
|
+
parts: OpencodePartRow[];
|
|
59
|
+
}, limits: ReadLimits): Snapshot;
|
|
60
|
+
export interface OpencodePassiveReaderOptions {
|
|
61
|
+
/** Overrides the default opencode store paths (does NOT read `OPENCODE_DB`). */
|
|
62
|
+
dbPaths?: string[];
|
|
63
|
+
sqliteBusyTimeoutMs?: number;
|
|
64
|
+
}
|
|
65
|
+
/** Keyset watermark for cross-DB discovery (one page continues after this key). */
|
|
66
|
+
export interface OpencodeDiscoveryCursor {
|
|
67
|
+
source_id: string;
|
|
68
|
+
sessionid: string;
|
|
69
|
+
}
|
|
70
|
+
export declare class OpencodePassiveReader {
|
|
71
|
+
private readonly dbPaths;
|
|
72
|
+
private readonly sqliteBusyTimeoutMs;
|
|
73
|
+
constructor(options?: OpencodePassiveReaderOptions);
|
|
74
|
+
private openDb;
|
|
75
|
+
/**
|
|
76
|
+
* Enumerate up to `cap` sources ordered by `source_id\0sessionid`, deduped by
|
|
77
|
+
* session id (the first configured DB that holds it is canonical). `after`
|
|
78
|
+
* continues a previous page. Bounded SQL (`LIMIT`) — no traversal budget, so
|
|
79
|
+
* `truncated` is always false.
|
|
80
|
+
*/
|
|
81
|
+
discover(limits: ReadLimits, cap: number, after?: OpencodeDiscoveryCursor | null): Promise<{
|
|
82
|
+
sources: Source[];
|
|
83
|
+
truncated: boolean;
|
|
84
|
+
}>;
|
|
85
|
+
inspect(source: Source, limits: ReadLimits): SourceState;
|
|
86
|
+
read(source: Source, revision: string, limits: ReadLimits): Snapshot;
|
|
87
|
+
}
|
|
88
|
+
export declare function createOpencodePassiveReader(options?: OpencodePassiveReaderOptions): OpencodePassiveReader;
|
|
89
|
+
export {};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Passive (read-only, bounded, certified) reader for Qwen Code sessions.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this reader does not follow either of qwen.ts's own headers
|
|
5
|
+
*
|
|
6
|
+
* `src/providers/qwen.ts` carries two conflicting claims: its file header says
|
|
7
|
+
* "Qwen Code is a Claude Code fork — same JSONL session format", while its
|
|
8
|
+
* `QwenUsageMetadata` comment says "Qwen Code is a Gemini CLI fork; usageMetadata
|
|
9
|
+
* follows Gemini API semantics". Neither label alone describes the records on
|
|
10
|
+
* disk, so this reader was written from the records instead. What a census of
|
|
11
|
+
* every `*.jsonl` under the local projects store actually shows (1155 records,
|
|
12
|
+
* 0 parse failures):
|
|
13
|
+
*
|
|
14
|
+
* - The ENVELOPE is Claude-shaped: `uuid`, `parentUuid`, `sessionId`,
|
|
15
|
+
* `timestamp`, `type`, `cwd`, `version`, `gitBranch` on 1155/1155 records.
|
|
16
|
+
* - The record TYPES diverge from Claude: `system` (592), `assistant` (276),
|
|
17
|
+
* `tool_result` (238), `user` (49). Claude folds tool results into `user`
|
|
18
|
+
* rows; qwen gives them their own type. That makes a `user` row an
|
|
19
|
+
* unambiguous external prompt admission here — no tool_result sniffing.
|
|
20
|
+
* - Usage exists in TWO shapes simultaneously, so "which fork" is the wrong
|
|
21
|
+
* question:
|
|
22
|
+
* 1. `assistant.usageMetadata` — Gemini camelCase
|
|
23
|
+
* (`promptTokenCount` / `candidatesTokenCount` / `thoughtsTokenCount` /
|
|
24
|
+
* `totalTokenCount` / `cachedContentTokenCount`), on 276 records, with
|
|
25
|
+
* NO per-response identity of any kind.
|
|
26
|
+
* 2. `system` rows with `subtype:"ui_telemetry"` and
|
|
27
|
+
* `systemPayload.uiEvent["event.name"] === "qwen-code.api_response"` —
|
|
28
|
+
* snake_case (`input_token_count` / `output_token_count` /
|
|
29
|
+
* `cached_content_token_count` / `thoughts_token_count` /
|
|
30
|
+
* `total_token_count`), on 289 records, each carrying a native
|
|
31
|
+
* `response_id` (`chatcmpl-…`).
|
|
32
|
+
*
|
|
33
|
+
* This reader treats shape (2) as authoritative, because it is the ONLY source
|
|
34
|
+
* with a native per-response id — the contract forbids fabricating a
|
|
35
|
+
* `response_id` out of an envelope uuid — and it strictly covers shape (1)
|
|
36
|
+
* (289 ≥ 276; the extra 13 are `subagent_name` responses that have no
|
|
37
|
+
* top-level assistant row at all).
|
|
38
|
+
*
|
|
39
|
+
* ## Token semantics: measured, not assumed
|
|
40
|
+
*
|
|
41
|
+
* All 289 local api_response records satisfy, with zero violations:
|
|
42
|
+
* input_token_count + output_token_count === total_token_count
|
|
43
|
+
* cached_content_token_count <= input_token_count
|
|
44
|
+
* thoughts_token_count <= output_token_count
|
|
45
|
+
*
|
|
46
|
+
* i.e. cached is a SUBSET of input and thoughts a SUBSET of output — Gemini
|
|
47
|
+
* semantics, now verified on real records rather than inherited from a comment.
|
|
48
|
+
* The three invariants are re-checked at read time; a violation makes the whole
|
|
49
|
+
* snapshot `partial` rather than emitting numbers whose meaning we cannot stand
|
|
50
|
+
* behind.
|
|
51
|
+
*
|
|
52
|
+
* `input_cache_write` is NULL, not 0: no cache-write counter exists anywhere in
|
|
53
|
+
* this wire (no such key on any of the 289 records), so its value is unrecorded,
|
|
54
|
+
* not known-zero.
|
|
55
|
+
*
|
|
56
|
+
* `model_provider` is NULL. The nearest native field is `auth_type`
|
|
57
|
+
* (`openai` / `qwen-oauth` locally), which is an AUTHENTICATION method, not a
|
|
58
|
+
* model-provider identity; reporting `qwen-oauth` as a provider would be a
|
|
59
|
+
* mislabel. See the note in the owner report.
|
|
60
|
+
*/
|
|
61
|
+
import type { ReadLimits, Snapshot, Source, SourceState } from "../passive-session-reader.js";
|
|
62
|
+
import { type TokenValues } from "./shared.js";
|
|
63
|
+
export declare const QWEN_PROVIDER = "qwen";
|
|
64
|
+
/** One raw line of a qwen chat transcript. Only fields this reader reads. */
|
|
65
|
+
interface QwenLine {
|
|
66
|
+
uuid?: unknown;
|
|
67
|
+
parentUuid?: unknown;
|
|
68
|
+
sessionId?: unknown;
|
|
69
|
+
type?: unknown;
|
|
70
|
+
subtype?: unknown;
|
|
71
|
+
timestamp?: unknown;
|
|
72
|
+
message?: unknown;
|
|
73
|
+
systemPayload?: unknown;
|
|
74
|
+
}
|
|
75
|
+
export interface QwenPassiveReaderOptions {
|
|
76
|
+
/**
|
|
77
|
+
* Discovery roots, in the caller's preferred order. Defaults to the live store
|
|
78
|
+
* plus every retained backup store (see `defaultQwenProjectRoots`).
|
|
79
|
+
*/
|
|
80
|
+
qwenProjectsDirs?: string[];
|
|
81
|
+
maxDiscoveryTraversalVisits?: number;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Default qwen discovery roots.
|
|
85
|
+
*
|
|
86
|
+
* `~/.qwen.bk1/projects` is a RETAINED ACCOUNT HISTORY store, not a redundant
|
|
87
|
+
* copy to skip. Verified on this box: 30 transcripts there against 27 in the
|
|
88
|
+
* live root, with **zero identity overlap** — 0 shared file names, 0 shared
|
|
89
|
+
* in-record `sessionId`s and 0 shared `response_id`s — over disjoint telemetry
|
|
90
|
+
* spans (backup 2026-03-14 → 04-12, live 2026-04-14 → 07-02). Excluding it
|
|
91
|
+
* would silently drop 89 real responses from any cost total.
|
|
92
|
+
*/
|
|
93
|
+
export declare function defaultQwenProjectRoots(): string[];
|
|
94
|
+
/**
|
|
95
|
+
* Map one api_response telemetry event onto the contract's four token slots.
|
|
96
|
+
* Returns null when a required counter is missing/not a non-negative integer, or
|
|
97
|
+
* when any of the three measured invariants fails — the caller turns that into a
|
|
98
|
+
* `partial` snapshot rather than guessing.
|
|
99
|
+
*/
|
|
100
|
+
export declare function qwenTokens(event: Record<string, unknown>): TokenValues | null;
|
|
101
|
+
export declare function readQwen(lines: QwenLine[], source: Source, revision: string, limits: ReadLimits): Snapshot;
|
|
102
|
+
/**
|
|
103
|
+
* Do the records agree with the session id the caller asked for? True when no
|
|
104
|
+
* record names a session at all (nothing to contradict) or when every named
|
|
105
|
+
* session equals `sessionid`. False on any disagreement, including a transcript
|
|
106
|
+
* that names two different sessions.
|
|
107
|
+
*/
|
|
108
|
+
export declare function qwenRecordsMatchSession(lines: QwenLine[], sessionid: string): boolean;
|
|
109
|
+
export declare class QwenPassiveReader {
|
|
110
|
+
/**
|
|
111
|
+
* Resolved discovery roots, in configured order. Exposed so a caller can map a
|
|
112
|
+
* `source_id` back to the root it came from without re-deriving the defaults.
|
|
113
|
+
*/
|
|
114
|
+
readonly roots: readonly string[];
|
|
115
|
+
private readonly maxDiscoveryTraversalVisits;
|
|
116
|
+
constructor(options?: QwenPassiveReaderOptions);
|
|
117
|
+
/**
|
|
118
|
+
* Enumerate up to `cap` sources across EVERY configured root, ordered by real
|
|
119
|
+
* path. `afterPath` continues a previous page; real-path order is total, so one
|
|
120
|
+
* cursor resumes the merged stream correctly.
|
|
121
|
+
*
|
|
122
|
+
* ## Provenance and cross-root identity
|
|
123
|
+
*
|
|
124
|
+
* `source_id` is the file's absolute real path, so it names the root a session
|
|
125
|
+
* came from (`…/.qwen/projects/…` vs `…/.qwen.bk1/projects/…`) without any
|
|
126
|
+
* extra field on the shared `Source` shape. Two sessions that happen to share a
|
|
127
|
+
* `sessionid` across roots therefore stay two DISTINCT sources with distinct
|
|
128
|
+
* `source_id`s, and both are read.
|
|
129
|
+
*
|
|
130
|
+
* This reader performs **no identity dedup** — that is the consumer's job, on
|
|
131
|
+
* real ids. The only collapsing done here is by real path, which removes a file
|
|
132
|
+
* reachable through two configured roots (e.g. one root symlinked inside
|
|
133
|
+
* another); that is the same physical file, never two sessions that merely look
|
|
134
|
+
* alike. Nothing is ever dropped because a similarly-named file exists
|
|
135
|
+
* elsewhere.
|
|
136
|
+
*/
|
|
137
|
+
discover(limits: ReadLimits, cap: number, afterPath?: string | null): Promise<{
|
|
138
|
+
sources: Source[];
|
|
139
|
+
truncated: boolean;
|
|
140
|
+
}>;
|
|
141
|
+
/**
|
|
142
|
+
* A source_id is only ours when it is a real, non-symlink file contained by one
|
|
143
|
+
* of the configured roots AND its basename is the claimed session id. Both
|
|
144
|
+
* checks run before any read.
|
|
145
|
+
*/
|
|
146
|
+
private matches;
|
|
147
|
+
inspect(source: Source, limits: ReadLimits): Promise<SourceState>;
|
|
148
|
+
read(source: Source, revision: string, limits: ReadLimits): Promise<Snapshot>;
|
|
149
|
+
}
|
|
150
|
+
export declare function createQwenPassiveReader(options?: QwenPassiveReaderOptions): QwenPassiveReader;
|
|
151
|
+
export {};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-contained primitives for the per-provider passive readers that live in
|
|
3
|
+
* this directory (agy, qwen).
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS FILE EXISTS: the equivalent helpers inside
|
|
6
|
+
* `src/providers/passive-session-reader.ts` are module-private, and that file is
|
|
7
|
+
* concurrently owned by the discovery-fairness redesign. Re-implementing the
|
|
8
|
+
* handful we need here keeps this slice landable without touching the shared
|
|
9
|
+
* dispatcher. Only the PUBLIC contract types are imported from it, so there is
|
|
10
|
+
* exactly one definition of the wire shape.
|
|
11
|
+
*
|
|
12
|
+
* At wiring time these should be de-duplicated against the dispatcher's own
|
|
13
|
+
* copies (see PASSIVE_EXTRACTION_SEMANTICS below — that constant in particular
|
|
14
|
+
* MUST stay equal to the dispatcher's `EXTRACTION_SEMANTICS`).
|
|
15
|
+
*/
|
|
16
|
+
import type { ExternalIoEntry, PricingFacts, ReadLimits } from "../passive-session-reader.js";
|
|
17
|
+
/**
|
|
18
|
+
* Extraction-semantics tag folded into every revision string.
|
|
19
|
+
*
|
|
20
|
+
* MUST equal `EXTRACTION_SEMANTICS` in `../passive-session-reader.ts` (currently
|
|
21
|
+
* "bx3"). A revision is compared by lmbi as a plain string, so a reader emitting
|
|
22
|
+
* a different tag for the same native bytes would look permanently un-indexed.
|
|
23
|
+
* Bump both together whenever extraction semantics change — this file's real
|
|
24
|
+
* de-dup against the dispatcher's copy is still pending (see file header);
|
|
25
|
+
* until then a bump here is easy to miss, as happened with the bx2→bx3 change.
|
|
26
|
+
*/
|
|
27
|
+
export declare const PASSIVE_EXTRACTION_SEMANTICS = "bx3";
|
|
28
|
+
export type FileDirection = "read" | "written";
|
|
29
|
+
export interface FileObservation {
|
|
30
|
+
path: string;
|
|
31
|
+
bytes: number | null;
|
|
32
|
+
}
|
|
33
|
+
export interface TokenValues {
|
|
34
|
+
input_uncached: number | null;
|
|
35
|
+
input_cached: number | null;
|
|
36
|
+
input_cache_write: number | null;
|
|
37
|
+
output: number | null;
|
|
38
|
+
}
|
|
39
|
+
export declare const EMPTY_TOKENS: TokenValues;
|
|
40
|
+
export declare function record(value: unknown): Record<string, unknown> | null;
|
|
41
|
+
export declare function nonnegativeInteger(value: unknown): number | null;
|
|
42
|
+
/** Non-negative integer counter, or null. Used for pricing_facts counts. */
|
|
43
|
+
export declare function optCount(value: unknown): number | null;
|
|
44
|
+
export declare function canonicalTimestamp(value: unknown): string | null;
|
|
45
|
+
export declare function checkedLimits(limits: ReadLimits): void;
|
|
46
|
+
/**
|
|
47
|
+
* Deterministic UTF-16 code-unit ordering for opaque identifiers/paths. ICU
|
|
48
|
+
* collation can differ across Node builds; two processes must agree on cursor
|
|
49
|
+
* order, so code-unit comparison is the safe primitive.
|
|
50
|
+
*/
|
|
51
|
+
export declare function compareCodeUnits(left: string, right: string): number;
|
|
52
|
+
/**
|
|
53
|
+
* Per-response file observations keyed by (call id, direction, path) — the
|
|
54
|
+
* "smallest honest unit". Two DIFFERENT call ids to the same path stay separate;
|
|
55
|
+
* a replayed record of the SAME call id coalesces (first recorded wins).
|
|
56
|
+
* `bytes` is UTF-8 content size: a number (including 0), or null when the
|
|
57
|
+
* content is genuinely unavailable.
|
|
58
|
+
*/
|
|
59
|
+
export declare class ObservationSet {
|
|
60
|
+
private readonly map;
|
|
61
|
+
record(callId: string, direction: FileDirection, path: unknown, bytes: number | null): void;
|
|
62
|
+
files(direction: FileDirection): FileObservation[];
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Collector for non-file external-I/O entries. Deduplicates by native
|
|
66
|
+
* (id, direction, kind); a MEASURED id-less entry is never deduplicated
|
|
67
|
+
* (content/byte dedup is forbidden by the contract), while id-less null-byte
|
|
68
|
+
* coverage markers may merge (one unknown marker forces NULL as well as two).
|
|
69
|
+
*/
|
|
70
|
+
export declare class ExternalIoSet {
|
|
71
|
+
private readonly map;
|
|
72
|
+
record(entry: ExternalIoEntry): void;
|
|
73
|
+
entries(): ExternalIoEntry[];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Per-direction aggregate: numeric ONLY when the whole direction is known —
|
|
77
|
+
* `files` non-null with every byte numeric AND every external_io entry in this
|
|
78
|
+
* direction numeric. ANY null contributor → null (never a partial sum posing as
|
|
79
|
+
* a total). A fully-classified direction with no contributors → 0.
|
|
80
|
+
*/
|
|
81
|
+
export declare function aggregateDirection(files: FileObservation[] | null, io: ExternalIoEntry[] | null, direction: FileDirection): number | null;
|
|
82
|
+
/**
|
|
83
|
+
* Build a validated pricing_facts object, dropping fields that are not present
|
|
84
|
+
* as non-negative integers and enforcing the two invariants: reasoning ≤ output,
|
|
85
|
+
* and (5m + 1h) cache-write TTL counts ≤ the known cache-write total. Returns
|
|
86
|
+
* null when nothing valid is present. A recorded 0 is preserved (0 ≠ unknown).
|
|
87
|
+
*/
|
|
88
|
+
export declare function buildPricingFacts(raw: {
|
|
89
|
+
cacheWrite5m?: unknown;
|
|
90
|
+
cacheWrite1h?: unknown;
|
|
91
|
+
reasoning?: unknown;
|
|
92
|
+
webSearch?: unknown;
|
|
93
|
+
webFetch?: unknown;
|
|
94
|
+
}, outputTokens: number | null, cacheWriteTotal: number | null): PricingFacts | null;
|
|
95
|
+
export declare function isInternalControlTool(name: unknown): boolean;
|
|
96
|
+
/** Record an opaque/uncertified tool call as unknown in BOTH directions. */
|
|
97
|
+
export declare function recordUnattributedBoth(io: ExternalIoSet, callId: string | null): void;
|
|
98
|
+
/**
|
|
99
|
+
* UTF-8 byte size of the content WRITTEN by a write call, taken from the call's
|
|
100
|
+
* OWN input (never the file on disk). Returns 0 for a genuinely empty write,
|
|
101
|
+
* null when no content field is present.
|
|
102
|
+
*/
|
|
103
|
+
export declare function writeInputBytes(input: unknown): number | null;
|
|
104
|
+
/**
|
|
105
|
+
* Resolve `path` to its real location and require it to be a regular,
|
|
106
|
+
* non-symlink file contained by one of `roots`. Returns the real path, or null.
|
|
107
|
+
*/
|
|
108
|
+
export declare function containedRealFile(path: string, roots: string[]): Promise<string | null>;
|
|
109
|
+
/**
|
|
110
|
+
* Read a JSONL file within the byte budget and hash its exact native bytes.
|
|
111
|
+
* Returns null when the file is missing, oversized, or changed underneath the
|
|
112
|
+
* read (dev/ino/size/mtime re-checked after) — a torn read is never reported as
|
|
113
|
+
* a clean revision.
|
|
114
|
+
*/
|
|
115
|
+
export declare function jsonlRevision(path: string, limits: ReadLimits): Promise<{
|
|
116
|
+
revision: string;
|
|
117
|
+
data: Buffer;
|
|
118
|
+
} | null>;
|
|
119
|
+
/**
|
|
120
|
+
* Parse a complete JSONL buffer. Returns null when the file does not end on a
|
|
121
|
+
* record boundary (a live append caught mid-line) or any line is not a JSON
|
|
122
|
+
* object — a torn/partial file is never silently treated as complete.
|
|
123
|
+
*/
|
|
124
|
+
export declare function parseJsonl<T>(data: Buffer, limits: ReadLimits): T[] | null;
|
|
125
|
+
/** Minimal read-only `node:sqlite` surface used by the sqlite-backed readers. */
|
|
126
|
+
export interface SqliteStatement {
|
|
127
|
+
all(...params: unknown[]): unknown[];
|
|
128
|
+
get(...params: unknown[]): unknown;
|
|
129
|
+
}
|
|
130
|
+
export interface SqliteDatabase {
|
|
131
|
+
close(): void;
|
|
132
|
+
exec(sql: string): void;
|
|
133
|
+
prepare(sql: string): SqliteStatement;
|
|
134
|
+
}
|
|
135
|
+
export type SqliteDatabaseConstructor = new (path: string, options?: {
|
|
136
|
+
open?: boolean;
|
|
137
|
+
readOnly?: boolean;
|
|
138
|
+
}) => SqliteDatabase;
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import type { CopilotSessionUsage } from "./passive-readers/copilot.js";
|
|
2
|
+
export interface Source {
|
|
3
|
+
provider: string;
|
|
4
|
+
sessionid: string;
|
|
5
|
+
source_id: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ReadLimits {
|
|
8
|
+
signal: AbortSignal;
|
|
9
|
+
maxBytes: number;
|
|
10
|
+
maxRows: number;
|
|
11
|
+
}
|
|
12
|
+
export type SourceState = {
|
|
13
|
+
status: "ready";
|
|
14
|
+
revision: string;
|
|
15
|
+
} | {
|
|
16
|
+
status: "partial" | "unsupported" | "unavailable";
|
|
17
|
+
};
|
|
18
|
+
/** A non-file external-I/O contributor for a response (phase 3 / bx2). File
|
|
19
|
+
* reads/writes stay in files_read/files_written and are NEVER duplicated here.
|
|
20
|
+
* An `unattributed` entry is a coverage marker (bytes=null, basis="unknown")
|
|
21
|
+
* that forces its whole direction's aggregate to NULL. */
|
|
22
|
+
export interface ExternalIoEntry {
|
|
23
|
+
direction: "read" | "write";
|
|
24
|
+
kind: "prompt" | "web" | "other" | "unattributed";
|
|
25
|
+
/** Native admission/call id for dedup; never a content body. */
|
|
26
|
+
id: string | null;
|
|
27
|
+
/** Bounded metadata ref (web URL sans credentials/query/fragment); null for
|
|
28
|
+
* prompt/unattributed; never a raw command/query/prompt body. */
|
|
29
|
+
ref: string | null;
|
|
30
|
+
bytes: number | null;
|
|
31
|
+
basis: "input_text" | "normalized_content" | "certified_counter" | "unknown";
|
|
32
|
+
}
|
|
33
|
+
/** Closed whitelist of native pricing/fidelity facts for an eventual lmprice
|
|
34
|
+
* join (note 387). Counts only — no rates, service tier/speed, credentials, or
|
|
35
|
+
* account info. `reasoning_output_tokens` is a subset of `tokens.output`; the
|
|
36
|
+
* cache-write TTL counts cannot exceed `tokens.input_cache_write`. */
|
|
37
|
+
export interface PricingFacts {
|
|
38
|
+
cache_write_5m_tokens?: number | null;
|
|
39
|
+
cache_write_1h_tokens?: number | null;
|
|
40
|
+
reasoning_output_tokens?: number | null;
|
|
41
|
+
web_search_requests?: number | null;
|
|
42
|
+
web_fetch_requests?: number | null;
|
|
43
|
+
}
|
|
44
|
+
export interface NativeResponseMetadata {
|
|
45
|
+
response_id: string;
|
|
46
|
+
timestamp: string | null;
|
|
47
|
+
model: string | null;
|
|
48
|
+
tokens: {
|
|
49
|
+
input_uncached: number | null;
|
|
50
|
+
input_cached: number | null;
|
|
51
|
+
input_cache_write: number | null;
|
|
52
|
+
output: number | null;
|
|
53
|
+
};
|
|
54
|
+
files_read: Array<{
|
|
55
|
+
path: string;
|
|
56
|
+
bytes: number | null;
|
|
57
|
+
}> | null;
|
|
58
|
+
files_written: Array<{
|
|
59
|
+
path: string;
|
|
60
|
+
bytes: number | null;
|
|
61
|
+
}> | null;
|
|
62
|
+
read_bytes?: number | null;
|
|
63
|
+
write_bytes?: number | null;
|
|
64
|
+
external_io?: ExternalIoEntry[] | null;
|
|
65
|
+
model_provider?: string | null;
|
|
66
|
+
pricing_facts?: PricingFacts | null;
|
|
67
|
+
}
|
|
68
|
+
export interface Discovery {
|
|
69
|
+
sources: Source[];
|
|
70
|
+
complete: boolean;
|
|
71
|
+
cursor: string | null;
|
|
72
|
+
}
|
|
73
|
+
export type Snapshot = {
|
|
74
|
+
status: "complete";
|
|
75
|
+
source: Source;
|
|
76
|
+
revision: string;
|
|
77
|
+
checkpoint: string | null;
|
|
78
|
+
responses: NativeResponseMetadata[];
|
|
79
|
+
/** Native session-scoped usage snapshots (currently: copilot's
|
|
80
|
+
* `session.shutdown` records) — surfaced BESIDE responses, never
|
|
81
|
+
* distributed into them, never a synthetic response. `undefined`/absent
|
|
82
|
+
* for providers with no such native concept; `null` for a provider that
|
|
83
|
+
* has the concept but this session has none yet (e.g. still live);
|
|
84
|
+
* present-and-non-empty only when real native snapshots exist. Every
|
|
85
|
+
* relevant native snapshot is retained (never "last wins") since a
|
|
86
|
+
* provider's own scope semantics may not cover the whole session. */
|
|
87
|
+
session_usage?: CopilotSessionUsage[] | null;
|
|
88
|
+
/** Explicit, additive coverage marker: `responses` above are real,
|
|
89
|
+
* identified, fully-valid rows — never fabricated or clamped-to-zero —
|
|
90
|
+
* but a KNOWN native-format gap means some other, uncapturable activity
|
|
91
|
+
* in this same source could not be attributed to a response (e.g. a
|
|
92
|
+
* Codex source mixing legacy `event_msg`/`token_count` telemetry
|
|
93
|
+
* alongside modern `token_usage_record`s: the old-format usage is
|
|
94
|
+
* conservatively treated as an unresolved gap whenever it co-occurs with
|
|
95
|
+
* captured rows — matching raw token counts is NOT proof of native
|
|
96
|
+
* identity, since distinct requests can share identical small integer
|
|
97
|
+
* counts, so a numeric match never clears this marker). Absent ⇒ legacy
|
|
98
|
+
* full-coverage semantics (nothing else exists to omit) —
|
|
99
|
+
* NEVER default a missing marker to "partial" or vice versa. A caller
|
|
100
|
+
* MUST reject any value other than "partial" here rather than guess a
|
|
101
|
+
* new meaning for an unrecognized marker. This is a coverage flag only:
|
|
102
|
+
* it never changes the validity of `responses`, and a caller retains
|
|
103
|
+
* and journals those rows while simply never advancing its incremental
|
|
104
|
+
* revision/retention watermark past a snapshot carrying this flag. */
|
|
105
|
+
capture_status?: "partial";
|
|
106
|
+
} | {
|
|
107
|
+
status: "partial" | "unsupported" | "unavailable";
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Effective discovery-coverage certification (lmbi backfill-reader-contract.md).
|
|
111
|
+
* This is an explicit certification of what this reader instance actually
|
|
112
|
+
* covers, NOT a list inferred from rows already returned — a caller must be
|
|
113
|
+
* able to tell "no adapter for this provider" apart from "adapter exists, its
|
|
114
|
+
* store is genuinely empty."
|
|
115
|
+
*/
|
|
116
|
+
export interface PassiveSessionReaderCapabilities {
|
|
117
|
+
/** All configured native account roots, no team/task/session filter. */
|
|
118
|
+
scope: "account";
|
|
119
|
+
/** Changes when extraction semantics OR effective declared root
|
|
120
|
+
* configuration change, so a caller knows to start a safe new traversal
|
|
121
|
+
* rather than trust a stale cursor/cache against new coverage. */
|
|
122
|
+
revision: string;
|
|
123
|
+
/** Providers this instance can discover/inspect/read at all (certified,
|
|
124
|
+
* even where token metering itself is only partial — see `metering`). */
|
|
125
|
+
providers: string[];
|
|
126
|
+
/** Per-provider metering completeness. Informational only — never an
|
|
127
|
+
* admission gate; an unlisted provider makes no metering claim either way. */
|
|
128
|
+
metering: Record<string, "per_response" | "partial" | "unavailable" | "session_only">;
|
|
129
|
+
}
|
|
130
|
+
export interface PassiveSessionReader {
|
|
131
|
+
discover(limits: ReadLimits & {
|
|
132
|
+
maxSources: number;
|
|
133
|
+
after?: string;
|
|
134
|
+
}): Promise<Discovery>;
|
|
135
|
+
inspect(source: Source, limits: ReadLimits): Promise<SourceState>;
|
|
136
|
+
read(source: Source, revision: string, limits: ReadLimits): Promise<Snapshot>;
|
|
137
|
+
capabilities(): PassiveSessionReaderCapabilities;
|
|
138
|
+
}
|
|
139
|
+
export interface PassiveSessionReaderOptions {
|
|
140
|
+
claudeProjectsDir?: string;
|
|
141
|
+
codexSessionDirs?: string[];
|
|
142
|
+
lmplayerDbPaths?: string[];
|
|
143
|
+
/** Overrides agy's default `$HOME/.gemini/antigravity-cli/conversations`. */
|
|
144
|
+
agyConversationsDir?: string;
|
|
145
|
+
/** Overrides dsh's default `~/.dsh/sessions` (or `$DSH_HOME/sessions`). */
|
|
146
|
+
dshHome?: string;
|
|
147
|
+
/** Overrides qwen's default live+backup project roots. */
|
|
148
|
+
qwenProjectsDirs?: string[];
|
|
149
|
+
/** Overrides opencode's default store paths (does NOT read `OPENCODE_DB`). */
|
|
150
|
+
opencodeDbPaths?: string[];
|
|
151
|
+
/** Overrides copilot's default `$HOME/.copilot/session-state`. */
|
|
152
|
+
copilotSessionStateDir?: string;
|
|
153
|
+
/** Overrides kimi's default `$KIMI_CODE_HOME`/`~/.kimi-code`. */
|
|
154
|
+
kimiHome?: string;
|
|
155
|
+
sqliteBusyTimeoutMs?: number;
|
|
156
|
+
/** Directory-traversal visit budget for discovery (see
|
|
157
|
+
* MAX_DISCOVERY_TRAVERSAL_VISITS). Overridable mainly so tests can exercise
|
|
158
|
+
* the truncation path without a pathological tree. */
|
|
159
|
+
maxDiscoveryTraversalVisits?: number;
|
|
160
|
+
}
|
|
161
|
+
export declare class NativePassiveSessionReader implements PassiveSessionReader {
|
|
162
|
+
private readonly claudeProjectsDir;
|
|
163
|
+
private readonly codexSessionDirs;
|
|
164
|
+
private readonly lmplayerDbPaths;
|
|
165
|
+
private readonly sqliteBusyTimeoutMs;
|
|
166
|
+
private readonly maxDiscoveryTraversalVisits;
|
|
167
|
+
private readonly agyReader;
|
|
168
|
+
private readonly dshReader;
|
|
169
|
+
private readonly qwenReader;
|
|
170
|
+
private readonly opencodeReader;
|
|
171
|
+
private readonly copilotReader;
|
|
172
|
+
private readonly kimiReader;
|
|
173
|
+
constructor(options?: PassiveSessionReaderOptions);
|
|
174
|
+
capabilities(): PassiveSessionReaderCapabilities;
|
|
175
|
+
discover(limits: ReadLimits & {
|
|
176
|
+
maxSources: number;
|
|
177
|
+
after?: string;
|
|
178
|
+
}): Promise<Discovery>;
|
|
179
|
+
inspect(source: Source, limits: ReadLimits): Promise<SourceState>;
|
|
180
|
+
read(source: Source, revision: string, limits: ReadLimits): Promise<Snapshot>;
|
|
181
|
+
private discoverProvider;
|
|
182
|
+
private jsonlSourceMatches;
|
|
183
|
+
private openLmplayer;
|
|
184
|
+
private discoverLmplayer;
|
|
185
|
+
private inspectLmplayer;
|
|
186
|
+
private readLmplayer;
|
|
187
|
+
}
|
|
188
|
+
export declare function createPassiveSessionReader(options?: PassiveSessionReaderOptions): PassiveSessionReader;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function canonicalPath(path: string): string;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pi coding agent session provider.
|
|
3
|
+
*
|
|
4
|
+
* pi stores sessions as JSONL files under
|
|
5
|
+
* ~/.pi/agent/sessions/--<encoded-cwd>--/<timestamp>_<session-id>.jsonl
|
|
6
|
+
* where <encoded-cwd> is the cwd with the leading separator stripped and
|
|
7
|
+
* "/", "\\", ":" replaced by "-". The first line is a session header
|
|
8
|
+
* ({"type":"session","id":...,"cwd":...}); subsequent lines are tree-linked
|
|
9
|
+
* entries (message / model_change / compaction / ...). File format:
|
|
10
|
+
* packages/coding-agent/docs/session-format.md in the pi repo.
|
|
11
|
+
*/
|
|
12
|
+
import type { SessionDetail, SessionProvider, SessionSummary } from "./types.js";
|
|
13
|
+
interface PiProviderOptions {
|
|
14
|
+
piDir?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare class PiProvider implements SessionProvider {
|
|
17
|
+
name: "pi";
|
|
18
|
+
private baseDir;
|
|
19
|
+
constructor(opts?: PiProviderOptions);
|
|
20
|
+
getWatchPaths(): {
|
|
21
|
+
directory: string;
|
|
22
|
+
filePattern: RegExp;
|
|
23
|
+
}[];
|
|
24
|
+
discoverSessions(): Promise<SessionSummary[]>;
|
|
25
|
+
private listSessionFiles;
|
|
26
|
+
private readHeader;
|
|
27
|
+
private extractSummary;
|
|
28
|
+
loadSession(sessionId: string): Promise<SessionDetail | null>;
|
|
29
|
+
resolveSessionPath(sessionId: string): Promise<string | null>;
|
|
30
|
+
deleteSession(sessionId: string): Promise<boolean>;
|
|
31
|
+
}
|
|
32
|
+
export {};
|