@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,289 @@
|
|
|
1
|
+
import { matchPattern } from "../tools/registry.js";
|
|
2
|
+
import { shortId } from "../util/id.js";
|
|
3
|
+
import { PermissionDeniedError } from "../core/errors.js";
|
|
4
|
+
/** Every mode, in the order the picker shows them. */
|
|
5
|
+
export const PERMISSION_MODES = [
|
|
6
|
+
"default",
|
|
7
|
+
"acceptEdits",
|
|
8
|
+
"plan",
|
|
9
|
+
"auto",
|
|
10
|
+
"bypassPermissions",
|
|
11
|
+
];
|
|
12
|
+
export const MODE_LABELS = {
|
|
13
|
+
default: "Manual",
|
|
14
|
+
acceptEdits: "Accept edits",
|
|
15
|
+
plan: "Plan",
|
|
16
|
+
auto: "Auto",
|
|
17
|
+
bypassPermissions: "Bypass permissions",
|
|
18
|
+
};
|
|
19
|
+
export const MODE_DESCRIPTIONS = {
|
|
20
|
+
default: "ask before anything that changes state",
|
|
21
|
+
acceptEdits: "file edits go through, everything else asks",
|
|
22
|
+
plan: "read-only; refuse every change",
|
|
23
|
+
auto: "edits and routine commands go through, risky ones ask",
|
|
24
|
+
bypassPermissions: "approve everything — only where a mistake is cheap",
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* A line off disk, or nothing.
|
|
28
|
+
*
|
|
29
|
+
* The trail is append-only and shared with future versions of this build, so a
|
|
30
|
+
* reader that cannot understand a line skips it rather than refusing the file.
|
|
31
|
+
*/
|
|
32
|
+
export function asAuditEntry(value) {
|
|
33
|
+
if (typeof value !== "object" || value === null)
|
|
34
|
+
return undefined;
|
|
35
|
+
const line = value;
|
|
36
|
+
if (typeof line["id"] !== "string" || typeof line["at"] !== "number")
|
|
37
|
+
return undefined;
|
|
38
|
+
if (line["kind"] === "decision") {
|
|
39
|
+
return typeof line["tool"] === "string" && typeof line["behavior"] === "string"
|
|
40
|
+
? line
|
|
41
|
+
: undefined;
|
|
42
|
+
}
|
|
43
|
+
if (line["kind"] === "answer") {
|
|
44
|
+
return typeof line["outcome"] === "string" ? line : undefined;
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
const EDIT_TOOLS = new Set(["write", "edit"]);
|
|
49
|
+
/**
|
|
50
|
+
* Decides whether a tool call may proceed.
|
|
51
|
+
*
|
|
52
|
+
* Rules are matched against `tool` and `Tool(argument)` forms so a policy can
|
|
53
|
+
* be as coarse as "all MCP GitHub tools" or as narrow as one bash command
|
|
54
|
+
* prefix.
|
|
55
|
+
*/
|
|
56
|
+
export class PermissionEngine {
|
|
57
|
+
mode;
|
|
58
|
+
rules;
|
|
59
|
+
sessionAllow = new Set();
|
|
60
|
+
settings;
|
|
61
|
+
auditors = new Set();
|
|
62
|
+
constructor(mode = "default", rules = {}) {
|
|
63
|
+
this.mode = mode;
|
|
64
|
+
this.rules = {
|
|
65
|
+
allow: rules.allow ?? [],
|
|
66
|
+
deny: rules.deny ?? [],
|
|
67
|
+
ask: rules.ask ?? [],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
getMode() {
|
|
71
|
+
return this.mode;
|
|
72
|
+
}
|
|
73
|
+
setMode(mode) {
|
|
74
|
+
this.mode = mode;
|
|
75
|
+
}
|
|
76
|
+
addRule(kind, pattern) {
|
|
77
|
+
this.rules[kind].push(pattern);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Remember an approval.
|
|
81
|
+
*
|
|
82
|
+
* The most specific candidate is what gets persisted — approving
|
|
83
|
+
* `Bash(npm test)` should not quietly grant every `Bash(...)`. Session-scoped
|
|
84
|
+
* approvals record every candidate because they expire with the process
|
|
85
|
+
* anyway.
|
|
86
|
+
*/
|
|
87
|
+
async rememberAllow(tool, input, scope = "session") {
|
|
88
|
+
const keys = candidates(tool, input);
|
|
89
|
+
for (const candidate of keys)
|
|
90
|
+
this.sessionAllow.add(candidate);
|
|
91
|
+
if (scope === "session" || !this.settings)
|
|
92
|
+
return undefined;
|
|
93
|
+
const pattern = keys[keys.length - 1] ?? tool.name;
|
|
94
|
+
await this.settings.addRule(scope, "allow", pattern);
|
|
95
|
+
this.rules.allow.push(pattern);
|
|
96
|
+
return pattern;
|
|
97
|
+
}
|
|
98
|
+
/** Where persisted rules are written; absent means session-only. */
|
|
99
|
+
attachSettings(settings) {
|
|
100
|
+
this.settings = settings;
|
|
101
|
+
}
|
|
102
|
+
listRules() {
|
|
103
|
+
return { ...this.rules, sessionAllow: [...this.sessionAllow] };
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Watch every decision. Returns the unsubscribe.
|
|
107
|
+
*
|
|
108
|
+
* A set rather than one slot, and a disposer rather than nothing, because
|
|
109
|
+
* there is more than one recorder now — the session's own trail and, when
|
|
110
|
+
* one is configured, a file — and because a recorder that can be attached
|
|
111
|
+
* but never detached is not something a switch can turn off.
|
|
112
|
+
*/
|
|
113
|
+
onDecision(auditor) {
|
|
114
|
+
this.auditors.add(auditor);
|
|
115
|
+
return () => void this.auditors.delete(auditor);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Tell the recorders, without letting one of them break a tool call.
|
|
119
|
+
*
|
|
120
|
+
* A throwing recorder is dropped on the floor: the trail exists to describe
|
|
121
|
+
* what the harness did, and a description that can veto the thing it
|
|
122
|
+
* describes is a new failure mode in the permission path.
|
|
123
|
+
*/
|
|
124
|
+
announce(entry) {
|
|
125
|
+
for (const auditor of this.auditors) {
|
|
126
|
+
try {
|
|
127
|
+
auditor(entry);
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// See above.
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
evaluate(tool, input) {
|
|
135
|
+
const decision = this.decide(tool, input);
|
|
136
|
+
const id = `dec_${shortId(8)}`;
|
|
137
|
+
this.announce({
|
|
138
|
+
kind: "decision",
|
|
139
|
+
id,
|
|
140
|
+
at: Date.now(),
|
|
141
|
+
tool: tool.name,
|
|
142
|
+
behavior: decision.behavior,
|
|
143
|
+
rule: decision.rule,
|
|
144
|
+
principal: candidates(tool, input).at(-1),
|
|
145
|
+
});
|
|
146
|
+
return { ...decision, auditId: id };
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Close the pair an `ask` opened.
|
|
150
|
+
*
|
|
151
|
+
* Called by whoever did the asking, because only they know how it ended —
|
|
152
|
+
* the engine that decided to ask has already returned by then. An ask with
|
|
153
|
+
* no answer in the trail means the process died while the question was on
|
|
154
|
+
* screen, which is a true thing to be able to read.
|
|
155
|
+
*/
|
|
156
|
+
recordAnswer(id, outcome, remembered) {
|
|
157
|
+
this.announce({ kind: "answer", id, at: Date.now(), outcome, remembered });
|
|
158
|
+
}
|
|
159
|
+
decide(tool, input) {
|
|
160
|
+
const keys = candidates(tool, input);
|
|
161
|
+
// Deny wins over everything, including bypass mode — an explicit deny rule
|
|
162
|
+
// is the one thing a user can rely on absolutely.
|
|
163
|
+
const denied = firstMatch(keys, this.rules.deny);
|
|
164
|
+
if (denied) {
|
|
165
|
+
return { behavior: "deny", rule: denied, message: `denied by rule ${denied}` };
|
|
166
|
+
}
|
|
167
|
+
if (this.mode === "plan" && !tool.readOnly) {
|
|
168
|
+
return {
|
|
169
|
+
behavior: "deny",
|
|
170
|
+
message: "Plan mode is active: no tool that modifies state may run. Present the plan instead.",
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
if (this.mode === "bypassPermissions")
|
|
174
|
+
return { behavior: "allow", rule: "bypass" };
|
|
175
|
+
// Read-only tools never need approval; that is the point of the flag.
|
|
176
|
+
if (tool.readOnly)
|
|
177
|
+
return { behavior: "allow", rule: "readOnly" };
|
|
178
|
+
// Neither do tools whose only effect is on this session's own scratch
|
|
179
|
+
// state. Deliberately below the plan-mode gate above: plan mode is about
|
|
180
|
+
// whether anything changes at all, and these do change something. This is
|
|
181
|
+
// about whether a human answering yes or no is protecting anything, and
|
|
182
|
+
// for a Map that dies with the process they are not — they are being
|
|
183
|
+
// charged a dialog for it. An explicit deny rule still wins, because it is
|
|
184
|
+
// matched before any of this.
|
|
185
|
+
if (tool.sessionLocal)
|
|
186
|
+
return { behavior: "allow", rule: "sessionLocal" };
|
|
187
|
+
const mustAsk = firstMatch(keys, this.rules.ask);
|
|
188
|
+
if (mustAsk)
|
|
189
|
+
return { behavior: "ask", rule: mustAsk };
|
|
190
|
+
const allowed = firstMatch(keys, this.rules.allow);
|
|
191
|
+
if (allowed)
|
|
192
|
+
return { behavior: "allow", rule: allowed };
|
|
193
|
+
if (keys.some((key) => this.sessionAllow.has(key))) {
|
|
194
|
+
return { behavior: "allow", rule: "session" };
|
|
195
|
+
}
|
|
196
|
+
if ((this.mode === "acceptEdits" || this.mode === "auto") &&
|
|
197
|
+
EDIT_TOOLS.has(tool.name)) {
|
|
198
|
+
return { behavior: "allow", rule: this.mode };
|
|
199
|
+
}
|
|
200
|
+
// Auto mode buys flow by approving routine commands. The classifier is
|
|
201
|
+
// deliberately conservative — anything it does not recognise as routine
|
|
202
|
+
// still asks — because the failure that matters is approving a command
|
|
203
|
+
// that destroys something, not asking about one more `ls`.
|
|
204
|
+
if (this.mode === "auto" && !isRisky(tool, input)) {
|
|
205
|
+
return { behavior: "allow", rule: "auto" };
|
|
206
|
+
}
|
|
207
|
+
return { behavior: "ask" };
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* The strings a rule may match: the bare tool name, and `Tool(arg)` using the
|
|
212
|
+
* argument that best identifies the call.
|
|
213
|
+
*/
|
|
214
|
+
export function candidates(tool, input) {
|
|
215
|
+
const keys = [tool.name];
|
|
216
|
+
const arg = principalArgument(tool.name, input);
|
|
217
|
+
if (arg !== undefined) {
|
|
218
|
+
keys.push(`${tool.name}(${arg})`);
|
|
219
|
+
// Capitalized form so `Bash(...)` reads naturally in a config file.
|
|
220
|
+
keys.push(`${capitalize(tool.name)}(${arg})`);
|
|
221
|
+
}
|
|
222
|
+
return keys;
|
|
223
|
+
}
|
|
224
|
+
function principalArgument(toolName, input) {
|
|
225
|
+
if (input === null || typeof input !== "object")
|
|
226
|
+
return undefined;
|
|
227
|
+
const record = input;
|
|
228
|
+
const field = toolName === "bash"
|
|
229
|
+
? "command"
|
|
230
|
+
: "path" in record
|
|
231
|
+
? "path"
|
|
232
|
+
: "pattern" in record
|
|
233
|
+
? "pattern"
|
|
234
|
+
: "url" in record
|
|
235
|
+
? "url"
|
|
236
|
+
: undefined;
|
|
237
|
+
if (!field)
|
|
238
|
+
return undefined;
|
|
239
|
+
const value = record[field];
|
|
240
|
+
return typeof value === "string" ? value : undefined;
|
|
241
|
+
}
|
|
242
|
+
function firstMatch(keys, patterns) {
|
|
243
|
+
for (const pattern of patterns) {
|
|
244
|
+
if (keys.some((key) => matchPattern(key, pattern)))
|
|
245
|
+
return pattern;
|
|
246
|
+
}
|
|
247
|
+
return undefined;
|
|
248
|
+
}
|
|
249
|
+
function capitalize(value) {
|
|
250
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Throw for a denial that must abort the caller rather than be handed back to
|
|
254
|
+
* the model as a tool error. The agent loop reports denials in-band; this is
|
|
255
|
+
* for embedders driving tools directly.
|
|
256
|
+
*/
|
|
257
|
+
export function assertAllowed(decision, title) {
|
|
258
|
+
if (!decision.allowed) {
|
|
259
|
+
throw new PermissionDeniedError(decision.reason ?? `${title} was denied`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Whether a call is risky enough that `auto` mode should still ask.
|
|
264
|
+
*
|
|
265
|
+
* This is a judgement about blast radius, not a security check. A rule in
|
|
266
|
+
* `deny` is the boundary you can rely on; this only decides which prompts
|
|
267
|
+
* `auto` mode is willing to skip. It answers "would a mistake here be
|
|
268
|
+
* expensive to undo?" and errs towards yes.
|
|
269
|
+
*/
|
|
270
|
+
export function isRisky(tool, input) {
|
|
271
|
+
if (tool.name !== "bash") {
|
|
272
|
+
// Any non-shell tool that mutates is already handled by the edit-tool set
|
|
273
|
+
// above; whatever is left reaches the network or the system.
|
|
274
|
+
return !tool.readOnly && !EDIT_TOOLS.has(tool.name);
|
|
275
|
+
}
|
|
276
|
+
const command = input?.command;
|
|
277
|
+
if (typeof command !== "string")
|
|
278
|
+
return true;
|
|
279
|
+
return RISKY_COMMAND.test(command);
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Destroys data, changes the world outside this machine, or escalates.
|
|
283
|
+
*
|
|
284
|
+
* Kept explicit rather than clever: a maintainer should be able to read this
|
|
285
|
+
* and say whether they agree, which is not true of a heuristic that scores
|
|
286
|
+
* commands.
|
|
287
|
+
*/
|
|
288
|
+
const RISKY_COMMAND = /\b(rm|rmdir|dd|mkfs|shutdown|reboot|kill|pkill|killall|chown|chmod|sudo|su|ssh|scp|rsync)\b|\bgit\s+(push|reset|clean|checkout\s+--|rebase)\b|\bnpm\s+(publish|unpublish)\b|\b(docker|kubectl|terraform|aws|gcloud|az)\b|\bcurl\b.*\B-[a-zA-Z]*d|>\s*\/(?!tmp\/)/;
|
|
289
|
+
//# sourceMappingURL=permissions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../src/agent/permissions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ModelSpec } from "../providers/types.ts";
|
|
2
|
+
import type { PermissionMode } from "./permissions.ts";
|
|
3
|
+
export interface SystemPromptInput {
|
|
4
|
+
/** Replaces the built-in base prompt entirely. */
|
|
5
|
+
systemPrompt?: string;
|
|
6
|
+
/** Appended after the base prompt — the usual extension point. */
|
|
7
|
+
appendSystemPrompt?: string;
|
|
8
|
+
cwd: string;
|
|
9
|
+
model: ModelSpec;
|
|
10
|
+
permissionMode: PermissionMode;
|
|
11
|
+
/** Project instructions (an AGENTS.md / CLAUDE.md equivalent). */
|
|
12
|
+
projectInstructions?: string;
|
|
13
|
+
/** `instructions` strings advertised by connected MCP servers. */
|
|
14
|
+
mcpInstructions?: string[];
|
|
15
|
+
toolNames: string[];
|
|
16
|
+
extraSections?: string[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Assemble the system prompt.
|
|
20
|
+
*
|
|
21
|
+
* Ordering is deliberate: stable content first, volatile content last, so
|
|
22
|
+
* providers with prefix-based prompt caching get a long reusable prefix.
|
|
23
|
+
*/
|
|
24
|
+
export declare function buildSystemPrompt(input: SystemPromptInput): string;
|
|
25
|
+
export declare const COMPACTION_PROMPT = "Summarize the conversation so far so that another agent can continue the work without seeing the original transcript.\n\nWrite it under these headings, omitting any that do not apply:\n1. **Goal** \u2014 what the user asked for, in their own terms.\n2. **Decisions** \u2014 choices made and the reasoning, including approaches ruled out.\n3. **Work done** \u2014 files created or modified with paths, commands run and their outcomes.\n4. **Current state** \u2014 what is working, what is broken, what was verified versus assumed.\n5. **Next steps** \u2014 what remains, specifically enough to act on.\n6. **Key details** \u2014 exact identifiers, paths, versions, error strings, and user preferences stated during the session.\n\nBe specific and concrete. Preserve exact names and paths verbatim \u2014 a summary that loses them is useless. Do not add commentary about the summarization itself.";
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { platform } from "node:os";
|
|
2
|
+
const BASE_PROMPT = `You are an agent operating through a tool-calling harness. You act on the user's behalf by calling tools, observing the results, and continuing until the task is done.
|
|
3
|
+
|
|
4
|
+
## Working style
|
|
5
|
+
- When you have enough information to act, act. Don't re-derive facts already established, and don't narrate options you won't pursue.
|
|
6
|
+
- Prefer the dedicated tools over shell equivalents: they are safer, produce better output, and let the harness show the user what is happening.
|
|
7
|
+
- Independent tool calls should be issued together in one turn so they run in parallel. Calls that depend on each other must be sequential.
|
|
8
|
+
- Read a file before editing it. Edits are rejected if the file changed since your last read.
|
|
9
|
+
- Report outcomes faithfully. If a command failed, say so and include the output; if you skipped a step, say that.
|
|
10
|
+
|
|
11
|
+
## Scope
|
|
12
|
+
Deliver what was asked, at the scope intended. Make routine judgment calls yourself; check in only when different readings would lead to materially different work. Don't add abstractions, error handling for impossible states, or refactors that weren't requested. Finish the whole task — report completion only when it is actually complete.
|
|
13
|
+
|
|
14
|
+
## Communicating
|
|
15
|
+
Lead with the outcome. Keep responses focused: the user is reading a terminal, not a report. Use complete sentences and spell out identifiers rather than compressing into shorthand.`;
|
|
16
|
+
const PLAN_MODE_NOTE = `## Plan mode is active
|
|
17
|
+
Tools that modify state are blocked. Investigate using read-only tools, then present a concrete plan and wait for approval. Do not attempt to make changes.`;
|
|
18
|
+
/**
|
|
19
|
+
* Assemble the system prompt.
|
|
20
|
+
*
|
|
21
|
+
* Ordering is deliberate: stable content first, volatile content last, so
|
|
22
|
+
* providers with prefix-based prompt caching get a long reusable prefix.
|
|
23
|
+
*/
|
|
24
|
+
export function buildSystemPrompt(input) {
|
|
25
|
+
const sections = [];
|
|
26
|
+
sections.push(input.systemPrompt ?? BASE_PROMPT);
|
|
27
|
+
if (input.appendSystemPrompt)
|
|
28
|
+
sections.push(input.appendSystemPrompt);
|
|
29
|
+
if (input.permissionMode === "plan")
|
|
30
|
+
sections.push(PLAN_MODE_NOTE);
|
|
31
|
+
if (input.projectInstructions?.trim()) {
|
|
32
|
+
sections.push(`# Project instructions\n${input.projectInstructions.trim()}`);
|
|
33
|
+
}
|
|
34
|
+
if (input.mcpInstructions?.length) {
|
|
35
|
+
sections.push(`# MCP server instructions\n${input.mcpInstructions.join("\n\n")}`);
|
|
36
|
+
}
|
|
37
|
+
for (const extra of input.extraSections ?? []) {
|
|
38
|
+
if (extra.trim())
|
|
39
|
+
sections.push(extra.trim());
|
|
40
|
+
}
|
|
41
|
+
// Environment last: it is the part most likely to change between sessions.
|
|
42
|
+
sections.push([
|
|
43
|
+
"# Environment",
|
|
44
|
+
`Working directory: ${input.cwd}`,
|
|
45
|
+
`Platform: ${platform()}`,
|
|
46
|
+
`Model: ${input.model.displayName ?? input.model.model} (${input.model.provider})`,
|
|
47
|
+
`Permission mode: ${input.permissionMode}`,
|
|
48
|
+
`Available tools: ${input.toolNames.join(", ") || "(none)"}`,
|
|
49
|
+
].join("\n"));
|
|
50
|
+
return sections.join("\n\n");
|
|
51
|
+
}
|
|
52
|
+
export const COMPACTION_PROMPT = `Summarize the conversation so far so that another agent can continue the work without seeing the original transcript.
|
|
53
|
+
|
|
54
|
+
Write it under these headings, omitting any that do not apply:
|
|
55
|
+
1. **Goal** — what the user asked for, in their own terms.
|
|
56
|
+
2. **Decisions** — choices made and the reasoning, including approaches ruled out.
|
|
57
|
+
3. **Work done** — files created or modified with paths, commands run and their outcomes.
|
|
58
|
+
4. **Current state** — what is working, what is broken, what was verified versus assumed.
|
|
59
|
+
5. **Next steps** — what remains, specifically enough to act on.
|
|
60
|
+
6. **Key details** — exact identifiers, paths, versions, error strings, and user preferences stated during the session.
|
|
61
|
+
|
|
62
|
+
Be specific and concrete. Preserve exact names and paths verbatim — a summary that loses them is useless. Do not add commentary about the summarization itself.`;
|
|
63
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/agent/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAoBnC,MAAM,WAAW,GAAG;;;;;;;;;;;;;sLAakK,CAAC;AAEvL,MAAM,cAAc,GAAG;4JACqI,CAAC;AAE7J;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAwB;IACxD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,IAAI,WAAW,CAAC,CAAC;IACjD,IAAI,KAAK,CAAC,kBAAkB;QAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACtE,IAAI,KAAK,CAAC,cAAc,KAAK,MAAM;QAAE,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAEnE,IAAI,KAAK,CAAC,mBAAmB,EAAE,IAAI,EAAE,EAAE,CAAC;QACtC,QAAQ,CAAC,IAAI,CAAC,2BAA2B,KAAK,CAAC,mBAAmB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC/E,CAAC;IACD,IAAI,KAAK,CAAC,eAAe,EAAE,MAAM,EAAE,CAAC;QAClC,QAAQ,CAAC,IAAI,CAAC,8BAA8B,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACpF,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,aAAa,IAAI,EAAE,EAAE,CAAC;QAC9C,IAAI,KAAK,CAAC,IAAI,EAAE;YAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,CACX;QACE,eAAe;QACf,sBAAsB,KAAK,CAAC,GAAG,EAAE;QACjC,aAAa,QAAQ,EAAE,EAAE;QACzB,UAAU,KAAK,CAAC,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK,CAAC,QAAQ,GAAG;QAClF,oBAAoB,KAAK,CAAC,cAAc,EAAE;QAC1C,oBAAoB,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ,EAAE;KAC7D,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAC;IAEF,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;gKAU+H,CAAC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { StoredArtifactRevision } from "../artifacts/store.ts";
|
|
2
|
+
import type { Message, Usage } from "../core/types.ts";
|
|
3
|
+
import type { ModelSpec } from "../providers/types.ts";
|
|
4
|
+
import type { ProviderRegistry } from "../providers/registry.ts";
|
|
5
|
+
import { PermissionEngine } from "./permissions.ts";
|
|
6
|
+
import type { PermissionMode, PermissionRules } from "./permissions.ts";
|
|
7
|
+
import { SessionStore } from "./store.ts";
|
|
8
|
+
import type { ArchivedMessage, SessionMeta } from "./store.ts";
|
|
9
|
+
export interface SessionOptions {
|
|
10
|
+
id?: string;
|
|
11
|
+
cwd?: string;
|
|
12
|
+
model: string;
|
|
13
|
+
providers: ProviderRegistry;
|
|
14
|
+
permissionMode?: PermissionMode;
|
|
15
|
+
permissionRules?: PermissionRules;
|
|
16
|
+
store?: SessionStore | false;
|
|
17
|
+
/**
|
|
18
|
+
* Artifact revisions read from disk, preferred over replaying the transcript.
|
|
19
|
+
*
|
|
20
|
+
* Passed in rather than read here because the constructor is synchronous and
|
|
21
|
+
* this comes off a file; `Session.resume` does the reading.
|
|
22
|
+
*/
|
|
23
|
+
artifactRevisions?: StoredArtifactRevision[];
|
|
24
|
+
messages?: Message[];
|
|
25
|
+
title?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Conversation state for one agent run: history, current model, permissions,
|
|
29
|
+
* accounting, and the scratch state tools share.
|
|
30
|
+
*
|
|
31
|
+
* History is stored provider-neutrally, which is what makes `setModel` a
|
|
32
|
+
* one-line operation instead of a migration.
|
|
33
|
+
*/
|
|
34
|
+
export declare class Session {
|
|
35
|
+
/** How many messages compaction has already archived, for stable seq numbers. */
|
|
36
|
+
private archivedCount;
|
|
37
|
+
readonly id: string;
|
|
38
|
+
readonly cwd: string;
|
|
39
|
+
readonly providers: ProviderRegistry;
|
|
40
|
+
readonly permissions: PermissionEngine;
|
|
41
|
+
/** Shared scratch space handed to tools via `ToolHost`. */
|
|
42
|
+
readonly state: Map<string, unknown>;
|
|
43
|
+
messages: Message[];
|
|
44
|
+
title?: string;
|
|
45
|
+
usage: Usage;
|
|
46
|
+
costUsd: number;
|
|
47
|
+
/** Session this one was branched from, surfaced in meta and `/sessions`. */
|
|
48
|
+
forkedFrom?: string;
|
|
49
|
+
turns: number;
|
|
50
|
+
/**
|
|
51
|
+
* Messages discarded as an incomplete tail when this session was loaded.
|
|
52
|
+
*
|
|
53
|
+
* Repairing the transcript silently would leave the user wondering where the
|
|
54
|
+
* last turn went, so the count is kept for the frontend to report.
|
|
55
|
+
*/
|
|
56
|
+
readonly droppedOnLoad: number;
|
|
57
|
+
private spec;
|
|
58
|
+
private readonly store?;
|
|
59
|
+
private readonly createdAt;
|
|
60
|
+
constructor(opts: SessionOptions);
|
|
61
|
+
/** Restore a persisted session, keeping its id and transcript. */
|
|
62
|
+
static resume(id: string, opts: Omit<SessionOptions, "id" | "messages"> & {
|
|
63
|
+
store?: SessionStore;
|
|
64
|
+
}): Promise<Session | undefined>;
|
|
65
|
+
/**
|
|
66
|
+
* Branch a stored session into a new one.
|
|
67
|
+
*
|
|
68
|
+
* `keepMessages` rewinds the branch to an earlier point; omit it to branch
|
|
69
|
+
* from where the parent stands. The parent is left untouched either way.
|
|
70
|
+
*/
|
|
71
|
+
static fork(sourceId: string, opts: Omit<SessionOptions, "id" | "messages"> & {
|
|
72
|
+
store?: SessionStore;
|
|
73
|
+
keepMessages?: number;
|
|
74
|
+
title?: string;
|
|
75
|
+
}): Promise<Session | undefined>;
|
|
76
|
+
get model(): ModelSpec;
|
|
77
|
+
/**
|
|
78
|
+
* Switch models mid-session. Because history is provider-neutral, the next
|
|
79
|
+
* request simply renders it for the new provider — no conversion needed.
|
|
80
|
+
*/
|
|
81
|
+
setModel(ref: string): {
|
|
82
|
+
from: ModelSpec;
|
|
83
|
+
to: ModelSpec;
|
|
84
|
+
};
|
|
85
|
+
addMessage(message: Message): Promise<void>;
|
|
86
|
+
/** Replace the transcript (after compaction) and rewrite the persisted copy. */
|
|
87
|
+
/**
|
|
88
|
+
* Replace the live transcript, archiving whatever is being dropped.
|
|
89
|
+
*
|
|
90
|
+
* `archivedCount` keeps sequence numbers meaningful across repeated
|
|
91
|
+
* compactions: without it the second compaction would restart at 0 and
|
|
92
|
+
* overwrite the first one's numbering in the archive.
|
|
93
|
+
*/
|
|
94
|
+
compactTo(messages: Message[], dropped: Message[]): Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Archive a message that never entered the transcript — the full text of a
|
|
97
|
+
* tool result that was too large to keep in context.
|
|
98
|
+
*/
|
|
99
|
+
archiveOverflow(message: Message): Promise<void>;
|
|
100
|
+
/** Everything compaction has archived, oldest first. */
|
|
101
|
+
archive(): Promise<ArchivedMessage[]>;
|
|
102
|
+
replaceMessages(messages: Message[]): Promise<void>;
|
|
103
|
+
recordUsage(usage: Usage | undefined): void;
|
|
104
|
+
meta(): SessionMeta;
|
|
105
|
+
persist(): Promise<void>;
|
|
106
|
+
/** Create the on-disk session directory. Safe to call more than once. */
|
|
107
|
+
init(): Promise<void>;
|
|
108
|
+
}
|