@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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from "next/server";
|
|
2
2
|
import { execFile } from "child_process";
|
|
3
3
|
import { promisify } from "util";
|
|
4
|
-
import { getDb, queries, type Session } from "@/lib/db";
|
|
4
|
+
import { getDb, queries, recordPr, type Session } from "@/lib/db";
|
|
5
5
|
import { gitPush } from "@/lib/git-exec";
|
|
6
6
|
|
|
7
7
|
const execFileAsync = promisify(execFile);
|
|
@@ -129,7 +129,7 @@ export async function GET(request: NextRequest, { params }: RouteParams) {
|
|
|
129
129
|
|
|
130
130
|
// Update session with PR info if found
|
|
131
131
|
if (pr) {
|
|
132
|
-
|
|
132
|
+
recordPr(db, id, pr);
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
return NextResponse.json({ pr });
|
|
@@ -197,7 +197,7 @@ export async function POST(request: NextRequest, { params }: RouteParams) {
|
|
|
197
197
|
);
|
|
198
198
|
|
|
199
199
|
// Save PR info to session
|
|
200
|
-
|
|
200
|
+
recordPr(db, id, pr);
|
|
201
201
|
|
|
202
202
|
return NextResponse.json({ pr }, { status: 201 });
|
|
203
203
|
} catch (error) {
|
|
@@ -16,9 +16,9 @@ export async function GET(
|
|
|
16
16
|
const { id } = await params;
|
|
17
17
|
const live = getSetup(id);
|
|
18
18
|
if (live) return NextResponse.json({ setup: live });
|
|
19
|
-
const session = db
|
|
20
|
-
|
|
21
|
-
| undefined;
|
|
19
|
+
const session = db
|
|
20
|
+
.prepare(`SELECT * FROM session_rows WHERE id = ?`)
|
|
21
|
+
.get(id) as Session | undefined;
|
|
22
22
|
if (!session)
|
|
23
23
|
return NextResponse.json({ error: "Session not found" }, { status: 404 });
|
|
24
24
|
const link = isRemoteHost(session.host_id) ? hostLink(session.host_id) : null;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
+
import { refuse } from "@/lib/tasks/contract/authorize";
|
|
1
2
|
import { NextRequest, NextResponse } from "next/server";
|
|
2
3
|
import { approvePlanNow } from "@/lib/models/plan-step";
|
|
3
|
-
import { callingDevice } from "@/lib/security/device-me";
|
|
4
|
-
import { planWaitRefusal } from "@/lib/tasks/held";
|
|
5
4
|
|
|
6
5
|
// Approves a plan-only task's plan: it continues on the implementation
|
|
7
6
|
// model. An optional message amends the plan (recorded as a scope change).
|
|
@@ -10,8 +9,8 @@ export async function POST(
|
|
|
10
9
|
{ params }: { params: Promise<{ id: string }> }
|
|
11
10
|
) {
|
|
12
11
|
const id = (await params).id;
|
|
13
|
-
const
|
|
14
|
-
if (
|
|
12
|
+
const refused = refuse(request, id, "plan");
|
|
13
|
+
if (refused) return refused;
|
|
15
14
|
try {
|
|
16
15
|
const body = await request.json().catch(() => ({}));
|
|
17
16
|
const message =
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
+
import { refuse } from "@/lib/tasks/contract/authorize";
|
|
1
2
|
import { NextRequest, NextResponse } from "next/server";
|
|
2
3
|
import { db, type Session } from "@/lib/db";
|
|
3
4
|
import { armTask, setArmed } from "@/lib/orchestrator/armed";
|
|
4
|
-
import { callingDevice } from "@/lib/security/device-me";
|
|
5
|
-
import { heldRefusal } from "@/lib/tasks/held";
|
|
6
5
|
|
|
7
6
|
// { armed: true } merges the task's PR by itself once every gate passes;
|
|
8
7
|
// false takes that back.
|
|
@@ -11,8 +10,8 @@ export async function POST(
|
|
|
11
10
|
{ params }: { params: Promise<{ id: string }> }
|
|
12
11
|
) {
|
|
13
12
|
const id = (await params).id;
|
|
14
|
-
const
|
|
15
|
-
if (
|
|
13
|
+
const refused = refuse(request, id, "open");
|
|
14
|
+
if (refused) return refused;
|
|
16
15
|
const { armed } = await request.json().catch(() => ({}));
|
|
17
16
|
if (typeof armed !== "boolean")
|
|
18
17
|
return NextResponse.json(
|
|
@@ -20,7 +19,9 @@ export async function POST(
|
|
|
20
19
|
{ status: 400 }
|
|
21
20
|
);
|
|
22
21
|
const task = db
|
|
23
|
-
.prepare(
|
|
22
|
+
.prepare(
|
|
23
|
+
`SELECT * FROM session_rows WHERE id = ? AND task_status IS NOT NULL`
|
|
24
|
+
)
|
|
24
25
|
.get((await params).id) as Session | undefined;
|
|
25
26
|
if (!task)
|
|
26
27
|
return NextResponse.json({ error: "No such task" }, { status: 404 });
|
|
@@ -2,7 +2,7 @@ import { NextRequest } from "next/server";
|
|
|
2
2
|
import { blockedReason } from "@/lib/tasks/state";
|
|
3
3
|
import { saidSinceLastMessage } from "@/lib/chat/store";
|
|
4
4
|
import { chatStateNow } from "@/lib/chat/runner";
|
|
5
|
-
import {
|
|
5
|
+
import { taskRoute } from "@/lib/tasks/contract/authorize";
|
|
6
6
|
|
|
7
7
|
// GET /api/tasks/:id/chat-facts - what its chat says about being blocked
|
|
8
8
|
// and whether a question is open, for the orchestrator that moved it here.
|
|
@@ -11,7 +11,7 @@ export async function GET(
|
|
|
11
11
|
request: NextRequest,
|
|
12
12
|
ctx: { params: Promise<{ id: string }> }
|
|
13
13
|
) {
|
|
14
|
-
return
|
|
14
|
+
return taskRoute(request, ctx, "holder", async (task) => {
|
|
15
15
|
if (task.view !== "chat") return { blocked: null, turn: null };
|
|
16
16
|
return {
|
|
17
17
|
blocked: blockedReason(saidSinceLastMessage(task.id)),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NextRequest } from "next/server";
|
|
2
2
|
import { chatTailText } from "@/lib/orchestrator/read";
|
|
3
|
-
import {
|
|
3
|
+
import { taskRoute } from "@/lib/tasks/contract/authorize";
|
|
4
4
|
|
|
5
5
|
// GET /api/tasks/:id/chat-tail?lines= - the last lines of its conversation,
|
|
6
6
|
// for the orchestrator that moved it here to read.
|
|
@@ -9,7 +9,7 @@ export async function GET(
|
|
|
9
9
|
ctx: { params: Promise<{ id: string }> }
|
|
10
10
|
) {
|
|
11
11
|
const lines = Number(request.nextUrl.searchParams.get("lines")) || 60;
|
|
12
|
-
return
|
|
12
|
+
return taskRoute(request, ctx, "holder", async (task) => ({
|
|
13
13
|
text: task.view === "chat" ? chatTailText(task.id, lines) : "",
|
|
14
14
|
}));
|
|
15
15
|
}
|
|
@@ -2,7 +2,7 @@ import { NextRequest, NextResponse } from "next/server";
|
|
|
2
2
|
import { db, queries, type Session } from "@/lib/db";
|
|
3
3
|
import { taskChecks } from "@/lib/tasks/checks-view";
|
|
4
4
|
import { LoopError, localTask } from "@/lib/tasks/loop-error";
|
|
5
|
-
import { isMirror } from "@/lib/tasks/
|
|
5
|
+
import { isMirror } from "@/lib/tasks/contract/start";
|
|
6
6
|
|
|
7
7
|
// GET /api/tasks/:id/checks - the PR head's checks, the tail of a failing
|
|
8
8
|
// job's log, and the review gate's verdict on the head. ?log=0 leaves the
|
|
@@ -2,7 +2,7 @@ import { NextRequest, NextResponse } from "next/server";
|
|
|
2
2
|
import { db, queries, type Session } from "@/lib/db";
|
|
3
3
|
import { taskDiff } from "@/lib/tasks/diff";
|
|
4
4
|
import { LoopError, localTask } from "@/lib/tasks/loop-error";
|
|
5
|
-
import { isMirror } from "@/lib/tasks/
|
|
5
|
+
import { isMirror } from "@/lib/tasks/contract/start";
|
|
6
6
|
|
|
7
7
|
// GET /api/tasks/:id/diff - the task's whole change, base to head: a file
|
|
8
8
|
// list with status and line counts; with ?file=<path>, that file's patch.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NextRequest } from "next/server";
|
|
2
2
|
import { doneSession } from "@/lib/done";
|
|
3
|
-
import {
|
|
3
|
+
import { taskRoute } from "@/lib/tasks/contract/authorize";
|
|
4
4
|
|
|
5
5
|
// POST /api/tasks/:id/done - its orchestrator finishes it: the agent stops
|
|
6
6
|
// and the worktree goes. Never a merge (noMerge): an open PR is refused
|
|
@@ -9,7 +9,7 @@ export async function POST(
|
|
|
9
9
|
request: NextRequest,
|
|
10
10
|
ctx: { params: Promise<{ id: string }> }
|
|
11
11
|
) {
|
|
12
|
-
return
|
|
12
|
+
return taskRoute(request, ctx, "holder", async (task) => ({
|
|
13
13
|
outcome: await doneSession(task.id, { by: "direct", noMerge: true }),
|
|
14
14
|
}));
|
|
15
15
|
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { refuse } from "@/lib/tasks/contract/authorize";
|
|
2
3
|
import { dropTask } from "@/lib/tasks";
|
|
3
4
|
|
|
4
5
|
export async function POST(
|
|
5
|
-
|
|
6
|
+
request: NextRequest,
|
|
6
7
|
{ params }: { params: Promise<{ id: string }> }
|
|
7
8
|
) {
|
|
9
|
+
const id = (await params).id;
|
|
10
|
+
const refused = refuse(request, id, "open");
|
|
11
|
+
if (refused) return refused;
|
|
8
12
|
try {
|
|
9
|
-
await dropTask(
|
|
13
|
+
await dropTask(id);
|
|
10
14
|
return NextResponse.json({ success: true });
|
|
11
15
|
} catch (error) {
|
|
12
16
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
+
import { refuse } from "@/lib/tasks/contract/authorize";
|
|
1
2
|
import { NextRequest, NextResponse } from "next/server";
|
|
2
3
|
import { exportOrResume } from "@/lib/tasks/move";
|
|
3
|
-
import { callingDevice } from "@/lib/security/device-me";
|
|
4
|
-
import { heldRefusal, planWaitRefusal } from "@/lib/tasks/held";
|
|
5
4
|
|
|
6
5
|
// POST /api/tasks/:id/export - stop the agent, push, hand over the task.
|
|
7
6
|
// The caller confirms with /moved, or restarts it here with /resume; if it
|
|
@@ -11,9 +10,8 @@ export async function POST(
|
|
|
11
10
|
{ params }: { params: Promise<{ id: string }> }
|
|
12
11
|
) {
|
|
13
12
|
const id = (await params).id;
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
if (held) return NextResponse.json({ error: held }, { status: 403 });
|
|
13
|
+
const refused = refuse(request, id, "plan");
|
|
14
|
+
if (refused) return refused;
|
|
17
15
|
try {
|
|
18
16
|
const { to } = await request.json().catch(() => ({}));
|
|
19
17
|
const bundle = await exportOrResume(
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { refuse } from "@/lib/tasks/contract/authorize";
|
|
1
2
|
import { NextRequest, NextResponse } from "next/server";
|
|
2
3
|
import { signOffTask } from "@/lib/tasks";
|
|
3
4
|
import { callingDevice } from "@/lib/security/device-me";
|
|
4
5
|
import { isBranchName } from "@/lib/git";
|
|
5
|
-
import { heldRefusal } from "@/lib/tasks/held";
|
|
6
6
|
|
|
7
7
|
// head: merge only if the PR is still at this commit; the answer names the
|
|
8
8
|
// commit it was pinned to, so another machine forwarding a merge can check.
|
|
@@ -11,8 +11,8 @@ export async function POST(
|
|
|
11
11
|
{ params }: { params: Promise<{ id: string }> }
|
|
12
12
|
) {
|
|
13
13
|
const id = (await params).id;
|
|
14
|
-
const
|
|
15
|
-
if (
|
|
14
|
+
const refused = refuse(request, id, "open");
|
|
15
|
+
if (refused) return refused;
|
|
16
16
|
const { head, base } = await request.json().catch(() => ({}));
|
|
17
17
|
if (
|
|
18
18
|
head !== undefined &&
|
|
@@ -3,7 +3,7 @@ import { statusDetector } from "@/lib/status-detector";
|
|
|
3
3
|
import { deliver } from "@/lib/bus";
|
|
4
4
|
import { sendChatConfirmed } from "@/lib/chat/runner";
|
|
5
5
|
import { cleanRemoteText } from "@/lib/hosts/remote-api";
|
|
6
|
-
import {
|
|
6
|
+
import { taskRoute } from "@/lib/tasks/contract/authorize";
|
|
7
7
|
|
|
8
8
|
const MAX_LINE = 40_000;
|
|
9
9
|
|
|
@@ -15,7 +15,7 @@ export async function POST(
|
|
|
15
15
|
ctx: { params: Promise<{ id: string }> }
|
|
16
16
|
) {
|
|
17
17
|
const body = await request.json().catch(() => ({}));
|
|
18
|
-
return
|
|
18
|
+
return taskRoute(request, ctx, "holder", async (task) => {
|
|
19
19
|
const line = typeof body.line === "string" ? body.line.trim() : "";
|
|
20
20
|
if (!line || line.length > MAX_LINE) throw new Error("Bad message");
|
|
21
21
|
const id = typeof body.id === "string" ? body.id.slice(0, 100) : undefined;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { refuse } from "@/lib/tasks/contract/authorize";
|
|
1
2
|
import { NextRequest, NextResponse } from "next/server";
|
|
2
3
|
import { resumeOnModel } from "@/lib/models/resume";
|
|
3
4
|
|
|
@@ -8,10 +9,13 @@ export async function POST(
|
|
|
8
9
|
request: NextRequest,
|
|
9
10
|
{ params }: { params: Promise<{ id: string }> }
|
|
10
11
|
) {
|
|
12
|
+
const id = (await params).id;
|
|
13
|
+
const refused = refuse(request, id, "open");
|
|
14
|
+
if (refused) return refused;
|
|
11
15
|
const { model, message } = await request.json().catch(() => ({}));
|
|
12
16
|
try {
|
|
13
17
|
const result = await resumeOnModel(
|
|
14
|
-
|
|
18
|
+
id,
|
|
15
19
|
model,
|
|
16
20
|
typeof message === "string" && message.trim() ? message : undefined
|
|
17
21
|
);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { refuse } from "@/lib/tasks/contract/authorize";
|
|
2
3
|
import { moveTask } from "@/lib/tasks";
|
|
3
4
|
import { getProgress } from "@/lib/tasks/move-progress";
|
|
4
5
|
|
|
@@ -7,9 +8,12 @@ export async function POST(
|
|
|
7
8
|
request: NextRequest,
|
|
8
9
|
{ params }: { params: Promise<{ id: string }> }
|
|
9
10
|
) {
|
|
11
|
+
const id = (await params).id;
|
|
12
|
+
const refused = refuse(request, id, "open");
|
|
13
|
+
if (refused) return refused;
|
|
10
14
|
try {
|
|
11
15
|
const { hostId } = await request.json();
|
|
12
|
-
const session = await moveTask(
|
|
16
|
+
const session = await moveTask(id, String(hostId || ""));
|
|
13
17
|
return NextResponse.json({ session });
|
|
14
18
|
} catch (error) {
|
|
15
19
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from "next/server";
|
|
2
2
|
import { markMoved } from "@/lib/tasks/move";
|
|
3
|
-
import {
|
|
4
|
-
import { heldRefusal } from "@/lib/tasks/held";
|
|
3
|
+
import { refuse } from "@/lib/tasks/contract/authorize";
|
|
5
4
|
import { cleanUpMovedAway } from "@/lib/tasks/moved-away";
|
|
6
5
|
import { runInBackground } from "@/lib/async-operations";
|
|
7
6
|
|
|
@@ -11,14 +10,13 @@ export async function POST(
|
|
|
11
10
|
{ params }: { params: Promise<{ id: string }> }
|
|
12
11
|
) {
|
|
13
12
|
const id = (await params).id;
|
|
14
|
-
const
|
|
15
|
-
if (
|
|
13
|
+
const refused = refuse(request, id, "open");
|
|
14
|
+
if (refused) return refused;
|
|
16
15
|
const { to } = await request.json().catch(() => ({}));
|
|
17
16
|
markMoved(id, String(to || "another machine").slice(0, 100));
|
|
18
17
|
// A task its orchestrator took back: its agent and worktree here go.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}, `moved-away-${id}`);
|
|
18
|
+
runInBackground(async () => {
|
|
19
|
+
await cleanUpMovedAway(id);
|
|
20
|
+
}, `moved-away-${id}`);
|
|
23
21
|
return NextResponse.json({ success: true });
|
|
24
22
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
+
import { refuse } from "@/lib/tasks/contract/authorize";
|
|
1
2
|
import fs from "fs/promises";
|
|
2
3
|
import path from "path";
|
|
3
4
|
import { NextRequest, NextResponse } from "next/server";
|
|
4
5
|
import { db, type Session } from "@/lib/db";
|
|
5
|
-
import { callingDevice } from "@/lib/security/device-me";
|
|
6
|
-
import { heldRefusal, planWaitRefusal } from "@/lib/tasks/held";
|
|
7
6
|
import { MAX_PLAN_BYTES } from "@/lib/tasks/move-bundle";
|
|
8
7
|
import { PLAN_FILE } from "@/lib/models/plan-step";
|
|
9
8
|
import { expandHome } from "@/lib/tasks/session";
|
|
@@ -15,11 +14,10 @@ export async function GET(
|
|
|
15
14
|
{ params }: { params: Promise<{ id: string }> }
|
|
16
15
|
) {
|
|
17
16
|
const id = (await params).id;
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
if (refusal) return NextResponse.json({ error: refusal }, { status: 403 });
|
|
17
|
+
const refused = refuse(request, id, "plan");
|
|
18
|
+
if (refused) return refused;
|
|
21
19
|
const s = db
|
|
22
|
-
.prepare(`SELECT plan_phase, worktree_path FROM
|
|
20
|
+
.prepare(`SELECT plan_phase, worktree_path FROM session_rows WHERE id = ?`)
|
|
23
21
|
.get(id) as Pick<Session, "plan_phase" | "worktree_path"> | undefined;
|
|
24
22
|
if (!s?.worktree_path)
|
|
25
23
|
return NextResponse.json({ error: "No such task" }, { status: 404 });
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { refuse } from "@/lib/tasks/contract/authorize";
|
|
1
2
|
import { NextRequest, NextResponse } from "next/server";
|
|
2
3
|
import { db, type Session } from "@/lib/db";
|
|
3
4
|
import { notifyStatusChanged } from "@/lib/status/hub";
|
|
5
|
+
import { setPlan } from "@/lib/db/session-parts";
|
|
4
6
|
|
|
5
7
|
// Saad's edits to a waiting plan's pieces: which to fold into the one before
|
|
6
8
|
// and which to leave out. Kept on the task and applied when the plan is
|
|
@@ -10,6 +12,8 @@ export async function PUT(
|
|
|
10
12
|
{ params }: { params: Promise<{ id: string }> }
|
|
11
13
|
) {
|
|
12
14
|
const { id } = await params;
|
|
15
|
+
const refused = refuse(request, id, "open");
|
|
16
|
+
if (refused) return refused;
|
|
13
17
|
const body = await request.json().catch(() => ({}));
|
|
14
18
|
const ints = (v: unknown) =>
|
|
15
19
|
Array.isArray(v)
|
|
@@ -18,17 +22,14 @@ export async function PUT(
|
|
|
18
22
|
const merge = ints(body?.merge);
|
|
19
23
|
const drop = ints(body?.drop).filter((n) => !merge.includes(n));
|
|
20
24
|
const session = db
|
|
21
|
-
.prepare(`SELECT plan_phase FROM
|
|
25
|
+
.prepare(`SELECT plan_phase FROM session_rows WHERE id = ?`)
|
|
22
26
|
.get(id) as Pick<Session, "plan_phase"> | undefined;
|
|
23
27
|
if (session?.plan_phase !== "awaiting")
|
|
24
28
|
return NextResponse.json(
|
|
25
29
|
{ error: "That task isn't waiting for plan approval" },
|
|
26
30
|
{ status: 409 }
|
|
27
31
|
);
|
|
28
|
-
db
|
|
29
|
-
JSON.stringify({ merge, drop }),
|
|
30
|
-
id
|
|
31
|
-
);
|
|
32
|
+
setPlan(db, id, { plan_edits: JSON.stringify({ merge, drop }) });
|
|
32
33
|
notifyStatusChanged();
|
|
33
34
|
return NextResponse.json({ success: true, merge, drop });
|
|
34
35
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { refuse } from "@/lib/tasks/contract/authorize";
|
|
2
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
3
|
import { setPriority } from "@/lib/tasks/queue";
|
|
3
4
|
import { isPriority } from "@/lib/tasks/schedule-types";
|
|
4
5
|
|
|
@@ -9,6 +10,8 @@ export async function PATCH(
|
|
|
9
10
|
{ params }: { params: Promise<{ id: string }> }
|
|
10
11
|
) {
|
|
11
12
|
const { id } = await params;
|
|
13
|
+
const refused = refuse(request as NextRequest, id, "open");
|
|
14
|
+
if (refused) return refused;
|
|
12
15
|
const body = (await request.json().catch(() => null)) as {
|
|
13
16
|
priority?: unknown;
|
|
14
17
|
} | null;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
+
import { refuse } from "@/lib/tasks/contract/authorize";
|
|
1
2
|
import { NextRequest, NextResponse } from "next/server";
|
|
2
3
|
import { resumeHere } from "@/lib/tasks/move-recover";
|
|
3
|
-
import { callingDevice } from "@/lib/security/device-me";
|
|
4
|
-
import { heldRefusal } from "@/lib/tasks/held";
|
|
5
4
|
|
|
6
5
|
// POST /api/tasks/:id/resume {force?} - start its agent again here, on its
|
|
7
6
|
// conversation. A task left moving asks its target first unless forced.
|
|
@@ -10,8 +9,8 @@ export async function POST(
|
|
|
10
9
|
{ params }: { params: Promise<{ id: string }> }
|
|
11
10
|
) {
|
|
12
11
|
const id = (await params).id;
|
|
13
|
-
const
|
|
14
|
-
if (
|
|
12
|
+
const refused = refuse(request, id, "open");
|
|
13
|
+
if (refused) return refused;
|
|
15
14
|
const { force } = await request.json().catch(() => ({}));
|
|
16
15
|
try {
|
|
17
16
|
const result = await resumeHere(id, force === true);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NextRequest } from "next/server";
|
|
2
2
|
import { stopSession } from "@/lib/sessions/stop";
|
|
3
|
-
import {
|
|
3
|
+
import { taskRoute } from "@/lib/tasks/contract/authorize";
|
|
4
4
|
|
|
5
5
|
// POST /api/tasks/:id/stop - its orchestrator stops the agent; the
|
|
6
6
|
// conversation and worktree are kept.
|
|
@@ -8,7 +8,7 @@ export async function POST(
|
|
|
8
8
|
request: NextRequest,
|
|
9
9
|
ctx: { params: Promise<{ id: string }> }
|
|
10
10
|
) {
|
|
11
|
-
return
|
|
11
|
+
return taskRoute(request, ctx, "holder", async (task) => ({
|
|
12
12
|
text: await stopSession(task),
|
|
13
13
|
}));
|
|
14
14
|
}
|
package/app/api/tasks/route.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { NextRequest, NextResponse } from "next/server";
|
|
|
3
3
|
import { createTask, listTasks, movedTasks } from "@/lib/tasks";
|
|
4
4
|
import { stampPriority, startOrQueue } from "@/lib/tasks/queue";
|
|
5
5
|
import { isPriority } from "@/lib/tasks/schedule-types";
|
|
6
|
-
import { TASK_CAPABILITIES } from "@/lib/tasks/
|
|
6
|
+
import { TASK_CAPABILITIES } from "@/lib/tasks/contract/types";
|
|
7
7
|
import { ensureProject, type ProjectRef } from "@/lib/tasks/project-ref";
|
|
8
8
|
import { callingDevice } from "@/lib/security/device-me";
|
|
9
9
|
import {
|
package/lib/approvals/enable.ts
CHANGED
|
@@ -29,7 +29,7 @@ async function sweep(): Promise<void> {
|
|
|
29
29
|
}
|
|
30
30
|
const plans = db
|
|
31
31
|
.prepare(
|
|
32
|
-
`SELECT id, host_id, plan_waits_for_caller FROM
|
|
32
|
+
`SELECT id, host_id, plan_waits_for_caller FROM session_rows WHERE plan_phase = 'awaiting' AND task_status = 'running'`
|
|
33
33
|
)
|
|
34
34
|
.all() as {
|
|
35
35
|
id: string;
|
package/lib/away/report.ts
CHANGED
|
@@ -137,7 +137,7 @@ function merged(
|
|
|
137
137
|
const rows = db
|
|
138
138
|
.prepare(
|
|
139
139
|
`SELECT s.id, s.name, p.name AS project, p.id AS project_id, s.pr_number, s.pr_url
|
|
140
|
-
FROM
|
|
140
|
+
FROM session_rows s JOIN projects p ON p.id = s.project_id
|
|
141
141
|
WHERE p.workspace_id = ? AND s.task_status = 'merged'
|
|
142
142
|
AND s.merged_at >= ? AND s.merged_at <= ?
|
|
143
143
|
ORDER BY s.merged_at, s.id`
|
|
@@ -178,7 +178,7 @@ function inFlight(workspaceId: string, awayId: number): InFlightItem[] {
|
|
|
178
178
|
.prepare(
|
|
179
179
|
`SELECT s.id, s.name, p.name AS project, s.pr_number, s.pr_url,
|
|
180
180
|
s.merge_armed_at AS armed_at
|
|
181
|
-
FROM
|
|
181
|
+
FROM session_rows s JOIN projects p ON p.id = s.project_id
|
|
182
182
|
WHERE p.workspace_id = ? AND s.role IS NULL AND s.archived_at IS NULL
|
|
183
183
|
AND s.task_status = 'running'
|
|
184
184
|
AND EXISTS (SELECT 1 FROM away_actions a WHERE a.away_id = ?
|
|
@@ -224,7 +224,7 @@ function failed(workspaceId: string, away: AwayRow, end: string) {
|
|
|
224
224
|
const deploys = db
|
|
225
225
|
.prepare(
|
|
226
226
|
`SELECT a.detail, s.name, s.pr_number, s.pr_url FROM away_actions a
|
|
227
|
-
LEFT JOIN
|
|
227
|
+
LEFT JOIN session_rows s ON s.id = a.subject
|
|
228
228
|
WHERE a.away_id = ? AND a.workspace_id = ? AND a.kind = 'deploy_failed'
|
|
229
229
|
ORDER BY a.id`
|
|
230
230
|
)
|
|
@@ -236,7 +236,7 @@ function failed(workspaceId: string, away: AwayRow, end: string) {
|
|
|
236
236
|
}[];
|
|
237
237
|
const ended = db
|
|
238
238
|
.prepare(
|
|
239
|
-
`SELECT s.name, s.task_status, s.pr_number, s.pr_url FROM
|
|
239
|
+
`SELECT s.name, s.task_status, s.pr_number, s.pr_url FROM session_rows s
|
|
240
240
|
JOIN projects p ON p.id = s.project_id
|
|
241
241
|
WHERE p.workspace_id = ? AND s.task_status IN ('dropped', 'done')
|
|
242
242
|
AND s.task_ended_at >= ? AND s.task_ended_at <= ?
|
package/lib/away/run.ts
CHANGED
|
@@ -69,7 +69,7 @@ function orchestrated(): { workspaceId: string; orchestratorId: string }[] {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
const sessionRow = (id: string) =>
|
|
72
|
-
db.prepare(`SELECT * FROM
|
|
72
|
+
db.prepare(`SELECT * FROM session_rows WHERE id = ?`).get(id) as
|
|
73
73
|
| Session
|
|
74
74
|
| undefined;
|
|
75
75
|
|
|
@@ -330,7 +330,7 @@ async function checkDeploys(
|
|
|
330
330
|
const found: DeployFinding[] = [];
|
|
331
331
|
const merged = db
|
|
332
332
|
.prepare(
|
|
333
|
-
`SELECT s.* FROM
|
|
333
|
+
`SELECT s.* FROM session_rows s JOIN projects p ON p.id = s.project_id
|
|
334
334
|
WHERE p.workspace_id = ? AND s.task_status = 'merged' AND s.merged_at >= ?
|
|
335
335
|
AND NOT EXISTS (SELECT 1 FROM away_actions a
|
|
336
336
|
WHERE a.away_id = ? AND a.key = 'deploy:' || s.id)`
|
package/lib/bookmarks/store.ts
CHANGED
|
@@ -57,7 +57,7 @@ type Joined = Row & {
|
|
|
57
57
|
// The bookmark with its chat's state as of now.
|
|
58
58
|
const SELECT = `
|
|
59
59
|
SELECT b.*, s.id AS s_id, s.task_status, s.archived_at
|
|
60
|
-
FROM chat_bookmarks b LEFT JOIN
|
|
60
|
+
FROM chat_bookmarks b LEFT JOIN session_rows s ON s.id = b.session_id`;
|
|
61
61
|
|
|
62
62
|
function toBookmark(r: Joined): Bookmark {
|
|
63
63
|
return {
|
|
@@ -141,7 +141,7 @@ export function createBookmark(input: {
|
|
|
141
141
|
throw new BookmarkError("scope must be 'item' or 'turn'", 400);
|
|
142
142
|
const note = cleanNote(input.note);
|
|
143
143
|
const session = db
|
|
144
|
-
.prepare(`SELECT * FROM
|
|
144
|
+
.prepare(`SELECT * FROM session_rows WHERE id = ?`)
|
|
145
145
|
.get(sessionId) as Session | undefined;
|
|
146
146
|
if (!session) throw new BookmarkError("Session not found", 404);
|
|
147
147
|
const existing = db
|
|
@@ -308,7 +308,7 @@ export function insertMovedBookmarks(
|
|
|
308
308
|
): void {
|
|
309
309
|
if (!rows.length) return;
|
|
310
310
|
const session = db
|
|
311
|
-
.prepare(`SELECT * FROM
|
|
311
|
+
.prepare(`SELECT * FROM session_rows WHERE id = ?`)
|
|
312
312
|
.get(sessionId) as Session | undefined;
|
|
313
313
|
if (!session) return;
|
|
314
314
|
const workspace = sessionWorkspace(session);
|
package/lib/bus/held-reply.ts
CHANGED
|
@@ -68,7 +68,7 @@ export function pendingReplies(device: string, limit = 50): PendingReply[] {
|
|
|
68
68
|
db
|
|
69
69
|
.prepare(
|
|
70
70
|
`SELECT m.id, m.from_id AS fromId, m.body, m.created_at AS createdAt
|
|
71
|
-
FROM bus_messages m JOIN
|
|
71
|
+
FROM bus_messages m JOIN session_rows s ON s.id = m.from_id
|
|
72
72
|
WHERE m.relay_state = 'pending' AND s.held_by_device = ?
|
|
73
73
|
ORDER BY m.id LIMIT ?`
|
|
74
74
|
)
|
|
@@ -81,7 +81,7 @@ export function ackReplies(device: string, ids: number[]): void {
|
|
|
81
81
|
const mark = db.prepare(
|
|
82
82
|
`UPDATE bus_messages SET relay_state = 'relayed', delivered_at = datetime('now')
|
|
83
83
|
WHERE id = ? AND relay_state = 'pending'
|
|
84
|
-
AND from_id IN (SELECT id FROM
|
|
84
|
+
AND from_id IN (SELECT id FROM session_rows WHERE held_by_device = ?)`
|
|
85
85
|
);
|
|
86
86
|
db.transaction(() => {
|
|
87
87
|
for (const id of ids) mark.run(id, device);
|
package/lib/bus/index.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { deliverToPane, type Delivery } from "./delivery";
|
|
|
15
15
|
import { resolveRef, wasNames, type Candidate } from "./resolve";
|
|
16
16
|
import { tmuxPane } from "./tmux-pane";
|
|
17
17
|
import { pauseHoldOf, pausedNote } from "../tasks/pause-hold";
|
|
18
|
-
import { isHeldRemote, relayToHeldTask } from "../tasks/held-
|
|
18
|
+
import { isHeldRemote, relayToHeldTask } from "../tasks/contract/held-calls";
|
|
19
19
|
import {
|
|
20
20
|
assertTakesMessages,
|
|
21
21
|
findQueuedTarget,
|
|
@@ -43,7 +43,7 @@ export interface Peer {
|
|
|
43
43
|
function liveSessions(): Session[] {
|
|
44
44
|
return db
|
|
45
45
|
.prepare(
|
|
46
|
-
`SELECT * FROM
|
|
46
|
+
`SELECT * FROM session_rows WHERE (task_status IS NULL OR task_status = 'running')
|
|
47
47
|
AND archived_at IS NULL
|
|
48
48
|
ORDER BY updated_at DESC`
|
|
49
49
|
)
|
|
@@ -63,7 +63,7 @@ function candidates(sessions: Session[]): Candidate[] {
|
|
|
63
63
|
|
|
64
64
|
const sessionById = (id: string | null | undefined) =>
|
|
65
65
|
id
|
|
66
|
-
? (db.prepare(`SELECT * FROM
|
|
66
|
+
? (db.prepare(`SELECT * FROM session_rows WHERE id = ?`).get(id) as
|
|
67
67
|
| Session
|
|
68
68
|
| undefined)
|
|
69
69
|
: undefined;
|
|
@@ -28,7 +28,7 @@ async function gitStatus(cwd: string): Promise<string> {
|
|
|
28
28
|
export async function compactContext(sessionId: string): Promise<string> {
|
|
29
29
|
const session = db
|
|
30
30
|
.prepare(
|
|
31
|
-
`SELECT working_directory, task_prompt, role FROM
|
|
31
|
+
`SELECT working_directory, task_prompt, role FROM session_rows WHERE id = ?`
|
|
32
32
|
)
|
|
33
33
|
.get(sessionId) as
|
|
34
34
|
| Pick<Session, "working_directory" | "task_prompt" | "role">
|
package/lib/chat/hold.ts
CHANGED
|
@@ -15,10 +15,10 @@ import { db } from "../db";
|
|
|
15
15
|
|
|
16
16
|
// The SQL form, for queries that take the session id as @sid.
|
|
17
17
|
export const HELD_SQL = `(
|
|
18
|
-
EXISTS (SELECT 1 FROM
|
|
18
|
+
EXISTS (SELECT 1 FROM session_rows WHERE id = @sid AND task_status = 'moving')
|
|
19
19
|
OR EXISTS (SELECT 1 FROM stack_items i JOIN stacks s ON s.id = i.stack_id
|
|
20
20
|
WHERE i.session_id = @sid AND s.status = 'landing')
|
|
21
|
-
OR EXISTS (SELECT 1 FROM
|
|
21
|
+
OR EXISTS (SELECT 1 FROM session_rows WHERE id = @sid
|
|
22
22
|
AND pause_state IN ('pausing', 'paused'))
|
|
23
23
|
)`;
|
|
24
24
|
|
|
@@ -58,7 +58,7 @@ const holdRow = (sessionId: string) =>
|
|
|
58
58
|
`SELECT task_status, moved_to, quick_of, pause_state, paused_until,
|
|
59
59
|
EXISTS (SELECT 1 FROM stack_items i JOIN stacks s ON s.id = i.stack_id
|
|
60
60
|
WHERE i.session_id = @sid AND s.status = 'landing') AS landing
|
|
61
|
-
FROM
|
|
61
|
+
FROM session_rows WHERE id = @sid`
|
|
62
62
|
)
|
|
63
63
|
.get({ sid: sessionId }) as HoldRow | undefined;
|
|
64
64
|
|
package/lib/chat/refresh.ts
CHANGED
|
@@ -163,7 +163,7 @@ export function autoCompactEnv(sessionId: string): Record<string, string> {
|
|
|
163
163
|
const row = db
|
|
164
164
|
.prepare(
|
|
165
165
|
`SELECT role, task_status, plan_phase, started_by, model_provider
|
|
166
|
-
FROM
|
|
166
|
+
FROM session_rows WHERE id = ?`
|
|
167
167
|
)
|
|
168
168
|
.get(sessionId) as
|
|
169
169
|
| {
|