@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.
Files changed (256) hide show
  1. package/dist/agent/agent.d.ts +196 -0
  2. package/dist/agent/agent.js +758 -0
  3. package/dist/agent/agent.js.map +1 -0
  4. package/dist/agent/compaction.d.ts +68 -0
  5. package/dist/agent/compaction.js +179 -0
  6. package/dist/agent/compaction.js.map +1 -0
  7. package/dist/agent/hooks.d.ts +77 -0
  8. package/dist/agent/hooks.js +152 -0
  9. package/dist/agent/hooks.js.map +1 -0
  10. package/dist/agent/memory.d.ts +45 -0
  11. package/dist/agent/memory.js +147 -0
  12. package/dist/agent/memory.js.map +1 -0
  13. package/dist/agent/overflow.d.ts +35 -0
  14. package/dist/agent/overflow.js +69 -0
  15. package/dist/agent/overflow.js.map +1 -0
  16. package/dist/agent/permissions.d.ts +181 -0
  17. package/dist/agent/permissions.js +289 -0
  18. package/dist/agent/permissions.js.map +1 -0
  19. package/dist/agent/prompt.d.ts +25 -0
  20. package/dist/agent/prompt.js +63 -0
  21. package/dist/agent/prompt.js.map +1 -0
  22. package/dist/agent/session.d.ts +108 -0
  23. package/dist/agent/session.js +231 -0
  24. package/dist/agent/session.js.map +1 -0
  25. package/dist/agent/settings.d.ts +37 -0
  26. package/dist/agent/settings.js +113 -0
  27. package/dist/agent/settings.js.map +1 -0
  28. package/dist/agent/skills.d.ts +58 -0
  29. package/dist/agent/skills.js +225 -0
  30. package/dist/agent/skills.js.map +1 -0
  31. package/dist/agent/store.d.ts +308 -0
  32. package/dist/agent/store.js +596 -0
  33. package/dist/agent/store.js.map +1 -0
  34. package/dist/agent/subagent.d.ts +44 -0
  35. package/dist/agent/subagent.js +138 -0
  36. package/dist/agent/subagent.js.map +1 -0
  37. package/dist/agent/titles.d.ts +8 -0
  38. package/dist/agent/titles.js +23 -0
  39. package/dist/agent/titles.js.map +1 -0
  40. package/dist/agent/workspace.d.ts +60 -0
  41. package/dist/agent/workspace.js +156 -0
  42. package/dist/agent/workspace.js.map +1 -0
  43. package/dist/artifacts/publish.d.ts +94 -0
  44. package/dist/artifacts/publish.js +104 -0
  45. package/dist/artifacts/publish.js.map +1 -0
  46. package/dist/artifacts/store.d.ts +324 -0
  47. package/dist/artifacts/store.js +500 -0
  48. package/dist/artifacts/store.js.map +1 -0
  49. package/dist/auth/index.d.ts +13 -0
  50. package/dist/auth/index.js +10 -0
  51. package/dist/auth/index.js.map +1 -0
  52. package/dist/auth/manager.d.ts +83 -0
  53. package/dist/auth/manager.js +266 -0
  54. package/dist/auth/manager.js.map +1 -0
  55. package/dist/auth/oauth.d.ts +83 -0
  56. package/dist/auth/oauth.js +327 -0
  57. package/dist/auth/oauth.js.map +1 -0
  58. package/dist/auth/providers/account.d.ts +59 -0
  59. package/dist/auth/providers/account.js +282 -0
  60. package/dist/auth/providers/account.js.map +1 -0
  61. package/dist/auth/providers/anthropic.d.ts +24 -0
  62. package/dist/auth/providers/anthropic.js +209 -0
  63. package/dist/auth/providers/anthropic.js.map +1 -0
  64. package/dist/auth/providers/generic.d.ts +57 -0
  65. package/dist/auth/providers/generic.js +155 -0
  66. package/dist/auth/providers/generic.js.map +1 -0
  67. package/dist/auth/providers/official.d.ts +3 -0
  68. package/dist/auth/providers/official.js +90 -0
  69. package/dist/auth/providers/official.js.map +1 -0
  70. package/dist/auth/providers/ottoport.d.ts +3 -0
  71. package/dist/auth/providers/ottoport.js +60 -0
  72. package/dist/auth/providers/ottoport.js.map +1 -0
  73. package/dist/auth/store.d.ts +22 -0
  74. package/dist/auth/store.js +138 -0
  75. package/dist/auth/store.js.map +1 -0
  76. package/dist/auth/types.d.ts +167 -0
  77. package/dist/auth/types.js +16 -0
  78. package/dist/auth/types.js.map +1 -0
  79. package/dist/config.d.ts +197 -0
  80. package/dist/config.js +195 -0
  81. package/dist/config.js.map +1 -0
  82. package/dist/core/errors.d.ts +46 -0
  83. package/dist/core/errors.js +83 -0
  84. package/dist/core/errors.js.map +1 -0
  85. package/dist/core/tokens.d.ts +12 -0
  86. package/dist/core/tokens.js +56 -0
  87. package/dist/core/tokens.js.map +1 -0
  88. package/dist/core/types.d.ts +91 -0
  89. package/dist/core/types.js +49 -0
  90. package/dist/core/types.js.map +1 -0
  91. package/dist/harness.d.ts +302 -0
  92. package/dist/harness.js +695 -0
  93. package/dist/harness.js.map +1 -0
  94. package/dist/index.d.ts +32 -0
  95. package/dist/index.js +21 -0
  96. package/dist/index.js.map +1 -0
  97. package/dist/mcp/client.d.ts +55 -0
  98. package/dist/mcp/client.js +199 -0
  99. package/dist/mcp/client.js.map +1 -0
  100. package/dist/mcp/index.d.ts +13 -0
  101. package/dist/mcp/index.js +8 -0
  102. package/dist/mcp/index.js.map +1 -0
  103. package/dist/mcp/manager.d.ts +77 -0
  104. package/dist/mcp/manager.js +325 -0
  105. package/dist/mcp/manager.js.map +1 -0
  106. package/dist/mcp/oauth.d.ts +63 -0
  107. package/dist/mcp/oauth.js +302 -0
  108. package/dist/mcp/oauth.js.map +1 -0
  109. package/dist/mcp/protocol.d.ts +130 -0
  110. package/dist/mcp/protocol.js +14 -0
  111. package/dist/mcp/protocol.js.map +1 -0
  112. package/dist/mcp/registry.d.ts +52 -0
  113. package/dist/mcp/registry.js +100 -0
  114. package/dist/mcp/registry.js.map +1 -0
  115. package/dist/mcp/registry.json +293 -0
  116. package/dist/mcp/secrets.d.ts +20 -0
  117. package/dist/mcp/secrets.js +75 -0
  118. package/dist/mcp/secrets.js.map +1 -0
  119. package/dist/mcp/transport.d.ts +51 -0
  120. package/dist/mcp/transport.js +158 -0
  121. package/dist/mcp/transport.js.map +1 -0
  122. package/dist/paths.d.ts +37 -0
  123. package/dist/paths.js +63 -0
  124. package/dist/paths.js.map +1 -0
  125. package/dist/plugins.d.ts +55 -0
  126. package/dist/plugins.js +201 -0
  127. package/dist/plugins.js.map +1 -0
  128. package/dist/providers/builtin.d.ts +23 -0
  129. package/dist/providers/builtin.js +141 -0
  130. package/dist/providers/builtin.js.map +1 -0
  131. package/dist/providers/catalog.d.ts +129 -0
  132. package/dist/providers/catalog.js +254 -0
  133. package/dist/providers/catalog.js.map +1 -0
  134. package/dist/providers/catalog.json +472 -0
  135. package/dist/providers/discovery.d.ts +16 -0
  136. package/dist/providers/discovery.js +161 -0
  137. package/dist/providers/discovery.js.map +1 -0
  138. package/dist/providers/http.d.ts +24 -0
  139. package/dist/providers/http.js +139 -0
  140. package/dist/providers/http.js.map +1 -0
  141. package/dist/providers/index.d.ts +10 -0
  142. package/dist/providers/index.js +8 -0
  143. package/dist/providers/index.js.map +1 -0
  144. package/dist/providers/piai.d.ts +124 -0
  145. package/dist/providers/piai.js +551 -0
  146. package/dist/providers/piai.js.map +1 -0
  147. package/dist/providers/registry.d.ts +65 -0
  148. package/dist/providers/registry.js +195 -0
  149. package/dist/providers/registry.js.map +1 -0
  150. package/dist/providers/resilient.d.ts +46 -0
  151. package/dist/providers/resilient.js +117 -0
  152. package/dist/providers/resilient.js.map +1 -0
  153. package/dist/providers/types.d.ts +182 -0
  154. package/dist/providers/types.js +50 -0
  155. package/dist/providers/types.js.map +1 -0
  156. package/dist/schedule/cron.d.ts +35 -0
  157. package/dist/schedule/cron.js +125 -0
  158. package/dist/schedule/cron.js.map +1 -0
  159. package/dist/schedule/scheduler.d.ts +87 -0
  160. package/dist/schedule/scheduler.js +237 -0
  161. package/dist/schedule/scheduler.js.map +1 -0
  162. package/dist/storage/kv.d.ts +56 -0
  163. package/dist/storage/kv.js +173 -0
  164. package/dist/storage/kv.js.map +1 -0
  165. package/dist/storage/migrate.d.ts +11 -0
  166. package/dist/storage/migrate.js +93 -0
  167. package/dist/storage/migrate.js.map +1 -0
  168. package/dist/storage/project.d.ts +9 -0
  169. package/dist/storage/project.js +57 -0
  170. package/dist/storage/project.js.map +1 -0
  171. package/dist/storage/records.d.ts +32 -0
  172. package/dist/storage/records.js +48 -0
  173. package/dist/storage/records.js.map +1 -0
  174. package/dist/sync/client.d.ts +79 -0
  175. package/dist/sync/client.js +118 -0
  176. package/dist/sync/client.js.map +1 -0
  177. package/dist/sync/mirror.d.ts +85 -0
  178. package/dist/sync/mirror.js +389 -0
  179. package/dist/sync/mirror.js.map +1 -0
  180. package/dist/sync/protocol.d.ts +67 -0
  181. package/dist/sync/protocol.js +16 -0
  182. package/dist/sync/protocol.js.map +1 -0
  183. package/dist/sync/settings.d.ts +56 -0
  184. package/dist/sync/settings.js +60 -0
  185. package/dist/sync/settings.js.map +1 -0
  186. package/dist/tools/builtin/artifact.d.ts +16 -0
  187. package/dist/tools/builtin/artifact.js +134 -0
  188. package/dist/tools/builtin/artifact.js.map +1 -0
  189. package/dist/tools/builtin/bash.d.ts +3 -0
  190. package/dist/tools/builtin/bash.js +142 -0
  191. package/dist/tools/builtin/bash.js.map +1 -0
  192. package/dist/tools/builtin/fs.d.ts +19 -0
  193. package/dist/tools/builtin/fs.js +280 -0
  194. package/dist/tools/builtin/fs.js.map +1 -0
  195. package/dist/tools/builtin/index.d.ts +22 -0
  196. package/dist/tools/builtin/index.js +42 -0
  197. package/dist/tools/builtin/index.js.map +1 -0
  198. package/dist/tools/builtin/mcp-resource.d.ts +3 -0
  199. package/dist/tools/builtin/mcp-resource.js +220 -0
  200. package/dist/tools/builtin/mcp-resource.js.map +1 -0
  201. package/dist/tools/builtin/memory.d.ts +10 -0
  202. package/dist/tools/builtin/memory.js +76 -0
  203. package/dist/tools/builtin/memory.js.map +1 -0
  204. package/dist/tools/builtin/recall.d.ts +13 -0
  205. package/dist/tools/builtin/recall.js +163 -0
  206. package/dist/tools/builtin/recall.js.map +1 -0
  207. package/dist/tools/builtin/search.d.ts +3 -0
  208. package/dist/tools/builtin/search.js +240 -0
  209. package/dist/tools/builtin/search.js.map +1 -0
  210. package/dist/tools/builtin/skill.d.ts +10 -0
  211. package/dist/tools/builtin/skill.js +46 -0
  212. package/dist/tools/builtin/skill.js.map +1 -0
  213. package/dist/tools/builtin/task.d.ts +9 -0
  214. package/dist/tools/builtin/task.js +68 -0
  215. package/dist/tools/builtin/task.js.map +1 -0
  216. package/dist/tools/builtin/todo.d.ts +20 -0
  217. package/dist/tools/builtin/todo.js +63 -0
  218. package/dist/tools/builtin/todo.js.map +1 -0
  219. package/dist/tools/define.d.ts +19 -0
  220. package/dist/tools/define.js +39 -0
  221. package/dist/tools/define.js.map +1 -0
  222. package/dist/tools/index.d.ts +7 -0
  223. package/dist/tools/index.js +5 -0
  224. package/dist/tools/index.js.map +1 -0
  225. package/dist/tools/registry.d.ts +39 -0
  226. package/dist/tools/registry.js +101 -0
  227. package/dist/tools/registry.js.map +1 -0
  228. package/dist/tools/types.d.ts +112 -0
  229. package/dist/tools/types.js +22 -0
  230. package/dist/tools/types.js.map +1 -0
  231. package/dist/util/id.d.ts +9 -0
  232. package/dist/util/id.js +19 -0
  233. package/dist/util/id.js.map +1 -0
  234. package/dist/util/json.d.ts +25 -0
  235. package/dist/util/json.js +100 -0
  236. package/dist/util/json.js.map +1 -0
  237. package/dist/util/jwt.d.ts +74 -0
  238. package/dist/util/jwt.js +177 -0
  239. package/dist/util/jwt.js.map +1 -0
  240. package/dist/util/logger.d.ts +13 -0
  241. package/dist/util/logger.js +59 -0
  242. package/dist/util/logger.js.map +1 -0
  243. package/dist/util/queue.d.ts +26 -0
  244. package/dist/util/queue.js +106 -0
  245. package/dist/util/queue.js.map +1 -0
  246. package/dist/util/schema.d.ts +17 -0
  247. package/dist/util/schema.js +98 -0
  248. package/dist/util/schema.js.map +1 -0
  249. package/dist/util/shell.d.ts +20 -0
  250. package/dist/util/shell.js +24 -0
  251. package/dist/util/shell.js.map +1 -0
  252. package/official.json +4 -0
  253. package/package.json +38 -0
  254. package/skills/artifact-authoring/SKILL.md +81 -0
  255. package/skills/canvas/SKILL.md +85 -0
  256. 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;