tryharness 0.7.22 → 0.7.25

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.
Files changed (64) hide show
  1. package/README.md +48 -8
  2. package/assets/opencode-plugin.js +48 -10
  3. package/assets/pi-extension.ts +106 -26
  4. package/cli.mjs +1281 -289
  5. package/lib/activity.mjs +197 -40
  6. package/lib/adapters.mjs +337 -54
  7. package/lib/agentnode.mjs +175 -35
  8. package/lib/atlas.mjs +604 -0
  9. package/lib/bip39-english.txt +2048 -0
  10. package/lib/cards.mjs +177 -39
  11. package/lib/claudemem.mjs +74 -18
  12. package/lib/commands.mjs +13 -5
  13. package/lib/context.mjs +628 -0
  14. package/lib/control-client.mjs +5 -2
  15. package/lib/coordinator.mjs +281 -56
  16. package/lib/daemon.mjs +5590 -838
  17. package/lib/desktop.mjs +149 -35
  18. package/lib/digest.mjs +81 -0
  19. package/lib/environment.mjs +180 -0
  20. package/lib/files.mjs +246 -13
  21. package/lib/firewall.mjs +288 -0
  22. package/lib/folders.mjs +223 -57
  23. package/lib/frames.mjs +333 -0
  24. package/lib/inference.mjs +663 -0
  25. package/lib/learn.mjs +182 -0
  26. package/lib/live.mjs +286 -61
  27. package/lib/localmodels.mjs +571 -0
  28. package/lib/mcp-stdio.mjs +912 -103
  29. package/lib/media.mjs +228 -0
  30. package/lib/members.json +1 -1
  31. package/lib/members.mjs +35 -9
  32. package/lib/models.mjs +342 -80
  33. package/lib/network.mjs +201 -42
  34. package/lib/notify.mjs +49 -12
  35. package/lib/nudge.mjs +190 -32
  36. package/lib/ocr.mjs +380 -0
  37. package/lib/onboard.mjs +321 -70
  38. package/lib/parsers.mjs +132 -41
  39. package/lib/paths.mjs +33 -10
  40. package/lib/personal.mjs +77 -0
  41. package/lib/phones.mjs +398 -51
  42. package/lib/places.mjs +566 -102
  43. package/lib/provenance.mjs +19 -4
  44. package/lib/receipts.mjs +82 -0
  45. package/lib/redact.mjs +730 -0
  46. package/lib/runner.mjs +347 -55
  47. package/lib/runtime-install.mjs +59 -17
  48. package/lib/screens.mjs +212 -0
  49. package/lib/sealed.mjs +160 -28
  50. package/lib/secrets.mjs +18 -5
  51. package/lib/service.mjs +173 -35
  52. package/lib/snippets.mjs +140 -0
  53. package/lib/speech.mjs +993 -0
  54. package/lib/store.mjs +1678 -301
  55. package/lib/swarm.mjs +598 -117
  56. package/lib/t3.mjs +400 -79
  57. package/lib/trails.mjs +401 -0
  58. package/lib/ui.mjs +95 -23
  59. package/lib/vectors.mjs +5 -2
  60. package/lib/wakes.mjs +284 -68
  61. package/lib/watches.mjs +362 -0
  62. package/lib/worktrees.mjs +186 -38
  63. package/package.json +6 -4
  64. package/lib/embedder.mjs +0 -156
@@ -6,12 +6,27 @@
6
6
 
7
7
  const USER_WRITERS = new Set(["user", "harness"]);
8
8
 
9
- export function provenanceFor(originAgent, parent = null, claimed = null, { personSession = false } = {}) {
9
+ export function provenanceFor(
10
+ originAgent,
11
+ parent = null,
12
+ claimed = null,
13
+ { personSession = false } = {}
14
+ ) {
10
15
  // A claim counts only when it tightens: an 'agent' label and its chain. A claim of 'user' adds nothing.
11
16
  const taint = claimed?.trust === "agent" ? claimed : null;
12
- const chain = [...(parent?.chain ?? (Array.isArray(taint?.chain) ? taint.chain.map(String) : [])), originAgent].slice(-16);
13
- const tainted = parent?.trust === "agent" || Boolean(taint) || (!USER_WRITERS.has(originAgent) && !personSession);
14
- return { trust: originAgent === "user" || !tainted ? "user" : "agent", chain };
17
+ const chain = [
18
+ ...(parent?.chain ??
19
+ (Array.isArray(taint?.chain) ? taint.chain.map(String) : [])),
20
+ originAgent,
21
+ ].slice(-16);
22
+ const tainted =
23
+ parent?.trust === "agent" ||
24
+ Boolean(taint) ||
25
+ (!USER_WRITERS.has(originAgent) && !personSession);
26
+ return {
27
+ trust: originAgent === "user" || !tainted ? "user" : "agent",
28
+ chain,
29
+ };
15
30
  }
16
31
 
17
32
  // What an agent reads before an agent-written brief: who wrote it, mechanically, every time.
@@ -0,0 +1,82 @@
1
+ import { insideRoot, isTestCommand } from "./trails.mjs";
2
+
3
+ // Receipts for agents' work: what a run's own session file shows it did after its last change, never what it says it
4
+ // did. A run that changed files and then ran its tests with a zero exit is "tested"; one whose last test after its last
5
+ // change failed is "tests failing"; one that changed files and ran no test after the change is "not tested after its
6
+ // last change". The words come from code over the transcript, so an agent cannot talk its way to "tested". Changes are
7
+ // code changes inside the run's folder: notes, docs and files elsewhere (an agent's own memory) do not need tests.
8
+
9
+ const DOC = /\.(?:md|mdx|txt|rst)$/i;
10
+
11
+ /** { verdict, changed, lastChangeAt, tests: [{ command, ok, at, afterChange }] } for a trail (trails.mjs). */
12
+ export function receipt(trail, { root = trail?.cwd } = {}) {
13
+ const calls = trail?.calls ?? [];
14
+ const code = (call) =>
15
+ call.edited
16
+ .map((token) => (root ? insideRoot(token, root, trail.cwd) : token))
17
+ .filter((file) => file && !DOC.test(file));
18
+ let lastChange = -1;
19
+ calls.forEach((call, i) => {
20
+ if (code(call).length) lastChange = i;
21
+ });
22
+ const changed = [...new Set(calls.flatMap(code))];
23
+ const tests = calls
24
+ .map((call, i) => ({ call, i }))
25
+ .filter(({ call }) => call.command && isTestCommand(call.command))
26
+ .map(({ call, i }) => ({
27
+ command: call.command.split("\n")[0].slice(0, 200),
28
+ ok: !call.error,
29
+ at: call.at,
30
+ afterChange: i > lastChange,
31
+ }));
32
+ const after = tests.filter((test) => test.afterChange);
33
+ const verdict = !changed.length
34
+ ? "no changes"
35
+ : !after.length
36
+ ? "not tested after its last change"
37
+ : after.at(-1).ok
38
+ ? "tested"
39
+ : "tests failing";
40
+ // The same test failing again and again is the clearest sign of a run going round in circles.
41
+ let repeated = 0;
42
+ let streak = 0;
43
+ tests.forEach((test, i) => {
44
+ streak =
45
+ !test.ok &&
46
+ i > 0 &&
47
+ !tests[i - 1].ok &&
48
+ tests[i - 1].command === test.command
49
+ ? streak + 1
50
+ : test.ok
51
+ ? 0
52
+ : 1;
53
+ repeated = Math.max(repeated, streak);
54
+ });
55
+ return {
56
+ verdict,
57
+ changed,
58
+ lastChangeAt: lastChange >= 0 ? calls[lastChange].at : null,
59
+ tests: after.length ? after : tests.slice(-3),
60
+ repeatedFailures: repeated,
61
+ };
62
+ }
63
+
64
+ /** The line the thread shows for a receipt. */
65
+ export function receiptLine(agentName, result) {
66
+ const line = verdictLine(agentName, result);
67
+ return result.repeatedFailures >= 3
68
+ ? `${line} The same test failed ${result.repeatedFailures} times in a row during the run.`
69
+ : line;
70
+ }
71
+
72
+ function verdictLine(agentName, result) {
73
+ const files = `${result.changed.length} file${result.changed.length === 1 ? "" : "s"}`;
74
+ if (result.verdict === "no changes")
75
+ return `Receipt: ${agentName} changed no files in this run.`;
76
+ const last = result.tests.filter((test) => test.afterChange).at(-1);
77
+ if (result.verdict === "tested")
78
+ return `Receipt: ${agentName} changed ${files}, then ran \`${last.command}\` and it passed.`;
79
+ if (result.verdict === "tests failing")
80
+ return `Receipt: ${agentName} changed ${files}; its last test after the change, \`${last.command}\`, failed.`;
81
+ return `Receipt: ${agentName} changed ${files} and ran no tests after its last change.`;
82
+ }