mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-29 03:44:06 +08:00
fix: resolve OpenCode v1.15.4 merge regressions
This commit is contained in:
@@ -285,7 +285,7 @@ for (const item of targets) {
|
||||
const sessionExportWorkerPath = "./src/kilocode/session-export/worker.ts" // kilocode_change
|
||||
const indexingWorkerPath = "./src/kilocode/indexing-worker.ts" // kilocode_change
|
||||
|
||||
// Use platform-specific bunfs root path based on target OS // kilocode_change
|
||||
// Use platform-specific bunfs root path based on target OS
|
||||
const bunfsRoot = item.os === "win32" ? "B:/~BUN/root/" : "/$bunfs/root/"
|
||||
const workerRelativePath = path.relative(dir, parserWorker).replaceAll("\\", "/")
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@ export class Agent implements ACPAgent {
|
||||
"terminal-auth": {
|
||||
command: "opencode",
|
||||
args: ["auth", "login"],
|
||||
label: "Kilo Login", // kilocode_change
|
||||
label: "Kilo Login",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -570,7 +570,7 @@ export class Agent implements ACPAgent {
|
||||
},
|
||||
authMethods: [authMethod],
|
||||
agentInfo: {
|
||||
name: "Kilo", // kilocode_change
|
||||
name: "Kilo",
|
||||
version: InstallationVersion,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Effect, Exit, Fiber, Layer, PubSub, Scope, Context, Stream, Schema } from "effect"
|
||||
import { Effect, Exit, Fiber, Layer, PubSub, Scope, Context, Stream, Schema } from "effect" // kilocode_change
|
||||
import { EffectBridge } from "@/effect/bridge"
|
||||
import * as Log from "@opencode-ai/core/util/log"
|
||||
import { BusEvent } from "./bus-event"
|
||||
@@ -6,7 +6,7 @@ import { GlobalBus } from "./global"
|
||||
import { InstanceState } from "@/effect/instance-state"
|
||||
import { makeRuntime } from "@/effect/run-service"
|
||||
import { Identifier } from "@/id/id"
|
||||
import { context as instanceContext, type InstanceContext } from "@/project/instance-context"
|
||||
import { context as instanceContext, type InstanceContext } from "@/project/instance-context" // kilocode_change
|
||||
import { InstanceRef } from "@/effect/instance-ref"
|
||||
import { LocalContext } from "@/util/local-context" // kilocode_change
|
||||
|
||||
|
||||
@@ -488,7 +488,7 @@ export const GithubRunCommand = effectCmd({
|
||||
? (payload as IssueCommentEvent | IssuesEvent).issue.number
|
||||
: (payload as PullRequestEvent | PullRequestReviewCommentEvent).pull_request.number
|
||||
const runUrl = `/${owner}/${repo}/actions/runs/${runId}`
|
||||
const shareBaseUrl = isMock ? "https://dev.kilo.ai" : "https://kilo.ai" // kilocode_change
|
||||
const shareBaseUrl = isMock ? "https://dev.kilo.ai" : "https://kilo.ai"
|
||||
|
||||
let appToken: string
|
||||
let octoRest: Octokit
|
||||
@@ -749,7 +749,7 @@ export const GithubRunCommand = effectCmd({
|
||||
|
||||
function normalizeOidcBaseUrl(): string {
|
||||
const value = process.env["OIDC_BASE_URL"]
|
||||
if (!value) return "https://api.kilo.ai" // kilocode_change
|
||||
if (!value) return "https://api.kilo.ai"
|
||||
return value.replace(/\/+$/, "")
|
||||
}
|
||||
|
||||
|
||||
@@ -203,7 +203,7 @@ export const RunCommand = effectCmd({
|
||||
})
|
||||
.option("attach", {
|
||||
type: "string",
|
||||
describe: "attach to a running kilo server (e.g., http://localhost:4096)", // kilocode_change
|
||||
describe: "attach to a running kilo server (e.g., http://localhost:4096)",
|
||||
})
|
||||
.option("password", {
|
||||
alias: ["p"],
|
||||
|
||||
@@ -317,7 +317,7 @@ export function DialogSessionList() {
|
||||
},
|
||||
{
|
||||
command: "session.rename",
|
||||
title: "rename", // kilocode_change
|
||||
title: "rename",
|
||||
// kilocode_change start
|
||||
onTrigger: async (option) => {
|
||||
const item = sessions().find((x) => x.id === option.value)
|
||||
|
||||
@@ -233,15 +233,15 @@ const TIPS: Tip[] = [
|
||||
"Tool definitions can invoke scripts written in Python, Go, etc",
|
||||
"Add {highlight}.ts{/highlight} files to {highlight}.opencode/plugins/{/highlight} for event hooks",
|
||||
"Use plugins to send OS notifications when sessions complete",
|
||||
"Create a plugin to prevent Kilo from reading sensitive files", // kilocode_change
|
||||
"Create a plugin to prevent Kilo from reading sensitive files",
|
||||
"Use {highlight}kilo run{/highlight} for non-interactive scripting", // kilocode_change
|
||||
"Use {highlight}kilo --continue{/highlight} to resume the last session", // kilocode_change
|
||||
"Use {highlight}kilo run -f file.ts{/highlight} to attach files via CLI", // kilocode_change
|
||||
"Use {highlight}--format json{/highlight} for machine-readable output in scripts",
|
||||
"Run {highlight}kilo serve{/highlight} for headless API access to Kilo", // kilocode_change
|
||||
"Run {highlight}kilo serve{/highlight} for headless API access to Kilo",
|
||||
"Use {highlight}kilo run --attach{/highlight} to connect to a running server", // kilocode_change
|
||||
"Run {highlight}kilo upgrade{/highlight} to update to the latest version", // kilocode_change
|
||||
"Run {highlight}kilo auth list{/highlight} to see all configured providers", // kilocode_change
|
||||
"Run {highlight}kilo upgrade{/highlight} to update to the latest version",
|
||||
"Run {highlight}kilo auth list{/highlight} to see all configured providers",
|
||||
"Run {highlight}kilo agent create{/highlight} for guided agent creation", // kilocode_change
|
||||
"Use {highlight}/opencode{/highlight} in GitHub issues/PRs to trigger AI actions",
|
||||
"Run {highlight}kilo github install{/highlight} to set up the GitHub workflow", // kilocode_change
|
||||
|
||||
@@ -33,7 +33,7 @@ function systemManagedConfigDir(): string {
|
||||
}
|
||||
|
||||
export function managedConfigDir() {
|
||||
return process.env.KILO_TEST_MANAGED_CONFIG_DIR || systemManagedConfigDir() // kilocode_change
|
||||
return process.env.KILO_TEST_MANAGED_CONFIG_DIR || systemManagedConfigDir()
|
||||
}
|
||||
|
||||
export function parseManagedPlist(json: string): string {
|
||||
|
||||
@@ -24,7 +24,7 @@ export const directories = Effect.fn("ConfigPaths.directories")(function* (direc
|
||||
const afs = yield* AppFileSystem.Service
|
||||
return unique([
|
||||
Global.Path.config,
|
||||
...(!Flag.KILO_DISABLE_PROJECT_CONFIG // kilocode_change
|
||||
...(!Flag.KILO_DISABLE_PROJECT_CONFIG
|
||||
? yield* afs.up({
|
||||
targets: [".kilocode", ".kilo", ".opencode"], // kilocode_change
|
||||
start: directory,
|
||||
@@ -36,7 +36,7 @@ export const directories = Effect.fn("ConfigPaths.directories")(function* (direc
|
||||
start: Global.Path.home,
|
||||
stop: Global.Path.home,
|
||||
})),
|
||||
...(Flag.KILO_CONFIG_DIR ? [Flag.KILO_CONFIG_DIR] : []), // kilocode_change
|
||||
...(Flag.KILO_CONFIG_DIR ? [Flag.KILO_CONFIG_DIR] : []),
|
||||
])
|
||||
})
|
||||
|
||||
|
||||
@@ -34,8 +34,7 @@ function captureSync() {
|
||||
export const bind = <Args extends readonly unknown[], Result>(fn: (...args: Args) => Result) => {
|
||||
const captured = captureSync()
|
||||
return (...args: Args) =>
|
||||
restore(captured.instance, captured.workspace, () =>
|
||||
// kilocode_change
|
||||
restore(captured.instance, captured.workspace, () => // kilocode_change
|
||||
Effect.runSync(
|
||||
attachWith(
|
||||
Effect.sync(() => fn(...args)),
|
||||
|
||||
@@ -26,8 +26,8 @@ export function attachWith<A, E, R>(effect: Effect.Effect<A, E, R>, refs: Refs):
|
||||
export function attach<A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> {
|
||||
const workspace = WorkspaceContext.workspaceID
|
||||
const fiber = Fiber.getCurrent()
|
||||
const current = fiber ? Context.getReferenceUnsafe(fiber.context, InstanceRef) : undefined
|
||||
// kilocode_change start - bridge legacy AsyncLocalStorage instance context into Effect runtimes
|
||||
const current = fiber ? Context.getReferenceUnsafe(fiber.context, InstanceRef) : undefined
|
||||
const instance = (() => {
|
||||
if (current) return current
|
||||
try {
|
||||
@@ -36,9 +36,9 @@ export function attach<A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A
|
||||
if (!(err instanceof LocalContext.NotFound)) throw err
|
||||
}
|
||||
})()
|
||||
// kilocode_change end
|
||||
return attachWith(effect, {
|
||||
instance,
|
||||
// kilocode_change end
|
||||
workspace: workspace ?? (fiber ? Context.getReferenceUnsafe(fiber.context, WorkspaceRef) : undefined),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,11 +5,13 @@ import { Bus as ProjectBus } from "@/bus"
|
||||
import { GlobalBus } from "@/bus/global"
|
||||
import { InstanceRef, WorkspaceRef } from "@/effect/instance-ref"
|
||||
import { InstanceStore } from "@/project/instance-store"
|
||||
import * as EventWire from "@/kilocode/event-wire" // kilocode_change
|
||||
import { SyncEvent } from "@/sync"
|
||||
import { EventV2 } from "@opencode-ai/core/event"
|
||||
import "@opencode-ai/core/catalog"
|
||||
import "@opencode-ai/core/session-event"
|
||||
import { Context, Effect, Layer, Option } from "effect"
|
||||
import { Schema } from "effect" // kilocode_change - encode EventV2 data at legacy boundaries
|
||||
|
||||
export function toSyncDefinition<D extends EventV2.Definition>(definition: D) {
|
||||
const result = {
|
||||
@@ -18,6 +20,7 @@ export function toSyncDefinition<D extends EventV2.Definition>(definition: D) {
|
||||
aggregate: definition.aggregate,
|
||||
schema: definition.data,
|
||||
properties: definition.data,
|
||||
wire: true, // kilocode_change
|
||||
}
|
||||
return result as SyncEvent.Definition<D["type"], D["data"], D["data"]>
|
||||
}
|
||||
@@ -31,24 +34,26 @@ export const layer = Layer.effect(
|
||||
const bus = yield* ProjectBus.Service
|
||||
const sync = yield* SyncEvent.Service
|
||||
|
||||
const publishGlobal = (event: EventV2.Payload) =>
|
||||
// kilocode_change start - legacy bus and SSE consumers require the schema's encoded representation
|
||||
const publishGlobal = (event: EventV2.Payload, data: unknown) =>
|
||||
Effect.sync(() => {
|
||||
GlobalBus.emit("event", {
|
||||
directory: event.location?.directory ?? "global",
|
||||
workspace: event.location?.workspaceID,
|
||||
payload: {
|
||||
id: event.id,
|
||||
type: event.type,
|
||||
properties: event.data,
|
||||
properties: data,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
const provideEventLocation = <E, R>(event: EventV2.Payload, effect: Effect.Effect<void, E, R>) => {
|
||||
const provideEventLocation = <E, R>(event: EventV2.Payload, data: unknown, effect: Effect.Effect<void, E, R>) => {
|
||||
return Effect.gen(function* () {
|
||||
const ctx = yield* InstanceRef
|
||||
if (ctx) return yield* effect
|
||||
const store = Option.getOrUndefined(yield* Effect.serviceOption(InstanceStore.Service))
|
||||
if (!event.location?.directory || !store) return yield* publishGlobal(event)
|
||||
if (!event.location?.directory || !store) return yield* publishGlobal(event, data)
|
||||
return yield* store.load({ directory: event.location.directory }).pipe(
|
||||
Effect.flatMap((ctx) => {
|
||||
const withInstance = effect.pipe(Effect.provideService(InstanceRef, ctx))
|
||||
@@ -58,21 +63,26 @@ export const layer = Layer.effect(
|
||||
)
|
||||
})
|
||||
}
|
||||
// kilocode_change end
|
||||
|
||||
const unsubscribe = yield* events.sync((event) => {
|
||||
const definition = EventV2.registry.get(event.type)
|
||||
if (!definition) return Effect.void
|
||||
const data = EventWire.encode(definition.data, event.data) // kilocode_change
|
||||
const aggregateID = definition.aggregate
|
||||
? (event.data as Record<string, unknown>)[definition.aggregate]
|
||||
: undefined
|
||||
|
||||
if (definition.version !== undefined && typeof aggregateID === "string") {
|
||||
return provideEventLocation(event, sync.run(toSyncDefinition(definition), event.data))
|
||||
return provideEventLocation(event, data, sync.run(toSyncDefinition(definition), event.data)) // kilocode_change
|
||||
}
|
||||
|
||||
return provideEventLocation(
|
||||
event,
|
||||
bus.publish({ type: definition.type, properties: definition.data }, event.data, { id: event.id }),
|
||||
// kilocode_change start
|
||||
data,
|
||||
bus.publish({ type: definition.type, properties: Schema.toEncoded(definition.data) }, data, { id: event.id }),
|
||||
// kilocode_change end
|
||||
)
|
||||
})
|
||||
yield* Effect.addFinalizer(() => unsubscribe)
|
||||
|
||||
@@ -16,7 +16,7 @@ import { FileIgnore } from "./ignore"
|
||||
import { Protected } from "./protected"
|
||||
import * as Log from "@opencode-ai/core/util/log"
|
||||
|
||||
declare const KILO_LIBC: string | undefined // kilocode_change
|
||||
declare const KILO_LIBC: string | undefined
|
||||
|
||||
const log = Log.create({ service: "file.watcher" })
|
||||
const SUBSCRIBE_TIMEOUT_MS = 10_000
|
||||
@@ -34,7 +34,7 @@ export const Event = {
|
||||
const watcher = lazy((): typeof import("@parcel/watcher") | undefined => {
|
||||
try {
|
||||
const binding = require(
|
||||
`@parcel/watcher-${process.platform}-${process.arch}${process.platform === "linux" ? `-${KILO_LIBC || "glibc"}` : ""}`, // kilocode_change
|
||||
`@parcel/watcher-${process.platform}-${process.arch}${process.platform === "linux" ? `-${KILO_LIBC || "glibc"}` : ""}`,
|
||||
)
|
||||
return createWrapper(binding) as typeof import("@parcel/watcher")
|
||||
} catch (error) {
|
||||
@@ -73,7 +73,7 @@ export const layer = Layer.effect(
|
||||
const state = yield* InstanceState.make(
|
||||
Effect.fn("FileWatcher.state")(
|
||||
function* () {
|
||||
if (yield* Flag.KILO_EXPERIMENTAL_DISABLE_FILEWATCHER) return // kilocode_change
|
||||
if (yield* Flag.KILO_EXPERIMENTAL_DISABLE_FILEWATCHER) return
|
||||
|
||||
const ctx = yield* InstanceState.context
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { ProviderTransform } from "../../../provider/transform"
|
||||
import { cmd } from "../../../cli/cmd/cmd"
|
||||
import { UI } from "../../../cli/ui"
|
||||
import { AppRuntime } from "../../../effect/app-runtime"
|
||||
import { RuntimeFlags } from "../../../effect/runtime-flags"
|
||||
import { generateText } from "ai"
|
||||
import { randomUUID } from "crypto"
|
||||
|
||||
@@ -134,6 +135,10 @@ function lang(model: Provider.Model) {
|
||||
return AppRuntime.runPromise(Provider.Service.use((svc) => svc.getLanguage(model)))
|
||||
}
|
||||
|
||||
export function outputLimit(model: Provider.Model, outputTokenMax?: number) {
|
||||
return ProviderTransform.maxOutputTokens(model, outputTokenMax)
|
||||
}
|
||||
|
||||
export async function handle(args: ArgumentsCamelCase) {
|
||||
const load = args.list ?? list
|
||||
|
||||
@@ -285,7 +290,9 @@ async function call(
|
||||
const sessionID = randomUUID()
|
||||
const options = ProviderTransform.options({ model, sessionID })
|
||||
const providerOptions = ProviderTransform.providerOptions(model, options)
|
||||
const maxOutputTokens = ProviderTransform.maxOutputTokens(model)
|
||||
const maxOutputTokens = await AppRuntime.runPromise(
|
||||
RuntimeFlags.Service.useSync((flags) => outputLimit(model, flags.outputTokenMax)),
|
||||
)
|
||||
const temperature = ProviderTransform.temperature(model)
|
||||
const topP = ProviderTransform.topP(model)
|
||||
const topK = ProviderTransform.topK(model)
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { DateTime, Schema } from "effect"
|
||||
|
||||
const codec = (schema: Schema.Top) => schema as Schema.Codec<unknown, unknown>
|
||||
|
||||
function wire(value: unknown): unknown {
|
||||
if (DateTime.isDateTime(value)) return DateTime.toEpochMillis(value)
|
||||
if (Array.isArray(value)) return value.map(wire)
|
||||
if (!value || typeof value !== "object") return value
|
||||
const json = (value as { toJSON?: () => unknown }).toJSON
|
||||
if (typeof json === "function") {
|
||||
const result = json.call(value)
|
||||
if (result !== value) return wire(result)
|
||||
}
|
||||
return Object.fromEntries(
|
||||
Object.entries(value)
|
||||
.filter(([, item]) => item !== undefined)
|
||||
.map(([key, item]) => [key, wire(item)]),
|
||||
)
|
||||
}
|
||||
|
||||
function legacy(value: unknown): unknown {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) return value
|
||||
const timestamp = (value as { timestamp?: unknown }).timestamp
|
||||
if (typeof timestamp !== "string") return value
|
||||
const millis = Date.parse(timestamp)
|
||||
if (!Number.isFinite(millis)) return value
|
||||
return { ...value, timestamp: millis }
|
||||
}
|
||||
|
||||
export function encode<S extends Schema.Top>(schema: S, value: unknown): S["Encoded"] {
|
||||
const target = codec(schema)
|
||||
const decoded = Schema.decodeUnknownSync(target)(wire(value))
|
||||
return Schema.encodeUnknownSync(target)(decoded) as S["Encoded"]
|
||||
}
|
||||
|
||||
export function decode<S extends Schema.Top>(schema: S, value: unknown): S["Type"] {
|
||||
const target = codec(schema)
|
||||
try {
|
||||
return Schema.decodeUnknownSync(target)(value) as S["Type"]
|
||||
} catch {
|
||||
return Schema.decodeUnknownSync(target)(legacy(value)) as S["Type"]
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { Effect } from "effect"
|
||||
import type { Agent } from "@/agent/agent"
|
||||
import type { Config } from "@/config/config"
|
||||
import type { Provider } from "@/provider/provider"
|
||||
import { ProviderTransform } from "@/provider/transform"
|
||||
import type { LLM } from "@/session/llm"
|
||||
import { MessageV2 } from "@/session/message-v2"
|
||||
import { usable } from "@/session/overflow"
|
||||
@@ -47,6 +48,7 @@ export namespace KiloCompactionChunks {
|
||||
messages: MessageV2.WithParts[]
|
||||
prompt: string
|
||||
target: MessageV2.Assistant
|
||||
outputTokenMax?: number
|
||||
updateMessage: UpdateMessage
|
||||
updatePart: Update
|
||||
}
|
||||
@@ -61,12 +63,18 @@ export namespace KiloCompactionChunks {
|
||||
return input.result === "stop" && input.error?.name === "ContextOverflowError"
|
||||
}
|
||||
|
||||
export function needed(input: { cfg: Config.Info; model: Provider.Model; tokens: number }) {
|
||||
export function needed(input: {
|
||||
cfg: Config.Info
|
||||
model: Provider.Model
|
||||
tokens: number
|
||||
outputTokenMax?: number
|
||||
}) {
|
||||
const mdl = model(input.model, input.outputTokenMax)
|
||||
// Apply 1.3x multiplier to token estimate to compensate for Token.estimate
|
||||
// under-counting actual provider tokenizer counts by ~15-30%.
|
||||
return (
|
||||
Math.ceil(input.tokens * 1.3) + model(input.model).limit.output >
|
||||
usable({ cfg: input.cfg, model: model(input.model) })
|
||||
Math.ceil(input.tokens * 1.3) + mdl.limit.output >
|
||||
usable({ cfg: input.cfg, model: mdl, outputTokenMax: input.outputTokenMax })
|
||||
)
|
||||
}
|
||||
|
||||
@@ -76,7 +84,7 @@ export namespace KiloCompactionChunks {
|
||||
index: 0,
|
||||
messages: [{ info: input.replay.info, parts: input.replay.parts }],
|
||||
}
|
||||
const size = budget({ cfg: input.cfg, model: input.model })
|
||||
const size = budget({ cfg: input.cfg, model: input.model, outputTokenMax: input.outputTokenMax })
|
||||
if (!(yield* large({ messages: chunk.messages, model: input.model, size }))) return input.replay
|
||||
const result = yield* summarize({ ...input, chunk, total: 1 })
|
||||
if (result.result !== "continue" || !result.output) return input.replay
|
||||
@@ -100,16 +108,21 @@ export namespace KiloCompactionChunks {
|
||||
})
|
||||
}
|
||||
|
||||
function budget(input: { cfg: Config.Info; model: Provider.Model }) {
|
||||
return Math.max(1_000, Math.floor(usable({ cfg: input.cfg, model: model(input.model) }) * RATIO))
|
||||
export function budget(input: { cfg: Config.Info; model: Provider.Model; outputTokenMax?: number }) {
|
||||
const mdl = model(input.model, input.outputTokenMax)
|
||||
return Math.max(
|
||||
1_000,
|
||||
Math.floor(usable({ cfg: input.cfg, model: mdl, outputTokenMax: input.outputTokenMax }) * RATIO),
|
||||
)
|
||||
}
|
||||
|
||||
function model(input: Provider.Model) {
|
||||
function model(input: Provider.Model, outputTokenMax?: number) {
|
||||
const cap = Math.min(OUTPUT, outputTokenMax ?? OUTPUT)
|
||||
return {
|
||||
...input,
|
||||
limit: {
|
||||
...input.limit,
|
||||
output: Math.min(input.limit.output, OUTPUT),
|
||||
output: ProviderTransform.maxOutputTokens(input, cap),
|
||||
},
|
||||
} satisfies Provider.Model
|
||||
}
|
||||
@@ -239,14 +252,17 @@ export namespace KiloCompactionChunks {
|
||||
function run(input: Input & { data: LLM.StreamInput["messages"]; text: string }) {
|
||||
return Effect.gen(function* () {
|
||||
const msg = yield* input.session.updateMessage(assistant({ base: input.target, sessionID: input.sessionID }))
|
||||
const mdl = model(input.model)
|
||||
const mdl = model(input.model, input.outputTokenMax)
|
||||
const worker = yield* input.processors.create({ assistantMessage: msg, sessionID: input.sessionID, model: mdl })
|
||||
const opts = input.agent.options
|
||||
const agent = {
|
||||
...input.agent,
|
||||
options: {
|
||||
...opts,
|
||||
maxOutputTokens: Math.min(OUTPUT, typeof opts?.maxOutputTokens === "number" ? opts.maxOutputTokens : OUTPUT),
|
||||
maxOutputTokens: Math.min(
|
||||
mdl.limit.output,
|
||||
typeof opts?.maxOutputTokens === "number" ? opts.maxOutputTokens : mdl.limit.output,
|
||||
),
|
||||
},
|
||||
}
|
||||
const out = yield* Effect.gen(function* () {
|
||||
@@ -276,7 +292,7 @@ export namespace KiloCompactionChunks {
|
||||
|
||||
function summarize(input: Input & { chunk: Chunk; total: number }) {
|
||||
return Effect.gen(function* () {
|
||||
const size = budget({ cfg: input.cfg, model: input.model })
|
||||
const size = budget({ cfg: input.cfg, model: input.model, outputTokenMax: input.outputTokenMax })
|
||||
const data = (yield* large({ messages: input.chunk.messages, model: input.model, size }))
|
||||
? [
|
||||
{
|
||||
@@ -325,7 +341,7 @@ export namespace KiloCompactionChunks {
|
||||
|
||||
export function process(input: Input) {
|
||||
return Effect.gen(function* () {
|
||||
const size = budget({ cfg: input.cfg, model: input.model })
|
||||
const size = budget({ cfg: input.cfg, model: input.model, outputTokenMax: input.outputTokenMax })
|
||||
const chunks = yield* split({ messages: input.messages, model: input.model, size })
|
||||
log.info("fallback", { chunks: chunks.length, concurrency: CONCURRENCY })
|
||||
|
||||
|
||||
@@ -44,8 +44,8 @@ export class McpOAuthProvider implements OAuthClientProvider {
|
||||
get clientMetadata(): OAuthClientMetadata {
|
||||
return {
|
||||
redirect_uris: [this.redirectUrl],
|
||||
client_name: "Kilo", // kilocode_change
|
||||
client_uri: "https://kilo.ai", // kilocode_change
|
||||
client_name: "Kilo",
|
||||
client_uri: "https://kilo.ai",
|
||||
grant_types: ["authorization_code", "refresh_token"],
|
||||
response_types: ["code"],
|
||||
token_endpoint_auth_method: this.config.clientSecret ? "client_secret_post" : "none",
|
||||
|
||||
@@ -4,7 +4,7 @@ import { InstanceRef } from "@/effect/instance-ref"
|
||||
import { disposeInstance as runDisposers } from "@/effect/instance-registry"
|
||||
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
||||
import { Context, Deferred, Duration, Effect, Exit, Layer, Scope } from "effect"
|
||||
import { context as instanceContext, type InstanceContext } from "./instance-context"
|
||||
import { context as instanceContext, type InstanceContext } from "./instance-context" // kilocode_change
|
||||
import { InstanceBootstrap } from "./bootstrap-service"
|
||||
import * as Project from "./project"
|
||||
|
||||
@@ -52,10 +52,11 @@ export const layer: Layer.Layer<Service, never, Project.Service | InstanceBootst
|
||||
project: result.project,
|
||||
})),
|
||||
)
|
||||
// kilocode_change - run bootstrap inside the Instance ALS so KilocodeBootstrap
|
||||
// kilocode_change start - run bootstrap inside the Instance ALS so KilocodeBootstrap
|
||||
// (and anything it forks via Effect.forkDetach) sees Instance.directory.
|
||||
const ready = bootstrap.run.pipe(Effect.provideService(InstanceRef, ctx)) as Effect.Effect<void>
|
||||
yield* Effect.promise(() => instanceContext.provide(ctx, () => Effect.runPromise(ready)))
|
||||
// kilocode_change end
|
||||
return ctx
|
||||
}).pipe(Effect.withSpan("InstanceStore.boot"))
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// kilocode_change - adapt Kilo model assembly to the upstream core models service
|
||||
// kilocode_change - new file
|
||||
import { Config } from "@/config/config"
|
||||
import { Auth } from "@/auth"
|
||||
import { ModelCache } from "./model-cache"
|
||||
|
||||
@@ -144,12 +144,12 @@ function buildPrompt(input: { previousSummary?: string; context: string[] }) {
|
||||
return [anchor, SUMMARY_TEMPLATE, ...input.context].join("\n\n")
|
||||
}
|
||||
|
||||
function preserveRecentBudget(input: { cfg: Config.Info; model: Provider.Model; outputTokenMax?: number }) {
|
||||
function preserveRecentBudget(input: { cfg: Config.Info; model: Provider.Model; outputTokenMax?: number }) { // kilocode_change
|
||||
return (
|
||||
input.cfg.compaction?.preserve_recent_tokens ??
|
||||
Math.min(MAX_PRESERVE_RECENT_TOKENS, Math.max(MIN_PRESERVE_RECENT_TOKENS, Math.floor(usable(input) * 0.25)))
|
||||
)
|
||||
} // kilocode_change
|
||||
}
|
||||
|
||||
function turns(messages: MessageV2.WithParts[]) {
|
||||
const result: Turn[] = []
|
||||
@@ -260,11 +260,13 @@ export const layer = Layer.effect(
|
||||
}) {
|
||||
const limit = input.cfg.compaction?.tail_turns ?? DEFAULT_TAIL_TURNS
|
||||
if (limit <= 0) return { head: input.messages, tail_start_id: undefined }
|
||||
// kilocode_change start
|
||||
const budget = preserveRecentBudget({
|
||||
cfg: input.cfg,
|
||||
model: input.model,
|
||||
outputTokenMax: flags.outputTokenMax,
|
||||
}) // kilocode_change
|
||||
})
|
||||
// kilocode_change end
|
||||
const all = turns(input.messages)
|
||||
if (!all.length) return { head: input.messages, tail_start_id: undefined }
|
||||
const recent = all.slice(-limit)
|
||||
@@ -466,7 +468,7 @@ export const layer = Layer.effect(
|
||||
model,
|
||||
})
|
||||
// kilocode_change start
|
||||
const result = KiloCompactionChunks.needed({ cfg, model, tokens })
|
||||
const result = KiloCompactionChunks.needed({ cfg, model, tokens, outputTokenMax: flags.outputTokenMax })
|
||||
? "compact"
|
||||
: yield* KiloCompactionPayloadRecovery.process({
|
||||
processor,
|
||||
@@ -493,6 +495,7 @@ export const layer = Layer.effect(
|
||||
sessionID: input.sessionID,
|
||||
model,
|
||||
cfg,
|
||||
outputTokenMax: flags.outputTokenMax,
|
||||
messages: selected.head,
|
||||
prompt: nextPrompt,
|
||||
target: processor.message,
|
||||
@@ -519,8 +522,7 @@ export const layer = Layer.effect(
|
||||
})
|
||||
}
|
||||
|
||||
if (fallback === "continue" && input.auto) {
|
||||
// kilocode_change
|
||||
if (fallback === "continue" && input.auto) { // kilocode_change
|
||||
if (replay) {
|
||||
// kilocode_change start - compact oversized replay turns instead of looping into replay overflow
|
||||
replay = yield* KiloCompactionChunks.replay({
|
||||
@@ -531,6 +533,7 @@ export const layer = Layer.effect(
|
||||
sessionID: input.sessionID,
|
||||
model,
|
||||
cfg,
|
||||
outputTokenMax: flags.outputTokenMax,
|
||||
messages: selected.head,
|
||||
prompt: nextPrompt,
|
||||
target: processor.message,
|
||||
@@ -677,8 +680,8 @@ export const layer = Layer.effect(
|
||||
yield* prune({ sessionID: input.sessionID, reason: "post-compaction" })
|
||||
yield* bus.publish(Event.Compacted, { sessionID: input.sessionID })
|
||||
}
|
||||
return fallback
|
||||
// kilocode_change end
|
||||
return fallback // kilocode_change
|
||||
})
|
||||
|
||||
const create = Effect.fn("SessionCompaction.create")(function* (input: {
|
||||
@@ -719,7 +722,7 @@ export const layer = Layer.effect(
|
||||
return Service.of({
|
||||
isOverflow,
|
||||
prune,
|
||||
process: (input) => processCompaction(input).pipe(Effect.orDie),
|
||||
process: (input) => processCompaction(input).pipe(Effect.orDie), // kilocode_change
|
||||
create,
|
||||
})
|
||||
}),
|
||||
|
||||
@@ -680,12 +680,14 @@ export const layer = Layer.effect(
|
||||
.pipe(Effect.ignore, Effect.forkIn(scope))
|
||||
if (
|
||||
!ctx.assistantMessage.summary &&
|
||||
// kilocode_change start
|
||||
isOverflow({
|
||||
cfg: yield* config.get(),
|
||||
tokens: usage.tokens,
|
||||
model: ctx.model,
|
||||
outputTokenMax: flags.outputTokenMax,
|
||||
}) // kilocode_change
|
||||
})
|
||||
// kilocode_change end
|
||||
) {
|
||||
ctx.needsCompaction = true
|
||||
// kilocode_change start
|
||||
@@ -893,8 +895,8 @@ export const layer = Layer.effect(
|
||||
yield* Effect.gen(function* () {
|
||||
ctx.currentText = undefined
|
||||
ctx.reasoningMap = {}
|
||||
ctx.step = { reasoning: false, text: false, tool: false } // kilocode_change
|
||||
// kilocode_change start
|
||||
ctx.step = { reasoning: false, text: false, tool: false }
|
||||
const stream = llm.stream({
|
||||
...streamInput,
|
||||
preflight: !ctx.assistantMessage.summary,
|
||||
|
||||
@@ -56,7 +56,7 @@ import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
||||
import { Truncate } from "@/tool/truncate"
|
||||
import { Image } from "@/image/image"
|
||||
import { decodeDataUrl } from "@/util/data-url"
|
||||
import { Cause, Effect, Exit, Latch, Layer, Option, Scope, Context, Schema, Types } from "effect" // kilocode_change - Process moved to the timeout helper
|
||||
import { Cause, Effect, Exit, Latch, Layer, Option, Scope, Context, Schema, Types } from "effect"
|
||||
import * as EffectLogger from "@opencode-ai/core/effect/logger"
|
||||
import { InstanceState } from "@/effect/instance-state"
|
||||
import { TaskTool, type TaskPromptOps } from "@/tool/task"
|
||||
@@ -963,7 +963,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
||||
yield* bus.publish(Session.Event.Error, { sessionID: input.sessionID, error: error.toObject() })
|
||||
throw error
|
||||
}
|
||||
const model = input.model ?? agent.model ?? (yield* currentModel(input.sessionID)) // kilocode_change
|
||||
const model = input.model ?? agent.model ?? (yield* currentModel(input.sessionID))
|
||||
const userMsg: MessageV2.User = {
|
||||
id: input.messageID ?? MessageID.ascending(),
|
||||
sessionID: input.sessionID,
|
||||
@@ -1014,7 +1014,6 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
||||
},
|
||||
}
|
||||
yield* sessions.updatePart(part)
|
||||
// kilocode_change start - preserve Kilo v2 shell event dual-write
|
||||
if (flags.experimentalEventSystem) {
|
||||
yield* events.publish(SessionEvent.Shell.Started, {
|
||||
sessionID: input.sessionID,
|
||||
@@ -1023,7 +1022,6 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
||||
command: input.command,
|
||||
})
|
||||
}
|
||||
// kilocode_change end
|
||||
return { msg, part, cwd: ctx.directory }
|
||||
}).pipe(Effect.ensuring(markReady))
|
||||
|
||||
@@ -1041,7 +1039,6 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
||||
}
|
||||
if (timeout) output += "\n\n" + ["<metadata>", timeout, "</metadata>"].join("\n") // kilocode_change
|
||||
const completed = Date.now()
|
||||
// kilocode_change start - preserve Kilo v2 shell event dual-write
|
||||
if (flags.experimentalEventSystem) {
|
||||
yield* events.publish(SessionEvent.Shell.Ended, {
|
||||
sessionID: input.sessionID,
|
||||
@@ -1050,7 +1047,6 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
||||
output,
|
||||
})
|
||||
}
|
||||
// kilocode_change end
|
||||
if (!msg.time.completed) {
|
||||
msg.time.completed = completed
|
||||
yield* sessions.updateMessage(msg)
|
||||
@@ -1137,7 +1133,6 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
||||
return yield* Effect.die(err)
|
||||
})
|
||||
|
||||
// kilocode_change start - preserve persisted per-session model selection
|
||||
const currentModel = Effect.fnUntraced(function* (sessionID: SessionID) {
|
||||
const current = Database.use((db) =>
|
||||
db.select({ model: SessionTable.model }).from(SessionTable).where(eq(SessionTable.id, sessionID)).get(),
|
||||
@@ -1155,7 +1150,6 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
||||
if (Option.isSome(match) && match.value.info.role === "user") return match.value.info.model
|
||||
return yield* provider.defaultModel()
|
||||
})
|
||||
// kilocode_change end
|
||||
|
||||
const createUserMessage = Effect.fn("SessionPrompt.createUserMessage")(function* (input: PromptInput) {
|
||||
const agentName = input.agent
|
||||
@@ -1175,7 +1169,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
||||
.where(eq(SessionTable.id, input.sessionID))
|
||||
.get(),
|
||||
)
|
||||
const model = input.model ?? ag.model ?? (yield* currentModel(input.sessionID)) // kilocode_change
|
||||
const model = input.model ?? ag.model ?? (yield* currentModel(input.sessionID))
|
||||
const same = ag.model && model.providerID === ag.model.providerID && model.modelID === ag.model.modelID
|
||||
const full =
|
||||
!input.variant && ag.variant && same
|
||||
@@ -1679,7 +1673,6 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
||||
synthetic: [] as string[],
|
||||
},
|
||||
)
|
||||
// kilocode_change start - preserve Kilo v2 prompt event dual-write
|
||||
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
||||
if (flags.experimentalEventSystem) {
|
||||
yield* events.publish(SessionEvent.Prompted, {
|
||||
@@ -1703,7 +1696,6 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
||||
})
|
||||
}
|
||||
}
|
||||
// kilocode_change end
|
||||
|
||||
return { info, parts }
|
||||
}, Effect.scoped)
|
||||
@@ -2265,7 +2257,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
||||
if (cmdAgent?.model) return cmdAgent.model
|
||||
}
|
||||
if (input.model) return Provider.parseModel(input.model)
|
||||
return yield* currentModel(input.sessionID) // kilocode_change
|
||||
return yield* currentModel(input.sessionID)
|
||||
})
|
||||
|
||||
yield* getModel(taskModel.providerID, taskModel.modelID, input.sessionID)
|
||||
@@ -2299,7 +2291,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
||||
const userModel = isSubtask
|
||||
? input.model
|
||||
? Provider.parseModel(input.model)
|
||||
: yield* currentModel(input.sessionID) // kilocode_change
|
||||
: yield* currentModel(input.sessionID)
|
||||
: taskModel
|
||||
|
||||
yield* plugin.trigger(
|
||||
|
||||
@@ -648,7 +648,7 @@ export const layer: Layer.Layer<
|
||||
)
|
||||
}
|
||||
// kilocode_change end
|
||||
yield* sync.run(Event.Deleted, { sessionID, info: session }, { publish: hasInstance }) // kilocode_change
|
||||
yield* sync.run(Event.Deleted, { sessionID, info: session }, { publish: hasInstance })
|
||||
// kilocode_change - capture final session-export workspace delta on close/delete
|
||||
const workspaceKey = hasInstance ? yield* InstanceState.directory : undefined // kilocode_change
|
||||
yield* Effect.promise(() => SessionExport.onSessionClose(sessionID, workspaceKey)) // kilocode_change
|
||||
@@ -728,7 +728,7 @@ export const layer: Layer.Layer<
|
||||
model: input?.model,
|
||||
permission: input?.permission,
|
||||
platform: input?.platform, // kilocode_change
|
||||
workspaceID: input?.workspaceID ?? workspace, // kilocode_change - allow explicit override
|
||||
workspaceID: input?.workspaceID ?? workspace,
|
||||
})
|
||||
return session
|
||||
})
|
||||
|
||||
@@ -268,8 +268,8 @@ export const layer = Layer.effect(
|
||||
flags.disableExternalSkills,
|
||||
flags.disableClaudeCodeSkills,
|
||||
ctx.directory,
|
||||
ctx.project.worktree,
|
||||
) // kilocode_change
|
||||
ctx.project.worktree, // kilocode_change
|
||||
)
|
||||
}),
|
||||
)
|
||||
const state = yield* InstanceState.make(
|
||||
|
||||
@@ -302,8 +302,7 @@ export const layer: Layer.Layer<Service, never, AppFileSystem.Service | AppProce
|
||||
)
|
||||
})
|
||||
|
||||
const track = Effect.fnUntraced(function* (opts?: Parameters<Interface["track"]>[0]) {
|
||||
// kilocode_change
|
||||
const track = Effect.fnUntraced(function* (opts?: Parameters<Interface["track"]>[0]) { // kilocode_change
|
||||
return yield* locked(
|
||||
Effect.gen(function* () {
|
||||
if (!(yield* enabled())) return
|
||||
|
||||
@@ -18,6 +18,7 @@ import { serviceUse } from "@/effect/service-use"
|
||||
import { InstanceState } from "@/effect/instance-state"
|
||||
import { RuntimeFlags } from "@/effect/runtime-flags"
|
||||
import { attachWith } from "@/effect/run-service"
|
||||
import * as EventWire from "@/kilocode/event-wire" // kilocode_change
|
||||
|
||||
// Keep `Event["data"]` mutable because projectors mutate the persisted shape
|
||||
// when writing to the database. Bus payloads (`Properties`) stay readonly —
|
||||
@@ -36,6 +37,7 @@ export type Definition<
|
||||
// passed at definition time (see `session.updated`, whose projector
|
||||
// expands the persisted data to a `{ sessionID, info }` bus payload).
|
||||
properties: BusSchema
|
||||
wire?: boolean // kilocode_change - EventV2 rows cross persistence and bus boundaries as encoded data
|
||||
}
|
||||
|
||||
export type Event<Def extends Definition = Definition> = {
|
||||
@@ -47,7 +49,12 @@ export type Event<Def extends Definition = Definition> = {
|
||||
|
||||
export type Properties<Def extends Definition = Definition> = EffectSchema.Schema.Type<Def["properties"]>
|
||||
|
||||
export type SerializedEvent<Def extends Definition = Definition> = Event<Def> & { type: string }
|
||||
// kilocode_change start - serialized rows carry the schema's encoded representation
|
||||
export type SerializedEvent<Def extends Definition = Definition> = Omit<Event<Def>, "data"> & {
|
||||
type: string
|
||||
data: DeepMutable<Def["schema"]["Encoded"]>
|
||||
}
|
||||
// kilocode_change end
|
||||
|
||||
type ProjectorFunc = (db: Database.TxOrDb, data: unknown, event: Event) => void
|
||||
type ConvertEvent = (type: string, data: Event["data"]) => unknown | Promise<unknown>
|
||||
@@ -113,13 +120,20 @@ export const layer = Layer.effect(Service)(
|
||||
workspace: yield* InstanceState.workspaceID,
|
||||
}
|
||||
: undefined
|
||||
process(def, event, {
|
||||
bus,
|
||||
publish,
|
||||
context,
|
||||
ownerID: options?.ownerID,
|
||||
experimentalWorkspaces: flags.experimentalWorkspaces,
|
||||
})
|
||||
// kilocode_change start - decode only EventV2 rows
|
||||
const data = def.wire ? EventWire.decode(def.schema, event.data) : event.data
|
||||
process(
|
||||
def,
|
||||
{ ...event, data },
|
||||
{
|
||||
bus,
|
||||
publish,
|
||||
context,
|
||||
ownerID: options?.ownerID,
|
||||
experimentalWorkspaces: flags.experimentalWorkspaces,
|
||||
},
|
||||
)
|
||||
// kilocode_change end
|
||||
})
|
||||
|
||||
const replayAll: Interface["replayAll"] = Effect.fn("SyncEvent.replayAll")(function* (events, options) {
|
||||
@@ -238,6 +252,7 @@ export function init(input: { projectors: Array<[Definition, ProjectorFunc]>; co
|
||||
aggregate: entry.aggregate,
|
||||
properties: entry.data,
|
||||
schema: entry.data,
|
||||
wire: true, // kilocode_change
|
||||
})
|
||||
}
|
||||
|
||||
@@ -326,6 +341,7 @@ function process<Def extends Definition>(
|
||||
|
||||
Database.transaction((tx) => {
|
||||
projector(tx, event.data, event)
|
||||
const data = def.wire ? EventWire.encode(def.schema, event.data) : event.data // kilocode_change
|
||||
|
||||
if (options.experimentalWorkspaces) {
|
||||
tx.insert(EventSequenceTable)
|
||||
@@ -345,7 +361,7 @@ function process<Def extends Definition>(
|
||||
seq: event.seq,
|
||||
aggregate_id: event.aggregateID,
|
||||
type: versionedType(def.type, def.version),
|
||||
data: event.data as Record<string, unknown>,
|
||||
data: data as Record<string, unknown>, // kilocode_change
|
||||
})
|
||||
.run()
|
||||
}
|
||||
@@ -357,13 +373,29 @@ function process<Def extends Definition>(
|
||||
}
|
||||
|
||||
const result = convertEvent(def.type, event.data)
|
||||
const publish = (data: unknown) =>
|
||||
Effect.runPromise(
|
||||
attachWith(options.bus.publish(def, data as Properties<Def>, { id: event.id }), {
|
||||
instance: options.context?.instance,
|
||||
workspace: options.context?.workspace,
|
||||
}),
|
||||
// kilocode_change start - encode EventV2 properties before crossing the legacy boundary
|
||||
const publish = (value: unknown) => {
|
||||
const refs = {
|
||||
instance: options.context?.instance,
|
||||
workspace: options.context?.workspace,
|
||||
}
|
||||
if (def.wire) {
|
||||
return Effect.runPromise(
|
||||
attachWith(
|
||||
options.bus.publish(
|
||||
{ type: def.type, properties: EffectSchema.toEncoded(def.properties) },
|
||||
EventWire.encode(def.properties, value),
|
||||
{ id: event.id },
|
||||
),
|
||||
refs,
|
||||
),
|
||||
)
|
||||
}
|
||||
return Effect.runPromise(
|
||||
attachWith(options.bus.publish(def, value as Properties<Def>, { id: event.id }), refs),
|
||||
)
|
||||
}
|
||||
// kilocode_change end
|
||||
if (result instanceof Promise) {
|
||||
void result.then(publish)
|
||||
} else {
|
||||
@@ -379,6 +411,7 @@ function process<Def extends Definition>(
|
||||
syncEvent: {
|
||||
type: versionedType(def.type, def.version),
|
||||
...event,
|
||||
data, // kilocode_change
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -312,8 +312,7 @@ const ask = Effect.fn("ShellTool.ask")(function* (
|
||||
|
||||
function cmd(shell: string, command: string, cwd: string, env: NodeJS.ProcessEnv) {
|
||||
if (process.platform === "win32" && Shell.ps(shell)) {
|
||||
return ChildProcess.make(shell, Shell.args(shell, command, cwd), {
|
||||
// kilocode_change - encoded PowerShell args
|
||||
return ChildProcess.make(shell, Shell.args(shell, command, cwd), { // kilocode_change - encoded PowerShell args
|
||||
cwd,
|
||||
env,
|
||||
stdin: "ignore",
|
||||
|
||||
@@ -61,7 +61,7 @@ export const CodebaseSearchTool = Tool.define(
|
||||
"Codebase search unavailable: free period ended. Set MORPH_API_KEY to continue. Get your key at https://www.morphllm.com/"
|
||||
if (isAuthOrRateLimit) {
|
||||
yield* Effect.promise(() =>
|
||||
Bus.publish(Instance.current, TuiEvent.ToastShow, {
|
||||
Bus.publish(Instance.current, TuiEvent.ToastShow, { // kilocode_change
|
||||
title: "Codebase Search Unavailable",
|
||||
message: "Free period has ended. Set MORPH_API_KEY to continue. Get your key at morphllm.com",
|
||||
variant: "error",
|
||||
|
||||
@@ -28,6 +28,7 @@ describe("tui sync (#26560)", () => {
|
||||
directory,
|
||||
project_id: "proj_test",
|
||||
}
|
||||
// kilocode_change start
|
||||
const { app, sync } = await provideTestInstance({
|
||||
directory: tmp.path,
|
||||
fn: () =>
|
||||
@@ -39,7 +40,8 @@ describe("tui sync (#26560)", () => {
|
||||
if (url.pathname === "/session") return json([sessionPayload])
|
||||
return undefined
|
||||
}),
|
||||
}) // kilocode_change
|
||||
})
|
||||
// kilocode_change end
|
||||
|
||||
try {
|
||||
await expect(sync.session.sync(sessionID)).resolves.toBeUndefined()
|
||||
|
||||
@@ -11,7 +11,7 @@ import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
|
||||
import type { Config } from "@/config/config"
|
||||
import { InstanceRef } from "../../src/effect/instance-ref"
|
||||
import { InstanceBootstrap } from "../../src/project/bootstrap-service"
|
||||
import { context as instanceContext, type InstanceContext } from "../../src/project/instance-context"
|
||||
import { context as instanceContext, type InstanceContext } from "../../src/project/instance-context" // kilocode_change
|
||||
import { InstanceRuntime } from "../../src/project/instance-runtime"
|
||||
import { InstanceStore } from "../../src/project/instance-store"
|
||||
import { TestLLMServer } from "../lib/llm-server"
|
||||
@@ -32,17 +32,19 @@ export async function provideTestInstance<R>(input: {
|
||||
const ctx = await runTestInstanceStore((store) => store.load({ directory: input.directory }))
|
||||
try {
|
||||
if (input.init) await testInstanceRuntime.runPromise(input.init.pipe(Effect.provideService(InstanceRef, ctx)))
|
||||
return await instanceContext.provide(ctx, () => input.fn(ctx))
|
||||
return await instanceContext.provide(ctx, () => input.fn(ctx)) // kilocode_change
|
||||
} finally {
|
||||
// kilocode_change start
|
||||
await instanceContext.provide(ctx, () =>
|
||||
runTestInstanceStore((store) => store.dispose(ctx).pipe(Effect.provideService(InstanceRef, ctx))),
|
||||
)
|
||||
// kilocode_change end
|
||||
}
|
||||
}
|
||||
|
||||
export async function withTestInstance<R>(input: { directory: string; fn: (ctx: InstanceContext) => R }) {
|
||||
const ctx = await runTestInstanceStore((store) => store.load({ directory: input.directory }))
|
||||
return instanceContext.provide(ctx, () => input.fn(ctx))
|
||||
return instanceContext.provide(ctx, () => input.fn(ctx)) // kilocode_change
|
||||
}
|
||||
|
||||
export async function reloadTestInstance(input: { directory: string }) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { test, expect, describe } from "bun:test"
|
||||
import { Provider } from "../../../src/provider/provider"
|
||||
import { formatTable, formatMarkdown, handle, isTextModel } from "../../../src/kilocode/cli/cmd/roll-call"
|
||||
import { formatTable, formatMarkdown, handle, isTextModel, outputLimit } from "../../../src/kilocode/cli/cmd/roll-call"
|
||||
|
||||
const base = {
|
||||
input: { text: false, audio: false, image: false, video: false, pdf: false },
|
||||
@@ -100,6 +100,17 @@ describe("isTextModel", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("outputLimit", () => {
|
||||
test("honors the configured runtime output cap", () => {
|
||||
const model = {
|
||||
...caps({ input: { text: true }, output: { text: true } }),
|
||||
limit: { context: 100_000, input: 90_000, output: 8_000 },
|
||||
} as Provider.Model
|
||||
|
||||
expect(outputLimit(model, 512)).toBe(512)
|
||||
})
|
||||
})
|
||||
|
||||
describe("formatMarkdown", () => {
|
||||
test("produces valid markdown table", () => {
|
||||
const rows = [
|
||||
|
||||
@@ -236,7 +236,7 @@ function runtime(layer: Layer.Layer<LLM.Service>, context = 7_000) {
|
||||
)
|
||||
}
|
||||
|
||||
function fakeRuntime() {
|
||||
function fakeRuntime(outputTokenMax?: number) {
|
||||
const calls: string[] = []
|
||||
const outputs: number[] = []
|
||||
const bus = Bus.layer
|
||||
@@ -291,7 +291,7 @@ function fakeRuntime() {
|
||||
Layer.provide(Plugin.defaultLayer),
|
||||
Layer.provide(SyncEvent.defaultLayer),
|
||||
Layer.provide(EventV2Bridge.defaultLayer),
|
||||
Layer.provide(RuntimeFlags.layer()),
|
||||
Layer.provide(RuntimeFlags.layer({ outputTokenMax })),
|
||||
Layer.provide(Reference.defaultLayer),
|
||||
Layer.provide(bus),
|
||||
Layer.provide(
|
||||
@@ -373,6 +373,15 @@ describe("KiloCompactionChunks", () => {
|
||||
)
|
||||
})
|
||||
|
||||
test("uses runtime output cap for fallback selection and chunk budget", () => {
|
||||
const model = ProviderTest.model({ providerID, id: modelID, limit: { context: 10_000, output: 8_000 } })
|
||||
const cfg = {} as Config.Info
|
||||
const outputTokenMax = 512
|
||||
|
||||
expect(KiloCompactionChunks.needed({ cfg, model, tokens: 5_000, outputTokenMax })).toBe(false)
|
||||
expect(KiloCompactionChunks.budget({ cfg, model, outputTokenMax })).toBe(5_692)
|
||||
})
|
||||
|
||||
test("falls back to chunk workers after the first compaction overflows", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
await provideTestInstance({
|
||||
@@ -569,15 +578,15 @@ describe("KiloCompactionChunks", () => {
|
||||
})
|
||||
})
|
||||
|
||||
test("caps worker output budget below oversized model output limit", async () => {
|
||||
const { rt, calls, outputs } = fakeRuntime()
|
||||
test("caps worker output budget below the configured runtime limit", async () => {
|
||||
const { rt, calls, outputs } = fakeRuntime(512)
|
||||
await using tmp = await tmpdir()
|
||||
await provideTestInstance({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const session = await svc.create({})
|
||||
const first = await user(session.id, "first " + "a".repeat(1_000))
|
||||
await assistant(session.id, first.id, tmp.path, "reply " + "b".repeat(1_000))
|
||||
const first = await user(session.id, "first " + "a".repeat(80_000))
|
||||
await assistant(session.id, first.id, tmp.path, "reply " + "b".repeat(80_000))
|
||||
await Effect.runPromise(
|
||||
KiloSessionCompaction.create({
|
||||
session: store,
|
||||
@@ -605,7 +614,7 @@ describe("KiloCompactionChunks", () => {
|
||||
|
||||
expect(result).toBe("continue")
|
||||
expect(calls.length).toBeGreaterThan(0)
|
||||
expect(outputs.every((value) => value <= 2_048)).toBe(true)
|
||||
expect(outputs.at(-1)).toBe(512)
|
||||
} finally {
|
||||
await rt.dispose()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
import { afterEach, describe, expect, test } from "bun:test"
|
||||
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
||||
import { SessionEvent } from "@opencode-ai/core/session-event"
|
||||
import { DateTime, Effect, Layer, Schema } from "effect"
|
||||
import { Bus } from "../../src/bus"
|
||||
import { RuntimeFlags } from "../../src/effect/runtime-flags"
|
||||
import { EventV2Bridge } from "../../src/event-v2-bridge"
|
||||
import * as EventWire from "../../src/kilocode/event-wire"
|
||||
import { SessionID } from "../../src/session/schema"
|
||||
import { Database, eq } from "../../src/storage/db"
|
||||
import { SyncEvent } from "../../src/sync"
|
||||
import { EventTable } from "../../src/sync/event.sql"
|
||||
import { resetDatabase } from "../fixture/db"
|
||||
import { provideTmpdirInstance } from "../fixture/fixture"
|
||||
import { awaitWithTimeout, testEffect } from "../lib/effect"
|
||||
|
||||
const it = testEffect(
|
||||
Layer.mergeAll(
|
||||
SyncEvent.layer.pipe(
|
||||
Layer.provide(RuntimeFlags.layer({ experimentalWorkspaces: true })),
|
||||
Layer.provideMerge(Bus.layer),
|
||||
),
|
||||
CrossSpawnSpawner.defaultLayer,
|
||||
),
|
||||
)
|
||||
|
||||
afterEach(resetDatabase)
|
||||
|
||||
describe("SyncEvent encoding", () => {
|
||||
test("preserves JSON values nested under unknown schemas", () => {
|
||||
const schema = Schema.Struct({ value: Schema.Unknown })
|
||||
|
||||
expect(EventWire.encode(schema, { value: new Date(0) })).toEqual({ value: "1970-01-01T00:00:00.000Z" })
|
||||
expect(EventWire.encode(schema, { value: new URL("https://kilo.ai/docs") })).toEqual({
|
||||
value: "https://kilo.ai/docs",
|
||||
})
|
||||
})
|
||||
|
||||
test("legacy timestamp decoding leaves unknown payload fields unchanged", () => {
|
||||
const schema = Schema.Struct({ timestamp: Schema.DateTimeUtcFromMillis, input: Schema.Unknown })
|
||||
const timestamp = "1970-01-01T00:00:01.234Z"
|
||||
const decoded = EventWire.decode(schema, { timestamp, input: { created: timestamp, released: timestamp } })
|
||||
|
||||
expect(DateTime.toEpochMillis(decoded.timestamp)).toBe(1_234)
|
||||
expect(decoded.input).toEqual({ created: timestamp, released: timestamp })
|
||||
})
|
||||
|
||||
it.live(
|
||||
"publishes encoded session data on the legacy bus",
|
||||
provideTmpdirInstance(() =>
|
||||
Effect.gen(function* () {
|
||||
const bus = yield* Bus.Service
|
||||
const sync = yield* SyncEvent.Service
|
||||
const def = EventV2Bridge.toSyncDefinition(SessionEvent.Text.Delta)
|
||||
const sessionID = SessionID.make("ses_event_bus")
|
||||
const events = new Array<{ type: string; properties: unknown }>()
|
||||
const received = Promise.withResolvers<void>()
|
||||
const dispose = yield* bus.subscribeAllCallback((event) => {
|
||||
if (event.type !== def.type) return
|
||||
events.push(event)
|
||||
received.resolve()
|
||||
})
|
||||
|
||||
try {
|
||||
yield* sync.run(def, { sessionID, timestamp: DateTime.makeUnsafe(1_234), delta: "hello" })
|
||||
yield* awaitWithTimeout(
|
||||
Effect.promise(() => received.promise),
|
||||
"legacy bus did not receive the session event",
|
||||
)
|
||||
expect((events[0]?.properties as { timestamp?: unknown }).timestamp).toBe(1_234)
|
||||
} finally {
|
||||
dispose()
|
||||
}
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
it.live(
|
||||
"persists encoded session data and decodes it during replay",
|
||||
provideTmpdirInstance(() =>
|
||||
Effect.gen(function* () {
|
||||
const sync = yield* SyncEvent.Service
|
||||
const def = EventV2Bridge.toSyncDefinition(SessionEvent.Text.Delta)
|
||||
const sessionID = SessionID.make("ses_event_replay")
|
||||
const timestamp = DateTime.makeUnsafe(1_234)
|
||||
|
||||
yield* sync.run(def, { sessionID, timestamp, delta: "hello" }, { publish: false })
|
||||
const row = Database.use((db) =>
|
||||
db.select().from(EventTable).where(eq(EventTable.aggregate_id, sessionID)).get(),
|
||||
)
|
||||
if (!row) throw new Error("missing persisted event")
|
||||
expect((row.data as { timestamp?: unknown }).timestamp).toBe(1_234)
|
||||
|
||||
yield* sync.remove(sessionID)
|
||||
yield* sync.replay({
|
||||
id: row.id,
|
||||
type: row.type,
|
||||
seq: row.seq,
|
||||
aggregateID: row.aggregate_id,
|
||||
data: { ...row.data, timestamp: "1970-01-01T00:00:01.234Z" },
|
||||
})
|
||||
|
||||
const replayed = Database.use((db) =>
|
||||
db.select().from(EventTable).where(eq(EventTable.aggregate_id, sessionID)).get(),
|
||||
)
|
||||
expect((replayed?.data as { timestamp?: unknown }).timestamp).toBe(1_234)
|
||||
}),
|
||||
),
|
||||
)
|
||||
})
|
||||
@@ -45,6 +45,11 @@ describe("InstanceStore", () => {
|
||||
|
||||
expect(ctx.directory).toBe(dir)
|
||||
expect(ctx.worktree).toBe(dir)
|
||||
|
||||
// kilocode_change start - capture prefers legacy ALS, then falls back to the Effect fiber reference
|
||||
const fallback = yield* Effect.sync(capture).pipe(Effect.provideService(InstanceRef, ctx))
|
||||
expect({ ambient: capture(), fallback }).toEqual({ ambient: undefined, fallback: ctx })
|
||||
// kilocode_change end
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -62,6 +67,7 @@ describe("InstanceStore", () => {
|
||||
yield* store.load({ directory: dir })
|
||||
|
||||
expect(initializedDirectory).toBe(dir)
|
||||
expect(capture()).toBeUndefined() // kilocode_change - bootstrap legacy ALS does not leak into the caller
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ async function markPluginDependenciesReady(dir: string) {
|
||||
await mkdir(path.join(dir, "node_modules"), { recursive: true })
|
||||
await Bun.write(
|
||||
path.join(dir, "package-lock.json"),
|
||||
JSON.stringify({ packages: { "": { dependencies: { "@kilocode/plugin": "0.0.0" } } } }), // kilocode_change
|
||||
JSON.stringify({ packages: { "": { dependencies: { "@kilocode/plugin": "0.0.0" } } } }),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { afterEach, expect } from "bun:test" // kilocode_change - blocking behavior now uses the scoped service test helper
|
||||
import { afterEach, expect } from "bun:test"
|
||||
import { Cause, Effect, Exit, Fiber, Layer, Queue } from "effect"
|
||||
import { Question } from "../../src/question"
|
||||
import { InstanceRef } from "../../src/effect/instance-ref"
|
||||
import { InstanceRuntime } from "../../src/project/instance-runtime"
|
||||
import { QuestionID } from "../../src/question/schema"
|
||||
import { disposeAllInstances, provideInstance, reloadTestInstance, tmpdirScoped } from "../fixture/fixture" // kilocode_change - blocking coverage no longer uses the Promise facade fixture
|
||||
import { disposeAllInstances, provideInstance, reloadTestInstance, tmpdirScoped } from "../fixture/fixture"
|
||||
import { SessionID } from "../../src/session/schema"
|
||||
import { testEffect } from "../lib/effect"
|
||||
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
||||
|
||||
@@ -59,8 +59,7 @@ describe("config HttpApi", () => {
|
||||
lsp: false,
|
||||
})
|
||||
yield* Fiber.join(disposed)
|
||||
expect(yield* Effect.promise(() => Bun.file(path.join(tmp.path, "opencode.json")).json())).toMatchObject({
|
||||
// kilocode_change
|
||||
expect(yield* Effect.promise(() => Bun.file(path.join(tmp.path, "opencode.json")).json())).toMatchObject({ // kilocode_change
|
||||
username: "patched-user",
|
||||
formatter: false,
|
||||
lsp: false,
|
||||
|
||||
@@ -5,8 +5,17 @@ import { InstanceRef } from "../../src/effect/instance-ref"
|
||||
import { Server } from "../../src/server/server"
|
||||
import { EventPaths } from "../../src/server/routes/instance/httpapi/groups/event"
|
||||
import { Event as ServerEvent } from "../../src/server/event"
|
||||
// kilocode_change start - verify transformed EventV2 values at the legacy SSE boundary
|
||||
import { Catalog } from "@opencode-ai/core/catalog"
|
||||
import { EventV2 } from "@opencode-ai/core/event"
|
||||
import { ModelV2 } from "@opencode-ai/core/model"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { SessionEvent } from "@opencode-ai/core/session-event"
|
||||
import { GlobalPaths } from "../../src/server/routes/instance/httpapi/groups/global"
|
||||
import { SessionID } from "../../src/session/schema"
|
||||
// kilocode_change end
|
||||
import * as Log from "@opencode-ai/core/util/log"
|
||||
import { Effect, Schema } from "effect"
|
||||
import { DateTime, Effect, Schema } from "effect"
|
||||
import { resetDatabase } from "../fixture/db"
|
||||
import { disposeAllInstances, reloadTestInstance, tmpdir } from "../fixture/fixture"
|
||||
|
||||
@@ -22,6 +31,17 @@ const EventData = Schema.Struct({
|
||||
properties: Schema.Record(Schema.String, Schema.Any),
|
||||
})
|
||||
|
||||
// kilocode_change start - inspect the real global SSE envelope
|
||||
const GlobalEventData = Schema.Struct({
|
||||
directory: Schema.optional(Schema.String),
|
||||
payload: Schema.Struct({
|
||||
id: Schema.optional(Schema.String),
|
||||
type: Schema.String,
|
||||
properties: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
|
||||
}),
|
||||
})
|
||||
// kilocode_change end
|
||||
|
||||
async function readChunk(reader: ReadableStreamDefaultReader<Uint8Array>, delay = 5_000) {
|
||||
let timeout: ReturnType<typeof setTimeout> | undefined
|
||||
try {
|
||||
@@ -90,6 +110,33 @@ async function readEventWithin(reader: ReadableStreamDefaultReader<Uint8Array>,
|
||||
return Schema.decodeUnknownSync(EventData)(JSON.parse(new TextDecoder().decode(result.value).replace(/^data: /, "")))
|
||||
}
|
||||
|
||||
// kilocode_change start - read transformed values from the global SSE wire payload
|
||||
async function readGlobal(reader: ReadableStreamDefaultReader<Uint8Array>, delay = 5_000) {
|
||||
const result = await readChunk(reader, delay)
|
||||
if (result.done || !result.value) throw new Error("global event stream closed")
|
||||
return Schema.decodeUnknownSync(GlobalEventData)(
|
||||
JSON.parse(new TextDecoder().decode(result.value).replace(/^data: /, "")),
|
||||
)
|
||||
}
|
||||
|
||||
function properties(event: Schema.Schema.Type<typeof GlobalEventData>) {
|
||||
if (!event.payload.properties) throw new Error(`event ${event.payload.type} has no properties`)
|
||||
return event.payload.properties
|
||||
}
|
||||
|
||||
async function readGlobalUntil(
|
||||
reader: ReadableStreamDefaultReader<Uint8Array>,
|
||||
predicate: (event: Schema.Schema.Type<typeof GlobalEventData>) => boolean,
|
||||
delay = 5_000,
|
||||
) {
|
||||
const end = Date.now() + delay
|
||||
while (true) {
|
||||
const event = await readGlobal(reader, end - Date.now())
|
||||
if (predicate(event)) return event
|
||||
}
|
||||
}
|
||||
// kilocode_change end
|
||||
|
||||
afterEach(async () => {
|
||||
await disposeAllInstances()
|
||||
await resetDatabase()
|
||||
@@ -145,4 +192,70 @@ describe("event HttpApi", () => {
|
||||
await reader.cancel()
|
||||
}
|
||||
})
|
||||
|
||||
// kilocode_change start - transformed EventV2 data is numeric on legacy SSE while domain data stays decoded
|
||||
test("encodes catalog and session EventV2 data on the global event stream", async () => {
|
||||
await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } })
|
||||
const response = await app().request(GlobalPaths.event)
|
||||
if (!response.body) throw new Error("missing response body")
|
||||
|
||||
const reader = response.body.getReader()
|
||||
try {
|
||||
expect(await readGlobal(reader)).toMatchObject({ payload: { type: "server.connected", properties: {} } })
|
||||
const ctx = await reloadTestInstance({ directory: tmp.path })
|
||||
const released = DateTime.makeUnsafe(1_750_000_000_123)
|
||||
const model = new ModelV2.Info({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("test"), ModelV2.ID.make("model")),
|
||||
time: { released },
|
||||
})
|
||||
const catalogID = EventV2.ID.create()
|
||||
const catalog = readGlobalUntil(reader, (event) => event.payload.id === catalogID)
|
||||
const catalogDomain = await AppRuntime.runPromise(
|
||||
EventV2.Service.use((events) => events.publish(Catalog.Event.ModelUpdated, { model }, { id: catalogID })).pipe(
|
||||
Effect.provideService(InstanceRef, ctx),
|
||||
),
|
||||
)
|
||||
|
||||
expect(DateTime.isDateTime(catalogDomain.data.model.time.released)).toBe(true)
|
||||
expect(properties(await catalog).model.time.released).toBe(1_750_000_000_123)
|
||||
|
||||
const globalID = EventV2.ID.create()
|
||||
const global = readGlobalUntil(reader, (event) => event.payload.id === globalID)
|
||||
await AppRuntime.runPromise(
|
||||
EventV2.Service.use((events) => events.publish(Catalog.Event.ModelUpdated, { model }, { id: globalID })),
|
||||
)
|
||||
expect((await global).directory).toBe("global")
|
||||
|
||||
const timestamp = DateTime.makeUnsafe(1_234)
|
||||
const sessionID = SessionID.make("ses_event_encoding")
|
||||
const session = readGlobalUntil(
|
||||
reader,
|
||||
(event) =>
|
||||
event.payload.type === SessionEvent.Text.Delta.type && properties(event).sessionID === sessionID,
|
||||
)
|
||||
const sessionDomain = await AppRuntime.runPromise(
|
||||
EventV2.Service.use((events) =>
|
||||
events.publish(SessionEvent.Text.Delta, { sessionID, timestamp, delta: "hello" }),
|
||||
).pipe(Effect.provideService(InstanceRef, ctx)),
|
||||
)
|
||||
|
||||
expect(DateTime.isDateTime(sessionDomain.data.timestamp)).toBe(true)
|
||||
expect(properties(await session).timestamp).toBe(1_234)
|
||||
|
||||
const prompted = readGlobalUntil(reader, (event) => event.payload.type === SessionEvent.Prompted.type)
|
||||
await AppRuntime.runPromise(
|
||||
EventV2.Service.use((events) =>
|
||||
events.publish(SessionEvent.Prompted, {
|
||||
sessionID,
|
||||
timestamp,
|
||||
prompt: { text: "hello", files: [], agents: [], references: [] },
|
||||
}),
|
||||
).pipe(Effect.provideService(InstanceRef, ctx)),
|
||||
)
|
||||
expect(properties(await prompted)).toMatchObject({ timestamp: 1_234, prompt: { text: "hello" } })
|
||||
} finally {
|
||||
await reader.cancel()
|
||||
}
|
||||
})
|
||||
// kilocode_change end
|
||||
})
|
||||
|
||||
@@ -295,7 +295,7 @@ describe("tool.registry", () => {
|
||||
yield* Effect.promise(() =>
|
||||
Bun.write(
|
||||
path.join(plugin, "package.json"),
|
||||
JSON.stringify({ name: "@kilocode/plugin", type: "module", exports: { ".": "./dist/index.js" } }),
|
||||
JSON.stringify({ name: "@kilocode/plugin", type: "module", exports: { ".": "./dist/index.js" } }), // kilocode_change
|
||||
),
|
||||
)
|
||||
yield* Effect.promise(() =>
|
||||
@@ -315,7 +315,7 @@ describe("tool.registry", () => {
|
||||
Bun.write(
|
||||
path.join(customTools, "addition.ts"),
|
||||
[
|
||||
'import { tool } from "@kilocode/plugin"',
|
||||
'import { tool } from "@kilocode/plugin"', // kilocode_change
|
||||
"export default tool({",
|
||||
" description: 'Use this tool to add two numbers and return their sum.',",
|
||||
" args: {",
|
||||
|
||||
@@ -264,8 +264,7 @@ export function Markdown(
|
||||
key: local.cacheKey,
|
||||
streaming: local.streaming ?? false,
|
||||
}),
|
||||
async (src): Promise<Rendered> => {
|
||||
// kilocode_change
|
||||
async (src): Promise<Rendered> => { // kilocode_change
|
||||
if (isServer) return { content: fallback(src.text), blocks: [] } // kilocode_change
|
||||
if (!src.text) return { content: "", blocks: [] } // kilocode_change
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ const testAllow: Record<string, { count: number; reason: string }> = {
|
||||
"provider/amazon-bedrock.test.ts": { count: 2, reason: "existing runtime integration test" },
|
||||
"provider/provider.test.ts": { count: 3, reason: "existing runtime integration test" },
|
||||
"server/experimental-session-list.test.ts": { count: 2, reason: "Kilo session list integration test" },
|
||||
"server/httpapi-event.test.ts": { count: 2, reason: "event stream integration test" },
|
||||
"server/httpapi-event.test.ts": { count: 6, reason: "event stream integration test" },
|
||||
"session/llm.test.ts": { count: 2, reason: "existing runtime integration test" },
|
||||
"tool/recall.test.ts": { count: 11, reason: "existing runtime integration test" },
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user