@saadnvd1/agent-os 0.3.700 → 0.3.723
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/app/api/agent-roles/route.test.ts +67 -0
- package/app/api/agent-roles/route.ts +61 -0
- package/app/api/analyst/[id]/recommendations/[recId]/again/route.ts +15 -0
- package/app/api/analyst/[id]/recommendations/[recId]/dismiss/route.ts +13 -0
- package/app/api/analyst/[id]/recommendations/[recId]/send/route.ts +13 -0
- package/app/api/analyst/[id]/review/route.ts +22 -0
- package/app/api/analyst/[id]/route.ts +44 -0
- package/app/api/analyst/route.ts +10 -0
- package/app/api/bus/groups/route.ts +44 -0
- package/app/api/bus/spawn/route.ts +34 -2
- package/app/api/claims/[id]/route.ts +20 -0
- package/app/api/claims/check/route.ts +23 -0
- package/app/api/claims/check.test.ts +113 -0
- package/app/api/claims/exchange/route.ts +42 -0
- package/app/api/claims/outside.test.ts +90 -0
- package/app/api/claims/route.test.ts +193 -0
- package/app/api/claims/route.ts +73 -0
- package/app/api/hosts/[id]/route.ts +9 -1
- package/app/api/models/availability/route.ts +9 -0
- package/app/api/models/choices/route.ts +9 -0
- package/app/api/peer/unlink/route.ts +17 -0
- package/app/api/peers/[hostId]/audit/route.ts +14 -0
- package/app/api/peers/[hostId]/route.ts +20 -0
- package/app/api/peers/gate.test.ts +64 -1
- package/app/api/peers/pending/route.test.ts +37 -0
- package/app/api/peers/pending/route.ts +11 -4
- package/app/api/session-groups/[id]/restore/route.ts +11 -0
- package/app/api/session-groups/[id]/route.ts +25 -0
- package/app/api/session-groups/order/route.ts +15 -0
- package/app/api/session-groups/route.ts +21 -0
- package/app/api/sessions/[id]/analyst/route.ts +11 -0
- package/app/api/sessions/[id]/crew/route.ts +38 -0
- package/app/api/sessions/[id]/export/estimate/route.ts +23 -0
- package/app/api/sessions/[id]/export/route.test.ts +125 -0
- package/app/api/sessions/[id]/export/route.ts +34 -0
- package/app/api/sessions/[id]/fork/route.ts +3 -0
- package/app/api/sessions/[id]/group/route.ts +18 -0
- package/app/api/sessions/[id]/model/route.ts +49 -0
- package/app/api/sessions/[id]/route.ts +14 -4
- package/app/api/sessions/[id]/summarize/route.ts +2 -0
- package/app/api/sessions/claims.test.ts +122 -0
- package/app/api/sessions/group/route.ts +17 -0
- package/app/api/sessions/route.ts +31 -14
- package/app/page.tsx +10 -0
- package/components/Chat/Messages.tsx +104 -10
- package/components/Claims/ClaimFiles.tsx +38 -0
- package/components/Claims/ClaimRow.tsx +82 -0
- package/components/Claims/ClaimsSheet.tsx +176 -0
- package/components/Claims/OverlapPill.tsx +31 -0
- package/components/Claims/StartClaimsNotice.tsx +70 -0
- package/components/Claims/claims-ui.test.ts +161 -0
- package/components/Claims/format.ts +57 -0
- package/components/Claims/index.ts +3 -0
- package/components/Draft/DraftChips.tsx +6 -1
- package/components/Draft/DraftPanel.tsx +90 -3
- package/components/Draft/ModelUnavailableDialog.tsx +78 -0
- package/components/Draft/RoleChips.tsx +110 -0
- package/components/Export/ExportActions.tsx +136 -0
- package/components/Export/ExportSheet.tsx +217 -0
- package/components/Export/RangePicker.tsx +144 -0
- package/components/Export/Seg.tsx +38 -0
- package/components/Export/TurnSelectBar.tsx +45 -0
- package/components/Hosts/AccountMachines.tsx +32 -18
- package/components/Hosts/HostRow.tsx +155 -14
- package/components/Hosts/IncomingLinkSheet.tsx +28 -10
- package/components/Hosts/PeerActivitySheet.tsx +111 -0
- package/components/Models/AgentRoleCard.tsx +114 -0
- package/components/Models/AgentRolesSection.tsx +132 -0
- package/components/Models/ModelBadges.tsx +48 -0
- package/components/Models/ModelsSection.tsx +4 -2
- package/components/Models/RoleModelSelect.tsx +87 -0
- package/components/Pane/DesktopTabBar.tsx +2 -0
- package/components/Pane/MobileTabBar.tsx +12 -0
- package/components/Pane/PaneMenu.tsx +10 -0
- package/components/SessionList/GroupShelves.tsx +195 -0
- package/components/SessionList/MoveToGroupMenu.tsx +105 -0
- package/components/SessionList/RowSubtitle.tsx +7 -1
- package/components/SessionList/SelectionToolbar.tsx +52 -1
- package/components/SessionList/SessionRow.tsx +19 -7
- package/components/SessionList/SessionRowMenu.tsx +23 -0
- package/components/SessionList/SessionShelves.tsx +5 -0
- package/components/SessionList/SidebarBody.tsx +6 -0
- package/components/SessionList/SidebarContextMenu.test.ts +104 -0
- package/components/SessionList/SidebarContextMenu.tsx +59 -0
- package/components/SessionList/SidebarHeader.tsx +5 -0
- package/components/SessionList/WorkspaceSwitcher.tsx +23 -6
- package/components/SessionList/index.tsx +18 -14
- package/components/SessionList/useSidebarData.ts +10 -0
- package/components/Sessions/RolePill.tsx +196 -0
- package/components/Settings/SettingsDialog.tsx +9 -0
- package/components/Share/ShareDialog.tsx +13 -0
- package/components/ui/notice.tsx +46 -0
- package/components/ui/status-pill.tsx +39 -0
- package/data/analyst/index.ts +3 -0
- package/data/analyst/keys.ts +6 -0
- package/data/analyst/mutations.ts +116 -0
- package/data/analyst/queries.ts +35 -0
- package/data/claims/index.ts +2 -0
- package/data/claims/keys.ts +5 -0
- package/data/claims/queries.ts +47 -0
- package/data/exports/index.ts +2 -0
- package/data/exports/keys.ts +3 -0
- package/data/exports/queries.ts +51 -0
- package/data/models/index.ts +98 -0
- package/data/peers/keys.ts +1 -0
- package/data/peers/queries.ts +31 -0
- package/data/push/topics.ts +9 -0
- package/data/session-groups/index.ts +156 -0
- package/data/sessions/index.ts +0 -1
- package/data/sessions/pin.ts +2 -6
- package/data/sessions/queries.ts +36 -29
- package/data/sessions/shelf.ts +2 -6
- package/data/tasks/index.ts +2 -0
- package/hooks/useSessions.ts +0 -12
- package/lib/__fixtures__/peer-rpc.ts +65 -33
- package/lib/agents/brief.ts +1 -0
- package/lib/agents/spawn.ts +4 -0
- package/lib/analyst/__fixtures__/items.ts +57 -0
- package/lib/analyst/__fixtures__/mocks.ts +49 -0
- package/lib/analyst/brief.ts +17 -0
- package/lib/analyst/buildDigest.ts +204 -0
- package/lib/analyst/createLink.ts +98 -0
- package/lib/analyst/endLink.ts +31 -0
- package/lib/analyst/forward.ts +109 -0
- package/lib/analyst/index.ts +16 -0
- package/lib/analyst/linkView.ts +38 -0
- package/lib/analyst/onTurnEnd.ts +119 -0
- package/lib/analyst/parseReview.ts +41 -0
- package/lib/analyst/pushDigest.ts +143 -0
- package/lib/analyst/resume.ts +35 -0
- package/lib/analyst/route.ts +50 -0
- package/lib/analyst/signals.ts +121 -0
- package/lib/analyst/store.ts +303 -0
- package/lib/analyst/types.ts +140 -0
- package/lib/bus/condense.ts +5 -1
- package/lib/bus/index.ts +13 -0
- package/lib/bus/read-scope.ts +12 -0
- package/lib/bus/role-alias.ts +70 -0
- package/lib/bus/targets.ts +3 -0
- package/lib/chat/drivers/opencode-mapper.ts +55 -1
- package/lib/chat/drivers/pi-approvals.ts +7 -4
- package/lib/chat/drivers/pi-mapper.ts +38 -0
- package/lib/chat/drivers/pi-text-tools.ts +104 -0
- package/lib/chat/drivers/pi.ts +8 -3
- package/lib/chat/drivers/turn-items.ts +36 -11
- package/lib/chat/drivers/turn-outcome.ts +108 -0
- package/lib/chat/events.ts +8 -0
- package/lib/chat/registry.ts +13 -0
- package/lib/chat/store.ts +19 -0
- package/lib/chat/turns.ts +1 -0
- package/lib/chat/worker/extras.ts +4 -2
- package/lib/claims/collectLocal.ts +123 -0
- package/lib/claims/describe.ts +41 -0
- package/lib/claims/exchange.ts +91 -0
- package/lib/claims/goal.ts +45 -0
- package/lib/claims/index.ts +10 -0
- package/lib/claims/loop.ts +51 -0
- package/lib/claims/machine.ts +21 -0
- package/lib/claims/mine.ts +43 -0
- package/lib/claims/notifyOverlaps.ts +101 -0
- package/lib/claims/onMerged.ts +85 -0
- package/lib/claims/outside.ts +36 -0
- package/lib/claims/overlap.ts +87 -0
- package/lib/claims/overlapCache.ts +86 -0
- package/lib/claims/overlapText.ts +36 -0
- package/lib/claims/peers.ts +56 -0
- package/lib/claims/startCheck.ts +100 -0
- package/lib/claims/startWarning.ts +29 -0
- package/lib/claims/store.ts +366 -0
- package/lib/claims/types.ts +102 -0
- package/lib/claims/view.ts +80 -0
- package/lib/claims/yield.ts +24 -0
- package/lib/db/changes.ts +12 -0
- package/lib/db/migrations/20261020T1400_work_claims.ts +35 -0
- package/lib/db/migrations/20261020T1515_peer_unlinked.ts +22 -0
- package/lib/db/migrations/20261020T1700_clean_connect_names.ts +18 -0
- package/lib/db/migrations/20261020T1800_session_groups.ts +49 -0
- package/lib/db/migrations/20261020T1900_task_queue_after_claim.ts +13 -0
- package/lib/db/migrations/20261020T2000_claim_notices.ts +20 -0
- package/lib/db/migrations/20261020T2100_claims_via_host.ts +21 -0
- package/lib/db/migrations/20261020T2200_work_claims_watch.ts +10 -0
- package/lib/db/migrations/20261021T1400_session_agent_roles.ts +20 -0
- package/lib/db/migrations/20261021T1430_analyst_links.ts +52 -0
- package/lib/db/queries.ts +0 -40
- package/lib/db/session-parts/columns.ts +3 -0
- package/lib/db/types.ts +19 -8
- package/lib/doctor/checks.ts +64 -1
- package/lib/done/archive.ts +2 -0
- package/lib/drafts.ts +4 -0
- package/lib/export/build.ts +470 -0
- package/lib/export/compact.ts +156 -0
- package/lib/export/estimate.ts +83 -0
- package/lib/export/fixtures.ts +45 -0
- package/lib/export/html.tsx +195 -0
- package/lib/export/json.ts +39 -0
- package/lib/export/load.ts +64 -0
- package/lib/export/markdown.ts +127 -0
- package/lib/export/request.ts +36 -0
- package/lib/export/service.ts +142 -0
- package/lib/export/summary.ts +86 -0
- package/lib/export/turns.ts +63 -0
- package/lib/export/view.ts +173 -0
- package/lib/git-remote.ts +43 -0
- package/lib/hosts/index.ts +10 -1
- package/lib/models/agent-binding.ts +102 -0
- package/lib/models/agent-roles-view.ts +157 -0
- package/lib/models/agent-roles.ts +87 -0
- package/lib/models/availability-refs.ts +17 -0
- package/lib/models/availability.ts +91 -0
- package/lib/models/catalog.ts +156 -0
- package/lib/models/config.ts +27 -0
- package/lib/models/locality.ts +33 -0
- package/lib/models/providers.ts +11 -0
- package/lib/models/resolve.ts +50 -12
- package/lib/models/roles.ts +12 -0
- package/lib/needs-you.ts +1 -1
- package/lib/orchestration.ts +2 -0
- package/lib/orchestrator/act.ts +54 -4
- package/lib/orchestrator/brief.ts +2 -1
- package/lib/orchestrator/claim-wait.ts +71 -0
- package/lib/orchestrator/conditions.ts +28 -1
- package/lib/orchestrator/describe.ts +6 -1
- package/lib/orchestrator/facts.ts +12 -2
- package/lib/orchestrator/read.ts +5 -1
- package/lib/orchestrator/serve.ts +37 -2
- package/lib/orchestrator/tool-names.ts +1 -0
- package/lib/orchestrator/tool-schemas.ts +31 -1
- package/lib/orchestrator/tools.ts +3 -1
- package/lib/orchestrator/untrusted.ts +5 -0
- package/lib/orchestrator/watcher.ts +19 -1
- package/lib/peer/account.ts +74 -2
- package/lib/peer/allowlist.ts +1 -1
- package/lib/peer/client.ts +26 -1
- package/lib/peer/link-cli.ts +19 -4
- package/lib/peer/links.ts +117 -5
- package/lib/peer/machines.ts +14 -2
- package/lib/peer/name.ts +32 -0
- package/lib/peer/pairing.ts +34 -5
- package/lib/peer/pin.ts +10 -3
- package/lib/peer/reasons.ts +7 -0
- package/lib/peer/revoke.ts +75 -0
- package/lib/peer/rpc-server.ts +53 -5
- package/lib/peer/unlink.ts +62 -0
- package/lib/peer/unlinked-proof.ts +44 -0
- package/lib/projects.ts +6 -2
- package/lib/security/demo.ts +7 -0
- package/lib/session-artifacts/agentos-files.ts +94 -0
- package/lib/session-artifacts/collect.ts +17 -0
- package/lib/session-groups/assignSessions.ts +81 -0
- package/lib/session-groups/cli.ts +61 -0
- package/lib/session-groups/createGroup.ts +28 -0
- package/lib/session-groups/index.ts +21 -0
- package/lib/session-groups/removeGroup.ts +36 -0
- package/lib/session-groups/reorderGroups.ts +31 -0
- package/lib/session-groups/resolveGroup.ts +19 -0
- package/lib/session-groups/route.ts +32 -0
- package/lib/session-groups/store.ts +85 -0
- package/lib/session-groups/types.ts +25 -0
- package/lib/session-groups/updateGroup.ts +35 -0
- package/lib/session-groups/workspace.ts +21 -0
- package/lib/sessions/crew.ts +72 -0
- package/lib/sessions/launch.ts +54 -11
- package/lib/settings-url.ts +1 -0
- package/lib/share/markdown.ts +2 -2
- package/lib/share/redact.ts +16 -10
- package/lib/share/render.tsx +1 -1
- package/lib/share/snapshot.ts +2 -2
- package/lib/sidebar/rollup.ts +1 -0
- package/lib/sidebar/row-menu.ts +8 -1
- package/lib/sidebar/shelves.ts +90 -15
- package/lib/sidebar/visible-order.ts +5 -0
- package/lib/tasks/hold-checks.ts +3 -1
- package/lib/tasks/holds.ts +3 -0
- package/lib/tasks/index.ts +11 -0
- package/lib/tasks/queue-rows.ts +8 -1
- package/lib/tasks/queue-start.ts +1 -0
- package/lib/tasks/queue-types.ts +9 -1
- package/lib/tasks/queue.ts +15 -5
- package/lib/tasks/schedule-types.ts +1 -0
- package/lib/test-temp.ts +19 -3
- package/lib/workspaces.ts +11 -3
- package/lib/worktree-deps/caches.ts +3 -2
- package/lib/worktree-deps/clone.ts +3 -1
- package/lib/worktree-deps/paths.ts +26 -0
- package/lib/worktree-sweep/testTemp.ts +23 -106
- package/package.json +1 -1
- package/scripts/agent-os +1 -1
- package/scripts/autodeploy +2 -0
- package/scripts/autodeploy.test.ts +31 -1
- package/scripts/claim-hook.sh +31 -0
- package/scripts/claim-hook.test.ts +230 -0
- package/scripts/lib/cli-links.sh +36 -0
- package/scripts/lib/commands.sh +3 -0
- package/scripts/lib/machine.sh +27 -2
- package/scripts/lib/machine.test.ts +25 -3
- package/scripts/sync-cli-links +7 -0
- package/scripts/sync-cli-links.test.ts +69 -0
- package/server.ts +12 -1
- package/stores/claimsUi.ts +22 -0
- package/stores/exportUi.ts +87 -0
- package/app/api/groups/[...path]/route.ts +0 -136
- package/app/api/groups/route.ts +0 -93
- package/data/groups/index.ts +0 -1
- package/data/groups/mutations.ts +0 -95
- package/hooks/useGroups.ts +0 -37
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { sessionLinkView } from "@/lib/analyst";
|
|
2
|
+
import { run } from "@/lib/analyst/route";
|
|
3
|
+
|
|
4
|
+
type Ctx = { params: Promise<{ id: string }> };
|
|
5
|
+
|
|
6
|
+
// GET /api/sessions/:id/analyst - the live pairing this session is in, as
|
|
7
|
+
// coder or analyst, or null.
|
|
8
|
+
export async function GET(_request: Request, { params }: Ctx) {
|
|
9
|
+
const { id } = await params;
|
|
10
|
+
return run(() => ({ link: sessionLinkView(id) }));
|
|
11
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { db } from "@/lib/db";
|
|
3
|
+
import { crewMembers } from "@/lib/sessions/crew";
|
|
4
|
+
import { modelCatalog } from "@/lib/models/catalog";
|
|
5
|
+
|
|
6
|
+
type RouteParams = { params: Promise<{ id: string }> };
|
|
7
|
+
|
|
8
|
+
// GET /api/sessions/:id/crew - the session's role and the sessions it works
|
|
9
|
+
// with (same crew), each with its model and whether that is local or cloud.
|
|
10
|
+
export async function GET(_request: NextRequest, { params }: RouteParams) {
|
|
11
|
+
const { id } = await params;
|
|
12
|
+
const row = db
|
|
13
|
+
.prepare(`SELECT agent_role, crew_id FROM session_rows WHERE id = ?`)
|
|
14
|
+
.get(id) as
|
|
15
|
+
{ agent_role: string | null; crew_id: string | null } | undefined;
|
|
16
|
+
if (!row)
|
|
17
|
+
return NextResponse.json({ error: "Session not found" }, { status: 404 });
|
|
18
|
+
const choices = new Map(modelCatalog().map((c) => [c.ref, c]));
|
|
19
|
+
const members = row.crew_id
|
|
20
|
+
? crewMembers(row.crew_id).map((m) => {
|
|
21
|
+
const ref = m.model
|
|
22
|
+
? `${m.agentType === "pi" ? "pi" : (m.modelProvider ?? "anthropic")}/${m.model}`
|
|
23
|
+
: null;
|
|
24
|
+
const choice = ref ? choices.get(ref) : undefined;
|
|
25
|
+
return {
|
|
26
|
+
...m,
|
|
27
|
+
ref,
|
|
28
|
+
locality: choice?.locality ?? "cloud",
|
|
29
|
+
availability: choice?.availability.state ?? "unknown",
|
|
30
|
+
};
|
|
31
|
+
})
|
|
32
|
+
: [];
|
|
33
|
+
return NextResponse.json({
|
|
34
|
+
agentRole: row.agent_role,
|
|
35
|
+
crewId: row.crew_id,
|
|
36
|
+
members,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { parseExportRequest } from "@/lib/export/request";
|
|
3
|
+
import { estimate } from "@/lib/export/service";
|
|
4
|
+
import { readJson, refuseDemo, toResponse } from "@/lib/share/route";
|
|
5
|
+
|
|
6
|
+
// POST /api/sessions/:id/export/estimate - what an export would hold: size,
|
|
7
|
+
// tokens, the turns to pick from, and warnings. No file text.
|
|
8
|
+
export async function POST(
|
|
9
|
+
request: Request,
|
|
10
|
+
{ params }: { params: Promise<{ id: string }> }
|
|
11
|
+
) {
|
|
12
|
+
const refused = refuseDemo();
|
|
13
|
+
if (refused) return refused;
|
|
14
|
+
const read = await readJson(request);
|
|
15
|
+
if ("refused" in read) return read.refused;
|
|
16
|
+
try {
|
|
17
|
+
const { id } = await params;
|
|
18
|
+
const req = parseExportRequest(read.body);
|
|
19
|
+
return NextResponse.json(await estimate(id, req));
|
|
20
|
+
} catch (err) {
|
|
21
|
+
return toResponse(err);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { randomUUID } from "crypto";
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { tempDir } from "@/lib/test-temp";
|
|
4
|
+
|
|
5
|
+
process.env.AGENTOS_ARTIFACTS_DIR = tempDir("agentos-export-art-");
|
|
6
|
+
const file = await import("./route");
|
|
7
|
+
const estimate = await import("./estimate/route");
|
|
8
|
+
const { saveItem } = await import("@/lib/chat/store");
|
|
9
|
+
const { seedSession } = await import("@/lib/orchestrator/testing");
|
|
10
|
+
const { createProject } = await import("@/lib/projects");
|
|
11
|
+
|
|
12
|
+
const secret = ["gh", "p_", "c".repeat(36)].join("");
|
|
13
|
+
const body = {
|
|
14
|
+
format: "md",
|
|
15
|
+
mode: "compact",
|
|
16
|
+
redact: "all",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const post = (b: unknown) =>
|
|
20
|
+
new Request("http://localhost/x", {
|
|
21
|
+
method: "POST",
|
|
22
|
+
headers: { "content-type": "application/json" },
|
|
23
|
+
body: JSON.stringify(b),
|
|
24
|
+
});
|
|
25
|
+
const params = (id: string) => ({ params: Promise.resolve({ id }) });
|
|
26
|
+
|
|
27
|
+
function session(view: "chat" | "terminal" = "chat") {
|
|
28
|
+
const project = createProject({
|
|
29
|
+
name: `p-${randomUUID().slice(0, 6)}`,
|
|
30
|
+
workingDirectory: "/tmp/p",
|
|
31
|
+
});
|
|
32
|
+
const id = seedSession({ projectId: project.id, name: "Export me", view });
|
|
33
|
+
let t = 1;
|
|
34
|
+
const add = (i: Record<string, unknown>) =>
|
|
35
|
+
saveItem(id, { id: `x${t}`, createdAt: t++, ...i } as never);
|
|
36
|
+
add({ kind: "user", text: `key ${secret}` });
|
|
37
|
+
add({ kind: "assistant", text: "ok" });
|
|
38
|
+
add({ kind: "user", text: "second" });
|
|
39
|
+
add({ kind: "assistant", text: "done" });
|
|
40
|
+
return id;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
afterEach(() => vi.unstubAllEnvs());
|
|
44
|
+
|
|
45
|
+
describe("POST /api/sessions/:id/export", () => {
|
|
46
|
+
it("returns each format as a named download with size headers", async () => {
|
|
47
|
+
const id = session();
|
|
48
|
+
for (const [format, type, ext] of [
|
|
49
|
+
["md", "text/markdown", "md"],
|
|
50
|
+
["json", "application/json", "json"],
|
|
51
|
+
["html", "text/html", "html"],
|
|
52
|
+
]) {
|
|
53
|
+
const res = await file.POST(post({ ...body, format }), params(id));
|
|
54
|
+
expect(res.status).toBe(200);
|
|
55
|
+
expect(res.headers.get("content-type")).toContain(type);
|
|
56
|
+
expect(res.headers.get("content-disposition")).toBe(
|
|
57
|
+
`attachment; filename="export-me-all.${ext}"`
|
|
58
|
+
);
|
|
59
|
+
expect(res.headers.get("cache-control")).toBe("no-store");
|
|
60
|
+
const text = await res.text();
|
|
61
|
+
expect(Number(res.headers.get("x-export-bytes"))).toBe(
|
|
62
|
+
Buffer.byteLength(text)
|
|
63
|
+
);
|
|
64
|
+
expect(Number(res.headers.get("x-export-tokens"))).toBeGreaterThan(0);
|
|
65
|
+
expect(text).not.toContain(secret);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("exports a range and names it", async () => {
|
|
70
|
+
const id = session();
|
|
71
|
+
const res = await file.POST(
|
|
72
|
+
post({ ...body, range: { from: 2, to: 2 } }),
|
|
73
|
+
params(id)
|
|
74
|
+
);
|
|
75
|
+
expect(res.headers.get("content-disposition")).toContain("-turn-2.md");
|
|
76
|
+
const text = await res.text();
|
|
77
|
+
expect(text).toContain("second");
|
|
78
|
+
expect(text).not.toContain("## Turn 1");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("404s an unknown session and 400s a terminal one", async () => {
|
|
82
|
+
expect((await file.POST(post(body), params("nope"))).status).toBe(404);
|
|
83
|
+
const res = await file.POST(post(body), params(session("terminal")));
|
|
84
|
+
expect(res.status).toBe(400);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("400s a bad format, mode, redaction or range", async () => {
|
|
88
|
+
const id = session();
|
|
89
|
+
for (const bad of [
|
|
90
|
+
{ ...body, format: "pdf" },
|
|
91
|
+
{ ...body, mode: "tiny" },
|
|
92
|
+
{ ...body, redact: "none" },
|
|
93
|
+
{ ...body, range: { from: 0, to: 2 } },
|
|
94
|
+
{ ...body, range: { from: 9, to: 12 } },
|
|
95
|
+
{ ...body, range: { last: -1 } },
|
|
96
|
+
])
|
|
97
|
+
expect((await file.POST(post(bad), params(id))).status).toBe(400);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("refuses in the demo", async () => {
|
|
101
|
+
vi.stubEnv("AGENTOS_DEMO", "1");
|
|
102
|
+
const res = await file.POST(post(body), params(session()));
|
|
103
|
+
expect(res.status).toBe(403);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe("POST /api/sessions/:id/export/estimate", () => {
|
|
108
|
+
it("returns sizes, turns and warnings, and no body text", async () => {
|
|
109
|
+
const id = session();
|
|
110
|
+
const res = await estimate.POST(post(body), params(id));
|
|
111
|
+
expect(res.status).toBe(200);
|
|
112
|
+
const text = await res.text();
|
|
113
|
+
const j = JSON.parse(text);
|
|
114
|
+
expect(j.turns.map((t: { n: number }) => t.n)).toEqual([1, 2]);
|
|
115
|
+
expect(j.selected).toMatchObject({ from: 1, to: 2, of: 2, turns: 2 });
|
|
116
|
+
expect(j.estimate.compact.bytes).toBeGreaterThan(0);
|
|
117
|
+
expect(j.estimate.full.bytes).toBeGreaterThan(0);
|
|
118
|
+
expect(j.hits.secret).toBe(1);
|
|
119
|
+
expect(j.warnings.map((w: { code: string }) => w.code)).toContain(
|
|
120
|
+
"sensitive"
|
|
121
|
+
);
|
|
122
|
+
expect(text).not.toContain("done");
|
|
123
|
+
expect(text).not.toContain(secret);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { parseExportRequest } from "@/lib/export/request";
|
|
3
|
+
import { buildFile } from "@/lib/export/service";
|
|
4
|
+
import { TOO_BIG, exportFileName } from "@/lib/export/view";
|
|
5
|
+
import { fail, readJson, refuseDemo, toResponse } from "@/lib/share/route";
|
|
6
|
+
|
|
7
|
+
// POST /api/sessions/:id/export - the session (or a range of its turns) as a
|
|
8
|
+
// Markdown, JSON or HTML file, redacted. Local work only.
|
|
9
|
+
export async function POST(
|
|
10
|
+
request: Request,
|
|
11
|
+
{ params }: { params: Promise<{ id: string }> }
|
|
12
|
+
) {
|
|
13
|
+
const refused = refuseDemo();
|
|
14
|
+
if (refused) return refused;
|
|
15
|
+
const read = await readJson(request);
|
|
16
|
+
if ("refused" in read) return read.refused;
|
|
17
|
+
try {
|
|
18
|
+
const { id } = await params;
|
|
19
|
+
const req = parseExportRequest(read.body);
|
|
20
|
+
const file = await buildFile(id, req);
|
|
21
|
+
if (file.tooBig) return fail(TOO_BIG, 413);
|
|
22
|
+
return new NextResponse(file.body, {
|
|
23
|
+
headers: {
|
|
24
|
+
"Content-Type": file.contentType,
|
|
25
|
+
"Content-Disposition": `attachment; filename="${exportFileName(file.title, file.slug, req.format)}"`,
|
|
26
|
+
"Cache-Control": "no-store",
|
|
27
|
+
"X-Export-Bytes": String(file.bytes),
|
|
28
|
+
"X-Export-Tokens": String(file.tokens),
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
} catch (err) {
|
|
32
|
+
return toResponse(err);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { inheritGroup } from "@/lib/session-groups/assignSessions";
|
|
1
2
|
import { NextRequest, NextResponse } from "next/server";
|
|
2
3
|
import { randomUUID } from "crypto";
|
|
3
4
|
import { getDb, queries, type Session, type Message } from "@/lib/db";
|
|
@@ -63,6 +64,8 @@ export async function POST(request: NextRequest, { params }: RouteParams) {
|
|
|
63
64
|
parent.host_id
|
|
64
65
|
);
|
|
65
66
|
|
|
67
|
+
inheritGroup(parentId, newId);
|
|
68
|
+
|
|
66
69
|
// NOTE: We do NOT copy claude_session_id here.
|
|
67
70
|
// When the forked session is first attached, it will use --fork-session flag
|
|
68
71
|
// with the parent's claude_session_id to create a new branched conversation.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { assignSessions } from "@/lib/session-groups";
|
|
3
|
+
import { groupRoute } from "@/lib/session-groups/route";
|
|
4
|
+
|
|
5
|
+
const body = z.object({ groupId: z.string().nullable() });
|
|
6
|
+
|
|
7
|
+
// POST /api/sessions/:id/group { groupId | null } - join a group of the
|
|
8
|
+
// session's workspace, or leave.
|
|
9
|
+
export async function POST(
|
|
10
|
+
request: Request,
|
|
11
|
+
{ params }: { params: Promise<{ id: string }> }
|
|
12
|
+
) {
|
|
13
|
+
const { id } = await params;
|
|
14
|
+
return groupRoute(request, body, (b) => ({
|
|
15
|
+
ok: true,
|
|
16
|
+
...assignSessions([id], b.groupId),
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { db, type Session } from "@/lib/db";
|
|
3
|
+
import { setChatModel } from "@/lib/chat/settings";
|
|
4
|
+
import { providerFor, sessionRole } from "@/lib/models/resolve";
|
|
5
|
+
import { switchSessionModel } from "@/lib/models/session-model";
|
|
6
|
+
import { PI } from "@/lib/models/providers";
|
|
7
|
+
import { formatModelRef, parseModelRef } from "@/lib/models/roles";
|
|
8
|
+
|
|
9
|
+
// POST /api/sessions/:id/model {ref} - move a running session to another
|
|
10
|
+
// model ("provider/model", or "pi/<provider>/<model>" for a Pi session),
|
|
11
|
+
// keeping its conversation.
|
|
12
|
+
export async function POST(
|
|
13
|
+
request: NextRequest,
|
|
14
|
+
{ params }: { params: Promise<{ id: string }> }
|
|
15
|
+
) {
|
|
16
|
+
const { id } = await params;
|
|
17
|
+
const { ref } = (await request.json().catch(() => ({}))) as { ref?: unknown };
|
|
18
|
+
const session = db
|
|
19
|
+
.prepare(`SELECT * FROM session_rows WHERE id = ?`)
|
|
20
|
+
.get(id) as Session | undefined;
|
|
21
|
+
if (!session)
|
|
22
|
+
return NextResponse.json({ error: "Session not found" }, { status: 404 });
|
|
23
|
+
if (typeof ref !== "string" || !ref.trim())
|
|
24
|
+
return NextResponse.json({ error: "ref is required" }, { status: 400 });
|
|
25
|
+
try {
|
|
26
|
+
const parsed = parseModelRef(ref);
|
|
27
|
+
if (session.agent_type === "pi") {
|
|
28
|
+
if (parsed.provider !== PI)
|
|
29
|
+
throw new Error(
|
|
30
|
+
"A Pi session runs on a Pi model (pi/<provider>/<model>)"
|
|
31
|
+
);
|
|
32
|
+
await setChatModel(id, parsed.model);
|
|
33
|
+
} else {
|
|
34
|
+
if (parsed.provider === PI)
|
|
35
|
+
throw new Error("Only a Pi session can move to a Pi model");
|
|
36
|
+
await switchSessionModel(id, {
|
|
37
|
+
role: sessionRole(session),
|
|
38
|
+
ref: formatModelRef(parsed),
|
|
39
|
+
provider: providerFor(parsed.provider),
|
|
40
|
+
model: parsed.model,
|
|
41
|
+
source: "task",
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return NextResponse.json({ ok: true });
|
|
45
|
+
} catch (error) {
|
|
46
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
47
|
+
return NextResponse.json({ error: message }, { status: 400 });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -19,6 +19,7 @@ import { statusDetector } from "@/lib/status-detector";
|
|
|
19
19
|
import { deleteItems } from "@/lib/chat/store";
|
|
20
20
|
import { clearQueue } from "@/lib/chat/queued";
|
|
21
21
|
import { recordPreviousName } from "@/lib/session-names";
|
|
22
|
+
import { getProject, moveSessionToProject } from "@/lib/projects";
|
|
22
23
|
import { generateBranchName, getCurrentBranch, renameBranch } from "@/lib/git";
|
|
23
24
|
import { runInBackground } from "@/lib/async-operations";
|
|
24
25
|
import {
|
|
@@ -206,9 +207,15 @@ export async function PATCH(request: NextRequest, { params }: RouteParams) {
|
|
|
206
207
|
updates.push("system_prompt = ?");
|
|
207
208
|
values.push(body.systemPrompt);
|
|
208
209
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
210
|
+
|
|
211
|
+
let clearedGroup: string | null = null;
|
|
212
|
+
if (typeof body.projectId === "string" && !existing.role) {
|
|
213
|
+
if (!getProject(body.projectId))
|
|
214
|
+
return NextResponse.json(
|
|
215
|
+
{ error: "Project not found" },
|
|
216
|
+
{ status: 404 }
|
|
217
|
+
);
|
|
218
|
+
clearedGroup = moveSessionToProject(id, body.projectId).clearedGroup;
|
|
212
219
|
}
|
|
213
220
|
|
|
214
221
|
if (updates.length > 0) {
|
|
@@ -221,7 +228,10 @@ export async function PATCH(request: NextRequest, { params }: RouteParams) {
|
|
|
221
228
|
}
|
|
222
229
|
|
|
223
230
|
const session = queries.getSession(db).get(id) as Session;
|
|
224
|
-
return NextResponse.json({
|
|
231
|
+
return NextResponse.json({
|
|
232
|
+
session,
|
|
233
|
+
...(clearedGroup && { clearedGroup }),
|
|
234
|
+
});
|
|
225
235
|
} catch (error) {
|
|
226
236
|
console.error("Error updating session:", error);
|
|
227
237
|
return NextResponse.json(
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { inheritGroup } from "@/lib/session-groups/assignSessions";
|
|
1
2
|
import { effectiveAutoApprove } from "@/lib/approvals/no-approvals";
|
|
2
3
|
import { routedRun } from "@/lib/models/launch";
|
|
3
4
|
import { NextRequest, NextResponse } from "next/server";
|
|
@@ -305,6 +306,7 @@ export async function POST(
|
|
|
305
306
|
session.host_id
|
|
306
307
|
);
|
|
307
308
|
|
|
309
|
+
inheritGroup(session.id, newId);
|
|
308
310
|
newSession = queries.getSession(db).get(newId) as Session;
|
|
309
311
|
const newTmuxSession = tmuxName;
|
|
310
312
|
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it } from "vitest";
|
|
2
|
+
import { seedSession, seedWorkspace } from "@/lib/orchestrator/testing";
|
|
3
|
+
|
|
4
|
+
const { db } = await import("@/lib/db");
|
|
5
|
+
const { machineId } = await import("@/lib/claims/machine");
|
|
6
|
+
const { upsertLocal, upsertRemote } = await import("@/lib/claims/store");
|
|
7
|
+
const { refreshOverlapCache, resetOverlapCache } =
|
|
8
|
+
await import("@/lib/claims/overlapCache");
|
|
9
|
+
const { GET } = await import("./route");
|
|
10
|
+
|
|
11
|
+
const REPO = "git.example.test/team/widgets";
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
db.prepare(`DELETE FROM work_claims`).run();
|
|
15
|
+
resetOverlapCache();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
function localClaim(sessionId: string, files: string[]) {
|
|
19
|
+
upsertLocal(
|
|
20
|
+
{ id: machineId(), name: "here" },
|
|
21
|
+
{
|
|
22
|
+
sessionId,
|
|
23
|
+
sessionName: "Tidy auth",
|
|
24
|
+
repo: REPO,
|
|
25
|
+
branch: "feature/tidy",
|
|
26
|
+
base: "main",
|
|
27
|
+
goal: "Tidy auth",
|
|
28
|
+
files,
|
|
29
|
+
filesMore: 0,
|
|
30
|
+
prUrl: null,
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const remote = (id: string, files: string[], branch = "feature/login") =>
|
|
36
|
+
upsertRemote(machineId(), "peer-1", {
|
|
37
|
+
id,
|
|
38
|
+
origin: "box-machine",
|
|
39
|
+
origin_name: "box",
|
|
40
|
+
hops: 0,
|
|
41
|
+
source: "session",
|
|
42
|
+
session_name: "Rework login sessions",
|
|
43
|
+
repo: REPO,
|
|
44
|
+
branch,
|
|
45
|
+
base: "main",
|
|
46
|
+
goal: "Rework login sessions",
|
|
47
|
+
files,
|
|
48
|
+
files_more: 0,
|
|
49
|
+
pr_url: null,
|
|
50
|
+
ttl_s: 600,
|
|
51
|
+
age_s: 60,
|
|
52
|
+
released: null,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const FILES = ["a/one.ts", "a/two.ts", "a/three.ts"];
|
|
56
|
+
|
|
57
|
+
const rows = async () => {
|
|
58
|
+
const body = await (await GET()).json();
|
|
59
|
+
return body as {
|
|
60
|
+
sessions: { id: string; overlap?: Record<string, unknown> | null }[];
|
|
61
|
+
claims: { id: string; fileCount: number; files?: string[] }[];
|
|
62
|
+
claimOverlaps: { a: string; b: string; strength: string }[];
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
describe("GET /api/sessions with claims", () => {
|
|
67
|
+
it("puts a strong overlap on the session that has it, and no other", async () => {
|
|
68
|
+
const t = seedWorkspace();
|
|
69
|
+
const mine = seedSession({ projectId: t.app.id, name: "Tidy auth" });
|
|
70
|
+
const other = seedSession({ projectId: t.app.id, name: "Unrelated" });
|
|
71
|
+
localClaim(mine, FILES);
|
|
72
|
+
remote("claim-box-1", FILES);
|
|
73
|
+
refreshOverlapCache();
|
|
74
|
+
const body = await rows();
|
|
75
|
+
expect(body.sessions.find((s) => s.id === mine)?.overlap).toEqual({
|
|
76
|
+
strength: "strong",
|
|
77
|
+
machine: "box",
|
|
78
|
+
goal: "Rework login sessions",
|
|
79
|
+
shared: 3,
|
|
80
|
+
claimId: "claim-box-1",
|
|
81
|
+
mineId: `s-${mine}`,
|
|
82
|
+
});
|
|
83
|
+
expect(body.sessions.find((s) => s.id === other)?.overlap).toBeUndefined();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("marks a small file overlap weak", async () => {
|
|
87
|
+
const t = seedWorkspace();
|
|
88
|
+
const mine = seedSession({ projectId: t.app.id, name: "Tidy auth" });
|
|
89
|
+
localClaim(mine, ["a/1.ts", "a/2.ts", "a/3.ts", "a/4.ts", "a/5.ts"]);
|
|
90
|
+
remote(
|
|
91
|
+
"claim-box-2",
|
|
92
|
+
["a/1.ts", "a/2.ts", "b/1.ts", "b/2.ts", "b/3.ts"],
|
|
93
|
+
"feature/other"
|
|
94
|
+
);
|
|
95
|
+
refreshOverlapCache();
|
|
96
|
+
const row = (await rows()).sessions.find((s) => s.id === mine);
|
|
97
|
+
expect(row?.overlap?.strength).toBe("weak");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("lists claims without their paths, and the overlapping pairs", async () => {
|
|
101
|
+
const t = seedWorkspace();
|
|
102
|
+
const mine = seedSession({ projectId: t.app.id, name: "Tidy auth" });
|
|
103
|
+
localClaim(mine, FILES);
|
|
104
|
+
remote("claim-box-1", FILES);
|
|
105
|
+
refreshOverlapCache();
|
|
106
|
+
const body = await rows();
|
|
107
|
+
expect(body.claims.map((c) => c.id).sort()).toEqual(
|
|
108
|
+
[`s-${mine}`, "claim-box-1"].sort()
|
|
109
|
+
);
|
|
110
|
+
expect(body.claims.every((c) => c.fileCount === 3 && !c.files)).toBe(true);
|
|
111
|
+
expect(body.claimOverlaps).toHaveLength(1);
|
|
112
|
+
expect(body.claimOverlaps[0].strength).toBe("strong");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("serves no claims before the loop has run", async () => {
|
|
116
|
+
const t = seedWorkspace();
|
|
117
|
+
seedSession({ projectId: t.app.id, name: "Tidy auth" });
|
|
118
|
+
const body = await rows();
|
|
119
|
+
expect(body.claims).toEqual([]);
|
|
120
|
+
expect(body.sessions.every((s) => !s.overlap)).toBe(true);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { assignSessions } from "@/lib/session-groups";
|
|
3
|
+
import { groupRoute } from "@/lib/session-groups/route";
|
|
4
|
+
|
|
5
|
+
const body = z.object({
|
|
6
|
+
ids: z.array(z.string()).min(1).max(200),
|
|
7
|
+
groupId: z.string().nullable(),
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
// POST /api/sessions/group { ids, groupId | null } - the multi-select move.
|
|
11
|
+
// All or none: one session of another workspace refuses the lot.
|
|
12
|
+
export async function POST(request: Request) {
|
|
13
|
+
return groupRoute(request, body, (b) => ({
|
|
14
|
+
ok: true,
|
|
15
|
+
...assignSessions(b.ids, b.groupId),
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
-
import { getDb, queries, type Session
|
|
2
|
+
import { getDb, queries, type Session } from "@/lib/db";
|
|
3
|
+
import { listGroups } from "@/lib/session-groups";
|
|
3
4
|
import { isValidAgentType } from "@/lib/providers";
|
|
4
5
|
import { clientSend } from "@/lib/chat/client-send";
|
|
5
6
|
import { CHAT_ACCESS } from "@/lib/chat/events";
|
|
6
7
|
import { launchSession } from "@/lib/sessions/launch";
|
|
8
|
+
import { isAgentRole } from "@/lib/models/agent-roles";
|
|
9
|
+
import { ModelUnavailableError } from "@/lib/models/agent-binding";
|
|
7
10
|
import { namesLine, waitBadges } from "@/lib/bus/waits/store";
|
|
11
|
+
import { claimsSnapshot } from "@/lib/claims";
|
|
8
12
|
import { isProjectRef } from "@/lib/tasks/project-ref";
|
|
9
13
|
|
|
10
|
-
// GET /api/sessions - List
|
|
14
|
+
// GET /api/sessions - List the sidebar's sessions and the user's groups
|
|
11
15
|
export async function GET() {
|
|
12
16
|
try {
|
|
13
17
|
const db = getDb();
|
|
@@ -15,12 +19,15 @@ export async function GET() {
|
|
|
15
19
|
// tasks live in the Tasks panel, a moved one on the machine it moved to,
|
|
16
20
|
// and archived sessions in the Archived view: none is in the sidebar.
|
|
17
21
|
const badges = waitBadges();
|
|
22
|
+
const claims = claimsSnapshot();
|
|
18
23
|
const sessions = (queries.getSidebarSessions(db).all() as Session[]).map(
|
|
19
24
|
(s) => {
|
|
20
25
|
const b = badges.get(s.id);
|
|
26
|
+
const overlap = claims.bySession.get(s.id);
|
|
27
|
+
const row = overlap ? { ...s, overlap } : s;
|
|
21
28
|
return b
|
|
22
29
|
? {
|
|
23
|
-
...
|
|
30
|
+
...row,
|
|
24
31
|
waiting_on: b.on.length
|
|
25
32
|
? namesLine(b.on.map((w) => w.name))
|
|
26
33
|
: null,
|
|
@@ -28,18 +35,15 @@ export async function GET() {
|
|
|
28
35
|
? namesLine(b.by.map((w) => w.name))
|
|
29
36
|
: null,
|
|
30
37
|
}
|
|
31
|
-
:
|
|
38
|
+
: row;
|
|
32
39
|
}
|
|
33
40
|
);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}));
|
|
41
|
-
|
|
42
|
-
return NextResponse.json({ sessions, groups: formattedGroups });
|
|
41
|
+
return NextResponse.json({
|
|
42
|
+
sessions,
|
|
43
|
+
sessionGroups: listGroups(),
|
|
44
|
+
claims: claims.claims,
|
|
45
|
+
claimOverlaps: claims.overlaps,
|
|
46
|
+
});
|
|
43
47
|
} catch (error) {
|
|
44
48
|
console.error("Error fetching sessions:", error);
|
|
45
49
|
return NextResponse.json(
|
|
@@ -95,8 +99,10 @@ export async function POST(request: NextRequest) {
|
|
|
95
99
|
name: str(body.name),
|
|
96
100
|
view: body.view === "terminal" ? "terminal" : undefined,
|
|
97
101
|
parentSessionId: str(body.parentSessionId),
|
|
98
|
-
groupPath: str(body.groupPath) ?? undefined,
|
|
99
102
|
systemPrompt: str(body.systemPrompt),
|
|
103
|
+
agentRole: isAgentRole(body.agentRole) ? body.agentRole : null,
|
|
104
|
+
crewId: str(body.crewId),
|
|
105
|
+
useFallback: body.onUnavailable === "fallback",
|
|
100
106
|
});
|
|
101
107
|
const db = getDb();
|
|
102
108
|
const claudeSessionId = repeat ? null : str(body.claudeSessionId);
|
|
@@ -129,6 +135,17 @@ export async function POST(request: NextRequest) {
|
|
|
129
135
|
{ status: 201 }
|
|
130
136
|
);
|
|
131
137
|
} catch (error) {
|
|
138
|
+
if (error instanceof ModelUnavailableError)
|
|
139
|
+
return NextResponse.json(
|
|
140
|
+
{
|
|
141
|
+
code: error.code,
|
|
142
|
+
role: error.role,
|
|
143
|
+
ref: error.ref,
|
|
144
|
+
fallback: error.fallback ?? null,
|
|
145
|
+
error: error.message,
|
|
146
|
+
},
|
|
147
|
+
{ status: 409 }
|
|
148
|
+
);
|
|
132
149
|
const message = error instanceof Error ? error.message : String(error);
|
|
133
150
|
console.error("Error creating session:", message);
|
|
134
151
|
return NextResponse.json({ error: message }, { status: 400 });
|
package/app/page.tsx
CHANGED
|
@@ -122,6 +122,10 @@ const LumifyHubDialogs = dynamic(
|
|
|
122
122
|
),
|
|
123
123
|
{ ssr: false }
|
|
124
124
|
);
|
|
125
|
+
const ClaimsSheet = dynamic(
|
|
126
|
+
() => import("@/components/Claims/ClaimsSheet").then((m) => m.ClaimsSheet),
|
|
127
|
+
{ ssr: false }
|
|
128
|
+
);
|
|
125
129
|
const UsageDialog = dynamic(
|
|
126
130
|
() => import("@/components/Usage/UsageDialog").then((m) => m.UsageDialog),
|
|
127
131
|
{ ssr: false }
|
|
@@ -134,6 +138,10 @@ const ShareDialog = dynamic(
|
|
|
134
138
|
() => import("@/components/Share/ShareDialog").then((m) => m.ShareDialog),
|
|
135
139
|
{ ssr: false }
|
|
136
140
|
);
|
|
141
|
+
const ExportSheet = dynamic(
|
|
142
|
+
() => import("@/components/Export/ExportSheet").then((m) => m.ExportSheet),
|
|
143
|
+
{ ssr: false }
|
|
144
|
+
);
|
|
137
145
|
const CommandPalette = dynamic(
|
|
138
146
|
() =>
|
|
139
147
|
import("@/components/CommandPalette/CommandPalette").then(
|
|
@@ -159,8 +167,10 @@ const AppDialogs = memo(function AppDialogs({
|
|
|
159
167
|
<CleanupDialog />
|
|
160
168
|
<LumifyHubDialogs />
|
|
161
169
|
<UsageDialog />
|
|
170
|
+
<ClaimsSheet />
|
|
162
171
|
<MoveDialog />
|
|
163
172
|
<ShareDialog />
|
|
173
|
+
<ExportSheet />
|
|
164
174
|
<AwayReport />
|
|
165
175
|
<IncomingLinks />
|
|
166
176
|
<CommandPalette />
|