heyio 0.42.0 → 1.0.0

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 (100) hide show
  1. package/README.md +40 -52
  2. package/dist/api/auth.js +35 -38
  3. package/dist/api/server.js +157 -1139
  4. package/dist/config.js +49 -32
  5. package/dist/copilot/agents.js +72 -1055
  6. package/dist/copilot/client.js +6 -17
  7. package/dist/copilot/io-scheduler.js +55 -139
  8. package/dist/copilot/model-router.js +100 -72
  9. package/dist/copilot/orchestrator.js +91 -515
  10. package/dist/copilot/scheduler.js +67 -189
  11. package/dist/copilot/skills.js +41 -366
  12. package/dist/copilot/system-message.js +40 -200
  13. package/dist/copilot/tools.js +191 -2042
  14. package/dist/daemon.js +54 -201
  15. package/dist/index.js +15 -133
  16. package/dist/mcp/config.js +23 -31
  17. package/dist/mcp/index.js +2 -3
  18. package/dist/mcp/registry.js +33 -88
  19. package/dist/notify.js +18 -100
  20. package/dist/paths.js +13 -24
  21. package/dist/setup.js +35 -0
  22. package/dist/store/db.js +111 -297
  23. package/dist/store/feed.js +29 -97
  24. package/dist/store/instances.js +56 -121
  25. package/dist/store/schedules.js +21 -73
  26. package/dist/store/squads.js +35 -186
  27. package/dist/store/tasks.js +25 -168
  28. package/dist/telegram/bot.js +20 -312
  29. package/dist/telegram/handlers.js +39 -3
  30. package/dist/watchdog.js +31 -45
  31. package/dist/wiki/fs.js +38 -155
  32. package/dist/wiki/search.js +31 -44
  33. package/package.json +5 -8
  34. package/web-dist/assets/ChatView-EFFiln1H.js +11 -0
  35. package/web-dist/assets/FeedView-bN4NMOL7.js +6 -0
  36. package/web-dist/assets/LoginView-CNtasq3n.js +1 -0
  37. package/web-dist/assets/McpView-C2CHiwsi.js +1 -0
  38. package/web-dist/assets/SchedulesView-CyilLban.js +1 -0
  39. package/web-dist/assets/SettingsView-1wLXKEF4.js +1 -0
  40. package/web-dist/assets/SkillsView-BLsD-0u0.js +1 -0
  41. package/web-dist/assets/SquadDetailView-CsCw2ZLp.js +21 -0
  42. package/web-dist/assets/SquadsView-DQ3vFlyO.js +6 -0
  43. package/web-dist/assets/WikiView-19M3oqnq.js +21 -0
  44. package/web-dist/assets/api-WGvTsXaE.js +1 -0
  45. package/web-dist/assets/index-D7M5O-_l.css +1 -0
  46. package/web-dist/assets/index-DZOS9syn.js +95 -0
  47. package/web-dist/assets/plus-BOvyX1BC.js +6 -0
  48. package/web-dist/assets/trash-2-DHoetkC4.js +6 -0
  49. package/web-dist/favicon.svg +4 -1
  50. package/web-dist/index.html +7 -10
  51. package/dist/api/logout.test.js +0 -129
  52. package/dist/api/mcp.test.js +0 -285
  53. package/dist/api/wiki.test.js +0 -283
  54. package/dist/auth/session-logic.js +0 -79
  55. package/dist/auth/session-logic.test.js +0 -201
  56. package/dist/copilot/auto-complete-instance.test.js +0 -104
  57. package/dist/copilot/cron.js +0 -136
  58. package/dist/copilot/event-summary.js +0 -286
  59. package/dist/copilot/instance-deactivate.test.js +0 -119
  60. package/dist/copilot/model-router.test.js +0 -71
  61. package/dist/copilot/review-backfill.js +0 -57
  62. package/dist/copilot/session-timeout.js +0 -112
  63. package/dist/copilot/session-timeout.test.js +0 -372
  64. package/dist/copilot/skills.test.js +0 -55
  65. package/dist/copilot/universes.js +0 -469
  66. package/dist/instance-watchdog.js +0 -104
  67. package/dist/instance-watchdog.test.js +0 -183
  68. package/dist/mcp/client.js +0 -109
  69. package/dist/mcp/client.test.js +0 -99
  70. package/dist/mcp/config.test.js +0 -49
  71. package/dist/mcp/registry.test.js +0 -79
  72. package/dist/notify.test.js +0 -232
  73. package/dist/store/feed.test.js +0 -279
  74. package/dist/store/instances.test.js +0 -310
  75. package/dist/store/io-schedules.js +0 -63
  76. package/dist/store/notifications.js +0 -79
  77. package/dist/store/notifications.test.js +0 -197
  78. package/dist/store/schedule-runs.js +0 -46
  79. package/dist/store/squads.test.js +0 -405
  80. package/dist/store/tasks.test.js +0 -150
  81. package/dist/store/worktrees.js +0 -83
  82. package/dist/tui/index.js +0 -286
  83. package/dist/update.js +0 -81
  84. package/dist/watchdog.test.js +0 -83
  85. package/dist/wiki/wiki-squad.test.js +0 -54
  86. package/web-dist/assets/AgentActivityView-CedxxE6K.js +0 -1
  87. package/web-dist/assets/ChatView-DMkYQo_V.js +0 -4
  88. package/web-dist/assets/FeedView-BH4q-31V.js +0 -1
  89. package/web-dist/assets/InboxView-BVwVP4EW.js +0 -1
  90. package/web-dist/assets/LoginView-DRPDhnwu.js +0 -1
  91. package/web-dist/assets/McpView-D8yWz-lq.js +0 -1
  92. package/web-dist/assets/SchedulesView-BzzyncGF.js +0 -1
  93. package/web-dist/assets/SettingsTabs.vue_vue_type_script_setup_true_lang-oW3ySu7Y.js +0 -1
  94. package/web-dist/assets/SkillsView-oxpYuhx7.js +0 -1
  95. package/web-dist/assets/SquadsView-CaKUIKlq.js +0 -1
  96. package/web-dist/assets/StatusIndicator.vue_vue_type_script_setup_true_lang-8U15Qp_Q.js +0 -1
  97. package/web-dist/assets/WikiView-C5jXUlfW.js +0 -1
  98. package/web-dist/assets/index-BrWzNw-N.css +0 -10
  99. package/web-dist/assets/index-f67odrrt.js +0 -81
  100. package/web-dist/icons.svg +0 -24
@@ -1,202 +1,80 @@
1
- // Squad scheduler fires recurring "stand-up" meetings on a cron schedule.
2
- //
3
- // Design:
4
- // - Runs as a single setInterval on the daemon (TICK_MS).
5
- // - Each tick: fetch schedules whose next_run_at is in the past.
6
- // - For each due schedule: compose a stand-up prompt and delegate it to the
7
- // squad lead via the existing delegateToAgent() pipeline. The lead then
8
- // orchestrates the agenda by delegating subtasks to teammates internally.
9
- // - After firing, advance next_run_at to the next cron occurrence.
10
- //
11
- // Schedules survive daemon restarts because next_run_at is persisted. On
12
- // startup we backfill any next_run_at fields that became stale (or are NULL).
13
- import { listSchedules, listDueSchedules, recordScheduleRun, setScheduleTimestamps, updateNextRun } from "../store/schedules.js";
14
- import { getSquad } from "../store/squads.js";
15
- import { delegateToAgent } from "./agents.js";
16
- import { nextRun } from "./cron.js";
17
- import { notifyBackground } from "../notify.js";
18
- import { startScheduleRun, completeScheduleRun, failScheduleRun } from "../store/schedule-runs.js";
19
- import { createFeedEntry } from "../store/feed.js";
20
- import { shouldRouteToInbox } from "./tools.js";
21
- const TICK_MS = 30_000;
22
- const AGENDA_BLOCKS = {
23
- triage: `**Triage**
24
- - Use the GitHub CLI (\`gh issue list\`) to pull open issues with the \`needs-triage\` label.
25
- - For each issue: read the body and decide on appropriate labels (priority, area, type, etc).
26
- - Apply labels with \`gh issue edit <num> --add-label "..."\` and remove \`needs-triage\` once labelled.
27
- - If the issue lacks information, post a clarifying comment with \`gh issue comment\` instead of labelling, and leave \`needs-triage\` on it.`,
28
- prioritize: `**Prioritize**
29
- - Identify open issues that are ready to be worked on: properly labelled, not blocked, no \`needs-triage\` or \`needs-review\` label, no open PR already addressing them.
30
- - Rank by priority labels and surface the top candidate.
31
- - After the stand-up, the team lead should immediately begin work on the highest-priority ready issue by delegating it to the right teammate.`,
32
- ideation: `**Ideation**
33
- - Brainstorm 1–3 concrete improvements or new features for the project.
34
- - Discuss as a team (use \`delegate_to_teammate\` to gather input from members whose expertise fits the idea).
35
- - For each idea the team agrees on, create a GitHub issue with \`gh issue create\` tagged with the \`needs-review\` label so the human can approve it before work begins.`,
36
- };
37
- function buildStandupPrompt(squad, schedule) {
38
- const blocks = schedule.agenda
39
- .map((item) => AGENDA_BLOCKS[item] ?? `**${item}** _(no built-in template — improvise)_`)
40
- .join("\n\n");
41
- const notes = schedule.notes ? `\n\n**Operator notes:** ${schedule.notes}` : "";
42
- return `# Scheduled stand-up: ${schedule.name}
43
-
44
- You are the team lead for the **${squad.name}** squad (\`${squad.slug}\`). Run a stand-up meeting now.
45
-
46
- **Project path:** \`${squad.project_path}\` — \`cd\` here before invoking the GitHub CLI so it picks up the right repo.
47
-
48
- **Agenda** (work through these in order; use \`delegate_to_teammate\` to pull in the right specialist for each item):
49
-
50
- ${blocks}
51
-
52
- When you finish the agenda, summarise what was triaged, what was prioritised (and what work you've kicked off), and what new issues were filed during ideation.${notes}`;
1
+ import { listSchedules, updateScheduleLastRun } from "../store/schedules.js";
2
+ import { sendToOrchestrator } from "./orchestrator.js";
3
+ let schedulerInterval;
4
+ export function startSquadScheduler() {
5
+ // Check every minute for due schedules
6
+ schedulerInterval = setInterval(() => {
7
+ checkSquadSchedules();
8
+ }, 60_000);
9
+ schedulerInterval.unref();
53
10
  }
54
- let timer;
55
- const inFlight = new Set();
56
- async function fireSchedule(schedule) {
57
- if (inFlight.has(schedule.id))
58
- return;
59
- const squad = getSquad(schedule.squad_slug);
60
- if (!squad) {
61
- console.error(`[io] scheduler: squad "${schedule.squad_slug}" missing for schedule ${schedule.id}; disabling next_run_at`);
62
- updateNextRun(schedule.id, null);
63
- return;
64
- }
65
- inFlight.add(schedule.id);
66
- const ranAt = new Date();
67
- let nextIso = null;
68
- try {
69
- nextIso = nextRun(schedule.cron_expr, ranAt).toISOString();
70
- }
71
- catch (err) {
72
- console.error(`[io] scheduler: cron parse error for schedule ${schedule.id}:`, err instanceof Error ? err.message : err);
73
- }
74
- recordScheduleRun(schedule.id, ranAt, nextIso);
75
- const prompt = buildStandupPrompt({ name: squad.name, slug: squad.slug, project_path: squad.project_path }, schedule);
76
- console.log(`[io] scheduler: firing schedule "${schedule.name}" for squad "${squad.slug}" (next run: ${nextIso ?? "never"})`);
77
- const run = startScheduleRun({
78
- schedule_type: "squad",
79
- schedule_id: schedule.id,
80
- schedule_name: schedule.name,
81
- squad_slug: squad.slug,
82
- });
83
- try {
84
- await delegateToAgent(squad.slug, prompt, (_taskId, result) => {
85
- if (shouldRouteToInbox(prompt)) {
86
- createFeedEntry({ type: "inbox", title: `[${squad.slug}] ${schedule.name}`, body: result });
87
- console.error(`[io] Schedule ${schedule.id} result routed to inbox`);
88
- completeScheduleRun(run.id, 0);
89
- }
90
- else {
91
- void notifyBackground({
92
- source: {
93
- type: "squad-schedule",
94
- scheduleId: schedule.id,
95
- squadSlug: squad.slug,
96
- scheduleName: schedule.name,
97
- },
98
- title: `${squad.name}: ${schedule.name}`,
99
- text: result,
100
- }).then((notifyResult) => {
101
- completeScheduleRun(run.id, notifyResult.id);
102
- }).catch((err) => {
103
- failScheduleRun(run.id, err instanceof Error ? err.message : String(err));
104
- });
11
+ function checkSquadSchedules() {
12
+ const schedules = listSchedules("squad");
13
+ const now = new Date();
14
+ for (const schedule of schedules) {
15
+ if (!schedule.enabled)
16
+ continue;
17
+ if (!isDue(schedule.cron, schedule.last_run, now))
18
+ continue;
19
+ updateScheduleLastRun(schedule.id);
20
+ const agenda = schedule.agenda || "triage";
21
+ const prompt = `[Squad Schedule] Run "${agenda}" stand-up for squad ${schedule.squad_id}. Agenda: ${agenda}`;
22
+ sendToOrchestrator(prompt, "scheduler", (_text, done) => {
23
+ if (done) {
24
+ console.log(`[scheduler] Squad stand-up completed for ${schedule.squad_id}`);
105
25
  }
106
26
  });
107
27
  }
108
- catch (err) {
109
- failScheduleRun(run.id, err instanceof Error ? err.message : String(err));
110
- console.error(`[io] scheduler: failed to delegate stand-up for schedule ${schedule.id}:`, err instanceof Error ? err.message : err);
111
- }
112
- finally {
113
- inFlight.delete(schedule.id);
114
- }
115
28
  }
116
- async function tick() {
117
- let due;
118
- try {
119
- due = listDueSchedules(new Date());
29
+ function isDue(cron, lastRun, now) {
30
+ // Simple cron matching: parse "minute hour day month weekday"
31
+ const parts = cron.split(" ");
32
+ if (parts.length !== 5)
33
+ return false;
34
+ const [minSpec, hourSpec, daySpec, monthSpec, weekdaySpec] = parts;
35
+ if (!matchesCronField(minSpec, now.getMinutes()))
36
+ return false;
37
+ if (!matchesCronField(hourSpec, now.getHours()))
38
+ return false;
39
+ if (!matchesCronField(daySpec, now.getDate()))
40
+ return false;
41
+ if (!matchesCronField(monthSpec, now.getMonth() + 1))
42
+ return false;
43
+ if (!matchesCronField(weekdaySpec, now.getDay()))
44
+ return false;
45
+ // Prevent running more than once per matching minute
46
+ if (lastRun) {
47
+ const lastDate = new Date(lastRun);
48
+ const diffMs = now.getTime() - lastDate.getTime();
49
+ if (diffMs < 60_000)
50
+ return false;
120
51
  }
121
- catch (err) {
122
- console.error("[io] scheduler tick failed:", err instanceof Error ? err.message : err);
123
- return;
52
+ return true;
53
+ }
54
+ function matchesCronField(spec, value) {
55
+ if (spec === "*")
56
+ return true;
57
+ // Handle ranges (e.g., "1-5")
58
+ if (spec.includes("-")) {
59
+ const [start, end] = spec.split("-").map(Number);
60
+ return value >= start && value <= end;
124
61
  }
125
- for (const s of due) {
126
- // Sequential — avoid stampeding multiple stand-ups simultaneously.
127
- await fireSchedule(s);
62
+ // Handle lists (e.g., "1,3,5")
63
+ if (spec.includes(",")) {
64
+ return spec.split(",").map(Number).includes(value);
128
65
  }
129
- }
130
- /**
131
- * Backfill next_run_at for any schedules where it's NULL or already in the past
132
- * but should not have fired (e.g. daemon was offline). We deliberately advance
133
- * to the next future occurrence rather than replaying missed runs.
134
- */
135
- export function reconcileSchedules(now = new Date()) {
136
- for (const s of listSchedules()) {
137
- if (!s.enabled)
138
- continue;
139
- let needsUpdate = false;
140
- if (!s.next_run_at) {
141
- needsUpdate = true;
142
- }
143
- else {
144
- const next = new Date(s.next_run_at);
145
- if (Number.isNaN(next.getTime()) || next <= now)
146
- needsUpdate = true;
147
- }
148
- if (!needsUpdate)
149
- continue;
150
- try {
151
- const next = nextRun(s.cron_expr, now);
152
- updateNextRun(s.id, next.toISOString());
153
- }
154
- catch (err) {
155
- console.error(`[io] scheduler: invalid cron "${s.cron_expr}" on schedule ${s.id}; disabling next_run_at:`, err instanceof Error ? err.message : err);
156
- updateNextRun(s.id, null);
157
- }
66
+ // Handle step values (e.g., "*/5")
67
+ if (spec.startsWith("*/")) {
68
+ const step = parseInt(spec.slice(2), 10);
69
+ return value % step === 0;
158
70
  }
71
+ // Exact match
72
+ return parseInt(spec, 10) === value;
159
73
  }
160
- export function startScheduler() {
161
- if (timer)
162
- return;
163
- reconcileSchedules();
164
- timer = setInterval(() => {
165
- void tick();
166
- }, TICK_MS);
167
- if (typeof timer.unref === "function")
168
- timer.unref();
169
- console.log(`[io] Scheduler started (tick every ${TICK_MS / 1000}s)`);
170
- }
171
- export function stopScheduler() {
172
- if (!timer)
173
- return;
174
- clearInterval(timer);
175
- timer = undefined;
176
- }
177
- /**
178
- * Manually fire a schedule. Used by squad_schedule_run_now.
179
- *
180
- * Snapshots last_run_at and next_run_at before firing and restores them
181
- * after, so a manual fire never disturbs the regular schedule (a user
182
- * testing a 05:00 schedule at 04:30 should not have today's 05:00 run
183
- * skipped or shifted). The fireSchedule path itself advances both fields
184
- * because that's correct for an automatic firing — only manual runs need
185
- * to leave the schedule untouched.
186
- */
187
- export async function runScheduleNow(scheduleId) {
188
- const all = listSchedules();
189
- const s = all.find((x) => x.id === scheduleId);
190
- if (!s)
191
- return { ok: false, error: `Schedule ${scheduleId} not found` };
192
- const previousLast = s.last_run_at;
193
- const previousNext = s.next_run_at;
194
- try {
195
- await fireSchedule(s);
196
- }
197
- finally {
198
- setScheduleTimestamps(scheduleId, previousLast, previousNext);
74
+ export function stopSquadScheduler() {
75
+ if (schedulerInterval) {
76
+ clearInterval(schedulerInterval);
77
+ schedulerInterval = undefined;
199
78
  }
200
- return { ok: true };
201
79
  }
202
80
  //# sourceMappingURL=scheduler.js.map