@letrquan/book 0.3.0 → 0.4.0-beta.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 (148) hide show
  1. package/CHANGELOG.md +1024 -0
  2. package/README.md +6 -3
  3. package/dist/{additional-roots-H7UNAEHS.js → additional-roots-VAWRQ625.js} +8 -7
  4. package/dist/{app-GE4OHJOC.js → app-J7BVPSUH.js} +5397 -2953
  5. package/dist/app-J7BVPSUH.js.map +1 -0
  6. package/dist/chunk-32WBCUAN.js +660 -0
  7. package/dist/chunk-32WBCUAN.js.map +1 -0
  8. package/dist/{chunk-525CAZPY.js → chunk-4DKHLZRI.js} +6 -6
  9. package/dist/{chunk-4AS7LKKQ.js → chunk-62EEAUID.js} +2 -2
  10. package/dist/{chunk-AQEQX4FF.js → chunk-AHC7OU75.js} +385 -55
  11. package/dist/chunk-AHC7OU75.js.map +1 -0
  12. package/dist/{chunk-EFJ2NH6E.js → chunk-AJZWLKLQ.js} +63 -14
  13. package/dist/chunk-AJZWLKLQ.js.map +1 -0
  14. package/dist/chunk-B5FC3MYZ.js +395 -0
  15. package/dist/chunk-B5FC3MYZ.js.map +1 -0
  16. package/dist/{chunk-67UZEWWF.js → chunk-DNFQTSMJ.js} +53 -11
  17. package/dist/chunk-DNFQTSMJ.js.map +1 -0
  18. package/dist/{chunk-TLX3H2CL.js → chunk-E5WLQSKA.js} +231 -40
  19. package/dist/chunk-E5WLQSKA.js.map +1 -0
  20. package/dist/chunk-ECLC4CIJ.js +1181 -0
  21. package/dist/chunk-ECLC4CIJ.js.map +1 -0
  22. package/dist/chunk-EEX6QNXV.js +127 -0
  23. package/dist/chunk-EEX6QNXV.js.map +1 -0
  24. package/dist/chunk-EFXVSQ5J.js +931 -0
  25. package/dist/chunk-EFXVSQ5J.js.map +1 -0
  26. package/dist/{chunk-RFJ6Q6LE.js → chunk-ENLB7NYV.js} +577 -164
  27. package/dist/chunk-ENLB7NYV.js.map +1 -0
  28. package/dist/{chunk-IME7R2UI.js → chunk-FPR7SKIE.js} +1268 -883
  29. package/dist/chunk-FPR7SKIE.js.map +1 -0
  30. package/dist/{chunk-VPNLCMVR.js → chunk-HFB74CLM.js} +13 -5
  31. package/dist/chunk-HFB74CLM.js.map +1 -0
  32. package/dist/{chunk-5RDRAO4B.js → chunk-HOZXKOE3.js} +37 -1
  33. package/dist/chunk-HOZXKOE3.js.map +1 -0
  34. package/dist/chunk-IDYWN37T.js +36 -0
  35. package/dist/chunk-IDYWN37T.js.map +1 -0
  36. package/dist/{chunk-YC6W4EJH.js → chunk-JGF5HLSI.js} +193 -75
  37. package/dist/chunk-JGF5HLSI.js.map +1 -0
  38. package/dist/{chunk-5YHTKWJN.js → chunk-KDNWMKXV.js} +2447 -907
  39. package/dist/chunk-KDNWMKXV.js.map +1 -0
  40. package/dist/{chunk-Y2VHN7TC.js → chunk-MEOB2DZA.js} +11 -1
  41. package/dist/chunk-MEOB2DZA.js.map +1 -0
  42. package/dist/{chunk-KBQS35OV.js → chunk-MV5PQNIA.js} +2 -2
  43. package/dist/chunk-N2G4Q7ME.js +2672 -0
  44. package/dist/chunk-N2G4Q7ME.js.map +1 -0
  45. package/dist/{chunk-62QL5QUP.js → chunk-OJBMDM5H.js} +17 -1
  46. package/dist/chunk-OJBMDM5H.js.map +1 -0
  47. package/dist/{chunk-56SIEMDX.js → chunk-PTDC7ZUP.js} +2 -2
  48. package/dist/{chunk-X4NJT436.js → chunk-REASC2QG.js} +67 -28
  49. package/dist/chunk-REASC2QG.js.map +1 -0
  50. package/dist/chunk-SBTU5BEC.js +114 -0
  51. package/dist/chunk-SBTU5BEC.js.map +1 -0
  52. package/dist/{chunk-HQI7NYGX.js → chunk-SWPDKLNM.js} +2 -2
  53. package/dist/{chunk-W3XFHEF4.js → chunk-UCFJ43OS.js} +140 -28
  54. package/dist/chunk-UCFJ43OS.js.map +1 -0
  55. package/dist/{chunk-QYBD22H5.js → chunk-W7SLYS3M.js} +80 -16
  56. package/dist/chunk-W7SLYS3M.js.map +1 -0
  57. package/dist/{chunk-SOW4WRQP.js → chunk-WV3O5PGX.js} +3 -3
  58. package/dist/{chunk-JTPOI3KU.js → chunk-XJRJVXJR.js} +262 -38
  59. package/dist/chunk-XJRJVXJR.js.map +1 -0
  60. package/dist/{chunk-224FJZLH.js → chunk-YIL6HTJX.js} +228 -5
  61. package/dist/chunk-YIL6HTJX.js.map +1 -0
  62. package/dist/{command-approvals-VBIU6BLM.js → command-approvals-ZRY66LCW.js} +5 -4
  63. package/dist/context-C3RAP6K5.js +41 -0
  64. package/dist/{hook-approvals-ZVT4V7IM.js → hook-approvals-DPQD5H65.js} +5 -4
  65. package/dist/index.js +657 -754
  66. package/dist/index.js.map +1 -1
  67. package/dist/{interactive-assets-T4I4SEHU.js → interactive-assets-73AQO5WP.js} +11 -6
  68. package/dist/{interactive-assets-T4I4SEHU.js.map → interactive-assets-73AQO5WP.js.map} +1 -1
  69. package/dist/job-runner.js +10 -3
  70. package/dist/job-runner.js.map +1 -1
  71. package/dist/job-supervisor.js +13 -4
  72. package/dist/job-supervisor.js.map +1 -1
  73. package/dist/loader-DQNPDKOX.js +27 -0
  74. package/dist/{mcp-approvals-HJ2VDYMC.js → mcp-approvals-27LJYADQ.js} +5 -4
  75. package/dist/{mcp-config-RNB5LEVE.js → mcp-config-GXSODNCM.js} +3 -2
  76. package/dist/memory-store-I6Z54VUN.js +51 -0
  77. package/dist/{model-window-store-MTSQ5JDJ.js → model-window-store-6VKXXTPJ.js} +4 -3
  78. package/dist/{openai-compatible-2OB54YST.js → openai-compatible-IPUNBJ53.js} +5 -3
  79. package/dist/{permission-approvals-YLV6UC5W.js → permission-approvals-Q5IMFEZI.js} +5 -4
  80. package/dist/{permissions-DHOKF3W2.js → permissions-7Y35X6HZ.js} +26 -9
  81. package/dist/provider-approvals-5AGH3RVT.js +76 -0
  82. package/dist/{sandbox-WRYTZH3D.js → sandbox-N2RY72GN.js} +6 -5
  83. package/dist/{scrollback-35ME2GFX.js → scrollback-GD76GNM4.js} +36 -28
  84. package/dist/scrollback-GD76GNM4.js.map +1 -0
  85. package/dist/sdk.d.ts +876 -64
  86. package/dist/sdk.js +57 -37
  87. package/dist/sdk.js.map +1 -1
  88. package/dist/settings-loader-KDBH45AQ.js +43 -0
  89. package/dist/{settings-removed-JWWPT4WA.js → settings-removed-3ATWJEF3.js} +2 -2
  90. package/dist/shell-selection-Z5WORZU4.js +31 -0
  91. package/dist/shell-selection-Z5WORZU4.js.map +1 -0
  92. package/package.json +5 -5
  93. package/dist/app-GE4OHJOC.js.map +0 -1
  94. package/dist/chunk-224FJZLH.js.map +0 -1
  95. package/dist/chunk-5RDRAO4B.js.map +0 -1
  96. package/dist/chunk-5YHTKWJN.js.map +0 -1
  97. package/dist/chunk-62QL5QUP.js.map +0 -1
  98. package/dist/chunk-67UZEWWF.js.map +0 -1
  99. package/dist/chunk-6OQQOMZR.js +0 -785
  100. package/dist/chunk-6OQQOMZR.js.map +0 -1
  101. package/dist/chunk-AQEQX4FF.js.map +0 -1
  102. package/dist/chunk-C7ADUNPR.js +0 -273
  103. package/dist/chunk-C7ADUNPR.js.map +0 -1
  104. package/dist/chunk-EFJ2NH6E.js.map +0 -1
  105. package/dist/chunk-IME7R2UI.js.map +0 -1
  106. package/dist/chunk-J62VZYOB.js +0 -1339
  107. package/dist/chunk-J62VZYOB.js.map +0 -1
  108. package/dist/chunk-JTPOI3KU.js.map +0 -1
  109. package/dist/chunk-QYBD22H5.js.map +0 -1
  110. package/dist/chunk-RFJ6Q6LE.js.map +0 -1
  111. package/dist/chunk-S4JMID4G.js +0 -259
  112. package/dist/chunk-S4JMID4G.js.map +0 -1
  113. package/dist/chunk-TLX3H2CL.js.map +0 -1
  114. package/dist/chunk-VPNLCMVR.js.map +0 -1
  115. package/dist/chunk-W3XFHEF4.js.map +0 -1
  116. package/dist/chunk-X4NJT436.js.map +0 -1
  117. package/dist/chunk-X66I2NQO.js +0 -137
  118. package/dist/chunk-X66I2NQO.js.map +0 -1
  119. package/dist/chunk-Y2VHN7TC.js.map +0 -1
  120. package/dist/chunk-YC6W4EJH.js.map +0 -1
  121. package/dist/context-CBOBDY5Q.js +0 -38
  122. package/dist/loader-S77OVTHH.js +0 -18
  123. package/dist/memory-store-DZUT5HUH.js +0 -75
  124. package/dist/scrollback-35ME2GFX.js.map +0 -1
  125. package/dist/settings-loader-JUNPGAAM.js +0 -35
  126. package/dist/shell-selection-CDHGJ4GC.js +0 -17
  127. /package/dist/{additional-roots-H7UNAEHS.js.map → additional-roots-VAWRQ625.js.map} +0 -0
  128. /package/dist/{chunk-525CAZPY.js.map → chunk-4DKHLZRI.js.map} +0 -0
  129. /package/dist/{chunk-4AS7LKKQ.js.map → chunk-62EEAUID.js.map} +0 -0
  130. /package/dist/{chunk-KBQS35OV.js.map → chunk-MV5PQNIA.js.map} +0 -0
  131. /package/dist/{chunk-56SIEMDX.js.map → chunk-PTDC7ZUP.js.map} +0 -0
  132. /package/dist/{chunk-HQI7NYGX.js.map → chunk-SWPDKLNM.js.map} +0 -0
  133. /package/dist/{chunk-SOW4WRQP.js.map → chunk-WV3O5PGX.js.map} +0 -0
  134. /package/dist/{command-approvals-VBIU6BLM.js.map → command-approvals-ZRY66LCW.js.map} +0 -0
  135. /package/dist/{context-CBOBDY5Q.js.map → context-C3RAP6K5.js.map} +0 -0
  136. /package/dist/{hook-approvals-ZVT4V7IM.js.map → hook-approvals-DPQD5H65.js.map} +0 -0
  137. /package/dist/{loader-S77OVTHH.js.map → loader-DQNPDKOX.js.map} +0 -0
  138. /package/dist/{mcp-approvals-HJ2VDYMC.js.map → mcp-approvals-27LJYADQ.js.map} +0 -0
  139. /package/dist/{mcp-config-RNB5LEVE.js.map → mcp-config-GXSODNCM.js.map} +0 -0
  140. /package/dist/{memory-store-DZUT5HUH.js.map → memory-store-I6Z54VUN.js.map} +0 -0
  141. /package/dist/{model-window-store-MTSQ5JDJ.js.map → model-window-store-6VKXXTPJ.js.map} +0 -0
  142. /package/dist/{openai-compatible-2OB54YST.js.map → openai-compatible-IPUNBJ53.js.map} +0 -0
  143. /package/dist/{permission-approvals-YLV6UC5W.js.map → permission-approvals-Q5IMFEZI.js.map} +0 -0
  144. /package/dist/{permissions-DHOKF3W2.js.map → permissions-7Y35X6HZ.js.map} +0 -0
  145. /package/dist/{sandbox-WRYTZH3D.js.map → provider-approvals-5AGH3RVT.js.map} +0 -0
  146. /package/dist/{settings-loader-JUNPGAAM.js.map → sandbox-N2RY72GN.js.map} +0 -0
  147. /package/dist/{settings-removed-JWWPT4WA.js.map → settings-loader-KDBH45AQ.js.map} +0 -0
  148. /package/dist/{shell-selection-CDHGJ4GC.js.map → settings-removed-3ATWJEF3.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/skills.ts"],"sourcesContent":["import {\n closeSync,\n existsSync,\n lstatSync,\n openSync,\n readFileSync,\n readSync,\n readdirSync,\n realpathSync,\n statSync,\n} from 'node:fs';\nimport { createHash } from 'node:crypto';\nimport { homedir } from 'node:os';\nimport { dirname, isAbsolute, join, parse, relative, resolve, sep } from 'node:path';\nimport { parseFrontmatter } from './frontmatter.js';\nimport { resolveBookHome } from './book-home.js';\nimport { isUserHome, tryRealPath } from './repo-containment.js';\nimport type { SkillActivation, SkillExecution } from './settings.js';\nimport { createUncGuard, reachesUncPath, type UncGuard } from './unc-path.js';\n\nexport const SKILL_ENTRY_FILENAME = 'SKILL.md';\nexport const MAX_SKILL_HEADER_BYTES = 64 * 1024;\nexport const MAX_SKILL_BODY_BYTES = 100 * 1024;\nexport const MAX_SKILL_RESOURCES = 200;\nexport const MAX_SKILL_RESOURCE_DEPTH = 4;\nexport const MAX_SKILL_RESOURCE_BYTES = 2 * 1024 * 1024;\n\nconst SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\nconst KNOWN_FRONTMATTER_FIELDS = new Set([\n 'name',\n 'description',\n 'license',\n 'compatibility',\n 'version',\n 'metadata',\n 'allowed-tools',\n 'tools',\n 'when_to_use',\n 'model',\n 'lifetime',\n 'disabled',\n // Interoperable Claude/Codex skill metadata. Invocation controls are\n // advisory to Book's explicit `$name` UI and do not grant permissions.\n 'disable-model-invocation',\n 'user-invocable',\n 'argument-hint',\n]);\n\nexport type SkillSource = 'user' | 'project';\nexport type SkillRootKind = 'book' | 'agents' | 'claude' | 'opencode';\nexport type SkillIssueSeverity = 'error' | 'warning';\n\nexport interface SkillIssue {\n code: string;\n message: string;\n severity: SkillIssueSeverity;\n}\n\nexport interface SkillResource {\n relativePath: string;\n byteSize: number;\n mtimeMs: number;\n digest: string;\n}\n\nexport interface ShadowedSkill {\n path: string;\n source: SkillSource;\n rootKind: SkillRootKind;\n}\n\n/** Metadata discovered without retaining or injecting the SKILL.md body. */\nexport interface Skill {\n id: string;\n version: string;\n descriptorDigest: string;\n resourceDigest: string;\n name: string;\n description: string;\n whenToUse?: string;\n license?: string;\n compatibility?: string;\n metadata: Record<string, string>;\n /** Requested tool ceiling. It never grants permission by itself. */\n allowedTools?: string[];\n model?: string;\n lifetime: 'turn' | 'run';\n /** Absolute path to the skill entrypoint. */\n path: string;\n /** Absolute directory that owns the skill and its resources. */\n rootPath: string;\n source: SkillSource;\n rootKind: SkillRootKind;\n activation: SkillActivation;\n execution: SkillExecution;\n invocationCount: number;\n entryByteSize: number;\n entryMtimeMs: number;\n resources: SkillResource[];\n issues: SkillIssue[];\n valid: boolean;\n shadowed: ShadowedSkill[];\n}\n\nexport interface LoadedSkillBody {\n body: string;\n digest: string;\n byteSize: number;\n}\n\nexport interface SkillRoot {\n path: string;\n source: SkillSource;\n kind: SkillRootKind;\n}\n\nexport interface DiscoverSkillsOptions {\n homeDir?: string;\n bookHomeDir?: string;\n includeUser?: boolean;\n projectRoot?: string;\n includeOpenCode?: boolean;\n enabled?: boolean;\n executionOverrides?: Readonly<Record<string, SkillExecution>>;\n}\n\nfunction pathKey(path: string): string {\n return process.platform === 'win32' ? resolve(path).toLowerCase() : resolve(path);\n}\n\n/**\n * The real path of a skill root, or undefined when it does not exist (it contributes no skills,\n * so there is nothing to fold).\n *\n * Only for asking whether a project root is a user root reached another way. {@link pathKey}\n * stays the path as written because it also builds the skill id, which keys consent and\n * execution overrides, and it is what roots are otherwise deduped by.\n */\nfunction realRootPath(path: string): string | undefined {\n try {\n const real = realpathSync.native(path);\n return process.platform === 'win32' ? real.toLowerCase() : real;\n } catch {\n return undefined;\n }\n}\n\nfunction isInside(root: string, candidate: string): boolean {\n const rel = relative(root, candidate);\n return rel === '' || (!rel.startsWith(`..${sep}`) && rel !== '..' && !isAbsolute(rel));\n}\n\n/**\n * The nearest directory above `workspace`, as written, with a `.git` entry; the workspace when\n * there is none. A `.git` at the home directory (`home`, the user's when not given) is a dotfiles\n * repository, not a project (`findRepositoryRoot` in `repo-containment.ts`), so it ends the walk\n * with no repository: a project under the home directory without its own `.git` scans its own\n * directory, not every directory from the home directory down.\n */\nfunction projectRootFor(workspace: string, home: string | undefined, reachesUnc: UncGuard): string {\n // A workspace that reaches a network share has no project roots (`skillRoots`), so nothing on\n // the way up is probed; nor is a `.git` that does, which `existsSync` would follow (#579).\n if (reachesUnc(workspace)) return resolve(workspace);\n let current = resolve(workspace);\n const filesystemRoot = parse(current).root;\n while (true) {\n const dotGit = join(current, '.git');\n // A `.git` it may not open is one it cannot read: the workspace is then its own root, as for\n // memory (`findGitRoot`).\n if (reachesUnc(dotGit)) return resolve(workspace);\n if (existsSync(dotGit)) {\n return isUserHome(tryRealPath(current) ?? current, home) ? resolve(workspace) : current;\n }\n if (current === filesystemRoot) return resolve(workspace);\n current = dirname(current);\n }\n}\n\nfunction directoriesFromRoot(root: string, workspace: string): string[] {\n const normalizedRoot = resolve(root);\n const normalizedWorkspace = resolve(workspace);\n if (!isInside(normalizedRoot, normalizedWorkspace)) return [normalizedWorkspace];\n const directories: string[] = [];\n let current = normalizedWorkspace;\n while (true) {\n directories.push(current);\n if (pathKey(current) === pathKey(normalizedRoot)) break;\n current = dirname(current);\n }\n return directories.reverse();\n}\n\n/**\n * Roots are ordered from lowest to highest precedence.\n *\n * A project root is a path a repository can commit as a link, and on Windows resolving a link to\n * a network share opens the share, which can send the user's credentials to the host it names\n * (#567, #579). A project root that reaches one is therefore not a root at all, so nothing that\n * takes its roots from here resolves it. The user's own roots are theirs to keep on a share.\n * `reachesUnc` is the UNC guard of the discovery this is part of, so the two share what it probed.\n */\nexport function skillRoots(\n workspace: string,\n options: DiscoverSkillsOptions = {},\n reachesUnc: UncGuard = createUncGuard(),\n): SkillRoot[] {\n const roots: SkillRoot[] = [];\n if (options.includeUser !== false) {\n const home = options.homeDir ?? homedir();\n const bookHome =\n options.bookHomeDir ?? (options.homeDir ? join(home, '.book') : resolveBookHome());\n roots.push(\n { path: join(home, '.claude', 'skills'), source: 'user', kind: 'claude' },\n { path: join(home, '.agents', 'skills'), source: 'user', kind: 'agents' },\n ...(options.includeOpenCode === false\n ? []\n : [\n {\n path: join(home, '.config', 'opencode', 'skills'),\n source: 'user' as const,\n kind: 'opencode' as const,\n },\n ]),\n { path: join(bookHome, 'skills'), source: 'user', kind: 'book' },\n );\n }\n\n const projectRoot = options.projectRoot ?? projectRootFor(workspace, options.homeDir, reachesUnc);\n for (const directory of directoriesFromRoot(projectRoot, workspace)) {\n roots.push(\n { path: join(directory, '.claude', 'skills'), source: 'project', kind: 'claude' },\n { path: join(directory, '.agents', 'skills'), source: 'project', kind: 'agents' },\n ...(options.includeOpenCode === false\n ? []\n : [\n {\n path: join(directory, '.opencode', 'skills'),\n source: 'project' as const,\n kind: 'opencode' as const,\n },\n ]),\n { path: join(directory, '.book', 'skills'), source: 'project', kind: 'book' },\n );\n }\n // The real paths of the user roots, for the one fold below. Two roots that merely alias each\n // other (a repository linking `.claude/skills` to `.book/skills`) are *not* merged: each keeps\n // its slot, and the later one still wins, as it did before roots were compared by real path.\n const userRealPaths = new Set<string>();\n for (const root of roots) {\n if (root.source !== 'user') continue;\n const real = realRootPath(root.path);\n if (real) userRealPaths.add(real);\n }\n const byPath = new Map<string, SkillRoot>();\n for (const root of roots) {\n const key = pathKey(root.path);\n // A workspace at `$HOME` walks the user's own roots again as project roots. They stay\n // user roots: a project skill asks before the model may run it, and the user's own\n // skills must not start asking because of where Book was launched (#429).\n if (byPath.get(key)?.source === 'user') continue;\n if (root.source === 'project' && reachesUnc(root.path)) continue;\n // The same, when the home is reached through a link, so the project spelling of the path\n // differs (#449). Only a project root is ever folded into a user root, never the reverse.\n if (root.source === 'project' && userRealPaths.size > 0) {\n const real = realRootPath(root.path);\n if (real && userRealPaths.has(real)) continue;\n }\n byPath.set(key, root);\n }\n return [...byPath.values()];\n}\n\nfunction readFrontmatterPrefix(path: string): {\n frontmatter: Record<string, unknown>;\n headerText: string;\n issues: SkillIssue[];\n} {\n const issues: SkillIssue[] = [];\n const fd = openSync(path, 'r');\n const chunks: Buffer[] = [];\n let total = 0;\n try {\n while (total < MAX_SKILL_HEADER_BYTES) {\n const chunk = Buffer.alloc(Math.min(4096, MAX_SKILL_HEADER_BYTES - total));\n const count = readSync(fd, chunk, 0, chunk.length, total);\n if (count === 0) break;\n chunks.push(chunk.subarray(0, count));\n total += count;\n const text = Buffer.concat(chunks).toString('utf8');\n if (/^---\\r?\\n[\\s\\S]*?\\r?\\n---(?:\\r?\\n|$)/.test(text)) {\n const match = text.match(/^---\\r?\\n[\\s\\S]*?\\r?\\n---(?:\\r?\\n|$)/);\n const headerText = match?.[0] ?? '';\n return { frontmatter: parseFrontmatter(headerText).frontmatter, headerText, issues };\n }\n }\n } finally {\n closeSync(fd);\n }\n issues.push({\n code: 'invalid_frontmatter',\n message: `Missing YAML frontmatter or closing delimiter within ${MAX_SKILL_HEADER_BYTES} bytes.`,\n severity: 'error',\n });\n return { frontmatter: {}, headerText: '', issues };\n}\n\nfunction stringField(frontmatter: Record<string, unknown>, name: string): string | undefined {\n const value = frontmatter[name];\n return typeof value === 'string' && value.trim() ? value.trim() : undefined;\n}\n\nfunction metadataFromHeader(headerText: string): Record<string, string> {\n const lines = headerText.split(/\\r?\\n/);\n const metadata: Record<string, string> = {};\n const start = lines.findIndex((line) => /^metadata\\s*:\\s*$/.test(line.trim()));\n if (start < 0) return metadata;\n for (const line of lines.slice(start + 1)) {\n if (/^---\\s*$/.test(line)) break;\n if (line.trim() && !/^\\s+/.test(line)) break;\n const match = line.match(/^\\s+([A-Za-z0-9_.-]+)\\s*:\\s*(.*?)\\s*$/);\n if (!match) continue;\n metadata[match[1]] = match[2].replace(/^[\"'](.*)[\"']$/, '$1');\n }\n return metadata;\n}\n\nfunction allowedToolsFrom(frontmatter: Record<string, unknown>): string[] | undefined {\n const value = frontmatter['allowed-tools'] ?? frontmatter.tools;\n if (Array.isArray(value)) {\n const tools = value\n .map(String)\n .map((tool) => tool.trim())\n .filter(Boolean);\n return tools.length ? tools : undefined;\n }\n if (typeof value === 'string') {\n const tools = value.match(/[^\\s]+(?:\\([^)]*\\))?/g)?.map((tool) => tool.trim()) ?? [];\n return tools.length ? tools : undefined;\n }\n return undefined;\n}\n\nfunction collectResources(\n rootPath: string,\n entryPath: string,\n): {\n resources: SkillResource[];\n issues: SkillIssue[];\n} {\n const resources: SkillResource[] = [];\n const issues: SkillIssue[] = [];\n let depthLimitReported = false;\n const visit = (directory: string, depth: number): void => {\n if (depth > MAX_SKILL_RESOURCE_DEPTH) {\n if (!depthLimitReported) {\n depthLimitReported = true;\n issues.push({\n code: 'resource_depth_limit',\n message: `Resources deeper than ${MAX_SKILL_RESOURCE_DEPTH} directories are ignored.`,\n severity: 'warning',\n });\n }\n return;\n }\n if (resources.length >= MAX_SKILL_RESOURCES) return;\n let entries: string[];\n try {\n entries = readdirSync(directory).sort();\n } catch (error) {\n issues.push({\n code: 'resource_unreadable',\n message: `Could not inspect resources in ${directory}: ${error instanceof Error ? error.message : String(error)}`,\n severity: 'warning',\n });\n return;\n }\n for (const entry of entries) {\n if (resources.length >= MAX_SKILL_RESOURCES) break;\n const fullPath = join(directory, entry);\n if (pathKey(fullPath) === pathKey(entryPath)) continue;\n let info;\n try {\n info = lstatSync(fullPath);\n } catch {\n continue;\n }\n if (info.isSymbolicLink()) {\n issues.push({\n code: 'resource_symlink_ignored',\n message: `Ignored symbolic-link resource: ${relative(rootPath, fullPath)}`,\n severity: 'warning',\n });\n continue;\n }\n if (info.isDirectory()) {\n visit(fullPath, depth + 1);\n continue;\n }\n if (!info.isFile()) continue;\n let digest = createHash('sha256')\n .update(`unavailable:${info.size}:${info.mtimeMs}`)\n .digest('hex');\n if (info.size > MAX_SKILL_RESOURCE_BYTES) {\n issues.push({\n code: 'resource_too_large',\n message: `Resource exceeds ${MAX_SKILL_RESOURCE_BYTES} bytes: ${relative(rootPath, fullPath)}`,\n severity: 'warning',\n });\n }\n try {\n const fd = openSync(fullPath, 'r');\n try {\n const prefix = Buffer.alloc(Math.min(info.size, 8192));\n const read = readSync(fd, prefix, 0, prefix.length, 0);\n if (prefix.subarray(0, read).includes(0)) {\n issues.push({\n code: 'binary_resource',\n message: `Binary resource cannot be loaded into model context: ${relative(rootPath, fullPath)}`,\n severity: 'warning',\n });\n }\n } finally {\n closeSync(fd);\n }\n } catch (error) {\n issues.push({\n code: 'resource_unreadable',\n message: `Could not validate resource ${relative(rootPath, fullPath)}: ${error instanceof Error ? error.message : String(error)}`,\n severity: 'warning',\n });\n }\n if (info.size <= MAX_SKILL_RESOURCE_BYTES) {\n try {\n digest = createHash('sha256').update(readFileSync(fullPath)).digest('hex');\n } catch {\n // The visible unreadable-resource issue above is the actionable diagnostic.\n }\n }\n resources.push({\n relativePath: relative(rootPath, fullPath).split(sep).join('/'),\n byteSize: info.size,\n mtimeMs: info.mtimeMs,\n digest,\n });\n }\n };\n visit(rootPath, 1);\n if (resources.length >= MAX_SKILL_RESOURCES) {\n issues.push({\n code: 'resource_limit',\n message: `Only the first ${MAX_SKILL_RESOURCES} resources are available.`,\n severity: 'warning',\n });\n }\n return { resources, issues };\n}\n\nfunction descriptorFromEntry(\n entryPath: string,\n root: SkillRoot,\n directoryName: string,\n flat = false,\n): Skill {\n const { frontmatter, headerText, issues: headerIssues } = readFrontmatterPrefix(entryPath);\n const issues = [...headerIssues];\n const declaredName = stringField(frontmatter, 'name');\n const name = declaredName ?? directoryName;\n const description = stringField(frontmatter, 'description') ?? '';\n\n if (!SKILL_NAME_PATTERN.test(name) || name.length > 64) {\n issues.push({\n code: 'invalid_name',\n message: 'Skill names must be 1-64 lowercase letters, numbers, or single hyphens.',\n severity: 'error',\n });\n }\n if (!flat && declaredName !== directoryName) {\n issues.push({\n code: 'name_directory_mismatch',\n message: `Declared name \"${declaredName ?? '(missing)'}\" must match directory \"${directoryName}\".`,\n severity: 'error',\n });\n }\n if (!declaredName && flat) {\n issues.push({\n code: 'legacy_flat_skill',\n message: 'Flat .skill.md files are supported for compatibility; use <name>/SKILL.md.',\n severity: 'warning',\n });\n }\n if (!description || description.length > 1024) {\n issues.push({\n code: 'invalid_description',\n message: 'Skill description is required and must be at most 1024 characters.',\n severity: 'error',\n });\n }\n const compatibility = stringField(frontmatter, 'compatibility');\n if (compatibility && compatibility.length > 500) {\n issues.push({\n code: 'invalid_compatibility',\n message: 'Skill compatibility must be at most 500 characters.',\n severity: 'error',\n });\n }\n const whenToUse = stringField(frontmatter, 'when_to_use');\n if (whenToUse && whenToUse.length > 1024) {\n issues.push({\n code: 'invalid_when_to_use',\n message: 'Skill when_to_use must be at most 1024 characters.',\n severity: 'error',\n });\n }\n const model = stringField(frontmatter, 'model');\n if (model) {\n issues.push({\n code: 'unsupported_model_hint',\n message: 'The model field is retained for compatibility but does not change Book models.',\n severity: 'warning',\n });\n }\n const requestedLifetime = stringField(frontmatter, 'lifetime');\n if (requestedLifetime && requestedLifetime !== 'run' && requestedLifetime !== 'turn') {\n issues.push({\n code: 'invalid_lifetime',\n message: 'Skill lifetime must be run or turn; Book will use run.',\n severity: 'warning',\n });\n }\n const lifetime = requestedLifetime === 'turn' ? 'turn' : 'run';\n for (const field of Object.keys(frontmatter)) {\n if (!KNOWN_FRONTMATTER_FIELDS.has(field)) {\n issues.push({\n code: 'unknown_frontmatter_field',\n message: `Unknown frontmatter field: ${field}`,\n severity: 'warning',\n });\n }\n }\n\n let entryByteSize = 0;\n let entryMtimeMs = 0;\n try {\n const entryInfo = statSync(entryPath);\n entryByteSize = entryInfo.size;\n entryMtimeMs = entryInfo.mtimeMs;\n if (entryByteSize > MAX_SKILL_BODY_BYTES + MAX_SKILL_HEADER_BYTES) {\n issues.push({\n code: 'body_too_large',\n message: `SKILL.md exceeds the ${MAX_SKILL_BODY_BYTES}-byte body budget.`,\n severity: 'error',\n });\n }\n } catch {\n issues.push({\n code: 'skill_unreadable',\n message: 'Skill entrypoint is unreadable.',\n severity: 'error',\n });\n }\n\n const rootPath = dirname(entryPath);\n const resourceResult = flat\n ? { resources: [] as SkillResource[], issues: [] as SkillIssue[] }\n : collectResources(rootPath, entryPath);\n issues.push(...resourceResult.issues);\n\n const metadata = metadataFromHeader(headerText);\n const version = stringField(frontmatter, 'version') ?? metadata.version ?? 'unversioned';\n const resourceDigest = createHash('sha256')\n .update(JSON.stringify(resourceResult.resources))\n .digest('hex');\n const descriptorDigest = createHash('sha256')\n .update(\n JSON.stringify({\n name,\n description,\n whenToUse,\n version,\n source: root.source,\n rootKind: root.kind,\n path: pathKey(entryPath),\n allowedTools: allowedToolsFrom(frontmatter),\n lifetime,\n resourceDigest,\n }),\n )\n .digest('hex');\n\n return {\n id: `${root.kind}:${pathKey(entryPath)}`,\n version,\n descriptorDigest,\n resourceDigest,\n name,\n description,\n whenToUse,\n license: stringField(frontmatter, 'license'),\n compatibility,\n metadata,\n allowedTools: allowedToolsFrom(frontmatter),\n model,\n lifetime,\n path: entryPath,\n rootPath,\n source: root.source,\n rootKind: root.kind,\n activation: String(frontmatter.disabled).toLowerCase() === 'true' ? 'off' : 'manual',\n execution: 'inherit',\n invocationCount: 0,\n entryByteSize,\n entryMtimeMs,\n resources: resourceResult.resources,\n issues,\n valid: !issues.some((issue) => issue.severity === 'error'),\n shadowed: [],\n };\n}\n\n/**\n * The skills in one root. In a project root, each skill directory and SKILL.md is a path a\n * repository can commit as a link to a network share, as the root itself can (see\n * {@link skillRoots}, which drops such a root): each is asked of `reachesUnc` before anything\n * follows it, and one that reaches a share is not listed. A resource link is never followed\n * (`collectResources`).\n */\nfunction skillsUnderRoot(root: SkillRoot, reachesUnc: UncGuard): Skill[] {\n const refused = (path: string) => root.source === 'project' && reachesUnc(path);\n if (!existsSync(root.path)) return [];\n let entries: string[];\n try {\n entries = readdirSync(root.path).sort();\n } catch {\n return [];\n }\n\n const skills: Skill[] = [];\n for (const entry of entries) {\n const fullPath = join(root.path, entry);\n let info;\n try {\n info = lstatSync(fullPath);\n } catch {\n continue;\n }\n let directory = info.isDirectory();\n if (info.isSymbolicLink()) {\n if (refused(fullPath)) continue;\n try {\n directory = statSync(fullPath).isDirectory();\n } catch {\n continue;\n }\n }\n if (directory) {\n const entryPath = join(fullPath, SKILL_ENTRY_FILENAME);\n if (!refused(entryPath) && existsSync(entryPath) && statSync(entryPath).isFile()) {\n skills.push(descriptorFromEntry(entryPath, root, entry));\n }\n } else if (info.isFile() && entry.endsWith('.skill.md')) {\n skills.push(descriptorFromEntry(fullPath, root, entry.replace(/\\.skill\\.md$/, ''), true));\n }\n }\n return skills;\n}\n\nexport function discoverSkills(\n workspace: string,\n overrides: Readonly<Record<string, SkillActivation>> = {},\n options: DiscoverSkillsOptions = {},\n): Skill[] {\n const byName = new Map<string, Skill>();\n const reachesUnc = createUncGuard();\n for (const root of skillRoots(workspace, options, reachesUnc)) {\n for (const skill of skillsUnderRoot(root, reachesUnc)) {\n const previous = byName.get(skill.name);\n if (previous) {\n skill.shadowed = [\n ...previous.shadowed,\n { path: previous.path, source: previous.source, rootKind: previous.rootKind },\n ];\n }\n byName.set(skill.name, skill);\n }\n }\n return applySkillOverrides(\n [...byName.values()].sort((a, b) => a.name.localeCompare(b.name)),\n overrides,\n options.executionOverrides ?? {},\n options.enabled ?? true,\n );\n}\n\nexport function applySkillOverrides(\n skills: readonly Skill[],\n overrides: Readonly<Record<string, SkillActivation>>,\n executionOverrides: Readonly<Record<string, SkillExecution>> = {},\n enabled = true,\n): Skill[] {\n return skills.map((skill) => ({\n ...skill,\n activation: enabled ? (overrides[skill.name] ?? skill.activation ?? 'manual') : 'off',\n execution: executionOverrides[skill.name] ?? skill.execution ?? 'inherit',\n }));\n}\n\nexport function loadSkillBody(skill: Skill): LoadedSkillBody {\n if (!skill.valid) throw new Error(`Skill \"${skill.name}\" is invalid.`);\n // Asked again here, not only at discovery: the directory may have become a link since (#579).\n if (skill.source === 'project' && reachesUncPath(skill.path)) {\n throw new Error(`Skill \"${skill.name}\" is on a network (UNC) path and is not loaded.`);\n }\n const canonicalRoot = realpathSync(skill.rootPath);\n const canonicalEntry = realpathSync(skill.path);\n if (!isInside(canonicalRoot, canonicalEntry)) {\n throw new Error(`Skill entrypoint escapes its root: ${skill.path}`);\n }\n const current = statSync(canonicalEntry);\n if (current.size !== skill.entryByteSize || current.mtimeMs !== skill.entryMtimeMs) {\n throw new Error(`Skill \"${skill.name}\" changed after discovery; reload skills before use.`);\n }\n const buffer = readFileSync(canonicalEntry);\n if (buffer.includes(0)) {\n throw new Error(`Skill \"${skill.name}\" contains binary content.`);\n }\n const raw = buffer.toString('utf8');\n const { body } = parseFrontmatter(raw);\n const byteSize = Buffer.byteLength(body);\n if (byteSize > MAX_SKILL_BODY_BYTES) {\n throw new Error(`Skill body exceeds ${MAX_SKILL_BODY_BYTES} bytes.`);\n }\n return {\n body,\n digest: createHash('sha256').update(body).digest('hex'),\n byteSize,\n };\n}\n\nexport interface SkillListingResult {\n text: string;\n budgetChars: number;\n visibleCount: number;\n included: string[];\n omitted: string[];\n collapsed: string[];\n charCount: number;\n}\n\n/**\n * Characters the skill listing may occupy in the system prompt.\n *\n * Lives here so the listing shipped to the model and the /skills diagnostics\n * that describe it cannot drift: both call this, neither re-derives the share\n * or the cap.\n */\nexport function skillListingBudgetChars(contextWindow: number): number {\n return Math.min(8000, Math.max(512, Math.floor(contextWindow * 0.08)));\n}\n\nexport function buildSkillListing(\n skills: readonly Skill[],\n budgetChars = 8000,\n): SkillListingResult {\n const visible = skills.filter(\n (skill) => skill.valid && (skill.activation === 'auto' || skill.activation === 'name-only'),\n );\n if (visible.length === 0) {\n return {\n text: '',\n budgetChars,\n visibleCount: 0,\n included: [],\n omitted: [],\n collapsed: [],\n charCount: 0,\n };\n }\n\n const sorted = [...visible].sort(\n (a, b) => b.invocationCount - a.invocationCount || a.name.localeCompare(b.name),\n );\n const lines = [\n '## Available skills',\n 'Use InvokeSkill when a request matches a description. A user can force exact use with `$skill-name`.',\n ];\n let remainingBudget = budgetChars - lines.join('\\n').length;\n const includedNames: string[] = [];\n const collapsedNames = new Set<string>();\n\n let included = 0;\n for (const skill of sorted) {\n if (skill.activation === 'name-only') {\n const bare = `- **${skill.name}**`;\n if (bare.length > remainingBudget) break;\n lines.push(bare);\n remainingBudget -= bare.length + 1;\n included++;\n includedNames.push(skill.name);\n collapsedNames.add(skill.name);\n continue;\n }\n const entry = `- **${skill.name}**: ${skill.description}`;\n const trigger = skill.whenToUse ? `\\n when: ${skill.whenToUse}` : '';\n const full = `${entry}${trigger}`;\n if (full.length > remainingBudget) {\n const bare = `- **${skill.name}**`;\n if (bare.length <= remainingBudget) {\n lines.push(bare);\n included++;\n includedNames.push(skill.name);\n collapsedNames.add(skill.name);\n }\n break;\n }\n lines.push(full);\n remainingBudget -= full.length + 1;\n included++;\n includedNames.push(skill.name);\n }\n let omitted = sorted.length - included;\n if (omitted > 0) {\n let warning = `- ${omitted} additional skill${omitted === 1 ? ' was' : 's were'} omitted from this prompt budget. Use /skills to inspect the full catalog.`;\n while (warning.length > remainingBudget && lines.length > 2) {\n const removed = lines.pop();\n if (!removed) break;\n remainingBudget += removed.length + 1;\n included--;\n const removedName = includedNames.pop();\n if (removedName) collapsedNames.delete(removedName);\n omitted = sorted.length - included;\n warning = `- ${omitted} additional skill${omitted === 1 ? ' was' : 's were'} omitted from this prompt budget. Use /skills to inspect the full catalog.`;\n }\n if (warning.length <= remainingBudget) lines.push(warning);\n }\n const text = lines.join('\\n');\n const includedSet = new Set(includedNames);\n return {\n text,\n budgetChars,\n visibleCount: sorted.length,\n included: includedNames,\n omitted: sorted.filter((skill) => !includedSet.has(skill.name)).map((skill) => skill.name),\n collapsed: includedNames.filter((name) => collapsedNames.has(name)),\n charCount: text.length,\n };\n}\n\nexport function generateSkillListing(skills: readonly Skill[], budgetChars = 8000): string {\n return buildSkillListing(skills, budgetChars).text;\n}\n\nexport function explicitSkillMentions(prompt: string, skills: readonly Skill[]): string[] {\n const available = new Set(skills.map((skill) => skill.name));\n const mentions: string[] = [];\n // A `$` that follows a path separator, a dot or a mention's `@` is part of a file or command name\n // (`@$deploy.md`, `docs/$deploy/`, `/$deploy`), not a request for a skill.\n const pattern = /(?<![A-Za-z0-9_\\-/\\\\@.])\\$([a-z0-9]+(?:-[a-z0-9]+)*)(?![A-Za-z0-9_-])/g;\n for (const match of prompt.matchAll(pattern)) {\n const name = match[1];\n if (available.has(name) && !mentions.includes(name)) mentions.push(name);\n }\n return mentions;\n}\n"],"mappings":"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","names":[]}
@@ -5,7 +5,7 @@ import {
5
5
  runHostReview,
6
6
  toElicitationFields,
7
7
  validateElicitationContent
8
- } from "./chunk-RFJ6Q6LE.js";
8
+ } from "./chunk-ENLB7NYV.js";
9
9
  import {
10
10
  SessionRuntime,
11
11
  buildAgentCompletionMessage,
@@ -20,64 +20,85 @@ import {
20
20
  normalizePersistedTodos,
21
21
  planApprovalFromUserQuestionResponse,
22
22
  planStopDecision,
23
+ seedShellIds,
23
24
  separateInlineReasoning,
24
25
  takeAgentCompletionBatch,
25
26
  uniqueAgentDisplayName
26
- } from "./chunk-5YHTKWJN.js";
27
- import {
28
- consoleMcpDiagnosticSink,
29
- resolveMcpServerList
30
- } from "./chunk-Y2VHN7TC.js";
27
+ } from "./chunk-KDNWMKXV.js";
31
28
  import {
32
29
  TRUSTED_SETTINGS_LOCATION,
30
+ collectSkippedInstructionNotices,
31
+ collectSkippedReviewNotices,
33
32
  compactionGate,
34
33
  createBuiltinCommandRegistry,
35
34
  estimateUsageCost,
35
+ formatGoalStatus,
36
36
  getPackageVersion,
37
+ goalKickoffPrompt,
37
38
  planWasLost,
38
39
  resolveContextLimit,
39
40
  resolvePermissionMode,
40
41
  usageAtGate,
41
42
  usagePressureTokens
42
- } from "./chunk-IME7R2UI.js";
43
+ } from "./chunk-FPR7SKIE.js";
44
+ import {
45
+ onAbort,
46
+ seedObservationLedger,
47
+ throwIfAborted
48
+ } from "./chunk-N2G4Q7ME.js";
43
49
  import {
44
50
  formatIgnoredWorkspaceSandboxKey,
45
51
  ignoredWorkspaceSandboxKeys,
52
+ isUserSettingsFile,
46
53
  loadSettingsFile,
47
54
  settingsLayerPaths,
48
55
  workspaceLayerThatEnabledSandbox
49
- } from "./chunk-W3XFHEF4.js";
50
- import {
51
- onAbort,
52
- seedObservationLedger,
53
- throwIfAborted
54
- } from "./chunk-J62VZYOB.js";
56
+ } from "./chunk-UCFJ43OS.js";
55
57
  import {
58
+ collectSkippedCommandNotices,
56
59
  commandEnforcementContext,
57
60
  discoverCommands,
58
61
  parseSlashInput,
59
62
  resolveCommandBody
60
- } from "./chunk-EFJ2NH6E.js";
63
+ } from "./chunk-AJZWLKLQ.js";
64
+ import {
65
+ consoleMcpDiagnosticSink,
66
+ resolveMcpServerList
67
+ } from "./chunk-MEOB2DZA.js";
68
+ import {
69
+ isProcessAlive
70
+ } from "./chunk-32WBCUAN.js";
61
71
  import {
62
72
  buildChildEnv
63
73
  } from "./chunk-XHAO3K7I.js";
64
74
  import {
65
75
  collectDeclaredDirectories,
66
76
  partitionProjectDirectories
67
- } from "./chunk-525CAZPY.js";
77
+ } from "./chunk-4DKHLZRI.js";
68
78
  import {
69
79
  getPrimaryArg,
70
80
  toolFailure,
71
81
  toolResultErrorMessage,
72
82
  toolSuccess
73
- } from "./chunk-224FJZLH.js";
83
+ } from "./chunk-YIL6HTJX.js";
74
84
  import {
75
85
  partitionProjectAllowRules
76
- } from "./chunk-KBQS35OV.js";
86
+ } from "./chunk-MV5PQNIA.js";
87
+ import {
88
+ LEGACY_BASE_URL_ID,
89
+ describeApiKeyDeclaration,
90
+ describeDroppedOverride,
91
+ describeOrigin,
92
+ describeReservedProvider,
93
+ describeUnaffectedSelection,
94
+ describeWithheldSelection,
95
+ providerTrustCommand
96
+ } from "./chunk-B5FC3MYZ.js";
77
97
  import {
78
98
  collectDeclaredHooks,
79
- partitionProjectHooks
80
- } from "./chunk-SOW4WRQP.js";
99
+ partitionProjectHooks,
100
+ renderUntrustedText
101
+ } from "./chunk-WV3O5PGX.js";
81
102
  import {
82
103
  resolveBookHome
83
104
  } from "./chunk-ZPSZZUWF.js";
@@ -87,10 +108,13 @@ import {
87
108
  closeSync,
88
109
  mkdirSync,
89
110
  openSync,
111
+ readdirSync,
90
112
  readFileSync,
91
113
  renameSync,
92
114
  rmSync,
93
115
  statfsSync,
116
+ statSync,
117
+ unlinkSync,
94
118
  writeSync
95
119
  } from "fs";
96
120
  import { join } from "path";
@@ -140,6 +164,43 @@ function readRunStatus(sessionId, home = resolveBookHome()) {
140
164
  return void 0;
141
165
  }
142
166
  }
167
+ var RUN_RECORD_NAME = /\.json$/;
168
+ var RUN_TEMP_NAME = /\.json\..+\.tmp$/;
169
+ function recordsLiveRun(path) {
170
+ try {
171
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
172
+ if (!parsed || typeof parsed !== "object") return false;
173
+ const record = parsed;
174
+ if (record.terminal || record.crash) return false;
175
+ const pid = record.pid;
176
+ if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0) return false;
177
+ return isProcessAlive(pid);
178
+ } catch {
179
+ return false;
180
+ }
181
+ }
182
+ function cleanupRunStatus(days, home = resolveBookHome(), now = Date.now()) {
183
+ const cutoff = now - Math.max(1, days) * 864e5;
184
+ const directory = runStatusDir(home);
185
+ let removed = 0;
186
+ try {
187
+ for (const entry of readdirSync(directory, { withFileTypes: true })) {
188
+ if (!entry.isFile()) continue;
189
+ const isRecord = RUN_RECORD_NAME.test(entry.name);
190
+ if (!isRecord && !RUN_TEMP_NAME.test(entry.name)) continue;
191
+ const path = join(directory, entry.name);
192
+ try {
193
+ if (statSync(path).mtimeMs >= cutoff) continue;
194
+ if (isRecord && recordsLiveRun(path)) continue;
195
+ unlinkSync(path);
196
+ removed++;
197
+ } catch {
198
+ }
199
+ }
200
+ } catch {
201
+ }
202
+ return removed;
203
+ }
143
204
  var RunStatusWriter = class {
144
205
  seed;
145
206
  last;
@@ -274,6 +335,10 @@ async function performPrintReview(effect, env, command) {
274
335
  // Every reviewer, lens and verifier agent is spawned under the host's run,
275
336
  // so the whole pipeline is billed and budgeted as one root.
276
337
  runner: reviewRunnerFor(env.agents.manager(), {
338
+ // The stored session the run belongs to, as the TUI passes its own. Without it
339
+ // each agent's tool-use records carry a made-up `agent:<id>` session id that
340
+ // matches no session file, and `book tool-stats` counts every one separately.
341
+ parentSessionId: env.agents.runContext.sessionId,
277
342
  rootRunId: env.agents.runContext.rootRunId,
278
343
  parentRunId: env.agents.runContext.runId
279
344
  })
@@ -290,12 +355,40 @@ function createPrintEffectRegistry() {
290
355
  return new PrintEffectRegistry().register("send-prompt", (effect) => ({
291
356
  kind: "prompt",
292
357
  prompt: effect.prompt,
293
- commandContext: effect.context
358
+ commandContext: effect.context,
359
+ projectCommand: effect.projectCommand
294
360
  })).register("local-message", (effect, _env, command) => {
295
361
  if (effect.isError) throw new PrintCommandUsageError(command, effect.content);
296
362
  return { kind: "handled", command, output: effect.content };
297
- }).register("review", performPrintReview);
363
+ }).register("review", performPrintReview).register("set-goal", (effect, env, command) => {
364
+ if (!env.goal) throw new UnsupportedPrintCommandError(command, GOAL_REFUSAL);
365
+ env.goal.set(effect.condition);
366
+ const kickoff = goalKickoffPrompt(effect.condition);
367
+ return {
368
+ kind: "prompt",
369
+ prompt: kickoff,
370
+ commandContext: {
371
+ command: {
372
+ name: "goal",
373
+ description: "Set a goal Book checks before stopping",
374
+ body: kickoff,
375
+ source: "project"
376
+ },
377
+ resolvedBody: kickoff
378
+ },
379
+ sessionNameSource: effect.condition
380
+ };
381
+ }).register("clear-goal", (_effect, env, command) => {
382
+ if (!env.goal) throw new UnsupportedPrintCommandError(command, GOAL_REFUSAL);
383
+ const cleared = env.goal.clear();
384
+ return {
385
+ kind: "handled",
386
+ command,
387
+ output: cleared ? formatGoalStatus({ state: "cleared", condition: cleared.condition }) : "No goal set"
388
+ };
389
+ });
298
390
  }
391
+ var GOAL_REFUSAL = "\u2715 /goal needs a host that checks goals; this one does not.";
299
392
  function printBuiltinContext(env, commands) {
300
393
  return {
301
394
  workspace: env.config.workspace,
@@ -308,6 +401,7 @@ function printBuiltinContext(env, commands) {
308
401
  runtimeConfig: env.config,
309
402
  mode: env.mode,
310
403
  usage: null,
404
+ activeGoal: env.goal?.get(),
311
405
  turnDurationMs: 0,
312
406
  contextHistory: [],
313
407
  compactBoundaries: [],
@@ -324,6 +418,9 @@ function supportedCommandHelp(builtins, commands) {
324
418
  const lines = ["Commands supported in print mode:"];
325
419
  for (const definition of builtins.getDefinitions()) {
326
420
  if (definition.nonInteractive) lines.push(` /${definition.name} \u2014 ${definition.description}`);
421
+ for (const subcommand of definition.nonInteractiveSubcommands ?? []) {
422
+ lines.push(` /${definition.name} ${subcommand} \u2014 ${definition.description}`);
423
+ }
327
424
  }
328
425
  const custom = commands.map((command) => `/${command.name}`);
329
426
  lines.push(
@@ -352,7 +449,9 @@ async function resolvePrintCommand(input, env, deps = {}) {
352
449
  const context = printBuiltinContext(env, commands);
353
450
  const resolved = builtins.resolve(parsed.name, context);
354
451
  if (resolved && !resolved.definition.tuiOnly) {
355
- if (!resolved.definition.nonInteractive) {
452
+ const subcommand = parsed.rawArguments.trim().split(/\s+/)[0] ?? "";
453
+ const allowedHere = resolved.definition.nonInteractive || (resolved.definition.nonInteractiveSubcommands?.includes(subcommand) ?? false);
454
+ if (!allowedHere) {
356
455
  throw unsupported(
357
456
  parsed.name,
358
457
  "needs an interactive session: it changes host state instead of producing a prompt.",
@@ -381,7 +480,8 @@ async function resolvePrintCommand(input, env, deps = {}) {
381
480
  commands
382
481
  );
383
482
  }
384
- return dispatch.kind === "handled" ? { ...dispatch, command: parsed.name } : dispatch;
483
+ if (dispatch.kind === "handled") return { ...dispatch, command: parsed.name };
484
+ return dispatch.kind === "prompt" ? { ...dispatch, sessionNameSource: dispatch.sessionNameSource ?? input.trim() } : dispatch;
385
485
  }
386
486
  const custom = commands.find((command) => command.name === parsed.name);
387
487
  if (!custom) return { kind: "passthrough", prompt: input };
@@ -400,7 +500,9 @@ async function resolvePrintCommand(input, env, deps = {}) {
400
500
  kind: "prompt",
401
501
  prompt: body,
402
502
  commandContext: commandEnforcementContext(custom, body),
403
- shellErrors
503
+ sessionNameSource: input.trim(),
504
+ shellErrors,
505
+ projectCommand: custom.source === "project"
404
506
  };
405
507
  }
406
508
 
@@ -584,7 +686,7 @@ function createReaderFlush(stdout, options = {}) {
584
686
  function carriedCostUsd(usage, models) {
585
687
  let worst = null;
586
688
  for (const model of models) {
587
- const quote = estimateUsageCost(model, usage);
689
+ const quote = estimateUsageCost(model, usage, "pool");
588
690
  if (quote.status !== "known") return null;
589
691
  worst = worst === null ? quote.costUsd : Math.max(worst, quote.costUsd);
590
692
  }
@@ -644,12 +746,7 @@ async function runHeadless(config, registry, opts) {
644
746
  let sessionDisposed = false;
645
747
  let startedSessionId;
646
748
  const agentSession = new AgentSession({
647
- // The transcript is the rendered conversation and still carries the tool
648
- // records compaction summarized out of the context history, so prefer it
649
- // when a resume supplies both.
650
- runtime: new SessionRuntime({
651
- history: opts.transcript?.length ? opts.transcript : opts.history
652
- })
749
+ runtime: new SessionRuntime()
653
750
  });
654
751
  const disposeSession = () => {
655
752
  if (sessionDisposed) return;
@@ -659,12 +756,14 @@ async function runHeadless(config, registry, opts) {
659
756
  const runtime = agentSession.getRuntime();
660
757
  runtime.todos.push(...normalizePersistedTodos(opts.plan?.todos));
661
758
  if (opts.plan?.tasks?.length) runtime.tasks.push(...opts.plan.tasks);
759
+ agentSession.restoreGoal(opts.goal);
662
760
  runtime.planUnrestored = planWasLost({
663
761
  planRecordExisted: opts.plan !== void 0,
664
762
  priorMessages: opts.history.length,
665
763
  todos: runtime.todos.length,
666
764
  tasks: runtime.tasks.length
667
765
  });
766
+ seedShellIds(runtime.backgroundShells, opts.transcript ?? opts.history, opts.history);
668
767
  try {
669
768
  if (opts.outputFormat === "stream-json") {
670
769
  emit({ type: "system", model: config.model, cwd: config.workspace });
@@ -979,7 +1078,7 @@ async function runHeadless(config, registry, opts) {
979
1078
  }
980
1079
  }
981
1080
  });
982
- const runParentTurn = async (prompt, displayMessage, userMessage, runContext, commandContext) => {
1081
+ const runParentTurn = async (prompt, displayMessage, userMessage, runContext, commandContext, typedText) => {
983
1082
  eventState.errorReported = false;
984
1083
  let runOutcome;
985
1084
  finalRunOpeningId = userMessage.id;
@@ -1018,6 +1117,8 @@ async function runHeadless(config, registry, opts) {
1018
1117
  parentSessionId: runtimeSessionId,
1019
1118
  runContext,
1020
1119
  runtime,
1120
+ // Only what the user typed invokes a skill by `$name`, as in the TUI.
1121
+ typedText,
1021
1122
  // Command frontmatter enforcement, exactly as the TUI passes it.
1022
1123
  allowedTools: commandContext?.allowedTools,
1023
1124
  modelOverride: commandContext?.modelOverride,
@@ -1076,8 +1177,19 @@ async function runHeadless(config, registry, opts) {
1076
1177
  hookEventSink: createHookEventHandler(opts, emit)
1077
1178
  }),
1078
1179
  runContext
1180
+ },
1181
+ goal: {
1182
+ get: () => agentSession.getGoal(),
1183
+ set: (condition) => {
1184
+ agentSession.setGoal(condition, goalTarget());
1185
+ },
1186
+ clear: () => agentSession.clearGoal(goalTarget())
1079
1187
  }
1080
1188
  });
1189
+ const goalTarget = () => ({
1190
+ sessionId,
1191
+ timelineStore: store && sessionId ? store : void 0
1192
+ });
1081
1193
  const commandResults = [];
1082
1194
  let commandUsage = null;
1083
1195
  const prompts = [];
@@ -1251,16 +1363,29 @@ async function runHeadless(config, registry, opts) {
1251
1363
  displayMessage: prompt,
1252
1364
  userMessage,
1253
1365
  sessionName,
1366
+ sessionNameSource: dispatch.kind === "prompt" ? dispatch.sessionNameSource : void 0,
1254
1367
  timelineStore: store && sessionId ? store : void 0,
1255
1368
  expandShellInput: false,
1369
+ projectCommand: dispatch.kind === "prompt" ? dispatch.projectCommand : void 0,
1256
1370
  runtime,
1257
1371
  signal: runSignal
1258
1372
  });
1373
+ for (const notice of recorded.mentionNotices) process.stderr.write(`${notice}
1374
+ `);
1259
1375
  sessionName = recorded.sessionName;
1260
1376
  const contextMessage = recorded.contextMessage;
1261
1377
  transcript.push(userMessage);
1262
1378
  rootContexts.set(runContext.rootRunId, runContext);
1263
- await runParentTurn(contextMessage, prompt, userMessage, runContext, commandContext);
1379
+ await runParentTurn(
1380
+ contextMessage,
1381
+ prompt,
1382
+ userMessage,
1383
+ runContext,
1384
+ commandContext,
1385
+ // What the user typed is the line they submitted, whatever it resolved to: a command's
1386
+ // body or a built-in command's prompt is not.
1387
+ submitted
1388
+ );
1264
1389
  const spentSoFar = runtime.runAccounting.snapshotRoot(runContext.rootRunId);
1265
1390
  carriedSpend = {
1266
1391
  usage: spentSoFar.inclusiveUsage,
@@ -1324,6 +1449,9 @@ async function runHeadless(config, registry, opts) {
1324
1449
  displayMessage,
1325
1450
  contextMessage: userMessage.contextContent,
1326
1451
  userMessage,
1452
+ // The local name is deliberately not reassigned from the result: a
1453
+ // notification never names the session (#446).
1454
+ sessionName,
1327
1455
  timelineStore: store && sessionId ? store : void 0,
1328
1456
  expandShellInput: false,
1329
1457
  runtime,
@@ -1491,6 +1619,17 @@ function emitAgentEvent(event, opts, emit, state) {
1491
1619
  else console.warn(event.message);
1492
1620
  return;
1493
1621
  }
1622
+ if (event.type === "goal_status") {
1623
+ const endsRunFailed = event.event.state === "failed" || event.event.state === "paused";
1624
+ if (opts.outputFormat === "stream-json") emit({ type: "goal_status", ...event.event });
1625
+ else if (opts.outputFormat === "text" && opts.runSource !== "sdk" && (opts.quiet !== true || endsRunFailed)) {
1626
+ process.stderr.write(
1627
+ `goal: ${progressLine(formatGoalStatus(event.event), PROGRESS_NOTICE_MAX)}
1628
+ `
1629
+ );
1630
+ }
1631
+ return;
1632
+ }
1494
1633
  if (event.type === "agent_notice") {
1495
1634
  if (opts.outputFormat === "stream-json") {
1496
1635
  emit({ type: "agent_notice", agentId: event.agentId, message: event.message });
@@ -1657,8 +1796,8 @@ function emitCompactBoundary(outputFormat, emit, result) {
1657
1796
  });
1658
1797
  }
1659
1798
  async function generatePromptSuggestions(config, _registry, history, signal, resolveAttachment) {
1660
- const { chatCompletionStream } = await import("./openai-compatible-2OB54YST.js");
1661
- const { buildMessages } = await import("./context-CBOBDY5Q.js");
1799
+ const { chatCompletionStream } = await import("./openai-compatible-IPUNBJ53.js");
1800
+ const { buildMessages } = await import("./context-C3RAP6K5.js");
1662
1801
  const suggestionMessages = await buildMessages(
1663
1802
  config,
1664
1803
  [
@@ -2267,10 +2406,51 @@ async function disconnectMcpServers(connections) {
2267
2406
 
2268
2407
  // src/project-approval-notices.ts
2269
2408
  import { join as join2 } from "path";
2409
+ function collectWithheldProviderNotices(gate, workspace) {
2410
+ const notices = [];
2411
+ const approveCommand = (id) => {
2412
+ const { command, note } = providerTrustCommand(workspace, id);
2413
+ return `\`${command}\`${note}`;
2414
+ };
2415
+ for (const dropped of gate.droppedOverrides) {
2416
+ notices.push(`\u26A0 ${describeDroppedOverride(dropped)}`);
2417
+ }
2418
+ for (const entry of gate.partition.reserved) {
2419
+ notices.push(`\u26A0 ${describeReservedProvider(entry)}`);
2420
+ }
2421
+ const pendingIds = new Set(gate.partition.pending.map((entry) => entry.id));
2422
+ for (const entry of gate.partition.pending) {
2423
+ const id = renderUntrustedText(entry.id, 64);
2424
+ const origin = entry.declaration.baseURL ?? entry.declaration.baseUrl;
2425
+ notices.push(
2426
+ `\u26A0 Ignoring workspace-declared provider "${id}" (${describeOrigin(origin)}, key: ${describeApiKeyDeclaration(
2427
+ entry.declaration.apiKey,
2428
+ workspace
2429
+ )}) from ${entry.layers.map((layer) => renderUntrustedText(layer)).join(", ")}: it requires approval. Run ${approveCommand(entry.id)} to review and approve it.`
2430
+ );
2431
+ }
2432
+ for (const selection of gate.withheldSelections) {
2433
+ if (!pendingIds.has(selection.providerId)) continue;
2434
+ notices.push(
2435
+ `\u26A0 ${describeWithheldSelection(selection)} Run ${approveCommand(selection.providerId)} to approve it.`
2436
+ );
2437
+ }
2438
+ for (const selection of gate.unaffectedSelections) {
2439
+ if (!pendingIds.has(selection.providerId)) continue;
2440
+ notices.push(`\u26A0 ${describeUnaffectedSelection(selection)}`);
2441
+ }
2442
+ if (gate.legacyBaseUrl?.state === "pending") {
2443
+ notices.push(
2444
+ `\u26A0 Ignoring baseUrl "${renderUntrustedText(gate.legacyBaseUrl.baseUrl)}" from ${LEGACY_BASE_URL_ID}: it would receive BOOK_API_KEY, so it requires approval. Run ${approveCommand(LEGACY_BASE_URL_ID)} to review and approve it.`
2445
+ );
2446
+ }
2447
+ return notices;
2448
+ }
2270
2449
  function collectWithheldProjectNotices(input) {
2271
- if (!input.settingsEnabled) return [];
2450
+ const providerNotices = input.providerGate ? collectWithheldProviderNotices(input.providerGate, input.workspace) : [];
2451
+ if (!input.settingsEnabled) return providerNotices;
2272
2452
  const projectPath = join2(input.workspace, ".book", "settings.json");
2273
- const projectSettings = loadSettingsFile(projectPath);
2453
+ const projectSettings = isUserSettingsFile(projectPath) ? null : loadSettingsFile(projectPath);
2274
2454
  const notices = [];
2275
2455
  const allow = partitionProjectAllowRules(
2276
2456
  projectSettings?.permissions?.allow ?? [],
@@ -2331,16 +2511,27 @@ function collectWithheldProjectNotices(input) {
2331
2511
  `\u26A0 ${activation} declared sandbox.enabled=true, so sandbox.autoAllowBashIfSandboxed is false for this session: no Bash command is auto-approved. ${TRUSTED_SETTINGS_LOCATION}`
2332
2512
  );
2333
2513
  }
2334
- return notices;
2514
+ return [...notices, ...providerNotices];
2515
+ }
2516
+ function collectSkippedProjectFileNotices(workspace) {
2517
+ return [
2518
+ ...collectSkippedInstructionNotices(workspace),
2519
+ ...collectSkippedReviewNotices(workspace),
2520
+ ...collectSkippedCommandNotices(workspace)
2521
+ ];
2335
2522
  }
2336
2523
 
2337
2524
  export {
2525
+ runStatusPath,
2338
2526
  readRunStatus,
2527
+ cleanupRunStatus,
2339
2528
  isReaderGoneError,
2340
2529
  runHeadless,
2341
2530
  buildMcpToolDefinitions,
2342
2531
  connectMcpServers,
2343
2532
  disconnectMcpServers,
2344
- collectWithheldProjectNotices
2533
+ collectWithheldProviderNotices,
2534
+ collectWithheldProjectNotices,
2535
+ collectSkippedProjectFileNotices
2345
2536
  };
2346
- //# sourceMappingURL=chunk-TLX3H2CL.js.map
2537
+ //# sourceMappingURL=chunk-E5WLQSKA.js.map