jefrichat-mcp 0.49.52 → 0.49.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +22 -21
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -75,7 +75,7 @@ from gateway.platforms.event import MessageEvent, MessageType, ProcessingOutcome
|
|
|
75
75
|
from gateway.platforms._shared import extra_or_secret, get_scoped_secret
|
|
76
76
|
from gateway.status import acquire_scoped_lock, release_scoped_lock
|
|
77
77
|
|
|
78
|
-
from .native_state import NativeState, read_config
|
|
78
|
+
from .native_state import NativeState, read_config
|
|
79
79
|
from .sdk_client import JefriClient
|
|
80
80
|
from .recovery import HistoryRecovery
|
|
81
81
|
from .delivery import deliver, delivery_code, reply_chunks
|
|
@@ -132,6 +132,7 @@ class JefriAdapter(BasePlatformAdapter):
|
|
|
132
132
|
self.waiter = None
|
|
133
133
|
self.current = None
|
|
134
134
|
self.last_error = None
|
|
135
|
+
self.delivery_error = None
|
|
135
136
|
self.stopping = False
|
|
136
137
|
self.send_index = 0
|
|
137
138
|
self.sent_keys = {}
|
|
@@ -191,6 +192,8 @@ class JefriAdapter(BasePlatformAdapter):
|
|
|
191
192
|
self.observed_revision = read_config(self.connection_file).get("controlRevision")
|
|
192
193
|
self.recovery = HistoryRecovery()
|
|
193
194
|
self.recovery_error = self.connection_error = self.last_error = None
|
|
195
|
+
if self.state.recovered_uncertain:
|
|
196
|
+
self.last_error = "Interrupted native tasks need attention and will not be replayed; the owner's ON/OFF setting is unchanged"
|
|
194
197
|
self.next_recovery = self.next_connection_check = self.retry_work_after = 0
|
|
195
198
|
try:
|
|
196
199
|
self.version = json.loads((Path(self.connection_file).parent / "runtime/package.json").read_text())["version"]
|
|
@@ -510,9 +513,8 @@ class JefriAdapter(BasePlatformAdapter):
|
|
|
510
513
|
return
|
|
511
514
|
self.last_error = detail
|
|
512
515
|
await self._notice(message, "This task needs attention: execution or reply delivery could not be confirmed. I will not repeat its actions automatically.", "attention")
|
|
513
|
-
|
|
514
|
-
if
|
|
515
|
-
private_write(self.connection_file, {**current, "enabled": False})
|
|
516
|
+
# This ID is now fenced against execution. Keep the owner's intent and
|
|
517
|
+
# the rest of the queue intact, including if a notice races an OFF/ON.
|
|
516
518
|
|
|
517
519
|
async def _work(self):
|
|
518
520
|
while not self.stopping:
|
|
@@ -536,6 +538,7 @@ class JefriAdapter(BasePlatformAdapter):
|
|
|
536
538
|
self.started_at = time.monotonic()
|
|
537
539
|
self.next_notice = self.started_at + self.notice_delay
|
|
538
540
|
self.send_index = 0
|
|
541
|
+
self.delivery_error = None
|
|
539
542
|
self.sent_keys = {}
|
|
540
543
|
self.send_receipts = {}
|
|
541
544
|
self.edits = {}
|
|
@@ -563,7 +566,7 @@ class JefriAdapter(BasePlatformAdapter):
|
|
|
563
566
|
self.last_error = None
|
|
564
567
|
self.last_processed = int(time.time() * 1000)
|
|
565
568
|
else:
|
|
566
|
-
await self._uncertain(item, m, "
|
|
569
|
+
await self._uncertain(item, m, self.delivery_error or "Native completion unconfirmed; inspect native session and Jefri history. This task will not be replayed; other eligible messages can continue")
|
|
567
570
|
self._warning("delivery_uncertain")
|
|
568
571
|
except Exception as error:
|
|
569
572
|
if not dispatched:
|
|
@@ -571,7 +574,7 @@ class JefriAdapter(BasePlatformAdapter):
|
|
|
571
574
|
self.retry_work_after = time.monotonic() + 30
|
|
572
575
|
self._warning("preflight_retry")
|
|
573
576
|
continue
|
|
574
|
-
await self._uncertain(item, m, "Native execution
|
|
577
|
+
await self._uncertain(item, m, "Native execution unconfirmed (" + delivery_code(error) + "); inspect session before retrying. This task will not be replayed; other eligible messages can continue")
|
|
575
578
|
self._warning("execution_uncertain")
|
|
576
579
|
finally:
|
|
577
580
|
self._retire_questions()
|
|
@@ -588,6 +591,7 @@ class JefriAdapter(BasePlatformAdapter):
|
|
|
588
591
|
self.waiter.set_result(outcome)
|
|
589
592
|
|
|
590
593
|
async def send(self, chat_id, content, reply_to=None, metadata=None):
|
|
594
|
+
turn_id = None
|
|
591
595
|
try:
|
|
592
596
|
if isinstance(metadata, dict) and metadata.get("expect_edits"):
|
|
593
597
|
# Some Hermes proxy/older paths still construct a stream for
|
|
@@ -618,6 +622,8 @@ class JefriAdapter(BasePlatformAdapter):
|
|
|
618
622
|
return SendResult(success=True, message_id=self.send_receipts[chunk_key])
|
|
619
623
|
except Exception as error:
|
|
620
624
|
self._warning("send_unconfirmed")
|
|
625
|
+
if turn_id and self.current and self.current["id"] == turn_id and native_turn.get() == turn_id:
|
|
626
|
+
self.delivery_error = "Native reply delivery unconfirmed (" + delivery_code(error) + "); inspect Jefri history. This task will not be replayed; other eligible messages can continue"
|
|
621
627
|
# Suppress Hermes's differently formatted fallback after an unknown
|
|
622
628
|
# result: changing the payload/key could duplicate an accepted send.
|
|
623
629
|
return SendResult(success=False, error="Jefri send timed out or is unconfirmed (" + delivery_code(error) + "); inspect history before retrying")
|
|
@@ -757,10 +763,9 @@ class NativeState:
|
|
|
757
763
|
self.db.execute("CREATE TABLE IF NOT EXISTS turns (id TEXT PRIMARY KEY, chat TEXT NOT NULL, message TEXT, state TEXT NOT NULL, at INTEGER NOT NULL, reply TEXT, sent INTEGER NOT NULL DEFAULT 0, detail TEXT)")
|
|
758
764
|
self.db.execute("CREATE TABLE IF NOT EXISTS sibling_admissions (id TEXT PRIMARY KEY, sender TEXT NOT NULL, at INTEGER NOT NULL)")
|
|
759
765
|
self.db.execute("CREATE TABLE IF NOT EXISTS group_rearm (id TEXT PRIMARY KEY, cutoff TEXT NOT NULL)")
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
self.cancel_queued()
|
|
766
|
+
self.recovered_uncertain = self.db.execute("UPDATE turns SET state='uncertain',detail='Gateway stopped mid-turn; inspect native session and Jefri history before retrying' WHERE state IN ('running','sending')").rowcount
|
|
767
|
+
# Fence interrupted IDs, not the whole responder. A restart must not
|
|
768
|
+
# change the owner's saved choice or discard undispatched queued work.
|
|
764
769
|
|
|
765
770
|
@staticmethod
|
|
766
771
|
def _db(file):
|
|
@@ -63539,17 +63544,14 @@ async function startOpenClawBridge(file, timing = { heartbeatMs: 1e3, recoveryMs
|
|
|
63539
63544
|
}
|
|
63540
63545
|
const journal = new NativeJournal(path11.join(path11.dirname(file), "turns.db"));
|
|
63541
63546
|
const questions = new NativeQuestions(path11.join(path11.dirname(file), "questions.db"));
|
|
63542
|
-
if (journal.recoveredUncertain) {
|
|
63543
|
-
setNativeEnabled(file, false);
|
|
63544
|
-
journal.cancelQueued();
|
|
63545
|
-
}
|
|
63546
63547
|
let socket, active, busy = false, stopping = false, closed = false;
|
|
63547
63548
|
let activeGroup;
|
|
63548
63549
|
let activeMessage, activeStartedAt = 0, nextNoticeAt = 0;
|
|
63549
63550
|
let noticeTask;
|
|
63550
63551
|
const notices = /* @__PURE__ */ new Set();
|
|
63551
63552
|
let activeRevision;
|
|
63552
|
-
let turnError =
|
|
63553
|
+
let turnError = journal.recoveredUncertain ? "Interrupted native tasks need attention and will not be replayed; the owner's ON/OFF setting is unchanged." : null;
|
|
63554
|
+
let admissionError = null, recoveryError = null;
|
|
63553
63555
|
let pumpAfter = 0, lastInboundAt = null, lastProcessedAt = null;
|
|
63554
63556
|
let observedRevision = config2.controlRevision;
|
|
63555
63557
|
let recoveryTask, recoveryAbort, nextRecoveryAt = 0;
|
|
@@ -63626,6 +63628,7 @@ async function startOpenClawBridge(file, timing = { heartbeatMs: 1e3, recoveryMs
|
|
|
63626
63628
|
while (enabled() && socket?.online && (next = journal.next())) {
|
|
63627
63629
|
const { id: id5, chat, message } = next;
|
|
63628
63630
|
let dispatched = false;
|
|
63631
|
+
let phase = "execution";
|
|
63629
63632
|
const revision = readNativeConfig(file).controlRevision;
|
|
63630
63633
|
const turnAllowed = async () => revision === readNativeConfig(file).controlRevision && await check2(message) && revision === readNativeConfig(file).controlRevision;
|
|
63631
63634
|
try {
|
|
@@ -63648,8 +63651,10 @@ async function startOpenClawBridge(file, timing = { heartbeatMs: 1e3, recoveryMs
|
|
|
63648
63651
|
const answer = questions.match(message, revision ?? null);
|
|
63649
63652
|
const modelMessage = answer ? { ...message, content: `Answer to your question ${JSON.stringify(answer.pending.question.question)}: ${answer.label}` } : message;
|
|
63650
63653
|
const reply = await runOpenClawTurn(config2, chat, modelMessage, signal);
|
|
63654
|
+
phase = "reply authorization";
|
|
63651
63655
|
if (!await turnAllowed()) throw Error("Native admission or group eligibility changed while working; reply withheld.");
|
|
63652
63656
|
lock2.withOwnership(() => journal.reply(id5, reply));
|
|
63657
|
+
phase = "reply delivery";
|
|
63653
63658
|
for (const [part, text6] of splitNativeReply(reply).entries()) {
|
|
63654
63659
|
if (!await turnAllowed()) throw Error("Reply stopped because eligibility changed.");
|
|
63655
63660
|
const result = await deliverNative(() => send(text6, part), turnAllowed);
|
|
@@ -63669,15 +63674,11 @@ async function startOpenClawBridge(file, timing = { heartbeatMs: 1e3, recoveryMs
|
|
|
63669
63674
|
pumpAfter = Date.now() + timing.recoveryMs;
|
|
63670
63675
|
break;
|
|
63671
63676
|
}
|
|
63672
|
-
turnError = e?.name === "AbortError" || e?.name === "TimeoutError" ? "Native turn interrupted; platform actions may still finish." : `Native
|
|
63677
|
+
turnError = e?.name === "AbortError" || e?.name === "TimeoutError" ? "Native turn interrupted; platform actions may still finish. This task will not be replayed; the owner's ON/OFF setting is unchanged." : `Native ${phase} uncertain (${nativeDeliveryCode(e)}). Inspect gateway session and Jefri history; this task will not be replayed. Other eligible messages can continue.`;
|
|
63673
63678
|
if (lock2.isOwner()) {
|
|
63674
63679
|
lock2.withOwnership(() => journal.mark(id5, "uncertain", turnError));
|
|
63675
63680
|
if (readNativeConfig(file).controlRevision === revision) {
|
|
63676
63681
|
await notice(message, "This task needs attention: execution or reply delivery could not be confirmed. I will not repeat its actions automatically.", "attention");
|
|
63677
|
-
if (lock2.isOwner() && readNativeConfig(file).controlRevision === revision) {
|
|
63678
|
-
setNativeEnabled(file, false);
|
|
63679
|
-
journal.cancelQueued();
|
|
63680
|
-
}
|
|
63681
63682
|
} else turnError = null;
|
|
63682
63683
|
}
|
|
63683
63684
|
} finally {
|
|
@@ -63790,7 +63791,7 @@ async function startOpenClawBridge(file, timing = { heartbeatMs: 1e3, recoveryMs
|
|
|
63790
63791
|
await Promise.allSettled([...admissions]);
|
|
63791
63792
|
while (busy) await new Promise((resolve) => setTimeout(resolve, 50));
|
|
63792
63793
|
await Promise.allSettled([...notices]);
|
|
63793
|
-
if (lock2.isOwner()) journal.cancelQueued();
|
|
63794
|
+
if (lock2.isOwner() && !readNativeConfig(file).enabled) journal.cancelQueued();
|
|
63794
63795
|
lock2.release();
|
|
63795
63796
|
status();
|
|
63796
63797
|
closed = true;
|