@sema-agent/cli 1.0.12 → 1.0.14
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/npm-shrinkwrap.json +8 -8
- package/package.json +4 -4
- package/sema.js +232 -31
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.14",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@sema-agent/cli",
|
|
9
|
-
"version": "1.0.
|
|
9
|
+
"version": "1.0.14",
|
|
10
10
|
"license": "BUSL-1.1",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@sema-agent/sdk": "0.0.53",
|
|
@@ -629,9 +629,9 @@
|
|
|
629
629
|
}
|
|
630
630
|
},
|
|
631
631
|
"node_modules/@sema-agent/core": {
|
|
632
|
-
"version": "1.
|
|
633
|
-
"resolved": "https://registry.npmjs.org/@sema-agent/core/-/core-1.
|
|
634
|
-
"integrity": "sha512-
|
|
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.
|
|
649
|
-
"resolved": "https://registry.npmjs.org/@sema-agent/registry-core/-/registry-core-0.10.
|
|
650
|
-
"integrity": "sha512-
|
|
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==",
|
|
651
651
|
"license": "BUSL-1.1",
|
|
652
652
|
"dependencies": {
|
|
653
653
|
"zod": "^3.24.0"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.14",
|
|
4
4
|
"description": "Sema — your own Claude Code-grade coding agent, in the terminal.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"bin": {
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
"@sema-agent/server": "1.218.0"
|
|
26
26
|
},
|
|
27
27
|
"overrides": {
|
|
28
|
-
"@sema-agent/core": "1.
|
|
29
|
-
"@sema-agent/registry-core": "0.10.
|
|
28
|
+
"@sema-agent/core": "1.312.0",
|
|
29
|
+
"@sema-agent/registry-core": "0.10.14"
|
|
30
30
|
},
|
|
31
|
-
"semaEngineVersion": "server 1.218.0 / core 1.
|
|
31
|
+
"semaEngineVersion": "server 1.218.0 / core 1.312.0 / registry-core 0.10.14",
|
|
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
|
}
|
|
@@ -359913,6 +359923,15 @@ function genericResultText(r, opts) {
|
|
|
359913
359923
|
return typeof bb?.text === "string" ? bb.text : bb?.type === "image" ? "[image]" : "";
|
|
359914
359924
|
}).filter(Boolean).join("\n");
|
|
359915
359925
|
}
|
|
359926
|
+
if (s.trimStart().startsWith("[")) {
|
|
359927
|
+
try {
|
|
359928
|
+
const arr = JSON.parse(s);
|
|
359929
|
+
if (Array.isArray(arr) && arr.length > 0 && arr.every((b3) => b3 && typeof b3 === "object" && (b3.type === "text" || b3.type === "image"))) {
|
|
359930
|
+
s = arr.map((bb) => typeof bb.text === "string" ? bb.text : bb.type === "image" ? "[image]" : "").filter(Boolean).join("\n");
|
|
359931
|
+
}
|
|
359932
|
+
} catch {
|
|
359933
|
+
}
|
|
359934
|
+
}
|
|
359916
359935
|
s = s.trimEnd();
|
|
359917
359936
|
if (!s) return "";
|
|
359918
359937
|
if (opts?.full) return s;
|
|
@@ -359967,7 +359986,7 @@ function UserToolSuccessMessage({
|
|
|
359967
359986
|
if (report) {
|
|
359968
359987
|
renderedMessage = /* @__PURE__ */ (0, import_jsx_runtime118.jsx)(SubagentReportSummary, { report, isTranscriptMode });
|
|
359969
359988
|
} else {
|
|
359970
|
-
const fallbackText = genericResultText(message.toolUseResult);
|
|
359989
|
+
const fallbackText = genericResultText(message.toolUseResult, { full: Boolean(isTranscriptMode) || verbose });
|
|
359971
359990
|
if (fallbackText) renderedMessage = /* @__PURE__ */ (0, import_jsx_runtime118.jsx)(MessageResponse, { children: /* @__PURE__ */ (0, import_jsx_runtime118.jsx)(ThemedText, { children: fallbackText }) });
|
|
359972
359991
|
}
|
|
359973
359992
|
}
|
|
@@ -406129,7 +406148,10 @@ var init_lifetimeMarkers = __esm({
|
|
|
406129
406148
|
|
|
406130
406149
|
// build-src/src/tools/ScheduleCronTool/UI.tsx
|
|
406131
406150
|
function renderCreateToolUseMessage(input) {
|
|
406132
|
-
|
|
406151
|
+
const s = input.schedule;
|
|
406152
|
+
const sched = input.cron ?? (s?.kind === "cron" && typeof s.expr === "string" ? s.expr : s?.kind === "delay" && typeof s.delaySec === "number" ? `in ${s.delaySec}s` : s?.kind === "at" ? `at ${typeof s.at === "string" ? s.at : "\u2026"}` : void 0);
|
|
406153
|
+
const label = input.label ? ` [${input.label}]` : "";
|
|
406154
|
+
return `${sched ?? ""}${label}${input.prompt ? `: ${truncate(input.prompt, 60, true)}` : ""}`;
|
|
406133
406155
|
}
|
|
406134
406156
|
function renderCreateResultMessage(output) {
|
|
406135
406157
|
return /* @__PURE__ */ (0, import_jsx_runtime154.jsx)(MessageResponse, { children: /* @__PURE__ */ (0, import_jsx_runtime154.jsxs)(ThemedText, { children: [
|
|
@@ -406203,10 +406225,17 @@ var init_CronCreateTool = __esm({
|
|
|
406203
406225
|
init_UI24();
|
|
406204
406226
|
MAX_JOBS = 50;
|
|
406205
406227
|
inputSchema35 = lazySchema(
|
|
406206
|
-
() => external_exports.
|
|
406207
|
-
cron: external_exports.string().describe(
|
|
406228
|
+
() => external_exports.object({
|
|
406229
|
+
cron: external_exports.string().optional().describe(
|
|
406208
406230
|
'Standard 5-field cron expression in local time: "M H DoM Mon DoW" (e.g. "*/5 * * * *" = every 5 minutes, "30 14 28 2 *" = Feb 28 at 2:30pm local once).'
|
|
406209
406231
|
),
|
|
406232
|
+
schedule: external_exports.object({
|
|
406233
|
+
kind: external_exports.string().optional(),
|
|
406234
|
+
expr: external_exports.string().optional(),
|
|
406235
|
+
delaySec: external_exports.number().optional(),
|
|
406236
|
+
at: external_exports.string().optional()
|
|
406237
|
+
}).optional().describe("Engine schedule form (kinds cron/at/delay) \u2014 accepted by the live engine tool face."),
|
|
406238
|
+
label: external_exports.string().optional(),
|
|
406210
406239
|
prompt: external_exports.string().describe("The prompt to enqueue at each fire time."),
|
|
406211
406240
|
recurring: semanticBoolean(external_exports.boolean().optional()).describe(
|
|
406212
406241
|
`true (default) = fire on every cron match until deleted or auto-expired after ${DEFAULT_MAX_AGE_DAYS} days. false = fire once at the next match, then auto-delete. Use false for "remind me at X" one-shot requests with pinned minute/hour/dom/month.`
|
|
@@ -406251,6 +406280,13 @@ var init_CronCreateTool = __esm({
|
|
|
406251
406280
|
return getCronFilePath();
|
|
406252
406281
|
},
|
|
406253
406282
|
async validateInput(input) {
|
|
406283
|
+
if (typeof input.cron !== "string" || input.cron.length === 0) {
|
|
406284
|
+
return {
|
|
406285
|
+
result: false,
|
|
406286
|
+
message: "This local CronCreate accepts the 5-field `cron` string form. The `schedule` object form is handled by the live engine tool \u2014 pass `cron` here.",
|
|
406287
|
+
errorCode: 5
|
|
406288
|
+
};
|
|
406289
|
+
}
|
|
406254
406290
|
if (!parseCronExpression(input.cron)) {
|
|
406255
406291
|
return {
|
|
406256
406292
|
result: false,
|
|
@@ -426077,7 +426113,7 @@ var init_BashTool = __esm({
|
|
|
426077
426113
|
init_toolName();
|
|
426078
426114
|
init_UI();
|
|
426079
426115
|
init_utils6();
|
|
426080
|
-
import_jsx_runtime163 = __toESM(require_jsx_runtime());
|
|
426116
|
+
import_jsx_runtime163 = __toESM(require_jsx_runtime(), 1);
|
|
426081
426117
|
EOL5 = "\n";
|
|
426082
426118
|
PROGRESS_THRESHOLD_MS3 = 2e3;
|
|
426083
426119
|
ASSISTANT_BLOCKING_BUDGET_MS2 = 15e3;
|
|
@@ -479492,8 +479528,8 @@ var init_sema_brand = __esm({
|
|
|
479492
479528
|
"Slash-command gating wired to the fleet capability handshake"
|
|
479493
479529
|
]
|
|
479494
479530
|
},
|
|
479495
|
-
productVersion: "1.0.
|
|
479496
|
-
announcement: "sema 1.0.
|
|
479531
|
+
productVersion: "1.0.14",
|
|
479532
|
+
announcement: "sema 1.0.14 \u2014 CJK input method candidate windows now anchor to the input caret (the terminal cursor is shown while typing), and autonomous-loop wakeups resolve to real tick instructions instead of a literal sentinel"
|
|
479497
479533
|
};
|
|
479498
479534
|
}
|
|
479499
479535
|
});
|
|
@@ -482427,8 +482463,8 @@ var require_sema_brand = __commonJS({
|
|
|
482427
482463
|
"Slash-command gating wired to the fleet capability handshake"
|
|
482428
482464
|
]
|
|
482429
482465
|
},
|
|
482430
|
-
productVersion: "1.0.
|
|
482431
|
-
announcement: "sema 1.0.
|
|
482466
|
+
productVersion: "1.0.14",
|
|
482467
|
+
announcement: "sema 1.0.14 \u2014 CJK input method candidate windows now anchor to the input caret (the terminal cursor is shown while typing), and autonomous-loop wakeups resolve to real tick instructions instead of a literal sentinel"
|
|
482432
482468
|
};
|
|
482433
482469
|
}
|
|
482434
482470
|
});
|
|
@@ -496964,6 +497000,7 @@ var init_types19 = __esm({
|
|
|
496964
497000
|
maxTokens: external_exports3.number().int().positive().optional(),
|
|
496965
497001
|
autoCompactTokens: external_exports3.number().int().positive().optional(),
|
|
496966
497002
|
charsPerToken: external_exports3.number().finite().positive().optional(),
|
|
497003
|
+
promptGuidance: external_exports3.array(external_exports3.string()).optional(),
|
|
496967
497004
|
cost: ModelCost.optional(),
|
|
496968
497005
|
extraBody: external_exports3.unknown().optional().transform((v2, ctx) => {
|
|
496969
497006
|
if (v2 === void 0)
|
|
@@ -556835,6 +556872,98 @@ var init_fs_write_gate_policy = __esm({
|
|
|
556835
556872
|
}
|
|
556836
556873
|
});
|
|
556837
556874
|
|
|
556875
|
+
// node_modules/@sema-agent/core/dist/tools/loop-tick.js
|
|
556876
|
+
function pushNotificationAppendix(persistentVariant) {
|
|
556877
|
+
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";
|
|
556878
|
+
return `
|
|
556879
|
+
|
|
556880
|
+
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.`;
|
|
556881
|
+
}
|
|
556882
|
+
function cronTick(push) {
|
|
556883
|
+
return `# Autonomous loop tick
|
|
556884
|
+
|
|
556885
|
+
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}`;
|
|
556886
|
+
}
|
|
556887
|
+
function dynamicTick(push) {
|
|
556888
|
+
return `# Autonomous loop tick (dynamic pacing)
|
|
556889
|
+
|
|
556890
|
+
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.
|
|
556891
|
+
|
|
556892
|
+
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}`;
|
|
556893
|
+
}
|
|
556894
|
+
function resolveAutonomousLoopPrompt(prompt, opts) {
|
|
556895
|
+
if (prompt !== AUTONOMOUS_LOOP_SENTINEL && prompt !== AUTONOMOUS_LOOP_DYNAMIC_SENTINEL)
|
|
556896
|
+
return null;
|
|
556897
|
+
const persistent = opts?.persistentVariant === true;
|
|
556898
|
+
const push = opts?.pushNotification === true ? pushNotificationAppendix(persistent) : "";
|
|
556899
|
+
const tick = prompt === AUTONOMOUS_LOOP_DYNAMIC_SENTINEL ? dynamicTick(push) : cronTick(push);
|
|
556900
|
+
if (opts?.firstTick !== true)
|
|
556901
|
+
return tick;
|
|
556902
|
+
const preamble = persistent ? AUTONOMOUS_LOOP_PREAMBLE_PERSISTENT : AUTONOMOUS_LOOP_PREAMBLE;
|
|
556903
|
+
return `${preamble}
|
|
556904
|
+
|
|
556905
|
+
---
|
|
556906
|
+
|
|
556907
|
+
${tick}`;
|
|
556908
|
+
}
|
|
556909
|
+
var AUTONOMOUS_LOOP_PREAMBLE, AUTONOMOUS_LOOP_PREAMBLE_PERSISTENT, DYNAMIC_APPENDIX;
|
|
556910
|
+
var init_loop_tick = __esm({
|
|
556911
|
+
"node_modules/@sema-agent/core/dist/tools/loop-tick.js"() {
|
|
556912
|
+
init_scheduler_tools();
|
|
556913
|
+
AUTONOMOUS_LOOP_PREAMBLE = `# Autonomous loop check
|
|
556914
|
+
|
|
556915
|
+
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.
|
|
556916
|
+
|
|
556917
|
+
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.
|
|
556918
|
+
|
|
556919
|
+
## What to act on
|
|
556920
|
+
|
|
556921
|
+
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.
|
|
556922
|
+
|
|
556923
|
+
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.
|
|
556924
|
+
|
|
556925
|
+
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.
|
|
556926
|
+
|
|
556927
|
+
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.
|
|
556928
|
+
|
|
556929
|
+
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.
|
|
556930
|
+
|
|
556931
|
+
## Repeated invocations
|
|
556932
|
+
|
|
556933
|
+
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.
|
|
556934
|
+
|
|
556935
|
+
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.
|
|
556936
|
+
`;
|
|
556937
|
+
AUTONOMOUS_LOOP_PREAMBLE_PERSISTENT = `# Autonomous loop check
|
|
556938
|
+
|
|
556939
|
+
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.
|
|
556940
|
+
|
|
556941
|
+
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.
|
|
556942
|
+
|
|
556943
|
+
## What to act on
|
|
556944
|
+
|
|
556945
|
+
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.
|
|
556946
|
+
|
|
556947
|
+
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.
|
|
556948
|
+
|
|
556949
|
+
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.
|
|
556950
|
+
|
|
556951
|
+
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.
|
|
556952
|
+
|
|
556953
|
+
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.)
|
|
556954
|
+
|
|
556955
|
+
## Repeated invocations
|
|
556956
|
+
|
|
556957
|
+
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.
|
|
556958
|
+
|
|
556959
|
+
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.
|
|
556960
|
+
`;
|
|
556961
|
+
DYNAMIC_APPENDIX = `
|
|
556962
|
+
|
|
556963
|
+
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).`;
|
|
556964
|
+
}
|
|
556965
|
+
});
|
|
556966
|
+
|
|
556838
556967
|
// node_modules/@sema-agent/core/dist/core/permission-rules.js
|
|
556839
556968
|
var init_permission_rules = __esm({
|
|
556840
556969
|
"node_modules/@sema-agent/core/dist/core/permission-rules.js"() {
|
|
@@ -557204,6 +557333,7 @@ var init_dist9 = __esm({
|
|
|
557204
557333
|
init_background_shell();
|
|
557205
557334
|
init_scheduler();
|
|
557206
557335
|
init_scheduler_tools();
|
|
557336
|
+
init_loop_tick();
|
|
557207
557337
|
init_tighten_task_spec();
|
|
557208
557338
|
init_tool_policy();
|
|
557209
557339
|
init_auto_mode();
|
|
@@ -640407,6 +640537,7 @@ async function ensurePrintModeEngine() {
|
|
|
640407
640537
|
}
|
|
640408
640538
|
async function ensureLocalEngineOneShot(label = "one-shot") {
|
|
640409
640539
|
seedUserSettingsEnv();
|
|
640540
|
+
process.env.SCHEDULER_SESSION_WAKEUP ??= "false";
|
|
640410
640541
|
try {
|
|
640411
640542
|
const { applyPersistedTlsTrust: applyPersistedTlsTrust2 } = await Promise.resolve().then(() => (init_tlsTrust(), tlsTrust_exports));
|
|
640412
640543
|
await applyPersistedTlsTrust2();
|
|
@@ -653659,7 +653790,7 @@ Auth: unix socket -R \u2192 local proxy`, "info");
|
|
|
653659
653790
|
pendingHookMessages
|
|
653660
653791
|
}, renderAndRun);
|
|
653661
653792
|
}
|
|
653662
|
-
}).version("sema 1.0.
|
|
653793
|
+
}).version("sema 1.0.14", "-v, --version", "Output the version number");
|
|
653663
653794
|
program2.option("-w, --worktree [name]", "Create a new git worktree for this session (optionally specify a name)");
|
|
653664
653795
|
program2.option("--tmux", "Create a tmux session for the worktree (requires --worktree). Uses iTerm2 native panes when available; use --tmux=classic for traditional tmux.");
|
|
653665
653796
|
if (canUserConfigureAdvisor()) {
|
|
@@ -657423,19 +657554,29 @@ var init_schedulerDaemon = __esm({
|
|
|
657423
657554
|
/** Run one tick: fire due intents, persist the rescheduled/reaped record set. Returns the count fired. */
|
|
657424
657555
|
async tickOnce() {
|
|
657425
657556
|
const nowMs = (this.opts.now ?? (() => Date.now()))();
|
|
657426
|
-
let
|
|
657427
|
-
|
|
657428
|
-
|
|
657429
|
-
|
|
657430
|
-
this.opts.
|
|
657431
|
-
|
|
657432
|
-
|
|
657433
|
-
|
|
657434
|
-
|
|
657435
|
-
|
|
657436
|
-
const { toFire, updated } = tickRecords(owned, nowMs);
|
|
657557
|
+
let toFire = [];
|
|
657558
|
+
const computeNext = (all4) => {
|
|
657559
|
+
const owned = [];
|
|
657560
|
+
const foreign = [];
|
|
657561
|
+
for (const r of all4) (this.opts.owns?.(r) ?? true ? owned : foreign).push(r);
|
|
657562
|
+
const t2 = tickRecords(owned, nowMs);
|
|
657563
|
+
toFire = t2.toFire;
|
|
657564
|
+
if (t2.toFire.length === 0 && t2.updated.length === owned.length && t2.updated.every((r, i) => r === owned[i])) return null;
|
|
657565
|
+
return [...t2.updated, ...foreign];
|
|
657566
|
+
};
|
|
657437
657567
|
try {
|
|
657438
|
-
this.store.
|
|
657568
|
+
if (this.store.mutate) {
|
|
657569
|
+
const m2 = this.store.mutate(computeNext);
|
|
657570
|
+
if (!m2.ok && m2.reason !== "aborted") {
|
|
657571
|
+
this.opts.onError?.(new Error(`scheduler store mutate failed: ${m2.reason}`));
|
|
657572
|
+
return 0;
|
|
657573
|
+
}
|
|
657574
|
+
if (!m2.ok) return 0;
|
|
657575
|
+
} else {
|
|
657576
|
+
const next = computeNext(this.store.load());
|
|
657577
|
+
if (next === null) return 0;
|
|
657578
|
+
this.store.save(next);
|
|
657579
|
+
}
|
|
657439
657580
|
} catch (e) {
|
|
657440
657581
|
this.opts.onError?.(e);
|
|
657441
657582
|
return 0;
|
|
@@ -657529,6 +657670,30 @@ function saveSchedulerStore(path27, records) {
|
|
|
657529
657670
|
writeFileSync21(tmp, serializeSchedulerStore(records), { mode: 384 });
|
|
657530
657671
|
renameSync10(tmp, path27);
|
|
657531
657672
|
}
|
|
657673
|
+
function mutateSchedulerStore(path27, fn2, opts) {
|
|
657674
|
+
const maxRetries = Math.max(0, opts?.retries ?? 5);
|
|
657675
|
+
const rawOf = () => {
|
|
657676
|
+
try {
|
|
657677
|
+
return readFileSync51(path27, "utf-8");
|
|
657678
|
+
} catch {
|
|
657679
|
+
return null;
|
|
657680
|
+
}
|
|
657681
|
+
};
|
|
657682
|
+
for (let attempt = 0; ; attempt++) {
|
|
657683
|
+
const baseRaw = rawOf();
|
|
657684
|
+
const records = baseRaw === null ? [] : parseSchedulerStore(baseRaw);
|
|
657685
|
+
const next = fn2(records);
|
|
657686
|
+
if (next === null)
|
|
657687
|
+
return { ok: false, reason: "aborted" };
|
|
657688
|
+
if (rawOf() !== baseRaw) {
|
|
657689
|
+
if (attempt >= maxRetries)
|
|
657690
|
+
return { ok: false, reason: "conflict_exhausted", retries: attempt };
|
|
657691
|
+
continue;
|
|
657692
|
+
}
|
|
657693
|
+
saveSchedulerStore(path27, next);
|
|
657694
|
+
return { ok: true, records: next, retries: attempt };
|
|
657695
|
+
}
|
|
657696
|
+
}
|
|
657532
657697
|
var init_scheduler_store_node = __esm({
|
|
657533
657698
|
"node_modules/@sema-agent/registry-core/dist/scheduler-store-node.js"() {
|
|
657534
657699
|
init_scheduler_store();
|
|
@@ -657556,8 +657721,18 @@ function effectiveFirePrompt(fire) {
|
|
|
657556
657721
|
substituted: true
|
|
657557
657722
|
};
|
|
657558
657723
|
}
|
|
657724
|
+
function makeSentinelResolver() {
|
|
657725
|
+
const ticked = /* @__PURE__ */ new Set();
|
|
657726
|
+
return (prompt, recordId) => {
|
|
657727
|
+
const firstTick = !ticked.has(recordId);
|
|
657728
|
+
const resolved = resolveAutonomousLoopPrompt(prompt, { firstTick });
|
|
657729
|
+
if (resolved !== null) ticked.add(recordId);
|
|
657730
|
+
return resolved ?? prompt;
|
|
657731
|
+
};
|
|
657732
|
+
}
|
|
657559
657733
|
var init_firePrompt = __esm({
|
|
657560
657734
|
"build-src/src/sema/scheduler/firePrompt.ts"() {
|
|
657735
|
+
init_dist9();
|
|
657561
657736
|
}
|
|
657562
657737
|
});
|
|
657563
657738
|
|
|
@@ -657570,7 +657745,9 @@ function schedulerStorePath() {
|
|
|
657570
657745
|
function fileSchedulerStoreIO(storePath) {
|
|
657571
657746
|
return {
|
|
657572
657747
|
load: () => loadSchedulerStore(storePath),
|
|
657573
|
-
save: (records) => saveSchedulerStore(storePath, records)
|
|
657748
|
+
save: (records) => saveSchedulerStore(storePath, records),
|
|
657749
|
+
// [1003] 唯一跨进程变更原语绑定:daemon tick/sessionReap/孤儿清扫的写路径全走它。
|
|
657750
|
+
mutate: (fn2) => mutateSchedulerStore(storePath, fn2)
|
|
657574
657751
|
};
|
|
657575
657752
|
}
|
|
657576
657753
|
var init_schedulerDaemonWire = __esm({
|
|
@@ -657599,6 +657776,15 @@ function partitionSessionReap(records, departedSessionIds) {
|
|
|
657599
657776
|
}
|
|
657600
657777
|
function reapSessionRecords(io, departedSessionIds) {
|
|
657601
657778
|
if (departedSessionIds.length === 0) return [];
|
|
657779
|
+
if (io.mutate) {
|
|
657780
|
+
let ids = [];
|
|
657781
|
+
const m2 = io.mutate((records) => {
|
|
657782
|
+
const { kept: kept2, reaped: reaped2 } = partitionSessionReap(records, departedSessionIds);
|
|
657783
|
+
ids = reaped2.map((r) => r.id);
|
|
657784
|
+
return reaped2.length === 0 ? null : kept2;
|
|
657785
|
+
});
|
|
657786
|
+
return m2.ok || !m2.ok && m2.reason === "aborted" ? ids : [];
|
|
657787
|
+
}
|
|
657602
657788
|
const { kept, reaped } = partitionSessionReap(io.load(), departedSessionIds);
|
|
657603
657789
|
if (reaped.length === 0) return [];
|
|
657604
657790
|
io.save(kept);
|
|
@@ -657677,13 +657863,26 @@ function sweepOrphanSessionRecords(io, storePath, opts = {}) {
|
|
|
657677
657863
|
cleanupDead: true
|
|
657678
657864
|
});
|
|
657679
657865
|
for (const id of opts.ownSessionIds ?? []) if (id) claimed.add(id);
|
|
657680
|
-
const
|
|
657681
|
-
|
|
657682
|
-
|
|
657683
|
-
|
|
657684
|
-
|
|
657685
|
-
|
|
657686
|
-
|
|
657866
|
+
const partition3 = (all4) => {
|
|
657867
|
+
const kept2 = [];
|
|
657868
|
+
const reapedIds2 = [];
|
|
657869
|
+
for (const r of all4) {
|
|
657870
|
+
const orphan = isSessionLifetimeRecord(r) && typeof r.sessionId === "string" && r.sessionId.length > 0 && !claimed.has(r.sessionId) && nowMs - r.createdMs > graceMs;
|
|
657871
|
+
if (orphan) reapedIds2.push(r.id);
|
|
657872
|
+
else kept2.push(r);
|
|
657873
|
+
}
|
|
657874
|
+
return { kept: kept2, reapedIds: reapedIds2 };
|
|
657875
|
+
};
|
|
657876
|
+
if (io.mutate) {
|
|
657877
|
+
let ids = [];
|
|
657878
|
+
const m2 = io.mutate((all4) => {
|
|
657879
|
+
const t2 = partition3(all4);
|
|
657880
|
+
ids = t2.reapedIds;
|
|
657881
|
+
return t2.reapedIds.length === 0 ? null : t2.kept;
|
|
657882
|
+
});
|
|
657883
|
+
return m2.ok || !m2.ok && m2.reason === "aborted" ? ids : [];
|
|
657884
|
+
}
|
|
657885
|
+
const { kept, reapedIds } = partition3(records);
|
|
657687
657886
|
if (reapedIds.length > 0) io.save(kept);
|
|
657688
657887
|
return reapedIds;
|
|
657689
657888
|
}
|
|
@@ -657772,8 +657971,10 @@ function wireSchedulerSessionLifecycle(opts = {}) {
|
|
|
657772
657971
|
};
|
|
657773
657972
|
}
|
|
657774
657973
|
function submitViaQueue(onWarn) {
|
|
657974
|
+
const resolveSentinel = makeSentinelResolver();
|
|
657775
657975
|
return async (fire) => {
|
|
657776
|
-
const { prompt, substituted } = effectiveFirePrompt(fire);
|
|
657976
|
+
const { prompt: rawPrompt, substituted } = effectiveFirePrompt(fire);
|
|
657977
|
+
const prompt = resolveSentinel(rawPrompt, fire.recordId);
|
|
657777
657978
|
if (substituted) {
|
|
657778
657979
|
onWarn?.(`scheduler: record ${fire.recordId} fired with an EMPTY prompt \u2014 substituted a placeholder (never inject empty user content)`);
|
|
657779
657980
|
}
|