@phnx-labs/agents-cli 1.22.82 → 1.22.84
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 +47 -0
- package/dist/commands/accounts.js +34 -6
- package/dist/commands/sessions-trace.d.ts +11 -0
- package/dist/commands/sessions-trace.js +71 -0
- package/dist/commands/view.js +3 -6
- package/dist/lib/account-registry.d.ts +40 -3
- package/dist/lib/account-registry.js +89 -18
- package/dist/lib/accounts/connect.d.ts +6 -0
- package/dist/lib/accounts/connect.js +70 -0
- package/dist/lib/claude-statusline.d.ts +13 -5
- package/dist/lib/claude-statusline.js +19 -6
- package/dist/lib/daemon-ticks.js +23 -2
- package/dist/lib/devices/fleet-inventory.js +3 -4
- package/dist/lib/devices/harness-inventory.js +4 -5
- package/dist/lib/feed/activity-stream.d.ts +60 -0
- package/dist/lib/feed/activity-stream.js +271 -0
- package/dist/lib/feed/activity.d.ts +7 -0
- package/dist/lib/feed/activity.js +103 -13
- package/dist/lib/feed/watch.d.ts +9 -3
- package/dist/lib/feed/watch.js +72 -36
- package/dist/lib/fleet-shared-state.d.ts +6 -0
- package/dist/lib/session/active.d.ts +38 -4
- package/dist/lib/session/active.js +36 -4
- package/dist/lib/session/bash-command.js +10 -0
- package/dist/lib/session/db.d.ts +47 -2
- package/dist/lib/session/db.js +131 -4
- package/dist/lib/session/mirror.d.ts +5 -0
- package/dist/lib/session/mirror.js +166 -0
- package/dist/lib/session/parse.d.ts +49 -0
- package/dist/lib/session/parse.js +324 -30
- package/dist/lib/session/prompt.d.ts +93 -2
- package/dist/lib/session/prompt.js +266 -15
- package/dist/lib/session/remote/peer-stream.d.ts +47 -0
- package/dist/lib/session/remote/peer-stream.js +142 -0
- package/dist/lib/session/remote/watch.d.ts +1 -1
- package/dist/lib/session/remote/watch.js +41 -42
- package/dist/lib/session/session-cache.d.ts +9 -0
- package/dist/lib/session/session-cache.js +40 -2
- package/dist/lib/session/timeline-pass.d.ts +129 -0
- package/dist/lib/session/timeline-pass.js +323 -0
- package/dist/lib/session/timeline.d.ts +182 -0
- package/dist/lib/session/timeline.js +636 -0
- package/dist/lib/session/types.d.ts +155 -1
- package/dist/lib/summarizer/pass.d.ts +2 -0
- package/dist/lib/summarizer/pass.js +14 -1
- package/dist/lib/summarizer/summarize.d.ts +7 -0
- package/dist/lib/summarizer/summarize.js +3 -0
- package/package.json +1 -1
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The narration-anchored session timeline (PHNX-3939) — a pure, resumable fold.
|
|
3
|
+
*
|
|
4
|
+
* A transcript is a flat event stream: the agent says a line, runs six tools,
|
|
5
|
+
* says the next line. A person reading a sidebar wants the LINES, with the tool
|
|
6
|
+
* calls collapsed underneath them. That is the whole model here: one
|
|
7
|
+
* {@link SessionStep} per narration beat, the calls until the next beat folded
|
|
8
|
+
* into counts, failures and milestones.
|
|
9
|
+
*
|
|
10
|
+
* Two properties this module is built around:
|
|
11
|
+
*
|
|
12
|
+
* - **Deterministic, no model.** The headline is the harness's OWN narration
|
|
13
|
+
* (Claude assistant text, Codex `AgentMessage` commentary, Gemini thought
|
|
14
|
+
* subject) and the per-tool label is the harness's own (`description`,
|
|
15
|
+
* `state.title`, the parsed command). Nothing is invented; the optional
|
|
16
|
+
* summarizer polishes ON TOP and is never required.
|
|
17
|
+
* - **Resumable.** {@link foldTimeline} takes the state from the previous fold
|
|
18
|
+
* and the events that arrived since, so the daemon reads only the bytes a
|
|
19
|
+
* transcript grew by. Folding the appended tail onto the prior state is
|
|
20
|
+
* identical to folding the whole file from zero — every piece of mutable
|
|
21
|
+
* state lives in the serializable {@link TimelineState}, nothing in a
|
|
22
|
+
* closure.
|
|
23
|
+
*
|
|
24
|
+
* No I/O: the pass (`timeline-pass.ts`) reads bytes and calls this. Same
|
|
25
|
+
* discipline as `digest.ts` and `trajectory.ts`.
|
|
26
|
+
*/
|
|
27
|
+
import type { SessionEvent, SessionFileChange, SessionFiles, SessionRequest, SessionStep, SessionTimeline, SessionVerbClass } from './types.js';
|
|
28
|
+
/**
|
|
29
|
+
* Bumped when the fold's OUTPUT changes, so a cached state computed by an older
|
|
30
|
+
* CLI is recomputed rather than rendered. Same role as
|
|
31
|
+
* `SESSION_SUMMARY_EXTRACTOR_VERSION`.
|
|
32
|
+
*/
|
|
33
|
+
export declare const TIMELINE_EXTRACTOR_VERSION = 1;
|
|
34
|
+
/** Steps carried on the row in full; everything older folds into a counter. */
|
|
35
|
+
export declare const TIMELINE_KEEP_STEPS = 8;
|
|
36
|
+
/** File rows carried on the row in full; `total` still reports the real count. */
|
|
37
|
+
export declare const TIMELINE_KEEP_FILES = 8;
|
|
38
|
+
/**
|
|
39
|
+
* Steps kept in the persisted resume state. A long session narrates for hours,
|
|
40
|
+
* and the state is re-read every tick, so the fold rolls everything past this
|
|
41
|
+
* into {@link TimelineState.dropped} — exact totals, bounded bytes.
|
|
42
|
+
*/
|
|
43
|
+
export declare const TIMELINE_STATE_MAX_STEPS = 200;
|
|
44
|
+
/** Paths kept in the persisted resume state; older ones roll into `filesDropped`. */
|
|
45
|
+
export declare const TIMELINE_STATE_MAX_FILES = 200;
|
|
46
|
+
/** One file the session touched, as accumulated during the fold. */
|
|
47
|
+
interface TimelineFileEntry {
|
|
48
|
+
op: SessionFileChange['op'];
|
|
49
|
+
edits: number;
|
|
50
|
+
at: string;
|
|
51
|
+
/** True when a harness file ledger (not a tool argument) recorded this path. */
|
|
52
|
+
harness: boolean;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Everything the fold needs to continue from where it stopped. Serialized into
|
|
56
|
+
* `session_timelines.state_json`; every field is plain JSON on purpose (`open`
|
|
57
|
+
* and `pending` are indices, not object references) so a resume is exact.
|
|
58
|
+
*/
|
|
59
|
+
export interface TimelineState {
|
|
60
|
+
version: number;
|
|
61
|
+
/** Byte offset just past the last COMPLETE record folded. */
|
|
62
|
+
offset: number;
|
|
63
|
+
steps: SessionStep[];
|
|
64
|
+
/** Index of the currently open step in {@link steps}, or -1. */
|
|
65
|
+
open: number;
|
|
66
|
+
/** Harness call id → index of the step that owns it, for results that arrive later. */
|
|
67
|
+
pending: Record<string, number>;
|
|
68
|
+
firstMs: number | null;
|
|
69
|
+
lastMs: number | null;
|
|
70
|
+
/** Genuine user turns seen so far. */
|
|
71
|
+
turns: number;
|
|
72
|
+
/** The latest genuine user turn, tidied — the session's operative request. */
|
|
73
|
+
request?: SessionRequest;
|
|
74
|
+
/** The FIRST genuine user turn, kept so history still shows where it started. */
|
|
75
|
+
firstRequest?: SessionRequest;
|
|
76
|
+
/** Path → accumulated change record. */
|
|
77
|
+
files: Record<string, TimelineFileEntry>;
|
|
78
|
+
/** Set when an event cap stopped the fold short: the projection reports `partial`. */
|
|
79
|
+
truncated?: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Steps rolled out of {@link steps} to keep the persisted state bounded. Their
|
|
82
|
+
* counts still ride the projection's `earlier` block and totals, so folding a
|
|
83
|
+
* compacted state and folding the whole file report the same numbers.
|
|
84
|
+
*/
|
|
85
|
+
dropped?: {
|
|
86
|
+
steps: number;
|
|
87
|
+
tools: number;
|
|
88
|
+
failed: number;
|
|
89
|
+
blocked: number;
|
|
90
|
+
};
|
|
91
|
+
/** Paths rolled out of {@link files} for the same reason; `total` stays exact. */
|
|
92
|
+
filesDropped?: number;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Roll the oldest steps and file rows out of the state so a session that runs
|
|
96
|
+
* for hours does not grow its cached state without bound. Their tallies survive
|
|
97
|
+
* in {@link TimelineState.dropped} / {@link TimelineState.filesDropped}, which
|
|
98
|
+
* the projection adds back — so this changes the state's SIZE, never its
|
|
99
|
+
* reported numbers.
|
|
100
|
+
*/
|
|
101
|
+
export declare function compactTimelineState(state: TimelineState, maxSteps?: number, maxFiles?: number): TimelineState;
|
|
102
|
+
/** A fresh state for a session nothing has been folded for yet. */
|
|
103
|
+
export declare function emptyTimelineState(): TimelineState;
|
|
104
|
+
/**
|
|
105
|
+
* Bucket one Bash command into a verb class, reusing the repo's single command
|
|
106
|
+
* classifier (`classifyBashCommand`) rather than re-deriving a second taxonomy.
|
|
107
|
+
* Its `category` answers most of it; the three things its taxonomy deliberately
|
|
108
|
+
* does not carry — a write redirection, `agents` subcommands, and whether a
|
|
109
|
+
* build tool is running tests — are added on top.
|
|
110
|
+
*
|
|
111
|
+
* Multi-segment commands take the most consequential class present, so
|
|
112
|
+
* `cd x && bun test` is a test, not a `cd`.
|
|
113
|
+
*/
|
|
114
|
+
export declare function classifyCommandVerb(command: string | undefined): SessionVerbClass;
|
|
115
|
+
/** The verb class for one tool call: the harness's own if it classified it, else derived. */
|
|
116
|
+
export declare function verbClassForEvent(event: SessionEvent): SessionVerbClass;
|
|
117
|
+
/** Options the fold needs that are not on the events themselves. */
|
|
118
|
+
export interface FoldTimelineOptions {
|
|
119
|
+
/** Row attachments, so an image the prose never named still rides the request. */
|
|
120
|
+
attachments?: import('./types.js').SessionAttachment[];
|
|
121
|
+
/** Stop after this many events and mark the state `truncated` (the projection reads `partial`). */
|
|
122
|
+
maxEvents?: number;
|
|
123
|
+
/** Byte offset the folded events end at; stored so the next fold resumes there. */
|
|
124
|
+
offset?: number;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Fold `events` onto `prior` (or a fresh state) and return the new state.
|
|
128
|
+
*
|
|
129
|
+
* Pure: no clock, no filesystem, no database. The rules, in the order they are
|
|
130
|
+
* applied per event, are the plan's fold table — narration opens, thinking opens
|
|
131
|
+
* only when there is nothing open or the open step is already a wall of calls,
|
|
132
|
+
* tools attach, a genuine user turn is its own step, results resolve
|
|
133
|
+
* failed-vs-blocked, and a harness file ledger both marks the step and feeds the
|
|
134
|
+
* files projection.
|
|
135
|
+
*/
|
|
136
|
+
export declare function foldTimeline(events: SessionEvent[], prior?: TimelineState, opts?: FoldTimelineOptions): TimelineState;
|
|
137
|
+
/** The activity values that mean the agent is mid-step, so the newest step is live. */
|
|
138
|
+
export type TimelineActivity = string | undefined;
|
|
139
|
+
/** How {@link projectTimeline} scrubs the transcript text it is about to ship. */
|
|
140
|
+
export interface ProjectTimelineOptions {
|
|
141
|
+
/** Redact secrets from every projected string. Default true — see below. */
|
|
142
|
+
redact?: boolean;
|
|
143
|
+
/** Known credential values to mask verbatim; defaults to {@link knownSecretValuesFromEnv}. */
|
|
144
|
+
knownSecrets?: readonly string[];
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Project the folded state onto the bounded shape that rides a session row.
|
|
148
|
+
*
|
|
149
|
+
* `keep` newest steps travel in full; everything older collapses into
|
|
150
|
+
* `earlier`. The newest step is marked `live` while the session is working —
|
|
151
|
+
* that is what the sidebar renders with the amber now-line, using the `now`
|
|
152
|
+
* label the harness wrote for the call that is running.
|
|
153
|
+
*
|
|
154
|
+
* **This is the single place raw transcript text leaves the fold**, so it is
|
|
155
|
+
* where scrubbing happens — every consumer (the session row, the git-tracked
|
|
156
|
+
* fleet mirror `daemon-state.json`, and `sessions trace --steps`) is covered at
|
|
157
|
+
* the source rather than each remembering to do it. Two passes, and they are
|
|
158
|
+
* not the same thing:
|
|
159
|
+
*
|
|
160
|
+
* - **Terminal escapes are ALWAYS stripped.** A transcript is untrusted input
|
|
161
|
+
* and these labels are printed to a terminal; `sanitizeEvents` never runs on
|
|
162
|
+
* the fold's parse path (only `tail.ts` / `stream-render.ts` call it), so
|
|
163
|
+
* this is the only place that scrub happens for a timeline.
|
|
164
|
+
* - **Secrets are redacted unless the caller opts out.** `redact` defaults to
|
|
165
|
+
* ON; only the local-only `--no-redact` path passes `redact: false`, and it
|
|
166
|
+
* buys exactly that — not raw control characters.
|
|
167
|
+
*/
|
|
168
|
+
export declare function projectTimeline(state: TimelineState, activity: TimelineActivity, keep?: number, options?: ProjectTimelineOptions): SessionTimeline;
|
|
169
|
+
/** The honest empty timeline for a harness that writes no parseable transcript. */
|
|
170
|
+
export declare function unavailableTimeline(reason: string): SessionTimeline;
|
|
171
|
+
/**
|
|
172
|
+
* Project the files the session changed. Bounded to `keep` rows, newest first,
|
|
173
|
+
* with `total` reporting the real count.
|
|
174
|
+
*
|
|
175
|
+
* `source` reports where the OPERATIONS came from: `harness` when at least one
|
|
176
|
+
* entry was written by the harness's own ledger (Codex `FileChange` carries
|
|
177
|
+
* add/update/delete per path, OpenCode `patch` and Claude `file-history-delta`
|
|
178
|
+
* carry the path set), `tools` when everything was derived from Edit/Write
|
|
179
|
+
* arguments because the harness records no ledger at all.
|
|
180
|
+
*/
|
|
181
|
+
export declare function projectSessionFiles(state: TimelineState, keep?: number): SessionFiles | undefined;
|
|
182
|
+
export {};
|