@saadnvd1/agent-os 0.3.777 → 0.3.778
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/lib/orchestrator/describe.ts +26 -2
- package/lib/orchestrator/facts.ts +4 -0
- package/lib/orchestrator/tools.ts +1 -1
- package/lib/program-status/store.ts +2 -1
- package/lib/session-activity.ts +5 -3
- package/lib/sidebar/shelves.ts +4 -1
- package/lib/status/collect.ts +2 -1
- package/package.json +1 -1
|
@@ -64,15 +64,35 @@ function sessionLine(f: SessionFacts): string {
|
|
|
64
64
|
return parts.join(" | ");
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
const compactLine = (f: SessionFacts) =>
|
|
68
|
+
`${f.name} (${f.project ?? "no project"}, id ${shortId(f.id)})`;
|
|
69
|
+
|
|
70
|
+
// Nothing to say about it beyond that it is idle.
|
|
71
|
+
const plainIdle = (f: SessionFacts) =>
|
|
72
|
+
f.status === "idle" &&
|
|
73
|
+
!f.activity &&
|
|
74
|
+
!f.task &&
|
|
75
|
+
!f.plan &&
|
|
76
|
+
!f.group &&
|
|
77
|
+
!f.stack &&
|
|
78
|
+
!f.compacting &&
|
|
79
|
+
!f.waits?.on.length &&
|
|
80
|
+
!f.waits?.by.length;
|
|
81
|
+
|
|
82
|
+
// The sessions tool's answer: one line per live session, busiest news first;
|
|
83
|
+
// then the done and plain idle ones, compactly, so a done can be seen to have
|
|
84
|
+
// worked.
|
|
68
85
|
export function describeSessions(
|
|
69
86
|
workspace: string,
|
|
70
87
|
facts: SessionFacts[],
|
|
71
88
|
groups: readonly string[] = []
|
|
72
89
|
): string {
|
|
73
90
|
if (!facts.length) return `No sessions are running in ${workspace}.`;
|
|
91
|
+
const done = facts.filter((f) => f.done && f.status !== "running");
|
|
92
|
+
const idle = facts.filter((f) => !f.done && plainIdle(f));
|
|
93
|
+
const shown = facts.filter((f) => !done.includes(f) && !idle.includes(f));
|
|
74
94
|
const rank = { waiting: 0, running: 1, idle: 2, dead: 3 };
|
|
75
|
-
const sorted = [...
|
|
95
|
+
const sorted = [...shown].sort((a, b) => rank[a.status] - rank[b.status]);
|
|
76
96
|
const counts = (["running", "waiting"] as const)
|
|
77
97
|
.map((s) => {
|
|
78
98
|
const n = facts.filter((f) => f.status === s).length;
|
|
@@ -84,6 +104,10 @@ export function describeSessions(
|
|
|
84
104
|
return [
|
|
85
105
|
`${facts.length} ${noun} in ${workspace}${summary}:`,
|
|
86
106
|
...sorted.map(sessionLine),
|
|
107
|
+
...(idle.length ? [`Idle: ${idle.map(compactLine).join("; ")}.`] : []),
|
|
108
|
+
...(done.length
|
|
109
|
+
? [`Done (on Saad's Done shelf): ${done.map(compactLine).join("; ")}.`]
|
|
110
|
+
: []),
|
|
87
111
|
...(groups.length
|
|
88
112
|
? [`Saad's groups here: ${groups.map((g) => `"${g}"`).join(", ")}.`]
|
|
89
113
|
: []),
|
|
@@ -27,6 +27,7 @@ import { commitTime, repoOf } from "./repo";
|
|
|
27
27
|
import { CHECKLESS_MS, checklessFor, ciSettleIn } from "./task-state";
|
|
28
28
|
import { repoHasCI } from "./repo-ci";
|
|
29
29
|
import { markStatusDirty } from "../status/dirty";
|
|
30
|
+
import { settledHow } from "../sidebar/shelves";
|
|
30
31
|
|
|
31
32
|
export type FactStatus = "running" | "waiting" | "idle" | "dead";
|
|
32
33
|
|
|
@@ -46,6 +47,8 @@ export interface SessionFacts {
|
|
|
46
47
|
compacting?: boolean;
|
|
47
48
|
// Saad's group it sits in, when it is in one.
|
|
48
49
|
group?: string | null;
|
|
50
|
+
// On Saad's Done shelf: marked done (or quiet for days) and not woken since.
|
|
51
|
+
done?: boolean;
|
|
49
52
|
// Last sign of life, in ms.
|
|
50
53
|
lastActive: number;
|
|
51
54
|
// Open waits (aos send --await, aos wait): who it waits on, and who waits on it.
|
|
@@ -346,6 +349,7 @@ export async function sessionFacts(
|
|
|
346
349
|
compacting:
|
|
347
350
|
s.view === "chat" && !hostLink(s.host_id) && !!chatCompacting(s.id),
|
|
348
351
|
group: groups.get(s.session_group_id ?? "") ?? null,
|
|
352
|
+
done: settledHow(s, Date.now()) !== null,
|
|
349
353
|
lastActive: await lastActiveFor(s),
|
|
350
354
|
branch: s.branch_name,
|
|
351
355
|
quick: !!s.quick_of,
|
|
@@ -116,7 +116,7 @@ const answer = async (call: Promise<string>): Promise<CallToolResult> => {
|
|
|
116
116
|
|
|
117
117
|
const DESCRIPTIONS: Record<ToolName, string> = {
|
|
118
118
|
sessions:
|
|
119
|
-
"Every session in this workspace: project, view, status, what it's doing, task/PR/CI state and stack position; then external sessions (tmux sessions AgentOS didn't start, here or on other machines, working in a workspace repository), read-only, with their branch's PR.",
|
|
119
|
+
"Every session in this workspace: project, view, status, what it's doing, task/PR/CI state and stack position; plain idle and done sessions (on Saad's Done shelf) are listed compactly after them; then external sessions (tmux sessions AgentOS didn't start, here or on other machines, working in a workspace repository), read-only, with their branch's PR.",
|
|
120
120
|
read: "The end of one session's terminal or chat, or an external session's screen, as plain text (capped at about 4k tokens).",
|
|
121
121
|
cards: "Cards on this workspace's linked LumifyHub boards, by list.",
|
|
122
122
|
logs: "Session logs AgentOS wrote when tasks merged: the last N headers (task, PR, cost, duration) of the workspace or one project, or one log in full (brief, plan, timeline, usage, outcome). Read straight from the database; nothing is summarised.",
|
|
@@ -85,7 +85,8 @@ function commit(sessionName: string, next: Records): boolean {
|
|
|
85
85
|
if (same(before, after)) return false;
|
|
86
86
|
if (after && after.state !== "idle" && after.state !== before?.state)
|
|
87
87
|
db.prepare(
|
|
88
|
-
`UPDATE sessions SET updated_at = datetime('now'), active_at = datetime('now') WHERE tmux_name = ? OR id =
|
|
88
|
+
`UPDATE sessions SET updated_at = datetime('now'), active_at = datetime('now') WHERE (tmux_name = ? OR id = ?)
|
|
89
|
+
AND (settled_at IS NULL OR active_at > settled_at)`
|
|
89
90
|
).run(sessionName, getSessionIdFromName(sessionName));
|
|
90
91
|
return true;
|
|
91
92
|
}
|
package/lib/session-activity.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { getDb } from "./db";
|
|
2
2
|
|
|
3
3
|
// A session did something: a message, a run starting, a need. Written at most
|
|
4
|
-
// once a minute, unless it wakes a snooze
|
|
4
|
+
// once a minute, unless it wakes a snooze. Never for a settled session: what
|
|
5
|
+
// comes after Done (the stop's own events, a status refresh) isn't the work
|
|
6
|
+
// starting again. Only a prompt does that (the chat worker writes it).
|
|
5
7
|
export function touchActive(sessionId: string): void {
|
|
6
8
|
try {
|
|
7
9
|
getDb()
|
|
@@ -9,8 +11,8 @@ export function touchActive(sessionId: string): void {
|
|
|
9
11
|
`UPDATE sessions SET active_at = datetime('now')
|
|
10
12
|
WHERE id = ? AND (active_at IS NULL
|
|
11
13
|
OR active_at < datetime('now', '-60 seconds')
|
|
12
|
-
OR (snoozed_at IS NOT NULL AND active_at <= snoozed_at)
|
|
13
|
-
|
|
14
|
+
OR (snoozed_at IS NOT NULL AND active_at <= snoozed_at))
|
|
15
|
+
AND (settled_at IS NULL OR active_at > settled_at)`
|
|
14
16
|
)
|
|
15
17
|
.run(sessionId);
|
|
16
18
|
} catch (err) {
|
package/lib/sidebar/shelves.ts
CHANGED
|
@@ -220,7 +220,10 @@ export function settledHow(
|
|
|
220
220
|
now: number
|
|
221
221
|
): "manual" | "auto" | null {
|
|
222
222
|
const active = activeMs(session, now);
|
|
223
|
-
|
|
223
|
+
// A settled session's own row is the clock: updated_at moves with
|
|
224
|
+
// bookkeeping, so it never takes a done session off the shelf.
|
|
225
|
+
const own = session.peer_mirror || session.active_at ? active : 0;
|
|
226
|
+
if (session.settled_at && own <= ms(session.settled_at)) return "manual";
|
|
224
227
|
return now - active >= SETTLE_AFTER_MS ? "auto" : null;
|
|
225
228
|
}
|
|
226
229
|
|
package/lib/status/collect.ts
CHANGED
|
@@ -421,7 +421,8 @@ async function collect(full = false): Promise<StatusSnapshot> {
|
|
|
421
421
|
|
|
422
422
|
// Batch update sessions and claude_session_id in a single transaction
|
|
423
423
|
const updateStatusStmt = db.prepare(
|
|
424
|
-
|
|
424
|
+
`UPDATE sessions SET updated_at = datetime('now'), active_at = datetime('now')
|
|
425
|
+
WHERE id = ? AND (settled_at IS NULL OR active_at > settled_at)`
|
|
425
426
|
);
|
|
426
427
|
const updateClaudeIdStmt = db.prepare(
|
|
427
428
|
"UPDATE sessions SET claude_session_id = ? WHERE id = ? AND (claude_session_id IS NULL OR claude_session_id != ?)"
|