svamp-cli 0.2.356 → 0.2.358

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 (26) hide show
  1. package/dist/{agentCommands-D91RcjqQ.mjs → agentCommands-C_KvvYFp.mjs} +5 -5
  2. package/dist/{claudeAuth-C0wc1GTM.mjs → claudeAuth-DMDvPNhO.mjs} +11 -6
  3. package/dist/cli.mjs +136 -67
  4. package/dist/{commands-CoOc4qiw.mjs → commands-B19lVZjm.mjs} +10 -3
  5. package/dist/{commands-C13-yE2R.mjs → commands-Bmoy6Rlw.mjs} +5 -4
  6. package/dist/{commands-sPvRcG3J.mjs → commands-CFBKkfX8.mjs} +9 -9
  7. package/dist/{commands-B-yczgz_.mjs → commands-CcdoQIkb.mjs} +114 -17
  8. package/dist/{commands-BMeDrpSv.mjs → commands-DH_YQrAP.mjs} +5 -21
  9. package/dist/{commands-BbmypSgx.mjs → commands-DRxANaS5.mjs} +61 -18
  10. package/dist/{commands-BkdbelFo.mjs → commands-nkvTnUJj.mjs} +6 -5
  11. package/dist/friendlyName-B-OEwt0t.mjs +341 -0
  12. package/dist/{headlessCli-C8HzQvis.mjs → headlessCli-BpTXz6FD.mjs} +24 -16
  13. package/dist/{hookSettings-FFc1pi8j.mjs → hookSettings-CvvUhwCK.mjs} +122 -30
  14. package/dist/{friendlyName-1UZcyDhu.mjs → inboxGuard-CDqmIypF.mjs} +32 -347
  15. package/dist/index.mjs +7 -6
  16. package/dist/{loopVerify-DALtTk2V.mjs → loopVerify-CxAKJB5a.mjs} +1 -1
  17. package/dist/package-DTTWvJbp.mjs +64 -0
  18. package/dist/{rpc-ccY7xvlo.mjs → rpc-CAlAHQAy.mjs} +3 -1
  19. package/dist/{rpc-CBkSkzsI.mjs → rpc-CacreLQ8.mjs} +10 -6
  20. package/dist/{run-DCq3aEiF.mjs → run-Bc81iurl.mjs} +26 -7
  21. package/dist/{run-DDmeV9K3.mjs → run-DKOND4dO.mjs} +48 -30
  22. package/dist/{scheduler-BkVJ27Ki.mjs → scheduler-IyLWBF8T.mjs} +25 -7
  23. package/dist/{serveCommands-CLhkccJM.mjs → serveCommands-B-Ym_QXc.mjs} +8 -8
  24. package/dist/{store-B4E6NzG6.mjs → store-D8dok6_d.mjs} +53 -14
  25. package/package.json +3 -3
  26. package/dist/package-BrsiNutS.mjs +0 -64
@@ -1,12 +1,12 @@
1
- import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { a as composeSessionId, g as generateFriendlyName } from './friendlyName-1UZcyDhu.mjs';
1
+ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { c as composeSessionId, g as generateFriendlyName } from './friendlyName-B-OEwt0t.mjs';
2
2
  import os from 'node:os';
3
3
  import { resolve, join } from 'node:path';
4
4
  import { existsSync, readFileSync, watch } from 'node:fs';
5
5
  import { c as connectToHypha } from './hyphaClient-ybivOjZo.mjs';
6
- import { c as createSessionStore, r as registerMachineService, g as generateHookSettings } from './hookSettings-FFc1pi8j.mjs';
6
+ import { c as createSessionStore, r as registerMachineService, g as generateHookSettings } from './hookSettings-CvvUhwCK.mjs';
7
7
  import { createServer } from 'node:http';
8
8
  import { spawn } from 'node:child_process';
9
- import { applyClaudeProxyEnv } from './claudeAuth-C0wc1GTM.mjs';
9
+ import { applyClaudeProxyEnv } from './claudeAuth-DMDvPNhO.mjs';
10
10
  import { createInterface } from 'node:readline';
11
11
  import 'node:crypto';
12
12
  import 'fs';
@@ -26,6 +26,7 @@ import './instance-CeHz5C8v.mjs';
26
26
  import './serveAuth-4M0vwMyj.mjs';
27
27
  import './envFlag-COOuJ3AH.mjs';
28
28
  import './codexProvider-BtxYc5Rj.mjs';
29
+ import './inboxGuard-CDqmIypF.mjs';
29
30
  import './fileLock-BMPwCrB6.mjs';
30
31
  import 'node:events';
31
32
  import './cron-hHGdb5kU.mjs';
@@ -321,6 +322,7 @@ async function runRemoteMode(opts) {
321
322
  opts.abort.addEventListener("abort", () => abortController.abort(), { once: true });
322
323
  }
323
324
  try {
325
+ let liveSessionId = opts.claudeSessionId;
324
326
  while (!exitReason && !abortController.signal.aborted) {
325
327
  const message = await Promise.race([
326
328
  opts.nextMessage(),
@@ -334,12 +336,25 @@ async function runRemoteMode(opts) {
334
336
  claudeBin,
335
337
  cwd,
336
338
  message,
337
- sessionId: opts.claudeSessionId,
339
+ // #1703: read the LIVE id, not the one captured when runRemoteMode was called.
340
+ //
341
+ // This used to pass `opts.claudeSessionId`, which is frozen at entry — and the id
342
+ // discovered on turn 1 is written by onSessionFound into loop.ts's own variable,
343
+ // which this function will not re-read until it returns and is called again. So
344
+ // turn 2 spawned with NO --resume: the web app sent "read foo.ts" then "now
345
+ // refactor it", and the second turn had never heard of foo.ts. EVERY remote-mode
346
+ // turn after the first was amnesiac, and each one was a full uncached prefix.
347
+ // loop.ts's header states the intended contract: "Session ID is preserved across
348
+ // switches (same Claude conversation)."
349
+ sessionId: liveSessionId,
338
350
  permissionMode: opts.permissionMode,
339
351
  hookSettingsPath: opts.hookSettingsPath,
340
352
  claudeArgs: opts.claudeArgs,
341
353
  signal: abortController.signal,
342
- onSessionFound: opts.onSessionFound,
354
+ onSessionFound: (id) => {
355
+ liveSessionId = id;
356
+ opts.onSessionFound(id);
357
+ },
343
358
  onThinkingChange,
344
359
  onMessage,
345
360
  log
@@ -385,6 +400,8 @@ async function runClaudeTurn(opts) {
385
400
  `);
386
401
  const spawnEnv = { ...process.env, CLAUDE_CODE_ENTRYPOINT: "sdk-ts" };
387
402
  delete spawnEnv.CLAUDECODE;
403
+ const proxyDesc = applyClaudeProxyEnv(spawnEnv);
404
+ if (proxyDesc) opts.log?.(`[remote] Claude auth: ${proxyDesc}`);
388
405
  const child = spawn(opts.claudeBin, args, {
389
406
  stdio: ["pipe", "pipe", "pipe"],
390
407
  cwd: opts.cwd,
@@ -526,7 +543,7 @@ function mapPermissionMode(mode) {
526
543
  async function loop(opts) {
527
544
  const { log } = opts;
528
545
  let mode = opts.startingMode;
529
- let claudeSessionId = null;
546
+ let claudeSessionId = opts.resumeSessionId || null;
530
547
  const onSessionFound = (id) => {
531
548
  if (id !== claudeSessionId) {
532
549
  log(`[loop] Session ID: ${id}`);
@@ -872,7 +889,7 @@ async function runInteractive(options) {
872
889
  process.on("SIGTERM", handleExit);
873
890
  process.on("SIGINT", handleExit);
874
891
  const claudeArgs = [...options.claudeArgs || []];
875
- if (options.resumeSessionId) ; else if (options.continueSession) {
892
+ if (!options.resumeSessionId && options.continueSession) {
876
893
  claudeArgs.push("--continue");
877
894
  }
878
895
  restoreConsoleLogs();
@@ -898,6 +915,8 @@ async function runInteractive(options) {
898
915
  try {
899
916
  const exitCode = await loop({
900
917
  cwd,
918
+ resumeSessionId: options.resumeSessionId,
919
+ // #1702
901
920
  startingMode: "local",
902
921
  hookSettingsPath: hookSettings?.settingsPath || "",
903
922
  permissionMode,
@@ -5,12 +5,13 @@ import { delimiter, join as join$1, resolve, dirname as dirname$1, basename as b
5
5
  import { fileURLToPath } from 'url';
6
6
  import { spawn as spawn$1, execFile, exec } from 'child_process';
7
7
  import { randomUUID, createHash } from 'crypto';
8
- import { a as authorizeRequest, b as countFileLines, P as ProcessSupervisor, d as retryStreakExhausted, e as computeRetryDelayMs, f as getRateLimitRetryConfig, g as generateHookSettings, h as getAccount, i as resolveAccountEnv, j as isRetryableRateLimit, c as createSessionStore, E as EXEC_MAX_BUFFER, k as execFailureOutcome, l as execSuccessOutcome, m as formatInboxMessageXml, n as loadInbox, o as isPending, p as ensureCodexHome, q as loadPersistedMachineMetadata, s as savePersistedMachineMetadata, r as registerMachineService, t as channelKeyGrantsFileAccess, C as ChannelStore, u as refreshDueAnthropicAccounts, v as refreshAnthropicToken } from './hookSettings-FFc1pi8j.mjs';
9
- import { a as composeSessionId, g as generateFriendlyName, d as friendlyNameFromId, f as formatHandle, e as clearInboundContext, w as writeInboundContext, i as isHopCapParked, j as classifyInbound, k as consumeAwaitingReply, l as peekAwaitingReply } from './friendlyName-1UZcyDhu.mjs';
8
+ import { a as authorizeRequest, b as countFileLines, P as ProcessSupervisor, d as retryStreakExhausted, e as computeRetryDelayMs, f as getRateLimitRetryConfig, g as generateHookSettings, h as getAccount, i as resolveAccountEnv, j as isRetryableRateLimit, c as createSessionStore, E as EXEC_MAX_BUFFER, k as execFailureOutcome, l as execSuccessOutcome, m as formatInboxMessageXml, n as loadInbox, o as isPending, p as ensureCodexHome, q as loadPersistedMachineMetadata, s as savePersistedMachineMetadata, r as registerMachineService, t as channelKeyGrantsFileAccess, C as ChannelStore, u as refreshDueAnthropicAccounts, v as refreshAnthropicToken } from './hookSettings-CvvUhwCK.mjs';
9
+ import { c as composeSessionId, g as generateFriendlyName, a as friendlyNameFromId, f as formatHandle } from './friendlyName-B-OEwt0t.mjs';
10
10
  import { w as withFileLock } from './fileLock-BMPwCrB6.mjs';
11
11
  import { mkdirSync, writeFileSync, rmSync, existsSync, renameSync, readFileSync, statSync, openSync, fstatSync, readSync, closeSync, readdirSync, unlinkSync, linkSync } from 'node:fs';
12
12
  import { join, dirname, basename, resolve as resolve$1 } from 'node:path';
13
13
  import { c as connectToHypha } from './hyphaClient-ybivOjZo.mjs';
14
+ import { clearInboundContext, writeInboundContext, isHopCapParked, classifyInbound, consumeAwaitingReply, peekAwaitingReply } from './inboxGuard-CDqmIypF.mjs';
14
15
  import { execSync, execFileSync, spawn } from 'node:child_process';
15
16
  import { S as SharingNotificationSync } from './sharingNotificationSync-qGIQ-3yf.mjs';
16
17
  import { AcpBackend } from './acpBackend-UaBD4zL3.mjs';
@@ -27,14 +28,14 @@ import { w as wrapWithIsolation } from './wrapCommand-CdNawSaa.mjs';
27
28
  import { applySecurityContext } from './securityContext-Cm5UTCuD.mjs';
28
29
  import { mkdir, copyFile, writeFile, chmod, rm, readdir, stat, readFile, rename } from 'node:fs/promises';
29
30
  import { homedir, platform } from 'node:os';
30
- import { applyClaudeProxyEnv, probeLongContext1MEntitlement, LONG_CONTEXT_ENTITLEMENT_RECHECK_MS, updateEnvFile } from './claudeAuth-C0wc1GTM.mjs';
31
+ import { applyClaudeProxyEnv, probeLongContext1MEntitlement, LONG_CONTEXT_ENTITLEMENT_RECHECK_MS, updateEnvFile } from './claudeAuth-DMDvPNhO.mjs';
31
32
  import { d as decodeTokenExpiry, i as isTokenExpired, c as classifyBootFailure, w as writeBootStatus, a as clearBootStatus, b as isAuthExpiryError, s as shouldRenewToken, T as TOKEN_LIFETIME_SEC, R as RENEW_CHECK_INTERVAL_MS } from './bootStatus-DqtNgJch.mjs';
32
33
  import { e as envFlagEnabled } from './envFlag-COOuJ3AH.mjs';
33
- import { r as resolveEvaluatorModel, c as backlogOraclePending, d as runLoopVerification, l as loopCancelledDuringVerify, e as resolveLoopDurable, f as decideLoopContinuation, g as resolveMaxAutoResumes, h as grantedExtensionCount, i as resolveMaxExtensions, j as resolveStuckLimit, k as decideVerifyErrorHandling, m as resolveVerifyErrorRetries, t as terminalPhaseForDone, n as accumulateLedger, s as sumUsageTokens, o as effectiveSoftCap, p as checkLoopBudget, q as isHardBudgetStop, u as isCostCeiling, v as summarizeExtensions, w as rearmStartedAt, x as oracleScopeSessionId, y as shouldRearmDormantLoop, z as parseDaemonShareEmails, D as DAEMON_SHARE_EMAILS_ENV, A as applyDaemonShareSeed, a as buildMachineShareUrl, B as recordProgressMarker, C as assessLoopProgress, E as resolveExtensionTurns, F as grantLoopExtension, G as buildGoalCondition, H as computeSoftTurnCap, U as UNBOUNDED_LOOP_ITERATIONS } from './loopVerify-DALtTk2V.mjs';
34
+ import { r as resolveEvaluatorModel, c as backlogOraclePending, d as runLoopVerification, l as loopCancelledDuringVerify, e as resolveLoopDurable, f as decideLoopContinuation, g as resolveMaxAutoResumes, h as grantedExtensionCount, i as resolveMaxExtensions, j as resolveStuckLimit, k as decideVerifyErrorHandling, m as resolveVerifyErrorRetries, t as terminalPhaseForDone, n as accumulateLedger, s as sumUsageTokens, o as effectiveSoftCap, p as checkLoopBudget, q as isHardBudgetStop, u as isCostCeiling, v as summarizeExtensions, w as rearmStartedAt, x as oracleScopeSessionId, y as shouldRearmDormantLoop, z as parseDaemonShareEmails, D as DAEMON_SHARE_EMAILS_ENV, A as applyDaemonShareSeed, a as buildMachineShareUrl, B as recordProgressMarker, C as assessLoopProgress, E as resolveExtensionTurns, F as grantLoopExtension, G as buildGoalCondition, H as computeSoftTurnCap, U as UNBOUNDED_LOOP_ITERATIONS } from './loopVerify-CxAKJB5a.mjs';
34
35
  import { p as parseJwtEmail } from './serveAuth-4M0vwMyj.mjs';
35
36
  import { getSkillsServer, getArtifactBaseUrl } from './api-BHjyC0Ph.mjs';
36
37
  import { l as loadInstanceConfig } from './instance-CeHz5C8v.mjs';
37
- import { l as listIssues, i as addIssue, q as routeCrewVerdict, r as resolveProjectRoot$1 } from './store-B4E6NzG6.mjs';
38
+ import { r as resolveProjectRoot$1, l as listIssues, i as addIssue, q as routeCrewVerdict } from './store-D8dok6_d.mjs';
38
39
  import { c as workflowSteps, d as workflowsDir, l as listWorkflows, i as isWorkflowEnabled } from './store-fi0f-ff1.mjs';
39
40
  import { randomBytes } from 'node:crypto';
40
41
 
@@ -1551,6 +1552,25 @@ function resolveContainedCwd(directory, cwd, hasSecurityContext) {
1551
1552
  return resolved;
1552
1553
  }
1553
1554
 
1555
+ function getLoopDir(directory, sessionId) {
1556
+ let root = directory;
1557
+ try {
1558
+ root = resolveProjectRoot$1(directory);
1559
+ } catch {
1560
+ }
1561
+ if (sessionId) {
1562
+ const rooted = join(root, ".svamp", sessionId, "loop");
1563
+ if (existsSync(join(rooted, "loop-state.json"))) return rooted;
1564
+ const rawScoped = join(directory, ".svamp", sessionId, "loop");
1565
+ if (existsSync(join(rawScoped, "loop-state.json"))) return rawScoped;
1566
+ for (const legacy of [join(root, ".claude", "loop"), join(directory, ".claude", "loop")]) {
1567
+ if (existsSync(join(legacy, "loop-state.json"))) return legacy;
1568
+ }
1569
+ return rooted;
1570
+ }
1571
+ return join(root, ".claude", "loop");
1572
+ }
1573
+
1554
1574
  const DEFAULT_LOG_PRUNE_POLICY = {
1555
1575
  keepFiles: 10,
1556
1576
  maxAgeDays: 14,
@@ -1684,6 +1704,14 @@ function shortRunId(seed) {
1684
1704
  }
1685
1705
  const RECONCILE_GRACE_MS = 12e4;
1686
1706
  const reportedWriteErrnos = /* @__PURE__ */ new Set();
1707
+ const RUN_STORE_LOCK_OPTS = (where) => ({
1708
+ deadlineMs: 3e3,
1709
+ breakOnDeadOwner: true,
1710
+ onFailOpen: (lockPath) => reportRunStoreWriteFailure(
1711
+ `${where}/fail-open`,
1712
+ Object.assign(new Error(`lock timeout on ${lockPath} \u2014 wrote UNLOCKED, a concurrent run record may have been lost`), { code: "ELOCKTIMEOUT" })
1713
+ )
1714
+ });
1687
1715
  function reportRunStoreWriteFailure(where, err) {
1688
1716
  const code = String(err?.code || err?.name || "UNKNOWN");
1689
1717
  if (reportedWriteErrnos.has(code)) return;
@@ -1708,7 +1736,7 @@ function recordRun(projectRoot, run) {
1708
1736
  const tmp = `${file}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
1709
1737
  writeFileSync(tmp, kept.map((r) => JSON.stringify(r)).join("\n") + "\n");
1710
1738
  renameSync(tmp, file);
1711
- });
1739
+ }, RUN_STORE_LOCK_OPTS("recordRun"));
1712
1740
  } catch (err) {
1713
1741
  reportRunStoreWriteFailure("recordRun", err);
1714
1742
  }
@@ -1786,7 +1814,7 @@ function reconcileInterruptedRuns(projectRoot) {
1786
1814
  const tmp = `${file}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
1787
1815
  writeFileSync(tmp, kept.map((r) => JSON.stringify(r)).join("\n") + "\n");
1788
1816
  renameSync(tmp, file);
1789
- });
1817
+ }, RUN_STORE_LOCK_OPTS("reconcileInterruptedRuns"));
1790
1818
  } catch (err) {
1791
1819
  reportRunStoreWriteFailure("reconcileInterruptedRuns", err);
1792
1820
  }
@@ -3210,16 +3238,6 @@ function writeSvampConfig(configPath, config) {
3210
3238
  }
3211
3239
  const DEFAULT_LOOP_MAX_ITERATIONS = UNBOUNDED_LOOP_ITERATIONS;
3212
3240
  const LOOP_CHECKPOINT_CADENCE = 25;
3213
- function getLoopDir(directory, sessionId) {
3214
- if (sessionId) {
3215
- const scoped = join$1(directory, ".svamp", sessionId, "loop");
3216
- if (existsSync$1(join$1(scoped, "loop-state.json"))) return scoped;
3217
- const legacy = join$1(directory, ".claude", "loop");
3218
- if (existsSync$1(join$1(legacy, "loop-state.json"))) return legacy;
3219
- return scoped;
3220
- }
3221
- return join$1(directory, ".claude", "loop");
3222
- }
3223
3241
  function readLoopState(directory, sessionId) {
3224
3242
  try {
3225
3243
  const p = join$1(getLoopDir(directory, sessionId), "loop-state.json");
@@ -4351,7 +4369,7 @@ async function startDaemon(options) {
4351
4369
  try {
4352
4370
  const dir = loadSessionIndex()[sessionId]?.directory;
4353
4371
  if (!dir) return;
4354
- const { reconcileServiceLinks } = await import('./agentCommands-D91RcjqQ.mjs');
4372
+ const { reconcileServiceLinks } = await import('./agentCommands-C_KvvYFp.mjs');
4355
4373
  const configPath = getSvampConfigPath(dir, sessionId);
4356
4374
  const config = readSvampConfig(configPath);
4357
4375
  const entries = Array.from(urls.entries());
@@ -4373,7 +4391,7 @@ async function startDaemon(options) {
4373
4391
  try {
4374
4392
  const dir = loadSessionIndex()[sessionId]?.directory;
4375
4393
  if (!dir) return;
4376
- const { reconcileServiceLinks } = await import('./agentCommands-D91RcjqQ.mjs');
4394
+ const { reconcileServiceLinks } = await import('./agentCommands-C_KvvYFp.mjs');
4377
4395
  const configPath = getSvampConfigPath(dir, sessionId);
4378
4396
  const config = readSvampConfig(configPath);
4379
4397
  const incoming = [{
@@ -4394,7 +4412,7 @@ async function startDaemon(options) {
4394
4412
  try {
4395
4413
  const dir = loadSessionIndex()[sessionId]?.directory;
4396
4414
  if (!dir) return;
4397
- const { dropServiceLinks } = await import('./agentCommands-D91RcjqQ.mjs');
4415
+ const { dropServiceLinks } = await import('./agentCommands-C_KvvYFp.mjs');
4398
4416
  const configPath = getSvampConfigPath(dir, sessionId);
4399
4417
  const config = readSvampConfig(configPath);
4400
4418
  if (dropServiceLinks(config, "serve", mountName)) {
@@ -4410,7 +4428,7 @@ async function startDaemon(options) {
4410
4428
  try {
4411
4429
  const dir = loadSessionIndex()[sessionId]?.directory;
4412
4430
  if (!dir) return;
4413
- const { dropServiceLinks } = await import('./agentCommands-D91RcjqQ.mjs');
4431
+ const { dropServiceLinks } = await import('./agentCommands-C_KvvYFp.mjs');
4414
4432
  const configPath = getSvampConfigPath(dir, sessionId);
4415
4433
  const config = readSvampConfig(configPath);
4416
4434
  if (dropServiceLinks(config, "tunnel", tunnelName)) {
@@ -4427,7 +4445,7 @@ async function startDaemon(options) {
4427
4445
  let managedLinkSessionsSeeded = false;
4428
4446
  async function reconcileAllServiceLinks() {
4429
4447
  try {
4430
- const { reconcileServiceLinksExact, hasManagedServiceLink } = await import('./agentCommands-D91RcjqQ.mjs');
4448
+ const { reconcileServiceLinksExact, hasManagedServiceLink } = await import('./agentCommands-C_KvvYFp.mjs');
4431
4449
  const desiredBySession = /* @__PURE__ */ new Map();
4432
4450
  const push = (sessionId, entry) => {
4433
4451
  if (!sessionId || !entry.url) return;
@@ -4501,7 +4519,7 @@ async function startDaemon(options) {
4501
4519
  }, delayMs);
4502
4520
  }
4503
4521
  async function createExposedTunnel(spec) {
4504
- const { FrpcTunnel } = await import('./hookSettings-FFc1pi8j.mjs').then(function (n) { return n.w; });
4522
+ const { FrpcTunnel } = await import('./hookSettings-CvvUhwCK.mjs').then(function (n) { return n.w; });
4505
4523
  const tunnel = new FrpcTunnel({
4506
4524
  name: spec.name,
4507
4525
  ports: spec.ports,
@@ -4522,7 +4540,7 @@ async function startDaemon(options) {
4522
4540
  }
4523
4541
  const tunnelRecreateState = /* @__PURE__ */ new Map();
4524
4542
  const tunnelRecreateInFlight = /* @__PURE__ */ new Set();
4525
- const { ServeManager } = await import('./hookSettings-FFc1pi8j.mjs').then(function (n) { return n.y; });
4543
+ const { ServeManager } = await import('./hookSettings-CvvUhwCK.mjs').then(function (n) { return n.y; });
4526
4544
  const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
4527
4545
  serveManager.setMountUnboundHook((sessionId, mountName) => {
4528
4546
  void dropMountSessionLink(sessionId, mountName);
@@ -6997,12 +7015,12 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
6997
7015
  });
6998
7016
  },
6999
7017
  onIssue: async (params) => {
7000
- const { issueRpc } = await import('./rpc-ccY7xvlo.mjs');
7018
+ const { issueRpc } = await import('./rpc-CAlAHQAy.mjs');
7001
7019
  const issueCwd = resolveContainedCwd(directory, params?.cwd, !!sessionMetadata.securityContext);
7002
7020
  return issueRpc(issueCwd, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
7003
7021
  },
7004
7022
  onWorkflow: async (params) => {
7005
- const { workflowRpc } = await import('./rpc-CBkSkzsI.mjs');
7023
+ const { workflowRpc } = await import('./rpc-CacreLQ8.mjs');
7006
7024
  const wfCwd = resolveContainedCwd(directory, params?.cwd, !!sessionMetadata.securityContext);
7007
7025
  return workflowRpc(wfCwd, params || {});
7008
7026
  },
@@ -7754,12 +7772,12 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
7754
7772
  });
7755
7773
  },
7756
7774
  onIssue: async (params) => {
7757
- const { issueRpc } = await import('./rpc-ccY7xvlo.mjs');
7775
+ const { issueRpc } = await import('./rpc-CAlAHQAy.mjs');
7758
7776
  const issueCwd = resolveContainedCwd(directory, params?.cwd, !!sessionMetadata.securityContext);
7759
7777
  return issueRpc(issueCwd, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
7760
7778
  },
7761
7779
  onWorkflow: async (params) => {
7762
- const { workflowRpc } = await import('./rpc-CBkSkzsI.mjs');
7780
+ const { workflowRpc } = await import('./rpc-CacreLQ8.mjs');
7763
7781
  const wfCwd = resolveContainedCwd(directory, params?.cwd, !!sessionMetadata.securityContext);
7764
7782
  return workflowRpc(wfCwd, params || {});
7765
7783
  },
@@ -9133,7 +9151,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
9133
9151
  const PING_TIMEOUT_MS = 15e3;
9134
9152
  const POST_RECONNECT_GRACE_MS = 2e4;
9135
9153
  const RECONNECT_JITTER_MS = 2500;
9136
- const { WorkflowScheduler } = await import('./scheduler-BkVJ27Ki.mjs');
9154
+ const { WorkflowScheduler } = await import('./scheduler-IyLWBF8T.mjs');
9137
9155
  const workflowProjectRoots = () => {
9138
9156
  const roots = /* @__PURE__ */ new Set();
9139
9157
  for (const s of pidToTrackedSession.values()) {
@@ -9823,4 +9841,4 @@ var run = /*#__PURE__*/Object.freeze({
9823
9841
  writeStopMarker: writeStopMarker
9824
9842
  });
9825
9843
 
9826
- export { stopDaemon as a, stopMarkerExists as b, clearStopMarker as c, daemonStatus as d, runStore as e, supervisorLock as f, getRun as g, run as h, listRuns as l, projectInfo as p, runWorkflow as r, startDaemon as s };
9844
+ export { stopDaemon as a, stopMarkerExists as b, clearStopMarker as c, daemonStatus as d, loadRuns as e, runStore as f, getRun as g, supervisorLock as h, run as i, listRuns as l, projectInfo as p, runWorkflow as r, startDaemon as s };
@@ -1,7 +1,7 @@
1
1
  import { i as inZone, c as cronMatches } from './cron-hHGdb5kU.mjs';
2
- import { r as resolveProjectRoot } from './store-B4E6NzG6.mjs';
2
+ import { r as resolveProjectRoot } from './store-D8dok6_d.mjs';
3
3
  import { l as listWorkflows, i as isWorkflowEnabled, w as workflowSchedules } from './store-fi0f-ff1.mjs';
4
- import { r as runWorkflow } from './run-DDmeV9K3.mjs';
4
+ import { r as runWorkflow, e as loadRuns } from './run-DKOND4dO.mjs';
5
5
  import 'node:fs';
6
6
  import 'node:path';
7
7
  import 'node:crypto';
@@ -15,7 +15,7 @@ import 'path';
15
15
  import 'url';
16
16
  import 'child_process';
17
17
  import 'crypto';
18
- import './hookSettings-FFc1pi8j.mjs';
18
+ import './hookSettings-CvvUhwCK.mjs';
19
19
  import './types-57J3Re0n.mjs';
20
20
  import './securityContext-Cm5UTCuD.mjs';
21
21
  import './machineTools-DygM1jXW.mjs';
@@ -27,10 +27,11 @@ import './instance-CeHz5C8v.mjs';
27
27
  import './serveAuth-4M0vwMyj.mjs';
28
28
  import 'node:os';
29
29
  import './envFlag-COOuJ3AH.mjs';
30
- import './claudeAuth-C0wc1GTM.mjs';
30
+ import './claudeAuth-DMDvPNhO.mjs';
31
31
  import './codexProvider-BtxYc5Rj.mjs';
32
- import './friendlyName-1UZcyDhu.mjs';
32
+ import './inboxGuard-CDqmIypF.mjs';
33
33
  import 'node:events';
34
+ import './friendlyName-B-OEwt0t.mjs';
34
35
  import './hyphaClient-ybivOjZo.mjs';
35
36
  import './sharingNotificationSync-qGIQ-3yf.mjs';
36
37
  import './acpBackend-UaBD4zL3.mjs';
@@ -50,7 +51,7 @@ import './detectIsolation-CrV8IFDv.mjs';
50
51
  import 'node:fs/promises';
51
52
  import './pinnedNono-5ZHb6328.mjs';
52
53
  import './bootStatus-DqtNgJch.mjs';
53
- import './loopVerify-DALtTk2V.mjs';
54
+ import './loopVerify-CxAKJB5a.mjs';
54
55
  import './api-BHjyC0Ph.mjs';
55
56
 
56
57
  function cronMatchesSafe(expr, date) {
@@ -60,6 +61,18 @@ function cronMatchesSafe(expr, date) {
60
61
  return false;
61
62
  }
62
63
  }
64
+ function alreadyFiredInMinute(root, name, minute) {
65
+ const boundary = Math.floor(minute.getTime() / 6e4);
66
+ try {
67
+ return loadRuns(root, name).some((r) => {
68
+ if (r.trigger !== "schedule") return false;
69
+ const started = Date.parse(String(r.startedAt || ""));
70
+ return Number.isFinite(started) && Math.floor(started / 6e4) === boundary;
71
+ });
72
+ } catch {
73
+ return false;
74
+ }
75
+ }
63
76
  class WorkflowScheduler {
64
77
  constructor(deps) {
65
78
  this.deps = deps;
@@ -133,7 +146,12 @@ class WorkflowScheduler {
133
146
  if (!isWorkflowEnabled(wf)) continue;
134
147
  const schedules = workflowSchedules(wf);
135
148
  if (!schedules.length) continue;
136
- if (minutes.some((m) => schedules.some((s) => cronMatchesSafe(s.cron, inZone(m, s.tz))))) {
149
+ const matched = minutes.find((m) => schedules.some((s) => cronMatchesSafe(s.cron, inZone(m, s.tz))));
150
+ if (matched) {
151
+ if (alreadyFiredInMinute(root, wf.name, matched)) {
152
+ this.deps.log?.(`[workflow] schedule skipped "${wf.name}" in ${root} \u2014 already fired this minute (persisted run history)`);
153
+ continue;
154
+ }
137
155
  this.fire(root, wf);
138
156
  fired.push(wf.name);
139
157
  }
@@ -56,7 +56,7 @@ async function handleServeCommand() {
56
56
  }
57
57
  }
58
58
  async function serveAdd(args, machineId) {
59
- const { connectAndGetMachine } = await import('./commands-B-yczgz_.mjs');
59
+ const { connectAndGetMachine } = await import('./commands-CcdoQIkb.mjs').then(function (n) { return n.d; });
60
60
  const pos = positionalArgs(args);
61
61
  const name = pos[0];
62
62
  if (!name) {
@@ -93,7 +93,7 @@ async function serveAdd(args, machineId) {
93
93
  }
94
94
  if (sessionId && result?.url) {
95
95
  try {
96
- const agentCommands = await import('./agentCommands-D91RcjqQ.mjs');
96
+ const agentCommands = await import('./agentCommands-C_KvvYFp.mjs');
97
97
  if (regenerateUrl) agentCommands.removeSessionLinkByService("serve", name, sessionId);
98
98
  agentCommands.autoAddSessionLink(String(result.url), name, void 0, { kind: "serve", name });
99
99
  } catch {
@@ -108,7 +108,7 @@ async function serveAdd(args, machineId) {
108
108
  }
109
109
  }
110
110
  async function serveApply(args, machineId) {
111
- const { connectAndGetMachine } = await import('./commands-B-yczgz_.mjs');
111
+ const { connectAndGetMachine } = await import('./commands-CcdoQIkb.mjs').then(function (n) { return n.d; });
112
112
  const fs = await import('fs');
113
113
  const yaml = await import('yaml');
114
114
  const file = positionalArgs(args)[0];
@@ -193,7 +193,7 @@ async function serveApply(args, machineId) {
193
193
  console.log(`URL: ${result.url}`);
194
194
  if (params.sessionId && result?.url) {
195
195
  try {
196
- const { autoAddSessionLink } = await import('./agentCommands-D91RcjqQ.mjs');
196
+ const { autoAddSessionLink } = await import('./agentCommands-C_KvvYFp.mjs');
197
197
  const prevSession = process.env.SVAMP_SESSION_ID;
198
198
  process.env.SVAMP_SESSION_ID = String(params.sessionId);
199
199
  try {
@@ -214,7 +214,7 @@ async function serveApply(args, machineId) {
214
214
  }
215
215
  }
216
216
  async function serveRemove(args, machineId) {
217
- const { connectAndGetMachine } = await import('./commands-B-yczgz_.mjs');
217
+ const { connectAndGetMachine } = await import('./commands-CcdoQIkb.mjs').then(function (n) { return n.d; });
218
218
  const pos = positionalArgs(args);
219
219
  const name = pos[0];
220
220
  if (!name) {
@@ -224,7 +224,7 @@ async function serveRemove(args, machineId) {
224
224
  const { machine, server } = await connectAndGetMachine(machineId);
225
225
  try {
226
226
  await machine.serveRemove({ name });
227
- const { removeSessionLinkByService } = await import('./agentCommands-D91RcjqQ.mjs');
227
+ const { removeSessionLinkByService } = await import('./agentCommands-C_KvvYFp.mjs');
228
228
  removeSessionLinkByService("serve", name);
229
229
  console.log(`Mount '${name}' removed.`);
230
230
  } catch (err) {
@@ -236,7 +236,7 @@ async function serveRemove(args, machineId) {
236
236
  }
237
237
  }
238
238
  async function serveList(args, machineId) {
239
- const { connectAndGetMachine } = await import('./commands-B-yczgz_.mjs');
239
+ const { connectAndGetMachine } = await import('./commands-CcdoQIkb.mjs').then(function (n) { return n.d; });
240
240
  const all = hasFlag(args, "--all", "-a");
241
241
  const json = hasFlag(args, "--json");
242
242
  const sessionId = getFlag(args, "--session") ?? process.env.SVAMP_SESSION_ID;
@@ -270,7 +270,7 @@ async function serveList(args, machineId) {
270
270
  }
271
271
  }
272
272
  async function serveInfo(machineId) {
273
- const { connectAndGetMachine } = await import('./commands-B-yczgz_.mjs');
273
+ const { connectAndGetMachine } = await import('./commands-CcdoQIkb.mjs').then(function (n) { return n.d; });
274
274
  const { machine, server } = await connectAndGetMachine(machineId);
275
275
  try {
276
276
  const info = await machine.serveInfo();
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync, statSync, mkdirSync, writeFileSync, readdirSync, unlinkSync, renameSync } from 'node:fs';
2
2
  import { join, dirname } from 'node:path';
3
3
  import { randomBytes } from 'node:crypto';
4
- import { execFileSync, execSync, exec } from 'node:child_process';
4
+ import { execFileSync, spawnSync, exec } from 'node:child_process';
5
5
  import { w as withFileLock } from './fileLock-BMPwCrB6.mjs';
6
6
 
7
7
  function normalizeStatus(s) {
@@ -21,7 +21,14 @@ const FIELD_ORDER = ["id", "title", "status", "scope", "labels", "verify", "disp
21
21
  function resolveProjectRoot(start = process.cwd()) {
22
22
  let dir = start;
23
23
  for (let i = 0; i < 40; i++) {
24
- if (existsSync(join(dir, ".git")) || existsSync(join(dir, ".svamp"))) return dir;
24
+ if (existsSync(join(dir, ".git"))) return dir;
25
+ const parent = dirname(dir);
26
+ if (parent === dir) break;
27
+ dir = parent;
28
+ }
29
+ dir = start;
30
+ for (let i = 0; i < 40; i++) {
31
+ if (existsSync(join(dir, ".svamp"))) return dir;
25
32
  const parent = dirname(dir);
26
33
  if (parent === dir) break;
27
34
  dir = parent;
@@ -211,7 +218,12 @@ function withIssueLock(projectRoot, id, fn) {
211
218
  mkdirSync(issuesDir(projectRoot), { recursive: true });
212
219
  } catch {
213
220
  }
214
- return withFileLock(issueLockPath(projectRoot, id), fn, { deadlineMs: ISSUE_LOCK_DEADLINE_MS });
221
+ return withFileLock(issueLockPath(projectRoot, id), fn, {
222
+ deadlineMs: ISSUE_LOCK_DEADLINE_MS,
223
+ breakOnDeadOwner: true,
224
+ // #1226: a fail-open here means a concurrent mutation may have been LOST. It was silent.
225
+ onFailOpen: (p) => console.error(`[issue] lock timeout after ${ISSUE_LOCK_DEADLINE_MS}ms \u2014 proceeding UNSYNCHRONIZED, a concurrent update to this issue may be lost: ${p}`)
226
+ });
215
227
  }
216
228
  function _updateIssueUnlocked(projectRoot, id, patch) {
217
229
  const cur = getIssue(projectRoot, id);
@@ -319,32 +331,59 @@ function searchIssuesInMemory(projectRoot, q) {
319
331
  const ql = q.toLowerCase();
320
332
  return listIssues(projectRoot, { includeArchived: true }).filter((i) => i.title.toLowerCase().includes(ql) || (i.body || "").toLowerCase().includes(ql) || i.labels.some((l) => l.toLowerCase().includes(ql)));
321
333
  }
334
+ const VERIFY_GATE_NOT_FOUND_STATUS = 127;
335
+ function isUnrunnableVerifyResult(status, output) {
336
+ if (status === VERIFY_GATE_NOT_FOUND_STATUS) return true;
337
+ return /: (command not found|not found)\s*$/m.test(output);
338
+ }
322
339
  function verifyGateCommand(issue) {
323
340
  return issue?.verify?.type === "command" ? (issue.verify.text || "").trim() : "";
324
341
  }
325
342
  const VERIFY_GATE_TIMEOUT_MS = 6e5;
326
343
  const VERIFY_GATE_MAX_BUFFER = 64 * 1024 * 1024;
327
- function verifyGateFailure(cmd, stdout, stderr) {
328
- const output = (String(stdout || "") + String(stderr || "")).split("\n").slice(-15).join("\n").trim();
329
- return { ok: false, cmd, output };
344
+ function verifyOutputTail(stdout, stderr) {
345
+ return (String(stdout || "") + String(stderr || "")).split("\n").slice(-15).join("\n").trim();
346
+ }
347
+ function verifyTreeProvenance(projectRoot) {
348
+ try {
349
+ const opts = { cwd: projectRoot, stdio: "pipe", timeout: 5e3 };
350
+ const head = String(execFileSync("git", ["rev-parse", "--short", "HEAD"], opts)).trim();
351
+ if (!head) return null;
352
+ const st = String(execFileSync("git", ["status", "--porcelain"], opts)).trim();
353
+ const dirty = st ? st.split("\n").length : 0;
354
+ return dirty ? `${head} (+${dirty} dirty)` : `${head} (clean)`;
355
+ } catch {
356
+ return null;
357
+ }
358
+ }
359
+ function verifyGatePass(res) {
360
+ const where = res.tree ? `tree ${res.tree}` : "tree unknown (not a git worktree?)";
361
+ const out = res.output ? `
362
+ ${res.output}` : "\n(verify-cmd produced no output)";
363
+ return `\u2713 verify-cmd passed in ${where}
364
+ $ ${res.cmd}${out}`;
365
+ }
366
+ function verifyGateFailure(cmd, stdout, stderr, status) {
367
+ const output = verifyOutputTail(stdout, stderr);
368
+ const notRunnable = isUnrunnableVerifyResult(status, output);
369
+ return notRunnable ? { ok: false, cmd, output, notRunnable } : { ok: false, cmd, output };
330
370
  }
331
371
  function checkVerifyCommand(projectRoot, issue) {
332
372
  const cmd = verifyGateCommand(issue);
333
373
  if (!cmd) return { ok: true };
334
- try {
335
- execSync(cmd, { cwd: projectRoot, stdio: "pipe", timeout: VERIFY_GATE_TIMEOUT_MS, maxBuffer: VERIFY_GATE_MAX_BUFFER });
336
- return { ok: true, cmd };
337
- } catch (e) {
338
- return verifyGateFailure(cmd, e?.stdout, e?.stderr);
374
+ const r = spawnSync(cmd, { cwd: projectRoot, shell: true, encoding: "utf8", timeout: VERIFY_GATE_TIMEOUT_MS, maxBuffer: VERIFY_GATE_MAX_BUFFER });
375
+ if (r.error || r.status !== 0) {
376
+ return verifyGateFailure(cmd, r.stdout, r.stderr, r.status ?? r.error?.code);
339
377
  }
378
+ return { ok: true, cmd, output: verifyOutputTail(r.stdout, r.stderr), tree: verifyTreeProvenance(projectRoot) ?? void 0 };
340
379
  }
341
380
  function checkVerifyCommandAsync(projectRoot, issue) {
342
381
  const cmd = verifyGateCommand(issue);
343
382
  if (!cmd) return Promise.resolve({ ok: true });
344
383
  return new Promise((resolve) => {
345
384
  exec(cmd, { cwd: projectRoot, timeout: VERIFY_GATE_TIMEOUT_MS, maxBuffer: VERIFY_GATE_MAX_BUFFER }, (err, stdout, stderr) => {
346
- if (err) resolve(verifyGateFailure(cmd, stdout, stderr));
347
- else resolve({ ok: true, cmd });
385
+ if (err) resolve(verifyGateFailure(cmd, stdout, stderr, err?.code));
386
+ else resolve({ ok: true, cmd, output: verifyOutputTail(stdout, stderr), tree: verifyTreeProvenance(projectRoot) ?? void 0 });
348
387
  });
349
388
  });
350
389
  }
@@ -353,4 +392,4 @@ function summarize(issues) {
353
392
  return { total: issues.length, ready: by("ready"), in_progress: by("in_progress"), paused: by("paused"), archived: by("archived") };
354
393
  }
355
394
 
356
- export { ISSUE_SCOPES as I, applyTriageStatus as a, parseStatusArg as b, checkVerifyCommandAsync as c, ISSUE_STATUSES as d, resumeIssue as e, pauseIssue as f, getIssue as g, addComment as h, addIssue as i, isPendingIssue as j, isVisibleTo as k, listIssues as l, checkVerifyCommand as m, summarize as n, resolveCrewIssueOnMerge as o, parseScopeArg as p, routeCrewVerdict as q, resolveProjectRoot as r, searchIssues as s, updateIssue as u };
395
+ export { ISSUE_SCOPES as I, applyTriageStatus as a, addComment as b, checkVerifyCommandAsync as c, parseStatusArg as d, ISSUE_STATUSES as e, resumeIssue as f, getIssue as g, pauseIssue as h, addIssue as i, isPendingIssue as j, isVisibleTo as k, listIssues as l, checkVerifyCommand as m, summarize as n, resolveCrewIssueOnMerge as o, parseScopeArg as p, routeCrewVerdict as q, resolveProjectRoot as r, searchIssues as s, updateIssue as u, verifyGatePass as v };