@di-code/coding-agent 0.1.4 → 0.1.6

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 (111) hide show
  1. package/README.md +74 -23
  2. package/dist/cli.d.ts +15 -0
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +96 -17
  5. package/dist/cli.js.map +1 -1
  6. package/dist/core/clipboard-image.d.ts +3 -3
  7. package/dist/core/clipboard-image.d.ts.map +1 -1
  8. package/dist/core/clipboard-image.js +26 -13
  9. package/dist/core/clipboard-image.js.map +1 -1
  10. package/dist/core/resources/loader.d.ts.map +1 -1
  11. package/dist/core/resources/loader.js +15 -42
  12. package/dist/core/resources/loader.js.map +1 -1
  13. package/dist/core/resources/types.d.ts +6 -9
  14. package/dist/core/resources/types.d.ts.map +1 -1
  15. package/dist/core/resources/types.js.map +1 -1
  16. package/dist/core/session/session-manager.d.ts +12 -1
  17. package/dist/core/session/session-manager.d.ts.map +1 -1
  18. package/dist/core/session/session-manager.js +66 -3
  19. package/dist/core/session/session-manager.js.map +1 -1
  20. package/dist/core/session/session-storage.d.ts.map +1 -1
  21. package/dist/core/session/session-storage.js +29 -15
  22. package/dist/core/session/session-storage.js.map +1 -1
  23. package/dist/core/session/types.d.ts +6 -1
  24. package/dist/core/session/types.d.ts.map +1 -1
  25. package/dist/core/session/types.js +1 -1
  26. package/dist/core/session/types.js.map +1 -1
  27. package/dist/core/session.d.ts +21 -1
  28. package/dist/core/session.d.ts.map +1 -1
  29. package/dist/core/session.js +81 -12
  30. package/dist/core/session.js.map +1 -1
  31. package/dist/core/system-prompt.js +2 -2
  32. package/dist/core/system-prompt.js.map +1 -1
  33. package/dist/core/tools/file-search.d.ts +14 -0
  34. package/dist/core/tools/file-search.d.ts.map +1 -0
  35. package/dist/core/tools/file-search.js +99 -0
  36. package/dist/core/tools/file-search.js.map +1 -0
  37. package/dist/core/tools/glob.d.ts +11 -0
  38. package/dist/core/tools/glob.d.ts.map +1 -0
  39. package/dist/core/tools/glob.js +44 -0
  40. package/dist/core/tools/glob.js.map +1 -0
  41. package/dist/core/tools/grep.d.ts +13 -0
  42. package/dist/core/tools/grep.d.ts.map +1 -0
  43. package/dist/core/tools/grep.js +67 -0
  44. package/dist/core/tools/grep.js.map +1 -0
  45. package/dist/core/tools/load-skill.d.ts +10 -0
  46. package/dist/core/tools/load-skill.d.ts.map +1 -0
  47. package/dist/core/tools/load-skill.js +22 -0
  48. package/dist/core/tools/load-skill.js.map +1 -0
  49. package/dist/core/tools/path-boundary.d.ts +2 -0
  50. package/dist/core/tools/path-boundary.d.ts.map +1 -1
  51. package/dist/core/tools/path-boundary.js +4 -0
  52. package/dist/core/tools/path-boundary.js.map +1 -1
  53. package/dist/core/user-data.d.ts +3 -0
  54. package/dist/core/user-data.d.ts.map +1 -0
  55. package/dist/core/user-data.js +8 -0
  56. package/dist/core/user-data.js.map +1 -0
  57. package/dist/entry.js +13 -0
  58. package/dist/entry.js.map +1 -1
  59. package/dist/extensions/runtime.js +2 -2
  60. package/dist/extensions/runtime.js.map +1 -1
  61. package/dist/i18n.d.ts +5 -0
  62. package/dist/i18n.d.ts.map +1 -0
  63. package/dist/i18n.js +223 -0
  64. package/dist/i18n.js.map +1 -0
  65. package/dist/index.d.ts +3 -0
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +3 -0
  68. package/dist/index.js.map +1 -1
  69. package/dist/main.d.ts +17 -0
  70. package/dist/main.d.ts.map +1 -1
  71. package/dist/main.js +238 -11
  72. package/dist/main.js.map +1 -1
  73. package/dist/mcp/config.d.ts +33 -0
  74. package/dist/mcp/config.d.ts.map +1 -0
  75. package/dist/mcp/config.js +211 -0
  76. package/dist/mcp/config.js.map +1 -0
  77. package/dist/mcp/loader.d.ts +21 -0
  78. package/dist/mcp/loader.d.ts.map +1 -0
  79. package/dist/mcp/loader.js +60 -0
  80. package/dist/mcp/loader.js.map +1 -0
  81. package/dist/mcp/tool-adapter.d.ts +5 -0
  82. package/dist/mcp/tool-adapter.d.ts.map +1 -0
  83. package/dist/mcp/tool-adapter.js +149 -0
  84. package/dist/mcp/tool-adapter.js.map +1 -0
  85. package/dist/modes/interactive-components.d.ts +2 -2
  86. package/dist/modes/interactive-components.d.ts.map +1 -1
  87. package/dist/modes/interactive-components.js +30 -33
  88. package/dist/modes/interactive-components.js.map +1 -1
  89. package/dist/modes/interactive-state.d.ts.map +1 -1
  90. package/dist/modes/interactive-state.js +1 -0
  91. package/dist/modes/interactive-state.js.map +1 -1
  92. package/dist/modes/interactive.d.ts +9 -0
  93. package/dist/modes/interactive.d.ts.map +1 -1
  94. package/dist/modes/interactive.js +191 -62
  95. package/dist/modes/interactive.js.map +1 -1
  96. package/dist/modes/tree-selector.d.ts +30 -0
  97. package/dist/modes/tree-selector.d.ts.map +1 -0
  98. package/dist/modes/tree-selector.js +205 -0
  99. package/dist/modes/tree-selector.js.map +1 -0
  100. package/dist/plugins/loader.d.ts +19 -1
  101. package/dist/plugins/loader.d.ts.map +1 -1
  102. package/dist/plugins/loader.js +21 -2
  103. package/dist/plugins/loader.js.map +1 -1
  104. package/dist/provider-onboarding.d.ts.map +1 -1
  105. package/dist/provider-onboarding.js +119 -22
  106. package/dist/provider-onboarding.js.map +1 -1
  107. package/dist/startup.d.ts +24 -1
  108. package/dist/startup.d.ts.map +1 -1
  109. package/dist/startup.js +200 -9
  110. package/dist/startup.js.map +1 -1
  111. package/package.json +6 -4
@@ -4,6 +4,7 @@ import { createRequire } from "node:module";
4
4
  import { tmpdir } from "node:os";
5
5
  import { basename, dirname, join, resolve } from "node:path";
6
6
  import { imageContentFromBytes } from "./image-input.js";
7
+ import { workspaceStorageKey } from "./user-data.js";
7
8
  const CLIPBOARD_FILE_PREFIX = "di-code-clipboard-";
8
9
  const CLIPBOARD_FILE_PATTERN = new RegExp(`^${CLIPBOARD_FILE_PREFIX}[0-9a-f-]+\\.(?:png|jpg|jpeg|gif|webp)$`, "i");
9
10
  const DEFAULT_STALE_AGE_MS = 24 * 60 * 60 * 1000;
@@ -38,19 +39,19 @@ function extensionForMimeType(mimeType) {
38
39
  return "png";
39
40
  }
40
41
  }
41
- export function clipboardImageDirectory(root) {
42
- return join(root, ".di-code", "clipboard");
42
+ export function clipboardImageDirectory(agentDir, root) {
43
+ return join(agentDir, "clipboard", workspaceStorageKey(root), String(process.pid));
43
44
  }
44
45
  export function isClipboardImagePath(path, directory) {
45
46
  const absolutePath = resolve(path);
46
47
  return dirname(absolutePath) === resolve(directory) && CLIPBOARD_FILE_PATTERN.test(basename(absolutePath));
47
48
  }
48
- /** Removes generated clipboard images that were left by an older process. */
49
- export async function cleanupStaleClipboardImages(root, maxAgeMs = DEFAULT_STALE_AGE_MS) {
50
- const directory = clipboardImageDirectory(root);
51
- let entries;
49
+ /** Removes generated clipboard images that were left by older processes for this workspace. */
50
+ export async function cleanupStaleClipboardImages(agentDir, root, maxAgeMs = DEFAULT_STALE_AGE_MS) {
51
+ const workspaceDirectory = join(agentDir, "clipboard", workspaceStorageKey(root));
52
+ let processDirectories;
52
53
  try {
53
- entries = await readdir(directory, { withFileTypes: true });
54
+ processDirectories = await readdir(workspaceDirectory, { withFileTypes: true });
54
55
  }
55
56
  catch (cause) {
56
57
  if (cause instanceof Error && "code" in cause && cause.code === "ENOENT")
@@ -58,13 +59,25 @@ export async function cleanupStaleClipboardImages(root, maxAgeMs = DEFAULT_STALE
58
59
  throw cause;
59
60
  }
60
61
  const cutoff = Date.now() - maxAgeMs;
61
- await Promise.all(entries
62
- .filter((entry) => entry.isFile() && CLIPBOARD_FILE_PATTERN.test(entry.name))
63
- .map(async (entry) => {
64
- const path = join(directory, entry.name);
62
+ await Promise.all(processDirectories
63
+ .filter((entry) => entry.isDirectory())
64
+ .map(async (processDirectory) => {
65
+ const directory = join(workspaceDirectory, processDirectory.name);
65
66
  try {
66
- if ((await stat(path)).mtimeMs < cutoff)
67
- await unlink(path);
67
+ const entries = await readdir(directory, { withFileTypes: true });
68
+ await Promise.all(entries
69
+ .filter((entry) => entry.isFile() && CLIPBOARD_FILE_PATTERN.test(entry.name))
70
+ .map(async (entry) => {
71
+ const path = join(directory, entry.name);
72
+ try {
73
+ if ((await stat(path)).mtimeMs < cutoff)
74
+ await unlink(path);
75
+ }
76
+ catch (cause) {
77
+ if (!(cause instanceof Error && "code" in cause && cause.code === "ENOENT"))
78
+ throw cause;
79
+ }
80
+ }));
68
81
  }
69
82
  catch (cause) {
70
83
  if (!(cause instanceof Error && "code" in cause && cause.code === "ENOENT"))
@@ -1 +1 @@
1
- {"version":3,"file":"clipboard-image.js","sourceRoot":"","sources":["../../src/core/clipboard-image.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport type { Dirent } from \"node:fs\";\nimport { mkdir, readdir, stat, unlink, writeFile } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport { basename, dirname, join, resolve } from \"node:path\";\nimport type { ImageContent } from \"@di-code/ai\";\nimport { imageContentFromBytes } from \"./image-input.ts\";\n\ninterface ClipboardModule {\n\thasImage(): boolean;\n\tgetImageBinary(): Promise<Uint8Array | readonly number[]>;\n}\n\nconst CLIPBOARD_FILE_PREFIX = \"di-code-clipboard-\";\nconst CLIPBOARD_FILE_PATTERN = new RegExp(`^${CLIPBOARD_FILE_PREFIX}[0-9a-f-]+\\\\.(?:png|jpg|jpeg|gif|webp)$`, \"i\");\nconst DEFAULT_STALE_AGE_MS = 24 * 60 * 60 * 1000;\n\nconst require = createRequire(import.meta.url);\n\nfunction loadClipboardModule(): ClipboardModule | undefined {\n\ttry {\n\t\treturn require(\"@mariozechner/clipboard\") as ClipboardModule;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/** Reads a native clipboard image when the optional platform package is available. */\nexport async function readClipboardImage(): Promise<ImageContent | null> {\n\tconst clipboard = loadClipboardModule();\n\tif (!clipboard?.hasImage()) return null;\n\tconst bytes = await clipboard.getImageBinary();\n\tif (bytes.length === 0) return null;\n\treturn imageContentFromBytes(Uint8Array.from(bytes), \"clipboard image\");\n}\n\nfunction extensionForMimeType(mimeType: string): string {\n\tswitch (mimeType.toLowerCase().split(\";\", 1)[0]) {\n\t\tcase \"image/jpeg\":\n\t\t\treturn \"jpg\";\n\t\tcase \"image/gif\":\n\t\t\treturn \"gif\";\n\t\tcase \"image/webp\":\n\t\t\treturn \"webp\";\n\t\tdefault:\n\t\t\treturn \"png\";\n\t}\n}\n\nexport function clipboardImageDirectory(root: string): string {\n\treturn join(root, \".di-code\", \"clipboard\");\n}\n\nexport function isClipboardImagePath(path: string, directory: string): boolean {\n\tconst absolutePath = resolve(path);\n\treturn dirname(absolutePath) === resolve(directory) && CLIPBOARD_FILE_PATTERN.test(basename(absolutePath));\n}\n\n/** Removes generated clipboard images that were left by an older process. */\nexport async function cleanupStaleClipboardImages(root: string, maxAgeMs = DEFAULT_STALE_AGE_MS): Promise<void> {\n\tconst directory = clipboardImageDirectory(root);\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = await readdir(directory, { withFileTypes: true });\n\t} catch (cause) {\n\t\tif (cause instanceof Error && \"code\" in cause && cause.code === \"ENOENT\") return;\n\t\tthrow cause;\n\t}\n\tconst cutoff = Date.now() - maxAgeMs;\n\tawait Promise.all(\n\t\tentries\n\t\t\t.filter((entry) => entry.isFile() && CLIPBOARD_FILE_PATTERN.test(entry.name))\n\t\t\t.map(async (entry) => {\n\t\t\t\tconst path = join(directory, entry.name);\n\t\t\t\ttry {\n\t\t\t\t\tif ((await stat(path)).mtimeMs < cutoff) await unlink(path);\n\t\t\t\t} catch (cause) {\n\t\t\t\t\tif (!(cause instanceof Error && \"code\" in cause && cause.code === \"ENOENT\")) throw cause;\n\t\t\t\t}\n\t\t\t}),\n\t);\n}\n\nexport async function removeClipboardImage(path: string, directory: string): Promise<void> {\n\tif (!isClipboardImagePath(path, directory)) return;\n\ttry {\n\t\tawait unlink(path);\n\t} catch (cause) {\n\t\tif (!(cause instanceof Error && \"code\" in cause && cause.code === \"ENOENT\")) throw cause;\n\t}\n}\n\n/** Writes one clipboard image to a temporary file, matching Pi's editor flow. */\nexport async function writeImageContentToTempFile(image: ImageContent, directory = tmpdir()): Promise<string> {\n\tconst extension = extensionForMimeType(image.mimeType);\n\tawait mkdir(directory, { recursive: true });\n\tconst path = join(directory, `${CLIPBOARD_FILE_PREFIX}${randomUUID()}.${extension}`);\n\tawait writeFile(path, Buffer.from(image.data, \"base64\"));\n\treturn path;\n}\n\n/** Reads the clipboard and returns the temporary path that should be inserted into the editor. */\nexport async function readClipboardImagePath(directory = tmpdir()): Promise<string | null> {\n\tconst image = await readClipboardImage();\n\treturn image ? writeImageContentToTempFile(image, directory) : null;\n}\n"]}
1
+ {"version":3,"file":"clipboard-image.js","sourceRoot":"","sources":["../../src/core/clipboard-image.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport type { Dirent } from \"node:fs\";\nimport { mkdir, readdir, stat, unlink, writeFile } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport { basename, dirname, join, resolve } from \"node:path\";\nimport type { ImageContent } from \"@di-code/ai\";\nimport { imageContentFromBytes } from \"./image-input.ts\";\nimport { workspaceStorageKey } from \"./user-data.ts\";\n\ninterface ClipboardModule {\n\thasImage(): boolean;\n\tgetImageBinary(): Promise<Uint8Array | readonly number[]>;\n}\n\nconst CLIPBOARD_FILE_PREFIX = \"di-code-clipboard-\";\nconst CLIPBOARD_FILE_PATTERN = new RegExp(`^${CLIPBOARD_FILE_PREFIX}[0-9a-f-]+\\\\.(?:png|jpg|jpeg|gif|webp)$`, \"i\");\nconst DEFAULT_STALE_AGE_MS = 24 * 60 * 60 * 1000;\n\nconst require = createRequire(import.meta.url);\n\nfunction loadClipboardModule(): ClipboardModule | undefined {\n\ttry {\n\t\treturn require(\"@mariozechner/clipboard\") as ClipboardModule;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/** Reads a native clipboard image when the optional platform package is available. */\nexport async function readClipboardImage(): Promise<ImageContent | null> {\n\tconst clipboard = loadClipboardModule();\n\tif (!clipboard?.hasImage()) return null;\n\tconst bytes = await clipboard.getImageBinary();\n\tif (bytes.length === 0) return null;\n\treturn imageContentFromBytes(Uint8Array.from(bytes), \"clipboard image\");\n}\n\nfunction extensionForMimeType(mimeType: string): string {\n\tswitch (mimeType.toLowerCase().split(\";\", 1)[0]) {\n\t\tcase \"image/jpeg\":\n\t\t\treturn \"jpg\";\n\t\tcase \"image/gif\":\n\t\t\treturn \"gif\";\n\t\tcase \"image/webp\":\n\t\t\treturn \"webp\";\n\t\tdefault:\n\t\t\treturn \"png\";\n\t}\n}\n\nexport function clipboardImageDirectory(agentDir: string, root: string): string {\n\treturn join(agentDir, \"clipboard\", workspaceStorageKey(root), String(process.pid));\n}\n\nexport function isClipboardImagePath(path: string, directory: string): boolean {\n\tconst absolutePath = resolve(path);\n\treturn dirname(absolutePath) === resolve(directory) && CLIPBOARD_FILE_PATTERN.test(basename(absolutePath));\n}\n\n/** Removes generated clipboard images that were left by older processes for this workspace. */\nexport async function cleanupStaleClipboardImages(\n\tagentDir: string,\n\troot: string,\n\tmaxAgeMs = DEFAULT_STALE_AGE_MS,\n): Promise<void> {\n\tconst workspaceDirectory = join(agentDir, \"clipboard\", workspaceStorageKey(root));\n\tlet processDirectories: Dirent[];\n\ttry {\n\t\tprocessDirectories = await readdir(workspaceDirectory, { withFileTypes: true });\n\t} catch (cause) {\n\t\tif (cause instanceof Error && \"code\" in cause && cause.code === \"ENOENT\") return;\n\t\tthrow cause;\n\t}\n\tconst cutoff = Date.now() - maxAgeMs;\n\tawait Promise.all(\n\t\tprocessDirectories\n\t\t\t.filter((entry) => entry.isDirectory())\n\t\t\t.map(async (processDirectory) => {\n\t\t\t\tconst directory = join(workspaceDirectory, processDirectory.name);\n\t\t\t\ttry {\n\t\t\t\t\tconst entries = await readdir(directory, { withFileTypes: true });\n\t\t\t\t\tawait Promise.all(\n\t\t\t\t\t\tentries\n\t\t\t\t\t\t\t.filter((entry) => entry.isFile() && CLIPBOARD_FILE_PATTERN.test(entry.name))\n\t\t\t\t\t\t\t.map(async (entry) => {\n\t\t\t\t\t\t\t\tconst path = join(directory, entry.name);\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tif ((await stat(path)).mtimeMs < cutoff) await unlink(path);\n\t\t\t\t\t\t\t\t} catch (cause) {\n\t\t\t\t\t\t\t\t\tif (!(cause instanceof Error && \"code\" in cause && cause.code === \"ENOENT\")) throw cause;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t} catch (cause) {\n\t\t\t\t\tif (!(cause instanceof Error && \"code\" in cause && cause.code === \"ENOENT\")) throw cause;\n\t\t\t\t}\n\t\t\t}),\n\t);\n}\n\nexport async function removeClipboardImage(path: string, directory: string): Promise<void> {\n\tif (!isClipboardImagePath(path, directory)) return;\n\ttry {\n\t\tawait unlink(path);\n\t} catch (cause) {\n\t\tif (!(cause instanceof Error && \"code\" in cause && cause.code === \"ENOENT\")) throw cause;\n\t}\n}\n\n/** Writes one clipboard image to a temporary file, matching Pi's editor flow. */\nexport async function writeImageContentToTempFile(image: ImageContent, directory = tmpdir()): Promise<string> {\n\tconst extension = extensionForMimeType(image.mimeType);\n\tawait mkdir(directory, { recursive: true });\n\tconst path = join(directory, `${CLIPBOARD_FILE_PREFIX}${randomUUID()}.${extension}`);\n\tawait writeFile(path, Buffer.from(image.data, \"base64\"));\n\treturn path;\n}\n\n/** Reads the clipboard and returns the temporary path that should be inserted into the editor. */\nexport async function readClipboardImagePath(directory = tmpdir()): Promise<string | null> {\n\tconst image = await readClipboardImage();\n\treturn image ? writeImageContentToTempFile(image, directory) : null;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/core/resources/loader.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAGX,cAAc,EACd,qBAAqB,EAErB,gBAAgB,EAEhB,MAAM,YAAY,CAAC;AAyFpB,qBAAa,qBAAsB,YAAW,cAAc;IAC3D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;gBAEpC,OAAO,EAAE,qBAAqB;IAIpC,IAAI,IAAI,OAAO,CAAC,gBAAgB,CAAC;CAmEvC;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAE7F"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/core/resources/loader.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAGX,cAAc,EACd,qBAAqB,EAErB,gBAAgB,EAEhB,MAAM,YAAY,CAAC;AAiFpB,qBAAa,qBAAsB,YAAW,cAAc;IAC3D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;gBAEpC,OAAO,EAAE,qBAAqB;IAIpC,IAAI,IAAI,OAAO,CAAC,gBAAgB,CAAC;CAsDvC;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAE7F"}
@@ -1,6 +1,6 @@
1
1
  import { access, readFile, stat } from "node:fs/promises";
2
- import { dirname, join, parse, resolve } from "node:path";
3
- import { discoverSkills, loadSkill } from "./skills.js";
2
+ import { join, resolve } from "node:path";
3
+ import { createSkillCatalog, discoverSkills, loadSkill } from "@di-code/skills";
4
4
  const MAX_CONTEXT_FILE_BYTES = 256 * 1024;
5
5
  function isMissingPath(cause) {
6
6
  return cause instanceof Error && "code" in cause && cause.code === "ENOENT";
@@ -64,17 +64,7 @@ async function loadContextFiles(options) {
64
64
  diagnostics.push(loaded.diagnostic);
65
65
  };
66
66
  await add(resolve(options.agentDir), "global");
67
- const ancestors = [];
68
- let current = resolve(options.cwd);
69
- while (true) {
70
- ancestors.unshift(current);
71
- const parent = dirname(current);
72
- if (parent === current || parse(current).root === current)
73
- break;
74
- current = parent;
75
- }
76
- for (const directory of ancestors)
77
- await add(directory, directory === resolve(options.cwd) ? "project" : "ancestor");
67
+ await add(resolve(options.cwd), "project");
78
68
  return { contextFiles, diagnostics };
79
69
  }
80
70
  async function exists(directory) {
@@ -96,29 +86,9 @@ export class DefaultResourceLoader {
96
86
  async load() {
97
87
  const context = await loadContextFiles(this.options);
98
88
  const diagnostics = [...context.diagnostics];
99
- const skills = new Map();
100
- const add = (candidate, candidateDiagnostics) => {
101
- diagnostics.push(...candidateDiagnostics);
102
- const existing = skills.get(candidate.name);
103
- if (existing) {
104
- diagnostics.push({
105
- path: candidate.filePath,
106
- kind: "skill",
107
- stage: "collision",
108
- severity: "warning",
109
- message: `Skill name collision: "${candidate.name}"; using ${existing.filePath}`,
110
- });
111
- return;
112
- }
113
- skills.set(candidate.name, candidate);
114
- };
89
+ const skillResults = [];
115
90
  const addResults = (results) => {
116
- for (const result of results) {
117
- if (result.skill)
118
- add(result.skill, result.diagnostics);
119
- else
120
- diagnostics.push(...result.diagnostics);
121
- }
91
+ skillResults.push(...results);
122
92
  };
123
93
  if (!this.options.noSkills) {
124
94
  for (const rawPath of this.options.skillPaths ?? []) {
@@ -129,10 +99,7 @@ export class DefaultResourceLoader {
129
99
  addResults(await discoverSkills(path, "explicit"));
130
100
  else {
131
101
  const result = await loadSkill(path, "explicit");
132
- if (result.skill)
133
- add(result.skill, result.diagnostics);
134
- else
135
- diagnostics.push(...result.diagnostics);
102
+ skillResults.push(result);
136
103
  }
137
104
  }
138
105
  catch (cause) {
@@ -144,7 +111,7 @@ export class DefaultResourceLoader {
144
111
  }
145
112
  const projectDirectories = [
146
113
  join(this.options.cwd, ".di-code", "skills"),
147
- join(this.options.cwd, ".pi", "skills"),
114
+ join(this.options.cwd, ".agents", "skills"),
148
115
  ];
149
116
  if (this.options.projectTrusted === true) {
150
117
  for (const directory of projectDirectories)
@@ -163,9 +130,15 @@ export class DefaultResourceLoader {
163
130
  }
164
131
  }
165
132
  }
166
- addResults(await discoverSkills(join(this.options.agentDir, "skills"), "global"));
133
+ addResults(await discoverSkills(join(this.options.agentDir, "skills"), "user"));
167
134
  }
168
- return { contextFiles: context.contextFiles, skills: [...skills.values()], diagnostics };
135
+ const catalog = createSkillCatalog(skillResults);
136
+ const skillDiagnostics = catalog.diagnostics.map((diagnostic) => diagnostic);
137
+ const skills = catalog.skills.map((skill) => ({
138
+ ...skill,
139
+ scope: skill.source === "explicit" ? "explicit" : skill.source === "project" ? "project" : "global",
140
+ }));
141
+ return { contextFiles: context.contextFiles, skills, diagnostics: [...diagnostics, ...skillDiagnostics] };
169
142
  }
170
143
  }
171
144
  export async function loadResources(options) {
@@ -1 +1 @@
1
- {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../../src/core/resources/loader.ts"],"names":[],"mappings":"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","sourcesContent":["import { access, readFile, stat } from \"node:fs/promises\";\nimport { dirname, join, parse, resolve } from \"node:path\";\nimport { discoverSkills, loadSkill } from \"./skills.ts\";\nimport type {\n\tContextFile,\n\tResourceDiagnostic,\n\tResourceLoader,\n\tResourceLoaderOptions,\n\tResourceScope,\n\tResourceSnapshot,\n\tSkillResource,\n} from \"./types.ts\";\n\nconst MAX_CONTEXT_FILE_BYTES = 256 * 1024;\n\nfunction isMissingPath(cause: unknown): boolean {\n\treturn cause instanceof Error && \"code\" in cause && cause.code === \"ENOENT\";\n}\n\nfunction contextDiagnostic(path: string, stage: ResourceDiagnostic[\"stage\"], message: string): ResourceDiagnostic {\n\treturn { path, kind: \"agents\", stage, severity: \"warning\", message };\n}\n\nasync function locateAgentsFile(directory: string): Promise<string | undefined> {\n\tfor (const name of [\"AGENTS.md\", \"AGENTS.MD\"]) {\n\t\tconst path = join(directory, name);\n\t\ttry {\n\t\t\tif ((await stat(path)).isFile()) return path;\n\t\t} catch (cause) {\n\t\t\tif (!isMissingPath(cause)) throw cause;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nasync function loadContextFile(\n\tpath: string,\n\tscope: ResourceScope,\n): Promise<{ file?: ContextFile; diagnostic?: ResourceDiagnostic }> {\n\ttry {\n\t\tconst metadata = await stat(path);\n\t\tif (metadata.size > MAX_CONTEXT_FILE_BYTES) {\n\t\t\treturn { diagnostic: contextDiagnostic(path, \"read\", `AGENTS.md exceeds ${MAX_CONTEXT_FILE_BYTES} byte limit`) };\n\t\t}\n\t\treturn { file: { kind: \"agents\", path: resolve(path), scope, content: await readFile(path, \"utf8\") } };\n\t} catch (cause) {\n\t\tconst message = cause instanceof Error ? cause.message : String(cause);\n\t\treturn { diagnostic: contextDiagnostic(path, \"read\", `Failed to read AGENTS.md: ${message}`) };\n\t}\n}\n\nasync function loadContextFiles(options: ResourceLoaderOptions): Promise<{\n\tcontextFiles: ContextFile[];\n\tdiagnostics: ResourceDiagnostic[];\n}> {\n\tif (options.noContextFiles) return { contextFiles: [], diagnostics: [] };\n\tconst contextFiles: ContextFile[] = [];\n\tconst diagnostics: ResourceDiagnostic[] = [];\n\tconst seen = new Set<string>();\n\tconst add = async (directory: string, scope: ResourceScope): Promise<void> => {\n\t\tlet path: string | undefined;\n\t\ttry {\n\t\t\tpath = await locateAgentsFile(directory);\n\t\t} catch (cause) {\n\t\t\tconst message = cause instanceof Error ? cause.message : String(cause);\n\t\t\tdiagnostics.push(contextDiagnostic(directory, \"discover\", `Failed to discover AGENTS.md: ${message}`));\n\t\t\treturn;\n\t\t}\n\t\tif (!path) return;\n\t\tconst absolutePath = resolve(path);\n\t\tif (seen.has(absolutePath)) return;\n\t\tseen.add(absolutePath);\n\t\tconst loaded = await loadContextFile(absolutePath, scope);\n\t\tif (loaded.file) contextFiles.push(loaded.file);\n\t\tif (loaded.diagnostic) diagnostics.push(loaded.diagnostic);\n\t};\n\n\tawait add(resolve(options.agentDir), \"global\");\n\tconst ancestors: string[] = [];\n\tlet current = resolve(options.cwd);\n\twhile (true) {\n\t\tancestors.unshift(current);\n\t\tconst parent = dirname(current);\n\t\tif (parent === current || parse(current).root === current) break;\n\t\tcurrent = parent;\n\t}\n\tfor (const directory of ancestors) await add(directory, directory === resolve(options.cwd) ? \"project\" : \"ancestor\");\n\treturn { contextFiles, diagnostics };\n}\n\nasync function exists(directory: string): Promise<boolean> {\n\ttry {\n\t\tawait access(directory);\n\t\treturn true;\n\t} catch (cause) {\n\t\tif (isMissingPath(cause)) return false;\n\t\tthrow cause;\n\t}\n}\n\nexport class DefaultResourceLoader implements ResourceLoader {\n\tprivate readonly options: ResourceLoaderOptions;\n\n\tconstructor(options: ResourceLoaderOptions) {\n\t\tthis.options = options;\n\t}\n\n\tasync load(): Promise<ResourceSnapshot> {\n\t\tconst context = await loadContextFiles(this.options);\n\t\tconst diagnostics = [...context.diagnostics];\n\t\tconst skills = new Map<string, SkillResource>();\n\t\tconst add = (candidate: SkillResource, candidateDiagnostics: readonly ResourceDiagnostic[]): void => {\n\t\t\tdiagnostics.push(...candidateDiagnostics);\n\t\t\tconst existing = skills.get(candidate.name);\n\t\t\tif (existing) {\n\t\t\t\tdiagnostics.push({\n\t\t\t\t\tpath: candidate.filePath,\n\t\t\t\t\tkind: \"skill\",\n\t\t\t\t\tstage: \"collision\",\n\t\t\t\t\tseverity: \"warning\",\n\t\t\t\t\tmessage: `Skill name collision: \"${candidate.name}\"; using ${existing.filePath}`,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tskills.set(candidate.name, candidate);\n\t\t};\n\t\tconst addResults = (results: Awaited<ReturnType<typeof discoverSkills>>): void => {\n\t\t\tfor (const result of results) {\n\t\t\t\tif (result.skill) add(result.skill, result.diagnostics);\n\t\t\t\telse diagnostics.push(...result.diagnostics);\n\t\t\t}\n\t\t};\n\n\t\tif (!this.options.noSkills) {\n\t\t\tfor (const rawPath of this.options.skillPaths ?? []) {\n\t\t\t\tconst path = resolve(this.options.cwd, rawPath);\n\t\t\t\ttry {\n\t\t\t\t\tconst metadata = await stat(path);\n\t\t\t\t\tif (metadata.isDirectory()) addResults(await discoverSkills(path, \"explicit\"));\n\t\t\t\t\telse {\n\t\t\t\t\t\tconst result = await loadSkill(path, \"explicit\");\n\t\t\t\t\t\tif (result.skill) add(result.skill, result.diagnostics);\n\t\t\t\t\t\telse diagnostics.push(...result.diagnostics);\n\t\t\t\t\t}\n\t\t\t\t} catch (cause) {\n\t\t\t\t\tconst message = isMissingPath(cause)\n\t\t\t\t\t\t? \"Skill path does not exist\"\n\t\t\t\t\t\t: `Failed to discover skill path: ${String(cause)}`;\n\t\t\t\t\tdiagnostics.push({ path, kind: \"skill\", stage: \"discover\", severity: \"warning\", message });\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst projectDirectories = [\n\t\t\t\tjoin(this.options.cwd, \".di-code\", \"skills\"),\n\t\t\t\tjoin(this.options.cwd, \".pi\", \"skills\"),\n\t\t\t];\n\t\t\tif (this.options.projectTrusted === true) {\n\t\t\t\tfor (const directory of projectDirectories) addResults(await discoverSkills(directory, \"project\"));\n\t\t\t} else {\n\t\t\t\tfor (const directory of projectDirectories) {\n\t\t\t\t\tif (await exists(directory)) {\n\t\t\t\t\t\tdiagnostics.push({\n\t\t\t\t\t\t\tpath: resolve(directory),\n\t\t\t\t\t\t\tkind: \"skill\",\n\t\t\t\t\t\t\tstage: \"trust\",\n\t\t\t\t\t\t\tseverity: \"warning\",\n\t\t\t\t\t\t\tmessage: \"Project skill skipped because project trust is not granted\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\taddResults(await discoverSkills(join(this.options.agentDir, \"skills\"), \"global\"));\n\t\t}\n\t\treturn { contextFiles: context.contextFiles, skills: [...skills.values()], diagnostics };\n\t}\n}\n\nexport async function loadResources(options: ResourceLoaderOptions): Promise<ResourceSnapshot> {\n\treturn new DefaultResourceLoader(options).load();\n}\n"]}
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../../src/core/resources/loader.ts"],"names":[],"mappings":"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","sourcesContent":["import { access, readFile, stat } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\nimport { createSkillCatalog, discoverSkills, loadSkill, type SkillLoadResult } from \"@di-code/skills\";\nimport type {\n\tContextFile,\n\tResourceDiagnostic,\n\tResourceLoader,\n\tResourceLoaderOptions,\n\tResourceScope,\n\tResourceSnapshot,\n\tSkillResource,\n} from \"./types.ts\";\n\nconst MAX_CONTEXT_FILE_BYTES = 256 * 1024;\n\nfunction isMissingPath(cause: unknown): boolean {\n\treturn cause instanceof Error && \"code\" in cause && cause.code === \"ENOENT\";\n}\n\nfunction contextDiagnostic(path: string, stage: ResourceDiagnostic[\"stage\"], message: string): ResourceDiagnostic {\n\treturn { path, kind: \"agents\", stage, severity: \"warning\", message };\n}\n\nasync function locateAgentsFile(directory: string): Promise<string | undefined> {\n\tfor (const name of [\"AGENTS.md\", \"AGENTS.MD\"]) {\n\t\tconst path = join(directory, name);\n\t\ttry {\n\t\t\tif ((await stat(path)).isFile()) return path;\n\t\t} catch (cause) {\n\t\t\tif (!isMissingPath(cause)) throw cause;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nasync function loadContextFile(\n\tpath: string,\n\tscope: ResourceScope,\n): Promise<{ file?: ContextFile; diagnostic?: ResourceDiagnostic }> {\n\ttry {\n\t\tconst metadata = await stat(path);\n\t\tif (metadata.size > MAX_CONTEXT_FILE_BYTES) {\n\t\t\treturn { diagnostic: contextDiagnostic(path, \"read\", `AGENTS.md exceeds ${MAX_CONTEXT_FILE_BYTES} byte limit`) };\n\t\t}\n\t\treturn { file: { kind: \"agents\", path: resolve(path), scope, content: await readFile(path, \"utf8\") } };\n\t} catch (cause) {\n\t\tconst message = cause instanceof Error ? cause.message : String(cause);\n\t\treturn { diagnostic: contextDiagnostic(path, \"read\", `Failed to read AGENTS.md: ${message}`) };\n\t}\n}\n\nasync function loadContextFiles(options: ResourceLoaderOptions): Promise<{\n\tcontextFiles: ContextFile[];\n\tdiagnostics: ResourceDiagnostic[];\n}> {\n\tif (options.noContextFiles) return { contextFiles: [], diagnostics: [] };\n\tconst contextFiles: ContextFile[] = [];\n\tconst diagnostics: ResourceDiagnostic[] = [];\n\tconst seen = new Set<string>();\n\tconst add = async (directory: string, scope: ResourceScope): Promise<void> => {\n\t\tlet path: string | undefined;\n\t\ttry {\n\t\t\tpath = await locateAgentsFile(directory);\n\t\t} catch (cause) {\n\t\t\tconst message = cause instanceof Error ? cause.message : String(cause);\n\t\t\tdiagnostics.push(contextDiagnostic(directory, \"discover\", `Failed to discover AGENTS.md: ${message}`));\n\t\t\treturn;\n\t\t}\n\t\tif (!path) return;\n\t\tconst absolutePath = resolve(path);\n\t\tif (seen.has(absolutePath)) return;\n\t\tseen.add(absolutePath);\n\t\tconst loaded = await loadContextFile(absolutePath, scope);\n\t\tif (loaded.file) contextFiles.push(loaded.file);\n\t\tif (loaded.diagnostic) diagnostics.push(loaded.diagnostic);\n\t};\n\n\tawait add(resolve(options.agentDir), \"global\");\n\tawait add(resolve(options.cwd), \"project\");\n\treturn { contextFiles, diagnostics };\n}\n\nasync function exists(directory: string): Promise<boolean> {\n\ttry {\n\t\tawait access(directory);\n\t\treturn true;\n\t} catch (cause) {\n\t\tif (isMissingPath(cause)) return false;\n\t\tthrow cause;\n\t}\n}\n\nexport class DefaultResourceLoader implements ResourceLoader {\n\tprivate readonly options: ResourceLoaderOptions;\n\n\tconstructor(options: ResourceLoaderOptions) {\n\t\tthis.options = options;\n\t}\n\n\tasync load(): Promise<ResourceSnapshot> {\n\t\tconst context = await loadContextFiles(this.options);\n\t\tconst diagnostics = [...context.diagnostics];\n\t\tconst skillResults: SkillLoadResult[] = [];\n\t\tconst addResults = (results: readonly SkillLoadResult[]): void => {\n\t\t\tskillResults.push(...results);\n\t\t};\n\n\t\tif (!this.options.noSkills) {\n\t\t\tfor (const rawPath of this.options.skillPaths ?? []) {\n\t\t\t\tconst path = resolve(this.options.cwd, rawPath);\n\t\t\t\ttry {\n\t\t\t\t\tconst metadata = await stat(path);\n\t\t\t\t\tif (metadata.isDirectory()) addResults(await discoverSkills(path, \"explicit\"));\n\t\t\t\t\telse {\n\t\t\t\t\t\tconst result = await loadSkill(path, \"explicit\");\n\t\t\t\t\t\tskillResults.push(result);\n\t\t\t\t\t}\n\t\t\t\t} catch (cause) {\n\t\t\t\t\tconst message = isMissingPath(cause)\n\t\t\t\t\t\t? \"Skill path does not exist\"\n\t\t\t\t\t\t: `Failed to discover skill path: ${String(cause)}`;\n\t\t\t\t\tdiagnostics.push({ path, kind: \"skill\", stage: \"discover\", severity: \"warning\", message });\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst projectDirectories = [\n\t\t\t\tjoin(this.options.cwd, \".di-code\", \"skills\"),\n\t\t\t\tjoin(this.options.cwd, \".agents\", \"skills\"),\n\t\t\t];\n\t\t\tif (this.options.projectTrusted === true) {\n\t\t\t\tfor (const directory of projectDirectories) addResults(await discoverSkills(directory, \"project\"));\n\t\t\t} else {\n\t\t\t\tfor (const directory of projectDirectories) {\n\t\t\t\t\tif (await exists(directory)) {\n\t\t\t\t\t\tdiagnostics.push({\n\t\t\t\t\t\t\tpath: resolve(directory),\n\t\t\t\t\t\t\tkind: \"skill\",\n\t\t\t\t\t\t\tstage: \"trust\",\n\t\t\t\t\t\t\tseverity: \"warning\",\n\t\t\t\t\t\t\tmessage: \"Project skill skipped because project trust is not granted\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\taddResults(await discoverSkills(join(this.options.agentDir, \"skills\"), \"user\"));\n\t\t}\n\t\tconst catalog = createSkillCatalog(skillResults);\n\t\tconst skillDiagnostics = catalog.diagnostics.map((diagnostic): ResourceDiagnostic => diagnostic);\n\t\tconst skills: SkillResource[] = catalog.skills.map((skill) => ({\n\t\t\t...skill,\n\t\t\tscope: skill.source === \"explicit\" ? \"explicit\" : skill.source === \"project\" ? \"project\" : \"global\",\n\t\t}));\n\t\treturn { contextFiles: context.contextFiles, skills, diagnostics: [...diagnostics, ...skillDiagnostics] };\n\t}\n}\n\nexport async function loadResources(options: ResourceLoaderOptions): Promise<ResourceSnapshot> {\n\treturn new DefaultResourceLoader(options).load();\n}\n"]}
@@ -1,4 +1,5 @@
1
- export type ResourceScope = "global" | "ancestor" | "project" | "explicit";
1
+ import type { SkillDescriptor, SkillSource } from "@di-code/skills";
2
+ export type ResourceScope = "global" | "project" | "explicit";
2
3
  export type ResourceDiagnosticStage = "discover" | "read" | "parse" | "collision" | "trust";
3
4
  export interface ResourceDiagnostic {
4
5
  readonly path: string;
@@ -13,15 +14,11 @@ export interface ContextFile {
13
14
  readonly scope: ResourceScope;
14
15
  readonly content: string;
15
16
  }
16
- export interface SkillResource {
17
- readonly kind: "skill";
18
- readonly name: string;
19
- readonly description: string;
20
- readonly filePath: string;
21
- readonly baseDir: string;
17
+ export type SkillResource = Omit<SkillDescriptor, "source" | "userInvocable"> & {
22
18
  readonly scope: ResourceScope;
23
- readonly disableModelInvocation: boolean;
24
- }
19
+ readonly source?: SkillSource;
20
+ readonly userInvocable?: boolean;
21
+ };
25
22
  export interface ResourceSnapshot {
26
23
  readonly contextFiles: readonly ContextFile[];
27
24
  readonly skills: readonly SkillResource[];
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/resources/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,CAAC;AAE3E,MAAM,MAAM,uBAAuB,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,WAAW,GAAG,OAAO,CAAC;AAE5F,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,uBAAuB,CAAC;IACxC,QAAQ,CAAC,QAAQ,EAAE,SAAS,GAAG,OAAO,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,sBAAsB,EAAE,OAAO,CAAC;CACzC;AAED,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,YAAY,EAAE,SAAS,WAAW,EAAE,CAAC;IAC9C,QAAQ,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,CAAC;IAC1C,QAAQ,CAAC,WAAW,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACpD;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED,MAAM,WAAW,cAAc;IAC9B,IAAI,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAC;CAClC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/resources/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEpE,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;AAE9D,MAAM,MAAM,uBAAuB,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,WAAW,GAAG,OAAO,CAAC;AAE5F,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,uBAAuB,CAAC;IACxC,QAAQ,CAAC,QAAQ,EAAE,SAAS,GAAG,OAAO,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,eAAe,EAAE,QAAQ,GAAG,eAAe,CAAC,GAAG;IAC/E,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CACjC,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,YAAY,EAAE,SAAS,WAAW,EAAE,CAAC;IAC9C,QAAQ,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,CAAC;IAC1C,QAAQ,CAAC,WAAW,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACpD;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED,MAAM,WAAW,cAAc;IAC9B,IAAI,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAC;CAClC"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/resources/types.ts"],"names":[],"mappings":"","sourcesContent":["export type ResourceScope = \"global\" | \"ancestor\" | \"project\" | \"explicit\";\n\nexport type ResourceDiagnosticStage = \"discover\" | \"read\" | \"parse\" | \"collision\" | \"trust\";\n\nexport interface ResourceDiagnostic {\n\treadonly path: string;\n\treadonly kind: \"agents\" | \"skill\";\n\treadonly stage: ResourceDiagnosticStage;\n\treadonly severity: \"warning\" | \"error\";\n\treadonly message: string;\n}\n\nexport interface ContextFile {\n\treadonly kind: \"agents\";\n\treadonly path: string;\n\treadonly scope: ResourceScope;\n\treadonly content: string;\n}\n\nexport interface SkillResource {\n\treadonly kind: \"skill\";\n\treadonly name: string;\n\treadonly description: string;\n\treadonly filePath: string;\n\treadonly baseDir: string;\n\treadonly scope: ResourceScope;\n\treadonly disableModelInvocation: boolean;\n}\n\nexport interface ResourceSnapshot {\n\treadonly contextFiles: readonly ContextFile[];\n\treadonly skills: readonly SkillResource[];\n\treadonly diagnostics: readonly ResourceDiagnostic[];\n}\n\nexport interface ResourceLoaderOptions {\n\treadonly cwd: string;\n\treadonly agentDir: string;\n\treadonly projectTrusted?: boolean;\n\treadonly noSkills?: boolean;\n\treadonly noContextFiles?: boolean;\n\treadonly skillPaths?: readonly string[];\n}\n\nexport interface ResourceLoader {\n\tload(): Promise<ResourceSnapshot>;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/resources/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { SkillDescriptor, SkillSource } from \"@di-code/skills\";\n\nexport type ResourceScope = \"global\" | \"project\" | \"explicit\";\n\nexport type ResourceDiagnosticStage = \"discover\" | \"read\" | \"parse\" | \"collision\" | \"trust\";\n\nexport interface ResourceDiagnostic {\n\treadonly path: string;\n\treadonly kind: \"agents\" | \"skill\";\n\treadonly stage: ResourceDiagnosticStage;\n\treadonly severity: \"warning\" | \"error\";\n\treadonly message: string;\n}\n\nexport interface ContextFile {\n\treadonly kind: \"agents\";\n\treadonly path: string;\n\treadonly scope: ResourceScope;\n\treadonly content: string;\n}\n\nexport type SkillResource = Omit<SkillDescriptor, \"source\" | \"userInvocable\"> & {\n\treadonly scope: ResourceScope;\n\treadonly source?: SkillSource;\n\treadonly userInvocable?: boolean;\n};\n\nexport interface ResourceSnapshot {\n\treadonly contextFiles: readonly ContextFile[];\n\treadonly skills: readonly SkillResource[];\n\treadonly diagnostics: readonly ResourceDiagnostic[];\n}\n\nexport interface ResourceLoaderOptions {\n\treadonly cwd: string;\n\treadonly agentDir: string;\n\treadonly projectTrusted?: boolean;\n\treadonly noSkills?: boolean;\n\treadonly noContextFiles?: boolean;\n\treadonly skillPaths?: readonly string[];\n}\n\nexport interface ResourceLoader {\n\tload(): Promise<ResourceSnapshot>;\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  import type { Message } from "@di-code/ai";
2
+ import { type BuiltSessionContext } from "../context-builder.ts";
2
3
  import { type SessionAppendOptions } from "./session-storage.ts";
3
- import { type SessionDiagnostic, type SessionEntry, type SessionHeader, type SessionMessageEntry, type SessionSummaryEntry } from "./types.ts";
4
+ import { type SessionDiagnostic, type SessionEntry, type SessionHeader, type SessionMessageEntry, type SessionSummaryEntry, type SessionTreeNode } from "./types.ts";
4
5
  export interface SessionManagerCreateOptions {
5
6
  readonly filePath: string;
6
7
  readonly cwd: string;
@@ -21,7 +22,10 @@ export declare class SessionManager {
21
22
  private readonly createId;
22
23
  private readonly appendOptions;
23
24
  private readonly sessionEntries;
25
+ private readonly entriesById;
26
+ private readonly childrenByParentId;
24
27
  private readonly sessionDiagnostics;
28
+ private activeLeafId;
25
29
  private fileCreated;
26
30
  private appendQueue;
27
31
  private constructor();
@@ -30,6 +34,12 @@ export declare class SessionManager {
30
34
  get entries(): readonly SessionEntry[];
31
35
  get header(): SessionHeader;
32
36
  get messages(): readonly Message[];
37
+ getEntry(id: string): SessionEntry | undefined;
38
+ getTree(): readonly SessionTreeNode[];
39
+ getBranch(leafId?: string): readonly SessionEntry[];
40
+ setLeaf(id: string): void;
41
+ resetLeaf(): void;
42
+ buildContext(leafId?: string): BuiltSessionContext;
33
43
  get latestSummary(): SessionSummaryEntry | undefined;
34
44
  get diagnostics(): readonly SessionDiagnostic[];
35
45
  get leafId(): string;
@@ -40,5 +50,6 @@ export declare class SessionManager {
40
50
  readonly firstKeptEntryId: string;
41
51
  readonly tokensBefore: number;
42
52
  }): Promise<SessionSummaryEntry>;
53
+ private addToIndexes;
43
54
  }
44
55
  //# sourceMappingURL=session-manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../../src/core/session/session-manager.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAIN,KAAK,oBAAoB,EACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAGN,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,2BAA2B;IAC3C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,MAAM,CAAC;IACjC,QAAQ,CAAC,aAAa,CAAC,EAAE,oBAAoB,CAAC;IAC9C,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,yBAAyB;IACzC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,MAAM,CAAC;IACjC,QAAQ,CAAC,aAAa,CAAC,EAAE,oBAAoB,CAAC;CAC9C;AAgBD,qBAAa,cAAc;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAuB;IACrD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAsB;IACzD,OAAO,CAAC,WAAW,CAAU;IAC7B,OAAO,CAAC,WAAW,CAAoC;IAEvD,OAAO;WAWM,MAAM,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,cAAc,CAAC;WAwBrE,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,yBAA8B,GAAG,OAAO,CAAC,cAAc,CAAC;IAKrG,IAAI,OAAO,IAAI,SAAS,YAAY,EAAE,CAErC;IAED,IAAI,MAAM,IAAI,aAAa,CAE1B;IAED,IAAI,QAAQ,IAAI,SAAS,OAAO,EAAE,CAMjC;IAED,IAAI,aAAa,IAAI,mBAAmB,GAAG,SAAS,CAMnD;IAED,IAAI,WAAW,IAAI,SAAS,iBAAiB,EAAE,CAE9C;IAED,IAAI,MAAM,IAAI,MAAM,CAEnB;YAEa,iBAAiB;IAM/B,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA2B7D,aAAa,CAAC,KAAK,EAAE;QACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;QAClC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;KAC9B,GAAG,OAAO,CAAC,mBAAmB,CAAC;CA4BhC"}
1
+ {"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../../src/core/session/session-manager.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { resolve } from "node:path";
3
+ import { buildSessionContext } from "../context-builder.js";
3
4
  import { appendSessionEntry, createSessionFile, loadSessionFile, } from "./session-storage.js";
4
5
  import { SESSION_FORMAT_VERSION, } from "./types.js";
5
6
  function assertRecordId(id) {
@@ -21,13 +22,19 @@ export class SessionManager {
21
22
  createId;
22
23
  appendOptions;
23
24
  sessionEntries;
25
+ entriesById = new Map();
26
+ childrenByParentId = new Map();
24
27
  sessionDiagnostics;
28
+ activeLeafId;
25
29
  fileCreated;
26
30
  appendQueue = Promise.resolve();
27
31
  constructor(filePath, loaded, options, fileCreated = true) {
28
32
  this.filePath = filePath;
29
33
  this.sessionHeader = structuredClone(loaded.header);
30
34
  this.sessionEntries = structuredClone([...loaded.entries]);
35
+ for (const entry of this.sessionEntries)
36
+ this.addToIndexes(entry);
37
+ this.activeLeafId = this.sessionEntries.at(-1)?.id ?? this.sessionHeader.id;
31
38
  this.sessionDiagnostics = structuredClone([...loaded.diagnostics]);
32
39
  this.now = options.now ?? Date.now;
33
40
  this.createId = options.createId ?? randomUUID;
@@ -68,9 +75,55 @@ export class SessionManager {
68
75
  .filter((entry) => entry.type === "message")
69
76
  .map((entry) => entry.message));
70
77
  }
78
+ getEntry(id) {
79
+ const entry = this.entriesById.get(id);
80
+ return entry ? structuredClone(entry) : undefined;
81
+ }
82
+ getTree() {
83
+ const nodes = new Map();
84
+ const roots = [];
85
+ for (const entry of this.sessionEntries) {
86
+ const node = { entry: structuredClone(entry), children: [] };
87
+ nodes.set(entry.id, node);
88
+ if (entry.parentId === this.sessionHeader.id) {
89
+ roots.push(node);
90
+ continue;
91
+ }
92
+ const parent = nodes.get(entry.parentId);
93
+ if (!parent)
94
+ throw new Error(`Session tree parent "${entry.parentId}" is missing.`);
95
+ parent.children.push(node);
96
+ }
97
+ return roots;
98
+ }
99
+ getBranch(leafId = this.activeLeafId) {
100
+ const branch = [];
101
+ let currentId = leafId;
102
+ while (currentId !== this.sessionHeader.id) {
103
+ const entry = this.entriesById.get(currentId);
104
+ if (!entry)
105
+ throw new Error(`Unknown session leaf "${currentId}".`);
106
+ branch.push(structuredClone(entry));
107
+ currentId = entry.parentId;
108
+ }
109
+ branch.reverse();
110
+ return branch;
111
+ }
112
+ setLeaf(id) {
113
+ if (id !== this.sessionHeader.id && !this.entriesById.has(id))
114
+ throw new Error(`Unknown session leaf "${id}".`);
115
+ this.activeLeafId = id;
116
+ }
117
+ resetLeaf() {
118
+ this.activeLeafId = this.sessionEntries.at(-1)?.id ?? this.sessionHeader.id;
119
+ }
120
+ buildContext(leafId = this.activeLeafId) {
121
+ return buildSessionContext(this.getBranch(leafId));
122
+ }
71
123
  get latestSummary() {
72
- for (let index = this.sessionEntries.length - 1; index >= 0; index--) {
73
- const entry = this.sessionEntries[index];
124
+ const branch = this.getBranch();
125
+ for (let index = branch.length - 1; index >= 0; index--) {
126
+ const entry = branch[index];
74
127
  if (entry?.type === "summary")
75
128
  return structuredClone(entry);
76
129
  }
@@ -80,7 +133,7 @@ export class SessionManager {
80
133
  return structuredClone(this.sessionDiagnostics);
81
134
  }
82
135
  get leafId() {
83
- return this.sessionEntries.at(-1)?.id ?? this.sessionHeader.id;
136
+ return this.activeLeafId;
84
137
  }
85
138
  async ensureFileCreated() {
86
139
  if (this.fileCreated)
@@ -105,6 +158,8 @@ export class SessionManager {
105
158
  };
106
159
  await appendSessionEntry(this.filePath, entry, expectedParentId, this.appendOptions);
107
160
  this.sessionEntries.push(structuredClone(entry));
161
+ this.addToIndexes(entry);
162
+ this.activeLeafId = entry.id;
108
163
  return structuredClone(entry);
109
164
  });
110
165
  this.appendQueue = operation.then(() => undefined, () => undefined);
@@ -129,10 +184,18 @@ export class SessionManager {
129
184
  };
130
185
  await appendSessionEntry(this.filePath, entry, expectedParentId, this.appendOptions);
131
186
  this.sessionEntries.push(structuredClone(entry));
187
+ this.addToIndexes(entry);
188
+ this.activeLeafId = entry.id;
132
189
  return structuredClone(entry);
133
190
  });
134
191
  this.appendQueue = operation.then(() => undefined, () => undefined);
135
192
  return operation;
136
193
  }
194
+ addToIndexes(entry) {
195
+ this.entriesById.set(entry.id, structuredClone(entry));
196
+ const children = this.childrenByParentId.get(entry.parentId) ?? [];
197
+ children.push(entry.id);
198
+ this.childrenByParentId.set(entry.parentId, children);
199
+ }
137
200
  }
138
201
  //# sourceMappingURL=session-manager.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-manager.js","sourceRoot":"","sources":["../../../src/core/session/session-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { resolve } from \"node:path\";\nimport type { Message } from \"@di-code/ai\";\nimport {\n\tappendSessionEntry,\n\tcreateSessionFile,\n\tloadSessionFile,\n\ttype SessionAppendOptions,\n} from \"./session-storage.ts\";\nimport {\n\ttype LoadedSession,\n\tSESSION_FORMAT_VERSION,\n\ttype SessionDiagnostic,\n\ttype SessionEntry,\n\ttype SessionHeader,\n\ttype SessionMessageEntry,\n\ttype SessionSummaryEntry,\n} from \"./types.ts\";\n\nexport interface SessionManagerCreateOptions {\n\treadonly filePath: string;\n\treadonly cwd: string;\n\treadonly now?: () => number;\n\treadonly createId?: () => string;\n\treadonly appendOptions?: SessionAppendOptions;\n\treadonly deferCreate?: boolean;\n}\n\nexport interface SessionManagerOpenOptions {\n\treadonly now?: () => number;\n\treadonly createId?: () => string;\n\treadonly appendOptions?: SessionAppendOptions;\n}\n\nfunction assertRecordId(id: string): void {\n\tif (!/^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/.test(id)) {\n\t\tthrow new Error(`Generated session record id is invalid: ${JSON.stringify(id)}.`);\n\t}\n}\n\nfunction createIsoTimestamp(now: () => number): string {\n\tconst milliseconds = now();\n\tif (!Number.isFinite(milliseconds)) {\n\t\tthrow new Error(\"Session clock must return a finite Unix millisecond timestamp.\");\n\t}\n\treturn new Date(milliseconds).toISOString();\n}\n\nexport class SessionManager {\n\treadonly filePath: string;\n\tprivate readonly sessionHeader: SessionHeader;\n\tprivate readonly now: () => number;\n\tprivate readonly createId: () => string;\n\tprivate readonly appendOptions: SessionAppendOptions;\n\tprivate readonly sessionEntries: SessionEntry[];\n\tprivate readonly sessionDiagnostics: SessionDiagnostic[];\n\tprivate fileCreated: boolean;\n\tprivate appendQueue: Promise<void> = Promise.resolve();\n\n\tprivate constructor(filePath: string, loaded: LoadedSession, options: SessionManagerOpenOptions, fileCreated = true) {\n\t\tthis.filePath = filePath;\n\t\tthis.sessionHeader = structuredClone(loaded.header);\n\t\tthis.sessionEntries = structuredClone([...loaded.entries]);\n\t\tthis.sessionDiagnostics = structuredClone([...loaded.diagnostics]);\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.createId = options.createId ?? randomUUID;\n\t\tthis.appendOptions = options.appendOptions ?? {};\n\t\tthis.fileCreated = fileCreated;\n\t}\n\n\tstatic async create(options: SessionManagerCreateOptions): Promise<SessionManager> {\n\t\tconst filePath = resolve(options.filePath);\n\t\tconst cwd = resolve(options.cwd);\n\t\tconst now = options.now ?? Date.now;\n\t\tconst createId = options.createId ?? randomUUID;\n\t\tconst id = createId();\n\t\tassertRecordId(id);\n\t\tconst header: SessionHeader = {\n\t\t\ttype: \"session\",\n\t\t\tversion: SESSION_FORMAT_VERSION,\n\t\t\tid,\n\t\t\tparentId: null,\n\t\t\ttimestamp: createIsoTimestamp(now),\n\t\t\tcwd,\n\t\t};\n\t\tif (!options.deferCreate) await createSessionFile(filePath, header);\n\t\treturn new SessionManager(\n\t\t\tfilePath,\n\t\t\t{ header, entries: [], messages: [], diagnostics: [] },\n\t\t\t{ now, createId, appendOptions: options.appendOptions },\n\t\t\t!options.deferCreate,\n\t\t);\n\t}\n\n\tstatic async open(filePath: string, options: SessionManagerOpenOptions = {}): Promise<SessionManager> {\n\t\tconst resolvedPath = resolve(filePath);\n\t\treturn new SessionManager(resolvedPath, await loadSessionFile(resolvedPath), options);\n\t}\n\n\tget entries(): readonly SessionEntry[] {\n\t\treturn structuredClone(this.sessionEntries);\n\t}\n\n\tget header(): SessionHeader {\n\t\treturn structuredClone(this.sessionHeader);\n\t}\n\n\tget messages(): readonly Message[] {\n\t\treturn structuredClone(\n\t\t\tthis.sessionEntries\n\t\t\t\t.filter((entry): entry is SessionMessageEntry => entry.type === \"message\")\n\t\t\t\t.map((entry) => entry.message),\n\t\t);\n\t}\n\n\tget latestSummary(): SessionSummaryEntry | undefined {\n\t\tfor (let index = this.sessionEntries.length - 1; index >= 0; index--) {\n\t\t\tconst entry = this.sessionEntries[index];\n\t\t\tif (entry?.type === \"summary\") return structuredClone(entry);\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tget diagnostics(): readonly SessionDiagnostic[] {\n\t\treturn structuredClone(this.sessionDiagnostics);\n\t}\n\n\tget leafId(): string {\n\t\treturn this.sessionEntries.at(-1)?.id ?? this.sessionHeader.id;\n\t}\n\n\tprivate async ensureFileCreated(): Promise<void> {\n\t\tif (this.fileCreated) return;\n\t\tawait createSessionFile(this.filePath, this.sessionHeader);\n\t\tthis.fileCreated = true;\n\t}\n\n\tappendMessage(message: Message): Promise<SessionMessageEntry> {\n\t\tconst messageSnapshot = structuredClone(message);\n\t\tconst operation = this.appendQueue.then(async () => {\n\t\t\tawait this.ensureFileCreated();\n\t\t\tconst id = this.createId();\n\t\t\tassertRecordId(id);\n\t\t\tconst expectedParentId = this.leafId;\n\t\t\tconst entry: SessionMessageEntry = {\n\t\t\t\ttype: \"message\",\n\t\t\t\tversion: SESSION_FORMAT_VERSION,\n\t\t\t\tid,\n\t\t\t\tparentId: expectedParentId,\n\t\t\t\ttimestamp: createIsoTimestamp(this.now),\n\t\t\t\tmessage: messageSnapshot,\n\t\t\t};\n\t\t\tawait appendSessionEntry(this.filePath, entry, expectedParentId, this.appendOptions);\n\t\t\tthis.sessionEntries.push(structuredClone(entry));\n\t\t\treturn structuredClone(entry);\n\t\t});\n\n\t\tthis.appendQueue = operation.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn operation;\n\t}\n\n\tappendSummary(input: {\n\t\treadonly summary: string;\n\t\treadonly firstKeptEntryId: string;\n\t\treadonly tokensBefore: number;\n\t}): Promise<SessionSummaryEntry> {\n\t\tconst inputSnapshot = structuredClone(input);\n\t\tconst operation = this.appendQueue.then(async () => {\n\t\t\tawait this.ensureFileCreated();\n\t\t\tconst id = this.createId();\n\t\t\tassertRecordId(id);\n\t\t\tconst expectedParentId = this.leafId;\n\t\t\tconst entry: SessionSummaryEntry = {\n\t\t\t\ttype: \"summary\",\n\t\t\t\tversion: SESSION_FORMAT_VERSION,\n\t\t\t\tid,\n\t\t\t\tparentId: expectedParentId,\n\t\t\t\ttimestamp: createIsoTimestamp(this.now),\n\t\t\t\tsummary: inputSnapshot.summary,\n\t\t\t\tfirstKeptEntryId: inputSnapshot.firstKeptEntryId,\n\t\t\t\ttokensBefore: inputSnapshot.tokensBefore,\n\t\t\t};\n\t\t\tawait appendSessionEntry(this.filePath, entry, expectedParentId, this.appendOptions);\n\t\t\tthis.sessionEntries.push(structuredClone(entry));\n\t\t\treturn structuredClone(entry);\n\t\t});\n\n\t\tthis.appendQueue = operation.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn operation;\n\t}\n}\n"]}
1
+ {"version":3,"file":"session-manager.js","sourceRoot":"","sources":["../../../src/core/session/session-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { resolve } from \"node:path\";\nimport type { Message } from \"@di-code/ai\";\nimport { type BuiltSessionContext, buildSessionContext } from \"../context-builder.ts\";\nimport {\n\tappendSessionEntry,\n\tcreateSessionFile,\n\tloadSessionFile,\n\ttype SessionAppendOptions,\n} from \"./session-storage.ts\";\nimport {\n\ttype LoadedSession,\n\tSESSION_FORMAT_VERSION,\n\ttype SessionDiagnostic,\n\ttype SessionEntry,\n\ttype SessionHeader,\n\ttype SessionMessageEntry,\n\ttype SessionSummaryEntry,\n\ttype SessionTreeNode,\n} from \"./types.ts\";\n\nexport interface SessionManagerCreateOptions {\n\treadonly filePath: string;\n\treadonly cwd: string;\n\treadonly now?: () => number;\n\treadonly createId?: () => string;\n\treadonly appendOptions?: SessionAppendOptions;\n\treadonly deferCreate?: boolean;\n}\n\nexport interface SessionManagerOpenOptions {\n\treadonly now?: () => number;\n\treadonly createId?: () => string;\n\treadonly appendOptions?: SessionAppendOptions;\n}\n\nfunction assertRecordId(id: string): void {\n\tif (!/^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/.test(id)) {\n\t\tthrow new Error(`Generated session record id is invalid: ${JSON.stringify(id)}.`);\n\t}\n}\n\nfunction createIsoTimestamp(now: () => number): string {\n\tconst milliseconds = now();\n\tif (!Number.isFinite(milliseconds)) {\n\t\tthrow new Error(\"Session clock must return a finite Unix millisecond timestamp.\");\n\t}\n\treturn new Date(milliseconds).toISOString();\n}\n\nexport class SessionManager {\n\treadonly filePath: string;\n\tprivate readonly sessionHeader: SessionHeader;\n\tprivate readonly now: () => number;\n\tprivate readonly createId: () => string;\n\tprivate readonly appendOptions: SessionAppendOptions;\n\tprivate readonly sessionEntries: SessionEntry[];\n\tprivate readonly entriesById = new Map<string, SessionEntry>();\n\tprivate readonly childrenByParentId = new Map<string, string[]>();\n\tprivate readonly sessionDiagnostics: SessionDiagnostic[];\n\tprivate activeLeafId: string;\n\tprivate fileCreated: boolean;\n\tprivate appendQueue: Promise<void> = Promise.resolve();\n\n\tprivate constructor(filePath: string, loaded: LoadedSession, options: SessionManagerOpenOptions, fileCreated = true) {\n\t\tthis.filePath = filePath;\n\t\tthis.sessionHeader = structuredClone(loaded.header);\n\t\tthis.sessionEntries = structuredClone([...loaded.entries]);\n\t\tfor (const entry of this.sessionEntries) this.addToIndexes(entry);\n\t\tthis.activeLeafId = this.sessionEntries.at(-1)?.id ?? this.sessionHeader.id;\n\t\tthis.sessionDiagnostics = structuredClone([...loaded.diagnostics]);\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.createId = options.createId ?? randomUUID;\n\t\tthis.appendOptions = options.appendOptions ?? {};\n\t\tthis.fileCreated = fileCreated;\n\t}\n\n\tstatic async create(options: SessionManagerCreateOptions): Promise<SessionManager> {\n\t\tconst filePath = resolve(options.filePath);\n\t\tconst cwd = resolve(options.cwd);\n\t\tconst now = options.now ?? Date.now;\n\t\tconst createId = options.createId ?? randomUUID;\n\t\tconst id = createId();\n\t\tassertRecordId(id);\n\t\tconst header: SessionHeader = {\n\t\t\ttype: \"session\",\n\t\t\tversion: SESSION_FORMAT_VERSION,\n\t\t\tid,\n\t\t\tparentId: null,\n\t\t\ttimestamp: createIsoTimestamp(now),\n\t\t\tcwd,\n\t\t};\n\t\tif (!options.deferCreate) await createSessionFile(filePath, header);\n\t\treturn new SessionManager(\n\t\t\tfilePath,\n\t\t\t{ header, entries: [], messages: [], diagnostics: [] },\n\t\t\t{ now, createId, appendOptions: options.appendOptions },\n\t\t\t!options.deferCreate,\n\t\t);\n\t}\n\n\tstatic async open(filePath: string, options: SessionManagerOpenOptions = {}): Promise<SessionManager> {\n\t\tconst resolvedPath = resolve(filePath);\n\t\treturn new SessionManager(resolvedPath, await loadSessionFile(resolvedPath), options);\n\t}\n\n\tget entries(): readonly SessionEntry[] {\n\t\treturn structuredClone(this.sessionEntries);\n\t}\n\n\tget header(): SessionHeader {\n\t\treturn structuredClone(this.sessionHeader);\n\t}\n\n\tget messages(): readonly Message[] {\n\t\treturn structuredClone(\n\t\t\tthis.sessionEntries\n\t\t\t\t.filter((entry): entry is SessionMessageEntry => entry.type === \"message\")\n\t\t\t\t.map((entry) => entry.message),\n\t\t);\n\t}\n\n\tgetEntry(id: string): SessionEntry | undefined {\n\t\tconst entry = this.entriesById.get(id);\n\t\treturn entry ? structuredClone(entry) : undefined;\n\t}\n\n\tgetTree(): readonly SessionTreeNode[] {\n\t\tconst nodes = new Map<string, { entry: SessionEntry; children: SessionTreeNode[] }>();\n\t\tconst roots: SessionTreeNode[] = [];\n\t\tfor (const entry of this.sessionEntries) {\n\t\t\tconst node = { entry: structuredClone(entry), children: [] as SessionTreeNode[] };\n\t\t\tnodes.set(entry.id, node);\n\t\t\tif (entry.parentId === this.sessionHeader.id) {\n\t\t\t\troots.push(node);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst parent = nodes.get(entry.parentId);\n\t\t\tif (!parent) throw new Error(`Session tree parent \"${entry.parentId}\" is missing.`);\n\t\t\tparent.children.push(node);\n\t\t}\n\t\treturn roots;\n\t}\n\n\tgetBranch(leafId = this.activeLeafId): readonly SessionEntry[] {\n\t\tconst branch: SessionEntry[] = [];\n\t\tlet currentId = leafId;\n\t\twhile (currentId !== this.sessionHeader.id) {\n\t\t\tconst entry = this.entriesById.get(currentId);\n\t\t\tif (!entry) throw new Error(`Unknown session leaf \"${currentId}\".`);\n\t\t\tbranch.push(structuredClone(entry));\n\t\t\tcurrentId = entry.parentId;\n\t\t}\n\t\tbranch.reverse();\n\t\treturn branch;\n\t}\n\n\tsetLeaf(id: string): void {\n\t\tif (id !== this.sessionHeader.id && !this.entriesById.has(id)) throw new Error(`Unknown session leaf \"${id}\".`);\n\t\tthis.activeLeafId = id;\n\t}\n\n\tresetLeaf(): void {\n\t\tthis.activeLeafId = this.sessionEntries.at(-1)?.id ?? this.sessionHeader.id;\n\t}\n\n\tbuildContext(leafId = this.activeLeafId): BuiltSessionContext {\n\t\treturn buildSessionContext(this.getBranch(leafId));\n\t}\n\n\tget latestSummary(): SessionSummaryEntry | undefined {\n\t\tconst branch = this.getBranch();\n\t\tfor (let index = branch.length - 1; index >= 0; index--) {\n\t\t\tconst entry = branch[index];\n\t\t\tif (entry?.type === \"summary\") return structuredClone(entry);\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tget diagnostics(): readonly SessionDiagnostic[] {\n\t\treturn structuredClone(this.sessionDiagnostics);\n\t}\n\n\tget leafId(): string {\n\t\treturn this.activeLeafId;\n\t}\n\n\tprivate async ensureFileCreated(): Promise<void> {\n\t\tif (this.fileCreated) return;\n\t\tawait createSessionFile(this.filePath, this.sessionHeader);\n\t\tthis.fileCreated = true;\n\t}\n\n\tappendMessage(message: Message): Promise<SessionMessageEntry> {\n\t\tconst messageSnapshot = structuredClone(message);\n\t\tconst operation = this.appendQueue.then(async () => {\n\t\t\tawait this.ensureFileCreated();\n\t\t\tconst id = this.createId();\n\t\t\tassertRecordId(id);\n\t\t\tconst expectedParentId = this.leafId;\n\t\t\tconst entry: SessionMessageEntry = {\n\t\t\t\ttype: \"message\",\n\t\t\t\tversion: SESSION_FORMAT_VERSION,\n\t\t\t\tid,\n\t\t\t\tparentId: expectedParentId,\n\t\t\t\ttimestamp: createIsoTimestamp(this.now),\n\t\t\t\tmessage: messageSnapshot,\n\t\t\t};\n\t\t\tawait appendSessionEntry(this.filePath, entry, expectedParentId, this.appendOptions);\n\t\t\tthis.sessionEntries.push(structuredClone(entry));\n\t\t\tthis.addToIndexes(entry);\n\t\t\tthis.activeLeafId = entry.id;\n\t\t\treturn structuredClone(entry);\n\t\t});\n\n\t\tthis.appendQueue = operation.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn operation;\n\t}\n\n\tappendSummary(input: {\n\t\treadonly summary: string;\n\t\treadonly firstKeptEntryId: string;\n\t\treadonly tokensBefore: number;\n\t}): Promise<SessionSummaryEntry> {\n\t\tconst inputSnapshot = structuredClone(input);\n\t\tconst operation = this.appendQueue.then(async () => {\n\t\t\tawait this.ensureFileCreated();\n\t\t\tconst id = this.createId();\n\t\t\tassertRecordId(id);\n\t\t\tconst expectedParentId = this.leafId;\n\t\t\tconst entry: SessionSummaryEntry = {\n\t\t\t\ttype: \"summary\",\n\t\t\t\tversion: SESSION_FORMAT_VERSION,\n\t\t\t\tid,\n\t\t\t\tparentId: expectedParentId,\n\t\t\t\ttimestamp: createIsoTimestamp(this.now),\n\t\t\t\tsummary: inputSnapshot.summary,\n\t\t\t\tfirstKeptEntryId: inputSnapshot.firstKeptEntryId,\n\t\t\t\ttokensBefore: inputSnapshot.tokensBefore,\n\t\t\t};\n\t\t\tawait appendSessionEntry(this.filePath, entry, expectedParentId, this.appendOptions);\n\t\t\tthis.sessionEntries.push(structuredClone(entry));\n\t\t\tthis.addToIndexes(entry);\n\t\t\tthis.activeLeafId = entry.id;\n\t\t\treturn structuredClone(entry);\n\t\t});\n\n\t\tthis.appendQueue = operation.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn operation;\n\t}\n\n\tprivate addToIndexes(entry: SessionEntry): void {\n\t\tthis.entriesById.set(entry.id, structuredClone(entry));\n\t\tconst children = this.childrenByParentId.get(entry.parentId) ?? [];\n\t\tchildren.push(entry.id);\n\t\tthis.childrenByParentId.set(entry.parentId, children);\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"session-storage.d.ts","sourceRoot":"","sources":["../../../src/core/session/session-storage.ts"],"names":[],"mappings":"AAGA,OAAO,EACN,KAAK,aAAa,EAElB,KAAK,YAAY,EACjB,KAAK,aAAa,EAKlB,MAAM,YAAY,CAAC;AA6MpB,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA+E9E;AAED,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACzD;AAaD,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB9F;AA8CD,wBAAsB,kBAAkB,CACvC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,YAAY,EACnB,gBAAgB,EAAE,MAAM,EACxB,OAAO,GAAE,oBAAyB,GAChC,OAAO,CAAC,IAAI,CAAC,CAkFf"}
1
+ {"version":3,"file":"session-storage.d.ts","sourceRoot":"","sources":["../../../src/core/session/session-storage.ts"],"names":[],"mappings":"AAGA,OAAO,EACN,KAAK,aAAa,EAElB,KAAK,YAAY,EACjB,KAAK,aAAa,EAKlB,MAAM,YAAY,CAAC;AA6MpB,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAiF9E;AAkBD,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACzD;AAaD,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB9F;AA8CD,wBAAsB,kBAAkB,CACvC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,YAAY,EACnB,gBAAgB,EAAE,MAAM,EACxB,OAAO,GAAE,oBAAyB,GAChC,OAAO,CAAC,IAAI,CAAC,CAmFf"}