dsh-plugin-xujie-memory 0.6.0 → 0.7.0
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/ACCEPTANCE.md +37 -0
- package/BUILD-PROVENANCE.json +53 -0
- package/CHANGELOG.md +12 -0
- package/README.md +48 -23
- package/README_EN.md +32 -9
- package/lib/client.js +17219 -0
- package/lib/controller.js +307 -0
- package/lib/index.js +87 -22
- package/lib/typert.host.d.ts +3 -0
- package/lib/typert.host.js +15104 -0
- package/lib/typert.remote-client.d.ts +40 -0
- package/lib/typert.remote-client.d.ts.map +1 -0
- package/lib/typert.remote-client.js +15093 -0
- package/lib/types.d.ts +13 -0
- package/lib/types.js +1 -0
- package/package.json +49 -5
- package/python/xujie_memory/__init__.py +123 -123
- package/python/xujie_memory/engine.py +1333 -1312
- package/python/xujie_memory/management.py +203 -0
- package/python/xujie_memory/memory.py +27 -21
- package/python/xujie_memory/privacy.py +105 -0
- package/python/xujie_memory/schema.sql +492 -480
- package/python/xujie_memory/server.py +52 -0
- package/scripts/build.mjs +19 -0
- package/scripts/prepack.mjs +9 -0
- package/scripts/test-all.mjs +9 -0
- package/scripts/test-node.mjs +10 -0
- package/src/client/MemoryPanel.tsx +536 -0
- package/src/client/NeuralField.tsx +88 -0
- package/src/client/css.d.ts +4 -0
- package/src/client/icons.tsx +28 -0
- package/src/client/index.tsx +59 -0
- package/src/client/memory-adapter.ts +67 -0
- package/src/client/model.ts +79 -0
- package/src/client/neural/engine.ts +999 -0
- package/src/client/neural/morphology.ts +219 -0
- package/src/client/panel.css +337 -0
- package/src/client/types.ts +6 -0
- package/src/controller.ts +88 -0
- package/src/generated/typert.host.js +449 -0
- package/src/generated/typert.remote-client.js +438 -0
- package/src/index.ts +2 -0
- package/src/types.ts +13 -0
- package/test/cordis-fixture.mjs +5 -0
- package/test/management_regression.py +187 -0
- package/test/node-regression.entry.mjs +52 -0
- package/vendor/DSH-LICENSE.txt +21 -0
- package/vendor/THIRD-PARTY.md +6 -0
- package/vendor/dsh-protocol/index.ts +484 -0
- package/vendor/dsh-protocol/json-value.ts +53 -0
- package/vendor/dsh-protocol/owned-value.ts +39 -0
- package/vendor/dsh-protocol/principal.ts +17 -0
- package/vendor/dsh-protocol/remote-access.ts +38 -0
- package/vendor/dsh-protocol/remote-error.ts +49 -0
- package/vendor/dsh-protocol/remote-info.ts +161 -0
- package/vendor/dsh-protocol/types.ts +939 -0
- package/vendor/licenses/jridgewell-gen-mapping-MIT.txt +19 -0
- package/vendor/licenses/jridgewell-resolve-uri-MIT.txt +19 -0
- package/vendor/licenses/jridgewell-sourcemap-codec-MIT.txt +19 -0
- package/vendor/licenses/jridgewell-trace-mapping-MIT.txt +19 -0
- package/vendor/licenses/zod-MIT.txt +21 -0
|
@@ -53,6 +53,7 @@ from . import __version__
|
|
|
53
53
|
from .encoder import RuleEncoder
|
|
54
54
|
from .engine import Engine, SpecRegistry
|
|
55
55
|
from .memory import Memory
|
|
56
|
+
from .management import Management, ManagementError
|
|
56
57
|
from .orchestrator import Orchestrator, TurnBudget
|
|
57
58
|
from .profile import ProfileStore
|
|
58
59
|
|
|
@@ -95,6 +96,8 @@ class MemoryService:
|
|
|
95
96
|
self.db_path = str(db_path)
|
|
96
97
|
self.engine = Engine(Path(db_path), SpecRegistry.from_dir(Path(specs_dir)))
|
|
97
98
|
self.profiles = ProfileStore(self.engine)
|
|
99
|
+
from .privacy import repair_legacy
|
|
100
|
+
repair_legacy(self.engine, self.profiles)
|
|
98
101
|
self.orchestrator = Orchestrator(
|
|
99
102
|
self.engine,
|
|
100
103
|
profiles=self.profiles,
|
|
@@ -106,6 +109,9 @@ class MemoryService:
|
|
|
106
109
|
# `INSERT OR IGNORE` 兜住了,这里只是避免无谓的重复计算。
|
|
107
110
|
self._scopes: dict[str, str] = {}
|
|
108
111
|
self._lock = threading.Lock()
|
|
112
|
+
# Serialize HTTP writes with reads of dependent projections. In
|
|
113
|
+
# particular, a /turn cannot republish a half-edited graph.
|
|
114
|
+
self.request_lock = threading.RLock()
|
|
109
115
|
|
|
110
116
|
# ------------------------------------------------------------ scope
|
|
111
117
|
def scope_of(self, session: str, user: str = "user") -> str:
|
|
@@ -342,7 +348,12 @@ class _Handler(BaseHTTPRequestHandler):
|
|
|
342
348
|
self._json(400, {"ok": False, "error": f"bad_json: {exc}"})
|
|
343
349
|
return
|
|
344
350
|
|
|
351
|
+
self.service.request_lock.acquire()
|
|
345
352
|
try:
|
|
353
|
+
if route.startswith("/manage/"):
|
|
354
|
+
payload = self._manage(route, body)
|
|
355
|
+
self._json(200, payload)
|
|
356
|
+
return
|
|
346
357
|
session = _str_field(body, "session")
|
|
347
358
|
if not session:
|
|
348
359
|
self._json(400, {"ok": False, "error": "missing_session"})
|
|
@@ -390,6 +401,13 @@ class _Handler(BaseHTTPRequestHandler):
|
|
|
390
401
|
except ValueError as exc:
|
|
391
402
|
# 库里没有这条 / 不属于这个 scope:调用方参数问题,不是服务端崩了。
|
|
392
403
|
raise BadRequest(str(exc)) from exc
|
|
404
|
+
elif route == "/memory/edit":
|
|
405
|
+
payload = Management(self.service).edit(
|
|
406
|
+
self.service.scope_of(session, user),
|
|
407
|
+
_str_field(body, "trace_id"),
|
|
408
|
+
_str_field(body, "content", limit=MAX_TEXT_CHARS),
|
|
409
|
+
_str_field(body, "expected_hash"),
|
|
410
|
+
)
|
|
393
411
|
elif route in ("/memory/mark", "/memory/unmark", "/memory/curve"):
|
|
394
412
|
trace_id = _str_field(body, "trace_id")
|
|
395
413
|
if not trace_id:
|
|
@@ -410,6 +428,12 @@ class _Handler(BaseHTTPRequestHandler):
|
|
|
410
428
|
else:
|
|
411
429
|
self._json(404, {"ok": False, "error": "not_found"})
|
|
412
430
|
return
|
|
431
|
+
except ManagementError as exc:
|
|
432
|
+
status = 409 if exc.code == "version_conflict" else (
|
|
433
|
+
404 if exc.code.endswith("_not_found") else 400
|
|
434
|
+
)
|
|
435
|
+
self._json(status, {"ok": False, "error": exc.code})
|
|
436
|
+
return
|
|
413
437
|
except BadRequest as exc:
|
|
414
438
|
self._json(400, {"ok": False, "error": "bad_request", "detail": str(exc)})
|
|
415
439
|
return
|
|
@@ -422,9 +446,37 @@ class _Handler(BaseHTTPRequestHandler):
|
|
|
422
446
|
self._json(500, {"ok": False, "error": type(exc).__name__,
|
|
423
447
|
"detail": str(exc)[:300]})
|
|
424
448
|
return
|
|
449
|
+
finally:
|
|
450
|
+
self.service.request_lock.release()
|
|
425
451
|
|
|
426
452
|
self._json(200, payload)
|
|
427
453
|
|
|
454
|
+
def _manage(self, route: str, body: dict[str, Any]) -> dict[str, Any]:
|
|
455
|
+
manager = Management(self.service)
|
|
456
|
+
if route == "/manage/catalog":
|
|
457
|
+
return manager.catalog()
|
|
458
|
+
scope = _str_field(body, "scope_id")
|
|
459
|
+
if not scope:
|
|
460
|
+
raise BadRequest("missing_scope_id")
|
|
461
|
+
if route == "/manage/snapshot":
|
|
462
|
+
return manager.snapshot(scope, int(_num_field(body, "limit", 250, 1, 500)))
|
|
463
|
+
if route == "/manage/search":
|
|
464
|
+
return manager.search(
|
|
465
|
+
scope, _str_field(body, "query", limit=MAX_TEXT_CHARS),
|
|
466
|
+
int(_num_field(body, "limit", 30, 1, 50)),
|
|
467
|
+
)
|
|
468
|
+
trace = _str_field(body, "trace_id")
|
|
469
|
+
if route == "/manage/detail":
|
|
470
|
+
return manager.detail(scope, trace)
|
|
471
|
+
expected = _str_field(body, "expected_hash")
|
|
472
|
+
if route == "/manage/edit":
|
|
473
|
+
return manager.edit(
|
|
474
|
+
scope, trace, _str_field(body, "content", limit=MAX_TEXT_CHARS), expected
|
|
475
|
+
)
|
|
476
|
+
if route == "/manage/forget":
|
|
477
|
+
return manager.forget(scope, trace, expected)
|
|
478
|
+
raise ManagementError("route_not_found")
|
|
479
|
+
|
|
428
480
|
# ---------------------------------------------------------- 工具
|
|
429
481
|
def _read_json(self) -> dict[str, Any]:
|
|
430
482
|
try:
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { build } from 'esbuild'
|
|
2
|
+
import { readFileSync, writeFileSync } from 'node:fs'
|
|
3
|
+
import { resolve } from 'node:path'
|
|
4
|
+
const root = resolve(import.meta.dirname, '..')
|
|
5
|
+
const alias = {
|
|
6
|
+
'@deepseek-ai/dsh-typert-protocol/remote-info': resolve(root, 'vendor/dsh-protocol/remote-info.ts'),
|
|
7
|
+
'@deepseek-ai/dsh-typert-protocol': resolve(root, 'vendor/dsh-protocol/index.ts'),
|
|
8
|
+
}
|
|
9
|
+
const common = { bundle:true,format:'esm',target:'es2022',logLevel:'warning',alias }
|
|
10
|
+
await build({...common,entryPoints:[resolve(root,'src/controller.ts')],platform:'node',external:['@deepseek-ai/cordis'],outfile:resolve(root,'lib/controller.js')})
|
|
11
|
+
for (const name of ['typert.host','typert.remote-client']) {
|
|
12
|
+
await build({...common,entryPoints:[resolve(root,`src/generated/${name}.js`)],platform:'neutral',outfile:resolve(root,`lib/${name}.js`)})
|
|
13
|
+
}
|
|
14
|
+
writeFileSync(resolve(root,'lib/types.d.ts'),readFileSync(resolve(root,'src/types.ts')))
|
|
15
|
+
writeFileSync(resolve(root,'lib/types.js'),'export {};\n')
|
|
16
|
+
if (process.argv.includes('--host-only')) process.exit(0)
|
|
17
|
+
const manifest = JSON.parse(readFileSync(resolve(root,'package.json'),'utf8'))
|
|
18
|
+
await build({...common,entryPoints:[resolve(root,'src/client/index.tsx')],platform:'browser',format:'cjs',external:['react','react/jsx-runtime','react-dom','react-dom/client','@deepseek-ai/cordis'],jsx:'automatic',outfile:resolve(root,'lib/client.js'),loader:{'.css':'text'},banner:{js:`window.__ModuleLoader__.load({id:${JSON.stringify(manifest.name)},factory:(require)=>{var module={exports:{}};var exports=module.exports;`},footer:{js:'return module.exports;}});'}})
|
|
19
|
+
console.log('Built host controller, strict contracts and DSH client factory.')
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import {readdirSync,rmSync,statSync,existsSync,readFileSync} from 'node:fs'
|
|
2
|
+
import {resolve,join} from 'node:path'
|
|
3
|
+
const root=resolve(import.meta.dirname,'..')
|
|
4
|
+
function clean(dir){for(const entry of readdirSync(dir,{withFileTypes:true})){const p=join(dir,entry.name);if(entry.isDirectory()){if(entry.name==='__pycache__')rmSync(p,{recursive:true,force:true});else clean(p)}else if(entry.name.endsWith('.pyc'))rmSync(p)}}
|
|
5
|
+
clean(join(root,'python'))
|
|
6
|
+
for(const name of ['lib/index.js','lib/controller.js','lib/client.js','lib/typert.host.js','lib/typert.remote-client.js','python/xujie_memory/management.py','ACCEPTANCE.md'])if(!existsSync(join(root,name)))throw new Error('Missing release artifact: '+name)
|
|
7
|
+
const m=JSON.parse(readFileSync(join(root,'package.json'),'utf8'))
|
|
8
|
+
if(m.version!=='0.7.0'||m.engines.dsh!=='0.2.1-rc.5')throw new Error('Unreviewed package/host version')
|
|
9
|
+
console.log('Release artifacts verified; Python bytecode caches excluded.')
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import {spawnSync} from 'node:child_process'
|
|
2
|
+
import {resolve} from 'node:path'
|
|
3
|
+
const root=resolve(import.meta.dirname,'..')
|
|
4
|
+
const python=process.env.PYTHON ?? (process.platform==='win32'?resolve(root,'runtime/win32-x64/python.exe'):'python3')
|
|
5
|
+
for (const [cmd,args] of [[python,['test/management_regression.py']],[process.execPath,['scripts/test-node.mjs']]]) {
|
|
6
|
+
const r=spawnSync(cmd,args,{cwd:root,stdio:'inherit',env:{...process.env,PYTHON:python}})
|
|
7
|
+
if(r.error)throw r.error
|
|
8
|
+
if(r.status!==0)process.exit(r.status??1)
|
|
9
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import {build} from 'esbuild'
|
|
2
|
+
import {spawnSync} from 'node:child_process'
|
|
3
|
+
import {mkdtempSync,rmSync} from 'node:fs'
|
|
4
|
+
import {tmpdir} from 'node:os'
|
|
5
|
+
import {join,resolve} from 'node:path'
|
|
6
|
+
const root=resolve(import.meta.dirname,'..'),dir=mkdtempSync(join(tmpdir(),'xujie-test-build-')),outfile=join(dir,'test.mjs')
|
|
7
|
+
try{
|
|
8
|
+
await build({entryPoints:[join(root,'test/node-regression.entry.mjs')],bundle:true,platform:'node',format:'esm',target:'node22',outfile,external:['node:*'],alias:{'@deepseek-ai/cordis':join(root,'test/cordis-fixture.mjs')},logLevel:'warning'})
|
|
9
|
+
const r=spawnSync(process.execPath,[outfile],{cwd:root,stdio:'inherit',env:process.env});process.exitCode=r.status??1
|
|
10
|
+
}finally{rmSync(dir,{recursive:true,force:true})}
|