|
|
@@ -93,13 +93,21 @@ export namespace Session {
|
|
|
),
|
|
|
}
|
|
|
|
|
|
- export async function create(parentID?: string, title?: string) {
|
|
|
- return createNext({
|
|
|
- parentID,
|
|
|
- directory: Instance.directory,
|
|
|
- title,
|
|
|
- })
|
|
|
- }
|
|
|
+ export const create = fn(
|
|
|
+ z
|
|
|
+ .object({
|
|
|
+ parentID: Identifier.schema("session").optional(),
|
|
|
+ title: z.string().optional(),
|
|
|
+ })
|
|
|
+ .optional(),
|
|
|
+ async (input) => {
|
|
|
+ return createNext({
|
|
|
+ parentID: input?.parentID,
|
|
|
+ directory: Instance.directory,
|
|
|
+ title: input?.title,
|
|
|
+ })
|
|
|
+ },
|
|
|
+ )
|
|
|
|
|
|
export const fork = fn(
|
|
|
z.object({
|
|
|
@@ -132,11 +140,11 @@ export namespace Session {
|
|
|
},
|
|
|
)
|
|
|
|
|
|
- export async function touch(sessionID: string) {
|
|
|
+ export const touch = fn(Identifier.schema("session"), async (sessionID) => {
|
|
|
await update(sessionID, (draft) => {
|
|
|
draft.time.updated = Date.now()
|
|
|
})
|
|
|
- }
|
|
|
+ })
|
|
|
|
|
|
export async function createNext(input: { id?: string; title?: string; parentID?: string; directory: string }) {
|
|
|
const result: Info = {
|
|
|
@@ -170,16 +178,16 @@ export namespace Session {
|
|
|
return result
|
|
|
}
|
|
|
|
|
|
- export async function get(id: string) {
|
|
|
+ export const get = fn(Identifier.schema("session"), async (id) => {
|
|
|
const read = await Storage.read<Info>(["session", Instance.project.id, id])
|
|
|
return read as Info
|
|
|
- }
|
|
|
+ })
|
|
|
|
|
|
- export async function getShare(id: string) {
|
|
|
+ export const getShare = fn(Identifier.schema("session"), async (id) => {
|
|
|
return Storage.read<ShareInfo>(["share", id])
|
|
|
- }
|
|
|
+ })
|
|
|
|
|
|
- export async function share(id: string) {
|
|
|
+ export const share = fn(Identifier.schema("session"), async (id) => {
|
|
|
const cfg = await Config.get()
|
|
|
if (cfg.share === "disabled") {
|
|
|
throw new Error("Sharing is disabled in configuration")
|
|
|
@@ -202,9 +210,9 @@ export namespace Session {
|
|
|
}
|
|
|
}
|
|
|
return share
|
|
|
- }
|
|
|
+ })
|
|
|
|
|
|
- export async function unshare(id: string) {
|
|
|
+ export const unshare = fn(Identifier.schema("session"), async (id) => {
|
|
|
const share = await getShare(id)
|
|
|
if (!share) return
|
|
|
await Storage.remove(["share", id])
|
|
|
@@ -212,7 +220,7 @@ export namespace Session {
|
|
|
draft.share = undefined
|
|
|
})
|
|
|
await Share.remove(id, share.secret)
|
|
|
- }
|
|
|
+ })
|
|
|
|
|
|
export async function update(id: string, editor: (session: Info) => void) {
|
|
|
const project = Instance.project
|
|
|
@@ -226,7 +234,7 @@ export namespace Session {
|
|
|
return result
|
|
|
}
|
|
|
|
|
|
- export async function messages(sessionID: string) {
|
|
|
+ export const messages = fn(Identifier.schema("session"), async (sessionID) => {
|
|
|
const result = [] as MessageV2.WithParts[]
|
|
|
for (const p of await Storage.list(["message", sessionID])) {
|
|
|
const read = await Storage.read<MessageV2.Info>(p)
|
|
|
@@ -237,16 +245,22 @@ export namespace Session {
|
|
|
}
|
|
|
result.sort((a, b) => (a.info.id > b.info.id ? 1 : -1))
|
|
|
return result
|
|
|
- }
|
|
|
+ })
|
|
|
|
|
|
- export async function getMessage(sessionID: string, messageID: string) {
|
|
|
- return {
|
|
|
- info: await Storage.read<MessageV2.Info>(["message", sessionID, messageID]),
|
|
|
- parts: await getParts(messageID),
|
|
|
- }
|
|
|
- }
|
|
|
+ export const getMessage = fn(
|
|
|
+ z.object({
|
|
|
+ sessionID: Identifier.schema("session"),
|
|
|
+ messageID: Identifier.schema("message"),
|
|
|
+ }),
|
|
|
+ async (input) => {
|
|
|
+ return {
|
|
|
+ info: await Storage.read<MessageV2.Info>(["message", input.sessionID, input.messageID]),
|
|
|
+ parts: await getParts(input.messageID),
|
|
|
+ }
|
|
|
+ },
|
|
|
+ )
|
|
|
|
|
|
- export async function getParts(messageID: string) {
|
|
|
+ export const getParts = fn(Identifier.schema("message"), async (messageID) => {
|
|
|
const result = [] as MessageV2.Part[]
|
|
|
for (const item of await Storage.list(["part", messageID])) {
|
|
|
const read = await Storage.read<MessageV2.Part>(item)
|
|
|
@@ -254,7 +268,7 @@ export namespace Session {
|
|
|
}
|
|
|
result.sort((a, b) => (a.id > b.id ? 1 : -1))
|
|
|
return result
|
|
|
- }
|
|
|
+ })
|
|
|
|
|
|
export async function* list() {
|
|
|
const project = Instance.project
|
|
|
@@ -263,7 +277,7 @@ export namespace Session {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- export async function children(parentID: string) {
|
|
|
+ export const children = fn(Identifier.schema("session"), async (parentID) => {
|
|
|
const project = Instance.project
|
|
|
const result = [] as Session.Info[]
|
|
|
for (const item of await Storage.list(["session", project.id])) {
|
|
|
@@ -272,9 +286,9 @@ export namespace Session {
|
|
|
result.push(session)
|
|
|
}
|
|
|
return result
|
|
|
- }
|
|
|
+ })
|
|
|
|
|
|
- export async function remove(sessionID: string) {
|
|
|
+ export const remove = fn(Identifier.schema("session"), async (sessionID) => {
|
|
|
const project = Instance.project
|
|
|
try {
|
|
|
const session = await get(sessionID)
|
|
|
@@ -295,56 +309,69 @@ export namespace Session {
|
|
|
} catch (e) {
|
|
|
log.error(e)
|
|
|
}
|
|
|
- }
|
|
|
+ })
|
|
|
|
|
|
- export async function updateMessage(msg: MessageV2.Info) {
|
|
|
+ export const updateMessage = fn(MessageV2.Info, async (msg) => {
|
|
|
await Storage.write(["message", msg.sessionID, msg.id], msg)
|
|
|
Bus.publish(MessageV2.Event.Updated, {
|
|
|
info: msg,
|
|
|
})
|
|
|
return msg
|
|
|
- }
|
|
|
+ })
|
|
|
|
|
|
- export async function removeMessage(sessionID: string, messageID: string) {
|
|
|
- await Storage.remove(["message", sessionID, messageID])
|
|
|
- Bus.publish(MessageV2.Event.Removed, {
|
|
|
- sessionID,
|
|
|
- messageID,
|
|
|
- })
|
|
|
- return messageID
|
|
|
- }
|
|
|
+ export const removeMessage = fn(
|
|
|
+ z.object({
|
|
|
+ sessionID: Identifier.schema("session"),
|
|
|
+ messageID: Identifier.schema("message"),
|
|
|
+ }),
|
|
|
+ async (input) => {
|
|
|
+ await Storage.remove(["message", input.sessionID, input.messageID])
|
|
|
+ Bus.publish(MessageV2.Event.Removed, {
|
|
|
+ sessionID: input.sessionID,
|
|
|
+ messageID: input.messageID,
|
|
|
+ })
|
|
|
+ return input.messageID
|
|
|
+ },
|
|
|
+ )
|
|
|
|
|
|
- export async function updatePart(part: MessageV2.Part) {
|
|
|
+ export const updatePart = fn(MessageV2.Part, async (part) => {
|
|
|
await Storage.write(["part", part.messageID, part.id], part)
|
|
|
Bus.publish(MessageV2.Event.PartUpdated, {
|
|
|
part,
|
|
|
})
|
|
|
return part
|
|
|
- }
|
|
|
+ })
|
|
|
|
|
|
- export function getUsage(model: ModelsDev.Model, usage: LanguageModelUsage, metadata?: ProviderMetadata) {
|
|
|
- const tokens = {
|
|
|
- input: usage.inputTokens ?? 0,
|
|
|
- output: usage.outputTokens ?? 0,
|
|
|
- reasoning: usage?.reasoningTokens ?? 0,
|
|
|
- cache: {
|
|
|
- write: (metadata?.["anthropic"]?.["cacheCreationInputTokens"] ??
|
|
|
- // @ts-expect-error
|
|
|
- metadata?.["bedrock"]?.["usage"]?.["cacheWriteInputTokens"] ??
|
|
|
- 0) as number,
|
|
|
- read: usage.cachedInputTokens ?? 0,
|
|
|
- },
|
|
|
- }
|
|
|
- return {
|
|
|
- cost: new Decimal(0)
|
|
|
- .add(new Decimal(tokens.input).mul(model.cost?.input ?? 0).div(1_000_000))
|
|
|
- .add(new Decimal(tokens.output).mul(model.cost?.output ?? 0).div(1_000_000))
|
|
|
- .add(new Decimal(tokens.cache.read).mul(model.cost?.cache_read ?? 0).div(1_000_000))
|
|
|
- .add(new Decimal(tokens.cache.write).mul(model.cost?.cache_write ?? 0).div(1_000_000))
|
|
|
- .toNumber(),
|
|
|
- tokens,
|
|
|
- }
|
|
|
- }
|
|
|
+ export const getUsage = fn(
|
|
|
+ z.object({
|
|
|
+ model: z.custom<ModelsDev.Model>(),
|
|
|
+ usage: z.custom<LanguageModelUsage>(),
|
|
|
+ metadata: z.custom<ProviderMetadata>().optional(),
|
|
|
+ }),
|
|
|
+ (input) => {
|
|
|
+ const tokens = {
|
|
|
+ input: input.usage.inputTokens ?? 0,
|
|
|
+ output: input.usage.outputTokens ?? 0,
|
|
|
+ reasoning: input.usage?.reasoningTokens ?? 0,
|
|
|
+ cache: {
|
|
|
+ write: (input.metadata?.["anthropic"]?.["cacheCreationInputTokens"] ??
|
|
|
+ // @ts-expect-error
|
|
|
+ input.metadata?.["bedrock"]?.["usage"]?.["cacheWriteInputTokens"] ??
|
|
|
+ 0) as number,
|
|
|
+ read: input.usage.cachedInputTokens ?? 0,
|
|
|
+ },
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ cost: new Decimal(0)
|
|
|
+ .add(new Decimal(tokens.input).mul(input.model.cost?.input ?? 0).div(1_000_000))
|
|
|
+ .add(new Decimal(tokens.output).mul(input.model.cost?.output ?? 0).div(1_000_000))
|
|
|
+ .add(new Decimal(tokens.cache.read).mul(input.model.cost?.cache_read ?? 0).div(1_000_000))
|
|
|
+ .add(new Decimal(tokens.cache.write).mul(input.model.cost?.cache_write ?? 0).div(1_000_000))
|
|
|
+ .toNumber(),
|
|
|
+ tokens,
|
|
|
+ }
|
|
|
+ },
|
|
|
+ )
|
|
|
|
|
|
export class BusyError extends Error {
|
|
|
constructor(public readonly sessionID: string) {
|
|
|
@@ -352,27 +379,30 @@ export namespace Session {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- export async function initialize(input: {
|
|
|
- sessionID: string
|
|
|
- modelID: string
|
|
|
- providerID: string
|
|
|
- messageID: string
|
|
|
- }) {
|
|
|
- await SessionPrompt.prompt({
|
|
|
- sessionID: input.sessionID,
|
|
|
- messageID: input.messageID,
|
|
|
- model: {
|
|
|
- providerID: input.providerID,
|
|
|
- modelID: input.modelID,
|
|
|
- },
|
|
|
- parts: [
|
|
|
- {
|
|
|
- id: Identifier.ascending("part"),
|
|
|
- type: "text",
|
|
|
- text: PROMPT_INITIALIZE.replace("${path}", Instance.worktree),
|
|
|
+ export const initialize = fn(
|
|
|
+ z.object({
|
|
|
+ sessionID: Identifier.schema("session"),
|
|
|
+ modelID: z.string(),
|
|
|
+ providerID: z.string(),
|
|
|
+ messageID: Identifier.schema("message"),
|
|
|
+ }),
|
|
|
+ async (input) => {
|
|
|
+ await SessionPrompt.prompt({
|
|
|
+ sessionID: input.sessionID,
|
|
|
+ messageID: input.messageID,
|
|
|
+ model: {
|
|
|
+ providerID: input.providerID,
|
|
|
+ modelID: input.modelID,
|
|
|
},
|
|
|
- ],
|
|
|
- })
|
|
|
- await Project.setInitialized(Instance.project.id)
|
|
|
- }
|
|
|
+ parts: [
|
|
|
+ {
|
|
|
+ id: Identifier.ascending("part"),
|
|
|
+ type: "text",
|
|
|
+ text: PROMPT_INITIALIZE.replace("${path}", Instance.worktree),
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ })
|
|
|
+ await Project.setInitialized(Instance.project.id)
|
|
|
+ },
|
|
|
+ )
|
|
|
}
|