@saadnvd1/agent-os 0.3.768 → 0.3.775
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]/artifacts/[artifactId]/compare/route.ts +41 -0
- package/app/api/sessions/resolve/route.test.ts +81 -0
- package/app/api/sessions/resolve/route.ts +14 -0
- package/app/api/shares/preview/route.ts +6 -1
- package/app/api/shares/route.test.ts +51 -0
- package/app/page.tsx +1 -0
- package/components/Artifacts/ArtifactActions.tsx +27 -3
- package/components/Artifacts/ArtifactPreview.tsx +27 -12
- package/components/Artifacts/Compare/DiffView.tsx +116 -0
- package/components/Artifacts/Compare/SideBySide.tsx +60 -0
- package/components/Artifacts/Compare/VersionPicker.tsx +47 -0
- package/components/Artifacts/Compare/index.tsx +191 -0
- package/components/Chat/ChatPanel.tsx +3 -9
- package/components/Chat/EventRow.tsx +4 -4
- package/components/Chat/Messages.tsx +12 -6
- package/components/Chat/SessionAnchor.tsx +22 -0
- package/components/Chat/ShareFromHere.tsx +39 -0
- package/components/Chat/session-link.ts +21 -4
- package/components/CommandPalette/CommandPalette.tsx +5 -4
- package/components/CommandPalette/CommandsBody.tsx +97 -131
- package/components/CommandPalette/PaletteRows.tsx +79 -0
- package/components/CommandPalette/PaletteTabs.tsx +51 -0
- package/components/CommandPalette/conversations/ConversationSearch.tsx +13 -11
- package/components/CommandPalette/useAppCommands.ts +14 -0
- package/components/CommandPalette/useConversationRows.ts +55 -0
- package/components/Health/EnvFailures/SessionList.tsx +2 -2
- package/components/Pane/PaneMenu.tsx +1 -1
- package/components/SessionList/QueuedRow.tsx +4 -4
- package/components/SessionList/SessionRowMenu.tsx +1 -1
- package/components/SessionList/useSessionSwitching.ts +2 -2
- package/components/Share/MessageList.tsx +87 -0
- package/components/Share/RangePicker.tsx +59 -0
- package/components/Share/ShareDialog.tsx +113 -52
- package/components/Shares/SharedLinksPanel.tsx +3 -5
- package/data/session-artifacts/keys.ts +2 -0
- package/data/session-artifacts/queries.ts +25 -0
- package/data/shares/queries.ts +11 -0
- package/hooks/useCopyToClipboard.ts +5 -2
- package/hooks/usePaletteCommands.ts +4 -1
- package/hooks/useSessionHref.ts +20 -0
- package/hooks/useSessionUrl.ts +40 -11
- package/lib/artifacts/actions.ts +11 -1
- package/lib/artifacts/compare/compareVersions.ts +66 -0
- package/lib/artifacts/compare/diff.ts +178 -0
- package/lib/artifacts/compare/extractText.ts +64 -0
- package/lib/artifacts/compare/index.ts +11 -0
- package/lib/artifacts/compare/types.ts +44 -0
- package/lib/artifacts/match.ts +64 -0
- package/lib/artifacts/store.ts +39 -10
- package/lib/artifacts/tools.ts +9 -4
- package/lib/artifacts/versions.ts +2 -1
- package/lib/away/deploy.ts +39 -2
- package/lib/palette/registry.ts +71 -17
- package/lib/palette/sections.ts +143 -0
- package/lib/palette/tabs.ts +21 -0
- package/lib/palette/usage.ts +81 -0
- package/lib/reports/evidence/build.ts +4 -3
- package/lib/security/demo.ts +3 -0
- package/lib/session-audit/render.ts +2 -1
- package/lib/session-logs/gather.ts +2 -2
- package/lib/session-slug.ts +29 -0
- package/lib/session-url.ts +162 -40
- package/lib/sessions/resolve.ts +55 -0
- package/lib/settings-url.ts +1 -1
- package/lib/share/markdown.ts +3 -0
- package/lib/share/range.ts +189 -0
- package/lib/share/render.tsx +3 -0
- package/lib/share/service.ts +64 -19
- package/lib/share/snapshot.ts +39 -2
- package/lib/share/styles.ts +2 -0
- package/lib/share/view.ts +11 -0
- package/lib/welcome-url.ts +1 -1
- package/next.config.ts +4 -0
- package/package.json +1 -1
- package/stores/chatJump.ts +5 -1
- package/stores/palette.ts +13 -11
- package/stores/shareUi.ts +6 -1
package/lib/share/service.ts
CHANGED
|
@@ -16,7 +16,11 @@ import {
|
|
|
16
16
|
renderChatSnapshot,
|
|
17
17
|
} from "./render";
|
|
18
18
|
import { artifactToMarkdown, chatToMarkdown } from "./markdown";
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
RangeGone,
|
|
21
|
+
buildArtifactSnapshot,
|
|
22
|
+
buildChatSnapshot,
|
|
23
|
+
} from "./snapshot";
|
|
20
24
|
|
|
21
25
|
export interface ShareRequest {
|
|
22
26
|
kind: "chat" | "artifact";
|
|
@@ -25,6 +29,9 @@ export interface ShareRequest {
|
|
|
25
29
|
includeTools: boolean;
|
|
26
30
|
/** Messages the reader left out in the preview. */
|
|
27
31
|
dropIds?: string[];
|
|
32
|
+
/** The part of the chat shared: from this message to that one, ends included. */
|
|
33
|
+
fromId?: string;
|
|
34
|
+
toId?: string;
|
|
28
35
|
follow?: "latest";
|
|
29
36
|
sharedAt: string;
|
|
30
37
|
}
|
|
@@ -34,6 +41,7 @@ export interface ItemRow {
|
|
|
34
41
|
type: string;
|
|
35
42
|
tools: boolean;
|
|
36
43
|
excerpt: string;
|
|
44
|
+
createdAt?: number;
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
export interface Page {
|
|
@@ -87,6 +95,15 @@ export function parseRequest(raw: unknown, sharedAt?: unknown): ShareRequest {
|
|
|
87
95
|
throw new BadShare("dropIds must be a list of ids", 400);
|
|
88
96
|
dropIds = b.dropIds as string[];
|
|
89
97
|
}
|
|
98
|
+
const end = (key: "fromId" | "toId") => {
|
|
99
|
+
const v = b[key];
|
|
100
|
+
if (v === undefined) return undefined;
|
|
101
|
+
if (typeof v !== "string" || !v || v.length > 200)
|
|
102
|
+
throw new BadShare(`${key} must be a message id`, 400);
|
|
103
|
+
return v;
|
|
104
|
+
};
|
|
105
|
+
const fromId = end("fromId");
|
|
106
|
+
const toId = end("toId");
|
|
90
107
|
if (b.follow !== undefined && b.follow !== "latest")
|
|
91
108
|
throw new BadShare("follow must be latest", 400);
|
|
92
109
|
if (b.follow && kind !== "artifact")
|
|
@@ -100,11 +117,41 @@ export function parseRequest(raw: unknown, sharedAt?: unknown): ShareRequest {
|
|
|
100
117
|
artifactId,
|
|
101
118
|
includeTools: b.includeTools === true,
|
|
102
119
|
dropIds,
|
|
120
|
+
fromId,
|
|
121
|
+
toId,
|
|
103
122
|
follow: b.follow === "latest" ? "latest" : undefined,
|
|
104
123
|
sharedAt: at,
|
|
105
124
|
};
|
|
106
125
|
}
|
|
107
126
|
|
|
127
|
+
const rowOf = ({ id, type, tools, excerpt, createdAt }: ItemRow): ItemRow => ({
|
|
128
|
+
id,
|
|
129
|
+
type,
|
|
130
|
+
tools,
|
|
131
|
+
excerpt,
|
|
132
|
+
createdAt,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
function chatSession(sessionId: string) {
|
|
136
|
+
let session;
|
|
137
|
+
try {
|
|
138
|
+
session = getSession(sessionId);
|
|
139
|
+
} catch {
|
|
140
|
+
throw new BadShare("No such chat", 404);
|
|
141
|
+
}
|
|
142
|
+
if (session.view !== "chat")
|
|
143
|
+
throw new BadShare("Only chats can be shared", 400);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Every shareable message of a chat, tool rows included, in order. */
|
|
147
|
+
export async function listChatItems(req: ShareRequest): Promise<ItemRow[]> {
|
|
148
|
+
chatSession(req.sessionId!);
|
|
149
|
+
const all = await buildChatSnapshot(req.sessionId!, shareContext(), {
|
|
150
|
+
includeTools: true,
|
|
151
|
+
});
|
|
152
|
+
return all.items.map(rowOf);
|
|
153
|
+
}
|
|
154
|
+
|
|
108
155
|
const sha256 = (s: string) => createHash("sha256").update(s).digest("hex");
|
|
109
156
|
|
|
110
157
|
/** Binds both parts: neither can change without the hash changing. */
|
|
@@ -120,29 +167,27 @@ export async function buildPage(req: ShareRequest): Promise<Page> {
|
|
|
120
167
|
let items: ItemRow[] = [];
|
|
121
168
|
|
|
122
169
|
if (req.kind === "chat") {
|
|
123
|
-
|
|
124
|
-
try {
|
|
125
|
-
session = getSession(req.sessionId!);
|
|
126
|
-
} catch {
|
|
127
|
-
throw new BadShare("No such chat", 404);
|
|
128
|
-
}
|
|
129
|
-
if (session.view !== "chat")
|
|
130
|
-
throw new BadShare("Only chats can be shared", 400);
|
|
170
|
+
chatSession(req.sessionId!);
|
|
131
171
|
const all = await buildChatSnapshot(req.sessionId!, ctx, {
|
|
132
172
|
includeTools: req.includeTools,
|
|
133
173
|
});
|
|
134
|
-
items = all.items.map(
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
? await buildChatSnapshot(req.sessionId!, ctx, {
|
|
174
|
+
items = all.items.map(rowOf);
|
|
175
|
+
const narrowed =
|
|
176
|
+
req.dropIds?.length || req.fromId !== undefined || req.toId !== undefined;
|
|
177
|
+
let snap = all;
|
|
178
|
+
if (narrowed) {
|
|
179
|
+
try {
|
|
180
|
+
snap = await buildChatSnapshot(req.sessionId!, ctx, {
|
|
142
181
|
includeTools: req.includeTools,
|
|
143
182
|
dropIds: req.dropIds,
|
|
144
|
-
|
|
145
|
-
|
|
183
|
+
fromId: req.fromId,
|
|
184
|
+
toId: req.toId,
|
|
185
|
+
});
|
|
186
|
+
} catch (err) {
|
|
187
|
+
if (err instanceof RangeGone) throw new BadShare(err.message, 409);
|
|
188
|
+
throw err;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
146
191
|
if (!snap.items.length)
|
|
147
192
|
throw new BadShare("Nothing to share: every message is left out", 400);
|
|
148
193
|
html = renderChatSnapshot(snap, { sharedAt: req.sharedAt });
|
package/lib/share/snapshot.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
type Hits,
|
|
16
16
|
type ShareContext,
|
|
17
17
|
} from "./redact";
|
|
18
|
+
import { windowOf } from "./range";
|
|
18
19
|
|
|
19
20
|
export type SnapshotType =
|
|
20
21
|
"user" | "assistant" | "plan" | "error" | "divider" | "artifact" | "detail";
|
|
@@ -32,6 +33,7 @@ export interface SnapshotItem {
|
|
|
32
33
|
excerpt: string;
|
|
33
34
|
/** Hidden unless "include tool calls and thinking" is on. */
|
|
34
35
|
tools: boolean;
|
|
36
|
+
createdAt?: number;
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
export interface Snapshot {
|
|
@@ -39,9 +41,19 @@ export interface Snapshot {
|
|
|
39
41
|
items: SnapshotItem[];
|
|
40
42
|
/** Whether the tools group was left out, to say so on the page. */
|
|
41
43
|
toolsHidden: boolean;
|
|
44
|
+
/** Messages before / after the shared range were left out, to say so. */
|
|
45
|
+
cutBefore?: boolean;
|
|
46
|
+
cutAfter?: boolean;
|
|
42
47
|
hits: Hits;
|
|
43
48
|
}
|
|
44
49
|
|
|
50
|
+
/** The range names a message that is no longer in the chat. */
|
|
51
|
+
export class RangeGone extends Error {
|
|
52
|
+
constructor() {
|
|
53
|
+
super("That part of the chat changed; pick the range again.");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
45
57
|
export const MAX_TOOL_INPUT = 4_000;
|
|
46
58
|
export const MAX_TOOL_OUTPUT = 20_000;
|
|
47
59
|
export const HIDDEN_FILE = "[contents of a secret file hidden]";
|
|
@@ -189,7 +201,12 @@ export function toRaw(item: ChatItem): Raw | null {
|
|
|
189
201
|
export async function buildChatSnapshot(
|
|
190
202
|
sessionId: string,
|
|
191
203
|
ctx: ShareContext,
|
|
192
|
-
opts: {
|
|
204
|
+
opts: {
|
|
205
|
+
includeTools: boolean;
|
|
206
|
+
dropIds?: string[];
|
|
207
|
+
fromId?: string;
|
|
208
|
+
toId?: string;
|
|
209
|
+
}
|
|
193
210
|
): Promise<Snapshot> {
|
|
194
211
|
const session = getSession(sessionId);
|
|
195
212
|
const dropped = new Set(opts.dropIds ?? []);
|
|
@@ -201,13 +218,31 @@ export async function buildChatSnapshot(
|
|
|
201
218
|
};
|
|
202
219
|
|
|
203
220
|
const items: SnapshotItem[] = [];
|
|
204
|
-
|
|
221
|
+
const all = visible(listItems(sessionId));
|
|
222
|
+
let window: { start: number; end: number } | null = null;
|
|
223
|
+
if (opts.fromId !== undefined || opts.toId !== undefined) {
|
|
224
|
+
window = windowOf(
|
|
225
|
+
all.map((i) => i.id),
|
|
226
|
+
opts.fromId,
|
|
227
|
+
opts.toId
|
|
228
|
+
);
|
|
229
|
+
if (!window) throw new RangeGone();
|
|
230
|
+
}
|
|
231
|
+
let cutBefore = false;
|
|
232
|
+
let cutAfter = false;
|
|
233
|
+
for (const [at, item] of all.entries()) {
|
|
205
234
|
const raw = toRaw(item);
|
|
206
235
|
if (!raw) continue;
|
|
207
236
|
if (raw.tools && !opts.includeTools) continue;
|
|
237
|
+
if (window && (at < window.start || at > window.end)) {
|
|
238
|
+
if (at < window.start) cutBefore = true;
|
|
239
|
+
else cutAfter = true;
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
208
242
|
if (dropped.has(raw.id)) continue;
|
|
209
243
|
const row: SnapshotItem = {
|
|
210
244
|
...raw,
|
|
245
|
+
createdAt: item.createdAt,
|
|
211
246
|
label: raw.label === undefined ? undefined : take(raw.label),
|
|
212
247
|
text: take(raw.text),
|
|
213
248
|
excerpt: "",
|
|
@@ -225,6 +260,8 @@ export async function buildChatSnapshot(
|
|
|
225
260
|
title: take(session.name).slice(0, 120) || "Chat",
|
|
226
261
|
items,
|
|
227
262
|
toolsHidden: !opts.includeTools,
|
|
263
|
+
cutBefore,
|
|
264
|
+
cutAfter,
|
|
228
265
|
hits,
|
|
229
266
|
};
|
|
230
267
|
}
|
package/lib/share/styles.ts
CHANGED
|
@@ -56,6 +56,8 @@ h1.title{margin:0 0 4px;font-size:20px;line-height:1.3}
|
|
|
56
56
|
.plan>.kicker{margin-top:12px}
|
|
57
57
|
.kicker{font-size:12px;font-weight:600;color:hsl(var(--muted-foreground));text-transform:uppercase;letter-spacing:.04em}
|
|
58
58
|
.error{color:hsl(var(--destructive));border:1px solid hsl(var(--destructive)/.4);border-radius:10px;padding:8px 14px;white-space:pre-wrap;overflow-wrap:anywhere}
|
|
59
|
+
.cut{display:flex;align-items:center;gap:12px;color:hsl(var(--muted-foreground));font-size:12px;font-style:italic}
|
|
60
|
+
.cut::before,.cut::after{content:"";flex:1;border-top:1px dashed hsl(var(--border))}
|
|
59
61
|
.divider{display:flex;align-items:center;gap:12px;color:hsl(var(--muted-foreground));font-size:12px}
|
|
60
62
|
.divider::before,.divider::after{content:"";flex:1;border-top:1px solid hsl(var(--border))}
|
|
61
63
|
details.tool{border:1px solid hsl(var(--border));border-radius:10px;background:hsl(var(--card))}
|
package/lib/share/view.ts
CHANGED
|
@@ -15,6 +15,11 @@ export interface PreviewRequest extends ShareTarget {
|
|
|
15
15
|
includeTools: boolean;
|
|
16
16
|
/** Messages left out of the share. */
|
|
17
17
|
dropIds?: string[];
|
|
18
|
+
/** Only the messages from this one to `toId`, both included; either end open when absent. */
|
|
19
|
+
fromId?: string;
|
|
20
|
+
toId?: string;
|
|
21
|
+
/** Just the list of messages, no page: what the range picker needs first. */
|
|
22
|
+
listOnly?: boolean;
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
export interface PreviewItem {
|
|
@@ -22,11 +27,17 @@ export interface PreviewItem {
|
|
|
22
27
|
type: string;
|
|
23
28
|
tools: boolean;
|
|
24
29
|
excerpt: string;
|
|
30
|
+
createdAt?: number;
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
/** Download formats a server can make; the client lists what it is told. */
|
|
28
34
|
export type ShareFormat = "pdf" | "html" | "md";
|
|
29
35
|
|
|
36
|
+
export interface MessageList {
|
|
37
|
+
/** Every shareable message in order, tool rows included. */
|
|
38
|
+
items: PreviewItem[];
|
|
39
|
+
}
|
|
40
|
+
|
|
30
41
|
export interface SharePreview {
|
|
31
42
|
html: string;
|
|
32
43
|
markdown: string;
|
package/lib/welcome-url.ts
CHANGED
package/next.config.ts
CHANGED
|
@@ -13,6 +13,10 @@ const nextConfig: NextConfig = {
|
|
|
13
13
|
experimental: {
|
|
14
14
|
agentUpgrade: false,
|
|
15
15
|
},
|
|
16
|
+
// A session's readable address (lib/session-url) is the same one page.
|
|
17
|
+
async rewrites() {
|
|
18
|
+
return { beforeFiles: [{ source: "/s/:ref", destination: "/" }] };
|
|
19
|
+
},
|
|
16
20
|
turbopack: {
|
|
17
21
|
root: resolve(import.meta.dirname),
|
|
18
22
|
},
|
package/package.json
CHANGED
package/stores/chatJump.ts
CHANGED
|
@@ -4,7 +4,11 @@ import { bookmarksUiActions } from "@/stores/bookmarksUi";
|
|
|
4
4
|
// Opens a chat by its address, then takes it to one message (bookmarks and
|
|
5
5
|
// search both end here). The chat takes the jump once it is on screen.
|
|
6
6
|
export function openChatAt(sessionId: string, itemId: string): void {
|
|
7
|
-
window.history.pushState(
|
|
7
|
+
window.history.pushState(
|
|
8
|
+
null,
|
|
9
|
+
"",
|
|
10
|
+
hrefWithSession(location.href, { id: sessionId })
|
|
11
|
+
);
|
|
8
12
|
window.dispatchEvent(new PopStateEvent("popstate"));
|
|
9
13
|
bookmarksUiActions.jump(sessionId, itemId);
|
|
10
14
|
}
|
package/stores/palette.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { proxy, ref } from "valtio";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
PaletteRegistry,
|
|
4
|
+
type PaletteCommand,
|
|
5
|
+
type PaletteTab,
|
|
6
|
+
} from "@/lib/palette/registry";
|
|
3
7
|
|
|
4
8
|
// A one-off choice in the palette (which project, which machine), in place
|
|
5
9
|
// of its commands. ⌘1-9 picks the first nine.
|
|
@@ -8,20 +12,18 @@ export interface PalettePicker {
|
|
|
8
12
|
items: PaletteCommand[];
|
|
9
13
|
}
|
|
10
14
|
|
|
11
|
-
export type PaletteMode = "commands" | "conversations";
|
|
12
|
-
|
|
13
15
|
export const paletteUi = proxy<{
|
|
14
16
|
open: boolean;
|
|
15
17
|
picker: PalettePicker | null;
|
|
16
|
-
|
|
17
|
-
// What was typed when the
|
|
18
|
+
tab: PaletteTab;
|
|
19
|
+
// What was typed when the tab changed, so a body that mounts starts with it.
|
|
18
20
|
query: string;
|
|
19
|
-
}>({ open: false, picker: null,
|
|
21
|
+
}>({ open: false, picker: null, tab: "all", query: "" });
|
|
20
22
|
|
|
21
23
|
export const paletteActions = {
|
|
22
24
|
open: () => {
|
|
23
25
|
paletteUi.picker = null;
|
|
24
|
-
paletteUi.
|
|
26
|
+
paletteUi.tab = "all";
|
|
25
27
|
paletteUi.query = "";
|
|
26
28
|
paletteUi.open = true;
|
|
27
29
|
},
|
|
@@ -29,17 +31,17 @@ export const paletteActions = {
|
|
|
29
31
|
paletteUi.open = open;
|
|
30
32
|
if (!open) {
|
|
31
33
|
paletteUi.picker = null;
|
|
32
|
-
paletteUi.
|
|
34
|
+
paletteUi.tab = "all";
|
|
33
35
|
paletteUi.query = "";
|
|
34
36
|
}
|
|
35
37
|
},
|
|
36
|
-
|
|
38
|
+
setTab: (tab: PaletteTab, query = "") => {
|
|
37
39
|
paletteUi.picker = null;
|
|
38
|
-
paletteUi.
|
|
40
|
+
paletteUi.tab = tab;
|
|
39
41
|
paletteUi.query = query;
|
|
40
42
|
},
|
|
41
43
|
pick: (placeholder: string, items: PaletteCommand[]) => {
|
|
42
|
-
paletteUi.
|
|
44
|
+
paletteUi.tab = "all";
|
|
43
45
|
paletteUi.picker = ref({
|
|
44
46
|
placeholder,
|
|
45
47
|
items: items.map((c, i) => (i < 9 ? { ...c, hint: `⌘${i + 1}` } : c)),
|
package/stores/shareUi.ts
CHANGED
|
@@ -5,7 +5,12 @@ import type { ShareTarget } from "@/data/shares";
|
|
|
5
5
|
// menu, the pane or the artifact card.
|
|
6
6
|
// A page's version, when it has more than one, is named in the title; the
|
|
7
7
|
// share is of that exact version.
|
|
8
|
-
|
|
8
|
+
// A chat share can start from one message ("Share from here").
|
|
9
|
+
export type ShareUiTarget = ShareTarget & {
|
|
10
|
+
title: string;
|
|
11
|
+
version?: number;
|
|
12
|
+
fromId?: string;
|
|
13
|
+
};
|
|
9
14
|
|
|
10
15
|
export const shareUi = proxy<{
|
|
11
16
|
target: ShareUiTarget | null;
|