feat(core): generalize session input inbox (#36005)

This commit is contained in:
Kit Langton 2026-07-08 22:07:45 -04:00 committed by GitHub
commit 984cab7938
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
47 changed files with 1590 additions and 767 deletions

View file

@ -286,8 +286,8 @@ export function Prompt(props: PromptProps) {
last.tokens.input + last.tokens.output + last.tokens.reasoning + last.tokens.cache.read + last.tokens.cache.write
if (tokens <= 0) return
const model = data.location
.model.list(session.location)
const model = data.location.model
.list(session.location)
?.find((model) => model.providerID === last.model.providerID && model.id === last.model.id)
const pct = model?.limit.context ? `${Math.round((tokens / model.limit.context) * 100)}%` : undefined
const cost = session.cost
@ -1139,11 +1139,9 @@ export function Prompt(props: PromptProps) {
const error = await sdk.api.session
.prompt({
sessionID,
prompt: {
text: [...editorParts.map((part) => part.text), inputText].filter(Boolean).join("\n\n"),
files: store.prompt.files,
agents: store.prompt.agents,
},
text: [...editorParts.map((part) => part.text), inputText].filter(Boolean).join("\n\n"),
files: store.prompt.files,
agents: store.prompt.agents,
})
.then(
() => undefined,

View file

@ -315,19 +315,17 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
setStore("session", "info", event.data.sessionID, "subpath", event.data.subpath)
}
break
case "session.prompt.promoted": {
case "session.input.promoted": {
message.update(event.data.sessionID, (draft, index) => {
const position = index.get(event.data.inputID)
if (position === undefined) return
const existing = draft[position]
if (existing?.type === "user" && store.session.input[event.data.sessionID]?.includes(event.data.inputID)) {
existing.time.created = event.created
draft.splice(position, 1)
draft.push(existing)
index.clear()
draft.forEach((message, indexValue) => index.set(message.id, indexValue))
return
}
if (!existing || !store.session.input[event.data.sessionID]?.includes(event.data.inputID)) return
existing.time.created = event.created
draft.splice(position, 1)
draft.push(existing)
index.clear()
draft.forEach((message, indexValue) => index.set(message.id, indexValue))
})
setStore(
"session",
@ -337,21 +335,30 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
)
break
}
case "session.prompt.admitted":
case "session.input.admitted":
if (!store.session.input[event.data.sessionID]?.includes(event.data.inputID))
setStore("session", "input", event.data.sessionID, [
...(store.session.input[event.data.sessionID] ?? []),
event.data.inputID,
])
message.update(event.data.sessionID, (draft, index) => {
message.append(draft, index, {
id: event.data.inputID,
type: "user",
text: event.data.prompt.text,
files: event.data.prompt.files,
agents: event.data.prompt.agents,
time: { created: event.created },
})
message.append(
draft,
index,
event.data.input.type === "user"
? {
id: event.data.inputID,
type: "user",
...event.data.input.data,
time: { created: event.created },
}
: {
id: event.data.inputID,
type: "synthetic",
...event.data.input.data,
time: { created: event.created },
},
)
})
break
case "session.instructions.updated":
@ -1069,7 +1076,9 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
console.error("Failed to refresh default location data", failure.reason)
const key = locationKey(defaultLocation())
const locations = new Map(
Object.values(store.session.info).map((session) => [locationKey(session.location), session.location] as const),
Object.values(store.session.info).map(
(session) => [locationKey(session.location), session.location] as const,
),
)
const refreshed = await Promise.allSettled(
Array.from(locations)

View file

@ -16,7 +16,7 @@ export type PastedText = {
}
}
export type PromptInfo = Types.DeepMutable<SessionPromptInput["prompt"]> & {
export type PromptInfo = Types.DeepMutable<Pick<SessionPromptInput, "text" | "files" | "agents">> & {
pasted: PastedText[]
mode?: "normal" | "shell"
}

View file

@ -75,7 +75,7 @@ export function createSessionRows(sessionID: Accessor<string>) {
on(
() =>
data.session.message.list(sessionID()).flatMap((message) =>
message.type === "user"
message.type === "user" || message.type === "synthetic"
? [
{
id: message.id,
@ -156,7 +156,7 @@ export function createSessionRows(sessionID: Accessor<string>) {
const isPending = (messageID: string) => {
const message = data.session.message.get(sessionID(), messageID)
if (message?.type === "user") return data.session.input.has(sessionID(), messageID)
if (message?.type === "user" || message?.type === "synthetic") return data.session.input.has(sessionID(), messageID)
return message?.type === "compaction" && message.status === "running"
}
@ -168,11 +168,21 @@ export function createSessionRows(sessionID: Accessor<string>) {
const message = (event: { id: string; data: { sessionID: string } }) => {
if (event.data.sessionID === sessionID()) appendMessage(event.id.replace(/^evt_/, "msg_"))
}
const input = (event: { data: { sessionID: string; inputID: string } }) => {
if (event.data.sessionID === sessionID()) appendMessage(event.data.inputID)
const input = (event: {
data: {
sessionID: string
inputID: string
input: { type: "user" } | { type: "synthetic"; data: { description?: string } }
}
}) => {
if (
event.data.sessionID === sessionID() &&
(event.data.input.type === "user" || event.data.input.data.description?.trim())
)
appendMessage(event.data.inputID)
}
const subscriptions = [
data.on("session.prompt.admitted", input),
data.on("session.input.admitted", input),
data.on("session.compaction.started", message),
data.on("session.instructions.updated", message),
data.on("session.synthetic", (event) => {