worktrust 0.7.4 → 0.7.6
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 +2 -0
- package/log-session.mjs +3 -2
- package/package.json +1 -1
- package/preserve-lines.mjs +8 -0
- package/stretch-evidence.mjs +55 -2
- package/worktrust.mjs +1 -1
package/README.md
CHANGED
|
@@ -163,6 +163,8 @@ nothing it does not:
|
|
|
163
163
|
| `recovery` | of the tool calls that failed in the stretch, how many a later call of the same kind recovered, the middle time that took, how many were retried unchanged and how many with a different approach; inputs compared on this computer by digest, never kept (0.7.2) |
|
|
164
164
|
| `delegation` | the longest and the middle chain of actions the agent took on its own between your turns (or your stopping it), and how often it stopped to ask you a question or to have a plan approved (0.7.3) |
|
|
165
165
|
| `context`, `routing` | how often the context was compacted and which context commands you used (compact, clear, resume, context, model, memory; never a command of your own), how many models answered and how often the model changed (0.7.4) |
|
|
166
|
+
| `steering` | the moments you stepped in while the agent worked (stopping it, or a message mid-task), how many changed what the agent did next, and how many were followed by a passing check (0.7.5) |
|
|
167
|
+
| `tools`, `complexity` | how many different tools the agent used, and the stretch's task complexity C1 to C5 by the rule complexity/1 (points for layers touched, tools, subagents, duration, a recovered failure, kinds of check and a delivery; the rule is in stretch-evidence.mjs), with its points and the rule's version (0.7.6) |
|
|
166
168
|
| `collector_version` | the CLI version that measured it |
|
|
167
169
|
| `seq`, `prev`, `hash` | the line's place, the hash of the line before it, and its own hash |
|
|
168
170
|
|
package/log-session.mjs
CHANGED
|
@@ -211,7 +211,7 @@ const excluded = (path) => excludes.some((text) => text && path.includes(text));
|
|
|
211
211
|
const { CODEX_ROLLOUT = /(?!)/, codexIdOf = (file) => String(file).split(/[\\/]/).at(-1).replace(/\.jsonl$/, ""), antigravityIdOf = (file) => String(file).split(/[\\/]/).at(-4), codexLines, codexRolloutFiles = function* () {}, codexCwd = () => null, ANTIGRAVITY_TRANSCRIPT = /(?!)/, antigravityLines, antigravityRoots = () => [], antigravityTranscripts = function* () {}, antigravityContext = () => ({}), rememberAntigravity = () => null } = (await import("./transcript-readers.mjs").catch(() => null)) ?? {};
|
|
212
212
|
// THE STRETCH'S DERIVED RECORD (0.7.4: split from this file): tool calls named by kind, verification, recovery, delegation,
|
|
213
213
|
// context and routing, for the local archive only. A copy without the module still measures and sends exactly as before.
|
|
214
|
-
const { callOf = (block) => ({ id: block.id, kinds: [], family: String(block.name), digest: "" }), deriveStretch = () => ({}) } = (await import("./stretch-evidence.mjs").catch(() => null)) ?? {};
|
|
214
|
+
const { callOf = (block) => ({ id: block.id, kinds: [], family: String(block.name), digest: "" }), deriveStretch = () => ({}), complexityOf = null } = (await import("./stretch-evidence.mjs").catch(() => null)) ?? {};
|
|
215
215
|
const { DATABASE_SESSION = /(?!)/, databaseLines = () => null, databaseSessions = function* () {} } = (await import("./session-databases.mjs").catch(() => null)) ?? {};
|
|
216
216
|
const DATABASE_CLIENTS = ["hermes", "goose", "opencode", "openclaw", "cursor", "copilot"]; // the registry's keys, the name each line carries
|
|
217
217
|
const LIVE_SOURCE = new RegExp(`^(codex|antigravity|${DATABASE_CLIENTS.join("|")}):`); // a sweep entry's id → the client it names (every database client, 0.6.16)
|
|
@@ -623,7 +623,8 @@ function payloadFor(stretch) {
|
|
|
623
623
|
at: new Date(stretch.to).toISOString(),
|
|
624
624
|
};
|
|
625
625
|
// WHAT STAYS ON THIS COMPUTER (0.7.1): the stretch's derived record rides beside the payload for the local archive, never in it.
|
|
626
|
-
|
|
626
|
+
// 0.7.6: and the stretch's complexity class, read from what the hook knows here (layers, subagents, duration) and the record.
|
|
627
|
+
if (stretch.derived) DERIVED.set(payload, { ...stretch.derived, ...(complexityOf ? { complexity: complexityOf(stretch.derived, { layers: Math.max(layers.length, layer ? 1 : 0), agentRuns: stretch.agents?.runs ?? 0, agentPeak: stretch.agents?.peak ?? 0, seconds: stretch.seconds }) } : {}) });
|
|
627
628
|
return payload;
|
|
628
629
|
}
|
|
629
630
|
|
package/package.json
CHANGED
package/preserve-lines.mjs
CHANGED
|
@@ -75,6 +75,14 @@ export function archiveLine(entry, behaviour = null, { ids = {}, collector } = {
|
|
|
75
75
|
if (entry.context.compactions > 0 || commands.length > 0) line.context = { compactions: entry.context.compactions, ...(commands.length ? { commands: Object.fromEntries(commands) } : {}) };
|
|
76
76
|
}
|
|
77
77
|
if (entry.routing && typeof entry.routing === "object" && whole(entry.routing.models) && whole(entry.routing.switches) && entry.routing.models > 0) line.routing = { models: entry.routing.models, switches: entry.routing.switches };
|
|
78
|
+
// STEERING (0.7.5): moments the person stepped in, how many changed the agent's course, how many led to a passing check.
|
|
79
|
+
if (entry.steering && typeof entry.steering === "object") {
|
|
80
|
+
const { moments, changed_course: changed, effective } = entry.steering;
|
|
81
|
+
if ([moments, changed, effective].every(whole) && moments > 0 && changed <= moments && effective <= changed) line.steering = { moments, changed_course: changed, effective };
|
|
82
|
+
}
|
|
83
|
+
// TOOL BREADTH AND COMPLEXITY (0.7.6): distinct tools; a class C1–C5 with its points and the rule's version.
|
|
84
|
+
if (whole(entry.tools) && entry.tools > 0) line.tools = entry.tools;
|
|
85
|
+
if (entry.complexity && /^C[1-5]$/.test(entry.complexity.class ?? "") && whole(entry.complexity.points) && /^complexity\/\d+$/.test(entry.complexity.rule ?? "")) line.complexity = { class: entry.complexity.class, points: entry.complexity.points, rule: entry.complexity.rule };
|
|
78
86
|
// The behaviour signals of this stretch (0.7.0): keys of the counter's rubric and whole counts, never a word of a turn.
|
|
79
87
|
if (behaviour && behaviour.signals && typeof behaviour.analyzer_version === "string" && /^counter@\d+\.\d+\.\d+$/.test(behaviour.analyzer_version)) {
|
|
80
88
|
const kept = Object.entries(behaviour.signals).filter(([key, count]) => SIGNAL.test(key) && Number.isInteger(count) && count > 0).sort();
|
package/stretch-evidence.mjs
CHANGED
|
@@ -142,8 +142,61 @@ function routingOf(messages) {
|
|
|
142
142
|
return models.size > 0 ? { models: models.size, switches } : null;
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
+
/**
|
|
146
|
+
* STEERING THAT WORKED (0.7.5; framework L4: "effective steering, not the raw number of interventions"). A moment the
|
|
147
|
+
* person stepped in while the agent was working: they stopped it, or their turn arrived right after a tool call or its
|
|
148
|
+
* result. It CHANGED COURSE when the agent's next action differed from its last one before (by kind, or by input
|
|
149
|
+
* digest), and it was EFFECTIVE when, after it, a check passed in the same stretch. Null when nobody stepped in.
|
|
150
|
+
*/
|
|
151
|
+
function steeringOf(messages, { isHumanTurn, INTERRUPTED, textOf }) {
|
|
152
|
+
const moments = [];
|
|
153
|
+
let lastCall = null, prev = null;
|
|
154
|
+
const passedAfter = new Map(), results = new Map();
|
|
155
|
+
for (const message of messages) for (const result of message.results ?? []) results.set(result.id, result.failed);
|
|
156
|
+
for (const message of messages) {
|
|
157
|
+
if (message.bridge) { prev = message; continue; }
|
|
158
|
+
const person = message.type === "user" && !message.meta && message.kind !== "tool_result";
|
|
159
|
+
const stopped = person && INTERRUPTED.test(textOf(message.content).trim());
|
|
160
|
+
const midTask = person && !stopped && isHumanTurn(message.content) && prev && (prev.kind === "tool_result" || prev.toolUse);
|
|
161
|
+
if (stopped || midTask) moments.push({ before: lastCall, after: null, effective: false });
|
|
162
|
+
for (const call of message.calls ?? []) {
|
|
163
|
+
for (const moment of moments) if (moment.after === null) moment.after = call;
|
|
164
|
+
if (CHECK_KINDS.some((kind) => call.kinds.includes(kind)) && results.get(call.id) === false) for (const moment of moments) if (moment.after) moment.effective = true;
|
|
165
|
+
lastCall = call;
|
|
166
|
+
}
|
|
167
|
+
prev = message;
|
|
168
|
+
}
|
|
169
|
+
if (moments.length === 0) return null;
|
|
170
|
+
const changed = moments.filter((moment) => moment.after && (!moment.before || moment.after.family !== moment.before.family || moment.after.digest !== moment.before.digest));
|
|
171
|
+
return { moments: moments.length, changed_course: changed.length, effective: changed.filter((moment) => moment.effective).length };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* TASK COMPLEXITY C1 TO C5 (0.7.6; framework §14 "Task complexity C1-C5"), by a VERSIONED rule, `complexity/1`, every
|
|
176
|
+
* point of it stated here and in the README so anyone can recount a class. Proposed, not calibrated: it weighs evidence,
|
|
177
|
+
* it is not a score of anyone.
|
|
178
|
+
* layers touched ≤1: 0 · 2: 1 · 3+: 2 distinct tools used <3: 0 · 3–6: 1 · 7+: 2
|
|
179
|
+
* subagents none: 0 · ran: 1 · 3+ at once: 2
|
|
180
|
+
* measured duration <30 min: 0 · 30–120 min: 1 · >120 min: 2
|
|
181
|
+
* a failure recovered 1 kinds of check run 0: 0 · 1–2: 1 · 3+: 2
|
|
182
|
+
* something delivered 1
|
|
183
|
+
* points 0–1 → C1 · 2–3 → C2 · 4–6 → C3 · 7–9 → C4 · 10+ → C5
|
|
184
|
+
*/
|
|
185
|
+
export const COMPLEXITY_RULE = "complexity/1";
|
|
186
|
+
export function complexityOf(derived, { layers = 0, agentRuns = 0, agentPeak = 0, seconds = null } = {}) {
|
|
187
|
+
const step = (value, low, high) => (value >= high ? 2 : value >= low ? 1 : 0);
|
|
188
|
+
const points = step(layers, 2, 3) + step(derived.tools ?? 0, 3, 7) + (agentPeak >= 3 ? 2 : agentRuns > 0 ? 1 : 0)
|
|
189
|
+
+ (seconds === null ? 0 : seconds > 7200 ? 2 : seconds >= 1800 ? 1 : 0) + ((derived.recovery?.recovered ?? 0) > 0 ? 1 : 0)
|
|
190
|
+
+ step(Object.keys(derived.verification ?? {}).length, 1, 3) + (derived.delivery ? 1 : 0);
|
|
191
|
+
const tier = points >= 10 ? 5 : points >= 7 ? 4 : points >= 4 ? 3 : points >= 2 ? 2 : 1;
|
|
192
|
+
return { class: `C${tier}`, points, rule: COMPLEXITY_RULE };
|
|
193
|
+
}
|
|
194
|
+
/** How many different tools the stretch's agent used (framework L11: tool breadth); null without a tool call. */
|
|
195
|
+
const toolsOf = (messages) => { const names = new Set(); for (const message of messages) for (const call of message.calls ?? []) names.add(call.family.split("|")[0]); return names.size > 0 ? names.size : null; };
|
|
196
|
+
|
|
145
197
|
/** The stretch's whole derived record; `helpers` are the hook's own readers of a human turn, so both read it the same way. */
|
|
146
198
|
export function deriveStretch(messages, helpers) {
|
|
147
|
-
const context = contextOf(messages, helpers), routing = routingOf(messages);
|
|
148
|
-
|
|
199
|
+
const context = contextOf(messages, helpers), routing = routingOf(messages), steering = steeringOf(messages, helpers);
|
|
200
|
+
const tools = toolsOf(messages);
|
|
201
|
+
return { ...verificationOf(messages, helpers), ...(context ? { context } : {}), ...(routing ? { routing } : {}), ...(steering ? { steering } : {}), ...(tools ? { tools } : {}) };
|
|
149
202
|
}
|
package/worktrust.mjs
CHANGED
|
@@ -71,7 +71,7 @@ const command = args.find((arg, at) => !arg.startsWith("--") && !(at > 0 && VALU
|
|
|
71
71
|
const flag = (name) => { const at = args.indexOf(`--${name}`); return at >= 0 ? args[at + 1] : undefined; };
|
|
72
72
|
const has = (name) => args.includes(`--${name}`);
|
|
73
73
|
/** This CLI's version, said to the door so the app can tell which computer runs an old one (check-cli-package holds it equal to package.json). */
|
|
74
|
-
const CLI_VERSION = "0.7.
|
|
74
|
+
const CLI_VERSION = "0.7.6";
|
|
75
75
|
const ORIGIN = (flag("origin") ?? process.env.WORKTRUST_ORIGIN ?? "https://app.worktrust.io").replace(/\/$/, "");
|
|
76
76
|
const MCP = flag("url") ?? process.env.WORKTRUST_MCP_URL ?? `${ORIGIN}/api/mcp`;
|
|
77
77
|
const HOME_DIR = join(homedir(), ".worktrust");
|