@sema-agent/cli 1.0.13 → 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 +5 -5
- package/package.json +3 -3
- package/sema.js +123 -8
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",
|
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.
|
|
28
|
+
"@sema-agent/core": "1.312.0",
|
|
29
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
|
}
|
|
@@ -479518,8 +479528,8 @@ var init_sema_brand = __esm({
|
|
|
479518
479528
|
"Slash-command gating wired to the fleet capability handshake"
|
|
479519
479529
|
]
|
|
479520
479530
|
},
|
|
479521
|
-
productVersion: "1.0.
|
|
479522
|
-
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"
|
|
479523
479533
|
};
|
|
479524
479534
|
}
|
|
479525
479535
|
});
|
|
@@ -482453,8 +482463,8 @@ var require_sema_brand = __commonJS({
|
|
|
482453
482463
|
"Slash-command gating wired to the fleet capability handshake"
|
|
482454
482464
|
]
|
|
482455
482465
|
},
|
|
482456
|
-
productVersion: "1.0.
|
|
482457
|
-
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"
|
|
482458
482468
|
};
|
|
482459
482469
|
}
|
|
482460
482470
|
});
|
|
@@ -556862,6 +556872,98 @@ var init_fs_write_gate_policy = __esm({
|
|
|
556862
556872
|
}
|
|
556863
556873
|
});
|
|
556864
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
|
+
|
|
556865
556967
|
// node_modules/@sema-agent/core/dist/core/permission-rules.js
|
|
556866
556968
|
var init_permission_rules = __esm({
|
|
556867
556969
|
"node_modules/@sema-agent/core/dist/core/permission-rules.js"() {
|
|
@@ -557231,6 +557333,7 @@ var init_dist9 = __esm({
|
|
|
557231
557333
|
init_background_shell();
|
|
557232
557334
|
init_scheduler();
|
|
557233
557335
|
init_scheduler_tools();
|
|
557336
|
+
init_loop_tick();
|
|
557234
557337
|
init_tighten_task_spec();
|
|
557235
557338
|
init_tool_policy();
|
|
557236
557339
|
init_auto_mode();
|
|
@@ -607564,8 +607667,8 @@ function _temp161() {
|
|
|
607564
607667
|
var import_compiler_runtime316, import_react313, SETTINGS_ERRORS_NOTIFICATION_KEY;
|
|
607565
607668
|
var init_useSettingsErrors = __esm({
|
|
607566
607669
|
"build-src/src/hooks/notifs/useSettingsErrors.tsx"() {
|
|
607567
|
-
import_compiler_runtime316 = __toESM(require_compiler_runtime()
|
|
607568
|
-
import_react313 = __toESM(require_react()
|
|
607670
|
+
import_compiler_runtime316 = __toESM(require_compiler_runtime());
|
|
607671
|
+
import_react313 = __toESM(require_react());
|
|
607569
607672
|
init_notifications();
|
|
607570
607673
|
init_state();
|
|
607571
607674
|
init_allErrors();
|
|
@@ -653687,7 +653790,7 @@ Auth: unix socket -R \u2192 local proxy`, "info");
|
|
|
653687
653790
|
pendingHookMessages
|
|
653688
653791
|
}, renderAndRun);
|
|
653689
653792
|
}
|
|
653690
|
-
}).version("sema 1.0.
|
|
653793
|
+
}).version("sema 1.0.14", "-v, --version", "Output the version number");
|
|
653691
653794
|
program2.option("-w, --worktree [name]", "Create a new git worktree for this session (optionally specify a name)");
|
|
653692
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.");
|
|
653693
653796
|
if (canUserConfigureAdvisor()) {
|
|
@@ -657618,8 +657721,18 @@ function effectiveFirePrompt(fire) {
|
|
|
657618
657721
|
substituted: true
|
|
657619
657722
|
};
|
|
657620
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
|
+
}
|
|
657621
657733
|
var init_firePrompt = __esm({
|
|
657622
657734
|
"build-src/src/sema/scheduler/firePrompt.ts"() {
|
|
657735
|
+
init_dist9();
|
|
657623
657736
|
}
|
|
657624
657737
|
});
|
|
657625
657738
|
|
|
@@ -657858,8 +657971,10 @@ function wireSchedulerSessionLifecycle(opts = {}) {
|
|
|
657858
657971
|
};
|
|
657859
657972
|
}
|
|
657860
657973
|
function submitViaQueue(onWarn) {
|
|
657974
|
+
const resolveSentinel = makeSentinelResolver();
|
|
657861
657975
|
return async (fire) => {
|
|
657862
|
-
const { prompt, substituted } = effectiveFirePrompt(fire);
|
|
657976
|
+
const { prompt: rawPrompt, substituted } = effectiveFirePrompt(fire);
|
|
657977
|
+
const prompt = resolveSentinel(rawPrompt, fire.recordId);
|
|
657863
657978
|
if (substituted) {
|
|
657864
657979
|
onWarn?.(`scheduler: record ${fire.recordId} fired with an EMPTY prompt \u2014 substituted a placeholder (never inject empty user content)`);
|
|
657865
657980
|
}
|