@zaly/agent 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-GDLdDc2w.mjs → agent-EkPynZRJ.mjs} +7 -9
- package/dist/agent-EkPynZRJ.mjs.map +1 -0
- package/dist/{bash-QNMTQ1PD.mjs → bash-COgq4W7U.mjs} +2 -2
- package/dist/{bash-QNMTQ1PD.mjs.map → bash-COgq4W7U.mjs.map} +1 -1
- package/dist/compactions-3AjPqjeZ.mjs.map +1 -1
- package/dist/{fetch-D8cmmA3E.mjs → fetch-DknuSrSX.mjs} +2 -2
- package/dist/{fetch-D8cmmA3E.mjs.map → fetch-DknuSrSX.mjs.map} +1 -1
- package/dist/{grep-B2Ax7ewy.mjs → grep-B4GF0svc.mjs} +2 -2
- package/dist/{grep-B2Ax7ewy.mjs.map → grep-B4GF0svc.mjs.map} +1 -1
- package/dist/index-node-esm-D6YQRKEz.mjs.map +1 -1
- package/dist/index.d.mts +42 -42
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +28 -22
- package/dist/index.mjs.map +1 -1
- package/dist/{jsonl-Dw_VBp46.mjs → jsonl-CvgEOakL.mjs} +2 -2
- package/dist/jsonl-CvgEOakL.mjs.map +1 -0
- package/dist/{manager-D-lWBTHG.mjs → manager-f-WjFTMX.mjs} +2 -2
- package/dist/manager-f-WjFTMX.mjs.map +1 -0
- package/dist/masker-Bv9KjkDF.mjs.map +1 -1
- package/dist/notify-DpG_m9iP.mjs.map +1 -1
- package/dist/{registry-CFmR5G3J.mjs → registry-BVf0aZEA.mjs} +328 -48
- package/dist/registry-BVf0aZEA.mjs.map +1 -0
- package/dist/session/claude.d.mts +2 -3
- package/dist/session/claude.d.mts.map +1 -1
- package/dist/session/claude.mjs +1 -1
- package/dist/{session-CUUdWFC5.mjs → session-Bv8JiT8P.mjs} +4 -4
- package/dist/session-Bv8JiT8P.mjs.map +1 -0
- package/dist/session.d.mts +8 -8
- package/dist/session.d.mts.map +1 -1
- package/dist/session.mjs +3 -3
- package/dist/{subagent-baqzjRMc.mjs → subagent-CX7Zjcws.mjs} +3 -3
- package/dist/{subagent-baqzjRMc.mjs.map → subagent-CX7Zjcws.mjs.map} +1 -1
- package/dist/swarm-BJ3gQA1Z.mjs.map +1 -1
- package/dist/{tasks-D8rARHZ1.mjs → tasks-BA-PCeGa.mjs} +4 -3
- package/dist/{tasks-D8rARHZ1.mjs.map → tasks-BA-PCeGa.mjs.map} +1 -1
- package/dist/{tasks-WmC270X2.mjs → tasks-WdRAHsOL.mjs} +5 -4
- package/dist/tasks-WdRAHsOL.mjs.map +1 -0
- package/dist/{tools-DW1dbcCz.mjs → tools-Bl-YI890.mjs} +75 -25
- package/dist/tools-Bl-YI890.mjs.map +1 -0
- package/dist/truncate-BSMXgiXj.mjs.map +1 -1
- package/dist/{uuid-ByXP5SO5.mjs → uuid-CAdJg4l8.mjs} +2 -2
- package/dist/{uuid-ByXP5SO5.mjs.map → uuid-CAdJg4l8.mjs.map} +1 -1
- package/package.json +9 -9
- package/dist/agent-GDLdDc2w.mjs.map +0 -1
- package/dist/jsonl-Dw_VBp46.mjs.map +0 -1
- package/dist/manager-D-lWBTHG.mjs.map +0 -1
- package/dist/registry-CFmR5G3J.mjs.map +0 -1
- package/dist/session-CUUdWFC5.mjs.map +0 -1
- package/dist/tasks-WmC270X2.mjs.map +0 -1
- package/dist/tools-DW1dbcCz.mjs.map +0 -1
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["#opts","#cwd","#reasoning","#session","#model","#skills","#prompt","#tools","#cache","#agent","builtin","#opts","#logger","#tokenize","#opts","#tool","#load","#update","#buildTool","#buildCatalogDesc","#call"],"sources":["../src/ctx.ts","../src/prompt/registry.ts","../src/tools/registry.ts","../src/commands.ts","../src/skills.ts","../src/compaction/utils.ts","../src/context/tokens.ts"],"sourcesContent":["import type { Model, ReasoningEffort, Tool } from \"@zaly/ai\"\nimport type { Agent } from \"./agent.ts\"\nimport type { AgentStatus } from \"./events.ts\"\nimport type { Masker } from \"./masker.ts\"\nimport type { Notifier, NotifyOptions } from \"./notify.ts\"\nimport type { PermissionManager, PermissionOptions } from \"./permissions/manager.ts\"\nimport type { Session } from \"./session/session.ts\"\nimport type { Skills } from \"./skills.ts\"\nimport type { Swarm } from \"./swarm.ts\"\nimport type { AgentOptions } from \"./types.ts\"\n\nimport { Emitter, isInstance, normPath } from \"@zaly/shared\"\nimport { LazyCache } from \"@zaly/shared/cache\"\n\ntype AgentContextOptions = Omit<AgentOptions, \"session\"> & { session: Session }\n\ntype Slots = {\n notifier: Notifier\n masker: Masker\n permissions: PermissionManager\n swarm: Swarm\n}\n\nexport type AgentContextEvents = {\n model: { model?: Model; prev?: Model }\n reasoning: { effort: ReasoningEffort; prev?: ReasoningEffort }\n session: { session: Session; prev?: Session }\n cwd: { cwd: string; prev?: string }\n skills: { skills?: Skills }\n}\n\nexport class AgentContext extends Emitter<AgentContextEvents> {\n #agent?: Agent\n #opts: AgentOptions\n #model?: Model\n #session: Session\n #reasoning: ReasoningEffort\n #cwd: string\n #cache = new LazyCache<Slots>()\n #tools: Tool[]\n #skills?: Skills\n #prompt: string[]\n\n constructor(opts: AgentContextOptions) {\n super()\n this.#opts = opts\n this.#cwd = normPath(opts.cwd)\n this.#reasoning = opts.request?.reasoning?.effort ?? \"medium\"\n this.#session = opts.session\n this.#model = opts.model\n this.#skills = opts.skills\n this.#prompt = opts.prompt ?? []\n\n this.#tools = opts.tools ?? []\n this.onEmitError = (error) => this.#opts.logger?.child(\"context\").error(error)\n\n this.on(\"model\", async ({ model }) => {\n if (!this.#session.started) return\n if (model) await this.session.update({ modelId: model.id })\n })\n .on(\"reasoning\", async ({ effort }) => {\n if (!this.#session.started) return\n await this.session.update({ reasoning: effort })\n })\n .on(\"cwd\", ({ cwd }) => {\n if (!this.#session.started) return\n this.#cache.forget(\"permissions\") // reset permissions to force reload with new cwd\n void this.session.update({ cwd })\n })\n }\n\n private async start() {\n const [_masker, notifier] = await Promise.all([this.masker(), this.notifier()])\n\n if (!this.model) throw new Error(\"model is required to start agent session\")\n\n if (notifier) notifier.attach(this.agent)\n\n await this.session.start({\n cwd: this.cwd,\n modelId: this.model.id,\n reasoning: this.reasoning,\n })\n // oxlint-disable-next-line no-await-in-loop\n for (const m of this.#opts.messages ?? []) await this.session.add(m)\n }\n\n attach(agent: Agent) {\n if (this.#agent === agent) return\n if (this.#agent) throw new Error(\"agent already attached to context\")\n if (agent.started) throw new Error(\"cannot attach agent that has already started\")\n this.#agent = agent\n agent.once(\"start\", () => this.start())\n }\n\n get messages() {\n return this.session.messages\n }\n\n get status(): AgentStatus | undefined {\n return this.#agent?.status\n }\n\n get opts() {\n return this.#opts\n }\n\n get cwd() {\n return this.#cwd\n }\n\n set cwd(c: string) {\n if (c === this.#cwd) return\n const prev = this.#cwd\n this.#cwd = normPath(c)\n void this.emit(\"cwd\", { cwd: this.#cwd, prev })\n }\n\n get signal() {\n return this.#agent?.signal\n }\n\n get agent() {\n if (!this.#agent) throw new Error(\"agent not attached to context\")\n return this.#agent\n }\n\n get reasoning() {\n return this.#reasoning\n }\n\n set reasoning(r: ReasoningEffort) {\n if (r === this.#reasoning) return\n const prev = this.#reasoning\n this.#reasoning = r\n void this.emit(\"reasoning\", { effort: r, prev })\n }\n\n get session(): Session {\n return this.#session\n }\n\n async useSession(s: Session): Promise<void> {\n const prev = this.#session\n this.#session = s\n const modelId = s.settings.modelId\n if (modelId && modelId !== this.model?.id) {\n if (this.#opts.loadModel) {\n this.model = await this.#opts.loadModel(modelId)\n } else {\n const { loadModel } = await import(\"@zaly/ai\")\n this.model = await loadModel(modelId)\n }\n }\n this.cwd = s.settings.cwd ?? this.cwd\n this.reasoning = s.settings.reasoning ?? this.reasoning\n await this.emit(\"session\", { prev, session: s })\n if (this.#agent?.started)\n await s.start({\n cwd: this.cwd,\n modelId: this.model?.id,\n reasoning: this.reasoning,\n })\n }\n\n get model() {\n return this.#model\n }\n\n set model(m: Model | undefined) {\n if (m === this.#model) return\n const prev = this.#model\n this.#model = m\n void this.emit(\"model\", { model: m, prev })\n }\n\n set tools(tools: Tool[]) {\n this.#tools = tools\n }\n\n get tools(): Tool[] {\n return [...this.#tools, ...(this.skills?.tool ? [this.skills.tool] : [])]\n }\n\n get prompt(): string[] {\n return this.#prompt\n }\n\n set prompt(p: string[]) {\n this.#prompt = p\n }\n\n async swarm() {\n return this.#cache.need(\n \"swarm\",\n async () => {\n const { Swarm } = await import(\"./swarm.ts\")\n return new Swarm()\n },\n this.#opts.swarm\n )\n }\n\n set skills(s: Skills | undefined) {\n if (s === this.#skills) return\n this.#skills = s\n void this.emit(\"skills\", { skills: s })\n }\n\n get skills(): Skills | undefined {\n return this.#skills\n }\n\n private async notifier(): Promise<Notifier | undefined> {\n return this.#cache.want(\n \"notifier\",\n async (opts?: NotifyOptions) => {\n const { Notifier } = await import(\"./notify.ts\")\n return new Notifier(opts)\n },\n this.#opts.notify\n )\n }\n\n async masker(): Promise<Masker | undefined> {\n return this.#cache.want(\n \"masker\",\n async (opts?: AgentOptions[\"mask\"]) => {\n const { Masker } = await import(\"./masker.ts\")\n return new Masker(this.agent, opts)\n },\n this.#opts.mask\n )\n }\n\n async permissions(): Promise<PermissionManager> {\n return this.#cache.need(\n \"permissions\",\n async (opts?: PermissionOptions) => {\n const { PermissionManager } = await import(\"./permissions/manager.ts\")\n return new PermissionManager({ ...opts, cwd: this.cwd })\n },\n this.#opts.permissions\n )\n }\n}\n\nasync function loadSession(spec?: AgentOptions[\"session\"]): Promise<Session> {\n if (isInstance<Session>(spec)) return spec\n else {\n const { Session } = await import(\"./session/session.ts\")\n return await Session.load({ ...spec })\n }\n}\n\nexport async function createAgentContext(opts: AgentOptions): Promise<AgentContext> {\n const session = await loadSession(opts.session)\n return new AgentContext({ ...opts, session })\n}\n\nexport async function createAgent(opts: AgentOptions): Promise<Agent> {\n const [{ Agent }, ctx] = await Promise.all([import(\"./agent.ts\"), createAgentContext(opts)])\n return new Agent(ctx)\n}\n","import type { Model } from \"@zaly/ai\"\n\nimport { BaseCollection } from \"@zaly/shared/collection\"\nimport { createRegistry } from \"@zaly/shared/registry\"\n\nexport interface PromptCtx {\n model: Model\n cwd: string\n}\n\nexport type PromptLoader = (ctx: PromptCtx) => Promise<string>\nexport type Prompt<T extends string | PromptLoader = string | PromptLoader> = {\n name: string\n text: T\n}\nexport type { PromptCollection }\n\nconst builtin = {\n \"AGENTS.md\": (ctx) => import(\"./markdown.ts\").then((m) => m.agentsMdPrompt(ctx)),\n \"MEMORY.md\": (ctx) => import(\"./markdown.ts\").then((m) => m.memoryMdPrompt(ctx)),\n agent: () => import(\"./agent.ts\").then((m) => m.agentPrompt),\n env: (ctx) => import(\"./env.ts\").then((m) => m.prompt(ctx)),\n model: (ctx) => import(\"./model.ts\").then((m) => m.prompt(ctx)),\n} as const satisfies Record<string, PromptLoader>\n\nexport type BuiltinPrompt = keyof typeof builtin\nexport type AnyPrompt = BuiltinPrompt | (string & {})\n\nexport const promptRegistry = createRegistry<PromptLoader>(\"prompt\").from(builtin)\n\nclass PromptCollection extends BaseCollection<AnyPrompt[], Prompt[], Prompt> {\n list(): Prompt[] {\n const ret = new Map<string, Prompt>()\n for (const k of promptRegistry.keys())\n ret.set(k, {\n name: k,\n text: (ctx) => promptRegistry.load(k, ctx),\n })\n for (const r of this.registered) ret.set(r.name, r)\n return [...ret.values()]\n }\n\n async render(ctx: PromptCtx & { prompts?: string[] }): Promise<Prompt<string>[]> {\n const all = new Map(this.list().map((p) => [p.name, p]))\n const ret = await Promise.all(\n (ctx.prompts ?? this.active).map(async (p) => {\n const def = all.get(p)\n if (!def) throw new Error(`Unknown prompt: ${p}`)\n return {\n name: def.name,\n text: typeof def.text === \"string\" ? def.text : await def.text(ctx),\n }\n })\n )\n return ret.map((p) => ({ name: p.name, text: p.text.trim() })).filter((p) => p.text.length > 0)\n }\n}\n\nexport async function promptCollection(): Promise<PromptCollection> {\n return new PromptCollection([\"agent\", \"env\", \"model\", \"AGENTS.md\", \"MEMORY.md\"])\n}\n","import type { Model, Tool } from \"@zaly/ai\"\nimport type { AnyKey } from \"@zaly/shared/registry\"\n\nimport { BaseCollection } from \"@zaly/shared/collection\"\nimport { createRegistry } from \"@zaly/shared/registry\"\n\n/** Per-load context handed to every tool factory. Lets factories\n * customise the tool — typically schema descriptions — based on the\n * agent's model and working directory. Tools that don't care about\n * init just ignore the arg. */\nexport interface ToolInit {\n model: Model\n cwd: string\n}\n\nexport type { ToolCollection }\nexport type ToolLoader = () => Promise<Tool>\nexport type BuiltinTool = keyof typeof builtin\nexport type AnyTool = AnyKey<BuiltinTool>\n\n// PERF: `import(\"./x\")` makes TS load the full module type.\n// The `as string` erases the dynamic import type while emitting the same JS.\nconst builtin = {\n agent_send: () => import(\"./swarm.ts\" as string).then((m) => m.agentSendTool as Tool),\n agent_spawn: () => import(\"./swarm.ts\" as string).then((m) => m.agentSpawnTool as Tool),\n bash: () => import(\"./bash.ts\" as string).then((m) => m.bashTool as Tool),\n edit: () => import(\"./edit.ts\" as string).then((m) => m.editTool as Tool),\n fetch: () => import(\"./fetch.ts\" as string).then((m) => m.fetchTool as Tool),\n find: () => import(\"./find.ts\" as string).then((m) => m.findTool as Tool),\n grep: () => import(\"./grep.ts\" as string).then((m) => m.grepTool as Tool),\n read: () => import(\"./read.ts\" as string).then((m) => m.readTool as Tool),\n search: () => import(\"./search.ts\" as string).then((m) => m.searchTool as Tool),\n subagent: () => import(\"./subagent.ts\" as string).then((m) => m.subagentTool as Tool),\n task_list: () => import(\"./tasks.ts\" as string).then((m) => m.taskListTool as Tool),\n task_poll: () => import(\"./tasks.ts\" as string).then((m) => m.taskPollTool as Tool),\n task_stop: () => import(\"./tasks.ts\" as string).then((m) => m.taskStopTool as Tool),\n wakeup: () => import(\"./wakeup.ts\" as string).then((m) => m.wakeupTool as Tool),\n write: () => import(\"./write.ts\" as string).then((m) => m.writeTool as Tool),\n} as const satisfies Record<string, ToolLoader>\n\nexport const toolRegistry = createRegistry<ToolLoader>(\"tool\").from(builtin)\n\nclass ToolCollection extends BaseCollection<AnyTool[], AnyTool[], Tool> {\n list(): AnyTool[] {\n const ret = new Set(toolRegistry.keys())\n for (const r of this.registered) ret.add(r.name)\n return [...ret]\n }\n\n async load(tools?: AnyTool[]): Promise<Tool[]> {\n const load = async (t: string) =>\n this.registered.findLast((r) => r.name === t) ?? toolRegistry.load(t)\n return await Promise.all((tools ?? this.active).map((t) => load(t)))\n }\n}\n\nexport async function toolCollection(): Promise<ToolCollection> {\n return new ToolCollection([])\n}\n","// oxlint-disable no-await-in-loop\nimport type { ArgsOpts, ArgsResult } from \"@zaly/shared/args\"\nimport type { Logger } from \"@zaly/shared/logger\"\n\nimport { normPath, prettyPath, safeReadFile, toError } from \"@zaly/shared\"\nimport { basename } from \"pathe\"\n\nexport type Command = {\n name: string\n body: string\n path: string\n description?: string\n args: ArgsOpts\n}\n\nexport interface CommandsOptions {\n /** cmd.md paths, sorted from highest to lowest precedence. */\n paths?: string[]\n logger?: Logger\n /** Bash execution of commands:\n * - `true`: Use the system default bash shell.\n * - `false`: Disable bash execution.\n * - `string[]`: Use a custom bash shell with the provided arguments. */\n bash?: boolean | string[]\n /** When `false`, template expressions are disabled and only simple variable interpolation is allowed. */\n expr?: boolean\n}\n\ntype Token =\n | { type: \"text\"; value: string }\n | { type: \"block\"; value: string; lang?: string; body: string }\n | { type: \"script\"; value: string }\n\nconst BASH_RE = /^!`(.*)`$/\n\nexport class Commands {\n #opts: CommandsOptions\n readonly catalog = new Map<string, Command>()\n #logger?: Logger\n\n constructor(opts: CommandsOptions = {}) {\n this.#opts = opts\n this.#logger = opts.logger\n }\n\n async load(): Promise<this> {\n const paths = this.#opts.paths ?? []\n await Promise.all(paths.map(async (path) => await this.add(path)))\n return this\n }\n\n async add(path: string): Promise<void> {\n path = normPath(path)\n const name = basename(path, \".md\")\n const { parseFrontmatter } = await import(\"@zaly/shared/yaml\")\n try {\n const content = await safeReadFile(path)\n if (!content) throw new Error(`Failed to read command file at ${path}`)\n\n const { fm, body } = await parseFrontmatter(content)\n\n const args: ArgsOpts = {}\n const fmArgs = (fm.args ?? {}) as Record<string, Partial<ArgsOpts[string]>>\n\n for (const [o, opt] of Object.entries(fmArgs)) {\n // Set default values for boolean\n if (opt.type === \"boolean\") opt.default ??= false\n // Make string options required if no default is provided\n else if (opt.type === \"string\") opt.required ??= opt.default === undefined\n else {\n this.#logger?.warn(\n `Unsupported argument type for \\`--${o}\\` in command **${name}** at \\`${prettyPath(path)}\\`\\nOnly \\`string\\` and \\`boolean\\` are supported. Skipping this argument.`\n )\n continue\n }\n args[o] = opt as ArgsOpts[string]\n }\n\n const cmd: Command = {\n ...fm,\n args,\n body,\n name,\n path,\n }\n if (this.catalog.has(name)) return\n this.catalog.set(name, cmd)\n } catch (error) {\n this.#logger?.error(`Error loading **command** from ${path}: ${(error as Error).message}`)\n }\n }\n\n get(name: string): Command | undefined {\n return this.catalog.get(name)\n }\n\n async format(input: string | ArgsResult, cmd: Command): Promise<string> {\n let args: ArgsResult\n if (typeof input === \"string\") {\n const { argsParse } = await import(\"@zaly/shared/args\")\n args = await argsParse(input, cmd.args)\n } else args = input\n\n const { createTemplate } = await import(\"@zaly/shared/template\")\n const tpl = createTemplate(cmd.body, {\n expr: this.#opts.expr,\n format: (value) => {\n if (value === args._) return args._.join(\" \")\n },\n name: cmd.name,\n })\n\n const vars = {\n ...args,\n args: args._,\n raw: args.$,\n }\n\n const rendered = tpl(vars)\n if (this.#opts.bash === false) return rendered\n const bash = Array.isArray(this.#opts.bash) ? this.#opts.bash : [\"bash\"]\n\n const lines = rendered.split(\"\\n\")\n\n const ret: string[] = []\n for (const token of this.#tokenize(lines, cmd)) {\n let text: string | undefined\n if (token.type === \"script\") text = await exec(token.value, { bash })\n else if (token.type === \"block\" && /^bash\\s*!$/.test(token.lang ?? \"\"))\n text = await exec(token.body, { bash })\n ret.push(text ?? token.value)\n }\n\n return ret.join(\"\\n\")\n }\n\n *#tokenize(lines: string[], cmd: Command): Generator<Token> {\n let block: string[] = []\n let marker: RegExp | undefined = undefined\n let lang: string | undefined = undefined\n for (const line of lines) {\n const m = line.match(/^\\s*(`{3,}|~{3,})(.*)$/)\n const script = line.match(BASH_RE)\n if (marker && line.match(marker)) {\n block.push(line)\n yield {\n body: block.slice(1, -1).join(\"\\n\"),\n lang,\n type: \"block\",\n value: block.join(\"\\n\"),\n }\n marker = undefined // end of code block\n } else if (!marker && m) {\n marker = new RegExp(`^\\\\s*${m[1]}\\\\s*$`) // start of code block\n lang = m[2].trim() || undefined\n block = [line]\n } else if (marker) block.push(line)\n else if (script) yield { type: \"script\", value: script[1] }\n else yield { type: \"text\", value: line }\n }\n if (marker) throw new Error(`Unclosed code block in command **${cmd.name}**`)\n }\n}\n\nasync function exec(script: string, opts: { bash?: string[] } = {}): Promise<string | undefined> {\n if (!script.trim()) return\n const { spawnCmd } = await import(\"@zaly/shared/process\")\n let result: string\n try {\n const r = await spawnCmd(script, { bash: opts.bash ?? true, throw: true })\n result = r?.trim() ?? \"\"\n } catch (error) {\n result = toError(error).message.trim()\n }\n const md: string[] = []\n if (script.includes(\"\\n\")) {\n md.push(\"```bash\")\n md.push(script)\n md.push(\"```\")\n md.push(\"\")\n md.push(\"```shellsession\")\n md.push(result)\n md.push(\"```\")\n } else {\n md.push(\"```shellsession\")\n md.push(`$ ${script}`)\n md.push(result)\n md.push(\"```\")\n }\n md.push(\"\")\n return md.join(\"\\n\")\n}\n","import type { MetaPart, TextPart, Tool, ToolContext } from \"@zaly/ai\"\nimport type { Agent } from \"./agent.ts\"\n\nimport { AiError, defineTool, extractToolResults } from \"@zaly/ai\"\nimport { safeStatAsync } from \"@zaly/shared\"\nimport { glob } from \"@zaly/shared/glob\"\nimport { readFile } from \"node:fs/promises\"\nimport { isDeepStrictEqual } from \"node:util\"\nimport { dirname } from \"pathe\"\nimport { Type } from \"typebox\"\n\nexport type SkillMeta = {\n name: string\n mtime: number\n desc: string\n path: string\n unchanged?: boolean\n}\n\nexport type SkillTool = Tool<{ name: string }, unknown, SkillMeta>\n\n/**\n * Agent Skills support — discovery, catalog, and the activation tool.\n *\n * Locations scanned (subdirectories containing `SKILL.md`):\n * - `${cwd}/.agent/skills/` — project\n * - `${homedir()}/.agent/skills/` — user (cross-project)\n *\n * Project shadows user on name collision.\n *\n * Progressive disclosure (per the Agent Skills spec):\n * - Catalog (name + description) lives in the tool's schema /\n * description. The model sees this from the start.\n * - Body of the SKILL.md is loaded only when the tool is invoked.\n * - References (files under the skill's directory) are *enumerated*\n * at activation, not read — the model reads them via the `read`\n * tool when its instructions call for it.\n *\n * Lifecycle:\n * - Construct with `new Skills({ cwd })` — no I/O yet.\n * - Call `await skills.load()` to populate the catalog. Idempotent;\n * re-call to pick up newly installed skills (`/reload-plugins`).\n * - `skills.tool` returns the current activation tool, or undefined\n * when the catalog is empty. Lazily built; invalidated on reload.\n */\n\nexport interface SkillEntry {\n name: string\n desc: string\n mtime: number\n body: string\n /** Absolute path to the SKILL.md file. */\n path: string\n /** Absolute path to the skill's base directory (parent of SKILL.md).\n * Used as the workspace anchor for permissions and as the root for\n * resolving references. */\n dir: string\n}\n\nexport interface SkillsOptions {\n /** SKILL.md paths, sorted from highest to lowest precedence. */\n paths?: string[]\n}\n\nexport class Skills {\n readonly catalog = new Map<string, SkillEntry>()\n #tool?: Tool\n #opts: SkillsOptions\n\n protected constructor(opts: SkillsOptions = {}) {\n this.#opts = opts\n }\n\n static async load(opts?: SkillsOptions): Promise<Skills> {\n return new Skills(opts).reload()\n }\n\n /** (Re)scan project + user `.agent/skills/` directories. Wipes the\n * current catalog and the cached tool, then repopulates. Safe to\n * call mid-session — agent uses `this.tool` per-step so the next\n * request after reload picks up the change. */\n async reload(): Promise<this> {\n this.catalog.clear()\n this.#tool = undefined\n const paths = this.#opts.paths ?? []\n // Load all skills in parallel, but add them to the catalog in order so\n // that the first one wins on name collision.\n const skills = await Promise.all(paths.map(async (path) => await this.#load(path)))\n for (const skill of skills)\n if (skill && !this.catalog.has(skill.name)) this.catalog.set(skill.name, skill)\n return this\n }\n\n async #load(path: string): Promise<SkillEntry | undefined> {\n if (!path.endsWith(\"SKILL.md\")) return\n const dir = dirname(path)\n try {\n const { meta, mtime, body } = await readSkill(path)\n if (!meta.name || !meta.description || this.catalog.has(meta.name)) return\n return {\n body,\n desc: meta.description,\n dir,\n mtime,\n name: meta.name,\n path,\n }\n } catch {}\n }\n\n async #update(entry: SkillEntry): Promise<SkillEntry> {\n const updated = (await this.#load(entry.path)) ?? entry\n if (isDeepStrictEqual(updated, entry)) return entry\n this.catalog.set(entry.name, updated)\n this.#tool = undefined\n return updated\n }\n\n /** Skill base directories — pass each to `agent.permissions.addWorkspace`\n * so the model can read bundled `references/` files without tripping\n * permission asks. */\n get dirs(): readonly string[] {\n return [...this.catalog.values()].map((s) => s.dir)\n }\n\n /** The activation tool. Returns `undefined` when the catalog is empty\n * (the agent then omits skills entirely from the model's tool list,\n * per the spec). Lazily built on first access; invalidated by\n * `load()`. */\n get tool(): Tool | undefined {\n if (this.catalog.size === 0) return undefined\n return (this.#tool ??= this.#buildTool())\n }\n\n #buildTool(): SkillTool {\n const names = [...this.catalog.keys()]\n // oxlint-disable-next-line sort-keys -- semantic field order: name, desc, params, call\n return defineTool({\n name: \"skill\",\n desc: this.#buildCatalogDesc(),\n parallel: true,\n params: Type.Object({\n name: Type.Union(\n names.map((n) => Type.Literal(n)),\n {\n description:\n \"Name of the skill to activate. The full SKILL.md \" +\n \"instructions are loaded as the tool result; bundled \" +\n \"files in the skill's directory are listed but not read \" +\n \"— fetch them via the `read` tool when the skill's \" +\n \"instructions require them.\",\n }\n ),\n }),\n call: (args, ctx: ToolContext<SkillMeta>) => this.#call(args.name as string, ctx),\n })\n }\n\n async #call(requested: string, ctx: ToolContext<SkillMeta>): Promise<(MetaPart | TextPart)[]> {\n let skill = this.catalog.get(requested)\n if (!skill) {\n const available = [...this.catalog.keys()]\n throw new AiError({\n code: \"UNKNOWN_SKILL\",\n data: { available, name: requested },\n message: `no skill named \"${requested}\". Available: ${available.join(\", \")}`,\n })\n }\n\n // Refresh the skill entry so we have an up-to-date mtime for staleness checks in `isActivated`.\n skill = await this.#update(skill)\n\n ctx.meta = { desc: skill.desc, mtime: skill.mtime, name: skill.name, path: skill.path }\n const activated = await this.isActivated(skill, ctx)\n if (activated) {\n // Already activated and fresh — no need to reload or update the tool result.\n ctx.meta.unchanged = true\n return [\n {\n content: `skill \"${skill.name}\" unchanged since last read: ${skill.path}`,\n tag: \"unchanged\",\n type: \"meta\",\n },\n ]\n }\n\n const references = await listReferences(skill.dir)\n return [\n {\n data: { dir: skill.dir, name: skill.name, references },\n tag: \"skill\",\n type: \"meta\",\n },\n { text: skill.body, type: \"text\" },\n ]\n }\n\n async isActivated(skill: SkillEntry, ctx: ToolContext<SkillMeta>): Promise<boolean> {\n const messages = ctx.messages ?? []\n for (const { m, $p, p } of extractToolResults<SkillMeta, \"skill\">(messages, [\"skill\"])) {\n const id = m.id\n if (!id || !p.meta || ctx.isMasked?.(id, $p)) continue\n if (p.meta.unchanged) continue\n if (p.meta.name === skill.name) return p.meta.mtime === skill.mtime\n }\n return false\n }\n\n async activate(name: string, agent: Agent) {\n const ret = await agent.useTool<SkillTool>(\n \"skill\",\n { name },\n `Skill \"${name}\" was activated by the user.`\n )\n if (ret.result.meta?.unchanged) return\n return ret\n }\n\n #buildCatalogDesc(): string {\n const header =\n \"Activate one of the available skills below. The skill's full \" +\n \"SKILL.md instructions are loaded as the tool result; the model \" +\n \"should follow them directly. Bundled files are listed in the \" +\n \"result but not eagerly read — fetch the ones the skill's \" +\n \"instructions reference via the `read` tool.\"\n const lines = [...this.catalog.values()].map((s) => `- ${s.name}: ${s.desc}`)\n return `${header}\\n\\nAvailable skills:\\n${lines.join(\"\\n\")}`\n }\n}\n\n// ── Discovery ──────────────────────────────────────────────────────────\n\nasync function readSkill(\n path: string\n): Promise<{ meta: { name?: string; description?: string }; body: string; mtime: number }> {\n const s = await safeStatAsync(path)\n if (!s?.isFile()) throw new Error(`Skill file not found at ${path}`)\n const raw = await readFile(path, \"utf8\")\n const { parseFrontmatter } = await import(\"@zaly/shared/yaml\")\n const { fm, body } = await parseFrontmatter(raw)\n return { body, meta: fm, mtime: s.mtimeMs }\n}\n\nasync function listReferences(dir: string): Promise<string[]> {\n const out: string[] = []\n for await (const rel of glob(\"**\", { cwd: dir, depth: 4, type: \"file\" })) {\n if (rel === \"SKILL.md\" || rel.endsWith(\"/SKILL.md\")) continue\n out.push(rel)\n if (out.length >= 200) break\n }\n return out.toSorted()\n}\n","import type { AnyPart, Message } from \"@zaly/ai\"\nimport type { BashTool } from \"../tools/bash.ts\"\nimport type { EditToolMeta } from \"../tools/edit.ts\"\nimport type { ReadToolMeta } from \"../tools/read.ts\"\nimport type { WriteToolMeta } from \"../tools/write.ts\"\n\nimport {\n attachmentToMeta,\n ContentTransform,\n errorToMeta,\n extractToolCalls,\n extractToolResults,\n metaToText,\n safeParseToolParams,\n sanitizeText,\n stringifyContent,\n toXml,\n} from \"@zaly/ai\"\nimport { safeStringify, formatDuration } from \"@zaly/shared\"\nimport { parseBash } from \"../utils/bash/parser.ts\"\nimport { TOOLS } from \"../utils/bash/tools.ts\"\n\nexport type ToolStat = {\n count: number\n score: number\n /** Turns ago at the most recent occurrence (0 = current/last turn).\n * Use with `lastTs` to give the agent both an \"in-experience\" recency\n * signal and a wall-clock staleness signal. */\n lastTurn: number\n /** Wall-clock ms timestamp of the most recent occurrence. `0` if no\n * occurrence carried a timestamp. */\n lastTs: number\n}\n\n/** Per-file activity tally produced by `extractFiles`. Each entry is a\n * path that the agent touched, with per-kind counts and a frecency\n * `score` that downweights older touches and weights writes/edits\n * more than reads. Sort the top-N by `score` to surface the active\n * working set; `reads` / `writes` / `edits` give the per-kind detail\n * for display. */\nexport type FileUsage = ToolStat & {\n path: string\n reads: number\n writes: number\n edits: number\n}\n\n/** Per-command activity tally produced by `extractBashCommands`. `count`\n * is raw invocations; `score` is the same number but exp-decay-weighted\n * by turn distance from the end of the conversation, so recently-used\n * commands rank above old hot ones. */\nexport type BashUsage = ToolStat & {\n command: string\n}\n\n// ── Frecency tuning ───────────────────────────────────────────────────\n// Half-life measured in user turns, not wall time — what matters for\n// compaction context is what's been hot in the agent's *recent\n// experience*, regardless of how long the user spent between turns.\n// 60 turns is a middle ground: actively-iterated work stays at the top\n// while recurring workflow patterns (git stash, test-runner flags) still\n// clear the minScore filter instead of getting buried under one-shot\n// noise from earlier in the session.\nconst HALF_LIFE_TURN = 60\nconst LAMBDA = Math.LN2 / HALF_LIFE_TURN\n// Per-kind weights — writes are the strongest intent signal (full-file\n// authorship), edits the iteration signal, reads the informational one.\nconst KIND_WEIGHTS = { edit: 2, read: 1, write: 3 } as const\n\nexport type ToolStatOptions = {\n minCount?: number\n minScore?: number\n limit?: number\n sort?: \"score\" | \"count\" | \"key\"\n}\n\nconst sorters = {\n count: (a: [string, ToolStat], b: [string, ToolStat]) => b[1].count - a[1].count,\n key: (a: [string, ToolStat], b: [string, ToolStat]) => a[0].localeCompare(b[0]),\n score: (a: [string, ToolStat], b: [string, ToolStat]) => b[1].score - a[1].score,\n} as const satisfies Record<string, (a: [string, ToolStat], b: [string, ToolStat]) => number>\n\nfunction turnInfo(messages: readonly Message[]) {\n const weights: number[] = []\n const turns: number[] = []\n let turn = 0\n for (let i = messages.length - 1; i >= 0; i--) {\n const m = messages[i]\n if (m.role === \"assistant\") turn++\n weights[i] = Math.exp(-LAMBDA * turn)\n turns[i] = turn\n }\n return { turns, weights }\n}\n\nfunction toolStats<T extends ToolStat = ToolStat>(\n map: Map<string, T>,\n opts: ToolStatOptions = {}\n): T[] {\n const limit = opts.limit ?? 50\n const sorter = sorters[opts.sort ?? \"score\"]\n const sorted = [...map.entries()].toSorted(sorter).map(([_, stat]) => stat)\n return sorted\n .filter((stat) => stat.count >= (opts.minCount ?? 1))\n .filter((stat) => stat.score >= (opts.minScore ?? 0))\n .slice(0, limit)\n}\n\n/** Walk assistant tool-calls for `read` / `write` / `edit` and tally\n * per-path activity with frecency-weighted scoring. Returns the top-N\n * by score. Each entry carries the per-kind breakdown so the\n * summarizer can describe the *kind* of activity (\"heavily edited\",\n * \"read once, then written\", \"iteratively refined\").\n *\n * Paths are canonicalized via the cwd recorded on each call's session\n * node — relative paths are resolved against the cwd that was active\n * when the call ran, and `~` is expanded. Calls under different cwds\n * that target the same file thus collapse into one bucket. */\nexport function extractFileUsage(\n messages: readonly Message[],\n opts: ToolStatOptions = {}\n): FileUsage[] {\n opts = { minScore: 0.5, ...opts }\n const map = new Map<string, FileUsage>()\n const { turns, weights } = turnInfo(messages)\n\n for (const { m, p, $m: idx } of extractToolResults<EditToolMeta | ReadToolMeta | WriteToolMeta>(\n messages,\n [\"read\", \"write\", \"edit\"]\n )) {\n const path = p.meta?.path\n if (!path) continue\n const entry = map.get(path) ?? {\n count: 0,\n edits: 0,\n lastTs: 0,\n lastTurn: Infinity,\n path,\n reads: 0,\n score: 0,\n writes: 0,\n }\n if (p.name === \"read\") entry.reads++\n else if (p.name === \"write\") entry.writes++\n else entry.edits++\n entry.count++\n entry.score += weights[idx] * KIND_WEIGHTS[p.name as keyof typeof KIND_WEIGHTS]\n if (turns[idx] < entry.lastTurn) entry.lastTurn = turns[idx]\n if (m.ts && m.ts > entry.lastTs) entry.lastTs = m.ts\n map.set(path, entry)\n }\n return toolStats(map, opts)\n}\n\n/** Walk assistant tool-calls for `bash`, parse each command line, and\n * tally per-command frecency. Plumbing commands (cat, head, grep, awk,\n * sed, …) are filtered out — they dominate raw counts but carry no\n * operational signal a summarizer would describe. Returns the top-N\n * by frecency `score`. */\nexport function extractBashUsage(\n messages: readonly Message[],\n opts: ToolStatOptions = {}\n): BashUsage[] {\n opts = { minCount: 2, minScore: 0.5, ...opts } // bash commands are noisier, so default to minCount=2\n const { turns, weights } = turnInfo(messages)\n const map = new Map<string, BashUsage>()\n for (const { m, p, $m: idx } of extractToolCalls(messages, [\"bash\"])) {\n const params = safeParseToolParams<BashTool>(p.params)\n const cmd = params?.command\n if (!cmd) continue\n const result = parseBash(cmd)\n if (!result.ok) continue\n const w = weights[idx] ?? 0\n const t = turns[idx] ?? Infinity\n for (const seg of result.segments) {\n if (TOOLS[seg.cmd]) continue\n const key = `${seg.cmd} ${seg.args.join(\" \")}`\n if (key.includes(\"\\n\")) continue\n const entry = map.get(key) ?? {\n command: key,\n count: 0,\n lastTs: 0,\n lastTurn: Infinity,\n score: 0,\n }\n entry.count++\n entry.score += w\n if (t < entry.lastTurn) entry.lastTurn = t\n if (m.ts && m.ts > entry.lastTs) entry.lastTs = m.ts\n map.set(key, entry)\n }\n }\n return toolStats(map, opts)\n}\n\n// ── Transcript ────────────────────────────────────────────────────────\n\nfunction truncate(text: string, len: number): string {\n return text.length <= len ? text : `${text.slice(0, len)}…`\n}\n\n/** Flatten a list of messages into a single tagged transcript. */\nexport function extractConversation(\n messages: readonly Message[],\n opts: { maxToolResultLen?: number } = {}\n): string {\n const transform = ContentTransform.create<AnyPart>()\n .drop(\"reasoning\")\n .map(\"tool-call\", (part) => ({\n content: [\n {\n text: `${part.name}(${safeStringify(part.params)})`,\n type: \"text\" as const,\n },\n ],\n tag: \"tool-call\",\n type: \"meta\",\n }))\n .map(\"tool-result\", (part) => {\n const text = truncate(stringifyContent(part.content), opts.maxToolResultLen ?? 2000)\n return {\n content: text === \"\" ? undefined : [{ text, type: \"text\" as const }],\n data: { tool: part.name, ...(part.isError ? { error: true } : {}) },\n tag: \"tool-result\",\n type: \"meta\" as const,\n }\n })\n .pipe(attachmentToMeta(\"image\", \"pdf\", \"audio\", \"video\"))\n .pipe(errorToMeta())\n .pipe(metaToText())\n .pipe(sanitizeText())\n\n const blocks: string[] = []\n let role: string | undefined = undefined\n for (const m of messages) {\n const parts =\n typeof m.content === \"string\" ? [{ text: m.content, type: \"text\" as const }] : m.content\n const transformed = transform.runSync(parts)\n const text = stringifyContent(transformed).trim()\n if (!text) continue\n let currentRole = m.role.charAt(0).toUpperCase() + m.role.slice(1) // capitalize\n currentRole = currentRole === \"Tool\" ? \"Assistant\" : currentRole // re-label \"Tool\" as \"Assistant\" for summarization purposes\n blocks.push(role === currentRole ? text : `\\n[${currentRole}]: ${text}`)\n role = currentRole\n }\n const text = blocks.join(\"\\n\").replace(/^\\s+$/gm, \"\").trim()\n return toXml(text, \"conversation\", { indent: false })\n}\n\n// ── Tail selection ────────────────────────────────────────────────────\n\nexport function messageTail(\n messages: readonly Message[],\n opts: { keepTokens?: number }\n): Message[] {\n const maxTokens = opts.keepTokens ?? 20_000\n messages = messages.toReversed()\n const tail: Message[] = []\n const queue: Message[] = []\n\n let used = 0\n let last: number | undefined = undefined\n\n for (const m of messages) {\n queue.unshift(m)\n const usage = m.role === \"assistant\" ? m.meta?.usage : undefined\n if (!usage) continue\n const current = usage.input + usage.output + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0)\n // clamp for masker and similar events that might shrink the context size\n const delta = Math.max(0, (last ?? current) - current)\n if (used + delta > maxTokens) break\n used += delta\n last = current\n tail.unshift(...queue.splice(0))\n }\n return tail\n}\n\n// -- Formatting helpers -────────────────────────────────────────────────────────\n\nconst lastCol = (lastTurn: number, lastTs: number): string => {\n const turn = lastTurn === Infinity ? \"?\" : `${lastTurn}t`\n const wall = lastTs > 0 ? formatDuration(lastTs) : \"?\"\n return `${turn} / ${wall}`\n}\n\nexport function formatBashUsage(commands: BashUsage[]): string {\n if (commands.length === 0) return \"(no bash commands found)\"\n const lines: string[] = []\n lines.push(\"=== Top bash commands (sorted by frecency) ===\")\n lines.push(` ${\"score\".padStart(7)} ${\"count\".padStart(5)} ${\"last\".padEnd(14)} command`)\n for (const c of commands) {\n lines.push(\n ` ${c.score.toFixed(2).padStart(7)} ${String(c.count).padStart(5)} ${lastCol(c.lastTurn, c.lastTs).padEnd(14)} ${c.command}`\n )\n }\n const text = lines.join(\"\\n\")\n return toXml(text, \"bash-commands\", { indent: false })\n}\n\nexport function formatFileUsage(files: FileUsage[]): string {\n if (files.length === 0) return \"(no file ops found)\"\n const lines: string[] = []\n lines.push(\"=== Top files touched (sorted by frecency) ===\")\n lines.push(\n ` ${\"score\".padStart(7)} ${\"total\".padStart(5)} r/w/e ${\"last\".padEnd(14)} path`\n )\n for (const f of files) {\n const rwe = `${f.reads}/${f.writes}/${f.edits}`.padEnd(12)\n lines.push(\n ` ${f.score.toFixed(2).padStart(7)} ${String(f.count).padStart(5)} ${rwe} ${lastCol(f.lastTurn, f.lastTs).padEnd(14)} ${f.path}`\n )\n }\n const text = lines.join(\"\\n\")\n return toXml(text, \"files\", { indent: false })\n}\n","import type { AnyContent, AnyPart, AnyType, Attachment, Message, Tool } from \"@zaly/ai\"\nimport type { Prompt } from \"../prompt/registry.ts\"\n\nimport { stringifyContent } from \"@zaly/ai\"\n\nexport type TokenCount = {\n type: AnyType | Message[\"role\"] | \"prompt\" | \"tool-schema\" | \"system-prompt\"\n kind?: string\n tokens: number\n children?: TokenCount[]\n}\n\nexport type TokenStats = {\n key: string\n tokens: number\n count: number\n children?: Map<string, TokenStats>\n}\n\n// ── token estimation ───────────────────────────────────────────────────\n\nconst CHARS_PER_TOKEN = 4\n\nconst ATTACHMENT_TOKENS: Record<Attachment[\"type\"], number> = {\n audio: 3000,\n image: 1500,\n pdf: 8000,\n video: 5000,\n}\n\nfunction fromText(text: string): number {\n return Math.ceil(text.length / CHARS_PER_TOKEN)\n}\n\nfunction sumTokens(stats: TokenCount[]): number {\n return stats.reduce((sum, s) => sum + s.tokens, 0)\n}\n\nexport function estimatePart(p: AnyPart): TokenCount {\n switch (p.type) {\n case \"reasoning\":\n case \"text\": {\n return { tokens: fromText(p.text), type: p.type }\n }\n case \"tool-call\": {\n return {\n kind: p.name,\n tokens: fromText(p.name) + fromText(JSON.stringify(p.params ?? {})),\n type: p.type,\n }\n }\n case \"tool-result\": {\n const children = estimateContent(p.content)\n return {\n children,\n kind: p.name,\n tokens: fromText(p.name) + sumTokens(children),\n type: p.type,\n }\n }\n case \"image\":\n case \"pdf\":\n case \"audio\":\n case \"video\": {\n return { tokens: ATTACHMENT_TOKENS[p.type], type: p.type }\n }\n case \"meta\": {\n const children = p.content ? estimateContent(p.content) : undefined\n return {\n children,\n kind: p.tag,\n tokens: fromText(stringifyContent(p)),\n type: p.type,\n }\n }\n case \"error\": {\n return { tokens: fromText(stringifyContent(p)), type: p.type }\n }\n default: {\n // @ts-expect-error: exhaustive check\n throw new Error(`Unknown part type: ${p.type}`)\n }\n }\n}\n\nfunction estimateContent(content: AnyContent): TokenCount[] {\n if (typeof content === \"string\") return [{ tokens: fromText(content), type: \"text\" }]\n let tokens = 0\n const children: TokenCount[] = []\n for (const p of content) {\n const partStats = estimatePart(p)\n tokens += partStats.tokens\n children.push(partStats)\n }\n return children\n}\n\nfunction estimateMessage(m: Message): TokenCount {\n const type = m.role\n const children = estimateContent(m.content)\n const kind = m.role === \"system\" ? m.meta?.kind : undefined\n return {\n children,\n kind,\n tokens: sumTokens(children),\n type,\n }\n}\n\nfunction estimatePrompt(prompt: (string | Prompt<string>)[], tools?: Tool[]): TokenCount {\n const children: TokenCount[] = prompt.map((p) => ({\n kind: typeof p === \"string\" ? undefined : p.name,\n tokens: fromText(typeof p === \"string\" ? p : p.text),\n type: \"prompt\" as const,\n }))\n children.push(\n ...(tools?.map((t) => ({\n kind: t.name,\n tokens: fromText(t.name) + fromText(JSON.stringify(t.params ?? {})),\n type: \"tool-schema\" as const,\n })) ?? [])\n )\n return {\n children,\n tokens: sumTokens(children),\n type: \"system-prompt\" as const,\n }\n}\n\nfunction addCount(stat: TokenCount, parent: TokenStats, expand?: (c: TokenCount) => boolean): void {\n const keys = stat.kind ? [stat.type, stat.kind] : [stat.type]\n let child: TokenStats | undefined\n\n for (const key of keys) {\n parent.children ??= new Map()\n child = parent.children.get(key)\n if (!child) {\n child = { children: new Map(), count: 0, key, tokens: 0 }\n parent.children.set(key, child)\n }\n child.tokens += stat.tokens\n child.count++\n parent = child\n }\n\n if (stat.children && child && (!expand || expand(stat))) {\n for (const c of stat.children) addCount(c, child, expand)\n }\n}\n\nexport function tokenStats(\n msgs: readonly Message[],\n opts: {\n prompt?: (string | Prompt<string>)[]\n tools?: Tool[]\n expand?: (c: TokenCount) => boolean\n } = {}\n): TokenStats {\n const root: TokenStats = { children: new Map(), count: 0, key: \"TOTAL\", tokens: 0 }\n const counts: TokenCount[] = []\n if (opts.prompt) counts.push(estimatePrompt(opts.prompt, opts.tools))\n counts.push(...msgs.map(estimateMessage))\n for (const stats of counts) {\n root.tokens += stats.tokens\n root.count++\n addCount(stats, root, opts.expand)\n }\n return root\n}\n\n// ── stats ──────────────────────────────────────────────────────────────\n\nfunction fmtStats(s: TokenStats, indent = 0): string {\n const header = \" \".repeat(indent) + s.key\n return `${header.padEnd(28)} ${fmt(s.count).padStart(6)}x ${fmt(s.tokens).padStart(14)}`\n}\n\nexport function formatTokenStats(s: TokenStats, indent = 0): string {\n const ret: string[] = []\n const children = s.children\n ? [...s.children.values()].toSorted((a, b) => b.tokens - a.tokens)\n : []\n for (const c of children) {\n ret.push(fmtStats(c, indent))\n if (c.children?.size) ret.push(formatTokenStats(c, indent + 1))\n if (indent === 0) ret.push(\"─\".repeat(51))\n }\n if (indent === 0) {\n ret.unshift(\"─\".repeat(51))\n ret.push(fmtStats(s, indent))\n ret.push(\"─\".repeat(51))\n }\n return ret.join(\"\\n\")\n}\n\nfunction fmt(n: number): string {\n return n.toLocaleString(\"en-US\").replace(/,/g, \"_\")\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["builtin"],"sources":["../src/ctx.ts","../src/prompt/registry.ts","../src/tools/registry.ts","../src/commands.ts","../src/skills.ts","../src/compaction/utils.ts","../src/context/tokens.ts"],"sourcesContent":["import type { Model, ReasoningEffort, Tool } from \"@zaly/ai\"\nimport type { Agent } from \"./agent.ts\"\nimport type { AgentStatus } from \"./events.ts\"\nimport type { Masker } from \"./masker.ts\"\nimport type { Notifier, NotifyOptions } from \"./notify.ts\"\nimport type { PermissionManager, PermissionOptions } from \"./permissions/manager.ts\"\nimport type { Session } from \"./session/session.ts\"\nimport type { Skills } from \"./skills.ts\"\nimport type { Swarm } from \"./swarm.ts\"\nimport type { AgentOptions } from \"./types.ts\"\n\nimport { Emitter, isInstance, normPath } from \"@zaly/shared\"\nimport { LazyCache } from \"@zaly/shared/cache\"\n\ntype AgentContextOptions = Omit<AgentOptions, \"session\"> & { session: Session }\n\ntype Slots = {\n notifier: Notifier\n masker: Masker\n permissions: PermissionManager\n swarm: Swarm\n}\n\nexport type AgentContextEvents = {\n model: { model?: Model; prev?: Model }\n reasoning: { effort: ReasoningEffort; prev?: ReasoningEffort }\n session: { session: Session; prev?: Session }\n cwd: { cwd: string; prev?: string }\n skills: { skills?: Skills }\n}\n\nexport class AgentContext extends Emitter<AgentContextEvents> {\n #agent?: Agent\n #opts: AgentOptions\n #model?: Model\n #session: Session\n #reasoning: ReasoningEffort\n #cwd: string\n #cache = new LazyCache<Slots>()\n #tools: Tool[]\n #skills?: Skills\n #prompt: string[]\n\n constructor(opts: AgentContextOptions) {\n super()\n this.#opts = opts\n this.#cwd = normPath(opts.cwd)\n this.#reasoning = opts.request?.reasoning?.effort ?? \"medium\"\n this.#session = opts.session\n this.#model = opts.model\n this.#skills = opts.skills\n this.#prompt = opts.prompt ?? []\n\n this.#tools = opts.tools ?? []\n this.onEmitError = (error) => this.#opts.logger?.child(\"context\").error(error)\n\n this.on(\"model\", async ({ model }) => {\n if (!this.#session.started) return\n if (model) await this.session.update({ modelId: model.id })\n })\n .on(\"reasoning\", async ({ effort }) => {\n if (!this.#session.started) return\n await this.session.update({ reasoning: effort })\n })\n .on(\"cwd\", ({ cwd }) => {\n if (!this.#session.started) return\n this.#cache.forget(\"permissions\") // reset permissions to force reload with new cwd\n void this.session.update({ cwd })\n })\n }\n\n private async start() {\n const [_masker, notifier] = await Promise.all([this.masker(), this.notifier()])\n\n if (!this.model) throw new Error(\"model is required to start agent session\")\n\n if (notifier) notifier.attach(this.agent)\n\n await this.session.start({\n cwd: this.cwd,\n modelId: this.model.id,\n reasoning: this.reasoning,\n })\n // oxlint-disable-next-line no-await-in-loop\n for (const m of this.#opts.messages ?? []) await this.session.add(m)\n }\n\n attach(agent: Agent) {\n if (this.#agent === agent) return\n if (this.#agent) throw new Error(\"agent already attached to context\")\n if (agent.started) throw new Error(\"cannot attach agent that has already started\")\n this.#agent = agent\n agent.once(\"start\", () => this.start())\n }\n\n get messages() {\n return this.session.messages\n }\n\n get status(): AgentStatus | undefined {\n return this.#agent?.status\n }\n\n get opts() {\n return this.#opts\n }\n\n get cwd() {\n return this.#cwd\n }\n\n set cwd(c: string) {\n if (c === this.#cwd) return\n const prev = this.#cwd\n this.#cwd = normPath(c)\n void this.emit(\"cwd\", { cwd: this.#cwd, prev })\n }\n\n get signal() {\n return this.#agent?.signal\n }\n\n get agent() {\n if (!this.#agent) throw new Error(\"agent not attached to context\")\n return this.#agent\n }\n\n get reasoning() {\n return this.#reasoning\n }\n\n set reasoning(r: ReasoningEffort) {\n if (r === this.#reasoning) return\n const prev = this.#reasoning\n this.#reasoning = r\n void this.emit(\"reasoning\", { effort: r, prev })\n }\n\n get session(): Session {\n return this.#session\n }\n\n async useSession(s: Session): Promise<void> {\n const prev = this.#session\n this.#session = s\n const modelId = s.settings.modelId\n if (modelId && modelId !== this.model?.id) {\n if (this.#opts.loadModel) {\n this.model = await this.#opts.loadModel(modelId)\n } else {\n const { loadModel } = await import(\"@zaly/ai\")\n this.model = await loadModel(modelId)\n }\n }\n this.cwd = s.settings.cwd ?? this.cwd\n this.reasoning = s.settings.reasoning ?? this.reasoning\n await this.emit(\"session\", { prev, session: s })\n if (this.#agent?.started)\n await s.start({\n cwd: this.cwd,\n modelId: this.model?.id,\n reasoning: this.reasoning,\n })\n }\n\n get model() {\n return this.#model\n }\n\n set model(m: Model | undefined) {\n if (m === this.#model) return\n const prev = this.#model\n this.#model = m\n void this.emit(\"model\", { model: m, prev })\n }\n\n set tools(tools: Tool[]) {\n this.#tools = tools\n }\n\n get tools(): Tool[] {\n return [...this.#tools, ...(this.skills?.tool ? [this.skills.tool] : [])]\n }\n\n get prompt(): string[] {\n return this.#prompt\n }\n\n set prompt(p: string[]) {\n this.#prompt = p\n }\n\n async swarm() {\n return this.#cache.need(\n \"swarm\",\n async () => {\n const { Swarm } = await import(\"./swarm.ts\")\n return new Swarm()\n },\n this.#opts.swarm\n )\n }\n\n set skills(s: Skills | undefined) {\n if (s === this.#skills) return\n this.#skills = s\n void this.emit(\"skills\", { skills: s })\n }\n\n get skills(): Skills | undefined {\n return this.#skills\n }\n\n private async notifier(): Promise<Notifier | undefined> {\n return this.#cache.want(\n \"notifier\",\n async (opts?: NotifyOptions) => {\n const { Notifier } = await import(\"./notify.ts\")\n return new Notifier(opts)\n },\n this.#opts.notify\n )\n }\n\n async masker(): Promise<Masker | undefined> {\n return this.#cache.want(\n \"masker\",\n async (opts?: AgentOptions[\"mask\"]) => {\n const { Masker } = await import(\"./masker.ts\")\n return new Masker(this.agent, opts)\n },\n this.#opts.mask\n )\n }\n\n async permissions(): Promise<PermissionManager> {\n return this.#cache.need(\n \"permissions\",\n async (opts?: PermissionOptions) => {\n const { PermissionManager } = await import(\"./permissions/manager.ts\")\n return new PermissionManager({ ...opts, cwd: this.cwd })\n },\n this.#opts.permissions\n )\n }\n}\n\nasync function loadSession(spec?: AgentOptions[\"session\"]): Promise<Session> {\n if (isInstance<Session>(spec)) return spec\n else {\n const { Session } = await import(\"./session/session.ts\")\n return await Session.load({ ...spec })\n }\n}\n\nexport async function createAgentContext(opts: AgentOptions): Promise<AgentContext> {\n const session = await loadSession(opts.session)\n return new AgentContext({ ...opts, session })\n}\n\nexport async function createAgent(opts: AgentOptions): Promise<Agent> {\n const [{ Agent }, ctx] = await Promise.all([import(\"./agent.ts\"), createAgentContext(opts)])\n return new Agent(ctx)\n}\n","import type { Model } from \"@zaly/ai\"\n\nimport { BaseCollection } from \"@zaly/shared/collection\"\nimport { createRegistry } from \"@zaly/shared/registry\"\n\nexport interface PromptCtx {\n model: Model\n cwd: string\n}\n\nexport type PromptLoader = (ctx: PromptCtx) => Promise<string>\nexport type Prompt<T extends string | PromptLoader = string | PromptLoader> = {\n name: string\n text: T\n}\nexport type { PromptCollection }\n\nconst builtin = {\n \"AGENTS.md\": (ctx) => import(\"./markdown.ts\").then((m) => m.agentsMdPrompt(ctx)),\n \"MEMORY.md\": (ctx) => import(\"./markdown.ts\").then((m) => m.memoryMdPrompt(ctx)),\n agent: () => import(\"./agent.ts\").then((m) => m.agentPrompt),\n env: (ctx) => import(\"./env.ts\").then((m) => m.prompt(ctx)),\n model: (ctx) => import(\"./model.ts\").then((m) => m.prompt(ctx)),\n} as const satisfies Record<string, PromptLoader>\n\nexport type BuiltinPrompt = keyof typeof builtin\nexport type AnyPrompt = BuiltinPrompt | (string & {})\n\nexport const promptRegistry = createRegistry<PromptLoader>(\"prompt\").from(builtin)\n\nclass PromptCollection extends BaseCollection<AnyPrompt[], Prompt[], Prompt> {\n list(): Prompt[] {\n const ret = new Map<string, Prompt>()\n for (const k of promptRegistry.keys())\n ret.set(k, {\n name: k,\n text: (ctx) => promptRegistry.load(k, ctx),\n })\n for (const r of this.registered) ret.set(r.name, r)\n return [...ret.values()]\n }\n\n async render(ctx: PromptCtx & { prompts?: string[] }): Promise<Prompt<string>[]> {\n const all = new Map(this.list().map((p) => [p.name, p]))\n const ret = await Promise.all(\n (ctx.prompts ?? this.active).map(async (p) => {\n const def = all.get(p)\n if (!def) throw new Error(`Unknown prompt: ${p}`)\n return {\n name: def.name,\n text: typeof def.text === \"string\" ? def.text : await def.text(ctx),\n }\n })\n )\n return ret.map((p) => ({ name: p.name, text: p.text.trim() })).filter((p) => p.text.length > 0)\n }\n}\n\nexport async function promptCollection(): Promise<PromptCollection> {\n return new PromptCollection([\"agent\", \"env\", \"model\", \"AGENTS.md\", \"MEMORY.md\"])\n}\n","import type { Model, Tool } from \"@zaly/ai\"\nimport type { AnyKey } from \"@zaly/shared/registry\"\n\nimport { BaseCollection } from \"@zaly/shared/collection\"\nimport { createRegistry } from \"@zaly/shared/registry\"\n\n/** Per-load context handed to every tool factory. Lets factories\n * customise the tool — typically schema descriptions — based on the\n * agent's model and working directory. Tools that don't care about\n * init just ignore the arg. */\nexport interface ToolInit {\n model: Model\n cwd: string\n}\n\nexport type { ToolCollection }\nexport type ToolLoader = () => Promise<Tool>\nexport type BuiltinTool = keyof typeof builtin\nexport type AnyTool = AnyKey<BuiltinTool>\n\n// PERF: `import(\"./x\")` makes TS load the full module type.\n// The `as string` erases the dynamic import type while emitting the same JS.\nconst builtin = {\n agent_send: () => import(\"./swarm.ts\" as string).then((m) => m.agentSendTool as Tool),\n agent_spawn: () => import(\"./swarm.ts\" as string).then((m) => m.agentSpawnTool as Tool),\n bash: () => import(\"./bash.ts\" as string).then((m) => m.bashTool as Tool),\n edit: () => import(\"./edit.ts\" as string).then((m) => m.editTool as Tool),\n fetch: () => import(\"./fetch.ts\" as string).then((m) => m.fetchTool as Tool),\n find: () => import(\"./find.ts\" as string).then((m) => m.findTool as Tool),\n grep: () => import(\"./grep.ts\" as string).then((m) => m.grepTool as Tool),\n read: () => import(\"./read.ts\" as string).then((m) => m.readTool as Tool),\n search: () => import(\"./search.ts\" as string).then((m) => m.searchTool as Tool),\n subagent: () => import(\"./subagent.ts\" as string).then((m) => m.subagentTool as Tool),\n task_list: () => import(\"./tasks.ts\" as string).then((m) => m.taskListTool as Tool),\n task_poll: () => import(\"./tasks.ts\" as string).then((m) => m.taskPollTool as Tool),\n task_stop: () => import(\"./tasks.ts\" as string).then((m) => m.taskStopTool as Tool),\n wakeup: () => import(\"./wakeup.ts\" as string).then((m) => m.wakeupTool as Tool),\n write: () => import(\"./write.ts\" as string).then((m) => m.writeTool as Tool),\n} as const satisfies Record<string, ToolLoader>\n\nexport const toolRegistry = createRegistry<ToolLoader>(\"tool\").from(builtin)\n\nclass ToolCollection extends BaseCollection<AnyTool[], AnyTool[], Tool> {\n list(): AnyTool[] {\n const ret = new Set(toolRegistry.keys())\n for (const r of this.registered) ret.add(r.name)\n return [...ret]\n }\n\n async load(tools?: AnyTool[]): Promise<Tool[]> {\n const load = async (t: string) =>\n this.registered.findLast((r) => r.name === t) ?? toolRegistry.load(t)\n return await Promise.all((tools ?? this.active).map((t) => load(t)))\n }\n}\n\nexport async function toolCollection(): Promise<ToolCollection> {\n return new ToolCollection([])\n}\n","// oxlint-disable no-await-in-loop\nimport type { ArgsOpts, ArgsResult } from \"@zaly/shared/args\"\nimport type { Logger } from \"@zaly/shared/logger\"\n\nimport { normPath, prettyPath, safeReadFile, toError } from \"@zaly/shared\"\nimport { basename } from \"pathe\"\n\nexport type Command = {\n name: string\n body: string\n path: string\n description?: string\n args: ArgsOpts\n}\n\nexport interface CommandsOptions {\n /** cmd.md paths, sorted from highest to lowest precedence. */\n paths?: string[]\n logger?: Logger\n /** Bash execution of commands:\n * - `true`: Use the system default bash shell.\n * - `false`: Disable bash execution.\n * - `string[]`: Use a custom bash shell with the provided arguments. */\n bash?: boolean | string[]\n /** When `false`, template expressions are disabled and only simple variable interpolation is allowed. */\n expr?: boolean\n}\n\ntype Token =\n | { type: \"text\"; value: string }\n | { type: \"block\"; value: string; lang?: string; body: string }\n | { type: \"script\"; value: string }\n\nconst BASH_RE = /^!`(.*)`$/\n\nexport class Commands {\n #opts: CommandsOptions\n readonly catalog = new Map<string, Command>()\n #logger?: Logger\n\n constructor(opts: CommandsOptions = {}) {\n this.#opts = opts\n this.#logger = opts.logger\n }\n\n async load(): Promise<this> {\n const paths = this.#opts.paths ?? []\n await Promise.all(paths.map(async (path) => await this.add(path)))\n return this\n }\n\n async add(path: string): Promise<void> {\n path = normPath(path)\n const name = basename(path, \".md\")\n const { parseFrontmatter } = await import(\"@zaly/shared/yaml\")\n try {\n const content = await safeReadFile(path)\n if (!content) throw new Error(`Failed to read command file at ${path}`)\n\n const { fm, body } = await parseFrontmatter(content)\n\n const args: ArgsOpts = {}\n const fmArgs = (fm.args ?? {}) as Record<string, Partial<ArgsOpts[string]>>\n\n for (const [o, opt] of Object.entries(fmArgs)) {\n // Set default values for boolean\n if (opt.type === \"boolean\") opt.default ??= false\n // Make string options required if no default is provided\n else if (opt.type === \"string\") opt.required ??= opt.default === undefined\n else {\n this.#logger?.warn(\n `Unsupported argument type for \\`--${o}\\` in command **${name}** at \\`${prettyPath(path)}\\`\\nOnly \\`string\\` and \\`boolean\\` are supported. Skipping this argument.`\n )\n continue\n }\n args[o] = opt as ArgsOpts[string]\n }\n\n const cmd: Command = {\n ...fm,\n args,\n body,\n name,\n path,\n }\n if (this.catalog.has(name)) return\n this.catalog.set(name, cmd)\n } catch (error) {\n this.#logger?.error(`Error loading **command** from ${path}: ${(error as Error).message}`)\n }\n }\n\n get(name: string): Command | undefined {\n return this.catalog.get(name)\n }\n\n async format(input: string | ArgsResult, cmd: Command): Promise<string> {\n let args: ArgsResult\n if (typeof input === \"string\") {\n const { argsParse } = await import(\"@zaly/shared/args\")\n args = await argsParse(input, cmd.args)\n } else args = input\n\n const { createTemplate } = await import(\"@zaly/shared/template\")\n const tpl = createTemplate(cmd.body, {\n expr: this.#opts.expr,\n format: (value) => {\n if (value === args._) return args._.join(\" \")\n },\n name: cmd.name,\n })\n\n const vars = {\n ...args,\n args: args._,\n raw: args.$,\n }\n\n const rendered = tpl(vars)\n if (this.#opts.bash === false) return rendered\n const bash = Array.isArray(this.#opts.bash) ? this.#opts.bash : [\"bash\"]\n\n const lines = rendered.split(\"\\n\")\n\n const ret: string[] = []\n for (const token of this.#tokenize(lines, cmd)) {\n let text: string | undefined\n if (token.type === \"script\") text = await exec(token.value, { bash })\n else if (token.type === \"block\" && /^bash\\s*!$/.test(token.lang ?? \"\"))\n text = await exec(token.body, { bash })\n ret.push(text ?? token.value)\n }\n\n return ret.join(\"\\n\")\n }\n\n *#tokenize(lines: string[], cmd: Command): Generator<Token> {\n let block: string[] = []\n let marker: RegExp | undefined = undefined\n let lang: string | undefined = undefined\n for (const line of lines) {\n const m = line.match(/^\\s*(`{3,}|~{3,})(.*)$/)\n const script = line.match(BASH_RE)\n if (marker && line.match(marker)) {\n block.push(line)\n yield {\n body: block.slice(1, -1).join(\"\\n\"),\n lang,\n type: \"block\",\n value: block.join(\"\\n\"),\n }\n marker = undefined // end of code block\n } else if (!marker && m) {\n marker = new RegExp(`^\\\\s*${m[1]}\\\\s*$`) // start of code block\n lang = m[2].trim() || undefined\n block = [line]\n } else if (marker) block.push(line)\n else if (script) yield { type: \"script\", value: script[1] }\n else yield { type: \"text\", value: line }\n }\n if (marker) throw new Error(`Unclosed code block in command **${cmd.name}**`)\n }\n}\n\nasync function exec(script: string, opts: { bash?: string[] } = {}): Promise<string | undefined> {\n if (!script.trim()) return\n const { spawnCmd } = await import(\"@zaly/shared/process\")\n let result: string\n try {\n const r = await spawnCmd(script, { bash: opts.bash ?? true, throw: true })\n result = r?.trim() ?? \"\"\n } catch (error) {\n result = toError(error).message.trim()\n }\n const md: string[] = []\n if (script.includes(\"\\n\")) {\n md.push(\"```bash\")\n md.push(script)\n md.push(\"```\")\n md.push(\"\")\n md.push(\"```shellsession\")\n md.push(result)\n md.push(\"```\")\n } else {\n md.push(\"```shellsession\")\n md.push(`$ ${script}`)\n md.push(result)\n md.push(\"```\")\n }\n md.push(\"\")\n return md.join(\"\\n\")\n}\n","import type { MetaPart, TextPart, Tool, ToolContext } from \"@zaly/ai\"\nimport type { Agent } from \"./agent.ts\"\n\nimport { AiError, defineTool, extractToolResults } from \"@zaly/ai\"\nimport { safeStatAsync } from \"@zaly/shared\"\nimport { glob } from \"@zaly/shared/glob\"\nimport { readFile } from \"node:fs/promises\"\nimport { isDeepStrictEqual } from \"node:util\"\nimport { dirname } from \"pathe\"\nimport { Type } from \"typebox\"\n\nexport type SkillMeta = {\n name: string\n mtime: number\n desc: string\n path: string\n unchanged?: boolean\n}\n\nexport type SkillTool = Tool<{ name: string }, unknown, SkillMeta>\n\n/**\n * Agent Skills support — discovery, catalog, and the activation tool.\n *\n * Locations scanned (subdirectories containing `SKILL.md`):\n * - `${cwd}/.agent/skills/` — project\n * - `${homedir()}/.agent/skills/` — user (cross-project)\n *\n * Project shadows user on name collision.\n *\n * Progressive disclosure (per the Agent Skills spec):\n * - Catalog (name + description) lives in the tool's schema /\n * description. The model sees this from the start.\n * - Body of the SKILL.md is loaded only when the tool is invoked.\n * - References (files under the skill's directory) are *enumerated*\n * at activation, not read — the model reads them via the `read`\n * tool when its instructions call for it.\n *\n * Lifecycle:\n * - Construct with `new Skills({ cwd })` — no I/O yet.\n * - Call `await skills.load()` to populate the catalog. Idempotent;\n * re-call to pick up newly installed skills (`/reload-plugins`).\n * - `skills.tool` returns the current activation tool, or undefined\n * when the catalog is empty. Lazily built; invalidated on reload.\n */\n\nexport interface SkillEntry {\n name: string\n desc: string\n mtime: number\n body: string\n /** Absolute path to the SKILL.md file. */\n path: string\n /** Absolute path to the skill's base directory (parent of SKILL.md).\n * Used as the workspace anchor for permissions and as the root for\n * resolving references. */\n dir: string\n}\n\nexport interface SkillsOptions {\n /** SKILL.md paths, sorted from highest to lowest precedence. */\n paths?: string[]\n}\n\nexport class Skills {\n readonly catalog = new Map<string, SkillEntry>()\n #tool?: Tool\n #opts: SkillsOptions\n\n protected constructor(opts: SkillsOptions = {}) {\n this.#opts = opts\n }\n\n static async load(opts?: SkillsOptions): Promise<Skills> {\n return new Skills(opts).reload()\n }\n\n /** (Re)scan project + user `.agent/skills/` directories. Wipes the\n * current catalog and the cached tool, then repopulates. Safe to\n * call mid-session — agent uses `this.tool` per-step so the next\n * request after reload picks up the change. */\n async reload(): Promise<this> {\n this.catalog.clear()\n this.#tool = undefined\n const paths = this.#opts.paths ?? []\n // Load all skills in parallel, but add them to the catalog in order so\n // that the first one wins on name collision.\n const skills = await Promise.all(paths.map(async (path) => await this.#load(path)))\n for (const skill of skills)\n if (skill && !this.catalog.has(skill.name)) this.catalog.set(skill.name, skill)\n return this\n }\n\n async #load(path: string): Promise<SkillEntry | undefined> {\n if (!path.endsWith(\"SKILL.md\")) return\n const dir = dirname(path)\n try {\n const { meta, mtime, body } = await readSkill(path)\n if (!meta.name || !meta.description || this.catalog.has(meta.name)) return\n return {\n body,\n desc: meta.description,\n dir,\n mtime,\n name: meta.name,\n path,\n }\n } catch {}\n }\n\n async #update(entry: SkillEntry): Promise<SkillEntry> {\n const updated = (await this.#load(entry.path)) ?? entry\n if (isDeepStrictEqual(updated, entry)) return entry\n this.catalog.set(entry.name, updated)\n this.#tool = undefined\n return updated\n }\n\n /** Skill base directories — pass each to `agent.permissions.addWorkspace`\n * so the model can read bundled `references/` files without tripping\n * permission asks. */\n get dirs(): readonly string[] {\n return [...this.catalog.values()].map((s) => s.dir)\n }\n\n /** The activation tool. Returns `undefined` when the catalog is empty\n * (the agent then omits skills entirely from the model's tool list,\n * per the spec). Lazily built on first access; invalidated by\n * `load()`. */\n get tool(): Tool | undefined {\n if (this.catalog.size === 0) return undefined\n return (this.#tool ??= this.#buildTool())\n }\n\n #buildTool(): SkillTool {\n const names = [...this.catalog.keys()]\n // oxlint-disable-next-line sort-keys -- semantic field order: name, desc, params, call\n return defineTool({\n name: \"skill\",\n desc: this.#buildCatalogDesc(),\n parallel: true,\n params: Type.Object({\n name: Type.Union(\n names.map((n) => Type.Literal(n)),\n {\n description:\n \"Name of the skill to activate. The full SKILL.md \" +\n \"instructions are loaded as the tool result; bundled \" +\n \"files in the skill's directory are listed but not read \" +\n \"— fetch them via the `read` tool when the skill's \" +\n \"instructions require them.\",\n }\n ),\n }),\n call: (args, ctx: ToolContext<SkillMeta>) => this.#call(args.name as string, ctx),\n })\n }\n\n async #call(requested: string, ctx: ToolContext<SkillMeta>): Promise<(MetaPart | TextPart)[]> {\n let skill = this.catalog.get(requested)\n if (!skill) {\n const available = [...this.catalog.keys()]\n throw new AiError({\n code: \"UNKNOWN_SKILL\",\n data: { available, name: requested },\n message: `no skill named \"${requested}\". Available: ${available.join(\", \")}`,\n })\n }\n\n // Refresh the skill entry so we have an up-to-date mtime for staleness checks in `isActivated`.\n skill = await this.#update(skill)\n\n ctx.meta = { desc: skill.desc, mtime: skill.mtime, name: skill.name, path: skill.path }\n const activated = await this.isActivated(skill, ctx)\n if (activated) {\n // Already activated and fresh — no need to reload or update the tool result.\n ctx.meta.unchanged = true\n return [\n {\n content: `skill \"${skill.name}\" unchanged since last read: ${skill.path}`,\n tag: \"unchanged\",\n type: \"meta\",\n },\n ]\n }\n\n const references = await listReferences(skill.dir)\n return [\n {\n data: { dir: skill.dir, name: skill.name, references },\n tag: \"skill\",\n type: \"meta\",\n },\n { text: skill.body, type: \"text\" },\n ]\n }\n\n async isActivated(skill: SkillEntry, ctx: ToolContext<SkillMeta>): Promise<boolean> {\n const messages = ctx.messages ?? []\n for (const { m, $p, p } of extractToolResults<SkillMeta, \"skill\">(messages, [\"skill\"])) {\n const id = m.id\n if (!id || !p.meta || ctx.isMasked?.(id, $p)) continue\n if (p.meta.unchanged) continue\n if (p.meta.name === skill.name) return p.meta.mtime === skill.mtime\n }\n return false\n }\n\n async activate(name: string, agent: Agent) {\n const ret = await agent.useTool<SkillTool>(\n \"skill\",\n { name },\n `Skill \"${name}\" was activated by the user.`\n )\n if (ret.result.meta?.unchanged) return\n return ret\n }\n\n #buildCatalogDesc(): string {\n const header =\n \"Activate one of the available skills below. The skill's full \" +\n \"SKILL.md instructions are loaded as the tool result; the model \" +\n \"should follow them directly. Bundled files are listed in the \" +\n \"result but not eagerly read — fetch the ones the skill's \" +\n \"instructions reference via the `read` tool.\"\n const lines = [...this.catalog.values()].map((s) => `- ${s.name}: ${s.desc}`)\n return `${header}\\n\\nAvailable skills:\\n${lines.join(\"\\n\")}`\n }\n}\n\n// ── Discovery ──────────────────────────────────────────────────────────\n\nasync function readSkill(\n path: string\n): Promise<{ meta: { name?: string; description?: string }; body: string; mtime: number }> {\n const s = await safeStatAsync(path)\n if (!s?.isFile()) throw new Error(`Skill file not found at ${path}`)\n const raw = await readFile(path, \"utf8\")\n const { parseFrontmatter } = await import(\"@zaly/shared/yaml\")\n const { fm, body } = await parseFrontmatter(raw)\n return { body, meta: fm, mtime: s.mtimeMs }\n}\n\nasync function listReferences(dir: string): Promise<string[]> {\n const out: string[] = []\n for await (const rel of glob(\"**\", { cwd: dir, depth: 4, type: \"file\" })) {\n if (rel === \"SKILL.md\" || rel.endsWith(\"/SKILL.md\")) continue\n out.push(rel)\n if (out.length >= 200) break\n }\n return out.toSorted()\n}\n","import type { AnyPart, Message } from \"@zaly/ai\"\nimport type { BashTool } from \"../tools/bash.ts\"\nimport type { EditToolMeta } from \"../tools/edit.ts\"\nimport type { ReadToolMeta } from \"../tools/read.ts\"\nimport type { WriteToolMeta } from \"../tools/write.ts\"\n\nimport {\n attachmentToMeta,\n ContentTransform,\n errorToMeta,\n extractToolCalls,\n extractToolResults,\n metaToText,\n safeParseToolParams,\n sanitizeText,\n stringifyContent,\n toXml,\n} from \"@zaly/ai\"\nimport { safeStringify, formatDuration } from \"@zaly/shared\"\nimport { parseBash } from \"../utils/bash/parser.ts\"\nimport { TOOLS } from \"../utils/bash/tools.ts\"\n\nexport type ToolStat = {\n count: number\n score: number\n /** Turns ago at the most recent occurrence (0 = current/last turn).\n * Use with `lastTs` to give the agent both an \"in-experience\" recency\n * signal and a wall-clock staleness signal. */\n lastTurn: number\n /** Wall-clock ms timestamp of the most recent occurrence. `0` if no\n * occurrence carried a timestamp. */\n lastTs: number\n}\n\n/** Per-file activity tally produced by `extractFiles`. Each entry is a\n * path that the agent touched, with per-kind counts and a frecency\n * `score` that downweights older touches and weights writes/edits\n * more than reads. Sort the top-N by `score` to surface the active\n * working set; `reads` / `writes` / `edits` give the per-kind detail\n * for display. */\nexport type FileUsage = ToolStat & {\n path: string\n reads: number\n writes: number\n edits: number\n}\n\n/** Per-command activity tally produced by `extractBashCommands`. `count`\n * is raw invocations; `score` is the same number but exp-decay-weighted\n * by turn distance from the end of the conversation, so recently-used\n * commands rank above old hot ones. */\nexport type BashUsage = ToolStat & {\n command: string\n}\n\n// ── Frecency tuning ───────────────────────────────────────────────────\n// Half-life measured in user turns, not wall time — what matters for\n// compaction context is what's been hot in the agent's *recent\n// experience*, regardless of how long the user spent between turns.\n// 60 turns is a middle ground: actively-iterated work stays at the top\n// while recurring workflow patterns (git stash, test-runner flags) still\n// clear the minScore filter instead of getting buried under one-shot\n// noise from earlier in the session.\nconst HALF_LIFE_TURN = 60\nconst LAMBDA = Math.LN2 / HALF_LIFE_TURN\n// Per-kind weights — writes are the strongest intent signal (full-file\n// authorship), edits the iteration signal, reads the informational one.\nconst KIND_WEIGHTS = { edit: 2, read: 1, write: 3 } as const\n\nexport type ToolStatOptions = {\n minCount?: number\n minScore?: number\n limit?: number\n sort?: \"score\" | \"count\" | \"key\"\n}\n\nconst sorters = {\n count: (a: [string, ToolStat], b: [string, ToolStat]) => b[1].count - a[1].count,\n key: (a: [string, ToolStat], b: [string, ToolStat]) => a[0].localeCompare(b[0]),\n score: (a: [string, ToolStat], b: [string, ToolStat]) => b[1].score - a[1].score,\n} as const satisfies Record<string, (a: [string, ToolStat], b: [string, ToolStat]) => number>\n\nfunction turnInfo(messages: readonly Message[]) {\n const weights: number[] = []\n const turns: number[] = []\n let turn = 0\n for (let i = messages.length - 1; i >= 0; i--) {\n const m = messages[i]\n if (m.role === \"assistant\") turn++\n weights[i] = Math.exp(-LAMBDA * turn)\n turns[i] = turn\n }\n return { turns, weights }\n}\n\nfunction toolStats<T extends ToolStat = ToolStat>(\n map: Map<string, T>,\n opts: ToolStatOptions = {}\n): T[] {\n const limit = opts.limit ?? 50\n const sorter = sorters[opts.sort ?? \"score\"]\n const sorted = [...map.entries()].toSorted(sorter).map(([, stat]) => stat)\n return sorted\n .filter((stat) => stat.count >= (opts.minCount ?? 1))\n .filter((stat) => stat.score >= (opts.minScore ?? 0))\n .slice(0, limit)\n}\n\n/** Walk assistant tool-calls for `read` / `write` / `edit` and tally\n * per-path activity with frecency-weighted scoring. Returns the top-N\n * by score. Each entry carries the per-kind breakdown so the\n * summarizer can describe the *kind* of activity (\"heavily edited\",\n * \"read once, then written\", \"iteratively refined\").\n *\n * Paths are canonicalized via the cwd recorded on each call's session\n * node — relative paths are resolved against the cwd that was active\n * when the call ran, and `~` is expanded. Calls under different cwds\n * that target the same file thus collapse into one bucket. */\nexport function extractFileUsage(\n messages: readonly Message[],\n opts: ToolStatOptions = {}\n): FileUsage[] {\n opts = { minScore: 0.5, ...opts }\n const map = new Map<string, FileUsage>()\n const { turns, weights } = turnInfo(messages)\n\n for (const { m, p, $m: idx } of extractToolResults<EditToolMeta | ReadToolMeta | WriteToolMeta>(\n messages,\n [\"read\", \"write\", \"edit\"]\n )) {\n const path = p.meta?.path\n if (!path) continue\n const entry = map.get(path) ?? {\n count: 0,\n edits: 0,\n lastTs: 0,\n lastTurn: Infinity,\n path,\n reads: 0,\n score: 0,\n writes: 0,\n }\n if (p.name === \"read\") entry.reads++\n else if (p.name === \"write\") entry.writes++\n else entry.edits++\n entry.count++\n entry.score += weights[idx] * KIND_WEIGHTS[p.name as keyof typeof KIND_WEIGHTS]\n if (turns[idx] < entry.lastTurn) entry.lastTurn = turns[idx]\n if (m.ts && m.ts > entry.lastTs) entry.lastTs = m.ts\n map.set(path, entry)\n }\n return toolStats(map, opts)\n}\n\n/** Walk assistant tool-calls for `bash`, parse each command line, and\n * tally per-command frecency. Plumbing commands (cat, head, grep, awk,\n * sed, …) are filtered out — they dominate raw counts but carry no\n * operational signal a summarizer would describe. Returns the top-N\n * by frecency `score`. */\nexport function extractBashUsage(\n messages: readonly Message[],\n opts: ToolStatOptions = {}\n): BashUsage[] {\n opts = { minCount: 2, minScore: 0.5, ...opts } // bash commands are noisier, so default to minCount=2\n const { turns, weights } = turnInfo(messages)\n const map = new Map<string, BashUsage>()\n for (const { m, p, $m: idx } of extractToolCalls(messages, [\"bash\"])) {\n const params = safeParseToolParams<BashTool>(p.params)\n const cmd = params?.command\n if (!cmd) continue\n const result = parseBash(cmd)\n if (!result.ok) continue\n const w = weights[idx] ?? 0\n const t = turns[idx] ?? Infinity\n for (const seg of result.segments) {\n if (TOOLS[seg.cmd]) continue\n const key = `${seg.cmd} ${seg.args.join(\" \")}`\n if (key.includes(\"\\n\")) continue\n const entry = map.get(key) ?? {\n command: key,\n count: 0,\n lastTs: 0,\n lastTurn: Infinity,\n score: 0,\n }\n entry.count++\n entry.score += w\n if (t < entry.lastTurn) entry.lastTurn = t\n if (m.ts && m.ts > entry.lastTs) entry.lastTs = m.ts\n map.set(key, entry)\n }\n }\n return toolStats(map, opts)\n}\n\n// ── Transcript ────────────────────────────────────────────────────────\n\nfunction truncate(text: string, len: number): string {\n return text.length <= len ? text : `${text.slice(0, len)}…`\n}\n\n/** Flatten a list of messages into a single tagged transcript. */\nexport function extractConversation(\n messages: readonly Message[],\n opts: { maxToolResultLen?: number } = {}\n): string {\n const transform = ContentTransform.create<AnyPart>()\n .drop(\"reasoning\")\n .map(\"tool-call\", (part) => ({\n content: [\n {\n text: `${part.name}(${safeStringify(part.params)})`,\n type: \"text\" as const,\n },\n ],\n tag: \"tool-call\",\n type: \"meta\",\n }))\n .map(\"tool-result\", (part) => {\n const text = truncate(stringifyContent(part.content), opts.maxToolResultLen ?? 2000)\n return {\n content: text === \"\" ? undefined : [{ text, type: \"text\" as const }],\n data: { tool: part.name, ...(part.isError ? { error: true } : {}) },\n tag: \"tool-result\",\n type: \"meta\" as const,\n }\n })\n .pipe(attachmentToMeta(\"image\", \"pdf\", \"audio\", \"video\"))\n .pipe(errorToMeta())\n .pipe(metaToText())\n .pipe(sanitizeText())\n\n const blocks: string[] = []\n let role: string | undefined = undefined\n for (const m of messages) {\n const parts =\n typeof m.content === \"string\" ? [{ text: m.content, type: \"text\" as const }] : m.content\n const transformed = transform.runSync(parts)\n const text = stringifyContent(transformed).trim()\n if (!text) continue\n let currentRole = m.role.charAt(0).toUpperCase() + m.role.slice(1) // capitalize\n currentRole = currentRole === \"Tool\" ? \"Assistant\" : currentRole // re-label \"Tool\" as \"Assistant\" for summarization purposes\n blocks.push(role === currentRole ? text : `\\n[${currentRole}]: ${text}`)\n role = currentRole\n }\n const text = blocks.join(\"\\n\").replace(/^\\s+$/gm, \"\").trim()\n return toXml(text, \"conversation\", { indent: false })\n}\n\n// ── Tail selection ────────────────────────────────────────────────────\n\nexport function messageTail(\n messages: readonly Message[],\n opts: { keepTokens?: number }\n): Message[] {\n const maxTokens = opts.keepTokens ?? 20_000\n messages = messages.toReversed()\n const tail: Message[] = []\n const queue: Message[] = []\n\n let used = 0\n let last: number | undefined = undefined\n\n for (const m of messages) {\n queue.unshift(m)\n const usage = m.role === \"assistant\" ? m.meta?.usage : undefined\n if (!usage) continue\n const current = usage.input + usage.output + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0)\n // clamp for masker and similar events that might shrink the context size\n const delta = Math.max(0, (last ?? current) - current)\n if (used + delta > maxTokens) break\n used += delta\n last = current\n tail.unshift(...queue.splice(0))\n }\n return tail\n}\n\n// -- Formatting helpers -────────────────────────────────────────────────────────\n\nconst lastCol = (lastTurn: number, lastTs: number): string => {\n const turn = lastTurn === Infinity ? \"?\" : `${lastTurn}t`\n const wall = lastTs > 0 ? formatDuration(lastTs) : \"?\"\n return `${turn} / ${wall}`\n}\n\nexport function formatBashUsage(commands: BashUsage[]): string {\n if (commands.length === 0) return \"(no bash commands found)\"\n const lines: string[] = []\n lines.push(\"=== Top bash commands (sorted by frecency) ===\")\n lines.push(` ${\"score\".padStart(7)} ${\"count\".padStart(5)} ${\"last\".padEnd(14)} command`)\n for (const c of commands) {\n lines.push(\n ` ${c.score.toFixed(2).padStart(7)} ${String(c.count).padStart(5)} ${lastCol(c.lastTurn, c.lastTs).padEnd(14)} ${c.command}`\n )\n }\n const text = lines.join(\"\\n\")\n return toXml(text, \"bash-commands\", { indent: false })\n}\n\nexport function formatFileUsage(files: FileUsage[]): string {\n if (files.length === 0) return \"(no file ops found)\"\n const lines: string[] = []\n lines.push(\"=== Top files touched (sorted by frecency) ===\")\n lines.push(\n ` ${\"score\".padStart(7)} ${\"total\".padStart(5)} r/w/e ${\"last\".padEnd(14)} path`\n )\n for (const f of files) {\n const rwe = `${f.reads}/${f.writes}/${f.edits}`.padEnd(12)\n lines.push(\n ` ${f.score.toFixed(2).padStart(7)} ${String(f.count).padStart(5)} ${rwe} ${lastCol(f.lastTurn, f.lastTs).padEnd(14)} ${f.path}`\n )\n }\n const text = lines.join(\"\\n\")\n return toXml(text, \"files\", { indent: false })\n}\n","import type { AnyContent, AnyPart, AnyType, Attachment, Message, Tool } from \"@zaly/ai\"\nimport type { Prompt } from \"../prompt/registry.ts\"\n\nimport { stringifyContent } from \"@zaly/ai\"\n\nexport type TokenCount = {\n type: AnyType | Message[\"role\"] | \"prompt\" | \"tool-schema\" | \"system-prompt\"\n kind?: string\n tokens: number\n children?: TokenCount[]\n}\n\nexport type TokenStats = {\n key: string\n tokens: number\n count: number\n children?: Map<string, TokenStats>\n}\n\n// ── token estimation ───────────────────────────────────────────────────\n\nconst CHARS_PER_TOKEN = 4\n\nconst ATTACHMENT_TOKENS: Record<Attachment[\"type\"], number> = {\n audio: 3000,\n image: 1500,\n pdf: 8000,\n video: 5000,\n}\n\nfunction fromText(text: string): number {\n return Math.ceil(text.length / CHARS_PER_TOKEN)\n}\n\nfunction sumTokens(stats: TokenCount[]): number {\n return stats.reduce((sum, s) => sum + s.tokens, 0)\n}\n\nexport function estimatePart(p: AnyPart): TokenCount {\n switch (p.type) {\n case \"reasoning\":\n case \"text\": {\n return { tokens: fromText(p.text), type: p.type }\n }\n case \"tool-call\": {\n return {\n kind: p.name,\n tokens: fromText(p.name) + fromText(JSON.stringify(p.params ?? {})),\n type: p.type,\n }\n }\n case \"tool-result\": {\n const children = estimateContent(p.content)\n return {\n children,\n kind: p.name,\n tokens: fromText(p.name) + sumTokens(children),\n type: p.type,\n }\n }\n case \"image\":\n case \"pdf\":\n case \"audio\":\n case \"video\": {\n return { tokens: ATTACHMENT_TOKENS[p.type], type: p.type }\n }\n case \"meta\": {\n const children = p.content ? estimateContent(p.content) : undefined\n return {\n children,\n kind: p.tag,\n tokens: fromText(stringifyContent(p)),\n type: p.type,\n }\n }\n case \"error\": {\n return { tokens: fromText(stringifyContent(p)), type: p.type }\n }\n default: {\n // @ts-expect-error: exhaustive check\n throw new Error(`Unknown part type: ${p.type}`)\n }\n }\n}\n\nfunction estimateContent(content: AnyContent): TokenCount[] {\n if (typeof content === \"string\") return [{ tokens: fromText(content), type: \"text\" }]\n let tokens = 0\n const children: TokenCount[] = []\n for (const p of content) {\n const partStats = estimatePart(p)\n tokens += partStats.tokens\n children.push(partStats)\n }\n return children\n}\n\nfunction estimateMessage(m: Message): TokenCount {\n const type = m.role\n const children = estimateContent(m.content)\n const kind = m.role === \"system\" ? m.meta?.kind : undefined\n return {\n children,\n kind,\n tokens: sumTokens(children),\n type,\n }\n}\n\nfunction estimatePrompt(prompt: (string | Prompt<string>)[], tools?: Tool[]): TokenCount {\n const children: TokenCount[] = prompt.map((p) => ({\n kind: typeof p === \"string\" ? undefined : p.name,\n tokens: fromText(typeof p === \"string\" ? p : p.text),\n type: \"prompt\" as const,\n }))\n children.push(\n ...(tools?.map((t) => ({\n kind: t.name,\n tokens: fromText(t.name) + fromText(JSON.stringify(t.params ?? {})),\n type: \"tool-schema\" as const,\n })) ?? [])\n )\n return {\n children,\n tokens: sumTokens(children),\n type: \"system-prompt\" as const,\n }\n}\n\nfunction addCount(stat: TokenCount, parent: TokenStats, expand?: (c: TokenCount) => boolean): void {\n const keys = stat.kind ? [stat.type, stat.kind] : [stat.type]\n let child: TokenStats | undefined\n\n for (const key of keys) {\n parent.children ??= new Map()\n child = parent.children.get(key)\n if (!child) {\n child = { children: new Map(), count: 0, key, tokens: 0 }\n parent.children.set(key, child)\n }\n child.tokens += stat.tokens\n child.count++\n parent = child\n }\n\n if (stat.children && child && (!expand || expand(stat))) {\n for (const c of stat.children) addCount(c, child, expand)\n }\n}\n\nexport function tokenStats(\n msgs: readonly Message[],\n opts: {\n prompt?: (string | Prompt<string>)[]\n tools?: Tool[]\n expand?: (c: TokenCount) => boolean\n } = {}\n): TokenStats {\n const root: TokenStats = { children: new Map(), count: 0, key: \"TOTAL\", tokens: 0 }\n const counts: TokenCount[] = []\n if (opts.prompt) counts.push(estimatePrompt(opts.prompt, opts.tools))\n counts.push(...msgs.map(estimateMessage))\n for (const stats of counts) {\n root.tokens += stats.tokens\n root.count++\n addCount(stats, root, opts.expand)\n }\n return root\n}\n\n// ── stats ──────────────────────────────────────────────────────────────\n\nfunction fmtStats(s: TokenStats, indent = 0): string {\n const header = \" \".repeat(indent) + s.key\n return `${header.padEnd(28)} ${fmt(s.count).padStart(6)}x ${fmt(s.tokens).padStart(14)}`\n}\n\nexport function formatTokenStats(s: TokenStats, indent = 0): string {\n const ret: string[] = []\n const children = s.children\n ? [...s.children.values()].toSorted((a, b) => b.tokens - a.tokens)\n : []\n for (const c of children) {\n ret.push(fmtStats(c, indent))\n if (c.children?.size) ret.push(formatTokenStats(c, indent + 1))\n if (indent === 0) ret.push(\"─\".repeat(51))\n }\n if (indent === 0) {\n ret.unshift(\"─\".repeat(51))\n ret.push(fmtStats(s, indent))\n ret.push(\"─\".repeat(51))\n }\n return ret.join(\"\\n\")\n}\n\nfunction fmt(n: number): string {\n return n.toLocaleString(\"en-US\").replace(/,/g, \"_\")\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { normPath, safeStringify, withLock } from "@zaly/shared";
|
|
2
2
|
import { appendFile, open } from "node:fs/promises";
|
|
3
3
|
//#region src/session/jsonl.ts
|
|
4
|
-
const CHUNK_SIZE =
|
|
4
|
+
const CHUNK_SIZE = 4194304;
|
|
5
5
|
/**
|
|
6
6
|
* JSONL-backed `SessionStore` — append-only file, one record per line.
|
|
7
7
|
*
|
|
@@ -131,4 +131,4 @@ var JsonlReader = class {
|
|
|
131
131
|
//#endregion
|
|
132
132
|
export { JsonlStore as n, JsonlReader as t };
|
|
133
133
|
|
|
134
|
-
//# sourceMappingURL=jsonl-
|
|
134
|
+
//# sourceMappingURL=jsonl-CvgEOakL.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonl-CvgEOakL.mjs","names":[],"sources":["../src/session/jsonl.ts"],"sourcesContent":["import type { FileHandle } from \"node:fs/promises\"\nimport type { SessionStore } from \"./store.ts\"\nimport type { SessionNode } from \"./types.ts\"\n\nimport { normPath, safeStringify, withLock } from \"@zaly/shared\"\nimport { appendFile, open } from \"node:fs/promises\"\n\nconst CHUNK_SIZE = 4 * 1024 * 1024\n\n/**\n * JSONL-backed `SessionStore` — append-only file, one record per line.\n *\n * Current implementation reads the entire file on `load()` and keeps the\n * full DAG in memory. Future optimizations (lazy reads with offset\n * index, LRU cache for huge sessions) can land behind this same\n * interface without changing `Session`.\n *\n * Crash tolerance: a truncated last line (interrupted mid-write) is\n * silently dropped; any other JSON parse error mid-file throws so real\n * corruption surfaces loudly.\n */\nexport class JsonlStore implements SessionStore {\n readonly path: string\n #nodes = new Map<string, SessionNode>()\n #root?: SessionNode\n #jsonl: JsonlReader<SessionNode>\n\n constructor(path: string) {\n this.path = path\n this.#jsonl = new JsonlReader(path)\n }\n\n /** Load + open: hydrate the DAG from `path` (when the file exists),\n * then open the writer in append mode. New nodes land on disk as\n * they're committed. */\n static async load(path: string): Promise<JsonlStore> {\n path = normPath(path)\n const ret = new JsonlStore(path)\n await ret.#fetch({ limit: 1 })\n return ret\n }\n\n async #fetch(opts?: { id?: string; limit?: number }): Promise<SessionNode | undefined> {\n let count = 0\n let value: SessionNode | undefined\n // oxlint-disable-next-line no-await-in-loop\n while ((value = await this.#jsonl.next()) !== undefined) {\n this.#nodes.set(value.uuid, value)\n this.#root ??= value\n if (opts?.limit && count++ >= opts.limit) break // safe — doesn't call .return()\n if (opts?.id && value.uuid === opts.id) return value\n }\n }\n\n get root(): SessionNode | undefined {\n return this.#root\n }\n\n async get(id: string): Promise<SessionNode | undefined> {\n return this.#nodes.get(id) ?? (await this.#fetch({ id }))\n }\n\n async write(node: SessionNode): Promise<void> {\n const line = `${safeStringify(node)}\\n`\n await withLock(this.path, async () => {\n await appendFile(this.path, line, \"utf8\")\n })\n this.#nodes.set(node.uuid, node)\n this.#root = node\n }\n\n async close(): Promise<void> {\n await this.#jsonl.close()\n }\n\n async *all(): AsyncIterable<SessionNode> {\n await this.#fetch()\n yield* this.#nodes.values()\n }\n}\n\nexport class JsonlReader<T> {\n #fd?: FileHandle\n #path: string\n #it?: AsyncIterableIterator<T>\n #count = 0\n\n constructor(readonly path: string) {\n this.#path = path\n this.#it = this.generator()\n }\n\n #toJson(line: string): T | undefined {\n if (line.trim() === \"\") return\n try {\n const ret = JSON.parse(line) as T\n this.#count++\n return ret\n } catch (error) {\n // Tolerate a truncated last line (crash mid-write); anything\n // else is real corruption.\n if (this.#count === 0) return\n throw new Error(\n `JsonlStore.load: malformed JSON at line ${this.#count + 1} of \"${this.#path}\": ${(error as Error).message}`,\n { cause: error }\n )\n }\n }\n\n private async *generator(): AsyncIterableIterator<T> {\n try {\n this.#fd = await open(this.#path, \"r\")\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return\n throw error\n }\n let { size: position } = await this.#fd.stat()\n let leftover = \"\"\n try {\n while (position > 0) {\n const length = Math.min(position, CHUNK_SIZE)\n position -= length\n // oxlint-disable-next-line no-await-in-loop\n const { buffer } = await this.#fd.read(Buffer.alloc(length), 0, length, position)\n const lines = (buffer.toString(\"utf8\") + leftover).split(/\\r?\\n/)\n leftover = lines.shift() ?? \"\"\n for (let i = lines.length - 1; i >= 0; i--) {\n const v = this.#toJson(lines[i])\n if (v) yield v\n }\n }\n if (leftover.length) {\n const v = this.#toJson(leftover)\n if (v) yield v\n }\n } finally {\n await this.#fd.close()\n this.#it = undefined\n }\n }\n\n async next(): Promise<T | undefined> {\n if (!this.#it) return\n const { value, done } = await this.#it.next()\n return done ? undefined : value\n }\n\n async close() {\n if (this.#it) {\n await this.#it.return?.() // runs the generator's finally\n this.#it = undefined\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as handlerRegistry } from "./registry-
|
|
1
|
+
import { t as handlerRegistry } from "./registry-BVf0aZEA.mjs";
|
|
2
2
|
import { normPath } from "@zaly/shared";
|
|
3
3
|
//#region src/permissions/presets.ts
|
|
4
4
|
/** Native tools that only inspect session state or workspace files. */
|
|
@@ -268,4 +268,4 @@ function parseRules(rules) {
|
|
|
268
268
|
//#endregion
|
|
269
269
|
export { PermissionManager };
|
|
270
270
|
|
|
271
|
-
//# sourceMappingURL=manager-
|
|
271
|
+
//# sourceMappingURL=manager-f-WjFTMX.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager-f-WjFTMX.mjs","names":[],"sources":["../src/permissions/presets.ts","../src/permissions/manager.ts"],"sourcesContent":["import type { Verdict } from \"./types.ts\"\n\n/**\n * Permission presets — verdict-keyed pattern lists in the same shape\n * `parseRules` accepts. The TUI exposes these as `--preset strict`,\n * `--preset readonly`, etc.; the manager resolves the chosen preset to\n * the structured `Rule<string>[]` at construction.\n *\n * Pattern grammar:\n * \"bash(ls:*)\" → bash command pattern\n * \"read(/src/**)\" → workspace-relative gitignore pattern\n * \"read(*)\" → matches every read in any workspace\n * \"bash\" → bare scope, equivalent to `bash(*)` (match any cmd)\n *\n * Scope names are exact matches against the tool name they gate\n * (snake_case). Underscored scopes work too (`task_stop`, `task_poll`).\n */\nexport type PermissionPresetName = \"strict\" | \"readonly\" | \"permissive\" | \"yolo\"\n\nexport interface PermissionPreset {\n description: string\n rules: Partial<Record<Verdict, string[]>>\n}\n\n// ── Pattern groups ─────────────────────────────────────────────────────────\n\n/** Native tools that only inspect session state or workspace files. */\nconst READONLY_TOOLS = [\n \"tool(read)\",\n \"tool(grep)\",\n \"tool(find)\",\n \"tool(task_list)\",\n \"tool(task_poll)\",\n]\n\n/** Read-only utilities the model can run without surprises: file\n * inspection, search, metadata. None of these modify state on their own.\n * (`sed`/`awk` writes still gated by Write rules via the bash handler's\n * composition with the file handler.) */\nconst READONLY_BASH = [\n \"bash(ls:*)\",\n \"bash(cat:*)\",\n \"bash(head:*)\",\n \"bash(tail:*)\",\n \"bash(wc:*)\",\n \"bash(find:*)\",\n \"bash(grep:*)\",\n \"bash(rg:*)\",\n \"bash(fd:*)\",\n \"bash(sort:*)\",\n \"bash(uniq:*)\",\n \"bash(cut:*)\",\n \"bash(diff:*)\",\n \"bash(stat:*)\",\n \"bash(file:*)\",\n \"bash(realpath:*)\",\n \"bash(readlink:*)\",\n \"bash(basename:*)\",\n \"bash(dirname:*)\",\n \"bash(echo:*)\",\n \"bash(printf:*)\",\n \"bash(sed:*)\",\n \"bash(awk:*)\",\n \"bash(xxd:*)\",\n \"bash(hexdump:*)\",\n \"bash(od:*)\",\n \"bash(tree:*)\",\n \"bash(pwd)\",\n \"bash(true)\",\n \"bash(false)\",\n \"bash(which:*)\",\n \"bash(type:*)\",\n \"bash(command:*)\",\n \"bash(test:*)\",\n \"bash(cd:*)\",\n]\n\n/** Read-only git + GitHub CLI subcommands. */\nconst READONLY_GIT = [\n \"bash(git status:*)\",\n \"bash(git log:*)\",\n \"bash(git diff:*)\",\n \"bash(git show:*)\",\n \"bash(git show-ref:*)\",\n \"bash(git branch:*)\",\n \"bash(git remote:*)\",\n \"bash(git rev-parse:*)\",\n \"bash(git config --get:*)\",\n \"bash(gh issue list:*)\",\n \"bash(gh issue view:*)\",\n \"bash(gh pr list:*)\",\n \"bash(gh pr view:*)\",\n \"bash(gh repo view:*)\",\n]\n\n/** Common dev-workflow commands — build, test, typecheck. Excludes\n * package management (`npm install`, `bun add`) which can drag arbitrary\n * code in over the network. */\nconst DEV_BASIC = [\n \"bash(bun test:*)\",\n \"bash(bun test:node:*)\",\n \"bash(bun test:bun:*)\",\n \"bash(bun check:*)\",\n \"bash(bun run:*)\",\n \"bash(bun build:*)\",\n \"bash(bunx tsc:*)\",\n \"bash(bun tsc:*)\",\n \"bash(bun x tsc:*)\",\n \"bash(bunx vitest:*)\",\n \"bash(bun x vitest:*)\",\n \"bash(bunx oxlint:*)\",\n \"bash(bun x oxlint:*)\",\n \"bash(oxlint:*)\",\n]\n\n/** Hard denies — never auto-allowed, regardless of preset. */\nconst HARD_DENIES = [\"bash(sudo:*)\"]\n\n/** Broader rules unlocked by the permissive preset. Trades some safety\n * for speed — appropriate for frontier models with strong instruction\n * following. Includes ad-hoc bun/node evals and git mutations. */\nconst PERMISSIVE_EXTRAS = [\n \"bash(bun -e:*)\",\n \"bash(bun:*)\",\n \"bash(node:*)\",\n \"bash(node -e:*)\",\n \"bash(make:*)\",\n \"bash(mkdir:*)\",\n \"bash(cp:*)\",\n \"bash(mv:*)\",\n \"bash(touch:*)\",\n \"bash(ln:*)\",\n \"bash(chmod:*)\",\n \"bash(git add:*)\",\n \"bash(git commit:*)\",\n \"bash(git checkout:*)\",\n \"bash(git restore:*)\",\n \"bash(git stash:*)\",\n \"bash(git fetch:*)\",\n \"bash(git pull:*)\",\n \"bash(curl:*)\",\n \"bash(jq:*)\",\n \"bash(perl -e:*)\",\n \"bash(timeout:*)\",\n \"bash(for:*)\",\n \"bash(while:*)\",\n]\n\n// ── Presets ────────────────────────────────────────────────────────────────\n\n// oxlint-disable sort-keys\nexport const permissionPresets = {\n strict: {\n description: \"Ask for everything. No commands or file ops auto-allowed.\",\n rules: {\n deny: HARD_DENIES,\n // Override the tool/file handlers' default allows.\n ask: [\"tool(*)\", \"read(*)\", \"write(*)\"],\n },\n },\n readonly: {\n description:\n \"Auto-allow read-only utilities, git read-only ops, and common build/test commands. \" +\n \"Reads inside the workspace are allowed (sensitive paths denied); writes always ask.\",\n rules: {\n deny: HARD_DENIES,\n allow: [...READONLY_TOOLS, ...READONLY_BASH, ...READONLY_GIT, ...DEV_BASIC],\n ask: [\"tool(*)\", \"write(*)\"],\n },\n },\n permissive: {\n description:\n \"Readonly preset plus broader dev commands (file moves, ad-hoc bun/node evals, \" +\n \"git mutations, network fetches) and writes inside the workspace.\",\n rules: {\n deny: HARD_DENIES,\n allow: [\n \"tool(*)\",\n ...READONLY_BASH,\n ...READONLY_GIT,\n ...DEV_BASIC,\n ...PERMISSIVE_EXTRAS,\n // Allow writes inside any workspace (sensitive paths still denied\n // by the file handler before rule resolution).\n \"write(*)\",\n ],\n },\n },\n yolo: {\n description: \"Allow everything. No prompts. Use only when you trust the model and the task.\",\n rules: {\n allow: [\"bash\", \"read(*)\", \"write(*)\", \"tool(*)\"],\n },\n },\n} as const satisfies Record<PermissionPresetName, PermissionPreset>\n","import type { PermissionPresetName } from \"./presets.ts\"\nimport type { CheckResult, Rule, Verdict } from \"./types.ts\"\n\nimport { normPath } from \"@zaly/shared\"\nimport { handlerRegistry } from \"./handlers/registry.ts\"\nimport { permissionPresets } from \"./presets.ts\"\n\nexport type PermissionOptions = {\n /** Session cwd — anchor for resolving relative paths and the default\n * spawn cwd for bash subprocesses. Pinned at construction; the agent\n * never mutates `process.cwd()`, and there's no `cd` tool. Defaults\n * to `process.cwd()`. */\n cwd?: string\n /** Named preset — supplies a baseline rule set. Explicit `rules` win\n * over preset rules on conflict (first-match-wins resolution; user\n * rules are listed before preset rules at construction). */\n preset?: PermissionPresetName\n rules?: Rule[] | Partial<Record<Verdict, string[] | undefined>>\n /** Initial workspace list. `cwd` is auto-added so the user doesn't\n * have to repeat it. Pass paths absolute (or relative — they'll be\n * resolved against `cwd`). */\n workspaces?: readonly string[]\n}\n\n/**\n * Owns per-session permission state and dispatches `validate(scope, input)`\n * to the registered handler for `scope`. Handlers receive rules\n * pre-filtered to their scope plus the shared workspace list.\n *\n * Mutators (`addWorkspace`, `addRule`, …) are how the prompt UI promotes\n * an `ask` verdict — once the user picks \"allow `Bash(git push:*)` for\n * this session\", the TUI calls `manager.addRule(...)` and subsequent\n * `validate()` calls pick it up via the filter.\n */\nexport class PermissionManager {\n readonly cwd: string\n #rules: Rule[] = []\n #workspaces: string[]\n\n constructor(opts?: PermissionOptions) {\n this.cwd = normPath(opts?.cwd)\n if (opts?.rules) {\n const more = Array.isArray(opts.rules) ? opts.rules : parseRules(opts.rules)\n this.#rules.push(...more)\n }\n if (opts?.preset) this.#rules.push(...parseRules(permissionPresets[opts.preset].rules))\n this.#workspaces = (opts?.workspaces ?? []).map((p) => normPath(this.cwd, p))\n // Auto-include cwd so users don't have to repeat it. Listed first so\n // longest-prefix lookup in handlers stays predictable.\n if (!this.#workspaces.includes(this.cwd)) this.#workspaces.unshift(this.cwd)\n }\n\n // ── Workspaces ──────────────────────────────────────────────────────\n\n get workspaces(): readonly string[] {\n return this.#workspaces\n }\n\n addWorkspace(path: string): void {\n const abs = normPath(this.cwd, path)\n if (!this.#workspaces.includes(abs)) this.#workspaces.push(abs)\n }\n\n removeWorkspace(path: string): void {\n const abs = normPath(this.cwd, path)\n const i = this.#workspaces.indexOf(abs)\n if (i !== -1) this.#workspaces.splice(i, 1)\n }\n\n // ── Rules ───────────────────────────────────────────────────────────\n\n get rules(): readonly Rule[] {\n return this.#rules\n }\n\n /** Rules whose scope has no registered handler — surfaces config\n * errors (typos, unknown scopes, malformed pattern strings). The TUI\n * can render these as a startup warning. */\n get invalidRules(): readonly Rule[] {\n return this.#rules.filter((r) => !handlerRegistry.has(r.scope))\n }\n\n addRule(rule: Rule): void {\n this.#rules.unshift(rule)\n }\n\n // ── Dispatch ────────────────────────────────────────────────────────\n\n validate(scope: string, input: string): CheckResult {\n if (!handlerRegistry.has(scope)) {\n throw new Error(`no permission handler registered for scope \"${scope}\"`)\n }\n const handler = handlerRegistry.load(scope)\n const rules = this.#rules.filter((r) => r.scope === scope)\n return handler.validate(input, {\n cwd: this.cwd,\n rules,\n scope,\n validate: this.validate.bind(this),\n workspaces: this.#workspaces,\n })\n }\n}\n\n/** Parse Claude-Code-style permission config (a verdict-keyed map of\n * pattern strings) into structured `Rule<string>[]`.\n *\n * Pattern shapes accepted:\n * \"Bash(ls:*)\" → { scope: \"bash\", pattern: \"ls:*\" }\n * \"WebSearch\" → { scope: \"websearch\", pattern: \"*\" } // bare name = scope-wide\n *\n * Scopes are normalized to lower-case so config can use either\n * TitleCase (\"Bash\") or lowercase (\"bash\") interchangeably.\n *\n * Malformed entries are pushed verbatim so `manager.invalidRules`\n * surfaces them — typos in the scope (\"Bahs(...)\") are caught the same\n * way (no handler registered → flagged invalid). */\nexport function parseRules(rules: Partial<Record<Verdict, string[] | undefined>>): Rule[] {\n const ret: Rule[] = []\n for (const policy of Object.keys(rules) as Verdict[]) {\n const patterns = rules[policy]\n if (!Array.isArray(patterns)) continue\n for (const raw of patterns) {\n const m = raw.match(/^(\\w+)(?:\\((.+)\\))?$/)\n if (m) {\n const [, scope, pat = \"*\"] = m\n ret.push({ pattern: pat, policy, scope: scope.toLowerCase() })\n } else {\n // Truly malformed — preserve verbatim so invalidRules surfaces it.\n ret.push({ pattern: raw, policy, scope: \"invalid\" })\n }\n }\n }\n return ret\n}\n"],"mappings":";;;;AA2BA,MAAM,iBAAiB;CACrB;CACA;CACA;CACA;CACA;AACF;;;;;AAMA,MAAM,gBAAgB;CACpB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;AAGA,MAAM,eAAe;CACnB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;AAKA,MAAM,YAAY;CAChB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;AAGA,MAAM,cAAc,CAAC,cAAc;;;;AAKnC,MAAM,oBAAoB;CACxB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAKA,MAAa,oBAAoB;CAC/B,QAAQ;EACN,aAAa;EACb,OAAO;GACL,MAAM;GAEN,KAAK;IAAC;IAAW;IAAW;GAAU;EACxC;CACF;CACA,UAAU;EACR,aACE;EAEF,OAAO;GACL,MAAM;GACN,OAAO;IAAC,GAAG;IAAgB,GAAG;IAAe,GAAG;IAAc,GAAG;GAAS;GAC1E,KAAK,CAAC,WAAW,UAAU;EAC7B;CACF;CACA,YAAY;EACV,aACE;EAEF,OAAO;GACL,MAAM;GACN,OAAO;IACL;IACA,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IAGH;GACF;EACF;CACF;CACA,MAAM;EACJ,aAAa;EACb,OAAO,EACL,OAAO;GAAC;GAAQ;GAAW;GAAY;EAAS,EAClD;CACF;AACF;;;;;;;;;;;;;AChKA,IAAa,oBAAb,MAA+B;CAC7B;CACA,SAAiB,CAAC;CAClB;CAEA,YAAY,MAA0B;EACpC,KAAK,MAAM,SAAS,MAAM,GAAG;EAC7B,IAAI,MAAM,OAAO;GACf,MAAM,OAAO,MAAM,QAAQ,KAAK,KAAK,IAAI,KAAK,QAAQ,WAAW,KAAK,KAAK;GAC3E,KAAK,OAAO,KAAK,GAAG,IAAI;EAC1B;EACA,IAAI,MAAM,QAAQ,KAAK,OAAO,KAAK,GAAG,WAAW,kBAAkB,KAAK,OAAO,CAAC,KAAK,CAAC;EACtF,KAAK,eAAe,MAAM,cAAc,CAAC,EAAA,CAAG,KAAK,MAAM,SAAS,KAAK,KAAK,CAAC,CAAC;EAG5E,IAAI,CAAC,KAAK,YAAY,SAAS,KAAK,GAAG,GAAG,KAAK,YAAY,QAAQ,KAAK,GAAG;CAC7E;CAIA,IAAI,aAAgC;EAClC,OAAO,KAAK;CACd;CAEA,aAAa,MAAoB;EAC/B,MAAM,MAAM,SAAS,KAAK,KAAK,IAAI;EACnC,IAAI,CAAC,KAAK,YAAY,SAAS,GAAG,GAAG,KAAK,YAAY,KAAK,GAAG;CAChE;CAEA,gBAAgB,MAAoB;EAClC,MAAM,MAAM,SAAS,KAAK,KAAK,IAAI;EACnC,MAAM,IAAI,KAAK,YAAY,QAAQ,GAAG;EACtC,IAAI,MAAM,IAAI,KAAK,YAAY,OAAO,GAAG,CAAC;CAC5C;CAIA,IAAI,QAAyB;EAC3B,OAAO,KAAK;CACd;;;;CAKA,IAAI,eAAgC;EAClC,OAAO,KAAK,OAAO,QAAQ,MAAM,CAAC,gBAAgB,IAAI,EAAE,KAAK,CAAC;CAChE;CAEA,QAAQ,MAAkB;EACxB,KAAK,OAAO,QAAQ,IAAI;CAC1B;CAIA,SAAS,OAAe,OAA4B;EAClD,IAAI,CAAC,gBAAgB,IAAI,KAAK,GAC5B,MAAM,IAAI,MAAM,+CAA+C,MAAM,EAAE;EAEzE,MAAM,UAAU,gBAAgB,KAAK,KAAK;EAC1C,MAAM,QAAQ,KAAK,OAAO,QAAQ,MAAM,EAAE,UAAU,KAAK;EACzD,OAAO,QAAQ,SAAS,OAAO;GAC7B,KAAK,KAAK;GACV;GACA;GACA,UAAU,KAAK,SAAS,KAAK,IAAI;GACjC,YAAY,KAAK;EACnB,CAAC;CACH;AACF;;;;;;;;;;;;;;AAeA,SAAgB,WAAW,OAA+D;CACxF,MAAM,MAAc,CAAC;CACrB,KAAK,MAAM,UAAU,OAAO,KAAK,KAAK,GAAgB;EACpD,MAAM,WAAW,MAAM;EACvB,IAAI,CAAC,MAAM,QAAQ,QAAQ,GAAG;EAC9B,KAAK,MAAM,OAAO,UAAU;GAC1B,MAAM,IAAI,IAAI,MAAM,sBAAsB;GAC1C,IAAI,GAAG;IACL,MAAM,GAAG,OAAO,MAAM,OAAO;IAC7B,IAAI,KAAK;KAAE,SAAS;KAAK;KAAQ,OAAO,MAAM,YAAY;IAAE,CAAC;GAC/D,OAEE,IAAI,KAAK;IAAE,SAAS;IAAK;IAAQ,OAAO;GAAU,CAAC;EAEvD;CACF;CACA,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"masker-Bv9KjkDF.mjs","names":["#rule","defaults","#policies","#groups","#messages","#policy","#reset","#index","#o","#agent","#opts","#session","#masked","#stats","#threshold","#maskOpts","#update","#mask","#stat"],"sources":["../src/context/scoring.ts","../src/masker.ts"],"sourcesContent":["// ── Frecency tuning ───────────────────────────────────────────────────\n// Half-life measured in assistant turns, not wall time — what matters for\n// compaction context is what's been hot in the agent's *recent\n// experience*, regardless of how long the user spent between turns.\n// 60 turns is a middle ground: actively-iterated work stays at the top\n// while recurring workflow patterns (git stash, test-runner flags) still\n// clear the minScore filter instead of getting buried under one-shot\n\nimport type {\n AnyPart,\n Message,\n MetaPart,\n ParamsOf,\n Role,\n SafeParamsOf,\n Tool,\n ToolCallPart,\n ToolResultPart,\n} from \"@zaly/ai\"\nimport type { BashTool } from \"../tools/bash.ts\"\nimport type { EditTool, EditToolMeta } from \"../tools/edit.ts\"\nimport type { ReadTool, ReadToolMeta } from \"../tools/read.ts\"\nimport type { AnyTool } from \"../tools/registry.ts\"\nimport type { WriteTool, WriteToolMeta } from \"../tools/write.ts\"\n\nimport { isAttachment, safeParseToolParams } from \"@zaly/ai\"\nimport { safeStringify } from \"@zaly/shared\"\n\nconst DEFAULT_HALF_LIFE = 40\nconst DEFAULT_WEIGHT = 1\nconst DEFAULT_GAMMA = 0.5\n\nconst maskedMeta = (result = \"result\", action = \"call\"): MetaPart => ({\n content: `Masked ${result}. Re-${action} to refresh`,\n tag: \"masked\",\n type: \"meta\",\n})\n\nexport type MsgPart<R extends Role = Role, P extends AnyPart = AnyPart> = {\n message: Message<R>\n part: P\n score: number\n turn: number\n $m: number\n $p: number\n}\n\nexport type MaskGroup<P extends MsgPart = MsgPart> = {\n id: string\n key: string\n policy: MaskPolicy<P, MaskGroup<P>>\n parts: P[]\n}\n\nexport type MaskRule<P extends MsgPart = MsgPart, G extends MaskGroup<P> = MaskGroup<P>> = {\n filter?: (part: MsgPart) => boolean\n id?: (part: P) => string | undefined\n update?: (group: G, part: P) => void\n gamma?: number | ((group: G) => number | undefined)\n halfLife?: number | ((group: G) => number | undefined)\n key?: \"id\" | ((group: G) => string | undefined)\n weight?: number | ((group: G) => number | undefined)\n mask: (part: P, group: G) => AnyPart\n}\n\nexport type ToolPart<T extends Tool = Tool, M extends object = object> = MsgPart<\n \"assistant\" | \"tool\" | \"system\",\n ToolCallPart<T[\"name\"], ParamsOf<T>> | ToolResultPart<T[\"name\"], M>\n>\n\nexport type ToolGroup<T extends Tool = Tool, M extends object = object> = MaskGroup<\n ToolPart<T, M>\n> & {\n name: T[\"name\"]\n params: SafeParamsOf<T>\n call?: ToolCallPart<T[\"name\"], ParamsOf<T>>\n result?: ToolResultPart<T[\"name\"], M>\n}\n\nexport type ToolRule<T extends Tool = Tool, M extends object = object> =\n | MaskRule<ToolPart<T, M>, ToolGroup<T, M>>\n | {\n key?: \"name\" | \"params\" | \"id\" | ((tool: ToolGroup<T, M>) => string | undefined)\n }\n\nclass MaskPolicy<\n P extends MsgPart = MsgPart,\n G extends MaskGroup<P> = MaskGroup<P>,\n> implements Required<MaskRule<P, G>> {\n #rule: MaskRule<P, G>\n\n constructor(rule: MaskRule<P, G>) {\n this.#rule = rule\n }\n\n update(group: G, part: P): void {\n group.parts.push(part)\n this.#rule.update?.(group, part)\n }\n\n mask(part: P, group: G): AnyPart {\n return this.#rule.mask(part, group)\n }\n\n filter(part: MsgPart): boolean {\n return this.#rule.filter?.(part) ?? true\n }\n\n id(part: P): string {\n return this.#rule.id?.(part) ?? `${part.$m}:${part.$p}`\n }\n\n halfLife(group: G): number {\n const halfLife = this.#rule.halfLife ?? DEFAULT_HALF_LIFE\n return typeof halfLife === \"number\" ? halfLife : (halfLife(group) ?? DEFAULT_HALF_LIFE)\n }\n\n lambda(group: G): number {\n return Math.LN2 / this.halfLife(group)\n }\n\n weight(group: G): number {\n const weight = this.#rule.weight ?? DEFAULT_WEIGHT\n return typeof weight === \"number\" ? weight : (weight(group) ?? DEFAULT_WEIGHT)\n }\n\n gamma(group: G): number {\n const gamma = this.#rule.gamma ?? DEFAULT_GAMMA\n return typeof gamma === \"number\" ? gamma : (gamma(group) ?? DEFAULT_GAMMA)\n }\n\n key(group: G): string {\n const key = this.#rule.key ?? \"id\"\n if (key === \"id\") return group.id\n else if (typeof key === \"function\") return key(group) ?? group.id\n return group.id\n }\n}\n\nclass ToolPolicy<T extends Tool = Tool, M extends object = object> extends MaskPolicy<\n ToolPart<T, M>,\n ToolGroup<T, M>\n> {\n constructor(name: string, rule: ToolRule<T, M>) {\n super({\n filter: (part) =>\n (part.part.type === \"tool-call\" || part.part.type === \"tool-result\") &&\n (part.part.name === name || name === \"*\"),\n id: (part) => part.part.id,\n mask: (part, group) => {\n if (part.part.type === \"tool-call\")\n return {\n ...part.part,\n params: { masked: truncate(safeStringify(group.params), 100) },\n }\n return { ...part.part, content: [maskedMeta()] }\n },\n update: (group, part) => {\n group.name = name\n if (part.part.type === \"tool-call\") {\n group.call = part.part\n group.params = safeParseToolParams(part.part.params)\n }\n if (part.part.type === \"tool-result\") group.result = part.part\n },\n ...rule,\n key: (group) => {\n const key = rule.key ?? \"params\"\n if (key === \"name\") return group.name\n if (key === \"params\" && group.params !== undefined) return safeStringify(group.params)\n if (key === \"id\") return group.id\n if (typeof key === \"function\") return key(group)\n return group.id\n },\n })\n }\n}\n\nfunction truncate(text: string, len: number): string {\n return text.length <= len ? text : `${text.slice(0, len)}…`\n}\n\nconst fileScore: ToolRule<\n ReadTool | WriteTool | EditTool,\n ReadToolMeta | WriteToolMeta | EditToolMeta\n> = {\n gamma: (t) => {\n if (t.name === \"write\") return 0.45\n if (t.name === \"edit\") return 0.65\n if (t.result?.meta?.full) return 0.75\n return 0.95\n },\n halfLife: (t) => {\n if (t.name === \"write\") return 90\n if (t.name === \"edit\") return 75\n if (t.result?.meta?.full) return 60\n return 50 // partial read\n },\n key: (t) => `file:${t.result?.meta?.path ?? t.id}`,\n mask: (part) => {\n if (part.part.type === \"tool-call\")\n return {\n ...part.part,\n params: { masked: true, path: part.part.params.path },\n }\n return part.part.isError ? part.part : { ...part.part, content: [maskedMeta()] }\n },\n weight: (t) => {\n if (t.name === \"write\") return 1.25\n if (t.name === \"edit\") return 1.2\n if (t.result?.meta?.full) return 1.1\n return 1\n },\n}\n\nexport type ContextScoringOptions = {\n tools?: Record<AnyTool | \"*\", ToolRule>\n parts?: MaskRule[]\n}\n\nconst defaults: ContextScoringOptions = {\n parts: [\n {\n filter: (part) => isAttachment(part.part),\n mask: (part) => maskedMeta(part.part.type, \"attach\"),\n },\n {\n filter: (part) => part.message.role === \"system\" && part.message.meta?.kind === \"task\",\n halfLife: 25,\n mask: (part) => {\n if (part.part.type === \"meta\") return part.part\n return maskedMeta(\"task\")\n },\n },\n ],\n // oxlint-disable-next-line sort-keys\n tools: {\n bash: {\n gamma: 0.35,\n halfLife: 25,\n key: (t: ToolGroup<BashTool>) => `bash:${t.params?.command?.replace(/\\s+/g, \" \") ?? t.id}`,\n },\n find: { gamma: 0.7, halfLife: 35 },\n grep: { gamma: 0.7, halfLife: 35 },\n search: { gamma: 0.5, halfLife: 40 },\n\n read: fileScore,\n edit: fileScore,\n write: fileScore,\n\n \"*\": { gamma: 0.5, halfLife: 40 },\n } as Record<AnyTool | \"*\", ToolRule>,\n}\n\nexport class ContextScoring {\n #groups = new Map<string, MaskGroup>()\n #messages: readonly Message[] = []\n #policies: MaskPolicy[] = []\n\n constructor(opts: ContextScoringOptions = {}) {\n const tools = { ...defaults.tools, ...opts.tools }\n for (const [name, tool] of Object.entries(tools)) {\n this.#policies.push(new ToolPolicy(name, tool) as MaskPolicy)\n }\n for (const rule of [...(defaults.parts ?? []), ...(opts.parts ?? [])]) {\n this.#policies.push(new MaskPolicy(rule))\n }\n }\n\n #reset() {\n this.#groups.clear()\n }\n\n #index() {\n let turn = 0\n for (let $m = this.#messages.length - 1; $m >= 0; $m--) {\n const m = this.#messages[$m]\n if (m.role === \"assistant\") turn++\n\n if (!Array.isArray(m.content)) continue\n\n for (let $p = m.content.length - 1; $p >= 0; $p--) {\n const part: MsgPart = { $m, $p, message: m, part: m.content[$p], score: 0, turn }\n const policy = this.#policy(part)\n if (!policy) continue\n const id = policy.id(part)\n const group = this.#groups.get(id) ?? { id, key: \"\", parts: [], policy }\n policy.update(group, part)\n this.#groups.set(id, group)\n }\n }\n }\n\n #policy(part: MsgPart): MaskPolicy | undefined {\n return this.#policies.find((policy) => policy.filter(part))\n }\n\n score(messages: readonly Message[]): MaskGroup[] {\n this.#reset()\n this.#messages = messages\n this.#index()\n // tools sorted by recency\n const groups = [...this.#groups.values()]\n const gammas = new Map<string, number>()\n for (const group of groups) {\n const key = group.policy.key(group)\n group.key = key\n const weight = group.policy.weight(group)\n const shadow = gammas.get(key) ?? 1\n const lambda = group.policy.lambda(group)\n for (const part of group.parts) {\n const decay = Math.exp(-lambda * part.turn)\n part.score = decay * weight * shadow\n }\n gammas.set(key, shadow * group.policy.gamma(group))\n }\n return groups\n }\n}\n","import type { AnyPart, Message, Role, Tool } from \"@zaly/ai\"\nimport type { MaybeGetter } from \"@zaly/shared\"\nimport type { Agent } from \"./agent.ts\"\nimport type { MsgPart } from \"./context/scoring.ts\"\nimport type { Session } from \"./session/session.ts\"\n\nimport { toValue } from \"@zaly/shared\"\nimport { ContextScoring } from \"./context/scoring.ts\"\nimport { estimatePart, tokenStats } from \"./context/tokens.ts\"\n\n/** Top-level masking config. */\nexport type MaskerOptions = {\n enabled?: boolean\n /** Don't mask tool-result parts whose original content is shorter\n * than this (estimated tokens). Skips tiny \"ok\"-style success\n * messages where the stub would be larger than the original.\n * Doesn't apply to attachments (always worth masking). */\n minTokens?: number\n /** How many turns to keep in the tail of the conversation, regardless\n * of score. Defaults to 20. */\n keepTurns?: number\n /** How far above the target ratio to trigger a new masking pass.\n * Defaults to 0.25 (25%). */\n delta?: number\n /** Target ratio of used/limit tokens to reach by masking. Defaults to 0.5 (50%). */\n target?: number\n}\n\nexport type MaskOpts = {\n force?: boolean\n limit?: number\n ratio?: number\n tools?: Tool[]\n prompt?: string[]\n}\n\nconst defaults = {\n delta: 0.25,\n enabled: true,\n keepTurns: 40,\n minTokens: 50,\n target: 0.5,\n} as const satisfies Required<MaskerOptions>\n\n/** In-place mask projection for the request stream.\n *\n * The masker replaces low-value parts with stable stubs to keep the\n * projected request within budget. A scoring pass ranks maskable parts\n * by recency, repeated-use shadowing, and per-policy weights; the budget\n * pass masks from lowest score upward until the target is reached or no\n * useful candidates remain.\n *\n * Masking intentionally happens rarely because it changes historical\n * bytes and busts prefix cache. The first pass after startup/session\n * reset always rebuilds the projection. Later passes use a hysteresis\n * threshold: mask toward `target`, then trigger again at roughly\n * `actual + delta`. If the target cannot be reached, the next threshold\n * rises automatically to avoid repeated no-op cache busts.\n */\nexport class Masker {\n #o: MaybeGetter<MaskerOptions>\n #stats = new Map<Role, Record<string, number>>()\n #masked = new Map<string, Map<number, AnyPart>>()\n #threshold?: number\n #agent: Agent\n\n constructor(agent: Agent, opts: MaybeGetter<MaskerOptions> = {}) {\n this.#o = opts\n this.#agent = agent\n agent.on(\"context\", async (ctx) => (ctx.messages = await this.mask(ctx.messages)))\n agent.ctx.on(\"session\", () => this.reset())\n }\n\n get #opts(): Required<MaskerOptions> {\n return { ...defaults, ...toValue(this.#o) }\n }\n\n get enabled(): boolean {\n return this.#opts.enabled\n }\n\n get #session(): Session {\n return this.#agent.session\n }\n\n #maskOpts(opts?: MaskOpts): Required<MaskOpts> {\n const pressure = this.#agent.pressure\n return {\n force: false,\n limit: pressure.limit,\n prompt: this.#agent.prompt,\n ratio: pressure.ratio,\n tools: this.#agent.tools,\n ...opts,\n }\n }\n\n reset(): void {\n this.#masked.clear()\n this.#stats.clear()\n this.#threshold = undefined\n }\n\n get stats(): Map<Role, Record<string, number>> {\n return this.#stats\n }\n\n /** Number of messages with at least one masked part. */\n get masked(): number {\n return this.#masked.size\n }\n\n isMasked(msgId: string, partIdx?: number): boolean {\n if (!this.enabled) return false\n const parts = this.#masked.get(msgId)\n if (parts === undefined) return false\n return partIdx === undefined ? parts.size > 0 : parts.has(partIdx)\n }\n\n /** Apply masks and return the projected message array.\n *\n * Always rebuild once after startup/session reset: `pressure.ratio`\n * may describe a previously masked projection, while this instance has\n * no mask decisions yet. After that, rebuild only when projected\n * pressure crosses the hysteresis threshold. */\n async mask(messages: readonly Message[], opts?: MaskOpts): Promise<Message[]> {\n if (!this.enabled || messages.length === 0) return [...messages]\n\n const resolved = this.#maskOpts(opts)\n\n // Masker has not yet been initialized for this session.\n // Use the session's mask checkpoint to restore the last threshold\n // and rebuild the mask decisions up to that point.\n if (this.#threshold === undefined) {\n const cp = this.#session.maskCheckpoint\n if (!cp) {\n // No checkpoint means the session has never been masked before, so\n // just set to the default threshold for the first pass, so that the next pass will\n // trigger only if the pressure exceeds the target + delta.\n this.#threshold = this.#opts.target + this.#opts.delta\n } else {\n this.#threshold = cp.threshold\n const idx = messages.findIndex((m) => m.id === cp.messageId)\n // NOTE: the checkpoint message should exists. If not mask all, just in case\n // Rebuild the mask decisions\n this.#update(idx === -1 ? messages : messages.slice(0, idx + 1), resolved)\n }\n }\n\n // If the current pressure ratio exceeds the threshold, rebuild the mask decisions.\n if (resolved.ratio >= this.#threshold || resolved.force) {\n const messageId = messages.at(-1)?.id\n if (!messageId) throw new Error(\"Message in masker without ID. Should never happen.\")\n // Create a checkpoint first, with the CURRENT threshold, not the new one.\n await this.#session.addMaskCheckpoint({\n messageId,\n threshold: this.#threshold,\n })\n this.#update(messages, resolved)\n }\n return this.#mask(messages)\n }\n\n #stat(role: Role, key: string, n = 1): void {\n const r = this.#stats.get(role) ?? {}\n r[key] = (r[key] ?? 0) + n\n this.#stats.set(role, r)\n }\n\n /** Recompute mask decisions for the current message history. */\n #update(messages: readonly Message[], opts: Required<MaskOpts>): void {\n this.#masked.clear()\n this.#stats.clear()\n this.#threshold = this.#opts.target + this.#opts.delta\n const usage = tokenStats(messages, { prompt: opts.prompt, tools: opts.tools })\n // `pressure.ratio` is based on the current projected/masked request,\n // not the raw session. Raw history may exceed model context; masking\n // only needs to keep the projected request in a safe band.\n const targetRatio = Math.max(this.#opts.target, this.#threshold - this.#opts.delta)\n const target = opts.limit * targetRatio\n\n // Return when already under target.\n if (usage.tokens <= target) return\n\n let mask = usage.tokens - target\n\n if (mask === 0) return\n\n const scorer = new ContextScoring()\n const scores = scorer.score(messages)\n const parts: (MsgPart & { mask: () => AnyPart })[] = []\n for (const s of scores) {\n for (const p of s.parts) {\n parts.push({\n ...p,\n mask: () => s.policy.mask(p, s),\n })\n }\n }\n parts.sort((a, b) => a.score - b.score)\n\n let masked = 0\n for (const p of parts) {\n if (mask <= 0) break\n const id = p.message.id\n if (!id) continue\n\n if (p.turn <= this.#opts.keepTurns) continue\n\n const tokens = estimatePart(p.part).tokens\n if (tokens < this.#opts.minTokens) continue\n\n const part = p.mask()\n if (part === p.part) continue\n\n const msgParts = this.#masked.get(id) ?? new Map()\n msgParts.set(p.$p, part)\n this.#stat(\n p.message.role,\n `${p.part.type}${p.part.type === \"tool-result\" || p.part.type === \"tool-call\" ? `:${p.part.name}` : \"\"}`\n )\n this.#masked.set(id, msgParts)\n const delta = tokens - estimatePart(part).tokens\n masked += delta\n mask -= delta\n }\n\n this.#threshold = Math.max(\n (usage.tokens - masked) / opts.limit + this.#opts.delta,\n this.#opts.target + this.#opts.delta\n )\n }\n\n /** Render the current projection using the last computed mask decisions. */\n #mask(messages: readonly Message[]): Message[] {\n return messages.map((m) => {\n if (m.id === undefined || typeof m.content === \"string\") return m\n const masked = m.id ? this.#masked.get(m.id) : undefined\n return masked && masked.size > 0\n ? ({\n ...m,\n content: m.content.map((p, i) => masked.get(i) ?? p),\n } as Message)\n : m\n })\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"masker-Bv9KjkDF.mjs","names":["defaults"],"sources":["../src/context/scoring.ts","../src/masker.ts"],"sourcesContent":["// ── Frecency tuning ───────────────────────────────────────────────────\n// Half-life measured in assistant turns, not wall time — what matters for\n// compaction context is what's been hot in the agent's *recent\n// experience*, regardless of how long the user spent between turns.\n// 60 turns is a middle ground: actively-iterated work stays at the top\n// while recurring workflow patterns (git stash, test-runner flags) still\n// clear the minScore filter instead of getting buried under one-shot\n\nimport type {\n AnyPart,\n Message,\n MetaPart,\n ParamsOf,\n Role,\n SafeParamsOf,\n Tool,\n ToolCallPart,\n ToolResultPart,\n} from \"@zaly/ai\"\nimport type { BashTool } from \"../tools/bash.ts\"\nimport type { EditTool, EditToolMeta } from \"../tools/edit.ts\"\nimport type { ReadTool, ReadToolMeta } from \"../tools/read.ts\"\nimport type { AnyTool } from \"../tools/registry.ts\"\nimport type { WriteTool, WriteToolMeta } from \"../tools/write.ts\"\n\nimport { isAttachment, safeParseToolParams } from \"@zaly/ai\"\nimport { safeStringify } from \"@zaly/shared\"\n\nconst DEFAULT_HALF_LIFE = 40\nconst DEFAULT_WEIGHT = 1\nconst DEFAULT_GAMMA = 0.5\n\nconst maskedMeta = (result = \"result\", action = \"call\"): MetaPart => ({\n content: `Masked ${result}. Re-${action} to refresh`,\n tag: \"masked\",\n type: \"meta\",\n})\n\nexport type MsgPart<R extends Role = Role, P extends AnyPart = AnyPart> = {\n message: Message<R>\n part: P\n score: number\n turn: number\n $m: number\n $p: number\n}\n\nexport type MaskGroup<P extends MsgPart = MsgPart> = {\n id: string\n key: string\n policy: MaskPolicy<P, MaskGroup<P>>\n parts: P[]\n}\n\nexport type MaskRule<P extends MsgPart = MsgPart, G extends MaskGroup<P> = MaskGroup<P>> = {\n filter?: (part: MsgPart) => boolean\n id?: (part: P) => string | undefined\n update?: (group: G, part: P) => void\n gamma?: number | ((group: G) => number | undefined)\n halfLife?: number | ((group: G) => number | undefined)\n key?: \"id\" | ((group: G) => string | undefined)\n weight?: number | ((group: G) => number | undefined)\n mask: (part: P, group: G) => AnyPart\n}\n\nexport type ToolPart<T extends Tool = Tool, M extends object = object> = MsgPart<\n \"assistant\" | \"tool\" | \"system\",\n ToolCallPart<T[\"name\"], ParamsOf<T>> | ToolResultPart<T[\"name\"], M>\n>\n\nexport type ToolGroup<T extends Tool = Tool, M extends object = object> = MaskGroup<\n ToolPart<T, M>\n> & {\n name: T[\"name\"]\n params: SafeParamsOf<T>\n call?: ToolCallPart<T[\"name\"], ParamsOf<T>>\n result?: ToolResultPart<T[\"name\"], M>\n}\n\nexport type ToolRule<T extends Tool = Tool, M extends object = object> =\n | MaskRule<ToolPart<T, M>, ToolGroup<T, M>>\n | {\n key?: \"name\" | \"params\" | \"id\" | ((tool: ToolGroup<T, M>) => string | undefined)\n }\n\nclass MaskPolicy<\n P extends MsgPart = MsgPart,\n G extends MaskGroup<P> = MaskGroup<P>,\n> implements Required<MaskRule<P, G>> {\n #rule: MaskRule<P, G>\n\n constructor(rule: MaskRule<P, G>) {\n this.#rule = rule\n }\n\n update(group: G, part: P): void {\n group.parts.push(part)\n this.#rule.update?.(group, part)\n }\n\n mask(part: P, group: G): AnyPart {\n return this.#rule.mask(part, group)\n }\n\n filter(part: MsgPart): boolean {\n return this.#rule.filter?.(part) ?? true\n }\n\n id(part: P): string {\n return this.#rule.id?.(part) ?? `${part.$m}:${part.$p}`\n }\n\n halfLife(group: G): number {\n const halfLife = this.#rule.halfLife ?? DEFAULT_HALF_LIFE\n return typeof halfLife === \"number\" ? halfLife : (halfLife(group) ?? DEFAULT_HALF_LIFE)\n }\n\n lambda(group: G): number {\n return Math.LN2 / this.halfLife(group)\n }\n\n weight(group: G): number {\n const weight = this.#rule.weight ?? DEFAULT_WEIGHT\n return typeof weight === \"number\" ? weight : (weight(group) ?? DEFAULT_WEIGHT)\n }\n\n gamma(group: G): number {\n const gamma = this.#rule.gamma ?? DEFAULT_GAMMA\n return typeof gamma === \"number\" ? gamma : (gamma(group) ?? DEFAULT_GAMMA)\n }\n\n key(group: G): string {\n const key = this.#rule.key ?? \"id\"\n if (key === \"id\") return group.id\n else if (typeof key === \"function\") return key(group) ?? group.id\n return group.id\n }\n}\n\nclass ToolPolicy<T extends Tool = Tool, M extends object = object> extends MaskPolicy<\n ToolPart<T, M>,\n ToolGroup<T, M>\n> {\n constructor(name: string, rule: ToolRule<T, M>) {\n super({\n filter: (part) =>\n (part.part.type === \"tool-call\" || part.part.type === \"tool-result\") &&\n (part.part.name === name || name === \"*\"),\n id: (part) => part.part.id,\n mask: (part, group) => {\n if (part.part.type === \"tool-call\")\n return {\n ...part.part,\n params: { masked: truncate(safeStringify(group.params), 100) },\n }\n return { ...part.part, content: [maskedMeta()] }\n },\n update: (group, part) => {\n group.name = name\n if (part.part.type === \"tool-call\") {\n group.call = part.part\n group.params = safeParseToolParams(part.part.params)\n }\n if (part.part.type === \"tool-result\") group.result = part.part\n },\n ...rule,\n key: (group) => {\n const key = rule.key ?? \"params\"\n if (key === \"name\") return group.name\n if (key === \"params\" && group.params !== undefined) return safeStringify(group.params)\n if (key === \"id\") return group.id\n if (typeof key === \"function\") return key(group)\n return group.id\n },\n })\n }\n}\n\nfunction truncate(text: string, len: number): string {\n return text.length <= len ? text : `${text.slice(0, len)}…`\n}\n\nconst fileScore: ToolRule<\n ReadTool | WriteTool | EditTool,\n ReadToolMeta | WriteToolMeta | EditToolMeta\n> = {\n gamma: (t) => {\n if (t.name === \"write\") return 0.45\n if (t.name === \"edit\") return 0.65\n if (t.result?.meta?.full) return 0.75\n return 0.95\n },\n halfLife: (t) => {\n if (t.name === \"write\") return 90\n if (t.name === \"edit\") return 75\n if (t.result?.meta?.full) return 60\n return 50 // partial read\n },\n key: (t) => `file:${t.result?.meta?.path ?? t.id}`,\n mask: (part) => {\n if (part.part.type === \"tool-call\")\n return {\n ...part.part,\n params: { masked: true, path: part.part.params.path },\n }\n return part.part.isError ? part.part : { ...part.part, content: [maskedMeta()] }\n },\n weight: (t) => {\n if (t.name === \"write\") return 1.25\n if (t.name === \"edit\") return 1.2\n if (t.result?.meta?.full) return 1.1\n return 1\n },\n}\n\nexport type ContextScoringOptions = {\n tools?: Record<AnyTool | \"*\", ToolRule>\n parts?: MaskRule[]\n}\n\nconst defaults: ContextScoringOptions = {\n parts: [\n {\n filter: (part) => isAttachment(part.part),\n mask: (part) => maskedMeta(part.part.type, \"attach\"),\n },\n {\n filter: (part) => part.message.role === \"system\" && part.message.meta?.kind === \"task\",\n halfLife: 25,\n mask: (part) => {\n if (part.part.type === \"meta\") return part.part\n return maskedMeta(\"task\")\n },\n },\n ],\n // oxlint-disable-next-line sort-keys\n tools: {\n bash: {\n gamma: 0.35,\n halfLife: 25,\n key: (t: ToolGroup<BashTool>) => `bash:${t.params?.command?.replace(/\\s+/g, \" \") ?? t.id}`,\n },\n find: { gamma: 0.7, halfLife: 35 },\n grep: { gamma: 0.7, halfLife: 35 },\n search: { gamma: 0.5, halfLife: 40 },\n\n read: fileScore,\n edit: fileScore,\n write: fileScore,\n\n \"*\": { gamma: 0.5, halfLife: 40 },\n } as Record<AnyTool | \"*\", ToolRule>,\n}\n\nexport class ContextScoring {\n #groups = new Map<string, MaskGroup>()\n #messages: readonly Message[] = []\n #policies: MaskPolicy[] = []\n\n constructor(opts: ContextScoringOptions = {}) {\n const tools = { ...defaults.tools, ...opts.tools }\n for (const [name, tool] of Object.entries(tools)) {\n this.#policies.push(new ToolPolicy(name, tool) as MaskPolicy)\n }\n for (const rule of [...(defaults.parts ?? []), ...(opts.parts ?? [])]) {\n this.#policies.push(new MaskPolicy(rule))\n }\n }\n\n #reset() {\n this.#groups.clear()\n }\n\n #index() {\n let turn = 0\n for (let $m = this.#messages.length - 1; $m >= 0; $m--) {\n const m = this.#messages[$m]\n if (m.role === \"assistant\") turn++\n\n if (!Array.isArray(m.content)) continue\n\n for (let $p = m.content.length - 1; $p >= 0; $p--) {\n const part: MsgPart = { $m, $p, message: m, part: m.content[$p], score: 0, turn }\n const policy = this.#policy(part)\n if (!policy) continue\n const id = policy.id(part)\n const group = this.#groups.get(id) ?? { id, key: \"\", parts: [], policy }\n policy.update(group, part)\n this.#groups.set(id, group)\n }\n }\n }\n\n #policy(part: MsgPart): MaskPolicy | undefined {\n return this.#policies.find((policy) => policy.filter(part))\n }\n\n score(messages: readonly Message[]): MaskGroup[] {\n this.#reset()\n this.#messages = messages\n this.#index()\n // tools sorted by recency\n const groups = [...this.#groups.values()]\n const gammas = new Map<string, number>()\n for (const group of groups) {\n const key = group.policy.key(group)\n group.key = key\n const weight = group.policy.weight(group)\n const shadow = gammas.get(key) ?? 1\n const lambda = group.policy.lambda(group)\n for (const part of group.parts) {\n const decay = Math.exp(-lambda * part.turn)\n part.score = decay * weight * shadow\n }\n gammas.set(key, shadow * group.policy.gamma(group))\n }\n return groups\n }\n}\n","import type { AnyPart, Message, Role, Tool } from \"@zaly/ai\"\nimport type { MaybeGetter } from \"@zaly/shared\"\nimport type { Agent } from \"./agent.ts\"\nimport type { MsgPart } from \"./context/scoring.ts\"\nimport type { Session } from \"./session/session.ts\"\n\nimport { toValue } from \"@zaly/shared\"\nimport { ContextScoring } from \"./context/scoring.ts\"\nimport { estimatePart, tokenStats } from \"./context/tokens.ts\"\n\n/** Top-level masking config. */\nexport type MaskerOptions = {\n enabled?: boolean\n /** Don't mask tool-result parts whose original content is shorter\n * than this (estimated tokens). Skips tiny \"ok\"-style success\n * messages where the stub would be larger than the original.\n * Doesn't apply to attachments (always worth masking). */\n minTokens?: number\n /** How many turns to keep in the tail of the conversation, regardless\n * of score. Defaults to 20. */\n keepTurns?: number\n /** How far above the target ratio to trigger a new masking pass.\n * Defaults to 0.25 (25%). */\n delta?: number\n /** Target ratio of used/limit tokens to reach by masking. Defaults to 0.5 (50%). */\n target?: number\n}\n\nexport type MaskOpts = {\n force?: boolean\n limit?: number\n ratio?: number\n tools?: Tool[]\n prompt?: string[]\n}\n\nconst defaults = {\n delta: 0.25,\n enabled: true,\n keepTurns: 40,\n minTokens: 50,\n target: 0.5,\n} as const satisfies Required<MaskerOptions>\n\n/** In-place mask projection for the request stream.\n *\n * The masker replaces low-value parts with stable stubs to keep the\n * projected request within budget. A scoring pass ranks maskable parts\n * by recency, repeated-use shadowing, and per-policy weights; the budget\n * pass masks from lowest score upward until the target is reached or no\n * useful candidates remain.\n *\n * Masking intentionally happens rarely because it changes historical\n * bytes and busts prefix cache. The first pass after startup/session\n * reset always rebuilds the projection. Later passes use a hysteresis\n * threshold: mask toward `target`, then trigger again at roughly\n * `actual + delta`. If the target cannot be reached, the next threshold\n * rises automatically to avoid repeated no-op cache busts.\n */\nexport class Masker {\n #o: MaybeGetter<MaskerOptions>\n #stats = new Map<Role, Record<string, number>>()\n #masked = new Map<string, Map<number, AnyPart>>()\n #threshold?: number\n #agent: Agent\n\n constructor(agent: Agent, opts: MaybeGetter<MaskerOptions> = {}) {\n this.#o = opts\n this.#agent = agent\n agent.on(\"context\", async (ctx) => (ctx.messages = await this.mask(ctx.messages)))\n agent.ctx.on(\"session\", () => this.reset())\n }\n\n get #opts(): Required<MaskerOptions> {\n return { ...defaults, ...toValue(this.#o) }\n }\n\n get enabled(): boolean {\n return this.#opts.enabled\n }\n\n get #session(): Session {\n return this.#agent.session\n }\n\n #maskOpts(opts?: MaskOpts): Required<MaskOpts> {\n const pressure = this.#agent.pressure\n return {\n force: false,\n limit: pressure.limit,\n prompt: this.#agent.prompt,\n ratio: pressure.ratio,\n tools: this.#agent.tools,\n ...opts,\n }\n }\n\n reset(): void {\n this.#masked.clear()\n this.#stats.clear()\n this.#threshold = undefined\n }\n\n get stats(): Map<Role, Record<string, number>> {\n return this.#stats\n }\n\n /** Number of messages with at least one masked part. */\n get masked(): number {\n return this.#masked.size\n }\n\n isMasked(msgId: string, partIdx?: number): boolean {\n if (!this.enabled) return false\n const parts = this.#masked.get(msgId)\n if (parts === undefined) return false\n return partIdx === undefined ? parts.size > 0 : parts.has(partIdx)\n }\n\n /** Apply masks and return the projected message array.\n *\n * Always rebuild once after startup/session reset: `pressure.ratio`\n * may describe a previously masked projection, while this instance has\n * no mask decisions yet. After that, rebuild only when projected\n * pressure crosses the hysteresis threshold. */\n async mask(messages: readonly Message[], opts?: MaskOpts): Promise<Message[]> {\n if (!this.enabled || messages.length === 0) return [...messages]\n\n const resolved = this.#maskOpts(opts)\n\n // Masker has not yet been initialized for this session.\n // Use the session's mask checkpoint to restore the last threshold\n // and rebuild the mask decisions up to that point.\n if (this.#threshold === undefined) {\n const cp = this.#session.maskCheckpoint\n if (!cp) {\n // No checkpoint means the session has never been masked before, so\n // just set to the default threshold for the first pass, so that the next pass will\n // trigger only if the pressure exceeds the target + delta.\n this.#threshold = this.#opts.target + this.#opts.delta\n } else {\n this.#threshold = cp.threshold\n const idx = messages.findIndex((m) => m.id === cp.messageId)\n // NOTE: the checkpoint message should exists. If not mask all, just in case\n // Rebuild the mask decisions\n this.#update(idx === -1 ? messages : messages.slice(0, idx + 1), resolved)\n }\n }\n\n // If the current pressure ratio exceeds the threshold, rebuild the mask decisions.\n if (resolved.ratio >= this.#threshold || resolved.force) {\n const messageId = messages.at(-1)?.id\n if (!messageId) throw new Error(\"Message in masker without ID. Should never happen.\")\n // Create a checkpoint first, with the CURRENT threshold, not the new one.\n await this.#session.addMaskCheckpoint({\n messageId,\n threshold: this.#threshold,\n })\n this.#update(messages, resolved)\n }\n return this.#mask(messages)\n }\n\n #stat(role: Role, key: string, n = 1): void {\n const r = this.#stats.get(role) ?? {}\n r[key] = (r[key] ?? 0) + n\n this.#stats.set(role, r)\n }\n\n /** Recompute mask decisions for the current message history. */\n #update(messages: readonly Message[], opts: Required<MaskOpts>): void {\n this.#masked.clear()\n this.#stats.clear()\n this.#threshold = this.#opts.target + this.#opts.delta\n const usage = tokenStats(messages, { prompt: opts.prompt, tools: opts.tools })\n // `pressure.ratio` is based on the current projected/masked request,\n // not the raw session. Raw history may exceed model context; masking\n // only needs to keep the projected request in a safe band.\n const targetRatio = Math.max(this.#opts.target, this.#threshold - this.#opts.delta)\n const target = opts.limit * targetRatio\n\n // Return when already under target.\n if (usage.tokens <= target) return\n\n let mask = usage.tokens - target\n\n if (mask === 0) return\n\n const scorer = new ContextScoring()\n const scores = scorer.score(messages)\n const parts: (MsgPart & { mask: () => AnyPart })[] = []\n for (const s of scores) {\n for (const p of s.parts) {\n parts.push({\n ...p,\n mask: () => s.policy.mask(p, s),\n })\n }\n }\n parts.sort((a, b) => a.score - b.score)\n\n let masked = 0\n for (const p of parts) {\n if (mask <= 0) break\n const id = p.message.id\n if (!id) continue\n\n if (p.turn <= this.#opts.keepTurns) continue\n\n const tokens = estimatePart(p.part).tokens\n if (tokens < this.#opts.minTokens) continue\n\n const part = p.mask()\n if (part === p.part) continue\n\n const msgParts = this.#masked.get(id) ?? new Map()\n msgParts.set(p.$p, part)\n this.#stat(\n p.message.role,\n `${p.part.type}${p.part.type === \"tool-result\" || p.part.type === \"tool-call\" ? `:${p.part.name}` : \"\"}`\n )\n this.#masked.set(id, msgParts)\n const delta = tokens - estimatePart(part).tokens\n masked += delta\n mask -= delta\n }\n\n this.#threshold = Math.max(\n (usage.tokens - masked) / opts.limit + this.#opts.delta,\n this.#opts.target + this.#opts.delta\n )\n }\n\n /** Render the current projection using the last computed mask decisions. */\n #mask(messages: readonly Message[]): Message[] {\n return messages.map((m) => {\n if (m.id === undefined || typeof m.content === \"string\") return m\n const masked = m.id ? this.#masked.get(m.id) : undefined\n return masked && masked.size > 0\n ? ({\n ...m,\n content: m.content.map((p, i) => masked.get(i) ?? p),\n } as Message)\n : m\n })\n }\n}\n"],"mappings":"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"}
|