@oni.bot/core 0.6.2 → 0.8.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 (129) hide show
  1. package/CHANGELOG.md +111 -0
  2. package/README.md +315 -200
  3. package/SECURITY.md +71 -0
  4. package/dist/checkpointers/sqlite.d.ts.map +1 -1
  5. package/dist/checkpointers/sqlite.js +20 -18
  6. package/dist/checkpointers/sqlite.js.map +1 -1
  7. package/dist/circuit-breaker.d.ts +20 -0
  8. package/dist/circuit-breaker.d.ts.map +1 -0
  9. package/dist/circuit-breaker.js +58 -0
  10. package/dist/circuit-breaker.js.map +1 -0
  11. package/dist/cli/index.d.ts +3 -0
  12. package/dist/cli/index.d.ts.map +1 -0
  13. package/dist/cli/index.js +32 -0
  14. package/dist/cli/index.js.map +1 -0
  15. package/dist/cli/init.d.ts +2 -0
  16. package/dist/cli/init.d.ts.map +1 -0
  17. package/dist/cli/init.js +17 -0
  18. package/dist/cli/init.js.map +1 -0
  19. package/dist/cli/templates.d.ts +8 -0
  20. package/dist/cli/templates.d.ts.map +1 -0
  21. package/dist/cli/templates.js +119 -0
  22. package/dist/cli/templates.js.map +1 -0
  23. package/dist/dlq.d.ts +17 -0
  24. package/dist/dlq.d.ts.map +1 -0
  25. package/dist/dlq.js +41 -0
  26. package/dist/dlq.js.map +1 -0
  27. package/dist/errors.d.ts +36 -1
  28. package/dist/errors.d.ts.map +1 -1
  29. package/dist/errors.js +163 -7
  30. package/dist/errors.js.map +1 -1
  31. package/dist/graph.d.ts +17 -0
  32. package/dist/graph.d.ts.map +1 -1
  33. package/dist/graph.js +13 -2
  34. package/dist/graph.js.map +1 -1
  35. package/dist/harness/agent-loop.d.ts +8 -0
  36. package/dist/harness/agent-loop.d.ts.map +1 -0
  37. package/dist/harness/agent-loop.js +327 -0
  38. package/dist/harness/agent-loop.js.map +1 -0
  39. package/dist/harness/context-compactor.d.ts +73 -0
  40. package/dist/harness/context-compactor.d.ts.map +1 -0
  41. package/dist/harness/context-compactor.js +162 -0
  42. package/dist/harness/context-compactor.js.map +1 -0
  43. package/dist/harness/harness.d.ts +41 -0
  44. package/dist/harness/harness.d.ts.map +1 -0
  45. package/dist/harness/harness.js +140 -0
  46. package/dist/harness/harness.js.map +1 -0
  47. package/dist/harness/hooks-engine.d.ts +71 -0
  48. package/dist/harness/hooks-engine.d.ts.map +1 -0
  49. package/dist/harness/hooks-engine.js +232 -0
  50. package/dist/harness/hooks-engine.js.map +1 -0
  51. package/dist/harness/index.d.ts +16 -0
  52. package/dist/harness/index.d.ts.map +1 -0
  53. package/dist/harness/index.js +19 -0
  54. package/dist/harness/index.js.map +1 -0
  55. package/dist/harness/safety-gate.d.ts +29 -0
  56. package/dist/harness/safety-gate.d.ts.map +1 -0
  57. package/dist/harness/safety-gate.js +72 -0
  58. package/dist/harness/safety-gate.js.map +1 -0
  59. package/dist/harness/skill-loader.d.ts +63 -0
  60. package/dist/harness/skill-loader.d.ts.map +1 -0
  61. package/dist/harness/skill-loader.js +214 -0
  62. package/dist/harness/skill-loader.js.map +1 -0
  63. package/dist/harness/todo-module.d.ts +39 -0
  64. package/dist/harness/todo-module.d.ts.map +1 -0
  65. package/dist/harness/todo-module.js +179 -0
  66. package/dist/harness/todo-module.js.map +1 -0
  67. package/dist/harness/types.d.ts +78 -0
  68. package/dist/harness/types.d.ts.map +1 -0
  69. package/dist/harness/types.js +9 -0
  70. package/dist/harness/types.js.map +1 -0
  71. package/dist/hitl/interrupt.d.ts.map +1 -1
  72. package/dist/hitl/interrupt.js +7 -6
  73. package/dist/hitl/interrupt.js.map +1 -1
  74. package/dist/index.d.ts +14 -3
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +12 -3
  77. package/dist/index.js.map +1 -1
  78. package/dist/models/google.d.ts.map +1 -1
  79. package/dist/models/google.js +7 -6
  80. package/dist/models/google.js.map +1 -1
  81. package/dist/models/index.d.ts +2 -0
  82. package/dist/models/index.d.ts.map +1 -1
  83. package/dist/models/index.js +1 -0
  84. package/dist/models/index.js.map +1 -1
  85. package/dist/models/openai.js +6 -1
  86. package/dist/models/openai.js.map +1 -1
  87. package/dist/models/openrouter.d.ts +13 -0
  88. package/dist/models/openrouter.d.ts.map +1 -0
  89. package/dist/models/openrouter.js +322 -0
  90. package/dist/models/openrouter.js.map +1 -0
  91. package/dist/prebuilt/tool-node.d.ts.map +1 -1
  92. package/dist/prebuilt/tool-node.js +0 -1
  93. package/dist/prebuilt/tool-node.js.map +1 -1
  94. package/dist/pregel.d.ts +11 -1
  95. package/dist/pregel.d.ts.map +1 -1
  96. package/dist/pregel.js +88 -7
  97. package/dist/pregel.js.map +1 -1
  98. package/dist/retry.d.ts.map +1 -1
  99. package/dist/retry.js +6 -1
  100. package/dist/retry.js.map +1 -1
  101. package/dist/store/index.d.ts +10 -0
  102. package/dist/store/index.d.ts.map +1 -1
  103. package/dist/store/index.js +15 -1
  104. package/dist/store/index.js.map +1 -1
  105. package/dist/stream-events.js +2 -2
  106. package/dist/stream-events.js.map +1 -1
  107. package/dist/streaming.d.ts +10 -0
  108. package/dist/streaming.d.ts.map +1 -1
  109. package/dist/streaming.js +28 -0
  110. package/dist/streaming.js.map +1 -1
  111. package/dist/swarm/graph.d.ts.map +1 -1
  112. package/dist/swarm/graph.js +0 -4
  113. package/dist/swarm/graph.js.map +1 -1
  114. package/dist/swarm/supervisor.d.ts.map +1 -1
  115. package/dist/swarm/supervisor.js +0 -4
  116. package/dist/swarm/supervisor.js.map +1 -1
  117. package/dist/telemetry.d.ts +41 -0
  118. package/dist/telemetry.d.ts.map +1 -0
  119. package/dist/telemetry.js +69 -0
  120. package/dist/telemetry.js.map +1 -0
  121. package/dist/testing/index.d.ts +33 -0
  122. package/dist/testing/index.d.ts.map +1 -0
  123. package/dist/testing/index.js +95 -0
  124. package/dist/testing/index.js.map +1 -0
  125. package/dist/types.d.ts +9 -0
  126. package/dist/types.d.ts.map +1 -1
  127. package/dist/types.js +1 -1
  128. package/dist/types.js.map +1 -1
  129. package/package.json +38 -5
@@ -0,0 +1,327 @@
1
+ // ============================================================
2
+ // @oni.bot/core/harness — AgentLoop
3
+ // Think → Act → Observe async generator driving each agent node
4
+ // ============================================================
5
+ import { generateId } from "./types.js";
6
+ // ─── agentLoop ─────────────────────────────────────────────────────────────
7
+ export async function* agentLoop(prompt, config) {
8
+ const sessionId = generateId("ses");
9
+ const threadId = config.threadId ?? generateId("thr");
10
+ const maxTurns = config.maxTurns ?? 10;
11
+ const messages = [];
12
+ let turn = 0;
13
+ // ── 1. Session Init ──────────────────────────────────────────────────
14
+ if (config.hooksEngine) {
15
+ const hookResult = await config.hooksEngine.fire("SessionStart", {
16
+ sessionId,
17
+ agentName: config.agentName,
18
+ tools: config.tools.map((t) => t.name),
19
+ });
20
+ if (hookResult?.additionalContext) {
21
+ messages.push({ role: "user", content: hookResult.additionalContext });
22
+ messages.push({ role: "assistant", content: "Context loaded." });
23
+ }
24
+ }
25
+ // Push user prompt
26
+ messages.push({ role: "user", content: prompt });
27
+ yield makeMessage("system", sessionId, turn, {
28
+ subtype: "init",
29
+ content: `Session ${sessionId} started for agent "${config.agentName}"`,
30
+ });
31
+ // ── 2. Build LLMToolDef[] ────────────────────────────────────────────
32
+ const llmTools = config.tools.map((t) => ({
33
+ name: t.name,
34
+ description: t.description,
35
+ parameters: t.schema,
36
+ }));
37
+ // Tool lookup map
38
+ const toolMap = new Map();
39
+ for (const t of config.tools) {
40
+ toolMap.set(t.name, t);
41
+ }
42
+ // ── 3. Main Loop ─────────────────────────────────────────────────────
43
+ while (turn < maxTurns) {
44
+ // ── 3a. Check AbortSignal ────────────────────────────────────────
45
+ if (config.signal?.aborted) {
46
+ yield makeMessage("error", sessionId, turn, {
47
+ content: "Agent loop aborted by signal",
48
+ });
49
+ return;
50
+ }
51
+ // ── 3b. Context Compaction ───────────────────────────────────────
52
+ if (config.compactor && config.compactor.shouldCompact(messages)) {
53
+ if (config.hooksEngine) {
54
+ await config.hooksEngine.fire("PreCompact", {
55
+ sessionId,
56
+ messageCount: messages.length,
57
+ estimatedTokens: config.compactor.estimateTokens(messages),
58
+ });
59
+ }
60
+ const compacted = await config.compactor.compact(messages);
61
+ messages.length = 0;
62
+ messages.push(...compacted);
63
+ yield makeMessage("system", sessionId, turn, {
64
+ subtype: "compact_boundary",
65
+ content: "Context compacted",
66
+ });
67
+ }
68
+ // ── 3c. Build system prompt ──────────────────────────────────────
69
+ let systemPrompt = config.systemPrompt;
70
+ if (config.env) {
71
+ const envLines = [];
72
+ if (config.env.cwd)
73
+ envLines.push(`Working directory: ${config.env.cwd}`);
74
+ if (config.env.platform)
75
+ envLines.push(`Platform: ${config.env.platform}`);
76
+ if (config.env.date)
77
+ envLines.push(`Date: ${config.env.date}`);
78
+ if (config.env.gitBranch)
79
+ envLines.push(`Git branch: ${config.env.gitBranch}`);
80
+ if (config.env.gitStatus)
81
+ envLines.push(`Git status: ${config.env.gitStatus}`);
82
+ if (envLines.length > 0) {
83
+ systemPrompt += `\n\n<env>\n${envLines.join("\n")}\n</env>`;
84
+ }
85
+ }
86
+ // ── 3d. Skill injection ──────────────────────────────────────────
87
+ if (config.skillLoader) {
88
+ const pending = config.skillLoader.getPendingInjection();
89
+ if (pending) {
90
+ messages.push({ role: "user", content: pending });
91
+ messages.push({ role: "assistant", content: "Skill instructions loaded." });
92
+ config.skillLoader.clearPendingInjection();
93
+ }
94
+ }
95
+ // ── 3e. Inference ────────────────────────────────────────────────
96
+ let response;
97
+ try {
98
+ response = await config.model.chat({
99
+ messages,
100
+ tools: llmTools.length > 0 ? llmTools : undefined,
101
+ systemPrompt,
102
+ maxTokens: 8192,
103
+ });
104
+ }
105
+ catch (err) {
106
+ yield makeMessage("error", sessionId, turn, {
107
+ content: `Inference error: ${err instanceof Error ? err.message : String(err)}`,
108
+ });
109
+ return;
110
+ }
111
+ // ── 3f. Yield assistant message ──────────────────────────────────
112
+ yield makeMessage("assistant", sessionId, turn, {
113
+ content: response.content,
114
+ toolCalls: response.toolCalls,
115
+ metadata: {
116
+ usage: response.usage,
117
+ stopReason: response.stopReason,
118
+ },
119
+ });
120
+ // ── 3g. Stop condition ───────────────────────────────────────────
121
+ if (!response.toolCalls || response.toolCalls.length === 0) {
122
+ if (config.hooksEngine) {
123
+ const stopResult = await config.hooksEngine.fire("Stop", {
124
+ sessionId,
125
+ response: response.content,
126
+ });
127
+ if (stopResult?.decision === "block") {
128
+ // Inject feedback as user message, increment turn, continue
129
+ const feedback = stopResult.reason ?? "Please provide a more complete response.";
130
+ messages.push({
131
+ role: "assistant",
132
+ content: response.content,
133
+ });
134
+ messages.push({
135
+ role: "user",
136
+ content: feedback,
137
+ });
138
+ turn++;
139
+ continue;
140
+ }
141
+ }
142
+ // Fire SessionEnd and yield result
143
+ if (config.hooksEngine) {
144
+ await config.hooksEngine.fire("SessionEnd", {
145
+ sessionId,
146
+ reason: "completed",
147
+ turns: turn + 1,
148
+ });
149
+ }
150
+ yield makeMessage("result", sessionId, turn, {
151
+ content: response.content,
152
+ metadata: { totalTurns: turn + 1 },
153
+ });
154
+ return;
155
+ }
156
+ // ── 3h. Tool execution ───────────────────────────────────────────
157
+ const toolResults = [];
158
+ for (const toolCall of response.toolCalls) {
159
+ // Fire PreToolUse hook
160
+ if (config.hooksEngine) {
161
+ const preResult = await config.hooksEngine.fire("PreToolUse", {
162
+ sessionId,
163
+ toolName: toolCall.name,
164
+ input: toolCall.args,
165
+ });
166
+ if (preResult?.decision === "deny") {
167
+ toolResults.push({
168
+ toolCallId: toolCall.id,
169
+ toolName: toolCall.name,
170
+ content: `Tool use denied: ${preResult.reason ?? "blocked by hook"}`,
171
+ isError: true,
172
+ });
173
+ continue;
174
+ }
175
+ // Apply modifiedInput if hook returned one
176
+ if (preResult?.modifiedInput) {
177
+ Object.assign(toolCall.args, preResult.modifiedInput);
178
+ }
179
+ }
180
+ // Safety gate check
181
+ if (config.safetyGate && config.safetyGate.requiresCheck(toolCall.name)) {
182
+ const safetyResult = await config.safetyGate.check({
183
+ id: toolCall.id,
184
+ name: toolCall.name,
185
+ args: toolCall.args,
186
+ });
187
+ if (!safetyResult.approved) {
188
+ toolResults.push({
189
+ toolCallId: toolCall.id,
190
+ toolName: toolCall.name,
191
+ content: `Tool blocked by safety gate: ${safetyResult.reason ?? "unsafe operation"}`,
192
+ isError: true,
193
+ });
194
+ continue;
195
+ }
196
+ }
197
+ // Find and execute tool
198
+ const toolDef = toolMap.get(toolCall.name);
199
+ if (!toolDef) {
200
+ toolResults.push({
201
+ toolCallId: toolCall.id,
202
+ toolName: toolCall.name,
203
+ content: `Unknown tool: ${toolCall.name}`,
204
+ isError: true,
205
+ });
206
+ continue;
207
+ }
208
+ const toolCtx = {
209
+ config: {},
210
+ store: null,
211
+ state: {},
212
+ emit: () => { },
213
+ sessionId,
214
+ threadId,
215
+ agentName: config.agentName,
216
+ turn,
217
+ signal: config.signal,
218
+ };
219
+ try {
220
+ const startTime = Date.now();
221
+ const rawResult = await toolDef.execute(toolCall.args, toolCtx);
222
+ const durationMs = Date.now() - startTime;
223
+ const content = typeof rawResult === "string" ? rawResult : JSON.stringify(rawResult);
224
+ // Fire PostToolUse
225
+ if (config.hooksEngine) {
226
+ await config.hooksEngine.fire("PostToolUse", {
227
+ sessionId,
228
+ toolName: toolCall.name,
229
+ input: toolCall.args,
230
+ output: rawResult,
231
+ durationMs,
232
+ });
233
+ }
234
+ toolResults.push({
235
+ toolCallId: toolCall.id,
236
+ toolName: toolCall.name,
237
+ content,
238
+ });
239
+ }
240
+ catch (err) {
241
+ const errorMsg = err instanceof Error ? err.message : String(err);
242
+ // Fire PostToolUseFailure
243
+ if (config.hooksEngine) {
244
+ await config.hooksEngine.fire("PostToolUseFailure", {
245
+ sessionId,
246
+ toolName: toolCall.name,
247
+ input: toolCall.args,
248
+ error: err instanceof Error ? err : errorMsg,
249
+ });
250
+ }
251
+ toolResults.push({
252
+ toolCallId: toolCall.id,
253
+ toolName: toolCall.name,
254
+ content: `Tool error: ${errorMsg}`,
255
+ isError: true,
256
+ });
257
+ }
258
+ }
259
+ // ── 3i. Update messages ──────────────────────────────────────────
260
+ messages.push({
261
+ role: "assistant",
262
+ content: response.content,
263
+ toolCalls: response.toolCalls,
264
+ });
265
+ for (const tr of toolResults) {
266
+ messages.push({
267
+ role: "tool",
268
+ content: tr.content,
269
+ toolCallId: tr.toolCallId,
270
+ name: tr.toolName,
271
+ });
272
+ }
273
+ // ── 3j. TODO reminder ────────────────────────────────────────────
274
+ if (config.todoModule) {
275
+ const todoState = config.todoModule.getState();
276
+ if (todoState.todos.length > 0) {
277
+ const reminder = config.todoModule.toContextString();
278
+ messages.push({ role: "user", content: `<system-reminder>\n${reminder}\n</system-reminder>` });
279
+ messages.push({ role: "assistant", content: "Noted." });
280
+ yield makeMessage("system", sessionId, turn, {
281
+ subtype: "todo_reminder",
282
+ content: reminder,
283
+ });
284
+ }
285
+ }
286
+ // ── 3k. Yield tool_result, increment turn ───────────────────────
287
+ yield makeMessage("tool_result", sessionId, turn, {
288
+ toolResults,
289
+ });
290
+ turn++;
291
+ }
292
+ // ── 4. maxTurns exceeded ─────────────────────────────────────────────
293
+ yield makeMessage("error", sessionId, turn, {
294
+ content: `Agent loop exceeded maxTurns (${maxTurns})`,
295
+ });
296
+ }
297
+ // ─── wrapWithAgentLoop ─────────────────────────────────────────────────────
298
+ export function wrapWithAgentLoop(config) {
299
+ return async (state) => {
300
+ const prompt = state.task ??
301
+ state.context ??
302
+ "";
303
+ let finalResult = "";
304
+ for await (const msg of agentLoop(prompt, config)) {
305
+ if (msg.type === "result") {
306
+ finalResult = msg.content ?? "";
307
+ }
308
+ }
309
+ return {
310
+ agentResults: {
311
+ ...(state.agentResults ?? {}),
312
+ [config.agentName]: finalResult,
313
+ },
314
+ };
315
+ };
316
+ }
317
+ // ─── Helpers ───────────────────────────────────────────────────────────────
318
+ function makeMessage(type, sessionId, turn, overrides = {}) {
319
+ return {
320
+ type,
321
+ sessionId,
322
+ turn,
323
+ timestamp: Date.now(),
324
+ ...overrides,
325
+ };
326
+ }
327
+ //# sourceMappingURL=agent-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-loop.js","sourceRoot":"","sources":["../../src/harness/agent-loop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import type { ONIModel, ONIModelMessage } from "../models/types.js";
2
+ export interface CompactorConfig {
3
+ /** Model used to generate the summary (typically a fast/cheap model) */
4
+ summaryModel: ONIModel;
5
+ /**
6
+ * Usage fraction at which compaction triggers.
7
+ * Research: performance degrades non-linearly above 65%.
8
+ * @default 0.68
9
+ */
10
+ threshold?: number;
11
+ /**
12
+ * Maximum token budget for the context window.
13
+ * @default 200_000
14
+ */
15
+ maxTokens?: number;
16
+ /**
17
+ * Approximate characters per token for estimation.
18
+ * @default 4
19
+ */
20
+ charsPerToken?: number;
21
+ /**
22
+ * Additional instructions included in the summarization prompt.
23
+ */
24
+ compactInstructions?: string;
25
+ }
26
+ export declare class ContextCompactor {
27
+ private readonly summaryModel;
28
+ private readonly threshold;
29
+ private readonly maxTokens;
30
+ private readonly charsPerToken;
31
+ private readonly compactInstructions?;
32
+ constructor(config: CompactorConfig);
33
+ /**
34
+ * Estimate token count from total character length of all messages.
35
+ * Uses ceiling to avoid underestimation.
36
+ */
37
+ estimateTokens(messages: ONIModelMessage[]): number;
38
+ /**
39
+ * Returns true when estimated usage exceeds the compaction threshold.
40
+ */
41
+ shouldCompact(messages: ONIModelMessage[]): boolean;
42
+ /**
43
+ * Current usage as a fraction of maxTokens (0–1+).
44
+ */
45
+ usageFraction(messages: ONIModelMessage[]): number;
46
+ /**
47
+ * Two-stage compaction:
48
+ * 1. Clear old tool results (less lossy)
49
+ * 2. Full summarization if still over threshold
50
+ *
51
+ * Returns the compacted message array.
52
+ */
53
+ compact(messages: ONIModelMessage[]): Promise<ONIModelMessage[]>;
54
+ /**
55
+ * Remove tool-role messages from the older portion of the conversation,
56
+ * keeping the most recent `keepRecent` messages intact.
57
+ */
58
+ private clearOldToolResults;
59
+ /**
60
+ * Build a history text from all messages and ask the summary model
61
+ * to produce a compact summary wrapped in <summary> tags.
62
+ */
63
+ private summarize;
64
+ /**
65
+ * Fallback when the summary model fails — return a minimal truncation notice.
66
+ */
67
+ private fallbackTruncation;
68
+ /**
69
+ * Get the character length of a message's content.
70
+ */
71
+ private contentLength;
72
+ }
73
+ //# sourceMappingURL=context-compactor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-compactor.d.ts","sourceRoot":"","sources":["../../src/harness/context-compactor.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAIpE,MAAM,WAAW,eAAe;IAC9B,wEAAwE;IACxE,YAAY,EAAE,QAAQ,CAAC;IAEvB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAID,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAW;IACxC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAS;gBAElC,MAAM,EAAE,eAAe;IAUnC;;;OAGG;IACH,cAAc,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,MAAM;IAQnD;;OAEG;IACH,aAAa,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,OAAO;IAInD;;OAEG;IACH,aAAa,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,MAAM;IAKlD;;;;;;OAMG;IACG,OAAO,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAsBtE;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAiB3B;;;OAGG;YACW,SAAS;IAmDvB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAe1B;;OAEG;IACH,OAAO,CAAC,aAAa;CAYtB"}
@@ -0,0 +1,162 @@
1
+ // ============================================================
2
+ // @oni.bot/core/harness — ContextCompactor
3
+ // Monitors token usage and compacts conversation history
4
+ // before context quality degrades (triggers at 68% by default).
5
+ // ============================================================
6
+ // ─── ContextCompactor ────────────────────────────────────────────────────────
7
+ export class ContextCompactor {
8
+ summaryModel;
9
+ threshold;
10
+ maxTokens;
11
+ charsPerToken;
12
+ compactInstructions;
13
+ constructor(config) {
14
+ this.summaryModel = config.summaryModel;
15
+ this.threshold = config.threshold ?? 0.68;
16
+ this.maxTokens = config.maxTokens ?? 200_000;
17
+ this.charsPerToken = config.charsPerToken ?? 4;
18
+ this.compactInstructions = config.compactInstructions;
19
+ }
20
+ // ── Public API ──────────────────────────────────────────────────────────
21
+ /**
22
+ * Estimate token count from total character length of all messages.
23
+ * Uses ceiling to avoid underestimation.
24
+ */
25
+ estimateTokens(messages) {
26
+ let totalChars = 0;
27
+ for (const msg of messages) {
28
+ totalChars += this.contentLength(msg);
29
+ }
30
+ return Math.ceil(totalChars / this.charsPerToken);
31
+ }
32
+ /**
33
+ * Returns true when estimated usage exceeds the compaction threshold.
34
+ */
35
+ shouldCompact(messages) {
36
+ return this.usageFraction(messages) >= this.threshold;
37
+ }
38
+ /**
39
+ * Current usage as a fraction of maxTokens (0–1+).
40
+ */
41
+ usageFraction(messages) {
42
+ if (messages.length === 0)
43
+ return 0;
44
+ return this.estimateTokens(messages) / this.maxTokens;
45
+ }
46
+ /**
47
+ * Two-stage compaction:
48
+ * 1. Clear old tool results (less lossy)
49
+ * 2. Full summarization if still over threshold
50
+ *
51
+ * Returns the compacted message array.
52
+ */
53
+ async compact(messages) {
54
+ if (!this.shouldCompact(messages)) {
55
+ return messages;
56
+ }
57
+ // Stage 1: clear old tool results
58
+ const cleaned = this.clearOldToolResults(messages);
59
+ if (!this.shouldCompact(cleaned)) {
60
+ return cleaned;
61
+ }
62
+ // Stage 2: full summarization
63
+ try {
64
+ return await this.summarize(cleaned);
65
+ }
66
+ catch {
67
+ // Fallback: simple truncation message
68
+ return this.fallbackTruncation();
69
+ }
70
+ }
71
+ // ── Private ─────────────────────────────────────────────────────────────
72
+ /**
73
+ * Remove tool-role messages from the older portion of the conversation,
74
+ * keeping the most recent `keepRecent` messages intact.
75
+ */
76
+ clearOldToolResults(messages, keepRecent = 10) {
77
+ if (messages.length <= keepRecent) {
78
+ return messages;
79
+ }
80
+ const cutoff = messages.length - keepRecent;
81
+ const olderPortion = messages.slice(0, cutoff);
82
+ const recentPortion = messages.slice(cutoff);
83
+ const filteredOlder = olderPortion.filter((m) => m.role !== "tool");
84
+ return [...filteredOlder, ...recentPortion];
85
+ }
86
+ /**
87
+ * Build a history text from all messages and ask the summary model
88
+ * to produce a compact summary wrapped in <summary> tags.
89
+ */
90
+ async summarize(messages) {
91
+ const historyText = messages
92
+ .map((m) => {
93
+ const text = typeof m.content === "string"
94
+ ? m.content
95
+ : m.content
96
+ .map((p) => p.text ?? "[image]")
97
+ .join(" ");
98
+ return `[${m.role}]: ${text}`;
99
+ })
100
+ .join("\n");
101
+ const instructions = this.compactInstructions
102
+ ? `\n\nAdditional instructions: ${this.compactInstructions}`
103
+ : "";
104
+ const systemMessage = {
105
+ role: "user",
106
+ content: `Summarize the following conversation history into a concise summary. ` +
107
+ `Preserve key decisions, code context, file paths, and task progress. ` +
108
+ `Wrap your summary in <summary></summary> tags.${instructions}\n\n` +
109
+ `--- CONVERSATION HISTORY ---\n${historyText}`,
110
+ };
111
+ const response = await this.summaryModel.chat({
112
+ messages: [systemMessage],
113
+ maxTokens: 2048,
114
+ });
115
+ const summaryMatch = /<summary>([\s\S]*?)<\/summary>/.exec(response.content);
116
+ const summaryText = summaryMatch
117
+ ? summaryMatch[1].trim()
118
+ : response.content.trim();
119
+ return [
120
+ {
121
+ role: "user",
122
+ content: `[Previous conversation was compacted. Summary of prior context]\n\n${summaryText}`,
123
+ },
124
+ {
125
+ role: "assistant",
126
+ content: "Context loaded.",
127
+ },
128
+ ];
129
+ }
130
+ /**
131
+ * Fallback when the summary model fails — return a minimal truncation notice.
132
+ */
133
+ fallbackTruncation() {
134
+ return [
135
+ {
136
+ role: "user",
137
+ content: "[The previous conversation was truncated due to context limits. " +
138
+ "Please continue from where we left off.]",
139
+ },
140
+ {
141
+ role: "assistant",
142
+ content: "Context loaded.",
143
+ },
144
+ ];
145
+ }
146
+ /**
147
+ * Get the character length of a message's content.
148
+ */
149
+ contentLength(msg) {
150
+ if (typeof msg.content === "string") {
151
+ return msg.content.length;
152
+ }
153
+ let len = 0;
154
+ for (const part of msg.content) {
155
+ if (part.text) {
156
+ len += part.text.length;
157
+ }
158
+ }
159
+ return len;
160
+ }
161
+ }
162
+ //# sourceMappingURL=context-compactor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-compactor.js","sourceRoot":"","sources":["../../src/harness/context-compactor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import { TodoModule } from "./todo-module.js";
2
+ import { HooksEngine } from "./hooks-engine.js";
3
+ import type { HooksConfig } from "./hooks-engine.js";
4
+ import { SkillLoader } from "./skill-loader.js";
5
+ import type { SkillDefinition } from "./skill-loader.js";
6
+ import type { HarnessConfig, AgentNodeConfig, LoopMessage } from "./types.js";
7
+ import type { ToolDefinition } from "../tools/types.js";
8
+ export interface SwarmAgentCompat {
9
+ name: string;
10
+ description: string;
11
+ capabilities: string[];
12
+ handler: (state: Record<string, unknown>) => Promise<Record<string, unknown>>;
13
+ }
14
+ export declare class ONIHarness {
15
+ private readonly config;
16
+ private readonly todoModule;
17
+ private readonly hooksEngine;
18
+ private readonly compactor;
19
+ private readonly safetyGate;
20
+ private readonly skillLoader;
21
+ private constructor();
22
+ static create(config: HarnessConfig): ONIHarness;
23
+ private buildLoopConfig;
24
+ run(prompt: string, agentConfig: AgentNodeConfig | string): AsyncGenerator<LoopMessage>;
25
+ runToResult(prompt: string, agentConfig: AgentNodeConfig | string): Promise<string>;
26
+ asNode<S extends Record<string, unknown> & {
27
+ task?: string;
28
+ context?: string;
29
+ agentResults?: Record<string, string>;
30
+ }>(agentConfig: AgentNodeConfig): (state: S) => Promise<Partial<S>>;
31
+ asSwarmAgent(name: string, soul: string, tools?: ToolDefinition[], opts?: {
32
+ description?: string;
33
+ capabilities?: string[];
34
+ }): SwarmAgentCompat;
35
+ getTodoModule(): TodoModule;
36
+ getHooksEngine(): HooksEngine;
37
+ getSkillLoader(): SkillLoader;
38
+ registerSkill(skill: SkillDefinition): void;
39
+ addHooks(config: HooksConfig): void;
40
+ }
41
+ //# sourceMappingURL=harness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../src/harness/harness.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EAEf,WAAW,EACZ,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAIxD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC/E;AAID,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmB;IAC7C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAE1C,OAAO;IA6CP,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,UAAU;IAMhD,OAAO,CAAC,eAAe;IAkChB,GAAG,CACR,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,eAAe,GAAG,MAAM,GACpC,cAAc,CAAC,WAAW,CAAC;IAYxB,WAAW,CACf,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,eAAe,GAAG,MAAM,GACpC,OAAO,CAAC,MAAM,CAAC;IAclB,MAAM,CACJ,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;QAClC,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACvC,EACD,WAAW,EAAE,eAAe,GAAG,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAOlE,YAAY,CACV,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,cAAc,EAAE,EACxB,IAAI,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,GACvD,gBAAgB;IAcnB,aAAa,IAAI,UAAU;IAI3B,cAAc,IAAI,WAAW;IAI7B,cAAc,IAAI,WAAW;IAM7B,aAAa,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI;IAI3C,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;CAGpC"}