worktrust 0.7.4 → 0.7.5
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 +1 -0
- package/package.json +1 -1
- package/preserve-lines.mjs +5 -0
- package/stretch-evidence.mjs +31 -2
- package/worktrust.mjs +1 -1
package/README.md
CHANGED
|
@@ -163,6 +163,7 @@ 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) |
|
|
166
167
|
| `collector_version` | the CLI version that measured it |
|
|
167
168
|
| `seq`, `prev`, `hash` | the line's place, the hash of the line before it, and its own hash |
|
|
168
169
|
|
package/package.json
CHANGED
package/preserve-lines.mjs
CHANGED
|
@@ -75,6 +75,11 @@ 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
|
+
}
|
|
78
83
|
// 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
84
|
if (behaviour && behaviour.signals && typeof behaviour.analyzer_version === "string" && /^counter@\d+\.\d+\.\d+$/.test(behaviour.analyzer_version)) {
|
|
80
85
|
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,37 @@ 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
|
+
|
|
145
174
|
/** 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
175
|
export function deriveStretch(messages, helpers) {
|
|
147
|
-
const context = contextOf(messages, helpers), routing = routingOf(messages);
|
|
148
|
-
return { ...verificationOf(messages, helpers), ...(context ? { context } : {}), ...(routing ? { routing } : {}) };
|
|
176
|
+
const context = contextOf(messages, helpers), routing = routingOf(messages), steering = steeringOf(messages, helpers);
|
|
177
|
+
return { ...verificationOf(messages, helpers), ...(context ? { context } : {}), ...(routing ? { routing } : {}), ...(steering ? { steering } : {}) };
|
|
149
178
|
}
|
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.5";
|
|
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");
|