thinkpool-pair 0.7.262 → 0.7.264
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/bridge.mjs +19 -12
- package/dispatch-permission-cleanup.mjs +13 -7
- package/package.json +1 -1
package/bridge.mjs
CHANGED
|
@@ -3104,17 +3104,20 @@ function cancelPendingDurablePermissions(s, permissionIds = null, reason = 'perm
|
|
|
3104
3104
|
})
|
|
3105
3105
|
}
|
|
3106
3106
|
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
})
|
|
3107
|
+
let restoredPermissionCleanupPending = false
|
|
3108
|
+
|
|
3109
|
+
async function cancelRestoredDurablePermissions() {
|
|
3110
|
+
restoredPermissionCleanupPending = true
|
|
3111
|
+
const result = await cancelDurablePermissions({
|
|
3112
|
+
supabaseUrl: SUPABASE_URL, anonKey: SUPABASE_ANON, token: codeAuthToken, roomCode: room,
|
|
3113
|
+
// A new process gets a fresh bridge id. Every approval bound to any older
|
|
3114
|
+
// id has lost its process-local resolver, including archived terminals that
|
|
3115
|
+
// no longer have a session file and therefore never enter loadAll().
|
|
3116
|
+
currentBridgeId: BRIDGE_ID, reason: 'bridge_restarted',
|
|
3117
|
+
})
|
|
3118
|
+
if (result.ok) restoredPermissionCleanupPending = false
|
|
3119
|
+
else if (process.env.TP_DEBUG) process.stderr.write(`\n ◇ restored permission cleanup incomplete (${result.code})\n`)
|
|
3120
|
+
return result
|
|
3118
3121
|
}
|
|
3119
3122
|
|
|
3120
3123
|
function drainPending(s) {
|
|
@@ -4059,7 +4062,7 @@ channel
|
|
|
4059
4062
|
// Cancel their durable dispatch rows BEFORE opening a restored SDK
|
|
4060
4063
|
// session, so a new permission raised during resume can never be
|
|
4061
4064
|
// mistaken for a pre-restart card from the same terminal.
|
|
4062
|
-
await cancelRestoredDurablePermissions(
|
|
4065
|
+
await cancelRestoredDurablePermissions()
|
|
4063
4066
|
if (all.length) for (const rec of all) {
|
|
4064
4067
|
const wasInterrupted = restoredTurnOpen(rec.log || [])
|
|
4065
4068
|
// Codex can persist a thread id before its rollout receives the
|
|
@@ -4601,6 +4604,10 @@ if (process.env.THINKPOOL_PAIR_AUTOUPDATE === '1' && VERSION) {
|
|
|
4601
4604
|
if (!ok && codeAuthToken) process.stderr.write('\n ⚠ realtime auth refresh failed; private bridge features may need a reconnect.\n')
|
|
4602
4605
|
})
|
|
4603
4606
|
refreshOwnerPlan()
|
|
4607
|
+
// A restart sweep may have raced an expiring owner JWT. Keep failed
|
|
4608
|
+
// room sweep queued until the supervisor hands us its next fresh token,
|
|
4609
|
+
// then retry without leaving archived-terminal questions behind.
|
|
4610
|
+
if (restoredPermissionCleanupPending) void cancelRestoredDurablePermissions()
|
|
4604
4611
|
}
|
|
4605
4612
|
})
|
|
4606
4613
|
setInterval(() => { try { process.send({ t: 'idle', idle: idleNow(), between: betweenTurns(), webPeer: webPeerPresent() }) } catch { /* parent gone */ } }, 5000).unref()
|
|
@@ -9,14 +9,17 @@ const SETTLED = new Set(['denied', 'idempotent_replay', 'already_resolved', 'sta
|
|
|
9
9
|
|
|
10
10
|
const safeString = (value) => typeof value === 'string' && value.length > 0
|
|
11
11
|
|
|
12
|
-
export function pendingDurablePermissionItems (items, { bridgeId = null, terminalId, permissionIds = null, actionKinds = ['approval', 'dispatch'] } = {}) {
|
|
13
|
-
if ((bridgeId != null && !safeString(bridgeId)) || !safeString(
|
|
12
|
+
export function pendingDurablePermissionItems (items, { bridgeId = null, currentBridgeId = null, terminalId = null, permissionIds = null, actionKinds = ['approval', 'dispatch'] } = {}) {
|
|
13
|
+
if ((bridgeId != null && !safeString(bridgeId)) || (currentBridgeId != null && !safeString(currentBridgeId)) ||
|
|
14
|
+
(terminalId != null && !safeString(terminalId)) || (terminalId == null && currentBridgeId == null)) return []
|
|
14
15
|
const requested = permissionIds == null ? null : new Set(permissionIds.filter(safeString))
|
|
15
16
|
const actions = new Set(actionKinds)
|
|
16
17
|
return (Array.isArray(items) ? items : []).filter((item) => {
|
|
17
18
|
const permissionId = item?.request_context?.permission_id
|
|
18
19
|
return PENDING.has(item?.status) && item?.item_kind === 'approval' && actions.has(item?.action_kind) &&
|
|
19
|
-
(!bridgeId || item?.bridge_authority_id === bridgeId) &&
|
|
20
|
+
(!bridgeId || item?.bridge_authority_id === bridgeId) &&
|
|
21
|
+
(!currentBridgeId || item?.bridge_authority_id !== currentBridgeId) &&
|
|
22
|
+
(!terminalId || item?.local_authority_id === terminalId) &&
|
|
20
23
|
safeString(permissionId) && (!requested || requested.has(permissionId))
|
|
21
24
|
})
|
|
22
25
|
}
|
|
@@ -37,10 +40,13 @@ const rpc = async ({ fetchImpl, supabaseUrl, headers, name, body }) => {
|
|
|
37
40
|
// room-member identity. Never accept an id supplied by a public broadcast.
|
|
38
41
|
export async function cancelDurablePermissions ({
|
|
39
42
|
fetchImpl = globalThis.fetch, supabaseUrl, anonKey, token, roomCode, bridgeId,
|
|
40
|
-
|
|
43
|
+
currentBridgeId = null, terminalId = null, permissionIds = null,
|
|
44
|
+
reason = 'permission_aborted', actionKinds = ['approval', 'dispatch'],
|
|
41
45
|
} = {}) {
|
|
42
46
|
if (typeof fetchImpl !== 'function' || !safeString(supabaseUrl) || !safeString(anonKey) ||
|
|
43
|
-
!safeString(token) || !safeString(roomCode) || (bridgeId != null && !safeString(bridgeId)) ||
|
|
47
|
+
!safeString(token) || !safeString(roomCode) || (bridgeId != null && !safeString(bridgeId)) ||
|
|
48
|
+
(currentBridgeId != null && !safeString(currentBridgeId)) ||
|
|
49
|
+
(terminalId != null && !safeString(terminalId)) || (terminalId == null && currentBridgeId == null)) {
|
|
44
50
|
return Object.freeze({ ok: false, code: 'cleanup_unavailable', canceled: 0 })
|
|
45
51
|
}
|
|
46
52
|
if (permissionIds != null && (!Array.isArray(permissionIds) || !permissionIds.length)) {
|
|
@@ -51,7 +57,7 @@ export async function cancelDurablePermissions ({
|
|
|
51
57
|
const listed = await rpc({ fetchImpl, supabaseUrl, headers, name: 'list_code_pair_controls', body: {
|
|
52
58
|
p_session_code: String(roomCode).toUpperCase(), p_status: null, p_limit: 100,
|
|
53
59
|
} })
|
|
54
|
-
const candidates = pendingDurablePermissionItems(listed, { bridgeId, terminalId, permissionIds, actionKinds })
|
|
60
|
+
const candidates = pendingDurablePermissionItems(listed, { bridgeId, currentBridgeId, terminalId, permissionIds, actionKinds })
|
|
55
61
|
const outcomes = await Promise.all(candidates.map(async (item) => {
|
|
56
62
|
const result = await rpc({ fetchImpl, supabaseUrl, headers, name: 'resolve_code_pair_control', body: {
|
|
57
63
|
p_item_id: item.id,
|
|
@@ -63,7 +69,7 @@ export async function cancelDurablePermissions ({
|
|
|
63
69
|
// by its immutable authority binding, so resolve it with that exact
|
|
64
70
|
// value after the owner-authenticated RLS list—not a guessed current ID.
|
|
65
71
|
p_bridge_authority_id: item.bridge_authority_id,
|
|
66
|
-
p_local_authority_id:
|
|
72
|
+
p_local_authority_id: item.local_authority_id,
|
|
67
73
|
p_idempotency_key: `cancel-permission:${item.id}:${reason}`.slice(0, 160),
|
|
68
74
|
} })
|
|
69
75
|
return { id: item.id, code: result?.code || 'invalid_response', ok: Boolean(result?.ok) && SETTLED.has(result.code) }
|