@reconcrap/people-network-memory 0.1.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.
Files changed (61) hide show
  1. package/README.md +476 -0
  2. package/docs/mcp_tools.md +138 -0
  3. package/harness_adapters/openclaw/mcp.managed.unix.template.json +25 -0
  4. package/harness_adapters/openclaw/mcp.managed.windows.template.json +26 -0
  5. package/harness_adapters/openclaw/mcp.template.json +14 -0
  6. package/harness_adapters/openclaw/ppl/SKILL.md +114 -0
  7. package/package.json +30 -0
  8. package/pyproject.toml +26 -0
  9. package/scripts/install_windows.ps1 +92 -0
  10. package/scripts/npm/people-memory.js +276 -0
  11. package/scripts/people_memory_bootstrap.py +247 -0
  12. package/scripts/run_graphiti_live_from_liepin.ps1 +87 -0
  13. package/scripts/run_tests_with_artifacts.ps1 +307 -0
  14. package/src/people_network_memory/__init__.py +6 -0
  15. package/src/people_network_memory/application/__init__.py +16 -0
  16. package/src/people_network_memory/application/normalization.py +1441 -0
  17. package/src/people_network_memory/application/services.py +921 -0
  18. package/src/people_network_memory/cli.py +1212 -0
  19. package/src/people_network_memory/config.py +268 -0
  20. package/src/people_network_memory/domain/__init__.py +55 -0
  21. package/src/people_network_memory/domain/identity.py +77 -0
  22. package/src/people_network_memory/domain/models.py +355 -0
  23. package/src/people_network_memory/fixtures/__init__.py +6 -0
  24. package/src/people_network_memory/fixtures/eval.py +398 -0
  25. package/src/people_network_memory/fixtures/extractor_eval.py +364 -0
  26. package/src/people_network_memory/fixtures/generator.py +290 -0
  27. package/src/people_network_memory/fixtures/report.py +252 -0
  28. package/src/people_network_memory/graphiti_adapter/__init__.py +9 -0
  29. package/src/people_network_memory/graphiti_adapter/episode_formatter.py +70 -0
  30. package/src/people_network_memory/graphiti_adapter/graphiti_store.py +655 -0
  31. package/src/people_network_memory/graphiti_adapter/indexer.py +194 -0
  32. package/src/people_network_memory/graphiti_adapter/ontology.py +68 -0
  33. package/src/people_network_memory/harness_adapters/__init__.py +2 -0
  34. package/src/people_network_memory/harness_adapters/openclaw/__init__.py +9 -0
  35. package/src/people_network_memory/harness_adapters/openclaw/installer.py +577 -0
  36. package/src/people_network_memory/harness_adapters/openclaw/integration_eval.py +508 -0
  37. package/src/people_network_memory/harness_adapters/openclaw/smoke.py +292 -0
  38. package/src/people_network_memory/infrastructure/__init__.py +2 -0
  39. package/src/people_network_memory/infrastructure/archive_backup.py +171 -0
  40. package/src/people_network_memory/infrastructure/diagnostics.py +171 -0
  41. package/src/people_network_memory/infrastructure/embeddings.py +155 -0
  42. package/src/people_network_memory/infrastructure/file_store.py +129 -0
  43. package/src/people_network_memory/infrastructure/graphiti_promotion.py +212 -0
  44. package/src/people_network_memory/infrastructure/id_generator.py +40 -0
  45. package/src/people_network_memory/infrastructure/in_memory_store.py +1008 -0
  46. package/src/people_network_memory/infrastructure/llm_extractor.py +476 -0
  47. package/src/people_network_memory/infrastructure/llm_identity_advisor.py +200 -0
  48. package/src/people_network_memory/infrastructure/llm_judge.py +162 -0
  49. package/src/people_network_memory/infrastructure/redaction.py +21 -0
  50. package/src/people_network_memory/infrastructure/release_check.py +186 -0
  51. package/src/people_network_memory/infrastructure/retrieval_intent.py +98 -0
  52. package/src/people_network_memory/infrastructure/semantic_index.py +262 -0
  53. package/src/people_network_memory/mcp_server/__init__.py +2 -0
  54. package/src/people_network_memory/mcp_server/contracts.py +85 -0
  55. package/src/people_network_memory/mcp_server/runtime.py +133 -0
  56. package/src/people_network_memory/mcp_server/tools.py +588 -0
  57. package/src/people_network_memory/ports/__init__.py +2 -0
  58. package/src/people_network_memory/ports/errors.py +25 -0
  59. package/src/people_network_memory/ports/interfaces.py +103 -0
  60. package/src/people_network_memory/projection/__init__.py +6 -0
  61. package/src/people_network_memory/projection/builders.py +46 -0
@@ -0,0 +1,247 @@
1
+ """Stdlib-only bootstrap launcher for AI harnesses.
2
+
3
+ The harness can call this script instead of calling the package directly. It
4
+ creates or repairs a local venv, installs the package, and then launches the MCP
5
+ server through that venv. Keep this file dependency-free.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import argparse
11
+ import hashlib
12
+ import json
13
+ import os
14
+ import platform
15
+ import subprocess
16
+ import sys
17
+ from datetime import datetime, timezone
18
+ from pathlib import Path
19
+ from typing import Any
20
+
21
+
22
+ STATE_SCHEMA_VERSION = 1
23
+ LOCAL_ENV_FILENAME = "local.env.json"
24
+
25
+
26
+ def main(argv: list[str] | None = None) -> int:
27
+ parser = _build_parser()
28
+ args = parser.parse_args(argv)
29
+ repo = Path(args.repo).expanduser().resolve() if args.repo else _repo_root()
30
+ venv = Path(args.venv).expanduser().resolve() if args.venv else repo / ".venv"
31
+ if args.command == "run":
32
+ result = ensure_runtime(
33
+ repo=repo,
34
+ venv=venv,
35
+ extra=args.extra,
36
+ force=args.force,
37
+ git_pull=args.git_pull,
38
+ )
39
+ env = os.environ.copy()
40
+ env.update({key: value for key, value in _load_local_env(repo).items() if key not in env})
41
+ env.setdefault("PEOPLE_MEMORY_BACKEND", args.backend)
42
+ env.setdefault("PEOPLE_MEMORY_SENSITIVITY_POLICY", "personal")
43
+ env.setdefault("GRAPHITI_TELEMETRY_ENABLED", "false")
44
+ passthrough_args = list(args.server_args)
45
+ if passthrough_args and passthrough_args[0] == "--":
46
+ passthrough_args = passthrough_args[1:]
47
+ server_args = ["-m", "people_network_memory.cli", "start", *passthrough_args]
48
+ return subprocess.call([result["python"], *server_args], cwd=str(repo), env=env)
49
+ if args.command in {"ensure", "update"}:
50
+ result = ensure_runtime(
51
+ repo=repo,
52
+ venv=venv,
53
+ extra=args.extra,
54
+ force=args.force or args.command == "update",
55
+ git_pull=args.git_pull,
56
+ )
57
+ print(json.dumps(result, ensure_ascii=False, indent=2, sort_keys=True))
58
+ return 0
59
+ parser.error(f"unsupported command: {args.command}")
60
+ return 2
61
+
62
+
63
+ def ensure_runtime(
64
+ *,
65
+ repo: Path,
66
+ venv: Path,
67
+ extra: str,
68
+ force: bool = False,
69
+ git_pull: bool = False,
70
+ ) -> dict[str, Any]:
71
+ if not (repo / "pyproject.toml").exists():
72
+ raise SystemExit(f"Repo root does not contain pyproject.toml: {repo}")
73
+ git_pull_status = "disabled"
74
+ if git_pull:
75
+ git_pull_status = _git_pull_best_effort(repo)
76
+ venv_python = _venv_python(venv)
77
+ created_venv = False
78
+ if not venv_python.exists():
79
+ _log(f"Creating venv at {venv}")
80
+ _run([sys.executable, "-m", "venv", str(venv)], label="create venv")
81
+ created_venv = True
82
+ state_path = _state_path(repo)
83
+ desired_hash = _desired_state_hash(repo, extra=extra, bootstrap_python=sys.executable)
84
+ current_hash = _read_state_hash(state_path)
85
+ installed = False
86
+ if force or created_venv or current_hash != desired_hash:
87
+ _log("Installing/updating people-network-memory runtime")
88
+ _run([str(venv_python), "-m", "pip", "install", "--upgrade", "pip"], label="upgrade pip")
89
+ target = str(repo) if extra == "base" else f"{repo}[{extra}]"
90
+ _run([str(venv_python), "-m", "pip", "install", "-e", target], label="install package")
91
+ _write_state(
92
+ state_path,
93
+ {
94
+ "schema_version": STATE_SCHEMA_VERSION,
95
+ "state_hash": desired_hash,
96
+ "repo": str(repo),
97
+ "venv": str(venv),
98
+ "extra": extra,
99
+ "bootstrap_python": sys.executable,
100
+ "installed_at": datetime.now(timezone.utc).isoformat(),
101
+ },
102
+ )
103
+ installed = True
104
+ return {
105
+ "ok": True,
106
+ "repo": str(repo),
107
+ "venv": str(venv),
108
+ "python": str(venv_python),
109
+ "extra": extra,
110
+ "created_venv": created_venv,
111
+ "installed_or_updated": installed,
112
+ "git_pull": git_pull,
113
+ "git_pull_status": git_pull_status,
114
+ }
115
+
116
+
117
+ def _build_parser() -> argparse.ArgumentParser:
118
+ parser = argparse.ArgumentParser(description="Bootstrap People Network Memory runtime.")
119
+ sub = parser.add_subparsers(dest="command", required=True)
120
+ for command in ["run", "ensure", "update"]:
121
+ item = sub.add_parser(command)
122
+ item.add_argument("--repo")
123
+ item.add_argument("--venv")
124
+ item.add_argument("--extra", choices=["base", "graphiti"], default="base")
125
+ item.add_argument("--backend", choices=["local_json", "graphiti", "inmemory"], default="local_json")
126
+ item.add_argument("--force", action="store_true")
127
+ item.add_argument("--git-pull", action="store_true")
128
+ if command == "run":
129
+ item.add_argument("server_args", nargs=argparse.REMAINDER)
130
+ else:
131
+ item.set_defaults(server_args=[])
132
+ return parser
133
+
134
+
135
+ def _repo_root() -> Path:
136
+ return Path(__file__).resolve().parents[1]
137
+
138
+
139
+ def _venv_python(venv: Path) -> Path:
140
+ if platform.system().lower().startswith("win"):
141
+ return venv / "Scripts" / "python.exe"
142
+ return venv / "bin" / "python"
143
+
144
+
145
+ def _state_path(repo: Path) -> Path:
146
+ return repo / ".people-network-memory" / "bootstrap-state.json"
147
+
148
+
149
+ def _desired_state_hash(repo: Path, *, extra: str, bootstrap_python: str) -> str:
150
+ digest = hashlib.sha256()
151
+ for path in [repo / "pyproject.toml", Path(__file__).resolve()]:
152
+ digest.update(path.name.encode("utf-8"))
153
+ digest.update(path.read_bytes())
154
+ digest.update(extra.encode("utf-8"))
155
+ digest.update(bootstrap_python.encode("utf-8"))
156
+ digest.update(f"{sys.version_info.major}.{sys.version_info.minor}".encode("ascii"))
157
+ return digest.hexdigest()
158
+
159
+
160
+ def _read_state_hash(path: Path) -> str | None:
161
+ try:
162
+ payload = json.loads(path.read_text(encoding="utf-8"))
163
+ except (FileNotFoundError, json.JSONDecodeError):
164
+ return None
165
+ value = payload.get("state_hash")
166
+ return value if isinstance(value, str) else None
167
+
168
+
169
+ def _write_state(path: Path, payload: dict[str, Any]) -> None:
170
+ path.parent.mkdir(parents=True, exist_ok=True)
171
+ path.write_text(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True), encoding="utf-8")
172
+
173
+
174
+ def _load_local_env(repo: Path) -> dict[str, str]:
175
+ path = repo / ".people-network-memory" / LOCAL_ENV_FILENAME
176
+ try:
177
+ payload = json.loads(path.read_text(encoding="utf-8"))
178
+ except FileNotFoundError:
179
+ return {}
180
+ except json.JSONDecodeError as exc:
181
+ raise SystemExit(f"Invalid local env JSON at {path}: {exc}") from exc
182
+ if not isinstance(payload, dict):
183
+ raise SystemExit(f"Local env JSON must be an object: {path}")
184
+ loaded: dict[str, str] = {}
185
+ for key, value in payload.items():
186
+ if not _allowed_local_env_name(key):
187
+ continue
188
+ if isinstance(value, str) and value:
189
+ loaded[key] = value
190
+ return loaded
191
+
192
+
193
+ def _allowed_local_env_name(name: str) -> bool:
194
+ return (
195
+ name.startswith("PEOPLE_MEMORY_")
196
+ or name == "GRAPHITI_TELEMETRY_ENABLED"
197
+ or name == "VOLCENGINE_" + "API" + "_KEY"
198
+ )
199
+
200
+
201
+ def _git_pull_best_effort(repo: Path) -> str:
202
+ inside_repo = subprocess.run(
203
+ ["git", "-C", str(repo), "rev-parse", "--is-inside-work-tree"],
204
+ capture_output=True,
205
+ text=True,
206
+ )
207
+ if inside_repo.returncode != 0:
208
+ _log("Skipping git pull: repo is not a git work tree")
209
+ return "skipped:not_git_repo"
210
+ upstream = subprocess.run(
211
+ ["git", "-C", str(repo), "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"],
212
+ capture_output=True,
213
+ text=True,
214
+ )
215
+ if upstream.returncode != 0:
216
+ _log("Skipping git pull: current branch has no upstream")
217
+ return "skipped:no_upstream"
218
+ command = ["git", "-C", str(repo), "pull", "--ff-only"]
219
+ _log(f"git pull: {' '.join(command)}")
220
+ result = subprocess.run(command, capture_output=True, text=True)
221
+ output = _trim_process_output(result.stdout, result.stderr)
222
+ if output:
223
+ _log(output)
224
+ if result.returncode != 0:
225
+ _log("Git pull failed; continuing with the existing checkout")
226
+ return f"failed:{result.returncode}"
227
+ return "ok"
228
+
229
+
230
+ def _trim_process_output(stdout: str, stderr: str, *, limit: int = 800) -> str:
231
+ output = "\n".join(part.strip() for part in [stdout, stderr] if part.strip())
232
+ if len(output) <= limit:
233
+ return output
234
+ return output[:limit] + "... [truncated]"
235
+
236
+
237
+ def _run(command: list[str], *, label: str) -> None:
238
+ _log(f"{label}: {' '.join(command)}")
239
+ subprocess.run(command, check=True)
240
+
241
+
242
+ def _log(message: str) -> None:
243
+ print(f"[people-memory-bootstrap] {message}", file=sys.stderr, flush=True)
244
+
245
+
246
+ if __name__ == "__main__":
247
+ raise SystemExit(main())
@@ -0,0 +1,87 @@
1
+ param(
2
+ [string]$ArtifactsDir = ".people-network-memory/test-artifacts/graphiti-live",
3
+ [string]$LiepinConfigPath = "$HOME/.liepin-recommend-mcp/screening-config.json",
4
+ [string]$PythonCommand = "python"
5
+ )
6
+
7
+ $ErrorActionPreference = "Stop"
8
+
9
+ $repoRoot = Split-Path -Parent $PSScriptRoot
10
+ Set-Location $repoRoot
11
+
12
+ $localEnvPath = Join-Path $repoRoot ".people-network-memory/local.env.json"
13
+ if (Test-Path -LiteralPath $localEnvPath) {
14
+ $localEnv = Get-Content -LiteralPath $localEnvPath -Raw | ConvertFrom-Json
15
+ foreach ($property in $localEnv.PSObject.Properties) {
16
+ $name = $property.Name
17
+ $isAllowed = (
18
+ $name.StartsWith("PEOPLE_MEMORY_") -or
19
+ $name -eq "GRAPHITI_TELEMETRY_ENABLED" -or
20
+ $name -eq "VOLCENGINE_API_KEY"
21
+ )
22
+ if ($isAllowed -and -not [System.Environment]::GetEnvironmentVariable($name, "Process")) {
23
+ [System.Environment]::SetEnvironmentVariable($name, [string]$property.Value, "Process")
24
+ }
25
+ }
26
+ }
27
+
28
+ $resolvedLiepinConfigPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath(
29
+ $LiepinConfigPath
30
+ )
31
+ if (-not (Test-Path -LiteralPath $resolvedLiepinConfigPath)) {
32
+ throw "Liepin LLM config not found: $resolvedLiepinConfigPath"
33
+ }
34
+
35
+ $liepinConfig = Get-Content -LiteralPath $resolvedLiepinConfigPath -Raw | ConvertFrom-Json
36
+ foreach ($field in @("baseUrl", "apiKey", "model")) {
37
+ if (-not $liepinConfig.PSObject.Properties.Name.Contains($field) -or -not $liepinConfig.$field) {
38
+ throw "Liepin LLM config is missing required field: $field"
39
+ }
40
+ }
41
+
42
+ if (-not $env:PEOPLE_MEMORY_EMBEDDING_API_KEY -and $env:VOLCENGINE_API_KEY) {
43
+ $env:PEOPLE_MEMORY_EMBEDDING_API_KEY = $env:VOLCENGINE_API_KEY
44
+ }
45
+ if (-not $env:PEOPLE_MEMORY_EMBEDDING_API_KEY) {
46
+ throw "Set PEOPLE_MEMORY_EMBEDDING_API_KEY or VOLCENGINE_API_KEY before running live Graphiti artifacts."
47
+ }
48
+
49
+ $env:PEOPLE_MEMORY_BACKEND = "graphiti"
50
+ $env:PEOPLE_MEMORY_GRAPH_BACKEND = "kuzu"
51
+ $env:PEOPLE_MEMORY_EMBEDDING_PROVIDER = "volcengine"
52
+ $env:PEOPLE_MEMORY_EMBEDDING_BASE_URL = "https://ark.cn-beijing.volces.com/api/coding/v3"
53
+ $env:PEOPLE_MEMORY_EMBEDDING_MODEL = "doubao-embedding-vision-251215"
54
+ $env:PEOPLE_MEMORY_LLM_PROVIDER = "openai_compatible"
55
+ $env:PEOPLE_MEMORY_LLM_BASE_URL = $liepinConfig.baseUrl
56
+ $env:PEOPLE_MEMORY_LLM_MODEL = $liepinConfig.model
57
+ $env:PEOPLE_MEMORY_LLM_API_KEY = $liepinConfig.apiKey
58
+ $env:PEOPLE_MEMORY_LLM_RESPONSE_FORMAT = "none"
59
+ $env:GRAPHITI_TELEMETRY_ENABLED = "false"
60
+
61
+ & powershell -ExecutionPolicy Bypass -File .\scripts\run_tests_with_artifacts.ps1 `
62
+ -ArtifactsDir $ArtifactsDir `
63
+ -PythonCommand $PythonCommand `
64
+ -Graphiti
65
+ $runnerExitCode = $LASTEXITCODE
66
+ if ($runnerExitCode -ne 0) {
67
+ exit $runnerExitCode
68
+ }
69
+
70
+ $resolvedArtifactsDir = if ([System.IO.Path]::IsPathRooted($ArtifactsDir)) {
71
+ [System.IO.Path]::GetFullPath($ArtifactsDir)
72
+ }
73
+ else {
74
+ [System.IO.Path]::GetFullPath((Join-Path $repoRoot $ArtifactsDir))
75
+ }
76
+ $manifestPath = Join-Path $resolvedArtifactsDir "manifest.json"
77
+ $manifest = Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json
78
+ $failedGraphiti = @(
79
+ $manifest.steps | Where-Object { $_.name -like "graphiti_*" -and $_.exit_code -ne 0 }
80
+ )
81
+ if ($failedGraphiti.Count -gt 0) {
82
+ Write-Host "Failed live Graphiti steps:"
83
+ $failedGraphiti | ForEach-Object { Write-Host " - $($_.name): exit $($_.exit_code)" }
84
+ exit 1
85
+ }
86
+
87
+ exit 0
@@ -0,0 +1,307 @@
1
+ param(
2
+ [string]$ArtifactsDir = ".people-network-memory/test-artifacts/latest",
3
+ [string]$PythonCommand = "python",
4
+ [switch]$Graphiti,
5
+ [switch]$LiveLlm
6
+ )
7
+
8
+ $ErrorActionPreference = "Stop"
9
+
10
+ $repoRoot = Split-Path -Parent $PSScriptRoot
11
+ Set-Location $repoRoot
12
+
13
+ $repoRootFull = [System.IO.Path]::GetFullPath($repoRoot)
14
+ $resolvedArtifactsDir = [System.IO.Path]::GetFullPath((Join-Path $repoRoot $ArtifactsDir))
15
+ if (-not $resolvedArtifactsDir.StartsWith($repoRootFull, [System.StringComparison]::OrdinalIgnoreCase)) {
16
+ throw "ArtifactsDir must resolve inside the repository root."
17
+ }
18
+
19
+ if ($PythonCommand -eq "python") {
20
+ $venvPython = Join-Path $repoRoot ".venv/Scripts/python.exe"
21
+ if (Test-Path -LiteralPath $venvPython) {
22
+ $PythonCommand = $venvPython
23
+ }
24
+ }
25
+
26
+ if (Test-Path -LiteralPath $resolvedArtifactsDir) {
27
+ Get-ChildItem -LiteralPath $resolvedArtifactsDir -Force | Remove-Item -Recurse -Force
28
+ }
29
+ New-Item -ItemType Directory -Force -Path $resolvedArtifactsDir | Out-Null
30
+
31
+ $manifest = [ordered]@{
32
+ schema_version = 1
33
+ created_at = (Get-Date).ToUniversalTime().ToString("o")
34
+ repo_root = $repoRoot
35
+ artifacts_dir = $resolvedArtifactsDir
36
+ steps = @()
37
+ }
38
+ $manifestPath = Join-Path $resolvedArtifactsDir "manifest.json"
39
+ $script:LastArtifactStepExitCode = $null
40
+
41
+ function Save-Manifest {
42
+ $manifest | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $manifestPath -Encoding UTF8
43
+ }
44
+
45
+ function Invoke-ArtifactStep {
46
+ param(
47
+ [string]$Name,
48
+ [string]$Command,
49
+ [string[]]$Arguments,
50
+ [string]$OutputFile,
51
+ [switch]$AllowFailure,
52
+ [switch]$CleanPeopleMemoryEnv
53
+ )
54
+
55
+ $outputPath = Join-Path $resolvedArtifactsDir $OutputFile
56
+ $startedAt = (Get-Date).ToUniversalTime().ToString("o")
57
+ Write-Host ""
58
+ Write-Host "==> $Name"
59
+ Write-Host " $Command $($Arguments -join ' ')"
60
+ $step = [ordered]@{
61
+ name = $Name
62
+ command = $Command
63
+ arguments = $Arguments
64
+ output = $outputPath
65
+ exit_code = $null
66
+ allow_failure = [bool]$AllowFailure
67
+ clean_people_memory_env = [bool]$CleanPeopleMemoryEnv
68
+ started_at = $startedAt
69
+ finished_at = $null
70
+ status = "running"
71
+ }
72
+ $manifest.steps += $step
73
+ Save-Manifest
74
+
75
+ $previousErrorActionPreference = $ErrorActionPreference
76
+ $envSnapshot = @{}
77
+ if ($CleanPeopleMemoryEnv) {
78
+ $namesToClean = @(
79
+ Get-ChildItem Env: |
80
+ Where-Object { $_.Name -like "PEOPLE_MEMORY_*" } |
81
+ ForEach-Object { $_.Name }
82
+ )
83
+ $namesToClean += "GRAPHITI_TELEMETRY_ENABLED"
84
+ $namesToClean = @($namesToClean | Select-Object -Unique)
85
+ foreach ($envVarName in $namesToClean) {
86
+ $envSnapshot[$envVarName] = [System.Environment]::GetEnvironmentVariable($envVarName, "Process")
87
+ [System.Environment]::SetEnvironmentVariable($envVarName, $null, "Process")
88
+ }
89
+ $envSnapshot["PEOPLE_MEMORY_DISABLE_LOCAL_ENV"] = [System.Environment]::GetEnvironmentVariable("PEOPLE_MEMORY_DISABLE_LOCAL_ENV", "Process")
90
+ [System.Environment]::SetEnvironmentVariable("PEOPLE_MEMORY_DISABLE_LOCAL_ENV", "true", "Process")
91
+ $env:GRAPHITI_TELEMETRY_ENABLED = "false"
92
+ }
93
+ $ErrorActionPreference = "Continue"
94
+ try {
95
+ & $Command @Arguments *> $outputPath
96
+ $exitCode = $LASTEXITCODE
97
+ if ($null -eq $exitCode) {
98
+ $exitCode = 0
99
+ }
100
+ }
101
+ finally {
102
+ $ErrorActionPreference = $previousErrorActionPreference
103
+ if ($CleanPeopleMemoryEnv) {
104
+ foreach ($envVarName in $envSnapshot.Keys) {
105
+ [System.Environment]::SetEnvironmentVariable($envVarName, $envSnapshot[$envVarName], "Process")
106
+ }
107
+ }
108
+ }
109
+ $finishedAt = (Get-Date).ToUniversalTime().ToString("o")
110
+
111
+ $step["exit_code"] = $exitCode
112
+ $step["finished_at"] = $finishedAt
113
+ $step["status"] = "completed"
114
+ $script:LastArtifactStepExitCode = $exitCode
115
+ Save-Manifest
116
+
117
+ if ($exitCode -ne 0 -and -not $AllowFailure) {
118
+ Write-Host " failed with exit code $exitCode"
119
+ }
120
+ }
121
+
122
+ function Add-SkippedArtifactStep {
123
+ param(
124
+ [string]$Name,
125
+ [string]$Reason
126
+ )
127
+ $now = (Get-Date).ToUniversalTime().ToString("o")
128
+ $manifest.steps += [ordered]@{
129
+ name = $Name
130
+ command = ""
131
+ arguments = @()
132
+ output = ""
133
+ exit_code = 0
134
+ allow_failure = $true
135
+ clean_people_memory_env = $false
136
+ started_at = $now
137
+ finished_at = $now
138
+ status = "skipped"
139
+ reason = $Reason
140
+ }
141
+ Save-Manifest
142
+ }
143
+
144
+ Invoke-ArtifactStep `
145
+ -Name "unit_tests" `
146
+ -Command $PythonCommand `
147
+ -Arguments @("-m", "unittest", "discover", "-s", "tests", "-v") `
148
+ -OutputFile "unittest.txt" `
149
+ -CleanPeopleMemoryEnv
150
+
151
+ Invoke-ArtifactStep `
152
+ -Name "mcp_test_mode_start" `
153
+ -Command $PythonCommand `
154
+ -Arguments @("-m", "people_network_memory.cli", "start", "--test-mode", "--once") `
155
+ -OutputFile "mcp-start.json" `
156
+ -CleanPeopleMemoryEnv
157
+
158
+ Invoke-ArtifactStep `
159
+ -Name "doctor_test_mode" `
160
+ -Command $PythonCommand `
161
+ -Arguments @("-m", "people_network_memory.cli", "doctor", "--test-mode") `
162
+ -OutputFile "doctor-test-mode.json" `
163
+ -CleanPeopleMemoryEnv
164
+
165
+ Invoke-ArtifactStep `
166
+ -Name "tool_schemas" `
167
+ -Command $PythonCommand `
168
+ -Arguments @("-m", "people_network_memory.cli", "tool-schemas") `
169
+ -OutputFile "tool-schemas.json" `
170
+ -CleanPeopleMemoryEnv
171
+
172
+ Invoke-ArtifactStep `
173
+ -Name "fixture_eval" `
174
+ -Command $PythonCommand `
175
+ -Arguments @(
176
+ "-m", "people_network_memory.cli", "eval-fixtures",
177
+ "--include-cases",
178
+ "--output", (Join-Path $resolvedArtifactsDir "eval-fixtures.json")
179
+ ) `
180
+ -OutputFile "eval-fixtures.stdout.json" `
181
+ -CleanPeopleMemoryEnv
182
+
183
+ Invoke-ArtifactStep `
184
+ -Name "fixture_eval_report" `
185
+ -Command $PythonCommand `
186
+ -Arguments @(
187
+ "-m", "people_network_memory.cli", "report-eval",
188
+ "--input", (Join-Path $resolvedArtifactsDir "eval-fixtures.json"),
189
+ "--output", (Join-Path $resolvedArtifactsDir "eval-fixtures-report.md"),
190
+ "--title", "Fixture Retrieval Test Report"
191
+ ) `
192
+ -OutputFile "eval-fixtures-report.stdout.json" `
193
+ -CleanPeopleMemoryEnv
194
+
195
+ Invoke-ArtifactStep `
196
+ -Name "openclaw_smoke" `
197
+ -Command $PythonCommand `
198
+ -Arguments @("-m", "people_network_memory.cli", "smoke-openclaw") `
199
+ -OutputFile "smoke-openclaw.json" `
200
+ -CleanPeopleMemoryEnv
201
+
202
+ Invoke-ArtifactStep `
203
+ -Name "harness_integration_eval" `
204
+ -Command $PythonCommand `
205
+ -Arguments @(
206
+ "-m", "people_network_memory.cli", "eval-harness-integration",
207
+ "--output", (Join-Path $resolvedArtifactsDir "harness-integration-eval.json")
208
+ ) `
209
+ -OutputFile "harness-integration-eval.stdout.json" `
210
+ -CleanPeopleMemoryEnv
211
+
212
+ Invoke-ArtifactStep `
213
+ -Name "release_check" `
214
+ -Command $PythonCommand `
215
+ -Arguments @(
216
+ "-m", "people_network_memory.cli", "release-check",
217
+ "--output", (Join-Path $resolvedArtifactsDir "release-check.json")
218
+ ) `
219
+ -OutputFile "release-check.stdout.json" `
220
+ -CleanPeopleMemoryEnv
221
+
222
+ if ($LiveLlm) {
223
+ Invoke-ArtifactStep `
224
+ -Name "llm_extractor_eval" `
225
+ -Command $PythonCommand `
226
+ -Arguments @(
227
+ "-m", "people_network_memory.cli", "eval-extractor",
228
+ "--output", (Join-Path $resolvedArtifactsDir "llm-extractor-eval.json")
229
+ ) `
230
+ -OutputFile "llm-extractor-eval.stdout.json"
231
+ }
232
+
233
+ Invoke-ArtifactStep `
234
+ -Name "git_diff_check" `
235
+ -Command "git" `
236
+ -Arguments @("diff", "--check") `
237
+ -OutputFile "git-diff-check.txt"
238
+
239
+ Invoke-ArtifactStep `
240
+ -Name "git_status" `
241
+ -Command "git" `
242
+ -Arguments @("status", "--short") `
243
+ -OutputFile "git-status.txt"
244
+
245
+ if ($Graphiti) {
246
+ Invoke-ArtifactStep `
247
+ -Name "graphiti_embedding_check" `
248
+ -Command $PythonCommand `
249
+ -Arguments @("-m", "people_network_memory.cli", "check-embedding") `
250
+ -OutputFile "embedding-check.json" `
251
+ -AllowFailure
252
+
253
+ Invoke-ArtifactStep `
254
+ -Name "graphiti_spike" `
255
+ -Command $PythonCommand `
256
+ -Arguments @("-m", "people_network_memory.cli", "spike-graphiti") `
257
+ -OutputFile "graphiti-spike.json" `
258
+ -AllowFailure
259
+
260
+ Invoke-ArtifactStep `
261
+ -Name "graphiti_promotion_gate" `
262
+ -Command $PythonCommand `
263
+ -Arguments @(
264
+ "-m", "people_network_memory.cli", "graphiti-gate",
265
+ "--isolated",
266
+ "--include-cases",
267
+ "--all-cases",
268
+ "--output", (Join-Path $resolvedArtifactsDir "graphiti-gate.json")
269
+ ) `
270
+ -OutputFile "graphiti-gate.stdout.json" `
271
+ -AllowFailure
272
+
273
+ if ($script:LastArtifactStepExitCode -eq 0) {
274
+ Invoke-ArtifactStep `
275
+ -Name "graphiti_eval_smoke" `
276
+ -Command $PythonCommand `
277
+ -Arguments @(
278
+ "-m", "people_network_memory.cli", "eval-graphiti",
279
+ "--isolated",
280
+ "--max-interactions", "2",
281
+ "--max-queries", "2",
282
+ "--output", (Join-Path $resolvedArtifactsDir "graphiti-eval.json")
283
+ ) `
284
+ -OutputFile "graphiti-eval.stdout.json" `
285
+ -AllowFailure
286
+ }
287
+ else {
288
+ Add-SkippedArtifactStep `
289
+ -Name "graphiti_eval_smoke" `
290
+ -Reason "Skipped because graphiti_promotion_gate did not pass."
291
+ }
292
+ }
293
+
294
+ Save-Manifest
295
+
296
+ $failed = @($manifest.steps | Where-Object { $null -ne $_.exit_code -and $_.exit_code -ne 0 -and -not $_.allow_failure })
297
+ Write-Host ""
298
+ Write-Host "Artifacts saved to $resolvedArtifactsDir"
299
+ Write-Host "Manifest: $manifestPath"
300
+
301
+ if ($failed.Count -gt 0) {
302
+ Write-Host "Failed required steps:"
303
+ $failed | ForEach-Object { Write-Host " - $($_.name): exit $($_.exit_code)" }
304
+ exit 1
305
+ }
306
+
307
+ Write-Host "All required artifact steps passed."
@@ -0,0 +1,6 @@
1
+ """People Network Memory package."""
2
+
3
+ __all__ = ["__version__"]
4
+
5
+ __version__ = "0.1.0"
6
+
@@ -0,0 +1,16 @@
1
+ """Application use cases."""
2
+
3
+ from people_network_memory.application.services import (
4
+ BuildPersonCardService,
5
+ RecordInteractionService,
6
+ ResolveIdentityService,
7
+ RetrieveContextService,
8
+ )
9
+
10
+ __all__ = [
11
+ "BuildPersonCardService",
12
+ "RecordInteractionService",
13
+ "ResolveIdentityService",
14
+ "RetrieveContextService",
15
+ ]
16
+