@ghostmind-dev/agent 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +141 -0
  3. package/dist/budget.d.ts +39 -0
  4. package/dist/budget.d.ts.map +1 -0
  5. package/dist/budget.js +34 -0
  6. package/dist/budget.js.map +1 -0
  7. package/dist/consult.d.ts +25 -0
  8. package/dist/consult.d.ts.map +1 -0
  9. package/dist/consult.js +80 -0
  10. package/dist/consult.js.map +1 -0
  11. package/dist/events.d.ts +119 -0
  12. package/dist/events.d.ts.map +1 -0
  13. package/dist/events.js +24 -0
  14. package/dist/events.js.map +1 -0
  15. package/dist/guide.d.ts +103 -0
  16. package/dist/guide.d.ts.map +1 -0
  17. package/dist/guide.js +268 -0
  18. package/dist/guide.js.map +1 -0
  19. package/dist/hooks.d.ts +80 -0
  20. package/dist/hooks.d.ts.map +1 -0
  21. package/dist/hooks.js +56 -0
  22. package/dist/hooks.js.map +1 -0
  23. package/dist/index.d.ts +33 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +21 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/jev.d.ts +74 -0
  28. package/dist/jev.d.ts.map +1 -0
  29. package/dist/jev.js +82 -0
  30. package/dist/jev.js.map +1 -0
  31. package/dist/loop.d.ts +142 -0
  32. package/dist/loop.d.ts.map +1 -0
  33. package/dist/loop.js +550 -0
  34. package/dist/loop.js.map +1 -0
  35. package/dist/model.d.ts +173 -0
  36. package/dist/model.d.ts.map +1 -0
  37. package/dist/model.js +205 -0
  38. package/dist/model.js.map +1 -0
  39. package/dist/seams.d.ts +137 -0
  40. package/dist/seams.d.ts.map +1 -0
  41. package/dist/seams.js +45 -0
  42. package/dist/seams.js.map +1 -0
  43. package/dist/skills.d.ts +36 -0
  44. package/dist/skills.d.ts.map +1 -0
  45. package/dist/skills.js +168 -0
  46. package/dist/skills.js.map +1 -0
  47. package/dist/testing.d.ts +36 -0
  48. package/dist/testing.d.ts.map +1 -0
  49. package/dist/testing.js +60 -0
  50. package/dist/testing.js.map +1 -0
  51. package/dist/tools.d.ts +114 -0
  52. package/dist/tools.d.ts.map +1 -0
  53. package/dist/tools.js +136 -0
  54. package/dist/tools.js.map +1 -0
  55. package/package.json +50 -0
package/dist/skills.js ADDED
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Skills — guides the agent loads only when a task calls for one.
3
+ *
4
+ * Loaded the Agent Skills way (agentskills.io): the model sees each picked skill's name and when to
5
+ * use it; `use_skill` brings in the full instructions and lists the reference files;
6
+ * `read_skill_file` brings in one of those. Nothing is loaded that the task does not call for.
7
+ *
8
+ * Which skills a run needs is a DECISION, not a generation, so Jev makes it (`pickSkills`): one
9
+ * choice question over every skill's description, answered in well under a second. TypeSafe
10
+ * measured the case — an agent reading a truncated index loaded the wrong skill 16.8% of the time
11
+ * against 7.3% when a System One model ranked them first — and it keeps the index out of the
12
+ * prompt however many skills exist. Without a `decide`, every skill is offered.
13
+ *
14
+ * Reading skills and guides is preparation, not work: the loop spends no tool call on them and gives
15
+ * the step back (see `guideTools` in the loop), because in production four guide reads once used up
16
+ * a whole budget and the answer then dropped what the person asked for.
17
+ */
18
+ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
19
+ import { join, relative, sep } from 'node:path';
20
+ const FILE_CAP = 24_000;
21
+ const cap = (s) => (s.length > FILE_CAP ? `${s.slice(0, FILE_CAP)}\n…(truncated, ${s.length} characters)` : s);
22
+ const stripFrontmatter = (s) => s.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, '');
23
+ /** name and description from a SKILL.md's frontmatter (simple `key: value` lines, quotes optional). */
24
+ export function readFrontmatter(text) {
25
+ const m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(text);
26
+ const out = {};
27
+ if (!m?.[1])
28
+ return out;
29
+ for (const line of m[1].split(/\r?\n/)) {
30
+ const kv = /^([A-Za-z0-9_-]+):\s*(.*)$/.exec(line);
31
+ if (kv?.[1])
32
+ out[kv[1]] = (kv[2] ?? '').trim().replace(/^(['"])([\s\S]*)\1$/, '$2');
33
+ }
34
+ return out;
35
+ }
36
+ /** Skills embedded in code (generated at build time, fetched from a database…). */
37
+ export function inlineSkills(skills) {
38
+ const find = (name) => skills.find(s => s.name === name);
39
+ return {
40
+ list: async () => skills.map(s => ({ name: s.name, description: s.description })),
41
+ open: async (name) => {
42
+ const s = find(name);
43
+ return s ? { instructions: stripFrontmatter(s.files['SKILL.md'] ?? ''), files: Object.keys(s.files).filter(f => f !== 'SKILL.md') } : null;
44
+ },
45
+ readFile: async (name, path) => find(name)?.files[path.replace(/^\.?\//, '')] ?? null,
46
+ };
47
+ }
48
+ function walk(dir, root, out = []) {
49
+ for (const entry of readdirSync(dir)) {
50
+ if (entry.startsWith('.'))
51
+ continue;
52
+ const full = join(dir, entry);
53
+ if (statSync(full).isDirectory())
54
+ walk(full, root, out);
55
+ else
56
+ out.push(relative(root, full).split(sep).join('/'));
57
+ }
58
+ return out;
59
+ }
60
+ /** Skills on disk: every `<dir>/<name>/SKILL.md` (Claude Code, Codex and Gemini CLI all use this layout). */
61
+ export function dirSkills(...dirs) {
62
+ const load = () => {
63
+ const found = new Map();
64
+ for (const dir of dirs) {
65
+ if (!existsSync(dir))
66
+ continue;
67
+ for (const entry of readdirSync(dir)) {
68
+ const root = join(dir, entry);
69
+ const file = join(root, 'SKILL.md');
70
+ if (!existsSync(file))
71
+ continue;
72
+ const fm = readFrontmatter(readFileSync(file, 'utf8'));
73
+ const name = fm.name || entry;
74
+ // The first directory that defines a name wins, so a project can override one it inherited.
75
+ if (!found.has(name) && fm.description)
76
+ found.set(name, { meta: { name, description: fm.description }, root });
77
+ }
78
+ }
79
+ return found;
80
+ };
81
+ return {
82
+ list: async () => [...load().values()].map(s => s.meta),
83
+ open: async (name) => {
84
+ const s = load().get(name);
85
+ if (!s)
86
+ return null;
87
+ return { instructions: stripFrontmatter(readFileSync(join(s.root, 'SKILL.md'), 'utf8')), files: walk(s.root, s.root).filter(f => f !== 'SKILL.md') };
88
+ },
89
+ readFile: async (name, path) => {
90
+ const s = load().get(name);
91
+ const clean = path.replace(/^\.?\//, '');
92
+ if (!s || clean.split('/').includes('..'))
93
+ return null;
94
+ const full = join(s.root, clean);
95
+ return existsSync(full) && statSync(full).isFile() ? readFileSync(full, 'utf8') : null;
96
+ },
97
+ };
98
+ }
99
+ export const NO_SKILL = 'none';
100
+ /**
101
+ * Which skills this task needs — usually none. Jev reads the message (and the last few before it)
102
+ * against every description; a clear pick, or the two likeliest when it is torn — never "none"
103
+ * dressed up as a skill. Without `decide`, every skill is returned. A failed decision picks none.
104
+ */
105
+ export async function pickSkills(model, skills, message, options = {}) {
106
+ if (!skills.length)
107
+ return { names: [], cost: 0 };
108
+ if (!model.decide)
109
+ return { names: skills.map(s => s.name), cost: 0 };
110
+ const paths = {
111
+ [NO_SKILL]: options.none ?? 'No guide is needed: the message can be handled with the tools alone. Almost every message is this.',
112
+ };
113
+ for (const s of skills.slice(0, 254))
114
+ paths[s.name] = s.description.slice(0, 600);
115
+ try {
116
+ const { answers, cost } = await model.decide({ message, earlier: (options.earlier ?? []).slice(-3) }, { skill: { type: 'choice', instructions: 'Which guide does handling `message` need, if any?', criteria: paths } }, options.signal);
117
+ const a = answers.skill;
118
+ if (a.type !== 'choice')
119
+ return { names: [], cost };
120
+ const ranked = Object.entries(a.probabilities).sort((x, y) => y[1] - x[1]);
121
+ const names = (a.confidence >= 0.5 ? ranked.slice(0, 1) : ranked.slice(0, 2)).filter(([name, p]) => name !== NO_SKILL && p >= 0.2 && name in paths).map(([name]) => name);
122
+ return { names, cost };
123
+ }
124
+ catch {
125
+ return { names: [], cost: 0 };
126
+ }
127
+ }
128
+ /** The tool names skillsToolset defines — the loop's default guide tools. */
129
+ export const SKILL_TOOLS = ['use_skill', 'read_skill_file'];
130
+ /** Tools to load the given skills (and only those). Null when there are none. */
131
+ export function skillsToolset(source, names, options = {}) {
132
+ if (!names.length)
133
+ return null;
134
+ return {
135
+ name: 'skills',
136
+ description: `Guides: ${names.join(', ')}. Load one when the task calls for it.`,
137
+ tools: [
138
+ {
139
+ name: 'use_skill',
140
+ description: `Load a skill's full instructions and the list of its reference files. Skills: ${names.join(', ')}.`,
141
+ parameters: { type: 'object', properties: { name: { type: 'string', enum: names } }, required: ['name'] },
142
+ run: async (a) => {
143
+ const name = String(a.name ?? '');
144
+ const skill = names.includes(name) ? await source.open(name) : null;
145
+ if (!skill)
146
+ throw new Error(`No skill "${name}". Skills: ${names.join(', ')}`);
147
+ return { skill: name, ...(options.preface ? { note: options.preface } : {}), instructions: cap(skill.instructions), reference_files: skill.files };
148
+ },
149
+ },
150
+ {
151
+ name: 'read_skill_file',
152
+ description: 'Read one reference file of a skill, as listed by use_skill.',
153
+ parameters: { type: 'object', properties: { name: { type: 'string', enum: names }, path: { type: 'string' } }, required: ['name', 'path'] },
154
+ run: async (a) => {
155
+ const name = String(a.name ?? '');
156
+ const path = String(a.path ?? '');
157
+ const content = names.includes(name) ? await source.readFile(name, path) : null;
158
+ if (content === null) {
159
+ const skill = names.includes(name) ? await source.open(name) : null;
160
+ throw new Error(`No file "${path}" in skill "${name}". Files: ${skill ? skill.files.join(', ') : 'unknown skill'}`);
161
+ }
162
+ return cap(content);
163
+ },
164
+ },
165
+ ],
166
+ };
167
+ }
168
+ //# sourceMappingURL=skills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.js","sourceRoot":"","sources":["../src/skills.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAKhD,MAAM,QAAQ,GAAG,MAAM,CAAC;AACxB,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAAC,MAAM,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACvH,MAAM,gBAAgB,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,iCAAiC,EAAE,EAAE,CAAC,CAAC;AAEzF,uGAAuG;AACvG,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,CAAC,GAAG,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnD,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAAE,OAAO,GAAG,CAAC;IACxB,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACvC,MAAM,EAAE,GAAG,4BAA4B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnD,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;YAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAAC;IACtF,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AASD,mFAAmF;AACnF,MAAM,UAAU,YAAY,CAAC,MAAqB;IAChD,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACjE,OAAO;QACL,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QACjF,IAAI,EAAE,KAAK,EAAC,IAAI,EAAC,EAAE;YACjB,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;YACrB,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC7I,CAAC;QACD,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI;KACtF,CAAC;AACJ,CAAC;AAED,SAAS,IAAI,CAAC,GAAW,EAAE,IAAY,EAAE,MAAgB,EAAE;IACzD,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QACpC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC9B,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE;YAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;;YACnD,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,6GAA6G;AAC7G,MAAM,UAAU,SAAS,CAAC,GAAG,IAAc;IACzC,MAAM,IAAI,GAAG,GAAmD,EAAE;QAChE,MAAM,KAAK,GAAG,IAAI,GAAG,EAA6C,CAAC;QACnE,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,SAAS;YAC/B,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;gBACrC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;gBAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;gBACpC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;oBAAE,SAAS;gBAChC,MAAM,EAAE,GAAG,eAAe,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;gBACvD,MAAM,IAAI,GAAG,EAAE,CAAC,IAAI,IAAI,KAAK,CAAC;gBAC9B,4FAA4F;gBAC5F,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW;oBAAE,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;YACjH,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IACF,OAAO;QACL,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACvD,IAAI,EAAE,KAAK,EAAC,IAAI,EAAC,EAAE;YACjB,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC3B,IAAI,CAAC,CAAC;gBAAE,OAAO,IAAI,CAAC;YACpB,OAAO,EAAE,YAAY,EAAE,gBAAgB,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,UAAU,CAAC,EAAE,CAAC;QACvJ,CAAC;QACD,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;YAC7B,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YACzC,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC;YACvD,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACjC,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACzF,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC;AAE/B;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,KAAoB,EACpB,MAAmB,EACnB,OAAe,EACf,UAAuE,EAAE;IAEzE,IAAI,CAAC,MAAM,CAAC,MAAM;QAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAClD,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IACtE,MAAM,KAAK,GAA2B;QACpC,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,IAAI,IAAI,oGAAoG;KACjI,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;QAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAClF,IAAI,CAAC;QACH,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,MAAM,KAAK,CAAC,MAAM,CAC1C,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,EACvD,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,mDAAmD,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EACjH,OAAO,CAAC,MAAM,CACf,CAAC;QACF,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC;QACxB,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;QACpD,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3E,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,UAAU,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,IAAI,GAAG,IAAI,IAAI,IAAI,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;QAC1K,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAChC,CAAC;AACH,CAAC;AAED,6EAA6E;AAC7E,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC;AAE5D,iFAAiF;AACjF,MAAM,UAAU,aAAa,CAAC,MAAmB,EAAE,KAAe,EAAE,UAAgC,EAAE;IACpG,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAC/B,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,WAAW,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,wCAAwC;QAChF,KAAK,EAAE;YACL;gBACE,IAAI,EAAE,WAAW;gBACjB,WAAW,EAAE,iFAAiF,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;gBACjH,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,MAAM,CAAC,EAAE;gBACzG,GAAG,EAAE,KAAK,EAAC,CAAC,EAAC,EAAE;oBACb,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;oBAClC,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;oBACpE,IAAI,CAAC,KAAK;wBAAE,MAAM,IAAI,KAAK,CAAC,aAAa,IAAI,cAAc,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;oBAC/E,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,YAAY,EAAE,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;gBACrJ,CAAC;aACF;YACD;gBACE,IAAI,EAAE,iBAAiB;gBACvB,WAAW,EAAE,6DAA6D;gBAC1E,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE;gBAC3I,GAAG,EAAE,KAAK,EAAC,CAAC,EAAC,EAAE;oBACb,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;oBAClC,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;oBAClC,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;oBAChF,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;wBACrB,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;wBACpE,MAAM,IAAI,KAAK,CAAC,YAAY,IAAI,eAAe,IAAI,aAAa,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC;oBACtH,CAAC;oBACD,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC;gBACtB,CAAC;aACF;SACF;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * A scripted ModelProvider — the engine's own tests, and any app's, run offline on it.
3
+ *
4
+ * The worker replies from a script, in order; Jev answers through a function (or picks the first
5
+ * option of every choice, which is "task" for the opening decision and "none" for skills). Every
6
+ * request is recorded, so a test can assert what the model was shown. It spends nothing unless the
7
+ * script says a step cost something — which is how a budget cap is tested without a key.
8
+ */
9
+ import type { Answer, ChatReply, ChatRequest, ModelCard, ModelProvider, Question } from './model.ts';
10
+ export type ScriptStep = string | {
11
+ text?: string | null;
12
+ calls?: {
13
+ name: string;
14
+ args?: Record<string, unknown> | string;
15
+ }[];
16
+ cost?: number;
17
+ } | ((request: ChatRequest) => ChatReply | Promise<ChatReply>);
18
+ export type ScriptedDecide = (state: unknown, questions: Record<string, Question>) => Record<string, Answer> | Promise<Record<string, Answer>>;
19
+ export interface ScriptedModel extends ModelProvider {
20
+ /** Every chat request, in order (messages copied as they were at the time). */
21
+ requests: ChatRequest[];
22
+ /** Every decide call, in order. */
23
+ decisions: {
24
+ state: unknown;
25
+ questions: Record<string, Question>;
26
+ }[];
27
+ }
28
+ /** Jev's default stub: the first option of every choice, 0.5 for yes/no, the first level of a score. */
29
+ export declare const firstOption: ScriptedDecide;
30
+ export declare function scriptedModel(script: ScriptStep[], options?: {
31
+ decide?: ScriptedDecide | false;
32
+ decideCost?: number;
33
+ model?: string; /** What `card()` says of the worker (how the guide sizes it up). */
34
+ card?: Partial<ModelCard>;
35
+ }): ScriptedModel;
36
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,QAAQ,EAAY,MAAM,YAAY,CAAC;AAE/G,MAAM,MAAM,UAAU,GAClB,MAAM,GACN;IAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAA;KAAE,EAAE,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5G,CAAC,CAAC,OAAO,EAAE,WAAW,KAAK,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;AAE/D,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAE/I,MAAM,WAAW,aAAc,SAAQ,aAAa;IAClD,+EAA+E;IAC/E,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,mCAAmC;IACnC,SAAS,EAAE;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;KAAE,EAAE,CAAC;CACtE;AAED,wGAAwG;AACxG,eAAO,MAAM,WAAW,EAAE,cAWvB,CAAC;AAIJ,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,cAAc,GAAG,KAAK,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC,oEAAoE;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,CAAA;CAAO,GAAG,aAAa,CA2CzP"}
@@ -0,0 +1,60 @@
1
+ /** Jev's default stub: the first option of every choice, 0.5 for yes/no, the first level of a score. */
2
+ export const firstOption = (_state, questions) => Object.fromEntries(Object.entries(questions).map(([id, q]) => {
3
+ if (q.type === 'noul')
4
+ return [id, { type: 'noul', noul: 0.5 }];
5
+ if (q.type === 'choice') {
6
+ const keys = Object.keys(q.criteria);
7
+ const first = keys[0] ?? '';
8
+ return [id, { type: 'choice', choice: first, confidence: 1, probabilities: Object.fromEntries(keys.map(k => [k, k === first ? 1 : 0])) }];
9
+ }
10
+ return [id, { type: 'score', score: 0, confidence: 1, probabilities: { '0': 1 }, legend: { '0': String(q.criteria[0] ?? '') } }];
11
+ }));
12
+ let seq = 0;
13
+ export function scriptedModel(script, options = {}) {
14
+ const queue = [...script];
15
+ const requests = [];
16
+ const decisions = [];
17
+ const provider = {
18
+ defaultModel: options.model ?? 'stub/worker',
19
+ requests,
20
+ decisions,
21
+ async chat(request) {
22
+ request.signal?.throwIfAborted();
23
+ requests.push({ ...request, messages: structuredClone(request.messages) });
24
+ const step = queue.shift();
25
+ if (step === undefined)
26
+ throw new Error(`The script ran out after ${requests.length - 1} replies.`);
27
+ if (typeof step === 'function')
28
+ return step(request);
29
+ const s = typeof step === 'string' ? { text: step } : step;
30
+ const calls = (s.calls ?? []).map(c => ({
31
+ id: `call_${++seq}`,
32
+ type: 'function',
33
+ function: { name: c.name, arguments: typeof c.args === 'string' ? c.args : JSON.stringify(c.args ?? {}) },
34
+ }));
35
+ const text = s.text ?? null;
36
+ if (text && request.onDelta)
37
+ request.onDelta(text);
38
+ return {
39
+ message: { role: 'assistant', content: text, ...(calls.length ? { tool_calls: calls } : {}) },
40
+ finishReason: calls.length ? 'tool_calls' : 'stop',
41
+ model: request.model ?? provider.defaultModel ?? 'stub/worker',
42
+ cost: s.cost ?? 0,
43
+ };
44
+ },
45
+ };
46
+ if (options.card) {
47
+ const card = options.card;
48
+ provider.card = async (model) => ({ id: model, name: model, prompt: 0, completion: 0, context: 128_000, tools: true, vision: false, ...card });
49
+ }
50
+ if (options.decide !== false) {
51
+ const answer = options.decide ?? firstOption;
52
+ provider.decide = async (state, questions, signal) => {
53
+ signal?.throwIfAborted();
54
+ decisions.push({ state: structuredClone(state), questions: structuredClone(questions) });
55
+ return { answers: (await answer(state, questions)), cost: options.decideCost ?? 0 };
56
+ };
57
+ }
58
+ return provider;
59
+ }
60
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Tools — what an app hands the engine, and the helpers every app ended up writing.
3
+ *
4
+ * A tool is a spec the model reads plus a `run`. A tool that CHANGES something says so with
5
+ * `write.describe`: the loop then never runs it on the model's say-so, it asks the app's `approve`
6
+ * first with that one-line account of the change. That split lives on the tool, not in the loop,
7
+ * because only the tool knows whether THIS call writes (an action tool may read or write depending
8
+ * on its arguments).
9
+ *
10
+ * `foldTools` exists because a few general tools beat many narrow ones (Pi's lesson, confirmed in
11
+ * Potion's evaluation): a router that offered only some tools per step was the main cause of
12
+ * "I have no tool for that". Folding keeps every action visible in ONE tool's description while
13
+ * each still runs, and asks approval, exactly as before.
14
+ *
15
+ * Arguments are read forgivingly (`forgivingArgs`, `readCall`): models send JSON as a string, put
16
+ * arguments beside `action` instead of inside `args`, or (GLM) pack them into the action name. Each
17
+ * of those used to cost a failed step; each is a harmless encoding slip, so it is read as meant.
18
+ */
19
+ import type { ToolSpec } from './model.ts';
20
+ export interface ToolContext {
21
+ signal: AbortSignal;
22
+ /** Add USD this tool spent (a model it consulted, a sub-agent) to the run's total and budget. */
23
+ spend(usd: number): void;
24
+ /** The model's id for this call. */
25
+ callId: string;
26
+ }
27
+ export interface AgentTool extends ToolSpec {
28
+ /** Whatever it returns is handed to the model as text (objects as JSON). A throw is reported to the model, not to the caller. */
29
+ run(args: Record<string, unknown>, ctx: ToolContext): Promise<unknown>;
30
+ /** A change: its approval sentence (may need a lookup). Null = this particular call only reads. */
31
+ write?: {
32
+ describe(args: Record<string, unknown>): string | null | Promise<string | null>;
33
+ };
34
+ }
35
+ /** A group of tools: one MCP server, one area of an app, one page's buttons. */
36
+ export interface Toolset {
37
+ /** A short stable key: "notes", "github-mcp". */
38
+ name: string;
39
+ /** What it is for. */
40
+ description: string;
41
+ tools: AgentTool[];
42
+ }
43
+ export interface ApprovalRequest {
44
+ id: string;
45
+ tool: string;
46
+ summary: string;
47
+ args: Record<string, unknown>;
48
+ }
49
+ /**
50
+ * JSON with keys sorted at every level: the same call written in another key order is the same
51
+ * call. Not JSON.stringify(v, keys): an array replacer is an allowlist applied at EVERY depth, so
52
+ * it emptied nested arguments — every call looked like a repeat of the first, and the repeat guard
53
+ * locked the model out after one miss.
54
+ */
55
+ export declare function stableJson(v: unknown): string;
56
+ /** Parse arguments a model sent as JSON strings; leave everything else as it is. */
57
+ export declare function forgivingArgs(args: unknown): Record<string, unknown>;
58
+ /** The action and its arguments of a folded call, however the model sent them. */
59
+ export declare function readCall(a: Record<string, unknown>): {
60
+ name: string;
61
+ args: Record<string, unknown>;
62
+ };
63
+ export interface FoldOptions {
64
+ /** The folded tool's name, e.g. the app's name. */
65
+ name: string;
66
+ /** The first line of its description: what it reaches. */
67
+ description: string;
68
+ /** Appended after the action reference: query grammars, argument shapes a model guesses wrong. */
69
+ appendix?: string;
70
+ }
71
+ /**
72
+ * Fold toolsets into ONE tool called as { action, args }. Its description is a compact reference of
73
+ * every action. Reads run directly; an action with `write` still asks approval (the folded tool's
74
+ * `write.describe` delegates, and says null for a read).
75
+ */
76
+ export declare function foldTools(toolsets: Toolset[], options: FoldOptions): AgentTool;
77
+ /**
78
+ * The shape of an MCP connection this engine can use. Structural on purpose: ensemble's
79
+ * `connect()` session satisfies it as-is, and any other client can with a three-line adapter.
80
+ * The engine speaks no MCP wire protocol itself — connecting (stdio, HTTP, OAuth) is the app's job.
81
+ */
82
+ export interface McpLike {
83
+ name: string;
84
+ listTools(options?: {
85
+ signal?: AbortSignal;
86
+ }): Promise<{
87
+ name: string;
88
+ description?: string;
89
+ inputSchema?: Record<string, unknown>;
90
+ }[]>;
91
+ call(tool: string, args: Record<string, unknown>, options?: {
92
+ signal?: AbortSignal;
93
+ }): Promise<{
94
+ text: string;
95
+ data?: unknown;
96
+ isError: boolean;
97
+ }>;
98
+ }
99
+ export interface McpToolsetOptions {
100
+ /** Default: the session's name. */
101
+ name?: string;
102
+ description?: string;
103
+ /** Only these tools (by name). */
104
+ only?: string[];
105
+ /**
106
+ * Which calls CHANGE something, and the sentence the person approves. MCP servers do not say
107
+ * reliably (annotations are hints), so the app decides. Default: every call is a read.
108
+ */
109
+ writes?: (tool: string, args: Record<string, unknown>) => string | null | Promise<string | null>;
110
+ signal?: AbortSignal;
111
+ }
112
+ /** One MCP server's tools as a Toolset. */
113
+ export declare function mcpToolset(session: McpLike, options?: McpToolsetOptions): Promise<Toolset>;
114
+ //# sourceMappingURL=tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
package/dist/tools.js ADDED
@@ -0,0 +1,136 @@
1
+ /**
2
+ * JSON with keys sorted at every level: the same call written in another key order is the same
3
+ * call. Not JSON.stringify(v, keys): an array replacer is an allowlist applied at EVERY depth, so
4
+ * it emptied nested arguments — every call looked like a repeat of the first, and the repeat guard
5
+ * locked the model out after one miss.
6
+ */
7
+ export function stableJson(v) {
8
+ if (Array.isArray(v))
9
+ return `[${v.map(stableJson).join(',')}]`;
10
+ if (v && typeof v === 'object') {
11
+ const o = v;
12
+ return `{${Object.keys(o)
13
+ .sort()
14
+ .filter(k => o[k] !== undefined)
15
+ .map(k => `${JSON.stringify(k)}:${stableJson(o[k])}`)
16
+ .join(',')}}`;
17
+ }
18
+ return JSON.stringify(v) ?? 'null';
19
+ }
20
+ function safeParse(s) {
21
+ try {
22
+ return JSON.parse(s);
23
+ }
24
+ catch {
25
+ return undefined;
26
+ }
27
+ }
28
+ /** Parse arguments a model sent as JSON strings; leave everything else as it is. */
29
+ export function forgivingArgs(args) {
30
+ const raw = typeof args === 'string' ? safeParse(args) : args;
31
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw))
32
+ return {};
33
+ const out = {};
34
+ for (const [k, v] of Object.entries(raw)) {
35
+ if (typeof v === 'string' && /^\s*[[{]/.test(v)) {
36
+ const parsed = safeParse(v);
37
+ out[k] = parsed !== undefined && typeof parsed === 'object' ? parsed : v;
38
+ }
39
+ else
40
+ out[k] = v;
41
+ }
42
+ return out;
43
+ }
44
+ /** The action and its arguments of a folded call, however the model sent them. */
45
+ export function readCall(a) {
46
+ let name = String(a.action ?? '').trim();
47
+ const packed = {};
48
+ if (name.includes('<arg_key>')) {
49
+ for (const m of name.matchAll(/<arg_key>([\s\S]*?)<\/arg_key>\s*<arg_value>([\s\S]*?)(?:<\/arg_value>|$)/g)) {
50
+ const value = (m[2] ?? '').trim();
51
+ const parsed = /^[[{"]|^-?\d|^(true|false|null)$/.test(value) ? safeParse(value) : undefined;
52
+ packed[(m[1] ?? '').trim()] = parsed !== undefined ? parsed : value;
53
+ }
54
+ name = name.slice(0, name.indexOf('<')).trim();
55
+ }
56
+ const { action: _action, args: nested, ...beside } = a;
57
+ const fromPacked = packed.args && typeof packed.args === 'object' ? packed.args : {};
58
+ const { args: _packedArgs, ...packedRest } = packed;
59
+ return { name, args: forgivingArgs({ ...beside, ...fromPacked, ...packedRest, ...forgivingArgs(nested) }) };
60
+ }
61
+ /** A compact argument signature from a JSON Schema: { query, limit? }. */
62
+ function signature(tool) {
63
+ const p = tool.parameters;
64
+ if (!p?.properties || !Object.keys(p.properties).length)
65
+ return '{}';
66
+ const req = new Set(p.required ?? []);
67
+ return `{ ${Object.keys(p.properties)
68
+ .map(k => (req.has(k) ? k : `${k}?`))
69
+ .join(', ')} }`;
70
+ }
71
+ /** The first sentence of a description. */
72
+ function gist(tool) {
73
+ const d = tool.description.trim();
74
+ const first = d.split(/(?<=\.)\s/)[0] ?? d;
75
+ return first.length > 140 ? `${first.slice(0, 140)}…` : first;
76
+ }
77
+ /**
78
+ * Fold toolsets into ONE tool called as { action, args }. Its description is a compact reference of
79
+ * every action. Reads run directly; an action with `write` still asks approval (the folded tool's
80
+ * `write.describe` delegates, and says null for a read).
81
+ */
82
+ export function foldTools(toolsets, options) {
83
+ const byName = new Map(toolsets.flatMap(s => s.tools).map(t => [t.name, t]));
84
+ const names = [...byName.keys()];
85
+ const reference = toolsets
86
+ .map(s => `${s.name.replace(/_/g, ' ')}:\n${s.tools.map(t => ` ${t.name} ${signature(t)}${t.write ? ' [asks approval]' : ''} — ${gist(t)}`).join('\n')}`)
87
+ .join('\n');
88
+ return {
89
+ name: options.name,
90
+ description: [`${options.description} Call as { action, args }. Actions marked [asks approval] run only once the person approves.`, reference, options.appendix]
91
+ .filter(Boolean)
92
+ .join('\n'),
93
+ parameters: {
94
+ type: 'object',
95
+ properties: {
96
+ action: { type: 'string', enum: names },
97
+ args: { type: 'object', description: "The action's arguments, as listed", additionalProperties: true },
98
+ },
99
+ required: ['action'],
100
+ },
101
+ write: {
102
+ describe: a => {
103
+ const call = readCall(a);
104
+ const t = byName.get(call.name);
105
+ return t?.write ? t.write.describe(call.args) : null;
106
+ },
107
+ },
108
+ run: async (a, ctx) => {
109
+ const call = readCall(a);
110
+ const t = byName.get(call.name);
111
+ if (!t)
112
+ throw new Error(`There is no action "${call.name}". Actions: ${names.join(', ')}`);
113
+ return t.run(call.args, ctx);
114
+ },
115
+ };
116
+ }
117
+ /** One MCP server's tools as a Toolset. */
118
+ export async function mcpToolset(session, options = {}) {
119
+ const listed = await session.listTools({ signal: options.signal });
120
+ const tools = listed
121
+ .filter(t => !options.only || options.only.includes(t.name))
122
+ .map(t => ({
123
+ name: t.name,
124
+ description: t.description ?? t.name,
125
+ parameters: t.inputSchema ?? { type: 'object', properties: {} },
126
+ ...(options.writes ? { write: { describe: (args) => options.writes(t.name, args) } } : {}),
127
+ run: async (args, ctx) => {
128
+ const out = await session.call(t.name, args, { signal: ctx.signal });
129
+ if (out.isError)
130
+ throw new Error(out.text || `${t.name} failed`);
131
+ return out.data ?? out.text;
132
+ },
133
+ }));
134
+ return { name: options.name ?? session.name, description: options.description ?? `Tools of the ${session.name} MCP server.`, tools };
135
+ }
136
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@ghostmind-dev/agent",
3
+ "version": "0.1.0",
4
+ "description": "The agent engine every app builds its own agent from: the tool loop and its guards, a model seam (OpenRouter + Jev), budgets with a USD cap, go/pause/stop hooks, and an append-only event log. A library, not a service.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ },
12
+ "./package.json": "./package.json"
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "keywords": [
20
+ "agent",
21
+ "tool-loop",
22
+ "llm",
23
+ "openrouter",
24
+ "jev",
25
+ "typesafe",
26
+ "mcp",
27
+ "agent-skills",
28
+ "hooks",
29
+ "budget"
30
+ ],
31
+ "scripts": {
32
+ "build": "rm -rf dist && tsc -p tsconfig.build.json",
33
+ "typecheck": "tsc --noEmit",
34
+ "test": "node test/run.mts",
35
+ "pretest": "npm run build",
36
+ "example": "node examples/two-tools.mts",
37
+ "bench": "node bench/guidance.mts",
38
+ "prepublishOnly": "npm run typecheck && npm test"
39
+ },
40
+ "dependencies": {},
41
+ "devDependencies": {
42
+ "@types/node": "^22.10.0",
43
+ "typescript": "^5.7.0"
44
+ },
45
+ "engines": {
46
+ "node": ">=22.18.0"
47
+ },
48
+ "main": "./dist/index.js",
49
+ "types": "./dist/index.d.ts"
50
+ }