@hivefloor/cli 0.1.0 → 0.1.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.
Files changed (31) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +1 -1
  3. package/dist/{chunk-4A5WOW2D.js → chunk-DQV6FAJU.js} +2 -2
  4. package/dist/{chunk-BAB4DPCN.js → chunk-DWHMBUCA.js} +2 -2
  5. package/dist/{chunk-M3PLXINO.js → chunk-E4V47VEV.js} +3 -9
  6. package/dist/{chunk-UPRHMVYJ.js → chunk-HT5KXWXM.js} +1 -1
  7. package/dist/{chunk-IEYWMDXA.js → chunk-LF2NWQ4B.js} +8 -3
  8. package/dist/{chunk-DIW4VIU7.js → chunk-NKMVO5BP.js} +4 -10
  9. package/dist/{chunk-LHF4KCEA.js → chunk-NLZXB26Y.js} +2 -2
  10. package/dist/{chunk-SGDM7MAT.js → chunk-O2TC3WSS.js} +68 -2
  11. package/dist/{chunk-6UZBZ7LR.js → chunk-O6VSA2JT.js} +4 -3
  12. package/dist/{chunk-4OFN5KHY.js → chunk-OOMBZN4M.js} +13 -13
  13. package/dist/{chunk-3IMV5EHP.js → chunk-T7TG6WA4.js} +1 -1
  14. package/dist/{chunk-RE2EQZWL.js → chunk-TH4ANHSG.js} +394 -198
  15. package/dist/{chunk-WNDUGQDX.js → chunk-VNCQFFQO.js} +8 -0
  16. package/dist/{chunk-OQ6QVFSH.js → chunk-YRNMECUF.js} +1 -1
  17. package/dist/{cli-7CIYC6A3.js → cli-F3F7SQLV.js} +13 -10
  18. package/dist/connect-TG2H5SMB.js +23 -0
  19. package/dist/{disconnect-NRYU6W5P.js → disconnect-FI76IEZJ.js} +10 -10
  20. package/dist/{hook-B2I42GE3.js → hook-N4ZTQEWM.js} +53 -19
  21. package/dist/index.js +2 -2
  22. package/dist/{link-EPWADGYT.js → link-JX4YLF2Z.js} +9 -9
  23. package/dist/{machine-connect-RWNZ77NO.js → machine-connect-NOQ3VZOO.js} +24 -24
  24. package/dist/{mcp-QX6VMTOD.js → mcp-EUBTVJNX.js} +7 -7
  25. package/dist/run-CKNHDPNX.js +1883 -0
  26. package/dist/{status-HADESN2E.js → status-SMAVPW4D.js} +14 -14
  27. package/dist/{token-SM5MDJKY.js → token-AUEAWQDH.js} +4 -4
  28. package/dist/where-7NI7UEEF.js +70 -0
  29. package/package.json +7 -3
  30. package/dist/connect-R3PMIY7G.js +0 -23
  31. package/dist/run-64ZYCJU2.js +0 -629
@@ -19786,14 +19786,21 @@ var hookEventSchema = external_exports.object({
19786
19786
  model: external_exports.string().min(1).max(200).optional(),
19787
19787
  repo: repoInfoSchema.optional(),
19788
19788
  /** ISO time on the client; display only (the server clock decides). */
19789
- client_at: external_exports.iso.datetime({ offset: true })
19789
+ client_at: external_exports.iso.datetime({ offset: true }),
19790
+ /** The CLI sets it on the event it posts for a hook whose output the tool reads as context (Claude's UserPromptSubmit and the like): only then does the answer carry the session's pending nudges. */
19791
+ deliver: external_exports.boolean().optional()
19790
19792
  });
19791
19793
  var hookBatchSchema = external_exports.object({ events: external_exports.array(hookEventSchema).min(1).max(HOOK_BATCH_MAX) });
19792
19794
  var hookBatchResponseSchema = external_exports.object({
19793
19795
  accepted: external_exports.number().int().nonnegative(),
19794
- dropped: external_exports.number().int().nonnegative()
19796
+ dropped: external_exports.number().int().nonnegative(),
19797
+ /** F2: nudges a person sent to the sessions of a `deliver` event, by the tool's own session id. The runner prints the text as the hook's `additionalContext`. */
19798
+ nudges: external_exports.array(external_exports.object({ session_id: external_exports.string(), text: external_exports.string() })).optional()
19795
19799
  });
19796
19800
 
19801
+ // ../shared/src/machine-health.ts
19802
+ var MACHINE_HEALTH_RULES = ["offline_with_claims", "disk_full", "disk_high", "memory_high", "cli_behind"];
19803
+
19797
19804
  // ../shared/src/api/projects.ts
19798
19805
  var idSchema = external_exports.string().regex(/^[0-9]{1,15}$/, "must be a numeric id");
19799
19806
  var projectNameSchema = external_exports.string().trim().min(1).max(100);
@@ -19818,7 +19825,7 @@ var logoutBody = external_exports.object({ everywhere: external_exports.boolean(
19818
19825
  var isoSchema = external_exports.string();
19819
19826
  var roleEnum = external_exports.enum(["owner", "admin", "member", "viewer"]);
19820
19827
  var okResponse = external_exports.object({ ok: external_exports.literal(true) }).strict();
19821
- var listOf = (item) => external_exports.object({ items: external_exports.array(item), next_cursor: external_exports.string().nullable() }).strict();
19828
+ var listOf = (item) => external_exports.object({ items: external_exports.array(item), next_cursor: external_exports.string().nullable(), total: external_exports.number().int().nonnegative().optional() }).strict();
19822
19829
  var publicConfigResponse = external_exports.object({ dev_login: external_exports.boolean(), github: external_exports.boolean(), github_app: external_exports.boolean(), push: external_exports.object({ enabled: external_exports.boolean(), public_key: external_exports.string().nullable() }).optional() }).strict();
19823
19830
  var publicUserSchema = external_exports.object({ id: idSchema, login: external_exports.string(), name: external_exports.string().nullable(), avatar_url: external_exports.string().nullable() }).strict();
19824
19831
  var projectBriefSchema = external_exports.object({ id: idSchema, key: external_exports.string(), name: external_exports.string(), role: roleEnum }).strict();
@@ -19964,6 +19971,10 @@ var machineAgentSchema = external_exports.object({
19964
19971
  live_sessions: external_exports.number().int().nonnegative(),
19965
19972
  last_seen_at: external_exports.string().nullable()
19966
19973
  });
19974
+ var machineHealthViewSchema = external_exports.object({
19975
+ level: external_exports.enum(["warn", "critical"]),
19976
+ findings: external_exports.array(external_exports.object({ rule: external_exports.enum(MACHINE_HEALTH_RULES), level: external_exports.enum(["warn", "critical"]), text: external_exports.string() }))
19977
+ });
19967
19978
  var machineSchema = external_exports.object({
19968
19979
  id: external_exports.string(),
19969
19980
  name: external_exports.string(),
@@ -19975,7 +19986,9 @@ var machineSchema = external_exports.object({
19975
19986
  default_project_id: external_exports.string().nullable(),
19976
19987
  projects: external_exports.array(external_exports.object({ id: external_exports.string(), key: external_exports.string(), name: external_exports.string(), default: external_exports.boolean() })),
19977
19988
  stats: machineStatsSchema.nullable(),
19978
- agents: external_exports.array(machineAgentSchema)
19989
+ agents: external_exports.array(machineAgentSchema),
19990
+ /** The rules that fire for this machine; null or absent when healthy (older servers omit it). */
19991
+ health: machineHealthViewSchema.nullable().optional()
19979
19992
  });
19980
19993
  var patchMachineBody = external_exports.object({
19981
19994
  name: external_exports.string().trim().min(1).max(100).optional(),
@@ -19995,121 +20008,44 @@ function cliNpxCommand(args = "", opts = {}) {
19995
20008
  }
19996
20009
  var CLI_CONNECT_COMMAND = cliNpxCommand("connect");
19997
20010
 
19998
- // ../shared/src/task-status.ts
19999
- var TASK_STATUSES = ["backlog", "ready", "claimed", "in_review", "done", "blocked", "parked"];
20000
- var taskStatusSchema = external_exports.enum(TASK_STATUSES);
20001
-
20002
- // ../shared/src/remote.ts
20003
- var SCHEME = /^([a-z][a-z0-9+.-]*):\/\//i;
20004
- function normalizeRemote(remote) {
20005
- let s = remote.trim();
20006
- if (!s || s.length > 2048 || /[\s\0]/.test(s)) return null;
20007
- let host2;
20008
- let path;
20009
- if (SCHEME.test(s)) {
20010
- s = s.replace(SCHEME, "");
20011
- const slash = s.indexOf("/");
20012
- if (slash < 0) return null;
20013
- host2 = s.slice(0, slash);
20014
- path = s.slice(slash + 1);
20015
- } else {
20016
- const colon = s.indexOf(":");
20017
- const slash = s.indexOf("/");
20018
- if (colon >= 0 && (slash < 0 || colon < slash)) {
20019
- host2 = s.slice(0, colon);
20020
- path = s.slice(colon + 1);
20021
- } else if (slash >= 0) {
20022
- host2 = s.slice(0, slash);
20023
- path = s.slice(slash + 1);
20024
- } else {
20025
- return null;
20026
- }
20027
- }
20028
- const at = host2.lastIndexOf("@");
20029
- if (at >= 0) host2 = host2.slice(at + 1);
20030
- host2 = host2.replace(/:\d*$/, "").toLowerCase();
20031
- if (!/^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$/.test(host2)) return null;
20032
- path = path.replace(/[?#].*$/, "");
20033
- const parts = path.split("/").filter((p) => p.length > 0);
20034
- if (parts.length < 2) return null;
20035
- let name = parts[parts.length - 1];
20036
- if (name.toLowerCase().endsWith(".git")) name = name.slice(0, -4);
20037
- const ownerParts = parts.slice(0, -1);
20038
- if (!name || [...ownerParts, name].some((p) => p === "." || p === ".." || !/^[A-Za-z0-9_.@+-]+$/.test(p))) return null;
20039
- return { host: host2, owner: ownerParts.join("/"), name };
20040
- }
20041
- function formatRemote(r) {
20042
- return `${r.host}/${r.owner}/${r.name}`;
20043
- }
20044
-
20045
- // ../shared/src/redact.ts
20046
- var tag = (kind) => `[redacted:${kind}]`;
20047
- function isPlaceholder(value) {
20048
- const v = value.replace(/^["']|["']$/g, "");
20049
- return v === "" || /^(\$|<|\{|%|\*+$|x+$|\.\.\.|\[redacted)/i.test(v) || /^(process\.env|os\.environ|env\.|ENV\[|config\.|settings\.)/i.test(v) || /^(null|none|undefined|true|false|changeme|example|your[_-])/i.test(v);
20050
- }
20051
- var KEY_PREFIX = String.raw`(?<![A-Za-z0-9])`;
20052
- var VALUE = String.raw`("[^"\r\n]*"|'[^'\r\n]*'|[^\s,;"'&)}\]]+)`;
20053
- var RULES = [
20054
- { kind: "pem", re: /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?(?:-----END [A-Z0-9 ]*PRIVATE KEY-----|$)/g },
20055
- { kind: "github", re: /\bgithub_pat_[A-Za-z0-9_]{20,}/g },
20056
- { kind: "github", re: /\bgh[pousr]_[A-Za-z0-9]{20,}/g },
20057
- { kind: "anthropic", re: /\bsk-ant-[A-Za-z0-9_-]{10,}/g },
20058
- // sk- keys are random base62; the digit lookahead keeps hyphenated words ("sk-learn-compatible-...") out.
20059
- { kind: "openai", re: /\bsk-(?=[A-Za-z0-9_-]*\d)[A-Za-z0-9_-]{20,}/g },
20060
- { kind: "aws", re: /\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/g },
20061
- { kind: "slack", re: /\bxox[abprs]-[A-Za-z0-9-]{10,}/g },
20062
- // hf_ tokens are 32 random bytes in base64url: long, with a digit or capital. Snake_case identifiers have neither.
20063
- { kind: "hivefloor", re: /\bhf_(?=[A-Za-z0-9_-]*[A-Z0-9])[A-Za-z0-9_-]{32,}/g },
20064
- // A fleet push token (hff_ + 32 random bytes) or machine token (hfm_ + 32 random bytes): must never end up in a status line or a title.
20065
- { kind: "hivefloor", re: /\bhf[fm]_[A-Za-z0-9_-]{20,}/g },
20066
- { kind: "webhook", re: /\bwhsec_[A-Za-z0-9_-]{16,}/g },
20067
- { kind: "vqb", re: /\bvqb_[A-Za-z0-9_-]{16,}/g },
20068
- { kind: "jwt", re: /\beyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]*/g },
20069
- {
20070
- kind: "bearer",
20071
- re: /\b(Bearer)\s+(?=[A-Za-z0-9._~+/=-]*\d)[A-Za-z0-9._~+/=-]{20,}/g,
20072
- replace: (_m, word) => `${word} ${tag("bearer")}`
20073
- },
20074
- // password=hunter2, "passwd": "x", DB_PASSWORD='x'. Any non-placeholder value counts.
20075
- {
20076
- kind: "password",
20077
- re: new RegExp(String.raw`${KEY_PREFIX}(password|passwd|pwd)(["']?\s*[:=]\s*)${VALUE}`, "gi"),
20078
- replace: (m, _key, sep = "", value = "") => isAssignedSecret(sep, value, 1) ? `${m.slice(0, m.length - value.length)}${tag("password")}` : null
20079
- },
20080
- // token=..., client_secret: ..., api_key=...: the value has to be long enough to be a credential.
20081
- {
20082
- kind: "secret",
20083
- re: new RegExp(String.raw`${KEY_PREFIX}(secret|token|api[_-]?key|apikey)(["']?\s*[:=]\s*)${VALUE}`, "gi"),
20084
- replace: (m, _key, sep = "", value = "") => isAssignedSecret(sep, value, 8) ? `${m.slice(0, m.length - value.length)}${tag("secret")}` : null
20085
- }
20086
- ];
20087
- function isAssignedSecret(sep, value, minLen) {
20088
- if (isPlaceholder(value)) return false;
20089
- const quoted = /^["']/.test(value);
20090
- const v = value.replace(/^["']|["']$/g, "");
20091
- if (v.length < minLen) return false;
20092
- if (sep.includes("=") || quoted) return true;
20093
- return v.length >= 6 && /[^A-Za-z]/.test(v);
20011
+ // ../shared/src/usage.ts
20012
+ var MIN_SPAN_MS = 5 * 6e4;
20013
+ var MAX_ETA_MS = 31 * 24 * 36e5;
20014
+ var USAGE_COUNTERS = ["tokens_in", "tokens_out", "cost_usd"];
20015
+ function isUsageCounter(value) {
20016
+ return USAGE_COUNTERS.includes(value);
20094
20017
  }
20095
- function redact(input2) {
20096
- let text2 = input2;
20097
- let count = 0;
20098
- for (const rule of RULES) {
20099
- text2 = text2.replace(rule.re, (...args) => {
20100
- const match = args[0];
20101
- const groups = args.slice(1, -2).filter((g) => typeof g === "string");
20102
- const out = rule.replace ? rule.replace(match, ...groups) : tag(rule.kind);
20103
- if (out === null) return match;
20104
- count += 1;
20105
- return out;
20106
- });
20018
+ function parseUsageKey(key2) {
20019
+ const parts = key2.split(":");
20020
+ if (parts.length < 3) return null;
20021
+ const tool = parts[0];
20022
+ const profile = parts[1];
20023
+ if (!TOOLS.includes(tool)) return null;
20024
+ if (!PROFILE_KEY_RE.test(profile)) return null;
20025
+ if (parts.length === 3) {
20026
+ const counter2 = parts[2];
20027
+ return isUsageCounter(counter2) ? { tool, profile, session: null, counter: counter2 } : null;
20107
20028
  }
20108
- return { text: text2, count };
20029
+ if (parts[2] !== "s" || parts.length < 5) return null;
20030
+ const session = parts.slice(3, -1).join(":");
20031
+ const counter = parts[parts.length - 1];
20032
+ if (!session || !isUsageCounter(counter)) return null;
20033
+ return { tool, profile, session, counter };
20109
20034
  }
20110
20035
 
20036
+ // ../shared/src/usage-alerts.ts
20037
+ var WASTE_WITHIN_MS = 3 * 36e5;
20038
+
20039
+ // ../shared/src/task-status.ts
20040
+ var TASK_STATUSES = ["backlog", "ready", "claimed", "in_review", "done", "blocked", "parked"];
20041
+ var taskStatusSchema = external_exports.enum(TASK_STATUSES);
20042
+
20111
20043
  // ../shared/src/health.ts
20112
- var SESSION_HEALTH = ["ok", "stalled", "lost", "looping"];
20044
+ var PARKED_AFTER_SEC = 20 * 60;
20045
+ var WAITING_LONG_AFTER_SEC = 15 * 60;
20046
+ var MIN_STALL_THRESHOLD_SEC = 60;
20047
+ var MAX_STALL_THRESHOLD_SEC = 7 * 24 * 3600;
20048
+ var SESSION_HEALTH = ["ok", "stalled", "lost", "looping", "parked", "waiting_long"];
20113
20049
 
20114
20050
  // ../shared/src/api/rules.ts
20115
20051
  var entityId = external_exports.string().regex(/^\d{1,20}$/, "an id is a decimal string");
@@ -20134,6 +20070,10 @@ var rulesConfigSchema = external_exports.object({
20134
20070
  wip_per_agent: cap,
20135
20071
  wip_per_machine: cap,
20136
20072
  stall_release_seconds: external_exports.number().int().min(MIN_STALL_RELEASE_SECONDS).max(MAX_STALL_RELEASE_SECONDS).nullable(),
20073
+ /** Seconds an idle session holds a claim before it shows as parked (default 20 min); null or omitted = default. An omitted field keeps what is stored. */
20074
+ parked_after_seconds: external_exports.number().int().min(MIN_STALL_THRESHOLD_SEC).max(MAX_STALL_THRESHOLD_SEC).nullable().optional(),
20075
+ /** Seconds a wait on a person lasts before it shows as waiting_long and the owner is pushed again (default 15 min); the admins hear at 4 times this. */
20076
+ waiting_long_after_seconds: external_exports.number().int().min(MIN_STALL_THRESHOLD_SEC).max(MAX_STALL_THRESHOLD_SEC).nullable().optional(),
20137
20077
  rules: external_exports.array(ruleSchema).max(MAX_RULES).refine((rs) => new Set(rs.map((r) => r.id)).size === rs.length, "rule ids must be unique")
20138
20078
  }).strict();
20139
20079
  var steerBody = external_exports.object({
@@ -20204,9 +20144,11 @@ var taskCardSchema = external_exports.object({
20204
20144
  done_at: isoTimeSchema.nullable(),
20205
20145
  archived_at: isoTimeSchema.nullable(),
20206
20146
  /** Tasks this one waits on (stage 2). It is claimable only when every one of them is done. */
20207
- blocked_by: external_exports.array(blockerRefSchema).default([])
20147
+ blocked_by: external_exports.array(blockerRefSchema).default([]),
20148
+ /** A claimed task whose holder's machine is offline: the work may be stuck there. Absent or false otherwise. */
20149
+ at_risk: external_exports.boolean().optional()
20208
20150
  });
20209
- var TASK_TIMELINE_KINDS = ["created", "claimed", "note", "blocked", "released", "lease_expired", "pr", "in_review", "done", "answered", "archived", "unarchived", "unblocked", "steered"];
20151
+ var TASK_TIMELINE_KINDS = ["created", "claimed", "note", "blocked", "released", "lease_expired", "pr", "in_review", "done", "answered", "archived", "unarchived", "unblocked", "steered", "nudged"];
20210
20152
  var taskTimelineKindSchema = external_exports.enum(TASK_TIMELINE_KINDS);
20211
20153
  var taskTimelineEventSchema = external_exports.object({
20212
20154
  id: entityIdSchema,
@@ -20222,9 +20164,12 @@ var taskTimelineEventSchema = external_exports.object({
20222
20164
  });
20223
20165
  var taskListSchema = external_exports.object({
20224
20166
  items: external_exports.array(taskCardSchema),
20225
- next_cursor: external_exports.string().nullable()
20167
+ next_cursor: external_exports.string().nullable(),
20168
+ /** Matching tasks across all pages. Only present when the request used `offset` (page-number lists). */
20169
+ total: external_exports.number().int().nonnegative().optional()
20226
20170
  });
20227
20171
  var queryId = external_exports.string().regex(/^[1-9][0-9]{0,14}$/);
20172
+ var queryOffset = external_exports.string().regex(/^(0|[1-9][0-9]{0,5})$/).transform(Number);
20228
20173
  var queryLimit = external_exports.string().regex(/^[1-9][0-9]{0,2}$/).transform(Number).pipe(external_exports.number().int().max(200));
20229
20174
  var replayCursor = external_exports.string().max(100).regex(/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d{6}Z_[1-9][0-9]{0,14}$/).refine((value) => isoTimeSchema.safeParse(value.split("_")[0]).success, "invalid cursor time");
20230
20175
  var queryStatus = external_exports.string().min(1).max(80).transform((value) => value.split(",").map((status) => status.trim())).pipe(external_exports.array(taskStatusSchema).min(1).max(7)).refine((values) => new Set(values).size === values.length, "statuses must be distinct");
@@ -20240,8 +20185,10 @@ var taskListQuerySchema = external_exports.object({
20240
20185
  parent: queryId.optional(),
20241
20186
  archived: external_exports.enum(["0", "1", "all", "false", "true"]).optional(),
20242
20187
  limit: queryLimit.optional(),
20243
- cursor: external_exports.union([queryId, replayCursor]).optional()
20244
- }).strict().refine((value) => value.agent === void 0 || value.holder_agent === void 0, "use agent or holder_agent");
20188
+ cursor: external_exports.union([queryId, replayCursor]).optional(),
20189
+ /** Page-number paging (the Done page): skip this many tasks and also return `total`. Not combinable with `cursor`. */
20190
+ offset: queryOffset.optional()
20191
+ }).strict().refine((value) => value.offset === void 0 || value.cursor === void 0, "use offset or cursor").refine((value) => value.agent === void 0 || value.holder_agent === void 0, "use agent or holder_agent");
20245
20192
  var agentTaskListQuerySchema = taskListQuerySchema.safeExtend({ project: projectRefSchema.optional() });
20246
20193
  var taskStatusLineSchema = external_exports.object({
20247
20194
  id: entityIdSchema,
@@ -20259,7 +20206,7 @@ var taskStatusLineQuerySchema = external_exports.object({
20259
20206
  }).strict();
20260
20207
  var taskResultSchema = external_exports.object({ task: taskCardSchema });
20261
20208
  var answerBodySchema = external_exports.object({ text: external_exports.string().trim().min(1).max(500) });
20262
- var NOTIFICATION_KINDS = ["task_done", "task_blocked", "agent_waiting", "invite_received", "member_added", "repo_link_requested", "agent_question"];
20209
+ var NOTIFICATION_KINDS = ["task_done", "task_blocked", "agent_waiting", "invite_received", "member_added", "repo_link_requested", "agent_question", "feedback", "usage_waste", "usage_high", "wait_escalated", "machine_unhealthy"];
20263
20210
  var notificationKindSchema = external_exports.enum(NOTIFICATION_KINDS);
20264
20211
  var notificationViewSchema = external_exports.object({
20265
20212
  id: entityIdSchema,
@@ -20352,50 +20299,6 @@ var releaseBody = external_exports.object({
20352
20299
  }).strict();
20353
20300
  var completeBody = external_exports.object({ claim_token: claimToken.optional(), summary: external_exports.string().min(1).max(5e3), pr_url: external_exports.string().max(1e3).optional(), session: sessionRef.optional() }).strict();
20354
20301
 
20355
- // ../shared/src/api/memory.ts
20356
- var CONTEXT_DECISIONS = 10;
20357
- var MEMORY_KINDS = ["handoff", "decision"];
20358
- var memoryKindSchema = external_exports.enum(MEMORY_KINDS);
20359
- var memoryEntrySchema = external_exports.object({
20360
- id: entityIdSchema,
20361
- kind: memoryKindSchema,
20362
- /** The task the entry is about; always set for a handoff. */
20363
- task_id: entityIdSchema.nullable(),
20364
- /** The task as people write it (`HF-12`). */
20365
- task_key: external_exports.string().nullable(),
20366
- text: external_exports.string(),
20367
- author: external_exports.object({ type: external_exports.enum(["agent", "user", "system"]), name: external_exports.string() }),
20368
- created_at: isoTimeSchema
20369
- });
20370
- var memoryContextSchema = external_exports.object({
20371
- handoff: memoryEntrySchema.nullable(),
20372
- decisions: external_exports.array(memoryEntrySchema).max(CONTEXT_DECISIONS)
20373
- });
20374
- var memoryListSchema = external_exports.object({
20375
- items: external_exports.array(memoryEntrySchema),
20376
- next_cursor: external_exports.string().nullable()
20377
- });
20378
- var taskIdSchema2 = external_exports.union([external_exports.string().regex(/^[0-9]{1,15}$/), external_exports.number().int().positive()]).transform((v) => String(v));
20379
- var sessionRef2 = external_exports.string().trim().min(1).max(200);
20380
- var addDecisionBody = external_exports.object({
20381
- project: projectRefSchema.optional(),
20382
- /** The task the decision is about (optional). */
20383
- task_id: taskIdSchema2.optional(),
20384
- text: external_exports.string().min(1).max(2e3),
20385
- session: sessionRef2.optional()
20386
- }).strict();
20387
- var getContextBody = external_exports.object({
20388
- project: projectRefSchema.optional(),
20389
- /** The task whose last handoff to include. */
20390
- task_id: taskIdSchema2.optional()
20391
- }).strict();
20392
- var humanDecisionBody = external_exports.object({
20393
- text: external_exports.string().trim().min(1).max(2e3),
20394
- /** The task number (not the id) the decision is about. */
20395
- task_num: external_exports.number().int().positive().max(2e9).optional()
20396
- }).strict();
20397
- var decisionResultSchema = external_exports.object({ entry: memoryEntrySchema });
20398
-
20399
20302
  // ../shared/src/api/usage.ts
20400
20303
  var isoTime = external_exports.iso.datetime({ offset: true });
20401
20304
  var USAGE_PROVIDERS = [
@@ -20517,6 +20420,210 @@ var taskUsageSchema = external_exports.object({
20517
20420
  agents: external_exports.array(taskUsageAgentSchema).max(50)
20518
20421
  }).strict();
20519
20422
 
20423
+ // ../shared/src/usage-route.ts
20424
+ var ROUTE_HORIZON_MS = 2 * 36e5;
20425
+ function usageProviderForTool(tool) {
20426
+ switch (tool) {
20427
+ case "claude":
20428
+ case "codex":
20429
+ case "cursor":
20430
+ case "gemini":
20431
+ case "grok":
20432
+ return tool;
20433
+ default:
20434
+ return null;
20435
+ }
20436
+ }
20437
+ function routeProviderFromParam(param) {
20438
+ const v = param.trim().toLowerCase();
20439
+ const fromTool = usageProviderForTool(v);
20440
+ if (fromTool) return fromTool;
20441
+ const p = usageProviderSchema.safeParse(v);
20442
+ return p.success ? p.data : null;
20443
+ }
20444
+ var isoTime2 = external_exports.iso.datetime({ offset: true });
20445
+ var routeAccountSchema = external_exports.object({
20446
+ id: external_exports.string().max(120),
20447
+ label: external_exports.string().max(60),
20448
+ plan: external_exports.string().max(40).optional(),
20449
+ usable: external_exports.boolean(),
20450
+ score: external_exports.number().nullable(),
20451
+ window: external_exports.string().max(40).optional(),
20452
+ percent_left: external_exports.number().min(0).max(100).optional(),
20453
+ resets_at: isoTime2.optional(),
20454
+ live_sessions: external_exports.number().int().nonnegative(),
20455
+ waste: external_exports.boolean(),
20456
+ reason: external_exports.string().max(200)
20457
+ }).strict();
20458
+ var routeProviderSchema = external_exports.object({
20459
+ provider: usageProviderSchema,
20460
+ name: external_exports.string().max(40),
20461
+ /** The account to use next: the first of `accounts` when it is usable, else null. */
20462
+ best: external_exports.string().max(120).nullable(),
20463
+ accounts: external_exports.array(routeAccountSchema).max(100)
20464
+ }).strict();
20465
+ var usageRouteSchema = external_exports.object({
20466
+ generated_at: isoTime2,
20467
+ /** The provider asked for, or null for all of them. */
20468
+ provider: usageProviderSchema.nullable(),
20469
+ providers: external_exports.array(routeProviderSchema).max(USAGE_PROVIDERS.length),
20470
+ /** Live sessions that could not be counted against an account (no label, unknown label, a tool with no usage). */
20471
+ unlinked_sessions: external_exports.number().int().nonnegative()
20472
+ }).strict();
20473
+
20474
+ // ../shared/src/remote.ts
20475
+ var SCHEME = /^([a-z][a-z0-9+.-]*):\/\//i;
20476
+ function normalizeRemote(remote) {
20477
+ let s = remote.trim();
20478
+ if (!s || s.length > 2048 || /[\s\0]/.test(s)) return null;
20479
+ let host2;
20480
+ let path;
20481
+ if (SCHEME.test(s)) {
20482
+ s = s.replace(SCHEME, "");
20483
+ const slash = s.indexOf("/");
20484
+ if (slash < 0) return null;
20485
+ host2 = s.slice(0, slash);
20486
+ path = s.slice(slash + 1);
20487
+ } else {
20488
+ const colon = s.indexOf(":");
20489
+ const slash = s.indexOf("/");
20490
+ if (colon >= 0 && (slash < 0 || colon < slash)) {
20491
+ host2 = s.slice(0, colon);
20492
+ path = s.slice(colon + 1);
20493
+ } else if (slash >= 0) {
20494
+ host2 = s.slice(0, slash);
20495
+ path = s.slice(slash + 1);
20496
+ } else {
20497
+ return null;
20498
+ }
20499
+ }
20500
+ const at = host2.lastIndexOf("@");
20501
+ if (at >= 0) host2 = host2.slice(at + 1);
20502
+ host2 = host2.replace(/:\d*$/, "").toLowerCase();
20503
+ if (!/^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$/.test(host2)) return null;
20504
+ path = path.replace(/[?#].*$/, "");
20505
+ const parts = path.split("/").filter((p) => p.length > 0);
20506
+ if (parts.length < 2) return null;
20507
+ let name = parts[parts.length - 1];
20508
+ if (name.toLowerCase().endsWith(".git")) name = name.slice(0, -4);
20509
+ const ownerParts = parts.slice(0, -1);
20510
+ if (!name || [...ownerParts, name].some((p) => p === "." || p === ".." || !/^[A-Za-z0-9_.@+-]+$/.test(p))) return null;
20511
+ return { host: host2, owner: ownerParts.join("/"), name };
20512
+ }
20513
+ function formatRemote(r) {
20514
+ return `${r.host}/${r.owner}/${r.name}`;
20515
+ }
20516
+
20517
+ // ../shared/src/redact.ts
20518
+ var tag = (kind) => `[redacted:${kind}]`;
20519
+ function isPlaceholder(value) {
20520
+ const v = value.replace(/^["']|["']$/g, "");
20521
+ return v === "" || /^(\$|<|\{|%|\*+$|x+$|\.\.\.|\[redacted)/i.test(v) || /^(process\.env|os\.environ|env\.|ENV\[|config\.|settings\.)/i.test(v) || /^(null|none|undefined|true|false|changeme|example|your[_-])/i.test(v);
20522
+ }
20523
+ var KEY_PREFIX = String.raw`(?<![A-Za-z0-9])`;
20524
+ var VALUE = String.raw`("[^"\r\n]*"|'[^'\r\n]*'|[^\s,;"'&)}\]]+)`;
20525
+ var RULES = [
20526
+ { kind: "pem", re: /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?(?:-----END [A-Z0-9 ]*PRIVATE KEY-----|$)/g },
20527
+ { kind: "github", re: /\bgithub_pat_[A-Za-z0-9_]{20,}/g },
20528
+ { kind: "github", re: /\bgh[pousr]_[A-Za-z0-9]{20,}/g },
20529
+ { kind: "anthropic", re: /\bsk-ant-[A-Za-z0-9_-]{10,}/g },
20530
+ // sk- keys are random base62; the digit lookahead keeps hyphenated words ("sk-learn-compatible-...") out.
20531
+ { kind: "openai", re: /\bsk-(?=[A-Za-z0-9_-]*\d)[A-Za-z0-9_-]{20,}/g },
20532
+ { kind: "aws", re: /\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/g },
20533
+ { kind: "slack", re: /\bxox[abprs]-[A-Za-z0-9-]{10,}/g },
20534
+ // hf_ tokens are 32 random bytes in base64url: long, with a digit or capital. Snake_case identifiers have neither.
20535
+ { kind: "hivefloor", re: /\bhf_(?=[A-Za-z0-9_-]*[A-Z0-9])[A-Za-z0-9_-]{32,}/g },
20536
+ // A fleet push token (hff_ + 32 random bytes) or machine token (hfm_ + 32 random bytes): must never end up in a status line or a title.
20537
+ { kind: "hivefloor", re: /\bhf[fm]_[A-Za-z0-9_-]{20,}/g },
20538
+ { kind: "webhook", re: /\bwhsec_[A-Za-z0-9_-]{16,}/g },
20539
+ { kind: "vqb", re: /\bvqb_[A-Za-z0-9_-]{16,}/g },
20540
+ { kind: "jwt", re: /\beyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]*/g },
20541
+ {
20542
+ kind: "bearer",
20543
+ re: /\b(Bearer)\s+(?=[A-Za-z0-9._~+/=-]*\d)[A-Za-z0-9._~+/=-]{20,}/g,
20544
+ replace: (_m, word) => `${word} ${tag("bearer")}`
20545
+ },
20546
+ // password=hunter2, "passwd": "x", DB_PASSWORD='x'. Any non-placeholder value counts.
20547
+ {
20548
+ kind: "password",
20549
+ re: new RegExp(String.raw`${KEY_PREFIX}(password|passwd|pwd)(["']?\s*[:=]\s*)${VALUE}`, "gi"),
20550
+ replace: (m, _key, sep = "", value = "") => isAssignedSecret(sep, value, 1) ? `${m.slice(0, m.length - value.length)}${tag("password")}` : null
20551
+ },
20552
+ // token=..., client_secret: ..., api_key=...: the value has to be long enough to be a credential.
20553
+ {
20554
+ kind: "secret",
20555
+ re: new RegExp(String.raw`${KEY_PREFIX}(secret|token|api[_-]?key|apikey)(["']?\s*[:=]\s*)${VALUE}`, "gi"),
20556
+ replace: (m, _key, sep = "", value = "") => isAssignedSecret(sep, value, 8) ? `${m.slice(0, m.length - value.length)}${tag("secret")}` : null
20557
+ }
20558
+ ];
20559
+ function isAssignedSecret(sep, value, minLen) {
20560
+ if (isPlaceholder(value)) return false;
20561
+ const quoted = /^["']/.test(value);
20562
+ const v = value.replace(/^["']|["']$/g, "");
20563
+ if (v.length < minLen) return false;
20564
+ if (sep.includes("=") || quoted) return true;
20565
+ return v.length >= 6 && /[^A-Za-z]/.test(v);
20566
+ }
20567
+ function redact(input2) {
20568
+ let text2 = input2;
20569
+ let count = 0;
20570
+ for (const rule of RULES) {
20571
+ text2 = text2.replace(rule.re, (...args) => {
20572
+ const match = args[0];
20573
+ const groups = args.slice(1, -2).filter((g) => typeof g === "string");
20574
+ const out = rule.replace ? rule.replace(match, ...groups) : tag(rule.kind);
20575
+ if (out === null) return match;
20576
+ count += 1;
20577
+ return out;
20578
+ });
20579
+ }
20580
+ return { text: text2, count };
20581
+ }
20582
+
20583
+ // ../shared/src/api/memory.ts
20584
+ var CONTEXT_DECISIONS = 10;
20585
+ var MEMORY_KINDS = ["handoff", "decision"];
20586
+ var memoryKindSchema = external_exports.enum(MEMORY_KINDS);
20587
+ var memoryEntrySchema = external_exports.object({
20588
+ id: entityIdSchema,
20589
+ kind: memoryKindSchema,
20590
+ /** The task the entry is about; always set for a handoff. */
20591
+ task_id: entityIdSchema.nullable(),
20592
+ /** The task as people write it (`HF-12`). */
20593
+ task_key: external_exports.string().nullable(),
20594
+ text: external_exports.string(),
20595
+ author: external_exports.object({ type: external_exports.enum(["agent", "user", "system"]), name: external_exports.string() }),
20596
+ created_at: isoTimeSchema
20597
+ });
20598
+ var memoryContextSchema = external_exports.object({
20599
+ handoff: memoryEntrySchema.nullable(),
20600
+ decisions: external_exports.array(memoryEntrySchema).max(CONTEXT_DECISIONS)
20601
+ });
20602
+ var memoryListSchema = external_exports.object({
20603
+ items: external_exports.array(memoryEntrySchema),
20604
+ next_cursor: external_exports.string().nullable()
20605
+ });
20606
+ var taskIdSchema2 = external_exports.union([external_exports.string().regex(/^[0-9]{1,15}$/), external_exports.number().int().positive()]).transform((v) => String(v));
20607
+ var sessionRef2 = external_exports.string().trim().min(1).max(200);
20608
+ var addDecisionBody = external_exports.object({
20609
+ project: projectRefSchema.optional(),
20610
+ /** The task the decision is about (optional). */
20611
+ task_id: taskIdSchema2.optional(),
20612
+ text: external_exports.string().min(1).max(2e3),
20613
+ session: sessionRef2.optional()
20614
+ }).strict();
20615
+ var getContextBody = external_exports.object({
20616
+ project: projectRefSchema.optional(),
20617
+ /** The task whose last handoff to include. */
20618
+ task_id: taskIdSchema2.optional()
20619
+ }).strict();
20620
+ var humanDecisionBody = external_exports.object({
20621
+ text: external_exports.string().trim().min(1).max(2e3),
20622
+ /** The task number (not the id) the decision is about. */
20623
+ task_num: external_exports.number().int().positive().max(2e9).optional()
20624
+ }).strict();
20625
+ var decisionResultSchema = external_exports.object({ entry: memoryEntrySchema });
20626
+
20520
20627
  // ../shared/src/api/floor.ts
20521
20628
  var FLOOR_TOOLS = ["claude", "codex", "cursor", "gemini", "grok", "other"];
20522
20629
  var floorToolSchema = external_exports.enum(FLOOR_TOOLS);
@@ -20544,8 +20651,12 @@ var floorSessionSchema = external_exports.object({
20544
20651
  agent_id: entityIdSchema,
20545
20652
  parent_id: entityIdSchema.nullable(),
20546
20653
  state: floorSessionStateSchema,
20547
- /** Derived from the clock (`stalled`: a working session gone quiet; `lost`: a subagent that never reported done; `looping`: the same error repeating, or no status change while the claim keeps being extended). See `sessionHealth`. */
20654
+ /** Derived from the clock (`stalled`: a working session gone quiet; `lost`: a subagent that never reported done; `looping`: the same error repeating, or no status change while the claim keeps being extended; `parked`: idle for a while holding a claim; `waiting_long`: waiting on a person past the threshold). See `sessionHealth`. */
20548
20655
  health: external_exports.enum(SESSION_HEALTH),
20656
+ /** When the state or status line last changed: the start of the current wait or park (age labels count from it). Older servers omit it; use `last_event_at`. */
20657
+ state_since: isoTimeSchema.optional(),
20658
+ /** For a `waiting` session: false once somebody answered this very wait. Older servers omit it (read as true). */
20659
+ wait_open: external_exports.boolean().optional(),
20549
20660
  /** The tool's own session id (what `claude --resume` takes), main sessions only. */
20550
20661
  tool_session_id: external_exports.string().nullable(),
20551
20662
  model: external_exports.string().nullable(),
@@ -20573,7 +20684,15 @@ var floorPodSchema = external_exports.object({
20573
20684
  agents: external_exports.array(external_exports.object({ agent: floorAgentSchema, sessions: external_exports.array(floorSessionSchema) }))
20574
20685
  });
20575
20686
  var floorSnapshotSchema = external_exports.object({
20576
- project: external_exports.object({ id: entityIdSchema, key: external_exports.string(), name: external_exports.string() }),
20687
+ project: external_exports.object({
20688
+ id: entityIdSchema,
20689
+ key: external_exports.string(),
20690
+ name: external_exports.string(),
20691
+ /** The project's thresholds for parked and waiting_long, seconds (the defaults when it sets none). The browser judges health with them. */
20692
+ stall: external_exports.object({ parked_after_sec: external_exports.number(), waiting_long_after_sec: external_exports.number() }).optional(),
20693
+ /** Health of the machines this project's agents run on, by machine name; only machines with something to report. Older servers omit it. */
20694
+ machine_health: external_exports.record(external_exports.string(), machineHealthViewSchema).optional()
20695
+ }),
20577
20696
  pods: external_exports.array(floorPodSchema),
20578
20697
  shelves: external_exports.object({
20579
20698
  /** Every ready task, most urgent first (at most 200). */
@@ -20710,6 +20829,8 @@ var projectAgentSchema = external_exports.object({
20710
20829
  ...agentBaseShape,
20711
20830
  /** True when the signed-in person owns the agent (they may revoke it even as a viewer). */
20712
20831
  mine: external_exports.boolean(),
20832
+ /** The tool profile (the AI account) the agent runs under, when known; older servers omit it. */
20833
+ profile: external_exports.string().nullable().optional(),
20713
20834
  sessions: external_exports.array(floorSessionSchema)
20714
20835
  });
20715
20836
  var projectAgentList = listOf(projectAgentSchema);
@@ -20732,7 +20853,7 @@ var LOOP_NO_CHANGE_SEC = 30 * 60;
20732
20853
  var LOOP_LEASE_FRESH_SEC = 5 * 60;
20733
20854
 
20734
20855
  // ../shared/src/api/webhooks.ts
20735
- var WEBHOOK_EVENTS = ["needs_you", "task_done", "agent_lost"];
20856
+ var WEBHOOK_EVENTS = ["needs_you", "task_done", "agent_lost", "usage_waste", "usage_high", "machine_unhealthy"];
20736
20857
  var webhookEventSchema = external_exports.enum(WEBHOOK_EVENTS);
20737
20858
  var putWebhookBody = external_exports.object({
20738
20859
  url: external_exports.string().trim().min(1).max(2048),
@@ -20757,31 +20878,6 @@ var webhookGetResponse = external_exports.object({ webhook: webhookViewSchema.nu
20757
20878
  var webhookPutResponse = external_exports.object({ webhook: webhookViewSchema, secret: external_exports.string().optional() }).strict();
20758
20879
  var webhookTestResponse = external_exports.object({ ok: external_exports.literal(true), queued: external_exports.literal(true) }).strict();
20759
20880
 
20760
- // ../shared/src/usage.ts
20761
- var MIN_SPAN_MS = 5 * 6e4;
20762
- var MAX_ETA_MS = 31 * 24 * 36e5;
20763
- var USAGE_COUNTERS = ["tokens_in", "tokens_out", "cost_usd"];
20764
- function isUsageCounter(value) {
20765
- return USAGE_COUNTERS.includes(value);
20766
- }
20767
- function parseUsageKey(key2) {
20768
- const parts = key2.split(":");
20769
- if (parts.length < 3) return null;
20770
- const tool = parts[0];
20771
- const profile = parts[1];
20772
- if (!TOOLS.includes(tool)) return null;
20773
- if (!PROFILE_KEY_RE.test(profile)) return null;
20774
- if (parts.length === 3) {
20775
- const counter2 = parts[2];
20776
- return isUsageCounter(counter2) ? { tool, profile, session: null, counter: counter2 } : null;
20777
- }
20778
- if (parts[2] !== "s" || parts.length < 5) return null;
20779
- const session = parts.slice(3, -1).join(":");
20780
- const counter = parts[parts.length - 1];
20781
- if (!session || !isUsageCounter(counter)) return null;
20782
- return { tool, profile, session, counter };
20783
- }
20784
-
20785
20881
  // ../shared/src/api/inbox.ts
20786
20882
  var INBOX_KINDS = ["blocked_task", "waiting", "question"];
20787
20883
  var inboxKindSchema = external_exports.enum(INBOX_KINDS);
@@ -20796,12 +20892,18 @@ var inboxItemSchema = external_exports.object({
20796
20892
  text: external_exports.string(),
20797
20893
  waiting_since: isoTimeSchema,
20798
20894
  /** Whether the caller may answer it (role, the project's answer setting, and for a member: owning the agent). */
20799
- can_answer: external_exports.boolean()
20895
+ can_answer: external_exports.boolean(),
20896
+ /** Whether the caller may nudge the item's session now (F2): owner or admin, and the session is stalled, parked, waiting too long or looping. */
20897
+ can_nudge: external_exports.boolean().optional()
20800
20898
  });
20801
20899
  var inboxListSchema = external_exports.object({
20802
20900
  items: external_exports.array(inboxItemSchema),
20803
20901
  /** Items the caller can answer: the sidebar badge. */
20804
- count: external_exports.number().int().min(0)
20902
+ count: external_exports.number().int().min(0),
20903
+ /** Every item the caller can see, answerable or not (`items` is one page of them). */
20904
+ total: external_exports.number().int().min(0).optional(),
20905
+ /** Offset of the next page, null on the last one. */
20906
+ next_cursor: external_exports.string().nullable().optional()
20805
20907
  });
20806
20908
  var inboxAnswerBodySchema = external_exports.object({
20807
20909
  text: external_exports.string().trim().min(1).max(4e3),
@@ -20872,7 +20974,12 @@ var teamMemberSchema = external_exports.object({
20872
20974
  member: teamUserSchema.omit({ avatar_url: true })
20873
20975
  }).strict())
20874
20976
  }).strict();
20875
- var teamViewSchema = external_exports.object({ members: external_exports.array(teamMemberSchema) }).strict();
20977
+ var teamViewSchema = external_exports.object({
20978
+ members: external_exports.array(teamMemberSchema),
20979
+ /** Members of the project, over every page (`members` is one page: `?limit=` default 12, `?cursor=` an offset). */
20980
+ total: external_exports.number().int().min(0).optional(),
20981
+ next_cursor: external_exports.string().nullable().optional()
20982
+ }).strict();
20876
20983
 
20877
20984
  // ../shared/src/api/fleet.ts
20878
20985
  var FLEET_STATES = ["working", "idle", "waiting", "paused", "stopped", "unknown"];
@@ -21071,7 +21178,34 @@ var digestAgentItemSchema = external_exports.object({
21071
21178
  machine: external_exports.string().nullable(),
21072
21179
  account_label: external_exports.string().nullable(),
21073
21180
  owner_login: external_exports.string(),
21074
- created_at: isoTimeSchema
21181
+ created_at: isoTimeSchema,
21182
+ /** A short-lived run agent (a mirrored fleet/vq lane, or named `*-run-*` / `vq-*`). */
21183
+ is_run: external_exports.boolean()
21184
+ }).strict();
21185
+ var digestWaitItemSchema = external_exports.object({
21186
+ session_id: idSchema,
21187
+ project_id: idSchema,
21188
+ project_key: external_exports.string(),
21189
+ agent_name: external_exports.string(),
21190
+ machine: external_exports.string().nullable(),
21191
+ /** `waiting` = a wait on a person (or blocked); `parked` = idle while holding a claimed task. */
21192
+ kind: external_exports.enum(["waiting", "parked"]),
21193
+ /** How long the wait has lasted so far (whole seconds). */
21194
+ seconds: external_exports.number().int().nonnegative(),
21195
+ /** Always true today: only waits still open are known (no state history is kept). */
21196
+ open: external_exports.boolean()
21197
+ }).strict();
21198
+ var digestLostTimeSchema = external_exports.object({
21199
+ waiting_hours: external_exports.number().nonnegative(),
21200
+ parked_hours: external_exports.number().nonnegative(),
21201
+ longest_waits: external_exports.array(digestWaitItemSchema).max(3)
21202
+ }).strict();
21203
+ var digestWastedAccountSchema = external_exports.object({ account: external_exports.string(), unused_points: external_exports.number().nonnegative(), resets: external_exports.number().int().positive() }).strict();
21204
+ var digestQuotaSchema = external_exports.object({
21205
+ wasted_points: external_exports.number().nonnegative(),
21206
+ accounts: external_exports.array(digestWastedAccountSchema).max(DIGEST_ITEMS_LIMIT),
21207
+ alerts_waste: external_exports.number().int().nonnegative(),
21208
+ alerts_high: external_exports.number().int().nonnegative()
21075
21209
  }).strict();
21076
21210
  var digestBucketSchema = (item) => external_exports.object({
21077
21211
  count: external_exports.number().int().nonnegative(),
@@ -21082,7 +21216,9 @@ var digestCountsSchema = external_exports.object({
21082
21216
  prs_merged: external_exports.number().int().nonnegative(),
21083
21217
  tasks_blocked: external_exports.number().int().nonnegative(),
21084
21218
  needs_you: external_exports.number().int().nonnegative(),
21085
- new_agents: external_exports.number().int().nonnegative()
21219
+ new_agents: external_exports.number().int().nonnegative(),
21220
+ /** `new_agents` without the short-lived run agents (`is_run`). */
21221
+ new_agents_persistent: external_exports.number().int().nonnegative()
21086
21222
  }).strict();
21087
21223
  var meDigestResponseSchema = external_exports.object({
21088
21224
  last_seen_at: isoTimeSchema.nullable(),
@@ -21094,7 +21230,11 @@ var meDigestResponseSchema = external_exports.object({
21094
21230
  prs_merged: digestBucketSchema(digestTaskItemSchema),
21095
21231
  tasks_blocked: digestBucketSchema(digestTaskItemSchema),
21096
21232
  needs_you: digestBucketSchema(digestNeedsYouItemSchema),
21097
- new_agents: digestBucketSchema(digestAgentItemSchema)
21233
+ new_agents: digestBucketSchema(digestAgentItemSchema),
21234
+ /** F7. `total` also counts one for each of `lost_time` and `quota` that has something to show. */
21235
+ lost_time: digestLostTimeSchema,
21236
+ /** Null on a project-scoped digest (accounts are not a project's). */
21237
+ quota: digestQuotaSchema.nullable()
21098
21238
  }).strict();
21099
21239
  var meDigestSeenBodySchema = external_exports.object({
21100
21240
  before: isoTimeSchema.optional()
@@ -21104,6 +21244,59 @@ var meDigestSeenResponseSchema = external_exports.object({
21104
21244
  last_seen_at: isoTimeSchema
21105
21245
  }).strict();
21106
21246
 
21247
+ // ../shared/src/api/feedback.ts
21248
+ var FEEDBACK_CATEGORIES = ["question", "idea", "bug"];
21249
+ var feedbackCategorySchema = external_exports.enum(FEEDBACK_CATEGORIES);
21250
+ var FEEDBACK_MESSAGE_MIN = 3;
21251
+ var FEEDBACK_MESSAGE_MAX = 2e3;
21252
+ var FEEDBACK_EMAIL_MAX = 254;
21253
+ var FEEDBACK_HONEYPOT_FIELD = "website";
21254
+ var optionalEmail = external_exports.preprocess(
21255
+ (v) => typeof v === "string" && v.trim() === "" ? void 0 : v,
21256
+ external_exports.string().trim().max(FEEDBACK_EMAIL_MAX).pipe(external_exports.email("Enter a valid email address")).optional()
21257
+ );
21258
+ var feedbackBodySchema = external_exports.object({
21259
+ message: external_exports.string().trim().min(FEEDBACK_MESSAGE_MIN, `Write at least ${FEEDBACK_MESSAGE_MIN} characters`).max(FEEDBACK_MESSAGE_MAX, `At most ${FEEDBACK_MESSAGE_MAX} characters`),
21260
+ email: optionalEmail,
21261
+ category: feedbackCategorySchema.default("question"),
21262
+ /** Honeypot: stays empty for a person. */
21263
+ [FEEDBACK_HONEYPOT_FIELD]: external_exports.string().max(500).optional()
21264
+ }).strict();
21265
+ var feedbackSentSchema = external_exports.object({ ok: external_exports.literal(true) }).strict();
21266
+ var feedbackMeSchema = external_exports.object({ email: external_exports.string().nullable(), is_owner: external_exports.boolean() }).strict();
21267
+ var feedbackItemSchema = external_exports.object({
21268
+ id: entityIdSchema,
21269
+ category: feedbackCategorySchema,
21270
+ message: external_exports.string(),
21271
+ email: external_exports.string().nullable(),
21272
+ /** The GitHub login of a signed-in sender, else null. */
21273
+ from_login: external_exports.string().nullable(),
21274
+ created_at: isoTimeSchema
21275
+ });
21276
+ var feedbackListSchema = external_exports.object({
21277
+ items: external_exports.array(feedbackItemSchema),
21278
+ next_cursor: external_exports.string().nullable(),
21279
+ total: external_exports.number().int().min(0)
21280
+ });
21281
+
21282
+ // ../shared/src/api/nudge.ts
21283
+ var NUDGE_TTL_SEC = 24 * 3600;
21284
+ var NUDGE_NOTE_MAX = 300;
21285
+ var nudgeBody = external_exports.object({ note: external_exports.string().trim().max(NUDGE_NOTE_MAX).optional() }).strict();
21286
+ var nudgeResponse = external_exports.object({
21287
+ nudge: external_exports.object({
21288
+ id: external_exports.string(),
21289
+ session_id: external_exports.string(),
21290
+ created_at: isoTimeSchema,
21291
+ /** When the next nudge of this session is allowed. */
21292
+ next_allowed_at: isoTimeSchema,
21293
+ /** The owner got a push (with the resume command when the tool has one). */
21294
+ pushed: external_exports.boolean(),
21295
+ resume_command: external_exports.string().nullable()
21296
+ })
21297
+ });
21298
+ var agentNudgeSchema = external_exports.object({ id: external_exports.string(), text: external_exports.string(), sent_at: isoTimeSchema });
21299
+
21107
21300
  // ../shared/src/index.ts
21108
21301
  var ROLES = ["owner", "admin", "member", "viewer"];
21109
21302
  var roleSchema2 = external_exports.enum(ROLES);
@@ -21215,7 +21408,10 @@ export {
21215
21408
  CLI_INSTALL_COMMAND,
21216
21409
  cliNpxCommand,
21217
21410
  redact,
21411
+ usageReadingSchema,
21218
21412
  parseUsageKey,
21413
+ routeProviderFromParam,
21414
+ usageRouteSchema,
21219
21415
  DEFAULT_HOST,
21220
21416
  defaultCtx,
21221
21417
  hostFromEnv,