@saadnvd1/agent-os 0.3.637 → 0.3.639
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/sessions/[id]/pr/route.ts +3 -3
- package/app/api/sessions/[id]/setup/route.ts +3 -3
- package/app/api/tasks/[id]/approve-plan/route.ts +3 -4
- package/app/api/tasks/[id]/arm/route.ts +6 -5
- package/app/api/tasks/[id]/chat-facts/route.ts +2 -2
- package/app/api/tasks/[id]/chat-tail/route.ts +2 -2
- package/app/api/tasks/[id]/checks/route.ts +1 -1
- package/app/api/tasks/[id]/diff/route.ts +1 -1
- package/app/api/tasks/[id]/done/route.ts +2 -2
- package/app/api/tasks/[id]/drop/route.ts +6 -2
- package/app/api/tasks/[id]/export/route.ts +3 -5
- package/app/api/tasks/[id]/merge/route.ts +3 -3
- package/app/api/tasks/[id]/message/route.ts +2 -2
- package/app/api/tasks/[id]/model/route.ts +5 -1
- package/app/api/tasks/[id]/move/route.ts +5 -1
- package/app/api/tasks/[id]/moved/route.ts +6 -8
- package/app/api/tasks/[id]/plan/route.ts +4 -6
- package/app/api/tasks/[id]/plan-edits/route.ts +6 -5
- package/app/api/tasks/[id]/priority/route.ts +4 -1
- package/app/api/tasks/[id]/resume/route.ts +3 -4
- package/app/api/tasks/[id]/stop/route.ts +2 -2
- package/app/api/tasks/route.ts +1 -1
- package/lib/approvals/enable.ts +1 -1
- package/lib/away/report.ts +4 -4
- package/lib/away/run.ts +2 -2
- package/lib/bookmarks/store.ts +3 -3
- package/lib/bus/held-reply.ts +2 -2
- package/lib/bus/index.ts +3 -3
- package/lib/chat/compact-context.ts +1 -1
- package/lib/chat/hold.ts +3 -3
- package/lib/chat/refresh.ts +1 -1
- package/lib/chat/registry.ts +3 -3
- package/lib/chat/runner.ts +1 -1
- package/lib/chat/worker/main.ts +1 -1
- package/lib/db/index.ts +5 -1
- package/lib/db/lumifyhub.ts +8 -9
- package/lib/db/machine-scope.ts +12 -0
- package/lib/db/migrations/20261015T1800_split_sessions_tables.ts +172 -0
- package/lib/db/migrations/README.md +7 -0
- package/lib/db/queries.ts +16 -31
- package/lib/db/session-parts/columns.ts +186 -0
- package/lib/db/session-parts/fill.ts +33 -0
- package/lib/db/session-parts/index.ts +9 -0
- package/lib/db/session-parts/pauses.ts +11 -0
- package/lib/db/session-parts/placement.ts +11 -0
- package/lib/db/session-parts/plans.ts +31 -0
- package/lib/db/session-parts/prs.ts +31 -0
- package/lib/db/session-parts/tasks.ts +27 -0
- package/lib/db/session-parts/view.ts +43 -0
- package/lib/db/session-parts/write.ts +69 -0
- package/lib/done/archive.ts +2 -2
- package/lib/done/auto-archive.ts +1 -1
- package/lib/done/bulk.ts +1 -1
- package/lib/done/index.ts +8 -14
- package/lib/health/evidence.ts +1 -1
- package/lib/health/facts.ts +1 -1
- package/lib/health/pieces.ts +1 -1
- package/lib/hosts/peer-actions.ts +2 -2
- package/lib/hosts/peer-mirror.ts +2 -2
- package/lib/hosts/peer-sync.ts +69 -32
- package/lib/learning-cards/input.ts +1 -1
- package/lib/learning-cards/store.ts +1 -1
- package/lib/models/plan-step.ts +10 -24
- package/lib/models/resume.ts +5 -7
- package/lib/notify/index.ts +1 -1
- package/lib/notify/push/events.ts +1 -1
- package/lib/notify/push/index.ts +1 -1
- package/lib/orchestrator/act.ts +1 -1
- package/lib/orchestrator/armed.ts +17 -15
- package/lib/orchestrator/ask-approvals.ts +1 -1
- package/lib/orchestrator/ask-heads.ts +1 -1
- package/lib/orchestrator/ask-settle.ts +1 -1
- package/lib/orchestrator/brakes.ts +1 -1
- package/lib/orchestrator/digest.ts +2 -2
- package/lib/orchestrator/external-pr.ts +2 -2
- package/lib/orchestrator/facts.ts +4 -28
- package/lib/orchestrator/held-asks.ts +3 -3
- package/lib/orchestrator/home.ts +2 -2
- package/lib/orchestrator/overview.ts +1 -1
- package/lib/orchestrator/pr-wake.ts +3 -5
- package/lib/orchestrator/questions.ts +1 -1
- package/lib/orchestrator/quick-store.ts +1 -1
- package/lib/orchestrator/quick.ts +10 -7
- package/lib/orchestrator/read.ts +1 -1
- package/lib/orchestrator/rebase.ts +8 -11
- package/lib/orchestrator/reconcile.ts +2 -2
- package/lib/orchestrator/signoff.ts +3 -1
- package/lib/orchestrator/targets.ts +1 -1
- package/lib/orchestrator/task-state.ts +4 -2
- package/lib/plans/features.ts +1 -1
- package/lib/plans/release.ts +1 -1
- package/lib/ports.ts +1 -1
- package/lib/resurrect/find.ts +1 -1
- package/lib/resurrect/index.ts +24 -18
- package/lib/schedules/store.ts +1 -1
- package/lib/session-audit/sources.ts +3 -3
- package/lib/session-logs/index.ts +1 -1
- package/lib/session-titles.ts +1 -1
- package/lib/sessions/setup-progress.ts +1 -1
- package/lib/sessions/stop.ts +1 -1
- package/lib/sessions/worktree-setup.ts +2 -1
- package/lib/simulator/index.ts +1 -1
- package/lib/simulator/task-devices.ts +1 -1
- package/lib/stacks/board.ts +1 -1
- package/lib/stacks/land.ts +1 -1
- package/lib/stacks/restack.ts +1 -1
- package/lib/stacks/start.ts +2 -2
- package/lib/stacks/tick.ts +1 -1
- package/lib/stacks/view.ts +1 -1
- package/lib/status/incremental.ts +1 -1
- package/lib/tasks/contract/actions.ts +35 -0
- package/lib/tasks/contract/authorize.ts +82 -0
- package/lib/tasks/contract/client.ts +296 -0
- package/lib/tasks/{held-remote.ts → contract/held-calls.ts} +35 -66
- package/lib/tasks/contract/index.ts +28 -0
- package/lib/tasks/{remote-plan-path.ts → contract/plan-path.ts} +2 -2
- package/lib/tasks/{remote-plan.ts → contract/plan.ts} +76 -110
- package/lib/tasks/contract/routes.ts +26 -0
- package/lib/tasks/contract/start.ts +122 -0
- package/lib/tasks/contract/sync.ts +126 -0
- package/lib/tasks/contract/types.ts +41 -0
- package/lib/tasks/{remote-views.ts → contract/views.ts} +11 -18
- package/lib/tasks/finish.ts +10 -9
- package/lib/tasks/held-replies.ts +7 -13
- package/lib/tasks/held.ts +14 -18
- package/lib/tasks/import.ts +19 -15
- package/lib/tasks/index.ts +34 -22
- package/lib/tasks/launch-gate.ts +1 -1
- package/lib/tasks/move-carry.ts +15 -7
- package/lib/tasks/move-flow.ts +29 -36
- package/lib/tasks/move-guard.ts +1 -1
- package/lib/tasks/move-recover.ts +6 -25
- package/lib/tasks/move.ts +23 -23
- package/lib/tasks/moved-away.ts +1 -1
- package/lib/tasks/pause-hold.ts +13 -5
- package/lib/tasks/pause.ts +19 -23
- package/lib/tasks/plan-view.ts +1 -1
- package/lib/tasks/queue-placement.ts +1 -1
- package/lib/tasks/queue-rows.ts +4 -4
- package/lib/tasks/queue-start.ts +5 -3
- package/lib/tasks/queue-tick.ts +2 -2
- package/lib/tasks/queue.ts +14 -6
- package/lib/tasks/session.ts +7 -4
- package/lib/tasks/start.ts +4 -4
- package/lib/tasks/starter.ts +8 -11
- package/lib/usage/calls.ts +1 -1
- package/lib/usage/compare.ts +1 -1
- package/lib/usage/quality.ts +1 -1
- package/lib/usage/turns.ts +1 -1
- package/lib/voice/vocab.ts +1 -1
- package/package.json +1 -1
- package/scripts/backfill-session-logs.ts +1 -1
- package/lib/tasks/held-routes.ts +0 -33
- package/lib/tasks/remote.ts +0 -255
package/lib/tasks/move.ts
CHANGED
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
latestSessionId,
|
|
37
37
|
readTranscript,
|
|
38
38
|
} from "./transcript";
|
|
39
|
+
import { setTask } from "../db/session-parts";
|
|
39
40
|
|
|
40
41
|
export * from "./move-bundle";
|
|
41
42
|
|
|
@@ -88,13 +89,13 @@ async function pushOwnBranch(cwd: string, branch: string): Promise<void> {
|
|
|
88
89
|
* arrived, and then it would run on both.
|
|
89
90
|
*/
|
|
90
91
|
function claimMoving(id: string, to: string): { fresh: boolean } {
|
|
91
|
-
const fresh =
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (fresh
|
|
92
|
+
const fresh = setTask(
|
|
93
|
+
db,
|
|
94
|
+
id,
|
|
95
|
+
{ task_status: "moving", moved_to: to },
|
|
96
|
+
{ task_status: "running" }
|
|
97
|
+
);
|
|
98
|
+
if (fresh) return { fresh: true };
|
|
98
99
|
const { task_status, moved_to } = getTaskSession(id);
|
|
99
100
|
if (task_status === "moving" && moved_to === to) return { fresh: false };
|
|
100
101
|
throw new Error(
|
|
@@ -250,12 +251,12 @@ export async function exportOrResume(
|
|
|
250
251
|
}
|
|
251
252
|
|
|
252
253
|
export function markMoved(id: string, to: string): void {
|
|
253
|
-
const moved =
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
254
|
+
const moved = setTask(
|
|
255
|
+
db,
|
|
256
|
+
id,
|
|
257
|
+
{ task_status: "moved", moved_to: to },
|
|
258
|
+
{ task_status: ["running", "moving"] }
|
|
259
|
+
);
|
|
259
260
|
// It goes on there with a copy of its own.
|
|
260
261
|
if (moved) void dropSessionDatabase(id);
|
|
261
262
|
}
|
|
@@ -269,25 +270,24 @@ export function markMoved(id: string, to: string): void {
|
|
|
269
270
|
*/
|
|
270
271
|
export async function resumeTask(id: string, note?: string): Promise<void> {
|
|
271
272
|
if (exporting.has(id)) throw new Error("It's moving right now");
|
|
272
|
-
const claimed =
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
273
|
+
const claimed = setTask(
|
|
274
|
+
db,
|
|
275
|
+
id,
|
|
276
|
+
{ task_status: "running" },
|
|
277
|
+
{ task_status: "moving" }
|
|
278
|
+
);
|
|
277
279
|
const session = getTaskSession(id);
|
|
278
|
-
if (claimed
|
|
280
|
+
if (!claimed) {
|
|
279
281
|
if (session.task_status === "running") return;
|
|
280
282
|
throw new Error(`Task is already ${session.task_status}`);
|
|
281
283
|
}
|
|
282
284
|
try {
|
|
283
285
|
await relaunch(session, note);
|
|
284
286
|
} catch (err) {
|
|
285
|
-
db
|
|
286
|
-
`UPDATE sessions SET task_status = 'moving' WHERE id = ? AND task_status = 'running'`
|
|
287
|
-
).run(id);
|
|
287
|
+
setTask(db, id, { task_status: "moving" }, { task_status: "running" });
|
|
288
288
|
throw err;
|
|
289
289
|
}
|
|
290
|
-
db
|
|
290
|
+
setTask(db, id, { moved_to: null });
|
|
291
291
|
}
|
|
292
292
|
|
|
293
293
|
async function relaunch(session: Session, note?: string): Promise<void> {
|
package/lib/tasks/moved-away.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { settleWorktree, type WorktreeFate } from "../done/worktree";
|
|
|
12
12
|
import { archiveSession } from "../done/archive";
|
|
13
13
|
|
|
14
14
|
export async function cleanUpMovedAway(id: string): Promise<WorktreeFate> {
|
|
15
|
-
const s = db.prepare(`SELECT * FROM
|
|
15
|
+
const s = db.prepare(`SELECT * FROM session_rows WHERE id = ?`).get(id) as
|
|
16
16
|
| Session
|
|
17
17
|
| undefined;
|
|
18
18
|
if (!s || s.task_status !== "moved" || !s.held_by_device)
|
package/lib/tasks/pause-hold.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// can hold a message for it without loading the pause machinery.
|
|
3
3
|
|
|
4
4
|
import { db, type TaskPause } from "../db";
|
|
5
|
+
import { setPause } from "../db/session-parts";
|
|
5
6
|
|
|
6
7
|
export interface PauseHold {
|
|
7
8
|
pauseId: number;
|
|
@@ -11,7 +12,7 @@ export interface PauseHold {
|
|
|
11
12
|
export function pauseHoldOf(sessionId: string): PauseHold | null {
|
|
12
13
|
const row = db
|
|
13
14
|
.prepare(
|
|
14
|
-
`SELECT pause_id, paused_until FROM
|
|
15
|
+
`SELECT pause_id, paused_until FROM session_rows
|
|
15
16
|
WHERE id = ? AND pause_state IN ('pausing', 'paused') AND pause_id IS NOT NULL`
|
|
16
17
|
)
|
|
17
18
|
.get(sessionId) as
|
|
@@ -45,10 +46,17 @@ export function endPause(
|
|
|
45
46
|
`UPDATE task_pauses SET state = 'ended', ended_at = datetime('now'), end_reason = ?
|
|
46
47
|
WHERE id = ? AND state != 'ended'`
|
|
47
48
|
).run(reason, row.id);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
setPause(
|
|
50
|
+
db,
|
|
51
|
+
row.session_id,
|
|
52
|
+
{
|
|
53
|
+
pause_state: null,
|
|
54
|
+
paused_until: null,
|
|
55
|
+
pause_id: null,
|
|
56
|
+
pause_note: null,
|
|
57
|
+
},
|
|
58
|
+
{ pause_id: row.id }
|
|
59
|
+
);
|
|
52
60
|
})();
|
|
53
61
|
}
|
|
54
62
|
|
package/lib/tasks/pause.ts
CHANGED
|
@@ -50,6 +50,7 @@ import {
|
|
|
50
50
|
type PauseView,
|
|
51
51
|
type Priority,
|
|
52
52
|
} from "./schedule-types";
|
|
53
|
+
import { setPause } from "../db/session-parts";
|
|
53
54
|
|
|
54
55
|
export class PauseRefused extends Error {}
|
|
55
56
|
|
|
@@ -72,7 +73,7 @@ const nudge = () =>
|
|
|
72
73
|
void import("./queue").then((q) => q.tickQueueSoon()).catch(() => undefined);
|
|
73
74
|
|
|
74
75
|
const sessionOf = (id: string) =>
|
|
75
|
-
db.prepare(`SELECT * FROM
|
|
76
|
+
db.prepare(`SELECT * FROM session_rows WHERE id = ?`).get(id) as
|
|
76
77
|
| Session
|
|
77
78
|
| undefined;
|
|
78
79
|
|
|
@@ -165,10 +166,9 @@ export function requestPause(sessionId: string, req: PauseRequest): TaskPause {
|
|
|
165
166
|
until ? toSqliteTime(until) : null,
|
|
166
167
|
existing.id
|
|
167
168
|
);
|
|
168
|
-
db
|
|
169
|
-
until ? toSqliteTime(until) : null,
|
|
170
|
-
|
|
171
|
-
);
|
|
169
|
+
setPause(db, sessionId, {
|
|
170
|
+
paused_until: until ? toSqliteTime(until) : null,
|
|
171
|
+
});
|
|
172
172
|
// "Pause now" takes over: the first driver is superseded, not raced.
|
|
173
173
|
drive(sessionId, existing.id, mode === "now");
|
|
174
174
|
return pauseRow(existing.id)!;
|
|
@@ -190,9 +190,11 @@ export function requestPause(sessionId: string, req: PauseRequest): TaskPause {
|
|
|
190
190
|
until ? toSqliteTime(until) : null
|
|
191
191
|
).lastInsertRowid
|
|
192
192
|
);
|
|
193
|
-
db
|
|
194
|
-
|
|
195
|
-
|
|
193
|
+
setPause(db, sessionId, {
|
|
194
|
+
pause_state: "pausing",
|
|
195
|
+
paused_until: until ? toSqliteTime(until) : null,
|
|
196
|
+
pause_id: id,
|
|
197
|
+
});
|
|
196
198
|
drive(sessionId, id);
|
|
197
199
|
return pauseRow(id)!;
|
|
198
200
|
}
|
|
@@ -307,10 +309,7 @@ function markPaused(sessionId: string, pauseId: number): void {
|
|
|
307
309
|
WHERE id = ? AND state = 'pausing'`
|
|
308
310
|
)
|
|
309
311
|
.run(pauseId).changes;
|
|
310
|
-
if (n)
|
|
311
|
-
db.prepare(`UPDATE sessions SET pause_state = 'paused' WHERE id = ?`).run(
|
|
312
|
-
sessionId
|
|
313
|
-
);
|
|
312
|
+
if (n) setPause(db, sessionId, { pause_state: "paused" });
|
|
314
313
|
return n;
|
|
315
314
|
})();
|
|
316
315
|
if (!changed) return;
|
|
@@ -359,10 +358,7 @@ export function setResumeTime(sessionId: string, until: Date | null): void {
|
|
|
359
358
|
db.prepare(
|
|
360
359
|
`UPDATE task_pauses SET until_at = ?, resume_requested_at = NULL WHERE id = ? AND state IN ('pausing','paused')`
|
|
361
360
|
).run(value, row.id);
|
|
362
|
-
db
|
|
363
|
-
value,
|
|
364
|
-
sessionId
|
|
365
|
-
);
|
|
361
|
+
setPause(db, sessionId, { paused_until: value });
|
|
366
362
|
})();
|
|
367
363
|
}
|
|
368
364
|
|
|
@@ -396,9 +392,11 @@ export function requestResume(sessionId: string, actor: PauseBy): void {
|
|
|
396
392
|
export { loadHoldFor, resumeHoldFor };
|
|
397
393
|
|
|
398
394
|
export function setPauseNote(sessionId: string, note: string | null): void {
|
|
399
|
-
db
|
|
400
|
-
`
|
|
401
|
-
|
|
395
|
+
const row = db
|
|
396
|
+
.prepare(`SELECT pause_note FROM session_rows WHERE id = ?`)
|
|
397
|
+
.get(sessionId) as { pause_note: string | null } | undefined;
|
|
398
|
+
if ((row?.pause_note ?? "") === (note ?? "")) return;
|
|
399
|
+
setPause(db, sessionId, { pause_note: note }, { pause_state: "paused" });
|
|
402
400
|
}
|
|
403
401
|
|
|
404
402
|
export type ResumeOutcome =
|
|
@@ -453,7 +451,7 @@ export function dueResumes(): DueResume[] {
|
|
|
453
451
|
.prepare(
|
|
454
452
|
`SELECT p.id AS pause_id, p.session_id, p.by_kind, p.until_at, p.resume_requested_at, p.resume_by_kind,
|
|
455
453
|
s.project_id, s.priority
|
|
456
|
-
FROM task_pauses p JOIN
|
|
454
|
+
FROM task_pauses p JOIN session_rows s ON s.id = p.session_id
|
|
457
455
|
WHERE p.state = 'paused' AND p.resume_requested_at IS NOT NULL
|
|
458
456
|
AND s.task_status IN ('running') AND s.archived_at IS NULL`
|
|
459
457
|
)
|
|
@@ -583,9 +581,7 @@ export async function claimResume(
|
|
|
583
581
|
}),
|
|
584
582
|
Date.now()
|
|
585
583
|
);
|
|
586
|
-
db
|
|
587
|
-
`UPDATE sessions SET pause_state = 'resuming', pause_note = NULL WHERE id = ?`
|
|
588
|
-
).run(sessionId);
|
|
584
|
+
setPause(db, sessionId, { pause_state: "resuming", pause_note: null });
|
|
589
585
|
return true;
|
|
590
586
|
})();
|
|
591
587
|
if (!claimed) return "gone";
|
package/lib/tasks/plan-view.ts
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
} from "./loop-types";
|
|
19
19
|
import { expandHome } from "./session";
|
|
20
20
|
import { isRemoteHost } from "../hosts";
|
|
21
|
-
import { remotePlanPath } from "./
|
|
21
|
+
import { remotePlanPath } from "./contract/plan-path";
|
|
22
22
|
|
|
23
23
|
const TITLE_MAX = 120;
|
|
24
24
|
|
|
@@ -10,7 +10,7 @@ import { peerReading, peerSource, refreshPeers } from "../hosts/peer-load";
|
|
|
10
10
|
import { projectMachines } from "../hosts/project-machines";
|
|
11
11
|
import { viewOf } from "../load/machines";
|
|
12
12
|
import { usesGitCrypt } from "../worktree-add";
|
|
13
|
-
import { hostTasks } from "./
|
|
13
|
+
import { list as hostTasks } from "./contract/client";
|
|
14
14
|
import {
|
|
15
15
|
asPlanPhase,
|
|
16
16
|
place,
|
package/lib/tasks/queue-rows.ts
CHANGED
|
@@ -18,7 +18,7 @@ export const row = (id: string) =>
|
|
|
18
18
|
| undefined;
|
|
19
19
|
|
|
20
20
|
export const sessionOf = (id: string) =>
|
|
21
|
-
db.prepare(`SELECT * FROM
|
|
21
|
+
db.prepare(`SELECT * FROM session_rows WHERE id = ?`).get(id) as
|
|
22
22
|
| Session
|
|
23
23
|
| undefined;
|
|
24
24
|
|
|
@@ -60,7 +60,7 @@ export function runningIn(
|
|
|
60
60
|
return (
|
|
61
61
|
db
|
|
62
62
|
.prepare(
|
|
63
|
-
`SELECT s.id FROM
|
|
63
|
+
`SELECT s.id FROM session_rows s JOIN projects p ON p.id = s.project_id
|
|
64
64
|
WHERE ${where} AND s.archived_at IS NULL AND s.quick_of IS NULL
|
|
65
65
|
AND (s.pause_state IS NULL OR s.pause_state != 'paused')
|
|
66
66
|
${forSlots ? `AND NOT (COALESCE(s.plan_only, 0) = 1 AND COALESCE(s.plan_phase, '') != 'implementing')` : ""}
|
|
@@ -127,7 +127,7 @@ export function resolveAfter(
|
|
|
127
127
|
const scope = workspaceId ? `AND p.workspace_id = @ws` : "";
|
|
128
128
|
const candidates = db
|
|
129
129
|
.prepare(
|
|
130
|
-
`SELECT s.id, s.name FROM
|
|
130
|
+
`SELECT s.id, s.name FROM session_rows s JOIN projects p ON p.id = s.project_id
|
|
131
131
|
WHERE s.archived_at IS NULL AND s.task_status IN ('running', 'moving') ${scope}
|
|
132
132
|
UNION ALL
|
|
133
133
|
SELECT q.id, COALESCE(q.name, q.prompt) AS name FROM task_queue q
|
|
@@ -227,7 +227,7 @@ export function lineSignature(): string {
|
|
|
227
227
|
.prepare(
|
|
228
228
|
`SELECT COUNT(*) AS n, COALESCE(MAX(position), 0) AS p,
|
|
229
229
|
COALESCE(SUM(priority = 'normal'), 0) AS pr,
|
|
230
|
-
(SELECT COUNT(*) FROM
|
|
230
|
+
(SELECT COUNT(*) FROM session_rows WHERE task_status IS NOT NULL
|
|
231
231
|
AND task_status NOT IN ('merged', 'dropped')) AS running
|
|
232
232
|
FROM task_queue WHERE status IN ('queued', 'starting')`
|
|
233
233
|
)
|
package/lib/tasks/queue-start.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
type QueueRow,
|
|
28
28
|
type StartOutcome,
|
|
29
29
|
} from "./queue-types";
|
|
30
|
+
import { setPlacement, setTask } from "../db/session-parts";
|
|
30
31
|
|
|
31
32
|
// Tells the orchestrator that queued it that it started.
|
|
32
33
|
export function announce(
|
|
@@ -205,7 +206,8 @@ function stampStarted(r: QueueRow, placed: Placement | null): void {
|
|
|
205
206
|
const priority =
|
|
206
207
|
r.priority && r.priority !== DEFAULT_PRIORITY ? r.priority : null;
|
|
207
208
|
if (!note && !priority) return;
|
|
208
|
-
db.
|
|
209
|
-
|
|
210
|
-
|
|
209
|
+
db.transaction(() => {
|
|
210
|
+
setTask(db, r.id, { priority: priority ?? undefined });
|
|
211
|
+
setPlacement(db, r.id, { placed_note: note ?? undefined });
|
|
212
|
+
})();
|
|
211
213
|
}
|
package/lib/tasks/queue-tick.ts
CHANGED
|
@@ -128,8 +128,8 @@ export function tickQueue(): Promise<void> {
|
|
|
128
128
|
tickState.ticking ??= (async () => {
|
|
129
129
|
try {
|
|
130
130
|
// Plans written on a linked machine, and approved plans coming here.
|
|
131
|
-
await import("./
|
|
132
|
-
.then((m) => m.
|
|
131
|
+
await import("./contract/sync")
|
|
132
|
+
.then((m) => m.syncMirrors())
|
|
133
133
|
.catch((error: unknown) => console.error("[queue] plans:", error));
|
|
134
134
|
for (let i = 0; i < 3; i++) {
|
|
135
135
|
if ((await tickOnce()) !== "stale") break;
|
package/lib/tasks/queue.ts
CHANGED
|
@@ -66,6 +66,7 @@ export type {
|
|
|
66
66
|
QueuedTaskView,
|
|
67
67
|
StartOutcome,
|
|
68
68
|
} from "./queue-types";
|
|
69
|
+
import { setTask } from "../db/session-parts";
|
|
69
70
|
export { afterMet, hasFinished, resolveAfter, slotState } from "./queue-rows";
|
|
70
71
|
export { findQueued, listQueue, queuedView } from "./queue-view";
|
|
71
72
|
export { tickOnce, tickQueue, tickQueueSoon } from "./queue-tick";
|
|
@@ -260,11 +261,18 @@ export function setPriority(id: string, priority: Priority): void {
|
|
|
260
261
|
`UPDATE task_queue SET priority = ? WHERE id = ? AND status IN ('queued', 'failed')`
|
|
261
262
|
)
|
|
262
263
|
.run(priority, id).changes;
|
|
263
|
-
const running = db
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
264
|
+
const running = db.transaction(() => {
|
|
265
|
+
const live = db
|
|
266
|
+
.prepare(
|
|
267
|
+
`SELECT 1 FROM session_rows WHERE id = ? AND task_status IS NOT NULL AND archived_at IS NULL`
|
|
268
|
+
)
|
|
269
|
+
.get(id);
|
|
270
|
+
return live
|
|
271
|
+
? setTask(db, id, {
|
|
272
|
+
priority: priority === DEFAULT_PRIORITY ? null : priority,
|
|
273
|
+
})
|
|
274
|
+
: false;
|
|
275
|
+
})();
|
|
268
276
|
if (!queued && !running) throw new Error("No such queued or running task");
|
|
269
277
|
tickQueueSoon();
|
|
270
278
|
}
|
|
@@ -272,7 +280,7 @@ export function setPriority(id: string, priority: Priority): void {
|
|
|
272
280
|
// A task started directly (never queued) keeps the priority it was given.
|
|
273
281
|
export function stampPriority(id: string, priority: Priority | undefined) {
|
|
274
282
|
if (!priority || priority === DEFAULT_PRIORITY) return;
|
|
275
|
-
db
|
|
283
|
+
setTask(db, id, { priority });
|
|
276
284
|
}
|
|
277
285
|
|
|
278
286
|
export function removeQueued(id: string): void {
|
package/lib/tasks/session.ts
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
import { notifyStatusChanged } from "../status/hub";
|
|
27
27
|
import type { TaskPR } from "./state";
|
|
28
28
|
import { markStatusDirty } from "../status/dirty";
|
|
29
|
+
import { setPr } from "../db/session-parts";
|
|
29
30
|
|
|
30
31
|
export const expandHome = (p: string) => p.replace(/^~/, os.homedir());
|
|
31
32
|
|
|
@@ -33,7 +34,7 @@ export const expandHome = (p: string) => p.replace(/^~/, os.homedir());
|
|
|
33
34
|
export function taskSessions(includeArchived = true): Session[] {
|
|
34
35
|
return db
|
|
35
36
|
.prepare(
|
|
36
|
-
`SELECT ${sessionListColumns(db)} FROM
|
|
37
|
+
`SELECT ${sessionListColumns(db)} FROM session_rows
|
|
37
38
|
WHERE task_status IS NOT NULL AND task_status != 'moved'
|
|
38
39
|
${includeArchived ? "" : "AND archived_at IS NULL"}
|
|
39
40
|
ORDER BY created_at DESC`
|
|
@@ -205,9 +206,11 @@ export async function prFor(
|
|
|
205
206
|
if (pr) pushPrChange(session, pr);
|
|
206
207
|
}
|
|
207
208
|
if (pr) {
|
|
208
|
-
db.
|
|
209
|
-
|
|
210
|
-
|
|
209
|
+
setPr(db, session.id, {
|
|
210
|
+
pr_url: pr.url,
|
|
211
|
+
pr_number: pr.number,
|
|
212
|
+
pr_status: pr.state.toLowerCase() as "open" | "merged" | "closed",
|
|
213
|
+
});
|
|
211
214
|
// Merged on GitHub rather than through AgentOS: its log is due too.
|
|
212
215
|
if (pr.state === "MERGED") {
|
|
213
216
|
if (session.pr_status !== "merged") pushMerged(session.id);
|
package/lib/tasks/start.ts
CHANGED
|
@@ -107,7 +107,7 @@ export async function finishTaskStart(
|
|
|
107
107
|
earlier?: TaskSetup
|
|
108
108
|
): Promise<TaskSetup> {
|
|
109
109
|
const session = db
|
|
110
|
-
.prepare(`SELECT * FROM
|
|
110
|
+
.prepare(`SELECT * FROM session_rows WHERE id = ?`)
|
|
111
111
|
.get(sessionId) as Session | undefined;
|
|
112
112
|
if (!session?.worktree_path) throw new Error(`No task ${sessionId}`);
|
|
113
113
|
const project = session.project_id ? getProject(session.project_id) : null;
|
|
@@ -133,7 +133,7 @@ export async function finishTaskStart(
|
|
|
133
133
|
// The setup's own error is kept, for the agent's prompt when it launches.
|
|
134
134
|
if (held) return done({ ...setup, status: "held" });
|
|
135
135
|
const now = db
|
|
136
|
-
.prepare(`SELECT task_status, archived_at FROM
|
|
136
|
+
.prepare(`SELECT task_status, archived_at FROM session_rows WHERE id = ?`)
|
|
137
137
|
.get(sessionId) as
|
|
138
138
|
| { task_status: string | null; archived_at: string | null }
|
|
139
139
|
| undefined;
|
|
@@ -226,7 +226,7 @@ export async function resumeTaskStarts(
|
|
|
226
226
|
): Promise<string[]> {
|
|
227
227
|
const rows = db
|
|
228
228
|
.prepare(
|
|
229
|
-
`SELECT id, tmux_name, view, setup_ms, setup_error FROM
|
|
229
|
+
`SELECT id, tmux_name, view, setup_ms, setup_error FROM session_rows
|
|
230
230
|
WHERE setup_status = 'running' AND task_status = 'running'
|
|
231
231
|
AND archived_at IS NULL`
|
|
232
232
|
)
|
|
@@ -269,7 +269,7 @@ export async function resumeTaskStarts(
|
|
|
269
269
|
export function resumeHeldStarts(): string[] {
|
|
270
270
|
const rows = db
|
|
271
271
|
.prepare(
|
|
272
|
-
`SELECT id, setup_ms, setup_error FROM
|
|
272
|
+
`SELECT id, setup_ms, setup_error FROM session_rows WHERE setup_status = 'held'
|
|
273
273
|
AND task_status = 'running' AND archived_at IS NULL`
|
|
274
274
|
)
|
|
275
275
|
.all() as ({ id: string } & SetupColumns)[];
|
package/lib/tasks/starter.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import path from "path";
|
|
6
6
|
import { db } from "../db";
|
|
7
|
+
import { setPlacement } from "../db/session-parts";
|
|
7
8
|
|
|
8
9
|
export type StartVia =
|
|
9
10
|
| "session"
|
|
@@ -59,7 +60,7 @@ export function callerSession(input: {
|
|
|
59
60
|
const cwd = path.resolve(input.cwd);
|
|
60
61
|
const rows = db
|
|
61
62
|
.prepare(
|
|
62
|
-
`SELECT id, role, worktree_path FROM
|
|
63
|
+
`SELECT id, role, worktree_path FROM session_rows
|
|
63
64
|
WHERE worktree_path IS NOT NULL AND archived_at IS NULL
|
|
64
65
|
AND task_status IS NOT NULL
|
|
65
66
|
AND (host_id IS NULL OR host_id = 'local')`
|
|
@@ -111,14 +112,10 @@ export function orchestratorOf(startedBy: string | null | undefined) {
|
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
export function recordStart(id: string, s: Starter): void {
|
|
114
|
-
db.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
s.
|
|
119
|
-
|
|
120
|
-
s.startedVia,
|
|
121
|
-
s.startedByDevice ?? null,
|
|
122
|
-
id
|
|
123
|
-
);
|
|
115
|
+
db.transaction(() => {
|
|
116
|
+
db.prepare(
|
|
117
|
+
`UPDATE sessions SET started_by = ?, parent_session_id = (SELECT id FROM sessions WHERE id = ?), started_via = ? WHERE id = ?`
|
|
118
|
+
).run(s.startedBy, s.parentSessionId, s.startedVia, id);
|
|
119
|
+
setPlacement(db, id, { started_by_device: s.startedByDevice ?? undefined });
|
|
120
|
+
})();
|
|
124
121
|
}
|
package/lib/usage/calls.ts
CHANGED
|
@@ -97,7 +97,7 @@ export function sessionContext(sessionId: string): SessionContext {
|
|
|
97
97
|
const s = db
|
|
98
98
|
.prepare(
|
|
99
99
|
`SELECT name, workspace_id, project_id, model_provider, role, task_status,
|
|
100
|
-
plan_phase, started_by FROM
|
|
100
|
+
plan_phase, started_by FROM session_rows WHERE id = ?`
|
|
101
101
|
)
|
|
102
102
|
.get(sessionId) as
|
|
103
103
|
| {
|
package/lib/usage/compare.ts
CHANGED
|
@@ -225,7 +225,7 @@ export function taskOutcomes(ids: string[]): Map<string, TaskOutcome> {
|
|
|
225
225
|
const marks = ids.map(() => "?").join(",");
|
|
226
226
|
const tasks = db
|
|
227
227
|
.prepare(
|
|
228
|
-
`SELECT id, task_status, plan_decided FROM
|
|
228
|
+
`SELECT id, task_status, plan_decided FROM session_rows
|
|
229
229
|
WHERE id IN (${marks}) AND task_status IS NOT NULL`
|
|
230
230
|
)
|
|
231
231
|
.all(...ids) as {
|
package/lib/usage/quality.ts
CHANGED
|
@@ -82,7 +82,7 @@ export function mergedTasks(from: number, to: number): MergedTask[] {
|
|
|
82
82
|
db
|
|
83
83
|
.prepare(
|
|
84
84
|
`SELECT id, project_id AS projectId, pr_number AS prNumber, merged_at AS mergedAt
|
|
85
|
-
FROM
|
|
85
|
+
FROM session_rows WHERE task_status = 'merged' AND merged_at >= ? AND merged_at < ?`
|
|
86
86
|
)
|
|
87
87
|
.all(sqlTime(from), sqlTime(to)) as MergedTask[]
|
|
88
88
|
).filter((t) => t.mergedAt);
|
package/lib/usage/turns.ts
CHANGED
|
@@ -61,7 +61,7 @@ export function recordTurn(
|
|
|
61
61
|
const current = db
|
|
62
62
|
.prepare(
|
|
63
63
|
`SELECT name, workspace_id, model, model_provider, role, task_status,
|
|
64
|
-
plan_phase, started_by FROM
|
|
64
|
+
plan_phase, started_by FROM session_rows WHERE id = ?`
|
|
65
65
|
)
|
|
66
66
|
.get(session.id) as
|
|
67
67
|
| Pick<
|
package/lib/voice/vocab.ts
CHANGED
|
@@ -44,7 +44,7 @@ export function vocabFor(workspaceId: string): string[] {
|
|
|
44
44
|
workspaceId
|
|
45
45
|
);
|
|
46
46
|
const sessions = names(
|
|
47
|
-
`SELECT s.name FROM
|
|
47
|
+
`SELECT s.name FROM session_rows s
|
|
48
48
|
JOIN projects p ON p.id = s.project_id
|
|
49
49
|
WHERE p.workspace_id = ?
|
|
50
50
|
AND (s.task_status IS NULL OR s.task_status NOT IN ('merged','dropped','done','moved'))
|
package/package.json
CHANGED
|
@@ -47,7 +47,7 @@ async function main() {
|
|
|
47
47
|
const { commitLogs } = await import("../lib/session-logs/hq");
|
|
48
48
|
const rows = db
|
|
49
49
|
.prepare(
|
|
50
|
-
`SELECT id, name FROM
|
|
50
|
+
`SELECT id, name FROM session_rows
|
|
51
51
|
WHERE task_prompt IS NOT NULL AND peer_mirror = 0
|
|
52
52
|
AND (task_status = 'merged' OR (pr_status = 'merged' AND task_status != 'dropped'))
|
|
53
53
|
ORDER BY COALESCE(merged_at, updated_at)`
|
package/lib/tasks/held-routes.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The routes only the device that moved a task here may call on it
|
|
3
|
-
* (held.ts): message, stop, done and read. Each is refused for any other
|
|
4
|
-
* device, and for a task that isn't held.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { NextRequest, NextResponse } from "next/server";
|
|
8
|
-
import { db, type Session } from "../db";
|
|
9
|
-
import { callingDevice } from "../security/device-me";
|
|
10
|
-
import { holderRefusal } from "./held";
|
|
11
|
-
|
|
12
|
-
type Ctx = { params: Promise<{ id: string }> };
|
|
13
|
-
|
|
14
|
-
export async function heldRoute(
|
|
15
|
-
request: NextRequest,
|
|
16
|
-
{ params }: Ctx,
|
|
17
|
-
run: (task: Session) => Promise<object>
|
|
18
|
-
): Promise<NextResponse> {
|
|
19
|
-
const id = (await params).id;
|
|
20
|
-
const task = db.prepare(`SELECT * FROM sessions WHERE id = ?`).get(id) as
|
|
21
|
-
| Session
|
|
22
|
-
| undefined;
|
|
23
|
-
if (!task?.held_by_device)
|
|
24
|
-
return NextResponse.json({ error: "No such held task" }, { status: 404 });
|
|
25
|
-
const refusal = holderRefusal(task, callingDevice(request));
|
|
26
|
-
if (refusal) return NextResponse.json({ error: refusal }, { status: 403 });
|
|
27
|
-
try {
|
|
28
|
-
return NextResponse.json(await run(task));
|
|
29
|
-
} catch (error) {
|
|
30
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
31
|
-
return NextResponse.json({ error: message }, { status: 409 });
|
|
32
|
-
}
|
|
33
|
-
}
|