@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,147 @@
|
|
|
1
|
+
import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { shortId } from "../util/id.js";
|
|
4
|
+
import { splitFrontmatter } from "./skills.js";
|
|
5
|
+
/** How much of the memory index may enter the system prompt. */
|
|
6
|
+
const MAX_PROMPT_CHARS = 4_000;
|
|
7
|
+
export class MemoryStore {
|
|
8
|
+
dir;
|
|
9
|
+
cache;
|
|
10
|
+
constructor(dir) {
|
|
11
|
+
this.dir = dir;
|
|
12
|
+
}
|
|
13
|
+
get location() {
|
|
14
|
+
return this.dir;
|
|
15
|
+
}
|
|
16
|
+
async load() {
|
|
17
|
+
if (this.cache)
|
|
18
|
+
return this.cache;
|
|
19
|
+
let files;
|
|
20
|
+
try {
|
|
21
|
+
files = (await readdir(this.dir)).filter((name) => name.endsWith(".md"));
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
this.cache = [];
|
|
25
|
+
return this.cache;
|
|
26
|
+
}
|
|
27
|
+
const entries = [];
|
|
28
|
+
for (const name of files) {
|
|
29
|
+
const file = path.join(this.dir, name);
|
|
30
|
+
let raw;
|
|
31
|
+
try {
|
|
32
|
+
raw = await readFile(file, "utf8");
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const { frontmatter, body } = splitFrontmatter(raw);
|
|
38
|
+
const summary = frontmatter["summary"]?.trim();
|
|
39
|
+
// Without a summary the entry cannot appear in the index, and an entry
|
|
40
|
+
// the model never sees is one it will never ask for.
|
|
41
|
+
if (!summary)
|
|
42
|
+
continue;
|
|
43
|
+
entries.push({
|
|
44
|
+
id: frontmatter["id"]?.trim() || name.replace(/\.md$/, ""),
|
|
45
|
+
summary,
|
|
46
|
+
body: body.trim(),
|
|
47
|
+
tags: (frontmatter["tags"] ?? "")
|
|
48
|
+
.split(/[,\s]+/)
|
|
49
|
+
.map((tag) => tag.trim())
|
|
50
|
+
.filter((tag) => tag !== ""),
|
|
51
|
+
createdAt: Number(frontmatter["createdAt"]) || 0,
|
|
52
|
+
file,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
entries.sort((a, b) => b.createdAt - a.createdAt);
|
|
56
|
+
this.cache = entries;
|
|
57
|
+
return entries;
|
|
58
|
+
}
|
|
59
|
+
async remember(input) {
|
|
60
|
+
await mkdir(this.dir, { recursive: true });
|
|
61
|
+
const existing = await this.load();
|
|
62
|
+
// Re-learning the same lesson should update the note, not add a duplicate
|
|
63
|
+
// that competes with it in the index.
|
|
64
|
+
const match = existing.find((entry) => entry.summary.toLowerCase() === input.summary.trim().toLowerCase());
|
|
65
|
+
const id = match?.id ?? `${slug(input.summary)}-${shortId(3)}`;
|
|
66
|
+
const createdAt = match?.createdAt || Date.now();
|
|
67
|
+
const file = match?.file ?? path.join(this.dir, `${id}.md`);
|
|
68
|
+
const frontmatter = [
|
|
69
|
+
"---",
|
|
70
|
+
`id: ${id}`,
|
|
71
|
+
`summary: ${input.summary.trim().replace(/\n/g, " ")}`,
|
|
72
|
+
`tags: ${(input.tags ?? []).join(", ")}`,
|
|
73
|
+
`createdAt: ${createdAt}`,
|
|
74
|
+
`updatedAt: ${Date.now()}`,
|
|
75
|
+
"---",
|
|
76
|
+
"",
|
|
77
|
+
].join("\n");
|
|
78
|
+
await writeFile(file, `${frontmatter}${input.body.trim()}\n`, "utf8");
|
|
79
|
+
this.cache = undefined;
|
|
80
|
+
return {
|
|
81
|
+
id,
|
|
82
|
+
summary: input.summary.trim(),
|
|
83
|
+
body: input.body.trim(),
|
|
84
|
+
tags: input.tags ?? [],
|
|
85
|
+
createdAt,
|
|
86
|
+
file,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
async forget(id) {
|
|
90
|
+
const entries = await this.load();
|
|
91
|
+
const match = entries.find((entry) => entry.id === id);
|
|
92
|
+
if (!match)
|
|
93
|
+
return false;
|
|
94
|
+
const { rm } = await import("node:fs/promises");
|
|
95
|
+
await rm(match.file, { force: true });
|
|
96
|
+
this.cache = undefined;
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
/** Full-text search across summaries, bodies and tags. */
|
|
100
|
+
async search(query) {
|
|
101
|
+
const needle = query.toLowerCase();
|
|
102
|
+
return (await this.load()).filter((entry) => entry.summary.toLowerCase().includes(needle) ||
|
|
103
|
+
entry.body.toLowerCase().includes(needle) ||
|
|
104
|
+
entry.tags.some((tag) => tag.toLowerCase().includes(needle)));
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The index that goes into the system prompt: summaries only.
|
|
108
|
+
*
|
|
109
|
+
* Same reasoning as skills — putting every note's body in the prompt would
|
|
110
|
+
* grow without bound and crowd out the actual conversation. The model reads
|
|
111
|
+
* a summary, then asks for the body if it matters.
|
|
112
|
+
*/
|
|
113
|
+
async promptSection() {
|
|
114
|
+
const entries = await this.load();
|
|
115
|
+
if (entries.length === 0)
|
|
116
|
+
return undefined;
|
|
117
|
+
const lines = [
|
|
118
|
+
"## Memory",
|
|
119
|
+
"",
|
|
120
|
+
"Lessons recorded in earlier sessions. Read the full note with the `memory` tool",
|
|
121
|
+
"before acting on one — these are summaries, not the whole story.",
|
|
122
|
+
"",
|
|
123
|
+
];
|
|
124
|
+
let budget = MAX_PROMPT_CHARS;
|
|
125
|
+
let omitted = 0;
|
|
126
|
+
for (const entry of entries) {
|
|
127
|
+
const line = `- \`${entry.id}\` — ${entry.summary}`;
|
|
128
|
+
if (line.length > budget) {
|
|
129
|
+
omitted++;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
lines.push(line);
|
|
133
|
+
budget -= line.length;
|
|
134
|
+
}
|
|
135
|
+
if (omitted > 0)
|
|
136
|
+
lines.push(`- … ${omitted} older notes; search them with the \`memory\` tool.`);
|
|
137
|
+
return lines.join("\n");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function slug(value) {
|
|
141
|
+
return (value
|
|
142
|
+
.toLowerCase()
|
|
143
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
144
|
+
.replace(/^-|-$/g, "")
|
|
145
|
+
.slice(0, 40) || "note");
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/agent/memory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Message } from "../core/types.ts";
|
|
2
|
+
import type { ToolResult } from "../tools/types.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Cap on a single tool result, in characters.
|
|
5
|
+
*
|
|
6
|
+
* The built-in tools cap their own output, but MCP and user-supplied tools do
|
|
7
|
+
* not — and a tool result enters the transcript *after* the compaction check
|
|
8
|
+
* has already run for this turn, so compaction cannot save a window that one
|
|
9
|
+
* oversized result just filled.
|
|
10
|
+
*/
|
|
11
|
+
export declare const DEFAULT_TOOL_RESULT_LIMIT = 30000;
|
|
12
|
+
export interface OverflowResult {
|
|
13
|
+
/** What goes into the transcript. */
|
|
14
|
+
result: ToolResult;
|
|
15
|
+
/**
|
|
16
|
+
* A synthetic message holding the full output, to be archived. `recall`
|
|
17
|
+
* searches archived messages, so the overflow is retrievable by keyword
|
|
18
|
+
* with no extra machinery.
|
|
19
|
+
*/
|
|
20
|
+
archived?: Message;
|
|
21
|
+
originalChars: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Trim a tool result that would otherwise dominate the context window.
|
|
25
|
+
*
|
|
26
|
+
* The full text is not discarded — it is handed back for archiving, and the
|
|
27
|
+
* truncation notice tells the model how to get it. Silently dropping the tail
|
|
28
|
+
* is worse than truncating loudly: the model cannot tell that it is reasoning
|
|
29
|
+
* from partial output.
|
|
30
|
+
*/
|
|
31
|
+
export declare function capToolResult(result: ToolResult, opts: {
|
|
32
|
+
toolName: string;
|
|
33
|
+
toolCallId: string;
|
|
34
|
+
limit?: number;
|
|
35
|
+
}): OverflowResult;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { estimateStringTokens } from "../core/tokens.js";
|
|
2
|
+
/**
|
|
3
|
+
* Cap on a single tool result, in characters.
|
|
4
|
+
*
|
|
5
|
+
* The built-in tools cap their own output, but MCP and user-supplied tools do
|
|
6
|
+
* not — and a tool result enters the transcript *after* the compaction check
|
|
7
|
+
* has already run for this turn, so compaction cannot save a window that one
|
|
8
|
+
* oversized result just filled.
|
|
9
|
+
*/
|
|
10
|
+
export const DEFAULT_TOOL_RESULT_LIMIT = 30_000;
|
|
11
|
+
/** Keep the head and the tail: errors surface at both ends of long output. */
|
|
12
|
+
const HEAD_FRACTION = 0.7;
|
|
13
|
+
/**
|
|
14
|
+
* Trim a tool result that would otherwise dominate the context window.
|
|
15
|
+
*
|
|
16
|
+
* The full text is not discarded — it is handed back for archiving, and the
|
|
17
|
+
* truncation notice tells the model how to get it. Silently dropping the tail
|
|
18
|
+
* is worse than truncating loudly: the model cannot tell that it is reasoning
|
|
19
|
+
* from partial output.
|
|
20
|
+
*/
|
|
21
|
+
export function capToolResult(result, opts) {
|
|
22
|
+
const limit = opts.limit ?? DEFAULT_TOOL_RESULT_LIMIT;
|
|
23
|
+
const textBlocks = result.content.filter((block) => block.type === "text");
|
|
24
|
+
const total = textBlocks.reduce((sum, block) => sum + block.text.length, 0);
|
|
25
|
+
if (total <= limit)
|
|
26
|
+
return { result, originalChars: total };
|
|
27
|
+
const full = textBlocks.map((block) => block.text).join("\n");
|
|
28
|
+
const headChars = Math.floor(limit * HEAD_FRACTION);
|
|
29
|
+
const tailChars = limit - headChars;
|
|
30
|
+
const head = full.slice(0, headChars);
|
|
31
|
+
const tail = full.slice(full.length - tailChars);
|
|
32
|
+
const omitted = full.length - headChars - tailChars;
|
|
33
|
+
const notice = `\n\n… ${omitted.toLocaleString()} characters omitted ` +
|
|
34
|
+
`(${estimateStringTokens(String(omitted))} · full output was ${full.length.toLocaleString()} chars). ` +
|
|
35
|
+
`The complete output is archived — search it with the \`recall\` tool ` +
|
|
36
|
+
`(try \`recall\` with a keyword, or the tool name "${opts.toolName}").\n\n`;
|
|
37
|
+
// Non-text blocks (images) pass through: they are already bounded, and
|
|
38
|
+
// dropping them would silently change what the model sees.
|
|
39
|
+
const passthrough = result.content.filter((block) => block.type !== "text");
|
|
40
|
+
return {
|
|
41
|
+
result: {
|
|
42
|
+
...result,
|
|
43
|
+
content: [{ type: "text", text: head + notice + tail }, ...passthrough],
|
|
44
|
+
metadata: {
|
|
45
|
+
...result.metadata,
|
|
46
|
+
truncated: true,
|
|
47
|
+
originalChars: full.length,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
archived: {
|
|
51
|
+
role: "user",
|
|
52
|
+
content: [
|
|
53
|
+
{
|
|
54
|
+
type: "text",
|
|
55
|
+
text: `<tool-output tool="${opts.toolName}" call="${opts.toolCallId}" chars="${full.length}">\n` +
|
|
56
|
+
`${full}\n</tool-output>`,
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
meta: {
|
|
60
|
+
timestamp: Date.now(),
|
|
61
|
+
synthetic: true,
|
|
62
|
+
overflowOf: opts.toolCallId,
|
|
63
|
+
toolName: opts.toolName,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
originalChars: full.length,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=overflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overflow.js","sourceRoot":"","sources":["../../src/agent/overflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAIzD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,MAAM,CAAC;AAEhD,8EAA8E;AAC9E,MAAM,aAAa,GAAG,GAAG,CAAC;AAc1B;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAC3B,MAAkB,EAClB,IAIC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,yBAAyB,CAAC;IAEtD,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CACtC,CAAC,KAAK,EAA2C,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,CAC1E,CAAC;IACF,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC5E,IAAI,KAAK,IAAI,KAAK;QAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;IAE5D,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9D,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,aAAa,CAAC,CAAC;IACpD,MAAM,SAAS,GAAG,KAAK,GAAG,SAAS,CAAC;IAEpC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;IAEpD,MAAM,MAAM,GACV,SAAS,OAAO,CAAC,cAAc,EAAE,sBAAsB;QACvD,IAAI,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,sBAAsB,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,WAAW;QACtG,uEAAuE;QACvE,qDAAqD,IAAI,CAAC,QAAQ,SAAS,CAAC;IAE9E,uEAAuE;IACvE,2DAA2D;IAC3D,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;IAE5E,OAAO;QACL,MAAM,EAAE;YACN,GAAG,MAAM;YACT,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,EAAE,EAAE,GAAG,WAAW,CAAC;YACvE,QAAQ,EAAE;gBACR,GAAG,MAAM,CAAC,QAAQ;gBAClB,SAAS,EAAE,IAAI;gBACf,aAAa,EAAE,IAAI,CAAC,MAAM;aAC3B;SACF;QACD,QAAQ,EAAE;YACR,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EACF,sBAAsB,IAAI,CAAC,QAAQ,WAAW,IAAI,CAAC,UAAU,YAAY,IAAI,CAAC,MAAM,MAAM;wBAC1F,GAAG,IAAI,kBAAkB;iBAC5B;aACF;YACD,IAAI,EAAE;gBACJ,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;gBACrB,SAAS,EAAE,IAAI;gBACf,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,QAAQ,EAAE,IAAI,CAAC,QAAQ;aACxB;SACF;QACD,aAAa,EAAE,IAAI,CAAC,MAAM;KAC3B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import type { Tool } from "../tools/types.ts";
|
|
2
|
+
import type { SettingsScope, SettingsStore } from "./settings.ts";
|
|
3
|
+
/**
|
|
4
|
+
* `default` — ask before anything that mutates state.
|
|
5
|
+
* `acceptEdits` — auto-approve file edits, still ask for the rest.
|
|
6
|
+
* `plan` — read-only; deny every mutating tool outright.
|
|
7
|
+
* `auto` — approve edits and routine commands, ask for the rest.
|
|
8
|
+
* `bypassPermissions` — approve everything (use only in sandboxes).
|
|
9
|
+
*/
|
|
10
|
+
export type PermissionMode = "default" | "acceptEdits" | "plan" | "auto" | "bypassPermissions";
|
|
11
|
+
/** Every mode, in the order the picker shows them. */
|
|
12
|
+
export declare const PERMISSION_MODES: PermissionMode[];
|
|
13
|
+
export declare const MODE_LABELS: Record<PermissionMode, string>;
|
|
14
|
+
export declare const MODE_DESCRIPTIONS: Record<PermissionMode, string>;
|
|
15
|
+
export type PermissionBehavior = "allow" | "deny" | "ask";
|
|
16
|
+
export interface PermissionRequest {
|
|
17
|
+
toolName: string;
|
|
18
|
+
input: unknown;
|
|
19
|
+
/** Human-readable rendering, e.g. `Bash(npm test)`. */
|
|
20
|
+
title: string;
|
|
21
|
+
/** Why the harness is asking, when it is not obvious. */
|
|
22
|
+
reason?: string;
|
|
23
|
+
suggestions?: string[];
|
|
24
|
+
}
|
|
25
|
+
export interface PermissionDecision {
|
|
26
|
+
behavior: PermissionBehavior;
|
|
27
|
+
/** Shown to the user on ask, and returned to the model on deny. */
|
|
28
|
+
message?: string;
|
|
29
|
+
/** Which rule decided this, for `/permissions` and debugging. */
|
|
30
|
+
rule?: string;
|
|
31
|
+
/**
|
|
32
|
+
* The trail entry this evaluation wrote, so the answer to an `ask` can be
|
|
33
|
+
* recorded against it. Present on every decision even though only `ask` is
|
|
34
|
+
* followed by an answer — a caller that has to check whether it got an id
|
|
35
|
+
* would eventually forget to.
|
|
36
|
+
*/
|
|
37
|
+
auditId: string;
|
|
38
|
+
}
|
|
39
|
+
/** Prompts the user. Returning `false` denies the call. */
|
|
40
|
+
export type PermissionPrompt = (request: PermissionRequest) => Promise<boolean | PermissionAnswer>;
|
|
41
|
+
export interface PermissionAnswer {
|
|
42
|
+
allow: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Persist the decision. `"session"` keeps it in memory; the other scopes
|
|
45
|
+
* write it to settings so the same command is not re-approved tomorrow.
|
|
46
|
+
*/
|
|
47
|
+
remember?: boolean | "session" | SettingsScope;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* One line of the trail.
|
|
51
|
+
*
|
|
52
|
+
* Two kinds rather than one flat record, because a decision to *ask* is not an
|
|
53
|
+
* outcome: the old single-line trail said "asked" and then never said what the
|
|
54
|
+
* person answered, which is the one thing anybody reading an audit afterwards
|
|
55
|
+
* wants to know. `id` pairs them.
|
|
56
|
+
*/
|
|
57
|
+
export type AuditEntry = DecisionAudit | AnswerAudit;
|
|
58
|
+
/** What the rules decided, before anyone was asked. */
|
|
59
|
+
export interface DecisionAudit {
|
|
60
|
+
kind: "decision";
|
|
61
|
+
/** Pairs this with the answer that follows, when it asked for one. */
|
|
62
|
+
id: string;
|
|
63
|
+
at: number;
|
|
64
|
+
tool: string;
|
|
65
|
+
behavior: PermissionBehavior;
|
|
66
|
+
rule?: string;
|
|
67
|
+
/** The most specific candidate, e.g. `Bash(npm test)`. */
|
|
68
|
+
principal?: string;
|
|
69
|
+
}
|
|
70
|
+
/** What came back from asking. Exactly one per `ask` decision. */
|
|
71
|
+
export interface AnswerAudit {
|
|
72
|
+
kind: "answer";
|
|
73
|
+
id: string;
|
|
74
|
+
at: number;
|
|
75
|
+
/**
|
|
76
|
+
* `unavailable` is the fail-closed case: nothing was able to ask, so the
|
|
77
|
+
* call was refused without a person ever seeing it. It reads very
|
|
78
|
+
* differently from a refusal somebody made, so it is not folded into `denied`.
|
|
79
|
+
*/
|
|
80
|
+
outcome: "allowed" | "denied" | "unavailable";
|
|
81
|
+
/** The rule that was persisted, when the answer was remembered. */
|
|
82
|
+
remembered?: string;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* A line off disk, or nothing.
|
|
86
|
+
*
|
|
87
|
+
* The trail is append-only and shared with future versions of this build, so a
|
|
88
|
+
* reader that cannot understand a line skips it rather than refusing the file.
|
|
89
|
+
*/
|
|
90
|
+
export declare function asAuditEntry(value: unknown): AuditEntry | undefined;
|
|
91
|
+
export interface PermissionRules {
|
|
92
|
+
/** Auto-approved, e.g. `Bash(npm run test:*)`, `read`, `mcp__github__*`. */
|
|
93
|
+
allow?: string[];
|
|
94
|
+
/** Always denied; takes precedence over `allow`. */
|
|
95
|
+
deny?: string[];
|
|
96
|
+
/** Always prompts, even in `acceptEdits`. */
|
|
97
|
+
ask?: string[];
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Decides whether a tool call may proceed.
|
|
101
|
+
*
|
|
102
|
+
* Rules are matched against `tool` and `Tool(argument)` forms so a policy can
|
|
103
|
+
* be as coarse as "all MCP GitHub tools" or as narrow as one bash command
|
|
104
|
+
* prefix.
|
|
105
|
+
*/
|
|
106
|
+
export declare class PermissionEngine {
|
|
107
|
+
private mode;
|
|
108
|
+
private readonly rules;
|
|
109
|
+
private readonly sessionAllow;
|
|
110
|
+
private settings?;
|
|
111
|
+
private readonly auditors;
|
|
112
|
+
constructor(mode?: PermissionMode, rules?: PermissionRules);
|
|
113
|
+
getMode(): PermissionMode;
|
|
114
|
+
setMode(mode: PermissionMode): void;
|
|
115
|
+
addRule(kind: keyof PermissionRules, pattern: string): void;
|
|
116
|
+
/**
|
|
117
|
+
* Remember an approval.
|
|
118
|
+
*
|
|
119
|
+
* The most specific candidate is what gets persisted — approving
|
|
120
|
+
* `Bash(npm test)` should not quietly grant every `Bash(...)`. Session-scoped
|
|
121
|
+
* approvals record every candidate because they expire with the process
|
|
122
|
+
* anyway.
|
|
123
|
+
*/
|
|
124
|
+
rememberAllow(tool: Tool, input: unknown, scope?: "session" | SettingsScope): Promise<string | undefined>;
|
|
125
|
+
/** Where persisted rules are written; absent means session-only. */
|
|
126
|
+
attachSettings(settings: SettingsStore): void;
|
|
127
|
+
listRules(): Required<PermissionRules> & {
|
|
128
|
+
sessionAllow: string[];
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Watch every decision. Returns the unsubscribe.
|
|
132
|
+
*
|
|
133
|
+
* A set rather than one slot, and a disposer rather than nothing, because
|
|
134
|
+
* there is more than one recorder now — the session's own trail and, when
|
|
135
|
+
* one is configured, a file — and because a recorder that can be attached
|
|
136
|
+
* but never detached is not something a switch can turn off.
|
|
137
|
+
*/
|
|
138
|
+
onDecision(auditor: (entry: AuditEntry) => void): () => void;
|
|
139
|
+
/**
|
|
140
|
+
* Tell the recorders, without letting one of them break a tool call.
|
|
141
|
+
*
|
|
142
|
+
* A throwing recorder is dropped on the floor: the trail exists to describe
|
|
143
|
+
* what the harness did, and a description that can veto the thing it
|
|
144
|
+
* describes is a new failure mode in the permission path.
|
|
145
|
+
*/
|
|
146
|
+
private announce;
|
|
147
|
+
evaluate(tool: Tool, input: unknown): PermissionDecision;
|
|
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: string, outcome: AnswerAudit["outcome"], remembered?: string): void;
|
|
157
|
+
private decide;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* The strings a rule may match: the bare tool name, and `Tool(arg)` using the
|
|
161
|
+
* argument that best identifies the call.
|
|
162
|
+
*/
|
|
163
|
+
export declare function candidates(tool: Tool, input: unknown): string[];
|
|
164
|
+
/**
|
|
165
|
+
* Throw for a denial that must abort the caller rather than be handed back to
|
|
166
|
+
* the model as a tool error. The agent loop reports denials in-band; this is
|
|
167
|
+
* for embedders driving tools directly.
|
|
168
|
+
*/
|
|
169
|
+
export declare function assertAllowed(decision: {
|
|
170
|
+
allowed: boolean;
|
|
171
|
+
reason?: string;
|
|
172
|
+
}, title: string): void;
|
|
173
|
+
/**
|
|
174
|
+
* Whether a call is risky enough that `auto` mode should still ask.
|
|
175
|
+
*
|
|
176
|
+
* This is a judgement about blast radius, not a security check. A rule in
|
|
177
|
+
* `deny` is the boundary you can rely on; this only decides which prompts
|
|
178
|
+
* `auto` mode is willing to skip. It answers "would a mistake here be
|
|
179
|
+
* expensive to undo?" and errs towards yes.
|
|
180
|
+
*/
|
|
181
|
+
export declare function isRisky(tool: Tool, input: unknown): boolean;
|