Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89fed0c818 |
+178
-11
@@ -911,6 +911,8 @@
|
||||
const DOC_CHAT_D_PREFIX = 'doc-chat:';
|
||||
const AVAILABLE_TOOLS = [];
|
||||
const DEFAULT_SKILL_TEMPLATE = 'system:\nYou are a helpful assistant.\n\nuser:\n{{message}}';
|
||||
const DEFAULT_DOCUMENT_SKILL_SLUG = 'default-document-skill';
|
||||
const DEFAULT_DOCUMENT_SKILL_DESCRIPTION = 'Default skill for document chat/edit JSON contract.';
|
||||
const NOTE_KINDS = [30023, 30024];
|
||||
const DOCUMENT_EDITOR_WRAPPER_SYSTEM = `You are collaborating with the user on a single markdown document.
|
||||
Return ONLY valid JSON with fields: {"chat": string, "document": string|null}.
|
||||
@@ -918,6 +920,16 @@ Return ONLY valid JSON with fields: {"chat": string, "document": string|null}.
|
||||
- document: full updated markdown document when making edits.
|
||||
- If no edit is needed, set document to null.
|
||||
Never include extra keys or text outside JSON.`;
|
||||
const DEFAULT_DOCUMENT_SKILL_TEMPLATE = `system:
|
||||
${DOCUMENT_EDITOR_WRAPPER_SYSTEM}
|
||||
|
||||
Current document:
|
||||
<document>
|
||||
{{document}}
|
||||
</document>
|
||||
|
||||
user:
|
||||
{{message}}`;
|
||||
|
||||
let updateIntervalId = null;
|
||||
let currentPubkey = null;
|
||||
@@ -1260,6 +1272,7 @@ Never include extra keys or text outside JSON.`;
|
||||
title,
|
||||
modelId: '',
|
||||
skillKeys: [],
|
||||
removedSkillKeys: [],
|
||||
messages: [],
|
||||
createdAt: nowTs(),
|
||||
updatedAt: nowTs()
|
||||
@@ -1586,13 +1599,22 @@ Never include extra keys or text outside JSON.`;
|
||||
};
|
||||
}
|
||||
|
||||
function parseSkillTemplate(template, userInput) {
|
||||
function parseSkillTemplate(template, userInput, context = {}) {
|
||||
const parts = splitSkillTemplate(template);
|
||||
const system = String(parts.system || '').trim();
|
||||
const rawSystem = String(parts.system || '').trim();
|
||||
const userTemplate = String(parts.userTemplate || '').trim();
|
||||
const messageText = String(userInput || '').trim();
|
||||
const documentText = String(context?.document || '').trim();
|
||||
const system = rawSystem
|
||||
.replaceAll('{{message}}', messageText)
|
||||
.replaceAll('{{document}}', documentText)
|
||||
.trim();
|
||||
const user = userTemplate
|
||||
? userTemplate.replaceAll('{{message}}', String(userInput || '').trim()).trim()
|
||||
: String(userInput || '').trim();
|
||||
? userTemplate
|
||||
.replaceAll('{{message}}', messageText)
|
||||
.replaceAll('{{document}}', documentText)
|
||||
.trim()
|
||||
: messageText;
|
||||
return { system, user, userTemplate };
|
||||
}
|
||||
|
||||
@@ -1759,27 +1781,80 @@ Never include extra keys or text outside JSON.`;
|
||||
};
|
||||
}
|
||||
|
||||
function getDefaultDocumentSkillKey() {
|
||||
if (!currentPubkey) return '';
|
||||
return skillKey(currentPubkey, DEFAULT_DOCUMENT_SKILL_SLUG);
|
||||
}
|
||||
|
||||
function isDefaultDocumentSkillKey(key) {
|
||||
return String(key || '').trim() === getDefaultDocumentSkillKey();
|
||||
}
|
||||
|
||||
function getConversationSkillKeys(convo) {
|
||||
return Array.isArray(convo?.skillKeys)
|
||||
? convo.skillKeys.map((x) => String(x || '').trim()).filter(Boolean)
|
||||
: [];
|
||||
}
|
||||
|
||||
function getConversationRemovedSkillKeys(convo) {
|
||||
return Array.isArray(convo?.removedSkillKeys)
|
||||
? convo.removedSkillKeys.map((x) => String(x || '').trim()).filter(Boolean)
|
||||
: [];
|
||||
}
|
||||
|
||||
function enforceDefaultSkillSelection() {
|
||||
const convo = getCurrentConversation();
|
||||
if (!convo) return;
|
||||
const defaultKey = getDefaultDocumentSkillKey();
|
||||
if (!defaultKey) return;
|
||||
const hasDefaultSkill = skills.some((s) => s.key === defaultKey);
|
||||
if (!hasDefaultSkill) return;
|
||||
|
||||
const removed = new Set(getConversationRemovedSkillKeys(convo));
|
||||
const selected = new Set(getConversationSkillKeys(convo));
|
||||
if (!removed.has(defaultKey) && !selected.has(defaultKey)) {
|
||||
selected.add(defaultKey);
|
||||
convo.skillKeys = Array.from(selected);
|
||||
selectedSkillKeys = [...convo.skillKeys];
|
||||
updateConversation({ skillKeys: [...convo.skillKeys] });
|
||||
renderSkillsList();
|
||||
renderSkillsEditor();
|
||||
}
|
||||
}
|
||||
|
||||
function syncSelectedSkillsFromConversation(convo) {
|
||||
const target = getConversationSkillKeys(convo);
|
||||
const curr = selectedSkillKeys.join('|');
|
||||
const next = target.join('|');
|
||||
if (curr === next) return;
|
||||
selectedSkillKeys = target;
|
||||
renderSkillsList();
|
||||
renderSkillsEditor();
|
||||
if (curr !== next) {
|
||||
selectedSkillKeys = target;
|
||||
renderSkillsList();
|
||||
renderSkillsEditor();
|
||||
}
|
||||
enforceDefaultSkillSelection();
|
||||
}
|
||||
|
||||
function persistSelectedSkillsToConversation() {
|
||||
const convo = getCurrentConversation();
|
||||
if (!convo) return;
|
||||
convo.skillKeys = [...selectedSkillKeys];
|
||||
updateConversation({ skillKeys: [...selectedSkillKeys] });
|
||||
convo.removedSkillKeys = getConversationRemovedSkillKeys(convo);
|
||||
updateConversation({
|
||||
skillKeys: [...selectedSkillKeys],
|
||||
removedSkillKeys: [...convo.removedSkillKeys]
|
||||
});
|
||||
}
|
||||
|
||||
function setDefaultSkillExplicitlyRemoved(removed) {
|
||||
const convo = getCurrentConversation();
|
||||
if (!convo) return;
|
||||
const defaultKey = getDefaultDocumentSkillKey();
|
||||
if (!defaultKey) return;
|
||||
const set = new Set(getConversationRemovedSkillKeys(convo));
|
||||
if (removed) set.add(defaultKey);
|
||||
else set.delete(defaultKey);
|
||||
convo.removedSkillKeys = Array.from(set);
|
||||
updateConversation({ removedSkillKeys: [...convo.removedSkillKeys] });
|
||||
}
|
||||
|
||||
function toggleSkillSelection(key, enabled) {
|
||||
@@ -1791,6 +1866,9 @@ Never include extra keys or text outside JSON.`;
|
||||
} else if (!enabled && exists) {
|
||||
selectedSkillKeys = selectedSkillKeys.filter((k) => k !== skillKeyRaw);
|
||||
}
|
||||
if (isDefaultDocumentSkillKey(skillKeyRaw)) {
|
||||
setDefaultSkillExplicitlyRemoved(!enabled);
|
||||
}
|
||||
persistSelectedSkillsToConversation();
|
||||
renderSkillsList();
|
||||
renderSkillsEditor();
|
||||
@@ -2113,6 +2191,7 @@ Never include extra keys or text outside JSON.`;
|
||||
}
|
||||
renderSkillsList();
|
||||
renderSkillsEditor();
|
||||
enforceDefaultSkillSelection();
|
||||
}
|
||||
|
||||
function clearSkillSubscription() {
|
||||
@@ -2185,7 +2264,9 @@ Never include extra keys or text outside JSON.`;
|
||||
|
||||
for (const skill of selected) {
|
||||
const edit = getSkillEditorValue(skill);
|
||||
const parsed = parseSkillTemplate(edit.template, userInput);
|
||||
const parsed = parseSkillTemplate(edit.template, userInput, {
|
||||
document: String(taDocument?.value || '')
|
||||
});
|
||||
if (parsed.system) systemParts.push(parsed.system);
|
||||
if (parsed.userTemplate) {
|
||||
resolvedUser = parsed.user;
|
||||
@@ -2223,6 +2304,7 @@ Never include extra keys or text outside JSON.`;
|
||||
model_id: String(convo?.modelId || ''),
|
||||
provider_name: String(aiConfig?.provider || ''),
|
||||
skill_keys: Array.isArray(convo?.skillKeys) ? convo.skillKeys : [],
|
||||
removed_skill_keys: getConversationRemovedSkillKeys(convo),
|
||||
messages: deleted ? [] : safeMessages,
|
||||
deleted: Boolean(deleted)
|
||||
};
|
||||
@@ -2327,6 +2409,9 @@ Never include extra keys or text outside JSON.`;
|
||||
skillKeys: Array.isArray(parsed?.skill_keys)
|
||||
? parsed.skill_keys.map((x) => String(x || '').trim()).filter(Boolean)
|
||||
: [],
|
||||
removedSkillKeys: Array.isArray(parsed?.removed_skill_keys)
|
||||
? parsed.removed_skill_keys.map((x) => String(x || '').trim()).filter(Boolean)
|
||||
: [],
|
||||
messages,
|
||||
createdAt: Number(Date.parse(d) || nowTs()),
|
||||
updatedAt: Number(parsed?.updated_at_ms || nowTs())
|
||||
@@ -3241,7 +3326,10 @@ Never include extra keys or text outside JSON.`;
|
||||
try {
|
||||
const apiPayload = conversationToApiMessages(convo, { currentPrompt: prompt });
|
||||
const wrapperSystem = buildDocumentSystemPrompt(prompt);
|
||||
const allMessages = [{ role: 'system', content: wrapperSystem }, ...apiPayload.messages];
|
||||
const hasDefaultSkillSelected = getSelectedSkills().some((s) => normalizeSlug(s.slug) === DEFAULT_DOCUMENT_SKILL_SLUG);
|
||||
const allMessages = hasDefaultSkillSelected
|
||||
? [...apiPayload.messages]
|
||||
: [{ role: 'system', content: wrapperSystem }, ...apiPayload.messages];
|
||||
|
||||
let rawContent = '';
|
||||
let responseJson = null;
|
||||
@@ -3465,6 +3553,7 @@ Never include extra keys or text outside JSON.`;
|
||||
if (btnSkillClearAll) {
|
||||
btnSkillClearAll.addEventListener('click', () => {
|
||||
selectedSkillKeys = [];
|
||||
setDefaultSkillExplicitlyRemoved(true);
|
||||
persistSelectedSkillsToConversation();
|
||||
renderSkillsList();
|
||||
renderSkillsEditor();
|
||||
@@ -3972,6 +4061,83 @@ Never include extra keys or text outside JSON.`;
|
||||
});
|
||||
}
|
||||
|
||||
async function ensureDefaultDocumentSkillExists() {
|
||||
if (!currentPubkey) return;
|
||||
const slug = DEFAULT_DOCUMENT_SKILL_SLUG;
|
||||
const key = getDefaultDocumentSkillKey();
|
||||
const existingInMemory = skills.find((s) => s.key === key);
|
||||
if (existingInMemory) {
|
||||
enforceDefaultSkillSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
const filter = {
|
||||
kinds: [31123],
|
||||
authors: [currentPubkey],
|
||||
'#d': [slug],
|
||||
limit: 1
|
||||
};
|
||||
|
||||
const cached = await queryCache(filter).catch(() => []);
|
||||
const cachedEvt = Array.isArray(cached) && cached.length > 0
|
||||
? [...cached].sort((a, b) => Number(b?.created_at || 0) - Number(a?.created_at || 0))[0]
|
||||
: null;
|
||||
if (cachedEvt) {
|
||||
upsertSkillFromEvent(cachedEvt);
|
||||
enforceDefaultSkillSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
const relays = await ndkFetchEvents(filter).catch(() => []);
|
||||
const relayEvt = Array.isArray(relays) && relays.length > 0
|
||||
? [...relays].sort((a, b) => Number(b?.created_at || 0) - Number(a?.created_at || 0))[0]
|
||||
: null;
|
||||
if (relayEvt) {
|
||||
upsertSkillFromEvent(relayEvt);
|
||||
enforceDefaultSkillSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
const draftSkill = {
|
||||
key,
|
||||
slug,
|
||||
description: DEFAULT_DOCUMENT_SKILL_DESCRIPTION,
|
||||
template: DEFAULT_DOCUMENT_SKILL_TEMPLATE,
|
||||
temperature: Number(aiConfig.temperature ?? 0),
|
||||
max_tokens: Math.max(1, Math.floor(Number(aiConfig.max_tokens ?? 10000))),
|
||||
llm: String(aiConfig.model || 'default').trim() || 'default',
|
||||
seed: null,
|
||||
requires_tools: [],
|
||||
optional_tools: [],
|
||||
requires_skills: [],
|
||||
unavailable_required_tools: [],
|
||||
needsUnavailableTools: false,
|
||||
author: currentPubkey,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
raw: null
|
||||
};
|
||||
|
||||
skillEditorValues[draftSkill.key] = {
|
||||
slug: draftSkill.slug,
|
||||
description: draftSkill.description,
|
||||
llm: draftSkill.llm,
|
||||
temperature: String(draftSkill.temperature),
|
||||
max_tokens: String(draftSkill.max_tokens),
|
||||
seed: '',
|
||||
template: draftSkill.template
|
||||
};
|
||||
|
||||
const skillEvent = buildSkillEventFromEditor(draftSkill);
|
||||
await publishEvent(skillEvent);
|
||||
upsertSkillFromEvent({
|
||||
...skillEvent,
|
||||
pubkey: currentPubkey,
|
||||
id: uid()
|
||||
});
|
||||
setStatus('Created default-document-skill.');
|
||||
enforceDefaultSkillSelection();
|
||||
}
|
||||
|
||||
(async function main() {
|
||||
const versionInfo = await getVersion();
|
||||
console.log(`[document.html ${versionInfo.VERSION}] Loading...`);
|
||||
@@ -4104,6 +4270,7 @@ Never include extra keys or text outside JSON.`;
|
||||
renderSkillsList();
|
||||
renderSkillsEditor();
|
||||
refreshSkills();
|
||||
await ensureDefaultDocumentSkillExists();
|
||||
bindRelayActivityListeners();
|
||||
setStatus('Ready.');
|
||||
getRoutstrBalance().catch((error) => {
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"VERSION": "v0.7.7",
|
||||
"VERSION_NUMBER": "0.7.7",
|
||||
"BUILD_DATE": "2026-04-28T14:00:02.302Z"
|
||||
"VERSION": "v0.7.8",
|
||||
"VERSION_NUMBER": "0.7.8",
|
||||
"BUILD_DATE": "2026-04-28T14:39:51.733Z"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user