const vscode = require("vscode"); /* const UCXL_REGEX = /^ucxl:\/\/([a-zA-Z0-9_-]+):([a-zA-Z0-9_-]+)@([a-zA-Z0-9_-]+):([a-zA-Z0-9_-]+)((\/(\^\^|~~))?\/[^\\s]*)?$/; */ // Updated regex to match the new UCXL address format with optional temporal and context path // The regex captures: // - Agent and Role: alphanumeric with underscores and hyphens // - Project and Task: alphanumeric with underscores and hyphens // - Temporal: optional, can be `#`, `~*`, `^*`, `~~`, `^^`, `~`, or `^` // - Context Path: optional, can be any string after the temporal part // The regex ensures that the address starts with `ucxl://` and is followed by the correct structure of components. // The regex is designed to be flexible while ensuring that the components are valid according to the UCXL specification. // The regex also allows for the context path to be any valid string, ensuring it captures the full address correctly. // The regex is designed to be used in a VSCode extension for validating and providing hover information // about UCXL addresses in text documents. //const UCXL_REGEX = /^ucxl:\/\/([a-zA-Z0-9_-]+):([a-zA-Z0-9_-]+)@([a-zA-Z0-9_-]+):([a-zA-Z0-9_-]+)\/(#|~\*|\^\*|~~|\^\^|~\d+|\^\d+)\/(.+)$/; // Agent ID regex for UCXL addresses const UCXL_REGEX = /^ucxl:\/\/([A-Z0-9]{3,5}|any|none):([a-zA-Z0-9_-]+)@([a-zA-Z0-9_-]+):([a-zA-Z0-9_-]+)\/(#|~\*|\^\*|~~|\^\^|~\d+|\^\d+)\/(.+)$/; function activate(context) { const diagnosticCollection = vscode.languages.createDiagnosticCollection("ucxl"); context.subscriptions.push(diagnosticCollection); function updateDiagnostics(doc) { if (!doc) return; const diagnostics = []; const text = doc.getText(); const lines = text.split(/\r?\n/); lines.forEach((line, idx) => { const matches = line.matchAll(/ucxl:\/\/[^\s]+/g); for (const match of matches) { const addr = match[0]; if (!UCXL_REGEX.test(addr)) { diagnostics.push( new vscode.Diagnostic( new vscode.Range(idx, match.index, idx, match.index + addr.length), "Invalid UCXL address syntax", vscode.DiagnosticSeverity.Error ) ); } } }); diagnosticCollection.set(doc.uri, diagnostics); } vscode.workspace.onDidOpenTextDocument(updateDiagnostics); vscode.workspace.onDidChangeTextDocument(e => updateDiagnostics(e.document)); vscode.workspace.onDidCloseTextDocument(doc => diagnosticCollection.delete(doc.uri)); // Hover Provider const hoverProvider = vscode.languages.registerHoverProvider( { scheme: "file", language: "*" }, { provideHover(document, position) { const range = document.getWordRangeAtPosition(position, /ucxl:\/\/[^\s]+/); if (!range) return; const address = document.getText(range); const match = address.match(UCXL_REGEX); if (!match) return new vscode.Hover(`$(error) **Invalid UCXL address**`); const [, agent, role, project, task, , temporal, , context_path] = match; const md = new vscode.MarkdownString(); md.isTrusted = true; md.appendMarkdown(`### UCXL Address Components\n`); md.appendMarkdown(`- **Agent**: \`${agent}\`\n`); md.appendMarkdown(`- **Role**: \`${role}\`\n`); md.appendMarkdown(`- **Project**: \`${project}\`\n`); md.appendMarkdown(`- **Task**: \`${task}\`\n`); if (temporal) md.appendMarkdown(`- **Temporal**: \`${temporal}\`\n`); if (context_path) md.appendMarkdown(`- **Context Path**: \`${context_path}\`\n`); return new vscode.Hover(md, range); } } ); context.subscriptions.push(hoverProvider); } function lintUcxlLine(lineText, lineNum, diagnostics) { const match = lineText.match(UCXL_REGEX); if (!match) return; const fullAddress = match[0]; const agentToken = match[1]; if (!isValidAgent(agentToken)) { const agentStart = lineText.indexOf(agentToken); const agentEnd = agentStart + agentToken.length; diagnostics.push( new vscode.Diagnostic( new vscode.Range(lineNum, agentStart, lineNum, agentEnd), "Invalid agent token (checksum or field error)", vscode.DiagnosticSeverity.Error ) ); } } function updateDiagnostics(doc, diagnosticCollection) { const diagnostics = []; for (let i = 0; i < doc.lineCount; i++) { const lineText = doc.lineAt(i).text; lintUcxlLine(lineText, i, diagnostics); } diagnosticCollection.set(doc.uri, diagnostics); } vscode.languages.registerHoverProvider("ucxl", { provideHover(document, position) { const wordRange = document.getWordRangeAtPosition(position, /[A-Z0-9]{5}/); if (!wordRange) return null; const token = document.getText(wordRange); if (!isValidAgent(token)) return null; const agentId = decodeToken(token); return new vscode.Hover(`**AgentID**: Version=${agentId.version}, Host=${agentId.hostId}, GPU=${agentId.gpuSlot}`); } }); const crypto = require("crypto"); const CROCKFORD_ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; const VERSION_BITS = 3; const HOST_ID_BITS = 10; const GPU_SLOT_BITS = 4; const RESERVED_BITS = 2; const CHECKSUM_BITS = 6; const PREFIX_BITS = VERSION_BITS + HOST_ID_BITS + GPU_SLOT_BITS + RESERVED_BITS; // 19 const TOTAL_BITS = PREFIX_BITS + CHECKSUM_BITS; // 25 const MAX_HOST_ID = (1 << HOST_ID_BITS) - 1; const MAX_GPU_SLOT = (1 << GPU_SLOT_BITS) - 1; // ------------------- // Base32 helpers // ------------------- function intToBase32(n, length) { let chars = Array(length).fill('0'); for (let i = length - 1; i >= 0; i--) { chars[i] = CROCKFORD_ALPHABET[n & 0x1F]; n >>= 5; } return chars.join(''); } function base32ToInt(s) { const charMap = {}; for (let i = 0; i < CROCKFORD_ALPHABET.length; i++) { charMap[CROCKFORD_ALPHABET[i]] = i; } if (s.length !== 5) throw new Error(`token length must be 5, got ${s.length}`); let n = 0; for (let ch of s.toUpperCase()) { if ("ILOU".includes(ch)) throw new Error(`invalid character ${ch}`); const val = charMap[ch]; if (val === undefined) throw new Error(`invalid character ${ch}`); n = (n << 5) | val; } return n; } // ------------------- // SHA256 first bits // ------------------- function sha256FirstBits(value, bits) { const bytes = Buffer.from([(value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF]); const hash = crypto.createHash('sha256').update(bytes).digest(); return hash[0] >> (8 - bits); } // ------------------- // Pack/Unpack // ------------------- function packFields(version, hostId, gpuSlot, reserved) { if (version >= (1 << VERSION_BITS)) throw new Error("version out of range"); if (hostId > MAX_HOST_ID) throw new Error("host_id out of range"); if (gpuSlot > MAX_GPU_SLOT) throw new Error("gpu_slot out of range"); if (reserved >= (1 << RESERVED_BITS)) throw new Error("reserved out of range"); let bits = 0; bits = (bits << VERSION_BITS) | version; bits = (bits << HOST_ID_BITS) | hostId; bits = (bits << GPU_SLOT_BITS) | gpuSlot; bits = (bits << RESERVED_BITS) | reserved; const checksum = sha256FirstBits(bits, CHECKSUM_BITS); bits = (bits << CHECKSUM_BITS) | checksum; if (bits >= (1 << TOTAL_BITS)) throw new Error("packed value exceeds allowed bit length"); return bits; } function unpackFields(packed) { if (packed >= (1 << TOTAL_BITS)) throw new Error("packed value exceeds allowed bit length"); const checksum = packed & ((1 << CHECKSUM_BITS) - 1); const prefix = packed >> CHECKSUM_BITS; let tmp = prefix; const reserved = tmp & ((1 << RESERVED_BITS) - 1); tmp >>= RESERVED_BITS; const gpuSlot = tmp & ((1 << GPU_SLOT_BITS) - 1); tmp >>= GPU_SLOT_BITS; const hostId = tmp & ((1 << HOST_ID_BITS) - 1); tmp >>= HOST_ID_BITS; const version = tmp & ((1 << VERSION_BITS) - 1); const expected = sha256FirstBits(prefix, CHECKSUM_BITS); if (expected !== checksum) throw new Error("checksum mismatch"); return { version, hostId, gpuSlot, reserved, checksum }; } // ------------------- // Encode / Decode Token // ------------------- function encodeToken(version, hostId, gpuSlot, reserved) { const packed = packFields(version, hostId, gpuSlot, reserved); return intToBase32(packed, 5); } function decodeToken(token) { const packed = base32ToInt(token); return unpackFields(packed); } // ------------------- // AgentID validator // ------------------- function isValidAgent(agent) { try { decodeToken(agent); return true; } catch (e) { return false; } } // Example usage: // console.log(isValidAgent(encodeToken(1, 42, 3, 0))); // true // console.log(isValidAgent("ABCDE")); // false if checksum fails function isValidUCXLAddress(address) { return UCXL_REGEX.test(address); } function deactivate() {} module.exports = { activate, deactivate };