@tomflow/chatloop-extension 0.3.47 → 0.3.48
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 +8 -7
- package/bin/cli.mjs +1 -1
- package/bin/native-host.mjs +1 -5
- package/extension/actions/clean.js +4 -10
- package/extension/actions/common.js +24 -1
- package/extension/actions/continue.js +10 -64
- package/extension/actions/dom.js +2 -25
- package/extension/actions/handoff.js +11 -50
- package/extension/actions/text.js +2 -25
- package/extension/background.js +36 -446
- package/extension/loop/core.js +3 -16
- package/extension/loop/resources.js +7 -13
- package/extension/manifest.json +1 -1
- package/extension/popup.html +0 -1
- package/extension/popup.js +1 -1
- package/lib/bridge.mjs +1 -10
- package/lib/session-file.mjs +1 -1
- package/package.json +1 -1
- package/extension/acceptance.js +0 -641
- package/extension/actions/clock.js +0 -29
- package/extension/clock.html +0 -4
- package/lib/mcp-tools.json +0 -26
package/extension/loop/core.js
CHANGED
|
@@ -23,20 +23,7 @@
|
|
|
23
23
|
if (standalone.length !== 1) return "UNKNOWN";
|
|
24
24
|
return match[1];
|
|
25
25
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
hash ^= text.charCodeAt(i);
|
|
30
|
-
hash = Math.imul(hash, 16777619);
|
|
31
|
-
}
|
|
32
|
-
return (hash >>> 0).toString(16) + ":" + text.length;
|
|
33
|
-
}
|
|
34
|
-
function decision(status, wasConfirming = false) {
|
|
35
|
-
if (status === "DONE") return "COMPLETED";
|
|
36
|
-
if (status === "BLOCKED") return "BLOCKED";
|
|
37
|
-
if (status === "CONTINUE") return "CONTINUE";
|
|
38
|
-
if (status === "HANDOFF") return "HANDOFF";
|
|
39
|
-
return wasConfirming ? "BLOCKED" : "CONFIRM";
|
|
40
|
-
}
|
|
41
|
-
globalThis.ChatLoopCoreV1 = Object.freeze({parseStatus, fingerprint, decision});
|
|
26
|
+
// Worker is the sole status consumer; observation fingerprints belong to
|
|
27
|
+
// observer.js and dispatch decisions belong to background.js.
|
|
28
|
+
globalThis.ChatLoopCoreV1 = Object.freeze({parseStatus});
|
|
42
29
|
})();
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
const KEY = 'chat-loop.resources.v1';
|
|
5
5
|
const ACTION_LIMIT = 3; // Includes machine Eye, Start, manual and timed Actions.
|
|
6
6
|
const CHAT_LIMIT = 2; // One owner plus at most one Handoff successor.
|
|
7
|
-
const AUDIT_TTL = 60_000;
|
|
7
|
+
const AUDIT_TTL = 60_000; // Reap obsolete audit pages from installed older Extension versions.
|
|
8
8
|
const READ_TTL = 180_000;
|
|
9
9
|
const TERMINAL_TTL = 30_000;
|
|
10
10
|
const READ_ONLY = new Set(['status','text','dom','screenshot','reload']);
|
|
11
11
|
const ACTIONS = new Set(['start','continue','handoff','status','text','dom',
|
|
12
|
-
'screenshot','reload','clean'
|
|
12
|
+
'screenshot','reload','clean']);
|
|
13
13
|
const validId = s => /^[a-f0-9]{32}$/.test(s || '');
|
|
14
14
|
const validTabId = s => /^[1-9][0-9]*$/.test(s || '');
|
|
15
15
|
const extensionBase = () => chrome.runtime.getURL('');
|
|
@@ -20,15 +20,8 @@
|
|
|
20
20
|
if (u.protocol !== 'chrome-extension:' || !tab.url.startsWith(extensionBase())
|
|
21
21
|
|| u.host !== new URL(extensionBase()).host) return null;
|
|
22
22
|
if (u.pathname === '/popup.html' && u.search === '?audit=1')
|
|
23
|
-
return {kind:'audit'};
|
|
24
|
-
|
|
25
|
-
const name=[...u.searchParams.keys()][0],value=u.searchParams.get(name);
|
|
26
|
-
const noId=new Set(['newTest','cleanup','cleanupVerified','cleanupStaleMachine']);
|
|
27
|
-
const tabId=new Set(['cleanupTestHome','cleanupFailedStartHome','cleanupBlockedE2E']);
|
|
28
|
-
if ((noId.has(name)&&value==='1') || (tabId.has(name)&&validTabId(value)))
|
|
29
|
-
return {kind:'manual',name:'acceptance'};
|
|
30
|
-
}
|
|
31
|
-
const name = u.pathname.match(/^\/(start|continue|handoff|status|text|dom|screenshot|reload|clean|clock)\.html$/)?.[1];
|
|
23
|
+
return {kind:'audit'}; // Legacy page reaping only; old audit UI is not executable.
|
|
24
|
+
const name = u.pathname.match(/^\/([a-z]+)\.html$/)?.[1];
|
|
32
25
|
if (!ACTIONS.has(name)) return null;
|
|
33
26
|
if (u.searchParams.size === 1 && validId(u.searchParams.get('requestId')))
|
|
34
27
|
return {kind:READ_ONLY.has(name) || ['continue','handoff'].includes(name)
|
|
@@ -178,11 +171,12 @@
|
|
|
178
171
|
:{ok:true,actionCount,chatCount};
|
|
179
172
|
}
|
|
180
173
|
async function registerAction(tab) {
|
|
181
|
-
|
|
174
|
+
const info=classify(tab);
|
|
175
|
+
if (!info) return false;
|
|
182
176
|
const state=await load();
|
|
183
177
|
if (!state.entries.some(e=>e.id===tab.id)) {
|
|
184
178
|
state.entries.push({id:tab.id,url:tab.url,kind:'action',
|
|
185
|
-
type:
|
|
179
|
+
type:info.kind,firstSeenAt:Date.now(),owned:true});
|
|
186
180
|
state.created++;
|
|
187
181
|
// Fast actions can finish before the next alarm/reconciliation. Count
|
|
188
182
|
// their observed creation in peak metrics even if no later scan sees them.
|
package/extension/manifest.json
CHANGED
package/extension/popup.html
CHANGED
package/extension/popup.js
CHANGED
|
@@ -55,7 +55,7 @@ async function showActionState() {
|
|
|
55
55
|
const key = "chat-loop.action.run.v1";
|
|
56
56
|
const run = (await chrome.storage.local.get(key))[key];
|
|
57
57
|
const allowed = ["phase", "action", "reason", "tabId", "page", "sourceTabId", "sourcePage",
|
|
58
|
-
"userKey", "assistantKey", "newUserKey", "
|
|
58
|
+
"userKey", "assistantKey", "newUserKey", "startedAt",
|
|
59
59
|
"sessionId", "projectId", "generation",
|
|
60
60
|
"deadlineAt", "handoffPending", "handoffPendingAt", "attemptedNewTabId"];
|
|
61
61
|
const summary = {version: chrome.runtime.getManifest().version};
|
package/lib/bridge.mjs
CHANGED
|
@@ -23,7 +23,7 @@ export function requestDir(id) {
|
|
|
23
23
|
if(typeof id!=='string'||!ID.test(id))throw Object.assign(Error('Invalid request ID'),{code:'INVALID_REQUEST_ID'});
|
|
24
24
|
return join(tempRoot(),id);
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
function validateIntent(action, intent) {
|
|
27
27
|
if(action==='start'){
|
|
28
28
|
if(!intent||typeof intent!=='object'||Array.isArray(intent)
|
|
29
29
|
||Object.keys(intent).some(k=>!['projectId','prompt','confirmed','replaceBlockedSessionId'].includes(k))
|
|
@@ -138,15 +138,6 @@ export function readResult(id) {
|
|
|
138
138
|
privateDir(requestDir(id));
|
|
139
139
|
return JSON.parse(readFileSync(p,'utf8'));
|
|
140
140
|
}
|
|
141
|
-
// Read-only cleanup proof. Only an aged, durably FAILED machine request
|
|
142
|
-
// can qualify; never expose the nonce, original intent, or result contents.
|
|
143
|
-
export function staleFailureConfirmed(id,action) {
|
|
144
|
-
if(!ACTIONS.includes(action))return false;
|
|
145
|
-
const result=readResult(id);
|
|
146
|
-
return !!(result && result.id===id && result.action===action
|
|
147
|
-
&& result.status==='FAILED' && Number.isSafeInteger(result.at)
|
|
148
|
-
&& result.at<=Date.now()-TTL_MS);
|
|
149
|
-
}
|
|
150
141
|
export function cleanup(){
|
|
151
142
|
const root=tempRoot();
|
|
152
143
|
if(!existsSync(root))return;
|
package/lib/session-file.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import {randomUUID} from 'node:crypto';
|
|
|
8
8
|
const ID=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
9
9
|
const CHAT=/^[0-9a-f-]{12,}$/i;
|
|
10
10
|
const PROJECT=/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/;
|
|
11
|
-
const PHASE=new Set(['STARTING','RESERVED','PENDING','WAITING_REPLY','
|
|
11
|
+
const PHASE=new Set(['STARTING','RESERVED','PENDING','WAITING_REPLY','DONE','BLOCKED']);
|
|
12
12
|
const fail=code=>Object.assign(Error(code),{code});
|
|
13
13
|
export const sessionPath=()=>join(homedir(),'.chat-loop','session','active-session.json');
|
|
14
14
|
const ensurePrivate=dir=>{
|