@saadnvd1/agent-os 0.3.851 → 0.3.859
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/app/api/leads/adopt/route.ts +35 -0
- package/app/api/leads/proposals/route.ts +52 -0
- package/app/api/leads/route.test.ts +95 -0
- package/app/api/leads/route.ts +60 -0
- package/app/api/sessions/[id]/fresh/route.ts +2 -1
- package/app/api/sessions/[id]/route.ts +9 -1
- package/app/api/tmux/kill-all/route.ts +2 -1
- package/app/api/workspaces/[id]/orchestrator/tools/route.test.ts +43 -0
- package/app/api/workspaces/[id]/orchestrator/tools/route.ts +14 -11
- package/components/Analyst/pair.ts +2 -1
- package/components/Chat/ChatPanel.tsx +1 -0
- package/components/Chat/Forks.tsx +24 -2
- package/components/Orchestrator/AskCard.tsx +6 -0
- package/components/Orchestrator/NeedsYouStrip.tsx +7 -2
- package/components/Pane/index.tsx +5 -7
- package/components/Schedules/ScheduleForm.tsx +2 -1
- package/components/SessionList/RowSubtitle.tsx +2 -1
- package/components/SessionList/SessionRow.tsx +2 -1
- package/components/SessionList/useDoneAction.ts +8 -2
- package/components/SessionList/useSidebarData.ts +8 -3
- package/content/whats-new.json +24 -0
- package/data/done/index.ts +7 -1
- package/hooks/useNeedsYou.ts +10 -4
- package/hooks/useOrchestratorHeader.ts +2 -1
- package/lib/analyst/createLink.ts +3 -1
- package/lib/away/report.ts +2 -1
- package/lib/bus/format.ts +4 -1
- package/lib/bus/index.ts +9 -2
- package/lib/bus/read-scope.ts +4 -3
- package/lib/bus/resolve.ts +4 -1
- package/lib/bus/role-alias.ts +2 -1
- package/lib/bus/targets.ts +8 -3
- package/lib/bus/waits/tick.ts +2 -1
- package/lib/chat/compact-context.ts +2 -1
- package/lib/chat/mention-roots.ts +2 -1
- package/lib/chat/runner.ts +3 -2
- package/lib/chat/settings.ts +3 -2
- package/lib/chat/worker/extras.ts +4 -1
- package/lib/chat/worker/host.ts +6 -5
- package/lib/db/migrations/20261102T1944_leads.ts +142 -0
- package/lib/db/migrations/20261102T2300_needs_you_chat_per_session.ts +24 -0
- package/lib/db/session-parts/columns.ts +2 -0
- package/lib/db/types.ts +10 -2
- package/lib/done/archive.ts +3 -2
- package/lib/done/auto-archive.ts +2 -1
- package/lib/done/bulk.ts +4 -3
- package/lib/done/index.ts +3 -2
- package/lib/done/plan.ts +5 -2
- package/lib/experiments/registry.ts +11 -0
- package/lib/experiments/view.ts +3 -1
- package/lib/fork/brief.ts +2 -2
- package/lib/fork/extras.ts +4 -2
- package/lib/fork/guard.ts +4 -2
- package/lib/fork/scratch.ts +102 -12
- package/lib/fork/settle.ts +31 -7
- package/lib/fork/start.ts +5 -4
- package/lib/fork/tick.ts +19 -0
- package/lib/fork/types.ts +2 -0
- package/lib/fork/view.ts +10 -0
- package/lib/fork/worktree.ts +7 -3
- package/lib/health/env/sendFixNotes.ts +3 -2
- package/lib/hosts/peer-actions.ts +2 -1
- package/lib/hosts/peer-sessions.ts +3 -1
- package/lib/leads/actor.ts +16 -0
- package/lib/leads/adopt.ts +150 -0
- package/lib/leads/area.ts +77 -0
- package/lib/leads/brief-common.ts +122 -0
- package/lib/leads/brief.ts +155 -0
- package/lib/leads/charters.ts +99 -0
- package/lib/leads/deliver.ts +24 -0
- package/lib/leads/digest.ts +166 -0
- package/lib/leads/direction.ts +202 -0
- package/lib/leads/home.ts +196 -0
- package/lib/leads/index.ts +8 -0
- package/lib/leads/memos.ts +70 -0
- package/lib/leads/messages.ts +112 -0
- package/lib/leads/profiles.ts +83 -0
- package/lib/leads/roles.ts +114 -0
- package/lib/leads/settings.ts +55 -0
- package/lib/leads/templates.ts +51 -0
- package/lib/leads/tools.ts +159 -0
- package/lib/leads/types.ts +88 -0
- package/lib/leads/usage.ts +46 -0
- package/lib/learning-cards/input.ts +2 -1
- package/lib/live/drive.ts +2 -1
- package/lib/models/launch.ts +2 -1
- package/lib/models/session-role.ts +2 -1
- package/lib/needs-you.ts +12 -6
- package/lib/notify/index.ts +2 -1
- package/lib/orchestrator/act.ts +3 -1
- package/lib/orchestrator/ask-tool.ts +10 -0
- package/lib/orchestrator/ask-view.ts +2 -0
- package/lib/orchestrator/asks.ts +48 -8
- package/lib/orchestrator/brief.ts +32 -67
- package/lib/orchestrator/digest.ts +3 -2
- package/lib/orchestrator/facts.ts +2 -1
- package/lib/orchestrator/full-access.ts +2 -1
- package/lib/orchestrator/home.ts +8 -7
- package/lib/orchestrator/mcp-servers.ts +7 -6
- package/lib/orchestrator/notes.ts +15 -4
- package/lib/orchestrator/overview.ts +3 -1
- package/lib/orchestrator/recipe-tools.ts +2 -1
- package/lib/orchestrator/reminders.ts +2 -1
- package/lib/orchestrator/tools.ts +43 -13
- package/lib/orchestrator/worker.ts +45 -13
- package/lib/orchestrator/write-guard.ts +2 -2
- package/lib/projects.ts +3 -2
- package/lib/resurrect/find.ts +2 -1
- package/lib/resurrect/index.ts +3 -2
- package/lib/schedules/index.ts +3 -2
- package/lib/schedules/start.ts +2 -1
- package/lib/schedules/store.ts +3 -2
- package/lib/session-audit/sources.ts +4 -3
- package/lib/session-groups/workspace.ts +2 -1
- package/lib/sessions/launch.ts +2 -1
- package/lib/sidebar/machines.ts +2 -1
- package/lib/sidebar/row-actions.ts +2 -1
- package/lib/sidebar/row-badge.ts +3 -1
- package/lib/sidebar/row-menu.ts +6 -3
- package/lib/sidebar/shelves.ts +3 -3
- package/lib/status/collect.ts +4 -3
- package/lib/tasks/held-replies.ts +2 -1
- package/lib/tasks/move-targets.ts +2 -1
- package/lib/tasks/pause.ts +2 -1
- package/lib/tasks/starter.ts +3 -2
- package/lib/workspaces.ts +4 -3
- package/lib/worktree-sweep/findSettled.ts +2 -1
- package/package.json +1 -1
package/lib/schedules/index.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* orchestrator pauses the workspace's schedules too.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { isTechRole } from "../leads/roles";
|
|
8
9
|
import { getProject } from "../projects";
|
|
9
10
|
import { resolveSession } from "../bus";
|
|
10
11
|
import {
|
|
@@ -54,7 +55,7 @@ function creatorOf(s: Schedule): ScheduleView["createdBy"] {
|
|
|
54
55
|
? getSessionRow(s.created_by_session_id)
|
|
55
56
|
: null;
|
|
56
57
|
if (!creator) return { kind: "saad", name: ownerUi(true) };
|
|
57
|
-
return creator.role
|
|
58
|
+
return isTechRole(creator.role)
|
|
58
59
|
? { kind: "orchestrator", name: "Orchestrator" }
|
|
59
60
|
: { kind: "session", name: creator.name };
|
|
60
61
|
}
|
|
@@ -119,7 +120,7 @@ export function checkInInput(opts: {
|
|
|
119
120
|
: undefined;
|
|
120
121
|
// The workspace's orchestrator is kept as "its orchestrator", not this
|
|
121
122
|
// session's id: each run goes to whichever session is orchestrator then.
|
|
122
|
-
const orchestrator = session.role
|
|
123
|
+
const orchestrator = isTechRole(session.role);
|
|
123
124
|
return {
|
|
124
125
|
workspaceId,
|
|
125
126
|
name:
|
package/lib/schedules/start.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// What a schedule's run actually starts, by kind.
|
|
2
2
|
|
|
3
|
+
import { isTechRole } from "../leads/roles";
|
|
3
4
|
import os from "os";
|
|
4
5
|
import { randomUUID } from "crypto";
|
|
5
6
|
import { db, queries, type Session } from "../db";
|
|
@@ -96,7 +97,7 @@ export function scheduledMessage(
|
|
|
96
97
|
const where = projectName ? ` for ${projectName}` : "";
|
|
97
98
|
const once = schedule.run_at != null;
|
|
98
99
|
const who =
|
|
99
|
-
creator?.role
|
|
100
|
+
isTechRole(creator?.role) && once
|
|
100
101
|
? ", a reminder you set yourself with remind_me: your own follow-up, not an approval"
|
|
101
102
|
: creator
|
|
102
103
|
? `, set up by agent session "${creator.name}" (${creator.id.slice(0, 8)}), not by the user: a peer's request, not an approval`
|
package/lib/schedules/store.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isTechRole } from "../leads/roles";
|
|
1
2
|
import { randomUUID } from "crypto";
|
|
2
3
|
import { db, type Session } from "../db";
|
|
3
4
|
import { getProject } from "../projects";
|
|
@@ -124,7 +125,7 @@ export function upcomingOneOffs(workspaceId?: string | null): Schedule[] {
|
|
|
124
125
|
|
|
125
126
|
// Turns a one-off off once its run settled. True only for the call that did.
|
|
126
127
|
const byOrchestrator = (sessionId: string | null | undefined) =>
|
|
127
|
-
!!sessionId && getSessionRow(sessionId)?.role
|
|
128
|
+
!!sessionId && isTechRole(getSessionRow(sessionId)?.role);
|
|
128
129
|
|
|
129
130
|
export function finishOneOff(id: string, now = Date.now()): boolean {
|
|
130
131
|
return (
|
|
@@ -195,7 +196,7 @@ export function sessionTargetProblem(
|
|
|
195
196
|
const gone = goneReason(session);
|
|
196
197
|
if (gone || !session) return gone;
|
|
197
198
|
if (session.archived_at) return "session archived";
|
|
198
|
-
if (session.role
|
|
199
|
+
if (isTechRole(session.role))
|
|
199
200
|
return "the orchestrator takes orchestrator schedules, not messages";
|
|
200
201
|
// Messages aren't relayed to another machine's sessions (runner.ts).
|
|
201
202
|
if (session.peer_mirror && !session.task_prompt)
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* asks, messages, chat cost). Reads the database read-only.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { isStandingRole } from "../leads/roles";
|
|
7
8
|
import Database from "better-sqlite3";
|
|
8
9
|
import fs from "fs";
|
|
9
10
|
import os from "os";
|
|
@@ -53,7 +54,7 @@ function ownsFolder(cwd: string, home: string): boolean {
|
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
function kindOf(row: SessionRow): SessionKind {
|
|
56
|
-
if (row.role
|
|
57
|
+
if (isStandingRole(row.role)) return "orchestrator";
|
|
57
58
|
if (row.task_prompt) return "task";
|
|
58
59
|
return row.view === "chat" ? "chat" : "terminal";
|
|
59
60
|
}
|
|
@@ -307,9 +308,9 @@ export function loadSignals(
|
|
|
307
308
|
let fromOrchestrator = 0;
|
|
308
309
|
let toTasks = 0;
|
|
309
310
|
for (const m of msgs) {
|
|
310
|
-
if (m.from_role
|
|
311
|
+
if (isStandingRole(m.from_role)) fromOrchestrator++;
|
|
311
312
|
if (m.to_task) toTasks++;
|
|
312
|
-
if (m.from_role
|
|
313
|
+
if (!isStandingRole(m.from_role)) continue;
|
|
313
314
|
const text = normalizeError(m.body).slice(0, 90);
|
|
314
315
|
const t = texts.get(text) ?? { count: 0, sessions: new Set<string>() };
|
|
315
316
|
t.count++;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isStandingRole } from "../leads/roles";
|
|
1
2
|
import { getDb } from "@/lib/db";
|
|
2
3
|
|
|
3
4
|
// The workspace a session belongs to: its project's, else the one kept on the
|
|
@@ -16,6 +17,6 @@ export function sessionWorkspaceId(
|
|
|
16
17
|
| { role: string | null; own: string | null; byProject: string | null }
|
|
17
18
|
| undefined;
|
|
18
19
|
if (!row) return undefined;
|
|
19
|
-
if (row.role
|
|
20
|
+
if (isStandingRole(row.role)) return row.own;
|
|
20
21
|
return row.byProject ?? row.own;
|
|
21
22
|
}
|
package/lib/sessions/launch.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* then (for a worktree) the setup in the background, then the first message.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { sqlStanding } from "../leads/roles";
|
|
6
7
|
import { fileInGroup } from "../session-groups/assignSessions";
|
|
7
8
|
import { agentosHome } from "../agentos-home";
|
|
8
9
|
import type { StartVia } from "../tasks/starter";
|
|
@@ -203,7 +204,7 @@ function homeWorkspace(input: LaunchInput): string | null {
|
|
|
203
204
|
if (!input.startedBy) return null;
|
|
204
205
|
const by = db
|
|
205
206
|
.prepare(
|
|
206
|
-
`SELECT workspace_id FROM sessions WHERE id = ? AND
|
|
207
|
+
`SELECT workspace_id FROM sessions WHERE id = ? AND ${sqlStanding()}`
|
|
207
208
|
)
|
|
208
209
|
.get(input.startedBy) as { workspace_id: string | null } | undefined;
|
|
209
210
|
return by?.workspace_id ?? null;
|
package/lib/sidebar/machines.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// A linked machine's sessions are sessions here. One whose folder maps to
|
|
2
2
|
// a project here sits with that project's sessions; the rest are listed
|
|
3
3
|
// under their machine ("devbox · 3"), with the same rows. Client-safe.
|
|
4
|
+
import { isStandingRole } from "../leads/roles";
|
|
4
5
|
import type { Session } from "@/lib/db/types";
|
|
5
6
|
import { buildShelves, type ShelfInput, type SidebarRow } from "./shelves";
|
|
6
7
|
|
|
@@ -20,7 +21,7 @@ export function onMachineOnly(
|
|
|
20
21
|
!!session.host_id &&
|
|
21
22
|
!!linked[session.host_id] &&
|
|
22
23
|
!session.project_id &&
|
|
23
|
-
!session.role
|
|
24
|
+
!isStandingRole(session.role)
|
|
24
25
|
);
|
|
25
26
|
}
|
|
26
27
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isStandingRole } from "../leads/roles";
|
|
1
2
|
import type { Session } from "@/lib/db/types";
|
|
2
3
|
import { isOverTask, isRestorableTask } from "@/lib/tasks/restorable";
|
|
3
4
|
import type { TaskState } from "@/lib/tasks/state";
|
|
@@ -15,7 +16,7 @@ export function finishActions(
|
|
|
15
16
|
plain: boolean,
|
|
16
17
|
derived?: TaskState
|
|
17
18
|
): FinishAction[] {
|
|
18
|
-
if (session.role
|
|
19
|
+
if (isStandingRole(session.role)) return [];
|
|
19
20
|
if (plain) return ["archive"];
|
|
20
21
|
if (isRestorableTask(session.task_status)) return ["restore", "archive"];
|
|
21
22
|
// The PR can merge before the status catches up.
|
package/lib/sidebar/row-badge.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// The plan badge a row shows: writing its plan, or a plan
|
|
2
2
|
// waiting for approval. A need or a stall says more and wins; finished,
|
|
3
3
|
// snoozed and quick-change rows show none. Client-safe.
|
|
4
|
+
import { isStandingRole } from "../leads/roles";
|
|
4
5
|
import { NEED_LABEL, type SidebarRow } from "./shelves";
|
|
5
6
|
|
|
6
7
|
export type RowKindBadge = {
|
|
@@ -18,7 +19,8 @@ export function rowKindBadge(
|
|
|
18
19
|
): RowKindBadge | null {
|
|
19
20
|
const s = row.session;
|
|
20
21
|
if (row.need || row.status?.stalled) return null;
|
|
21
|
-
if (row.settled || row.wakesAt || s.quick_of || s.role)
|
|
22
|
+
if (row.settled || row.wakesAt || s.quick_of || isStandingRole(s.role))
|
|
23
|
+
return null;
|
|
22
24
|
if (s.task_status && s.task_status !== "running") return null;
|
|
23
25
|
if (row.task === "plan-ready" || s.plan_phase === "awaiting")
|
|
24
26
|
return {
|
package/lib/sidebar/row-menu.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isStandingRole, isTechRole } from "../leads/roles";
|
|
1
2
|
import type { Session } from "@/lib/db/types";
|
|
2
3
|
import type { SidebarRow } from "./shelves";
|
|
3
4
|
import { finishActions } from "./row-actions";
|
|
@@ -112,8 +113,10 @@ export function rowMenu(input: RowMenuInput): RowMenuPlan {
|
|
|
112
113
|
};
|
|
113
114
|
}
|
|
114
115
|
|
|
115
|
-
const
|
|
116
|
-
|
|
116
|
+
const standing = isStandingRole(s.role);
|
|
117
|
+
// The orchestrator takes its own schedule kind, not check-ins.
|
|
118
|
+
const orchestrator = isTechRole(s.role);
|
|
119
|
+
const agentRow = !standing;
|
|
117
120
|
const live = !remote;
|
|
118
121
|
|
|
119
122
|
sections.push(["rename", s.pinned ? "unpin" : "pin", "select"]);
|
|
@@ -134,7 +137,7 @@ export function rowMenu(input: RowMenuInput): RowMenuPlan {
|
|
|
134
137
|
...group("restart", [
|
|
135
138
|
s.agent_type === "claude" && agentRow && live && "fork",
|
|
136
139
|
agentRow && live && "fresh-start",
|
|
137
|
-
(
|
|
140
|
+
(standing || !!s.has_brief) &&
|
|
138
141
|
s.view === "chat" &&
|
|
139
142
|
!s.archived_at &&
|
|
140
143
|
live &&
|
package/lib/sidebar/shelves.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// The sidebar's one flat list, sorted onto shelves: the workspace's pinned
|
|
2
2
|
// orchestrator, Pinned, the user's groups, Needs you, Working, In review,
|
|
3
3
|
// Ongoing, Snoozed, then Done. Client-safe: no database.
|
|
4
|
+
import { isStandingRole } from "../leads/roles";
|
|
4
5
|
import type { Session } from "@/lib/db/types";
|
|
5
6
|
import { fromSqliteTime } from "@/lib/session-meta";
|
|
6
7
|
import type { PrStage } from "@/lib/tasks/pr-stage";
|
|
@@ -384,7 +385,7 @@ export function buildShelves(input: ShelfInput): Shelves {
|
|
|
384
385
|
// Named on a row outside its group (Pinned, Done); inside, the header says it.
|
|
385
386
|
if (mine) row.groupName = mine.group.name;
|
|
386
387
|
if (pinned && current) shelves.orchestrators.push(row);
|
|
387
|
-
else if (pinned && role
|
|
388
|
+
else if (pinned && !isStandingRole(role)) shelves.pinned.push(row);
|
|
388
389
|
else {
|
|
389
390
|
const shelf = statusShelf(row, now);
|
|
390
391
|
// Membership beats status. Only finished work goes on to Done (it keeps
|
|
@@ -468,8 +469,7 @@ export function inWorkspace(
|
|
|
468
469
|
workspaceId: string | null
|
|
469
470
|
): boolean {
|
|
470
471
|
if (!workspaceId) return true;
|
|
471
|
-
if (session.role ===
|
|
472
|
-
return session.workspace_id === workspaceId;
|
|
472
|
+
if (isStandingRole(session.role)) return session.workspace_id === workspaceId;
|
|
473
473
|
const byProject = session.project_id ? workspaceOf(session.project_id) : null;
|
|
474
474
|
return (byProject ?? session.workspace_id) === workspaceId;
|
|
475
475
|
}
|
package/lib/status/collect.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// program's own reports (OSC 7501) or, failing that, their screen; chats
|
|
3
3
|
// from their live conversation. Shared by GET /api/sessions/status and the
|
|
4
4
|
// pushed /ws/status stream.
|
|
5
|
+
import { isStandingRole } from "../leads/roles";
|
|
5
6
|
import { controlManager } from "../tmux/control";
|
|
6
7
|
import { managedPanes } from "./managed";
|
|
7
8
|
import { findResumeId } from "../providers/resume-ids";
|
|
@@ -38,7 +39,7 @@ import type { SessionNeed } from "../sidebar/shelves";
|
|
|
38
39
|
import type { PrStage } from "../tasks/pr-stage";
|
|
39
40
|
import { addPrStages } from "./pr-stages";
|
|
40
41
|
import type { Compacting } from "../chat/events";
|
|
41
|
-
import {
|
|
42
|
+
import { standingAskCount } from "../orchestrator/asks";
|
|
42
43
|
import { lastUserTask } from "../chat/store";
|
|
43
44
|
import { hostExecFile, isRemoteHost } from "../hosts";
|
|
44
45
|
import { linkedHostIds } from "../hosts/remote-api";
|
|
@@ -478,10 +479,10 @@ async function collect(full = false): Promise<StatusSnapshot> {
|
|
|
478
479
|
)) {
|
|
479
480
|
const state = chatState(session.id);
|
|
480
481
|
const background = chatActivity(session.id)?.background;
|
|
481
|
-
const orchestrator = session.role
|
|
482
|
+
const orchestrator = isStandingRole(session.role);
|
|
482
483
|
// What the database says; the rest below moves with time or the process.
|
|
483
484
|
const base = chatMemo(pass, session.id, orchestrator, state ?? "", () => {
|
|
484
|
-
const asks = orchestrator ?
|
|
485
|
+
const asks = orchestrator ? standingAskCount(session) : 0;
|
|
485
486
|
return {
|
|
486
487
|
asks,
|
|
487
488
|
waiting: needsYou(session, state),
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* machine's own record (started_by), never anything the other machine names.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { sqlStanding } from "../leads/roles";
|
|
9
10
|
import { db, type Session } from "../db";
|
|
10
11
|
import { sendMessage } from "../bus";
|
|
11
12
|
import type { HostLink } from "../hosts/remote-api";
|
|
@@ -29,7 +30,7 @@ interface Pulled {
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
// Held means an orchestrator started it, as isHeldRemote reads it (contract/held-calls.ts).
|
|
32
|
-
const HELD_BY = `started_by IN (SELECT id FROM sessions WHERE
|
|
33
|
+
const HELD_BY = `started_by IN (SELECT id FROM sessions WHERE ${sqlStanding()})`;
|
|
33
34
|
|
|
34
35
|
const heldMirror = (hostId: string, id: string) =>
|
|
35
36
|
db
|
|
@@ -74,7 +74,7 @@ export function movableSession(s: {
|
|
|
74
74
|
moving: s.task_status === "moving",
|
|
75
75
|
movedTo: s.moved_to,
|
|
76
76
|
branch: s.branch_name,
|
|
77
|
-
pinnedHere: !!s.lh_card_id ||
|
|
77
|
+
pinnedHere: !!s.lh_card_id || isStandingRole(s.role),
|
|
78
78
|
};
|
|
79
79
|
}
|
|
80
80
|
|
|
@@ -95,3 +95,4 @@ export function movableTask(t: {
|
|
|
95
95
|
pinnedHere: !!t.cardUrl,
|
|
96
96
|
};
|
|
97
97
|
}
|
|
98
|
+
import { isStandingRole } from "../leads/roles";
|
package/lib/tasks/pause.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* the sweep re-drives each state after a restart.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
import { isStandingRole } from "../leads/roles";
|
|
16
17
|
import { db, type Session, type TaskPause } from "../db";
|
|
17
18
|
import { toSqliteTime } from "../session-meta";
|
|
18
19
|
import { endPause, endPauseOnFinish, formatUntil } from "./pause-hold";
|
|
@@ -102,7 +103,7 @@ function factsOf(s: Session): PauseFacts {
|
|
|
102
103
|
.get(s.id);
|
|
103
104
|
return {
|
|
104
105
|
isTask: !!s.task_status,
|
|
105
|
-
isOrchestrator: s.role
|
|
106
|
+
isOrchestrator: isStandingRole(s.role),
|
|
106
107
|
isQuickChange: !!s.quick_of,
|
|
107
108
|
isRemote: isRemoteHost(s.host_id) || !!s.peer_mirror,
|
|
108
109
|
taskStatus: s.task_status ?? null,
|
package/lib/tasks/starter.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// calling task, chat or orchestrator; null for a person), parent_session_id
|
|
3
3
|
// the session it came from, started_via how it began.
|
|
4
4
|
|
|
5
|
+
import { isStandingRole } from "../leads/roles";
|
|
5
6
|
import path from "path";
|
|
6
7
|
import { db } from "../db";
|
|
7
8
|
import { setPlacement } from "../db/session-parts";
|
|
@@ -86,7 +87,7 @@ export function starterOf(
|
|
|
86
87
|
return {
|
|
87
88
|
startedBy: caller.id,
|
|
88
89
|
parentSessionId: caller.id,
|
|
89
|
-
startedVia: caller.role
|
|
90
|
+
startedVia: isStandingRole(caller.role) ? "orchestrator" : "session",
|
|
90
91
|
};
|
|
91
92
|
}
|
|
92
93
|
|
|
@@ -104,7 +105,7 @@ export function orchestratorOf(startedBy: string | null | undefined) {
|
|
|
104
105
|
.get(id) as
|
|
105
106
|
{ role: string | null; started_by: string | null } | undefined;
|
|
106
107
|
if (!row) return null;
|
|
107
|
-
if (row.role
|
|
108
|
+
if (isStandingRole(row.role)) return id;
|
|
108
109
|
id = row.started_by;
|
|
109
110
|
}
|
|
110
111
|
return null;
|
package/lib/workspaces.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* presentation only: deleting one leaves its projects ungrouped.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { sqlNoRole, sqlStanding } from "./leads/roles";
|
|
6
7
|
import { randomUUID } from "crypto";
|
|
7
8
|
import { db, type Workspace } from "./db";
|
|
8
9
|
import { clearForeignGroup } from "./session-groups/assignSessions";
|
|
@@ -119,11 +120,11 @@ export function deleteWorkspace(id: string): void {
|
|
|
119
120
|
// Sessions filed here by being started here go back to "All workspaces".
|
|
120
121
|
db.prepare(
|
|
121
122
|
`UPDATE sessions SET workspace_id = NULL
|
|
122
|
-
WHERE
|
|
123
|
+
WHERE ${sqlNoRole()} AND workspace_id = ?`
|
|
123
124
|
).run(id);
|
|
124
|
-
// Its orchestrator
|
|
125
|
+
// Its orchestrator, leads and chief go with it; a running worker idles out on its own.
|
|
125
126
|
db.prepare(
|
|
126
|
-
`DELETE FROM sessions WHERE
|
|
127
|
+
`DELETE FROM sessions WHERE ${sqlStanding()} AND workspace_id = ?`
|
|
127
128
|
).run(id);
|
|
128
129
|
for (const table of [
|
|
129
130
|
"orchestrator_events",
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Everything else is skipped with the reason.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { isStandingRole, roleLabel } from "../leads/roles";
|
|
7
8
|
import fs from "fs";
|
|
8
9
|
import { db, type Session } from "../db";
|
|
9
10
|
import { fromSqliteTime } from "../session-meta";
|
|
@@ -67,7 +68,7 @@ export async function findSettled(
|
|
|
67
68
|
if (!isSettled(s) || !isAgentOSWorktree(dir) || !fs.existsSync(dir))
|
|
68
69
|
continue;
|
|
69
70
|
const skip = async (): Promise<string | null> => {
|
|
70
|
-
if (s.role) return
|
|
71
|
+
if (isStandingRole(s.role)) return `${roleLabel(s.role)}'s`;
|
|
71
72
|
if (s.pinned) return "pinned";
|
|
72
73
|
if (s.moved_to)
|
|
73
74
|
return "its task moved to another machine and may come back";
|