-
Notifications
You must be signed in to change notification settings - Fork 0
Claude/access emma intelligence 01 prp ffh cxeq hduz pxs3abij #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
1fb267f
6cb7a9c
77ef465
4aeb054
10afe85
db06f18
13584cd
4e679ec
062a2aa
6cd8157
e67ba82
c5d959b
3aaa600
78be834
cf93568
829b9b6
33bc4ab
cddc125
7b7e91f
04a82b2
93df1fa
2888ed5
1ce5808
5e9b4d7
6943151
781fc1b
10a5bf1
7dc0d50
24a2ef4
be25f56
912e662
76f095e
6c3545a
040029d
60d4774
8385c07
5e41541
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| { | ||
| "git.ignoreLimitWarning": true | ||
| "git.ignoreLimitWarning": true, | ||
| "chatgpt.openOnStartup": true | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,7 +4,11 @@ | |
| <meta charset="UTF-8"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| <title>Add Person - Emma</title> | ||
| <link rel="stylesheet" href="css/theme-tokens.css"> | ||
| <link rel="stylesheet" href="css/main.css"> | ||
| <script src="themes/theme-catalog.js"></script> | ||
| <script src="js/theme-manager.js"></script> | ||
| <script nonce="c3RhdGljLWlubGluZQ==" src="js/sanitize.js"></script> | ||
| <style> | ||
| .add-person-container { | ||
| max-width: 600px; | ||
|
|
@@ -149,7 +153,7 @@ <h1>Add New Person</h1> | |
| </div> | ||
|
|
||
| <div class="success-message" id="successMessage"> | ||
| Person added successfully! <a href="people.html">View all people</a> | ||
| Person added successfully! <a href="pages/people-emma.html">View all people</a> | ||
| </div> | ||
|
|
||
| <form id="addPersonForm" onsubmit="addPerson(event)"> | ||
|
|
@@ -194,10 +198,23 @@ <h1>Add New Person</h1> | |
| </div> | ||
| </div> | ||
|
|
||
| <script type="module"> | ||
| <script nonce="c3RhdGljLWlubGluZQ==" type="module"> | ||
| // Import identity crypto functions | ||
| import { generateIdentity, generateIdentityCard } from './js/vault/identity-crypto.js'; | ||
|
|
||
| const sanitize = (value) => { | ||
| const fallback = (input) => String(input ?? '') | ||
| .replace(/&/g, '&') | ||
| .replace(/</g, '<') | ||
| .replace(/>/g, '>') | ||
| .replace(/"/g, '"') | ||
| .replace(/'/g, '''); | ||
| if (typeof window !== 'undefined' && typeof window.escapeHtml === 'function') { | ||
| return window.escapeHtml(value ?? ''); | ||
| } | ||
| return fallback(value); | ||
| }; | ||
|
|
||
| // Add person functionality | ||
| async function addPerson(event) { | ||
| event.preventDefault(); | ||
|
|
@@ -253,17 +270,23 @@ <h1>Add New Person</h1> | |
|
|
||
| // Show success message with identity info | ||
| const successMessage = document.getElementById('successMessage'); | ||
| successMessage.innerHTML = ` | ||
| <p><strong>✅ ${newPerson.name} added successfully!</strong></p> | ||
| ${newPerson.relationship === 'self' ? ` | ||
| <div style="margin-top: 10px; padding: 10px; background: rgba(134, 88, 255, 0.1); border-radius: 8px;"> | ||
| <p style="margin: 0 0 5px 0;"><strong>🔐 Cryptographic Identity Created</strong></p> | ||
| <p style="margin: 0; font-size: 12px;">Fingerprint: <code>${identity.fingerprint.substring(0, 20)}...</code></p> | ||
| <button onclick="window.open('people.html', '_self')" style="margin-top: 10px; padding: 5px 10px; background: #8658ff; color: white; border: none; border-radius: 4px; cursor: pointer;"> | ||
| const safeName = sanitize(newPerson.name); | ||
| const rawFingerprint = identity?.fingerprint || ''; | ||
| const truncatedFingerprint = rawFingerprint ? `${sanitize(rawFingerprint.substring(0, 20))}${rawFingerprint.length > 20 ? '...' : ''}` : 'N/A'; | ||
| const identityDetails = newPerson.relationship === 'self' | ||
| ? ` | ||
| <div style="margin-top: 10px; padding: 10px; background: rgba(111, 99, 217, 0.1); border-radius: 8px;"> | ||
| <p style="margin: 0 0 5px 0;"><strong>Cryptographic Identity Created</strong></p> | ||
| <p style="margin: 0; font-size: 12px;">Fingerprint: <code>${truncatedFingerprint}</code></p> | ||
| <button onclick="window.open('pages/people-emma.html', '_self')" style="margin-top: 10px; padding: 5px 10px; background: #6f63d9; color: white; border: none; border-radius: 4px; cursor: pointer;"> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: Inline onclick handler bypasses CSP nonce protectionThe dynamically generated button uses an inline |
||
| View in People List | ||
| </button> | ||
| </div> | ||
| ` : ''} | ||
| ` | ||
| : ''; | ||
| successMessage.innerHTML = ` | ||
| <p><strong>${safeName} added successfully!</strong></p> | ||
| ${identityDetails} | ||
| `; | ||
| successMessage.style.display = 'block'; | ||
| document.getElementById('addPersonForm').reset(); | ||
|
|
@@ -324,7 +347,7 @@ <h1>Add New Person</h1> | |
| if (window.history.length > 1) { | ||
| window.history.back(); | ||
| } else { | ||
| window.location.href = 'people.html'; | ||
| window.location.href = 'pages/people-emma.html'; | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -334,4 +357,4 @@ <h1>Add New Person</h1> | |
| }); | ||
| </script> | ||
| </body> | ||
| </html> | ||
| </html> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| 'use strict'; | ||
|
|
||
| const VaultService = require('../lib/vault-service'); | ||
| const { TOOL_DEFINITIONS } = require('../lib/tool-definitions'); | ||
|
|
||
| const APP_NAME = 'Emma Memory Companion'; | ||
| const APP_DESCRIPTION = 'A gentle memory companion that can recall, capture, and enrich family stories with full vault access.'; | ||
| const APP_DEFAULT_MODEL = 'gpt-4.1-mini'; | ||
| const APP_INSTRUCTIONS = [ | ||
| "You are Emma, a gentle memory companion designed to support families navigating dementia.", | ||
| "Introduce yourself as Emma and explain that you protect the family's private memory vault.", | ||
| "Use validation therapy techniques: acknowledge feelings, reflect emotions, and avoid correcting memories.", | ||
| "When helpful, call the available vault tools to look up people, recall memories, or save new stories with photos.", | ||
| "Be transparent about any tool calls and summarize the outcome for the user.", | ||
| "Keep every interaction private and never fabricate vault data." | ||
| ].join(' '); | ||
|
|
||
| let appsSdk = null; | ||
| try { | ||
| // Lazy load the Apps SDK if it is installed in the host environment. | ||
| appsSdk = require('@openai/applications'); | ||
| } catch (error) { | ||
| appsSdk = null; | ||
| } | ||
|
|
||
| function cloneParameters(parameters) { | ||
| return parameters ? JSON.parse(JSON.stringify(parameters)) : undefined; | ||
| } | ||
|
|
||
| function buildToolExecutor(vaultService, schema) { | ||
| return async (args = {}) => { | ||
| return vaultService.execute(schema.name, args); | ||
| }; | ||
| } | ||
|
|
||
| function buildConfig(vaultService, options = {}) { | ||
| const model = options.model || APP_DEFAULT_MODEL; | ||
|
|
||
| return { | ||
| name: APP_NAME, | ||
| description: APP_DESCRIPTION, | ||
| instructions: options.instructions || APP_INSTRUCTIONS, | ||
| model, | ||
| tools: TOOL_DEFINITIONS.map((schema) => ({ | ||
| name: schema.name, | ||
| description: schema.description, | ||
| parameters: cloneParameters(schema.parameters), | ||
| execute: buildToolExecutor(vaultService, schema) | ||
| })) | ||
| }; | ||
| } | ||
|
|
||
| function createEmmaApp(options = {}) { | ||
| const vaultService = options.vaultService || new VaultService(options.vaultOptions || {}); | ||
| const config = buildConfig(vaultService, options); | ||
|
|
||
| if (!appsSdk) { | ||
| return config; | ||
| } | ||
|
|
||
| if (typeof appsSdk.createApp === 'function') { | ||
| return appsSdk.createApp(config); | ||
| } | ||
|
|
||
| if (appsSdk.App && typeof appsSdk.tool === 'function') { | ||
| const { App, tool } = appsSdk; | ||
| return new App({ | ||
| name: config.name, | ||
| description: config.description, | ||
| model: config.model, | ||
| instructions: config.instructions, | ||
| tools: config.tools.map((toolSchema) => | ||
| tool({ | ||
| name: toolSchema.name, | ||
| description: toolSchema.description, | ||
| parameters: toolSchema.parameters, | ||
| execute: toolSchema.execute | ||
| }) | ||
| ) | ||
| }); | ||
| } | ||
|
|
||
| return config; | ||
| } | ||
|
|
||
| function getEmmaAppManifest(options = {}) { | ||
| const vaultService = options.vaultService || new VaultService(options.vaultOptions || {}); | ||
| const config = buildConfig(vaultService, options); | ||
| return { | ||
| name: config.name, | ||
| description: config.description, | ||
| model: config.model, | ||
| instructions: config.instructions, | ||
| tools: config.tools.map(({ name, description, parameters }) => ({ | ||
| type: 'function', | ||
| name, | ||
| description, | ||
| parameters | ||
| })) | ||
| }; | ||
| } | ||
|
|
||
| module.exports = { | ||
| createEmmaApp, | ||
| getEmmaAppManifest, | ||
| APP_INSTRUCTIONS, | ||
| APP_NAME, | ||
| APP_DEFAULT_MODEL, | ||
| APP_DESCRIPTION | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| <title>Emma - Chat</title> | ||
|
|
||
| <!-- Emma Vault System --> | ||
| <script src="js/emma-env.js"></script> | ||
| <script src="js/emma-logger.js"></script> | ||
| <script src="js/emma-input-modal.js"></script> | ||
| <script src="js/clean-password-modal.js"></script> | ||
| <script src="js/modal-helpers.js"></script> | ||
| <script src="js/emma-feature-flags.js"></script> | ||
| <script src="js/emma-vault-primary.js"></script> | ||
| <script src="js/emma-web-vault.js"></script> | ||
| <script src="js/web-vault-status.js"></script> | ||
|
|
||
| <link rel="stylesheet" href="css/emma-chat-clean.css"> | ||
| <style> | ||
| body { | ||
| display: flex; | ||
| justify-content: center; | ||
| align-items: center; | ||
| height: 100vh; | ||
| background-color: #f0f0f0; | ||
| } | ||
| .chat-container { | ||
| width: 100%; | ||
| max-width: 600px; | ||
| height: 80vh; | ||
| display: flex; | ||
| flex-direction: column; | ||
| } | ||
| </style> | ||
| </head> | ||
| <body> | ||
| <div class="chat-container"> | ||
| <div class="chat-messages" id="chat-messages"> | ||
| <div id="welcome-message"> | ||
| <h2>Welcome to Emma Chat</h2> | ||
| <p>Start a conversation below.</p> | ||
| </div> | ||
| </div> | ||
| <div id="typing-indicator" class="typing-indicator"> | ||
| <span></span><span></span><span></span> | ||
| </div> | ||
| <div class="chat-input-area"> | ||
| <textarea id="chat-input" placeholder="Talk to Emma..." onkeydown="emmaWsChat.handleKeyPress(event)"></textarea> | ||
| <button onclick="emmaWsChat.sendMessage()">Send</button> | ||
| </div> | ||
| </div> | ||
|
|
||
| <!-- Initialize Emma Web Vault --> | ||
| <script> | ||
| // Ensure Emma Web Vault is initialized before ws-chat.js uses it | ||
| (async function initializeVault() { | ||
| if (typeof EmmaWebVault === 'undefined') { | ||
| console.error('EmmaWebVault class not loaded!'); | ||
| return; | ||
| } | ||
|
|
||
| if (!window.emmaWebVault) { | ||
| window.emmaWebVault = new EmmaWebVault(); | ||
| console.log('✓ EmmaWebVault initialized for chat interface'); | ||
| } | ||
| })(); | ||
| </script> | ||
|
|
||
| <script src="js/ws-chat.js"></script> | ||
| </body> | ||
| </html> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Fingerprint truncation logic error with sanitization
The fingerprint truncation logic checks
rawFingerprint.length > 20after callingsubstring(0, 20), but then sanitizes the substring. This creates a mismatch: the ellipsis condition checks the original length, but the sanitized substring might have a different length due to HTML entity encoding. If the first 20 characters contain special characters like&,<, or>, the sanitized version will be longer (e.g.,&becomes&), making the ellipsis logic incorrect. The length check should happen on the sanitized substring, not the raw fingerprint.