@@ -33,7 +33,7 @@ import {buildAgentTools} from './pi_tool_adapter.js';
3333import { toolConfigChannel } from './tool_catalog.js' ;
3434import './dao_chat_history_panel.js' ;
3535import { ensurePiAppStorage , syncActiveKeyToPiStorage } from './pi_app_storage.js' ;
36- import { getAllSkills , initSkillRegistry , loadSkillInstructions , type SkillRegistryEntry } from './skill_registry.js' ;
36+ import { getAllSkills , initSkillRegistry , loadSkillInstructions , refreshSkillRegistry , refreshSkillRegistryIfStale , type SkillRegistryEntry } from './skill_registry.js' ;
3737import { t } from './i18n/i18n.js' ;
3838// eslint-disable-next-line @typescript-eslint/no-explicit-any
3939import * as pi from './vendor/pi_runtime_bundle.js' ;
@@ -1870,6 +1870,19 @@ export class DaoChatView extends CrLitElement {
18701870 this . hideSkillPicker_ ( ) ;
18711871 return ;
18721872 }
1873+ // Cross-WebUI sync: skills created/deleted in the dao://skills tab
1874+ // don't update this page's module-local registry cache. Refresh on
1875+ // demand (throttled to avoid spamming on every keystroke) and re-run
1876+ // the picker once new data lands so newly-added skills show up
1877+ // without a sidebar reload.
1878+ void refreshSkillRegistryIfStale ( ) . then ( ( refreshed ) => {
1879+ if ( ! refreshed ) return ;
1880+ const ta2 = this . composerTextarea_ ;
1881+ if ( ! ta2 ) return ;
1882+ if ( / ^ \/ ( [ A - Z a - z 0 - 9 _ - ] * ) $ / . test ( ta2 . value ) ) {
1883+ this . updateSkillPicker_ ( ) ;
1884+ }
1885+ } ) ;
18731886 this . skillPickerQuery_ = ( m [ 1 ] || '' ) . toLowerCase ( ) ;
18741887 const all = getAllSkills ( ) ;
18751888 const q = this . skillPickerQuery_ ;
@@ -2002,8 +2015,20 @@ export class DaoChatView extends CrLitElement {
20022015 // Warm the instructions cache before the message hits state.messages so
20032016 // convertToLlm can splice the body in synchronously on the next turn.
20042017 private async ensureSkillLoadedFromText_ ( text : string ) : Promise < void > {
2005- const parsed = this . parseSkillPrefix_ ( text ) ;
2006- if ( ! parsed ) return ;
2018+ let parsed = this . parseSkillPrefix_ ( text ) ;
2019+ if ( ! parsed ) {
2020+ // The text looks like `/<id> ...` but the id wasn't in our cache.
2021+ // This is the "user created a skill in dao://skills and typed
2022+ // /id fast enough to skip the picker" path. Bypass the staleness
2023+ // throttle (a recent refresh from picker-open could still be from
2024+ // before the skill was created in the other tab) and force one
2025+ // fresh fetch before declaring the prefix unknown.
2026+ if ( / ^ \/ ( [ A - Z a - z 0 - 9 _ - ] + ) (?: \s | $ ) / . test ( text . trim ( ) ) ) {
2027+ await refreshSkillRegistry ( ) ;
2028+ parsed = this . parseSkillPrefix_ ( text ) ;
2029+ }
2030+ if ( ! parsed ) return ;
2031+ }
20072032 if ( this . skillInstructionsCache_ . has ( parsed . skillId ) ) return ;
20082033 const content = await loadSkillInstructions ( parsed . skillId ) ;
20092034 if ( content && content . instructions ) {
0 commit comments