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.
- package/README.md +48 -8
- package/assets/opencode-plugin.js +48 -10
- package/assets/pi-extension.ts +106 -26
- package/cli.mjs +1281 -289
- package/lib/activity.mjs +197 -40
- package/lib/adapters.mjs +337 -54
- package/lib/agentnode.mjs +175 -35
- package/lib/atlas.mjs +604 -0
- package/lib/bip39-english.txt +2048 -0
- package/lib/cards.mjs +177 -39
- package/lib/claudemem.mjs +74 -18
- package/lib/commands.mjs +13 -5
- package/lib/context.mjs +628 -0
- package/lib/control-client.mjs +5 -2
- package/lib/coordinator.mjs +281 -56
- package/lib/daemon.mjs +5590 -838
- package/lib/desktop.mjs +149 -35
- package/lib/digest.mjs +81 -0
- package/lib/environment.mjs +180 -0
- package/lib/files.mjs +246 -13
- package/lib/firewall.mjs +288 -0
- package/lib/folders.mjs +223 -57
- package/lib/frames.mjs +333 -0
- package/lib/inference.mjs +663 -0
- package/lib/learn.mjs +182 -0
- package/lib/live.mjs +286 -61
- package/lib/localmodels.mjs +571 -0
- package/lib/mcp-stdio.mjs +912 -103
- package/lib/media.mjs +228 -0
- package/lib/members.json +1 -1
- package/lib/members.mjs +35 -9
- package/lib/models.mjs +342 -80
- package/lib/network.mjs +201 -42
- package/lib/notify.mjs +49 -12
- package/lib/nudge.mjs +190 -32
- package/lib/ocr.mjs +380 -0
- package/lib/onboard.mjs +321 -70
- package/lib/parsers.mjs +132 -41
- package/lib/paths.mjs +33 -10
- package/lib/personal.mjs +77 -0
- package/lib/phones.mjs +398 -51
- package/lib/places.mjs +566 -102
- package/lib/provenance.mjs +19 -4
- package/lib/receipts.mjs +82 -0
- package/lib/redact.mjs +730 -0
- package/lib/runner.mjs +347 -55
- package/lib/runtime-install.mjs +59 -17
- package/lib/screens.mjs +212 -0
- package/lib/sealed.mjs +160 -28
- package/lib/secrets.mjs +18 -5
- package/lib/service.mjs +173 -35
- package/lib/snippets.mjs +140 -0
- package/lib/speech.mjs +993 -0
- package/lib/store.mjs +1678 -301
- package/lib/swarm.mjs +598 -117
- package/lib/t3.mjs +400 -79
- package/lib/trails.mjs +401 -0
- package/lib/ui.mjs +95 -23
- package/lib/vectors.mjs +5 -2
- package/lib/wakes.mjs +284 -68
- package/lib/watches.mjs +362 -0
- package/lib/worktrees.mjs +186 -38
- package/package.json +6 -4
- package/lib/embedder.mjs +0 -156
package/lib/provenance.mjs
CHANGED
|
@@ -6,12 +6,27 @@
|
|
|
6
6
|
|
|
7
7
|
const USER_WRITERS = new Set(["user", "harness"]);
|
|
8
8
|
|
|
9
|
-
export function provenanceFor(
|
|
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 = [
|
|
13
|
-
|
|
14
|
-
|
|
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.
|
package/lib/receipts.mjs
ADDED
|
@@ -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
|
+
}
|