@hyperdrive.bot/paseo-protocol 0.3.66 → 0.3.68

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.
@@ -136,8 +136,21 @@ export declare function deriveAgentOrigin(labels: Record<string, string> | null
136
136
  * This is a MODE, not a second dimension. AND-ing "created today" with "active
137
137
  * today" produces a mostly-empty list; the reader means one question or the
138
138
  * other.
139
+ *
140
+ * `archived` (2026-08-28) measures `archivedAt`, so the same spans answer "what
141
+ * did I archive in this period". That question cannot be asked with the
142
+ * `archivedMode` dimension alone: `only` isolates the archive but says nothing
143
+ * about WHEN. The two stay orthogonal - this picks the DATE, that picks the
144
+ * ROWS - and no coupling is needed to make the pair behave, because a session
145
+ * that was never archived has `archivedAt === null` and every caller's resolver
146
+ * yields null, which `isWithinAgentTimeWindow` already refuses to match.
147
+ *
148
+ * The one pairing that is empty by construction is `archived` with
149
+ * `archivedMode: "hide"`: rows that are NOT archived, dated by when they were
150
+ * archived. Surfaces should name that rather than render a bare zero, which
151
+ * reads as a broken filter.
139
152
  */
140
- export type AgentTimeField = "created" | "active";
153
+ export type AgentTimeField = "created" | "active" | "archived";
141
154
  /**
142
155
  * The rolling presets. They NEST (`today` is inside `3d` is inside `7d` ...),
143
156
  * which is why the span is single-select: OR-ing two always collapses to the
@@ -173,6 +186,39 @@ export type AgentTimeSelection = {
173
186
  range: AgentDateRange;
174
187
  };
175
188
  export declare function isAgentTimeField(value: unknown): value is AgentTimeField;
189
+ /**
190
+ * The field a cycling control lands on from `current`. Wraps at the end.
191
+ *
192
+ * Exported from the protocol so the app's chip cannot drift from the declared
193
+ * list. The two-way flip this replaced (`f === "created" ? "active" : "created"`)
194
+ * made any third member unreachable: the label rendered, no tap could select
195
+ * it. Driving the cycle off AGENT_TIME_FIELDS means a fourth field is
196
+ * selectable the moment it joins the union.
197
+ */
198
+ export declare function nextAgentTimeField(current: AgentTimeField): AgentTimeField;
199
+ /** The agent timestamps a time field can name. Shapes differ per caller, so this is the union of them. */
200
+ export interface AgentTimeSource {
201
+ createdAt?: Date | null;
202
+ lastActivityAt?: Date | null;
203
+ archivedAt?: Date | null;
204
+ }
205
+ /**
206
+ * Collapses an agent's timestamps to the ONE the reader selected.
207
+ *
208
+ * This is what makes the field a mode rather than a second dimension:
209
+ * everything downstream compares a single value and never learns which field
210
+ * produced it. Exported so the four surfaces that each carried their own copy
211
+ * of this mapping (the app's Sessions screen and workspace tab strip, the
212
+ * daemon's `list_agents`, the CLI's `agent ls`) share one definition.
213
+ *
214
+ * A `switch`, not a ternary. All four copies were written as
215
+ * `field === "created" ? created : active`, which treats every field that is
216
+ * not "created" as "active" - so a new union member compiled clean on all four
217
+ * and silently measured the wrong timestamp while the UI named the new one.
218
+ * Exhaustive here, so a fifth field is a type error instead of a filter that
219
+ * quietly lies.
220
+ */
221
+ export declare function resolveAgentTimeValue(agent: AgentTimeSource, field: AgentTimeField): Date | null;
176
222
  export declare function isAgentTimeWindow(value: unknown): value is AgentTimeWindow;
177
223
  /** True for a real `YYYY-MM-DD` calendar day (rejects 2026-02-31). */
178
224
  export declare function isAgentDay(value: unknown): value is string;
@@ -190,12 +190,51 @@ export function isAgentOrigin(value) {
190
190
  export function deriveAgentOrigin(labels) {
191
191
  return isDelegatedAgent({ labels }) ? "delegated" : "root";
192
192
  }
193
- export const AGENT_TIME_FIELDS = ["created", "active"];
193
+ export const AGENT_TIME_FIELDS = ["created", "active", "archived"];
194
194
  export const AGENT_TIME_WINDOWS = ["today", "3d", "7d", "30d", "stale"];
195
195
  const DAY_MS = 24 * 60 * 60 * 1000;
196
196
  const DAY_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
197
197
  export function isAgentTimeField(value) {
198
- return value === "created" || value === "active";
198
+ return value === "created" || value === "active" || value === "archived";
199
+ }
200
+ /**
201
+ * The field a cycling control lands on from `current`. Wraps at the end.
202
+ *
203
+ * Exported from the protocol so the app's chip cannot drift from the declared
204
+ * list. The two-way flip this replaced (`f === "created" ? "active" : "created"`)
205
+ * made any third member unreachable: the label rendered, no tap could select
206
+ * it. Driving the cycle off AGENT_TIME_FIELDS means a fourth field is
207
+ * selectable the moment it joins the union.
208
+ */
209
+ export function nextAgentTimeField(current) {
210
+ const index = AGENT_TIME_FIELDS.indexOf(current);
211
+ return AGENT_TIME_FIELDS[(index + 1) % AGENT_TIME_FIELDS.length] ?? "active";
212
+ }
213
+ /**
214
+ * Collapses an agent's timestamps to the ONE the reader selected.
215
+ *
216
+ * This is what makes the field a mode rather than a second dimension:
217
+ * everything downstream compares a single value and never learns which field
218
+ * produced it. Exported so the four surfaces that each carried their own copy
219
+ * of this mapping (the app's Sessions screen and workspace tab strip, the
220
+ * daemon's `list_agents`, the CLI's `agent ls`) share one definition.
221
+ *
222
+ * A `switch`, not a ternary. All four copies were written as
223
+ * `field === "created" ? created : active`, which treats every field that is
224
+ * not "created" as "active" - so a new union member compiled clean on all four
225
+ * and silently measured the wrong timestamp while the UI named the new one.
226
+ * Exhaustive here, so a fifth field is a type error instead of a filter that
227
+ * quietly lies.
228
+ */
229
+ export function resolveAgentTimeValue(agent, field) {
230
+ switch (field) {
231
+ case "created":
232
+ return agent.createdAt ?? null;
233
+ case "active":
234
+ return agent.lastActivityAt ?? null;
235
+ case "archived":
236
+ return agent.archivedAt ?? null;
237
+ }
199
238
  }
200
239
  export function isAgentTimeWindow(value) {
201
240
  return (value === "today" || value === "3d" || value === "7d" || value === "30d" || value === "stale");