slopwaresystems 0.5.0 → 0.6.0

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 (2) hide show
  1. package/dist/index.js +64 -37
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -26478,7 +26478,7 @@ var TaskEventRecord = external_exports.object({
26478
26478
 
26479
26479
  // ../contract/src/work/sessions.ts
26480
26480
  var AgentHarness = external_exports.enum(["claude", "codex", "cursor", "other"]);
26481
- var WORKERS_PER_SESSION = 8;
26481
+ var WORKERS_PER_SESSION = 200;
26482
26482
  var WORKER_LABEL_MAX = 80;
26483
26483
  var DOING_MAX = 120;
26484
26484
  var SessionWorker = external_exports.object({
@@ -26490,6 +26490,13 @@ var SessionWorker = external_exports.object({
26490
26490
  doing: external_exports.string().trim().max(DOING_MAX).nullable().default(null),
26491
26491
  since: Timestamp
26492
26492
  });
26493
+ var SessionWorkerEntry = SessionWorker.omit({ doing: true });
26494
+ var SessionWorkerLine = external_exports.object({ ref: SessionWorker.shape.ref, doing: SessionWorker.shape.doing });
26495
+ var SessionRoster = external_exports.object({ workers: external_exports.array(SessionWorkerEntry).max(WORKERS_PER_SESSION) });
26496
+ var SessionDoing = external_exports.object({
26497
+ doing: external_exports.string().trim().max(DOING_MAX).nullable().default(null),
26498
+ lines: external_exports.array(SessionWorkerLine).max(WORKERS_PER_SESSION).default([])
26499
+ });
26493
26500
  var SessionActivity = external_exports.object({
26494
26501
  doing: external_exports.string().trim().max(DOING_MAX).nullable().default(null),
26495
26502
  workers: external_exports.array(SessionWorker).max(WORKERS_PER_SESSION).default([])
@@ -26543,12 +26550,18 @@ var SessionPush = external_exports.discriminatedUnion("type", [
26543
26550
  external_exports.object({ type: external_exports.literal("reachable"), on: external_exports.boolean() }),
26544
26551
  /** Whether anyone is looking at this project's board. Off, an agent stops saying what it is doing. */
26545
26552
  external_exports.object({ type: external_exports.literal("watched"), on: external_exports.boolean() }),
26546
- /** To board watchers: what one agent and its workers are doing now. Carries its own payload; nothing to refetch. */
26547
- SessionActivity.extend({ type: external_exports.literal("activity"), session: Id }),
26553
+ /** To board watchers: one agent's workers now, replacing the ones before. Carries its own payload; nothing to refetch. */
26554
+ SessionRoster.extend({ type: external_exports.literal("workers"), session: Id }),
26555
+ /** To board watchers: what one agent is doing now, and the lines of its workers that changed. Nothing to refetch. */
26556
+ SessionDoing.extend({ type: external_exports.literal("doing"), session: Id }),
26548
26557
  /** To board watchers: something in the project changed; refetch. */
26549
26558
  external_exports.object({ type: external_exports.literal("changed"), scope: external_exports.string() })
26550
26559
  ]);
26551
- var SessionUp = external_exports.discriminatedUnion("type", [SessionActivity.extend({ type: external_exports.literal("activity") })]);
26560
+ var SessionUp = external_exports.discriminatedUnion("type", [
26561
+ SessionRoster.extend({ type: external_exports.literal("workers") }),
26562
+ SessionDoing.extend({ type: external_exports.literal("doing") }),
26563
+ SessionActivity.extend({ type: external_exports.literal("activity") })
26564
+ ]);
26552
26565
  var SessionMessagesInput = external_exports.object({ cursor: external_exports.coerce.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).optional() });
26553
26566
  var SessionMessages = external_exports.object({
26554
26567
  cursor: external_exports.number().int().nonnegative(),
@@ -29141,8 +29154,6 @@ var HeldSession = class {
29141
29154
  delivered = /* @__PURE__ */ new Set();
29142
29155
  /** A Worker older than this CLI refuses what it does not know. One refusal is enough: stop saying it. */
29143
29156
  upstreamRefused = false;
29144
- /** Whether this socket has been told anything about what the agent is doing; a new socket knows nothing again. */
29145
- toldThisSocket = false;
29146
29157
  identity;
29147
29158
  session = null;
29148
29159
  /**
@@ -29205,7 +29216,6 @@ var HeldSession = class {
29205
29216
  return;
29206
29217
  }
29207
29218
  greeted = true;
29208
- this.toldThisSocket = false;
29209
29219
  this.api.sessionId = push.session.id;
29210
29220
  this.session = push.session;
29211
29221
  this.taskKey = push.session.taskKey;
@@ -29288,16 +29298,16 @@ var HeldSession = class {
29288
29298
  }
29289
29299
  }
29290
29300
  /**
29291
- * Says what this agent and its workers are doing. Dropped when the socket is not up: the room holds only what is
29292
- * true now, and the next report follows the reconnect. The only thing this client ever sends but a ping.
29301
+ * Says who this agent's workers are, or what it and they are doing. Dropped when the socket is not up: the room
29302
+ * holds only what is true now, and the reporter says it all again after the next hello. The reporter says only what
29303
+ * changed, so an agent with no subagents that nobody is watching sends nothing at all, which is most agents most of
29304
+ * the time. The only thing this client ever sends but a ping.
29293
29305
  */
29294
- reportActivity(activity) {
29306
+ reportActivity(message) {
29295
29307
  const socket = this.socket;
29296
29308
  if (this.stopped || this.upstreamRefused || !this.session || !socket || socket.readyState !== WebSocket.OPEN) return;
29297
- if (!this.toldThisSocket && activity.doing === null && activity.workers.length === 0) return;
29298
29309
  try {
29299
- socket.send(JSON.stringify({ type: "activity", ...activity }));
29300
- this.toldThisSocket = true;
29310
+ socket.send(JSON.stringify(message));
29301
29311
  } catch {
29302
29312
  }
29303
29313
  }
@@ -29687,7 +29697,10 @@ var HarnessActivity = class {
29687
29697
  watched = false;
29688
29698
  discoveredAt = 0;
29689
29699
  timer = null;
29690
- reported = "";
29700
+ /** What the room knows: the roster it was last told, what this agent is doing, and each worker's line. */
29701
+ toldRoster = "[]";
29702
+ toldDoing = null;
29703
+ toldLines = /* @__PURE__ */ new Map();
29691
29704
  stopped = false;
29692
29705
  start() {
29693
29706
  if (this.timer || this.stopped) return;
@@ -29702,9 +29715,11 @@ var HarnessActivity = class {
29702
29715
  if (on) this.doing = null;
29703
29716
  this.tick();
29704
29717
  }
29705
- /** Say it again: the room forgets a session's activity when its socket drops, and only the agent can refill it. */
29718
+ /** Say it again: a new socket starts knowing nothing, and only the agent can refill it. */
29706
29719
  repeat() {
29707
- this.reported = "";
29720
+ this.toldRoster = "[]";
29721
+ this.toldDoing = null;
29722
+ this.toldLines.clear();
29708
29723
  this.tick();
29709
29724
  }
29710
29725
  stop() {
@@ -29726,11 +29741,20 @@ var HarnessActivity = class {
29726
29741
  } catch {
29727
29742
  }
29728
29743
  }
29729
- const activity = this.build();
29730
- const said = JSON.stringify(activity);
29731
- if (said === this.reported) return;
29732
- this.reported = said;
29733
- this.onReport(activity);
29744
+ const roster = this.buildRoster();
29745
+ const said = JSON.stringify(roster);
29746
+ if (said !== this.toldRoster) {
29747
+ this.toldRoster = said;
29748
+ const refs = new Set(roster.map((worker) => worker.ref));
29749
+ for (const ref of [...this.toldLines.keys()]) if (!refs.has(ref)) this.toldLines.delete(ref);
29750
+ this.onReport({ type: "workers", workers: roster });
29751
+ }
29752
+ const doing = this.watched ? this.doing : null;
29753
+ const changed = this.buildWorkerLines(roster, now).filter((line) => (this.toldLines.get(line.ref) ?? null) !== line.doing);
29754
+ if (changed.length === 0 && doing === this.toldDoing) return;
29755
+ this.toldDoing = doing;
29756
+ for (const line of changed) this.toldLines.set(line.ref, line.doing);
29757
+ this.onReport({ type: "doing", doing, lines: changed });
29734
29758
  }
29735
29759
  /** Which files to read: this chat's own, and one per worker still working. */
29736
29760
  discover(now) {
@@ -29772,10 +29796,19 @@ var HarnessActivity = class {
29772
29796
  }
29773
29797
  for (const worker of this.workers.values()) worker.doing = findNewestCall(worker.tail) ?? worker.doing;
29774
29798
  }
29775
- build() {
29776
- const now = Date.now();
29777
- const workers = [...this.workers.values()].sort((a, b) => b.changedAt - a.changedAt).slice(0, WORKERS_PER_SESSION).map((worker) => ({ ref: worker.ref, label: worker.label, directory: worker.directory, branch: worker.branch, doing: this.watched && now - worker.changedAt < WORKER_FRESH_MS ? worker.doing : null, since: worker.since }));
29778
- return { doing: this.watched ? this.doing : null, workers };
29799
+ /**
29800
+ * Every worker, oldest first, so the roster changes only when one starts or finishes and never because one got
29801
+ * busy. The contract's guard is far past what a harness runs; past it, the oldest are the ones listed.
29802
+ */
29803
+ buildRoster() {
29804
+ return [...this.workers.values()].sort((a, b) => a.since.localeCompare(b.since) || a.ref.localeCompare(b.ref)).slice(0, WORKERS_PER_SESSION).map((worker) => ({ ref: worker.ref, label: worker.label, directory: worker.directory, branch: worker.branch, since: worker.since }));
29805
+ }
29806
+ /** Quiet for a while: still listed, because it is probably thinking, but saying nothing about what it is doing. */
29807
+ buildWorkerLines(roster, now) {
29808
+ return roster.map(({ ref }) => {
29809
+ const worker = this.workers.get(ref);
29810
+ return { ref, doing: this.watched && now - worker.changedAt < WORKER_FRESH_MS ? worker.doing : null };
29811
+ });
29779
29812
  }
29780
29813
  };
29781
29814
 
@@ -31088,11 +31121,7 @@ function createProjectTools(api, project, identity) {
31088
31121
  const requireOwnSession = () => {
31089
31122
  if (!canHoldOwnSession()) throw new Error(SPAWNED_BY_ANOTHER_AGENT);
31090
31123
  };
31091
- let told = null;
31092
- const activity = new HarnessActivity({ id: identity.conversation, harness: identity.harness }, (report) => {
31093
- told = report;
31094
- held?.reportActivity(report);
31095
- });
31124
+ const activity = new HarnessActivity({ id: identity.conversation, harness: identity.harness }, (report) => held?.reportActivity(report));
31096
31125
  let waking = false;
31097
31126
  const wakeOwnHarness = () => {
31098
31127
  if (waking || stopped) return;
@@ -31126,10 +31155,8 @@ function createProjectTools(api, project, identity) {
31126
31155
  onMessage: wakeOwnHarness,
31127
31156
  claim: resolveOwnAgentIdentity,
31128
31157
  onReconnect: () => recovery.sync(),
31129
- // The room forgets a session's activity when its socket goes: whatever was true is said again on the new one.
31130
- onHello: () => {
31131
- if (told) held?.reportActivity(told);
31132
- },
31158
+ // A new socket starts knowing nothing about the workers: whatever is true is said again on it.
31159
+ onHello: () => activity.repeat(),
31133
31160
  onWatched: (on) => activity.setWatched(on)
31134
31161
  });
31135
31162
  activity.start();
@@ -32259,7 +32286,7 @@ async function trustCodexHooks(codexHome) {
32259
32286
  const sendNotification = (method, params) => server.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", method, params })}
32260
32287
  `);
32261
32288
  try {
32262
- await callCodexMethod("initialize", { clientInfo: { name: "slop", version: "0.5.0" }, capabilities: { experimentalApi: true } });
32289
+ await callCodexMethod("initialize", { clientInfo: { name: "slop", version: "0.6.0" }, capabilities: { experimentalApi: true } });
32263
32290
  sendNotification("initialized", {});
32264
32291
  const listed = await callCodexMethod("hooks/list", { cwds: [process.cwd()] });
32265
32292
  const ours = listed.data.flatMap((entry) => entry.hooks).filter((hook) => hook.command?.startsWith("slop hook "));
@@ -32460,7 +32487,7 @@ ${written.map((path3) => ` ${path3}`).join("\n")}`);
32460
32487
  var args = process.argv.slice(2);
32461
32488
  var optionEnd = args.indexOf("--");
32462
32489
  updateJsonMode((optionEnd < 0 ? args : args.slice(0, optionEnd)).includes("--json"));
32463
- var program2 = new Command().name("slop").description("Slopware Tasks from the terminal. Everything a person can do on the board, an agent can do here.").version("0.5.0").option("--json", "machine-readable output: JSON on stdout, errors as JSON on stderr").configureOutput({
32490
+ var program2 = new Command().name("slop").description("Slopware Tasks from the terminal. Everything a person can do on the board, an agent can do here.").version("0.6.0").option("--json", "machine-readable output: JSON on stdout, errors as JSON on stderr").configureOutput({
32464
32491
  outputError: (message, write) => {
32465
32492
  if (!isJsonMode()) write(message);
32466
32493
  },
@@ -32476,7 +32503,7 @@ var taskCommand = registerTaskCommands(program2);
32476
32503
  var sessionCommand = registerSessionCommands(program2);
32477
32504
  registerThreadCommands(program2, taskCommand);
32478
32505
  registerOperationCommands(program2, taskCommand, sessionCommand);
32479
- registerMcpCommands(program2, "0.5.0");
32506
+ registerMcpCommands(program2, "0.6.0");
32480
32507
  registerGuideCommand(program2);
32481
32508
  registerSkillCommands(program2);
32482
32509
  program2.parseAsync().catch(exitWithError);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slopwaresystems",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "private": false,
5
5
  "description": "slop: the Slopware Tasks board from the terminal, for people and coding agents.",
6
6
  "type": "module",