feat: add URL-driven workspace state and streamed Codex research

This commit is contained in:
2026-09-28 20:08:28 +09:00
parent 85d23a328b
commit 8085ad2f90
207 changed files with 15287 additions and 16026 deletions
+37 -49
View File
@@ -1,82 +1,73 @@
import { z } from 'zod'
import type { Connection } from '#/features/connections/model'
import { mastodonSourceSchema } from '#/features/platforms/mastodon-source'
import { InputError } from '#/features/posts/inputs'
import { columnSchema, deckSchema, MAX_COLUMNS, newId } from './model'
import { z } from "zod";
import type { Connection } from "#/features/connections/model";
import { mastodonSourceSchema } from "#/features/platforms/mastodon-source";
import { InputError } from "#/features/posts/inputs";
import { columnSchema, deckSchema, MAX_COLUMNS, newId } from "./model";
const toolColumn = columnSchema.omit({ id: true, source: true }).extend({
id: columnSchema.shape.id
.optional()
.describe(
'Keep an existing column ID when editing; omit for a new column.',
),
.describe("Keep an existing column ID when editing; omit for a new column."),
source: z.union([
mastodonSourceSchema,
z.discriminatedUnion('kind', [
z.discriminatedUnion("kind", [
z
.object({
platform: z.literal('twitter').default('twitter'),
kind: z.literal('search'),
platform: z.literal("twitter").default("twitter"),
kind: z.literal("search"),
query: z.string().trim().min(1).max(512),
product: z.enum(['Latest', 'Top']).default('Latest'),
product: z.enum(["Latest", "Top"]).default("Latest"),
following: z.boolean().default(false),
})
.strict(),
z
.object({
platform: z.literal('twitter').default('twitter'),
kind: z.literal('user'),
platform: z.literal("twitter").default("twitter"),
kind: z.literal("user"),
target: z.string().min(1).max(256),
})
.strict(),
z
.object({
platform: z.literal('twitter').default('twitter'),
kind: z.literal('list'),
platform: z.literal("twitter").default("twitter"),
kind: z.literal("list"),
target: z.string().min(1).max(256),
})
.strict(),
]),
]),
})
export const emptyToolInput = z.object({}).strict()
export const deckIdInput = z.object({ deckId: deckSchema.shape.id }).strict()
});
export const emptyToolInput = z.object({}).strict();
export const deckIdInput = z.object({ deckId: deckSchema.shape.id }).strict();
export const deleteDeckToolInput = deckIdInput
.extend({ expectedRevision: z.number().int().positive().optional() })
.strict()
export const getDeckInput = z
.object({ deckId: deckSchema.shape.id.optional() })
.strict()
.strict();
export const getDeckInput = z.object({ deckId: deckSchema.shape.id.optional() }).strict();
export const setDeckInput = z
.object({
deckId: deckSchema.shape.id
.optional()
.describe(
'Omit to create a temporary view in this tab; supply an existing ID to replace and activate it.',
"Omit to create a temporary view in this tab; supply an existing ID to replace and activate it.",
),
expectedRevision: z
.number()
.int()
.positive()
.optional()
.describe(
'Required when replacing a saved deck; omit for a temporary view.',
),
.describe("Required when replacing a saved deck; omit for a temporary view."),
title: deckSchema.shape.title,
columns: z
.array(toolColumn)
.max(MAX_COLUMNS)
.describe(
'Complete ordered columns, each bound to a connection ID from list_connections. Omitted existing columns are removed.',
"Complete ordered columns, each bound to a connection ID from list_connections. Omitted existing columns are removed.",
),
})
.strict()
.strict();
export function prepareDeck(
input: unknown,
connections: readonly Connection[],
) {
const parsed = setDeckInput.parse(input)
export function prepareDeck(input: unknown, connections: readonly Connection[]) {
const parsed = setDeckInput.parse(input);
const deck = deckSchema.parse({
id: parsed.deckId ?? newId(),
title: parsed.title,
@@ -84,45 +75,42 @@ export function prepareDeck(
...column,
id: column.id ?? newId(),
})),
})
});
for (const column of deck.columns) {
if (
!connections.some(
(connection) =>
connection.id === column.connectionId &&
connection.platform === column.source.platform &&
connection.status === 'connected',
connection.status === "connected",
)
)
throw new InputError(`Unavailable connection: ${column.connectionId}`)
throw new InputError(`Unavailable connection: ${column.connectionId}`);
}
return deck
return deck;
}
export async function toolResult(run: () => unknown | Promise<unknown>) {
export async function toolResult(run: () => unknown) {
try {
return {
content: [
{ type: 'text' as const, text: JSON.stringify(await run()) },
] as const,
}
content: [{ type: "text" as const, text: JSON.stringify(await run()) }] as const,
};
} catch (error) {
return {
isError: true,
content: [
{
type: 'text' as const,
type: "text" as const,
text: JSON.stringify({
code:
error instanceof z.ZodError || error instanceof InputError
? 'invalid-input'
: 'tool-error',
message:
error instanceof Error ? error.message : 'Tool execution failed.',
? "invalid-input"
: "tool-error",
message: error instanceof Error ? error.message : "Tool execution failed.",
retryable: false,
}),
},
] as const,
}
};
}
}