crewly 1.20.160 → 1.20.162

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 (157) hide show
  1. package/config/roles/orchestrator/prompt.md +2 -0
  2. package/config/roles/team-leader/prompt.md +12 -0
  3. package/config/roles/team-leader/tl-addon.md +12 -0
  4. package/config/skills/agent/core/project-tickets/SKILL.md +32 -1
  5. package/config/skills/agent/core/project-tickets/execute.sh +56 -2
  6. package/config/skills/agent/core/project-tickets/execute.test.sh +22 -0
  7. package/dist/backend/backend/src/constants.d.ts +125 -1
  8. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  9. package/dist/backend/backend/src/constants.js +146 -16
  10. package/dist/backend/backend/src/constants.js.map +1 -1
  11. package/dist/backend/backend/src/controllers/browser/browser.controller.d.ts +27 -0
  12. package/dist/backend/backend/src/controllers/browser/browser.controller.d.ts.map +1 -1
  13. package/dist/backend/backend/src/controllers/browser/browser.controller.js +203 -1
  14. package/dist/backend/backend/src/controllers/browser/browser.controller.js.map +1 -1
  15. package/dist/backend/backend/src/controllers/browser/browser.routes.d.ts.map +1 -1
  16. package/dist/backend/backend/src/controllers/browser/browser.routes.js +4 -1
  17. package/dist/backend/backend/src/controllers/browser/browser.routes.js.map +1 -1
  18. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.d.ts +43 -0
  19. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.d.ts.map +1 -1
  20. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.js +85 -0
  21. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.js.map +1 -1
  22. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.routes.d.ts +12 -0
  23. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.routes.d.ts.map +1 -1
  24. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.routes.js +19 -1
  25. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.routes.js.map +1 -1
  26. package/dist/backend/backend/src/index.d.ts.map +1 -1
  27. package/dist/backend/backend/src/index.js +30 -1
  28. package/dist/backend/backend/src/index.js.map +1 -1
  29. package/dist/backend/backend/src/models/Project.d.ts +3 -0
  30. package/dist/backend/backend/src/models/Project.d.ts.map +1 -1
  31. package/dist/backend/backend/src/models/Project.js +7 -0
  32. package/dist/backend/backend/src/models/Project.js.map +1 -1
  33. package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
  34. package/dist/backend/backend/src/routes/api.routes.js +3 -1
  35. package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
  36. package/dist/backend/backend/src/services/agent/oauth-relogin-monitor.service.d.ts +66 -13
  37. package/dist/backend/backend/src/services/agent/oauth-relogin-monitor.service.d.ts.map +1 -1
  38. package/dist/backend/backend/src/services/agent/oauth-relogin-monitor.service.js +161 -32
  39. package/dist/backend/backend/src/services/agent/oauth-relogin-monitor.service.js.map +1 -1
  40. package/dist/backend/backend/src/services/browser/browser-session.service.d.ts +15 -0
  41. package/dist/backend/backend/src/services/browser/browser-session.service.d.ts.map +1 -1
  42. package/dist/backend/backend/src/services/browser/browser-session.service.js +25 -0
  43. package/dist/backend/backend/src/services/browser/browser-session.service.js.map +1 -1
  44. package/dist/backend/backend/src/services/browser/owner-browser-input.d.ts +211 -0
  45. package/dist/backend/backend/src/services/browser/owner-browser-input.d.ts.map +1 -0
  46. package/dist/backend/backend/src/services/browser/owner-browser-input.js +373 -0
  47. package/dist/backend/backend/src/services/browser/owner-browser-input.js.map +1 -0
  48. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.d.ts.map +1 -1
  49. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js +5 -3
  50. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js.map +1 -1
  51. package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.d.ts +48 -0
  52. package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.d.ts.map +1 -1
  53. package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.js +77 -1
  54. package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.js.map +1 -1
  55. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.d.ts +223 -0
  56. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.d.ts.map +1 -0
  57. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.js +240 -0
  58. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.js.map +1 -0
  59. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.d.ts +94 -0
  60. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.d.ts.map +1 -0
  61. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.js +195 -0
  62. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.js.map +1 -0
  63. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.d.ts +286 -0
  64. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.d.ts.map +1 -0
  65. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.js +626 -0
  66. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.js.map +1 -0
  67. package/dist/backend/backend/src/services/reconciler/reconcile-rules.d.ts +37 -3
  68. package/dist/backend/backend/src/services/reconciler/reconcile-rules.d.ts.map +1 -1
  69. package/dist/backend/backend/src/services/reconciler/reconcile-rules.js +57 -6
  70. package/dist/backend/backend/src/services/reconciler/reconcile-rules.js.map +1 -1
  71. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts +21 -1
  72. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts.map +1 -1
  73. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js +62 -4
  74. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js.map +1 -1
  75. package/dist/backend/backend/src/services/reconciler/reconciler.service.d.ts +6 -0
  76. package/dist/backend/backend/src/services/reconciler/reconciler.service.d.ts.map +1 -1
  77. package/dist/backend/backend/src/services/reconciler/reconciler.service.js +9 -5
  78. package/dist/backend/backend/src/services/reconciler/reconciler.service.js.map +1 -1
  79. package/dist/backend/backend/src/services/task-pool/claim.service.d.ts +23 -3
  80. package/dist/backend/backend/src/services/task-pool/claim.service.d.ts.map +1 -1
  81. package/dist/backend/backend/src/services/task-pool/claim.service.js +52 -11
  82. package/dist/backend/backend/src/services/task-pool/claim.service.js.map +1 -1
  83. package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts +42 -0
  84. package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
  85. package/dist/backend/backend/src/services/task-pool/task-pool.service.js +82 -1
  86. package/dist/backend/backend/src/services/task-pool/task-pool.service.js.map +1 -1
  87. package/dist/backend/backend/src/services/task-pool/ticket-claim-policy.d.ts +19 -0
  88. package/dist/backend/backend/src/services/task-pool/ticket-claim-policy.d.ts.map +1 -1
  89. package/dist/backend/backend/src/services/task-pool/ticket-claim-policy.js +33 -0
  90. package/dist/backend/backend/src/services/task-pool/ticket-claim-policy.js.map +1 -1
  91. package/dist/backend/backend/src/services/v3/agent-auto-claim.service.d.ts +28 -0
  92. package/dist/backend/backend/src/services/v3/agent-auto-claim.service.d.ts.map +1 -1
  93. package/dist/backend/backend/src/services/v3/agent-auto-claim.service.js +74 -29
  94. package/dist/backend/backend/src/services/v3/agent-auto-claim.service.js.map +1 -1
  95. package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.d.ts.map +1 -1
  96. package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.js +3 -2
  97. package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.js.map +1 -1
  98. package/dist/backend/backend/src/types/index.d.ts +6 -0
  99. package/dist/backend/backend/src/types/index.d.ts.map +1 -1
  100. package/dist/backend/backend/src/types/index.js.map +1 -1
  101. package/dist/backend/backend/src/types/ticket-autopilot.types.d.ts +73 -0
  102. package/dist/backend/backend/src/types/ticket-autopilot.types.d.ts.map +1 -0
  103. package/dist/backend/backend/src/types/ticket-autopilot.types.js +88 -0
  104. package/dist/backend/backend/src/types/ticket-autopilot.types.js.map +1 -0
  105. package/dist/backend/backend/src/types/v2/claim.types.d.ts +27 -0
  106. package/dist/backend/backend/src/types/v2/claim.types.d.ts.map +1 -1
  107. package/dist/backend/backend/src/types/v2/claim.types.js +37 -0
  108. package/dist/backend/backend/src/types/v2/claim.types.js.map +1 -1
  109. package/dist/backend/backend/src/types/v2/work-item.types.d.ts +1 -1
  110. package/dist/backend/backend/src/types/v2/work-item.types.d.ts.map +1 -1
  111. package/dist/backend/backend/src/types/v2/work-item.types.js +1 -0
  112. package/dist/backend/backend/src/types/v2/work-item.types.js.map +1 -1
  113. package/dist/backend/build-info.json +2 -2
  114. package/dist/cli/backend/src/constants.d.ts +125 -1
  115. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  116. package/dist/cli/backend/src/constants.js +146 -16
  117. package/dist/cli/backend/src/constants.js.map +1 -1
  118. package/dist/cli/backend/src/models/Project.d.ts +3 -0
  119. package/dist/cli/backend/src/models/Project.d.ts.map +1 -1
  120. package/dist/cli/backend/src/models/Project.js +7 -0
  121. package/dist/cli/backend/src/models/Project.js.map +1 -1
  122. package/dist/cli/backend/src/services/agent/oauth-relogin-monitor.service.d.ts +66 -13
  123. package/dist/cli/backend/src/services/agent/oauth-relogin-monitor.service.d.ts.map +1 -1
  124. package/dist/cli/backend/src/services/agent/oauth-relogin-monitor.service.js +161 -32
  125. package/dist/cli/backend/src/services/agent/oauth-relogin-monitor.service.js.map +1 -1
  126. package/dist/cli/backend/src/services/task-pool/claim.service.d.ts +23 -3
  127. package/dist/cli/backend/src/services/task-pool/claim.service.d.ts.map +1 -1
  128. package/dist/cli/backend/src/services/task-pool/claim.service.js +52 -11
  129. package/dist/cli/backend/src/services/task-pool/claim.service.js.map +1 -1
  130. package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts +42 -0
  131. package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
  132. package/dist/cli/backend/src/services/task-pool/task-pool.service.js +82 -1
  133. package/dist/cli/backend/src/services/task-pool/task-pool.service.js.map +1 -1
  134. package/dist/cli/backend/src/services/task-pool/ticket-claim-policy.d.ts +19 -0
  135. package/dist/cli/backend/src/services/task-pool/ticket-claim-policy.d.ts.map +1 -1
  136. package/dist/cli/backend/src/services/task-pool/ticket-claim-policy.js +33 -0
  137. package/dist/cli/backend/src/services/task-pool/ticket-claim-policy.js.map +1 -1
  138. package/dist/cli/backend/src/types/index.d.ts +6 -0
  139. package/dist/cli/backend/src/types/index.d.ts.map +1 -1
  140. package/dist/cli/backend/src/types/index.js.map +1 -1
  141. package/dist/cli/backend/src/types/ticket-autopilot.types.d.ts +73 -0
  142. package/dist/cli/backend/src/types/ticket-autopilot.types.d.ts.map +1 -0
  143. package/dist/cli/backend/src/types/ticket-autopilot.types.js +88 -0
  144. package/dist/cli/backend/src/types/ticket-autopilot.types.js.map +1 -0
  145. package/dist/cli/backend/src/types/v2/claim.types.d.ts +27 -0
  146. package/dist/cli/backend/src/types/v2/claim.types.d.ts.map +1 -1
  147. package/dist/cli/backend/src/types/v2/claim.types.js +37 -0
  148. package/dist/cli/backend/src/types/v2/claim.types.js.map +1 -1
  149. package/dist/cli/backend/src/types/v2/work-item.types.d.ts +1 -1
  150. package/dist/cli/backend/src/types/v2/work-item.types.d.ts.map +1 -1
  151. package/dist/cli/backend/src/types/v2/work-item.types.js +1 -0
  152. package/dist/cli/backend/src/types/v2/work-item.types.js.map +1 -1
  153. package/frontend/dist/assets/index-0707e2ac.css +42 -0
  154. package/frontend/dist/assets/{index-f79676aa.js → index-64c8a616.js} +265 -265
  155. package/frontend/dist/index.html +2 -2
  156. package/package.json +1 -1
  157. package/frontend/dist/assets/index-edd6f262.css +0 -42
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Ticket autopilot — the pure decisions (specs/2026-09-30-ticket-autopilot.md).
3
+ *
4
+ * Everything here is a function of its inputs (tickets, teams, clock, the
5
+ * autopilot's own bookkeeping), so the rules can be unit-tested without a
6
+ * pool, a filesystem or timers:
7
+ * - which tickets need the driver's triage ({@link selectTriageCandidates});
8
+ * - whether to wake the driver now ({@link decideTriage});
9
+ * - whether to send the owner the batched questions ({@link decideOwnerQuestions})
10
+ * or the evening digest ({@link decideDigest}).
11
+ *
12
+ * @module services/project-tickets/ticket-autopilot-decision
13
+ */
14
+ import { ORCHESTRATOR_SESSION_NAME, PROJECT_TICKET_CONSTANTS, TICKET_AUTOPILOT_CONSTANTS } from '../../constants.js';
15
+ import { projectTicketPriorityRank } from '../../types/project-ticket.types.js';
16
+ import { isTeamLead } from './project-ticket-workflow.service.js';
17
+ /**
18
+ * Whether a ticket carries the needs-owner label.
19
+ *
20
+ * @param ticket - Ticket
21
+ * @returns True when it waits on the owner
22
+ */
23
+ export function hasNeedsOwnerLabel(ticket) {
24
+ return ticket.labels.includes(TICKET_AUTOPILOT_CONSTANTS.NEEDS_OWNER_LABEL);
25
+ }
26
+ /**
27
+ * The one-line question for the owner recorded on a ticket (the latest Log
28
+ * line written by `ask-owner`), or null.
29
+ *
30
+ * @param ticket - Ticket
31
+ * @returns The question text, or null
32
+ */
33
+ export function readOwnerQuestion(ticket) {
34
+ const prefix = TICKET_AUTOPILOT_CONSTANTS.OWNER_QUESTION_LOG_PREFIX;
35
+ for (let i = ticket.log.length - 1; i >= 0; i--) {
36
+ const at = ticket.log[i].indexOf(prefix);
37
+ if (at >= 0) {
38
+ const text = ticket.log[i].slice(at + prefix.length).trim();
39
+ if (text)
40
+ return text;
41
+ }
42
+ }
43
+ return null;
44
+ }
45
+ /**
46
+ * Whether a ticket was filed by a worker — an agent that is neither the
47
+ * orchestrator nor a lead of one of the project's teams. Owner, request,
48
+ * migration and lead/orc tickets are not worker-created.
49
+ *
50
+ * @param ticket - Ticket
51
+ * @param teams - The project's teams
52
+ * @returns True for a worker-created ticket
53
+ */
54
+ export function isWorkerCreated(ticket, teams) {
55
+ const source = ticket.source ?? '';
56
+ if (!source.startsWith('agent:'))
57
+ return false;
58
+ const session = source.slice('agent:'.length).trim();
59
+ if (!session || session === ORCHESTRATOR_SESSION_NAME)
60
+ return false;
61
+ for (const team of teams) {
62
+ for (const m of team.members ?? []) {
63
+ if ((m.sessionName === session || m.agentId === session) && isTeamLead(team, m))
64
+ return false;
65
+ }
66
+ }
67
+ return true;
68
+ }
69
+ /**
70
+ * Whether a member is running and idle.
71
+ *
72
+ * @param member - Team member
73
+ * @returns True when active (or started) and not working
74
+ */
75
+ export function isMemberIdle(member) {
76
+ return (member.agentStatus === 'active' || member.agentStatus === 'started') && member.workingStatus === 'idle';
77
+ }
78
+ /**
79
+ * Whether anyone could ever auto-claim a `ready` ticket: a member of an
80
+ * eligible team (the ticket's `team`, else any project team) who is not a
81
+ * lead — or a lead who is the only member of its team. Mirrors the
82
+ * AutoClaim rule (specs/2026-09-28-project-tickets.md §5).
83
+ *
84
+ * @param ticket - Ticket
85
+ * @param teams - The project's teams
86
+ * @returns True when some member could take it
87
+ */
88
+ export function hasPossibleTaker(ticket, teams) {
89
+ return teams
90
+ .filter((t) => !ticket.team || t.id === ticket.team)
91
+ .some((t) => {
92
+ const members = t.members ?? [];
93
+ return members.some((m) => !isTeamLead(t, m) || members.length === 1);
94
+ });
95
+ }
96
+ /**
97
+ * Tickets the driver should triage now, in brief order.
98
+ *
99
+ * - `backlog` tickets not waiting on the owner (no `needs-owner` label);
100
+ * - `ready` tickets nobody can take (no eligible claimer), or untouched for
101
+ * {@link TICKET_AUTOPILOT_CONSTANTS.READY_STALE_MS};
102
+ * - minus tickets already listed in a brief and unchanged since, until
103
+ * {@link TICKET_AUTOPILOT_CONSTANTS.TRIAGE_RELIST_AFTER_MS} has passed (a
104
+ * ticket the driver chose to leave is not re-sent every half hour).
105
+ *
106
+ * @param input - Tickets, teams, clock, earlier listings
107
+ * @returns Candidates (capped) and how many more are waiting
108
+ */
109
+ export function selectTriageCandidates(input) {
110
+ const listed = input.listed ?? {};
111
+ const all = [];
112
+ for (const ticket of input.tickets) {
113
+ let reason = null;
114
+ if (ticket.status === 'backlog' && !hasNeedsOwnerLabel(ticket)) {
115
+ reason = 'backlog';
116
+ }
117
+ else if (ticket.status === 'ready' && !hasNeedsOwnerLabel(ticket)) {
118
+ if (!hasPossibleTaker(ticket, input.teams))
119
+ reason = 'ready_no_taker';
120
+ else if (input.now - (Date.parse(ticket.updatedAt) || input.now) >= TICKET_AUTOPILOT_CONSTANTS.READY_STALE_MS)
121
+ reason = 'ready_stale';
122
+ }
123
+ if (!reason)
124
+ continue;
125
+ const seen = listed[ticket.id];
126
+ if (seen && seen.updatedAt === ticket.updatedAt && input.now - seen.at < TICKET_AUTOPILOT_CONSTANTS.TRIAGE_RELIST_AFTER_MS)
127
+ continue;
128
+ all.push({ ticket, reason, workerCreated: isWorkerCreated(ticket, input.teams) });
129
+ }
130
+ all.sort((a, b) => projectTicketPriorityRank(a.ticket.priority) - projectTicketPriorityRank(b.ticket.priority) ||
131
+ (Date.parse(a.ticket.createdAt) || 0) - (Date.parse(b.ticket.createdAt) || 0) ||
132
+ a.ticket.id.localeCompare(b.ticket.id));
133
+ const max = TICKET_AUTOPILOT_CONSTANTS.TRIAGE_MAX_TICKETS;
134
+ return { candidates: all.slice(0, max), more: Math.max(0, all.length - max) };
135
+ }
136
+ /**
137
+ * Whether to wake the driver with a triage item now. Checked in order:
138
+ * switch off → no driver → budget reached → a triage already live → nothing
139
+ * to triage → nobody idle → too soon since the last one (30 min on the
140
+ * periodic tick, {@link TICKET_AUTOPILOT_CONSTANTS.IDLE_TRIGGER_MIN_INTERVAL_MS}
141
+ * when a member just went idle with nothing ready).
142
+ *
143
+ * @param input - State of the project
144
+ * @returns `triage`, or `skip` with the reason
145
+ */
146
+ export function decideTriage(input) {
147
+ if (!input.enabled)
148
+ return { action: 'skip', reason: 'off' };
149
+ if (!input.driver)
150
+ return { action: 'skip', reason: 'no_driver' };
151
+ if (input.spentTodayUsd >= input.dailyBudgetUsd)
152
+ return { action: 'skip', reason: 'budget_reached' };
153
+ if (input.liveTriage)
154
+ return { action: 'skip', reason: 'triage_in_flight' };
155
+ if (input.candidateCount === 0)
156
+ return { action: 'skip', reason: 'nothing_to_triage' };
157
+ if (!input.anyoneIdle)
158
+ return { action: 'skip', reason: 'nobody_idle' };
159
+ const gap = input.trigger === 'member_idle' ? TICKET_AUTOPILOT_CONSTANTS.IDLE_TRIGGER_MIN_INTERVAL_MS : TICKET_AUTOPILOT_CONSTANTS.TRIAGE_MIN_INTERVAL_MS;
160
+ if (input.lastTriageAt !== undefined && input.now - input.lastTriageAt < gap)
161
+ return { action: 'skip', reason: 'too_soon' };
162
+ return { action: 'triage' };
163
+ }
164
+ /**
165
+ * Whether to send the batched owner questions now: only when at least one
166
+ * question is new, and then at most every
167
+ * {@link TICKET_AUTOPILOT_CONSTANTS.QUESTIONS_MIN_INTERVAL_MS} — unless a new
168
+ * one is urgent (P0), which goes out at once.
169
+ *
170
+ * @param input - Pending questions, what was sent, clock
171
+ * @returns `send` and why
172
+ */
173
+ export function decideOwnerQuestions(input) {
174
+ const fresh = input.pending.filter((q) => !input.sentKeys.has(q.key));
175
+ if (fresh.length === 0)
176
+ return { send: false, reason: 'nothing_new' };
177
+ if (fresh.some((q) => q.priority === TICKET_AUTOPILOT_CONSTANTS.URGENT_PRIORITY))
178
+ return { send: true, reason: 'urgent' };
179
+ if (input.lastSentAt === undefined || input.now - input.lastSentAt >= TICKET_AUTOPILOT_CONSTANTS.QUESTIONS_MIN_INTERVAL_MS) {
180
+ return { send: true, reason: 'due' };
181
+ }
182
+ return { send: false, reason: 'too_soon' };
183
+ }
184
+ /**
185
+ * Whether to send the daily digest now: once per local day, at or after
186
+ * {@link TICKET_AUTOPILOT_CONSTANTS.DIGEST_HOUR_LOCAL}, and only when some
187
+ * ticket changed since the previous digest (or in the last day, for the
188
+ * first one).
189
+ *
190
+ * @param input - Clock, last digest, latest ticket change
191
+ * @returns `send` and why
192
+ */
193
+ export function decideDigest(input) {
194
+ if (input.now.getHours() < TICKET_AUTOPILOT_CONSTANTS.DIGEST_HOUR_LOCAL)
195
+ return { send: false, reason: 'not_yet' };
196
+ if (input.lastSentDate === localDateKey(input.now))
197
+ return { send: false, reason: 'already_sent' };
198
+ const since = input.lastSentAt ?? input.now.getTime() - 24 * 60 * 60 * 1000;
199
+ if (!(input.latestTicketChangeAt > since))
200
+ return { send: false, reason: 'nothing_changed' };
201
+ return { send: true, reason: 'due' };
202
+ }
203
+ /**
204
+ * Local midnight of a moment's day.
205
+ *
206
+ * @param now - Moment
207
+ * @returns 00:00 local time of that day
208
+ */
209
+ export function localMidnight(now) {
210
+ const d = new Date(now.getTime());
211
+ d.setHours(0, 0, 0, 0);
212
+ return d;
213
+ }
214
+ /**
215
+ * Local calendar date key.
216
+ *
217
+ * @param now - Moment
218
+ * @returns `YYYY-MM-DD` in local time
219
+ */
220
+ export function localDateKey(now) {
221
+ const pad = (n) => String(n).padStart(2, '0');
222
+ return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
223
+ }
224
+ /**
225
+ * Tickets in progress per assignee.
226
+ *
227
+ * @param tickets - Tickets of a project
228
+ * @returns Session → count of `in_progress` tickets
229
+ */
230
+ export function inFlightByAssignee(tickets) {
231
+ const out = new Map();
232
+ for (const t of tickets) {
233
+ if (t.status === 'in_progress' && t.assignee)
234
+ out.set(t.assignee, (out.get(t.assignee) ?? 0) + 1);
235
+ }
236
+ return out;
237
+ }
238
+ /** Statuses that count as "open" for owner questions (a closed ticket asks nothing). */
239
+ export const OPEN_TICKET_STATUSES = new Set(PROJECT_TICKET_CONSTANTS.STATUSES.filter((s) => s !== 'done' && s !== 'cancelled'));
240
+ //# sourceMappingURL=ticket-autopilot-decision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ticket-autopilot-decision.js","sourceRoot":"","sources":["../../../../../../backend/src/services/project-tickets/ticket-autopilot-decision.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Ticket autopilot — the texts (specs/2026-09-30-ticket-autopilot.md §3, §5):
3
+ * the driver's triage brief, the owner's batched questions and the evening
4
+ * digest. Pure functions; the owner-facing texts are phone-sized and carry no
5
+ * harness mechanics (no WorkItem ids, claims or pool states).
6
+ *
7
+ * @module services/project-tickets/ticket-autopilot-messages
8
+ */
9
+ import type { ProjectTicket } from '../../types/project-ticket.types.js';
10
+ import type { TriageCandidate } from './ticket-autopilot-decision.js';
11
+ /**
12
+ * Actions that need the owner's explicit OK even with the autopilot on.
13
+ * Spelled out in the triage brief and in the team-leader prompt.
14
+ */
15
+ export declare const TICKET_AUTOPILOT_BOUNDARIES: readonly string[];
16
+ /** A team member as the triage brief shows it. */
17
+ export interface TriageBriefMember {
18
+ session: string;
19
+ /** Display name, when different from the session */
20
+ name?: string;
21
+ role?: string;
22
+ idle: boolean;
23
+ /** Tickets in progress for this member */
24
+ inFlight: number;
25
+ }
26
+ /** Inputs of {@link buildTriageBrief}. */
27
+ export interface TriageBriefInput {
28
+ project: {
29
+ id: string;
30
+ name: string;
31
+ };
32
+ candidates: TriageCandidate[];
33
+ /** Tickets that need triage but did not fit */
34
+ more: number;
35
+ members: TriageBriefMember[];
36
+ maxInFlightPerMember: number;
37
+ /** Clock (epoch ms), for ticket ages */
38
+ now: number;
39
+ }
40
+ /**
41
+ * Human age of a timestamp ("3h", "2d").
42
+ *
43
+ * @param iso - ISO time
44
+ * @param now - Clock (epoch ms)
45
+ * @returns Short age, or "?" when unreadable
46
+ */
47
+ export declare function formatAge(iso: string, now: number): string;
48
+ /**
49
+ * The triage brief the driver receives (WorkItem `briefMarkdown`).
50
+ *
51
+ * @param input - Project, tickets, team, limits, clock
52
+ * @returns Markdown brief
53
+ */
54
+ export declare function buildTriageBrief(input: TriageBriefInput): string;
55
+ /** A question for the owner. */
56
+ export interface OwnerQuestionItem {
57
+ projectName: string;
58
+ ticketId: string;
59
+ title: string;
60
+ question: string;
61
+ }
62
+ /**
63
+ * The one batched message with every open owner question — numbered, short,
64
+ * answerable with a tap or a word.
65
+ *
66
+ * @param items - Questions (already ordered)
67
+ * @returns Message text
68
+ */
69
+ export declare function buildOwnerQuestionsMessage(items: OwnerQuestionItem[]): string;
70
+ /** One project's section of the digest. */
71
+ export interface DigestProject {
72
+ name: string;
73
+ doneToday: ProjectTicket[];
74
+ inProgress: ProjectTicket[];
75
+ waitingOnOwner: ProjectTicket[];
76
+ }
77
+ /**
78
+ * The evening digest: per project, done today / in progress / waiting on the
79
+ * owner. Projects with nothing to report are left out.
80
+ *
81
+ * @param projects - Per-project sections
82
+ * @returns Message text, or null when there is nothing to say
83
+ */
84
+ export declare function buildDigestMessage(projects: DigestProject[]): string | null;
85
+ /**
86
+ * The once-a-day notice that the autopilot paused on its budget.
87
+ *
88
+ * @param projectName - Project
89
+ * @param spentUsd - Spent today
90
+ * @param budgetUsd - Daily budget
91
+ * @returns Message text
92
+ */
93
+ export declare function buildBudgetPausedMessage(projectName: string, spentUsd: number, budgetUsd: number): string;
94
+ //# sourceMappingURL=ticket-autopilot-messages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ticket-autopilot-messages.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/project-tickets/ticket-autopilot-messages.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAEtE;;;GAGG;AACH,eAAO,MAAM,2BAA2B,EAAE,SAAS,MAAM,EAKxD,CAAC;AAEF,kDAAkD;AAClD,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,0CAA0C;AAC1C,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,wCAAwC;IACxC,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAQ1D;AA0BD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAuDhE;AAED,gCAAgC;AAChC,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,MAAM,CAU7E;AAED,2CAA2C;AAC3C,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,UAAU,EAAE,aAAa,EAAE,CAAC;IAC5B,cAAc,EAAE,aAAa,EAAE,CAAC;CACjC;AAoBD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,MAAM,GAAG,IAAI,CAa3E;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEzG"}
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Ticket autopilot — the texts (specs/2026-09-30-ticket-autopilot.md §3, §5):
3
+ * the driver's triage brief, the owner's batched questions and the evening
4
+ * digest. Pure functions; the owner-facing texts are phone-sized and carry no
5
+ * harness mechanics (no WorkItem ids, claims or pool states).
6
+ *
7
+ * @module services/project-tickets/ticket-autopilot-messages
8
+ */
9
+ import { TICKET_AUTOPILOT_CONSTANTS } from '../../constants.js';
10
+ /**
11
+ * Actions that need the owner's explicit OK even with the autopilot on.
12
+ * Spelled out in the triage brief and in the team-leader prompt.
13
+ */
14
+ export const TICKET_AUTOPILOT_BOUNDARIES = [
15
+ 'sending email or messages to outside people',
16
+ 'publishing content publicly',
17
+ 'deploying to production',
18
+ 'spending money',
19
+ ];
20
+ /**
21
+ * Human age of a timestamp ("3h", "2d").
22
+ *
23
+ * @param iso - ISO time
24
+ * @param now - Clock (epoch ms)
25
+ * @returns Short age, or "?" when unreadable
26
+ */
27
+ export function formatAge(iso, now) {
28
+ const t = Date.parse(iso);
29
+ if (!Number.isFinite(t))
30
+ return '?';
31
+ const minutes = Math.max(0, Math.floor((now - t) / 60_000));
32
+ if (minutes < 60)
33
+ return `${minutes}m`;
34
+ const hours = Math.floor(minutes / 60);
35
+ if (hours < 48)
36
+ return `${hours}h`;
37
+ return `${Math.floor(hours / 24)}d`;
38
+ }
39
+ /**
40
+ * Who created a ticket, for the brief.
41
+ *
42
+ * @param source - Ticket `source`
43
+ * @returns `owner`, the agent session, the request, or `unknown`
44
+ */
45
+ function creatorOf(source) {
46
+ if (!source)
47
+ return 'unknown';
48
+ if (source.startsWith('agent:'))
49
+ return source.slice('agent:'.length);
50
+ return source;
51
+ }
52
+ /**
53
+ * Shorten free text to one excerpt line.
54
+ *
55
+ * @param text - Text
56
+ * @param max - Max characters
57
+ * @returns Single-line excerpt
58
+ */
59
+ function excerpt(text, max) {
60
+ const flat = text.replace(/\s+/g, ' ').trim();
61
+ return flat.length > max ? `${flat.slice(0, max - 1)}…` : flat;
62
+ }
63
+ /**
64
+ * The triage brief the driver receives (WorkItem `briefMarkdown`).
65
+ *
66
+ * @param input - Project, tickets, team, limits, clock
67
+ * @returns Markdown brief
68
+ */
69
+ export function buildTriageBrief(input) {
70
+ const p = input.project.id;
71
+ const tk = '$AGENT_SKILLS_PATH/core/project-tickets/execute.sh';
72
+ const lines = [
73
+ `# Ticket triage — ${input.project.name} (${input.candidates.length} ticket${input.candidates.length === 1 ? '' : 's'})`,
74
+ '',
75
+ 'Ticket autopilot is on for this project: you keep its backlog moving while the owner is away (on a phone).',
76
+ 'Decide every ticket below, then complete this WorkItem (complete-task with its id) with one line per ticket saying what you did.',
77
+ '',
78
+ '## For each ticket, do one of these',
79
+ '',
80
+ `1. **Ready + assign** — \`bash ${tk} assign --project ${p} --id <ID> --to <member>\`, or \`… update --project ${p} --id <ID> --status ready\` to let the next idle member take it. At most ${input.maxInFlightPerMember} ticket${input.maxInFlightPerMember === 1 ? '' : 's'} in progress per member.`,
81
+ `2. **Split** — create smaller tickets (\`… create --project ${p} --title "…" --acceptance "…" --status ready\`), then cancel the original with a note naming the new ids.`,
82
+ `3. **Needs the owner** — \`bash ${tk} ask-owner --project ${p} --id <ID> --question "<one line the owner can answer with a tap or a word>"\`. Crewly batches these to the owner's phone; do not message the owner about them yourself.`,
83
+ `4. **Cancel** — \`… update --project ${p} --id <ID> --status cancelled --note "<reason>"\`.`,
84
+ '',
85
+ '## Boundaries — the autopilot does NOT lift these',
86
+ '',
87
+ 'Even with the autopilot on, these need the owner\'s explicit OK:',
88
+ ...TICKET_AUTOPILOT_BOUNDARIES.map((b) => `- ${b};`),
89
+ '',
90
+ 'A ticket whose completion needs one of these may be worked up to a draft or a PR; then use ask-owner for the final step.',
91
+ '"The autopilot is on" is never that OK, and neither is your own judgement.',
92
+ '',
93
+ 'Tickets marked **worker-created — review first** were filed by a team member, not by the owner, a lead or the orchestrator.',
94
+ 'Check that they are wanted and in scope before making them ready; if unsure, ask the owner.',
95
+ '',
96
+ '## Team',
97
+ '',
98
+ ...(input.members.length > 0
99
+ ? input.members.map((m) => `- ${m.session}${m.name && m.name !== m.session ? ` (${m.name}${m.role ? `, ${m.role}` : ''})` : m.role ? ` (${m.role})` : ''} — ${m.idle ? 'idle' : 'busy'}, ${m.inFlight} in progress`)
100
+ : ['- (no members found)']),
101
+ '',
102
+ '## Tickets',
103
+ '',
104
+ ];
105
+ for (const c of input.candidates) {
106
+ const t = c.ticket;
107
+ const why = c.reason === 'backlog' ? 'backlog' : c.reason === 'ready_no_taker' ? 'ready, but nobody on the team can take it' : 'ready, but nobody has taken it for a day';
108
+ lines.push(`### ${t.id} · ${t.priority} · ${why} · ${formatAge(t.createdAt, input.now)} old`);
109
+ lines.push(t.title);
110
+ const meta = [`created by ${creatorOf(t.source)}`];
111
+ if (t.labels.length > 0)
112
+ meta.push(`labels: ${t.labels.join(', ')}`);
113
+ if (t.team)
114
+ meta.push(`team: ${t.team}`);
115
+ lines.push(`${meta.join(' · ')}${c.workerCreated ? ' · **worker-created — review first**' : ''}`);
116
+ if (t.description)
117
+ lines.push(`> ${excerpt(t.description, TICKET_AUTOPILOT_CONSTANTS.TRIAGE_DESCRIPTION_EXCERPT_CHARS)}`);
118
+ lines.push('');
119
+ }
120
+ if (input.more > 0)
121
+ lines.push(`${input.more} more ticket${input.more === 1 ? '' : 's'} will come in the next triage.`, '');
122
+ lines.push(`Full ticket: \`bash ${tk} show --project ${p} --id <ID>\`.`);
123
+ return lines.join('\n');
124
+ }
125
+ /**
126
+ * The one batched message with every open owner question — numbered, short,
127
+ * answerable with a tap or a word.
128
+ *
129
+ * @param items - Questions (already ordered)
130
+ * @returns Message text
131
+ */
132
+ export function buildOwnerQuestionsMessage(items) {
133
+ const multiProject = new Set(items.map((i) => i.projectName)).size > 1;
134
+ const lines = [
135
+ items.length === 1 ? 'One ticket is waiting on you:' : `${items.length} tickets are waiting on you:`,
136
+ '',
137
+ ...items.map((q, i) => `${i + 1}. ${q.ticketId}${multiProject ? ` (${q.projectName})` : ''} ${excerpt(q.title, 60)} — ${q.question}`),
138
+ '',
139
+ 'Reply with the number and your answer (e.g. "1 yes").',
140
+ ];
141
+ return lines.join('\n');
142
+ }
143
+ /**
144
+ * One digest section line: count plus the first few tickets.
145
+ *
146
+ * @param label - Section label
147
+ * @param tickets - Tickets
148
+ * @param withAssignee - Append the assignee
149
+ * @returns Line, or null when empty
150
+ */
151
+ function digestSection(label, tickets, withAssignee) {
152
+ if (tickets.length === 0)
153
+ return null;
154
+ const max = TICKET_AUTOPILOT_CONSTANTS.DIGEST_MAX_ITEMS_PER_SECTION;
155
+ const named = tickets
156
+ .slice(0, max)
157
+ .map((t) => `${t.id} ${excerpt(t.title, 50)}${withAssignee && t.assignee ? ` (${t.assignee})` : ''}`);
158
+ const rest = tickets.length > max ? `; +${tickets.length - max} more` : '';
159
+ return `${label} (${tickets.length}): ${named.join('; ')}${rest}`;
160
+ }
161
+ /**
162
+ * The evening digest: per project, done today / in progress / waiting on the
163
+ * owner. Projects with nothing to report are left out.
164
+ *
165
+ * @param projects - Per-project sections
166
+ * @returns Message text, or null when there is nothing to say
167
+ */
168
+ export function buildDigestMessage(projects) {
169
+ const blocks = [];
170
+ for (const p of projects) {
171
+ const rows = [
172
+ digestSection('Done today', p.doneToday, false),
173
+ digestSection('In progress', p.inProgress, true),
174
+ digestSection('Waiting on you', p.waitingOnOwner, false),
175
+ ].filter((r) => r !== null);
176
+ if (rows.length === 0)
177
+ continue;
178
+ blocks.push([`*${p.name}*`, ...rows.map((r) => `- ${r}`)].join('\n'));
179
+ }
180
+ if (blocks.length === 0)
181
+ return null;
182
+ return ['Tickets today', '', blocks.join('\n\n')].join('\n');
183
+ }
184
+ /**
185
+ * The once-a-day notice that the autopilot paused on its budget.
186
+ *
187
+ * @param projectName - Project
188
+ * @param spentUsd - Spent today
189
+ * @param budgetUsd - Daily budget
190
+ * @returns Message text
191
+ */
192
+ export function buildBudgetPausedMessage(projectName, spentUsd, budgetUsd) {
193
+ return `Ticket autopilot paused for today on ${projectName}: the team has used $${spentUsd.toFixed(2)} of its $${budgetUsd.toFixed(2)} daily budget. It picks up again tomorrow. Reply "raise the budget to $N" to change it.`;
194
+ }
195
+ //# sourceMappingURL=ticket-autopilot-messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ticket-autopilot-messages.js","sourceRoot":"","sources":["../../../../../../backend/src/services/project-tickets/ticket-autopilot-messages.ts"],"names":[],"mappings":"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"}