| 62 | const TWENTY_FOUR_HOURS_MS = 1440 * 60 * 1e3; |
| 63 | const ONE_HOUR_MS = 3600 * 1e3; |
| 64 | function registerDiagnosticsFunction(sdk, kv) { |
| 65 | sdk.registerFunction("mem::diagnose", async (data) => { |
| 66 | const categories = data.categories && data.categories.length > 0 ? data.categories.filter((c) => ALL_CATEGORIES.includes(c)) : ALL_CATEGORIES; |
| 67 | const checks = []; |
| 68 | const now = Date.now(); |
| 69 | if (categories.includes("actions")) { |
| 70 | const actions = await kv.list(KV.actions); |
| 71 | const allEdges = await kv.list(KV.actionEdges); |
| 72 | const leases = await kv.list(KV.leases); |
| 73 | const actionMap = new Map(actions.map((a) => [a.id, a])); |
| 74 | for (const action of actions) { |
| 75 | if (action.status === "active") { |
| 76 | if (!leases.some((l) => l.actionId === action.id && l.status === "active" && new Date(l.expiresAt).getTime() > now)) checks.push({ |
| 77 | name: `active-no-lease:${action.id}`, |
| 78 | category: "actions", |
| 79 | status: "warn", |
| 80 | message: `Action "${action.title}" is active but has no active lease`, |
| 81 | fixable: false |
| 82 | }); |
| 83 | } |
| 84 | if (action.status === "blocked") { |
| 85 | const deps = allEdges.filter((e) => e.sourceActionId === action.id && e.type === "requires"); |
| 86 | if (deps.length > 0) { |
| 87 | if (deps.every((d) => { |
| 88 | const target = actionMap.get(d.targetActionId); |
| 89 | return target && target.status === "done"; |
| 90 | })) checks.push({ |
| 91 | name: `blocked-deps-done:${action.id}`, |
| 92 | category: "actions", |
| 93 | status: "fail", |
| 94 | message: `Action "${action.title}" is blocked but all dependencies are done`, |
| 95 | fixable: true |
| 96 | }); |
| 97 | } |
| 98 | } |
| 99 | if (action.status === "pending") { |
| 100 | const deps = allEdges.filter((e) => e.sourceActionId === action.id && e.type === "requires"); |
| 101 | if (deps.length > 0) { |
| 102 | if (deps.some((d) => { |
| 103 | const target = actionMap.get(d.targetActionId); |
| 104 | return !target || target.status !== "done"; |
| 105 | })) checks.push({ |
| 106 | name: `pending-unsatisfied-deps:${action.id}`, |
| 107 | category: "actions", |
| 108 | status: "fail", |
| 109 | message: `Action "${action.title}" is pending but has unsatisfied dependencies`, |
| 110 | fixable: true |
| 111 | }); |
| 112 | } |
| 113 | } |
| 114 | } |
| 115 | if (!checks.some((c) => c.category === "actions" && c.status !== "pass")) checks.push({ |
| 116 | name: "actions-ok", |
| 117 | category: "actions", |
| 118 | status: "pass", |
| 119 | message: `All ${actions.length} actions are consistent`, |
| 120 | fixable: false |
| 121 | }); |