@zaly/agent 0.0.11 → 0.1.0

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.
Files changed (47) hide show
  1. package/dist/{agent-D3wVIibN.mjs → agent-D5NrjvG5.mjs} +16 -16
  2. package/dist/agent-D5NrjvG5.mjs.map +1 -0
  3. package/dist/{bash-COgq4W7U.mjs → bash-9x8PPg0A.mjs} +6 -6
  4. package/dist/bash-9x8PPg0A.mjs.map +1 -0
  5. package/dist/{compactions-B8VdZJoU.mjs → compactions-D9XlwQGO.mjs} +4 -3
  6. package/dist/{compactions-B8VdZJoU.mjs.map → compactions-D9XlwQGO.mjs.map} +1 -1
  7. package/dist/fetch-D0IeDN6H.mjs +159 -0
  8. package/dist/fetch-D0IeDN6H.mjs.map +1 -0
  9. package/dist/{find-CimUGn-L.mjs → find-D1Z2ac_b.mjs} +28 -10
  10. package/dist/find-D1Z2ac_b.mjs.map +1 -0
  11. package/dist/{grep-6O3mjSvV.mjs → grep-By4wLRDk.mjs} +17 -7
  12. package/dist/grep-By4wLRDk.mjs.map +1 -0
  13. package/dist/html-J629LNIm.mjs +33225 -0
  14. package/dist/html-J629LNIm.mjs.map +1 -0
  15. package/dist/index.d.mts +77 -21
  16. package/dist/index.d.mts.map +1 -1
  17. package/dist/index.mjs +2 -2
  18. package/dist/{manager-BqlbCGYV.mjs → manager-BJLjayBz.mjs} +2 -2
  19. package/dist/{manager-BqlbCGYV.mjs.map → manager-BJLjayBz.mjs.map} +1 -1
  20. package/dist/{masker-DRFztUwc.mjs → masker-jgSUXzjY.mjs} +2 -2
  21. package/dist/{masker-DRFztUwc.mjs.map → masker-jgSUXzjY.mjs.map} +1 -1
  22. package/dist/{read-CSL_8nAk.mjs → read-B-hwcc2Q.mjs} +7 -4
  23. package/dist/read-B-hwcc2Q.mjs.map +1 -0
  24. package/dist/{registry-L9GDccS0.mjs → registry-Zo0QmKdR.mjs} +14 -16
  25. package/dist/registry-Zo0QmKdR.mjs.map +1 -0
  26. package/dist/rolldown-runtime-DktXgydw.mjs +35 -0
  27. package/dist/{search-CRw247ES.mjs → search-CH0D5CkV.mjs} +19 -15
  28. package/dist/search-CH0D5CkV.mjs.map +1 -0
  29. package/dist/{session-BSvUrr12.mjs → session-CACkHyDR.mjs} +2 -2
  30. package/dist/{session-BSvUrr12.mjs.map → session-CACkHyDR.mjs.map} +1 -1
  31. package/dist/session.mjs +1 -1
  32. package/dist/{src-fK6DL2bg.mjs → src-DoXC_gDY.mjs} +15 -14
  33. package/dist/{src-fK6DL2bg.mjs.map → src-DoXC_gDY.mjs.map} +1 -1
  34. package/dist/{subagent-C8XCEecM.mjs → subagent-BMQMbANL.mjs} +2 -2
  35. package/dist/{subagent-C8XCEecM.mjs.map → subagent-BMQMbANL.mjs.map} +1 -1
  36. package/dist/{tools-wvkkuXWH.mjs → tools-CJ2x45EZ.mjs} +3 -34
  37. package/dist/{tools-wvkkuXWH.mjs.map → tools-CJ2x45EZ.mjs.map} +1 -1
  38. package/package.json +46 -8
  39. package/dist/agent-D3wVIibN.mjs.map +0 -1
  40. package/dist/bash-COgq4W7U.mjs.map +0 -1
  41. package/dist/fetch-VKhSWdEh.mjs +0 -111
  42. package/dist/fetch-VKhSWdEh.mjs.map +0 -1
  43. package/dist/find-CimUGn-L.mjs.map +0 -1
  44. package/dist/grep-6O3mjSvV.mjs.map +0 -1
  45. package/dist/read-CSL_8nAk.mjs.map +0 -1
  46. package/dist/registry-L9GDccS0.mjs.map +0 -1
  47. package/dist/search-CRw247ES.mjs.map +0 -1
package/dist/index.d.mts CHANGED
@@ -2,7 +2,7 @@ import { p as SessionOptions, t as Session } from "./session-Ds5nJz5A.mjs";
2
2
  import { Emitter, Envelope, MaybeGetter } from "@zaly/shared";
3
3
  import { BaseCollection } from "@zaly/shared/collection";
4
4
  import { AnyKey } from "@zaly/shared/registry";
5
- import { AiError, AnyPart, AnyType, AssistantMessage, CollectOptions, Content, FinishReason, Message, MetaOf, MetaPart, Model, ParamsOf, ReasoningEffort, Role, StreamEvent, StreamOptions, TokenCount as TokenCount$1, Tool, ToolCallPart, ToolContext, ToolResult, ToolResultPart, Usage } from "@zaly/ai";
5
+ import { AiError, AnyPart, AnyType, AssistantMessage, Attachment, CollectOptions, Content, FinishReason, Message, MetaOf, MetaPart, Model, ParamsOf, ReasoningEffort, Role, StreamEvent, StreamOptions, TokenCount as TokenCount$1, Tool, ToolCallPart, ToolContext, ToolResult, ToolResultPart, Usage } from "@zaly/ai";
6
6
  import { Logger } from "@zaly/shared/logger";
7
7
  import { ArgsOpts, ArgsResult } from "@zaly/shared/args";
8
8
  //#region src/events.d.ts
@@ -67,6 +67,7 @@ type AgentEvents = {
67
67
  };
68
68
  context: {
69
69
  cacheKey?: string;
70
+ sessionId?: string;
70
71
  prompt: string[];
71
72
  tools: Tool[];
72
73
  messages: Message[];
@@ -807,10 +808,14 @@ declare class Agent extends Emitter<AgentEvents> {
807
808
  * tools (`task_list`, etc.). Mutating directly is a foot-gun;
808
809
  * prefer the agent's higher-level surface. */
809
810
  get tasks(): Tasks;
810
- /** Append a message and (re)start the loop. The user spoke; the agent
811
- * responds. If a loop is currently running, the message lands on the
812
- * follow-up queue and is processed after the current turn naturally
813
- * stops. Otherwise the loop starts immediately. */
811
+ /** Queue messages and start/resume the loop when idle or paused.
812
+ * `inject` (default) delivers before the next model step without
813
+ * interrupting an active response or tool batch. `append` waits until
814
+ * the current turn finishes naturally and no injections remain.
815
+ * Injections batch in queue order; appends run one send() submission
816
+ * at a time, keeping each submission's messages together.
817
+ * With `run: false`, only enqueue; omit messages to start/resume
818
+ * processing already queued work. */
814
819
  send(message?: Message | Message[], opts?: {
815
820
  mode?: SendMode;
816
821
  run?: boolean;
@@ -1301,6 +1306,31 @@ interface AgentOptions extends CollectOptions {
1301
1306
  loadModel?: (id: string) => Promise<Model>;
1302
1307
  }
1303
1308
  //#endregion
1309
+ //#region src/utils/file.d.ts
1310
+ type FileMeta = {
1311
+ path: string;
1312
+ mtime: number;
1313
+ kind: "read" | "write" | "edit";
1314
+ /** True when the result reflects the whole-file content — set by
1315
+ * un-sliced `read` and by `write` (which always replaces the
1316
+ * full file). Used by the masker to know whether this result
1317
+ * subsumes earlier reads of the same path. `edit` never sets it
1318
+ * because the result is patch-relative. */
1319
+ full?: boolean;
1320
+ unchanged?: boolean;
1321
+ };
1322
+ type FileTool = Tool<{
1323
+ path: string;
1324
+ }, unknown, FileMeta>;
1325
+ declare function assertFresh(path: string, ctx: ToolContext): void;
1326
+ declare function isUnchanged(path: string, ctx: ToolContext): boolean;
1327
+ declare function checkFresh(path: string, ctx: ToolContext, opts?: {
1328
+ full?: boolean;
1329
+ }): AiError | true;
1330
+ /** Build the canonical freshness error. `data.reason` distinguishes
1331
+ * never-read from changed-since-read for callers and renderers. */
1332
+ declare function freshnessError(path: string, reason: "NOT_READ" | "STALE"): AiError;
1333
+ //#endregion
1304
1334
  //#region src/prompt/registry.d.ts
1305
1335
  export interface PromptCtx {
1306
1336
  model: Model;
@@ -1392,24 +1422,10 @@ type BashTool = typeof bashTool;
1392
1422
  declare const bashTool: import("@zaly/ai").Tool<{
1393
1423
  command: string;
1394
1424
  description?: string | undefined;
1395
- max_lines?: number | undefined;
1425
+ limit?: number | undefined;
1396
1426
  timeout?: number | undefined;
1397
1427
  }, unknown, object>;
1398
1428
  //#endregion
1399
- //#region src/utils/file.d.ts
1400
- type FileMeta = {
1401
- path: string;
1402
- mtime: number;
1403
- kind: "read" | "write" | "edit";
1404
- /** True when the result reflects the whole-file content — set by
1405
- * un-sliced `read` and by `write` (which always replaces the
1406
- * full file). Used by the masker to know whether this result
1407
- * subsumes earlier reads of the same path. `edit` never sets it
1408
- * because the result is patch-relative. */
1409
- full?: boolean;
1410
- unchanged?: boolean;
1411
- };
1412
- //#endregion
1413
1429
  //#region src/tools/edit.d.ts
1414
1430
  type EditTool = typeof editTool;
1415
1431
  type EditToolMeta = FileMeta & {
@@ -1444,6 +1460,46 @@ declare const editTool: import("@zaly/ai").Tool<{
1444
1460
  }[];
1445
1461
  }, unknown, EditToolMeta>;
1446
1462
  //#endregion
1463
+ //#region src/tools/fetch.d.ts
1464
+ type FetchTool = typeof fetchTool;
1465
+ type FetchToolMeta = {
1466
+ kind: "fetch";
1467
+ url: string;
1468
+ type: "text" | Attachment["type"];
1469
+ format?: string;
1470
+ title?: string;
1471
+ status: number;
1472
+ statusText: string;
1473
+ ok: boolean;
1474
+ contentType?: string;
1475
+ durationMs: number;
1476
+ warning?: string;
1477
+ truncated?: {
1478
+ fullOutputPath: string;
1479
+ totalBytes: number;
1480
+ totalLines: number;
1481
+ };
1482
+ };
1483
+ /**
1484
+ * Fetch APIs or static pages. HTML becomes readable Markdown; raw mode
1485
+ * preserves the response text. Conversion never runs page scripts.
1486
+ * Truncated text is saved in full alongside the session, like bash output.
1487
+ *
1488
+ * Permissions: routes through the `fetch` scope when the agent wires up
1489
+ * tool-side permission checks. Until then, callers should restrict via
1490
+ * a wrapping permission layer (rule-based or explicit allowlist).
1491
+ */
1492
+ declare const fetchTool: import("@zaly/ai").Tool<{
1493
+ url: string;
1494
+ method?: "DELETE" | "GET" | "PATCH" | "POST" | "PUT" | undefined;
1495
+ headers?: Record<string, string> | undefined;
1496
+ query?: Record<string, string> | undefined;
1497
+ body?: string | undefined;
1498
+ format?: "auto" | "markdown" | "raw" | undefined;
1499
+ limit?: number | undefined;
1500
+ jsonpath?: string | undefined;
1501
+ }, unknown, FetchToolMeta>;
1502
+ //#endregion
1447
1503
  //#region src/tools/find.d.ts
1448
1504
  type FindTool = typeof findTool;
1449
1505
  type FindToolMeta = {
@@ -1589,5 +1645,5 @@ export declare function tokenStats(msgs: readonly Message[], opts?: {
1589
1645
  }): TokenStats;
1590
1646
  export declare function formatTokenStats(s: TokenStats, indent?: number): string;
1591
1647
  //#endregion
1592
- export type { Agent, AgentContext, AgentContextEvents, AgentEvents, AgentInit, AgentOptions, AgentStatus, AgentStop, AgentStopKind, AnyScope, BashTool, BuiltinScope, CheckResult, ContextPressure, DoneTaskInfo, EditTool, EditToolMeta, FindTool, FindToolMeta, GrepTool, GrepToolMeta, PermissionContext, PermissionHandler, PermissionManager, PermissionOptions, PermissionPreset, PermissionPresetName, PermissionRequest, PermissionScope, PermissionScopes, PromptCollection, ReadTool, ReadToolMeta, Rule, SendMode, StepKind, StepResult, StopOptions, StopPolicy, Suggestion, TaskInfo, TaskMeta, TaskStatus, Tasks, TasksEvents, TokenUsage, ToolCollection, TurnResult, Verdict, WriteTool, WriteToolMeta, addUsage, createAgentContext, taskCompletionMessage, taskInfoPart };
1648
+ export type { Agent, AgentContext, AgentContextEvents, AgentEvents, AgentInit, AgentOptions, AgentStatus, AgentStop, AgentStopKind, AnyScope, BashTool, BuiltinScope, CheckResult, ContextPressure, DoneTaskInfo, EditTool, EditToolMeta, FetchTool, FetchToolMeta, FileMeta, FileTool, FindTool, FindToolMeta, GrepTool, GrepToolMeta, PermissionContext, PermissionHandler, PermissionManager, PermissionOptions, PermissionPreset, PermissionPresetName, PermissionRequest, PermissionScope, PermissionScopes, PromptCollection, ReadTool, ReadToolMeta, Rule, SendMode, StepKind, StepResult, StopOptions, StopPolicy, Suggestion, TaskInfo, TaskMeta, TaskStatus, Tasks, TasksEvents, TokenUsage, ToolCollection, TurnResult, Verdict, WriteTool, WriteToolMeta, addUsage, assertFresh, checkFresh, createAgentContext, freshnessError, isUnchanged, taskCompletionMessage, taskInfoPart };
1593
1649
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":["TokenCount","TokenCount","builtin","TokenCount","TokenCount","builtin"],"sources":["../src/events.ts","../src/masker.ts","../src/permissions/types.ts","../src/permissions/presets.ts","../src/permissions/manager.ts","../src/skills.ts","../src/swarm.ts","../src/ctx.ts","../src/tasks.ts","../src/utils/usage.ts","../src/agent.ts","../src/compaction/utils.ts","../src/compaction/compactions.ts","../src/notify.ts","../src/permissions/handlers/registry.ts","../src/stop.ts","../src/types.ts","../src/prompt/registry.ts","../src/tools/registry.ts","../src/tools/bash.ts","../src/utils/file.ts","../src/tools/edit.ts","../src/tools/find.ts","../src/tools/grep.ts","../src/tools/read.ts","../src/tools/write.ts","../src/utils/uuid.ts","../src/commands.ts","../src/context/tokens.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":["TokenCount","TokenCount","builtin","TokenCount","TokenCount","builtin"],"sources":["../src/events.ts","../src/masker.ts","../src/permissions/types.ts","../src/permissions/presets.ts","../src/permissions/manager.ts","../src/skills.ts","../src/swarm.ts","../src/ctx.ts","../src/tasks.ts","../src/utils/usage.ts","../src/agent.ts","../src/compaction/utils.ts","../src/compaction/compactions.ts","../src/notify.ts","../src/permissions/handlers/registry.ts","../src/stop.ts","../src/types.ts","../src/utils/file.ts","../src/prompt/registry.ts","../src/tools/registry.ts","../src/tools/bash.ts","../src/tools/edit.ts","../src/tools/fetch.ts","../src/tools/find.ts","../src/tools/grep.ts","../src/tools/read.ts","../src/tools/write.ts","../src/utils/uuid.ts","../src/commands.ts","../src/context/tokens.ts"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as extractConversation, c as formatFileUsage, d as Commands, f as toolCollection, g as createAgent, h as promptRegistry, i as extractBashUsage, l as messageTail, m as promptCollection, n as formatTokenStats, o as extractFileUsage, p as toolRegistry, r as tokenStats, s as formatBashUsage, t as estimatePart, u as Skills } from "./src-fK6DL2bg.mjs";
2
- import { t as handlerRegistry } from "./registry-L9GDccS0.mjs";
1
+ import { a as extractConversation, c as formatFileUsage, d as Commands, f as toolCollection, g as createAgent, h as promptRegistry, i as extractBashUsage, l as messageTail, m as promptCollection, n as formatTokenStats, o as extractFileUsage, p as toolRegistry, r as tokenStats, s as formatBashUsage, t as estimatePart, u as Skills } from "./src-DoXC_gDY.mjs";
2
+ import { t as handlerRegistry } from "./registry-Zo0QmKdR.mjs";
3
3
  import { n as isUuidv7Like, r as uuidv7, t as isUuidv7 } from "./uuid-CAdJg4l8.mjs";
4
4
  export { Commands, Skills, createAgent, estimatePart, extractBashUsage, extractConversation, extractFileUsage, formatBashUsage, formatFileUsage, formatTokenStats, handlerRegistry, isUuidv7, isUuidv7Like, messageTail, promptCollection, promptRegistry, tokenStats, toolCollection, toolRegistry, uuidv7 };
@@ -1,4 +1,4 @@
1
- import { t as handlerRegistry } from "./registry-L9GDccS0.mjs";
1
+ import { t as handlerRegistry } from "./registry-Zo0QmKdR.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. */
@@ -278,4 +278,4 @@ function parseRules(rules) {
278
278
  //#endregion
279
279
  export { PermissionManager };
280
280
 
281
- //# sourceMappingURL=manager-BqlbCGYV.mjs.map
281
+ //# sourceMappingURL=manager-BJLjayBz.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"manager-BqlbCGYV.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 /** True only when a blanket allow has no earlier restrictive exceptions. */\n allowsAll(scope: string): boolean {\n for (const rule of this.#rules) {\n if (rule.scope !== scope) continue\n if (rule.policy !== \"allow\") return false\n if (rule.pattern === \"*\") return true\n }\n return false\n }\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 allowsAll: this.allowsAll.bind(this),\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;;CAKA,UAAU,OAAwB;EAChC,KAAK,MAAM,QAAQ,KAAK,QAAQ;GAC9B,IAAI,KAAK,UAAU,OAAO;GAC1B,IAAI,KAAK,WAAW,SAAS,OAAO;GACpC,IAAI,KAAK,YAAY,KAAK,OAAO;EACnC;EACA,OAAO;CACT;CAEA,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,WAAW,KAAK,UAAU,KAAK,IAAI;GACnC,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
+ {"version":3,"file":"manager-BJLjayBz.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 /** True only when a blanket allow has no earlier restrictive exceptions. */\n allowsAll(scope: string): boolean {\n for (const rule of this.#rules) {\n if (rule.scope !== scope) continue\n if (rule.policy !== \"allow\") return false\n if (rule.pattern === \"*\") return true\n }\n return false\n }\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 allowsAll: this.allowsAll.bind(this),\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;;CAKA,UAAU,OAAwB;EAChC,KAAK,MAAM,QAAQ,KAAK,QAAQ;GAC9B,IAAI,KAAK,UAAU,OAAO;GAC1B,IAAI,KAAK,WAAW,SAAS,OAAO;GACpC,IAAI,KAAK,YAAY,KAAK,OAAO;EACnC;EACA,OAAO;CACT;CAEA,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,WAAW,KAAK,UAAU,KAAK,IAAI;GACnC,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,4 +1,4 @@
1
- import { r as tokenStats, t as estimatePart } from "./src-fK6DL2bg.mjs";
1
+ import { r as tokenStats, t as estimatePart } from "./src-DoXC_gDY.mjs";
2
2
  import { safeStringify, toValue } from "@zaly/shared";
3
3
  import { isAttachment, safeParseToolParams } from "@zaly/ai";
4
4
  //#region src/context/scoring.ts
@@ -393,4 +393,4 @@ var Masker = class {
393
393
  //#endregion
394
394
  export { Masker };
395
395
 
396
- //# sourceMappingURL=masker-DRFztUwc.mjs.map
396
+ //# sourceMappingURL=masker-jgSUXzjY.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"masker-DRFztUwc.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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"}
1
+ {"version":3,"file":"masker-jgSUXzjY.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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"}
@@ -23,7 +23,7 @@ function checkFresh(path, ctx, opts = {}) {
23
23
  let ret = freshnessError(path, "NOT_READ");
24
24
  for (const { m, $p, p } of extractToolResults(messages)) {
25
25
  const id = m.id;
26
- if (!id || !isFileMeta(p.meta) || ctx.isMasked?.(id, $p)) continue;
26
+ if (!id || !isFileMeta(p.meta) || p.meta.path !== path || ctx.isMasked?.(id, $p)) continue;
27
27
  if (opts.full && !p.meta.full) continue;
28
28
  if (p.meta.mtime === mtime) return true;
29
29
  ret = freshnessError(path, "STALE");
@@ -162,13 +162,16 @@ function formatTextSlice(content, { offset, limit }) {
162
162
  if (start >= lines.length) start = lines.length;
163
163
  const end = Math.min(lines.length, start + limit);
164
164
  const out = [];
165
+ let full = start === 0 && end === lines.length;
165
166
  for (let i = start; i < end; i++) {
166
167
  let line = lines[i];
167
- if (line.length > MAX_LINE_LENGTH) line = `${line.slice(0, MAX_LINE_LENGTH)}… [line truncated, ${line.length} chars]`;
168
+ if (line.length > MAX_LINE_LENGTH) {
169
+ full = false;
170
+ line = `${line.slice(0, MAX_LINE_LENGTH)}… [line truncated, ${line.length} chars]`;
171
+ }
168
172
  out.push(line);
169
173
  }
170
174
  const text = out.join("\n");
171
- const full = start === 0 && end === lines.length;
172
175
  return {
173
176
  content: [{
174
177
  content: start === end && lines.length > 1 ? `offset ${offset} past end of file (${lines.length} lines)` : `showing ${start + 1}-${end} of ${lines.length}`,
@@ -186,4 +189,4 @@ function formatTextSlice(content, { offset, limit }) {
186
189
  //#endregion
187
190
  export { readTool };
188
191
 
189
- //# sourceMappingURL=read-CSL_8nAk.mjs.map
192
+ //# sourceMappingURL=read-B-hwcc2Q.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-B-hwcc2Q.mjs","names":[],"sources":["../src/utils/file.ts","../src/tools/read.ts"],"sourcesContent":["import type { Tool, ToolContext } from \"@zaly/ai\"\n\nimport { AiError, extractToolResults } from \"@zaly/ai\"\nimport { normPath, safeStat } from \"@zaly/shared\"\n\nexport type FileMeta = {\n path: string\n mtime: number\n kind: \"read\" | \"write\" | \"edit\"\n /** True when the result reflects the whole-file content — set by\n * un-sliced `read` and by `write` (which always replaces the\n * full file). Used by the masker to know whether this result\n * subsumes earlier reads of the same path. `edit` never sets it\n * because the result is patch-relative. */\n full?: boolean\n unchanged?: boolean\n}\n\nexport type FileTool = Tool<{ path: string }, unknown, FileMeta>\n\nfunction isFileMeta(meta: unknown): meta is FileMeta {\n const m = meta as Partial<Record<string, unknown>> | undefined | null\n return (\n m !== undefined &&\n m !== null &&\n typeof m === \"object\" &&\n typeof m.path === \"string\" &&\n typeof m.mtime === \"number\" &&\n (m.kind === \"read\" || m.kind === \"write\" || m.kind === \"edit\")\n )\n}\n\nexport function assertFresh(path: string, ctx: ToolContext) {\n const err = checkFresh(path, ctx)\n if (err !== true) throw err\n}\n\nexport function isUnchanged(path: string, ctx: ToolContext) {\n return checkFresh(path, ctx, { full: true }) === true\n}\n\nexport function checkFresh(\n path: string,\n ctx: ToolContext,\n opts: { full?: boolean } = {}\n): AiError | true {\n path = normPath(ctx.cwd, path)\n const mtime = safeStat(path)?.mtimeMs\n if (mtime === undefined)\n return new AiError({ code: \"NOT_FOUND\", message: `${path}: file not found` })\n const messages = ctx.messages ?? []\n let ret: AiError = freshnessError(path, \"NOT_READ\")\n\n for (const { m, $p, p } of extractToolResults<FileMeta>(messages)) {\n const id = m.id\n if (!id || !isFileMeta(p.meta) || p.meta.path !== path || ctx.isMasked?.(id, $p)) continue\n if (opts.full && !p.meta.full) continue\n if (p.meta.mtime === mtime) return true // Fresh! The file's mtime matches what we saw at read time.\n ret = freshnessError(path, \"STALE\")\n }\n return ret\n}\n\n/** Build the canonical freshness error. `data.reason` distinguishes\n * never-read from changed-since-read for callers and renderers. */\nexport function freshnessError(path: string, reason: \"NOT_READ\" | \"STALE\"): AiError {\n return new AiError({\n code: \"FILE_NOT_FRESH\",\n data: { path, reason },\n message:\n reason === \"NOT_READ\"\n ? `${path}: read this file before mutating it.`\n : `${path}: file changed since last read. Re-read before mutating.`,\n })\n}\n","import type { Content, ToolContext } from \"@zaly/ai\"\nimport type { FileMeta } from \"../utils/file.ts\"\n\nimport { AiError, defineTool, toAttachment } from \"@zaly/ai\"\nimport { normPath } from \"@zaly/shared\"\nimport { fileDetect } from \"@zaly/shared/detect\"\nimport { normalizeEol } from \"@zaly/shared/text\"\nimport { stat } from \"node:fs/promises\"\nimport { Type } from \"typebox\"\nimport { isUnchanged } from \"../utils/file.ts\"\n\n/**\n * Read a file from disk.\n *\n * Output shape:\n * - Text files → plain text plus a `<slice>` metadata part describing\n * the returned line range, including when reading the whole file.\n * - Image files (png, jpeg, webp, gif, avif, …) → `ImagePart` attachment.\n * The model sees the image natively if its modality supports it.\n * - Other binary files → `BINARY_FILE` error with a hint.\n *\n * Path resolution: relative paths resolve against the agent's session\n * cwd via `process.cwd()` for now. When the permission system gates this\n * via a tool-side hook, the cwd from `PermissionContext` will be used\n * instead so resolution stays consistent across the loop.\n */\nconst DEFAULT_LIMIT = 2000\nconst MAX_LINE_LENGTH = 2000\n\nexport type ReadTool = typeof readTool\n\nexport type ReadToolMeta = FileMeta & {\n offset: number\n limit: number\n}\n\n/** Build the `read` tool with model-aware schema description. The\n * attachment-shape blurb is included only when the loaded model\n * actually accepts the relevant modality — text-only models see a\n * description that promises only text output, so they don't reach\n * for the tool expecting images they can't process. */\n// oxlint-disable-next-line sort-keys -- semantic field order: name, desc, params, call\nexport const readTool = defineTool({\n name: \"read\",\n desc: `Read a file. Text files return plain text with line-range metadata. Image & PDF files are sent as attachments. Use \\`offset\\`/\\`limit\\` to slice large files.`,\n parallel: true,\n // oxlint-disable-next-line sort-keys -- semantic param order\n params: Type.Object({\n path: Type.String({ description: \"Path to the file. Absolute or cwd-relative.\" }),\n offset: Type.Optional(\n Type.Integer({\n default: 1,\n description:\n \"1-based line number to start at. Negative values count from \" +\n \"the end: -50 starts 50 lines before EOF (so `offset: -50` \" +\n \"with the default limit returns the last 50 lines, like \" +\n \"`tail -n 50`). `offset: -50, limit: 20` reads 20 lines \" +\n \"starting 50 from the end.\",\n })\n ),\n limit: Type.Optional(\n Type.Integer({\n default: DEFAULT_LIMIT,\n description: \"Maximum number of lines to return.\",\n minimum: 1,\n })\n ),\n }),\n\n async preflight(args, ctx: ToolContext<ReadToolMeta>) {\n const path = normPath(ctx.cwd, args.path)\n await ctx.need?.(\"read\", path)\n },\n\n async call(args, ctx: ToolContext<ReadToolMeta>): Promise<Content> {\n const path = normPath(ctx.cwd, args.path)\n\n const fileStat = await stat(path).catch((error: unknown) => {\n throw new AiError({\n cause: error,\n code: \"NOT_FOUND\",\n message: `cannot read ${path}: file not found`,\n })\n })\n if (!fileStat.isFile()) {\n throw new AiError({ code: \"NOT_A_FILE\", message: `${path} is not a regular file` })\n }\n\n if (isUnchanged(path, ctx)) {\n // Fresh! We've seen this file's current bytes, so we can skip\n // returning the content again.\n ctx.meta = {\n full: false,\n kind: \"read\",\n limit: 0,\n mtime: fileStat.mtimeMs,\n offset: 0,\n path,\n unchanged: true,\n }\n return [\n { content: `file unchanged since last read: ${path}`, tag: \"unchanged\", type: \"meta\" },\n ]\n }\n\n const file = await fileDetect(path)\n if (!file) {\n throw new AiError({\n code: \"READ_ERROR\",\n message: `${path}: could not read file`,\n })\n }\n\n // Threshold-based binary check — files with sporadic control bytes\n // (logs with ANSI styling, source with form feeds) still read as\n // text; only when binary content dominates the sample do we bail.\n if (file.type === \"binary\") {\n throw new AiError({\n code: \"BINARY_FILE\",\n data: { bytes: file.data.length, path },\n message: `${path}: binary file (${file.data.length} bytes); not displayable as text`,\n })\n }\n\n const att = await toAttachment(file)\n if (att) return [att]\n\n if (file.type !== \"text\") {\n throw new AiError({\n code: \"UNSUPPORTED_FILE\",\n data: { path, type: file.type },\n message: `${path}: unsupported file type ${file.type}`,\n })\n }\n\n const text = new TextDecoder().decode(file.data)\n const slice = formatTextSlice(text, {\n limit: args.limit ?? DEFAULT_LIMIT,\n offset: args.offset ?? 1,\n })\n\n // Record the observed mtime for unchanged-read detection. The masker\n // uses `full` to know whether this read subsumes earlier reads.\n ctx.meta = {\n full: slice.full,\n kind: \"read\",\n limit: slice.limit,\n mtime: fileStat.mtimeMs,\n offset: slice.offset,\n path,\n }\n\n return slice.content\n },\n})\n\n/** Return plain text and line-range metadata for both full and sliced reads. */\nfunction formatTextSlice(\n content: string,\n { offset, limit }: { offset: number; limit: number }\n): { content: Content; full?: boolean; offset: number; limit: number } {\n // Normalize line endings to LF for display. The model always sees LF;\n // edit/write detect the file's actual style and re-apply it on disk.\n content = normalizeEol(content)\n const lines = content.split(\"\\n\")\n // `split` on a final newline produces a trailing empty string — drop\n // it so a 3-line file with trailing LF reads as 3 lines, not 4.\n if (lines.at(-1) === \"\") lines.pop()\n\n // Negative offset = \"from the end\" — Python-slice / `tail -n` semantic.\n // `-1000` on a 30-line file clamps to 0 (read whole file) rather than\n // erroring; `0` is treated as `1` (head, off-by-one tolerance).\n let start = offset < 0 ? Math.max(0, lines.length + offset) : Math.max(0, offset - 1)\n // Overshoot clamps to `lines.length` so the slice loop emits nothing.\n // The agent gets an empty text + a `<slice>` meta that reports the\n // real file size, then can re-issue with a sensible offset.\n if (start >= lines.length) start = lines.length\n const end = Math.min(lines.length, start + limit)\n\n const out: string[] = []\n let full = start === 0 && end === lines.length\n for (let i = start; i < end; i++) {\n let line = lines[i]\n if (line.length > MAX_LINE_LENGTH) {\n full = false\n line = `${line.slice(0, MAX_LINE_LENGTH)}… [line truncated, ${line.length} chars]`\n }\n out.push(line)\n }\n\n const text = out.join(\"\\n\")\n\n // Empty slice (overshot offset): tell the agent the offset they asked\n // for and the real file size, rather than emitting a nonsensical\n // \"showing 6-5 of 5\"-style range.\n const sliceMsg =\n start === end && lines.length > 1\n ? `offset ${offset} past end of file (${lines.length} lines)`\n : `showing ${start + 1}-${end} of ${lines.length}`\n\n return {\n content: [\n { content: sliceMsg, tag: \"slice\", type: \"meta\" },\n { text, type: \"text\" },\n ],\n full,\n limit: end - start,\n offset: start + 1,\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
- import { a as __toESM, n as parseBash, r as __commonJSMin, t as TOOLS } from "./tools-wvkkuXWH.mjs";
1
+ import { i as __toESM, t as __commonJSMin } from "./rolldown-runtime-DktXgydw.mjs";
2
+ import { n as parseBash, t as TOOLS } from "./tools-CJ2x45EZ.mjs";
2
3
  import { normPath, prettyPath } from "@zaly/shared";
3
4
  import { createRegistry } from "@zaly/shared/registry";
4
5
  import { dirname, isAbsolute, relative } from "pathe";
@@ -182,10 +183,6 @@ var import_ignore = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exp
182
183
  return value;
183
184
  };
184
185
  const RETURN_FALSE = () => false;
185
- const cleanRangeBackSlash = (slashes) => {
186
- const { length } = slashes;
187
- return slashes.slice(0, length - length % 2);
188
- };
189
186
  const POSIX_CLASSES = {
190
187
  alnum: "0-9A-Za-z",
191
188
  alpha: "A-Za-z",
@@ -240,9 +237,9 @@ var import_ignore = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exp
240
237
  while (end < length && pattern[end] !== "]") end++;
241
238
  if (end === length) return null;
242
239
  if (end > nameStart && pattern[end - 1] === ":") {
243
- const expanded = POSIX_CLASSES[pattern.slice(nameStart, end - 1)];
244
- if (expanded === UNDEFINED) return null;
245
- body += expanded;
240
+ const name = pattern.slice(nameStart, end - 1);
241
+ if (!Object.prototype.hasOwnProperty.call(POSIX_CLASSES, name)) return null;
242
+ body += POSIX_CLASSES[name];
246
243
  prev = EMPTY;
247
244
  index = end;
248
245
  } else {
@@ -266,6 +263,7 @@ var import_ignore = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exp
266
263
  const REGEX_RESTORE_PLACEHOLDER = new RegExp(`${PLACEHOLDER}(\\d+)${PLACEHOLDER}`, "g");
267
264
  const TRAILING_WILDCARD = "";
268
265
  const TRAILING_DOUBLESTAR = "";
266
+ const isReserved = (char) => char === PLACEHOLDER || char === TRAILING_WILDCARD || char === TRAILING_DOUBLESTAR;
269
267
  const extractBrackets = (pattern) => {
270
268
  const sources = [];
271
269
  const hold = (source) => `${PLACEHOLDER}${sources.push(source) - 1}${PLACEHOLDER}`;
@@ -277,11 +275,11 @@ var import_ignore = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exp
277
275
  if (char === ESCAPE) {
278
276
  const escaped = pattern[index + 1];
279
277
  if (escaped === UNDEFINED || escaped === SLASH && index + 2 === length) out += hold(NEVER_MATCH);
280
- else if (escaped === "*" || escaped === "[" || escaped === SPACE || escaped === ESCAPE) out += pattern.slice(index, index + 2);
281
- else out += hold(REGEX_LITERAL_SPECIAL.test(escaped) ? ESCAPE + escaped : escaped);
278
+ else if (escaped === "*" || escaped === "[" || escaped === SPACE) out += pattern.slice(index, index + 2);
279
+ else out += hold(REGEX_LITERAL_SPECIAL.test(escaped) ? ESCAPE + escaped : isReserved(escaped) ? `[${escaped}]` : escaped);
282
280
  index += 2;
283
- } else if (char === PLACEHOLDER) {
284
- out += hold(`[${PLACEHOLDER}]`);
281
+ } else if (isReserved(char)) {
282
+ out += hold(`[${char}]`);
285
283
  index++;
286
284
  } else if (char === "[") {
287
285
  const scanned = scanBracket(pattern, index);
@@ -349,7 +347,7 @@ var import_ignore = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exp
349
347
  ],
350
348
  [
351
349
  /(^|[^\\])((?:\\\\)*)\\\*$/,
352
- (match, p1, p2) => p2.length / 2 % 2 === 0 ? p1 + p2 + TRAILING_WILDCARD : match,
350
+ (match, p1, p2) => p2 === EMPTY ? p1 + TRAILING_WILDCARD : match,
353
351
  "*"
354
352
  ],
355
353
  [
@@ -363,8 +361,8 @@ var import_ignore = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exp
363
361
  "\\\\"
364
362
  ],
365
363
  [
366
- /\\\[([^\]/]*?)(\\*)($|\])/g,
367
- (match, range, endEscape, close) => `\\[${range}${cleanRangeBackSlash(endEscape)}${close}`,
364
+ /\\\[([^\]/]*?)($|\])/g,
365
+ (match, range, close) => `\\[${range}${close}`,
368
366
  "["
369
367
  ],
370
368
  [DIRECT, (source) => {
@@ -887,4 +885,4 @@ const handlerRegistry = createRegistry("scope").from({
887
885
  //#endregion
888
886
  export { handlerRegistry as t };
889
887
 
890
- //# sourceMappingURL=registry-L9GDccS0.mjs.map
888
+ //# sourceMappingURL=registry-Zo0QmKdR.mjs.map