@kernel.chat/kbot 3.99.20 → 3.99.22
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/README.md +11 -0
- package/dist/agent.js +23 -0
- package/dist/agents/producer.js +65 -23
- package/dist/auth.d.ts +2 -0
- package/dist/cli.js +7 -4
- package/dist/critic-gate.d.ts +29 -0
- package/dist/critic-gate.js +223 -0
- package/dist/critic-retrospect.d.ts +64 -0
- package/dist/critic-retrospect.js +279 -0
- package/dist/critic-taxonomy.d.ts +40 -0
- package/dist/critic-taxonomy.js +146 -0
- package/dist/growth.d.ts +37 -0
- package/dist/growth.js +272 -0
- package/dist/integrations/ableton.d.ts +30 -0
- package/dist/integrations/ableton.js +66 -0
- package/dist/integrations/kbot-control-client.d.ts +66 -0
- package/dist/integrations/kbot-control-client.js +224 -0
- package/dist/observer.d.ts +13 -0
- package/dist/observer.js +5 -1
- package/dist/planner/hierarchical/dag.d.ts +71 -0
- package/dist/planner/hierarchical/dag.js +97 -0
- package/dist/planner/hierarchical/persistence.d.ts +26 -0
- package/dist/planner/hierarchical/persistence.js +113 -0
- package/dist/planner/hierarchical/session-planner.d.ts +68 -0
- package/dist/planner/hierarchical/session-planner.js +141 -0
- package/dist/planner/hierarchical/types.d.ts +116 -0
- package/dist/planner/hierarchical/types.js +18 -0
- package/dist/tool-pipeline.d.ts +39 -1
- package/dist/tool-pipeline.js +109 -1
- package/dist/tools/ableton-listen.d.ts +2 -0
- package/dist/tools/ableton-listen.js +126 -0
- package/dist/tools/ableton.js +477 -12
- package/dist/tools/index.js +2 -0
- package/dist/tools/kbot-control.d.ts +2 -0
- package/dist/tools/kbot-control.js +63 -0
- package/package.json +1 -1
package/dist/tool-pipeline.d.ts
CHANGED
|
@@ -57,6 +57,40 @@ export declare function truncationMiddleware(maxSize?: number): ToolMiddleware;
|
|
|
57
57
|
* Emits tool_call_start and tool_call_end events.
|
|
58
58
|
*/
|
|
59
59
|
export declare function telemetryMiddleware(emit: (event: string, data: any) => void): ToolMiddleware;
|
|
60
|
+
/**
|
|
61
|
+
* Thresholds for outcome classification.
|
|
62
|
+
* Exported so downstream code (training, analytics) can mirror them.
|
|
63
|
+
*/
|
|
64
|
+
export declare const OUTCOME_EMPTY_THRESHOLD = 5;
|
|
65
|
+
export declare const OUTCOME_LARGE_THRESHOLD = 10240;
|
|
66
|
+
/**
|
|
67
|
+
* Classify a tool execution outcome from its ToolContext.
|
|
68
|
+
*
|
|
69
|
+
* Priority:
|
|
70
|
+
* 1. timeout — aborted with timeout reason
|
|
71
|
+
* 2. error — ctx.error is set (or aborted for any other reason)
|
|
72
|
+
* 3. empty — result present but shorter than OUTCOME_EMPTY_THRESHOLD chars
|
|
73
|
+
* 4. large — result byte length > OUTCOME_LARGE_THRESHOLD
|
|
74
|
+
* 5. success — anything else
|
|
75
|
+
*/
|
|
76
|
+
export declare function classifyOutcome(ctx: ToolContext): 'success' | 'error' | 'timeout' | 'empty' | 'large';
|
|
77
|
+
/**
|
|
78
|
+
* Observer middleware — writes an observation to ~/.kbot/observer/session.jsonl.
|
|
79
|
+
*
|
|
80
|
+
* Records the three fields that cannot be backfilled for action-token training:
|
|
81
|
+
* - durationMs: wall-clock time of tool execution
|
|
82
|
+
* - outcome: success | error | timeout | empty | large
|
|
83
|
+
* - resultSize: byte length of the serialized result
|
|
84
|
+
*
|
|
85
|
+
* Emits schema v2 events. Backward compatible — consumers that don't know
|
|
86
|
+
* about the new fields will ignore them (the tokenizer has fallbacks).
|
|
87
|
+
*
|
|
88
|
+
* Place this as the OUTERMOST middleware so duration captures the true
|
|
89
|
+
* wall-clock of the full pipeline (including timeout, truncation, fallback).
|
|
90
|
+
*/
|
|
91
|
+
export declare function observerMiddleware(sessionId: string, options?: {
|
|
92
|
+
enabled?: () => boolean;
|
|
93
|
+
}): ToolMiddleware;
|
|
60
94
|
/**
|
|
61
95
|
* Execution middleware — the actual tool call.
|
|
62
96
|
* This should be the last middleware in the pipeline.
|
|
@@ -100,7 +134,7 @@ export declare function fallbackMiddleware(rules: FallbackRule[], execute: (name
|
|
|
100
134
|
export declare function resourceAwareMiddleware(): ToolMiddleware;
|
|
101
135
|
/**
|
|
102
136
|
* Create the default pipeline with the standard middleware stack.
|
|
103
|
-
* Order: telemetry? → permission → hooks → resource → metrics → timeout → truncation → fallback? → execution
|
|
137
|
+
* Order: observer? → telemetry? → permission → hooks → resource → metrics → timeout → truncation → fallback? → execution
|
|
104
138
|
*/
|
|
105
139
|
export declare function createDefaultPipeline(deps: {
|
|
106
140
|
checkPermission: (name: string, args: any) => Promise<boolean>;
|
|
@@ -116,5 +150,9 @@ export declare function createDefaultPipeline(deps: {
|
|
|
116
150
|
recordMetrics: (name: string, duration: number, error?: string) => void;
|
|
117
151
|
emit?: (event: string, data: any) => void;
|
|
118
152
|
fallbackRules?: FallbackRule[];
|
|
153
|
+
/** If set, observerMiddleware is added as the outermost layer and writes to ~/.kbot/observer/session.jsonl. */
|
|
154
|
+
observerSessionId?: string;
|
|
155
|
+
/** Runtime gate for observer writes. */
|
|
156
|
+
observerEnabled?: () => boolean;
|
|
119
157
|
}): ToolPipeline;
|
|
120
158
|
//# sourceMappingURL=tool-pipeline.d.ts.map
|
package/dist/tool-pipeline.js
CHANGED
|
@@ -162,6 +162,111 @@ export function telemetryMiddleware(emit) {
|
|
|
162
162
|
});
|
|
163
163
|
};
|
|
164
164
|
}
|
|
165
|
+
// ── Outcome classification ──
|
|
166
|
+
/**
|
|
167
|
+
* Thresholds for outcome classification.
|
|
168
|
+
* Exported so downstream code (training, analytics) can mirror them.
|
|
169
|
+
*/
|
|
170
|
+
export const OUTCOME_EMPTY_THRESHOLD = 5; // result under this many chars → "empty"
|
|
171
|
+
export const OUTCOME_LARGE_THRESHOLD = 10_240; // result over this many bytes → "large"
|
|
172
|
+
/**
|
|
173
|
+
* Classify a tool execution outcome from its ToolContext.
|
|
174
|
+
*
|
|
175
|
+
* Priority:
|
|
176
|
+
* 1. timeout — aborted with timeout reason
|
|
177
|
+
* 2. error — ctx.error is set (or aborted for any other reason)
|
|
178
|
+
* 3. empty — result present but shorter than OUTCOME_EMPTY_THRESHOLD chars
|
|
179
|
+
* 4. large — result byte length > OUTCOME_LARGE_THRESHOLD
|
|
180
|
+
* 5. success — anything else
|
|
181
|
+
*/
|
|
182
|
+
export function classifyOutcome(ctx) {
|
|
183
|
+
// Timeout takes precedence over generic error because the error string is set too.
|
|
184
|
+
if (ctx.aborted && /timed?\s*out|timeout/i.test(ctx.abortReason ?? ctx.error ?? '')) {
|
|
185
|
+
return 'timeout';
|
|
186
|
+
}
|
|
187
|
+
if (ctx.error || ctx.aborted)
|
|
188
|
+
return 'error';
|
|
189
|
+
const result = ctx.result ?? '';
|
|
190
|
+
if (result.length < OUTCOME_EMPTY_THRESHOLD)
|
|
191
|
+
return 'empty';
|
|
192
|
+
if (Buffer.byteLength(result, 'utf8') > OUTCOME_LARGE_THRESHOLD)
|
|
193
|
+
return 'large';
|
|
194
|
+
return 'success';
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Observer middleware — writes an observation to ~/.kbot/observer/session.jsonl.
|
|
198
|
+
*
|
|
199
|
+
* Records the three fields that cannot be backfilled for action-token training:
|
|
200
|
+
* - durationMs: wall-clock time of tool execution
|
|
201
|
+
* - outcome: success | error | timeout | empty | large
|
|
202
|
+
* - resultSize: byte length of the serialized result
|
|
203
|
+
*
|
|
204
|
+
* Emits schema v2 events. Backward compatible — consumers that don't know
|
|
205
|
+
* about the new fields will ignore them (the tokenizer has fallbacks).
|
|
206
|
+
*
|
|
207
|
+
* Place this as the OUTERMOST middleware so duration captures the true
|
|
208
|
+
* wall-clock of the full pipeline (including timeout, truncation, fallback).
|
|
209
|
+
*/
|
|
210
|
+
export function observerMiddleware(sessionId, options = {}) {
|
|
211
|
+
return async (ctx, next) => {
|
|
212
|
+
const start = Date.now();
|
|
213
|
+
let threw = undefined;
|
|
214
|
+
try {
|
|
215
|
+
await next();
|
|
216
|
+
}
|
|
217
|
+
catch (err) {
|
|
218
|
+
// Capture so we can still log; rethrow after.
|
|
219
|
+
threw = err;
|
|
220
|
+
if (!ctx.error)
|
|
221
|
+
ctx.error = err instanceof Error ? err.message : String(err);
|
|
222
|
+
}
|
|
223
|
+
const durationMs = Date.now() - start;
|
|
224
|
+
// Allow runtime opt-out (e.g. user disabled observer).
|
|
225
|
+
if (options.enabled && !options.enabled()) {
|
|
226
|
+
if (threw)
|
|
227
|
+
throw threw;
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const result = ctx.result ?? '';
|
|
231
|
+
const resultSize = Buffer.byteLength(result, 'utf8');
|
|
232
|
+
const outcome = classifyOutcome(ctx);
|
|
233
|
+
try {
|
|
234
|
+
const { recordObservation } = await import('./observer.js');
|
|
235
|
+
// Extract a few "safe" arg fields for indexing — mirror what agent.ts does.
|
|
236
|
+
const a = (ctx.toolArgs ?? {});
|
|
237
|
+
const args = {};
|
|
238
|
+
if (typeof a.file_path === 'string')
|
|
239
|
+
args.file_path = a.file_path;
|
|
240
|
+
else if (typeof a.path === 'string')
|
|
241
|
+
args.path = a.path;
|
|
242
|
+
if (typeof a.command === 'string')
|
|
243
|
+
args.command = a.command.slice(0, 200);
|
|
244
|
+
if (typeof a.pattern === 'string')
|
|
245
|
+
args.pattern = a.pattern;
|
|
246
|
+
if (typeof a.url === 'string')
|
|
247
|
+
args.url = a.url;
|
|
248
|
+
if (typeof a.query === 'string')
|
|
249
|
+
args.query = a.query;
|
|
250
|
+
recordObservation({
|
|
251
|
+
schema: 2,
|
|
252
|
+
ts: new Date(start).toISOString(),
|
|
253
|
+
tool: ctx.toolName,
|
|
254
|
+
args,
|
|
255
|
+
result_length: result.length,
|
|
256
|
+
session: sessionId,
|
|
257
|
+
error: Boolean(ctx.error),
|
|
258
|
+
durationMs,
|
|
259
|
+
outcome,
|
|
260
|
+
resultSize,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
/* observer is non-critical — never break tool execution */
|
|
265
|
+
}
|
|
266
|
+
if (threw)
|
|
267
|
+
throw threw;
|
|
268
|
+
};
|
|
269
|
+
}
|
|
165
270
|
/**
|
|
166
271
|
* Execution middleware — the actual tool call.
|
|
167
272
|
* This should be the last middleware in the pipeline.
|
|
@@ -313,10 +418,13 @@ export function resourceAwareMiddleware() {
|
|
|
313
418
|
}
|
|
314
419
|
/**
|
|
315
420
|
* Create the default pipeline with the standard middleware stack.
|
|
316
|
-
* Order: telemetry? → permission → hooks → resource → metrics → timeout → truncation → fallback? → execution
|
|
421
|
+
* Order: observer? → telemetry? → permission → hooks → resource → metrics → timeout → truncation → fallback? → execution
|
|
317
422
|
*/
|
|
318
423
|
export function createDefaultPipeline(deps) {
|
|
319
424
|
const pipeline = new ToolPipeline();
|
|
425
|
+
if (deps.observerSessionId) {
|
|
426
|
+
pipeline.use(observerMiddleware(deps.observerSessionId, { enabled: deps.observerEnabled }));
|
|
427
|
+
}
|
|
320
428
|
if (deps.emit) {
|
|
321
429
|
pipeline.use(telemetryMiddleware(deps.emit));
|
|
322
430
|
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// ableton_listen — subscribe to real-time LOM events via kbot-control.
|
|
2
|
+
//
|
|
3
|
+
// AbletonOSC has no listener API. kbot-control implements listeners using
|
|
4
|
+
// native Max LiveAPI property callbacks, streamed over the TCP JSON-RPC
|
|
5
|
+
// connection as "notify" messages. This tool exposes that capability to
|
|
6
|
+
// the agent so it can subscribe to beat, playing_position, parameter
|
|
7
|
+
// changes, etc., and pull a buffered history for inspection.
|
|
8
|
+
import { registerTool } from './index.js';
|
|
9
|
+
import { KbotControlClient } from '../integrations/kbot-control-client.js';
|
|
10
|
+
// Per-path ring buffer of recent events so the agent can poll history.
|
|
11
|
+
const MAX_PER_PATH = 100;
|
|
12
|
+
const history = new Map();
|
|
13
|
+
const subscriptions = new Map();
|
|
14
|
+
function recordEvent(path, value) {
|
|
15
|
+
let buf = history.get(path);
|
|
16
|
+
if (!buf) {
|
|
17
|
+
buf = [];
|
|
18
|
+
history.set(path, buf);
|
|
19
|
+
}
|
|
20
|
+
buf.push({ path, value, at: Date.now() });
|
|
21
|
+
if (buf.length > MAX_PER_PATH)
|
|
22
|
+
buf.shift();
|
|
23
|
+
}
|
|
24
|
+
export function registerAbletonListenTool() {
|
|
25
|
+
registerTool({
|
|
26
|
+
name: 'ableton_listen',
|
|
27
|
+
description: 'Subscribe to real-time Ableton events via kbot-control.amxd. ' +
|
|
28
|
+
'Subscriptions stream over the persistent TCP connection and the tool buffers the last 100 events per path. ' +
|
|
29
|
+
'Use action="subscribe" with a LOM-ish path (e.g. "song.is_playing", "song.tempo", "tracks[0].output_meter_left"), ' +
|
|
30
|
+
'then action="history" to pull what came in. action="list" shows active subscriptions. ' +
|
|
31
|
+
'Requires kbot-control.amxd loaded in Ableton.',
|
|
32
|
+
parameters: {
|
|
33
|
+
action: {
|
|
34
|
+
type: 'string',
|
|
35
|
+
description: '"subscribe" | "unsubscribe" | "list" | "history" | "clear"',
|
|
36
|
+
required: true,
|
|
37
|
+
},
|
|
38
|
+
path: {
|
|
39
|
+
type: 'string',
|
|
40
|
+
description: 'LOM path for subscribe/unsubscribe/history. Examples: "song.is_playing", "song.tempo", "song.current_song_time", "tracks[0].mute".',
|
|
41
|
+
},
|
|
42
|
+
limit: {
|
|
43
|
+
type: 'number',
|
|
44
|
+
description: 'For "history": max events to return (default 25).',
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
tier: 'free',
|
|
48
|
+
timeout: 15_000,
|
|
49
|
+
async execute(args) {
|
|
50
|
+
const action = String(args.action || '').toLowerCase();
|
|
51
|
+
const path = args.path !== undefined ? String(args.path) : '';
|
|
52
|
+
try {
|
|
53
|
+
switch (action) {
|
|
54
|
+
case 'subscribe': {
|
|
55
|
+
if (!path)
|
|
56
|
+
return 'Error: subscribe needs a path.';
|
|
57
|
+
if (subscriptions.has(path))
|
|
58
|
+
return `Already subscribed to ${path}`;
|
|
59
|
+
const handler = (value) => recordEvent(path, value);
|
|
60
|
+
await KbotControlClient.get().subscribe(path, handler);
|
|
61
|
+
subscriptions.set(path, handler);
|
|
62
|
+
return `Subscribed to \`${path}\`. Events will be recorded; call action="history" to pull them.`;
|
|
63
|
+
}
|
|
64
|
+
case 'unsubscribe': {
|
|
65
|
+
if (!path)
|
|
66
|
+
return 'Error: unsubscribe needs a path.';
|
|
67
|
+
const handler = subscriptions.get(path);
|
|
68
|
+
if (!handler)
|
|
69
|
+
return `Not subscribed to ${path}`;
|
|
70
|
+
await KbotControlClient.get().unsubscribe(path, handler);
|
|
71
|
+
subscriptions.delete(path);
|
|
72
|
+
return `Unsubscribed from ${path}`;
|
|
73
|
+
}
|
|
74
|
+
case 'list': {
|
|
75
|
+
if (subscriptions.size === 0)
|
|
76
|
+
return 'No active subscriptions.';
|
|
77
|
+
const lines = ['## Active subscriptions', ''];
|
|
78
|
+
for (const p of subscriptions.keys()) {
|
|
79
|
+
const buf = history.get(p);
|
|
80
|
+
lines.push(`- \`${p}\` — ${buf ? buf.length : 0} events buffered`);
|
|
81
|
+
}
|
|
82
|
+
return lines.join('\n');
|
|
83
|
+
}
|
|
84
|
+
case 'history': {
|
|
85
|
+
if (!path) {
|
|
86
|
+
// Return summary of all paths
|
|
87
|
+
if (history.size === 0)
|
|
88
|
+
return 'No events recorded yet.';
|
|
89
|
+
const lines = ['## Event history summary', ''];
|
|
90
|
+
for (const [p, buf] of history.entries()) {
|
|
91
|
+
const last = buf[buf.length - 1];
|
|
92
|
+
lines.push(`- \`${p}\`: ${buf.length} events, last value = ${JSON.stringify(last?.value)}`);
|
|
93
|
+
}
|
|
94
|
+
return lines.join('\n');
|
|
95
|
+
}
|
|
96
|
+
const buf = history.get(path);
|
|
97
|
+
if (!buf || buf.length === 0)
|
|
98
|
+
return `No events recorded for \`${path}\`.`;
|
|
99
|
+
const limit = Number(args.limit) || 25;
|
|
100
|
+
const recent = buf.slice(-limit);
|
|
101
|
+
const lines = [`## \`${path}\` — last ${recent.length} events`, ''];
|
|
102
|
+
for (const ev of recent) {
|
|
103
|
+
const dt = new Date(ev.at).toISOString().slice(11, 23);
|
|
104
|
+
lines.push(`- ${dt} → ${JSON.stringify(ev.value)}`);
|
|
105
|
+
}
|
|
106
|
+
return lines.join('\n');
|
|
107
|
+
}
|
|
108
|
+
case 'clear': {
|
|
109
|
+
if (path) {
|
|
110
|
+
history.delete(path);
|
|
111
|
+
return `Cleared history for ${path}`;
|
|
112
|
+
}
|
|
113
|
+
history.clear();
|
|
114
|
+
return 'Cleared all history';
|
|
115
|
+
}
|
|
116
|
+
default:
|
|
117
|
+
return `Unknown action "${action}". Options: subscribe, unsubscribe, list, history, clear`;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch (e) {
|
|
121
|
+
return `ableton_listen error: ${e.message}`;
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=ableton-listen.js.map
|