slopwaresystems 0.9.4 → 0.9.5

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/dist/index.js CHANGED
@@ -146,7 +146,7 @@ function fetchLaunchCandidate(launcher2) {
146
146
  var path = fileURLToPath(import.meta.url);
147
147
  var launcher = {
148
148
  path,
149
- bundledVersion: "0.9.4",
149
+ bundledVersion: "0.9.5",
150
150
  bundledMain: fileURLToPath(new URL("./main.js", import.meta.url)),
151
151
  installed: isInstalledLauncherPath(path)
152
152
  };
package/dist/main.js CHANGED
@@ -26542,7 +26542,7 @@ var TaskEventRecord = external_exports.object({
26542
26542
  });
26543
26543
 
26544
26544
  // ../contract/src/work/cli-release.ts
26545
- var CLI_RELEASE = "0.9.4";
26545
+ var CLI_RELEASE = "0.9.5";
26546
26546
  var CLI_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
26547
26547
  var CLI_VERSION_MAX = 64;
26548
26548
  function parseCliVersion(version2) {
@@ -28760,6 +28760,25 @@ function persistRememberedConversation(cwd, conversation) {
28760
28760
  const directory = `${buildConversationPath(cwd)}.d`;
28761
28761
  persistLocalState(join3(directory, `${generateLocalStateKey(cwd, conversation.id)}.json`), JSON.stringify(conversation));
28762
28762
  }
28763
+ var buildRelayConversationPath = (relay) => join3(buildSessionsDir(), `relay-${relay.relayPid}-${relay.harnessPid}.conversation.json`);
28764
+ function persistRelayConversation(relay, conversation) {
28765
+ try {
28766
+ persistLocalState(buildRelayConversationPath(relay), JSON.stringify(conversation));
28767
+ } catch {
28768
+ }
28769
+ }
28770
+ function fetchRelayConversation(relay) {
28771
+ try {
28772
+ const parsed = JSON.parse(readFileSync4(buildRelayConversationPath(relay), "utf8"));
28773
+ const harness = AgentHarness.safeParse(parsed.harness);
28774
+ return typeof parsed.id === "string" && parsed.id && harness.success ? { id: parsed.id, harness: harness.data } : null;
28775
+ } catch {
28776
+ return null;
28777
+ }
28778
+ }
28779
+ function deleteRelayConversation(relay) {
28780
+ rmSync4(buildRelayConversationPath(relay), { force: true });
28781
+ }
28763
28782
  function fetchRememberedConversation(cwd, notBefore, harness) {
28764
28783
  const path3 = buildConversationPath(cwd);
28765
28784
  const candidates = [];
@@ -29378,6 +29397,24 @@ function isMainChatThread(roots, threadId) {
29378
29397
  const head = fetchFileHead(path3, HEAD_BYTES);
29379
29398
  return head === "" ? true : isMainChatRollout(head.split("\n")[0] ?? "");
29380
29399
  }
29400
+ function parseCodexThreadFromToolCallMeta(meta3) {
29401
+ const turn = meta3["x-codex-turn-metadata"];
29402
+ if (typeof turn !== "object" || turn === null || Array.isArray(turn)) return null;
29403
+ const thread = turn.thread_id ?? meta3.threadId;
29404
+ return typeof thread === "string" && thread.trim() ? thread.trim() : null;
29405
+ }
29406
+ var PARENT_THREAD_HOPS = 8;
29407
+ function fetchCodexChatThreadId(roots, threadId) {
29408
+ let current = threadId;
29409
+ for (let hop = 0; hop < PARENT_THREAD_HOPS; hop += 1) {
29410
+ const path3 = fetchCodexRolloutPath(roots, current);
29411
+ const line = path3 ? fetchFileHead(path3, HEAD_BYTES).split("\n")[0] ?? "" : "";
29412
+ const parent = /"parent_thread_id"\s*:\s*"([^"]+)"/.exec(line)?.[1];
29413
+ if (!parent || parent === current) return current;
29414
+ current = parent;
29415
+ }
29416
+ return current;
29417
+ }
29381
29418
  function fetchCodexRolloutPath(roots, threadId) {
29382
29419
  const suffix = `-${threadId}.jsonl`;
29383
29420
  let best = null;
@@ -29451,15 +29488,14 @@ function fetchKnownConversation(cwd) {
29451
29488
  if (remembered?.harness === "codex" && isMainChatThread(fetchCodexHomes().map((home) => join9(home, "sessions")), remembered.id)) return remembered;
29452
29489
  return remembered?.harness === "codex" ? null : remembered;
29453
29490
  }
29454
- function resolveAgentIdentity(cwd, fallback = "process") {
29455
- const conversation = fetchKnownConversation(cwd);
29491
+ function resolveAgentIdentity(cwd, fallback = "process", conversation = fetchKnownConversation(cwd)) {
29456
29492
  const repo = fetchRepo();
29457
29493
  return {
29458
29494
  conversation: conversation?.id ?? (fallback === "process" ? processConversation : `dir:${resolve4(cwd)}`),
29459
29495
  harness: conversation?.harness ?? null,
29460
29496
  directory: repo ? basename3(repo.worktree) : basename3(cwd),
29461
29497
  branch: fetchCurrentBranch(cwd),
29462
- cliVersion: "0.9.4"
29498
+ cliVersion: "0.9.5"
29463
29499
  };
29464
29500
  }
29465
29501
 
@@ -29924,6 +29960,9 @@ linked to ${link.projectKey} ${link.projectName} (${link.baseUrl})`
29924
29960
  });
29925
29961
  }
29926
29962
 
29963
+ // src/commands/mcp.ts
29964
+ import { join as join26 } from "path";
29965
+
29927
29966
  // src/platform/mcp.ts
29928
29967
  import { createInterface } from "readline";
29929
29968
 
@@ -30008,6 +30047,7 @@ function buildMcpRequestHandler(tools, info, sendJsonRpcMessage, options2 = {})
30008
30047
  const output2 = new McpOutput();
30009
30048
  let text;
30010
30049
  try {
30050
+ options2.onToolCall?.(isRecord(params._meta) ? params._meta : {});
30011
30051
  const suppliedArguments = params.arguments ?? {};
30012
30052
  const args2 = tool.parseInput ? tool.parseInput(suppliedArguments) : suppliedArguments;
30013
30053
  text = output2.formatResult(await tool.run(args2, output2));
@@ -30123,7 +30163,7 @@ function buildUpdateNotice() {
30123
30163
  }
30124
30164
  function resolveLatestCliVersion() {
30125
30165
  const deprecated = fetchDeprecatedVersions();
30126
- return sortCliVersions([CLI_RELEASE, "0.9.4", fetchUpdateStatus().latest ?? CLI_RELEASE]).filter((version2) => !deprecated.has(version2)).at(-1) ?? CLI_RELEASE;
30166
+ return sortCliVersions([CLI_RELEASE, "0.9.5", fetchUpdateStatus().latest ?? CLI_RELEASE]).filter((version2) => !deprecated.has(version2)).at(-1) ?? CLI_RELEASE;
30127
30167
  }
30128
30168
  function formatAgentCliVersion(version2, latest = resolveLatestCliVersion()) {
30129
30169
  const label = version2 ? `slop ${version2}` : "an older slop";
@@ -30559,7 +30599,7 @@ async function updateInstalledCliFromRegistry(launcher, options2) {
30559
30599
  const running = fetchLaunchCandidate(launcher).version;
30560
30600
  const action = running === previous ? "current" : isNewerVersion(running, previous) ? "updated" : "rolled-back";
30561
30601
  const latest = resolveNewestOfferedRelease(index);
30562
- deleteUnusedVersions(/* @__PURE__ */ new Set([running, previous, current, launcher.bundledVersion, "0.9.4"]));
30602
+ deleteUnusedVersions(/* @__PURE__ */ new Set([running, previous, current, launcher.bundledVersion, "0.9.5"]));
30563
30603
  persistUpdateStatus({ checkedAt: now(), latest, failures: 0, lastError: null });
30564
30604
  return { action, running, previous, latest };
30565
30605
  } catch (error62) {
@@ -31366,7 +31406,7 @@ function watchLaunchTarget(onChange) {
31366
31406
  }
31367
31407
  function runMcpRelay() {
31368
31408
  const launcher = readLauncherInfo();
31369
- const own2 = { version: "0.9.4", main: fileURLToPath(import.meta.url), downloaded: false };
31409
+ const own2 = { version: "0.9.5", main: fileURLToPath(import.meta.url), downloaded: false };
31370
31410
  const relay = new McpRelay({
31371
31411
  input: process.stdin,
31372
31412
  output: process.stdout,
@@ -31807,7 +31847,7 @@ async function waitForMessage(api, projectId, taskKey, options2 = {}) {
31807
31847
  setTimeout(() => resolve6(outcome), 50);
31808
31848
  };
31809
31849
  const held = new HeldSession(api, projectId, taskKey, {
31810
- claim: () => options2.claim ?? { harness: null, directory: null, branch: null, cliVersion: "0.9.4" },
31850
+ claim: () => options2.claim ?? { harness: null, directory: null, branch: null, cliVersion: "0.9.5" },
31811
31851
  reconnect: false,
31812
31852
  onHello: options2.onHello,
31813
31853
  onEnd: () => finishWait({ kind: "ended" }),
@@ -35445,7 +35485,7 @@ var SplatRendererConnection = class {
35445
35485
  try {
35446
35486
  let path3;
35447
35487
  try {
35448
- const ticket = await this.api.post(`/projects/${this.projectId}/splat/renderer/ticket`, ApiResponses.ConnectTicket, { cliVersion: "0.9.4", onlyMe: this.options.onlyMe ?? false, agent: this.options.agent ?? null, capabilities: ["workspaces"] });
35488
+ const ticket = await this.api.post(`/projects/${this.projectId}/splat/renderer/ticket`, ApiResponses.ConnectTicket, { cliVersion: "0.9.5", onlyMe: this.options.onlyMe ?? false, agent: this.options.agent ?? null, capabilities: ["workspaces"] });
35449
35489
  path3 = ticket.path;
35450
35490
  } catch (error62) {
35451
35491
  if (error62 instanceof CliError && ["unauthenticated", "not_found", "forbidden", "bad_request"].includes(error62.code)) {
@@ -37479,17 +37519,17 @@ function hasHarnessSetup() {
37479
37519
  function syncMachineSetupAfterUpdate() {
37480
37520
  const nothing = { skills: [], retired: [], reinstalling: false };
37481
37521
  try {
37482
- if (!isUpdateCheckEnabled() || fetchMachineSetupState()?.version === "0.9.4") return nothing;
37522
+ if (!isUpdateCheckEnabled() || fetchMachineSetupState()?.version === "0.9.5") return nothing;
37483
37523
  const lock = claimFileLock(buildMachineSetupLockDirectory(), { staleAfterMs: 6e4 });
37484
37524
  if (!lock) return nothing;
37485
37525
  try {
37486
37526
  const state = fetchMachineSetupState();
37487
- if (state?.version === "0.9.4") return nothing;
37527
+ if (state?.version === "0.9.5") return nothing;
37488
37528
  const skills = hasInstalledSkills() ? installSkills() : [];
37489
37529
  const shipped = Object.keys(SKILLS);
37490
37530
  const retired = deleteRetiredSkills((state?.skills ?? []).filter((name) => !shipped.includes(name)));
37491
37531
  const setupRevision = state?.setupRevision ?? (hasHarnessSetup() ? UNRECORDED_SETUP_REVISION : null);
37492
- persistMachineSetupState({ version: "0.9.4", setupRevision, skills: hasInstalledSkills() ? shipped : [] });
37532
+ persistMachineSetupState({ version: "0.9.5", setupRevision, skills: hasInstalledSkills() ? shipped : [] });
37493
37533
  const reinstalling = setupRevision !== null && setupRevision < SETUP_REVISION && runMcpInstallInBackground();
37494
37534
  return { skills, retired, reinstalling };
37495
37535
  } finally {
@@ -37656,7 +37696,7 @@ async function trustCodexHooks(codexHome) {
37656
37696
  const sendNotification = (method, params) => server.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", method, params })}
37657
37697
  `);
37658
37698
  try {
37659
- await callCodexMethod("initialize", { clientInfo: { name: "slop", version: "0.9.4" }, capabilities: { experimentalApi: true } });
37699
+ await callCodexMethod("initialize", { clientInfo: { name: "slop", version: "0.9.5" }, capabilities: { experimentalApi: true } });
37660
37700
  sendNotification("initialized", {});
37661
37701
  const listed = await callCodexMethod("hooks/list", { cwds: [process.cwd()] });
37662
37702
  const ours = listed.data.flatMap((entry) => entry.hooks).filter((hook) => hook.command?.startsWith("slop hook "));
@@ -37689,18 +37729,27 @@ function registerMcpInstallCommand(mcp) {
37689
37729
  }
37690
37730
 
37691
37731
  // src/commands/mcp.ts
37692
- function buildTools(lifecycle) {
37693
- if (isNonInteractiveHarness()) return { tools: [], startPresence: () => void 0, releaseSessions: () => void 0 };
37732
+ function buildTools(lifecycle, relay) {
37733
+ if (isNonInteractiveHarness()) return { tools: [], startPresence: () => void 0, releaseSessions: () => void 0, observeToolCall: () => void 0 };
37694
37734
  let api = null;
37695
37735
  const requireApi = () => api ??= buildAuthedApiClient();
37696
37736
  let identity = null;
37737
+ let called = relay ? fetchRelayConversation(relay) : null;
37738
+ const observeToolCall = (meta3) => {
37739
+ if (called) return;
37740
+ const thread = parseCodexThreadFromToolCallMeta(meta3);
37741
+ if (!thread) return;
37742
+ called = { id: fetchCodexChatThreadId(fetchCodexHomes().map((home) => join26(home, "sessions")), thread), harness: "codex" };
37743
+ if (relay) persistRelayConversation(relay, called);
37744
+ };
37745
+ const fetchOwnConversation = () => called ?? fetchKnownConversation(process.cwd());
37697
37746
  let stopped = false;
37698
37747
  const projects = /* @__PURE__ */ new Map();
37699
37748
  const requireProjectTools = (project) => {
37700
37749
  if (stopped) throw new Error("The MCP server is stopping");
37701
37750
  let context2 = projects.get(project.id);
37702
37751
  if (!context2) {
37703
- identity ??= resolveAgentIdentity(process.cwd());
37752
+ identity ??= resolveAgentIdentity(process.cwd(), "process", fetchOwnConversation());
37704
37753
  context2 = createProjectTools(buildAuthedApiClient(), project, identity);
37705
37754
  projects.set(project.id, context2);
37706
37755
  }
@@ -37719,8 +37768,9 @@ function buildTools(lifecycle) {
37719
37768
  if (presenceStarted) return;
37720
37769
  presenceStarted = true;
37721
37770
  void (async () => {
37722
- for (let waited = 0; waited < 1e4 && !fetchKnownConversation(process.cwd()) && !stopped; waited += 250) await new Promise((resolve6) => setTimeout(resolve6, 250));
37771
+ for (let waited = 0; waited < 1e4 && !fetchOwnConversation() && !stopped; waited += 250) await new Promise((resolve6) => setTimeout(resolve6, 250));
37723
37772
  if (stopped) return;
37773
+ if (!identity && !fetchOwnConversation() && resolveParentHarness() === "codex") return;
37724
37774
  await requireProjectTools(await resolveProject(requireApi())).start();
37725
37775
  })().catch(() => void 0);
37726
37776
  };
@@ -37728,6 +37778,7 @@ function buildTools(lifecycle) {
37728
37778
  if (stopped) return;
37729
37779
  stopped = true;
37730
37780
  for (const context2 of projects.values()) context2.stop(reason);
37781
+ if (relay && reason === "released") deleteRelayConversation(relay);
37731
37782
  };
37732
37783
  let exiting = false;
37733
37784
  const endSessionAndExit = (reason) => {
@@ -37739,7 +37790,7 @@ function buildTools(lifecycle) {
37739
37790
  process.stdin.once("end", () => endSessionAndExit(lifecycle.onInputEnd));
37740
37791
  process.stdin.once("close", () => endSessionAndExit(lifecycle.onInputEnd));
37741
37792
  for (const signal of ["SIGTERM", "SIGHUP", "SIGINT"]) process.once(signal, () => endSessionAndExit("released"));
37742
- return { tools: createProjectGroupedTools(runHandlerByName), startPresence, releaseSessions };
37793
+ return { tools: createProjectGroupedTools(runHandlerByName), startPresence, releaseSessions, observeToolCall };
37743
37794
  }
37744
37795
  function attachUpdateNoticeToTools(list) {
37745
37796
  return list.map((tool) => ({
@@ -37754,10 +37805,12 @@ ${notice}` : result;
37754
37805
  }));
37755
37806
  }
37756
37807
  function serveBoardTools(version2) {
37757
- const underRelay = drainRelayChildContextFromEnvironment() !== null;
37808
+ const relay = drainRelayChildContextFromEnvironment();
37809
+ const underRelay = relay !== null;
37758
37810
  const lifecycle = { onInputEnd: underRelay ? "orphaned" : "released" };
37759
- const { tools, startPresence, releaseSessions } = buildTools(lifecycle);
37811
+ const { tools, startPresence, releaseSessions, observeToolCall } = buildTools(lifecycle, relay);
37760
37812
  serveMcp(attachUpdateNoticeToTools(tools), { name: "slop", version: version2 }, {
37813
+ onToolCall: observeToolCall,
37761
37814
  onNotification: (method) => {
37762
37815
  if (method === RELAY_START_NOTIFICATION) startPresence();
37763
37816
  else if (method === RELAY_HANDOVER_NOTIFICATION) {
@@ -37921,7 +37974,7 @@ var options = optionEnd < 0 ? args : args.slice(0, optionEnd);
37921
37974
  updateJsonMode(options.includes("--json"));
37922
37975
  syncMachineSetupAfterUpdate();
37923
37976
  if (!options.includes("--background") && !options.includes("--child")) scheduleBackgroundUpdate();
37924
- 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.9.4").option("--json", "machine-readable output: JSON on stdout, errors as JSON on stderr").configureOutput({
37977
+ 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.9.5").option("--json", "machine-readable output: JSON on stdout, errors as JSON on stderr").configureOutput({
37925
37978
  outputError: (message, write) => {
37926
37979
  if (!isJsonMode()) write(message);
37927
37980
  },
@@ -37938,7 +37991,7 @@ var sessionCommand = registerSessionCommands(program2);
37938
37991
  registerThreadCommands(program2, taskCommand);
37939
37992
  registerOperationCommands(program2, taskCommand, sessionCommand);
37940
37993
  registerSplatCommands(program2);
37941
- registerMcpCommands(program2, "0.9.4");
37994
+ registerMcpCommands(program2, "0.9.5");
37942
37995
  registerGuideCommand(program2);
37943
37996
  registerSkillCommands(program2);
37944
37997
  registerUpdateCommand(program2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slopwaresystems",
3
- "version": "0.9.4",
3
+ "version": "0.9.5",
4
4
  "private": false,
5
5
  "description": "slop: the Slopware Tasks board from the terminal, for people and coding agents.",
6
6
  "type": "module",