@sema-agent/cli 1.0.13 → 1.0.15

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 (3) hide show
  1. package/npm-shrinkwrap.json +17 -17
  2. package/package.json +5 -5
  3. package/sema.js +172 -18
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@sema-agent/cli",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@sema-agent/cli",
9
- "version": "1.0.13",
9
+ "version": "1.0.15",
10
10
  "license": "BUSL-1.1",
11
11
  "dependencies": {
12
12
  "@sema-agent/sdk": "0.0.53",
13
- "@sema-agent/server": "1.218.0"
13
+ "@sema-agent/server": "1.220.0"
14
14
  },
15
15
  "bin": {
16
16
  "sema": "sema.js"
@@ -629,9 +629,9 @@
629
629
  }
630
630
  },
631
631
  "node_modules/@sema-agent/core": {
632
- "version": "1.309.0",
633
- "resolved": "https://registry.npmjs.org/@sema-agent/core/-/core-1.309.0.tgz",
634
- "integrity": "sha512-xn97ll9ia9sdIYHloZCD2IMmMxFyMeEygPoXNpGdD5JNx/owpudZhae1NYQU9NR7yBvnRla7uZz4C841+mLMHw==",
632
+ "version": "1.312.0",
633
+ "resolved": "https://registry.npmjs.org/@sema-agent/core/-/core-1.312.0.tgz",
634
+ "integrity": "sha512-a50syYHBa3TT1FjqZCGwK2YH3gt6o4YiyamJYGwY4ABdwCaj8wGBJZmkQw48J+1XEDo7NL/rV5xtaNzxn656Gw==",
635
635
  "license": "BUSL-1.1",
636
636
  "dependencies": {
637
637
  "@modelcontextprotocol/sdk": "1.29.0",
@@ -645,9 +645,9 @@
645
645
  }
646
646
  },
647
647
  "node_modules/@sema-agent/registry-core": {
648
- "version": "0.10.14",
649
- "resolved": "https://registry.npmjs.org/@sema-agent/registry-core/-/registry-core-0.10.14.tgz",
650
- "integrity": "sha512-6EJe0WmrOrnEY/mITs8FHwCbBy5zcUWzRd1QaOYdEPF5H1DGvv/YcowZ4fR9HTWOpa9l8VI5mJv8gJA4EVxD0w==",
648
+ "version": "0.10.15",
649
+ "resolved": "https://registry.npmjs.org/@sema-agent/registry-core/-/registry-core-0.10.15.tgz",
650
+ "integrity": "sha512-nOwzBLFSMSgpkP9ftPLPj68IzhsP8fHrT+isCojNPF9oywFsROpFiNzNszmFsTt53eBnpQ+tHwxdsJXU0vV1xA==",
651
651
  "license": "BUSL-1.1",
652
652
  "dependencies": {
653
653
  "zod": "^3.24.0"
@@ -678,13 +678,13 @@
678
678
  }
679
679
  },
680
680
  "node_modules/@sema-agent/server": {
681
- "version": "1.218.0",
682
- "resolved": "https://registry.npmjs.org/@sema-agent/server/-/server-1.218.0.tgz",
683
- "integrity": "sha512-D7XfWT1qu7JR/3tFcJSvi79cuKw23cceFbD8Ic/dflxyPAvUm1OOez6tDViUA1bqONkQjNqDI9I+7EzUtmM9Gg==",
681
+ "version": "1.220.0",
682
+ "resolved": "https://registry.npmjs.org/@sema-agent/server/-/server-1.220.0.tgz",
683
+ "integrity": "sha512-3i1zW48+gxS6Csb7YDD8PiLInphHtFeLBKTTexwoBCA6bYKqwHSJeO1Zr3YzHTzSm/+gqmOs2VGRCqNYbOz8gg==",
684
684
  "license": "BUSL-1.1",
685
685
  "dependencies": {
686
- "@sema-agent/core": "^1.305.0",
687
- "@sema-agent/registry-core": "^0.10.12",
686
+ "@sema-agent/core": "^1.311.0",
687
+ "@sema-agent/registry-core": "^0.10.14",
688
688
  "e2b": "^2.28.0",
689
689
  "libsodium-wrappers": "^0.8.4",
690
690
  "mysql2": "^3.22.4",
@@ -1077,9 +1077,9 @@
1077
1077
  }
1078
1078
  },
1079
1079
  "node_modules/e2b": {
1080
- "version": "2.34.0",
1081
- "resolved": "https://registry.npmjs.org/e2b/-/e2b-2.34.0.tgz",
1082
- "integrity": "sha512-oQ+5k1WHBEgZEPPx/ziBbFR9fM+x0ywsd74MGRcbUZQuC0XDbL8tVpHrv/gfGLOEwmZwewxTZcLHTg4xVZp5Lg==",
1080
+ "version": "2.35.0",
1081
+ "resolved": "https://registry.npmjs.org/e2b/-/e2b-2.35.0.tgz",
1082
+ "integrity": "sha512-ZOu2aNbnTtUcMBbLMB6houapLJxACco+PbmybzP42Id3H7ud8Z1HkQAVKj9nXwSH4g8llojwsRx6Ro6asQGJrg==",
1083
1083
  "license": "MIT",
1084
1084
  "dependencies": {
1085
1085
  "@bufbuild/protobuf": "^2.12.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/cli",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "Sema — your own Claude Code-grade coding agent, in the terminal.",
5
5
  "license": "BUSL-1.1",
6
6
  "bin": {
@@ -22,13 +22,13 @@
22
22
  ],
23
23
  "dependencies": {
24
24
  "@sema-agent/sdk": "0.0.53",
25
- "@sema-agent/server": "1.218.0"
25
+ "@sema-agent/server": "1.220.0"
26
26
  },
27
27
  "overrides": {
28
- "@sema-agent/core": "1.309.0",
29
- "@sema-agent/registry-core": "0.10.14"
28
+ "@sema-agent/core": "1.312.0",
29
+ "@sema-agent/registry-core": "0.10.15"
30
30
  },
31
- "semaEngineVersion": "server 1.218.0 / core 1.309.0 / registry-core 0.10.14",
31
+ "semaEngineVersion": "server 1.220.0 / core 1.312.0 / registry-core 0.10.15",
32
32
  "engines": {
33
33
  "node": ">=18"
34
34
  }
package/sema.js CHANGED
@@ -107341,6 +107341,8 @@ var init_ink = __esm({
107341
107341
  // screen readers / screen magnifiers track it — so parking at the text
107342
107342
  // input's caret makes CJK input appear inline and lets a11y tools follow.
107343
107343
  cursorDeclaration = null;
107344
+ /** sema 光标/IME:声明光标当前是否已 ?25h 显示(防每帧重复写显隐序)。 */
107345
+ declaredCursorShown = false;
107344
107346
  // Main-screen: physical cursor position after the declared-cursor move,
107345
107347
  // tracked separately from frame.cursor (which must stay at content-bottom
107346
107348
  // for log-update's relative-move invariants). Alt-screen doesn't need
@@ -107627,6 +107629,10 @@ var init_ink = __esm({
107627
107629
  });
107628
107630
  }
107629
107631
  }
107632
+ if (!this.declaredCursorShown) {
107633
+ optimized.push({ type: "stdout", content: "\x1B[?25h" });
107634
+ this.declaredCursorShown = true;
107635
+ }
107630
107636
  this.displayCursor = target;
107631
107637
  } else {
107632
107638
  if (parked !== null && !this.altScreenActive && !hasDiff) {
@@ -107639,6 +107645,10 @@ var init_ink = __esm({
107639
107645
  });
107640
107646
  }
107641
107647
  }
107648
+ if (this.declaredCursorShown) {
107649
+ optimized.push({ type: "stdout", content: "\x1B[?25l" });
107650
+ this.declaredCursorShown = false;
107651
+ }
107642
107652
  this.displayCursor = null;
107643
107653
  }
107644
107654
  }
@@ -157477,8 +157487,23 @@ var init_selfOrchestrationWireCaps = __esm({
157477
157487
  }
157478
157488
  });
157479
157489
 
157480
- // build-src/src/sema/forkWireCaps.ts
157490
+ // build-src/src/sema/retainBackgroundWireCaps.ts
157481
157491
  function isTruthy(v2) {
157492
+ const s = typeof v2 === "string" ? v2.trim().toLowerCase() : "";
157493
+ return s === "1" || s === "true" || s === "yes" || s === "on";
157494
+ }
157495
+ function retainBackgroundFromEnv(env5 = process.env) {
157496
+ return isTruthy(env5[RETAIN_BACKGROUND_ENV]) ? true : void 0;
157497
+ }
157498
+ var RETAIN_BACKGROUND_ENV;
157499
+ var init_retainBackgroundWireCaps = __esm({
157500
+ "build-src/src/sema/retainBackgroundWireCaps.ts"() {
157501
+ RETAIN_BACKGROUND_ENV = "SEMA_RETAIN_BACKGROUND";
157502
+ }
157503
+ });
157504
+
157505
+ // build-src/src/sema/forkWireCaps.ts
157506
+ function isTruthy2(v2) {
157482
157507
  const s = v2.trim().toLowerCase();
157483
157508
  return s === "1" || s === "true" || s === "yes" || s === "on";
157484
157509
  }
@@ -157489,7 +157514,7 @@ function isFalsy2(v2) {
157489
157514
  function enableForkFromEnv(env5 = process.env) {
157490
157515
  const raw2 = env5[ENABLE_FORK_ENV];
157491
157516
  if (typeof raw2 !== "string" || raw2.trim() === "") return void 0;
157492
- if (isTruthy(raw2)) return true;
157517
+ if (isTruthy2(raw2)) return true;
157493
157518
  if (isFalsy2(raw2)) return false;
157494
157519
  return void 0;
157495
157520
  }
@@ -331848,9 +331873,9 @@ function streamingTailWrap(unstableSuffix) {
331848
331873
  var import_compiler_runtime43, import_react43, import_jsx_runtime53, TOKEN_CACHE_MAX, tokenCache, MD_SYNTAX_RE;
331849
331874
  var init_Markdown = __esm({
331850
331875
  "build-src/src/components/Markdown.tsx"() {
331851
- import_compiler_runtime43 = __toESM(require_compiler_runtime());
331876
+ import_compiler_runtime43 = __toESM(require_compiler_runtime(), 1);
331852
331877
  init_marked_esm();
331853
- import_react43 = __toESM(require_react());
331878
+ import_react43 = __toESM(require_react(), 1);
331854
331879
  init_useSettings();
331855
331880
  init_ink2();
331856
331881
  init_cliHighlight();
@@ -331858,7 +331883,7 @@ var init_Markdown = __esm({
331858
331883
  init_markdown();
331859
331884
  init_messages4();
331860
331885
  init_MarkdownTable();
331861
- import_jsx_runtime53 = __toESM(require_jsx_runtime());
331886
+ import_jsx_runtime53 = __toESM(require_jsx_runtime(), 1);
331862
331887
  TOKEN_CACHE_MAX = 500;
331863
331888
  tokenCache = /* @__PURE__ */ new Map();
331864
331889
  MD_SYNTAX_RE = /[#*`|[>\-_~]|\n\n|^\d+\. |\n\d+\. /;
@@ -433312,6 +433337,10 @@ function toTaskRequest(params) {
433312
433337
  if (selfOrchestration && process.env.SEMA_DEBUG) {
433313
433338
  console.error("[sema][seamQuery] selfOrchestration wire: SEMA_SELF_ORCHESTRATION \u2192 TaskRequest.selfOrchestration=true (run_workflow)");
433314
433339
  }
433340
+ const retainBackground = process.env.SEMA_LIVE_BASEURL ? retainBackgroundFromEnv() : void 0;
433341
+ if (retainBackground && process.env.SEMA_DEBUG) {
433342
+ console.error("[sema][seamQuery] retainBackground wire: SEMA_RETAIN_BACKGROUND \u2192 TaskRequest.retainBackgroundProcesses=true");
433343
+ }
433315
433344
  const attachments = process.env.SEMA_LIVE_BASEURL ? attachmentsForRequest() : void 0;
433316
433345
  if (attachments && process.env.SEMA_DEBUG) {
433317
433346
  console.error(`[sema][seamQuery] attachments wire: ${Object.keys(attachments).join("+")} \u2192 TaskRequest.attachments`);
@@ -433391,6 +433420,7 @@ ${objective}`;
433391
433420
  // Workflow super-set: the top-level selfOrchestration flag (SEMA_SELF_ORCHESTRATION env opt-in, live-gated).
433392
433421
  // Present ⇒ the SERVICE mounts core run_workflow (gated on its deployment/tenant policy); absent ⇒ no stamp.
433393
433422
  ...selfOrchestration ? { selfOrchestration } : {},
433423
+ ...retainBackground ? { retainBackgroundProcesses: retainBackground } : {},
433394
433424
  // Fork super-set: the top-level enableFork flag — EXPLICIT boolean on live (core ≥1.257 treats absent
433395
433425
  // as granted; the shell's default-OFF product decision must be spelled out). Mock path stays unstamped.
433396
433426
  ...enableFork !== void 0 ? { enableFork } : {},
@@ -433711,6 +433741,7 @@ var init_seamQuery = __esm({
433711
433741
  init_imagesWireCaps();
433712
433742
  init_webSearchWireCaps();
433713
433743
  init_selfOrchestrationWireCaps();
433744
+ init_retainBackgroundWireCaps();
433714
433745
  init_forkWireCaps();
433715
433746
  init_attachmentsWireCaps();
433716
433747
  init_clientContextWireCaps();
@@ -450640,7 +450671,7 @@ function Settings({
450640
450671
  var import_react113, import_jsx_runtime192;
450641
450672
  var init_cmd_config = __esm({
450642
450673
  "build-src/src/sema/overrides/cmd-config.tsx"() {
450643
- import_react113 = __toESM(require_react());
450674
+ import_react113 = __toESM(require_react(), 1);
450644
450675
  init_useKeybinding();
450645
450676
  init_useExitOnCtrlCDWithKeybindings();
450646
450677
  init_useTerminalSize();
@@ -450651,7 +450682,7 @@ var init_cmd_config = __esm({
450651
450682
  init_Config();
450652
450683
  init_settings_usage();
450653
450684
  init_Stats();
450654
- import_jsx_runtime192 = __toESM(require_jsx_runtime());
450685
+ import_jsx_runtime192 = __toESM(require_jsx_runtime(), 1);
450655
450686
  }
450656
450687
  });
450657
450688
 
@@ -479518,8 +479549,8 @@ var init_sema_brand = __esm({
479518
479549
  "Slash-command gating wired to the fleet capability handshake"
479519
479550
  ]
479520
479551
  },
479521
- productVersion: "1.0.13",
479522
- announcement: "sema 1.0.13 \u2014 tool cards render faithfully again (CronCreate headers, skill results, full output behind ctrl+o), and scheduler writes are concurrency-safe across sessions and daemons"
479552
+ productVersion: "1.0.15",
479553
+ announcement: "sema 1.0.15 \u2014 headless (-p) runs stop mounting scheduler tools they can never honor (leaner prompts, faster turns), the init frame reports the tools actually mounted, and background processes can be retained past teardown with SEMA_RETAIN_BACKGROUND=1"
479523
479554
  };
479524
479555
  }
479525
479556
  });
@@ -482453,8 +482484,8 @@ var require_sema_brand = __commonJS({
482453
482484
  "Slash-command gating wired to the fleet capability handshake"
482454
482485
  ]
482455
482486
  },
482456
- productVersion: "1.0.13",
482457
- announcement: "sema 1.0.13 \u2014 tool cards render faithfully again (CronCreate headers, skill results, full output behind ctrl+o), and scheduler writes are concurrency-safe across sessions and daemons"
482487
+ productVersion: "1.0.15",
482488
+ announcement: "sema 1.0.15 \u2014 headless (-p) runs stop mounting scheduler tools they can never honor (leaner prompts, faster turns), the init frame reports the tools actually mounted, and background processes can be retained past teardown with SEMA_RETAIN_BACKGROUND=1"
482458
482489
  };
482459
482490
  }
482460
482491
  });
@@ -556862,6 +556893,98 @@ var init_fs_write_gate_policy = __esm({
556862
556893
  }
556863
556894
  });
556864
556895
 
556896
+ // node_modules/@sema-agent/core/dist/tools/loop-tick.js
556897
+ function pushNotificationAppendix(persistentVariant) {
556898
+ const r = persistentVariant ? "newly blocked on a decision you won't make alone, you're ending the loop" : "newly blocked on a decision you won't make alone, third straight tick with nothing to do, you're ending the loop";
556899
+ return `
556900
+
556901
+ Use PushNotification when the loop can't move further without the user, or when something landed that they'd want to act on now: ${r}, or a major update arrived (CI went red, a review changes the plan). Progress you made yourself isn't a trigger \u2014 the transcript covers that. One ping per state, not per tick.`;
556902
+ }
556903
+ function cronTick(push) {
556904
+ return `# Autonomous loop tick
556905
+
556906
+ Run the autonomous check using the loop instructions established earlier in this conversation. If you cannot find them, treat this as a no-op tick. The recurring cron will fire the next tick automatically \u2014 do not call ${SCHEDULE_WAKEUP_TOOL_NAME} from this tick.${push}`;
556907
+ }
556908
+ function dynamicTick(push) {
556909
+ return `# Autonomous loop tick (dynamic pacing)
556910
+
556911
+ Run the autonomous check using the loop instructions established earlier in this conversation. If you cannot find them, treat this as a no-op tick.
556912
+
556913
+ You scheduled this tick via the ${SCHEDULE_WAKEUP_TOOL_NAME} tool (not a recurring cron). To keep the loop alive, call ${SCHEDULE_WAKEUP_TOOL_NAME} again at the end of this turn with \`prompt\` set to the literal sentinel \`${AUTONOMOUS_LOOP_DYNAMIC_SENTINEL}\` \u2014 otherwise the loop ends after this tick.${DYNAMIC_APPENDIX}${push}`;
556914
+ }
556915
+ function resolveAutonomousLoopPrompt(prompt, opts) {
556916
+ if (prompt !== AUTONOMOUS_LOOP_SENTINEL && prompt !== AUTONOMOUS_LOOP_DYNAMIC_SENTINEL)
556917
+ return null;
556918
+ const persistent = opts?.persistentVariant === true;
556919
+ const push = opts?.pushNotification === true ? pushNotificationAppendix(persistent) : "";
556920
+ const tick = prompt === AUTONOMOUS_LOOP_DYNAMIC_SENTINEL ? dynamicTick(push) : cronTick(push);
556921
+ if (opts?.firstTick !== true)
556922
+ return tick;
556923
+ const preamble = persistent ? AUTONOMOUS_LOOP_PREAMBLE_PERSISTENT : AUTONOMOUS_LOOP_PREAMBLE;
556924
+ return `${preamble}
556925
+
556926
+ ---
556927
+
556928
+ ${tick}`;
556929
+ }
556930
+ var AUTONOMOUS_LOOP_PREAMBLE, AUTONOMOUS_LOOP_PREAMBLE_PERSISTENT, DYNAMIC_APPENDIX;
556931
+ var init_loop_tick = __esm({
556932
+ "node_modules/@sema-agent/core/dist/tools/loop-tick.js"() {
556933
+ init_scheduler_tools();
556934
+ AUTONOMOUS_LOOP_PREAMBLE = `# Autonomous loop check
556935
+
556936
+ You're being invoked on a timer while the user is away or occupied. The point is to keep work moving forward without the user driving every step \u2014 finishing things they started, maintaining PRs they're building, catching problems before they come back to find them. You're a steward, not an initiator. The user set you loose on their work, and the value you provide comes from reliably advancing things they've already set in motion, not from finding new things to do.
556937
+
556938
+ The key tension to navigate: the user trusts you enough to run autonomously, but that trust is easily lost. Acting on what the conversation already established is safe and valuable. Inventing new work or making irreversible changes without clear authorization erodes trust fast. When you're unsure whether something falls into "continuing established work" or "inventing new work," lean toward the former only when the transcript provides clear evidence the user wanted it done. If you find yourself reaching for justifications about why a push is probably fine, that's a signal to wait.
556939
+
556940
+ ## What to act on
556941
+
556942
+ The current conversation is your highest-signal source \u2014 re-read the transcript above, since everything there is something the user was actively engaged with. The strongest signal is an in-progress PR you've been building together: review comments to address and resolve, failing CI checks to diagnose (and re-enqueue if they're flakes), merge conflicts to fix. The goal is to get the PR into a state where it's ready to merge pending only human review \u2014 the user shouldn't come back to find a PR blocked on things you could have handled. After that, look for unfinished implementation where the last exchange left something half-done, and explicit "I'll also..." or "next I'll..." commitments the conversation made and didn't honor. Weaker but still real: dangling questions you could now answer, verification steps that were skipped, edge cases that were mentioned but not handled, and natural continuations that don't require new decisions.
556943
+
556944
+ If you find anything in this category, act on it \u2014 actually do the work, don't describe what could be done. Run the tests, don't say "you could run the tests." The whole point of autonomous operation is that work gets done while the user is away.
556945
+
556946
+ When the conversation transcript has nothing left, the current branch's pull/merge request on the user's SCM is the next-best place to look. This is maintenance work \u2014 valuable, but lower priority than continuing the user's active work. Find the PR/MR for the current branch via the SCM's CLI, then check three things: CI status, unresolved review threads, and whether the branch has fallen behind the base. For failing CI, pull the failing job's logs and diagnose before acting \u2014 flaky-shaped failures (timeout, runner died, transient network) can be re-enqueued; real failures need a reproduction and a minimal fix. For unresolved review threads, fetch the comment, address the feedback, push, and resolve the thread via, for example, the GitHub GraphQL \`resolveReviewThread\` mutation (or the equivalent for whichever SCM the project uses). Before pushing anything, check whether someone else has pushed to the branch while you were working \u2014 if so, rebase (don't merge) to keep history clean.
556947
+
556948
+ When CI is green, threads are clear, and there's idle time, sweeping the branch for issues is a good use of that time \u2014 bug-hunt or simplification passes catch problems before reviewers do, saving everyone a round-trip.
556949
+
556950
+ If everything is genuinely quiet \u2014 no conversation work, no PR maintenance \u2014 say so in one sentence and stop. No summary of what you checked, no list of what you might do later. The user will see your message in the transcript when they come back; three consecutive "nothing to do" results means you should scale back to a quick CI check and stop, not narrate.
556951
+
556952
+ ## Repeated invocations
556953
+
556954
+ If you see earlier autonomous checks in this conversation, adjust your scope accordingly. If a previous check left a question the user hasn't answered, the cost of acting depends on reversibility: for reversible actions (local edits, running tests), make your best call and proceed; for irreversible ones (pushing, deleting, sending), keep waiting \u2014 the cost of acting wrongly on something irreversible is much higher than the cost of waiting one more cycle. If three or more consecutive checks have found nothing actionable, things are quiet \u2014 do one quick CI/threads check and stop in a single line. Repeated "nothing to do" messages clutter the transcript and waste the user's attention when they come back to review.
556955
+
556956
+ Read and analyze freely \u2014 understanding the state of things has no blast radius. Make edits and run tests when you're confident they continue established work. Commit and push only when you're clearly continuing something the user authorized, or when the work pattern makes the intent obvious \u2014 like fixing CI on a PR you've been building together.
556957
+ `;
556958
+ AUTONOMOUS_LOOP_PREAMBLE_PERSISTENT = `# Autonomous loop check
556959
+
556960
+ You're being invoked on a timer while the user is away or occupied. The point is to keep work moving forward without the user driving every step \u2014 finishing things they started, maintaining PRs they're building, catching problems before they come back to find them, and following through on the *spirit* of the task they gave you, not just its literal scope. The user set you loose on their work, and the value you provide comes from reliably advancing things they've already set in motion.
556961
+
556962
+ The key tension to navigate: the user trusts you enough to run autonomously, but that trust is easily lost. Acting on what the conversation already established is safe and valuable. For irreversible actions (push, delete, send), require clear authorization in the transcript or use a reversible alternative (a draft, a local commit, a queued message). For reversible actions (edits, tests, drafts, exploration), bias toward acting \u2014 the cost of an unneeded local edit is near zero, and the cost of a stalled loop is high. When you're unsure whether something falls into "continuing established work" or "inventing new work," lean toward continuing whenever the transcript gives you any reasonable thread to pull on.
556963
+
556964
+ ## What to act on
556965
+
556966
+ The current conversation is your highest-signal source \u2014 re-read the transcript above, since everything there is something the user was actively engaged with. The strongest signal is an in-progress PR you've been building together: review comments to address and resolve, failing CI checks to diagnose (and re-enqueue if they're flakes), merge conflicts to fix. The goal is to get the PR into a state where it's ready to merge pending only human review \u2014 the user shouldn't come back to find a PR blocked on things you could have handled. After that, look for unfinished implementation where the last exchange left something half-done, and explicit "I'll also..." or "next I'll..." commitments the conversation made and didn't honor. Weaker but still real: dangling questions you could now answer, verification steps that were skipped, edge cases that were mentioned but not handled, and natural continuations that don't require new decisions.
556967
+
556968
+ If you find anything in this category, act on it \u2014 actually do the work, don't describe what could be done. Run the tests, don't say "you could run the tests." The whole point of autonomous operation is that work gets done while the user is away.
556969
+
556970
+ When the conversation transcript has nothing left, the current branch's pull/merge request on the user's SCM is the next-best place to look. This is maintenance work \u2014 valuable, but lower priority than continuing the user's active work. Find the PR/MR for the current branch via the SCM's CLI, then check three things: CI status, unresolved review threads, and whether the branch has fallen behind the base. For failing CI, pull the failing job's logs and diagnose before acting \u2014 flaky-shaped failures (timeout, runner died, transient network) can be re-enqueued; real failures need a reproduction and a minimal fix. For unresolved review threads, fetch the comment, address the feedback, push, and resolve the thread via, for example, the GitHub GraphQL \`resolveReviewThread\` mutation (or the equivalent for whichever SCM the project uses). Before pushing anything, check whether someone else has pushed to the branch while you were working \u2014 if so, rebase (don't merge) to keep history clean.
556971
+
556972
+ When CI is green, threads are clear, and there's idle time, sweeping the branch for issues is a good use of that time \u2014 bug-hunt or simplification passes catch problems before reviewers do, saving everyone a round-trip.
556973
+
556974
+ If everything is genuinely quiet \u2014 no conversation work, no PR maintenance \u2014 say so in one sentence and keep the loop alive. Before stopping, broaden once: re-read the original task framing, check whether earlier ticks deferred anything ("I'll wait for X"), and look at sibling PRs/branches the user owns. Persistence is the point of autonomous mode. Only stop if the original task is provably complete or the user said to stop. (Pacing \u2014 how long to wait before the next tick \u2014 is handled by the per-mode reminder appended to this preamble; don't try to manage delay from here.)
556975
+
556976
+ ## Repeated invocations
556977
+
556978
+ If you see earlier autonomous checks in this conversation, adjust your scope accordingly. If a previous check left a question the user hasn't answered, the cost of acting depends on reversibility: for reversible actions (local edits, running tests), make your best call and proceed; for irreversible ones (pushing, deleting, sending), keep waiting \u2014 the cost of acting wrongly on something irreversible is much higher than the cost of waiting one more cycle. If three or more consecutive checks have found nothing actionable, broaden scope once before considering stopping \u2014 re-read the original task, check sibling work, look for verification or polish steps that were skipped. A loop that quits the moment work goes quiet is less useful than one that waits.
556979
+
556980
+ Read and analyze freely \u2014 understanding the state of things has no blast radius. Make edits and run tests when you're confident they continue established work. Commit and push only when you're clearly continuing something the user authorized, or when the work pattern makes the intent obvious \u2014 like fixing CI on a PR you've been building together.
556981
+ `;
556982
+ DYNAMIC_APPENDIX = `
556983
+
556984
+ If a Monitor is armed (check TaskList), keep \`delaySeconds\` at 1200\u20131800s \u2014 the Monitor is the wake signal and this is only the fallback heartbeat. If you were woken by a \`<task-notification>\`, handle the event before deciding whether to re-arm. To stop the loop, call ${SCHEDULE_WAKEUP_TOOL_NAME} with \`stop: true\` and TaskStop the monitor (use TaskList to find its task ID if no longer in context).`;
556985
+ }
556986
+ });
556987
+
556865
556988
  // node_modules/@sema-agent/core/dist/core/permission-rules.js
556866
556989
  var init_permission_rules = __esm({
556867
556990
  "node_modules/@sema-agent/core/dist/core/permission-rules.js"() {
@@ -557231,6 +557354,7 @@ var init_dist9 = __esm({
557231
557354
  init_background_shell();
557232
557355
  init_scheduler();
557233
557356
  init_scheduler_tools();
557357
+ init_loop_tick();
557234
557358
  init_tighten_task_spec();
557235
557359
  init_tool_policy();
557236
557360
  init_auto_mode();
@@ -628841,8 +628965,19 @@ __export(liveInitToolFace_exports, {
628841
628965
  ENGINE_SCENARIO_EXTRAS_DEFAULT: () => ENGINE_SCENARIO_EXTRAS_DEFAULT,
628842
628966
  _resetLiveInitToolFaceForTest: () => _resetLiveInitToolFaceForTest,
628843
628967
  probeScenarioTools: () => probeScenarioTools,
628844
- resolveLiveInitToolFace: () => resolveLiveInitToolFace
628968
+ resolveLiveInitToolFace: () => resolveLiveInitToolFace,
628969
+ runnerFaceForEnv: () => runnerFaceForEnv
628845
628970
  });
628971
+ function runnerFaceForEnv(env5) {
628972
+ let face = [...ENGINE_RUNNER_FACE];
628973
+ if (env5.SCHEDULER_ENABLED === "false") {
628974
+ face = face.filter((n2) => !["CronCreate", "CronDelete", "CronList", "ScheduleWakeup"].includes(n2));
628975
+ }
628976
+ if (env5.SELF_ORCHESTRATION_ENABLED === "false") {
628977
+ face = face.filter((n2) => n2 !== "Workflow");
628978
+ }
628979
+ return face;
628980
+ }
628846
628981
  function _resetLiveInitToolFaceForTest() {
628847
628982
  cached4 = null;
628848
628983
  }
@@ -628896,7 +629031,7 @@ async function resolveLiveInitToolFace(argv, env5, opts) {
628896
629031
  ...askUserQuestion ? ["AskUserQuestion"] : [],
628897
629032
  ...webSearch ? ["WebSearch"] : []
628898
629033
  ];
628899
- const key = `${baseUrl}::${scenario2 ?? ""}::${askUserQuestion ? "q" : ""}${webSearch ? "w" : ""}`;
629034
+ const key = `${baseUrl}::${scenario2 ?? ""}::${askUserQuestion ? "q" : ""}${webSearch ? "w" : ""}::${env5.SCHEDULER_ENABLED === "false" ? "ns" : ""}${env5.SELF_ORCHESTRATION_ENABLED === "false" ? "nw" : ""}`;
628900
629035
  if (cached4 && cached4.key === key) return cached4.face;
628901
629036
  let names;
628902
629037
  let source = "wire-vocabulary";
@@ -628916,7 +629051,7 @@ async function resolveLiveInitToolFace(argv, env5, opts) {
628916
629051
  });
628917
629052
  if (probed) {
628918
629053
  const handsBand = probed.toolset === "full-body" ? ENGINE_HANDS_BAND : [];
628919
- names = [.../* @__PURE__ */ new Set([...probed.tools, ...handsBand, ...ENGINE_RUNNER_FACE, ...knobTools])];
629054
+ names = [.../* @__PURE__ */ new Set([...probed.tools, ...handsBand, ...runnerFaceForEnv(env5), ...knobTools])];
628920
629055
  source = "scenario-probe";
628921
629056
  if (env5.SEMA_DEBUG) {
628922
629057
  console.error(
@@ -628924,7 +629059,7 @@ async function resolveLiveInitToolFace(argv, env5, opts) {
628924
629059
  );
628925
629060
  }
628926
629061
  } else {
628927
- names = [...ENGINE_HANDS_BAND, ...ENGINE_SCENARIO_EXTRAS_DEFAULT, ...ENGINE_RUNNER_FACE, ...knobTools];
629062
+ names = [...ENGINE_HANDS_BAND, ...ENGINE_SCENARIO_EXTRAS_DEFAULT, ...runnerFaceForEnv(env5), ...knobTools];
628928
629063
  if (env5.SEMA_DEBUG) {
628929
629064
  console.error(
628930
629065
  `[sema] init tool face: scenario "${scenario2}" detail probe unavailable \u2014 falling back to the default wire vocabulary (may not match this scenario's roster)`
@@ -628932,7 +629067,7 @@ async function resolveLiveInitToolFace(argv, env5, opts) {
628932
629067
  }
628933
629068
  }
628934
629069
  } else {
628935
- names = [...ENGINE_HANDS_BAND, ...ENGINE_SCENARIO_EXTRAS_DEFAULT, ...ENGINE_RUNNER_FACE, ...knobTools];
629070
+ names = [...ENGINE_HANDS_BAND, ...ENGINE_SCENARIO_EXTRAS_DEFAULT, ...runnerFaceForEnv(env5), ...knobTools];
628936
629071
  }
628937
629072
  const face = { tools: names.map((name) => ({ name })), source };
628938
629073
  cached4 = { key, face };
@@ -629393,6 +629528,10 @@ async function* ask(params) {
629393
629528
  if (selfOrchestration && process.env.SEMA_DEBUG) {
629394
629529
  console.error("[sema] print-mode ask: selfOrchestration wire \u2192 TaskRequest.selfOrchestration=true (run_workflow)");
629395
629530
  }
629531
+ const retainBackground = process.env.SEMA_LIVE_BASEURL ? retainBackgroundFromEnv() : void 0;
629532
+ if (retainBackground && process.env.SEMA_DEBUG) {
629533
+ console.error("[sema] print-mode ask: retainBackground wire \u2192 TaskRequest.retainBackgroundProcesses=true");
629534
+ }
629396
629535
  const attachments = process.env.SEMA_LIVE_BASEURL ? attachmentsForRequest() : void 0;
629397
629536
  if (attachments && process.env.SEMA_DEBUG) {
629398
629537
  console.error(`[sema] print-mode ask: attachments wire \u2192 TaskRequest.attachments (${Object.keys(attachments).join("+")})`);
@@ -629584,6 +629723,7 @@ async function* ask(params) {
629584
629723
  ...interactiveToolsField.interactiveTools === false ? { interactiveTools: false } : {},
629585
629724
  ...webSearch || wireHooks ? { settings: { ...webSearch ? { webSearch } : {}, ...wireHooks ? { hooks: wireHooks } : {} } } : {},
629586
629725
  ...selfOrchestration ? { selfOrchestration } : {},
629726
+ ...retainBackground ? { retainBackgroundProcesses: retainBackground } : {},
629587
629727
  ...enableFork !== void 0 ? { enableFork } : {},
629588
629728
  ...attachments ? { attachments } : {},
629589
629729
  ...skillsMcpFields
@@ -629774,6 +629914,7 @@ var init_seamQueryEngine = __esm({
629774
629914
  init_mockClient();
629775
629915
  init_webSearchWireCaps();
629776
629916
  init_selfOrchestrationWireCaps();
629917
+ init_retainBackgroundWireCaps();
629777
629918
  init_forkWireCaps();
629778
629919
  init_attachmentsWireCaps();
629779
629920
  init_hooksWireCaps();
@@ -640435,6 +640576,7 @@ async function ensurePrintModeEngine() {
640435
640576
  async function ensureLocalEngineOneShot(label = "one-shot") {
640436
640577
  seedUserSettingsEnv();
640437
640578
  process.env.SCHEDULER_SESSION_WAKEUP ??= "false";
640579
+ process.env.SCHEDULER_ENABLED ??= "false";
640438
640580
  try {
640439
640581
  const { applyPersistedTlsTrust: applyPersistedTlsTrust2 } = await Promise.resolve().then(() => (init_tlsTrust(), tlsTrust_exports));
640440
640582
  await applyPersistedTlsTrust2();
@@ -653687,7 +653829,7 @@ Auth: unix socket -R \u2192 local proxy`, "info");
653687
653829
  pendingHookMessages
653688
653830
  }, renderAndRun);
653689
653831
  }
653690
- }).version("sema 1.0.13", "-v, --version", "Output the version number");
653832
+ }).version("sema 1.0.15", "-v, --version", "Output the version number");
653691
653833
  program2.option("-w, --worktree [name]", "Create a new git worktree for this session (optionally specify a name)");
653692
653834
  program2.option("--tmux", "Create a tmux session for the worktree (requires --worktree). Uses iTerm2 native panes when available; use --tmux=classic for traditional tmux.");
653693
653835
  if (canUserConfigureAdvisor()) {
@@ -657618,8 +657760,18 @@ function effectiveFirePrompt(fire) {
657618
657760
  substituted: true
657619
657761
  };
657620
657762
  }
657763
+ function makeSentinelResolver() {
657764
+ const ticked = /* @__PURE__ */ new Set();
657765
+ return (prompt, recordId) => {
657766
+ const firstTick = !ticked.has(recordId);
657767
+ const resolved = resolveAutonomousLoopPrompt(prompt, { firstTick });
657768
+ if (resolved !== null) ticked.add(recordId);
657769
+ return resolved ?? prompt;
657770
+ };
657771
+ }
657621
657772
  var init_firePrompt = __esm({
657622
657773
  "build-src/src/sema/scheduler/firePrompt.ts"() {
657774
+ init_dist9();
657623
657775
  }
657624
657776
  });
657625
657777
 
@@ -657858,8 +658010,10 @@ function wireSchedulerSessionLifecycle(opts = {}) {
657858
658010
  };
657859
658011
  }
657860
658012
  function submitViaQueue(onWarn) {
658013
+ const resolveSentinel = makeSentinelResolver();
657861
658014
  return async (fire) => {
657862
- const { prompt, substituted } = effectiveFirePrompt(fire);
658015
+ const { prompt: rawPrompt, substituted } = effectiveFirePrompt(fire);
658016
+ const prompt = resolveSentinel(rawPrompt, fire.recordId);
657863
658017
  if (substituted) {
657864
658018
  onWarn?.(`scheduler: record ${fire.recordId} fired with an EMPTY prompt \u2014 substituted a placeholder (never inject empty user content)`);
657865
658019
  }