@tomflow/chatloop-extension 0.3.34 → 0.3.36
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/README.md +1 -1
- package/extension/acceptance.js +2 -1
- package/extension/background.js +27 -5
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ Run: chatloop-extension extension <command> --help
|
|
|
30
30
|
{"projectId":"new-distinct-test","prompt":"新的独立任务","confirmed":true,"replaceBlockedSessionId":"11111111-1111-4111-a111-111111111111"}
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
`PAGE_ERROR` 且无原生 Retry 时,Worker 仅观察最多四次;只有原用户消息 ID
|
|
33
|
+
`PAGE_ERROR` 且无原生 Retry 时,Worker 仅观察最多四次;只有原用户消息 ID 未变且连续两次证明错误消失才继续观察;若回查时已经识别到同一 Turn 的可靠完成事件或已发出完成事件,则让已排队的完成回执自然结项,避免 4 秒回查窗口错误覆盖 DONE。其余情况依旧 BLOCKED。正式 `popup.html?audit=1` Audit 包含最近一条真实已核验故障的 `run.lastFaultProof`(不含正文);若仅有页面告警而无此字段,不能宣称 Worker 实际进入了恢复分支。**不能把当前网页错误消失当成原消息未发送的证据**。
|
|
34
34
|
|
|
35
35
|
Clean 只清空输入框,不发送,不更改 Owner;必须指定精确标签页 ID 且验证目标。Chrome 已安装扩展不随 npm 包自动安装。本地源码与已发布 npm 版本可能不同,真实结果请看 [CURRENT](../../context/CURRENT.md)。
|
|
36
36
|
|
package/extension/acceptance.js
CHANGED
|
@@ -55,7 +55,8 @@
|
|
|
55
55
|
"clockAdvancedAt", "clockAdvanceMinutes", "clockRunningProof",
|
|
56
56
|
"wallTestHours", "wallHoldAt", "wallReleaseAt", "wallReleasedAt",
|
|
57
57
|
"wallTestMinutes", "wallAdjustedAt", "wallOriginalReleaseAt",
|
|
58
|
-
"countMode", "countValue", "countHandoffs", "countLastSendAt", "countReleaseAt"
|
|
58
|
+
"countMode", "countValue", "countHandoffs", "countLastSendAt", "countReleaseAt",
|
|
59
|
+
"lastFaultProof"]
|
|
59
60
|
.filter(k => run[k] !== undefined).map(k => [k, run[k]])) : null
|
|
60
61
|
};
|
|
61
62
|
if (run?.parseEvidence && owned.some(item => item.id === run.tabId || item.id === run.sourceTabId)) {
|
package/extension/background.js
CHANGED
|
@@ -319,6 +319,14 @@ async function onTurnBlocked(message, sender) {
|
|
|
319
319
|
const second = await verify();
|
|
320
320
|
if (!second || first.retryAvailable !== second.retryAvailable)
|
|
321
321
|
return {status: "IGNORED", code: "ERROR_NOT_STABLE"};
|
|
322
|
+
// A bounded non-content receipt lets the existing Audit prove which
|
|
323
|
+
// decision the ONLY Worker actually took. Never log raw Chat text.
|
|
324
|
+
const faultProof = code => ({code, reason, userKey: message.userKey, at: Date.now()});
|
|
325
|
+
const recordFault = async code => {
|
|
326
|
+
await saveRun({...previous, lastFaultProof: faultProof(code)});
|
|
327
|
+
return {status: "IGNORED", code};
|
|
328
|
+
};
|
|
329
|
+
await saveRun({...previous, lastFaultProof: faultProof("ERROR_CONFIRMED")});
|
|
322
330
|
// A network interruption may show a transient page error without a native
|
|
323
331
|
// Retry button. Only *observe* for a bounded grace period: never resend an
|
|
324
332
|
// uncertain user Turn. Resume normal observation only if the EXACT submitted
|
|
@@ -333,9 +341,17 @@ async function onTurnBlocked(message, sender) {
|
|
|
333
341
|
const sameTurn = proof?.contract === "chat-loop.turn-observer.v1"
|
|
334
342
|
&& proof.page === previous.page
|
|
335
343
|
&& proof.userKey === previous.newUserKey;
|
|
336
|
-
|
|
344
|
+
// The Observer may emit its verified completion while this error handler
|
|
345
|
+
// owns the serialized Worker queue. That completion message is waiting
|
|
346
|
+
// behind us: fail-closing here would discard a real, finished Turn.
|
|
347
|
+
// TURN_FINISHED or the exact already-emitted state is sufficient proof
|
|
348
|
+
// that the SAME user Turn recovered; do not invent/send another message.
|
|
349
|
+
if (sameTurn && (proof.status === "TURN_FINISHED"
|
|
350
|
+
|| proof.status === "IDLE" && proof.reason === "TURN_ALREADY_EMITTED"))
|
|
351
|
+
return recordFault("PAGE_ERROR_COMPLETION_QUEUED");
|
|
352
|
+
if (sameTurn && ["RUNNING", "UNKNOWN"].includes(proof.status)) {
|
|
337
353
|
recovered++;
|
|
338
|
-
if (recovered === 2) return
|
|
354
|
+
if (recovered === 2) return recordFault("PAGE_ERROR_CLEARED_SAME_TURN");
|
|
339
355
|
} else recovered = 0;
|
|
340
356
|
}
|
|
341
357
|
}
|
|
@@ -343,6 +359,7 @@ async function onTurnBlocked(message, sender) {
|
|
|
343
359
|
// or evade site access controls. Keep the original Chat and user Turn intact.
|
|
344
360
|
if (reason === "SECURITY_CHALLENGE") {
|
|
345
361
|
await saveRun({...previous, phase:"BLOCKED", reason,
|
|
362
|
+
lastFaultProof:faultProof("SECURITY_CHALLENGE"),
|
|
346
363
|
errorUserKey:message.userKey, errorAt:Date.now()});
|
|
347
364
|
return {status:"BLOCKED", code:reason};
|
|
348
365
|
}
|
|
@@ -354,7 +371,8 @@ async function onTurnBlocked(message, sender) {
|
|
|
354
371
|
const retry = reason === "PAGE_ERROR";
|
|
355
372
|
const token = crypto.randomUUID();
|
|
356
373
|
let next = {...previous, phase: "RESERVED", action: retry ? "CONTINUE" : "HANDOFF",
|
|
357
|
-
token, reason: "",
|
|
374
|
+
token, reason: "", lastFaultProof:faultProof(retry ? "NATIVE_RETRY_ONCE" : "CONTEXT_HANDOFF"),
|
|
375
|
+
...(retry ? {retryMode: "NATIVE", retryCount: 1}
|
|
358
376
|
: {handoffMode: "ERROR", errorReason: reason})};
|
|
359
377
|
if (previous.modelIntent) {
|
|
360
378
|
next=updateModelReceipt(next,previous.modelIntent.id,{
|
|
@@ -372,9 +390,12 @@ async function onTurnBlocked(message, sender) {
|
|
|
372
390
|
return {status: "BLOCKED", code: "ERROR_ACTION_CREATE_FAILED"};
|
|
373
391
|
}
|
|
374
392
|
}
|
|
375
|
-
|
|
393
|
+
const finalReason = reason === "PAGE_ERROR"
|
|
376
394
|
? previous.retryCount === 1 ? "PAGE_ERROR_AFTER_RETRY" : "PAGE_ERROR_NO_RETRY_BUTTON"
|
|
377
|
-
: "CONTEXT_HANDOFF_REPEATED"
|
|
395
|
+
: "CONTEXT_HANDOFF_REPEATED";
|
|
396
|
+
await saveRun({...previous, phase:"BLOCKED", reason:finalReason,
|
|
397
|
+
lastFaultProof:faultProof(finalReason),
|
|
398
|
+
errorUserKey:message.userKey, errorAt:Date.now()});
|
|
378
399
|
return {status: "BLOCKED", code: reason === "PAGE_ERROR" ? "PAGE_ERROR" : "CONTEXT_HANDOFF_REPEATED"};
|
|
379
400
|
}
|
|
380
401
|
// A model-driven request can only be queued against a currently RUNNING,
|
|
@@ -563,6 +584,7 @@ async function onTurnFinished(message, sender) {
|
|
|
563
584
|
tabId, page: message.page, userKey: message.userKey,
|
|
564
585
|
assistantKey: message.assistantKey, phase: "RESERVED", token,
|
|
565
586
|
action: nextAction, startedAt, modelReceipts:receiptHistory(previous),
|
|
587
|
+
...(previous?.lastFaultProof ? {lastFaultProof:previous.lastFaultProof} : {}),
|
|
566
588
|
...(countMode ? {countMode:true, countValue:verifiedCount,
|
|
567
589
|
countLastSendAt:previous.countLastSendAt,
|
|
568
590
|
countHandoffs:previous.countHandoffs || 0} : {}),
|
package/extension/manifest.json
CHANGED