@songsid/agend 2.3.0-beta.22 → 2.3.0-beta.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/README.zh-TW.md +2 -2
- package/dist/agent-cli.js +4 -2
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.d.ts +1 -0
- package/dist/agent-endpoint.js +4 -0
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/agent-whats-new.d.ts +5 -0
- package/dist/agent-whats-new.js +16 -1
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/auth-api.d.ts +7 -0
- package/dist/auth-api.js +8 -2
- package/dist/auth-api.js.map +1 -1
- package/dist/backend/codex-package.d.ts +30 -0
- package/dist/backend/codex-package.js +133 -0
- package/dist/backend/codex-package.js.map +1 -0
- package/dist/backend/codex.d.ts +2 -0
- package/dist/backend/codex.js +9 -2
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/kiro.d.ts +9 -1
- package/dist/backend/kiro.js +102 -1
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/opencode-permission.js +1 -1
- package/dist/backend/opencode-permission.js.map +1 -1
- package/dist/backend/opencode.js +2 -1
- package/dist/backend/opencode.js.map +1 -1
- package/dist/binary-probe.js +1 -1
- package/dist/bug-report-cli.js +1 -1
- package/dist/bundled-skills.d.ts +7 -3
- package/dist/bundled-skills.js +16 -7
- package/dist/bundled-skills.js.map +1 -1
- package/dist/channel/adapters/discord.js +3 -3
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/mcp-server.js +12 -4
- package/dist/channel/mcp-server.js.map +1 -1
- package/dist/channel/mcp-tools.d.ts +3 -1
- package/dist/channel/mcp-tools.js +36 -0
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/child-process.d.ts +21 -0
- package/dist/child-process.js +54 -0
- package/dist/child-process.js.map +1 -0
- package/dist/cli.js +167 -182
- package/dist/cli.js.map +1 -1
- package/dist/command-table.d.ts +1 -1
- package/dist/command-table.js +1 -1
- package/dist/command-table.js.map +1 -1
- package/dist/config-validator.js +20 -1
- package/dist/config-validator.js.map +1 -1
- package/dist/config.js +3 -1
- package/dist/config.js.map +1 -1
- package/dist/daemon.d.ts +8 -0
- package/dist/daemon.js +31 -8
- package/dist/daemon.js.map +1 -1
- package/dist/darwin-memory.js +1 -1
- package/dist/doctor.d.ts +5 -0
- package/dist/doctor.js +42 -0
- package/dist/doctor.js.map +1 -1
- package/dist/export-import.js +1 -1
- package/dist/export-import.js.map +1 -1
- package/dist/fleet-level-config.d.ts +1 -1
- package/dist/fleet-level-config.js +2 -0
- package/dist/fleet-level-config.js.map +1 -1
- package/dist/fleet-manager.d.ts +45 -0
- package/dist/fleet-manager.js +301 -21
- package/dist/fleet-manager.js.map +1 -1
- package/dist/full-restart.d.ts +5 -5
- package/dist/full-restart.js +5 -5
- package/dist/full-restart.js.map +1 -1
- package/dist/general-knowledge/skills/fleet-config/SKILL.md +1 -1
- package/dist/general-knowledge/skills/fleet-restart/SKILL.md +7 -8
- package/dist/general-knowledge/skills/multi-channel/SKILL.md +1 -1
- package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +319 -0
- package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +529 -0
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +16 -3
- package/dist/general-knowledge/steering/core-rules.md +1 -1
- package/dist/instance-config-impact.js +10 -3
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.js +4 -4
- package/dist/locale.js +118 -4
- package/dist/locale.js.map +1 -1
- package/dist/login-flows.js +1 -1
- package/dist/login-shell.js +3 -1
- package/dist/login-shell.js.map +1 -1
- package/dist/ls-rows.js +1 -1
- package/dist/needs-you-hub.d.ts +98 -1
- package/dist/needs-you-hub.js +313 -44
- package/dist/needs-you-hub.js.map +1 -1
- package/dist/needs-you-live.d.ts +5 -2
- package/dist/needs-you-live.js +6 -3
- package/dist/needs-you-live.js.map +1 -1
- package/dist/needs-you.d.ts +58 -3
- package/dist/needs-you.js +36 -3
- package/dist/needs-you.js.map +1 -1
- package/dist/outbound-schemas.d.ts +3 -3
- package/dist/public-link-progress.d.ts +12 -1
- package/dist/public-link-progress.js +5 -10
- package/dist/public-link-progress.js.map +1 -1
- package/dist/public-tunnel-config.d.ts +5 -0
- package/dist/public-tunnel-config.js +45 -1
- package/dist/public-tunnel-config.js.map +1 -1
- package/dist/public-web-link.js +6 -5
- package/dist/public-web-link.js.map +1 -1
- package/dist/quickstart.js +14 -8
- package/dist/quickstart.js.map +1 -1
- package/dist/resource-report.js +1 -1
- package/dist/scheduler/task-board.d.ts +41 -1
- package/dist/scheduler/task-board.js +150 -4
- package/dist/scheduler/task-board.js.map +1 -1
- package/dist/sd-notify.js +2 -1
- package/dist/sd-notify.js.map +1 -1
- package/dist/service-installer.d.ts +69 -0
- package/dist/service-installer.js +181 -0
- package/dist/service-installer.js.map +1 -1
- package/dist/service-owner.d.ts +44 -0
- package/dist/service-owner.js +48 -0
- package/dist/service-owner.js.map +1 -0
- package/dist/settings-api.d.ts +4 -0
- package/dist/settings-api.js +21 -3
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +85 -2
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-effect.js +2 -0
- package/dist/settings-effect.js.map +1 -1
- package/dist/settings-http-confirmation.d.ts +2 -0
- package/dist/settings-http-confirmation.js +3 -2
- package/dist/settings-http-confirmation.js.map +1 -1
- package/dist/settings-request-capability.d.ts +4 -1
- package/dist/settings-request-capability.js +11 -3
- package/dist/settings-request-capability.js.map +1 -1
- package/dist/setup-host.js +1 -1
- package/dist/setup-wizard.js +1 -1
- package/dist/setup-wizard.js.map +1 -1
- package/dist/task-board-summary.d.ts +101 -0
- package/dist/task-board-summary.js +177 -0
- package/dist/task-board-summary.js.map +1 -0
- package/dist/task-board-waker.d.ts +8 -1
- package/dist/task-board-waker.js +15 -6
- package/dist/task-board-waker.js.map +1 -1
- package/dist/tmux-control.js +1 -1
- package/dist/tmux-control.js.map +1 -1
- package/dist/tmux-exec-helper.d.ts +5 -1
- package/dist/tmux-exec-helper.js +13 -21
- package/dist/tmux-exec-helper.js.map +1 -1
- package/dist/tmux-manager.js +1 -1
- package/dist/tmux-read.js +1 -1
- package/dist/tmux-read.js.map +1 -1
- package/dist/topic-commands.d.ts +3 -0
- package/dist/topic-commands.js +29 -18
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-monitor.js +5 -0
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/transcript-sources.d.ts +2 -0
- package/dist/transcript-sources.js +4 -0
- package/dist/transcript-sources.js.map +1 -1
- package/dist/tunnel/cloudflared-install.js +3 -1
- package/dist/tunnel/cloudflared-install.js.map +1 -1
- package/dist/tunnel/cloudflared.d.ts +1 -1
- package/dist/tunnel/cloudflared.js +51 -10
- package/dist/tunnel/cloudflared.js.map +1 -1
- package/dist/tunnel/named-diagnostics.d.ts +13 -0
- package/dist/tunnel/named-diagnostics.js +52 -0
- package/dist/tunnel/named-diagnostics.js.map +1 -0
- package/dist/tunnel/types.d.ts +2 -1
- package/dist/tunnel/types.js.map +1 -1
- package/dist/types.d.ts +9 -0
- package/dist/ui/app-effects.js +10 -4
- package/dist/ui/chat-strings.js +3 -0
- package/dist/ui/chat-thread.js +14 -7
- package/dist/ui/effect-trial-offer.js +2 -31
- package/dist/ui/effects-waker.js +2 -70
- package/dist/ui/fleet-task-card.js +5 -5
- package/dist/ui/fleet-task-common.js +41 -7
- package/dist/ui/fleet-task-work.js +107 -0
- package/dist/ui/fleet-tasks.js +46 -12
- package/dist/ui/image-lightbox.js +39 -1
- package/dist/ui/image-zoom.js +242 -0
- package/dist/ui/panel-chat.js +24 -13
- package/dist/ui/panel-details.js +2 -0
- package/dist/ui/panel-fleet.js +6 -2
- package/dist/ui/panel-needs.js +21 -6
- package/dist/ui/panel-settings.js +35 -13
- package/dist/ui/preview-panel.js +2 -1
- package/dist/ui/search-dialog.js +2 -1
- package/dist/ui/settings-appearance.js +33 -8
- package/dist/ui/settings-dialogs.js +33 -17
- package/dist/ui/settings-discord.js +6 -2
- package/dist/ui/settings-guests.js +2 -1
- package/dist/ui/settings-model.js +2 -11
- package/dist/ui/settings-mods.js +6 -3
- package/dist/ui/settings-strings.js +2 -0
- package/dist/ui/settings-token.js +3 -1
- package/dist/ui/settings-web-authority.js +6 -3
- package/dist/ui/settings-wizard.js +13 -7
- package/dist/ui/shared/app-i18n.js +2 -2
- package/dist/ui/shared/app-needs.js +2 -2
- package/dist/ui/shared/app-store.js +2 -2
- package/dist/ui/shared/app.css +80 -9
- package/dist/ui/shared/app.js +5 -1
- package/dist/ui/shared/effect-app-runtime.js +7 -5
- package/dist/ui/shared/effect-chat-runtime.js +2 -7
- package/dist/ui/shared/effect-cursor.js +3 -2
- package/dist/ui/shared/effect-display-metadata.js +39 -0
- package/dist/ui/shared/effect-registry.js +4 -2
- package/dist/ui/shared/effect-runtime-loader.js +3 -1
- package/dist/ui/shared/effect-stock-trust.js +1 -1
- package/dist/ui/shared/feature-flags.js +127 -0
- package/dist/ui/shared/identity-color.js +6 -3
- package/dist/ui/shared/instance-nav.js +3 -2
- package/dist/ui/shared/text-undo.js +210 -0
- package/dist/ui/shared/ui-dialog.js +4 -3
- package/dist/ui/shared/ui-icons.js +2 -0
- package/dist/update-dispatch.js +1 -1
- package/dist/update-launch.d.ts +1 -1
- package/dist/update-launch.js +1 -2
- package/dist/update-launch.js.map +1 -1
- package/dist/usage/providers.js +1 -1
- package/dist/usage/providers.js.map +1 -1
- package/dist/view-api.js +1 -1
- package/dist/web-api.d.ts +6 -0
- package/dist/web-api.js +152 -14
- package/dist/web-api.js.map +1 -1
- package/dist/web-appearance-tools.d.ts +1 -0
- package/dist/web-appearance-tools.js +7 -3
- package/dist/web-appearance-tools.js.map +1 -1
- package/dist/web-appearance.d.ts +4 -10
- package/dist/web-appearance.js +4 -3
- package/dist/web-appearance.js.map +1 -1
- package/dist/web-chat-history.d.ts +14 -0
- package/dist/web-chat-history.js +26 -1
- package/dist/web-chat-history.js.map +1 -1
- package/dist/web-experimental-http.d.ts +2 -0
- package/dist/web-experimental-http.js +12 -0
- package/dist/web-experimental-http.js.map +1 -0
- package/dist/web-experimental.d.ts +11 -0
- package/dist/web-experimental.js +49 -0
- package/dist/web-experimental.js.map +1 -0
- package/dist/web-guest-access.d.ts +1 -0
- package/dist/web-guest-access.js +4 -2
- package/dist/web-guest-access.js.map +1 -1
- package/dist/web-mod-store.d.ts +2 -0
- package/dist/web-mod-store.js +10 -0
- package/dist/web-mod-store.js.map +1 -1
- package/dist/web-preview.d.ts +2 -0
- package/dist/web-preview.js +2 -0
- package/dist/web-preview.js.map +1 -1
- package/dist/web-settings-patch.d.ts +2 -1
- package/dist/web-settings-patch.js +5 -1
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/web-settings-tools.d.ts +1 -0
- package/dist/web-settings-tools.js +14 -10
- package/dist/web-settings-tools.js.map +1 -1
- package/dist/web-terminal.js +1 -1
- package/dist/web-theme-store.d.ts +2 -2
- package/dist/web-theme-store.js +6 -3
- package/dist/web-theme-store.js.map +1 -1
- package/package.json +1 -1
|
@@ -166,12 +166,26 @@ function toCard(r) {
|
|
|
166
166
|
seenEntryId: Number(r.seen_entry_id ?? 0),
|
|
167
167
|
};
|
|
168
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* #2352: a lead is a name, or none ("" and null alike). Whether it is one of the fleet's instances is the fleet's to
|
|
171
|
+
* check (the board keeps names, as for an assignee); a lead no longer in fleet.yaml reads as none.
|
|
172
|
+
*/
|
|
173
|
+
function checkLeadInstance(raw) {
|
|
174
|
+
const name = (raw ?? "").trim();
|
|
175
|
+
if (!name)
|
|
176
|
+
return null;
|
|
177
|
+
// eslint-disable-next-line no-control-regex
|
|
178
|
+
if (name.length > 256 || /[\s\u0000-\u001f\u007f]/.test(name))
|
|
179
|
+
throw new TaskBoardError("invalid", "a lead is an instance's name", "lead_not_instance");
|
|
180
|
+
return name;
|
|
181
|
+
}
|
|
169
182
|
function toProject(r) {
|
|
170
183
|
return {
|
|
171
184
|
id: Number(r.id),
|
|
172
185
|
name: r.name,
|
|
173
186
|
description: r.description,
|
|
174
187
|
archived: Number(r.archived) === 1,
|
|
188
|
+
leadInstance: typeof r.lead_instance === "string" && r.lead_instance ? r.lead_instance : null,
|
|
175
189
|
sort: Number(r.sort),
|
|
176
190
|
createdAt: r.created_at,
|
|
177
191
|
};
|
|
@@ -269,6 +283,17 @@ export class TaskBoard {
|
|
|
269
283
|
CREATE INDEX IF NOT EXISTS idx_task_events_task ON task_events(task_id, id);
|
|
270
284
|
CREATE TABLE IF NOT EXISTS task_counters (prefix TEXT PRIMARY KEY, next INTEGER NOT NULL);
|
|
271
285
|
`);
|
|
286
|
+
// #2352: a project's lead instance, added in place (null: its waiting cards' buttons stay in General).
|
|
287
|
+
const projectCols = new Set(this.db.prepare("PRAGMA table_info(task_projects)").all().map((c) => c.name));
|
|
288
|
+
if (!projectCols.has("lead_instance")) {
|
|
289
|
+
try {
|
|
290
|
+
this.db.exec("ALTER TABLE task_projects ADD COLUMN lead_instance TEXT");
|
|
291
|
+
}
|
|
292
|
+
catch (err) {
|
|
293
|
+
if (!/duplicate column/i.test(String(err)))
|
|
294
|
+
throw err;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
272
297
|
// Every open (the CLI opens this file for each command) only reads: the write lock is taken only when a counter
|
|
273
298
|
// is missing or a row needs a number. (CREATE … IF NOT EXISTS of what exists takes no lock; an INSERT does.)
|
|
274
299
|
if (this.db.prepare("SELECT COUNT(*) AS n FROM task_counters WHERE prefix IN ('M', 'S', 'R')").get().n < 3) {
|
|
@@ -330,8 +355,9 @@ export class TaskBoard {
|
|
|
330
355
|
const name = this.checkProjectName(input.name, null);
|
|
331
356
|
const description = checkText("the description", input.description ?? "", CARD_LIMITS.projectDescription);
|
|
332
357
|
const { s } = this.db.prepare("SELECT COALESCE(MAX(sort), 0) + 1 AS s FROM task_projects").get();
|
|
333
|
-
const
|
|
334
|
-
|
|
358
|
+
const lead = checkLeadInstance(input.leadInstance ?? null);
|
|
359
|
+
const row = this.db.prepare("INSERT INTO task_projects (name, description, sort, created_at, lead_instance) VALUES (?, ?, ?, ?, ?) RETURNING *")
|
|
360
|
+
.get(name, description, s, this.nowIso(), lead);
|
|
335
361
|
this.projectsChanged = true;
|
|
336
362
|
return toProject(row);
|
|
337
363
|
});
|
|
@@ -346,8 +372,10 @@ export class TaskBoard {
|
|
|
346
372
|
project.description = checkText("the description", patch.description, CARD_LIMITS.projectDescription);
|
|
347
373
|
if (patch.archived !== undefined)
|
|
348
374
|
project.archived = patch.archived;
|
|
349
|
-
|
|
350
|
-
|
|
375
|
+
if (patch.leadInstance !== undefined)
|
|
376
|
+
project.leadInstance = checkLeadInstance(patch.leadInstance);
|
|
377
|
+
this.db.prepare("UPDATE task_projects SET name = ?, description = ?, archived = ?, lead_instance = ? WHERE id = ?")
|
|
378
|
+
.run(project.name, project.description, project.archived ? 1 : 0, project.leadInstance, project.id);
|
|
351
379
|
this.projectsChanged = true;
|
|
352
380
|
return project;
|
|
353
381
|
});
|
|
@@ -733,6 +761,11 @@ export class TaskBoard {
|
|
|
733
761
|
* (its own open cards, and the unassigned sub tasks of main tasks it coordinates, dependencies done) and what waits
|
|
734
762
|
* on dependencies; comments it has not seen and work sent back; the main tasks it coordinates and how far along.
|
|
735
763
|
*/
|
|
764
|
+
/** Who holds a live card (open, in progress, blocked, waiting for the person): the instances, and any other name. */
|
|
765
|
+
liveAssignees() {
|
|
766
|
+
return this.db.prepare("SELECT DISTINCT assignee FROM tasks WHERE assignee IS NOT NULL AND status IN ('open', 'claimed', 'blocked', 'review') ORDER BY assignee").all()
|
|
767
|
+
.map((r) => r.assignee);
|
|
768
|
+
}
|
|
736
769
|
myWork(name) {
|
|
737
770
|
const mine = this.db.prepare(`SELECT * FROM tasks WHERE assignee = ? AND status IN ('open', 'claimed', 'blocked', 'review') ORDER BY ${STATUS_ORDER}, ${PRIORITY_ORDER}, sort`)
|
|
738
771
|
.all(name).map(toCard);
|
|
@@ -766,6 +799,113 @@ export class TaskBoard {
|
|
|
766
799
|
mains: coordinating.map((card) => ({ card, progress: this.progress(card) })),
|
|
767
800
|
};
|
|
768
801
|
}
|
|
802
|
+
/**
|
|
803
|
+
* What waits for the person (step 3: Needs you): cards in review that need their confirmation, and blocked cards —
|
|
804
|
+
* oldest first, each with when it started waiting (its last move to that status).
|
|
805
|
+
*/
|
|
806
|
+
waitingForPerson() {
|
|
807
|
+
const rows = this.db.prepare("SELECT * FROM tasks WHERE (status = 'review' AND needs_review = 1) OR status = 'blocked' ORDER BY updated_at").all().map(toCard);
|
|
808
|
+
const moved = this.db.prepare("SELECT at FROM task_events WHERE task_id = ? AND type = 'moved' AND json_extract(data, '$.to') = ? ORDER BY id DESC LIMIT 1");
|
|
809
|
+
return rows.map((card) => {
|
|
810
|
+
const at = moved.get(card.uuid, card.status)?.at;
|
|
811
|
+
return { card, since: at ?? card.submittedAt ?? card.updatedAt };
|
|
812
|
+
}).sort((a, b) => a.since.localeCompare(b.since));
|
|
813
|
+
}
|
|
814
|
+
/** Cards by their numbers, in one read (the ones that exist; #2341 review: the cards a summary line named). */
|
|
815
|
+
cardsByCode(codes) {
|
|
816
|
+
const out = [];
|
|
817
|
+
for (let i = 0; i < codes.length; i += 500) {
|
|
818
|
+
const chunk = codes.slice(i, i + 500);
|
|
819
|
+
out.push(...this.db.prepare(`SELECT * FROM tasks WHERE code IN (${chunk.map(() => "?").join(", ")})`).all(...chunk).map(toCard));
|
|
820
|
+
}
|
|
821
|
+
return out;
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* myWork() for many names in one read (the dashboard's Instances list, GET /ui/tasks/work): the live cards, every
|
|
825
|
+
* card's light row (sub tasks, dependencies) and the live cards' unseen entries, each read once — three statements
|
|
826
|
+
* however many names and cards (#2316 review: N myWork() calls cost tens of ms on the fleet's event loop). Same
|
|
827
|
+
* answers as myWork() name by name; tests/task-board-work-2316.test.ts pins both.
|
|
828
|
+
*/
|
|
829
|
+
workOf(names) {
|
|
830
|
+
const out = new Map();
|
|
831
|
+
if (!names.length)
|
|
832
|
+
return out;
|
|
833
|
+
const live = this.db.prepare(`SELECT * FROM tasks WHERE status IN ('open', 'claimed', 'blocked', 'review') ORDER BY ${STATUS_ORDER}, ${PRIORITY_ORDER}, sort`).all().map(toCard);
|
|
834
|
+
const all = new Map();
|
|
835
|
+
const subs = new Map();
|
|
836
|
+
for (const r of this.db.prepare("SELECT id, code, status, depends_on, parent_id FROM tasks").all()) {
|
|
837
|
+
const light = { code: r.code ?? r.id.slice(0, 8), status: r.status, deps: parseJsonArray(r.depends_on, []), parent: r.parent_id ?? null };
|
|
838
|
+
all.set(r.id, light);
|
|
839
|
+
if (light.parent && light.status !== "cancelled") {
|
|
840
|
+
const p = subs.get(light.parent) ?? { total: 0, open: 0, claimed: 0, blocked: 0, review: 0, done: 0 };
|
|
841
|
+
p.total++;
|
|
842
|
+
p[light.status]++;
|
|
843
|
+
subs.set(light.parent, p);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
// Unseen entries of the live cards: comments and rejections by anyone but the card's assignee, after what it saw.
|
|
847
|
+
const fresh = new Map();
|
|
848
|
+
for (const r of this.db.prepare(`SELECT e.* FROM task_entries e JOIN tasks t ON e.task_id = t.id
|
|
849
|
+
WHERE t.status IN ('open', 'claimed', 'blocked', 'review') AND e.id > t.seen_entry_id AND e.kind IN ('comment', 'reject') AND e.author IS NOT t.assignee ORDER BY e.id`).all()) {
|
|
850
|
+
const list = fresh.get(r.task_id) ?? [];
|
|
851
|
+
list.push({ id: Number(r.id), cardId: all.get(r.task_id)?.code ?? r.task_id.slice(0, 8), kind: r.kind, author: r.author,
|
|
852
|
+
authorKind: r.author_kind, text: r.text, at: r.at });
|
|
853
|
+
fresh.set(r.task_id, list);
|
|
854
|
+
}
|
|
855
|
+
/** unmetDeps(), on the light rows: own dependencies that exist and are not done, then the main task's not yet named. */
|
|
856
|
+
const unmet = (deps, parent) => {
|
|
857
|
+
const own = deps.filter((d) => { const dep = all.get(d); return !!dep && dep.status !== "done"; });
|
|
858
|
+
const main = parent ? all.get(parent) : undefined;
|
|
859
|
+
return main ? [...own, ...unmet(main.deps, main.parent).filter((d) => !own.includes(d))] : own;
|
|
860
|
+
};
|
|
861
|
+
const rank = { urgent: 0, high: 1, normal: 2 };
|
|
862
|
+
const byPriority = (a, b) => (rank[a.priority] ?? 3) - (rank[b.priority] ?? 3) || a.sort - b.sort;
|
|
863
|
+
const openUnassigned = live.filter((c) => c.assignee === null && c.status === "open");
|
|
864
|
+
for (const name of new Set(names)) {
|
|
865
|
+
const mine = live.filter((c) => c.assignee === name);
|
|
866
|
+
const coordinating = mine.filter((c) => c.kind === "main" && (subs.get(c.uuid)?.total ?? 0) > 0);
|
|
867
|
+
const work = mine.filter((c) => !coordinating.includes(c));
|
|
868
|
+
const theirs = new Set(coordinating.map((c) => c.uuid));
|
|
869
|
+
const unassigned = coordinating.length ? openUnassigned.filter((c) => c.parent !== null && theirs.has(c.parent)).sort(byPriority) : [];
|
|
870
|
+
const candidates = [...work.filter((c) => c.status === "open"), ...unassigned].map((card) => ({ card, waitsFor: unmet(card.deps, card.parent).map((d) => all.get(d).code) }));
|
|
871
|
+
const newComments = [];
|
|
872
|
+
const sentBack = [];
|
|
873
|
+
for (const card of mine) {
|
|
874
|
+
const entries = fresh.get(card.uuid) ?? [];
|
|
875
|
+
const reject = [...entries].reverse().find((e) => e.kind === "reject");
|
|
876
|
+
if (reject)
|
|
877
|
+
sentBack.push({ card, entry: reject });
|
|
878
|
+
const comments = entries.filter((e) => e.kind === "comment");
|
|
879
|
+
if (comments.length)
|
|
880
|
+
newComments.push({ card, entries: comments });
|
|
881
|
+
}
|
|
882
|
+
out.set(name, {
|
|
883
|
+
doing: work.filter((c) => c.status === "claimed"),
|
|
884
|
+
blocked: work.filter((c) => c.status === "blocked"),
|
|
885
|
+
review: work.filter((c) => c.status === "review"),
|
|
886
|
+
ready: candidates.filter((x) => !x.waitsFor.length).map((x) => x.card),
|
|
887
|
+
waiting: candidates.filter((x) => x.waitsFor.length),
|
|
888
|
+
newComments,
|
|
889
|
+
sentBack,
|
|
890
|
+
mains: coordinating.map((card) => ({ card, progress: { ...subs.get(card.uuid) } })),
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
return out;
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Step 3, the daily summary (task-board-summary.ts): what waits for the person now, what got done and what was
|
|
897
|
+
* opened in [since, until) (not cancelled), and every live main task's project and assignee.
|
|
898
|
+
*/
|
|
899
|
+
summaryRead(since, until) {
|
|
900
|
+
const between = (sql) => this.db.prepare(sql).all(since, until).map(toCard);
|
|
901
|
+
return {
|
|
902
|
+
waiting: this.waitingForPerson().map((w) => w.card),
|
|
903
|
+
done: between("SELECT * FROM tasks WHERE status = 'done' AND done_at >= ? AND done_at < ? ORDER BY sort"),
|
|
904
|
+
created: between("SELECT * FROM tasks WHERE status != 'cancelled' AND created_at >= ? AND created_at < ? ORDER BY sort"),
|
|
905
|
+
mains: this.db.prepare("SELECT project_id, assignee FROM tasks WHERE kind = 'main' AND status != 'cancelled'").all()
|
|
906
|
+
.map((r) => ({ projectId: r.project_id ?? null, assignee: r.assignee ?? null })),
|
|
907
|
+
};
|
|
908
|
+
}
|
|
769
909
|
// ── Writing: the board ──────────────────────────────────
|
|
770
910
|
createCards(actor, input) {
|
|
771
911
|
if (input.cards.length === 0)
|
|
@@ -1683,6 +1823,9 @@ export class TaskBoard {
|
|
|
1683
1823
|
add(card, card.assignee, "cancelled", e.actor, why);
|
|
1684
1824
|
continue;
|
|
1685
1825
|
}
|
|
1826
|
+
// Its dispatcher tells the person it waits for them (not the board).
|
|
1827
|
+
if (d.to === "review" && !isPerson(e.actor) && requiresReview(card))
|
|
1828
|
+
add(card, dispatcherOf(), "to_confirm", e.actor);
|
|
1686
1829
|
if (d.auto) {
|
|
1687
1830
|
if (d.to === "review" && card.kind === "main" && wrapUpGap(card))
|
|
1688
1831
|
add(card, card.assignee, "wrap_up", e.actor);
|
|
@@ -1737,6 +1880,9 @@ export class TaskBoard {
|
|
|
1737
1880
|
return false;
|
|
1738
1881
|
if (r === "decide")
|
|
1739
1882
|
return handedOut && card.status === "blocked";
|
|
1883
|
+
// #2341 review: and still waiting for the person — its confirmation not switched off during the gather.
|
|
1884
|
+
if (r === "to_confirm")
|
|
1885
|
+
return handedOut && card.status === "review" && requiresReview(card);
|
|
1740
1886
|
if (!own)
|
|
1741
1887
|
return false;
|
|
1742
1888
|
if (r === "wrap_up")
|