@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,231 @@
|
|
|
1
|
+
import { dropDanglingTail } from "./compaction.js";
|
|
2
|
+
import { titleFromMessage } from "./titles.js";
|
|
3
|
+
import { addUsage, emptyUsage, messageText } from "../core/types.js";
|
|
4
|
+
import { getArtifactStore, rehydrateArtifacts, restoreArtifacts } from "../artifacts/store.js";
|
|
5
|
+
import { estimateCost } from "../providers/types.js";
|
|
6
|
+
import { sessionId as newSessionId } from "../util/id.js";
|
|
7
|
+
import { PermissionEngine } from "./permissions.js";
|
|
8
|
+
import { SessionStore } from "./store.js";
|
|
9
|
+
/**
|
|
10
|
+
* Conversation state for one agent run: history, current model, permissions,
|
|
11
|
+
* accounting, and the scratch state tools share.
|
|
12
|
+
*
|
|
13
|
+
* History is stored provider-neutrally, which is what makes `setModel` a
|
|
14
|
+
* one-line operation instead of a migration.
|
|
15
|
+
*/
|
|
16
|
+
export class Session {
|
|
17
|
+
/** How many messages compaction has already archived, for stable seq numbers. */
|
|
18
|
+
archivedCount = 0;
|
|
19
|
+
id;
|
|
20
|
+
cwd;
|
|
21
|
+
providers;
|
|
22
|
+
permissions;
|
|
23
|
+
/** Shared scratch space handed to tools via `ToolHost`. */
|
|
24
|
+
state = new Map();
|
|
25
|
+
messages;
|
|
26
|
+
title;
|
|
27
|
+
usage = emptyUsage();
|
|
28
|
+
costUsd = 0;
|
|
29
|
+
/** Session this one was branched from, surfaced in meta and `/sessions`. */
|
|
30
|
+
forkedFrom;
|
|
31
|
+
turns = 0;
|
|
32
|
+
/**
|
|
33
|
+
* Messages discarded as an incomplete tail when this session was loaded.
|
|
34
|
+
*
|
|
35
|
+
* Repairing the transcript silently would leave the user wondering where the
|
|
36
|
+
* last turn went, so the count is kept for the frontend to report.
|
|
37
|
+
*/
|
|
38
|
+
droppedOnLoad;
|
|
39
|
+
spec;
|
|
40
|
+
store;
|
|
41
|
+
createdAt;
|
|
42
|
+
constructor(opts) {
|
|
43
|
+
this.id = opts.id ?? newSessionId();
|
|
44
|
+
this.cwd = opts.cwd ?? process.cwd();
|
|
45
|
+
this.providers = opts.providers;
|
|
46
|
+
this.spec = opts.providers.resolve(opts.model);
|
|
47
|
+
// Every path that hydrates history runs through here — resume, fork, and
|
|
48
|
+
// whatever an embedder hands in — so this is the one place the invariant
|
|
49
|
+
// "history never ends on an unanswered tool call" can be made total.
|
|
50
|
+
const history = opts.messages ?? [];
|
|
51
|
+
this.messages = dropDanglingTail(history);
|
|
52
|
+
this.droppedOnLoad = history.length - this.messages.length;
|
|
53
|
+
// Artifacts come back from whichever source knows the most, and only one
|
|
54
|
+
// of them: the log carries the version numbers and the authorship that
|
|
55
|
+
// actually happened, while replaying the transcript can only count writes
|
|
56
|
+
// and cannot see a revision a person made by hand. Running both would
|
|
57
|
+
// renumber the history and duplicate every revision.
|
|
58
|
+
//
|
|
59
|
+
// The replay stays as the fallback because every session recorded before
|
|
60
|
+
// artifacts were written down has no log, and those still deserve to come
|
|
61
|
+
// back with the things their messages made.
|
|
62
|
+
let seedArtifactLog = false;
|
|
63
|
+
if (opts.artifactRevisions && opts.artifactRevisions.length > 0) {
|
|
64
|
+
restoreArtifacts(getArtifactStore(this.state), opts.artifactRevisions);
|
|
65
|
+
}
|
|
66
|
+
else if (this.messages.length > 0) {
|
|
67
|
+
// Nothing written down, so this session predates the log. Whatever the
|
|
68
|
+
// replay finds is seeded into it below, or the first revision written
|
|
69
|
+
// from here would become the whole of a history that starts at v1.
|
|
70
|
+
seedArtifactLog = rehydrateArtifacts(getArtifactStore(this.state), this.messages) > 0;
|
|
71
|
+
}
|
|
72
|
+
this.title = opts.title;
|
|
73
|
+
this.permissions = new PermissionEngine(opts.permissionMode ?? "default", opts.permissionRules ?? {});
|
|
74
|
+
this.store = opts.store === false ? undefined : (opts.store ?? new SessionStore());
|
|
75
|
+
this.createdAt = Date.now();
|
|
76
|
+
// Every revision, from either writer, goes to disk. Fire-and-forget on
|
|
77
|
+
// purpose: an artifact is a scratch surface, and making the model's turn
|
|
78
|
+
// wait on a file write — or fail with it — would be paying for durability
|
|
79
|
+
// in the wrong currency. A revision that does not land is one the session
|
|
80
|
+
// still holds and can be written again by the next one.
|
|
81
|
+
if (this.store) {
|
|
82
|
+
const store = this.store;
|
|
83
|
+
const id = this.id;
|
|
84
|
+
const artifacts = getArtifactStore(this.state);
|
|
85
|
+
artifacts.onRevision = (revision) => {
|
|
86
|
+
void store.appendArtifactRevision(id, revision).catch(() => { });
|
|
87
|
+
};
|
|
88
|
+
// One-time migration for a session whose artifacts came from the
|
|
89
|
+
// transcript. Ordinary appends, so a crash mid-way leaves a shorter log
|
|
90
|
+
// rather than a corrupt one, and the next resume replays and seeds again.
|
|
91
|
+
if (seedArtifactLog) {
|
|
92
|
+
for (const revision of artifacts.export()) {
|
|
93
|
+
void store.appendArtifactRevision(id, revision).catch(() => { });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/** Restore a persisted session, keeping its id and transcript. */
|
|
99
|
+
static async resume(id, opts) {
|
|
100
|
+
const store = opts.store ?? new SessionStore();
|
|
101
|
+
const record = await store.load(id);
|
|
102
|
+
if (!record)
|
|
103
|
+
return undefined;
|
|
104
|
+
const artifactRevisions = await store.artifactRevisions(id);
|
|
105
|
+
const session = new Session({
|
|
106
|
+
...opts,
|
|
107
|
+
artifactRevisions,
|
|
108
|
+
id: record.meta.id,
|
|
109
|
+
cwd: record.meta.cwd,
|
|
110
|
+
// Prefer the model the session was using, unless the caller overrode it.
|
|
111
|
+
model: opts.model || record.meta.model,
|
|
112
|
+
messages: record.messages,
|
|
113
|
+
title: record.meta.title,
|
|
114
|
+
store,
|
|
115
|
+
});
|
|
116
|
+
session.usage = record.meta.usage;
|
|
117
|
+
session.costUsd = record.meta.costUsd;
|
|
118
|
+
session.turns = record.meta.turns;
|
|
119
|
+
session.forkedFrom = record.meta.forkedFrom;
|
|
120
|
+
return session;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Branch a stored session into a new one.
|
|
124
|
+
*
|
|
125
|
+
* `keepMessages` rewinds the branch to an earlier point; omit it to branch
|
|
126
|
+
* from where the parent stands. The parent is left untouched either way.
|
|
127
|
+
*/
|
|
128
|
+
static async fork(sourceId, opts) {
|
|
129
|
+
const store = opts.store ?? new SessionStore();
|
|
130
|
+
const forked = await store.fork(sourceId, {
|
|
131
|
+
...(opts.keepMessages !== undefined ? { keepMessages: opts.keepMessages } : {}),
|
|
132
|
+
...(opts.title !== undefined ? { title: opts.title } : {}),
|
|
133
|
+
});
|
|
134
|
+
if (!forked)
|
|
135
|
+
return undefined;
|
|
136
|
+
const session = new Session({
|
|
137
|
+
...opts,
|
|
138
|
+
id: forked.id,
|
|
139
|
+
cwd: forked.meta.cwd,
|
|
140
|
+
model: opts.model || forked.meta.model,
|
|
141
|
+
messages: forked.messages,
|
|
142
|
+
title: forked.meta.title,
|
|
143
|
+
store,
|
|
144
|
+
});
|
|
145
|
+
session.forkedFrom = sourceId;
|
|
146
|
+
// The branch starts its own accounting; the parent keeps its own.
|
|
147
|
+
return session;
|
|
148
|
+
}
|
|
149
|
+
get model() {
|
|
150
|
+
return this.spec;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Switch models mid-session. Because history is provider-neutral, the next
|
|
154
|
+
* request simply renders it for the new provider — no conversion needed.
|
|
155
|
+
*/
|
|
156
|
+
setModel(ref) {
|
|
157
|
+
const from = this.spec;
|
|
158
|
+
this.spec = this.providers.resolve(ref);
|
|
159
|
+
return { from, to: this.spec };
|
|
160
|
+
}
|
|
161
|
+
async addMessage(message) {
|
|
162
|
+
this.messages.push(message);
|
|
163
|
+
// The first thing asked names the session. Without this every list — the
|
|
164
|
+
// CLI's /sessions, the web sidebar — shows a column of timestamps and
|
|
165
|
+
// hex, which tells the user nothing about which one they want.
|
|
166
|
+
if (!this.title && message.role === "user")
|
|
167
|
+
this.title = titleFromMessage(message);
|
|
168
|
+
await this.store?.appendMessage(this.id, message);
|
|
169
|
+
}
|
|
170
|
+
/** Replace the transcript (after compaction) and rewrite the persisted copy. */
|
|
171
|
+
/**
|
|
172
|
+
* Replace the live transcript, archiving whatever is being dropped.
|
|
173
|
+
*
|
|
174
|
+
* `archivedCount` keeps sequence numbers meaningful across repeated
|
|
175
|
+
* compactions: without it the second compaction would restart at 0 and
|
|
176
|
+
* overwrite the first one's numbering in the archive.
|
|
177
|
+
*/
|
|
178
|
+
async compactTo(messages, dropped) {
|
|
179
|
+
if (dropped.length > 0) {
|
|
180
|
+
await this.store?.archiveMessages(this.id, dropped.map((message, i) => ({ seq: this.archivedCount + i, message })));
|
|
181
|
+
this.archivedCount += dropped.length;
|
|
182
|
+
}
|
|
183
|
+
await this.replaceMessages(messages);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Archive a message that never entered the transcript — the full text of a
|
|
187
|
+
* tool result that was too large to keep in context.
|
|
188
|
+
*/
|
|
189
|
+
async archiveOverflow(message) {
|
|
190
|
+
await this.store?.archiveMessages(this.id, [{ seq: this.archivedCount, message }]);
|
|
191
|
+
this.archivedCount += 1;
|
|
192
|
+
}
|
|
193
|
+
/** Everything compaction has archived, oldest first. */
|
|
194
|
+
async archive() {
|
|
195
|
+
return (await this.store?.readArchive(this.id)) ?? [];
|
|
196
|
+
}
|
|
197
|
+
async replaceMessages(messages) {
|
|
198
|
+
this.messages = messages;
|
|
199
|
+
await this.store?.replaceMessages(this.id, messages);
|
|
200
|
+
}
|
|
201
|
+
recordUsage(usage) {
|
|
202
|
+
if (!usage)
|
|
203
|
+
return;
|
|
204
|
+
this.usage = addUsage(this.usage, usage);
|
|
205
|
+
this.costUsd += estimateCost(this.spec, usage);
|
|
206
|
+
}
|
|
207
|
+
meta() {
|
|
208
|
+
return {
|
|
209
|
+
id: this.id,
|
|
210
|
+
createdAt: this.createdAt,
|
|
211
|
+
updatedAt: Date.now(),
|
|
212
|
+
cwd: this.cwd,
|
|
213
|
+
model: this.spec.id,
|
|
214
|
+
title: this.title,
|
|
215
|
+
turns: this.turns,
|
|
216
|
+
usage: this.usage,
|
|
217
|
+
costUsd: this.costUsd,
|
|
218
|
+
// Lineage has to survive a persist/reload round trip, or a forked
|
|
219
|
+
// session forgets its parent the moment it is resumed.
|
|
220
|
+
...(this.forkedFrom ? { forkedFrom: this.forkedFrom } : {}),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
async persist() {
|
|
224
|
+
await this.store?.writeMeta(this.meta());
|
|
225
|
+
}
|
|
226
|
+
/** Create the on-disk session directory. Safe to call more than once. */
|
|
227
|
+
async init() {
|
|
228
|
+
await this.store?.create(this.meta());
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/agent/session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { PermissionRules } from "./permissions.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Where a remembered permission decision goes.
|
|
4
|
+
*
|
|
5
|
+
* Three scopes because the answers genuinely differ. "Always allow `npm test`"
|
|
6
|
+
* belongs to the project and should be reviewed like any other config.
|
|
7
|
+
* "Always allow `gh pr create`" is a personal habit. And a decision made while
|
|
8
|
+
* poking at someone else's repo should not turn into a commit.
|
|
9
|
+
*/
|
|
10
|
+
export type SettingsScope = "user" | "project" | "local";
|
|
11
|
+
export interface Settings {
|
|
12
|
+
permissions?: PermissionRules;
|
|
13
|
+
}
|
|
14
|
+
export interface SettingsPaths {
|
|
15
|
+
user: string;
|
|
16
|
+
project: string;
|
|
17
|
+
local: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function settingsPaths(cwd: string, home?: string): SettingsPaths;
|
|
20
|
+
export declare class SettingsStore {
|
|
21
|
+
private readonly paths;
|
|
22
|
+
constructor(cwd: string, home?: string);
|
|
23
|
+
pathFor(scope: SettingsScope): string;
|
|
24
|
+
read(scope: SettingsScope): Promise<Settings>;
|
|
25
|
+
/** All three scopes merged, user first so project and local win. */
|
|
26
|
+
readAll(): Promise<PermissionRules>;
|
|
27
|
+
/**
|
|
28
|
+
* Add a rule to one scope, preserving whatever else the file holds.
|
|
29
|
+
*
|
|
30
|
+
* Read-modify-write rather than overwrite: these files are hand-edited, and
|
|
31
|
+
* clobbering a comment-laden config to record one approval would be a poor
|
|
32
|
+
* trade.
|
|
33
|
+
*/
|
|
34
|
+
addRule(scope: SettingsScope, kind: keyof PermissionRules, pattern: string): Promise<void>;
|
|
35
|
+
removeRule(scope: SettingsScope, kind: keyof PermissionRules, pattern: string): Promise<boolean>;
|
|
36
|
+
private write;
|
|
37
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { userPath } from "../paths.js";
|
|
4
|
+
import { parseJson } from "../util/json.js";
|
|
5
|
+
export function settingsPaths(cwd, home) {
|
|
6
|
+
// The user scope goes through the shared resolver, so `OHARNESS_HOME`
|
|
7
|
+
// redirects settings along with everything else rather than settings being
|
|
8
|
+
// the one thing still landing in the real home directory.
|
|
9
|
+
return {
|
|
10
|
+
user: home ? path.join(home, ".oharness", "settings.json") : userPath("settings.json"),
|
|
11
|
+
project: path.join(cwd, ".oharness", "settings.json"),
|
|
12
|
+
// Local settings are per-checkout and belong in .gitignore, which is why
|
|
13
|
+
// they are a separate file rather than a field in the project one.
|
|
14
|
+
local: path.join(cwd, ".oharness", "settings.local.json"),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export class SettingsStore {
|
|
18
|
+
paths;
|
|
19
|
+
constructor(cwd, home) {
|
|
20
|
+
this.paths = settingsPaths(cwd, home);
|
|
21
|
+
}
|
|
22
|
+
pathFor(scope) {
|
|
23
|
+
return this.paths[scope];
|
|
24
|
+
}
|
|
25
|
+
async read(scope) {
|
|
26
|
+
let raw;
|
|
27
|
+
try {
|
|
28
|
+
raw = await readFile(this.paths[scope], "utf8");
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return {};
|
|
32
|
+
}
|
|
33
|
+
const parsed = parseJson(raw);
|
|
34
|
+
// A hand-edited file with a syntax error should not block every tool
|
|
35
|
+
// call; the user finds out when their rule fails to apply.
|
|
36
|
+
return parsed.ok ? parsed.value : {};
|
|
37
|
+
}
|
|
38
|
+
/** All three scopes merged, user first so project and local win. */
|
|
39
|
+
async readAll() {
|
|
40
|
+
const merged = { allow: [], deny: [], ask: [] };
|
|
41
|
+
for (const scope of ["user", "project", "local"]) {
|
|
42
|
+
const settings = await this.read(scope);
|
|
43
|
+
merged.allow.push(...(settings.permissions?.allow ?? []));
|
|
44
|
+
merged.deny.push(...(settings.permissions?.deny ?? []));
|
|
45
|
+
merged.ask.push(...(settings.permissions?.ask ?? []));
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
allow: dedupe(merged.allow),
|
|
49
|
+
// Denials accumulate across scopes and are never overridden — a project
|
|
50
|
+
// must be able to forbid something the user allowed globally.
|
|
51
|
+
deny: dedupe(merged.deny),
|
|
52
|
+
ask: dedupe(merged.ask),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Add a rule to one scope, preserving whatever else the file holds.
|
|
57
|
+
*
|
|
58
|
+
* Read-modify-write rather than overwrite: these files are hand-edited, and
|
|
59
|
+
* clobbering a comment-laden config to record one approval would be a poor
|
|
60
|
+
* trade.
|
|
61
|
+
*/
|
|
62
|
+
async addRule(scope, kind, pattern) {
|
|
63
|
+
const settings = await this.read(scope);
|
|
64
|
+
const rules = settings.permissions ?? {};
|
|
65
|
+
const existing = rules[kind] ?? [];
|
|
66
|
+
if (existing.includes(pattern))
|
|
67
|
+
return;
|
|
68
|
+
const updated = {
|
|
69
|
+
...settings,
|
|
70
|
+
permissions: { ...rules, [kind]: [...existing, pattern] },
|
|
71
|
+
};
|
|
72
|
+
await this.write(scope, updated);
|
|
73
|
+
}
|
|
74
|
+
async removeRule(scope, kind, pattern) {
|
|
75
|
+
const settings = await this.read(scope);
|
|
76
|
+
const existing = settings.permissions?.[kind] ?? [];
|
|
77
|
+
if (!existing.includes(pattern))
|
|
78
|
+
return false;
|
|
79
|
+
await this.write(scope, {
|
|
80
|
+
...settings,
|
|
81
|
+
permissions: {
|
|
82
|
+
...settings.permissions,
|
|
83
|
+
[kind]: existing.filter((entry) => entry !== pattern),
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
async write(scope, settings) {
|
|
89
|
+
const file = this.paths[scope];
|
|
90
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
91
|
+
// Temp file plus rename, so an interrupted write cannot leave a truncated
|
|
92
|
+
// settings file that silently drops every rule in it.
|
|
93
|
+
const temp = tempName(file);
|
|
94
|
+
await writeFile(temp, `${JSON.stringify(settings, null, 2)}\n`, "utf8");
|
|
95
|
+
await rename(temp, file);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function dedupe(values) {
|
|
99
|
+
return [...new Set(values)];
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* A unique name for each in-flight write.
|
|
103
|
+
*
|
|
104
|
+
* `file.<pid>.tmp` collides when one process writes twice at once: the first
|
|
105
|
+
* rename moves the shared temp file away and the second fails with ENOENT.
|
|
106
|
+
* The counter makes concurrent writers independent.
|
|
107
|
+
*/
|
|
108
|
+
let tempSeq = 0;
|
|
109
|
+
function tempName(file) {
|
|
110
|
+
tempSeq += 1;
|
|
111
|
+
return `${file}.${process.pid}.${tempSeq}.tmp`;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/agent/settings.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { Logger } from "../util/logger.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Skills: packaged instructions loaded on demand.
|
|
4
|
+
*
|
|
5
|
+
* The point is progressive disclosure. A skill's *description* costs a line in
|
|
6
|
+
* the system prompt; its body — which can be thousands of tokens of workflow
|
|
7
|
+
* detail — is only loaded when the model decides the task matches. Putting
|
|
8
|
+
* every skill body in the prompt would defeat the purpose.
|
|
9
|
+
*/
|
|
10
|
+
export interface Skill {
|
|
11
|
+
name: string;
|
|
12
|
+
/** The one line the model sees up front; this is what triggers loading. */
|
|
13
|
+
description: string;
|
|
14
|
+
/** Full instructions, loaded by the `skill` tool. */
|
|
15
|
+
body: string;
|
|
16
|
+
/** Directory holding SKILL.md, so the body can reference scripts/ etc. */
|
|
17
|
+
directory: string;
|
|
18
|
+
/** Where it came from, for `/skills`. */
|
|
19
|
+
source: "builtin" | "project" | "user" | "config";
|
|
20
|
+
}
|
|
21
|
+
export declare class SkillRegistry {
|
|
22
|
+
private readonly skills;
|
|
23
|
+
private readonly logger;
|
|
24
|
+
constructor(logger?: Logger);
|
|
25
|
+
register(skill: Skill): this;
|
|
26
|
+
get(name: string): Skill | undefined;
|
|
27
|
+
list(): Skill[];
|
|
28
|
+
get size(): number;
|
|
29
|
+
/**
|
|
30
|
+
* Discover skills from the conventional locations. Project skills override
|
|
31
|
+
* user skills of the same name, so a repo can pin its own version.
|
|
32
|
+
*/
|
|
33
|
+
discover(cwd: string, extraDirs?: string[]): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* The block injected into the system prompt: names and descriptions only.
|
|
36
|
+
* Returns undefined when there is nothing to advertise, so the prompt does
|
|
37
|
+
* not carry an empty section.
|
|
38
|
+
*/
|
|
39
|
+
promptSection(): string | undefined;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Render a loaded skill for the model.
|
|
43
|
+
*
|
|
44
|
+
* The base directory and file listing matter: skills routinely say "run
|
|
45
|
+
* `scripts/check.py`", and without knowing where they live the model cannot
|
|
46
|
+
* act on that.
|
|
47
|
+
*/
|
|
48
|
+
export declare function renderSkill(skill: Skill): Promise<string>;
|
|
49
|
+
/** Parse YAML-ish frontmatter. Only `name` and `description` are meaningful. */
|
|
50
|
+
export declare function parseSkillFile(raw: string, fallbackName: string): {
|
|
51
|
+
name: string;
|
|
52
|
+
description: string;
|
|
53
|
+
body: string;
|
|
54
|
+
} | undefined;
|
|
55
|
+
export declare function splitFrontmatter(raw: string): {
|
|
56
|
+
frontmatter: Record<string, string>;
|
|
57
|
+
body: string;
|
|
58
|
+
};
|