worktrust 0.8.8 → 0.9.1

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 CHANGED
@@ -173,6 +173,9 @@ nothing it does not:
173
173
  | `unconfirmed` | checks the stretch ran whose outcome no reader could see (Codex's code mode prints no exit code; a background terminal), per kind: counted, never as a pass (0.8.5) |
174
174
  | `autonomy`, `authorship` | the permission mode the AI app ran in, on one scale (ask, edits, plan, auto, full), how often it changed and the turns in plan mode; of the stretch's commits, how many name an AI as co-author (the trailer is read here, its names never leave) (0.8.7) |
175
175
  | `framing`, `quality`, `risk`, `tool_mix`, `reads`, `context_files` | how the work was framed (your turns before the agent's first action, a plan first or not), checked (a check passing after the last change, the failures before a check passed, the change looked at before delivery; security scans count as checks), risked (destructive commands proposed, refused, run), which kinds of tools were used, files read again unchanged, and which of the AI app's own context files (instructions, skills, agents, commands, settings, MCP) the commits changed; kinds and counts only (0.8.8) |
176
+ | `quality.change_blocks`, `checked_blocks`, `rework_cycles`, `stage` | how many change blocks (edits with no check between them) a check followed, how often a failed check was followed by another change before it ran again (rework), and the furthest step the stretch reached here: attempted, verified, committed, pushed, pr, deployed; counts and one word only (0.8.9) |
177
+ | `timing` | how fast the stretch moved: seconds from your first turn to the first action and from the first change to the first check, the actions and seconds from a failure to the change that answered it, the calls from a failed check to it passing, and the calls back to work after a compaction; medians from the order and the clocks, never what was said (0.9.0) |
178
+ | `hygiene` | risk and hygiene: rollbacks, privileged commands (sudo, a forced push), hooks skipped (--no-verify), secrets touched (a .env printed, a private key read), destructive commands and how many a look or a check came before, the answer to a guard (retried, changed, stopped), and whether a check followed delivery; counts only, never the command or the path (0.9.1) |
176
179
  | `collector_version` | the CLI version that measured it |
177
180
  | `seq`, `prev`, `hash` | the line's place, the hash of the line before it, and its own hash |
178
181
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "worktrust",
3
- "version": "0.8.8",
3
+ "version": "0.9.1",
4
4
  "description": "Couple this computer to WorkTrust: approve a code in your browser, and every AI app here reports what you built. Metadata only, no dependencies.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -105,7 +105,23 @@ export function archiveLine(entry, behaviour = null, { ids = {}, collector, salt
105
105
  // PRACTICE (0.8.8): framing, verification quality, risk, tool mix, re-reads, context files; whole counts and flags only.
106
106
  const bool = (value) => value === true || value === false;
107
107
  if (entry.framing && whole(entry.framing.turns_before_action) && bool(entry.framing.planned_first)) line.framing = { turns_before_action: entry.framing.turns_before_action, planned_first: entry.framing.planned_first };
108
- if (entry.quality && whole(entry.quality.fix_cycles_max) && (bool(entry.quality.checked_after_change) || entry.quality.checked_after_change === null) && (bool(entry.quality.inspected_first) || entry.quality.inspected_first === null)) line.quality = { checked_after_change: entry.quality.checked_after_change, fix_cycles_max: entry.quality.fix_cycles_max, inspected_first: entry.quality.inspected_first };
108
+ if (entry.quality && whole(entry.quality.fix_cycles_max) && (bool(entry.quality.checked_after_change) || entry.quality.checked_after_change === null) && (bool(entry.quality.inspected_first) || entry.quality.inspected_first === null)) {
109
+ line.quality = { checked_after_change: entry.quality.checked_after_change, fix_cycles_max: entry.quality.fix_cycles_max, inspected_first: entry.quality.inspected_first };
110
+ // 0.8.9: change blocks, how many a check followed, rework cycles.
111
+ if (whole(entry.quality.change_blocks) && whole(entry.quality.checked_blocks) && whole(entry.quality.rework_cycles) && entry.quality.checked_blocks <= entry.quality.change_blocks) Object.assign(line.quality, { change_blocks: entry.quality.change_blocks, checked_blocks: entry.quality.checked_blocks, rework_cycles: entry.quality.rework_cycles });
112
+ }
113
+ if (STAGES.includes(entry.stage)) line.stage = entry.stage;
114
+ // 0.9.0: how fast the stretch moved; whole seconds and counts, each key optional.
115
+ if (entry.timing && typeof entry.timing === "object") {
116
+ const timing = Object.fromEntries(TIMING_KEYS.filter((key) => whole(entry.timing[key])).map((key) => [key, entry.timing[key]]));
117
+ if (Object.keys(timing).length > 0) line.timing = timing;
118
+ }
119
+ // 0.9.1: risk and hygiene; whole counts, and whether a check followed delivery.
120
+ if (entry.hygiene && typeof entry.hygiene === "object") {
121
+ const hygiene = Object.fromEntries(HYGIENE_KEYS.filter((key) => whole(entry.hygiene[key])).map((key) => [key, entry.hygiene[key]]));
122
+ if (typeof entry.hygiene.checked_after_delivery === "boolean") hygiene.checked_after_delivery = entry.hygiene.checked_after_delivery;
123
+ if (Object.keys(hygiene).length > 0) line.hygiene = hygiene;
124
+ }
109
125
  const risk = pick(entry.risk, ["proposed", "refused", "run"]);
110
126
  if (risk && risk.proposed > 0 && risk.refused + risk.run <= risk.proposed) line.risk = risk;
111
127
  const keyed = (record, keys) => { const kept = Object.entries(record && typeof record === "object" ? record : {}).filter(([key, n]) => keys.includes(key) && whole(n) && n > 0).sort(); return kept.length ? Object.fromEntries(kept) : null; };
@@ -162,7 +178,13 @@ export function archiveEntries(dir, ownKey) {
162
178
  * a line of their own that names the stretch it adds to (`supplements`) and carries only what no earlier line of that
163
179
  * stretch carries, in the same chain, under the same day's root. Null when there is nothing new.
164
180
  */
165
- export const DERIVED_KEYS = ["signals", "analyzer_version", "verification", "unconfirmed", "delivery", "recovery", "delegation", "context", "routing", "steering", "tools", "complexity", "oversight", "planning", "changes", "autonomy", "authorship", "framing", "quality", "risk", "tool_mix", "reads", "context_files", "interrupts", "steers", "utc_offset"];
181
+ /** The furthest step a stretch reached on the computer (0.8.9). */
182
+ const STAGES = ["attempted", "verified", "committed", "pushed", "pr", "deployed"];
183
+ /** How fast a stretch moved (0.9.0). */
184
+ /** Risk and hygiene (0.9.1). */
185
+ const HYGIENE_KEYS = ["rollbacks", "privileged", "bypasses", "secrets", "destructive", "destructive_checked", "guard_retried", "guard_changed", "guard_stopped"];
186
+ const TIMING_KEYS = ["first_action_s", "first_check_s", "detect_actions", "detect_s", "recovery_calls", "resume_actions"];
187
+ export const DERIVED_KEYS = ["signals", "analyzer_version", "verification", "unconfirmed", "delivery", "recovery", "delegation", "context", "routing", "steering", "tools", "complexity", "oversight", "planning", "changes", "autonomy", "authorship", "framing", "quality", "risk", "tool_mix", "reads", "context_files", "stage", "timing", "hygiene", "interrupts", "steers", "utc_offset"];
166
188
  export function supplementFor(line, earlier) {
167
189
  const missing = DERIVED_KEYS.filter((key) => line[key] !== undefined && !earlier.some((old) => old[key] !== undefined));
168
190
  if (missing.length === 0) return null;
@@ -36,6 +36,29 @@ export const callKindsOf = (block) => {
36
36
  const command = block && SHELL_TOOLS.has(String(block.name)) ? block.input?.command ?? block.input?.CommandLine ?? block.input?.cmd : null;
37
37
  return typeof command === "string" ? CALL_KINDS.filter(([, pattern]) => pattern.test(command)).map(([kind]) => kind) : [];
38
38
  };
39
+ /**
40
+ * RISK AND HYGIENE MARKS (0.9.1), beside the kinds and outside a call's family so no earlier measure moves: a ROLLBACK
41
+ * (git revert or restore, a hard reset, a deploy rolled back), a PRIVILEGED command (sudo, a forced push, a world-
42
+ * writable chmod), a BYPASS (hooks skipped with --no-verify), and a SECRET touched (a .env, a private key, a credentials
43
+ * or token file read or printed). The path and the command are read here and never kept; only the mark travels.
44
+ */
45
+ const MARKS = [
46
+ ["rollback", /\bgit\s+(revert|restore)\b|\bgit\s+reset\s+--hard\b|\b(vercel|fly|netlify)\s+rollback\b|\bkubectl\s+rollout\s+undo\b/],
47
+ ["privilege", /(^|[\s;&|])sudo\s|\bgit\s+push\b[^\n]*\s(-f|--force(-with-lease)?)\b|\bchmod\s+(-R\s+)?777\b/],
48
+ ["bypass", /\s--no-verify\b/],
49
+ ];
50
+ const SECRET_FILE = /(^|[\/\s'"])(\.env(\.[\w-]+)?|id_(rsa|ed25519|ecdsa)|[\w.-]+\.(pem|p12|pfx|key)|credentials(\.json)?|\.npmrc|\.netrc|\.pgpass)(['"\s]|$)/;
51
+ const PRINTS = /\b(cat|less|more|head|tail|bat|type|Get-Content|grep|sed|awk|cp|scp|base64)\b/;
52
+ const READ_TOOLS = new Set(["Read", "read_file", "view", "open_file"]);
53
+ export const marksOf = (block) => {
54
+ const name = String(block?.name ?? "");
55
+ const command = SHELL_TOOLS.has(name) ? block.input?.command ?? block.input?.CommandLine ?? block.input?.cmd : null;
56
+ const path = READ_TOOLS.has(name) ? block.input?.file_path ?? block.input?.path ?? null : null;
57
+ const marks = typeof command === "string" ? MARKS.filter(([, pattern]) => pattern.test(command)).map(([mark]) => mark) : [];
58
+ if ((typeof path === "string" && SECRET_FILE.test(path)) || (typeof command === "string" && PRINTS.test(command) && SECRET_FILE.test(command))) marks.push("secret");
59
+ return marks;
60
+ };
61
+
39
62
  /**
40
63
  * A TOOL RESULT AS THE STRETCH READS IT (0.8.1): its call's id, whether it failed, and, read here and never kept, WHO
41
64
  * refused it when it was refused: the PERSON (Claude Code's fixed sentence when they decline a tool use or a plan) or a
@@ -54,7 +77,7 @@ export const resultOf = (block) => {
54
77
  const planOf = (block) => (block.name === "TodoWrite" && Array.isArray(block.input?.todos) ? { items: block.input.todos.length, done: block.input.todos.filter((todo) => todo && todo.status === "completed").length } : null);
55
78
 
56
79
  /** One tool call as the stretch reads it: its id, its kinds, its FAMILY (the tool and its check kinds) and a digest of its input (compared, never kept). */
57
- export const callOf = (block) => { const kinds = callKindsOf(block), plan = planOf(block); return { id: block.id, kinds, family: `${String(block.name)}|${kinds.join("+")}`, digest: createHash("sha256").update(JSON.stringify(block.input ?? null)).digest("base64url").slice(0, 16), ...(plan ? { plan } : {}) }; };
80
+ export const callOf = (block) => { const kinds = callKindsOf(block), plan = planOf(block), marks = marksOf(block); return { id: block.id, kinds, ...(marks.length ? { marks } : {}), family: `${String(block.name)}|${kinds.join("+")}`, digest: createHash("sha256").update(JSON.stringify(block.input ?? null)).digest("base64url").slice(0, 16), ...(plan ? { plan } : {}) }; };
58
81
 
59
82
  /**
60
83
  * VERIFICATION PER STRETCH (0.7.1; framework L7, L12): per kind of check, how many ran and how many failed; per step of
@@ -268,6 +291,10 @@ function autonomyOf(messages) {
268
291
  * (plan mode, a plan approved, a to-do list) came before it.
269
292
  * quality: whether a check passed AFTER the stretch's last change; the most failures one check family took before it
270
293
  * passed; whether the change was looked at (git diff, show, status) before the first delivery step.
294
+ * 0.8.9: change BLOCKS (edits with no check between them) and how many a check followed before the next one;
295
+ * REWORK cycles (a check failed, then a change before that check ran again: change, fail, change).
296
+ * stage: the furthest step the stretch reached on this computer: attempted (it changed something), verified (a check
297
+ * passed), committed, pushed, pr, deployed (0.8.9). Merged and confirmed are the server's, from GitHub.
271
298
  * risk: destructive commands proposed, refused (by the person or a guard) and run.
272
299
  * tool_mix: calls per kind of tool (search, read, edit, shell, agent, web, data, mcp, plan).
273
300
  * reads: files read again unchanged (the same read twice), the cost of context that did not hold.
@@ -282,6 +309,8 @@ function practiceOf(messages, { isHumanTurn }) {
282
309
  let turns = 0, acted = false, planned = false, turnsBefore = 0, plannedFirst = false;
283
310
  let lastChange = -1, passedAfter = false, delivered = false, inspectedFirst = false;
284
311
  const failuresBefore = new Map(); let cyclesMax = 0;
312
+ let blocks = 0, checkedBlocks = 0, inBlock = false, rework = 0, passedAny = false;
313
+ const pendingFail = new Set(), reached = new Set();
285
314
  const risk = { proposed: 0, refused: 0, run: 0 }, mix = {}, reads = new Map();
286
315
  let index = 0;
287
316
  for (const message of messages) {
@@ -294,15 +323,21 @@ function practiceOf(messages, { isHumanTurn }) {
294
323
  mix[toolKind(tool)] = (mix[toolKind(tool)] ?? 0) + 1;
295
324
  if (PLAN_TOOLS.has(tool) && tool !== "AskUserQuestion") planned = true;
296
325
  if (!acted && ACTION_TOOLS.has(tool)) { acted = true; turnsBefore = turns; plannedFirst = planned; }
297
- if (EDIT_TOOLS.has(tool)) { lastChange = index; passedAfter = false; }
326
+ if (EDIT_TOOLS.has(tool)) {
327
+ lastChange = index; passedAfter = false;
328
+ if (!inBlock) { blocks += 1; inBlock = true; }
329
+ rework += pendingFail.size; pendingFail.clear();
330
+ }
298
331
  if (tool === "Read") reads.set(call.digest, (reads.get(call.digest) ?? 0) + 1);
299
332
  const checks = call.kinds.filter((kind) => CHECK_KINDS.includes(kind));
333
+ if (checks.length > 0 && inBlock) { checkedBlocks += 1; inBlock = false; }
300
334
  for (const kind of checks) {
301
- if (result?.failed === true) failuresBefore.set(kind, (failuresBefore.get(kind) ?? 0) + 1);
335
+ if (result?.failed === true) { failuresBefore.set(kind, (failuresBefore.get(kind) ?? 0) + 1); pendingFail.add(kind); }
336
+ if (result?.failed === false) { passedAny = true; pendingFail.delete(kind); }
302
337
  if (result?.failed === false) { cyclesMax = Math.max(cyclesMax, failuresBefore.get(kind) ?? 0); failuresBefore.set(kind, 0); if (index > lastChange) passedAfter = true; }
303
338
  }
304
339
  if (call.kinds.includes("inspect") && !delivered) inspectedFirst = true;
305
- if (call.kinds.some((kind) => DELIVERY_KINDS.includes(kind)) && result?.failed === false) delivered = true;
340
+ if (call.kinds.some((kind) => DELIVERY_KINDS.includes(kind)) && result?.failed === false) { delivered = true; for (const kind of call.kinds) if (DELIVERY_KINDS.includes(kind)) reached.add(kind); }
306
341
  if (call.kinds.includes("destructive")) {
307
342
  risk.proposed += 1;
308
343
  if (result?.refusal) risk.refused += 1; else if (result?.failed === false) risk.run += 1;
@@ -311,18 +346,100 @@ function practiceOf(messages, { isHumanTurn }) {
311
346
  }
312
347
  if (index === 0) return null;
313
348
  const repeated = [...reads.values()].reduce((sum, n) => sum + Math.max(0, n - 1), 0);
349
+ const stage = ["deploy", "pr", "push", "commit"].find((kind) => reached.has(kind)) ?? (lastChange > 0 ? (passedAny ? "verified" : "attempted") : null);
314
350
  return {
315
351
  framing: { turns_before_action: acted ? turnsBefore : turns, planned_first: acted ? plannedFirst : planned },
316
- quality: { checked_after_change: lastChange > 0 ? passedAfter : null, fix_cycles_max: cyclesMax, inspected_first: delivered ? inspectedFirst : null },
352
+ quality: { checked_after_change: lastChange > 0 ? passedAfter : null, fix_cycles_max: cyclesMax, inspected_first: delivered ? inspectedFirst : null, change_blocks: blocks, checked_blocks: checkedBlocks, rework_cycles: rework },
353
+ ...(stage ? { stage: { deploy: "deployed", pr: "pr", push: "pushed", commit: "committed" }[stage] ?? stage } : {}),
317
354
  ...(risk.proposed > 0 ? { risk } : {}),
318
355
  tool_mix: mix,
319
356
  reads: { repeated },
320
357
  };
321
358
  }
322
359
 
360
+ /**
361
+ * HOW FAST A STRETCH MOVED (0.9.0): seconds from the person's first turn to the agent's first action (an edit, a write or
362
+ * a shell command) and from the first change to the first check; per failure, the actions and seconds until the next
363
+ * change that answered it (detection), and the calls from a failed check to that check passing (recovery depth); after
364
+ * a resume, a clear or a compaction, the calls until the next change or check (back to work). Medians, from the order
365
+ * and the clocks alone; nothing of what was said or run.
366
+ */
367
+ const median = (values) => { if (values.length === 0) return null; const sorted = [...values].sort((a, b) => a - b); const middle = Math.floor(sorted.length / 2); return Math.round(sorted.length % 2 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2); };
368
+ function timingOf(messages, { isHumanTurn, textOf }) {
369
+ let firstTurn = null, firstAction = null, firstChange = null, firstCheck = null, index = 0, resumedAt = null;
370
+ const kindsById = new Map(), open = [], openChecks = new Map(), detect = [], detectSeconds = [], depth = [], resume = [];
371
+ for (const message of messages) {
372
+ if (message.bridge || !Number.isFinite(message.at)) continue;
373
+ const text = message.type === "user" ? textOf(message.content) : "";
374
+ if (message.compacted || ["compact", "clear", "resume"].includes(COMMAND.exec(text)?.[1] ?? "")) resumedAt = index;
375
+ if (message.type === "user" && !message.meta && message.kind !== "tool_result" && isHumanTurn(message.content) && firstTurn === null) firstTurn = message.at;
376
+ for (const result of message.results ?? []) {
377
+ const kinds = kindsById.get(result.id) ?? [];
378
+ if (result.failed !== true) {
379
+ for (const kind of kinds.filter((item) => CHECK_KINDS.includes(item) && result.failed === false)) { const at = openChecks.get(kind); if (at !== undefined) { depth.push(index - at); openChecks.delete(kind); } }
380
+ continue;
381
+ }
382
+ open.push({ index, at: message.at });
383
+ for (const kind of kinds.filter((item) => CHECK_KINDS.includes(item))) if (!openChecks.has(kind)) openChecks.set(kind, index);
384
+ }
385
+ for (const call of message.calls ?? []) {
386
+ index += 1;
387
+ kindsById.set(call.id, call.kinds);
388
+ const tool = call.family.split("|")[0];
389
+ if (firstAction === null && ACTION_TOOLS.has(tool)) firstAction = message.at;
390
+ if (EDIT_TOOLS.has(tool)) {
391
+ if (firstChange === null) firstChange = message.at;
392
+ for (const failure of open.splice(0)) { detect.push(index - failure.index); detectSeconds.push(Math.max(0, (message.at - failure.at) / 1000)); }
393
+ }
394
+ if (firstChange !== null && firstCheck === null && call.kinds.some((kind) => CHECK_KINDS.includes(kind))) firstCheck = message.at;
395
+ if (resumedAt !== null && (EDIT_TOOLS.has(tool) || call.kinds.some((kind) => CHECK_KINDS.includes(kind)))) { resume.push(index - resumedAt); resumedAt = null; }
396
+ }
397
+ }
398
+ const out = {
399
+ first_action_s: firstTurn !== null && firstAction !== null && firstAction >= firstTurn ? Math.round((firstAction - firstTurn) / 1000) : null,
400
+ first_check_s: firstChange !== null && firstCheck !== null ? Math.round((firstCheck - firstChange) / 1000) : null,
401
+ detect_actions: median(detect), detect_s: median(detectSeconds), recovery_calls: median(depth), resume_actions: median(resume),
402
+ };
403
+ const kept = Object.fromEntries(Object.entries(out).filter(([, value]) => value !== null));
404
+ return Object.keys(kept).length > 0 ? kept : null;
405
+ }
406
+
407
+ /**
408
+ * RISK AND HYGIENE PER STRETCH (0.9.1): rollbacks, privileged commands, hooks skipped, secrets touched; destructive
409
+ * commands and how many a look (git diff, show, status) or a check came before since the last change; after a guard
410
+ * refused a call, whether the next call was the same one again (retried), another (changed), or none (stopped); and
411
+ * whether a check ran after the stretch's first delivery step (a push, a deploy). Counts only.
412
+ */
413
+ function hygieneOf(messages) {
414
+ const results = new Map();
415
+ for (const message of messages) for (const result of message.results ?? []) results.set(result.id, result);
416
+ const out = { rollbacks: 0, privileged: 0, bypasses: 0, secrets: 0, destructive: 0, destructive_checked: 0, guard_retried: 0, guard_changed: 0, guard_stopped: 0 };
417
+ let lookedSinceChange = false, delivered = false, checkedAfterDelivery = false, refusedDigest = null;
418
+ for (const message of messages) {
419
+ if (message.bridge) continue;
420
+ for (const call of message.calls ?? []) {
421
+ const tool = call.family.split("|")[0], result = results.get(call.id), marks = call.marks ?? [];
422
+ if (refusedDigest !== null) { if (call.digest === refusedDigest) out.guard_retried += 1; else out.guard_changed += 1; refusedDigest = null; }
423
+ if (marks.includes("rollback")) out.rollbacks += 1;
424
+ if (marks.includes("privilege")) out.privileged += 1;
425
+ if (marks.includes("bypass")) out.bypasses += 1;
426
+ if (marks.includes("secret")) out.secrets += 1;
427
+ if (call.kinds.includes("destructive")) { out.destructive += 1; if (lookedSinceChange) out.destructive_checked += 1; }
428
+ if (EDIT_TOOLS.has(tool)) lookedSinceChange = false;
429
+ if (call.kinds.includes("inspect") || call.kinds.some((kind) => CHECK_KINDS.includes(kind))) { lookedSinceChange = true; if (delivered) checkedAfterDelivery = true; }
430
+ if ((call.kinds.includes("push") || call.kinds.includes("deploy")) && result?.failed === false) delivered = true;
431
+ if (result?.refusal === "guard") refusedDigest = call.digest;
432
+ }
433
+ }
434
+ if (refusedDigest !== null) out.guard_stopped += 1;
435
+ const kept = Object.fromEntries(Object.entries(out).filter(([, value]) => value > 0));
436
+ if (delivered) kept.checked_after_delivery = checkedAfterDelivery;
437
+ return Object.keys(kept).length > 0 ? kept : null;
438
+ }
439
+
323
440
  /** The stretch's whole derived record; `helpers` are the hook's own readers of a human turn, so both read it the same way. */
324
441
  export function deriveStretch(messages, helpers) {
325
442
  const context = contextOf(messages, helpers), routing = routingOf(messages), steering = steeringOf(messages, helpers);
326
- const tools = toolsOf(messages), oversight = oversightOf(messages), planning = planningOf(messages), autonomy = autonomyOf(messages), practice = practiceOf(messages, helpers);
327
- return { ...(practice ?? {}), ...(oversight ? { oversight } : {}), ...(planning ? { planning } : {}), ...(autonomy ? { autonomy } : {}), ...verificationOf(messages, helpers), ...(context ? { context } : {}), ...(routing ? { routing } : {}), ...(steering ? { steering } : {}), ...(tools ? { tools } : {}) };
443
+ const timing = timingOf(messages, helpers), hygiene = hygieneOf(messages), tools = toolsOf(messages), oversight = oversightOf(messages), planning = planningOf(messages), autonomy = autonomyOf(messages), practice = practiceOf(messages, helpers);
444
+ return { ...(practice ?? {}), ...(oversight ? { oversight } : {}), ...(planning ? { planning } : {}), ...(autonomy ? { autonomy } : {}), ...verificationOf(messages, helpers), ...(context ? { context } : {}), ...(routing ? { routing } : {}), ...(steering ? { steering } : {}), ...(tools ? { tools } : {}), ...(timing ? { timing } : {}), ...(hygiene ? { hygiene } : {}) };
328
445
  }
package/worktrust.mjs CHANGED
@@ -73,7 +73,7 @@ const command = args.find((arg, at) => !arg.startsWith("--") && !(at > 0 && VALU
73
73
  const flag = (name) => { const at = args.indexOf(`--${name}`); return at >= 0 ? args[at + 1] : undefined; };
74
74
  const has = (name) => args.includes(`--${name}`);
75
75
  /** 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). */
76
- const CLI_VERSION = "0.8.8";
76
+ const CLI_VERSION = "0.9.1";
77
77
  const ORIGIN = (flag("origin") ?? process.env.WORKTRUST_ORIGIN ?? "https://app.worktrust.io").replace(/\/$/, "");
78
78
  const MCP = flag("url") ?? process.env.WORKTRUST_MCP_URL ?? `${ORIGIN}/api/mcp`;
79
79
  const HOME_DIR = join(homedir(), ".worktrust");