mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-01 15:32:11 +08:00
b976b5a013
* feat(memory): opt-in project memory — capture, recall, CLI + TUI integration Add project memory: the standalone @kilocode/kilo-memory effect layer plus the opencode CLI/server/TUI integration. Memory is disabled by default, so it is a no-op until enabled (no behavior change when off). Capture (turn-close consolidation): per-op parse salvage, secret redaction that skips the offending op instead of aborting the batch, supersede-only auto-updates (never model-driven deletes), correction-aware echo handling, non-LLM fallback digests on interrupted/error turns, a shared interval throttle with idle-flush. Recall + injection: keyword tokenizer with camelCase/compound splitting, light stemming, and an English-first stopword filter (Unicode-aware; non-English falls back to plain token-overlap), a live relevance floor, a budget-reserved startup index, a session-digest catalog, and per-session prompt-cache pinning of the injected memory block. Surfaces: kilo_memory_save / kilo_memory_recall tools, the memory HTTP API (contract schemas live in the package), and a status-focused TUI sidebar showing auto-save, loaded context, and active recall, plus the /memory dialog. * fix(memory): address PR review feedback - C1: bump @kilocode/kilo-memory in the changeset - C2: redact secrets before they hit the audit log (skip + salvage paths); redact before truncating in salvageTyped so a secret straddling the 500-char cap can't leak an unmatched fragment; opText -> salvageText - C3: de-abbreviate savedOperations, "changes" wording, ops.ts -> operations.ts - C4: log.warn on the remaining silent-catch fallbacks (turn diff, memory context injection, tool-visibility check) - C5: relocate memory storage from ~/.kilo to Global.Path.data, delete the now-dead needsDependencyInstall guard, add /memory status (root path) and /memory edit ($VISUAL/$EDITOR + auto-rebuild) - C6: replace the hardcoded English stopword list with corpus-derived ubiquitous-term filtering (df across the user's own entries) and the English suffix stemmer with suffix-tolerant term matching, so recall noise-filtering works in any language - C8: delete the CORRECTION_INTENT English regex; echo turns now run typed capture (digest stays echo-gated), bounded by the interval throttle, with the typed prompt as the language-agnostic content filter - C9: exclude generated paths (dist/build/coverage/*.gen.*/*.map/snapshots) from the durable-diff churn fallback so generated churn can't burn a consolidation call - C11: fix duplicated assert in httpapi-memory test; assert the error body - kilo-code-bot batch: clause-boundary regex fix, byte-safe catalog truncation, max-length guards on remember/correct/forget payloads (text, query, key, sessionID), trim consistency in reconcile, param-shadowing rename, missing doc entry for kilo_memory_recall, dead-code removal, dialog UI fixes, memoryEnabledCache eviction bound, dedicated Configure schema, recall permission renderer, covered-session pointer cap, redact chat transcript before the consolidation model call, split configProtected metadata from disableAlways so memory-save prompts don't show config-file copy, drop unused MemoryService.layer provide from tool registry - redact colon-separated low-entropy secrets too (password: hunterx), accepting the prose false-positive tradeoff (secret: enabled) in favor of not missing a real secret - rename lastConsolidatedAt -> lastTypedConsolidationAt to make its narrow scope (typed-consolidation throttle clock) explicit; regen openapi/SDK - drop now-dead home/config fields from MemoryPaths.Host after the data-dir relocation; add Process.splitCommand for quoted $EDITOR/$VISUAL paths with spaces, used by /memory edit and the pre-existing Editor.open utility * refactor(memory): shared client helpers, capture hardening, /memory UX rework - extract client-side derivations into kilo-memory so both frontends share one implementation: MemoryDecisions.summarize (decision-log summary), MemoryAutosaveStatus.summarize (autosave-status semantics), and MemoryMarkerMeta (marker wire contract encode/decode) - match exact-key upserts via the canonical stored id (slugged key, normalized section) so a re-emitted spaced/uppercase key updates the entry instead of falling to fuzzy dedupe - salvageTyped throws on valid JSON without an operations array so the caller's fallback path records a parse error instead of a silent zero-op success - rename memory tool metadata files -> sources (stripPartMetadata rewrites tool-part metadata.files assuming apply_patch records, mangling string[]) - read state instead of status for tool enabled checks; dedupe TUI helpers (errorMessage, shared route(), Locale.number, relativeTime) - /memory UX: bare /memory opens a help modal driven by a structured command catalog in kilo-memory; /memory on|off become the canonical toggle verbs (enable/disable kept as quiet aliases); /memory status opens a clean overview dialog (root path, autosave, startup context, source counts, index size) instead of a toast; /memory show is the single full audit view (inspect removed)
161 lines
5.4 KiB
TypeScript
161 lines
5.4 KiB
TypeScript
import { Layer, ManagedRuntime } from "effect"
|
|
import { attach } from "./run-service"
|
|
import * as Observability from "@opencode-ai/core/effect/observability"
|
|
|
|
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
|
import { Bus } from "@/bus"
|
|
import { Auth } from "@/auth"
|
|
import { Account } from "@/account/account"
|
|
import { Config } from "@/config/config"
|
|
import { Git } from "@/git"
|
|
import { Ripgrep } from "@/file/ripgrep"
|
|
import { File } from "@/file"
|
|
import { FileWatcher } from "@/file/watcher"
|
|
import { Storage } from "@/storage/storage"
|
|
import { Snapshot } from "@/snapshot"
|
|
import { Plugin } from "@/plugin"
|
|
import { ModelsDev } from "@opencode-ai/core/models-dev"
|
|
import { ModelCache } from "@/provider/model-cache" // kilocode_change
|
|
import { Provider } from "@/provider/provider"
|
|
import { ProviderAuth } from "@/provider/auth"
|
|
import { Agent } from "@/agent/agent"
|
|
import { Skill } from "@/skill"
|
|
import { Discovery } from "@/skill/discovery"
|
|
import { Question } from "@/question"
|
|
import { Permission } from "@/permission"
|
|
import { Todo } from "@/session/todo"
|
|
import { Session } from "@/session/session"
|
|
import { SessionStatus } from "@/session/status"
|
|
import { SessionRunState } from "@/session/run-state"
|
|
import { SessionProcessor } from "@/session/processor"
|
|
import { SessionCompaction } from "@/session/compaction"
|
|
import { SessionRevert } from "@/session/revert"
|
|
import { SessionSummary } from "@/session/summary"
|
|
import { SessionPrompt } from "@/session/prompt"
|
|
import { Instruction } from "@/session/instruction"
|
|
import { LLM } from "@/session/llm"
|
|
import { LSP } from "@/lsp/lsp"
|
|
import { MCP } from "@/mcp"
|
|
import { McpAuth } from "@/mcp/auth"
|
|
import { Command } from "@/command"
|
|
import { Truncate } from "@/tool/truncate"
|
|
import { ToolRegistry } from "@/tool/registry"
|
|
import { Format } from "@/format"
|
|
import { InstanceLayer } from "@/project/instance-layer"
|
|
import { Project } from "@/project/project"
|
|
import { Vcs } from "@/project/vcs"
|
|
import { Reference } from "@/reference/reference"
|
|
import { Workspace } from "@/control-plane/workspace"
|
|
import { Worktree } from "@/worktree"
|
|
import { Pty } from "@/pty"
|
|
import { PtyTicket } from "@/pty/ticket"
|
|
import { Installation } from "@/installation"
|
|
import { MemoryService } from "@kilocode/kilo-memory/effect/service" // kilocode_change
|
|
import { ShareNext } from "@/share/share-next"
|
|
import { SessionShare } from "@/share/session"
|
|
import { SyncEvent } from "@/sync"
|
|
import { Npm } from "@opencode-ai/core/npm"
|
|
import { memoMap } from "@opencode-ai/core/effect/memo-map"
|
|
import { DataMigration } from "@/data-migration"
|
|
import { BackgroundJob } from "@/background/job"
|
|
import { EventV2Bridge } from "@/event-v2-bridge"
|
|
import { RuntimeFlags } from "@/effect/runtime-flags"
|
|
import { Notebook } from "@/kilocode/notebook/service" // kilocode_change
|
|
|
|
const CoreLayer = Layer.mergeAll(
|
|
Npm.defaultLayer,
|
|
AppFileSystem.defaultLayer,
|
|
Bus.defaultLayer,
|
|
Auth.defaultLayer,
|
|
Account.defaultLayer,
|
|
Config.defaultLayer,
|
|
Git.defaultLayer,
|
|
Ripgrep.defaultLayer,
|
|
File.defaultLayer,
|
|
FileWatcher.defaultLayer,
|
|
Storage.defaultLayer,
|
|
Snapshot.defaultLayer,
|
|
Plugin.defaultLayer,
|
|
ModelCache.defaultLayer, // kilocode_change
|
|
ModelsDev.defaultLayer,
|
|
Provider.defaultLayer,
|
|
ProviderAuth.defaultLayer,
|
|
Agent.defaultLayer,
|
|
Skill.defaultLayer,
|
|
Discovery.defaultLayer,
|
|
)
|
|
|
|
const SessionLayer = Layer.mergeAll(
|
|
Question.defaultLayer,
|
|
Notebook.defaultLayer, // kilocode_change
|
|
Permission.defaultLayer,
|
|
Todo.defaultLayer,
|
|
Session.defaultLayer,
|
|
SessionStatus.defaultLayer,
|
|
BackgroundJob.defaultLayer,
|
|
RuntimeFlags.defaultLayer,
|
|
SessionRunState.defaultLayer,
|
|
SessionProcessor.defaultLayer,
|
|
SessionCompaction.defaultLayer,
|
|
SessionRevert.defaultLayer,
|
|
SessionSummary.defaultLayer,
|
|
SessionPrompt.defaultLayer,
|
|
Instruction.defaultLayer,
|
|
LLM.defaultLayer,
|
|
LSP.defaultLayer,
|
|
MCP.defaultLayer,
|
|
McpAuth.defaultLayer,
|
|
Command.defaultLayer,
|
|
Truncate.defaultLayer,
|
|
)
|
|
|
|
const FeatureLayer = Layer.mergeAll(
|
|
ToolRegistry.defaultLayer,
|
|
Format.defaultLayer,
|
|
Project.defaultLayer,
|
|
Vcs.defaultLayer,
|
|
Reference.defaultLayer,
|
|
Workspace.defaultLayer,
|
|
Worktree.appLayer,
|
|
Pty.defaultLayer,
|
|
PtyTicket.defaultLayer,
|
|
Installation.defaultLayer,
|
|
MemoryService.layer, // kilocode_change
|
|
ShareNext.defaultLayer,
|
|
SessionShare.defaultLayer,
|
|
SyncEvent.defaultLayer,
|
|
EventV2Bridge.defaultLayer,
|
|
DataMigration.defaultLayer,
|
|
)
|
|
|
|
export const AppLayer = Layer.mergeAll(CoreLayer, SessionLayer, FeatureLayer).pipe(
|
|
Layer.provideMerge(InstanceLayer.layer),
|
|
Layer.provideMerge(Observability.layer),
|
|
)
|
|
|
|
const rt = ManagedRuntime.make(AppLayer, { memoMap })
|
|
type Runtime = Pick<typeof rt, "runSync" | "runPromise" | "runPromiseExit" | "runFork" | "runCallback" | "dispose">
|
|
|
|
/** Services provided by AppRuntime — i.e. what an Effect run via AppRuntime.runPromise can yield. */
|
|
export type AppServices = ManagedRuntime.ManagedRuntime.Services<typeof rt>
|
|
const wrap = (effect: Parameters<typeof rt.runSync>[0]) => attach(effect as never) as never
|
|
|
|
export const AppRuntime: Runtime = {
|
|
runSync(effect) {
|
|
return rt.runSync(wrap(effect))
|
|
},
|
|
runPromise(effect, options) {
|
|
return rt.runPromise(wrap(effect), options)
|
|
},
|
|
runPromiseExit(effect, options) {
|
|
return rt.runPromiseExit(wrap(effect), options)
|
|
},
|
|
runFork(effect) {
|
|
return rt.runFork(wrap(effect))
|
|
},
|
|
runCallback(effect) {
|
|
return rt.runCallback(wrap(effect))
|
|
},
|
|
dispose: () => rt.dispose(),
|
|
}
|