Merge branch 'dev' into project

This commit is contained in:
Dax Raad
2025-08-12 18:52:30 -04:00
214 changed files with 15187 additions and 2009 deletions
+92 -25
View File
@@ -10,7 +10,6 @@ import {
type LanguageModelUsage,
type ProviderMetadata,
type ModelMessage,
stepCountIs,
type StreamTextResult,
} from "ai"
@@ -42,6 +41,8 @@ import { Plugin } from "../plugin"
import { Project } from "../project/project"
import { Instance } from "../project/instance"
import { Agent } from "../agent/agent"
import { Permission } from "../permission"
import { Wildcard } from "../util/wildcard"
export namespace Session {
const log = Log.create({ service: "session" })
@@ -525,6 +526,7 @@ export namespace Session {
t.execute(args, {
sessionID: input.sessionID,
abort: new AbortController().signal,
agent: input.agent!,
messageID: userMsg.id,
metadata: async () => {},
}),
@@ -611,15 +613,6 @@ export namespace Session {
]
}),
).then((x) => x.flat())
if (inputAgent === "plan")
userParts.push({
id: Identifier.ascending("part"),
messageID: userMsg.id,
sessionID: input.sessionID,
type: "text",
text: PROMPT_PLAN,
synthetic: true,
})
await Plugin.trigger(
"chat.message",
{},
@@ -682,7 +675,10 @@ export namespace Session {
generateText({
maxOutputTokens: small.info.reasoning ? 1024 : 20,
providerOptions: {
[input.providerID]: small.info.options,
[input.providerID]: {
...small.info.options,
...ProviderTransform.options(input.providerID, small.info.id, input.sessionID),
},
},
messages: [
...SystemPrompt.title(input.providerID).map(
@@ -719,6 +715,16 @@ export namespace Session {
}
const agent = await Agent.get(inputAgent)
if (agent.name === "plan") {
msgs.at(-1)?.parts.push({
id: Identifier.ascending("part"),
messageID: userMsg.id,
sessionID: input.sessionID,
type: "text",
text: PROMPT_PLAN,
synthetic: true,
})
}
let system = SystemPrompt.header(input.providerID)
system.push(
...(() => {
@@ -763,11 +769,11 @@ export namespace Session {
const enabledTools = pipe(
agent.tools,
mergeDeep(await ToolRegistry.enabled(input.providerID, input.modelID)),
mergeDeep(await ToolRegistry.enabled(input.providerID, input.modelID, agent)),
mergeDeep(input.tools ?? {}),
)
for (const item of await ToolRegistry.tools(input.providerID, input.modelID)) {
if (enabledTools[item.id] === false) continue
if (Wildcard.all(item.id, enabledTools) === false) continue
tools[item.id] = tool({
id: item.id as any,
description: item.description,
@@ -789,6 +795,7 @@ export namespace Session {
abort: options.abortSignal!,
messageID: assistantMsg.id,
callID: options.toolCallId,
agent: agent.name,
metadata: async (val) => {
const match = processor.partFromToolCall(options.toolCallId)
if (match && match.state.status === "running") {
@@ -828,7 +835,7 @@ export namespace Session {
}
for (const [key, item] of Object.entries(await MCP.tools())) {
if (enabledTools[key] === false) continue
if (Wildcard.all(key, enabledTools) === false) continue
const execute = item.execute
if (!execute) continue
item.execute = async (args, opts) => {
@@ -851,20 +858,24 @@ export namespace Session {
tools[key] = item
}
const params = {
temperature: model.info.temperature
? (agent.temperature ?? ProviderTransform.temperature(input.providerID, input.modelID))
: undefined,
topP: agent.topP ?? ProviderTransform.topP(input.providerID, input.modelID),
}
await Plugin.trigger(
const params = await Plugin.trigger(
"chat.params",
{
model: model.info,
provider: await Provider.getProvider(input.providerID),
message: userMsg,
},
params,
{
temperature: model.info.temperature
? (agent.temperature ?? ProviderTransform.temperature(input.providerID, input.modelID))
: undefined,
topP: agent.topP ?? ProviderTransform.topP(input.providerID, input.modelID),
options: {
...ProviderTransform.options(input.providerID, input.modelID, input.sessionID),
...model.info.options,
...agent.options,
},
},
)
console.log(outputLimit)
const stream = streamText({
@@ -933,9 +944,20 @@ export namespace Session {
activeTools: Object.keys(tools).filter((x) => x !== "invalid"),
maxOutputTokens: outputLimit,
abortSignal: abort.signal,
stopWhen: stepCountIs(1000),
stopWhen: async ({ steps }) => {
if (steps.length >= 1000) {
return true
}
// Check if processor flagged that we should stop
if (processor.getShouldStop()) {
return true
}
return false
},
providerOptions: {
[input.providerID]: model.info.options,
[input.providerID]: params.options,
},
temperature: params.temperature,
topP: params.topP,
@@ -981,13 +1003,18 @@ export namespace Session {
function createProcessor(assistantMsg: MessageV2.Assistant, model: ModelsDev.Model) {
const toolcalls: Record<string, MessageV2.ToolPart> = {}
let snapshot: string | undefined
let shouldStop = false
return {
partFromToolCall(toolCallID: string) {
return toolcalls[toolCallID]
},
getShouldStop() {
return shouldStop
},
async process(stream: StreamTextResult<Record<string, AITool>, never>) {
try {
let currentText: MessageV2.TextPart | undefined
let reasoningMap: Record<string, MessageV2.ReasoningPart> = {}
for await (const value of stream.fullStream) {
log.info("part", {
@@ -997,6 +1024,44 @@ export namespace Session {
case "start":
break
case "reasoning-start":
if (value.id in reasoningMap) {
continue
}
reasoningMap[value.id] = {
id: Identifier.ascending("part"),
messageID: assistantMsg.id,
sessionID: assistantMsg.sessionID,
type: "reasoning",
text: "",
time: {
start: Date.now(),
},
}
break
case "reasoning-delta":
if (value.id in reasoningMap) {
const part = reasoningMap[value.id]
part.text += value.text
if (part.text) await updatePart(part)
}
break
case "reasoning-end":
if (value.id in reasoningMap) {
const part = reasoningMap[value.id]
part.text = part.text.trimEnd()
part.metadata = value.providerMetadata
part.time = {
...part.time,
end: Date.now(),
}
await updatePart(part)
delete reasoningMap[value.id]
}
break
case "tool-input-start":
const part = await updatePart({
id: toolcalls[value.id]?.id ?? Identifier.ascending("part"),
@@ -1061,6 +1126,9 @@ export namespace Session {
case "tool-error": {
const match = toolcalls[value.toolCallId]
if (match && match.state.status === "running") {
if (value.error instanceof Permission.RejectedError) {
shouldStop = true
}
await updatePart({
...match,
state: {
@@ -1077,7 +1145,6 @@ export namespace Session {
}
break
}
case "error":
throw value.error