| |
| |
| |
|
|
|
|
| class NotebookContextBuilder {
|
| constructor() {
|
| this.maxContextTokens = 4000;
|
| this.maxCellSummary = 500;
|
| }
|
|
|
| |
| |
|
|
| extractCells() {
|
| const cellElements = document.querySelectorAll('.notebook-cell');
|
| const cells = [];
|
|
|
| cellElements.forEach((elem, index) => {
|
| const source = elem.querySelector('.cell-editor textarea')?.value || '';
|
| const output = elem.querySelector('.cell-output')?.textContent || '';
|
| const cellType = elem.dataset.cellType || 'code';
|
|
|
| cells.push({
|
| index,
|
| type: cellType,
|
| source: source.substring(0, this.maxCellSummary),
|
| output: output.substring(0, this.maxCellSummary),
|
| hash: this.hashCell(source, output),
|
| });
|
| });
|
|
|
| return cells;
|
| }
|
|
|
| |
| |
|
|
| hashCell(source, output) {
|
| const combined = source + output;
|
| let hash = 0;
|
| for (let i = 0; i < combined.length; i++) {
|
| const char = combined.charCodeAt(i);
|
| hash = ((hash << 5) - hash) + char;
|
| hash = hash & hash;
|
| }
|
| return Math.abs(hash).toString(16);
|
| }
|
|
|
| |
| |
|
|
| buildNotebookSummary() {
|
| const notebookTitle = document.querySelector('#notebook-header h1')?.textContent || 'Untitled';
|
| const cells = this.extractCells();
|
|
|
| const summary = {
|
| title: notebookTitle,
|
| cellCount: cells.length,
|
| cells: cells,
|
| recentCells: cells.slice(-3),
|
| };
|
|
|
| return summary;
|
| }
|
|
|
| |
| |
|
|
| buildContextPacket(userQuestion, selectedCellIndex = null) {
|
| const summary = this.buildNotebookSummary();
|
| let tokenCount = 0;
|
|
|
| const packet = {
|
| question: userQuestion,
|
| notebookTitle: summary.title,
|
| cells: [],
|
| recentOutputs: [],
|
| trustPolicies: this.extractTrustPolicies(),
|
| receiptSummary: this.extractReceiptMetadata(),
|
| };
|
|
|
|
|
| for (const cell of summary.cells) {
|
| if (selectedCellIndex !== null && Math.abs(cell.index - selectedCellIndex) > 5) {
|
| continue;
|
| }
|
|
|
| const cellText = `Cell ${cell.index} (${cell.type}):\n${cell.source}\nOutput: ${cell.output}\n`;
|
| tokenCount += cellText.length / 4;
|
|
|
| if (tokenCount < this.maxContextTokens) {
|
| packet.cells.push({
|
| index: cell.index,
|
| type: cell.type,
|
| source: cell.source,
|
| output: cell.output,
|
| });
|
| }
|
| }
|
|
|
|
|
| for (const cell of summary.recentCells) {
|
| if (cell.output) {
|
| packet.recentOutputs.push({
|
| cellIndex: cell.index,
|
| output: cell.output.substring(0, 200),
|
| });
|
| }
|
| }
|
|
|
| return packet;
|
| }
|
|
|
| |
| |
|
|
| extractTrustPolicies() {
|
| const trustElements = document.querySelectorAll('#trust-list div');
|
| const policies = [];
|
|
|
| trustElements.forEach(elem => {
|
| const text = elem.textContent.trim();
|
| if (text) {
|
| policies.push(text);
|
| }
|
| });
|
|
|
| return policies;
|
| }
|
|
|
| |
| |
|
|
| extractReceiptMetadata() {
|
| const receiptElements = document.querySelectorAll('.cell-receipt');
|
| const metadata = {
|
| totalReceipts: receiptElements.length,
|
| recentReceipts: [],
|
| };
|
|
|
|
|
| Array.from(receiptElements)
|
| .slice(-3)
|
| .forEach(elem => {
|
| const hash = elem.dataset.hash || 'unknown';
|
| const status = elem.dataset.status || 'sealed';
|
| metadata.recentReceipts.push({ hash: hash.substring(0, 16), status });
|
| });
|
|
|
| return metadata;
|
| }
|
|
|
| |
| |
|
|
| formatSystemPrompt(packet) {
|
| let prompt = `You are the assistant for the Isomorphic WORM Notebook.\n\n`;
|
| prompt += `Notebook: "${packet.notebookTitle}"\n`;
|
| prompt += `Total cells: ${packet.cells.length}\n`;
|
| prompt += `Recent receipts: ${packet.receiptSummary.totalReceipts}\n\n`;
|
|
|
| if (packet.cells.length > 0) {
|
| prompt += `Visible cells:\n`;
|
| for (const cell of packet.cells) {
|
| prompt += `\n[Cell ${cell.index}] ${cell.type}\n`;
|
| prompt += `Source: ${cell.source.substring(0, 100)}...\n`;
|
| if (cell.output) {
|
| prompt += `Output: ${cell.output.substring(0, 100)}...\n`;
|
| }
|
| }
|
| }
|
|
|
| if (packet.trustPolicies.length > 0) {
|
| prompt += `\nTrust policies:\n`;
|
| for (const policy of packet.trustPolicies) {
|
| prompt += `- ${policy}\n`;
|
| }
|
| }
|
|
|
| prompt += `\nUser question: ${packet.question}\n`;
|
| prompt += `Please answer concisely, cite cell references (Cell N), and preserve Unicode exactly.`;
|
|
|
| return prompt;
|
| }
|
|
|
| |
| |
|
|
| estimateTokens(text) {
|
|
|
| return Math.ceil(text.length / 4);
|
| }
|
|
|
| |
| |
|
|
| validateContextSize(packet) {
|
| const systemPrompt = this.formatSystemPrompt(packet);
|
| const tokenCount = this.estimateTokens(systemPrompt);
|
|
|
| return {
|
| valid: tokenCount < this.maxContextTokens,
|
| tokenCount: tokenCount,
|
| maxTokens: this.maxContextTokens,
|
| warning: tokenCount > this.maxContextTokens * 0.8 ? 'Context approaching limit' : null,
|
| };
|
| }
|
|
|
| |
| |
|
|
| getCellByIndex(index) {
|
| const cells = this.extractCells();
|
| return cells.find(c => c.index === index);
|
| }
|
|
|
| |
| |
|
|
| buildDependencyGraph() {
|
| const cells = this.extractCells();
|
| const graph = {};
|
|
|
| cells.forEach(cell => {
|
| graph[cell.index] = [];
|
|
|
|
|
| const cellReferences = cell.source.match(/Cell\s*(\d+)/gi) || [];
|
| for (const ref of cellReferences) {
|
| const refIndex = parseInt(ref.match(/\d+/)[0]);
|
| if (refIndex !== cell.index && cells.some(c => c.index === refIndex)) {
|
| graph[cell.index].push(refIndex);
|
| }
|
| }
|
| });
|
|
|
| return graph;
|
| }
|
|
|
| |
| |
|
|
| exportContext(userQuestion, selectedCellIndex = null) {
|
| const packet = this.buildContextPacket(userQuestion, selectedCellIndex);
|
| const systemPrompt = this.formatSystemPrompt(packet);
|
| const validation = this.validateContextSize(packet);
|
|
|
| return {
|
| timestamp: new Date().toISOString(),
|
| notebook: packet.notebookTitle,
|
| question: userQuestion,
|
| systemPrompt: systemPrompt,
|
| contextPacket: packet,
|
| validation: validation,
|
| dependencyGraph: this.buildDependencyGraph(),
|
| };
|
| }
|
| }
|
|
|
|
|
| if (typeof module !== 'undefined' && module.exports) {
|
| module.exports = NotebookContextBuilder;
|
| }
|
|
|