@hienlh/ppm 0.9.39 → 0.9.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/CHANGELOG.md +3 -50
  2. package/dist/web/assets/browser-tab--V6I70pH.js +1 -0
  3. package/dist/web/assets/chat-tab-CrkhvVjF.js +10 -0
  4. package/dist/web/assets/code-editor-BfMyExLp.js +2 -0
  5. package/dist/web/assets/{database-viewer-TjRo2b8_.js → database-viewer-CeRUrZKj.js} +1 -1
  6. package/dist/web/assets/{diff-viewer-BMhCz0xk.js → diff-viewer-D2p3WTMS.js} +1 -1
  7. package/dist/web/assets/{extension-webview-DiVdlE2r.js → extension-webview-DQWAHMlR.js} +1 -1
  8. package/dist/web/assets/git-graph-BWRMlCdK.js +1 -0
  9. package/dist/web/assets/index-C7esr4gM.css +2 -0
  10. package/dist/web/assets/index-DU6UVgQY.js +30 -0
  11. package/dist/web/assets/keybindings-store-BE2T8jM9.js +1 -0
  12. package/dist/web/assets/{markdown-renderer-IyEzLrC6.js → markdown-renderer-C7lKs47M.js} +4 -4
  13. package/dist/web/assets/{postgres-viewer-CSynGGkJ.js → postgres-viewer-Cr9jpBNd.js} +1 -1
  14. package/dist/web/assets/{settings-tab-BdI4HhRa.js → settings-tab-DKy-YDg2.js} +1 -1
  15. package/dist/web/assets/{sqlite-viewer-C5mviyU5.js → sqlite-viewer-9AmeF-Zs.js} +1 -1
  16. package/dist/web/assets/square-oPKIkJiw.js +1 -0
  17. package/dist/web/assets/{terminal-tab-CDyC1grg.js → terminal-tab-DFhB4Rxh.js} +1 -1
  18. package/dist/web/assets/{use-monaco-theme-DcVicB_i.js → use-monaco-theme-B7XLw-OX.js} +1 -1
  19. package/dist/web/index.html +2 -3
  20. package/dist/web/sw.js +1 -1
  21. package/docs/codebase-summary.md +3 -33
  22. package/docs/project-changelog.md +0 -47
  23. package/docs/project-roadmap.md +7 -14
  24. package/docs/system-architecture.md +2 -65
  25. package/package.json +1 -1
  26. package/src/server/index.ts +0 -7
  27. package/src/server/routes/settings.ts +1 -72
  28. package/src/services/config.service.ts +1 -1
  29. package/src/services/db.service.ts +1 -279
  30. package/src/services/git.service.ts +2 -2
  31. package/src/types/config.ts +0 -26
  32. package/src/web/components/browser/browser-tab.tsx +128 -97
  33. package/src/web/components/chat/chat-history-bar.tsx +3 -8
  34. package/src/web/components/layout/command-palette.tsx +1 -1
  35. package/src/web/components/settings/settings-tab.tsx +1 -4
  36. package/src/web/hooks/use-url-sync.ts +1 -1
  37. package/dist/web/assets/browser-tab-DnIsHiCc.js +0 -1
  38. package/dist/web/assets/chat-tab-il6D4jql.js +0 -10
  39. package/dist/web/assets/code-editor-BUc1jBqm.js +0 -2
  40. package/dist/web/assets/git-graph-4eGJ8B1A.js +0 -1
  41. package/dist/web/assets/index-BmcV1di6.js +0 -30
  42. package/dist/web/assets/index-CcFDEPCo.css +0 -2
  43. package/dist/web/assets/keybindings-store--5T5hsAj.js +0 -1
  44. package/dist/web/assets/tab-store-BXMIUvsE.js +0 -1
  45. package/docs/streaming-input-guide.md +0 -267
  46. package/snapshot-state.md +0 -1526
  47. package/src/services/ppmbot/ppmbot-formatter.ts +0 -88
  48. package/src/services/ppmbot/ppmbot-memory.ts +0 -333
  49. package/src/services/ppmbot/ppmbot-service.ts +0 -545
  50. package/src/services/ppmbot/ppmbot-session.ts +0 -199
  51. package/src/services/ppmbot/ppmbot-streamer.ts +0 -288
  52. package/src/services/ppmbot/ppmbot-telegram.ts +0 -279
  53. package/src/types/ppmbot.ts +0 -103
  54. package/src/web/components/settings/ppmbot-settings-section.tsx +0 -270
  55. package/test-session-ops.mjs +0 -444
  56. package/test-tokens.mjs +0 -212
package/dist/web/sw.js CHANGED
@@ -1 +1 @@
1
- try{self[`workbox:core:7.3.0`]&&_()}catch{}var e=(e,...t)=>{let n=e;return t.length>0&&(n+=` :: ${JSON.stringify(t)}`),n},t=class extends Error{constructor(t,n){let r=e(t,n);super(r),this.name=t,this.details=n}},n={googleAnalytics:`googleAnalytics`,precache:`precache-v2`,prefix:`workbox`,runtime:`runtime`,suffix:typeof registration<`u`?registration.scope:``},r=e=>[n.prefix,e,n.suffix].filter(e=>e&&e.length>0).join(`-`),i=e=>{for(let t of Object.keys(n))e(t)},a={updateDetails:e=>{i(t=>{typeof e[t]==`string`&&(n[t]=e[t])})},getGoogleAnalyticsName:e=>e||r(n.googleAnalytics),getPrecacheName:e=>e||r(n.precache),getPrefix:()=>n.prefix,getRuntimeName:e=>e||r(n.runtime),getSuffix:()=>n.suffix};function o(e,t){let n=t();return e.waitUntil(n),n}try{self[`workbox:precaching:7.3.0`]&&_()}catch{}var s=`__WB_REVISION__`;function c(e){if(!e)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(typeof e==`string`){let t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}let{revision:n,url:r}=e;if(!r)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(!n){let e=new URL(r,location.href);return{cacheKey:e.href,url:e.href}}let i=new URL(r,location.href),a=new URL(r,location.href);return i.searchParams.set(s,n),{cacheKey:i.href,url:a.href}}var l=class{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:n})=>{if(e.type===`install`&&t&&t.originalRequest&&t.originalRequest instanceof Request){let e=t.originalRequest.url;n?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return n}}},u=class{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:e,params:t})=>{let n=t?.cacheKey||this._precacheController.getCacheKeyForURL(e.url);return n?new Request(n,{headers:e.headers}):e},this._precacheController=e}},d;function f(){if(d===void 0){let e=new Response(``);if(`body`in e)try{new Response(e.body),d=!0}catch{d=!1}d=!1}return d}async function p(e,n){let r=null;if(e.url&&(r=new URL(e.url).origin),r!==self.location.origin)throw new t(`cross-origin-copy-response`,{origin:r});let i=e.clone(),a={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=n?n(a):a,s=f()?i.body:await i.blob();return new Response(s,o)}var m=e=>new URL(String(e),location.href).href.replace(RegExp(`^${location.origin}`),``);function h(e,t){let n=new URL(e);for(let e of t)n.searchParams.delete(e);return n.href}async function g(e,t,n,r){let i=h(t.url,n);if(t.url===i)return e.match(t,r);let a=Object.assign(Object.assign({},r),{ignoreSearch:!0}),o=await e.keys(t,a);for(let t of o)if(i===h(t.url,n))return e.match(t,r)}var v=class{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}},y=new Set;async function b(){for(let e of y)await e()}function x(e){return new Promise(t=>setTimeout(t,e))}try{self[`workbox:strategies:7.3.0`]&&_()}catch{}function S(e){return typeof e==`string`?new Request(e):e}var C=class{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new v,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(let e of this._plugins)this._pluginStateMap.set(e,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){let{event:n}=this,r=S(e);if(r.mode===`navigate`&&n instanceof FetchEvent&&n.preloadResponse){let e=await n.preloadResponse;if(e)return e}let i=this.hasCallback(`fetchDidFail`)?r.clone():null;try{for(let e of this.iterateCallbacks(`requestWillFetch`))r=await e({request:r.clone(),event:n})}catch(e){if(e instanceof Error)throw new t(`plugin-error-request-will-fetch`,{thrownErrorMessage:e.message})}let a=r.clone();try{let e;e=await fetch(r,r.mode===`navigate`?void 0:this._strategy.fetchOptions);for(let t of this.iterateCallbacks(`fetchDidSucceed`))e=await t({event:n,request:a,response:e});return e}catch(e){throw i&&await this.runCallbacks(`fetchDidFail`,{error:e,event:n,originalRequest:i.clone(),request:a.clone()}),e}}async fetchAndCachePut(e){let t=await this.fetch(e),n=t.clone();return this.waitUntil(this.cachePut(e,n)),t}async cacheMatch(e){let t=S(e),n,{cacheName:r,matchOptions:i}=this._strategy,a=await this.getCacheKey(t,`read`),o=Object.assign(Object.assign({},i),{cacheName:r});n=await caches.match(a,o);for(let e of this.iterateCallbacks(`cachedResponseWillBeUsed`))n=await e({cacheName:r,matchOptions:i,cachedResponse:n,request:a,event:this.event})||void 0;return n}async cachePut(e,n){let r=S(e);await x(0);let i=await this.getCacheKey(r,`write`);if(!n)throw new t(`cache-put-with-no-response`,{url:m(i.url)});let a=await this._ensureResponseSafeToCache(n);if(!a)return!1;let{cacheName:o,matchOptions:s}=this._strategy,c=await self.caches.open(o),l=this.hasCallback(`cacheDidUpdate`),u=l?await g(c,i.clone(),[`__WB_REVISION__`],s):null;try{await c.put(i,l?a.clone():a)}catch(e){if(e instanceof Error)throw e.name===`QuotaExceededError`&&await b(),e}for(let e of this.iterateCallbacks(`cacheDidUpdate`))await e({cacheName:o,oldResponse:u,newResponse:a.clone(),request:i,event:this.event});return!0}async getCacheKey(e,t){let n=`${e.url} | ${t}`;if(!this._cacheKeys[n]){let r=e;for(let e of this.iterateCallbacks(`cacheKeyWillBeUsed`))r=S(await e({mode:t,request:r,event:this.event,params:this.params}));this._cacheKeys[n]=r}return this._cacheKeys[n]}hasCallback(e){for(let t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(let n of this.iterateCallbacks(e))await n(t)}*iterateCallbacks(e){for(let t of this._strategy.plugins)if(typeof t[e]==`function`){let n=this._pluginStateMap.get(t);yield r=>{let i=Object.assign(Object.assign({},r),{state:n});return t[e](i)}}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){for(;this._extendLifetimePromises.length;){let e=this._extendLifetimePromises.splice(0),t=(await Promise.allSettled(e)).find(e=>e.status===`rejected`);if(t)throw t.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,n=!1;for(let e of this.iterateCallbacks(`cacheWillUpdate`))if(t=await e({request:this.request,response:t,event:this.event})||void 0,n=!0,!t)break;return n||t&&t.status!==200&&(t=void 0),t}},w=class{constructor(e={}){this.cacheName=a.getRuntimeName(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){let[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});let t=e.event,n=typeof e.request==`string`?new Request(e.request):e.request,r=`params`in e?e.params:void 0,i=new C(this,{event:t,request:n,params:r}),a=this._getResponse(i,n,t);return[a,this._awaitComplete(a,i,n,t)]}async _getResponse(e,n,r){await e.runCallbacks(`handlerWillStart`,{event:r,request:n});let i;try{if(i=await this._handle(n,e),!i||i.type===`error`)throw new t(`no-response`,{url:n.url})}catch(t){if(t instanceof Error){for(let a of e.iterateCallbacks(`handlerDidError`))if(i=await a({error:t,event:r,request:n}),i)break}if(!i)throw t}for(let t of e.iterateCallbacks(`handlerWillRespond`))i=await t({event:r,request:n,response:i});return i}async _awaitComplete(e,t,n,r){let i,a;try{i=await e}catch{}try{await t.runCallbacks(`handlerDidRespond`,{event:r,request:n,response:i}),await t.doneWaiting()}catch(e){e instanceof Error&&(a=e)}if(await t.runCallbacks(`handlerDidComplete`,{event:r,request:n,response:i,error:a}),t.destroy(),a)throw a}},T=class e extends w{constructor(t={}){t.cacheName=a.getPrecacheName(t.cacheName),super(t),this._fallbackToNetwork=t.fallbackToNetwork!==!1,this.plugins.push(e.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){return await t.cacheMatch(e)||(t.event&&t.event.type===`install`?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,n){let r,i=n.params||{};if(this._fallbackToNetwork){let t=i.integrity,a=e.integrity,o=!a||a===t;r=await n.fetch(new Request(e,{integrity:e.mode===`no-cors`?void 0:a||t})),t&&o&&e.mode!==`no-cors`&&(this._useDefaultCacheabilityPluginIfNeeded(),await n.cachePut(e,r.clone()))}else throw new t(`missing-precache-entry`,{cacheName:this.cacheName,url:e.url});return r}async _handleInstall(e,n){this._useDefaultCacheabilityPluginIfNeeded();let r=await n.fetch(e);if(!await n.cachePut(e,r.clone()))throw new t(`bad-precaching-response`,{url:e.url,status:r.status});return r}_useDefaultCacheabilityPluginIfNeeded(){let t=null,n=0;for(let[r,i]of this.plugins.entries())i!==e.copyRedirectedCacheableResponsesPlugin&&(i===e.defaultPrecacheCacheabilityPlugin&&(t=r),i.cacheWillUpdate&&n++);n===0?this.plugins.push(e.defaultPrecacheCacheabilityPlugin):n>1&&t!==null&&this.plugins.splice(t,1)}};T.defaultPrecacheCacheabilityPlugin={async cacheWillUpdate({response:e}){return!e||e.status>=400?null:e}},T.copyRedirectedCacheableResponsesPlugin={async cacheWillUpdate({response:e}){return e.redirected?await p(e):e}};var E=class{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:n=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new T({cacheName:a.getPrecacheName(e),plugins:[...t,new u({precacheController:this})],fallbackToNetwork:n}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this._strategy}precache(e){this.addToCacheList(e),this._installAndActiveListenersAdded||=(self.addEventListener(`install`,this.install),self.addEventListener(`activate`,this.activate),!0)}addToCacheList(e){let n=[];for(let r of e){typeof r==`string`?n.push(r):r&&r.revision===void 0&&n.push(r.url);let{cacheKey:e,url:i}=c(r),a=typeof r!=`string`&&r.revision?`reload`:`default`;if(this._urlsToCacheKeys.has(i)&&this._urlsToCacheKeys.get(i)!==e)throw new t(`add-to-cache-list-conflicting-entries`,{firstEntry:this._urlsToCacheKeys.get(i),secondEntry:e});if(typeof r!=`string`&&r.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==r.integrity)throw new t(`add-to-cache-list-conflicting-integrities`,{url:i});this._cacheKeysToIntegrities.set(e,r.integrity)}if(this._urlsToCacheKeys.set(i,e),this._urlsToCacheModes.set(i,a),n.length>0){let e=`Workbox is precaching URLs without revision info: ${n.join(`, `)}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(e)}}}install(e){return o(e,async()=>{let t=new l;this.strategy.plugins.push(t);for(let[t,n]of this._urlsToCacheKeys){let r=this._cacheKeysToIntegrities.get(n),i=this._urlsToCacheModes.get(t),a=new Request(t,{integrity:r,cache:i,credentials:`same-origin`});await Promise.all(this.strategy.handleAll({params:{cacheKey:n},request:a,event:e}))}let{updatedURLs:n,notUpdatedURLs:r}=t;return{updatedURLs:n,notUpdatedURLs:r}})}activate(e){return o(e,async()=>{let e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),n=new Set(this._urlsToCacheKeys.values()),r=[];for(let i of t)n.has(i.url)||(await e.delete(i),r.push(i.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let t=e instanceof Request?e.url:e,n=this.getCacheKeyForURL(t);if(n)return(await self.caches.open(this.strategy.cacheName)).match(n)}createHandlerBoundToURL(e){let n=this.getCacheKeyForURL(e);if(!n)throw new t(`non-precached-url`,{url:e});return t=>(t.request=new Request(e),t.params=Object.assign({cacheKey:n},t.params),this.strategy.handle(t))}},D,O=()=>(D||=new E,D);try{self[`workbox:routing:7.3.0`]&&_()}catch{}var k=e=>e&&typeof e==`object`?e:{handle:e},A=class{constructor(e,t,n=`GET`){this.handler=k(t),this.match=e,this.method=n}setCatchHandler(e){this.catchHandler=k(e)}},j=class extends A{constructor(e,t,n){super(({url:t})=>{let n=e.exec(t.href);if(n&&!(t.origin!==location.origin&&n.index!==0))return n.slice(1)},t,n)}},M=class{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener(`fetch`,(e=>{let{request:t}=e,n=this.handleRequest({request:t,event:e});n&&e.respondWith(n)}))}addCacheListener(){self.addEventListener(`message`,(e=>{if(e.data&&e.data.type===`CACHE_URLS`){let{payload:t}=e.data,n=Promise.all(t.urlsToCache.map(t=>{typeof t==`string`&&(t=[t]);let n=new Request(...t);return this.handleRequest({request:n,event:e})}));e.waitUntil(n),e.ports&&e.ports[0]&&n.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){let n=new URL(e.url,location.href);if(!n.protocol.startsWith(`http`))return;let r=n.origin===location.origin,{params:i,route:a}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:n}),o=a&&a.handler,s=e.method;if(!o&&this._defaultHandlerMap.has(s)&&(o=this._defaultHandlerMap.get(s)),!o)return;let c;try{c=o.handle({url:n,request:e,event:t,params:i})}catch(e){c=Promise.reject(e)}let l=a&&a.catchHandler;return c instanceof Promise&&(this._catchHandler||l)&&(c=c.catch(async r=>{if(l)try{return await l.handle({url:n,request:e,event:t,params:i})}catch(e){e instanceof Error&&(r=e)}if(this._catchHandler)return this._catchHandler.handle({url:n,request:e,event:t});throw r})),c}findMatchingRoute({url:e,sameOrigin:t,request:n,event:r}){let i=this._routes.get(n.method)||[];for(let a of i){let i,o=a.match({url:e,sameOrigin:t,request:n,event:r});if(o)return i=o,(Array.isArray(i)&&i.length===0||o.constructor===Object&&Object.keys(o).length===0||typeof o==`boolean`)&&(i=void 0),{route:a,params:i}}return{}}setDefaultHandler(e,t=`GET`){this._defaultHandlerMap.set(t,k(e))}setCatchHandler(e){this._catchHandler=k(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new t(`unregister-route-but-not-found-with-method`,{method:e.method});let n=this._routes.get(e.method).indexOf(e);if(n>-1)this._routes.get(e.method).splice(n,1);else throw new t(`unregister-route-route-not-registered`)}},N,P=()=>(N||(N=new M,N.addFetchListener(),N.addCacheListener()),N);function F(e,n,r){let i;if(typeof e==`string`){let t=new URL(e,location.href);i=new A(({url:e})=>e.href===t.href,n,r)}else if(e instanceof RegExp)i=new j(e,n,r);else if(typeof e==`function`)i=new A(e,n,r);else if(e instanceof A)i=e;else throw new t(`unsupported-route-type`,{moduleName:`workbox-routing`,funcName:`registerRoute`,paramName:`capture`});return P().registerRoute(i),i}function I(e,t=[]){for(let n of[...e.searchParams.keys()])t.some(e=>e.test(n))&&e.searchParams.delete(n);return e}function*L(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:n=`index.html`,cleanURLs:r=!0,urlManipulation:i}={}){let a=new URL(e,location.href);a.hash=``,yield a.href;let o=I(a,t);if(yield o.href,n&&o.pathname.endsWith(`/`)){let e=new URL(o.href);e.pathname+=n,yield e.href}if(r){let e=new URL(o.href);e.pathname+=`.html`,yield e.href}if(i){let e=i({url:a});for(let t of e)yield t.href}}var R=class extends A{constructor(e,t){super(({request:n})=>{let r=e.getURLsToCacheKeys();for(let i of L(n.url,t)){let t=r.get(i);if(t)return{cacheKey:t,integrity:e.getIntegrityForCacheKey(t)}}},e.strategy)}};function z(e){F(new R(O(),e))}function B(e){O().precache(e)}function V(e,t){B(e),z(t)}V([{"revision":"1872c500de691dce40960bb85481de07","url":"registerSW.js"},{"revision":"9f50533cb3ab66547532f7b18f4f74c1","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"948e060affb598c339be40d69e1f6f9c","url":"monacoeditorwork/ts.worker.bundle.js"},{"revision":"a5d8a1acfc29c2a4c882a54ffc93def3","url":"monacoeditorwork/json.worker.bundle.js"},{"revision":"d0f94ce046cf8cf09605ee7664dac557","url":"monacoeditorwork/html.worker.bundle.js"},{"revision":"a424156a79b9c1b907db93aa3180585a","url":"monacoeditorwork/editor.worker.bundle.js"},{"revision":"b3a7f967560c9816492a1567b3f7f0dc","url":"monacoeditorwork/css.worker.bundle.js"},{"revision":null,"url":"assets/xychartDiagram-JWTSCODW-DylHYNtJ.js"},{"revision":null,"url":"assets/vennDiagram-LZ73GAT5-ywK7LMaH.js"},{"revision":null,"url":"assets/utils-DMiycH3O.js"},{"revision":null,"url":"assets/use-monaco-theme-DcVicB_i.js"},{"revision":null,"url":"assets/treemap-KZPCXAKY-C9TYRE0k.js"},{"revision":null,"url":"assets/timeline-definition-YZTLITO2-A4PN_Efm.js"},{"revision":null,"url":"assets/terminal-tab-CDyC1grg.js"},{"revision":null,"url":"assets/terminal-tab-BrP-ENHg.css"},{"revision":null,"url":"assets/tag-CXMT0QB6.js"},{"revision":null,"url":"assets/table-DFevCOMd.js"},{"revision":null,"url":"assets/tab-store-BXMIUvsE.js"},{"revision":null,"url":"assets/stateDiagram-v2-FVOUBMTO-B-UjZch3.js"},{"revision":null,"url":"assets/stateDiagram-RAJIS63D-C4EMl6jf.js"},{"revision":null,"url":"assets/src-Dw4QhedI.js"},{"revision":null,"url":"assets/sqlite-viewer-C5mviyU5.js"},{"revision":null,"url":"assets/settings-tab-BdI4HhRa.js"},{"revision":null,"url":"assets/sequenceDiagram-2WXFIKYE-DM-tMAhx.js"},{"revision":null,"url":"assets/sankeyDiagram-WA2Y5GQK-RolPi8bU.js"},{"revision":null,"url":"assets/rough.esm-nHaDi0Kw.js"},{"revision":null,"url":"assets/requirementDiagram-Z7DCOOCP-B9F_Cx_p.js"},{"revision":null,"url":"assets/react-nm2Ru1Pt.js"},{"revision":null,"url":"assets/react-dom-Bpkvzu3U.js"},{"revision":null,"url":"assets/react-SKk5z-bm.js"},{"revision":null,"url":"assets/radar-KQ55EAFF-Z-Tr5wtS.js"},{"revision":null,"url":"assets/quadrantDiagram-337W2JSQ-CdjGIDfw.js"},{"revision":null,"url":"assets/preload-helper-Bf_JiD2A.js"},{"revision":null,"url":"assets/postgres-viewer-CSynGGkJ.js"},{"revision":null,"url":"assets/pieDiagram-SKSYHLDU-At5Kz0KK.js"},{"revision":null,"url":"assets/pie-UPGHQEXC-CvXHKAzp.js"},{"revision":null,"url":"assets/path-DIKpVbHL.js"},{"revision":null,"url":"assets/packet-RMMSAZCW-IVa5F-go.js"},{"revision":null,"url":"assets/ordinal-LFEjVtwQ.js"},{"revision":null,"url":"assets/mindmap-definition-YRQLILUH-BaOBwb-W.js"},{"revision":null,"url":"assets/mermaid-parser.core-8u2leTXI.js"},{"revision":null,"url":"assets/math-y9zN1W-N.js"},{"revision":null,"url":"assets/markdown-renderer-IyEzLrC6.js"},{"revision":null,"url":"assets/linear-Bcjv9FQt.js"},{"revision":null,"url":"assets/line-B75-Rx70.js"},{"revision":null,"url":"assets/lib-BeaDXEkP.js"},{"revision":null,"url":"assets/keybindings-store--5T5hsAj.js"},{"revision":null,"url":"assets/katex-DzXRfQ_m.js"},{"revision":null,"url":"assets/kanban-definition-K7BYSVSG-h4g10UHL.js"},{"revision":null,"url":"assets/jsx-runtime-kMwlnEGE.js"},{"revision":null,"url":"assets/journeyDiagram-4ABVD52K-CgDI-UG4.js"},{"revision":null,"url":"assets/ishikawaDiagram-PHBUUO56-Cu0Rt1Ok.js"},{"revision":null,"url":"assets/isEmpty-B9L-Ge-H.js"},{"revision":null,"url":"assets/isArrayLikeObject-CGBoxvCD.js"},{"revision":null,"url":"assets/init-C0r9Gk5G.js"},{"revision":null,"url":"assets/infoDiagram-LFFYTUFH-ZZmpgc6t.js"},{"revision":null,"url":"assets/info-3K5VOQVL-BDzTLc11.js"},{"revision":null,"url":"assets/index-CcFDEPCo.css"},{"revision":null,"url":"assets/index-BmcV1di6.js"},{"revision":null,"url":"assets/graphlib-Duh_bWLa.js"},{"revision":null,"url":"assets/gitGraphDiagram-K3NZZRJ6-BeHSX7kk.js"},{"revision":null,"url":"assets/gitGraph-HDMCJU4V-CNlas3Rz.js"},{"revision":null,"url":"assets/git-graph-4eGJ8B1A.js"},{"revision":null,"url":"assets/ganttDiagram-A5KZAMGK-BdjmoMLS.js"},{"revision":null,"url":"assets/flowDiagram-PKNHOUZH-CRxlE9Sr.js"},{"revision":null,"url":"assets/extension-webview-DiVdlE2r.js"},{"revision":null,"url":"assets/erDiagram-INFDFZHY-DLeYhAAT.js"},{"revision":null,"url":"assets/dist-DRTW9IWi.js"},{"revision":null,"url":"assets/dist-DKlZwvf8.js"},{"revision":null,"url":"assets/dist-Cep75xXf.js"},{"revision":null,"url":"assets/diff-viewer-BMhCz0xk.js"},{"revision":null,"url":"assets/diagram-P4PSJMXO-hzmp0GHK.js"},{"revision":null,"url":"assets/diagram-IFDJBPK2-sqTog_XV.js"},{"revision":null,"url":"assets/diagram-E7M64L7V-DxPjK7_c.js"},{"revision":null,"url":"assets/defaultLocale-CrJzLgRD.js"},{"revision":null,"url":"assets/database-viewer-TjRo2b8_.js"},{"revision":null,"url":"assets/dagre-KLK3FWXG-C3O-MTLf.js"},{"revision":null,"url":"assets/dagre-BFcnKyBF.js"},{"revision":null,"url":"assets/cytoscape.esm-CWPXKqbJ.js"},{"revision":null,"url":"assets/csv-preview--ZSEumXf.js"},{"revision":null,"url":"assets/createLucideIcon-PuMiQgHl.js"},{"revision":null,"url":"assets/cose-bilkent-S5V4N54A-qudEiMCT.js"},{"revision":null,"url":"assets/columns-2-cEVJHYd7.js"},{"revision":null,"url":"assets/code-editor-BUc1jBqm.js"},{"revision":null,"url":"assets/clone-B2hUek6n.js"},{"revision":null,"url":"assets/classDiagram-v2-RAHNMMFH-D8IvcV_B.js"},{"revision":null,"url":"assets/classDiagram-VBA2DB6C-Dp4Kk3Yb.js"},{"revision":null,"url":"assets/chunk-YBOYWFTD-av5aeHLq.js"},{"revision":null,"url":"assets/chunk-XZSTWKYB-Cb0iqycX.js"},{"revision":null,"url":"assets/chunk-XPW4576I-BPEX8KhL.js"},{"revision":null,"url":"assets/chunk-XIRO2GV7-DRJEb7Zb.js"},{"revision":null,"url":"assets/chunk-WL4C6EOR-CXuQvlyu.js"},{"revision":null,"url":"assets/chunk-R5LLSJPH-CMY0PkRK.js"},{"revision":null,"url":"assets/chunk-QZHKN3VN-DFKFM_C1.js"},{"revision":null,"url":"assets/chunk-PU5JKC2W-C7Gry6md.js"},{"revision":null,"url":"assets/chunk-PQ6SQG4A-DX0xW7kO.js"},{"revision":null,"url":"assets/chunk-OZEHJAEY-rG0P22U9.js"},{"revision":null,"url":"assets/chunk-O4XLMI2P-BsUWb9d0.js"},{"revision":null,"url":"assets/chunk-NQ4KR5QH-DXUTQ-BL.js"},{"revision":null,"url":"assets/chunk-MX3YWQON-C2UEioMs.js"},{"revision":null,"url":"assets/chunk-L3YUKLVL-HG_eMj_C.js"},{"revision":null,"url":"assets/chunk-KYZI473N-Djw13C-3.js"},{"revision":null,"url":"assets/chunk-KX2RTZJC-DP36BDiU.js"},{"revision":null,"url":"assets/chunk-JSJVCQXG-BBmymCjA.js"},{"revision":null,"url":"assets/chunk-HHEYEP7N-BBw_z0fW.js"},{"revision":null,"url":"assets/chunk-GLR3WWYH-DBdWQ3zy.js"},{"revision":null,"url":"assets/chunk-GEFDOKGD-tDjHsAUs.js"},{"revision":null,"url":"assets/chunk-FMBD7UC4-D23YVTOU.js"},{"revision":null,"url":"assets/chunk-EGIJ26TM-Cpr87sBR.js"},{"revision":null,"url":"assets/chunk-CFjPhJqf.js"},{"revision":null,"url":"assets/chunk-C72U2L5F-CtqKiH4q.js"},{"revision":null,"url":"assets/chunk-7R4GIKGN-Dvbyu4Zw.js"},{"revision":null,"url":"assets/chunk-7E7YKBS2-CkFGv6Zs.js"},{"revision":null,"url":"assets/chunk-55IACEB6-D5cABeB9.js"},{"revision":null,"url":"assets/chunk-4BX2VUAB-C3aZvW7B.js"},{"revision":null,"url":"assets/chevron-right-5HgK6l7K.js"},{"revision":null,"url":"assets/chat-tab-il6D4jql.js"},{"revision":null,"url":"assets/channel-C2fMafck.js"},{"revision":null,"url":"assets/c4Diagram-IC4MRINW--pF1r5lr.js"},{"revision":null,"url":"assets/browser-tab-DnIsHiCc.js"},{"revision":null,"url":"assets/blockDiagram-WCTKOSBZ-h3cDF2vI.js"},{"revision":null,"url":"assets/arrow-up-BYhx9ckd.js"},{"revision":null,"url":"assets/array-DqLCdDFv.js"},{"revision":null,"url":"assets/architectureDiagram-2XIMDMQ5-DqAZP_F6.js"},{"revision":null,"url":"assets/architecture-PBZL5I3N-CFzkFKEL.js"},{"revision":null,"url":"assets/arc-B9n1Gvb5.js"},{"revision":null,"url":"assets/api-settings-Bid0NHuI.js"},{"revision":null,"url":"assets/api-client-BKIT_Qeg.js"},{"revision":null,"url":"assets/_baseUniq-Yy35llnn.js"},{"revision":null,"url":"assets/_basePickBy-3Xe18azI.js"},{"revision":"79c8870653c8f419f2e3323085e1f4be","url":"manifest.webmanifest"}]),self.addEventListener(`push`,e=>{e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{if(t.some(e=>e.visibilityState===`visible`))return;let n=e.data?.json()??{title:`PPM`,body:`Chat completed`};return self.registration.showNotification(n.title,{body:n.body,icon:`/icon-192.png`,badge:`/icon-192.png`,tag:`ppm-chat-done`,silent:!1,data:{url:self.location.origin}})}))}),self.addEventListener(`notificationclick`,e=>{e.notification.close(),e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{for(let e of t)if(e.url.includes(self.location.origin)&&`focus`in e)return e.focus();return self.clients.openWindow(e.notification.data?.url||`/`)}))});
1
+ try{self[`workbox:core:7.3.0`]&&_()}catch{}var e=(e,...t)=>{let n=e;return t.length>0&&(n+=` :: ${JSON.stringify(t)}`),n},t=class extends Error{constructor(t,n){let r=e(t,n);super(r),this.name=t,this.details=n}},n={googleAnalytics:`googleAnalytics`,precache:`precache-v2`,prefix:`workbox`,runtime:`runtime`,suffix:typeof registration<`u`?registration.scope:``},r=e=>[n.prefix,e,n.suffix].filter(e=>e&&e.length>0).join(`-`),i=e=>{for(let t of Object.keys(n))e(t)},a={updateDetails:e=>{i(t=>{typeof e[t]==`string`&&(n[t]=e[t])})},getGoogleAnalyticsName:e=>e||r(n.googleAnalytics),getPrecacheName:e=>e||r(n.precache),getPrefix:()=>n.prefix,getRuntimeName:e=>e||r(n.runtime),getSuffix:()=>n.suffix};function o(e,t){let n=t();return e.waitUntil(n),n}try{self[`workbox:precaching:7.3.0`]&&_()}catch{}var s=`__WB_REVISION__`;function c(e){if(!e)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(typeof e==`string`){let t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}let{revision:n,url:r}=e;if(!r)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(!n){let e=new URL(r,location.href);return{cacheKey:e.href,url:e.href}}let i=new URL(r,location.href),a=new URL(r,location.href);return i.searchParams.set(s,n),{cacheKey:i.href,url:a.href}}var l=class{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:n})=>{if(e.type===`install`&&t&&t.originalRequest&&t.originalRequest instanceof Request){let e=t.originalRequest.url;n?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return n}}},u=class{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:e,params:t})=>{let n=t?.cacheKey||this._precacheController.getCacheKeyForURL(e.url);return n?new Request(n,{headers:e.headers}):e},this._precacheController=e}},d;function f(){if(d===void 0){let e=new Response(``);if(`body`in e)try{new Response(e.body),d=!0}catch{d=!1}d=!1}return d}async function p(e,n){let r=null;if(e.url&&(r=new URL(e.url).origin),r!==self.location.origin)throw new t(`cross-origin-copy-response`,{origin:r});let i=e.clone(),a={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=n?n(a):a,s=f()?i.body:await i.blob();return new Response(s,o)}var m=e=>new URL(String(e),location.href).href.replace(RegExp(`^${location.origin}`),``);function h(e,t){let n=new URL(e);for(let e of t)n.searchParams.delete(e);return n.href}async function g(e,t,n,r){let i=h(t.url,n);if(t.url===i)return e.match(t,r);let a=Object.assign(Object.assign({},r),{ignoreSearch:!0}),o=await e.keys(t,a);for(let t of o)if(i===h(t.url,n))return e.match(t,r)}var v=class{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}},y=new Set;async function b(){for(let e of y)await e()}function x(e){return new Promise(t=>setTimeout(t,e))}try{self[`workbox:strategies:7.3.0`]&&_()}catch{}function S(e){return typeof e==`string`?new Request(e):e}var C=class{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new v,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(let e of this._plugins)this._pluginStateMap.set(e,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){let{event:n}=this,r=S(e);if(r.mode===`navigate`&&n instanceof FetchEvent&&n.preloadResponse){let e=await n.preloadResponse;if(e)return e}let i=this.hasCallback(`fetchDidFail`)?r.clone():null;try{for(let e of this.iterateCallbacks(`requestWillFetch`))r=await e({request:r.clone(),event:n})}catch(e){if(e instanceof Error)throw new t(`plugin-error-request-will-fetch`,{thrownErrorMessage:e.message})}let a=r.clone();try{let e;e=await fetch(r,r.mode===`navigate`?void 0:this._strategy.fetchOptions);for(let t of this.iterateCallbacks(`fetchDidSucceed`))e=await t({event:n,request:a,response:e});return e}catch(e){throw i&&await this.runCallbacks(`fetchDidFail`,{error:e,event:n,originalRequest:i.clone(),request:a.clone()}),e}}async fetchAndCachePut(e){let t=await this.fetch(e),n=t.clone();return this.waitUntil(this.cachePut(e,n)),t}async cacheMatch(e){let t=S(e),n,{cacheName:r,matchOptions:i}=this._strategy,a=await this.getCacheKey(t,`read`),o=Object.assign(Object.assign({},i),{cacheName:r});n=await caches.match(a,o);for(let e of this.iterateCallbacks(`cachedResponseWillBeUsed`))n=await e({cacheName:r,matchOptions:i,cachedResponse:n,request:a,event:this.event})||void 0;return n}async cachePut(e,n){let r=S(e);await x(0);let i=await this.getCacheKey(r,`write`);if(!n)throw new t(`cache-put-with-no-response`,{url:m(i.url)});let a=await this._ensureResponseSafeToCache(n);if(!a)return!1;let{cacheName:o,matchOptions:s}=this._strategy,c=await self.caches.open(o),l=this.hasCallback(`cacheDidUpdate`),u=l?await g(c,i.clone(),[`__WB_REVISION__`],s):null;try{await c.put(i,l?a.clone():a)}catch(e){if(e instanceof Error)throw e.name===`QuotaExceededError`&&await b(),e}for(let e of this.iterateCallbacks(`cacheDidUpdate`))await e({cacheName:o,oldResponse:u,newResponse:a.clone(),request:i,event:this.event});return!0}async getCacheKey(e,t){let n=`${e.url} | ${t}`;if(!this._cacheKeys[n]){let r=e;for(let e of this.iterateCallbacks(`cacheKeyWillBeUsed`))r=S(await e({mode:t,request:r,event:this.event,params:this.params}));this._cacheKeys[n]=r}return this._cacheKeys[n]}hasCallback(e){for(let t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(let n of this.iterateCallbacks(e))await n(t)}*iterateCallbacks(e){for(let t of this._strategy.plugins)if(typeof t[e]==`function`){let n=this._pluginStateMap.get(t);yield r=>{let i=Object.assign(Object.assign({},r),{state:n});return t[e](i)}}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){for(;this._extendLifetimePromises.length;){let e=this._extendLifetimePromises.splice(0),t=(await Promise.allSettled(e)).find(e=>e.status===`rejected`);if(t)throw t.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,n=!1;for(let e of this.iterateCallbacks(`cacheWillUpdate`))if(t=await e({request:this.request,response:t,event:this.event})||void 0,n=!0,!t)break;return n||t&&t.status!==200&&(t=void 0),t}},w=class{constructor(e={}){this.cacheName=a.getRuntimeName(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){let[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});let t=e.event,n=typeof e.request==`string`?new Request(e.request):e.request,r=`params`in e?e.params:void 0,i=new C(this,{event:t,request:n,params:r}),a=this._getResponse(i,n,t);return[a,this._awaitComplete(a,i,n,t)]}async _getResponse(e,n,r){await e.runCallbacks(`handlerWillStart`,{event:r,request:n});let i;try{if(i=await this._handle(n,e),!i||i.type===`error`)throw new t(`no-response`,{url:n.url})}catch(t){if(t instanceof Error){for(let a of e.iterateCallbacks(`handlerDidError`))if(i=await a({error:t,event:r,request:n}),i)break}if(!i)throw t}for(let t of e.iterateCallbacks(`handlerWillRespond`))i=await t({event:r,request:n,response:i});return i}async _awaitComplete(e,t,n,r){let i,a;try{i=await e}catch{}try{await t.runCallbacks(`handlerDidRespond`,{event:r,request:n,response:i}),await t.doneWaiting()}catch(e){e instanceof Error&&(a=e)}if(await t.runCallbacks(`handlerDidComplete`,{event:r,request:n,response:i,error:a}),t.destroy(),a)throw a}},T=class e extends w{constructor(t={}){t.cacheName=a.getPrecacheName(t.cacheName),super(t),this._fallbackToNetwork=t.fallbackToNetwork!==!1,this.plugins.push(e.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){return await t.cacheMatch(e)||(t.event&&t.event.type===`install`?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,n){let r,i=n.params||{};if(this._fallbackToNetwork){let t=i.integrity,a=e.integrity,o=!a||a===t;r=await n.fetch(new Request(e,{integrity:e.mode===`no-cors`?void 0:a||t})),t&&o&&e.mode!==`no-cors`&&(this._useDefaultCacheabilityPluginIfNeeded(),await n.cachePut(e,r.clone()))}else throw new t(`missing-precache-entry`,{cacheName:this.cacheName,url:e.url});return r}async _handleInstall(e,n){this._useDefaultCacheabilityPluginIfNeeded();let r=await n.fetch(e);if(!await n.cachePut(e,r.clone()))throw new t(`bad-precaching-response`,{url:e.url,status:r.status});return r}_useDefaultCacheabilityPluginIfNeeded(){let t=null,n=0;for(let[r,i]of this.plugins.entries())i!==e.copyRedirectedCacheableResponsesPlugin&&(i===e.defaultPrecacheCacheabilityPlugin&&(t=r),i.cacheWillUpdate&&n++);n===0?this.plugins.push(e.defaultPrecacheCacheabilityPlugin):n>1&&t!==null&&this.plugins.splice(t,1)}};T.defaultPrecacheCacheabilityPlugin={async cacheWillUpdate({response:e}){return!e||e.status>=400?null:e}},T.copyRedirectedCacheableResponsesPlugin={async cacheWillUpdate({response:e}){return e.redirected?await p(e):e}};var E=class{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:n=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new T({cacheName:a.getPrecacheName(e),plugins:[...t,new u({precacheController:this})],fallbackToNetwork:n}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this._strategy}precache(e){this.addToCacheList(e),this._installAndActiveListenersAdded||=(self.addEventListener(`install`,this.install),self.addEventListener(`activate`,this.activate),!0)}addToCacheList(e){let n=[];for(let r of e){typeof r==`string`?n.push(r):r&&r.revision===void 0&&n.push(r.url);let{cacheKey:e,url:i}=c(r),a=typeof r!=`string`&&r.revision?`reload`:`default`;if(this._urlsToCacheKeys.has(i)&&this._urlsToCacheKeys.get(i)!==e)throw new t(`add-to-cache-list-conflicting-entries`,{firstEntry:this._urlsToCacheKeys.get(i),secondEntry:e});if(typeof r!=`string`&&r.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==r.integrity)throw new t(`add-to-cache-list-conflicting-integrities`,{url:i});this._cacheKeysToIntegrities.set(e,r.integrity)}if(this._urlsToCacheKeys.set(i,e),this._urlsToCacheModes.set(i,a),n.length>0){let e=`Workbox is precaching URLs without revision info: ${n.join(`, `)}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(e)}}}install(e){return o(e,async()=>{let t=new l;this.strategy.plugins.push(t);for(let[t,n]of this._urlsToCacheKeys){let r=this._cacheKeysToIntegrities.get(n),i=this._urlsToCacheModes.get(t),a=new Request(t,{integrity:r,cache:i,credentials:`same-origin`});await Promise.all(this.strategy.handleAll({params:{cacheKey:n},request:a,event:e}))}let{updatedURLs:n,notUpdatedURLs:r}=t;return{updatedURLs:n,notUpdatedURLs:r}})}activate(e){return o(e,async()=>{let e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),n=new Set(this._urlsToCacheKeys.values()),r=[];for(let i of t)n.has(i.url)||(await e.delete(i),r.push(i.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let t=e instanceof Request?e.url:e,n=this.getCacheKeyForURL(t);if(n)return(await self.caches.open(this.strategy.cacheName)).match(n)}createHandlerBoundToURL(e){let n=this.getCacheKeyForURL(e);if(!n)throw new t(`non-precached-url`,{url:e});return t=>(t.request=new Request(e),t.params=Object.assign({cacheKey:n},t.params),this.strategy.handle(t))}},D,O=()=>(D||=new E,D);try{self[`workbox:routing:7.3.0`]&&_()}catch{}var k=e=>e&&typeof e==`object`?e:{handle:e},A=class{constructor(e,t,n=`GET`){this.handler=k(t),this.match=e,this.method=n}setCatchHandler(e){this.catchHandler=k(e)}},j=class extends A{constructor(e,t,n){super(({url:t})=>{let n=e.exec(t.href);if(n&&!(t.origin!==location.origin&&n.index!==0))return n.slice(1)},t,n)}},M=class{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener(`fetch`,(e=>{let{request:t}=e,n=this.handleRequest({request:t,event:e});n&&e.respondWith(n)}))}addCacheListener(){self.addEventListener(`message`,(e=>{if(e.data&&e.data.type===`CACHE_URLS`){let{payload:t}=e.data,n=Promise.all(t.urlsToCache.map(t=>{typeof t==`string`&&(t=[t]);let n=new Request(...t);return this.handleRequest({request:n,event:e})}));e.waitUntil(n),e.ports&&e.ports[0]&&n.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){let n=new URL(e.url,location.href);if(!n.protocol.startsWith(`http`))return;let r=n.origin===location.origin,{params:i,route:a}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:n}),o=a&&a.handler,s=e.method;if(!o&&this._defaultHandlerMap.has(s)&&(o=this._defaultHandlerMap.get(s)),!o)return;let c;try{c=o.handle({url:n,request:e,event:t,params:i})}catch(e){c=Promise.reject(e)}let l=a&&a.catchHandler;return c instanceof Promise&&(this._catchHandler||l)&&(c=c.catch(async r=>{if(l)try{return await l.handle({url:n,request:e,event:t,params:i})}catch(e){e instanceof Error&&(r=e)}if(this._catchHandler)return this._catchHandler.handle({url:n,request:e,event:t});throw r})),c}findMatchingRoute({url:e,sameOrigin:t,request:n,event:r}){let i=this._routes.get(n.method)||[];for(let a of i){let i,o=a.match({url:e,sameOrigin:t,request:n,event:r});if(o)return i=o,(Array.isArray(i)&&i.length===0||o.constructor===Object&&Object.keys(o).length===0||typeof o==`boolean`)&&(i=void 0),{route:a,params:i}}return{}}setDefaultHandler(e,t=`GET`){this._defaultHandlerMap.set(t,k(e))}setCatchHandler(e){this._catchHandler=k(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new t(`unregister-route-but-not-found-with-method`,{method:e.method});let n=this._routes.get(e.method).indexOf(e);if(n>-1)this._routes.get(e.method).splice(n,1);else throw new t(`unregister-route-route-not-registered`)}},N,P=()=>(N||(N=new M,N.addFetchListener(),N.addCacheListener()),N);function F(e,n,r){let i;if(typeof e==`string`){let t=new URL(e,location.href);i=new A(({url:e})=>e.href===t.href,n,r)}else if(e instanceof RegExp)i=new j(e,n,r);else if(typeof e==`function`)i=new A(e,n,r);else if(e instanceof A)i=e;else throw new t(`unsupported-route-type`,{moduleName:`workbox-routing`,funcName:`registerRoute`,paramName:`capture`});return P().registerRoute(i),i}function I(e,t=[]){for(let n of[...e.searchParams.keys()])t.some(e=>e.test(n))&&e.searchParams.delete(n);return e}function*L(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:n=`index.html`,cleanURLs:r=!0,urlManipulation:i}={}){let a=new URL(e,location.href);a.hash=``,yield a.href;let o=I(a,t);if(yield o.href,n&&o.pathname.endsWith(`/`)){let e=new URL(o.href);e.pathname+=n,yield e.href}if(r){let e=new URL(o.href);e.pathname+=`.html`,yield e.href}if(i){let e=i({url:a});for(let t of e)yield t.href}}var R=class extends A{constructor(e,t){super(({request:n})=>{let r=e.getURLsToCacheKeys();for(let i of L(n.url,t)){let t=r.get(i);if(t)return{cacheKey:t,integrity:e.getIntegrityForCacheKey(t)}}},e.strategy)}};function z(e){F(new R(O(),e))}function B(e){O().precache(e)}function V(e,t){B(e),z(t)}V([{"revision":"1872c500de691dce40960bb85481de07","url":"registerSW.js"},{"revision":"c3c62c20ca227632c659fcfddaf3386f","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"948e060affb598c339be40d69e1f6f9c","url":"monacoeditorwork/ts.worker.bundle.js"},{"revision":"a5d8a1acfc29c2a4c882a54ffc93def3","url":"monacoeditorwork/json.worker.bundle.js"},{"revision":"d0f94ce046cf8cf09605ee7664dac557","url":"monacoeditorwork/html.worker.bundle.js"},{"revision":"a424156a79b9c1b907db93aa3180585a","url":"monacoeditorwork/editor.worker.bundle.js"},{"revision":"b3a7f967560c9816492a1567b3f7f0dc","url":"monacoeditorwork/css.worker.bundle.js"},{"revision":null,"url":"assets/xychartDiagram-JWTSCODW-DylHYNtJ.js"},{"revision":null,"url":"assets/vennDiagram-LZ73GAT5-ywK7LMaH.js"},{"revision":null,"url":"assets/utils-DMiycH3O.js"},{"revision":null,"url":"assets/use-monaco-theme-B7XLw-OX.js"},{"revision":null,"url":"assets/treemap-KZPCXAKY-C9TYRE0k.js"},{"revision":null,"url":"assets/timeline-definition-YZTLITO2-A4PN_Efm.js"},{"revision":null,"url":"assets/terminal-tab-DFhB4Rxh.js"},{"revision":null,"url":"assets/terminal-tab-BrP-ENHg.css"},{"revision":null,"url":"assets/tag-CXMT0QB6.js"},{"revision":null,"url":"assets/table-DFevCOMd.js"},{"revision":null,"url":"assets/stateDiagram-v2-FVOUBMTO-B-UjZch3.js"},{"revision":null,"url":"assets/stateDiagram-RAJIS63D-C4EMl6jf.js"},{"revision":null,"url":"assets/src-Dw4QhedI.js"},{"revision":null,"url":"assets/square-oPKIkJiw.js"},{"revision":null,"url":"assets/sqlite-viewer-9AmeF-Zs.js"},{"revision":null,"url":"assets/settings-tab-DKy-YDg2.js"},{"revision":null,"url":"assets/sequenceDiagram-2WXFIKYE-DM-tMAhx.js"},{"revision":null,"url":"assets/sankeyDiagram-WA2Y5GQK-RolPi8bU.js"},{"revision":null,"url":"assets/rough.esm-nHaDi0Kw.js"},{"revision":null,"url":"assets/requirementDiagram-Z7DCOOCP-B9F_Cx_p.js"},{"revision":null,"url":"assets/react-nm2Ru1Pt.js"},{"revision":null,"url":"assets/react-dom-Bpkvzu3U.js"},{"revision":null,"url":"assets/react-SKk5z-bm.js"},{"revision":null,"url":"assets/radar-KQ55EAFF-Z-Tr5wtS.js"},{"revision":null,"url":"assets/quadrantDiagram-337W2JSQ-CdjGIDfw.js"},{"revision":null,"url":"assets/preload-helper-Bf_JiD2A.js"},{"revision":null,"url":"assets/postgres-viewer-Cr9jpBNd.js"},{"revision":null,"url":"assets/pieDiagram-SKSYHLDU-At5Kz0KK.js"},{"revision":null,"url":"assets/pie-UPGHQEXC-CvXHKAzp.js"},{"revision":null,"url":"assets/path-DIKpVbHL.js"},{"revision":null,"url":"assets/packet-RMMSAZCW-IVa5F-go.js"},{"revision":null,"url":"assets/ordinal-LFEjVtwQ.js"},{"revision":null,"url":"assets/mindmap-definition-YRQLILUH-BaOBwb-W.js"},{"revision":null,"url":"assets/mermaid-parser.core-8u2leTXI.js"},{"revision":null,"url":"assets/math-y9zN1W-N.js"},{"revision":null,"url":"assets/markdown-renderer-C7lKs47M.js"},{"revision":null,"url":"assets/linear-Bcjv9FQt.js"},{"revision":null,"url":"assets/line-B75-Rx70.js"},{"revision":null,"url":"assets/lib-BeaDXEkP.js"},{"revision":null,"url":"assets/keybindings-store-BE2T8jM9.js"},{"revision":null,"url":"assets/katex-DzXRfQ_m.js"},{"revision":null,"url":"assets/kanban-definition-K7BYSVSG-h4g10UHL.js"},{"revision":null,"url":"assets/jsx-runtime-kMwlnEGE.js"},{"revision":null,"url":"assets/journeyDiagram-4ABVD52K-CgDI-UG4.js"},{"revision":null,"url":"assets/ishikawaDiagram-PHBUUO56-Cu0Rt1Ok.js"},{"revision":null,"url":"assets/isEmpty-B9L-Ge-H.js"},{"revision":null,"url":"assets/isArrayLikeObject-CGBoxvCD.js"},{"revision":null,"url":"assets/init-C0r9Gk5G.js"},{"revision":null,"url":"assets/infoDiagram-LFFYTUFH-ZZmpgc6t.js"},{"revision":null,"url":"assets/info-3K5VOQVL-BDzTLc11.js"},{"revision":null,"url":"assets/index-DU6UVgQY.js"},{"revision":null,"url":"assets/index-C7esr4gM.css"},{"revision":null,"url":"assets/graphlib-Duh_bWLa.js"},{"revision":null,"url":"assets/gitGraphDiagram-K3NZZRJ6-BeHSX7kk.js"},{"revision":null,"url":"assets/gitGraph-HDMCJU4V-CNlas3Rz.js"},{"revision":null,"url":"assets/git-graph-BWRMlCdK.js"},{"revision":null,"url":"assets/ganttDiagram-A5KZAMGK-BdjmoMLS.js"},{"revision":null,"url":"assets/flowDiagram-PKNHOUZH-CRxlE9Sr.js"},{"revision":null,"url":"assets/extension-webview-DQWAHMlR.js"},{"revision":null,"url":"assets/erDiagram-INFDFZHY-DLeYhAAT.js"},{"revision":null,"url":"assets/dist-DRTW9IWi.js"},{"revision":null,"url":"assets/dist-DKlZwvf8.js"},{"revision":null,"url":"assets/dist-Cep75xXf.js"},{"revision":null,"url":"assets/diff-viewer-D2p3WTMS.js"},{"revision":null,"url":"assets/diagram-P4PSJMXO-hzmp0GHK.js"},{"revision":null,"url":"assets/diagram-IFDJBPK2-sqTog_XV.js"},{"revision":null,"url":"assets/diagram-E7M64L7V-DxPjK7_c.js"},{"revision":null,"url":"assets/defaultLocale-CrJzLgRD.js"},{"revision":null,"url":"assets/database-viewer-CeRUrZKj.js"},{"revision":null,"url":"assets/dagre-KLK3FWXG-C3O-MTLf.js"},{"revision":null,"url":"assets/dagre-BFcnKyBF.js"},{"revision":null,"url":"assets/cytoscape.esm-CWPXKqbJ.js"},{"revision":null,"url":"assets/csv-preview--ZSEumXf.js"},{"revision":null,"url":"assets/createLucideIcon-PuMiQgHl.js"},{"revision":null,"url":"assets/cose-bilkent-S5V4N54A-qudEiMCT.js"},{"revision":null,"url":"assets/columns-2-cEVJHYd7.js"},{"revision":null,"url":"assets/code-editor-BfMyExLp.js"},{"revision":null,"url":"assets/clone-B2hUek6n.js"},{"revision":null,"url":"assets/classDiagram-v2-RAHNMMFH-D8IvcV_B.js"},{"revision":null,"url":"assets/classDiagram-VBA2DB6C-Dp4Kk3Yb.js"},{"revision":null,"url":"assets/chunk-YBOYWFTD-av5aeHLq.js"},{"revision":null,"url":"assets/chunk-XZSTWKYB-Cb0iqycX.js"},{"revision":null,"url":"assets/chunk-XPW4576I-BPEX8KhL.js"},{"revision":null,"url":"assets/chunk-XIRO2GV7-DRJEb7Zb.js"},{"revision":null,"url":"assets/chunk-WL4C6EOR-CXuQvlyu.js"},{"revision":null,"url":"assets/chunk-R5LLSJPH-CMY0PkRK.js"},{"revision":null,"url":"assets/chunk-QZHKN3VN-DFKFM_C1.js"},{"revision":null,"url":"assets/chunk-PU5JKC2W-C7Gry6md.js"},{"revision":null,"url":"assets/chunk-PQ6SQG4A-DX0xW7kO.js"},{"revision":null,"url":"assets/chunk-OZEHJAEY-rG0P22U9.js"},{"revision":null,"url":"assets/chunk-O4XLMI2P-BsUWb9d0.js"},{"revision":null,"url":"assets/chunk-NQ4KR5QH-DXUTQ-BL.js"},{"revision":null,"url":"assets/chunk-MX3YWQON-C2UEioMs.js"},{"revision":null,"url":"assets/chunk-L3YUKLVL-HG_eMj_C.js"},{"revision":null,"url":"assets/chunk-KYZI473N-Djw13C-3.js"},{"revision":null,"url":"assets/chunk-KX2RTZJC-DP36BDiU.js"},{"revision":null,"url":"assets/chunk-JSJVCQXG-BBmymCjA.js"},{"revision":null,"url":"assets/chunk-HHEYEP7N-BBw_z0fW.js"},{"revision":null,"url":"assets/chunk-GLR3WWYH-DBdWQ3zy.js"},{"revision":null,"url":"assets/chunk-GEFDOKGD-tDjHsAUs.js"},{"revision":null,"url":"assets/chunk-FMBD7UC4-D23YVTOU.js"},{"revision":null,"url":"assets/chunk-EGIJ26TM-Cpr87sBR.js"},{"revision":null,"url":"assets/chunk-CFjPhJqf.js"},{"revision":null,"url":"assets/chunk-C72U2L5F-CtqKiH4q.js"},{"revision":null,"url":"assets/chunk-7R4GIKGN-Dvbyu4Zw.js"},{"revision":null,"url":"assets/chunk-7E7YKBS2-CkFGv6Zs.js"},{"revision":null,"url":"assets/chunk-55IACEB6-D5cABeB9.js"},{"revision":null,"url":"assets/chunk-4BX2VUAB-C3aZvW7B.js"},{"revision":null,"url":"assets/chevron-right-5HgK6l7K.js"},{"revision":null,"url":"assets/chat-tab-CrkhvVjF.js"},{"revision":null,"url":"assets/channel-C2fMafck.js"},{"revision":null,"url":"assets/c4Diagram-IC4MRINW--pF1r5lr.js"},{"revision":null,"url":"assets/browser-tab--V6I70pH.js"},{"revision":null,"url":"assets/blockDiagram-WCTKOSBZ-h3cDF2vI.js"},{"revision":null,"url":"assets/arrow-up-BYhx9ckd.js"},{"revision":null,"url":"assets/array-DqLCdDFv.js"},{"revision":null,"url":"assets/architectureDiagram-2XIMDMQ5-DqAZP_F6.js"},{"revision":null,"url":"assets/architecture-PBZL5I3N-CFzkFKEL.js"},{"revision":null,"url":"assets/arc-B9n1Gvb5.js"},{"revision":null,"url":"assets/api-settings-Bid0NHuI.js"},{"revision":null,"url":"assets/api-client-BKIT_Qeg.js"},{"revision":null,"url":"assets/_baseUniq-Yy35llnn.js"},{"revision":null,"url":"assets/_basePickBy-3Xe18azI.js"},{"revision":"79c8870653c8f419f2e3323085e1f4be","url":"manifest.webmanifest"}]),self.addEventListener(`push`,e=>{e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{if(t.some(e=>e.visibilityState===`visible`))return;let n=e.data?.json()??{title:`PPM`,body:`Chat completed`};return self.registration.showNotification(n.title,{body:n.body,icon:`/icon-192.png`,badge:`/icon-192.png`,tag:`ppm-chat-done`,silent:!1,data:{url:self.location.origin}})}))}),self.addEventListener(`notificationclick`,e=>{e.notification.close(),e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{for(let e of t)if(e.url.includes(self.location.origin)&&`focus`in e)return e.focus();return self.clients.openWindow(e.notification.data?.url||`/`)}))});
@@ -1,7 +1,7 @@
1
1
  # PPM Codebase Summary
2
2
 
3
- **Last Updated:** 2026-04-06
4
- **Version:** 0.9.10
3
+ **Last Updated:** 2026-03-26
4
+ **Version:** 0.8.60
5
5
  **Repository:** PPM (Project & Process Manager) — Multi-provider web IDE/project manager with Claude Agent SDK
6
6
 
7
7
  **Core Statistics:**
@@ -66,13 +66,6 @@ src/
66
66
  │ ├── extension-rpc.ts # RPC channel (request/response/events)
67
67
  │ ├── extension-host-worker.ts # Worker-side extension loading
68
68
  │ ├── contribution-registry.ts # Central registry for commands, views, config
69
- │ ├── clawbot/ # Telegram bot service layer
70
- │ │ ├── clawbot.service.ts # Main orchestrator (poller lifecycle, routing)
71
- │ │ ├── clawbot-telegram.ts # Telegram API (long-polling, send, edit, typing)
72
- │ │ ├── clawbot-session.ts # Session mapping (chatID → PPM sessionID)
73
- │ │ ├── clawbot-memory.ts # FTS5 memory (save, recall, decay, supersede)
74
- │ │ ├── clawbot-formatter.ts # Markdown → Telegram HTML, chunking
75
- │ │ └── clawbot-streamer.ts # ChatEvent → progressive message edits
76
69
  │ ├── database/
77
70
  │ │ ├── adapter-registry.ts # SQLite/Postgres adapter registry
78
71
  │ │ ├── sqlite-adapter.ts
@@ -276,12 +269,6 @@ src/
276
269
  - **DatabaseAdapterRegistry** — Register/retrieve DatabaseAdapter implementations (extensible pattern)
277
270
  - **SQLiteAdapter** — SQLite connection/query execution with readonly checks
278
271
  - **PostgresAdapter** — PostgreSQL connection/query execution with readonly checks
279
- - **ClawBotService** — Telegram bot orchestrator (startup, shutdown, message routing)
280
- - **ClawBotTelegramService** — Telegram API (long-polling, send, edit, typing, command handling)
281
- - **ClawBotSessionService** — chatID → PPM sessionID mapping, session state tracking
282
- - **ClawBotMemoryService** — FTS5 persistent memory (save, recall, decay, project-aware search)
283
- - **ClawBotFormatterService** — Markdown → Telegram HTML, message chunking (4096 char limit)
284
- - **ClawBotStreamerService** — ChatEvent streaming → progressive Telegram message editing
285
272
  - **Pattern:** Singleton services, dependency injection via imports, adapter registry for extensibility
286
273
 
287
274
  ### Provider Layer (src/providers/)
@@ -604,24 +591,7 @@ ppm ext dev /path/to/src # Dev symlink
604
591
 
605
592
  ---
606
593
 
607
- ## Recent Changes (v0.9.0+)
608
-
609
- ### v0.9.10 (ClawBot Telegram Integration)
610
- - **Telegram Bot Service** — Long-polling Telegram bot with message routing
611
- - Session mapping: chatID → PPM sessionID (per-user thread isolation)
612
- - Pairing system: Code-based device pairing with owner approval in web UI
613
- - Message queue: Handle concurrent Telegram messages without race conditions
614
- - **Memory System** — FTS5 persistent conversation memory
615
- - Hybrid extraction: AI extraction (primary) + regex fallback
616
- - Cross-project search: Auto-detect project name mentions → include memories
617
- - Decay/supersede: Memory relevance based on age + custom decay factors
618
- - **Response Streaming** — Progressive Telegram message editing
619
- - ChatEvent streaming with 1s throttle
620
- - Markdown → Telegram HTML formatting with chunking (4096 char limit)
621
- - **Settings & History**
622
- - Settings UI: Enable/disable, paired devices, default project, system prompt, display toggles, debounce config
623
- - Chat history: [Claw] prefix sessions with robot icon for easy identification
624
- - **Database Schema v13** — `clawbot_sessions`, `clawbot_memories` (FTS5), `clawbot_paired_chats` tables
594
+ ## Recent Changes (v0.8.60+)
625
595
 
626
596
  ### v0.9.0 (Extension System Phase 1)
627
597
  - **Extension Framework** — VSCode-compatible npm-installable extensions
@@ -6,53 +6,6 @@ All notable changes to PPM are documented here. Format follows [Keep a Changelog
6
6
 
7
7
  ---
8
8
 
9
- ## [0.9.10] — 2026-04-06
10
-
11
- ### Added
12
- - **ClawBot Telegram Integration** — Telegram bot service layer for AI-powered messaging
13
- - Telegram long-polling: receive messages via polling (no webhooks needed for self-hosted)
14
- - Session routing: chatID → PPM session mapping with per-user thread isolation
15
- - Memory system: SQLite FTS5 for persistent conversation history + recall with decay/supersede
16
- - Response streaming: ChatEvent → progressive Telegram message editing (1s throttle)
17
- - Message formatting: Markdown → Telegram HTML with 4096-character chunking
18
- - Pairing system: Code-based device pairing for security (owner approves in web UI)
19
- - Message queue: Handle concurrent Telegram messages without race conditions
20
- - Settings UI: Enable/disable, paired devices, default project, system prompt, display toggles, debounce config
21
- - Chat history: [Claw] prefix sessions with robot icon for easy identification
22
- - Cross-project memory: Auto-detect project name mentions → include that project's memories in context
23
-
24
- ### Technical Details
25
- - **Database Migration (v13):**
26
- - `clawbot_sessions` — chatID, sessionID, pairedAt, lastUsed
27
- - `clawbot_memories` — sessionID, content, role, created, decay_factor (FTS5)
28
- - `clawbot_paired_chats` — chatID, pairingCode, approvedAt, approvedBy
29
- - **Files Created:**
30
- - `src/services/clawbot/clawbot.service.ts` — Main orchestrator
31
- - `src/services/clawbot/clawbot-telegram.ts` — Telegram API polling
32
- - `src/services/clawbot/clawbot-session.ts` — Session mapping
33
- - `src/services/clawbot/clawbot-memory.ts` — FTS5 memory CRUD + hybrid extraction (AI + regex)
34
- - `src/services/clawbot/clawbot-formatter.ts` — Markdown → Telegram HTML
35
- - `src/services/clawbot/clawbot-streamer.ts` — ChatEvent → progressive message edits
36
- - `src/types/clawbot.ts` — Type definitions
37
- - `src/web/components/settings/clawbot-settings-section.tsx` — Settings UI
38
- - **Files Modified:**
39
- - `src/services/db.service.ts` — Schema v13 migration
40
- - `src/types/config.ts` — ClawBotConfig interface
41
- - `src/server/index.ts` — ClawBot poller startup
42
- - `src/server/routes/settings.ts` — ClawBot settings endpoints (GET/PUT)
43
- - `src/web/components/settings/settings-tab.tsx` — ClawBot category
44
- - `src/web/components/chat/chat-history-bar.tsx` — [Claw] prefix + icon tagging
45
-
46
- ### Key Design Principles
47
- - **Long-polling over webhooks** — Simpler self-hosted setup, no public URL required
48
- - **bypassPermissions by default** — Headless bot, no manual tool approvals needed
49
- - **Hybrid memory extraction** — AI extraction (primary) + regex fallback (fallback)
50
- - **Progressive message editing** — 1s throttle balances UX with Telegram rate limits
51
- - **Message debouncing** — 2s default, configurable per session
52
- - **Pairing-based security** — Replace allowlists with owner-approved pairing codes
53
-
54
- ---
55
-
56
9
  ## [0.9.9] — 2026-04-04
57
10
 
58
11
  ### Added
@@ -109,15 +109,14 @@ PPM is the **lightest path from phone to code** — a self-hosted, BYOK, multi-d
109
109
 
110
110
  ### v0.11.0 — "Intelligence" (Q3–Q4 2026)
111
111
 
112
- **Theme:** Event system + PPM's own AI layer + Telegram bot. Hooks → Skills API → Clawbot dependency chain.
112
+ **Theme:** Event system + PPM's own AI layer. Hooks → Skills API → Clawbot dependency chain.
113
113
 
114
- | Feature | Priority | Status | Description |
115
- |---------|----------|--------|-------------|
116
- | **Hooks system** | High | — | Event hooks for PPM lifecycle (file save, git commit, chat message, etc.). Foundation for Skills API and deeper extension integration. |
117
- | **PPM Skills API** | High | — | Stable internal API for AI to control PPM: file.read/write/search, terminal.run, git.status/commit/diff, db.query, editor.open/goto, project.switch. Skills are the bridge between AI and PPM features. |
118
- | **Telegram Bot (ClawBot)** | High | Done (v0.9.10) | Long-polling Telegram bot with session routing, FTS5 memory system, progressive message editing, pairing-based security. Routes through existing providers (Claude SDK, Cursor). No external bot infrastructure. |
119
- | **Built-in Clawbot** | High | | Lightweight AI agent built into PPM using Anthropic Messages API (not Agent SDK). Uses Skills API + MCP tools. Instant response, no external CLI deps. For quick tasks: file search, code explanation, simple refactors. |
120
- | **More providers** | Medium | — | Gemini CLI (Tier 2), OpenAI Codex (Tier 2), Tier 3 chat-only (any OpenAI-compatible API). Provider interface already clean from v0.9. |
114
+ | Feature | Priority | Description |
115
+ |---------|----------|-------------|
116
+ | **Hooks system** | High | Event hooks for PPM lifecycle (file save, git commit, chat message, etc.). Foundation for Skills API and deeper extension integration. |
117
+ | **PPM Skills API** | High | Stable internal API for AI to control PPM: file.read/write/search, terminal.run, git.status/commit/diff, db.query, editor.open/goto, project.switch. Skills are the bridge between AI and PPM features. |
118
+ | **Built-in Clawbot** | High | Lightweight AI agent built into PPM using Anthropic Messages API (not Agent SDK). Uses Skills API + MCP tools. Instant response, no external CLI deps. For quick tasks: file search, code explanation, simple refactors. |
119
+ | **More providers** | Medium | Gemini CLI (Tier 2), OpenAI Codex (Tier 2), Tier 3 chat-only (any OpenAI-compatible API). Provider interface already clean from v0.9. |
121
120
 
122
121
  **Built-in Clawbot — why it matters:**
123
122
  - Claude Agent SDK spawns subprocess — heavy, slow startup, requires CLI installed
@@ -125,12 +124,6 @@ PPM is the **lightest path from phone to code** — a self-hosted, BYOK, multi-d
125
124
  - Users can use Clawbot to create extensions → zero-friction extension authoring
126
125
  - Foundation for "AI creates extensions on demand" vision
127
126
 
128
- **Telegram Bot — why it matters:**
129
- - Extends PPM beyond the web UI → reach users on mobile, messaging apps
130
- - Long-polling avoids webhooks — simpler for self-hosted, no public URL needed
131
- - Paired devices enable secure multi-user access — owner controls who can use the bot
132
- - Persistent memory in FTS5 — bot learns conversation context over time
133
-
134
127
  ---
135
128
 
136
129
  ### v1.0.0 — "Production Ready" (Q4 2026)
@@ -193,14 +193,8 @@ Tab IDs are deterministic: `{type}:{identifier}` (e.g., `editor:src/index.ts`, `
193
193
  | **AccountService** | Account CRUD, token encryption/decryption | getAccounts, createAccount, updateAccount, deleteAccount |
194
194
  | **AccountSelectorService** | Select active account based on config | getActiveAccount, setActiveAccount, selectByProject |
195
195
  | **UpgradeService** | Version checking, installation, self-replace signaling | checkForUpdate, applyUpgrade, getInstallMethod, compareSemver |
196
- | **ClawBotService** | Telegram bot orchestrator | start, stop, handleMessage, routeToChat |
197
- | **ClawBotTelegramService** | Telegram long-polling, message ops | getUpdates, sendMessage, editMessage, setTyping, handleCommands |
198
- | **ClawBotSessionService** | chatID → PPM sessionID mapping | mapSession, getSession, deleteSession |
199
- | **ClawBotMemoryService** | FTS5 memory persistence | saveMemory, recallMemories, decayMemories, searchByProject |
200
- | **ClawBotFormatterService** | Markdown → Telegram HTML + chunking | formatMarkdown, chunkMessage |
201
- | **ClawBotStreamerService** | ChatEvent → progressive Telegram edits | streamMessageEdits |
202
196
 
203
- **Key Files:** `src/services/*.service.ts`, `src/services/clawbot/*.ts`
197
+ **Key Files:** `src/services/*.service.ts`
204
198
 
205
199
  ---
206
200
 
@@ -269,63 +263,6 @@ PPM supports multiple AI providers through a generic `AIProvider` interface and
269
263
 
270
264
  ---
271
265
 
272
- ### ClawBot Service Layer (Telegram Bot Integration)
273
- **Component:** Telegram bot service + subsidiary services
274
-
275
- **Responsibilities:**
276
- - Receive Telegram messages via long-polling (no webhooks needed)
277
- - Route Telegram user (chatID) to PPM session with pairing-based security
278
- - Persist session state + conversation memory in SQLite (FTS5)
279
- - Stream AI responses back to Telegram with progressive message editing
280
- - Format responses as Telegram HTML with proper chunking (4096 char limit)
281
-
282
- **Architecture:**
283
- ```
284
- Telegram → ClawBotTelegramService (polling) → ClawBotService (orchestrator)
285
-
286
- ClawBotSessionService (chatID→sessionID)
287
- ClawBotMemoryService (FTS5 recall)
288
- ChatService + ProviderRegistry
289
- ClawBotStreamerService (ChatEvent→edits)
290
- ClawBotFormatterService (Markdown→HTML)
291
- ```
292
-
293
- **Services (src/services/clawbot/):**
294
- - **ClawBotService** — Lifecycle management (start/stop), message queue, routing logic
295
- - **ClawBotTelegramService** — Telegram Bot API wrapper (getUpdates long-polling, sendMessage, editMessage, setTyping, command handlers)
296
- - **ClawBotSessionService** — chatID ↔ PPM sessionID bidirectional mapping, session state tracking
297
- - **ClawBotMemoryService** — FTS5 persistent memory (save, recall with relevance, decay factor, supersede logic, cross-project search by name mention)
298
- - **ClawBotFormatterService** — Markdown → Telegram HTML conversion, message chunking (respects 4096 char limit), code block formatting
299
- - **ClawBotStreamerService** — ChatEvent async generator → progressive Telegram message edits (1s throttle for rate limiting)
300
-
301
- **Security Model:**
302
- - **Pairing System** — Replace allowlists with code-based pairing: User requests pairing → receives code → owner approves in web UI → chatID registered in `clawbot_paired_chats`
303
- - **Per-User Sessions** — Each Telegram chatID maps to isolated PPM session (no cross-user interference)
304
- - **bypassPermissions** — ClawBot bot runs headless, auto-approves tools (no manual approval flow)
305
-
306
- **Database Schema (v13):**
307
- - `clawbot_sessions` — chatID (PK), sessionID (FK chat_sessions), pairedAt, lastUsed
308
- - `clawbot_memories` — id (PK), sessionID (FK), content (text), role (user|assistant), created, decay_factor (FTS5 full-text index)
309
- - `clawbot_paired_chats` — chatID (PK), pairingCode (unique, 6 chars), approvedAt, approvedBy (user ID)
310
-
311
- **Key Design Decisions:**
312
- 1. **Long-polling** — No webhooks = no public URL required, simpler for self-hosted
313
- 2. **Message queue** — Concurrent Telegram messages queued FIFO, prevents race conditions
314
- 3. **Progressive edits** — Edit same message for long responses, reduce Telegram API calls, better UX
315
- 4. **Memory system** — Hybrid extraction (AI primary + regex fallback), supports cross-project search by project name mention
316
- 5. **Config reuse** — Shares existing Telegram bot_token with notifications, separate ClawBotConfig section
317
- 6. **Session tagging** — [Claw] prefix visible in web UI without schema changes, robot icon for identification
318
-
319
- **Settings Endpoints:**
320
- - `GET /api/settings/clawbot` — Fetch config (enabled, bot token, default project, system prompt, debounce, display toggles)
321
- - `PUT /api/settings/clawbot` — Update config
322
- - `GET /api/clawbot/paired-chats` — List paired Telegram chatIDs
323
- - `POST /api/clawbot/pairing` — Request pairing code (returns code)
324
- - `POST /api/clawbot/pairing/:code/approve` — Approve pairing code (owner only)
325
- - `DELETE /api/clawbot/paired-chats/:chatId` — Revoke pairing
326
-
327
- ---
328
-
329
266
  ### Data Access Layer (SQLite + Filesystem + Git)
330
267
  **Components:** SQLite via bun:sqlite, direct filesystem access, simple-git wrapper
331
268
 
@@ -337,7 +274,7 @@ Telegram → ClawBotTelegramService (polling) → ClawBotService (orchestrator)
337
274
  - Enforce security (no parent directory access)
338
275
 
339
276
  **Key Patterns:**
340
- - SQLite: WAL mode, foreign keys, lazy init, schema v13 (13 tables: config, connections, accounts, usage_history, session_logs, push_subscriptions, session_map, table_metadata, workspace_state, extension_storage, mcp_servers, clawbot_sessions, clawbot_memories, clawbot_paired_chats)
277
+ - SQLite: WAL mode, foreign keys, lazy init, schema v10 (10 tables: config, connections, accounts, usage_history, session_logs, push_subscriptions, session_map, table_metadata, session_logs, workspace_state)
341
278
  - Path validation: `projectPath/relativePath` only, reject `..`
342
279
  - Caching: Directory trees cached with TTL
343
280
  - Error handling: Descriptive messages (file not found, permission denied)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hienlh/ppm",
3
- "version": "0.9.39",
3
+ "version": "0.9.41",
4
4
  "description": "Personal Project Manager — mobile-first web IDE with AI assistance",
5
5
  "author": "hienlh",
6
6
  "license": "MIT",
@@ -477,12 +477,5 @@ if (process.argv.includes("__serve__")) {
477
477
  console.error("[ExtService] Startup error:", e);
478
478
  });
479
479
 
480
- // Start PPMBot Telegram poller (if enabled)
481
- import("../services/ppmbot/ppmbot-service.ts")
482
- .then(({ ppmbotService }) => ppmbotService.start())
483
- .catch((e) => {
484
- console.error("[ppmbot] Startup error:", e);
485
- });
486
-
487
480
  console.log(`Server child ready on port ${port}`);
488
481
  }
@@ -1,14 +1,12 @@
1
1
  import { Hono } from "hono";
2
2
  import { configService } from "../../services/config.service.ts";
3
- import { getConfigValue, setConfigValue, listPairedChats, getPairingByCode, approvePairing, revokePairing } from "../../services/db.service.ts";
3
+ import { getConfigValue, setConfigValue } from "../../services/db.service.ts";
4
4
  import {
5
5
  validateAIProviderConfig,
6
6
  validateDefaultProvider,
7
7
  VALID_PROVIDERS,
8
- DEFAULT_CONFIG,
9
8
  type AIProviderConfig,
10
9
  type TelegramConfig,
11
- type PPMBotConfig,
12
10
  type ThemeConfig,
13
11
  } from "../../types/config.ts";
14
12
  import { ok, err } from "../../types/api.ts";
@@ -318,72 +316,3 @@ settingsRoutes.put("/proxy", async (c) => {
318
316
  return c.json(err((e as Error).message), 400);
319
317
  }
320
318
  });
321
-
322
- // ── PPMBot ─────────────────────────────────────────────────────
323
-
324
- /** GET /settings/clawbot — return current clawbot config */
325
- settingsRoutes.get("/clawbot", (c) => {
326
- const config = configService.get("clawbot") as PPMBotConfig | undefined;
327
- if (!config) return c.json(ok(DEFAULT_CONFIG.clawbot));
328
- return c.json(ok(config));
329
- });
330
-
331
- /** PUT /settings/clawbot — update clawbot config */
332
- settingsRoutes.put("/clawbot", async (c) => {
333
- try {
334
- const body = await c.req.json<Partial<PPMBotConfig>>();
335
- const current = (configService.get("clawbot") as PPMBotConfig | undefined)
336
- ?? structuredClone(DEFAULT_CONFIG.clawbot!);
337
- const updated: PPMBotConfig = { ...current, ...body };
338
-
339
- if (updated.debounce_ms < 0 || updated.debounce_ms > 30000) {
340
- return c.json(err("debounce_ms must be 0-30000"), 400);
341
- }
342
-
343
- configService.set("clawbot", updated);
344
- configService.save();
345
-
346
- // Restart clawbot if running state changed
347
- try {
348
- const { ppmbotService } = await import("../../services/ppmbot/ppmbot-service.ts");
349
- if (updated.enabled && !ppmbotService.isRunning) {
350
- await ppmbotService.start();
351
- } else if (!updated.enabled && ppmbotService.isRunning) {
352
- ppmbotService.stop();
353
- }
354
- } catch { /* PPMBot module not loaded yet — OK */ }
355
-
356
- return c.json(ok(updated));
357
- } catch (e) {
358
- return c.json(err((e as Error).message), 400);
359
- }
360
- });
361
-
362
- /** GET /settings/clawbot/paired — list paired devices */
363
- settingsRoutes.get("/clawbot/paired", (c) => {
364
- return c.json(ok(listPairedChats()));
365
- });
366
-
367
- /** POST /settings/clawbot/paired/approve — approve pairing by code */
368
- settingsRoutes.post("/clawbot/paired/approve", async (c) => {
369
- try {
370
- const { code } = await c.req.json<{ code: string }>();
371
- const pairing = getPairingByCode(code);
372
- if (!pairing) return c.json(err("Invalid pairing code"), 404);
373
- approvePairing(pairing.telegram_chat_id);
374
- // Notify user on Telegram
375
- try {
376
- const { ppmbotService } = await import("../../services/ppmbot/ppmbot-service.ts");
377
- await ppmbotService.notifyPairingApproved(pairing.telegram_chat_id);
378
- } catch { /* OK */ }
379
- return c.json(ok({ approved: pairing.telegram_chat_id }));
380
- } catch (e) {
381
- return c.json(err((e as Error).message), 400);
382
- }
383
- });
384
-
385
- /** DELETE /settings/clawbot/paired/:chatId — revoke pairing */
386
- settingsRoutes.delete("/clawbot/paired/:chatId", (c) => {
387
- revokePairing(c.req.param("chatId"));
388
- return c.json(ok({ revoked: true }));
389
- });
@@ -19,7 +19,7 @@ const PPM_DIR = resolve(homedir(), ".ppm");
19
19
 
20
20
  /** Top-level config keys stored in the config table (not projects) */
21
21
  const CONFIG_TABLE_KEYS: (keyof PpmConfig)[] = [
22
- "device_name", "port", "host", "theme", "auth", "ai", "push", "telegram", "clawbot",
22
+ "device_name", "port", "host", "theme", "auth", "ai", "push", "telegram",
23
23
  ];
24
24
 
25
25
  class ConfigService {