@di-code/coding-agent 0.1.1 → 0.1.3

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 (100) hide show
  1. package/README.md +489 -143
  2. package/dist/cli.d.ts +12 -0
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +74 -0
  5. package/dist/cli.js.map +1 -1
  6. package/dist/core/clipboard-image.d.ts +13 -0
  7. package/dist/core/clipboard-image.d.ts.map +1 -0
  8. package/dist/core/clipboard-image.js +99 -0
  9. package/dist/core/clipboard-image.js.map +1 -0
  10. package/dist/core/image-input.d.ts +13 -0
  11. package/dist/core/image-input.d.ts.map +1 -0
  12. package/dist/core/image-input.js +102 -0
  13. package/dist/core/image-input.js.map +1 -0
  14. package/dist/core/resources/index.d.ts +3 -0
  15. package/dist/core/resources/index.d.ts.map +1 -0
  16. package/dist/core/resources/index.js +2 -0
  17. package/dist/core/resources/index.js.map +1 -0
  18. package/dist/core/resources/loader.d.ts +8 -0
  19. package/dist/core/resources/loader.d.ts.map +1 -0
  20. package/dist/core/resources/loader.js +174 -0
  21. package/dist/core/resources/loader.js.map +1 -0
  22. package/dist/core/resources/skills.d.ts +10 -0
  23. package/dist/core/resources/skills.d.ts.map +1 -0
  24. package/dist/core/resources/skills.js +167 -0
  25. package/dist/core/resources/skills.js.map +1 -0
  26. package/dist/core/resources/types.d.ts +41 -0
  27. package/dist/core/resources/types.d.ts.map +1 -0
  28. package/dist/core/resources/types.js +2 -0
  29. package/dist/core/resources/types.js.map +1 -0
  30. package/dist/core/session.d.ts +10 -1
  31. package/dist/core/session.d.ts.map +1 -1
  32. package/dist/core/session.js +33 -5
  33. package/dist/core/session.js.map +1 -1
  34. package/dist/core/skill-command.d.ts +3 -0
  35. package/dist/core/skill-command.d.ts.map +1 -0
  36. package/dist/core/skill-command.js +34 -0
  37. package/dist/core/skill-command.js.map +1 -0
  38. package/dist/core/system-prompt.d.ts +6 -0
  39. package/dist/core/system-prompt.d.ts.map +1 -0
  40. package/dist/core/system-prompt.js +57 -0
  41. package/dist/core/system-prompt.js.map +1 -0
  42. package/dist/extensions/runtime.d.ts +7 -4
  43. package/dist/extensions/runtime.d.ts.map +1 -1
  44. package/dist/extensions/runtime.js +45 -10
  45. package/dist/extensions/runtime.js.map +1 -1
  46. package/dist/index.d.ts +3 -0
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +3 -0
  49. package/dist/index.js.map +1 -1
  50. package/dist/main.d.ts +1 -0
  51. package/dist/main.d.ts.map +1 -1
  52. package/dist/main.js +91 -2
  53. package/dist/main.js.map +1 -1
  54. package/dist/modes/interactive-components.d.ts +1 -0
  55. package/dist/modes/interactive-components.d.ts.map +1 -1
  56. package/dist/modes/interactive-components.js +48 -20
  57. package/dist/modes/interactive-components.js.map +1 -1
  58. package/dist/modes/interactive-layout.d.ts +2 -1
  59. package/dist/modes/interactive-layout.d.ts.map +1 -1
  60. package/dist/modes/interactive-layout.js +17 -1
  61. package/dist/modes/interactive-layout.js.map +1 -1
  62. package/dist/modes/interactive-state.d.ts +12 -2
  63. package/dist/modes/interactive-state.d.ts.map +1 -1
  64. package/dist/modes/interactive-state.js +50 -3
  65. package/dist/modes/interactive-state.js.map +1 -1
  66. package/dist/modes/interactive.d.ts +12 -0
  67. package/dist/modes/interactive.d.ts.map +1 -1
  68. package/dist/modes/interactive.js +125 -9
  69. package/dist/modes/interactive.js.map +1 -1
  70. package/dist/plugins/index.d.ts +5 -0
  71. package/dist/plugins/index.d.ts.map +1 -0
  72. package/dist/plugins/index.js +5 -0
  73. package/dist/plugins/index.js.map +1 -0
  74. package/dist/plugins/loader.d.ts +18 -0
  75. package/dist/plugins/loader.d.ts.map +1 -0
  76. package/dist/plugins/loader.js +92 -0
  77. package/dist/plugins/loader.js.map +1 -0
  78. package/dist/plugins/manager.d.ts +23 -0
  79. package/dist/plugins/manager.d.ts.map +1 -0
  80. package/dist/plugins/manager.js +152 -0
  81. package/dist/plugins/manager.js.map +1 -0
  82. package/dist/plugins/manifest.d.ts +7 -0
  83. package/dist/plugins/manifest.d.ts.map +1 -0
  84. package/dist/plugins/manifest.js +116 -0
  85. package/dist/plugins/manifest.js.map +1 -0
  86. package/dist/plugins/types.d.ts +41 -0
  87. package/dist/plugins/types.d.ts.map +1 -0
  88. package/dist/plugins/types.js +2 -0
  89. package/dist/plugins/types.js.map +1 -0
  90. package/dist/provider-onboarding.d.ts.map +1 -1
  91. package/dist/provider-onboarding.js +20 -1
  92. package/dist/provider-onboarding.js.map +1 -1
  93. package/dist/startup.d.ts.map +1 -1
  94. package/dist/startup.js +65 -16
  95. package/dist/startup.js.map +1 -1
  96. package/dist/utils/syntax-highlight.d.ts +2 -0
  97. package/dist/utils/syntax-highlight.d.ts.map +1 -0
  98. package/dist/utils/syntax-highlight.js +55 -0
  99. package/dist/utils/syntax-highlight.js.map +1 -0
  100. package/package.json +19 -4
@@ -0,0 +1,174 @@
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";
4
+ const MAX_CONTEXT_FILE_BYTES = 256 * 1024;
5
+ function isMissingPath(cause) {
6
+ return cause instanceof Error && "code" in cause && cause.code === "ENOENT";
7
+ }
8
+ function contextDiagnostic(path, stage, message) {
9
+ return { path, kind: "agents", stage, severity: "warning", message };
10
+ }
11
+ async function locateAgentsFile(directory) {
12
+ for (const name of ["AGENTS.md", "AGENTS.MD"]) {
13
+ const path = join(directory, name);
14
+ try {
15
+ if ((await stat(path)).isFile())
16
+ return path;
17
+ }
18
+ catch (cause) {
19
+ if (!isMissingPath(cause))
20
+ throw cause;
21
+ }
22
+ }
23
+ return undefined;
24
+ }
25
+ async function loadContextFile(path, scope) {
26
+ try {
27
+ const metadata = await stat(path);
28
+ if (metadata.size > MAX_CONTEXT_FILE_BYTES) {
29
+ return { diagnostic: contextDiagnostic(path, "read", `AGENTS.md exceeds ${MAX_CONTEXT_FILE_BYTES} byte limit`) };
30
+ }
31
+ return { file: { kind: "agents", path: resolve(path), scope, content: await readFile(path, "utf8") } };
32
+ }
33
+ catch (cause) {
34
+ const message = cause instanceof Error ? cause.message : String(cause);
35
+ return { diagnostic: contextDiagnostic(path, "read", `Failed to read AGENTS.md: ${message}`) };
36
+ }
37
+ }
38
+ async function loadContextFiles(options) {
39
+ if (options.noContextFiles)
40
+ return { contextFiles: [], diagnostics: [] };
41
+ const contextFiles = [];
42
+ const diagnostics = [];
43
+ const seen = new Set();
44
+ const add = async (directory, scope) => {
45
+ let path;
46
+ try {
47
+ path = await locateAgentsFile(directory);
48
+ }
49
+ catch (cause) {
50
+ const message = cause instanceof Error ? cause.message : String(cause);
51
+ diagnostics.push(contextDiagnostic(directory, "discover", `Failed to discover AGENTS.md: ${message}`));
52
+ return;
53
+ }
54
+ if (!path)
55
+ return;
56
+ const absolutePath = resolve(path);
57
+ if (seen.has(absolutePath))
58
+ return;
59
+ seen.add(absolutePath);
60
+ const loaded = await loadContextFile(absolutePath, scope);
61
+ if (loaded.file)
62
+ contextFiles.push(loaded.file);
63
+ if (loaded.diagnostic)
64
+ diagnostics.push(loaded.diagnostic);
65
+ };
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");
78
+ return { contextFiles, diagnostics };
79
+ }
80
+ async function exists(directory) {
81
+ try {
82
+ await access(directory);
83
+ return true;
84
+ }
85
+ catch (cause) {
86
+ if (isMissingPath(cause))
87
+ return false;
88
+ throw cause;
89
+ }
90
+ }
91
+ export class DefaultResourceLoader {
92
+ options;
93
+ constructor(options) {
94
+ this.options = options;
95
+ }
96
+ async load() {
97
+ const context = await loadContextFiles(this.options);
98
+ 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
+ };
115
+ 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
+ }
122
+ };
123
+ if (!this.options.noSkills) {
124
+ for (const rawPath of this.options.skillPaths ?? []) {
125
+ const path = resolve(this.options.cwd, rawPath);
126
+ try {
127
+ const metadata = await stat(path);
128
+ if (metadata.isDirectory())
129
+ addResults(await discoverSkills(path, "explicit"));
130
+ else {
131
+ const result = await loadSkill(path, "explicit");
132
+ if (result.skill)
133
+ add(result.skill, result.diagnostics);
134
+ else
135
+ diagnostics.push(...result.diagnostics);
136
+ }
137
+ }
138
+ catch (cause) {
139
+ const message = isMissingPath(cause)
140
+ ? "Skill path does not exist"
141
+ : `Failed to discover skill path: ${String(cause)}`;
142
+ diagnostics.push({ path, kind: "skill", stage: "discover", severity: "warning", message });
143
+ }
144
+ }
145
+ const projectDirectories = [
146
+ join(this.options.cwd, ".di-code", "skills"),
147
+ join(this.options.cwd, ".pi", "skills"),
148
+ ];
149
+ if (this.options.projectTrusted === true) {
150
+ for (const directory of projectDirectories)
151
+ addResults(await discoverSkills(directory, "project"));
152
+ }
153
+ else {
154
+ for (const directory of projectDirectories) {
155
+ if (await exists(directory)) {
156
+ diagnostics.push({
157
+ path: resolve(directory),
158
+ kind: "skill",
159
+ stage: "trust",
160
+ severity: "warning",
161
+ message: "Project skill skipped because project trust is not granted",
162
+ });
163
+ }
164
+ }
165
+ }
166
+ addResults(await discoverSkills(join(this.options.agentDir, "skills"), "global"));
167
+ }
168
+ return { contextFiles: context.contextFiles, skills: [...skills.values()], diagnostics };
169
+ }
170
+ }
171
+ export async function loadResources(options) {
172
+ return new DefaultResourceLoader(options).load();
173
+ }
174
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +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"]}
@@ -0,0 +1,10 @@
1
+ import type { ResourceDiagnostic, ResourceScope, SkillResource } from "./types.ts";
2
+ interface ParsedSkill {
3
+ readonly skill?: SkillResource;
4
+ readonly diagnostics: readonly ResourceDiagnostic[];
5
+ }
6
+ export declare function loadSkill(path: string, scope: ResourceScope): Promise<ParsedSkill>;
7
+ export declare function readSkillContent(skill: SkillResource, signal?: AbortSignal): Promise<string>;
8
+ export declare function discoverSkills(directory: string, scope: ResourceScope): Promise<ParsedSkill[]>;
9
+ export {};
10
+ //# sourceMappingURL=skills.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../../src/core/resources/skills.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAMnF,UAAU,WAAW;IACpB,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACpD;AAyDD,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,WAAW,CAAC,CA6CxF;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAgBlG;AAED,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CA2BpG"}
@@ -0,0 +1,167 @@
1
+ import { readdir, readFile, stat } from "node:fs/promises";
2
+ import { dirname, join, resolve } from "node:path";
3
+ const MAX_SKILL_BYTES = 256 * 1024;
4
+ const MAX_NAME_LENGTH = 64;
5
+ const MAX_DESCRIPTION_LENGTH = 1024;
6
+ function diagnostic(path, stage, message, severity = "warning") {
7
+ return { path, kind: "skill", stage, severity, message };
8
+ }
9
+ function parseScalar(value) {
10
+ const trimmed = value.trim();
11
+ if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
12
+ return trimmed.slice(1, -1);
13
+ }
14
+ return trimmed;
15
+ }
16
+ function parseFrontmatter(source) {
17
+ const lines = source.replace(/^\uFEFF/, "").split(/\r?\n/);
18
+ if (lines[0] !== "---")
19
+ return undefined;
20
+ const end = lines.findIndex((line, index) => index > 0 && line === "---");
21
+ if (end < 0)
22
+ return undefined;
23
+ const entries = {};
24
+ for (const line of lines.slice(1, end)) {
25
+ if (!line.trim() || line.trimStart().startsWith("#"))
26
+ continue;
27
+ const separator = line.indexOf(":");
28
+ if (separator < 1)
29
+ continue;
30
+ entries[line.slice(0, separator).trim()] = parseScalar(line.slice(separator + 1));
31
+ }
32
+ return entries;
33
+ }
34
+ function extractSkillBody(source) {
35
+ const lines = source.replace(/^\uFEFF/, "").split(/\r?\n/);
36
+ if (lines[0] !== "---")
37
+ return undefined;
38
+ const end = lines.findIndex((line, index) => index > 0 && line === "---");
39
+ if (end < 0)
40
+ return undefined;
41
+ return lines.slice(end + 1).join("\n");
42
+ }
43
+ function validateName(name) {
44
+ if (name.length === 0)
45
+ return "name is required";
46
+ if (name.length > MAX_NAME_LENGTH)
47
+ return `name exceeds ${MAX_NAME_LENGTH} characters`;
48
+ if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name)) {
49
+ return "name must contain only lowercase letters, numbers, and single hyphens";
50
+ }
51
+ return undefined;
52
+ }
53
+ function validateDescription(description) {
54
+ if (description.length === 0)
55
+ return "description is required";
56
+ if (description.length > MAX_DESCRIPTION_LENGTH)
57
+ return `description exceeds ${MAX_DESCRIPTION_LENGTH} characters`;
58
+ return undefined;
59
+ }
60
+ export async function loadSkill(path, scope) {
61
+ const filePath = resolve(path);
62
+ const diagnostics = [];
63
+ try {
64
+ const metadata = await stat(filePath);
65
+ if (!metadata.isFile())
66
+ return { diagnostics: [diagnostic(filePath, "discover", "Skill path is not a file")] };
67
+ if (metadata.size > MAX_SKILL_BYTES) {
68
+ return { diagnostics: [diagnostic(filePath, "read", `Skill exceeds ${MAX_SKILL_BYTES} byte limit`)] };
69
+ }
70
+ const source = await readFile(filePath, "utf8");
71
+ const frontmatter = parseFrontmatter(source);
72
+ if (!frontmatter)
73
+ return { diagnostics: [diagnostic(filePath, "parse", "Skill frontmatter is required")] };
74
+ const name = frontmatter.name ?? "";
75
+ const description = frontmatter.description ?? "";
76
+ const nameError = validateName(name);
77
+ const descriptionError = validateDescription(description);
78
+ if (nameError)
79
+ diagnostics.push(diagnostic(filePath, "parse", nameError));
80
+ if (descriptionError)
81
+ diagnostics.push(diagnostic(filePath, "parse", descriptionError));
82
+ if (nameError || descriptionError)
83
+ return { diagnostics };
84
+ const disableModelInvocationValue = frontmatter["disable-model-invocation"];
85
+ if (disableModelInvocationValue !== undefined &&
86
+ disableModelInvocationValue !== "true" &&
87
+ disableModelInvocationValue !== "false") {
88
+ return {
89
+ diagnostics: [diagnostic(filePath, "parse", "disable-model-invocation must be true or false")],
90
+ };
91
+ }
92
+ return {
93
+ skill: {
94
+ kind: "skill",
95
+ name,
96
+ description,
97
+ filePath,
98
+ baseDir: dirname(filePath),
99
+ scope,
100
+ disableModelInvocation: disableModelInvocationValue === "true",
101
+ },
102
+ diagnostics,
103
+ };
104
+ }
105
+ catch (cause) {
106
+ const message = cause instanceof Error ? cause.message : String(cause);
107
+ return { diagnostics: [diagnostic(filePath, "read", `Failed to read skill: ${message}`, "error")] };
108
+ }
109
+ }
110
+ export async function readSkillContent(skill, signal) {
111
+ if (signal?.aborted)
112
+ throw new Error("Operation aborted");
113
+ const path = resolve(skill.filePath);
114
+ try {
115
+ const metadata = await stat(path);
116
+ if (!metadata.isFile())
117
+ throw new Error("Skill path is not a file");
118
+ if (metadata.size > MAX_SKILL_BYTES)
119
+ throw new Error(`Skill exceeds ${MAX_SKILL_BYTES} byte limit`);
120
+ const source = await readFile(path, { encoding: "utf8", signal });
121
+ const body = extractSkillBody(source);
122
+ if (body === undefined)
123
+ throw new Error("Skill frontmatter is required");
124
+ return body;
125
+ }
126
+ catch (cause) {
127
+ if (cause instanceof Error && cause.message === "Operation aborted")
128
+ throw cause;
129
+ const message = cause instanceof Error ? cause.message : String(cause);
130
+ throw new Error(`Failed to load skill "${skill.name}": ${message}`, { cause });
131
+ }
132
+ }
133
+ export async function discoverSkills(directory, scope) {
134
+ const root = resolve(directory);
135
+ const results = [];
136
+ async function visit(current) {
137
+ let entries;
138
+ try {
139
+ entries = await readdir(current, { withFileTypes: true });
140
+ }
141
+ catch (cause) {
142
+ if (isMissingPath(cause))
143
+ return;
144
+ const message = cause instanceof Error ? cause.message : String(cause);
145
+ results.push({
146
+ diagnostics: [diagnostic(current, "discover", `Failed to read skill directory: ${message}`, "error")],
147
+ });
148
+ return;
149
+ }
150
+ const skillEntry = entries.find((entry) => entry.name === "SKILL.md" && entry.isFile());
151
+ if (skillEntry) {
152
+ results.push(await loadSkill(join(current, skillEntry.name), scope));
153
+ return;
154
+ }
155
+ for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
156
+ if (entry.name.startsWith(".") || entry.name === "node_modules" || !entry.isDirectory())
157
+ continue;
158
+ await visit(join(current, entry.name));
159
+ }
160
+ }
161
+ await visit(root);
162
+ return results;
163
+ }
164
+ function isMissingPath(cause) {
165
+ return cause instanceof Error && "code" in cause && cause.code === "ENOENT";
166
+ }
167
+ //# sourceMappingURL=skills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.js","sourceRoot":"","sources":["../../../src/core/resources/skills.ts"],"names":[],"mappings":"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","sourcesContent":["import { readdir, readFile, stat } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport type { ResourceDiagnostic, ResourceScope, SkillResource } from \"./types.ts\";\n\nconst MAX_SKILL_BYTES = 256 * 1024;\nconst MAX_NAME_LENGTH = 64;\nconst MAX_DESCRIPTION_LENGTH = 1024;\n\ninterface ParsedSkill {\n\treadonly skill?: SkillResource;\n\treadonly diagnostics: readonly ResourceDiagnostic[];\n}\n\nfunction diagnostic(\n\tpath: string,\n\tstage: ResourceDiagnostic[\"stage\"],\n\tmessage: string,\n\tseverity: ResourceDiagnostic[\"severity\"] = \"warning\",\n): ResourceDiagnostic {\n\treturn { path, kind: \"skill\", stage, severity, message };\n}\n\nfunction parseScalar(value: string): string {\n\tconst trimmed = value.trim();\n\tif ((trimmed.startsWith('\"') && trimmed.endsWith('\"')) || (trimmed.startsWith(\"'\") && trimmed.endsWith(\"'\"))) {\n\t\treturn trimmed.slice(1, -1);\n\t}\n\treturn trimmed;\n}\n\nfunction parseFrontmatter(source: string): Record<string, string> | undefined {\n\tconst lines = source.replace(/^\\uFEFF/, \"\").split(/\\r?\\n/);\n\tif (lines[0] !== \"---\") return undefined;\n\tconst end = lines.findIndex((line, index) => index > 0 && line === \"---\");\n\tif (end < 0) return undefined;\n\tconst entries: Record<string, string> = {};\n\tfor (const line of lines.slice(1, end)) {\n\t\tif (!line.trim() || line.trimStart().startsWith(\"#\")) continue;\n\t\tconst separator = line.indexOf(\":\");\n\t\tif (separator < 1) continue;\n\t\tentries[line.slice(0, separator).trim()] = parseScalar(line.slice(separator + 1));\n\t}\n\treturn entries;\n}\n\nfunction extractSkillBody(source: string): string | undefined {\n\tconst lines = source.replace(/^\\uFEFF/, \"\").split(/\\r?\\n/);\n\tif (lines[0] !== \"---\") return undefined;\n\tconst end = lines.findIndex((line, index) => index > 0 && line === \"---\");\n\tif (end < 0) return undefined;\n\treturn lines.slice(end + 1).join(\"\\n\");\n}\n\nfunction validateName(name: string): string | undefined {\n\tif (name.length === 0) return \"name is required\";\n\tif (name.length > MAX_NAME_LENGTH) return `name exceeds ${MAX_NAME_LENGTH} characters`;\n\tif (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name)) {\n\t\treturn \"name must contain only lowercase letters, numbers, and single hyphens\";\n\t}\n\treturn undefined;\n}\n\nfunction validateDescription(description: string): string | undefined {\n\tif (description.length === 0) return \"description is required\";\n\tif (description.length > MAX_DESCRIPTION_LENGTH) return `description exceeds ${MAX_DESCRIPTION_LENGTH} characters`;\n\treturn undefined;\n}\n\nexport async function loadSkill(path: string, scope: ResourceScope): Promise<ParsedSkill> {\n\tconst filePath = resolve(path);\n\tconst diagnostics: ResourceDiagnostic[] = [];\n\ttry {\n\t\tconst metadata = await stat(filePath);\n\t\tif (!metadata.isFile()) return { diagnostics: [diagnostic(filePath, \"discover\", \"Skill path is not a file\")] };\n\t\tif (metadata.size > MAX_SKILL_BYTES) {\n\t\t\treturn { diagnostics: [diagnostic(filePath, \"read\", `Skill exceeds ${MAX_SKILL_BYTES} byte limit`)] };\n\t\t}\n\t\tconst source = await readFile(filePath, \"utf8\");\n\t\tconst frontmatter = parseFrontmatter(source);\n\t\tif (!frontmatter) return { diagnostics: [diagnostic(filePath, \"parse\", \"Skill frontmatter is required\")] };\n\t\tconst name = frontmatter.name ?? \"\";\n\t\tconst description = frontmatter.description ?? \"\";\n\t\tconst nameError = validateName(name);\n\t\tconst descriptionError = validateDescription(description);\n\t\tif (nameError) diagnostics.push(diagnostic(filePath, \"parse\", nameError));\n\t\tif (descriptionError) diagnostics.push(diagnostic(filePath, \"parse\", descriptionError));\n\t\tif (nameError || descriptionError) return { diagnostics };\n\t\tconst disableModelInvocationValue = frontmatter[\"disable-model-invocation\"];\n\t\tif (\n\t\t\tdisableModelInvocationValue !== undefined &&\n\t\t\tdisableModelInvocationValue !== \"true\" &&\n\t\t\tdisableModelInvocationValue !== \"false\"\n\t\t) {\n\t\t\treturn {\n\t\t\t\tdiagnostics: [diagnostic(filePath, \"parse\", \"disable-model-invocation must be true or false\")],\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\tskill: {\n\t\t\t\tkind: \"skill\",\n\t\t\t\tname,\n\t\t\t\tdescription,\n\t\t\t\tfilePath,\n\t\t\t\tbaseDir: dirname(filePath),\n\t\t\t\tscope,\n\t\t\t\tdisableModelInvocation: disableModelInvocationValue === \"true\",\n\t\t\t},\n\t\t\tdiagnostics,\n\t\t};\n\t} catch (cause) {\n\t\tconst message = cause instanceof Error ? cause.message : String(cause);\n\t\treturn { diagnostics: [diagnostic(filePath, \"read\", `Failed to read skill: ${message}`, \"error\")] };\n\t}\n}\n\nexport async function readSkillContent(skill: SkillResource, signal?: AbortSignal): Promise<string> {\n\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\tconst path = resolve(skill.filePath);\n\ttry {\n\t\tconst metadata = await stat(path);\n\t\tif (!metadata.isFile()) throw new Error(\"Skill path is not a file\");\n\t\tif (metadata.size > MAX_SKILL_BYTES) throw new Error(`Skill exceeds ${MAX_SKILL_BYTES} byte limit`);\n\t\tconst source = await readFile(path, { encoding: \"utf8\", signal });\n\t\tconst body = extractSkillBody(source);\n\t\tif (body === undefined) throw new Error(\"Skill frontmatter is required\");\n\t\treturn body;\n\t} catch (cause) {\n\t\tif (cause instanceof Error && cause.message === \"Operation aborted\") throw cause;\n\t\tconst message = cause instanceof Error ? cause.message : String(cause);\n\t\tthrow new Error(`Failed to load skill \"${skill.name}\": ${message}`, { cause });\n\t}\n}\n\nexport async function discoverSkills(directory: string, scope: ResourceScope): Promise<ParsedSkill[]> {\n\tconst root = resolve(directory);\n\tconst results: ParsedSkill[] = [];\n\tasync function visit(current: string): Promise<void> {\n\t\tlet entries: import(\"node:fs\").Dirent<string>[];\n\t\ttry {\n\t\t\tentries = await readdir(current, { withFileTypes: true });\n\t\t} catch (cause) {\n\t\t\tif (isMissingPath(cause)) return;\n\t\t\tconst message = cause instanceof Error ? cause.message : String(cause);\n\t\t\tresults.push({\n\t\t\t\tdiagnostics: [diagnostic(current, \"discover\", `Failed to read skill directory: ${message}`, \"error\")],\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tconst skillEntry = entries.find((entry) => entry.name === \"SKILL.md\" && entry.isFile());\n\t\tif (skillEntry) {\n\t\t\tresults.push(await loadSkill(join(current, skillEntry.name), scope));\n\t\t\treturn;\n\t\t}\n\t\tfor (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {\n\t\t\tif (entry.name.startsWith(\".\") || entry.name === \"node_modules\" || !entry.isDirectory()) continue;\n\t\t\tawait visit(join(current, entry.name));\n\t\t}\n\t}\n\tawait visit(root);\n\treturn results;\n}\n\nfunction isMissingPath(cause: unknown): boolean {\n\treturn cause instanceof Error && \"code\" in cause && cause.code === \"ENOENT\";\n}\n"]}
@@ -0,0 +1,41 @@
1
+ export type ResourceScope = "global" | "ancestor" | "project" | "explicit";
2
+ export type ResourceDiagnosticStage = "discover" | "read" | "parse" | "collision" | "trust";
3
+ export interface ResourceDiagnostic {
4
+ readonly path: string;
5
+ readonly kind: "agents" | "skill";
6
+ readonly stage: ResourceDiagnosticStage;
7
+ readonly severity: "warning" | "error";
8
+ readonly message: string;
9
+ }
10
+ export interface ContextFile {
11
+ readonly kind: "agents";
12
+ readonly path: string;
13
+ readonly scope: ResourceScope;
14
+ readonly content: string;
15
+ }
16
+ export interface SkillResource {
17
+ readonly kind: "skill";
18
+ readonly name: string;
19
+ readonly description: string;
20
+ readonly filePath: string;
21
+ readonly baseDir: string;
22
+ readonly scope: ResourceScope;
23
+ readonly disableModelInvocation: boolean;
24
+ }
25
+ export interface ResourceSnapshot {
26
+ readonly contextFiles: readonly ContextFile[];
27
+ readonly skills: readonly SkillResource[];
28
+ readonly diagnostics: readonly ResourceDiagnostic[];
29
+ }
30
+ export interface ResourceLoaderOptions {
31
+ readonly cwd: string;
32
+ readonly agentDir: string;
33
+ readonly projectTrusted?: boolean;
34
+ readonly noSkills?: boolean;
35
+ readonly noContextFiles?: boolean;
36
+ readonly skillPaths?: readonly string[];
37
+ }
38
+ export interface ResourceLoader {
39
+ load(): Promise<ResourceSnapshot>;
40
+ }
41
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +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,5 +1,7 @@
1
1
  import { type AgentListener } from "@di-code/agent";
2
- import type { AssistantMessage, Message, Model, Provider, Usage } from "@di-code/ai";
2
+ import type { AssistantMessage, ImageContent, Message, Model, Provider, Usage } from "@di-code/ai";
3
+ import type { ExtensionHost } from "../extensions/runtime.ts";
4
+ import type { SkillResource } from "./resources/types.ts";
3
5
  import type { SessionManager } from "./session/session-manager.ts";
4
6
  import type { SessionDiagnostic } from "./session/types.ts";
5
7
  export interface AgentSessionCompactionOptions {
@@ -23,9 +25,12 @@ export interface AgentSessionOptions {
23
25
  readonly allowedRoot: string;
24
26
  readonly provider: Provider;
25
27
  readonly model: Model;
28
+ readonly systemPrompt?: string;
29
+ readonly skills?: readonly SkillResource[];
26
30
  readonly now?: () => number;
27
31
  readonly sessionManager?: SessionManager;
28
32
  readonly compaction?: AgentSessionCompactionOptions;
33
+ readonly extensionHost?: ExtensionHost;
29
34
  }
30
35
  export type AgentSessionEvent = import("@di-code/agent").AgentEvent | {
31
36
  type: "compaction_start";
@@ -45,6 +50,8 @@ export declare class AgentSession {
45
50
  private readonly allowedRootValue;
46
51
  private readonly sessionManager?;
47
52
  private readonly provider;
53
+ private readonly skills;
54
+ private readonly extensionHost?;
48
55
  private model;
49
56
  private readonly sessionIdValue;
50
57
  private readonly now;
@@ -69,6 +76,8 @@ export declare class AgentSession {
69
76
  get sessionDiagnostics(): readonly SessionDiagnostic[];
70
77
  get usage(): SessionUsage;
71
78
  prompt(text: string, signal?: AbortSignal): Promise<AssistantMessage>;
79
+ /** Sends one text prompt with explicitly supplied image attachments. */
80
+ promptWithImages(text: string, images: readonly ImageContent[], signal?: AbortSignal): Promise<AssistantMessage>;
72
81
  compact(signal?: AbortSignal): Promise<void>;
73
82
  subscribe(listener: AgentListener): () => void;
74
83
  subscribeSession(listener: AgentSessionListener): () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/core/session.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/core/session.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { Agent, estimateContextTokens, generateCompactionSummary, prepareCompaction, resolveContextBudget, shouldCompact, } from "@di-code/agent";
3
3
  import { buildSessionContext } from "./context-builder.js";
4
+ import { resolveSkillCommand } from "./skill-command.js";
4
5
  import { createBashTool } from "./tools/bash.js";
5
6
  import { createEditTool } from "./tools/edit.js";
6
7
  import { createReadTool } from "./tools/read.js";
@@ -10,6 +11,8 @@ export class AgentSession {
10
11
  allowedRootValue;
11
12
  sessionManager;
12
13
  provider;
14
+ skills;
15
+ extensionHost;
13
16
  model;
14
17
  sessionIdValue;
15
18
  now;
@@ -24,6 +27,8 @@ export class AgentSession {
24
27
  this.allowedRootValue = options.allowedRoot;
25
28
  this.sessionManager = options.sessionManager;
26
29
  this.provider = options.provider;
30
+ this.skills = structuredClone([...(options.skills ?? [])]);
31
+ this.extensionHost = options.extensionHost;
27
32
  this.model = options.model;
28
33
  this.sessionIdValue = options.sessionManager?.header.id ?? randomUUID();
29
34
  this.now = options.now ?? Date.now;
@@ -54,12 +59,24 @@ export class AgentSession {
54
59
  this.agent = new Agent({
55
60
  provider: options.provider,
56
61
  model: options.model,
62
+ systemPrompt: options.systemPrompt,
57
63
  sessionId: this.sessionIdValue,
58
64
  tools: [
59
65
  createReadTool(options.allowedRoot),
60
66
  createWriteTool(options.allowedRoot),
61
67
  createEditTool(options.allowedRoot),
62
68
  createBashTool(options.allowedRoot),
69
+ ...(this.extensionHost?.listTools().map((tool) => ({
70
+ name: tool.name,
71
+ description: tool.description,
72
+ parameters: tool.parameters,
73
+ execute: async (toolCallId, parameters, signal) => {
74
+ const result = await this.extensionHost?.runTool(tool.name, toolCallId, parameters, signal);
75
+ if (result === undefined)
76
+ throw new Error(`Extension tool "${tool.name}" is unavailable.`);
77
+ return [...result];
78
+ },
79
+ })) ?? []),
63
80
  ],
64
81
  now: this.now,
65
82
  initialMessages: options.sessionManager?.messages,
@@ -83,7 +100,9 @@ export class AgentSession {
83
100
  this.addUsage(event.message.usage);
84
101
  await this.emitSession({ type: "usage_update", usage: this.usage });
85
102
  });
86
- this.agent.subscribe(async (event) => {
103
+ this.agent.subscribe(async (event, signal) => {
104
+ if (this.extensionHost)
105
+ await this.extensionHost.emit(event, signal);
87
106
  await this.emitSession(event);
88
107
  });
89
108
  }
@@ -143,6 +162,10 @@ export class AgentSession {
143
162
  };
144
163
  }
145
164
  async prompt(text, signal) {
165
+ return this.promptWithImages(text, [], signal);
166
+ }
167
+ /** Sends one text prompt with explicitly supplied image attachments. */
168
+ async promptWithImages(text, images, signal) {
146
169
  if (this.persistenceError !== undefined) {
147
170
  throw this.persistenceError;
148
171
  }
@@ -151,8 +174,13 @@ export class AgentSession {
151
174
  }
152
175
  this.promptActive = true;
153
176
  try {
154
- await this.compactIfNeeded(text, signal);
155
- return await this.agent.prompt(text, signal);
177
+ if (images.length > 0 && !this.model.input.includes("image")) {
178
+ throw new Error(`Model "${this.model.id}" does not support image input.`);
179
+ }
180
+ const prompt = await resolveSkillCommand(text, this.skills, signal);
181
+ const content = [{ type: "text", text: prompt }, ...structuredClone([...images])];
182
+ await this.compactIfNeeded(content, signal);
183
+ return await this.agent.promptWithContent(content, signal);
156
184
  }
157
185
  finally {
158
186
  this.promptActive = false;
@@ -199,13 +227,13 @@ export class AgentSession {
199
227
  },
200
228
  };
201
229
  }
202
- async compactIfNeeded(text, signal) {
230
+ async compactIfNeeded(content, signal) {
203
231
  if (!this.compactionEnabledValue || !this.sessionManager)
204
232
  return;
205
233
  const context = buildSessionContext(this.sessionManager.entries);
206
234
  const pendingUser = {
207
235
  role: "user",
208
- content: [{ type: "text", text }],
236
+ content: structuredClone([...content]),
209
237
  timestamp: 0,
210
238
  };
211
239
  const estimatedTokens = estimateContextTokens([...context.messages, pendingUser]);