@popmelt.com/core 0.18.0 → 0.18.1
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.
- package/README.md +65 -0
- package/codex-plugin/.codex-plugin/plugin.json +1 -1
- package/dist/adapter-react-three-fiber.d.mts +36 -0
- package/dist/adapter-react-three-fiber.mjs +2 -0
- package/dist/adapter-three.d.mts +2 -0
- package/dist/adapter-three.mjs +1 -1
- package/dist/bridge-entry.mjs +1 -1
- package/dist/chunk-J3UIBBU2.mjs +2 -0
- package/dist/{chunk-GQOEG3E5.mjs → chunk-P2Y3NJ7A.mjs} +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +9 -6
- package/dist/index.mjs +40 -40
- package/dist/media.d.mts +3 -2
- package/dist/plugin-astro.mjs +1 -1
- package/dist/plugin-vite.mjs +1 -1
- package/dist/{server-MUVH7FWR.mjs → server-VX66FOQO.mjs} +1 -1
- package/dist/server.mjs +1 -1
- package/dist/{video-adapter-DzNtG67g.d.mts → subjects-CeqtFged.d.mts} +1 -337
- package/dist/video-adapter-Gq8saCDn.d.mts +339 -0
- package/package.json +14 -4
package/README.md
CHANGED
|
@@ -243,6 +243,71 @@ If the toolbar opens but cannot reach an AI, check that:
|
|
|
243
243
|
- The framework plugin or config wrapper is installed
|
|
244
244
|
- The development server was restarted after installation
|
|
245
245
|
|
|
246
|
+
## React Three Fiber
|
|
247
|
+
|
|
248
|
+
Popmelt can target objects rendered by React Three Fiber while Core continues to
|
|
249
|
+
own the familiar hover, annotation, and thread presentation. Install the R3F
|
|
250
|
+
peers alongside Core:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
npm install @popmelt.com/core @react-three/fiber three
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Keep `PopmeltProvider` outside the canvas and mount `PopmeltThreeAdapter` inside
|
|
257
|
+
it. The adapter renders nothing; it registers the current scene, camera, and
|
|
258
|
+
renderer with the nearest provider.
|
|
259
|
+
|
|
260
|
+
```tsx
|
|
261
|
+
import { Canvas } from '@react-three/fiber';
|
|
262
|
+
import { PopmeltProvider } from '@popmelt.com/core';
|
|
263
|
+
import { PopmeltThreeAdapter } from '@popmelt.com/core/react-three-fiber';
|
|
264
|
+
|
|
265
|
+
export function ProductPreview() {
|
|
266
|
+
return (
|
|
267
|
+
<PopmeltProvider>
|
|
268
|
+
<Canvas>
|
|
269
|
+
<PopmeltThreeAdapter />
|
|
270
|
+
<ProductScene />
|
|
271
|
+
</Canvas>
|
|
272
|
+
</PopmeltProvider>
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Named objects receive readable labels automatically. Add stable application
|
|
278
|
+
identity when object names or scene ordering may change:
|
|
279
|
+
|
|
280
|
+
```tsx
|
|
281
|
+
<mesh
|
|
282
|
+
name="Bottle"
|
|
283
|
+
userData={{ popmeltId: 'hero-bottle', popmeltLabel: 'Hero bottle' }}
|
|
284
|
+
>
|
|
285
|
+
{/* geometry and material */}
|
|
286
|
+
</mesh>
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Use a distinct `id` for each adapter when a page owns multiple canvases. Pass
|
|
290
|
+
`root` to target only one scene subtree, `captureFrame={false}` to omit renderer
|
|
291
|
+
screenshots, or `select`, `key`, `label`, and `describe` to adapt an application's
|
|
292
|
+
scene semantics.
|
|
293
|
+
|
|
294
|
+
For integrations that need the registered adapter instance, use the hook inside
|
|
295
|
+
the canvas instead of the component:
|
|
296
|
+
|
|
297
|
+
```tsx
|
|
298
|
+
import { usePopmeltThreeAdapter } from '@popmelt.com/core/react-three-fiber';
|
|
299
|
+
|
|
300
|
+
function ProductPopmeltAdapter() {
|
|
301
|
+
const adapter = usePopmeltThreeAdapter({ id: 'product-preview' });
|
|
302
|
+
// Optional application-specific coordination with adapter.
|
|
303
|
+
void adapter;
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
The component is the recommended default; the hook exposes the same lifecycle
|
|
309
|
+
for advanced composition.
|
|
310
|
+
|
|
246
311
|
## Media annotation
|
|
247
312
|
|
|
248
313
|
Manual image and video annotation are enabled by default in `PopmeltProvider`,
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Camera, Object3D, Raycaster } from 'three';
|
|
2
|
+
import { P as PopmeltSubjectAdapter } from './subjects-CeqtFged.mjs';
|
|
3
|
+
|
|
4
|
+
type ThreeSubjectAdapterOptions = {
|
|
5
|
+
/** Unique adapter identity when a page owns more than one Three.js surface. */
|
|
6
|
+
id?: string;
|
|
7
|
+
canvas: HTMLCanvasElement;
|
|
8
|
+
camera: Camera;
|
|
9
|
+
scene: Object3D;
|
|
10
|
+
raycaster?: Raycaster;
|
|
11
|
+
/** Select a meaningful parent from the raw intersected render object. */
|
|
12
|
+
select?: (object: Object3D) => Object3D | null;
|
|
13
|
+
/** Override durable identity. Explicit application IDs are preferred. */
|
|
14
|
+
key?: (object: Object3D) => string | null | undefined;
|
|
15
|
+
label?: (object: Object3D) => string;
|
|
16
|
+
/** Add application-specific provenance or source hints to the model context. */
|
|
17
|
+
describe?: (object: Object3D) => Record<string, string | undefined>;
|
|
18
|
+
/** Capture the renderer's current frame for DOM-cloned screenshots. */
|
|
19
|
+
captureFrame?: () => string | null | Promise<string | null>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type PopmeltThreeAdapterOptions = Omit<ThreeSubjectAdapterOptions, 'canvas' | 'camera' | 'scene' | 'captureFrame'> & {
|
|
23
|
+
/** Scene root Popmelt should target. Defaults to the complete R3F scene. */
|
|
24
|
+
root?: Object3D;
|
|
25
|
+
/** Override frame capture, or set false to omit renderer screenshots. */
|
|
26
|
+
captureFrame?: ThreeSubjectAdapterOptions['captureFrame'] | false;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Register an R3F-owned Three.js scene with the nearest PopmeltProvider.
|
|
30
|
+
* Must run inside Canvas so React Three Fiber's renderer state is available.
|
|
31
|
+
*/
|
|
32
|
+
declare function usePopmeltThreeAdapter(options?: PopmeltThreeAdapterOptions): PopmeltSubjectAdapter;
|
|
33
|
+
/** Declarative, zero-render integration for use inside an R3F Canvas. */
|
|
34
|
+
declare function PopmeltThreeAdapter(props: PopmeltThreeAdapterOptions): null;
|
|
35
|
+
|
|
36
|
+
export { PopmeltThreeAdapter, type PopmeltThreeAdapterOptions, usePopmeltThreeAdapter };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";import{b as g}from"./chunk-J3UIBBU2.mjs";import{useThree as M}from"@react-three/fiber";import{useEffect as C,useMemo as E}from"react";import{Box3 as x,Raycaster as D,Vector2 as T,Vector3 as P}from"three";function h(e){return[e.x,e.y,e.z].map(r=>Number(r.toFixed(4))).join(", ")}function y(e){let r=e.material;return r&&(Array.isArray(r)?r:[r]).map(n=>n.name?.trim()||n.type).filter(Boolean).join(", ")||void 0}function j(e,r,s){let c=e.domElement.width,n=e.domElement.height;if(c<=0||n<=0)return null;let l=e.getRenderTarget(),t=e.xr.enabled;try{return e.xr.enabled=!1,e.setRenderTarget(null),e.render(r,s),e.domElement.toDataURL("image/png")}catch{return null}finally{e.setRenderTarget(l),e.xr.enabled=t}}function A(e){let c=(e.parent?.children??[]).filter(t=>t.type===e.type&&t.name===e.name),n=Math.max(0,c.indexOf(e)),l=e.name.trim()||e.type;return`${encodeURIComponent(l)}[${n}]`}function f(e){let r=[],s=e;for(;s?.parent;)r.push(A(s)),s=s.parent;return r.reverse().join("/")}function v(e){let r=e.userData.popmeltId;return typeof r=="string"&&r.trim()?r.trim():f(e)}function S(e){let r=e.userData.popmeltLabel;return typeof r=="string"&&r.trim()?r.trim():e.name.trim()||e.type}function F(e){let r=e;for(;r.parent;){let s=r.name.trim(),c=s.match(/^(.*)_(?:alpha|normal|material)(?:_\d+)+$/i),n=r.parent;for(;n.parent&&!n.name.trim();)n=n.parent;let l=n.name.trim();if(!l||!s.startsWith(`${l}_`)&&!c)break;r=n}return r}function R(e,r,s){let c=new x().setFromObject(e,!0);if(c.isEmpty())return null;let n=s.getBoundingClientRect(),l=[];for(let p of[c.min.x,c.max.x])for(let d of[c.min.y,c.max.y])for(let b of[c.min.z,c.max.z])l.push(new P(p,d,b).project(r));let t=l.map(p=>n.left+(p.x+1)/2*n.width),a=l.map(p=>n.top+(1-p.y)/2*n.height),i=Math.max(n.left,Math.min(...t)),o=Math.min(n.right,Math.max(...t)),m=Math.max(n.top,Math.min(...a)),u=Math.min(n.bottom,Math.max(...a));return o<=i||u<=m?null:{left:i,top:m,width:o-i,height:u-m}}function O(e){let r=e.raycaster??new D,s=new T,c=t=>e.key?.(t)??v(t),n=(t,a)=>{let i=c(t);if(!i)return null;let o=R(t,e.camera,e.canvas);return o?{key:i,label:e.label?.(t)??S(t),sourceElement:e.canvas,bounds:o,context:f(t),data:{renderer:"Three.js",objectType:t.type,...t.name?{objectName:t.name}:{},scenePath:f(t),...a&&a!==t?{renderObjectName:a.name.trim()||a.type,renderObjectType:a.type,renderScenePath:f(a)}:{},position:h(t.position),rotation:h(t.rotation),scale:h(t.scale),visible:String(t.visible),...y(t)?{material:y(t)}:{},...Object.fromEntries(Object.entries(e.describe?.(t)??{}).filter(m=>typeof m[1]=="string"&&m[1].length>0))}}:null},l=t=>{let a=null;return e.scene.traverse(i=>{!a&&c(i)===t&&(a=i)}),a};return{id:e.id?.trim()||"three",hitTest({x:t,y:a}){let i=e.canvas.getBoundingClientRect();if(t<i.left||t>i.right||a<i.top||a>i.bottom)return null;s.set((t-i.left)/i.width*2-1,-((a-i.top)/i.height)*2+1),r.setFromCamera(s,e.camera);for(let o of r.intersectObject(e.scene,!0)){let m=e.select?e.select(o.object):F(o.object);if(!m||!m.visible)continue;let u=n(m,o.object);if(u)return u}return null},resolve(t){let a=l(t);return a?n(a):null},...e.captureFrame?{async captureSurfaces(){let t=await e.captureFrame?.();return t?[{element:e.canvas,dataUrl:t}]:[]}}:{}}}function w(e={}){let{registerSubjectAdapter:r}=g(),{gl:s,camera:c,scene:n}=M(),{root:l=n,captureFrame:t,describe:a,id:i,key:o,label:m,raycaster:u,select:p}=e,d=E(()=>O({id:i,canvas:s.domElement,camera:c,scene:l,raycaster:u,select:p,key:o,label:m,describe:b=>({framework:"@react-three/fiber",...a?.(b)}),...t===!1?{}:{captureFrame:t??(()=>j(s,n,c))}}),[c,t,a,s,i,o,m,u,l,n,p]);return C(()=>r(d),[d,r]),d}function U(e){return w(e),null}export{U as PopmeltThreeAdapter,w as usePopmeltThreeAdapter};
|
package/dist/adapter-three.d.mts
CHANGED
|
@@ -152,6 +152,8 @@ type PopmeltSubjectAdapter = {
|
|
|
152
152
|
};
|
|
153
153
|
|
|
154
154
|
type ThreeSubjectAdapterOptions = {
|
|
155
|
+
/** Unique adapter identity when a page owns more than one Three.js surface. */
|
|
156
|
+
id?: string;
|
|
155
157
|
canvas: HTMLCanvasElement;
|
|
156
158
|
camera: Camera;
|
|
157
159
|
scene: Object3D;
|
package/dist/adapter-three.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Box3 as y,Raycaster as x,Vector2 as O,Vector3 as j}from"three";function b(e){return[e.x,e.y,e.z].map(n=>Number(n.toFixed(4))).join(", ")}function
|
|
1
|
+
import{Box3 as y,Raycaster as x,Vector2 as O,Vector3 as j}from"three";function b(e){return[e.x,e.y,e.z].map(n=>Number(n.toFixed(4))).join(", ")}function d(e){let n=e.material;return n&&(Array.isArray(n)?n:[n]).map(r=>r.name?.trim()||r.type).filter(Boolean).join(", ")||void 0}function M(e,n,c){let s=e.domElement.width,r=e.domElement.height;if(s<=0||r<=0)return null;let l=e.getRenderTarget(),t=e.xr.enabled;try{return e.xr.enabled=!1,e.setRenderTarget(null),e.render(n,c),e.domElement.toDataURL("image/png")}catch{return null}finally{e.setRenderTarget(l),e.xr.enabled=t}}function D(e){let s=(e.parent?.children??[]).filter(t=>t.type===e.type&&t.name===e.name),r=Math.max(0,s.indexOf(e)),l=e.name.trim()||e.type;return`${encodeURIComponent(l)}[${r}]`}function f(e){let n=[],c=e;for(;c?.parent;)n.push(D(c)),c=c.parent;return n.reverse().join("/")}function v(e){let n=e.userData.popmeltId;return typeof n=="string"&&n.trim()?n.trim():f(e)}function T(e){let n=e.userData.popmeltLabel;return typeof n=="string"&&n.trim()?n.trim():e.name.trim()||e.type}function R(e){let n=e;for(;n.parent;){let c=n.name.trim(),s=c.match(/^(.*)_(?:alpha|normal|material)(?:_\d+)+$/i),r=n.parent;for(;r.parent&&!r.name.trim();)r=r.parent;let l=r.name.trim();if(!l||!c.startsWith(`${l}_`)&&!s)break;n=r}return n}function P(e,n,c){let s=new y().setFromObject(e,!0);if(s.isEmpty())return null;let r=c.getBoundingClientRect(),l=[];for(let p of[s.min.x,s.max.x])for(let h of[s.min.y,s.max.y])for(let g of[s.min.z,s.max.z])l.push(new j(p,h,g).project(n));let t=l.map(p=>r.left+(p.x+1)/2*r.width),a=l.map(p=>r.top+(1-p.y)/2*r.height),i=Math.max(r.left,Math.min(...t)),u=Math.min(r.right,Math.max(...t)),m=Math.max(r.top,Math.min(...a)),o=Math.min(r.bottom,Math.max(...a));return u<=i||o<=m?null:{left:i,top:m,width:u-i,height:o-m}}function C(e){let n=e.raycaster??new x,c=new O,s=t=>e.key?.(t)??v(t),r=(t,a)=>{let i=s(t);if(!i)return null;let u=P(t,e.camera,e.canvas);return u?{key:i,label:e.label?.(t)??T(t),sourceElement:e.canvas,bounds:u,context:f(t),data:{renderer:"Three.js",objectType:t.type,...t.name?{objectName:t.name}:{},scenePath:f(t),...a&&a!==t?{renderObjectName:a.name.trim()||a.type,renderObjectType:a.type,renderScenePath:f(a)}:{},position:b(t.position),rotation:b(t.rotation),scale:b(t.scale),visible:String(t.visible),...d(t)?{material:d(t)}:{},...Object.fromEntries(Object.entries(e.describe?.(t)??{}).filter(m=>typeof m[1]=="string"&&m[1].length>0))}}:null},l=t=>{let a=null;return e.scene.traverse(i=>{!a&&s(i)===t&&(a=i)}),a};return{id:e.id?.trim()||"three",hitTest({x:t,y:a}){let i=e.canvas.getBoundingClientRect();if(t<i.left||t>i.right||a<i.top||a>i.bottom)return null;c.set((t-i.left)/i.width*2-1,-((a-i.top)/i.height)*2+1),n.setFromCamera(c,e.camera);for(let u of n.intersectObject(e.scene,!0)){let m=e.select?e.select(u.object):R(u.object);if(!m||!m.visible)continue;let o=r(m,u.object);if(o)return o}return null},resolve(t){let a=l(t);return a?r(a):null},...e.captureFrame?{async captureSurfaces(){let t=await e.captureFrame?.();return t?[{element:e.canvas,dataUrl:t}]:[]}}:{}}}export{M as captureThreeRendererFrame,C as createThreeSubjectAdapter,R as selectThreeNamedPrimitiveParent,f as threeObjectPath};
|
package/dist/bridge-entry.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import{mkdir as su,unlink as au,writeFile as du}from"fs/promises";import{join as
|
|
|
3
3
|
`);let o=new Map;for(let r of e){let i=r.pathname||"(unknown)",s=o.get(i)??[];s.push(r.instruction),o.set(i,s)}return[...o.entries()].map(([r,i])=>`\`${r}\`
|
|
4
4
|
${i.map(s=>`- ${s}`).join(`
|
|
5
5
|
`)}`).join(`
|
|
6
|
-
`)}var ri={name:"@popmelt.com/core",version:"0.18.
|
|
6
|
+
`)}var ri={name:"@popmelt.com/core",version:"0.18.1",description:"A tiny, powerful design layer for humans and AI",license:"PolyForm-Shield-1.0.0",author:"Popmelt <reb@popmelt.com> (https://popmelt.com)",homepage:"https://github.com/popmelt/core",bugs:{url:"https://github.com/popmelt/core/issues"},repository:{type:"git",url:"git+https://github.com/popmelt/core.git"},keywords:["ai","annotations","collaboration","devtools","react","visual-feedback"],bin:{popmelt:"dist/cli.mjs"},sideEffects:!1,packageManager:"pnpm@9.12.0",engines:{node:">=18.0.0"},scripts:{build:"tsup",clean:"git clean -xdf .turbo node_modules dist",format:'prettier --check "**/*.{ts,tsx}"',lint:"eslint . && node scripts/check-eslint-suppressions.mjs","pack:smoke":"node scripts/pack-smoke.mjs","peer:smoke":"node scripts/peer-compat-smoke.mjs",prepublishOnly:"pnpm run verify",test:"vitest run","test:coverage":"vitest run --coverage.enabled","test:watch":"vitest",typecheck:"tsc --noEmit --incremental false",verify:"pnpm run typecheck && pnpm run format && pnpm run lint && pnpm run test:coverage && pnpm run build && pnpm run pack:smoke"},exports:{".":{types:"./dist/index.d.mts",import:"./dist/index.mjs"},"./provider":{types:"./dist/index.d.mts",import:"./dist/index.mjs"},"./server":{types:"./dist/server.d.mts",import:"./dist/server.mjs"},"./memory":{types:"./dist/memory.d.mts",import:"./dist/memory.mjs"},"./memory/legacy":{types:"./dist/memory-legacy.d.mts",import:"./dist/memory-legacy.mjs"},"./memory/store":{types:"./dist/memory-store.d.mts",import:"./dist/memory-store.mjs"},"./next":{types:"./dist/plugin-next.d.mts",import:"./dist/plugin-next.mjs",default:"./dist/plugin-next.mjs"},"./vite":{types:"./dist/plugin-vite.d.mts",import:"./dist/plugin-vite.mjs",default:"./dist/plugin-vite.mjs"},"./astro":{types:"./dist/plugin-astro.d.mts",import:"./dist/plugin-astro.mjs",default:"./dist/plugin-astro.mjs"},"./media":{types:"./dist/media.d.mts",import:"./dist/media.mjs"},"./three":{types:"./dist/adapter-three.d.mts",import:"./dist/adapter-three.mjs"},"./react-three-fiber":{types:"./dist/adapter-react-three-fiber.d.mts",import:"./dist/adapter-react-three-fiber.mjs"}},files:["dist","codex-plugin","LICENSE","README.md","THIRD_PARTY_NOTICES.md"],dependencies:{"modern-screenshot":"4.7.0",ws:"8.21.3"},peerDependencies:{"lucide-react":">=1.23.0",next:">=14.0.0",react:">=18.0.0","react-dom":">=18.0.0","@react-three/fiber":">=8.0.0",three:">=0.150.0"},peerDependenciesMeta:{next:{optional:!0},"@react-three/fiber":{optional:!0},three:{optional:!0}},devDependencies:{"@kit/eslint-config":"workspace:^","@react-three/fiber":"9.7.0","@tanstack/react-router":"1.120.5","@types/react":"19.1.0","@types/react-dom":"19.1.2","@types/three":"^0.185.4","@types/ws":"8.18.1","@vitejs/plugin-react":"^5.1.1","@vitest/coverage-v8":"3.2.4",eslint:"^9.24.0",geist:"1.7.2",jsdom:"26.1.0","lucide-react":"^1.23.0",next:"15.5.24",prettier:"3.5.3",react:"19.1.0","react-dom":"19.1.0",three:"^0.185.1",tsup:"^8.5.1",typescript:"^5.8.3",vite:"7.3.6",vitest:"3.2.4"}};var yn=ri.version;var Da=new Set(["minimal","low","medium","high","xhigh","max","ultra"]),Na=new Set(["default","flex","fast","priority"]);function Ut(n){let e=typeof n.reasoningEffort=="string"&&Da.has(n.reasoningEffort)?n.reasoningEffort:void 0,t=typeof n.serviceTier=="string"&&Na.has(n.serviceTier)?n.serviceTier:void 0;return e||t?{reasoningEffort:e,serviceTier:t}:void 0}function eo(n){let e=[];return n?.reasoningEffort&&e.push(`model_reasoning_effort=${JSON.stringify(n.reasoningEffort)}`),n?.serviceTier&&e.push(`service_tier=${JSON.stringify(n.serviceTier)}`),e}import{createHash as ec,randomUUID as tc}from"crypto";import{isAbsolute as nc,relative as oc}from"path";import{randomUUID as ft}from"crypto";var ii={project:0,route:1,component:2,selector:2,file:3},si={intention:0,principle:1,pattern:2};function zt(n,e){let t=ii[n.scope.kind]-ii[e.scope.kind];return t!==0?t:si[n.kind]-si[e.kind]}function ai(n){return n==="expresses"||n==="excepts"}function Ba(n,e){if(e.type==="expresses"||e.type==="excepts")return structuredClone(e);if(e.type==="operationalizes")return{...structuredClone(e),type:"expresses"};if(e.type==="instantiates")return{...structuredClone(e),type:"expresses",fromId:e.toId,toId:e.fromId};if(e.type==="refines"){let t=n.explanations.find(i=>i.id===e.fromId),o=n.explanations.find(i=>i.id===e.toId);if(!t||!o)return;let r=zt(t,o);return r===0?void 0:r>0?{...structuredClone(e),type:"expresses"}:{...structuredClone(e),type:"expresses",fromId:e.toId,toId:e.fromId}}}function Mt(n){let e=structuredClone(n),t=new Set;return e.relations=n.relations.flatMap(o=>{let r=Ba(n,o);if(!r)return[];let i=`${r.type}:${r.fromId}:${r.toStoreId??""}:${r.toId}`;return t.has(i)?[]:(t.add(i),[r])}),e}import{createHash as Ja}from"crypto";import{mkdir as qa,open as La,readFile as di,readdir as Fa,rename as Ua,unlink as za}from"fs/promises";import{dirname as Wa,join as Wt}from"path";function We(n){return structuredClone(n)}function ci(n){return Ja("sha256").update(JSON.stringify(n)).digest("hex")}async function jt(n,e){await qa(Wa(n),{recursive:!0});let t=`${n}.${process.pid}.${Date.now()}.tmp`,o=await La(t,"w");try{await o.writeFile(`${JSON.stringify(e,null,2)}
|
|
7
7
|
`,"utf8"),await o.sync()}finally{await o.close()}try{await Ua(t,n)}catch(r){throw await za(t).catch(()=>{}),r}}function Ga(n,e){return{version:1,projectId:n,createdAt:e,updatedAt:e,head:{sequence:0,hash:null},explanations:[],concrete:[],relations:[]}}function Va(n,e){let t=n.explanations.findIndex(o=>o.id===e.id);t>=0?n.explanations[t]=We(e):n.explanations.push(We(e))}function Qo(n,e){let t=We(n);for(let o of e)if(o.op==="add_explanation"){if(t.explanations.some(r=>r.id===o.explanation.id))throw new Error(`Imprint explanation already exists: ${o.explanation.id}`);t.explanations.push(We(o.explanation))}else if(o.op==="replace_explanation")Va(t,o.explanation);else if(o.op==="remove_explanation")t.explanations=t.explanations.filter(r=>r.id!==o.explanationId),t.relations=t.relations.filter(r=>r.fromId!==o.explanationId&&(r.toStoreId!==void 0||r.toId!==o.explanationId));else if(o.op==="add_concrete")t.concrete.some(r=>r.id===o.concrete.id)||t.concrete.push(We(o.concrete));else if(o.op==="remove_concrete")t.concrete=t.concrete.filter(r=>r.id!==o.concreteId),t.relations=t.relations.filter(r=>r.fromId!==o.concreteId&&(r.toStoreId!==void 0||r.toId!==o.concreteId));else if(o.op==="add_evidence"){let r=t.explanations.find(i=>i.id===o.explanationId);if(!r)throw new Error(`Unknown imprint explanation: ${o.explanationId}`);r.evidence.some(i=>i.id===o.evidence.id)||(r.evidence.push(We(o.evidence)),r.updatedAt=o.evidence.createdAt)}else if(o.op==="revise_explanation"){let r=t.explanations.find(i=>i.id===o.explanationId);if(!r)throw new Error(`Unknown imprint explanation: ${o.explanationId}`);Object.assign(r,We(o.patch),{updatedAt:o.updatedAt})}else if(o.op==="add_relation"){let r=o.relation,i=t.explanations.find(I=>I.id===r.fromId),s=!!r.toStoreId,a=s?void 0:t.explanations.find(I=>I.id===r.toId),d=t.concrete.some(I=>I.id===r.fromId),c=!s&&t.concrete.some(I=>I.id===r.toId);if(ai(r.type)){if(!(r.type==="expresses"?s?!!i:!!((i||d)&&a):!!(i&&(a||s))))throw new Error(`Imprint ${r.type} relation has invalid endpoints: ${r.id}`);if(t.relations.some(w=>w.type===r.type&&w.fromId===r.fromId&&w.toStoreId===r.toStoreId&&w.toId===r.toId))throw new Error(`Duplicate imprint relation: ${r.type}:${r.fromId}:${r.toStoreId??""}:${r.toId}`);if(r.type==="expresses"&&i&&a){if(zt(i,a)<0)throw new Error(`Imprint expresses relation must point from specific to abstract: ${r.id}`);let w=new Map;for(let b of t.relations)b.type!=="expresses"||b.toStoreId||w.set(b.fromId,[...w.get(b.fromId)??[],b.toId]);let g=[r.toId],k=new Set;for(;g.length;){let b=g.pop();if(b===r.fromId)throw new Error(`Imprint expresses relation would create a cycle: ${r.id}`);k.has(b)||(k.add(b),g.push(...w.get(b)??[]))}}}else if(!i||!a&&!c)throw new Error(`Imprint relation references an unknown explanation: ${r.id}`);t.relations.some(I=>I.id===r.id)||t.relations.push(We(r))}else o.op==="remove_relation"&&(t.relations=t.relations.filter(r=>r.id!==o.relationId));return t}function Ka(n,e){let t=[],o=We(n);for(let r of e){if(r.op==="add_explanation")t.unshift({op:"remove_explanation",explanationId:r.explanation.id});else if(r.op==="remove_explanation"){let i=o.explanations.find(s=>s.id===r.explanationId);i&&t.unshift({op:"add_explanation",explanation:We(i)})}else if(r.op==="add_concrete")t.unshift({op:"remove_concrete",concreteId:r.concrete.id});else if(r.op==="remove_concrete"){let i=o.concrete.find(s=>s.id===r.concreteId);i&&t.unshift({op:"add_concrete",concrete:We(i)})}else if(r.op==="replace_explanation"||r.op==="revise_explanation"||r.op==="add_evidence"){let i=r.op==="replace_explanation"?r.explanation.id:r.explanationId,s=o.explanations.find(a=>a.id===i);s&&t.unshift({op:"replace_explanation",explanation:We(s)})}else if(r.op==="add_relation")t.unshift({op:"remove_relation",relationId:r.relation.id});else if(r.op==="remove_relation"){let i=o.relations.find(s=>s.id===r.relationId);i&&t.unshift({op:"add_relation",relation:We(i)})}o=Qo(o,[r])}return t}function li(n){return n.op==="add_explanation"||n.op==="replace_explanation"?[n.explanation.id]:n.op==="remove_explanation"||n.op==="add_evidence"||n.op==="revise_explanation"?[n.explanationId]:n.op==="add_relation"?[n.relation.fromId,...n.relation.toStoreId?[]:[n.relation.toId]]:[]}var to=class{constructor(e,t){this.projectRoot=e;this.projectId=t;this.writeChain=Promise.resolve();this.imprintDir=Wt(e,".popmelt","imprint"),this.manifestPath=Wt(this.imprintDir,"manifest.json"),this.graphPath=Wt(this.imprintDir,"graph.json"),this.journalDir=Wt(this.imprintDir,"journal")}async readHead(){return(await this.readManifest()).head}async readManifest(){try{let e=JSON.parse(await di(this.manifestPath,"utf8"));if(e.version!==1||e.projectId!==this.projectId)throw new Error("Imprint manifest does not match this project.");return e}catch(e){if(e.code!=="ENOENT")throw e;let t=new Date().toISOString();return{version:1,projectId:this.projectId,createdAt:t,updatedAt:t,head:{sequence:0,hash:null}}}}async loadRaw(){let e=await this.readManifest(),o=(await Fa(this.journalDir).catch(a=>{if(a.code==="ENOENT")return[];throw a})).filter(a=>/^\d{12}-.+\.json$/.test(a)).sort(),r=Ga(this.projectId,e.createdAt),i=[],s=null;for(let a=0;a<o.length;a++){let d=JSON.parse(await di(Wt(this.journalDir,o[a]),"utf8")),{hash:c,...I}=d;if(d.sequence!==a+1||d.previousHash!==s||c!==ci(I))throw new Error(`Imprint journal hash mismatch at sequence ${d.sequence}.`);r=Qo(r,d.mutations),r.updatedAt=d.committedAt,r.head={sequence:d.sequence,hash:d.hash},i.push(d),s=d.hash}return{graph:r,transactions:i,recoveredTransactions:Math.max(0,i.length-e.head.sequence)}}async load(){let e=await this.loadRaw();return{...e,graph:Mt(e.graph)}}async migrateTopology(){let e=await this.readManifest();if(e.topologyVersion===2)return{migrated:!1,relationMutations:0};let t=await this.loadRaw(),o=Mt(t.graph),r=new Map(o.relations.map(s=>[s.id,s])),i=[];for(let s of t.graph.relations){let a=r.get(s.id);a&&a.type===s.type&&a.fromId===s.fromId&&a.toStoreId===s.toStoreId&&a.toId===s.toId||(i.push({op:"remove_relation",relationId:s.id}),a&&i.push({op:"add_relation",relation:a}))}if(i.length){let s=new Date().toISOString();return await this.commit({transactionId:`topology-v2-${t.graph.head.sequence+1}`,committedAt:s,source:{kind:"migration",threadId:"imprint-topology",jobId:"imprint-topology-v2"},summary:"Simplify Imprint topology to expresses and excepts",mutations:i}),{migrated:!0,relationMutations:i.length}}return await jt(this.graphPath,o),await jt(this.manifestPath,{...e,topologyVersion:2}),{migrated:!0,relationMutations:0}}async commit(e){let t;return this.writeChain=this.writeChain.then(async()=>{let o=await this.load();if(o.transactions.some(w=>w.transactionId===e.transactionId))throw new Error(`Imprint transaction already exists: ${e.transactionId}`);let r=o.graph.head.sequence+1,i=Ka(o.graph,e.mutations),s={version:1,sequence:r,transactionId:e.transactionId,projectId:this.projectId,committedAt:e.committedAt,previousHash:o.graph.head.hash,source:e.source,summary:e.summary,mutations:We(e.mutations),inverseMutations:i,...e.undoesTransactionId?{undoesTransactionId:e.undoesTransactionId}:{}},a={...s,hash:ci(s)},d=Qo(o.graph,a.mutations);d.updatedAt=e.committedAt,d.head={sequence:r,hash:a.hash};let c=`${String(r).padStart(12,"0")}-${e.transactionId}.json`;await jt(Wt(this.journalDir,c),a);let I={version:1,topologyVersion:2,projectId:this.projectId,createdAt:d.createdAt,updatedAt:e.committedAt,head:d.head};await jt(this.graphPath,d),await jt(this.manifestPath,I),t={graph:d,transaction:a}}),await this.writeChain,t}async undo(e,t,o,r){let i=await this.load(),s=i.transactions.find(c=>c.transactionId===e);if(!s)throw new Error("The imprint update is no longer available to undo.");if(i.transactions.some(c=>c.undoesTransactionId===e))throw new Error("This imprint update has already been undone.");let a=new Set(s.mutations.flatMap(li));if(i.transactions.filter(c=>c.sequence>s.sequence).some(c=>c.mutations.some(I=>li(I).some(w=>a.has(w)))))throw new Error("This imprint update has newer dependent changes and cannot be undone cleanly. Edit or challenge the current explanation instead.");return this.commit({transactionId:r,committedAt:o,source:t,summary:`Undo: ${s.summary}`,mutations:s.inverseMutations,undoesTransactionId:e})}async recover(){let e=await this.load();return await jt(this.graphPath,e.graph),await jt(this.manifestPath,{version:1,topologyVersion:2,projectId:this.projectId,createdAt:e.graph.createdAt,updatedAt:e.graph.updatedAt,head:e.graph.head}),{...e,recoveredTransactions:0}}};var Ha=new Set(["application_only","transient_state","concrete_without_model_path","duplicate","insufficient_evidence","task_specific_detail","no_shared_understanding_change"]),Qa=new Set(["about","after","again","before","being","change","from","have","into","make","should","that","the","their","this","with","would","here","only","consistent","match","matched","matching","mirror","mirrored","mirroring","align","aligned","aligning"]),Xa=[/\bconsistent\s+with\b/i,/\bmatch(?:es|ed|ing)?\b/i,/\bmirror(?:s|ed|ing)?\b/i,/\bthe\s+same\s+as\b/i,/\bborrow(?:s|ed|ing)?\s+from\b/i,/\balign(?:s|ed|ing)?\s+with\b/i,/\b(?:more|look|looks|feel|feels|behave|behaves)\s+like\b/i],Ya=/\b(all|any|every|everywhere|across|throughout|globally|project[- ]wide)\b/i;function Yo(n){return!!(n&&Ya.test(n))}function Za(n){return n.scope.kind==="project"||Yo(`${n.statement} ${n.rationale}`)}function ed(n){let e=n?.scope.values.filter(t=>t!=="*").join(", ");return e?`Does applying this understanding here match your intent for ${e}?`:"Does applying this understanding here match your intent?"}function td(n){return n.replace(/([a-z0-9])([A-Z])/g,"$1 $2").toLowerCase().replace(/[^a-z0-9]+/g," ").trim().replace(/\s+/g," ")}function nd(n){return n.length>4&&n.endsWith("ies")?[n,`${n.slice(0,-3)}y`]:n.length>3&&n.endsWith("s")&&!n.endsWith("ss")?[n,n.slice(0,-1)]:[n]}function mt(n){return new Set(td(n).split(" ").filter(e=>e.length>2&&!Qa.has(e)).flatMap(nd))}function pi(n){return n.replace(/^\s*(?:please\s+)?(?:let(?:'|’)s\s+)?(?:make|apply|use|keep|change|update)\s+/i,"").replace(/[\s,.:;!?]+$/g,"").trim().slice(0,240)}function od(n){if(n)for(let e of Xa){let t=e.exec(n);if(!t||t.index<=0)continue;let o=pi(n.slice(0,t.index)),r=pi(n.slice(t.index+t[0].length));if(o&&r)return{target:o,reference:r}}}function xt(n,e){if(!n.size||!e.size)return 0;let t=0;for(let o of n)e.has(o)&&(t+=1);return t/Math.min(n.size,e.size)}function Xo(n){return!n||!Array.isArray(n.values)||n.values.length===0?{kind:"project",values:["*"]}:{kind:n.kind,values:[...new Set(n.values.map(e=>e.trim()).filter(Boolean))]}}function St(n,e,t){return{id:ft(),kind:e==="excepts"?"exception":e==="challenges"?"correction":"observation",stance:e,summary:t,threadId:n.threadId,jobId:n.jobId,createdAt:n.occurredAt??new Date().toISOString()}}function Se(n,e=800){return typeof n=="string"?n.trim().slice(0,e):""}function ui(n){if(!n||typeof n!="object"||Array.isArray(n))return;let e=n;if(!["project","route","component","selector","file"].includes(String(e.kind))||!Array.isArray(e.values))return;let t=e.values.filter(o=>typeof o=="string"&&!!o.trim());return t.length?{kind:e.kind,values:t}:void 0}function mi(n,e,t){if(n&&typeof n=="object"&&!Array.isArray(n)){let i=n,s=Se(i.explanationId,100),a=Se(i.clientId,100),d=Se(i.storeId,100);if(s&&a)throw new Error("relation endpoint must use either explanationId or clientId, not both");let c=s||a;return c?{explanationId:c,...d?{storeId:d}:{}}:void 0}let o=Se(e,100),r=Se(t,100);return o?{explanationId:o,...r?{storeId:r}:{}}:void 0}function fi(n,e){if(!n||!e.trim())return{found:!1};let t=n;for(let o of e.split(".").filter(Boolean))if(Array.isArray(t)){let r=Number(o);if(!Number.isInteger(r)||r<0||r>=t.length)return{found:!1};t=t[r]}else if(t&&typeof t=="object"&&Object.prototype.hasOwnProperty.call(t,o))t=t[o];else return{found:!1};return{found:!0,value:t}}function In(n,e={}){let t=[...n.matchAll(/<imprint>([\s\S]*?)<\/imprint>/g)],o=n.replace(/<imprint>[\s\S]*?<\/imprint>/g,"").replace(/<imprint>[\s\S]*$/g,"").trim(),r=t.at(-1)?.[1],i,s=[];if(r)i=r;else{let d=[...n.matchAll(/<resolution>\s*([\s\S]*?)\s*<\/resolution>/g)].at(-1)?.[1];if(!d)return{visibleText:o,error:"imprint assessment is missing"};try{let c=JSON.parse(d);if(!c||typeof c!="object"||Array.isArray(c)||!Object.prototype.hasOwnProperty.call(c,"imprint"))return{visibleText:o,error:"imprint assessment is missing"};let I=c;i=I.imprint,s=Array.isArray(I.annotations)?I.annotations:[]}catch{return{visibleText:o,error:"resolution envelope is invalid"}}}try{let a=typeof i=="string"?JSON.parse(i):i;if(!a||typeof a!="object"||Array.isArray(a))throw new Error("imprint assessment is invalid");if(a.decision==="none"){let h=String(a.reason);if(!Ha.has(h))throw new Error("imprint no-op reason is invalid");if(h==="task_specific_detail"&&s.some(M=>{if(!M||typeof M!="object"||Array.isArray(M))return!1;let $=M;return($.finalScope??$.inferredScope)?.breadth==="pattern"}))throw new Error("pattern-scoped resolution cannot be classified as task_specific_detail");let p=Array.isArray(a.usedExplanationIds)?a.usedExplanationIds.filter(M=>typeof M=="string"&&!!M.trim()):[],y=Array.isArray(a.appliedExplanationIds)?a.appliedExplanationIds.filter(M=>typeof M=="string"&&!!M.trim()):[],v=new Set(e.context?.explanations.map(M=>M.id)??[]);if([...new Set([...p,...y])].some(M=>!v.has(M)))throw new Error("imprint use must cite only surfaced explanations");let x=[...new Set([...p,...e.applicationOccurred?[]:y])],m=e.applicationOccurred?y:[],f;if(m.length){f=m;let M=new Set(e.context?.pendingGroundingExplanationIds??[]),$=m.filter(E=>{let F=e.context?.explanations.find(Y=>Y.id===E);return v.has(E)&&F&&(F.state==="emerging"||F.state==="testing")&&!M.has(E)});if($.length){let E=a.grounding,F=typeof E?.explanationId=="string"&&$.includes(E.explanationId)?E.explanationId:$[0],Y=Se(E?.question,300);return{draft:{decision:"apply",summary:{title:Se(a.summary?.title,120)||"Applied shared understanding",detail:Se(a.summary?.detail,500)||Se(e.requestText||o,500)||"Applied an ungrounded explanation in a new context."},appliedExplanationIds:m,grounding:{explanationId:F,question:Y||"Does applying this understanding here match your intent?"}},visibleText:o}}}if(h==="duplicate"){let M=Se(a.duplicateOfExplanationId,100),$=a.scopeComparison;if(!M)throw new Error("duplicate assessment requires duplicateOfExplanationId");if($!=="equivalent"&&$!=="existing_broader")throw new Error("duplicate assessment requires an equivalent or existing_broader scopeComparison");let E=e.context?.explanations.find(F=>F.id===M);if(!E)throw new Error("duplicate assessment must cite a surfaced explanation");if(Yo(e.requestText)&&!Za(E))throw new Error("duplicate explanation does not cover the requested broader scope");return{draft:{decision:"none",reason:h,duplicateOfExplanationId:M,scopeComparison:$,...x.length?{usedExplanationIds:x}:{},...f?{appliedExplanationIds:f}:{}},visibleText:o}}return{draft:{decision:"none",reason:h,...x.length?{usedExplanationIds:x}:{},...f?{appliedExplanationIds:f}:{}},visibleText:o}}if(a.decision==="recognize"){let h=Se(a.explanationId,100);if(!h)throw new Error("recognition requires explanationId");let p=e.context?.explanations.find(u=>u.id===h);if(!p)throw new Error("recognition must cite a surfaced explanation");if(p.state!=="grounded")throw new Error("recognition must cite a grounded explanation");let y=a.summary,v={title:Se(y?.title,120),detail:Se(y?.detail,500)};if(!v.title||!v.detail)throw new Error("recognition summary title and detail are required");return{draft:{decision:"recognize",explanationId:h,summary:v},visibleText:o}}if(a.decision==="apply"){let h=a.summary,p={title:Se(h?.title,120),detail:Se(h?.detail,500)};if(!p.title||!p.detail)throw new Error("application summary title and detail are required");let y=Array.isArray(a.appliedExplanationIds)?a.appliedExplanationIds.filter(M=>typeof M=="string"&&!!M.trim()):[];if(!y.length)throw new Error("application requires appliedExplanationIds");let v=new Map(e.context?.explanations.map(M=>[M.id,M])??[]);if(y.some(M=>!v.has(M)))throw new Error("application must cite only surfaced explanations");if(!e.applicationOccurred)return{draft:{decision:"none",reason:"application_only",usedExplanationIds:[...new Set(y)]},visibleText:o};let u=new Set(e.context?.pendingGroundingExplanationIds??[]),x=y.filter(M=>{let $=v.get(M);return $&&($.state==="emerging"||$.state==="testing")&&!u.has(M)});if(!x.length){if(y.every($=>v.get($)?.state==="grounded"||u.has($)))return{draft:{decision:"none",reason:"application_only",appliedExplanationIds:y},visibleText:o};throw new Error("application must cite a surfaced ungrounded explanation")}let m=a.grounding,f=typeof m?.explanationId=="string"&&x.includes(m.explanationId)?m.explanationId:x[0];return{draft:{decision:"apply",summary:p,appliedExplanationIds:y,grounding:{explanationId:f,question:Se(m?.question,300)||"Does applying this understanding here match your intent?"}},visibleText:o}}if(a.decision!==void 0&&a.decision!=="mutate")throw new Error("imprint decision is invalid");let d=a.summary,c=d?.kind;if(!["added","strengthened","refined","challenged","exception","consolidated"].includes(String(c)))throw new Error("summary.kind is invalid");let I={kind:c,title:Se(d?.title,120),detail:Se(d?.detail,500)};if(!I.title||!I.detail)throw new Error("summary title and detail are required");let w=Array.isArray(a.appliedExplanationIds)?a.appliedExplanationIds.filter(h=>typeof h=="string"&&!!h.trim()):[],g=[];for(let h of Array.isArray(a.mutations)?a.mutations:[]){if(!h||typeof h!="object")continue;let p=h;if(p.op==="add"&&["intention","principle","pattern"].includes(String(p.kind))){let y=Se(p.statement),v=Se(p.rationale);if(!y||!v)continue;g.push({op:"add",clientId:Se(p.clientId,100)||void 0,kind:p.kind,statement:y,rationale:v,scope:ui(p.scope),rootReason:Se(p.rootReason,300)||void 0})}else if(p.op==="revise"&&typeof p.explanationId=="string"){let y={op:"revise",explanationId:p.explanationId,statement:Se(p.statement)||void 0,rationale:Se(p.rationale)||void 0,scope:ui(p.scope),reopenGrounding:p.reopenGrounding===!0};(y.statement||y.rationale||y.scope||y.reopenGrounding)&&g.push(y)}else if(p.op==="evidence"&&typeof p.explanationId=="string"&&["supports","challenges","excepts"].includes(String(p.stance))){let y=Se(p.summary);y&&g.push({op:"evidence",explanationId:p.explanationId,stance:p.stance,summary:y})}else if(p.op==="relation"){if(!["expresses","excepts"].includes(String(p.type)))throw new Error("relation type is invalid");let y=Se(p.rationale),v=mi(p.from,p.fromId,p.fromStoreId),u=mi(p.to,p.toId,p.toStoreId);if(!y)throw new Error("relation rationale is required");if(!v||!u)throw new Error("relation endpoints require an explanationId or clientId");g.push({op:"relation",type:p.type,from:v,to:u,rationale:y})}else if(p.op==="supersede"&&typeof p.explanationId=="string"&&typeof p.replacementId=="string"){let y=Se(p.reason);y&&g.push({op:"supersede",explanationId:p.explanationId,replacementId:p.replacementId,reason:y})}else if(p.op==="bind_concrete"&&typeof p.explanationId=="string"){let y=Se(p.modelPath,300);y&&g.push({op:"bind_concrete",explanationId:p.explanationId,modelPath:y,rationale:Se(p.rationale)})}else p.op==="retire"&&typeof p.explanationId=="string"&&g.push({op:"retire",explanationId:p.explanationId,reason:Se(p.reason)})}let k=a.grounding,b=k&&typeof k.explanationId=="string"&&Se(k.question)?{explanationId:k.explanationId,question:Se(k.question,300)}:void 0;return{draft:{decision:"mutate",summary:I,appliedExplanationIds:w,mutations:g,grounding:b},visibleText:o}}catch(a){return{visibleText:o,error:a instanceof Error?a.message:String(a)}}}function wn(n,e={}){let t=n?.explanations??[],o=new Set(n?.pendingGroundingExplanationIds??[]),r=new Set(n?.correctionExplanationIds??[]),i=Yo(e.requestText),s=n?.retrievalIntent,a=n?.historicalCaveats??[],d=t.find(g=>g.storeId&&!g.readOnly)?.storeId,c=(g,k)=>{let b=g[k];return b?`[${b.storeId}] ${b.explanationId}`:k==="from"?g.fromId:g.toId},I=!!n?.relations?.some(g=>g.from&&g.to&&g.from.storeId!==g.to.storeId),w=t.some(g=>g.readOnly);return["","## Continuous imprinting","","An imprint is provisional shared understanding learned through human-AI collaboration. It is advisory, not authoritative product truth. Accepted product decisions above always win when they conflict.",...t.length?["","Relevant explanations from the project imprint:",...t.map((g,k)=>[`${k+1}. [${g.state}; ${g.kind}] ${g.statement}`,` - Why: ${g.rationale}`,` - Scope: ${g.scope.kind} ${g.scope.values.join(", ")}`,` - Explanation: ${g.id}`,...g.storeId?[` - Store: ${g.storeId}${g.readOnly?" (read-only)":" (writable primary)"}`]:[],...o.has(g.id)?[" - Grounding: already pending; do not ask again"]:[],...r.has(g.id)?[" - Correction: the developer selected No for this explanation and the current reply supplies the missing correction context"]:[],...g.concrete.map(b=>` - Concrete terminal: ${b.modelPath} = ${JSON.stringify(b.value)}`)].join(`
|
|
8
8
|
`)),...n?.truncated?["Additional imprint context exists outside this bounded packet."]:[]]:[],...n?.relations?.length?["","Known explanation topology:",...n.relations.map(g=>`- ${g.storeId?`[owned by ${g.storeId}${g.readOnly?"; read-only":""}] `:""}${c(g,"from")} --${g.type}--> ${c(g,"to")}: ${g.rationale}`)]:[],...w||I?["","Store-qualified topology remains locally owned: the relation must originate at an explanation in the writable primary store, while its target may be in a mounted read-only store. Never proxy a relation whose source belongs to a mounted store.",`For a cross-store relation, use qualified endpoints: {"op":"relation","type":"expresses|excepts","from":{"storeId":"${d??"<writable-primary-store>"}","clientId":"<new-specific-client-id>"},"to":{"storeId":"<mounted-target-store>","explanationId":"<existing-abstract-target-id>"},"rationale":"<why>"}. Use clientId for a new explanation created in this transaction and explanationId for an existing durable explanation.`]:[],...s?["","Explicit pattern transfer detected:",`- Target: ${s.target}`,`- Reference: ${s.reference}`,"Before editing, state which dimensions you intend to transfer from the reference and which target-specific behavior you will preserve. Compare the relevant surface, spacing, typography, radius, and interaction behavior; transfer only the dimensions supported by the request and evidence. If the intended transfer remains materially ambiguous after inspecting both anchors, ask one focused question."]:[],...a.length?["","Directly relevant challenged history (historical caveats, not current Imprint truth):",...a.map((g,k)=>[`${k+1}. [challenged; ${g.kind}] ${g.statement}`,` - Why it was previously proposed: ${g.rationale}`,` - Scope: ${g.scope.kind} ${g.scope.values.join(", ")}`,` - Historical explanation: ${g.id}`,...g.storeId?[` - Store: ${g.storeId}${g.readOnly?" (read-only)":" (writable primary)"}`]:[]].join(`
|
|
9
9
|
`)),"Use these caveats only to avoid repeating a rejected or over-broad transfer. Do not cite them as usedExplanationIds or treat them as active guidance."]:[],...t.some(g=>g.readOnly)?["","Mounted-store explanations are read-only context. They may inform the work and be cited as used understanding, but do not mutate, ground, revise, or add evidence to them without an explicit writable orientation for that store."]:[],...n?.unavailableStoreIds?.length?["",`Some mounted Imprint knowledge was unavailable during this bounded read: ${n.unavailableStoreIds.join(", ")}. Do not infer its contents.`]:[],...n?.unavailableTopologyStoreIds?.length?["",`Some mounted Imprint topology was unavailable during this bounded read: ${n.unavailableTopologyStoreIds.join(", ")}. Available explanations remain usable, but do not infer missing relationships.`]:[],"","Work normally and preserve developer agency. A testing explanation may guide action as a hypothesis without asking permission first. An emerging explanation is observation-only: use it to recognize recurrence, not to choose behavior. If the developer independently repeats an emerging observation in a meaningfully different occurrence, that new application may promote it into testing and request grounding. Silence is not confirmation.","Assess learning proactively as part of completing every thread. Do not wait for the developer to ask whether there was a learning. Before choosing none, look one abstraction level above the exact edit and ask: would the reasoning behind this outcome change how a future agent handles a meaningfully different occurrence?","The developer's original annotation, ordinary instruction, scope language, and later corrections are already human-supplied evidence. They can support learning when they establish a reusable dependency, boundary, or rationale; a separate post-completion learning prompt is not required. Mere acceptance of completed work is still insufficient.","The default outcome is no imprint mutation. Completing work or making a successful design choice does not by itself constitute learning.","Application evidence is distinct from changing shared understanding. When an emerging or testing explanation materially influenced a completed action, report decision apply even when no explanation wording changed.","Applying grounded understanding does not mutate the graph. Usually report none. Use decision recognize only when visibly acknowledging the grounded explanation closes a correction loop or otherwise makes consequential reuse legible to the developer. Recognition must cite one exact surfaced grounded explanation ID; never infer or invent an ID.","Application and recognition are whole-statement operations. Cite an existing explanation only when the completed behavior is compatible with every material clause, its scope, and its exceptions. Sharing one useful clause is not a match. If the durable behavior is narrower, add one atomic explanation linked to the broader explanation when the admission tests pass; otherwise report none.","Mutate the imprint only when this collaboration changed reusable shared understanding: a future agent facing a meaningfully different task would behave differently because of what was learned here.","Relationship-first admission: before calling any completed change task-specific, compare its requested behavior and effective scope with every surfaced explanation that could plausibly govern the same subject or treatment. The developer does not need to name an Imprint concept or relation. Infer the relationship from the ordinary instruction, the annotated context, the implementation, and the surfaced explanations.","A narrower completed behavior that departs from an applicable broader explanation establishes an applicability boundary even when the developer did not say exception. Add one narrow testing explanation and link it to the broader rule with excepts. If the behaviors concern distinct concepts rather than the same rule, assess the reusable behavior as a sibling explanation instead. Do not manufacture an exception merely because implementations differ.","Scope and Imprint assessment must agree. If any resolved annotation has an effective inferred or final breadth of pattern, do not classify the Imprint decision as task_specific_detail. Either capture the reusable conceptual change, report application of existing understanding, cite a valid duplicate, or use another accurate no-op reason. The bridge rejects pattern-scoped task_specific_detail assessments.","","Before proposing a mutation, apply every admission test:","1. Reusable: it guides a future decision beyond reproducing this exact edit.","2. Novel: it is not already expressed by an explanation or its evidence.","3. Durable: it is an intention, rationale, relationship, or recurring pattern, not temporary implementation state.","4. Atomic: it expresses one claim at one abstraction level.","5. Scoped: its applicability can be named without silently generalizing.","6. Supported: the human supplied evidence, correction, or confirmation anywhere in the thread, including the original instruction, not merely acceptance of completed work or the model's own implementation choice.","If any test fails, return a decision of none.","","Usually not learning: exact copy substitutions, one-pixel adjustments, element reordering, implementation mechanics, keeping something hidden temporarily, applying an already-known pattern, or a stylistic choice made without human feedback. Distinguish mechanics from the reusable reasoning they may reveal: a CSS property alone is not learning, but a human-directed causal rule such as keeping optical adjustment out of layout geometry may be. This heuristic never overrides relationship-first admission: a small local change that narrows or contradicts an applicable surfaced explanation may establish an exception or correction. A task-specific edit otherwise becomes learning only if the human establishes a reusable dependency, applicability boundary, or rationale beyond that edit.","When a candidate overlaps an existing explanation, consolidate before adding. Same meaning plus a new observation means evidence only. A concrete application means bind_concrete, not a broader explanation. A scope correction means revise scope. A direct disagreement means add the replacement and supersede the contradicted explanation. A different abstraction level means a separate related explanation.","Duplicate means the same reusable claim is already represented at an equivalent or broader conceptual scope. Shared code, a centralized component, or implementation reuse does not establish conceptual duplication. A narrower component-, route-, selector-, or file-scoped explanation is not a duplicate of newly broadened applicability.","Words such as all, every, across, throughout, globally, and project-wide are explicit applicability signals. When the human broadens a grounded explanation, preserve the grounded narrower explanation and add a broader testing explanation. Link the narrower explanation to the broader one with expresses. Do not substitute another no-op reason for that scope change.",...i?["CURRENT SCOPE SIGNAL: The developer used explicit broadening language in this instruction. Do not classify it as duplicate unless the cited existing explanation already expresses universal or project-wide applicability."]:[],"Keep correction identity explicit. If feedback refines an emerging or testing explanation without rejecting its underlying claim, revise it and set reopenGrounding true; do not also add challenging evidence to that same explanation. If feedback rejects the claim as written, preserve that rejected wording, add a distinct replacement, and supersede the old explanation. Never revise an explanation and mark that same identity challenged in one update.","When an explanation is marked Correction in the supplied context, the developer selected No but the graph has not yet been changed. Interpret the current reply before mutating anything. Decide whether it corrects only the implementation result, the explanation scope, or the reasoning. Do not challenge the whole explanation for a narrow correction, and do not recognize a different explanation merely because one clause overlaps.","Topology assessment is mandatory for every add mutation. Imprint topology has exactly two relation types: expresses and excepts. expresses always points from the more specific, concrete, or lower-level explanation to the more abstract explanation it expresses. excepts points from the narrower exception to the rule whose applicability it limits. Support and challenge belong in evidence; correction belongs in supersede; neither is a topology relation. If an addition is genuinely a new root with no meaningful relative, include rootReason explaining why. Unmapped additions without rootReason are rejected.","Never append implementation facts to an intention merely because they occurred in the same component. Grounded explanations are append-only: add evidence for a compatible observation, but never revise or demote the grounded explanation. A material change in meaning, causal rationale, abstraction level, or applicability must be a new testing explanation linked with expresses or excepts. Multiple specific explanations may express the same parent, and one explanation may express multiple parents; never create an expresses cycle.","Grounded explanations may support new higher-level theory. Emerging explanations must not be the sole basis for another durable explanation. Concrete project facts should remain concrete; do not turn a one-off value into a universal principle.","Request grounding when an ungrounded explanation materially influenced the action, this application produced genuinely new evidence, and that explanation does not already have grounding pending. Ask at most one grounding question per thread. The bridge supplies a fallback question when the application is otherwise valid.","","At the end, emit exactly one machine-readable <resolution> envelope after the normal response. Its `annotations` array reports annotation outcomes, and its required `imprint` field completes the learning assessment. The envelope is hidden from the developer. Do not emit a separate <imprint> block.",'As soon as one or more surfaced explanations become actively useful during the work, emit `<imprint-use>{"explanationIds":["<every actively useful surfaced explanation id>"]}</imprint-use>` immediately. This live receipt is hidden from conversational prose. Emit it at the point of use rather than waiting for the final response. Emit a later marker only when additional explanations become actively useful. Never emit it for explanations that were merely retrieved, read, searched, or considered.',"Treat Imprint as actively useful when a surfaced explanation materially informs an answer, recommendation, implementation choice, or other decision. Whenever it is actively useful, always cite every materially used surfaced explanation in `usedExplanationIds`. This produces a usage receipt only; it does not strengthen or mutate shared understanding. Do not cite explanations that were merely retrieved, read, or considered but did not influence the result.","`appliedExplanationIds` is narrower: include an explanation there only when it governed a completed implementation or other observable action that genuinely tests the explanation. Never use `appliedExplanationIds` for an answer-only, informational, research, or read-only request. An explanation may be used without being applied; every applied explanation is also understood to be used.","For no learning, include:",'"imprint":{"decision":"none","reason":"application_only|transient_state|concrete_without_model_path|duplicate|insufficient_evidence|task_specific_detail|no_shared_understanding_change","usedExplanationIds":["<every surfaced explanation that was actively useful, or omit when none>"]}',"A no-op reason cannot suppress genuine application evidence. When a completed action was governed by an ungrounded explanation, report decision apply directly so the summary and grounding question are intentional. Answering a question with that explanation is usage, not application.","When and only when reason is duplicate, also include `duplicateOfExplanationId` naming one surfaced explanation and `scopeComparison` set to `equivalent` or `existing_broader`. The bridge rejects uncited duplicates and duplicates whose cited explanation cannot cover explicit broadening language.","For application evidence without a conceptual change, include:",'"imprint":{"decision":"apply","summary":{"title":"<short expandable-summary title>","detail":"<how the explanation influenced this completed action>"},"appliedExplanationIds":["<surfaced emerging or testing explanation id>"],"grounding":{"explanationId":"<one applied id>","question":"<brief post-application confirmation question>"}}',"For a consequential, non-mutating reuse of grounded understanding that should be visible, include:",'"imprint":{"decision":"recognize","explanationId":"<one exact surfaced grounded explanation id>","summary":{"title":"<short name for the recognized understanding>","detail":"<how it guided this outcome>"}}',"Or, only after every admission test passes for a conceptual change:",'"imprint":{"decision":"mutate","summary":{"kind":"added|strengthened|refined|challenged|exception|consolidated","title":"<short expandable-summary title>","detail":"<what changed in shared understanding>"},"appliedExplanationIds":["<only an ungrounded explanation materially tested here>"],"mutations":[{"op":"add","clientId":"new-1","kind":"intention|principle|pattern","statement":"<one durable atomic explanation>","rationale":"<why the evidence supports it>","scope":{"kind":"project|route|component|selector|file","values":["<scope>"]},"rootReason":"<only when this is genuinely unrelated to every surfaced or new explanation>"},{"op":"revise","explanationId":"<only an emerging or testing id>","statement":"<optional>","rationale":"<optional>","scope":{"kind":"component","values":["CardTile"]},"reopenGrounding":false},{"op":"evidence","explanationId":"<id>","stance":"supports|challenges|excepts","summary":"<thread evidence>"},{"op":"relation","type":"expresses|excepts","from":{"clientId":"<new-specific-client-id>"},"to":{"explanationId":"<existing-abstract-id>"},"rationale":"<why; use clientId for new transaction-local explanations and explanationId for existing durable explanations>"},{"op":"supersede","explanationId":"<contradicted old id>","replacementId":"<new clientId or existing id>","reason":"<direct conflict>"},{"op":"bind_concrete","explanationId":"<id or clientId>","modelPath":"<exact existing model path>","rationale":"<how this fact expresses the explanation>"},{"op":"retire","explanationId":"<id>","reason":"<why>"}],"grounding":{"explanationId":"<one applied ungrounded id or clientId>","question":"<brief post-application confirmation question>"}}','The final shape is `<resolution>{"annotations":[...],"imprint":{...}}</resolution>`. The `imprint` field is required even when the decision is none; omission leaves the completed run explicitly unassessed.',"Include only mutation objects actually needed. A new explanation established through completed implementation must include its clientId in appliedExplanationIds; the bridge also derives this application from a completed change as a safeguard. Applying a grounded explanation never requires a mutation, but actively using it still requires its id in appliedExplanationIds. Never claim that a new explanation is grounded."].join(`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as p}from"./chunk-NOHXTT24.mjs";import{request as c}from"http";function u(){let e=process.env.POPMELT_BRIDGE_URL;if(!e)return null;try{let t=new URL(e);return{port:t.port?Number.parseInt(t.port,10):null,projectId:process.env.POPMELT_PROJECT_ID||null,leaseEligible:!1}}catch{return null}}async function h(e,t,i){let r=u();if(e?.bridge===!1||r)return r??{port:null,projectId:null,leaseEligible:!1};try{let{startPopmelt:o}=await import("./server-
|
|
1
|
+
import{a as p}from"./chunk-NOHXTT24.mjs";import{request as c}from"http";function u(){let e=process.env.POPMELT_BRIDGE_URL;if(!e)return null;try{let t=new URL(e);return{port:t.port?Number.parseInt(t.port,10):null,projectId:process.env.POPMELT_PROJECT_ID||null,leaseEligible:!1}}catch{return null}}async function h(e,t,i){let r=u();if(e?.bridge===!1||r)return r??{port:null,projectId:null,leaseEligible:!1};try{let{startPopmelt:o}=await import("./server-VX66FOQO.mjs"),n=t.config?.server?.host,l=n===!0?"0.0.0.0":n||"localhost",s=t.config?.server?.port??i,d=`http://${l==="0.0.0.0"?"localhost":l}:${s}`,a=await o({port:e?.port,projectRoot:e?.projectRoot,devOrigin:d,force:!0,detached:!0,manageLease:!1});return console.log(`[popmelt] bridge ready at http://localhost:${a.port}`),{port:a.port,projectId:a.projectId,leaseEligible:!0}}catch(o){let n=o instanceof Error?o.message:String(o);return console.warn("[popmelt] bridge failed to start:",n),{port:null,projectId:null,leaseEligible:!1}}}async function v(e,t,i=fetch){if(!e.leaseEligible||e.port===null)return null;let r=await p({port:e.port,projectId:e.projectId},i);if(!r)return null;let o=()=>{n()},n=async()=>{process.removeListener("beforeExit",o),await r.release()};return process.once("beforeExit",o),t.httpServer?.once("close",()=>{n()}),{id:r.id,release:n}}function g(e,t,i){if(i.port===null)return null;let r=new URL(t,"http://popmelt.local");if(r.pathname!==`${e}/status`)return null;let o=new URL("/status",`http://localhost:${i.port}`);return r.searchParams.forEach((n,l)=>o.searchParams.append(l,n)),i.projectId&&o.searchParams.set("projectId",i.projectId),o}function b(e,t){return(i,r,o)=>{let n=g(e,i.url||"",t());if(!n)return o();let l=c(n,{method:i.method,headers:{...i.headers,host:n.host}},s=>{r.writeHead(s.statusCode||502,s.headers),s.pipe(r)});l.on("error",()=>{r.writeHead(502,{"Content-Type":"text/plain"}),r.end(`Popmelt bridge not running on port ${t().port}`)}),i.pipe(l)}}function P(e){let t=e.port===null?null:`http://localhost:${e.port}`;return`window.__POPMELT_BRIDGE_URL__=${JSON.stringify(t)};window.__POPMELT_PROJECT_ID__=${JSON.stringify(e.projectId)};`}export{h as a,v as b,b as c,P as d};
|
package/dist/cli.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import{spawn as qf}from"child_process";import{readFile as Lf,unlink as Ff}from"f
|
|
|
4
4
|
`);let o=new Map;for(let r of e){let i=r.pathname||"(unknown)",s=o.get(i)??[];s.push(r.instruction),o.set(i,s)}return[...o.entries()].map(([r,i])=>`\`${r}\`
|
|
5
5
|
${i.map(s=>`- ${s}`).join(`
|
|
6
6
|
`)}`).join(`
|
|
7
|
-
`)}var zi={name:"@popmelt.com/core",version:"0.18.
|
|
7
|
+
`)}var zi={name:"@popmelt.com/core",version:"0.18.1",description:"A tiny, powerful design layer for humans and AI",license:"PolyForm-Shield-1.0.0",author:"Popmelt <reb@popmelt.com> (https://popmelt.com)",homepage:"https://github.com/popmelt/core",bugs:{url:"https://github.com/popmelt/core/issues"},repository:{type:"git",url:"git+https://github.com/popmelt/core.git"},keywords:["ai","annotations","collaboration","devtools","react","visual-feedback"],bin:{popmelt:"dist/cli.mjs"},sideEffects:!1,packageManager:"pnpm@9.12.0",engines:{node:">=18.0.0"},scripts:{build:"tsup",clean:"git clean -xdf .turbo node_modules dist",format:'prettier --check "**/*.{ts,tsx}"',lint:"eslint . && node scripts/check-eslint-suppressions.mjs","pack:smoke":"node scripts/pack-smoke.mjs","peer:smoke":"node scripts/peer-compat-smoke.mjs",prepublishOnly:"pnpm run verify",test:"vitest run","test:coverage":"vitest run --coverage.enabled","test:watch":"vitest",typecheck:"tsc --noEmit --incremental false",verify:"pnpm run typecheck && pnpm run format && pnpm run lint && pnpm run test:coverage && pnpm run build && pnpm run pack:smoke"},exports:{".":{types:"./dist/index.d.mts",import:"./dist/index.mjs"},"./provider":{types:"./dist/index.d.mts",import:"./dist/index.mjs"},"./server":{types:"./dist/server.d.mts",import:"./dist/server.mjs"},"./memory":{types:"./dist/memory.d.mts",import:"./dist/memory.mjs"},"./memory/legacy":{types:"./dist/memory-legacy.d.mts",import:"./dist/memory-legacy.mjs"},"./memory/store":{types:"./dist/memory-store.d.mts",import:"./dist/memory-store.mjs"},"./next":{types:"./dist/plugin-next.d.mts",import:"./dist/plugin-next.mjs",default:"./dist/plugin-next.mjs"},"./vite":{types:"./dist/plugin-vite.d.mts",import:"./dist/plugin-vite.mjs",default:"./dist/plugin-vite.mjs"},"./astro":{types:"./dist/plugin-astro.d.mts",import:"./dist/plugin-astro.mjs",default:"./dist/plugin-astro.mjs"},"./media":{types:"./dist/media.d.mts",import:"./dist/media.mjs"},"./three":{types:"./dist/adapter-three.d.mts",import:"./dist/adapter-three.mjs"},"./react-three-fiber":{types:"./dist/adapter-react-three-fiber.d.mts",import:"./dist/adapter-react-three-fiber.mjs"}},files:["dist","codex-plugin","LICENSE","README.md","THIRD_PARTY_NOTICES.md"],dependencies:{"modern-screenshot":"4.7.0",ws:"8.21.3"},peerDependencies:{"lucide-react":">=1.23.0",next:">=14.0.0",react:">=18.0.0","react-dom":">=18.0.0","@react-three/fiber":">=8.0.0",three:">=0.150.0"},peerDependenciesMeta:{next:{optional:!0},"@react-three/fiber":{optional:!0},three:{optional:!0}},devDependencies:{"@kit/eslint-config":"workspace:^","@react-three/fiber":"9.7.0","@tanstack/react-router":"1.120.5","@types/react":"19.1.0","@types/react-dom":"19.1.2","@types/three":"^0.185.4","@types/ws":"8.18.1","@vitejs/plugin-react":"^5.1.1","@vitest/coverage-v8":"3.2.4",eslint:"^9.24.0",geist:"1.7.2",jsdom:"26.1.0","lucide-react":"^1.23.0",next:"15.5.24",prettier:"3.5.3",react:"19.1.0","react-dom":"19.1.0",three:"^0.185.1",tsup:"^8.5.1",typescript:"^5.8.3",vite:"7.3.6",vitest:"3.2.4"}};var ot=zi.version;var tc=new Set(["minimal","low","medium","high","xhigh","max","ultra"]),nc=new Set(["default","flex","fast","priority"]);function Zt(t){let e=typeof t.reasoningEffort=="string"&&tc.has(t.reasoningEffort)?t.reasoningEffort:void 0,n=typeof t.serviceTier=="string"&&nc.has(t.serviceTier)?t.serviceTier:void 0;return e||n?{reasoningEffort:e,serviceTier:n}:void 0}function To(t){let e=[];return t?.reasoningEffort&&e.push(`model_reasoning_effort=${JSON.stringify(t.reasoningEffort)}`),t?.serviceTier&&e.push(`service_tier=${JSON.stringify(t.serviceTier)}`),e}import{createHash as fl,randomUUID as hl}from"crypto";import{isAbsolute as gl,relative as yl}from"path";import{randomUUID as yt}from"crypto";var Gi={project:0,route:1,component:2,selector:2,file:3},Wi={intention:0,principle:1,pattern:2};function en(t,e){let n=Gi[t.scope.kind]-Gi[e.scope.kind];return n!==0?n:Wi[t.kind]-Wi[e.kind]}function Hi(t){return t==="expresses"||t==="excepts"}function oc(t,e){if(e.type==="expresses"||e.type==="excepts")return structuredClone(e);if(e.type==="operationalizes")return{...structuredClone(e),type:"expresses"};if(e.type==="instantiates")return{...structuredClone(e),type:"expresses",fromId:e.toId,toId:e.fromId};if(e.type==="refines"){let n=t.explanations.find(i=>i.id===e.fromId),o=t.explanations.find(i=>i.id===e.toId);if(!n||!o)return;let r=en(n,o);return r===0?void 0:r>0?{...structuredClone(e),type:"expresses"}:{...structuredClone(e),type:"expresses",fromId:e.toId,toId:e.fromId}}}function Nt(t){let e=structuredClone(t),n=new Set;return e.relations=t.relations.flatMap(o=>{let r=oc(t,o);if(!r)return[];let i=`${r.type}:${r.fromId}:${r.toStoreId??""}:${r.toId}`;return n.has(i)?[]:(n.add(i),[r])}),e}import{createHash as rc}from"crypto";import{mkdir as ic,open as sc,readFile as Vi,readdir as ac,rename as dc,unlink as cc}from"fs/promises";import{dirname as lc,join as tn}from"path";function We(t){return structuredClone(t)}function Ki(t){return rc("sha256").update(JSON.stringify(t)).digest("hex")}async function Bt(t,e){await ic(lc(t),{recursive:!0});let n=`${t}.${process.pid}.${Date.now()}.tmp`,o=await sc(n,"w");try{await o.writeFile(`${JSON.stringify(e,null,2)}
|
|
8
8
|
`,"utf8"),await o.sync()}finally{await o.close()}try{await dc(n,t)}catch(r){throw await cc(n).catch(()=>{}),r}}function pc(t,e){return{version:1,projectId:t,createdAt:e,updatedAt:e,head:{sequence:0,hash:null},explanations:[],concrete:[],relations:[]}}function uc(t,e){let n=t.explanations.findIndex(o=>o.id===e.id);n>=0?t.explanations[n]=We(e):t.explanations.push(We(e))}function xr(t,e){let n=We(t);for(let o of e)if(o.op==="add_explanation"){if(n.explanations.some(r=>r.id===o.explanation.id))throw new Error(`Imprint explanation already exists: ${o.explanation.id}`);n.explanations.push(We(o.explanation))}else if(o.op==="replace_explanation")uc(n,o.explanation);else if(o.op==="remove_explanation")n.explanations=n.explanations.filter(r=>r.id!==o.explanationId),n.relations=n.relations.filter(r=>r.fromId!==o.explanationId&&(r.toStoreId!==void 0||r.toId!==o.explanationId));else if(o.op==="add_concrete")n.concrete.some(r=>r.id===o.concrete.id)||n.concrete.push(We(o.concrete));else if(o.op==="remove_concrete")n.concrete=n.concrete.filter(r=>r.id!==o.concreteId),n.relations=n.relations.filter(r=>r.fromId!==o.concreteId&&(r.toStoreId!==void 0||r.toId!==o.concreteId));else if(o.op==="add_evidence"){let r=n.explanations.find(i=>i.id===o.explanationId);if(!r)throw new Error(`Unknown imprint explanation: ${o.explanationId}`);r.evidence.some(i=>i.id===o.evidence.id)||(r.evidence.push(We(o.evidence)),r.updatedAt=o.evidence.createdAt)}else if(o.op==="revise_explanation"){let r=n.explanations.find(i=>i.id===o.explanationId);if(!r)throw new Error(`Unknown imprint explanation: ${o.explanationId}`);Object.assign(r,We(o.patch),{updatedAt:o.updatedAt})}else if(o.op==="add_relation"){let r=o.relation,i=n.explanations.find(g=>g.id===r.fromId),s=!!r.toStoreId,a=s?void 0:n.explanations.find(g=>g.id===r.toId),d=n.concrete.some(g=>g.id===r.fromId),c=!s&&n.concrete.some(g=>g.id===r.toId);if(Hi(r.type)){if(!(r.type==="expresses"?s?!!i:!!((i||d)&&a):!!(i&&(a||s))))throw new Error(`Imprint ${r.type} relation has invalid endpoints: ${r.id}`);if(n.relations.some(I=>I.type===r.type&&I.fromId===r.fromId&&I.toStoreId===r.toStoreId&&I.toId===r.toId))throw new Error(`Duplicate imprint relation: ${r.type}:${r.fromId}:${r.toStoreId??""}:${r.toId}`);if(r.type==="expresses"&&i&&a){if(en(i,a)<0)throw new Error(`Imprint expresses relation must point from specific to abstract: ${r.id}`);let I=new Map;for(let b of n.relations)b.type!=="expresses"||b.toStoreId||I.set(b.fromId,[...I.get(b.fromId)??[],b.toId]);let m=[r.toId],S=new Set;for(;m.length;){let b=m.pop();if(b===r.fromId)throw new Error(`Imprint expresses relation would create a cycle: ${r.id}`);S.has(b)||(S.add(b),m.push(...I.get(b)??[]))}}}else if(!i||!a&&!c)throw new Error(`Imprint relation references an unknown explanation: ${r.id}`);n.relations.some(g=>g.id===r.id)||n.relations.push(We(r))}else o.op==="remove_relation"&&(n.relations=n.relations.filter(r=>r.id!==o.relationId));return n}function mc(t,e){let n=[],o=We(t);for(let r of e){if(r.op==="add_explanation")n.unshift({op:"remove_explanation",explanationId:r.explanation.id});else if(r.op==="remove_explanation"){let i=o.explanations.find(s=>s.id===r.explanationId);i&&n.unshift({op:"add_explanation",explanation:We(i)})}else if(r.op==="add_concrete")n.unshift({op:"remove_concrete",concreteId:r.concrete.id});else if(r.op==="remove_concrete"){let i=o.concrete.find(s=>s.id===r.concreteId);i&&n.unshift({op:"add_concrete",concrete:We(i)})}else if(r.op==="replace_explanation"||r.op==="revise_explanation"||r.op==="add_evidence"){let i=r.op==="replace_explanation"?r.explanation.id:r.explanationId,s=o.explanations.find(a=>a.id===i);s&&n.unshift({op:"replace_explanation",explanation:We(s)})}else if(r.op==="add_relation")n.unshift({op:"remove_relation",relationId:r.relation.id});else if(r.op==="remove_relation"){let i=o.relations.find(s=>s.id===r.relationId);i&&n.unshift({op:"add_relation",relation:We(i)})}o=xr(o,[r])}return n}function Qi(t){return t.op==="add_explanation"||t.op==="replace_explanation"?[t.explanation.id]:t.op==="remove_explanation"||t.op==="add_evidence"||t.op==="revise_explanation"?[t.explanationId]:t.op==="add_relation"?[t.relation.fromId,...t.relation.toStoreId?[]:[t.relation.toId]]:[]}var Po=class{constructor(e,n){this.projectRoot=e;this.projectId=n;this.writeChain=Promise.resolve();this.imprintDir=tn(e,".popmelt","imprint"),this.manifestPath=tn(this.imprintDir,"manifest.json"),this.graphPath=tn(this.imprintDir,"graph.json"),this.journalDir=tn(this.imprintDir,"journal")}async readHead(){return(await this.readManifest()).head}async readManifest(){try{let e=JSON.parse(await Vi(this.manifestPath,"utf8"));if(e.version!==1||e.projectId!==this.projectId)throw new Error("Imprint manifest does not match this project.");return e}catch(e){if(e.code!=="ENOENT")throw e;let n=new Date().toISOString();return{version:1,projectId:this.projectId,createdAt:n,updatedAt:n,head:{sequence:0,hash:null}}}}async loadRaw(){let e=await this.readManifest(),o=(await ac(this.journalDir).catch(a=>{if(a.code==="ENOENT")return[];throw a})).filter(a=>/^\d{12}-.+\.json$/.test(a)).sort(),r=pc(this.projectId,e.createdAt),i=[],s=null;for(let a=0;a<o.length;a++){let d=JSON.parse(await Vi(tn(this.journalDir,o[a]),"utf8")),{hash:c,...g}=d;if(d.sequence!==a+1||d.previousHash!==s||c!==Ki(g))throw new Error(`Imprint journal hash mismatch at sequence ${d.sequence}.`);r=xr(r,d.mutations),r.updatedAt=d.committedAt,r.head={sequence:d.sequence,hash:d.hash},i.push(d),s=d.hash}return{graph:r,transactions:i,recoveredTransactions:Math.max(0,i.length-e.head.sequence)}}async load(){let e=await this.loadRaw();return{...e,graph:Nt(e.graph)}}async migrateTopology(){let e=await this.readManifest();if(e.topologyVersion===2)return{migrated:!1,relationMutations:0};let n=await this.loadRaw(),o=Nt(n.graph),r=new Map(o.relations.map(s=>[s.id,s])),i=[];for(let s of n.graph.relations){let a=r.get(s.id);a&&a.type===s.type&&a.fromId===s.fromId&&a.toStoreId===s.toStoreId&&a.toId===s.toId||(i.push({op:"remove_relation",relationId:s.id}),a&&i.push({op:"add_relation",relation:a}))}if(i.length){let s=new Date().toISOString();return await this.commit({transactionId:`topology-v2-${n.graph.head.sequence+1}`,committedAt:s,source:{kind:"migration",threadId:"imprint-topology",jobId:"imprint-topology-v2"},summary:"Simplify Imprint topology to expresses and excepts",mutations:i}),{migrated:!0,relationMutations:i.length}}return await Bt(this.graphPath,o),await Bt(this.manifestPath,{...e,topologyVersion:2}),{migrated:!0,relationMutations:0}}async commit(e){let n;return this.writeChain=this.writeChain.then(async()=>{let o=await this.load();if(o.transactions.some(I=>I.transactionId===e.transactionId))throw new Error(`Imprint transaction already exists: ${e.transactionId}`);let r=o.graph.head.sequence+1,i=mc(o.graph,e.mutations),s={version:1,sequence:r,transactionId:e.transactionId,projectId:this.projectId,committedAt:e.committedAt,previousHash:o.graph.head.hash,source:e.source,summary:e.summary,mutations:We(e.mutations),inverseMutations:i,...e.undoesTransactionId?{undoesTransactionId:e.undoesTransactionId}:{}},a={...s,hash:Ki(s)},d=xr(o.graph,a.mutations);d.updatedAt=e.committedAt,d.head={sequence:r,hash:a.hash};let c=`${String(r).padStart(12,"0")}-${e.transactionId}.json`;await Bt(tn(this.journalDir,c),a);let g={version:1,topologyVersion:2,projectId:this.projectId,createdAt:d.createdAt,updatedAt:e.committedAt,head:d.head};await Bt(this.graphPath,d),await Bt(this.manifestPath,g),n={graph:d,transaction:a}}),await this.writeChain,n}async undo(e,n,o,r){let i=await this.load(),s=i.transactions.find(c=>c.transactionId===e);if(!s)throw new Error("The imprint update is no longer available to undo.");if(i.transactions.some(c=>c.undoesTransactionId===e))throw new Error("This imprint update has already been undone.");let a=new Set(s.mutations.flatMap(Qi));if(i.transactions.filter(c=>c.sequence>s.sequence).some(c=>c.mutations.some(g=>Qi(g).some(I=>a.has(I)))))throw new Error("This imprint update has newer dependent changes and cannot be undone cleanly. Edit or challenge the current explanation instead.");return this.commit({transactionId:r,committedAt:o,source:n,summary:`Undo: ${s.summary}`,mutations:s.inverseMutations,undoesTransactionId:e})}async recover(){let e=await this.load();return await Bt(this.graphPath,e.graph),await Bt(this.manifestPath,{version:1,topologyVersion:2,projectId:this.projectId,createdAt:e.graph.createdAt,updatedAt:e.graph.updatedAt,head:e.graph.head}),{...e,recoveredTransactions:0}}};var fc=new Set(["application_only","transient_state","concrete_without_model_path","duplicate","insufficient_evidence","task_specific_detail","no_shared_understanding_change"]),hc=new Set(["about","after","again","before","being","change","from","have","into","make","should","that","the","their","this","with","would","here","only","consistent","match","matched","matching","mirror","mirrored","mirroring","align","aligned","aligning"]),gc=[/\bconsistent\s+with\b/i,/\bmatch(?:es|ed|ing)?\b/i,/\bmirror(?:s|ed|ing)?\b/i,/\bthe\s+same\s+as\b/i,/\bborrow(?:s|ed|ing)?\s+from\b/i,/\balign(?:s|ed|ing)?\s+with\b/i,/\b(?:more|look|looks|feel|feels|behave|behaves)\s+like\b/i],yc=/\b(all|any|every|everywhere|across|throughout|globally|project[- ]wide)\b/i;function Tr(t){return!!(t&&yc.test(t))}function Ic(t){return t.scope.kind==="project"||Tr(`${t.statement} ${t.rationale}`)}function wc(t){let e=t?.scope.values.filter(n=>n!=="*").join(", ");return e?`Does applying this understanding here match your intent for ${e}?`:"Does applying this understanding here match your intent?"}function vc(t){return t.replace(/([a-z0-9])([A-Z])/g,"$1 $2").toLowerCase().replace(/[^a-z0-9]+/g," ").trim().replace(/\s+/g," ")}function bc(t){return t.length>4&&t.endsWith("ies")?[t,`${t.slice(0,-3)}y`]:t.length>3&&t.endsWith("s")&&!t.endsWith("ss")?[t,t.slice(0,-1)]:[t]}function gt(t){return new Set(vc(t).split(" ").filter(e=>e.length>2&&!hc.has(e)).flatMap(bc))}function Xi(t){return t.replace(/^\s*(?:please\s+)?(?:let(?:'|’)s\s+)?(?:make|apply|use|keep|change|update)\s+/i,"").replace(/[\s,.:;!?]+$/g,"").trim().slice(0,240)}function xc(t){if(t)for(let e of gc){let n=e.exec(t);if(!n||n.index<=0)continue;let o=Xi(t.slice(0,n.index)),r=Xi(t.slice(n.index+n[0].length));if(o&&r)return{target:o,reference:r}}}function jt(t,e){if(!t.size||!e.size)return 0;let n=0;for(let o of t)e.has(o)&&(n+=1);return n/Math.min(t.size,e.size)}function Sr(t){return!t||!Array.isArray(t.values)||t.values.length===0?{kind:"project",values:["*"]}:{kind:t.kind,values:[...new Set(t.values.map(e=>e.trim()).filter(Boolean))]}}function At(t,e,n){return{id:yt(),kind:e==="excepts"?"exception":e==="challenges"?"correction":"observation",stance:e,summary:n,threadId:t.threadId,jobId:t.jobId,createdAt:t.occurredAt??new Date().toISOString()}}function Se(t,e=800){return typeof t=="string"?t.trim().slice(0,e):""}function Yi(t){if(!t||typeof t!="object"||Array.isArray(t))return;let e=t;if(!["project","route","component","selector","file"].includes(String(e.kind))||!Array.isArray(e.values))return;let n=e.values.filter(o=>typeof o=="string"&&!!o.trim());return n.length?{kind:e.kind,values:n}:void 0}function Zi(t,e,n){if(t&&typeof t=="object"&&!Array.isArray(t)){let i=t,s=Se(i.explanationId,100),a=Se(i.clientId,100),d=Se(i.storeId,100);if(s&&a)throw new Error("relation endpoint must use either explanationId or clientId, not both");let c=s||a;return c?{explanationId:c,...d?{storeId:d}:{}}:void 0}let o=Se(e,100),r=Se(n,100);return o?{explanationId:o,...r?{storeId:r}:{}}:void 0}function es(t,e){if(!t||!e.trim())return{found:!1};let n=t;for(let o of e.split(".").filter(Boolean))if(Array.isArray(n)){let r=Number(o);if(!Number.isInteger(r)||r<0||r>=n.length)return{found:!1};n=n[r]}else if(n&&typeof n=="object"&&Object.prototype.hasOwnProperty.call(n,o))n=n[o];else return{found:!1};return{found:!0,value:n}}function On(t,e={}){let n=[...t.matchAll(/<imprint>([\s\S]*?)<\/imprint>/g)],o=t.replace(/<imprint>[\s\S]*?<\/imprint>/g,"").replace(/<imprint>[\s\S]*$/g,"").trim(),r=n.at(-1)?.[1],i,s=[];if(r)i=r;else{let d=[...t.matchAll(/<resolution>\s*([\s\S]*?)\s*<\/resolution>/g)].at(-1)?.[1];if(!d)return{visibleText:o,error:"imprint assessment is missing"};try{let c=JSON.parse(d);if(!c||typeof c!="object"||Array.isArray(c)||!Object.prototype.hasOwnProperty.call(c,"imprint"))return{visibleText:o,error:"imprint assessment is missing"};let g=c;i=g.imprint,s=Array.isArray(g.annotations)?g.annotations:[]}catch{return{visibleText:o,error:"resolution envelope is invalid"}}}try{let a=typeof i=="string"?JSON.parse(i):i;if(!a||typeof a!="object"||Array.isArray(a))throw new Error("imprint assessment is invalid");if(a.decision==="none"){let y=String(a.reason);if(!fc.has(y))throw new Error("imprint no-op reason is invalid");if(y==="task_specific_detail"&&s.some(A=>{if(!A||typeof A!="object"||Array.isArray(A))return!1;let $=A;return($.finalScope??$.inferredScope)?.breadth==="pattern"}))throw new Error("pattern-scoped resolution cannot be classified as task_specific_detail");let p=Array.isArray(a.usedExplanationIds)?a.usedExplanationIds.filter(A=>typeof A=="string"&&!!A.trim()):[],w=Array.isArray(a.appliedExplanationIds)?a.appliedExplanationIds.filter(A=>typeof A=="string"&&!!A.trim()):[],v=new Set(e.context?.explanations.map(A=>A.id)??[]);if([...new Set([...p,...w])].some(A=>!v.has(A)))throw new Error("imprint use must cite only surfaced explanations");let x=[...new Set([...p,...e.applicationOccurred?[]:w])],f=e.applicationOccurred?w:[],h;if(f.length){h=f;let A=new Set(e.context?.pendingGroundingExplanationIds??[]),$=f.filter(E=>{let F=e.context?.explanations.find(Y=>Y.id===E);return v.has(E)&&F&&(F.state==="emerging"||F.state==="testing")&&!A.has(E)});if($.length){let E=a.grounding,F=typeof E?.explanationId=="string"&&$.includes(E.explanationId)?E.explanationId:$[0],Y=Se(E?.question,300);return{draft:{decision:"apply",summary:{title:Se(a.summary?.title,120)||"Applied shared understanding",detail:Se(a.summary?.detail,500)||Se(e.requestText||o,500)||"Applied an ungrounded explanation in a new context."},appliedExplanationIds:f,grounding:{explanationId:F,question:Y||"Does applying this understanding here match your intent?"}},visibleText:o}}}if(y==="duplicate"){let A=Se(a.duplicateOfExplanationId,100),$=a.scopeComparison;if(!A)throw new Error("duplicate assessment requires duplicateOfExplanationId");if($!=="equivalent"&&$!=="existing_broader")throw new Error("duplicate assessment requires an equivalent or existing_broader scopeComparison");let E=e.context?.explanations.find(F=>F.id===A);if(!E)throw new Error("duplicate assessment must cite a surfaced explanation");if(Tr(e.requestText)&&!Ic(E))throw new Error("duplicate explanation does not cover the requested broader scope");return{draft:{decision:"none",reason:y,duplicateOfExplanationId:A,scopeComparison:$,...x.length?{usedExplanationIds:x}:{},...h?{appliedExplanationIds:h}:{}},visibleText:o}}return{draft:{decision:"none",reason:y,...x.length?{usedExplanationIds:x}:{},...h?{appliedExplanationIds:h}:{}},visibleText:o}}if(a.decision==="recognize"){let y=Se(a.explanationId,100);if(!y)throw new Error("recognition requires explanationId");let p=e.context?.explanations.find(u=>u.id===y);if(!p)throw new Error("recognition must cite a surfaced explanation");if(p.state!=="grounded")throw new Error("recognition must cite a grounded explanation");let w=a.summary,v={title:Se(w?.title,120),detail:Se(w?.detail,500)};if(!v.title||!v.detail)throw new Error("recognition summary title and detail are required");return{draft:{decision:"recognize",explanationId:y,summary:v},visibleText:o}}if(a.decision==="apply"){let y=a.summary,p={title:Se(y?.title,120),detail:Se(y?.detail,500)};if(!p.title||!p.detail)throw new Error("application summary title and detail are required");let w=Array.isArray(a.appliedExplanationIds)?a.appliedExplanationIds.filter(A=>typeof A=="string"&&!!A.trim()):[];if(!w.length)throw new Error("application requires appliedExplanationIds");let v=new Map(e.context?.explanations.map(A=>[A.id,A])??[]);if(w.some(A=>!v.has(A)))throw new Error("application must cite only surfaced explanations");if(!e.applicationOccurred)return{draft:{decision:"none",reason:"application_only",usedExplanationIds:[...new Set(w)]},visibleText:o};let u=new Set(e.context?.pendingGroundingExplanationIds??[]),x=w.filter(A=>{let $=v.get(A);return $&&($.state==="emerging"||$.state==="testing")&&!u.has(A)});if(!x.length){if(w.every($=>v.get($)?.state==="grounded"||u.has($)))return{draft:{decision:"none",reason:"application_only",appliedExplanationIds:w},visibleText:o};throw new Error("application must cite a surfaced ungrounded explanation")}let f=a.grounding,h=typeof f?.explanationId=="string"&&x.includes(f.explanationId)?f.explanationId:x[0];return{draft:{decision:"apply",summary:p,appliedExplanationIds:w,grounding:{explanationId:h,question:Se(f?.question,300)||"Does applying this understanding here match your intent?"}},visibleText:o}}if(a.decision!==void 0&&a.decision!=="mutate")throw new Error("imprint decision is invalid");let d=a.summary,c=d?.kind;if(!["added","strengthened","refined","challenged","exception","consolidated"].includes(String(c)))throw new Error("summary.kind is invalid");let g={kind:c,title:Se(d?.title,120),detail:Se(d?.detail,500)};if(!g.title||!g.detail)throw new Error("summary title and detail are required");let I=Array.isArray(a.appliedExplanationIds)?a.appliedExplanationIds.filter(y=>typeof y=="string"&&!!y.trim()):[],m=[];for(let y of Array.isArray(a.mutations)?a.mutations:[]){if(!y||typeof y!="object")continue;let p=y;if(p.op==="add"&&["intention","principle","pattern"].includes(String(p.kind))){let w=Se(p.statement),v=Se(p.rationale);if(!w||!v)continue;m.push({op:"add",clientId:Se(p.clientId,100)||void 0,kind:p.kind,statement:w,rationale:v,scope:Yi(p.scope),rootReason:Se(p.rootReason,300)||void 0})}else if(p.op==="revise"&&typeof p.explanationId=="string"){let w={op:"revise",explanationId:p.explanationId,statement:Se(p.statement)||void 0,rationale:Se(p.rationale)||void 0,scope:Yi(p.scope),reopenGrounding:p.reopenGrounding===!0};(w.statement||w.rationale||w.scope||w.reopenGrounding)&&m.push(w)}else if(p.op==="evidence"&&typeof p.explanationId=="string"&&["supports","challenges","excepts"].includes(String(p.stance))){let w=Se(p.summary);w&&m.push({op:"evidence",explanationId:p.explanationId,stance:p.stance,summary:w})}else if(p.op==="relation"){if(!["expresses","excepts"].includes(String(p.type)))throw new Error("relation type is invalid");let w=Se(p.rationale),v=Zi(p.from,p.fromId,p.fromStoreId),u=Zi(p.to,p.toId,p.toStoreId);if(!w)throw new Error("relation rationale is required");if(!v||!u)throw new Error("relation endpoints require an explanationId or clientId");m.push({op:"relation",type:p.type,from:v,to:u,rationale:w})}else if(p.op==="supersede"&&typeof p.explanationId=="string"&&typeof p.replacementId=="string"){let w=Se(p.reason);w&&m.push({op:"supersede",explanationId:p.explanationId,replacementId:p.replacementId,reason:w})}else if(p.op==="bind_concrete"&&typeof p.explanationId=="string"){let w=Se(p.modelPath,300);w&&m.push({op:"bind_concrete",explanationId:p.explanationId,modelPath:w,rationale:Se(p.rationale)})}else p.op==="retire"&&typeof p.explanationId=="string"&&m.push({op:"retire",explanationId:p.explanationId,reason:Se(p.reason)})}let S=a.grounding,b=S&&typeof S.explanationId=="string"&&Se(S.question)?{explanationId:S.explanationId,question:Se(S.question,300)}:void 0;return{draft:{decision:"mutate",summary:g,appliedExplanationIds:I,mutations:m,grounding:b},visibleText:o}}catch(a){return{visibleText:o,error:a instanceof Error?a.message:String(a)}}}function Dn(t,e={}){let n=t?.explanations??[],o=new Set(t?.pendingGroundingExplanationIds??[]),r=new Set(t?.correctionExplanationIds??[]),i=Tr(e.requestText),s=t?.retrievalIntent,a=t?.historicalCaveats??[],d=n.find(m=>m.storeId&&!m.readOnly)?.storeId,c=(m,S)=>{let b=m[S];return b?`[${b.storeId}] ${b.explanationId}`:S==="from"?m.fromId:m.toId},g=!!t?.relations?.some(m=>m.from&&m.to&&m.from.storeId!==m.to.storeId),I=n.some(m=>m.readOnly);return["","## Continuous imprinting","","An imprint is provisional shared understanding learned through human-AI collaboration. It is advisory, not authoritative product truth. Accepted product decisions above always win when they conflict.",...n.length?["","Relevant explanations from the project imprint:",...n.map((m,S)=>[`${S+1}. [${m.state}; ${m.kind}] ${m.statement}`,` - Why: ${m.rationale}`,` - Scope: ${m.scope.kind} ${m.scope.values.join(", ")}`,` - Explanation: ${m.id}`,...m.storeId?[` - Store: ${m.storeId}${m.readOnly?" (read-only)":" (writable primary)"}`]:[],...o.has(m.id)?[" - Grounding: already pending; do not ask again"]:[],...r.has(m.id)?[" - Correction: the developer selected No for this explanation and the current reply supplies the missing correction context"]:[],...m.concrete.map(b=>` - Concrete terminal: ${b.modelPath} = ${JSON.stringify(b.value)}`)].join(`
|
|
9
9
|
`)),...t?.truncated?["Additional imprint context exists outside this bounded packet."]:[]]:[],...t?.relations?.length?["","Known explanation topology:",...t.relations.map(m=>`- ${m.storeId?`[owned by ${m.storeId}${m.readOnly?"; read-only":""}] `:""}${c(m,"from")} --${m.type}--> ${c(m,"to")}: ${m.rationale}`)]:[],...I||g?["","Store-qualified topology remains locally owned: the relation must originate at an explanation in the writable primary store, while its target may be in a mounted read-only store. Never proxy a relation whose source belongs to a mounted store.",`For a cross-store relation, use qualified endpoints: {"op":"relation","type":"expresses|excepts","from":{"storeId":"${d??"<writable-primary-store>"}","clientId":"<new-specific-client-id>"},"to":{"storeId":"<mounted-target-store>","explanationId":"<existing-abstract-target-id>"},"rationale":"<why>"}. Use clientId for a new explanation created in this transaction and explanationId for an existing durable explanation.`]:[],...s?["","Explicit pattern transfer detected:",`- Target: ${s.target}`,`- Reference: ${s.reference}`,"Before editing, state which dimensions you intend to transfer from the reference and which target-specific behavior you will preserve. Compare the relevant surface, spacing, typography, radius, and interaction behavior; transfer only the dimensions supported by the request and evidence. If the intended transfer remains materially ambiguous after inspecting both anchors, ask one focused question."]:[],...a.length?["","Directly relevant challenged history (historical caveats, not current Imprint truth):",...a.map((m,S)=>[`${S+1}. [challenged; ${m.kind}] ${m.statement}`,` - Why it was previously proposed: ${m.rationale}`,` - Scope: ${m.scope.kind} ${m.scope.values.join(", ")}`,` - Historical explanation: ${m.id}`,...m.storeId?[` - Store: ${m.storeId}${m.readOnly?" (read-only)":" (writable primary)"}`]:[]].join(`
|
|
10
10
|
`)),"Use these caveats only to avoid repeating a rejected or over-broad transfer. Do not cite them as usedExplanationIds or treat them as active guidance."]:[],...n.some(m=>m.readOnly)?["","Mounted-store explanations are read-only context. They may inform the work and be cited as used understanding, but do not mutate, ground, revise, or add evidence to them without an explicit writable orientation for that store."]:[],...t?.unavailableStoreIds?.length?["",`Some mounted Imprint knowledge was unavailable during this bounded read: ${t.unavailableStoreIds.join(", ")}. Do not infer its contents.`]:[],...t?.unavailableTopologyStoreIds?.length?["",`Some mounted Imprint topology was unavailable during this bounded read: ${t.unavailableTopologyStoreIds.join(", ")}. Available explanations remain usable, but do not infer missing relationships.`]:[],"","Work normally and preserve developer agency. A testing explanation may guide action as a hypothesis without asking permission first. An emerging explanation is observation-only: use it to recognize recurrence, not to choose behavior. If the developer independently repeats an emerging observation in a meaningfully different occurrence, that new application may promote it into testing and request grounding. Silence is not confirmation.","Assess learning proactively as part of completing every thread. Do not wait for the developer to ask whether there was a learning. Before choosing none, look one abstraction level above the exact edit and ask: would the reasoning behind this outcome change how a future agent handles a meaningfully different occurrence?","The developer's original annotation, ordinary instruction, scope language, and later corrections are already human-supplied evidence. They can support learning when they establish a reusable dependency, boundary, or rationale; a separate post-completion learning prompt is not required. Mere acceptance of completed work is still insufficient.","The default outcome is no imprint mutation. Completing work or making a successful design choice does not by itself constitute learning.","Application evidence is distinct from changing shared understanding. When an emerging or testing explanation materially influenced a completed action, report decision apply even when no explanation wording changed.","Applying grounded understanding does not mutate the graph. Usually report none. Use decision recognize only when visibly acknowledging the grounded explanation closes a correction loop or otherwise makes consequential reuse legible to the developer. Recognition must cite one exact surfaced grounded explanation ID; never infer or invent an ID.","Application and recognition are whole-statement operations. Cite an existing explanation only when the completed behavior is compatible with every material clause, its scope, and its exceptions. Sharing one useful clause is not a match. If the durable behavior is narrower, add one atomic explanation linked to the broader explanation when the admission tests pass; otherwise report none.","Mutate the imprint only when this collaboration changed reusable shared understanding: a future agent facing a meaningfully different task would behave differently because of what was learned here.","Relationship-first admission: before calling any completed change task-specific, compare its requested behavior and effective scope with every surfaced explanation that could plausibly govern the same subject or treatment. The developer does not need to name an Imprint concept or relation. Infer the relationship from the ordinary instruction, the annotated context, the implementation, and the surfaced explanations.","A narrower completed behavior that departs from an applicable broader explanation establishes an applicability boundary even when the developer did not say exception. Add one narrow testing explanation and link it to the broader rule with excepts. If the behaviors concern distinct concepts rather than the same rule, assess the reusable behavior as a sibling explanation instead. Do not manufacture an exception merely because implementations differ.","Scope and Imprint assessment must agree. If any resolved annotation has an effective inferred or final breadth of pattern, do not classify the Imprint decision as task_specific_detail. Either capture the reusable conceptual change, report application of existing understanding, cite a valid duplicate, or use another accurate no-op reason. The bridge rejects pattern-scoped task_specific_detail assessments.","","Before proposing a mutation, apply every admission test:","1. Reusable: it guides a future decision beyond reproducing this exact edit.","2. Novel: it is not already expressed by an explanation or its evidence.","3. Durable: it is an intention, rationale, relationship, or recurring pattern, not temporary implementation state.","4. Atomic: it expresses one claim at one abstraction level.","5. Scoped: its applicability can be named without silently generalizing.","6. Supported: the human supplied evidence, correction, or confirmation anywhere in the thread, including the original instruction, not merely acceptance of completed work or the model's own implementation choice.","If any test fails, return a decision of none.","","Usually not learning: exact copy substitutions, one-pixel adjustments, element reordering, implementation mechanics, keeping something hidden temporarily, applying an already-known pattern, or a stylistic choice made without human feedback. Distinguish mechanics from the reusable reasoning they may reveal: a CSS property alone is not learning, but a human-directed causal rule such as keeping optical adjustment out of layout geometry may be. This heuristic never overrides relationship-first admission: a small local change that narrows or contradicts an applicable surfaced explanation may establish an exception or correction. A task-specific edit otherwise becomes learning only if the human establishes a reusable dependency, applicability boundary, or rationale beyond that edit.","When a candidate overlaps an existing explanation, consolidate before adding. Same meaning plus a new observation means evidence only. A concrete application means bind_concrete, not a broader explanation. A scope correction means revise scope. A direct disagreement means add the replacement and supersede the contradicted explanation. A different abstraction level means a separate related explanation.","Duplicate means the same reusable claim is already represented at an equivalent or broader conceptual scope. Shared code, a centralized component, or implementation reuse does not establish conceptual duplication. A narrower component-, route-, selector-, or file-scoped explanation is not a duplicate of newly broadened applicability.","Words such as all, every, across, throughout, globally, and project-wide are explicit applicability signals. When the human broadens a grounded explanation, preserve the grounded narrower explanation and add a broader testing explanation. Link the narrower explanation to the broader one with expresses. Do not substitute another no-op reason for that scope change.",...i?["CURRENT SCOPE SIGNAL: The developer used explicit broadening language in this instruction. Do not classify it as duplicate unless the cited existing explanation already expresses universal or project-wide applicability."]:[],"Keep correction identity explicit. If feedback refines an emerging or testing explanation without rejecting its underlying claim, revise it and set reopenGrounding true; do not also add challenging evidence to that same explanation. If feedback rejects the claim as written, preserve that rejected wording, add a distinct replacement, and supersede the old explanation. Never revise an explanation and mark that same identity challenged in one update.","When an explanation is marked Correction in the supplied context, the developer selected No but the graph has not yet been changed. Interpret the current reply before mutating anything. Decide whether it corrects only the implementation result, the explanation scope, or the reasoning. Do not challenge the whole explanation for a narrow correction, and do not recognize a different explanation merely because one clause overlaps.","Topology assessment is mandatory for every add mutation. Imprint topology has exactly two relation types: expresses and excepts. expresses always points from the more specific, concrete, or lower-level explanation to the more abstract explanation it expresses. excepts points from the narrower exception to the rule whose applicability it limits. Support and challenge belong in evidence; correction belongs in supersede; neither is a topology relation. If an addition is genuinely a new root with no meaningful relative, include rootReason explaining why. Unmapped additions without rootReason are rejected.","Never append implementation facts to an intention merely because they occurred in the same component. Grounded explanations are append-only: add evidence for a compatible observation, but never revise or demote the grounded explanation. A material change in meaning, causal rationale, abstraction level, or applicability must be a new testing explanation linked with expresses or excepts. Multiple specific explanations may express the same parent, and one explanation may express multiple parents; never create an expresses cycle.","Grounded explanations may support new higher-level theory. Emerging explanations must not be the sole basis for another durable explanation. Concrete project facts should remain concrete; do not turn a one-off value into a universal principle.","Request grounding when an ungrounded explanation materially influenced the action, this application produced genuinely new evidence, and that explanation does not already have grounding pending. Ask at most one grounding question per thread. The bridge supplies a fallback question when the application is otherwise valid.","","At the end, emit exactly one machine-readable <resolution> envelope after the normal response. Its `annotations` array reports annotation outcomes, and its required `imprint` field completes the learning assessment. The envelope is hidden from the developer. Do not emit a separate <imprint> block.",'As soon as one or more surfaced explanations become actively useful during the work, emit `<imprint-use>{"explanationIds":["<every actively useful surfaced explanation id>"]}</imprint-use>` immediately. This live receipt is hidden from conversational prose. Emit it at the point of use rather than waiting for the final response. Emit a later marker only when additional explanations become actively useful. Never emit it for explanations that were merely retrieved, read, searched, or considered.',"Treat Imprint as actively useful when a surfaced explanation materially informs an answer, recommendation, implementation choice, or other decision. Whenever it is actively useful, always cite every materially used surfaced explanation in `usedExplanationIds`. This produces a usage receipt only; it does not strengthen or mutate shared understanding. Do not cite explanations that were merely retrieved, read, or considered but did not influence the result.","`appliedExplanationIds` is narrower: include an explanation there only when it governed a completed implementation or other observable action that genuinely tests the explanation. Never use `appliedExplanationIds` for an answer-only, informational, research, or read-only request. An explanation may be used without being applied; every applied explanation is also understood to be used.","For no learning, include:",'"imprint":{"decision":"none","reason":"application_only|transient_state|concrete_without_model_path|duplicate|insufficient_evidence|task_specific_detail|no_shared_understanding_change","usedExplanationIds":["<every surfaced explanation that was actively useful, or omit when none>"]}',"A no-op reason cannot suppress genuine application evidence. When a completed action was governed by an ungrounded explanation, report decision apply directly so the summary and grounding question are intentional. Answering a question with that explanation is usage, not application.","When and only when reason is duplicate, also include `duplicateOfExplanationId` naming one surfaced explanation and `scopeComparison` set to `equivalent` or `existing_broader`. The bridge rejects uncited duplicates and duplicates whose cited explanation cannot cover explicit broadening language.","For application evidence without a conceptual change, include:",'"imprint":{"decision":"apply","summary":{"title":"<short expandable-summary title>","detail":"<how the explanation influenced this completed action>"},"appliedExplanationIds":["<surfaced emerging or testing explanation id>"],"grounding":{"explanationId":"<one applied id>","question":"<brief post-application confirmation question>"}}',"For a consequential, non-mutating reuse of grounded understanding that should be visible, include:",'"imprint":{"decision":"recognize","explanationId":"<one exact surfaced grounded explanation id>","summary":{"title":"<short name for the recognized understanding>","detail":"<how it guided this outcome>"}}',"Or, only after every admission test passes for a conceptual change:",'"imprint":{"decision":"mutate","summary":{"kind":"added|strengthened|refined|challenged|exception|consolidated","title":"<short expandable-summary title>","detail":"<what changed in shared understanding>"},"appliedExplanationIds":["<only an ungrounded explanation materially tested here>"],"mutations":[{"op":"add","clientId":"new-1","kind":"intention|principle|pattern","statement":"<one durable atomic explanation>","rationale":"<why the evidence supports it>","scope":{"kind":"project|route|component|selector|file","values":["<scope>"]},"rootReason":"<only when this is genuinely unrelated to every surfaced or new explanation>"},{"op":"revise","explanationId":"<only an emerging or testing id>","statement":"<optional>","rationale":"<optional>","scope":{"kind":"component","values":["CardTile"]},"reopenGrounding":false},{"op":"evidence","explanationId":"<id>","stance":"supports|challenges|excepts","summary":"<thread evidence>"},{"op":"relation","type":"expresses|excepts","from":{"clientId":"<new-specific-client-id>"},"to":{"explanationId":"<existing-abstract-id>"},"rationale":"<why; use clientId for new transaction-local explanations and explanationId for existing durable explanations>"},{"op":"supersede","explanationId":"<contradicted old id>","replacementId":"<new clientId or existing id>","reason":"<direct conflict>"},{"op":"bind_concrete","explanationId":"<id or clientId>","modelPath":"<exact existing model path>","rationale":"<how this fact expresses the explanation>"},{"op":"retire","explanationId":"<id>","reason":"<why>"}],"grounding":{"explanationId":"<one applied ungrounded id or clientId>","question":"<brief post-application confirmation question>"}}','The final shape is `<resolution>{"annotations":[...],"imprint":{...}}</resolution>`. The `imprint` field is required even when the decision is none; omission leaves the completed run explicitly unassessed.',"Include only mutation objects actually needed. A new explanation established through completed implementation must include its clientId in appliedExplanationIds; the bridge also derives this application from a completed change as a safeguard. Applying a grounded explanation never requires a mutation, but actively using it still requires its id in appliedExplanationIds. Never claim that a new explanation is grounded."].join(`
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { PropsWithChildren } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
export {
|
|
3
|
+
import { P as PopmeltSubjectAdapter, A as Annotation, E as ElementInfo, a as AnnotationAttachment, S as SubjectFeedbackAttachment, b as StyleModification, T as TextModification, D as DomModification, c as SpacingTokenChange } from './subjects-CeqtFged.mjs';
|
|
4
|
+
export { d as AnnotationType, B as BridgeStatus, C as ClaudeResponse, I as ImageObjectContext, O as OpaqueTargetReference, e as Point, f as PopmeltSubject, g as PopmeltSubjectBounds, h as PopmeltSubjectCreationSession, i as PopmeltSubjectTrackingAnchor, j as PopmeltSurfaceSnapshot, R as ReactSourceLocation, k as StyleChange, l as ToolType, V as VideoObjectContext } from './subjects-CeqtFged.mjs';
|
|
5
|
+
import { I as ImageRecognitionProvider, V as VideoTrackingProvider, a as ImageSubjectAdapterOptions, b as VideoSubjectAdapterOptions } from './video-adapter-Gq8saCDn.mjs';
|
|
5
6
|
|
|
6
7
|
/** Client-side envelope used for persisted and streaming bridge events. */
|
|
7
8
|
type BridgeEvent = {
|
|
@@ -33,6 +34,12 @@ type ImageAnnotationOptions = Omit<ImageSubjectAdapterOptions, 'id'>;
|
|
|
33
34
|
|
|
34
35
|
type VideoAnnotationOptions = Omit<VideoSubjectAdapterOptions, 'id'>;
|
|
35
36
|
|
|
37
|
+
type PopmeltContextValue = {
|
|
38
|
+
isEnabled: boolean;
|
|
39
|
+
registerSubjectAdapter(adapter: PopmeltSubjectAdapter): () => void;
|
|
40
|
+
};
|
|
41
|
+
declare function usePopmelt(): PopmeltContextValue;
|
|
42
|
+
|
|
36
43
|
declare global {
|
|
37
44
|
interface Window {
|
|
38
45
|
__POPMELT_BRIDGE_URL__?: string;
|
|
@@ -40,9 +47,6 @@ declare global {
|
|
|
40
47
|
}
|
|
41
48
|
}
|
|
42
49
|
|
|
43
|
-
type PopmeltContextValue = {
|
|
44
|
-
isEnabled: boolean;
|
|
45
|
-
};
|
|
46
50
|
type PopmeltProviderProps = PropsWithChildren<{
|
|
47
51
|
enabled?: boolean;
|
|
48
52
|
bridgeUrl?: string;
|
|
@@ -61,7 +65,6 @@ type PopmeltProviderProps = PropsWithChildren<{
|
|
|
61
65
|
videoAnnotation?: boolean | VideoAnnotationOptions;
|
|
62
66
|
}>;
|
|
63
67
|
declare function PopmeltProvider({ children, enabled, bridgeUrl, projectId: projectIdProp, navigate: navigateProp, subjectAdapters, extensions, imageAnnotation, videoAnnotation, }: PopmeltProviderProps): react.JSX.Element;
|
|
64
|
-
declare function usePopmelt(): PopmeltContextValue;
|
|
65
68
|
|
|
66
69
|
type AnnotationData = {
|
|
67
70
|
id: string;
|