@percepteye/agent-flywheel 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/LICENSE +21 -0
- package/README.md +368 -0
- package/cordis.patch.yml +11 -0
- package/openclaw.plugin.json +134 -0
- package/package.json +67 -0
- package/schema/flywheel-1.json +432 -0
- package/src/capture.js +733 -0
- package/src/classify.js +115 -0
- package/src/config.js +249 -0
- package/src/describe.js +355 -0
- package/src/dsh-classify.js +110 -0
- package/src/dsh.js +130 -0
- package/src/errors.js +37 -0
- package/src/evidence.js +109 -0
- package/src/execution-identity.js +444 -0
- package/src/host.js +63 -0
- package/src/http.js +249 -0
- package/src/index.js +380 -0
- package/src/mode.js +152 -0
- package/src/model-calls.js +214 -0
- package/src/policy.js +934 -0
- package/src/record.js +83 -0
- package/src/rollout.js +884 -0
- package/src/scope.js +242 -0
- package/src/session.js +42 -0
- package/src/trajectory.js +148 -0
- package/src/transport.js +403 -0
- package/src/turns.js +437 -0
- package/src/unattended.js +251 -0
- package/src/wire.js +182 -0
package/src/record.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One `after_tool_call` event becomes one trajectory record.
|
|
3
|
+
*
|
|
4
|
+
* There is no correlation state here, and that is the point of choosing this
|
|
5
|
+
* hook: the event already carries the arguments, the result, the error, the
|
|
6
|
+
* duration and the call id together. The alternative surface
|
|
7
|
+
* (`registerAgentEventSubscription`) splits the same facts across a
|
|
8
|
+
* `phase:"start"` event and a `phase:"result"` event, which forces a pending
|
|
9
|
+
* map keyed by `toolCallId` -- an unbounded one leaks inside a long-lived
|
|
10
|
+
* Gateway, and a bounded one silently drops calls under load. Neither failure
|
|
11
|
+
* exists on this path.
|
|
12
|
+
*/
|
|
13
|
+
import { classify } from "./classify.js";
|
|
14
|
+
import { extractEntityIds } from "./evidence.js";
|
|
15
|
+
import { toWire } from "./trajectory.js";
|
|
16
|
+
|
|
17
|
+
export function createRecorder({ writer }) {
|
|
18
|
+
let written = 0;
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
get written() {
|
|
22
|
+
return written;
|
|
23
|
+
},
|
|
24
|
+
/**
|
|
25
|
+
* @param {{toolName?:string, params?:object, toolCallId?:string,
|
|
26
|
+
* result?:unknown, error?:string, durationMs?:number}} event
|
|
27
|
+
* @param {{agentId?:string, runId?:string}} [ctx]
|
|
28
|
+
* @returns {boolean} whether a record was written
|
|
29
|
+
*/
|
|
30
|
+
handle(event, ctx = {}) {
|
|
31
|
+
if (!event || typeof event !== "object") return false;
|
|
32
|
+
const name = typeof event.toolName === "string" ? event.toolName : null;
|
|
33
|
+
// A record with no tool name is unattributable, and the contract rejects
|
|
34
|
+
// an empty name outright. Nothing useful can be said about it.
|
|
35
|
+
if (!name) return false;
|
|
36
|
+
|
|
37
|
+
const verdict = classify(name, event.error, event.result);
|
|
38
|
+
// Resolve the identity out of the result and let the result itself go.
|
|
39
|
+
// The record carries who was written, never what the response said.
|
|
40
|
+
const entity_ids = extractEntityIds(event.result);
|
|
41
|
+
const ok = writer.write(toWire({
|
|
42
|
+
name,
|
|
43
|
+
// The host passes the arguments the tool ACTUALLY ran with -- it
|
|
44
|
+
// resolves any `before_tool_call` adjustment before dispatching here.
|
|
45
|
+
// Recording the pre-adjustment arguments would describe a call that
|
|
46
|
+
// never happened.
|
|
47
|
+
args: event.params,
|
|
48
|
+
outcome: verdict.outcome,
|
|
49
|
+
status_code: null,
|
|
50
|
+
error: verdict.error,
|
|
51
|
+
error_class: verdict.error_class,
|
|
52
|
+
// Host-measured, so it includes queueing and approval waits that a
|
|
53
|
+
// timer started inside this plugin would miss.
|
|
54
|
+
latency_ms: typeof event.durationMs === "number"
|
|
55
|
+
? event.durationMs
|
|
56
|
+
: undefined,
|
|
57
|
+
// Which agent made the call. Absent means unattributed, which is the
|
|
58
|
+
// honest answer for a single agent -- never a claim that one agent
|
|
59
|
+
// made everything.
|
|
60
|
+
agent_name: typeof ctx.agentId === "string" ? ctx.agentId : undefined,
|
|
61
|
+
// The id the model assigned. It is the join key that says which
|
|
62
|
+
// completion this call followed, without clocks and without breaking
|
|
63
|
+
// under concurrent tools.
|
|
64
|
+
tool_call_id: typeof event.toolCallId === "string"
|
|
65
|
+
? event.toolCallId
|
|
66
|
+
: undefined,
|
|
67
|
+
entity_ids,
|
|
68
|
+
}), {
|
|
69
|
+
// Which TURN this call belongs to. Ignored by the default writer
|
|
70
|
+
// (a rollout has one directory); used by production capture, where a
|
|
71
|
+
// trajectory belongs to one turn. The host puts `runId` on both the
|
|
72
|
+
// event and the context, so a missing one here means the host gave us
|
|
73
|
+
// neither -- and an unattributable call must not land in some other
|
|
74
|
+
// turn's directory.
|
|
75
|
+
runId: (typeof event.runId === "string" && event.runId)
|
|
76
|
+
|| (typeof ctx.runId === "string" && ctx.runId)
|
|
77
|
+
|| null,
|
|
78
|
+
});
|
|
79
|
+
if (ok) written += 1;
|
|
80
|
+
return ok;
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|