@paigy/harness 0.3.29 → 0.3.30

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/dist/cli.js CHANGED
@@ -24186,7 +24186,7 @@ async function subscribeWake(onNudge, opts = {}) {
24186
24186
  }
24187
24187
  };
24188
24188
  }
24189
- var import_undici, require2, BACKEND_URL, NETWORK_MSG, PROXY_ENV, agent, INSTANCE_ID, WORKSPACE_ID, envSession, lineage, ignoreOverride, defaultOptions, getDefaultOptions, getRefs, getRelativePath, parseCatchDef, integerDateParser, isJsonSchema7AllOfType, emojiRegex2, zodPatterns, ALPHA_NUMERIC, primitiveMappings, asAnyOf, parseOptionalDef, parsePipelineDef, parseReadonlyDef, selectParser, get$ref, addMeta, zodToJsonSchema, OPTIONS_MIN, OPTIONS_MAX, OptionSchema, OptionInputSchema, NIGHT, AskInputSchema, StartContactSchema, ContactSchema, CONTACT_SCHEMA, CONTACT_DESCRIPTION, CreateGoalSchema, CreateGoalToolSchema, CREATE_GOAL_DESCRIPTION, UpdateGoalSchema, UpdateGoalToolSchema, ClaimGoalSchema, GetGoalSchema, GET_GOAL_DESCRIPTION, UPDATE_GOAL_DESCRIPTION, CLAIM_GOAL_DESCRIPTION, CHECK_REPLIES_DESCRIPTION, CheckRepliesSchema, AGENT_TOOLS, AGENT_TOOL_NAMES, LIVE_MS, ContextSchema, ParticipantSchema, TransformSchema, VisualSchema, NotifyLevelSchema, SelectShapeSchema, ReceiptEventSchema, AttentionSchema, NotifyRequestFields, NotifyRequestSchema, NotifyStatusSchema, AgentStateSchema, TurnSchema, UserAnswerSchema, IntentSchema, RideAlongSchema, AwaitItemSchema, VoiceKeySchema, AgendaTurnSchema, CLAIM_STALE_MS, InboxItemSchema, APNS_TOKEN_RE, PushTokenSchema, MissedCallSchema, clock, QUIET, BrokerTuningSchema, UserSettingsSchema, HistoryWorkSchema, HistoryEntrySchema, HistoryPageSchema, ACTIVITY_LINES, ACTIVITY_LINE_MAX, AgentActivitySchema, ConnectionSummarySchema, LedgerItemSchema, AgentLedgerSchema, ReassignResultSchema, MoveRingSchema, MoveSchema, QueueQuestionSchema, QueueReplySchema, QueueItemSchema, COLD_AFTER_MS, NoteSourceSchema, NoteStatusSchema, NoteRepeatSchema, DecisionSchema, NoteSchema, TriageItemSchema, TriageAssignmentSchema, TriageStatusSchema, SubmitTriageSchema, TriageProposalSchema, AcceptTriageSchema, AcceptTriageResultSchema, DeliveryModeSchema, WAKE_EVENT, wakeChannel, RegisterDeliverySchema, OAuthStartSchema, DeliveryConfigSchema, HostDecisionSchema, WakeNudgeSchema, PairingStatusSchema, DeviceCodeSchema, DeviceInfoSchema, DeviceTokenSchema, SupportRequestSchema, NotificationFeedbackKindSchema, SlimOptionSchema, QuestionRowSchema, WorkStateSchema, WorkRowSchema, ComputerRowSchema, AgentRowSchema, SnapshotSchema, TOKEN_PATH, KEY_PATH, sleep, UnpairedError, RevokedError, ApiError, tokenOverride, authToken, AWAIT_WINDOW_MS, asked, NOT_SENT, HEARTBEAT_MS, RETRY_MS, JOIN, JOIN_TIMEOUT_MS, lines;
24189
+ var import_undici, require2, BACKEND_URL, NETWORK_MSG, PROXY_ENV, agent, INSTANCE_ID, WORKSPACE_ID, envSession, lineage, ignoreOverride, defaultOptions, getDefaultOptions, getRefs, getRelativePath, parseCatchDef, integerDateParser, isJsonSchema7AllOfType, emojiRegex2, zodPatterns, ALPHA_NUMERIC, primitiveMappings, asAnyOf, parseOptionalDef, parsePipelineDef, parseReadonlyDef, selectParser, get$ref, addMeta, zodToJsonSchema, OPTIONS_MIN, OPTIONS_MAX, OptionSchema, OptionInputSchema, NIGHT, AskInputSchema, StartContactSchema, ContactSchema, CONTACT_SCHEMA, CONTACT_DESCRIPTION, CreateGoalSchema, CreateGoalToolSchema, CREATE_GOAL_DESCRIPTION, UpdateGoalSchema, UpdateGoalToolSchema, ClaimGoalSchema, GetGoalSchema, GET_GOAL_DESCRIPTION, UPDATE_GOAL_DESCRIPTION, CLAIM_GOAL_DESCRIPTION, CHECK_REPLIES_DESCRIPTION, CheckRepliesSchema, AGENT_TOOLS, AGENT_TOOL_NAMES, LIVE_MS, WORKING_MS, ContextSchema, ParticipantSchema, TransformSchema, VisualSchema, NotifyLevelSchema, SelectShapeSchema, ReceiptEventSchema, AttentionSchema, NotifyRequestFields, NotifyRequestSchema, NotifyStatusSchema, AgentStateSchema, TurnSchema, UserAnswerSchema, IntentSchema, RideAlongSchema, AwaitItemSchema, VoiceKeySchema, AgendaTurnSchema, CLAIM_STALE_MS, InboxItemSchema, APNS_TOKEN_RE, PushTokenSchema, MissedCallSchema, clock, QUIET, BrokerTuningSchema, UserSettingsSchema, HistoryWorkSchema, HistoryEntrySchema, HistoryPageSchema, ACTIVITY_LINES, ACTIVITY_LINE_MAX, AgentActivitySchema, ConnectionSummarySchema, LedgerItemSchema, AgentLedgerSchema, ReassignResultSchema, MoveRingSchema, MoveSchema, QueueQuestionSchema, QueueReplySchema, QueueItemSchema, COLD_AFTER_MS, NoteSourceSchema, NoteStatusSchema, NoteRepeatSchema, DecisionSchema, NoteSchema, TriageItemSchema, TriageAssignmentSchema, TriageStatusSchema, SubmitTriageSchema, TriageProposalSchema, AcceptTriageSchema, AcceptTriageResultSchema, DeliveryModeSchema, WAKE_EVENT, wakeChannel, RegisterDeliverySchema, OAuthStartSchema, DeliveryConfigSchema, HostDecisionSchema, WakeNudgeSchema, PairingStatusSchema, DeviceCodeSchema, DeviceInfoSchema, DeviceTokenSchema, SupportRequestSchema, NotificationFeedbackKindSchema, SlimOptionSchema, QuestionRowSchema, WorkStateSchema, WorkRowSchema, ComputerRowSchema, AgentRowSchema, SnapshotSchema, CallRecapSchema, TOKEN_PATH, KEY_PATH, sleep, UnpairedError, RevokedError, ApiError, tokenOverride, authToken, AWAIT_WINDOW_MS, asked, NOT_SENT, HEARTBEAT_MS, RETRY_MS, JOIN, JOIN_TIMEOUT_MS, lines;
24190
24190
  var init_dist = __esm({
24191
24191
  "../../packages/sdk/dist/index.js"() {
24192
24192
  "use strict";
@@ -24666,7 +24666,7 @@ var init_dist = __esm({
24666
24666
  history: external_exports.boolean().optional(),
24667
24667
  /** WHEN A READ HAS CONFUSED YOU. Adds `diagnosis`: every reader that already answers a question
24668
24668
  * about this work, each answer attributed to the reader that gave it, and every disagreement
24669
- * between two of them named. Off by default; `apps/api/src/goal/diagnose-design.md`. */
24669
+ * between two of them named. Off by default; `docs/model/goal/diagnose-design.md`. */
24670
24670
  diagnose: external_exports.boolean().optional()
24671
24671
  }).strict();
24672
24672
  GET_GOAL_DESCRIPTION = "Read one Goal without claiming it: its outcome, state, revision, progress, blockers, the conversation on it (each question with its options and what was decided), `next`, the one step to take, and `more`, where to read further. The conversation carries everything the person said, every open question and your newest entry; your older entries are left out and counted \u2014 history: true reads every entry in full. Another account's Goals, and another agent's Goal that is not below one of yours, are not disclosed.\n\ndiagnose: true is for when a read has CONFUSED you \u2014 not for the working loop. It adds `diagnosis`, which asks every reader that already answers a question about your work and NAMES the reader behind every value, so you never guess which of two places to look: what state your work is really in (the stored `goals.state` beside the derived `goal_execution_state`, which can differ); whether anything is armed to ring and when (`ladder_candidates`, with the ladder's own last decision); whether a wake fired for you and what it did (durable `wake.*` events, beside when your process was last heard from); and what has reached you (`list_waiting` \u2014 review flags, unstarted work, open questions, the person's newest words). Read `diagnosis.disagreements` FIRST: each one is two readers giving different values for the same fact, with both values and both sources, and it never chooses between them \u2014 that is yours to do, from the Goal's own history. `diagnosis.looked` names every reader asked, including any that could not answer, so an empty answer is never confused with a broken one.";
@@ -24684,6 +24684,7 @@ var init_dist = __esm({
24684
24684
  ];
24685
24685
  AGENT_TOOL_NAMES = AGENT_TOOLS.map((t) => t.name);
24686
24686
  LIVE_MS = 3 * 6e4;
24687
+ WORKING_MS = 30 * 6e4;
24687
24688
  ContextSchema = external_exports.object({
24688
24689
  title: external_exports.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
24689
24690
  description: external_exports.array(external_exports.string().min(1)).describe(
@@ -24754,7 +24755,7 @@ var init_dist = __esm({
24754
24755
  points: external_exports.array(external_exports.string()).nullable(),
24755
24756
  /** Whether the ask blocks the sender — what lets arbitration escalate it on silence. */
24756
24757
  blocking: external_exports.boolean(),
24757
- /** Reserved (MODEL.md lists it): a response deadline. No row column yet — a later Phase 2
24758
+ /** Reserved (docs/model/model.md lists it): a response deadline. No row column yet — a later Phase 2
24758
24759
  * slice wires it; optional so today's rows/callers project cleanly. */
24759
24760
  deadline: external_exports.string().datetime().nullable().optional()
24760
24761
  });
@@ -24805,7 +24806,7 @@ var init_dist = __esm({
24805
24806
  * derive agent-side before sealing, so the server only ever shapes plaintext). */
24806
24807
  // 10k, not a sentence budget. What the human hears is bounded by the BROKER — it splits
24807
24808
  // the ask into topics and gives each one at most three sentences and one question
24808
- // (broker/agenda-design.md) — not by a wire cap the agent has to pre-summarize under.
24809
+ // (docs/brain/broker/agenda-design.md) — not by a wire cap the agent has to pre-summarize under.
24809
24810
  // Owner, 2026-07-28: "our actual limitation on how long something is to the user should
24810
24811
  // come from the broker splitting and summarizing." The cap that remains is a size guard.
24811
24812
  ask: external_exports.string().min(1).max(1e4).optional().describe(
@@ -24882,7 +24883,7 @@ var init_dist = __esm({
24882
24883
  external_exports.object({ kind: external_exports.literal("clarify"), chunks: external_exports.array(external_exports.string()).min(1) }),
24883
24884
  external_exports.object({ kind: external_exports.literal("confirm"), approved: external_exports.boolean() }),
24884
24885
  external_exports.object({ kind: external_exports.literal("turns"), turns: external_exports.array(TurnSchema).min(1) }),
24885
- /** An auto-answer derived from the user's PAST decisions (broker/precedent-design.md §2):
24886
+ /** An auto-answer derived from the user's PAST decisions (docs/brain/broker/precedent-design.md §2):
24886
24887
  * delivered through the same settle/await path as a human answer, carrying the judge's
24887
24888
  * derivation and the precedent ids it grew from. Always paired with a visible trail
24888
24889
  * card the user can reply to — the broker never overrides the user. */
@@ -24931,7 +24932,7 @@ var init_dist = __esm({
24931
24932
  * and now knows exactly which call to make. Absent when either half is missing —
24932
24933
  * a sentence with a hole in it is worse than no sentence. */
24933
24934
  note: external_exports.string().optional(),
24934
- /** The call record rendered for THIS agent (`voice/record-design.md`): the words the
24935
+ /** The call record rendered for THIS agent (`docs/brain/voice/record-design.md`): the words the
24935
24936
  * shaped answer was mapped from, filtered to its own claims. There is no second list
24936
24937
  * of labels beside it — the acts went 2026-09-04 and `intents` went with them (owner,
24937
24938
  * 2026-09-04): the agent reads the sentence and decides. */
@@ -25027,10 +25028,11 @@ var init_dist = __esm({
25027
25028
  agentId: external_exports.string().optional(),
25028
25029
  select: SelectShapeSchema.optional(),
25029
25030
  options: external_exports.array(OptionSchema.omit({ id: true })).optional(),
25030
- /** Pacing (#826, owner 2026-08-03: "how fast we move through them ... are parameters"):
25031
- * seconds the floor stays open after this turn speaks. Absent = the bot's defaults
25032
- * (the beat for context, the answer window for asks). Clamped bot-side. */
25033
- pace: external_exports.number().positive().optional(),
25031
+ /* `pace` STOOD HERE (#826). A turn could carry seconds and the model chose them. The walk
25032
+ paces itself now — a short beat between the sentences of a turn, the longer one at its end
25033
+ (owner, 2026-09-30: "remove the bot deciding pace") — and it does that where the words are
25034
+ spoken, not where the plan is written, so nothing between the brain and the walk decides
25035
+ anything. A caller's own `beat_s` tuning is what it used to override. */
25034
25036
  /** Whether the walk WAITS for an answer before moving on. Absent = derived as today
25035
25037
  * (a question blocks, context flows). blocking:false on a question = ask and move
25036
25038
  * on, the claim stays pending; blocking:true on context = hold for a reply. */
@@ -25065,7 +25067,7 @@ var init_dist = __esm({
25065
25067
  * instant the agent picks it up (#997). Absent = pre-trigger row; fall back to
25066
25068
  * `createdAt`. */
25067
25069
  agentStateAt: external_exports.string().datetime().optional(),
25068
- /** THE QUESTIONS A CALL CARRIES — the call screen's agenda spine (walk/design.md §11, owner
25070
+ /** THE QUESTIONS A CALL CARRIES — the call screen's agenda spine (docs/clients/app/walk/design.md §11, owner
25069
25071
  * 2026-09-22). One per DecisionNeed on the Call, in the Call's order, answered or open (a
25070
25072
  * superseded or cancelled need is no longer a question anyone is asked). Present only on a
25071
25073
  * Call's cards, and every card of that Call carries the same list: the call screen reads it
@@ -25126,7 +25128,7 @@ var init_dist = __esm({
25126
25128
  * off the same open list the card came from, so it clears when the Call does. A card is the
25127
25129
  * backup for a call not taken; while the call has it, the call is where it is answered. */
25128
25130
  onCall: external_exports.literal(true).optional(),
25129
- /** THE RING, ON THE ITEM (walk/design.md §12 §17, #2251): the last ring on this card was
25131
+ /** THE RING, ON THE ITEM (docs/clients/app/walk/design.md §12 §17, #2251): the last ring on this card was
25130
25132
  * declined, and what the ladder will do next — read off the cron's own row, never computed
25131
25133
  * on the phone. Present only while a `declined` receipt stands on the card's last Call.
25132
25134
  * The ladder is ACCOUNT-WIDE (#2259): `anchorAt` and `step` are the account's position;
@@ -25157,7 +25159,7 @@ var init_dist = __esm({
25157
25159
  "Labels for a select:'confirm' paige \u2014 'yesno' (Yes/No) or 'approve' (Approve/Deny). Ignored unless select is 'confirm'."
25158
25160
  ),
25159
25161
  /** Real downstream work is stuck behind this one — set by the agent, independent of
25160
- * urgency (see the main README's "premier use case" + notify/states.md). Drives the
25162
+ * urgency (see the main README's "premier use case" + docs/delivery/notify/states.md). Drives the
25161
25163
  * inbox's blocking badge and the extra confirm step before dismissing it. */
25162
25164
  blocking: external_exports.boolean().default(false),
25163
25165
  /** The user's locked-in answer; present only for replied items (GET /api/replied/:id). */
@@ -25231,9 +25233,16 @@ var init_dist = __esm({
25231
25233
  banner: external_exports.boolean(),
25232
25234
  push: external_exports.boolean()
25233
25235
  }),
25236
+ /** LockedIn / Default / DateNight on screen; the stored words are unchanged on purpose —
25237
+ * they are an enum on a live column across every account, and the rename is a rename of
25238
+ * what people read (owner, 2026-09-30). */
25234
25239
  sessionMode: external_exports.enum(["default", "all_calls", "silent"]),
25235
- silentPush: external_exports.boolean(),
25236
- autoCallback: external_exports.boolean(),
25240
+ /** DEAD, AND ACCEPTED ANYWAY (#2813). DateNight is inbox-only with no way back, so nothing
25241
+ * reads this. It stays OPTIONAL rather than deleted because a phone on the old bundle
25242
+ * PATCHes the whole settings object and would be refused for sending a key we stopped
25243
+ * wanting — the same trap `voiceMode` below already documents. #2813 drops the field and
25244
+ * its column once no installed build still sends it. */
25245
+ silentPush: external_exports.boolean().optional(),
25237
25246
  /** Opt-in (default false) to using your content to improve Paigy and train models. */
25238
25247
  improveConsent: external_exports.boolean(),
25239
25248
  missedCall: MissedCallSchema.default("backoff_standard"),
@@ -25244,12 +25253,16 @@ var init_dist = __esm({
25244
25253
  * must not silently reset this privacy choice. Absent = leave unchanged on
25245
25254
  * write, 'hosted' on read (see store.ts). */
25246
25255
  voiceMode: external_exports.enum(["hosted", "on_device"]).optional(),
25247
- /** Talk — after you answer, the next step is read aloud (walk/design.md §6). ALWAYS ON until
25256
+ /** Talk — after you answer, the next step is read aloud (docs/clients/app/walk/design.md §6). ALWAYS ON until
25248
25257
  * turned off (owner, 2026-09-18, #2249): a setting, not a per-walk toggle. Optional, NOT
25249
25258
  * defaulted, for the same reason `voiceMode` is: a stale client PATCHing the full settings
25250
25259
  * object must not silently turn it back on. Absent = leave unchanged on write, true on
25251
25260
  * read (see store.ts). */
25252
25261
  talk: external_exports.boolean().optional(),
25262
+ /** CALL DIAGNOSTICS (owner, 2026-10-01): the call report carries each listen and the bot's own
25263
+ * load timings. SERVER-SET, no UI — on for every account that existed on 2026-10-01, off for
25264
+ * newer ones (migration 20261001132859). Read-only here: the settings PATCH never writes it. */
25265
+ callDiagnostics: external_exports.boolean().optional(),
25253
25266
  /** Per-user ring budget (#603): calls per rolling day before further calls
25254
25267
  * degrade to banner. Absent = the global default (25). A number, never a
25255
25268
  * bypass — every account keeps a ceiling. No UI; set per user for testing. */
@@ -25258,9 +25271,6 @@ var init_dist = __esm({
25258
25271
  * slower speaker). No API-side semantics; the bot resolves each key with its
25259
25272
  * own defaults. Set per user (no UI yet); absent = bot defaults. */
25260
25273
  voiceTuning: external_exports.record(external_exports.string(), external_exports.union([external_exports.number(), external_exports.string()])).optional(),
25261
- /** Opt-in to real-phone (PSTN) calls when the app can't ring. Optional, not
25262
- * defaulted — an older client PATCHing the full object must not clobber it. */
25263
- pstnCalls: external_exports.boolean().optional(),
25264
25274
  /** The user's IANA timezone (e.g. "America/Bogota"), recorded by the app — it is the
25265
25275
  * only party that knows it. REMINDERS are why it exists: "remind me at ten" becomes
25266
25276
  * an absolute `due_at` only if we know whose ten. Optional and never defaulted, for
@@ -25304,7 +25314,7 @@ var init_dist = __esm({
25304
25314
  /** The credential kind: "device" = a paired machine (mint-only — it hosts and mints, it
25305
25315
  * never talks); "agent" = an identity that sends. The roster and devices surfaces split
25306
25316
  * on this. Optional/absent reads as "agent" (a row predating the kind column). See
25307
- * apps/api/src/tokens/devices-vs-agents-design.md. */
25317
+ * docs/server/tokens/devices-vs-agents-design.md. */
25308
25318
  kind: external_exports.enum(["device", "agent"]).optional(),
25309
25319
  /** For an agent, the token id of the DEVICE that minted it — so agents group under their
25310
25320
  * machine, and revoking a device cascades to them. Null on devices, and on unlinked
@@ -25322,7 +25332,7 @@ var init_dist = __esm({
25322
25332
  * user on the agent's own page. null = no ceiling (today's behaviour for every
25323
25333
  * connection). Android binds importance to a relationship rather than to each message,
25324
25334
  * and that is the thing our roster could not say: "Marlow may always call me; Otto
25325
- * never may" (navigation-design.md, gap 2). Clamped in `arbitrateLevel`, so it binds
25335
+ * never may" (docs/clients/app/party/navigation-design.md, gap 2). Clamped in `arbitrateLevel`, so it binds
25326
25336
  * every surface at once and outranks even `sessionMode: all_calls` — a mode the user
25327
25337
  * set once must not overrule a rule they set about one agent. */
25328
25338
  reach: NotifyLevelSchema.nullable().optional(),
@@ -25333,7 +25343,14 @@ var init_dist = __esm({
25333
25343
  /** Last presence heartbeat from a running agent process (POST /api/presence) — the
25334
25344
  * desktop app while open. Null = never seen; stale = offline. */
25335
25345
  lastSeenAt: external_exports.string().datetime().nullable().optional(),
25336
- /** What a live desktop can run (companion.md §2.2), advertised on its heartbeat:
25346
+ /** WORKING, NOT JUST CONNECTED (owner, 2026-09-30): the last time the agent itself acted on one of
25347
+ * its Goals — took its lease or recorded an operation (`tokens.last_worked_at`). Within
25348
+ * `WORKING_MS` it is working; otherwise it is connected but idle. Null = not seen working yet. */
25349
+ lastWorkedAt: external_exports.string().datetime().nullable().optional(),
25350
+ /** The oldest of its Goals that is `ready` for it — work handed to it that nobody has started.
25351
+ * With no work of its own for `WORKING_MS`, an agent sitting on this is not taking its work. */
25352
+ oldestReadyAt: external_exports.string().datetime().nullable().optional(),
25353
+ /** What a live desktop can run (docs/clients/desktop/companion.md §2.2), advertised on its heartbeat:
25337
25354
  * harness availabilities + granted workspaces — the option set the phone's
25338
25355
  * "new session" sheet offers. Absent for ordinary MCP agents. */
25339
25356
  runtime: external_exports.object({
@@ -25341,7 +25358,12 @@ var init_dist = __esm({
25341
25358
  * shows its age here (`apps/desktop/src/update.ts`). */
25342
25359
  version: external_exports.string().optional(),
25343
25360
  harnesses: external_exports.array(external_exports.object({ name: external_exports.string(), label: external_exports.string(), status: external_exports.string() })).optional(),
25344
- workspaces: external_exports.array(external_exports.string()).optional()
25361
+ workspaces: external_exports.array(external_exports.string()).optional(),
25362
+ /** THE GIT REPOS IN THOSE FOLDERS (2026-10-01, Goal 26982211): each granted folder that is a
25363
+ * repo, and each repo directly inside one, with its `origin` remote. A session started for
25364
+ * work on `mauurda/paigy` opens in that repo rather than the folder above it, where the repo's
25365
+ * own AGENTS.md is never read (`workspaceForRepo`). Absent on hosts that predate it. */
25366
+ repos: external_exports.array(external_exports.object({ path: external_exports.string(), remote: external_exports.string() })).optional()
25345
25367
  }).optional(),
25346
25368
  /** The tail of this agent's working log, when a harness is driving it — the agent page's
25347
25369
  * live strip. Absent for anything the desktop harness isn't running (a hatched identity
@@ -25424,7 +25446,7 @@ var init_dist = __esm({
25424
25446
  * for a settled question whose reply carried nothing readable. */
25425
25447
  answer: external_exports.string().nullable().default(null),
25426
25448
  /** The Goal this question belongs to — a step knows its Goal on its own, not only through
25427
- * an `InboxItem`'s `communication.goalIds[0]` (walk/design.md §12 item 3).
25449
+ * an `InboxItem`'s `communication.goalIds[0]` (docs/clients/app/walk/design.md §12 item 3).
25428
25450
  * READ BY `apps/client/src/walk/order.ts`, which stamps it onto every `WalkStep`: the walk's
25429
25451
  * order, its route, home's trees and the list of steps all take a step's Goal from here, so
25430
25452
  * this is the field they agree through rather than each re-deriving it from the row it
@@ -25516,7 +25538,19 @@ var init_dist = __esm({
25516
25538
  said: external_exports.string(),
25517
25539
  at: external_exports.string(),
25518
25540
  entryId: external_exports.string()
25519
- }).nullable().optional()
25541
+ }).nullable().optional(),
25542
+ /** WHEN THIS PERSON LAST PUT A HAND ON IT THEMSELVES (owner, Paigy Goal 16d18f51, 2026-09-30):
25543
+ * the newest Entry on the Goal they wrote, of any kind — a line they added, a reply to a note, an
25544
+ * answer to a question, a voice note filed as work. Null when the only hands on it have been its
25545
+ * agent's; optional, so a hand-built queue (fixtures, the demo) and a door older than
25546
+ * 20260930222356 need not spell it — `work/list.ts`'s `yoursAt` keeps its other sources, so the
25547
+ * Work tab orders as it did before the migration rather than throwing on the missing key.
25548
+ *
25549
+ * It is a FACT, not a reconstruction: `latest` holds one Entry, so an agent's progress note a
25550
+ * minute after the person speaks erases their instant from it, and the durable traces the client
25551
+ * can see (`replies`, `questions[].answeredAt`) miss a spontaneous note entirely — a `request`
25552
+ * Entry with no `about_id` is in neither. */
25553
+ lastPersonAt: external_exports.string().nullable().optional()
25520
25554
  });
25521
25555
  COLD_AFTER_MS = 3 * 24 * 60 * 60 * 1e3;
25522
25556
  NoteSourceSchema = external_exports.enum(["app", "call"]);
@@ -25546,7 +25580,7 @@ var init_dist = __esm({
25546
25580
  assignee: external_exports.string().nullable(),
25547
25581
  /** The request thread minted at assignment; null until assigned. */
25548
25582
  parentId: external_exports.string().nullable(),
25549
- /** REMINDERS (reminders-design.md): the NOT-BEFORE this becomes eligible to ride a
25583
+ /** REMINDERS (docs/model/notes/reminders-design.md): the NOT-BEFORE this becomes eligible to ride a
25550
25584
  * call — never a deadline. It only ever comes from the user's own words, so when it
25551
25585
  * passes Paigy rings ONCE (#1293, owner 2026-08-26: a time said out loud is consent to
25552
25586
  * a ring at that time); after that it rides like any other. Null = "the very next
@@ -25808,6 +25842,29 @@ var init_dist = __esm({
25808
25842
  computers: external_exports.array(ComputerRowSchema).nullable()
25809
25843
  }).nullable()
25810
25844
  });
25845
+ CallRecapSchema = external_exports.object({
25846
+ call: external_exports.object({
25847
+ status: external_exports.string(),
25848
+ startedAt: external_exports.string(),
25849
+ durationMs: external_exports.number().nullable(),
25850
+ agents: external_exports.array(external_exports.object({ id: external_exports.string(), name: external_exports.string().nullable() }))
25851
+ }),
25852
+ topics: external_exports.array(external_exports.object({
25853
+ goalId: external_exports.string().uuid(),
25854
+ title: external_exports.string(),
25855
+ owner: external_exports.string(),
25856
+ state: external_exports.string(),
25857
+ questions: external_exports.array(external_exports.object({ id: external_exports.string().uuid(), state: external_exports.string(), title: external_exports.string() })),
25858
+ /** `words` is always what they SAID, verbatim — the record, never replaced. `headline` is
25859
+ * their answer on one line when the call's read wrote one (owner, 2026-10-01: "render them
25860
+ * summarized like a pre-made option is"), so the row scans like a chosen option and their
25861
+ * own words stay under it. Absent on every line stored before the read wrote them, and on
25862
+ * anything that is not an answer. */
25863
+ lines: external_exports.array(external_exports.object({ entryId: external_exports.string().uuid(), words: external_exports.string(), headline: external_exports.string().optional() }))
25864
+ })),
25865
+ unfiled: external_exports.array(external_exports.object({ lineId: external_exports.string().uuid(), words: external_exports.string(), atMs: external_exports.number() })),
25866
+ more: external_exports.object({ lines: external_exports.number(), entries: external_exports.number(), topics: external_exports.number() })
25867
+ });
25811
25868
  TOKEN_PATH = join22(homedir22(), ".paigy", "token.json");
25812
25869
  KEY_PATH = join22(homedir22(), ".paigy", "key.json");
25813
25870
  sleep = (ms) => new Promise((r) => setTimeout(r, ms));
@@ -32989,7 +33046,7 @@ var GetGoalSchema2 = external_exports.object({
32989
33046
  history: external_exports.boolean().optional(),
32990
33047
  /** WHEN A READ HAS CONFUSED YOU. Adds `diagnosis`: every reader that already answers a question
32991
33048
  * about this work, each answer attributed to the reader that gave it, and every disagreement
32992
- * between two of them named. Off by default; `apps/api/src/goal/diagnose-design.md`. */
33049
+ * between two of them named. Off by default; `docs/model/goal/diagnose-design.md`. */
32993
33050
  diagnose: external_exports.boolean().optional()
32994
33051
  }).strict();
32995
33052
  var GET_GOAL_DESCRIPTION2 = "Read one Goal without claiming it: its outcome, state, revision, progress, blockers, the conversation on it (each question with its options and what was decided), `next`, the one step to take, and `more`, where to read further. The conversation carries everything the person said, every open question and your newest entry; your older entries are left out and counted \u2014 history: true reads every entry in full. Another account's Goals, and another agent's Goal that is not below one of yours, are not disclosed.\n\ndiagnose: true is for when a read has CONFUSED you \u2014 not for the working loop. It adds `diagnosis`, which asks every reader that already answers a question about your work and NAMES the reader behind every value, so you never guess which of two places to look: what state your work is really in (the stored `goals.state` beside the derived `goal_execution_state`, which can differ); whether anything is armed to ring and when (`ladder_candidates`, with the ladder's own last decision); whether a wake fired for you and what it did (durable `wake.*` events, beside when your process was last heard from); and what has reached you (`list_waiting` \u2014 review flags, unstarted work, open questions, the person's newest words). Read `diagnosis.disagreements` FIRST: each one is two readers giving different values for the same fact, with both values and both sources, and it never chooses between them \u2014 that is yours to do, from the Goal's own history. `diagnosis.looked` names every reader asked, including any that could not answer, so an empty answer is never confused with a broken one.";
@@ -33022,6 +33079,7 @@ function entryWords(entry) {
33022
33079
  return entry.sources.map((source) => source.text).join("\n");
33023
33080
  }
33024
33081
  var LIVE_MS2 = 3 * 6e4;
33082
+ var WORKING_MS2 = 30 * 6e4;
33025
33083
  var ContextSchema2 = external_exports.object({
33026
33084
  title: external_exports.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
33027
33085
  description: external_exports.array(external_exports.string().min(1)).describe(
@@ -33092,7 +33150,7 @@ var AttentionSchema2 = external_exports.object({
33092
33150
  points: external_exports.array(external_exports.string()).nullable(),
33093
33151
  /** Whether the ask blocks the sender — what lets arbitration escalate it on silence. */
33094
33152
  blocking: external_exports.boolean(),
33095
- /** Reserved (MODEL.md lists it): a response deadline. No row column yet — a later Phase 2
33153
+ /** Reserved (docs/model/model.md lists it): a response deadline. No row column yet — a later Phase 2
33096
33154
  * slice wires it; optional so today's rows/callers project cleanly. */
33097
33155
  deadline: external_exports.string().datetime().nullable().optional()
33098
33156
  });
@@ -33143,7 +33201,7 @@ var NotifyRequestFields2 = external_exports.object({
33143
33201
  * derive agent-side before sealing, so the server only ever shapes plaintext). */
33144
33202
  // 10k, not a sentence budget. What the human hears is bounded by the BROKER — it splits
33145
33203
  // the ask into topics and gives each one at most three sentences and one question
33146
- // (broker/agenda-design.md) — not by a wire cap the agent has to pre-summarize under.
33204
+ // (docs/brain/broker/agenda-design.md) — not by a wire cap the agent has to pre-summarize under.
33147
33205
  // Owner, 2026-07-28: "our actual limitation on how long something is to the user should
33148
33206
  // come from the broker splitting and summarizing." The cap that remains is a size guard.
33149
33207
  ask: external_exports.string().min(1).max(1e4).optional().describe(
@@ -33220,7 +33278,7 @@ var UserAnswerSchema2 = external_exports.discriminatedUnion("kind", [
33220
33278
  external_exports.object({ kind: external_exports.literal("clarify"), chunks: external_exports.array(external_exports.string()).min(1) }),
33221
33279
  external_exports.object({ kind: external_exports.literal("confirm"), approved: external_exports.boolean() }),
33222
33280
  external_exports.object({ kind: external_exports.literal("turns"), turns: external_exports.array(TurnSchema2).min(1) }),
33223
- /** An auto-answer derived from the user's PAST decisions (broker/precedent-design.md §2):
33281
+ /** An auto-answer derived from the user's PAST decisions (docs/brain/broker/precedent-design.md §2):
33224
33282
  * delivered through the same settle/await path as a human answer, carrying the judge's
33225
33283
  * derivation and the precedent ids it grew from. Always paired with a visible trail
33226
33284
  * card the user can reply to — the broker never overrides the user. */
@@ -33269,7 +33327,7 @@ var AwaitItemSchema2 = external_exports.discriminatedUnion("type", [
33269
33327
  * and now knows exactly which call to make. Absent when either half is missing —
33270
33328
  * a sentence with a hole in it is worse than no sentence. */
33271
33329
  note: external_exports.string().optional(),
33272
- /** The call record rendered for THIS agent (`voice/record-design.md`): the words the
33330
+ /** The call record rendered for THIS agent (`docs/brain/voice/record-design.md`): the words the
33273
33331
  * shaped answer was mapped from, filtered to its own claims. There is no second list
33274
33332
  * of labels beside it — the acts went 2026-09-04 and `intents` went with them (owner,
33275
33333
  * 2026-09-04): the agent reads the sentence and decides. */
@@ -33365,10 +33423,11 @@ var AgendaTurnSchema2 = external_exports.object({
33365
33423
  agentId: external_exports.string().optional(),
33366
33424
  select: SelectShapeSchema2.optional(),
33367
33425
  options: external_exports.array(OptionSchema2.omit({ id: true })).optional(),
33368
- /** Pacing (#826, owner 2026-08-03: "how fast we move through them ... are parameters"):
33369
- * seconds the floor stays open after this turn speaks. Absent = the bot's defaults
33370
- * (the beat for context, the answer window for asks). Clamped bot-side. */
33371
- pace: external_exports.number().positive().optional(),
33426
+ /* `pace` STOOD HERE (#826). A turn could carry seconds and the model chose them. The walk
33427
+ paces itself now — a short beat between the sentences of a turn, the longer one at its end
33428
+ (owner, 2026-09-30: "remove the bot deciding pace") — and it does that where the words are
33429
+ spoken, not where the plan is written, so nothing between the brain and the walk decides
33430
+ anything. A caller's own `beat_s` tuning is what it used to override. */
33372
33431
  /** Whether the walk WAITS for an answer before moving on. Absent = derived as today
33373
33432
  * (a question blocks, context flows). blocking:false on a question = ask and move
33374
33433
  * on, the claim stays pending; blocking:true on context = hold for a reply. */
@@ -33403,7 +33462,7 @@ var InboxItemSchema2 = external_exports.object({
33403
33462
  * instant the agent picks it up (#997). Absent = pre-trigger row; fall back to
33404
33463
  * `createdAt`. */
33405
33464
  agentStateAt: external_exports.string().datetime().optional(),
33406
- /** THE QUESTIONS A CALL CARRIES — the call screen's agenda spine (walk/design.md §11, owner
33465
+ /** THE QUESTIONS A CALL CARRIES — the call screen's agenda spine (docs/clients/app/walk/design.md §11, owner
33407
33466
  * 2026-09-22). One per DecisionNeed on the Call, in the Call's order, answered or open (a
33408
33467
  * superseded or cancelled need is no longer a question anyone is asked). Present only on a
33409
33468
  * Call's cards, and every card of that Call carries the same list: the call screen reads it
@@ -33464,7 +33523,7 @@ var InboxItemSchema2 = external_exports.object({
33464
33523
  * off the same open list the card came from, so it clears when the Call does. A card is the
33465
33524
  * backup for a call not taken; while the call has it, the call is where it is answered. */
33466
33525
  onCall: external_exports.literal(true).optional(),
33467
- /** THE RING, ON THE ITEM (walk/design.md §12 §17, #2251): the last ring on this card was
33526
+ /** THE RING, ON THE ITEM (docs/clients/app/walk/design.md §12 §17, #2251): the last ring on this card was
33468
33527
  * declined, and what the ladder will do next — read off the cron's own row, never computed
33469
33528
  * on the phone. Present only while a `declined` receipt stands on the card's last Call.
33470
33529
  * The ladder is ACCOUNT-WIDE (#2259): `anchorAt` and `step` are the account's position;
@@ -33495,7 +33554,7 @@ var InboxItemSchema2 = external_exports.object({
33495
33554
  "Labels for a select:'confirm' paige \u2014 'yesno' (Yes/No) or 'approve' (Approve/Deny). Ignored unless select is 'confirm'."
33496
33555
  ),
33497
33556
  /** Real downstream work is stuck behind this one — set by the agent, independent of
33498
- * urgency (see the main README's "premier use case" + notify/states.md). Drives the
33557
+ * urgency (see the main README's "premier use case" + docs/delivery/notify/states.md). Drives the
33499
33558
  * inbox's blocking badge and the extra confirm step before dismissing it. */
33500
33559
  blocking: external_exports.boolean().default(false),
33501
33560
  /** The user's locked-in answer; present only for replied items (GET /api/replied/:id). */
@@ -33569,9 +33628,16 @@ var UserSettingsSchema2 = external_exports.object({
33569
33628
  banner: external_exports.boolean(),
33570
33629
  push: external_exports.boolean()
33571
33630
  }),
33631
+ /** LockedIn / Default / DateNight on screen; the stored words are unchanged on purpose —
33632
+ * they are an enum on a live column across every account, and the rename is a rename of
33633
+ * what people read (owner, 2026-09-30). */
33572
33634
  sessionMode: external_exports.enum(["default", "all_calls", "silent"]),
33573
- silentPush: external_exports.boolean(),
33574
- autoCallback: external_exports.boolean(),
33635
+ /** DEAD, AND ACCEPTED ANYWAY (#2813). DateNight is inbox-only with no way back, so nothing
33636
+ * reads this. It stays OPTIONAL rather than deleted because a phone on the old bundle
33637
+ * PATCHes the whole settings object and would be refused for sending a key we stopped
33638
+ * wanting — the same trap `voiceMode` below already documents. #2813 drops the field and
33639
+ * its column once no installed build still sends it. */
33640
+ silentPush: external_exports.boolean().optional(),
33575
33641
  /** Opt-in (default false) to using your content to improve Paigy and train models. */
33576
33642
  improveConsent: external_exports.boolean(),
33577
33643
  missedCall: MissedCallSchema2.default("backoff_standard"),
@@ -33582,12 +33648,16 @@ var UserSettingsSchema2 = external_exports.object({
33582
33648
  * must not silently reset this privacy choice. Absent = leave unchanged on
33583
33649
  * write, 'hosted' on read (see store.ts). */
33584
33650
  voiceMode: external_exports.enum(["hosted", "on_device"]).optional(),
33585
- /** Talk — after you answer, the next step is read aloud (walk/design.md §6). ALWAYS ON until
33651
+ /** Talk — after you answer, the next step is read aloud (docs/clients/app/walk/design.md §6). ALWAYS ON until
33586
33652
  * turned off (owner, 2026-09-18, #2249): a setting, not a per-walk toggle. Optional, NOT
33587
33653
  * defaulted, for the same reason `voiceMode` is: a stale client PATCHing the full settings
33588
33654
  * object must not silently turn it back on. Absent = leave unchanged on write, true on
33589
33655
  * read (see store.ts). */
33590
33656
  talk: external_exports.boolean().optional(),
33657
+ /** CALL DIAGNOSTICS (owner, 2026-10-01): the call report carries each listen and the bot's own
33658
+ * load timings. SERVER-SET, no UI — on for every account that existed on 2026-10-01, off for
33659
+ * newer ones (migration 20261001132859). Read-only here: the settings PATCH never writes it. */
33660
+ callDiagnostics: external_exports.boolean().optional(),
33591
33661
  /** Per-user ring budget (#603): calls per rolling day before further calls
33592
33662
  * degrade to banner. Absent = the global default (25). A number, never a
33593
33663
  * bypass — every account keeps a ceiling. No UI; set per user for testing. */
@@ -33596,9 +33666,6 @@ var UserSettingsSchema2 = external_exports.object({
33596
33666
  * slower speaker). No API-side semantics; the bot resolves each key with its
33597
33667
  * own defaults. Set per user (no UI yet); absent = bot defaults. */
33598
33668
  voiceTuning: external_exports.record(external_exports.string(), external_exports.union([external_exports.number(), external_exports.string()])).optional(),
33599
- /** Opt-in to real-phone (PSTN) calls when the app can't ring. Optional, not
33600
- * defaulted — an older client PATCHing the full object must not clobber it. */
33601
- pstnCalls: external_exports.boolean().optional(),
33602
33669
  /** The user's IANA timezone (e.g. "America/Bogota"), recorded by the app — it is the
33603
33670
  * only party that knows it. REMINDERS are why it exists: "remind me at ten" becomes
33604
33671
  * an absolute `due_at` only if we know whose ten. Optional and never defaulted, for
@@ -33642,7 +33709,7 @@ var ConnectionSummarySchema2 = external_exports.object({
33642
33709
  /** The credential kind: "device" = a paired machine (mint-only — it hosts and mints, it
33643
33710
  * never talks); "agent" = an identity that sends. The roster and devices surfaces split
33644
33711
  * on this. Optional/absent reads as "agent" (a row predating the kind column). See
33645
- * apps/api/src/tokens/devices-vs-agents-design.md. */
33712
+ * docs/server/tokens/devices-vs-agents-design.md. */
33646
33713
  kind: external_exports.enum(["device", "agent"]).optional(),
33647
33714
  /** For an agent, the token id of the DEVICE that minted it — so agents group under their
33648
33715
  * machine, and revoking a device cascades to them. Null on devices, and on unlinked
@@ -33660,7 +33727,7 @@ var ConnectionSummarySchema2 = external_exports.object({
33660
33727
  * user on the agent's own page. null = no ceiling (today's behaviour for every
33661
33728
  * connection). Android binds importance to a relationship rather than to each message,
33662
33729
  * and that is the thing our roster could not say: "Marlow may always call me; Otto
33663
- * never may" (navigation-design.md, gap 2). Clamped in `arbitrateLevel`, so it binds
33730
+ * never may" (docs/clients/app/party/navigation-design.md, gap 2). Clamped in `arbitrateLevel`, so it binds
33664
33731
  * every surface at once and outranks even `sessionMode: all_calls` — a mode the user
33665
33732
  * set once must not overrule a rule they set about one agent. */
33666
33733
  reach: NotifyLevelSchema2.nullable().optional(),
@@ -33671,7 +33738,14 @@ var ConnectionSummarySchema2 = external_exports.object({
33671
33738
  /** Last presence heartbeat from a running agent process (POST /api/presence) — the
33672
33739
  * desktop app while open. Null = never seen; stale = offline. */
33673
33740
  lastSeenAt: external_exports.string().datetime().nullable().optional(),
33674
- /** What a live desktop can run (companion.md §2.2), advertised on its heartbeat:
33741
+ /** WORKING, NOT JUST CONNECTED (owner, 2026-09-30): the last time the agent itself acted on one of
33742
+ * its Goals — took its lease or recorded an operation (`tokens.last_worked_at`). Within
33743
+ * `WORKING_MS` it is working; otherwise it is connected but idle. Null = not seen working yet. */
33744
+ lastWorkedAt: external_exports.string().datetime().nullable().optional(),
33745
+ /** The oldest of its Goals that is `ready` for it — work handed to it that nobody has started.
33746
+ * With no work of its own for `WORKING_MS`, an agent sitting on this is not taking its work. */
33747
+ oldestReadyAt: external_exports.string().datetime().nullable().optional(),
33748
+ /** What a live desktop can run (docs/clients/desktop/companion.md §2.2), advertised on its heartbeat:
33675
33749
  * harness availabilities + granted workspaces — the option set the phone's
33676
33750
  * "new session" sheet offers. Absent for ordinary MCP agents. */
33677
33751
  runtime: external_exports.object({
@@ -33679,7 +33753,12 @@ var ConnectionSummarySchema2 = external_exports.object({
33679
33753
  * shows its age here (`apps/desktop/src/update.ts`). */
33680
33754
  version: external_exports.string().optional(),
33681
33755
  harnesses: external_exports.array(external_exports.object({ name: external_exports.string(), label: external_exports.string(), status: external_exports.string() })).optional(),
33682
- workspaces: external_exports.array(external_exports.string()).optional()
33756
+ workspaces: external_exports.array(external_exports.string()).optional(),
33757
+ /** THE GIT REPOS IN THOSE FOLDERS (2026-10-01, Goal 26982211): each granted folder that is a
33758
+ * repo, and each repo directly inside one, with its `origin` remote. A session started for
33759
+ * work on `mauurda/paigy` opens in that repo rather than the folder above it, where the repo's
33760
+ * own AGENTS.md is never read (`workspaceForRepo`). Absent on hosts that predate it. */
33761
+ repos: external_exports.array(external_exports.object({ path: external_exports.string(), remote: external_exports.string() })).optional()
33683
33762
  }).optional(),
33684
33763
  /** The tail of this agent's working log, when a harness is driving it — the agent page's
33685
33764
  * live strip. Absent for anything the desktop harness isn't running (a hatched identity
@@ -33762,7 +33841,7 @@ var QueueQuestionSchema2 = external_exports.object({
33762
33841
  * for a settled question whose reply carried nothing readable. */
33763
33842
  answer: external_exports.string().nullable().default(null),
33764
33843
  /** The Goal this question belongs to — a step knows its Goal on its own, not only through
33765
- * an `InboxItem`'s `communication.goalIds[0]` (walk/design.md §12 item 3).
33844
+ * an `InboxItem`'s `communication.goalIds[0]` (docs/clients/app/walk/design.md §12 item 3).
33766
33845
  * READ BY `apps/client/src/walk/order.ts`, which stamps it onto every `WalkStep`: the walk's
33767
33846
  * order, its route, home's trees and the list of steps all take a step's Goal from here, so
33768
33847
  * this is the field they agree through rather than each re-deriving it from the row it
@@ -33854,7 +33933,19 @@ var QueueItemSchema2 = external_exports.object({
33854
33933
  said: external_exports.string(),
33855
33934
  at: external_exports.string(),
33856
33935
  entryId: external_exports.string()
33857
- }).nullable().optional()
33936
+ }).nullable().optional(),
33937
+ /** WHEN THIS PERSON LAST PUT A HAND ON IT THEMSELVES (owner, Paigy Goal 16d18f51, 2026-09-30):
33938
+ * the newest Entry on the Goal they wrote, of any kind — a line they added, a reply to a note, an
33939
+ * answer to a question, a voice note filed as work. Null when the only hands on it have been its
33940
+ * agent's; optional, so a hand-built queue (fixtures, the demo) and a door older than
33941
+ * 20260930222356 need not spell it — `work/list.ts`'s `yoursAt` keeps its other sources, so the
33942
+ * Work tab orders as it did before the migration rather than throwing on the missing key.
33943
+ *
33944
+ * It is a FACT, not a reconstruction: `latest` holds one Entry, so an agent's progress note a
33945
+ * minute after the person speaks erases their instant from it, and the durable traces the client
33946
+ * can see (`replies`, `questions[].answeredAt`) miss a spontaneous note entirely — a `request`
33947
+ * Entry with no `about_id` is in neither. */
33948
+ lastPersonAt: external_exports.string().nullable().optional()
33858
33949
  });
33859
33950
  var COLD_AFTER_MS2 = 3 * 24 * 60 * 60 * 1e3;
33860
33951
  var NoteSourceSchema2 = external_exports.enum(["app", "call"]);
@@ -33884,7 +33975,7 @@ var NoteSchema2 = external_exports.object({
33884
33975
  assignee: external_exports.string().nullable(),
33885
33976
  /** The request thread minted at assignment; null until assigned. */
33886
33977
  parentId: external_exports.string().nullable(),
33887
- /** REMINDERS (reminders-design.md): the NOT-BEFORE this becomes eligible to ride a
33978
+ /** REMINDERS (docs/model/notes/reminders-design.md): the NOT-BEFORE this becomes eligible to ride a
33888
33979
  * call — never a deadline. It only ever comes from the user's own words, so when it
33889
33980
  * passes Paigy rings ONCE (#1293, owner 2026-08-26: a time said out loud is consent to
33890
33981
  * a ring at that time); after that it rides like any other. Null = "the very next
@@ -34144,6 +34235,29 @@ var SnapshotSchema2 = external_exports.object({
34144
34235
  computers: external_exports.array(ComputerRowSchema2).nullable()
34145
34236
  }).nullable()
34146
34237
  });
34238
+ var CallRecapSchema2 = external_exports.object({
34239
+ call: external_exports.object({
34240
+ status: external_exports.string(),
34241
+ startedAt: external_exports.string(),
34242
+ durationMs: external_exports.number().nullable(),
34243
+ agents: external_exports.array(external_exports.object({ id: external_exports.string(), name: external_exports.string().nullable() }))
34244
+ }),
34245
+ topics: external_exports.array(external_exports.object({
34246
+ goalId: external_exports.string().uuid(),
34247
+ title: external_exports.string(),
34248
+ owner: external_exports.string(),
34249
+ state: external_exports.string(),
34250
+ questions: external_exports.array(external_exports.object({ id: external_exports.string().uuid(), state: external_exports.string(), title: external_exports.string() })),
34251
+ /** `words` is always what they SAID, verbatim — the record, never replaced. `headline` is
34252
+ * their answer on one line when the call's read wrote one (owner, 2026-10-01: "render them
34253
+ * summarized like a pre-made option is"), so the row scans like a chosen option and their
34254
+ * own words stay under it. Absent on every line stored before the read wrote them, and on
34255
+ * anything that is not an answer. */
34256
+ lines: external_exports.array(external_exports.object({ entryId: external_exports.string().uuid(), words: external_exports.string(), headline: external_exports.string().optional() }))
34257
+ })),
34258
+ unfiled: external_exports.array(external_exports.object({ lineId: external_exports.string().uuid(), words: external_exports.string(), atMs: external_exports.number() })),
34259
+ more: external_exports.object({ lines: external_exports.number(), entries: external_exports.number(), topics: external_exports.number() })
34260
+ });
34147
34261
 
34148
34262
  // src/paigy/activity.ts
34149
34263
  function shortenPaths(s) {
@@ -34878,7 +34992,7 @@ function ago(iso, now) {
34878
34992
  init_catalog();
34879
34993
 
34880
34994
  // src/workspaces.ts
34881
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
34995
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync3, statSync as statSync3, writeFileSync as writeFileSync3 } from "fs";
34882
34996
  import { dirname as dirname2, join as join5, resolve as resolve2 } from "path";
34883
34997
  import { homedir as homedir6 } from "os";
34884
34998
  function workspacesFile() {
@@ -34913,11 +35027,42 @@ function resolveWakeDir(pinned, deps) {
34913
35027
  }
34914
35028
  return listWorkspaces(deps)[0];
34915
35029
  }
35030
+ function originOf(dir) {
35031
+ try {
35032
+ if (!statSync3(join5(dir, ".git")).isDirectory()) return null;
35033
+ const config = readFileSync3(join5(dir, ".git", "config"), "utf8");
35034
+ const section = config.split(/^\[/m).find((s) => /^remote\s+"origin"\]/.test(s));
35035
+ return section?.match(/^\s*url\s*=\s*(.+)$/m)?.[1]?.trim() ?? null;
35036
+ } catch {
35037
+ return null;
35038
+ }
35039
+ }
35040
+ var MAX_CHILDREN = 300;
35041
+ var MAX_REPOS = 100;
35042
+ function listRepos(deps) {
35043
+ const repos = [];
35044
+ for (const root of listWorkspaces(deps)) {
35045
+ const own = originOf(root);
35046
+ if (own) repos.push({ path: root, remote: own });
35047
+ let children = [];
35048
+ try {
35049
+ children = readdirSync2(root, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith(".")).map((d) => d.name).sort().slice(0, MAX_CHILDREN);
35050
+ } catch {
35051
+ }
35052
+ for (const name of children) {
35053
+ if (repos.length >= MAX_REPOS) return repos;
35054
+ const path = join5(root, name);
35055
+ const remote = originOf(path);
35056
+ if (remote) repos.push({ path, remote });
35057
+ }
35058
+ }
35059
+ return repos.slice(0, MAX_REPOS);
35060
+ }
34916
35061
 
34917
35062
  // src/holder.ts
34918
35063
  init_dist();
34919
35064
  import { execFileSync as execFileSync3 } from "child_process";
34920
- import { readFileSync as readFileSync4, readdirSync as readdirSync2 } from "fs";
35065
+ import { readFileSync as readFileSync4, readdirSync as readdirSync3 } from "fs";
34921
35066
  import { homedir as homedir7 } from "os";
34922
35067
  import { join as join6 } from "path";
34923
35068
  function isAlive(pid) {
@@ -34950,7 +35095,7 @@ function holds(pid, procStart, deps) {
34950
35095
  }
34951
35096
  function localHolders(deps = {}) {
34952
35097
  const dir = deps.dir ?? join6(process.env.CLAUDE_CONFIG_DIR?.trim() || join6(homedir7(), ".claude"), "sessions");
34953
- const list = deps.list ?? ((d) => readdirSync2(d));
35098
+ const list = deps.list ?? ((d) => readdirSync3(d));
34954
35099
  const read = deps.read ?? ((f) => readFileSync4(f, "utf8"));
34955
35100
  const check = { alive: deps.alive ?? isAlive, started: deps.started ?? psStart };
34956
35101
  const held = /* @__PURE__ */ new Map();
@@ -34978,7 +35123,7 @@ function localHolders(deps = {}) {
34978
35123
  }
34979
35124
 
34980
35125
  // src/owner.ts
34981
- import { closeSync as closeSync2, openSync as openSync2, readSync, readdirSync as readdirSync3 } from "fs";
35126
+ import { closeSync as closeSync2, openSync as openSync2, readSync, readdirSync as readdirSync4 } from "fs";
34982
35127
  import { homedir as homedir8 } from "os";
34983
35128
  import { join as join7 } from "path";
34984
35129
  var HEAD_BYTES = 64e3;
@@ -34997,7 +35142,7 @@ function ownerSession(slot, deps = {}) {
34997
35142
  if (!m) return null;
34998
35143
  const prefix = m[1];
34999
35144
  const root = deps.projectsDir ?? join7(homedir8(), ".claude", "projects");
35000
- const list = deps.list ?? ((dir) => readdirSync3(dir));
35145
+ const list = deps.list ?? ((dir) => readdirSync4(dir));
35001
35146
  const head = deps.head ?? readHead;
35002
35147
  let folders;
35003
35148
  try {
@@ -35232,7 +35377,9 @@ function startHost(opts) {
35232
35377
  void heartbeat({
35233
35378
  version: VERSION,
35234
35379
  harnesses: detectAll().map((a) => ({ name: a.name, label: a.label, status: a.status })),
35235
- workspaces: listWorkspaces(opts.wsDeps)
35380
+ workspaces: listWorkspaces(opts.wsDeps),
35381
+ // The repos inside them, so a session started for work on one opens IN it (Goal 26982211).
35382
+ repos: listRepos(opts.wsDeps)
35236
35383
  }, asHost).catch(() => {
35237
35384
  });
35238
35385
  void listenSlots().then(sweepSlots).catch(() => {