@oharness/engine 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.
- package/dist/agent/agent.d.ts +196 -0
- package/dist/agent/agent.js +758 -0
- package/dist/agent/agent.js.map +1 -0
- package/dist/agent/compaction.d.ts +68 -0
- package/dist/agent/compaction.js +179 -0
- package/dist/agent/compaction.js.map +1 -0
- package/dist/agent/hooks.d.ts +77 -0
- package/dist/agent/hooks.js +152 -0
- package/dist/agent/hooks.js.map +1 -0
- package/dist/agent/memory.d.ts +45 -0
- package/dist/agent/memory.js +147 -0
- package/dist/agent/memory.js.map +1 -0
- package/dist/agent/overflow.d.ts +35 -0
- package/dist/agent/overflow.js +69 -0
- package/dist/agent/overflow.js.map +1 -0
- package/dist/agent/permissions.d.ts +181 -0
- package/dist/agent/permissions.js +289 -0
- package/dist/agent/permissions.js.map +1 -0
- package/dist/agent/prompt.d.ts +25 -0
- package/dist/agent/prompt.js +63 -0
- package/dist/agent/prompt.js.map +1 -0
- package/dist/agent/session.d.ts +108 -0
- package/dist/agent/session.js +231 -0
- package/dist/agent/session.js.map +1 -0
- package/dist/agent/settings.d.ts +37 -0
- package/dist/agent/settings.js +113 -0
- package/dist/agent/settings.js.map +1 -0
- package/dist/agent/skills.d.ts +58 -0
- package/dist/agent/skills.js +225 -0
- package/dist/agent/skills.js.map +1 -0
- package/dist/agent/store.d.ts +308 -0
- package/dist/agent/store.js +596 -0
- package/dist/agent/store.js.map +1 -0
- package/dist/agent/subagent.d.ts +44 -0
- package/dist/agent/subagent.js +138 -0
- package/dist/agent/subagent.js.map +1 -0
- package/dist/agent/titles.d.ts +8 -0
- package/dist/agent/titles.js +23 -0
- package/dist/agent/titles.js.map +1 -0
- package/dist/agent/workspace.d.ts +60 -0
- package/dist/agent/workspace.js +156 -0
- package/dist/agent/workspace.js.map +1 -0
- package/dist/artifacts/publish.d.ts +94 -0
- package/dist/artifacts/publish.js +104 -0
- package/dist/artifacts/publish.js.map +1 -0
- package/dist/artifacts/store.d.ts +324 -0
- package/dist/artifacts/store.js +500 -0
- package/dist/artifacts/store.js.map +1 -0
- package/dist/auth/index.d.ts +13 -0
- package/dist/auth/index.js +10 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/manager.d.ts +83 -0
- package/dist/auth/manager.js +266 -0
- package/dist/auth/manager.js.map +1 -0
- package/dist/auth/oauth.d.ts +83 -0
- package/dist/auth/oauth.js +327 -0
- package/dist/auth/oauth.js.map +1 -0
- package/dist/auth/providers/account.d.ts +59 -0
- package/dist/auth/providers/account.js +282 -0
- package/dist/auth/providers/account.js.map +1 -0
- package/dist/auth/providers/anthropic.d.ts +24 -0
- package/dist/auth/providers/anthropic.js +209 -0
- package/dist/auth/providers/anthropic.js.map +1 -0
- package/dist/auth/providers/generic.d.ts +57 -0
- package/dist/auth/providers/generic.js +155 -0
- package/dist/auth/providers/generic.js.map +1 -0
- package/dist/auth/providers/official.d.ts +3 -0
- package/dist/auth/providers/official.js +90 -0
- package/dist/auth/providers/official.js.map +1 -0
- package/dist/auth/providers/ottoport.d.ts +3 -0
- package/dist/auth/providers/ottoport.js +60 -0
- package/dist/auth/providers/ottoport.js.map +1 -0
- package/dist/auth/store.d.ts +22 -0
- package/dist/auth/store.js +138 -0
- package/dist/auth/store.js.map +1 -0
- package/dist/auth/types.d.ts +167 -0
- package/dist/auth/types.js +16 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/config.d.ts +197 -0
- package/dist/config.js +195 -0
- package/dist/config.js.map +1 -0
- package/dist/core/errors.d.ts +46 -0
- package/dist/core/errors.js +83 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/tokens.d.ts +12 -0
- package/dist/core/tokens.js +56 -0
- package/dist/core/tokens.js.map +1 -0
- package/dist/core/types.d.ts +91 -0
- package/dist/core/types.js +49 -0
- package/dist/core/types.js.map +1 -0
- package/dist/harness.d.ts +302 -0
- package/dist/harness.js +695 -0
- package/dist/harness.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/client.d.ts +55 -0
- package/dist/mcp/client.js +199 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/index.d.ts +13 -0
- package/dist/mcp/index.js +8 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/manager.d.ts +77 -0
- package/dist/mcp/manager.js +325 -0
- package/dist/mcp/manager.js.map +1 -0
- package/dist/mcp/oauth.d.ts +63 -0
- package/dist/mcp/oauth.js +302 -0
- package/dist/mcp/oauth.js.map +1 -0
- package/dist/mcp/protocol.d.ts +130 -0
- package/dist/mcp/protocol.js +14 -0
- package/dist/mcp/protocol.js.map +1 -0
- package/dist/mcp/registry.d.ts +52 -0
- package/dist/mcp/registry.js +100 -0
- package/dist/mcp/registry.js.map +1 -0
- package/dist/mcp/registry.json +293 -0
- package/dist/mcp/secrets.d.ts +20 -0
- package/dist/mcp/secrets.js +75 -0
- package/dist/mcp/secrets.js.map +1 -0
- package/dist/mcp/transport.d.ts +51 -0
- package/dist/mcp/transport.js +158 -0
- package/dist/mcp/transport.js.map +1 -0
- package/dist/paths.d.ts +37 -0
- package/dist/paths.js +63 -0
- package/dist/paths.js.map +1 -0
- package/dist/plugins.d.ts +55 -0
- package/dist/plugins.js +201 -0
- package/dist/plugins.js.map +1 -0
- package/dist/providers/builtin.d.ts +23 -0
- package/dist/providers/builtin.js +141 -0
- package/dist/providers/builtin.js.map +1 -0
- package/dist/providers/catalog.d.ts +129 -0
- package/dist/providers/catalog.js +254 -0
- package/dist/providers/catalog.js.map +1 -0
- package/dist/providers/catalog.json +472 -0
- package/dist/providers/discovery.d.ts +16 -0
- package/dist/providers/discovery.js +161 -0
- package/dist/providers/discovery.js.map +1 -0
- package/dist/providers/http.d.ts +24 -0
- package/dist/providers/http.js +139 -0
- package/dist/providers/http.js.map +1 -0
- package/dist/providers/index.d.ts +10 -0
- package/dist/providers/index.js +8 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/piai.d.ts +124 -0
- package/dist/providers/piai.js +551 -0
- package/dist/providers/piai.js.map +1 -0
- package/dist/providers/registry.d.ts +65 -0
- package/dist/providers/registry.js +195 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/providers/resilient.d.ts +46 -0
- package/dist/providers/resilient.js +117 -0
- package/dist/providers/resilient.js.map +1 -0
- package/dist/providers/types.d.ts +182 -0
- package/dist/providers/types.js +50 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/schedule/cron.d.ts +35 -0
- package/dist/schedule/cron.js +125 -0
- package/dist/schedule/cron.js.map +1 -0
- package/dist/schedule/scheduler.d.ts +87 -0
- package/dist/schedule/scheduler.js +237 -0
- package/dist/schedule/scheduler.js.map +1 -0
- package/dist/storage/kv.d.ts +56 -0
- package/dist/storage/kv.js +173 -0
- package/dist/storage/kv.js.map +1 -0
- package/dist/storage/migrate.d.ts +11 -0
- package/dist/storage/migrate.js +93 -0
- package/dist/storage/migrate.js.map +1 -0
- package/dist/storage/project.d.ts +9 -0
- package/dist/storage/project.js +57 -0
- package/dist/storage/project.js.map +1 -0
- package/dist/storage/records.d.ts +32 -0
- package/dist/storage/records.js +48 -0
- package/dist/storage/records.js.map +1 -0
- package/dist/sync/client.d.ts +79 -0
- package/dist/sync/client.js +118 -0
- package/dist/sync/client.js.map +1 -0
- package/dist/sync/mirror.d.ts +85 -0
- package/dist/sync/mirror.js +389 -0
- package/dist/sync/mirror.js.map +1 -0
- package/dist/sync/protocol.d.ts +67 -0
- package/dist/sync/protocol.js +16 -0
- package/dist/sync/protocol.js.map +1 -0
- package/dist/sync/settings.d.ts +56 -0
- package/dist/sync/settings.js +60 -0
- package/dist/sync/settings.js.map +1 -0
- package/dist/tools/builtin/artifact.d.ts +16 -0
- package/dist/tools/builtin/artifact.js +134 -0
- package/dist/tools/builtin/artifact.js.map +1 -0
- package/dist/tools/builtin/bash.d.ts +3 -0
- package/dist/tools/builtin/bash.js +142 -0
- package/dist/tools/builtin/bash.js.map +1 -0
- package/dist/tools/builtin/fs.d.ts +19 -0
- package/dist/tools/builtin/fs.js +280 -0
- package/dist/tools/builtin/fs.js.map +1 -0
- package/dist/tools/builtin/index.d.ts +22 -0
- package/dist/tools/builtin/index.js +42 -0
- package/dist/tools/builtin/index.js.map +1 -0
- package/dist/tools/builtin/mcp-resource.d.ts +3 -0
- package/dist/tools/builtin/mcp-resource.js +220 -0
- package/dist/tools/builtin/mcp-resource.js.map +1 -0
- package/dist/tools/builtin/memory.d.ts +10 -0
- package/dist/tools/builtin/memory.js +76 -0
- package/dist/tools/builtin/memory.js.map +1 -0
- package/dist/tools/builtin/recall.d.ts +13 -0
- package/dist/tools/builtin/recall.js +163 -0
- package/dist/tools/builtin/recall.js.map +1 -0
- package/dist/tools/builtin/search.d.ts +3 -0
- package/dist/tools/builtin/search.js +240 -0
- package/dist/tools/builtin/search.js.map +1 -0
- package/dist/tools/builtin/skill.d.ts +10 -0
- package/dist/tools/builtin/skill.js +46 -0
- package/dist/tools/builtin/skill.js.map +1 -0
- package/dist/tools/builtin/task.d.ts +9 -0
- package/dist/tools/builtin/task.js +68 -0
- package/dist/tools/builtin/task.js.map +1 -0
- package/dist/tools/builtin/todo.d.ts +20 -0
- package/dist/tools/builtin/todo.js +63 -0
- package/dist/tools/builtin/todo.js.map +1 -0
- package/dist/tools/define.d.ts +19 -0
- package/dist/tools/define.js +39 -0
- package/dist/tools/define.js.map +1 -0
- package/dist/tools/index.d.ts +7 -0
- package/dist/tools/index.js +5 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/registry.d.ts +39 -0
- package/dist/tools/registry.js +101 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/types.d.ts +112 -0
- package/dist/tools/types.js +22 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/util/id.d.ts +9 -0
- package/dist/util/id.js +19 -0
- package/dist/util/id.js.map +1 -0
- package/dist/util/json.d.ts +25 -0
- package/dist/util/json.js +100 -0
- package/dist/util/json.js.map +1 -0
- package/dist/util/jwt.d.ts +74 -0
- package/dist/util/jwt.js +177 -0
- package/dist/util/jwt.js.map +1 -0
- package/dist/util/logger.d.ts +13 -0
- package/dist/util/logger.js +59 -0
- package/dist/util/logger.js.map +1 -0
- package/dist/util/queue.d.ts +26 -0
- package/dist/util/queue.js +106 -0
- package/dist/util/queue.js.map +1 -0
- package/dist/util/schema.d.ts +17 -0
- package/dist/util/schema.js +98 -0
- package/dist/util/schema.js.map +1 -0
- package/dist/util/shell.d.ts +20 -0
- package/dist/util/shell.js +24 -0
- package/dist/util/shell.js.map +1 -0
- package/official.json +4 -0
- package/package.json +38 -0
- package/skills/artifact-authoring/SKILL.md +81 -0
- package/skills/canvas/SKILL.md +85 -0
- package/skills/skill-authoring/SKILL.md +85 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { userPath } from "../paths.js";
|
|
2
|
+
import { readFile, readdir, stat } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { createLogger } from "../util/logger.js";
|
|
6
|
+
const SKILL_FILE = "SKILL.md";
|
|
7
|
+
/**
|
|
8
|
+
* Skills that ship with the harness.
|
|
9
|
+
*
|
|
10
|
+
* Three, and the bar for a fourth is high: a skill's *description* sits in the
|
|
11
|
+
* system prompt of every request forever, so anything a model already knows
|
|
12
|
+
* from pretraining — reviewing a diff, debugging, writing tests — is a tax
|
|
13
|
+
* with no return. What is here is only what cannot be guessed: the rules this
|
|
14
|
+
* harness imposes on artifacts, the artboard format a canvas is written in,
|
|
15
|
+
* and the file format skills themselves use.
|
|
16
|
+
*
|
|
17
|
+
* `canvas` earned its own entry rather than a section inside
|
|
18
|
+
* artifact-authoring because it is not advice about a mime — it is a format
|
|
19
|
+
* the model has no way to derive (which element, which attributes, who writes
|
|
20
|
+
* the editor) plus a set of consequences that only exist for canvases (a
|
|
21
|
+
* person's edits become revisions, and a revision of yours can replace them).
|
|
22
|
+
*
|
|
23
|
+
* The path is the same from `src/agent/` and from `dist/agent/`, which is why
|
|
24
|
+
* these live beside the package rather than inside src — no build step has to
|
|
25
|
+
* remember to copy them, and forgetting one would ship an install whose
|
|
26
|
+
* built-in skills silently do not exist.
|
|
27
|
+
*/
|
|
28
|
+
function builtinSkillsDir() {
|
|
29
|
+
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "skills");
|
|
30
|
+
}
|
|
31
|
+
/** Enough for the model to know what is there without dumping a tree. */
|
|
32
|
+
const MAX_LISTED_FILES = 20;
|
|
33
|
+
export class SkillRegistry {
|
|
34
|
+
skills = new Map();
|
|
35
|
+
logger;
|
|
36
|
+
constructor(logger) {
|
|
37
|
+
this.logger = (logger ?? createLogger("skills")).child("registry");
|
|
38
|
+
}
|
|
39
|
+
register(skill) {
|
|
40
|
+
this.skills.set(skill.name, skill);
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
get(name) {
|
|
44
|
+
return this.skills.get(name);
|
|
45
|
+
}
|
|
46
|
+
list() {
|
|
47
|
+
return [...this.skills.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
48
|
+
}
|
|
49
|
+
get size() {
|
|
50
|
+
return this.skills.size;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Discover skills from the conventional locations. Project skills override
|
|
54
|
+
* user skills of the same name, so a repo can pin its own version.
|
|
55
|
+
*/
|
|
56
|
+
async discover(cwd, extraDirs = []) {
|
|
57
|
+
const roots = [
|
|
58
|
+
// First, so everything else can override one by name. A user who
|
|
59
|
+
// disagrees with a built-in replaces it rather than being stuck with it.
|
|
60
|
+
{ dir: builtinSkillsDir(), source: "builtin" },
|
|
61
|
+
{ dir: userPath("skills"), source: "user" },
|
|
62
|
+
{ dir: path.join(cwd, ".oharness", "skills"), source: "project" },
|
|
63
|
+
...extraDirs.map((dir) => ({ dir: path.resolve(cwd, dir), source: "config" })),
|
|
64
|
+
];
|
|
65
|
+
for (const root of roots) {
|
|
66
|
+
for (const skill of await scanDirectory(root.dir, root.source, this.logger)) {
|
|
67
|
+
this.register(skill);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The block injected into the system prompt: names and descriptions only.
|
|
73
|
+
* Returns undefined when there is nothing to advertise, so the prompt does
|
|
74
|
+
* not carry an empty section.
|
|
75
|
+
*/
|
|
76
|
+
promptSection() {
|
|
77
|
+
const skills = this.list();
|
|
78
|
+
if (skills.length === 0)
|
|
79
|
+
return undefined;
|
|
80
|
+
const lines = [
|
|
81
|
+
"## Available skills",
|
|
82
|
+
"",
|
|
83
|
+
"Skills are packaged instructions for specific kinds of work. When a task matches",
|
|
84
|
+
"one of the descriptions below, call the `skill` tool with its name to load the full",
|
|
85
|
+
"instructions before you start. Do not guess at a skill's contents from its description.",
|
|
86
|
+
"",
|
|
87
|
+
];
|
|
88
|
+
for (const skill of skills) {
|
|
89
|
+
lines.push(`- **${skill.name}** — ${skill.description}`);
|
|
90
|
+
}
|
|
91
|
+
return lines.join("\n");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Render a loaded skill for the model.
|
|
96
|
+
*
|
|
97
|
+
* The base directory and file listing matter: skills routinely say "run
|
|
98
|
+
* `scripts/check.py`", and without knowing where they live the model cannot
|
|
99
|
+
* act on that.
|
|
100
|
+
*/
|
|
101
|
+
export async function renderSkill(skill) {
|
|
102
|
+
const files = await listSkillFiles(skill.directory);
|
|
103
|
+
const parts = [
|
|
104
|
+
`<skill name="${skill.name}">`,
|
|
105
|
+
skill.body.trim(),
|
|
106
|
+
"",
|
|
107
|
+
`Base directory: ${skill.directory}`,
|
|
108
|
+
"Relative paths in this skill (scripts/, reference/, …) resolve against that directory.",
|
|
109
|
+
];
|
|
110
|
+
if (files.length > 0) {
|
|
111
|
+
parts.push("", "Files in this skill:");
|
|
112
|
+
for (const file of files.slice(0, MAX_LISTED_FILES))
|
|
113
|
+
parts.push(` ${file}`);
|
|
114
|
+
if (files.length > MAX_LISTED_FILES) {
|
|
115
|
+
parts.push(` … ${files.length - MAX_LISTED_FILES} more`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
parts.push("</skill>");
|
|
119
|
+
return parts.join("\n");
|
|
120
|
+
}
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
async function scanDirectory(root, source, logger) {
|
|
123
|
+
let entries;
|
|
124
|
+
try {
|
|
125
|
+
entries = await readdir(root, { withFileTypes: true });
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return []; // The directory simply does not exist.
|
|
129
|
+
}
|
|
130
|
+
const out = [];
|
|
131
|
+
for (const entry of entries) {
|
|
132
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
133
|
+
continue;
|
|
134
|
+
const directory = path.join(root, entry.name);
|
|
135
|
+
const file = path.join(directory, SKILL_FILE);
|
|
136
|
+
let raw;
|
|
137
|
+
try {
|
|
138
|
+
const info = await stat(file);
|
|
139
|
+
if (!info.isFile())
|
|
140
|
+
continue;
|
|
141
|
+
raw = await readFile(file, "utf8");
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
continue; // A directory without SKILL.md is not a skill.
|
|
145
|
+
}
|
|
146
|
+
const parsed = parseSkillFile(raw, entry.name);
|
|
147
|
+
if (!parsed) {
|
|
148
|
+
// A skill with no description can never be selected, since the
|
|
149
|
+
// description is the only thing the model sees.
|
|
150
|
+
logger.warn("skill has no description; skipping", { directory });
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
out.push({ ...parsed, directory, source });
|
|
154
|
+
}
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
/** Parse YAML-ish frontmatter. Only `name` and `description` are meaningful. */
|
|
158
|
+
export function parseSkillFile(raw, fallbackName) {
|
|
159
|
+
const { frontmatter, body } = splitFrontmatter(raw);
|
|
160
|
+
const name = frontmatter["name"]?.trim() || fallbackName;
|
|
161
|
+
const description = frontmatter["description"]?.trim();
|
|
162
|
+
if (!description)
|
|
163
|
+
return undefined;
|
|
164
|
+
return { name, description, body };
|
|
165
|
+
}
|
|
166
|
+
export function splitFrontmatter(raw) {
|
|
167
|
+
const normalized = raw.replace(/^/, "");
|
|
168
|
+
if (!normalized.startsWith("---"))
|
|
169
|
+
return { frontmatter: {}, body: normalized };
|
|
170
|
+
const end = normalized.indexOf("\n---", 3);
|
|
171
|
+
if (end === -1)
|
|
172
|
+
return { frontmatter: {}, body: normalized };
|
|
173
|
+
const block = normalized.slice(normalized.indexOf("\n") + 1, end);
|
|
174
|
+
const body = normalized.slice(normalized.indexOf("\n", end + 1) + 1);
|
|
175
|
+
const frontmatter = {};
|
|
176
|
+
let currentKey;
|
|
177
|
+
for (const line of block.split("\n")) {
|
|
178
|
+
const match = /^([A-Za-z0-9_-]+):\s*(.*)$/.exec(line);
|
|
179
|
+
if (match) {
|
|
180
|
+
currentKey = match[1];
|
|
181
|
+
frontmatter[currentKey] = stripQuotes(match[2].trim());
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
// Continuation of a folded value, e.g. a description wrapped over lines.
|
|
185
|
+
if (currentKey && /^\s+\S/.test(line)) {
|
|
186
|
+
frontmatter[currentKey] = `${frontmatter[currentKey]} ${line.trim()}`.trim();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return { frontmatter, body };
|
|
190
|
+
}
|
|
191
|
+
function stripQuotes(value) {
|
|
192
|
+
if (value.length >= 2 && (value.startsWith('"') || value.startsWith("'"))) {
|
|
193
|
+
const quote = value[0];
|
|
194
|
+
if (value.endsWith(quote))
|
|
195
|
+
return value.slice(1, -1);
|
|
196
|
+
}
|
|
197
|
+
return value;
|
|
198
|
+
}
|
|
199
|
+
/** Relative paths of the files a skill ships, excluding SKILL.md itself. */
|
|
200
|
+
async function listSkillFiles(directory, prefix = "") {
|
|
201
|
+
let entries;
|
|
202
|
+
try {
|
|
203
|
+
entries = await readdir(directory, { withFileTypes: true });
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
return [];
|
|
207
|
+
}
|
|
208
|
+
const out = [];
|
|
209
|
+
for (const entry of entries) {
|
|
210
|
+
if (entry.name.startsWith("."))
|
|
211
|
+
continue;
|
|
212
|
+
const relative = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
213
|
+
if (entry.isDirectory()) {
|
|
214
|
+
// One level deep is enough to show structure without walking a tree.
|
|
215
|
+
if (prefix === "")
|
|
216
|
+
out.push(...(await listSkillFiles(path.join(directory, entry.name), relative)));
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (relative === SKILL_FILE)
|
|
220
|
+
continue;
|
|
221
|
+
out.push(relative);
|
|
222
|
+
}
|
|
223
|
+
return out.sort();
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/agent/skills.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import type { Message, Usage } from "../core/types.ts";
|
|
2
|
+
import type { AuditEntry } from "./permissions.ts";
|
|
3
|
+
import type { StoredArtifactRevision } from "../artifacts/store.ts";
|
|
4
|
+
import type { MessageRecord } from "../storage/records.ts";
|
|
5
|
+
export interface SessionMeta {
|
|
6
|
+
id: string;
|
|
7
|
+
createdAt: number;
|
|
8
|
+
updatedAt: number;
|
|
9
|
+
cwd: string;
|
|
10
|
+
model: string;
|
|
11
|
+
title?: string;
|
|
12
|
+
turns: number;
|
|
13
|
+
usage: Usage;
|
|
14
|
+
costUsd: number;
|
|
15
|
+
/** Session this one was branched from, if any. */
|
|
16
|
+
forkedFrom?: string;
|
|
17
|
+
/** How many messages the parent had at the branch point. */
|
|
18
|
+
forkedAt?: number;
|
|
19
|
+
/**
|
|
20
|
+
* The project the session belongs to — a repository, not a path. Sessions
|
|
21
|
+
* recorded before this existed are assigned one when they are read.
|
|
22
|
+
*/
|
|
23
|
+
projectId?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Bumped when the transcript is rewritten rather than appended to, which is
|
|
26
|
+
* what compaction does. A merge takes the highest generation whole: replaying
|
|
27
|
+
* appends from before a rewrite would resurrect what compaction dropped.
|
|
28
|
+
*/
|
|
29
|
+
gen?: number;
|
|
30
|
+
/** Highest sequence number handed out in this session's log. */
|
|
31
|
+
seq?: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Cap on how much of an archive a single `recall` loads. Generous enough that
|
|
35
|
+
* ordinary sessions read in full, small enough that a runaway one cannot
|
|
36
|
+
* exhaust memory.
|
|
37
|
+
*/
|
|
38
|
+
export declare const MAX_ARCHIVE_READ_BYTES: number;
|
|
39
|
+
/**
|
|
40
|
+
* Cap on how much of a permission trail one read loads. Smaller than the
|
|
41
|
+
* archive's: these are short lines, and a page showing the trail wants the
|
|
42
|
+
* recent end of it, not a session's entire history of tool calls.
|
|
43
|
+
*/
|
|
44
|
+
export declare const MAX_DECISION_READ_BYTES: number;
|
|
45
|
+
export interface SessionRecord {
|
|
46
|
+
meta: SessionMeta;
|
|
47
|
+
messages: Message[];
|
|
48
|
+
}
|
|
49
|
+
/** One archived message, as written when compaction dropped it. */
|
|
50
|
+
export interface ArchivedMessage {
|
|
51
|
+
/** Position in the original transcript, stable across compactions. */
|
|
52
|
+
seq: number;
|
|
53
|
+
/** When the compaction that archived it ran. */
|
|
54
|
+
archivedAt: number;
|
|
55
|
+
message: Message;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Where sessions live.
|
|
59
|
+
*
|
|
60
|
+
* Keyed files under one root rather than a directory per session:
|
|
61
|
+
*
|
|
62
|
+
* storage/migration how many layout changes have run
|
|
63
|
+
* storage/device.json this machine's id, for merges
|
|
64
|
+
* storage/project/<projectId>.json a repository, by its root commit
|
|
65
|
+
* storage/session/<projectId>/<id>.json session metadata
|
|
66
|
+
* storage/message/<id>.jsonl the transcript, one record a line
|
|
67
|
+
* storage/archive/<id>.jsonl what compaction dropped
|
|
68
|
+
* storage/decision/<id>.jsonl every permission decision
|
|
69
|
+
*
|
|
70
|
+
* Grouping sessions under a project makes listing one project's history a
|
|
71
|
+
* single directory read instead of opening every session on the machine, and
|
|
72
|
+
* gives sync something to scope to. Messages stay in a flat log per session
|
|
73
|
+
* because appending is the hot path and appending must not rewrite.
|
|
74
|
+
*/
|
|
75
|
+
/**
|
|
76
|
+
* A local write, as reported to whoever is watching.
|
|
77
|
+
*
|
|
78
|
+
* `meta` is carried on a deletion too, and captured before the files go: a
|
|
79
|
+
* tombstone has to travel or the other device restores what was deleted, and
|
|
80
|
+
* after `remove` there is nothing left to describe.
|
|
81
|
+
*/
|
|
82
|
+
export interface StoreChange {
|
|
83
|
+
kind: "write" | "delete";
|
|
84
|
+
id: string;
|
|
85
|
+
projectId: string;
|
|
86
|
+
meta: SessionMeta;
|
|
87
|
+
}
|
|
88
|
+
export declare class SessionStore {
|
|
89
|
+
private readonly kv;
|
|
90
|
+
private readonly legacyRoot;
|
|
91
|
+
private ready;
|
|
92
|
+
private deviceId;
|
|
93
|
+
/** Which project a session was found under, so a second read is a direct hit. */
|
|
94
|
+
private readonly located;
|
|
95
|
+
private readonly watchers;
|
|
96
|
+
constructor(root?: string);
|
|
97
|
+
get root(): string;
|
|
98
|
+
/**
|
|
99
|
+
* Watch local writes. Returns the unsubscribe.
|
|
100
|
+
*
|
|
101
|
+
* Sync needs to know when to push, and the alternatives are worse: polling
|
|
102
|
+
* the directory turns an event into a delay, and calling a client from the
|
|
103
|
+
* write paths would put a network dependency inside the only thing here that
|
|
104
|
+
* must not be able to fail. A watcher is told what changed and decides for
|
|
105
|
+
* itself, so nothing above this file is reachable from below it.
|
|
106
|
+
*
|
|
107
|
+
* Listeners are called after the write has landed, and one that throws is
|
|
108
|
+
* ignored — a mirror falling over is not a reason to lose a transcript.
|
|
109
|
+
*/
|
|
110
|
+
watch(listener: (change: StoreChange) => void): () => void;
|
|
111
|
+
private announce;
|
|
112
|
+
/** A session's transcript as records, ordered and deduplicated. */
|
|
113
|
+
recordsOf(id: string): Promise<MessageRecord[]>;
|
|
114
|
+
/** A session's metadata, without being told which project it is under. */
|
|
115
|
+
readMeta(id: string): Promise<SessionMeta | undefined>;
|
|
116
|
+
/**
|
|
117
|
+
* Take records that were written somewhere else.
|
|
118
|
+
*
|
|
119
|
+
* Distinct from `appendMessage`, which mints an id, a seq and this device —
|
|
120
|
+
* exactly the three fields that must survive the trip for a merge to work.
|
|
121
|
+
* The log is append-only and `orderRecords` deduplicates on read, so writing
|
|
122
|
+
* a record that is already held costs a line and changes nothing, which is
|
|
123
|
+
* what makes a retried pull safe.
|
|
124
|
+
*/
|
|
125
|
+
mergeRecords(id: string, incoming: MessageRecord[]): Promise<number>;
|
|
126
|
+
/**
|
|
127
|
+
* Take metadata written somewhere else, if it is newer.
|
|
128
|
+
*
|
|
129
|
+
* Generation first, then wall clock. A generation is bumped by a rewrite, so
|
|
130
|
+
* a device that compacted a session wins over one that merely kept appending
|
|
131
|
+
* to it — no amount of clock skew should let a stale full transcript
|
|
132
|
+
* overwrite a summary that replaced it.
|
|
133
|
+
*/
|
|
134
|
+
mergeMeta(incoming: SessionMeta): Promise<boolean>;
|
|
135
|
+
/** Layout changes run once, on first use, before anything reads. */
|
|
136
|
+
private prepare;
|
|
137
|
+
/**
|
|
138
|
+
* This machine's id.
|
|
139
|
+
*
|
|
140
|
+
* Two devices appending to one session need to be distinguishable, and the
|
|
141
|
+
* tiebreak has to survive restarts — so it is stored rather than generated
|
|
142
|
+
* per process.
|
|
143
|
+
*/
|
|
144
|
+
private device;
|
|
145
|
+
private projectOf;
|
|
146
|
+
/** Find a session's metadata without being told which project it is in. */
|
|
147
|
+
private findMeta;
|
|
148
|
+
create(meta: SessionMeta): Promise<void>;
|
|
149
|
+
writeMeta(meta: SessionMeta): Promise<void>;
|
|
150
|
+
/**
|
|
151
|
+
* The write itself, without telling anyone.
|
|
152
|
+
*
|
|
153
|
+
* Applying what a pull returned goes through here: announcing it would have
|
|
154
|
+
* the mirror push back what it had just received, and while the server
|
|
155
|
+
* deduplicates that, a loop that is merely harmless is still a loop.
|
|
156
|
+
*/
|
|
157
|
+
private putMeta;
|
|
158
|
+
private touchProject;
|
|
159
|
+
appendMessage(id: string, message: Message): Promise<void>;
|
|
160
|
+
/**
|
|
161
|
+
* Record one artifact revision on disk.
|
|
162
|
+
*
|
|
163
|
+
* A log rather than a document, for the same reason the transcript is one:
|
|
164
|
+
* a revision is a thing that happened, appending never rewrites what is
|
|
165
|
+
* already there, and a crashed process still leaves everything written
|
|
166
|
+
* before it. It also means the version history comes free — the file is the
|
|
167
|
+
* history, in order.
|
|
168
|
+
*
|
|
169
|
+
* Keyed by session alone, like `message` and `archive`, because an artifact
|
|
170
|
+
* belongs to the conversation that made it and is found the same way.
|
|
171
|
+
*
|
|
172
|
+
* This is what makes an artifact something other than a by-product of the
|
|
173
|
+
* transcript. A model's write was always recoverable, because the content is
|
|
174
|
+
* the argument of a tool call and tool calls are in the transcript. A
|
|
175
|
+
* revision a person made in the canvas is not a tool call and was therefore
|
|
176
|
+
* nowhere: a restart brought the model's version back as the newest one and
|
|
177
|
+
* said nothing about what it had replaced.
|
|
178
|
+
*/
|
|
179
|
+
appendArtifactRevision(id: string, revision: StoredArtifactRevision): Promise<void>;
|
|
180
|
+
/**
|
|
181
|
+
* Take artifact revisions written on another device.
|
|
182
|
+
*
|
|
183
|
+
* The counterpart to `mergeRecords`, and deliberately its shape: dedupe on
|
|
184
|
+
* the record id, append what is new, and let the reader fold the file
|
|
185
|
+
* together. A revision already held costs a line and changes nothing, which
|
|
186
|
+
* is what makes a retried pull safe.
|
|
187
|
+
*/
|
|
188
|
+
mergeArtifactRevisions(id: string, incoming: StoredArtifactRevision[]): Promise<number>;
|
|
189
|
+
/**
|
|
190
|
+
* Sessions that have written an artifact, whether or not they are loaded.
|
|
191
|
+
*
|
|
192
|
+
* A directory read, not a transcript read. Artifacts belong to sessions and
|
|
193
|
+
* a session's store is memory, so "everything on this machine" used to mean
|
|
194
|
+
* "everything currently open" — which is a listing that changes depending on
|
|
195
|
+
* which tabs happen to be open, and that is not a listing anybody can trust.
|
|
196
|
+
* The logs are the durable record; this says which ones exist so a caller
|
|
197
|
+
* can fold the ones it wants without opening a session to ask.
|
|
198
|
+
*/
|
|
199
|
+
artifactSessions(): Promise<string[]>;
|
|
200
|
+
/**
|
|
201
|
+
* Every artifact revision recorded for a session, oldest first.
|
|
202
|
+
*
|
|
203
|
+
* `maxBytes` reads the tail, for a caller that is building a listing rather
|
|
204
|
+
* than restoring a session: the newest lines are the ones a listing shows,
|
|
205
|
+
* and a log with a megabyte of superseded revisions in it should not cost a
|
|
206
|
+
* megabyte to summarise.
|
|
207
|
+
*/
|
|
208
|
+
artifactRevisions(id: string, maxBytes?: number): Promise<StoredArtifactRevision[]>;
|
|
209
|
+
/**
|
|
210
|
+
* Record one permission decision, with the session it happened in.
|
|
211
|
+
*
|
|
212
|
+
* Beside the transcript rather than in a file of its own somewhere else,
|
|
213
|
+
* because a decision is a thing that happened *in this conversation*: kept
|
|
214
|
+
* here it is found the way everything else about the session is found,
|
|
215
|
+
* deleted when the session is deleted, and readable next to the tool call it
|
|
216
|
+
* was about. A separate audit file is a second truth that has to be
|
|
217
|
+
* correlated by timestamp, and correlating by timestamp is how two records
|
|
218
|
+
* of the same event end up disagreeing.
|
|
219
|
+
*
|
|
220
|
+
* No seq and no device, unlike messages and artifact revisions: nothing
|
|
221
|
+
* merges this log. It stays on the machine that wrote it — see `SessionMirror`,
|
|
222
|
+
* which pushes messages and artifacts and deliberately not this.
|
|
223
|
+
*/
|
|
224
|
+
appendDecision(id: string, entry: AuditEntry): Promise<void>;
|
|
225
|
+
/**
|
|
226
|
+
* A session's permission trail, oldest first.
|
|
227
|
+
*
|
|
228
|
+
* Bounded like the archive read: the trail grows for the life of a session —
|
|
229
|
+
* every tool call adds a line — and a long unattended run should not be able
|
|
230
|
+
* to make reading it the most expensive thing the harness does.
|
|
231
|
+
*/
|
|
232
|
+
decisions(id: string, maxBytes?: number): Promise<AuditEntry[]>;
|
|
233
|
+
/**
|
|
234
|
+
* Append messages to the archive before they leave the live transcript.
|
|
235
|
+
*
|
|
236
|
+
* Compaction replaces history with a lossy summary; without this the
|
|
237
|
+
* originals would be unrecoverable — not just to the agent, but to anyone
|
|
238
|
+
* debugging the session afterwards. The archive is append-only and is never
|
|
239
|
+
* rewritten.
|
|
240
|
+
*/
|
|
241
|
+
archiveMessages(id: string, entries: Array<{
|
|
242
|
+
seq: number;
|
|
243
|
+
message: Message;
|
|
244
|
+
}>): Promise<void>;
|
|
245
|
+
/**
|
|
246
|
+
* Read the archive, newest-biased and bounded.
|
|
247
|
+
*
|
|
248
|
+
* The archive is append-only and grows for the life of a session — every
|
|
249
|
+
* compaction and every oversized tool result adds to it. Reading all of it
|
|
250
|
+
* into memory to answer one `recall` would eventually be the most expensive
|
|
251
|
+
* thing the harness does, so only the tail is loaded past a threshold.
|
|
252
|
+
*/
|
|
253
|
+
readArchive(id: string, maxBytes?: number): Promise<ArchivedMessage[]>;
|
|
254
|
+
/**
|
|
255
|
+
* Branch a session into a new one.
|
|
256
|
+
*
|
|
257
|
+
* The parent is never touched — that is the whole point. Exploring a
|
|
258
|
+
* different approach should not cost you the transcript that got you here,
|
|
259
|
+
* and mutating in place makes "go back and try the other thing" impossible.
|
|
260
|
+
*/
|
|
261
|
+
fork(sourceId: string, opts?: {
|
|
262
|
+
keepMessages?: number;
|
|
263
|
+
title?: string;
|
|
264
|
+
newId?: string;
|
|
265
|
+
}): Promise<{
|
|
266
|
+
id: string;
|
|
267
|
+
meta: SessionMeta;
|
|
268
|
+
messages: Message[];
|
|
269
|
+
} | undefined>;
|
|
270
|
+
/** Permanently remove a session and everything it recorded. */
|
|
271
|
+
delete(id: string): Promise<boolean>;
|
|
272
|
+
/**
|
|
273
|
+
* Every session recorded in one tree, deleted.
|
|
274
|
+
*
|
|
275
|
+
* By working directory rather than by project id, because that is what the
|
|
276
|
+
* sidebar groups by and therefore what somebody is looking at when they ask
|
|
277
|
+
* for this. Deleting one at a time is the same work either way — there is no
|
|
278
|
+
* bulk primitive underneath — but each one still announces its own tombstone,
|
|
279
|
+
* so a device syncing with this one loses exactly the same set.
|
|
280
|
+
*
|
|
281
|
+
* Returns the ids that went, so a caller holding open tabs knows which of
|
|
282
|
+
* them are now pointing at nothing.
|
|
283
|
+
*/
|
|
284
|
+
deleteIn(cwd: string): Promise<string[]>;
|
|
285
|
+
rename(id: string, title: string): Promise<boolean>;
|
|
286
|
+
/** Sessions branched from a given one. */
|
|
287
|
+
children(id: string): Promise<SessionMeta[]>;
|
|
288
|
+
/**
|
|
289
|
+
* Delete old sessions.
|
|
290
|
+
*
|
|
291
|
+
* Nothing reclaimed these before, so a machine used daily accumulates every
|
|
292
|
+
* transcript and archive it ever produced. Returns the ids removed.
|
|
293
|
+
*/
|
|
294
|
+
prune(opts?: {
|
|
295
|
+
maxAgeDays?: number;
|
|
296
|
+
keep?: number;
|
|
297
|
+
}): Promise<string[]>;
|
|
298
|
+
/** Rewrite the transcript wholesale — used after compaction. */
|
|
299
|
+
replaceMessages(id: string, messages: Message[]): Promise<void>;
|
|
300
|
+
private records;
|
|
301
|
+
load(id: string): Promise<SessionRecord | undefined>;
|
|
302
|
+
list(limit?: number, opts?: {
|
|
303
|
+
projectId?: string;
|
|
304
|
+
}): Promise<SessionMeta[]>;
|
|
305
|
+
private backfillTitle;
|
|
306
|
+
/** Most recently updated session for a directory, for `--continue`. */
|
|
307
|
+
mostRecent(cwd?: string): Promise<SessionMeta | undefined>;
|
|
308
|
+
}
|