tryharness 0.7.2 → 0.7.4

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/lib/daemon.mjs CHANGED
@@ -369,7 +369,9 @@ export async function startDaemon() {
369
369
  const realDir = (dir) => { try { return fs.realpathSync(dir); } catch { return path.resolve(dir); } };
370
370
  const within = (dir, root) => { const rel = path.relative(realDir(root), realDir(dir)); return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel)); };
371
371
  const aboveHome = (dir) => within(os.homedir(), dir);
372
- const covers = (rule, dir) => rule.workspaces.some((root) => rule.mode === "default" && aboveHome(root) ? realDir(root) === realDir(dir) : within(dir, root));
372
+ // A coding agent's own scratch folder, for the person's tasks that name none: nothing of theirs is in it.
373
+ const scratchFolder = (agent) => path.join(paths.home, "work", agent);
374
+ const covers = (rule, dir) => realDir(dir) === realDir(scratchFolder(rule.agent)) || rule.workspaces.some((root) => rule.mode === "default" && aboveHome(root) ? realDir(root) === realDir(dir) : within(dir, root));
373
375
  const standingRules = () => store.standing(runners(), startOfToday());
374
376
  // null when the run may start now; otherwise why it waits for the person.
375
377
  const standingVerdict = ({ trust, target, workspace }) => {
@@ -616,7 +618,12 @@ export async function startDaemon() {
616
618
  // which Claude Code and Codex start in the session's folder); never home or above it.
617
619
  const posterFolder = !request.thread && !goalParent && request.inSession !== true && POSTERS_WITH_FOLDER.has(agent) && RUNNERS.has(String(request.targetAgent || ""))
618
620
  && typeof request.posterCwd === "string" && path.isAbsolute(request.posterCwd) && !aboveHome(request.posterCwd) ? request.posterCwd : null;
619
- const runWorkspace = request.runWorkspace ? String(request.runWorkspace) : goalParent?.goal.workspace && RUNNERS.has(String(request.targetAgent || "")) ? goalParent.goal.workspace : posterFolder;
621
+ // The person's own task for a coding agent that names no folder runs in that agent's scratch folder, so it starts like
622
+ // any covered run (same daily limit, same off switch) instead of waiting for a session to look at the board.
623
+ const target = String(request.targetAgent || "");
624
+ const scratch = !request.runWorkspace && !goalParent && !posterFolder && !request.thread && provenance.trust === "user" && RUNNERS.has(target) && installedAgents.includes(target) ? scratchFolder(target) : null;
625
+ if (scratch) fs.mkdirSync(scratch, { recursive: true });
626
+ const runWorkspace = request.runWorkspace ? String(request.runWorkspace) : goalParent?.goal.workspace && RUNNERS.has(target) ? goalParent.goal.workspace : posterFolder ?? scratch;
620
627
  if (runWorkspace && !path.isAbsolute(runWorkspace)) return { ok: false, error: "a run needs an absolute workspace directory" };
621
628
  if (runWorkspace && !request.targetAgent && !(Array.isArray(request.targetAgents) && request.targetAgents.length)) return { ok: false, error: "a run needs a target agent" };
622
629
  const refs = Array.isArray(request.contextRefs) ? request.contextRefs.slice(0, 50) : [];
@@ -646,7 +653,8 @@ export async function startDaemon() {
646
653
  }
647
654
  const task = store.postTask({ id: request.id != null ? String(request.id) : randomUUID(), objective: objective.slice(0, 500), brief: String(request.brief || "").slice(0, 200_000),
648
655
  originAgent: agent, provenance, targetAgent: request.targetAgent ? String(request.targetAgent) : null, parentId: request.parentId ? String(request.parentId) : null, contextRefs: refs,
649
- runWorkspace: thread ? null : runWorkspace, runAuthorizedBy: !thread && runWorkspace && agent === "user" ? "user" : null });
656
+ // Asking for a run in a folder is the person's Start; a scratch folder they never asked for goes by standing permission.
657
+ runWorkspace: thread ? null : runWorkspace, runAuthorizedBy: !thread && runWorkspace && !scratch && agent === "user" ? "user" : null });
650
658
  if (thread) store.openThread(task.id, { withAgent: String(thread.with), role: thread.role });
651
659
  // A job in a goal's room runs on the goal's authority; anywhere else, standing permission decides.
652
660
  const standing = thread ? undefined : goalParent ? (task.run_workspace && store.authorizeGoalRun(task.id) ? { started: true, by: "goal" } : undefined) : applyStanding(task);
@@ -777,6 +785,9 @@ export async function startDaemon() {
777
785
 
778
786
  const control = net.createServer((socket) => {
779
787
  socket.setEncoding("utf8");
788
+ // A caller that hung up before its answer (its own timeout, a killed hook) loses that answer; it never takes the
789
+ // daemon, and the runs it holds, down with it.
790
+ socket.on("error", () => {});
780
791
  let buffer = "";
781
792
  socket.on("data", (chunk) => {
782
793
  buffer += chunk;
package/lib/desktop.mjs CHANGED
@@ -80,7 +80,12 @@ export function canShowDesktop({ env = process.env, platform = process.platform
80
80
 
81
81
  export function openDesktop({ target, platform = process.platform, execute = run }) {
82
82
  if (platform === "darwin") {
83
- const opened = execute("open", [target]);
83
+ // Just after the old copy quits, LaunchServices can still refuse (error -600) for a moment.
84
+ let opened = execute("open", [target]);
85
+ for (let i = 0; i < 20 && !opened.ok && /-600/.test(opened.stderr); i++) {
86
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 250);
87
+ opened = execute("open", [target]);
88
+ }
84
89
  if (!opened.ok) throw new Error(opened.stderr.trim() || opened.error || "open failed");
85
90
  return;
86
91
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tryharness",
3
- "version": "0.7.2",
3
+ "version": "0.7.4",
4
4
  "description": "Harness Relay: the daemon that turns the agents on this machine into your personal swarm. Memory, history, and work handed between any of them.",
5
5
  "bin": {
6
6
  "tryharness": "cli.mjs"
@@ -35,9 +35,9 @@
35
35
  "onnxruntime-web": "1.26.0-dev.20260416-b7804b056c"
36
36
  },
37
37
  "optionalDependencies": {
38
- "tryharness-desktop-darwin-arm64": "0.7.2",
39
- "tryharness-desktop-darwin-x64": "0.7.2",
40
- "tryharness-desktop-linux-x64": "0.7.2",
41
- "tryharness-desktop-win32-x64": "0.7.2"
38
+ "tryharness-desktop-darwin-arm64": "0.7.4",
39
+ "tryharness-desktop-darwin-x64": "0.7.4",
40
+ "tryharness-desktop-linux-x64": "0.7.4",
41
+ "tryharness-desktop-win32-x64": "0.7.4"
42
42
  }
43
43
  }