toolnet-memory 0.2.1 → 0.2.2
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 +55 -28
- package/bin/toolnet-memory +55 -55
- package/bundle/agy.js +88 -0
- package/bundle/codex.js +92 -0
- package/bundle/context-runtime.js +56 -0
- package/bundle/context.js +62 -0
- package/bundle/doctor.js +3 -0
- package/bundle/full-index.js +8 -0
- package/bundle/learner.js +8 -0
- package/bundle/mcp.js +7 -0
- package/bundle/opencode.js +314 -0
- package/bundle/project-manual.js +54 -0
- package/bundle/setup.js +31 -0
- package/bundle/snapshot.js +4 -0
- package/bundle/work-continuity.js +12 -0
- package/package.json +9 -6
- package/dist/packages/cli/commands.js +0 -1
- package/dist/packages/cli/index.js +0 -1
- package/dist/packages/sdk/client.js +0 -78
- package/dist/packages/sdk/index.js +0 -1
- package/dist/packages/sdk/types.js +0 -1
- package/dist/src/api/index.js +0 -1
- package/dist/src/api/routes/health.js +0 -1
- package/dist/src/api/routes/index.js +0 -1
- package/dist/src/api/routes/memory.js +0 -1
- package/dist/src/api/routes/projects.js +0 -1
- package/dist/src/api/routes/search.js +0 -1
- package/dist/src/api/server.js +0 -1
- package/dist/src/capture/activity-capture.js +0 -21
- package/dist/src/capture/command-capture.js +0 -12
- package/dist/src/capture/error-capture.js +0 -14
- package/dist/src/capture/event-deduplicator.js +0 -28
- package/dist/src/capture/event-queue.js +0 -20
- package/dist/src/capture/file-capture.js +0 -23
- package/dist/src/capture/index.js +0 -8
- package/dist/src/capture/session-capture.js +0 -18
- package/dist/src/capture/tool-capture.js +0 -13
- package/dist/src/code-intelligence/analysis/analysis-engine.js +0 -26
- package/dist/src/code-intelligence/analysis/dead-code-analyzer.js +0 -127
- package/dist/src/code-intelligence/analysis/dependency-analyzer.js +0 -66
- package/dist/src/code-intelligence/analysis/index.js +0 -4
- package/dist/src/code-intelligence/analysis/types.js +0 -1
- package/dist/src/code-intelligence/architecture/architecture-engine.js +0 -48
- package/dist/src/code-intelligence/architecture/clusters.js +0 -322
- package/dist/src/code-intelligence/architecture/entry-points.js +0 -150
- package/dist/src/code-intelligence/architecture/hotspots.js +0 -136
- package/dist/src/code-intelligence/architecture/index.js +0 -6
- package/dist/src/code-intelligence/architecture/layers.js +0 -197
- package/dist/src/code-intelligence/architecture/types.js +0 -1
- package/dist/src/code-intelligence/chunks/index.js +0 -2
- package/dist/src/code-intelligence/chunks/smart-chunker.js +0 -125
- package/dist/src/code-intelligence/chunks/types.js +0 -1
- package/dist/src/code-intelligence/git/git-diff.js +0 -109
- package/dist/src/code-intelligence/git/index.js +0 -1
- package/dist/src/code-intelligence/graph/architecture.js +0 -26
- package/dist/src/code-intelligence/graph/graph-builder.js +0 -213
- package/dist/src/code-intelligence/graph/graph-store.js +0 -57
- package/dist/src/code-intelligence/graph/index.js +0 -1
- package/dist/src/code-intelligence/graph/relationships.js +0 -1
- package/dist/src/code-intelligence/graph/trace.js +0 -65
- package/dist/src/code-intelligence/impact/blast-radius.js +0 -160
- package/dist/src/code-intelligence/impact/change-mapper.js +0 -69
- package/dist/src/code-intelligence/impact/edit-tool-detector.js +0 -92
- package/dist/src/code-intelligence/impact/impact-analyzer.js +0 -60
- package/dist/src/code-intelligence/impact/impact-guard.js +0 -33
- package/dist/src/code-intelligence/impact/index.js +0 -5
- package/dist/src/code-intelligence/incremental/file-hash.js +0 -8
- package/dist/src/code-intelligence/incremental/incremental-indexer.js +0 -106
- package/dist/src/code-intelligence/incremental/manifest-builder.js +0 -19
- package/dist/src/code-intelligence/incremental/manifest.js +0 -31
- package/dist/src/code-intelligence/index.js +0 -23
- package/dist/src/code-intelligence/indexer/file-watcher.js +0 -1
- package/dist/src/code-intelligence/indexer/incremental-indexer.js +0 -1
- package/dist/src/code-intelligence/indexer/index.js +0 -1
- package/dist/src/code-intelligence/indexer/repository-indexer.js +0 -31
- package/dist/src/code-intelligence/indexer/repository-scanner.js +0 -50
- package/dist/src/code-intelligence/parsers/index.js +0 -1
- package/dist/src/code-intelligence/parsers/tree-sitter/index.js +0 -1
- package/dist/src/code-intelligence/parsers/tree-sitter/languages.js +0 -1
- package/dist/src/code-intelligence/parsers/tree-sitter/parser.js +0 -1
- package/dist/src/code-intelligence/parsers/typescript-parser.js +0 -190
- package/dist/src/code-intelligence/query/graph-query-engine.js +0 -275
- package/dist/src/code-intelligence/query/index.js +0 -2
- package/dist/src/code-intelligence/query/types.js +0 -1
- package/dist/src/code-intelligence/resolution/index.js +0 -2
- package/dist/src/code-intelligence/resolution/types.js +0 -1
- package/dist/src/code-intelligence/resolution/typescript-resolver.js +0 -316
- package/dist/src/code-intelligence/rich/index.js +0 -1
- package/dist/src/code-intelligence/rich/rich-graph-enricher.js +0 -463
- package/dist/src/code-intelligence/semantic/index.js +0 -2
- package/dist/src/code-intelligence/semantic/semantic-code-engine.js +0 -213
- package/dist/src/code-intelligence/semantic/source-priority.js +0 -22
- package/dist/src/code-intelligence/symbols/index.js +0 -1
- package/dist/src/code-intelligence/symbols/reference-resolver.js +0 -21
- package/dist/src/code-intelligence/symbols/symbol-extractor.js +0 -1
- package/dist/src/code-intelligence/test-analysis.js +0 -72
- package/dist/src/code-intelligence/test-architecture.js +0 -67
- package/dist/src/code-intelligence/test-impact.js +0 -47
- package/dist/src/code-intelligence/test-incremental.js +0 -24
- package/dist/src/code-intelligence/test-index.js +0 -39
- package/dist/src/code-intelligence/test-resolution.js +0 -54
- package/dist/src/code-intelligence/test-rich-graph.js +0 -48
- package/dist/src/code-intelligence/test-semantic.js +0 -57
- package/dist/src/code-intelligence/test-visualization.js +0 -36
- package/dist/src/code-intelligence/types.js +0 -1
- package/dist/src/code-intelligence/visualization/index.js +0 -2
- package/dist/src/code-intelligence/visualization/types.js +0 -1
- package/dist/src/code-intelligence/visualization/visualization-builder.js +0 -104
- package/dist/src/core/config.js +0 -66
- package/dist/src/core/global-env.js +0 -70
- package/dist/src/core/importance.js +0 -30
- package/dist/src/core/index.js +0 -5
- package/dist/src/core/memory-engine.js +0 -183
- package/dist/src/core/project-manager.js +0 -243
- package/dist/src/core/types.js +0 -1
- package/dist/src/embeddings/batch-embedder.js +0 -1
- package/dist/src/embeddings/cache.js +0 -1
- package/dist/src/embeddings/embedder.js +0 -51
- package/dist/src/embeddings/index.js +0 -4
- package/dist/src/embeddings/local.js +0 -37
- package/dist/src/embeddings/provider.js +0 -1
- package/dist/src/embeddings/remote.js +0 -76
- package/dist/src/embeddings/test-connection.js +0 -16
- package/dist/src/hooks/after-edit.js +0 -3
- package/dist/src/hooks/after-tool.js +0 -3
- package/dist/src/hooks/auto-context.js +0 -68
- package/dist/src/hooks/auto-impact-guard.js +0 -94
- package/dist/src/hooks/auto-retrieval.js +0 -27
- package/dist/src/hooks/before-agent.js +0 -18
- package/dist/src/hooks/before-compact.js +0 -3
- package/dist/src/hooks/before-edit.js +0 -20
- package/dist/src/hooks/index.js +0 -11
- package/dist/src/hooks/runtime.js +0 -110
- package/dist/src/hooks/session-end.js +0 -3
- package/dist/src/hooks/session-start.js +0 -3
- package/dist/src/index.js +0 -22
- package/dist/src/mcp/bootstrap.js +0 -68
- package/dist/src/mcp/context.js +0 -1
- package/dist/src/mcp/index.js +0 -3
- package/dist/src/mcp/server.js +0 -45
- package/dist/src/mcp/tools/analyze-impact.js +0 -41
- package/dist/src/mcp/tools/dead-code.js +0 -55
- package/dist/src/mcp/tools/find-callers.js +0 -24
- package/dist/src/mcp/tools/find-dependencies.js +0 -57
- package/dist/src/mcp/tools/find-symbol.js +0 -7
- package/dist/src/mcp/tools/get-architecture.js +0 -11
- package/dist/src/mcp/tools/graph-dependents.js +0 -24
- package/dist/src/mcp/tools/graph-neighborhood.js +0 -58
- package/dist/src/mcp/tools/graph-path.js +0 -43
- package/dist/src/mcp/tools/graph-query-utils.js +0 -32
- package/dist/src/mcp/tools/impact-guard.js +0 -34
- package/dist/src/mcp/tools/index-repository.js +0 -1
- package/dist/src/mcp/tools/index.js +0 -20
- package/dist/src/mcp/tools/memory-decisions.js +0 -1
- package/dist/src/mcp/tools/memory-forget.js +0 -14
- package/dist/src/mcp/tools/memory-recent.js +0 -1
- package/dist/src/mcp/tools/memory-remember.js +0 -33
- package/dist/src/mcp/tools/memory-rules.js +0 -1
- package/dist/src/mcp/tools/memory-search.js +0 -18
- package/dist/src/mcp/tools/memory-todos.js +0 -1
- package/dist/src/mcp/tools/project-context.js +0 -25
- package/dist/src/mcp/tools/search-code.js +0 -21
- package/dist/src/mcp/tools/semantic-code-search.js +0 -33
- package/dist/src/mcp/tools/snapshot-create.js +0 -13
- package/dist/src/mcp/tools/snapshot-list.js +0 -7
- package/dist/src/mcp/tools/snapshot-restore.js +0 -13
- package/dist/src/mcp/tools/trace-calls.js +0 -37
- package/dist/src/memory/activity.js +0 -5
- package/dist/src/memory/conflict-detector.js +0 -105
- package/dist/src/memory/consolidator.js +0 -104
- package/dist/src/memory/decay.js +0 -59
- package/dist/src/memory/decisions.js +0 -3
- package/dist/src/memory/deduplicate.js +0 -25
- package/dist/src/memory/index.js +0 -11
- package/dist/src/memory/maintenance.js +0 -16
- package/dist/src/memory/remember.js +0 -3
- package/dist/src/memory/rules.js +0 -3
- package/dist/src/memory/search.js +0 -3
- package/dist/src/memory/summaries.js +0 -1
- package/dist/src/memory/todos.js +0 -3
- package/dist/src/processor/decision-extractor.js +0 -13
- package/dist/src/processor/importance-scorer.js +0 -22
- package/dist/src/processor/index.js +0 -7
- package/dist/src/processor/memory-extractor.js +0 -59
- package/dist/src/processor/memory-processor.js +0 -105
- package/dist/src/processor/rule-extractor.js +0 -13
- package/dist/src/processor/summarizer.js +0 -71
- package/dist/src/processor/todo-extractor.js +0 -12
- package/dist/src/production/config-check.js +0 -34
- package/dist/src/production/doctor.js +0 -58
- package/dist/src/production/full-index.js +0 -114
- package/dist/src/production/health.js +0 -58
- package/dist/src/production/index-pipeline.js +0 -151
- package/dist/src/production/index.js +0 -4
- package/dist/src/production/project-lock.js +0 -95
- package/dist/src/production/project-status.js +0 -16
- package/dist/src/production/recovery.js +0 -15
- package/dist/src/production/setup.js +0 -219
- package/dist/src/production/snapshot-cli.js +0 -58
- package/dist/src/project-manual/cli.js +0 -77
- package/dist/src/project-manual/index.js +0 -2
- package/dist/src/project-manual/manager.js +0 -147
- package/dist/src/project-manual/types.js +0 -1
- package/dist/src/retrieval/bm25/index.js +0 -1
- package/dist/src/retrieval/bm25/search.js +0 -1
- package/dist/src/retrieval/bm25.js +0 -30
- package/dist/src/retrieval/context-budget.js +0 -1
- package/dist/src/retrieval/context-builder.js +0 -22
- package/dist/src/retrieval/graph/index.js +0 -1
- package/dist/src/retrieval/graph/search.js +0 -1
- package/dist/src/retrieval/hybrid/hybrid-search.js +0 -1
- package/dist/src/retrieval/hybrid/index.js +0 -1
- package/dist/src/retrieval/hybrid-search.js +0 -68
- package/dist/src/retrieval/importance.js +0 -16
- package/dist/src/retrieval/index.js +0 -12
- package/dist/src/retrieval/query-analyzer.js +0 -32
- package/dist/src/retrieval/query-classifier.js +0 -1
- package/dist/src/retrieval/query-expansion.js +0 -1
- package/dist/src/retrieval/recency.js +0 -12
- package/dist/src/retrieval/reranker/index.js +0 -1
- package/dist/src/retrieval/reranker/reranker.js +0 -1
- package/dist/src/retrieval/retrieval-engine.js +0 -17
- package/dist/src/retrieval/tokenizer.js +0 -9
- package/dist/src/retrieval/type-priority.js +0 -10
- package/dist/src/retrieval/types.js +0 -1
- package/dist/src/retrieval/vector/index.js +0 -4
- package/dist/src/retrieval/vector/metadata-filter.js +0 -1
- package/dist/src/retrieval/vector/persistence.js +0 -67
- package/dist/src/retrieval/vector/search.js +0 -12
- package/dist/src/retrieval/vector/test-persistence.js +0 -36
- package/dist/src/retrieval/vector/upsert.js +0 -40
- package/dist/src/retrieval/vector/vector-store.js +0 -89
- package/dist/src/retrieval/vector-hybrid-engine.js +0 -91
- package/dist/src/runtime/bootstrap.js +0 -28
- package/dist/src/runtime/graceful.js +0 -17
- package/dist/src/runtime/index.js +0 -3
- package/dist/src/runtime/toolnet-memory-runtime.js +0 -156
- package/dist/src/security/encryption.js +0 -1
- package/dist/src/security/file-filter.js +0 -30
- package/dist/src/security/index.js +0 -3
- package/dist/src/security/sanitizer.js +0 -49
- package/dist/src/security/secret-scanner.js +0 -56
- package/dist/src/session/agy/adapter.js +0 -159
- package/dist/src/session/agy/cli.js +0 -87
- package/dist/src/session/agy/hook-installer.js +0 -62
- package/dist/src/session/agy/hook.js +0 -167
- package/dist/src/session/agy/index.js +0 -5
- package/dist/src/session/agy/project-resolver.js +0 -27
- package/dist/src/session/agy/recovery.js +0 -112
- package/dist/src/session/agy/transcript.js +0 -191
- package/dist/src/session/codex/adapter.js +0 -138
- package/dist/src/session/codex/cli.js +0 -236
- package/dist/src/session/codex/context-hook-installer.js +0 -70
- package/dist/src/session/codex/context-hook.js +0 -69
- package/dist/src/session/codex/discovery.js +0 -138
- package/dist/src/session/codex/index.js +0 -6
- package/dist/src/session/codex/notify-installer.js +0 -135
- package/dist/src/session/codex/project-resolver.js +0 -25
- package/dist/src/session/codex/recovery.js +0 -27
- package/dist/src/session/codex/rollout.js +0 -186
- package/dist/src/session/core.js +0 -239
- package/dist/src/session/identity.js +0 -57
- package/dist/src/session/index.js +0 -9
- package/dist/src/session/learner/cli.js +0 -62
- package/dist/src/session/learner/extractor.js +0 -461
- package/dist/src/session/learner/index.js +0 -4
- package/dist/src/session/learner/journal.js +0 -159
- package/dist/src/session/learner/learner.js +0 -124
- package/dist/src/session/learner/types.js +0 -1
- package/dist/src/session/opencode/adapter.js +0 -553
- package/dist/src/session/opencode/cli.js +0 -121
- package/dist/src/session/opencode/index.js +0 -2
- package/dist/src/session/opencode/plugin-installer.js +0 -230
- package/dist/src/session/store.js +0 -242
- package/dist/src/session/types.js +0 -1
- package/dist/src/session/utils.js +0 -47
- package/dist/src/session/wal.js +0 -303
- package/dist/src/snapshot/index.js +0 -2
- package/dist/src/snapshot/manager.js +0 -130
- package/dist/src/snapshot/test-snapshot.js +0 -28
- package/dist/src/snapshot/types.js +0 -1
- package/dist/src/storage/architecture-store.js +0 -37
- package/dist/src/storage/cleanup-health.js +0 -20
- package/dist/src/storage/code-analysis-store.js +0 -35
- package/dist/src/storage/code-chunk-store.js +0 -25
- package/dist/src/storage/code-graph-store.js +0 -24
- package/dist/src/storage/code-manifest-store.js +0 -24
- package/dist/src/storage/code-vector-store.js +0 -25
- package/dist/src/storage/github/client.js +0 -1
- package/dist/src/storage/github/index.js +0 -1
- package/dist/src/storage/google-drive/client.js +0 -1
- package/dist/src/storage/google-drive/index.js +0 -1
- package/dist/src/storage/huggingface/client.js +0 -116
- package/dist/src/storage/huggingface/index.js +0 -1
- package/dist/src/storage/index.js +0 -16
- package/dist/src/storage/list-bucket-raw.js +0 -14
- package/dist/src/storage/list-bucket-tree.js +0 -38
- package/dist/src/storage/local/client.js +0 -99
- package/dist/src/storage/local/index.js +0 -1
- package/dist/src/storage/memory-store.js +0 -30
- package/dist/src/storage/project/cleanup-layout-v1.js +0 -72
- package/dist/src/storage/project/folder.js +0 -15
- package/dist/src/storage/project/index.js +0 -3
- package/dist/src/storage/project/layout.js +0 -74
- package/dist/src/storage/project/migrate-layout-v2.js +0 -101
- package/dist/src/storage/project/migrate.js +0 -73
- package/dist/src/storage/project/scoped-provider.js +0 -165
- package/dist/src/storage/provider.js +0 -26
- package/dist/src/storage/retrying-provider.js +0 -23
- package/dist/src/storage/test-connection.js +0 -32
- package/dist/src/storage/type-resolution-store.js +0 -26
- package/dist/src/storage/types.js +0 -1
- package/dist/src/storage/vector-store.js +0 -30
- package/dist/src/storage/visualization-store.js +0 -25
- package/dist/src/sync/compressor.js +0 -1
- package/dist/src/sync/conflict-resolver.js +0 -1
- package/dist/src/sync/downloader.js +0 -1
- package/dist/src/sync/index.js +0 -1
- package/dist/src/sync/sync-engine.js +0 -1
- package/dist/src/sync/upload-queue.js +0 -1
- package/dist/src/utils/retry.js +0 -29
- package/dist/src/visualization/server.js +0 -270
- package/dist/src/work-continuity/brief-cache.js +0 -107
- package/dist/src/work-continuity/brief.js +0 -349
- package/dist/src/work-continuity/cli.js +0 -96
- package/dist/src/work-continuity/context-cli.js +0 -106
- package/dist/src/work-continuity/context-runtime-cli.js +0 -88
- package/dist/src/work-continuity/extractor.js +0 -339
- package/dist/src/work-continuity/handoff.js +0 -185
- package/dist/src/work-continuity/index.js +0 -14
- package/dist/src/work-continuity/injection.js +0 -84
- package/dist/src/work-continuity/journal.js +0 -49
- package/dist/src/work-continuity/learner.js +0 -127
- package/dist/src/work-continuity/reducer.js +0 -316
- package/dist/src/work-continuity/semantic-extractor.js +0 -360
- package/dist/src/work-continuity/semantic-journal.js +0 -55
- package/dist/src/work-continuity/semantic-learner.js +0 -125
- package/dist/src/work-continuity/semantic-reducer.js +0 -268
- package/dist/src/work-continuity/semantic-types.js +0 -1
- package/dist/src/work-continuity/types.js +0 -1
- package/dist/visualization/public/index.html +0 -2896
- package/dist/visualization/public/vendor/3d-force-graph.min.js +0 -5
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import{existsSync as Wt,readFileSync as zt}from"node:fs";import{homedir as Bt}from"node:os";import{join as Ft}from"node:path";function Dt(t){let e=t.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
|
|
2
|
+
`).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function Kt(){let t=process.env.TOOLNET_GLOBAL_ENV??Ft(Bt(),".config","toolnet-memory",".env");if(!Wt(t))return;let e=zt(t,"utf8");for(let n of e.split(/\r?\n/)){let r=n.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let s=r.indexOf("=");if(s<=0)continue;let o=r.slice(0,s).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(o)&&process.env[o]===void 0&&(process.env[o]=Dt(r.slice(s+1)))}}Kt();function q(t,e){return t===void 0?e:["1","true","yes","on"].includes(t.toLowerCase())}function F(t,e){if(!t)return e;let n=Number(t);return Number.isFinite(n)?n:e}function Me(){return{memory:{autoCapture:q(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:q(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:q(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:q(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:F(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:F(process.env.MEMORY_RERANK_TOP,10),finalContext:F(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:F(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:F(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as qt}from"node:crypto";import{existsSync as _e,mkdirSync as Jt,readFileSync as Vt,renameSync as Ut,writeFileSync as Ht}from"node:fs";import{basename as Yt,dirname as J,join as ge,parse as $e,resolve as V}from"node:path";var Le=".toolnet",Gt="project.json";function Qt(t){return qt("sha256").update(t).digest("hex").slice(0,16)}function he(t){return ge(t,Le,Gt)}function Xt(t){return _e(he(t))}function Zt(t){let e=V(t),n=$e(e).root;for(;;){if(Xt(e))return e;if(e===n)break;let r=J(e);if(r===e)break;e=r}return null}function en(t){let e=V(t),n=$e(e).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(o=>_e(ge(e,o))))return e;if(e===n)break;let s=J(e);if(s===e)break;e=s}return V(t)}function tn(t){let e;try{e=JSON.parse(Vt(t,"utf8"))}catch(s){throw new Error(`Invalid ToolNet project manifest: ${t}: ${s instanceof Error?s.message:String(s)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${t}`);let n=e;if(typeof n.id!="string"||!n.id.trim())throw new Error(`ToolNet project manifest is missing id: ${t}`);if(typeof n.name!="string"||!n.name.trim())throw new Error(`ToolNet project manifest is missing name: ${t}`);let r=new Date().toISOString();return{version:1,id:n.id,name:n.name,remote:typeof n.remote=="string"&&n.remote.trim()?n.remote:n.name,rootPath:typeof n.rootPath=="string"?n.rootPath:J(J(t)),createdAt:typeof n.createdAt=="string"?n.createdAt:r,updatedAt:typeof n.updatedAt=="string"?n.updatedAt:r,graphVersion:typeof n.graphVersion=="number"?n.graphVersion:0,memoryVersion:typeof n.memoryVersion=="number"?n.memoryVersion:0,metadata:n.metadata&&typeof n.metadata=="object"?n.metadata:void 0}}function Ae(t,e){let n=ge(t,Le);Jt(n,{recursive:!0});let r=he(t),s=`${r}.tmp-${process.pid}`;Ht(s,JSON.stringify(e,null,2)+`
|
|
3
|
+
`,{encoding:"utf8",mode:384}),Ut(s,r)}function Ne(t,e){return{id:t.id,name:t.name,remote:t.remote,rootPath:e,createdAt:t.createdAt,updatedAt:t.updatedAt,graphVersion:t.graphVersion,memoryVersion:t.memoryVersion,metadata:t.metadata}}var U=class{detect(e=process.cwd()){let n=V(e),r=Zt(n);if(r){let c=he(r),a=tn(c);return a.rootPath!==r&&(a.rootPath=r,a.updatedAt=new Date().toISOString(),Ae(r,a)),Ne(a,r)}let s=en(n),o=new Date().toISOString(),i=Yt(s),d={version:1,id:Qt(s),name:i,remote:i,rootPath:s,createdAt:o,updatedAt:o,graphVersion:0,memoryVersion:0};return Ae(s,d),Ne(d,s)}};var nn=[{type:"openai_key",regex:/\bsk-[A-Za-z0-9_\-]{20,}\b/g},{type:"huggingface_token",regex:/\bhf_[A-Za-z0-9]{20,}\b/g},{type:"hf_s3_access_key",regex:/\bHFAK[A-Za-z0-9]{8,}\b/g},{type:"bearer_token",regex:/\bBearer\s+[A-Za-z0-9._\-]{16,}\b/gi},{type:"jwt",regex:/\beyJ[A-Za-z0-9_\-]{8,}\.[A-Za-z0-9_\-]{8,}\.[A-Za-z0-9_\-]{8,}\b/g},{type:"private_key",regex:/-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/g},{type:"password_assignment",regex:/\b(password|passwd|pwd)\s*[:=]\s*["']?[^"'\s]{6,}["']?/gi},{type:"secret_assignment",regex:/\b(secret|api[_-]?key|access[_-]?token|refresh[_-]?token)\s*[:=]\s*["']?[^"'\s]{8,}["']?/gi},{type:"cookie",regex:/\b(cookie|set-cookie)\s*[:=]\s*[^;\n]{8,}/gi}],H=class{scan(e){let n=[];for(let r of nn){let s=new RegExp(r.regex.source,r.regex.flags);for(let o of e.matchAll(s))n.push({type:r.type,value:o[0]})}return n}hasSecrets(e){return this.scan(e).length>0}};var D=class{scanner=new H;sanitize(e){let n=e,r=this.scanner.scan(e),s=new Set;for(let o of r)s.add(o.type),n=n.split(o.value).join(`[REDACTED:${o.type}]`);return{text:n,redacted:r.length,secretTypes:[...s]}}sanitizeValue(e){if(typeof e=="string")return this.sanitize(e).text;if(Array.isArray(e))return e.map(n=>this.sanitizeValue(n));if(e&&typeof e=="object"){let n={};for(let[r,s]of Object.entries(e)){let o=r.toLowerCase();o.includes("password")||o.includes("secret")||o.includes("token")||o.includes("cookie")||o.includes("authorization")?n[r]="[REDACTED]":n[r]=this.sanitizeValue(s)}return n}return e}};import{homedir as vn}from"node:os";import{join as bn}from"node:path";import{DeleteObjectCommand as rn,GetObjectCommand as sn,HeadObjectCommand as on,ListObjectsV2Command as an,PutObjectCommand as cn,S3Client as un}from"@aws-sdk/client-s3";import{getSignedUrl as dn}from"@aws-sdk/s3-request-presigner";var Y=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new un({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,n,r="application/octet-stream"){let s=typeof n=="string"?Buffer.from(n,"utf8"):n;await this.client.send(new cn({Bucket:this.bucket,Key:e,Body:s,ContentType:r}))}async get(e){let n=await dn(this.client,new sn({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(n,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`HF download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let n=await this.get(e);return n?Buffer.from(n).toString("utf8"):null}async exists(e){try{return await this.client.send(new on({Bucket:this.bucket,Key:e})),!0}catch(n){if(typeof n=="object"&&n!==null&&"$metadata"in n&&n.$metadata?.httpStatusCode===404)return!1;throw n}}async delete(e){await this.client.send(new rn({Bucket:this.bucket,Key:e}))}async list(e=""){let n=[],r;do{let s=await this.client.send(new an({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let o of s.Contents??[])o.Key&&n.push({key:o.Key,size:o.Size,updatedAt:o.LastModified?.toISOString()});r=s.IsTruncated?s.NextContinuationToken:void 0}while(r);return n}};import{access as We,mkdir as pn,readFile as ln,readdir as fn,rm as mn,stat as ze,writeFile as gn}from"node:fs/promises";import{dirname as hn,join as yn,relative as Be,resolve as Sn}from"node:path";var K=class{constructor(e){this.root=e}root;name="local";path(e){let n=e.replace(/^\/+/,"");return Sn(this.root,n)}async put(e,n){let r=this.path(e);await pn(hn(r),{recursive:!0}),await gn(r,n)}async get(e){try{return await ln(this.path(e))}catch(n){if(typeof n=="object"&&n!==null&&"code"in n&&n.code==="ENOENT")return null;throw n}}async getText(e){let n=await this.get(e);return n?Buffer.from(n).toString("utf8"):null}async exists(e){try{return await We(this.path(e)),!0}catch{return!1}}async delete(e){await mn(this.path(e),{force:!0})}async list(e=""){let n=this.path(e),r=[];try{await We(n)}catch{return r}let s=async i=>{let d=await fn(i,{withFileTypes:!0});for(let c of d){let a=yn(i,c.name);if(c.isDirectory()){await s(a);continue}let f=await ze(a);r.push({key:Be(this.root,a),size:f.size,updatedAt:f.mtime.toISOString()})}},o=await ze(n);return o.isDirectory()?await s(n):r.push({key:Be(this.root,n),size:o.size,updatedAt:o.mtime.toISOString()}),r}};function Fe(t){let e=t.localRoot??bn(vn(),".toolnet-memory","storage");if(t.provider==="huggingface"){let n=t.huggingface;return n?.namespace&&n.bucket&&n.accessKeyId&&n.secretAccessKey?new Y({namespace:n.namespace,bucket:n.bucket,accessKeyId:n.accessKeyId,secretAccessKey:n.secretAccessKey}):(console.warn("[storage] Hugging Face credentials missing. Using local fallback."),new K(e))}return new K(e)}function jn(t){return new Promise(e=>setTimeout(e,t))}async function De(t,e={}){let n=Math.max(1,e.attempts??3),r=e.baseDelayMs??150,s=e.maxDelayMs??2e3,o;for(let i=1;i<=n;i++)try{return await t()}catch(d){if(o=d,i>=n)break;let c=Math.min(s,r*2**(i-1)),a=Math.floor(Math.random()*Math.max(1,c*.2));await jn(c+a)}throw o}var wn=new Set(["put","get","getText","delete","list"]);function Ke(t,e={}){return new Proxy(t,{get(n,r){let s=Reflect.get(n,r,n);return typeof s!="function"?s:wn.has(r)?(...o)=>De(()=>Promise.resolve(s.apply(n,o)),e):s.bind(n)}})}function qe(t){let e=t.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function Je(t){let e=t.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let n=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(n)return`${n[1]}memory/records/${n[2]}`;if(n=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),n)return`${n[1]}memory/vectors/${n[2]}`;if(n=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),n)return`${n[1]}code/graph/${n[2]}`;let r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[2]}`;let s=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return s?`${s[1]}memory/records/${s[2]}`:(s=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),s?`${s[1]}memory/vectors/${s[2]}`:(s=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),s?`${s[1]}code/graph/${s[2]}`:e))}var G=class{constructor(e,n,r,s){this.provider=e;this.name=e.name,this.projectId=n,this.projectName=r,this.folder=qe(s??r),this.sourcePrefix=`projects/${n}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,n=new Date().toISOString(),r=n,s=await this.provider.getText(e);if(s){let i;try{i=JSON.parse(s)}catch(d){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${d instanceof Error?d.message:String(d)}`)}if(typeof i.id=="string"&&i.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${i.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof i.createdAt=="string"&&(r=i.createdAt)}let o={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:n};await this.provider.put(e,JSON.stringify(o,null,2)+`
|
|
4
|
+
`,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=Je(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,n,r){return await this.ensureRegistered(),this.provider.put(this.key(e),n,r)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import{existsSync as qr}from"node:fs";import{homedir as Jr}from"node:os";import{isAbsolute as Vr,join as Ur,relative as Hr,resolve as wt}from"node:path";import{DatabaseSync as Yr}from"node:sqlite";import{join as Cn}from"node:path";import{createHash as kn}from"node:crypto";import{dirname as xn}from"node:path";import{mkdirSync as In,readFileSync as On,renameSync as Pn,writeFileSync as En}from"node:fs";function v(t){return kn("sha256").update(t).digest("hex")}function ye(t){if(Array.isArray(t))return t.map(ye);if(t&&typeof t=="object"){let e=t,n={};for(let r of Object.keys(e).sort())n[r]=ye(e[r]);return n}return t}function Ve(t){return JSON.stringify(ye(t))}function Ue(t){try{return JSON.parse(On(t,"utf8"))}catch{return null}}function x(t,e){In(xn(t),{recursive:!0});let n=`${t}.${process.pid}.tmp`;En(n,JSON.stringify(e,null,2)+`
|
|
5
|
+
`,{encoding:"utf8",mode:384}),Pn(n,t)}function He(t,e){let n=t.trim(),r=n.replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,""),s=v(n).slice(0,12);if(!r||r==="."||r==="..")return`${e}--${s}`;let o=r.slice(0,100);return o===n&&n.length<=100?o:`${o.slice(0,85)}--${s}`}function Ye(t,e,n){let r=e.trim(),s=n.trim();if(!r)throw new Error("Session agent is required");if(!s)throw new Error("Native session ID is required");let o=He(r.toLowerCase(),"agent"),i=He(s,"session");return{projectId:t.id,projectName:t.name,projectRoot:t.rootPath,agent:r,nativeSessionId:s,sessionKey:`${r}:${s}`,remotePrefix:["projects",t.id,"sessions",o,i].join("/"),localDirectory:Cn(t.rootPath,".toolnet","sessions",o,i)}}function Ge(t){return String(t).padStart(12,"0")}var Q=class{constructor(e,n=100,r=512*1024){this.storage=e;this.maxEventsPerChunk=n;this.maxChunkBytes=r;if(n<1)throw new Error("maxEventsPerChunk must be positive");if(r<1024)throw new Error("maxChunkBytes is too small")}storage;maxEventsPerChunk;maxChunkBytes;async getJson(e){let n=await this.storage.getText(e);return n?JSON.parse(n):null}async putJson(e,n){await this.storage.put(e,JSON.stringify(n,null,2)+`
|
|
6
|
+
`,"application/json")}async scan(e){let n=`${e.remotePrefix}/events/`,r=await this.storage.list(n),s=[],o=0;for(let i of r){let d=i.key.match(/\/events\/(\d+)-(\d+)-[a-f0-9]+\.jsonl$/);if(!d)continue;let c=Number(d[1]),a=Number(d[2]);!Number.isFinite(c)||!Number.isFinite(a)||(s.push({key:i.key,start:c,end:a}),o=Math.max(o,a))}return s.sort((i,d)=>i.start-d.start),{chunks:s,maxSequence:o}}split(e){let n=[],r=[],s=0;for(let o of e){let i=Buffer.byteLength(JSON.stringify(o)+`
|
|
7
|
+
`,"utf8");r.length>0&&(r.length>=this.maxEventsPerChunk||s+i>this.maxChunkBytes)&&(n.push(r),r=[],s=0),r.push(o),s+=i}return r.length>0&&n.push(r),n}async loadManifest(e){return this.getJson(`${e.remotePrefix}/session.json`)}async loadCursor(e){return this.getJson(`${e.remotePrefix}/cursor.json`)}async recover(e){let n=await this.scan(e);return{maxSequence:n.maxSequence,chunkCount:n.chunks.length}}async append(e,n,r,s={}){let o=await this.loadManifest(e),i=await this.scan(e),d=n.filter(m=>m.sequence>i.maxSequence),c=0;for(let m of this.split(d)){let S=m[0],E=m[m.length-1],M=m.map(A=>JSON.stringify(A)).join(`
|
|
8
|
+
`)+`
|
|
9
|
+
`,_=v(M).slice(0,16),C=[e.remotePrefix,"events",`${Ge(S.sequence)}-${Ge(E.sequence)}-${_}.jsonl`].join("/");await this.storage.exists(C)||await this.storage.put(C,M,"application/x-ndjson"),c+=m.length}let a=await this.scan(e),f=n[n.length-1],u=o?.status??"active";f?.type==="session_end"||f?.type==="session_idle"?u="idle":f?.type==="error"?u="error":n.length>0&&(u="active");let g=new Date().toISOString(),p=n[0],y={version:1,projectId:e.projectId,projectName:e.projectName,agent:e.agent,nativeSessionId:e.nativeSessionId,sessionKey:e.sessionKey,status:u,createdAt:o?.createdAt??p?.timestamp??g,updatedAt:f?.timestamp??g,firstEventAt:o?.firstEventAt??p?.timestamp,lastEventAt:f?.timestamp??o?.lastEventAt,eventCount:a.maxSequence,chunkCount:a.chunks.length,metadata:{...o?.metadata,...s.metadata}};(s.title??o?.title)&&(y.title=s.title??o?.title);let h={version:1,projectId:e.projectId,agent:e.agent,nativeSessionId:e.nativeSessionId,lastLocalSequence:n.length>0?n[n.length-1].sequence:a.maxSequence,lastRemoteSequence:a.maxSequence,sourceCursors:r,updatedAt:g};return await this.putJson(`${e.remotePrefix}/cursor.json`,h),await this.putJson(`${e.remotePrefix}/session.json`,y),{uploadedEvents:c,lastRemoteSequence:a.maxSequence,eventCount:y.eventCount,chunkCount:y.chunkCount,status:u}}};import{closeSync as Se,existsSync as Rn,fsyncSync as Tn,mkdirSync as Mn,openSync as ve,readSync as An,rmSync as Qe,statSync as Xe,writeSync as Nn}from"node:fs";import{join as be}from"node:path";var _n=12e4,$n=80,Ln=2e3;function Wn(t){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,t)}var X=class{constructor(e){this.identity=e;Mn(e.localDirectory,{recursive:!0}),this.eventsFile=be(e.localDirectory,"events.jsonl"),this.stateFile=be(e.localDirectory,"state.json"),this.lockFile=be(e.localDirectory,"journal.lock")}identity;eventsFile;stateFile;lockFile;initialState(){let e=new Date().toISOString();return{version:1,projectId:this.identity.projectId,agent:this.identity.agent,nativeSessionId:this.identity.nativeSessionId,status:"idle",createdAt:e,updatedAt:e,lastSequence:0,lastRemoteSequence:0,remoteByteOffset:0,sourceCursors:{},recentEventIds:[]}}loadStateUnsafe(){return Ue(this.stateFile)??this.initialState()}loadState(){return this.withLock(()=>this.loadStateUnsafe())}saveStateUnsafe(e){x(this.stateFile,e)}acquireLock(){for(let e=0;e<$n;e+=1)try{return ve(this.lockFile,"wx",384)}catch(n){if(n.code!=="EEXIST")throw n;try{if(Date.now()-Xe(this.lockFile).mtimeMs>_n){Qe(this.lockFile,{force:!0});continue}}catch{}Wn(25)}throw new Error(`Session journal is locked: ${this.lockFile}`)}withLock(e){let n=this.acquireLock();try{return e()}finally{Se(n),Qe(this.lockFile,{force:!0})}}append(e){return e.length===0?[]:this.withLock(()=>{let n=this.loadStateUnsafe(),r=new Set(n.recentEventIds),s=n.lastSequence,o=[];for(let u of e){let g=u.timestamp??new Date().toISOString(),p=u.data??{},y=u.provenance?.rawDigest??v(Ve(p)),h=u.sourceEventId?[this.identity.projectId,this.identity.agent,this.identity.nativeSessionId,u.sourceEventId].join("|"):[this.identity.projectId,this.identity.agent,this.identity.nativeSessionId,s+1,u.type,g,y].join("|"),m=v(h).slice(0,32);if(r.has(m))continue;s+=1;let S={version:1,id:m,sequence:s,projectId:this.identity.projectId,agent:this.identity.agent,nativeSessionId:this.identity.nativeSessionId,type:u.type,timestamp:g,data:p,provenance:{...u.provenance,rawDigest:y}};u.role!==void 0&&(S.role=u.role),u.sourceEventId!==void 0&&(S.sourceEventId=u.sourceEventId),u.sourceSequence!==void 0&&(S.sourceSequence=u.sourceSequence),o.push(S),r.add(m)}if(o.length===0)return[];let i=o.map(u=>JSON.stringify(u)).join(`
|
|
10
|
+
`)+`
|
|
11
|
+
`,d=ve(this.eventsFile,"a",384);try{Nn(d,i,null,"utf8"),Tn(d)}finally{Se(d)}let c=o[o.length-1],a="active";c.type==="session_end"||c.type==="session_idle"?a="idle":c.type==="error"&&(a="error");let f=Array.from(r).slice(-Ln);return this.saveStateUnsafe({...n,status:a,updatedAt:c.timestamp,lastSequence:c.sequence,recentEventIds:f}),o})}readPending(){return this.withLock(()=>{let e=this.loadStateUnsafe();if(!Rn(this.eventsFile))return{events:[],startOffset:e.remoteByteOffset,endOffset:e.remoteByteOffset};let n=Xe(this.eventsFile).size,r=Math.min(e.remoteByteOffset,n);if(n<=r)return{events:[],startOffset:r,endOffset:n};let s=n-r,o=Buffer.alloc(s),i=ve(this.eventsFile,"r");try{An(i,o,0,s,r)}finally{Se(i)}return{events:o.toString("utf8").split(`
|
|
12
|
+
`).filter(Boolean).map(a=>JSON.parse(a)),startOffset:r,endOffset:n}})}markRemote(e,n){this.withLock(()=>{let r=this.loadStateUnsafe();this.saveStateUnsafe({...r,lastRemoteSequence:Math.max(r.lastRemoteSequence,e),remoteByteOffset:Math.max(r.remoteByteOffset,n),updatedAt:new Date().toISOString()})})}setSourceCursor(e,n){this.withLock(()=>{let r=this.loadStateUnsafe();this.saveStateUnsafe({...r,sourceCursors:{...r.sourceCursors,[e]:String(n)},updatedAt:new Date().toISOString()})})}};import{closeSync as er,existsSync as tr,openSync as nr,readSync as rr,statSync as sr}from"node:fs";function Ze(t,e){let n=e.toLowerCase();return n.includes("kh\xF4ng \u0111\u01B0\u1EE3c")||n.includes("tuy\u1EC7t \u0111\u1ED1i")||n.includes("must not")||n.includes("never ")?"critical":t==="rule"||t==="decision"?"high":t==="todo"||n.includes("error")||n.includes("failed")||n.includes("exception")?"normal":"temporary"}var rt=[/không được/iu,/tuyệt đối/iu,/bắt buộc/iu,/đừng\s+/iu,/must not/iu,/do not/iu,/don't/iu,/\bnever\b/iu],zn=[/từ giờ/iu,/về sau/iu,/mỗi lần/iu,/luôn luôn/iu,/\bluôn\b/iu,/quy tắc/iu,/workflow/iu,/\balways\b/iu,/\brequired\b/iu,/\bmust\b/iu,/from now on/iu],Bn=[/\bchốt\b/iu,/quyết định/iu,/sẽ dùng/iu,/chọn .+ thay/iu,/đổi sang/iu,/chuyển sang/iu,/\bdecided\b/iu,/\bchosen\b/iu,/we will use/iu,/use .+ instead/iu,/switch(?:ed)? to/iu],Fn=[/\btodo\b/iu,/cần làm/iu,/cần thêm/iu,/cần sửa/iu,/cần kiểm tra/iu,/tiếp theo/iu,/bước tiếp theo/iu,/còn phải/iu,/còn cần/iu,/next step/iu,/\bneed to\b/iu,/\bremaining\b/iu,/follow[- ]?up/iu],Dn=[/đã sửa/iu,/đã fix/iu,/đã khắc phục/iu,/đã xử lý/iu,/hoàn tất/iu,/hoàn thành/iu,/\bfixed\b/iu,/\bresolved\b/iu,/\bimplemented\b/iu,/\bcompleted\b/iu,/\bpasses?\b/iu],et=[/kiến trúc/iu,/pipeline/iu,/adapter/iu,/schema/iu,/runtime/iu,/namespace/iu,/storage/iu,/lưu trữ/iu,/workflow/iu,/session core/iu,/memory engine/iu,/retrieval/iu],Kn=[/\bdùng\b/iu,/\btách\b/iu,/\bthay\b/iu,/\bchuyển\b/iu,/\blưu\b/iu,/\bmap\b/iu,/\buse\b/iu,/\bsplit\b/iu,/\bstore\b/iu,/\breplace\b/iu,/\bmove\b/iu],qn=[/đường dẫn/iu,/\bpath\b/iu,/\bport\b/iu,/\bendpoint\b/iu,/\bdomain\b/iu,/\bbucket\b/iu,/\brepository\b/iu,/\brepo\b/iu,/\bbranch\b/iu,/\/[A-Za-z0-9._/-]{4,}/u],Jn=[/\blà\b/iu,/\bở\b/iu,/\bdùng\b/iu,/\bnằm\b/iu,/\bis\b/iu,/\buse\b/iu,/located/iu,/runs on/iu],tt=new Set(["content","text","message","prompt","summary","description","reason","title","last_assistant_message","lastAssistantMessage","input_messages","inputMessages"]),Vn=new Set(["payload","data","content","message","messages","parts","summary"]);function I(t,e){return e.some(n=>n.test(t))}function st(t){return t.normalize("NFKC").replace(/\r/g,"").replace(/^[\s>*#\-•]+/u,"").replace(/\s+/g," ").trim()}function Un(t){return st(t).toLowerCase().replace(/[^\p{L}\p{N}:/._-]+/gu," ").replace(/\s+/g," ").trim()}function Hn(t){return!(t.length<12||t.length>1e3||(t.match(new RegExp("\\p{L}","gu"))??[]).length<6||/^(?:https?:\/\/\S+|[A-Za-z0-9+/=]{80,})$/u.test(t))}function je(t,e,n,r=0){if(!(r>6)){if(typeof t=="string"){(!e||tt.has(e))&&n.push(t);return}if(Array.isArray(t)){for(let s of t.slice(0,50))je(s,e,n,r+1);return}if(!(!t||typeof t!="object"))for(let[s,o]of Object.entries(t))(tt.has(s)||Vn.has(s))&&je(o,s,n,r+1)}}function Yn(t){let e=[];je(t.data,void 0,e);let n=[],r=new Set;for(let s of e)for(let o of s.split(/\n+|(?<=[.!?])\s+/u)){let i=st(o);if(Hn(i)&&!r.has(i)&&(r.add(i),n.push(i),n.length>=50))return n}return n}function nt(t){return(t.role??(typeof t.data.role=="string"?t.data.role:"")).toLowerCase()}function Gn(t,e,n){if(n.type==="decision")return{kind:"decision",confidence:1};if(n.type==="todo")return{kind:"todo",confidence:1};let r=e==="user"||n.type==="user_prompt",s=e==="assistant"||n.type==="assistant_message";return r&&I(t,rt)?{kind:"rule",confidence:.98}:r&&I(t,zn)?{kind:"rule",confidence:.92}:I(t,Bn)?{kind:I(t,et)?"architecture":"decision",confidence:r?.93:.86}:r&&I(t,Fn)?{kind:"todo",confidence:.87}:I(t,et)&&I(t,Kn)?{kind:"architecture",confidence:r?.88:.82}:s&&I(t,Dn)?{kind:"fix",confidence:.8}:r&&I(t,qn)&&I(t,Jn)?{kind:"context",confidence:.79}:null}function Qn(t){switch(t){case"rule":return"rule";case"decision":case"architecture":return"decision";case"todo":return"todo";case"fix":case"context":return"code"}}function Xn(t,e,n){return t==="rule"&&I(n,rt)?"critical":t==="architecture"||t==="decision"||t==="rule"?"high":t==="fix"||t==="context"?"normal":Ze(e,n)}function ot(t,e){let n=[],r=new Set,s=new Map;for(let o of e){let i=typeof o.data.messageId=="string"?o.data.messageId:void 0,d=nt(o);i&&d&&s.set(i,d)}for(let o of e){let i=nt(o),d=typeof o.data.messageId=="string"?o.data.messageId:void 0;!i&&d&&(i=s.get(d)??"");for(let c of Yn(o)){let a=Gn(c,i,o);if(!a||a.confidence<.75)continue;let f=Qn(a.kind),u=Un(c),g=v([t.projectId,a.kind,u].join("|"));if(r.has(g))continue;r.add(g);let p=o.provenance.sourcePath?[o.provenance.sourcePath]:[],y=o.sourceEventId?[o.sourceEventId]:[];n.push({version:1,fingerprint:g,projectId:t.projectId,agent:t.agent,nativeSessionId:t.nativeSessionId,sessionKey:t.sessionKey,kind:a.kind,type:f,content:c,confidence:a.confidence,importance:Xn(a.kind,f,c),tags:[f],provenance:{agent:t.agent,nativeSessionId:t.nativeSessionId,sessionKey:t.sessionKey,eventIds:[o.id],sourceEventIds:y,sourcePaths:p,firstSequence:o.sequence,lastSequence:o.sequence},createdAt:o.timestamp})}}return n}function it(t){return String(t).padStart(12,"0")}function Zn(t){return t.remotePrefix.replace("/sessions/","/memory/learned/")}var Z=class{constructor(e){this.storage=e}storage;async write(e,n,r){if(r.length===0||n.length===0)return null;let s=Math.min(...n.map(f=>f.sequence)),o=Math.max(...n.map(f=>f.sequence)),i={version:1,projectId:e.projectId,agent:e.agent,nativeSessionId:e.nativeSessionId,sessionKey:e.sessionKey,createdAt:new Date().toISOString(),firstSequence:s,lastSequence:o,candidateCount:r.length,candidates:r},d=JSON.stringify(i,null,2)+`
|
|
13
|
+
`,c=v(r.map(f=>f.fingerprint).sort().join("|")).slice(0,16),a=[Zn(e),"batches",`${it(s)}-${it(o)}-${c}.json`].join("/");return await this.storage.exists(a)||await this.storage.put(a,d,"application/json"),a}};function or(t,e){if(!tr(t))return{events:[],nextOffset:e};let n=sr(t).size,r=Number.isFinite(e)?Math.max(0,e):0;if(r>n&&(r=0),r===n)return{events:[],nextOffset:n};let s=n-r,o=Buffer.alloc(s),i=nr(t,"r");try{rr(i,o,0,s,r)}finally{er(i)}let d=o.toString("utf8"),c=d.lastIndexOf(`
|
|
14
|
+
`);if(c<0)return{events:[],nextOffset:r};let a=d.slice(0,c+1);return{events:a.split(`
|
|
15
|
+
`).filter(Boolean).flatMap(u=>{try{return[JSON.parse(u)]}catch{return[]}}),nextOffset:r+Buffer.byteLength(a,"utf8")}}var ee=class{constructor(e){this.options=e;this.journal=new Z(e.storage)}options;journal;async learnNew(){let e=this.options.wal.loadState(),n=Number(e.sourceCursors["memory.learner.offset"]??0),r=Number.isFinite(n)?n:0,s=or(this.options.wal.eventsFile,r);if(s.events.length===0)return{scannedEvents:0,candidates:0,journalWritten:!1,nextOffset:s.nextOffset};let o=ot(this.options.identity,s.events),i=!1;return o.length>0&&(i=!!await this.journal.write(this.options.identity,s.events,o)),this.options.wal.setSourceCursor("memory.learner.offset",s.nextOffset),{scannedEvents:s.events.length,candidates:o.length,journalWritten:i,nextOffset:s.nextOffset}}};import{closeSync as lr,existsSync as fr,openSync as mr,readSync as gr,statSync as hr}from"node:fs";var ir=new Set(["content","text","message","prompt","summary","description","title","reason","last_assistant_message","lastAssistantMessage"]);function L(t){return t.normalize("NFKC").replace(/\s+/g," ").replace(/^[\s>*#•-]+/u,"").trim()}function ct(t){return L(t).toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function $(t,e,n=0){if(!(n>6)){if(typeof t=="string"){e.push(t);return}if(Array.isArray(t)){for(let r of t.slice(0,50))$(r,e,n+1);return}if(!(!t||typeof t!="object"))for(let[r,s]of Object.entries(t))(ir.has(r)||["data","payload","parts","messages"].includes(r))&&$(s,e,n+1)}}function te(t){return/\b(cancelled|canceled)\b|đã hủy|bỏ qua/iu.test(t)?"cancelled":/\b(blocked|blocker)\b|đang vướng|bị vướng|đang kẹt|chưa thể/iu.test(t)?"blocked":/\b(completed|complete|done|finished|passed)\b|hoàn thành|hoàn tất|đã xong|đã làm xong|\bxong\b/iu.test(t)?"completed":/\bin[\s_-]*progress\b|working on|đang làm|đang thực hiện|đang xử lý|đang triển khai/iu.test(t)?"in_progress":"pending"}function at(t){let e=L(t);return/^(?:đang\s+làm|đang\s+thực\s+hiện|đang\s+xử\s+lý|đang\s+triển\s+khai|hoàn\s+thành|hoàn\s+tất|đã\s+xong|đã\s+làm\s+xong|xong|pending|in[\s_-]*progress|completed|complete|done|finished|blocked|cancelled|canceled)[.!]*$/iu.test(e)}function w(t,e,n,r,s={}){let o=L(r),i=s.key??ct(o);return{version:1,id:v([t.projectId,n,i,e.id,o,s.status??"",s.order??""].join("|")).slice(0,32),projectId:t.projectId,kind:n,key:i,text:o,status:s.status,order:s.order,confidence:s.confidence??.85,occurredAt:e.timestamp,sequence:e.sequence,agent:t.agent,nativeSessionId:t.nativeSessionId,sessionKey:t.sessionKey,eventId:e.id,sourceEventId:e.sourceEventId}}function ar(t,e,n){let r=L(n);if(r.length<5||r.length>1200)return[];let s=[],o=/^(?:next|next action|next step|tiếp theo|bước tiếp theo|cần làm tiếp)\b/iu.test(r),i=r.match(/^(?:mục tiêu|goal|objective)\s*(?::|-)\s*(.+)$/iu);i?.[1]&&s.push(w(t,e,"goal",i[1],{confidence:.97}));let d=r.match(/^(?:kế hoạch|plan)\s*(?::|-)\s*(.+)$/iu);d?.[1]&&s.push(w(t,e,"plan",d[1],{confidence:.95}));let c=/\b(?:phase|giai đoạn|giai doan|stage)\s*(\d+)\s*(?:[:\-–—]\s*)?([^.;\n]{0,220})/giu,a;for(;!o&&(a=c.exec(r));){let g=Number(a[1]),p=L(a[2]??""),y=p&&!at(p)?`Phase ${g} - ${p}`:`Phase ${g}`;s.push(w(t,e,"phase",y,{key:`phase:${g}`,order:g,status:te(r),confidence:.93}))}let f=n.match(/^\s*[-*]\s*\[([ xX])\]\s*(.+)$/u);f?.[2]&&s.push(w(t,e,"task",f[2],{status:f[1].trim()?"completed":te(f[2]),confidence:.96}));let u=r.match(/^(?:todo|task|việc)\s*(\d+)?(?:\s*[:.\-–—]\s*|\s+)(.+)$/iu);if(u?.[2]){let g=u[1]?Number(u[1]):void 0,p=L(u[2]),y=at(p);s.push(w(t,e,"task",y&&g!==void 0?`TODO ${g}`:p,{key:g!==void 0?`task:${g}`:ct(p),order:g,status:te(r),confidence:.93}))}if(/^(?:next|next action|next step|tiếp theo|bước tiếp theo|cần làm tiếp)\b/iu.test(r)){let g=r.replace(/^(?:next|next action|next step|tiếp theo|bước tiếp theo|cần làm tiếp)\s*(?::|-)?\s*/iu,"");g&&s.push(w(t,e,"next_action",g,{confidence:.9}))}return/\b(blocker|blocked)\b|đang vướng|bị vướng|đang kẹt/iu.test(r)&&s.push(w(t,e,"blocker",r,{confidence:.9})),/\b(chú ý|lưu ý|warning|attention|cẩn thận)\b/iu.test(r)&&s.push(w(t,e,"warning",r,{confidence:.88})),/\b(chốt|quyết định|decided|decision)\b/iu.test(r)&&s.push(w(t,e,"decision",r,{confidence:.9})),s}function ut(t,e){if(e.length===0)return[];let n=[],r=new Set;function s(i){r.has(i.id)||(r.add(i.id),n.push(i))}for(let i of e){if(i.type==="decision"){let c=[];$(i.data,c);for(let a of c)s(w(t,i,"decision",a,{confidence:1}))}if(i.type==="todo"){let c=[];$(i.data,c);for(let a of c)s(w(t,i,"task",a,{status:te(a),confidence:1}))}if(["file_write","file_edit"].includes(i.type))for(let c of["filePath","path","file"]){let a=i.data[c];typeof a=="string"&&a&&s(w(t,i,"file",a,{confidence:1}))}if(i.type==="test"){let c=[];$(i.data,c);for(let a of c)s(w(t,i,"test",a,{confidence:1}))}let d=[];$(i.data,d);for(let c of d)for(let a of c.split(/\n+/u))for(let f of ar(t,i,a))s(f)}let o=e[e.length-1];return s(w(t,o,"session",`${t.agent}:${t.nativeSessionId}`,{key:t.sessionKey,confidence:1})),n}function dt(t){return String(t).padStart(12,"0")}var ne=class{constructor(e){this.storage=e}storage;async write(e,n){if(n.length===0)return null;let r=Math.min(...n.map(a=>a.sequence)),s=Math.max(...n.map(a=>a.sequence)),o={version:1,projectId:e.projectId,agent:e.agent,nativeSessionId:e.nativeSessionId,sessionKey:e.sessionKey,createdAt:new Date().toISOString(),firstSequence:r,lastSequence:s,observations:n},i=JSON.stringify(o,null,2)+`
|
|
16
|
+
`,d=v(n.map(a=>a.id).sort().join("|")).slice(0,16),c=[`projects/${e.projectId}`,"work","observations",e.agent,e.nativeSessionId,`${dt(r)}-${dt(s)}-${d}.json`].join("/");return await this.storage.exists(c)||await this.storage.put(c,i,"application/json"),c}};import{join as pt}from"node:path";import{mkdirSync as cr}from"node:fs";function ur(t){return t.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function W(t,e=20){let n=[],r=new Set;for(let s of t.slice().reverse()){let o=ur(s);if(!(!o||r.has(o))&&(r.add(o),n.push(s),n.length>=e))break}return n.reverse()}function dr(t){return t.kind==="phase"?/^Phase\s+\d+$/iu.test(t.text):t.kind==="task"?/^(?:TODO|Task|Việc)\s+\d+$/iu.test(t.text):!1}function lt(t,e){let n=e.status??t?.status??"pending",r=n;t&&(t.status==="completed"&&n!=="completed"?r="completed":n==="pending"&&(t.status==="in_progress"||t.status==="blocked")&&(r=t.status));let s=t&&dr(e)?t.title:e.text;return{id:t?.id??v(e.key).slice(0,24),title:s,status:r,order:e.order??t?.order,confidence:Math.max(e.confidence,t?.confidence??0),updatedAt:e.occurredAt,updatedBy:{agent:e.agent,nativeSessionId:e.nativeSessionId,eventId:e.eventId}}}async function pr(t,e){let n=`projects/${t.id}/work/observations/`,r=await e.list(n),s=[];for(let o of r.filter(i=>i.key.endsWith(".json")).sort((i,d)=>i.key.localeCompare(d.key))){let i=await e.getText(o.key);if(i)try{let d=JSON.parse(i);d.version===1&&Array.isArray(d.observations)&&s.push(d)}catch{}}return s}async function re(t,e){let r=(await pr(t,e)).flatMap(l=>l.observations).sort((l,k)=>{let Re=l.occurredAt.localeCompare(k.occurredAt);if(Re!==0)return Re;let Te=l.sequence-k.sequence;return Te!==0?Te:l.id.localeCompare(k.id)}),s=new Map,o=new Map,i,d,c,a=[],f=[],u=[],g=[],p=[],y=[];for(let l of r)switch(l.kind){case"goal":i=l.text;break;case"plan":d=l.text;break;case"phase":s.set(l.key,lt(s.get(l.key),l));break;case"task":o.set(l.key,lt(o.get(l.key),l));break;case"decision":a.push(l.text);break;case"blocker":f.push(l.text);break;case"warning":u.push(l.text);break;case"next_action":g.push(l.text);break;case"file":p.push(l.text);break;case"test":y.push(l.text);break;case"session":c={agent:l.agent,nativeSessionId:l.nativeSessionId,sessionKey:l.sessionKey,updatedAt:l.occurredAt};break}let h=Array.from(s.values()).sort((l,k)=>(l.order??Number.MAX_SAFE_INTEGER)-(k.order??Number.MAX_SAFE_INTEGER)),m=Array.from(o.values()).sort((l,k)=>(l.order??Number.MAX_SAFE_INTEGER)-(k.order??Number.MAX_SAFE_INTEGER)),S=h.find(l=>l.status==="in_progress")??h.find(l=>l.status==="blocked")??h.find(l=>l.status==="pending"),E=m.find(l=>l.status==="in_progress")??m.find(l=>l.status==="blocked")??m.find(l=>l.status==="pending"),M=W([...g,...E?[E.title]:[],...!E&&S?[S.title]:[],...m.filter(l=>l.status==="pending").slice(0,5).map(l=>l.title)],8),_=W([...f,...h.filter(l=>l.status==="blocked").map(l=>l.title),...m.filter(l=>l.status==="blocked").map(l=>l.title)],20),C={version:1,projectId:t.id,projectName:t.name,goal:i,plan:d,phases:h,tasks:m,decisions:W(a,20),blockers:_,warnings:W(u,20),nextActions:M,filesTouched:W(p,30),tests:W(y,20),currentPhase:S,currentTask:E,progress:{phasesTotal:h.length,phasesCompleted:h.filter(l=>l.status==="completed").length,tasksTotal:m.length,tasksCompleted:m.filter(l=>l.status==="completed").length,blocked:h.filter(l=>l.status==="blocked").length+m.filter(l=>l.status==="blocked").length},lastSession:c,updatedAt:r.length?r[r.length-1].occurredAt:new Date().toISOString()},A=pt(t.rootPath,".toolnet","work");return cr(A,{recursive:!0}),x(pt(A,"current.json"),C),await e.put(`projects/${t.id}/work/current.json`,JSON.stringify(C,null,2)+`
|
|
17
|
+
`,"application/json"),C}async function se(t,e){let n=await e.getText(`projects/${t.id}/work/current.json`);if(!n)return null;try{return JSON.parse(n)}catch{return null}}function yr(t,e){if(!fr(t))return{events:[],nextOffset:e};let n=hr(t).size,r=Number.isFinite(e)?Math.max(0,e):0;if(r>n&&(r=0),r===n)return{events:[],nextOffset:n};let s=n-r,o=Buffer.alloc(s),i=mr(t,"r");try{gr(i,o,0,s,r)}finally{lr(i)}let d=o.toString("utf8"),c=d.lastIndexOf(`
|
|
18
|
+
`);if(c<0)return{events:[],nextOffset:r};let a=d.slice(0,c+1);return{events:a.split(`
|
|
19
|
+
`).filter(Boolean).flatMap(u=>{try{return[JSON.parse(u)]}catch{return[]}}),nextOffset:r+Buffer.byteLength(a,"utf8")}}var oe=class{constructor(e){this.options=e;this.journal=new ne(e.storage)}options;journal;async learnNew(){let e=this.options.wal.loadState(),n=Number(e.sourceCursors["work.continuity.offset"]??0),r=yr(this.options.wal.eventsFile,Number.isFinite(n)?n:0);if(r.events.length===0)return{scannedEvents:0,observations:0,journalWritten:!1,reconciled:!1,nextOffset:r.nextOffset};let s=ut(this.options.identity,r.events),o=!1,i=!1;return s.length>0&&(o=!!await this.journal.write(this.options.identity,s),o&&(await re(this.options.project,this.options.storage),i=!0)),this.options.wal.setSourceCursor("work.continuity.offset",r.nextOffset),{scannedEvents:r.events.length,observations:s.length,journalWritten:o,reconciled:i,nextOffset:r.nextOffset}}};import{closeSync as Or,existsSync as Pr,openSync as Er,readSync as Cr,statSync as Rr}from"node:fs";var Sr=new Set(["content","text","message","prompt","summary","description","title","reason","last_assistant_message","lastAssistantMessage"]);function z(t){return t.normalize("NFKC").replace(/\s+/g," ").replace(/^[\s>*#•-]+/u,"").trim()}function we(t,e,n=0){if(!(n>6)){if(typeof t=="string"){e.push(t);return}if(Array.isArray(t)){for(let r of t.slice(0,50))we(r,e,n+1);return}if(!(!t||typeof t!="object"))for(let[r,s]of Object.entries(t))(Sr.has(r)||["data","payload","parts","messages"].includes(r))&&we(s,e,n+1)}}function O(t,e,n,r,s,o=.95){let i=z(r);return{version:1,id:v([t.projectId,n,s.type,s.key??"",i.toLowerCase(),e.id].join("|")).slice(0,32),projectId:t.projectId,kind:n,value:i,scope:s.type,scopeKey:s.key,scopeOrder:s.order,confidence:o,evidence:{agent:t.agent,nativeSessionId:t.nativeSessionId,sessionKey:t.sessionKey,eventId:e.id,sourceEventId:e.sourceEventId,sequence:e.sequence,occurredAt:e.timestamp}}}function P(t,e){let n=t.toLowerCase();for(let r of e){let s=r.toLowerCase();if(n.startsWith(`${s}:`)||n.startsWith(`${s} -`)||n.startsWith(`${s} \u2014`))return z(t.slice(r.length+1))}return null}function vr(t){let e=t.trimStart();return e.startsWith("- ")||e.startsWith("* ")||/^\d+[.)]\s+/u.test(e)}function br(t){return z(t.trim().replace(/^[-*]\s+/u,"").replace(/^\d+[.)]\s+/u,""))}function ft(t,e){let n=[],r=new Set;function s(o){!o.value||o.value.length<3||r.has(o.id)||(r.add(o.id),n.push(o))}for(let o of e){let i=[];we(o.data,i);for(let d of i){let c={type:"project"},a=null;for(let f of d.split(/\r?\n/u)){let u=z(f);if(!u){a=null;continue}let g=u.match(/^(?:phase|giai đoạn|giai doan|stage)\s*(\d+)\s*(?:[:\-–—]\s*)?(.*)$/iu);if(g){let k=Number(g[1]);c={type:"phase",key:`phase:${k}`,order:k,title:z(g[2]??"")},a=null;continue}let p=u.match(/^(?:todo|task|việc)\s*(\d+)\s*(?:[:\-–—]\s*)?(.*)$/iu);if(p){let k=Number(p[1]);c={type:"task",key:`task:${k}`,order:k,title:z(p[2]??"")},a=null;continue}let y=P(u,["mission","s\u1EE9 m\u1EC7nh","m\u1EE5c ti\xEAu t\u1ED5ng th\u1EC3","m\u1EE5c ti\xEAu project","project goal"]);if(y){s(O(t,o,"mission",y,{type:"project"},.99)),a=null;continue}let h=P(u,["current objective","active objective","m\u1EE5c ti\xEAu hi\u1EC7n t\u1EA1i","objective","m\u1EE5c ti\xEAu","m\u1EE5c \u0111\xEDch"]);if(h){s(O(t,o,c.type==="phase"?"phase_objective":"objective",h,c,.98)),a=null;continue}let m=P(u,["why","why this","why this phase","reason","rationale","v\xEC sao","l\xFD do","t\u1EA1i sao","\xFD ngh\u0129a"]);if(m){s(O(t,o,c.type==="phase"?"phase_why":"why",m,c,.98)),a=null;continue}let S=P(u,["desired outcome","final outcome","k\u1EBFt qu\u1EA3 cu\u1ED1i","k\u1EBFt qu\u1EA3 mong mu\u1ED1n","m\u1EE5c ti\xEAu cu\u1ED1i"]);if(S){s(O(t,o,"desired_outcome",S,{type:"project"},.98)),a=null;continue}let E=P(u,["plan rationale","approach rationale","why this approach","t\u1EA1i sao ch\u1ECDn h\u01B0\u1EDBng n\xE0y","l\xFD do ch\u1ECDn h\u01B0\u1EDBng n\xE0y","l\xFD do ch\u1ECDn ki\u1EBFn tr\xFAc"]);if(E){s(O(t,o,"plan_rationale",E,{type:"project"},.98)),a=null;continue}let M=P(u,["deliverable","output","k\u1EBFt qu\u1EA3 c\u1EA7n \u0111\u1EA1t","ph\u1EA3i t\u1EA1o ra","\u0111\u1EA7u ra"]);if(M){s(O(t,o,"phase_deliverable",M,c,.97)),a=null;continue}let _=P(u,["acceptance criteria","definition of done","done khi","ho\xE0n th\xE0nh khi","ti\xEAu ch\xED ho\xE0n th\xE0nh"]);if(_){s(O(t,o,"acceptance_criterion",_,c,.98)),a="acceptance_criterion";continue}let C=P(u,["depends on","dependency","dependencies","ph\u1EE5 thu\u1ED9c","c\u1EA7n c\xF3 tr\u01B0\u1EDBc"]);if(C){s(O(t,o,"dependency",C,c,.97)),a="dependency";continue}let A=P(u,["open question","open questions","c\xE2u h\u1ECFi m\u1EDF","ch\u01B0a quy\u1EBFt \u0111\u1ECBnh","ch\u01B0a r\xF5"]);if(A){s(O(t,o,"open_question",A,c,.95)),a="open_question";continue}let l=P(u,["constraint","constraints","r\xE0ng bu\u1ED9c","gi\u1EDBi h\u1EA1n"]);if(l){s(O(t,o,"constraint",l,c,.97)),a="constraint";continue}if(/^(?:acceptance criteria|definition of done|done khi|tiêu chí hoàn thành)\s*:?\s*$/iu.test(u)){a="acceptance_criterion";continue}if(/^(?:dependencies|dependency|phụ thuộc)\s*:?\s*$/iu.test(u)){a="dependency";continue}if(/^(?:open questions|open question|câu hỏi mở)\s*:?\s*$/iu.test(u)){a="open_question";continue}if(/^(?:constraints|constraint|ràng buộc)\s*:?\s*$/iu.test(u)){a="constraint";continue}if(a&&vr(f)){s(O(t,o,a,br(f),c,.96));continue}a=null}}}return n}function mt(t){return String(t).padStart(12,"0")}var ie=class{constructor(e){this.storage=e}storage;async write(e,n){if(n.length===0)return null;let r=Math.min(...n.map(c=>c.evidence.sequence)),s=Math.max(...n.map(c=>c.evidence.sequence)),o={version:1,projectId:e.projectId,agent:e.agent,nativeSessionId:e.nativeSessionId,sessionKey:e.sessionKey,firstSequence:r,lastSequence:s,createdAt:new Date().toISOString(),observations:n},i=v(n.map(c=>c.id).sort().join("|")).slice(0,16),d=[`projects/${e.projectId}`,"work","semantic","observations",e.agent,e.nativeSessionId,`${mt(r)}-${mt(s)}-${i}.json`].join("/");return await this.storage.exists(d)||await this.storage.put(d,JSON.stringify(o,null,2)+`
|
|
20
|
+
`,"application/json"),d}};import{mkdirSync as jr}from"node:fs";import{join as gt}from"node:path";function wr(t){return{value:t.value,confidence:t.confidence,evidence:t.evidence}}function kr(t,e){if(!e)return!0;let n=t.evidence.occurredAt.localeCompare(e.evidence.occurredAt);return n!==0?n>0:t.evidence.sessionKey===e.evidence.sessionKey?t.evidence.sequence>=e.evidence.sequence:t.confidence>=e.confidence}function R(t,e){return kr(e,t)?e:t}function T(t,e=30){let n=new Set,r=[];for(let s of t){let o=s.value.normalize("NFKC").toLowerCase().replace(/\s+/g," ").trim();!o||n.has(o)||(n.add(o),r.push(s))}return r.slice(-e)}async function xr(t,e){let n=`projects/${t.id}/work/semantic/observations/`,r=await e.list(n),s=[];for(let o of r.filter(i=>i.key.endsWith(".json")).sort((i,d)=>i.key.localeCompare(d.key))){let i=await e.getText(o.key);if(i)try{let d=JSON.parse(i);d.version===1&&Array.isArray(d.observations)&&s.push(d)}catch{}}return s}function Ir(t){return{key:t.scopeKey??`phase:${t.scopeOrder??0}`,order:t.scopeOrder??0,acceptanceCriteria:[],dependencies:[],openQuestions:[],constraints:[],notes:[]}}async function ht(t,e){let r=(await xr(t,e)).flatMap(h=>h.observations).sort((h,m)=>{let S=h.evidence.occurredAt.localeCompare(m.evidence.occurredAt);return S!==0?S:h.evidence.sessionKey===m.evidence.sessionKey?h.evidence.sequence-m.evidence.sequence:h.id.localeCompare(m.id)}),s,o,i,d,c,a=new Map,f=[],u=[],g=[];for(let h of r){let m=wr(h);if(h.scope==="phase"&&h.scopeKey){let S=a.get(h.scopeKey)??Ir(h);switch(h.kind){case"phase_objective":S.objective=R(S.objective,m);break;case"phase_why":S.why=R(S.why,m);break;case"phase_deliverable":S.deliverable=R(S.deliverable,m);break;case"acceptance_criterion":S.acceptanceCriteria.push(m);break;case"dependency":S.dependencies.push(m);break;case"open_question":S.openQuestions.push(m);break;case"constraint":S.constraints.push(m);break;case"note":S.notes.push(m);break}a.set(S.key,S);continue}switch(h.kind){case"mission":s=R(s,m);break;case"objective":o=R(o,m);break;case"why":i=R(i,m);break;case"desired_outcome":d=R(d,m);break;case"plan_rationale":c=R(c,m);break;case"open_question":f.push(m);break;case"constraint":u.push(m);break;case"note":g.push(m);break}}for(let h of a.values())h.acceptanceCriteria=T(h.acceptanceCriteria,20),h.dependencies=T(h.dependencies,15),h.openQuestions=T(h.openQuestions,15),h.constraints=T(h.constraints,15),h.notes=T(h.notes,20);let p={version:1,projectId:t.id,projectName:t.name,mission:s,activeObjective:o,why:i,desiredOutcome:d,planRationale:c,phases:Array.from(a.values()).sort((h,m)=>h.order-m.order),openQuestions:T(f,20),constraints:T(u,20),notes:T(g,20),updatedAt:r.length?r[r.length-1].evidence.occurredAt:new Date().toISOString()},y=gt(t.rootPath,".toolnet","work");return jr(y,{recursive:!0}),x(gt(y,"semantic-current.json"),p),await e.put(`projects/${t.id}/work/semantic/current.json`,JSON.stringify(p,null,2)+`
|
|
21
|
+
`,"application/json"),p}async function yt(t,e){let n=await e.getText(`projects/${t.id}/work/semantic/current.json`);if(!n)return null;try{return JSON.parse(n)}catch{return null}}function Tr(t,e){if(!Pr(t))return{events:[],nextOffset:e};let n=Rr(t).size,r=Number.isFinite(e)?Math.max(0,e):0;if(r>n&&(r=0),r===n)return{events:[],nextOffset:n};let s=Buffer.alloc(n-r),o=Er(t,"r");try{Cr(o,s,0,s.length,r)}finally{Or(o)}let i=s.toString("utf8"),d=i.lastIndexOf(`
|
|
22
|
+
`);if(d<0)return{events:[],nextOffset:r};let c=i.slice(0,d+1);return{events:c.split(`
|
|
23
|
+
`).filter(Boolean).flatMap(a=>{try{return[JSON.parse(a)]}catch{return[]}}),nextOffset:r+Buffer.byteLength(c,"utf8")}}var ae=class{constructor(e){this.options=e;this.journal=new ie(e.storage)}options;journal;async learnNew(){let e=this.options.wal.loadState(),n=Number(e.sourceCursors["work.semantic.offset"]??0),r=Tr(this.options.wal.eventsFile,Number.isFinite(n)?n:0);if(r.events.length===0)return{scannedEvents:0,observations:0,journalWritten:!1,reconciled:!1,nextOffset:r.nextOffset};let s=ft(this.options.identity,r.events),o=!1,i=!1;return s.length>0&&(o=!!await this.journal.write(this.options.identity,s),o&&(await ht(this.options.project,this.options.storage),i=!0)),this.options.wal.setSourceCursor("work.semantic.offset",r.nextOffset),{scannedEvents:r.events.length,observations:s.length,journalWritten:o,reconciled:i,nextOffset:r.nextOffset}}};import{mkdirSync as Fr}from"node:fs";import{join as ke}from"node:path";import{existsSync as bt,mkdirSync as Mr,readFileSync as Ar,statSync as St,writeFileSync as Nr}from"node:fs";import{dirname as _r,join as $r}from"node:path";var vt=64*1024,Lr=`# ToolNet Project Operating Manual
|
|
24
|
+
|
|
25
|
+
This file contains persistent instructions for AI agents working on this project.
|
|
26
|
+
|
|
27
|
+
## Critical Rules
|
|
28
|
+
|
|
29
|
+
<!--
|
|
30
|
+
Examples:
|
|
31
|
+
|
|
32
|
+
- [enforce] Never edit production files directly.
|
|
33
|
+
- [enforce] Edit source only in /path/to/source.
|
|
34
|
+
- [enforce] Deploy only with ./deploy.sh --apply
|
|
35
|
+
-->
|
|
36
|
+
|
|
37
|
+
## Workflow
|
|
38
|
+
|
|
39
|
+
<!--
|
|
40
|
+
Describe the correct working process.
|
|
41
|
+
-->
|
|
42
|
+
|
|
43
|
+
## Architecture
|
|
44
|
+
|
|
45
|
+
<!--
|
|
46
|
+
Important architecture conventions.
|
|
47
|
+
-->
|
|
48
|
+
|
|
49
|
+
## Verification
|
|
50
|
+
|
|
51
|
+
<!--
|
|
52
|
+
Tests / QA required after changes.
|
|
53
|
+
-->
|
|
54
|
+
|
|
55
|
+
## Known Gotchas
|
|
56
|
+
|
|
57
|
+
<!--
|
|
58
|
+
Things an AI agent should always remember.
|
|
59
|
+
-->
|
|
60
|
+
|
|
61
|
+
## Preferences
|
|
62
|
+
|
|
63
|
+
<!--
|
|
64
|
+
- [advisory] Prefer small focused files.
|
|
65
|
+
-->
|
|
66
|
+
`;function ce(t){return $r(t.rootPath,".toolnet","PROJECT.md")}function Wr(t){return t.normalize("NFKC").replace(/\s+/g," ").trim()}function zr(t){let e=[],n=new Set,r=/^\s*[-*]\s+\[(enforce|advisory)\]\s+(.+?)\s*$/gimu,s;for(;s=r.exec(t);){let o=s[1].toLowerCase(),i=Wr(s[2]);if(!i)continue;let d=`${o}:${i.toLowerCase()}`;n.has(d)||(n.add(d),e.push({id:v(d).slice(0,24),mode:o,text:i,source:"manual"}))}return e}function Br(t){let e=ce(t);return bt(e)||(Mr(_r(e),{recursive:!0}),Nr(e,Lr,{encoding:"utf8",mode:384})),e}function ue(t,e=!1){let n=e?Br(t):ce(t);if(!bt(n))return null;if(St(n).size>vt)throw new Error(`PROJECT.md exceeds ${vt} bytes`);let s=Ar(n,"utf8");return{path:n,content:s,digest:v(s),rules:zr(s),bytes:Buffer.byteLength(s,"utf8"),updatedAt:new Date(St(n).mtimeMs).toISOString()}}function Dr(t){return{goal:t.goal,plan:t.plan,phases:t.phases.map(e=>({id:e.id,title:e.title,status:e.status,order:e.order})),tasks:t.tasks.map(e=>({id:e.id,title:e.title,status:e.status,order:e.order})),decisions:t.decisions,blockers:t.blockers,warnings:t.warnings,nextActions:t.nextActions,filesTouched:t.filesTouched,tests:t.tests}}function Kr(t){return t.phases.some(e=>e.status==="pending"||e.status==="in_progress"||e.status==="blocked")||t.tasks.some(e=>e.status==="pending"||e.status==="in_progress"||e.status==="blocked")||t.nextActions.length>0||t.blockers.length>0}var de=class{constructor(e){this.options=e}options;async capture(e,n){let r=await se(this.options.project,this.options.storage);if(r||(r=await re(this.options.project,this.options.storage)),!Kr(r))return null;let s=ue(this.options.project,!1),o=s?s.rules.filter(g=>g.mode==="enforce").map(g=>g.text):[],i=v(JSON.stringify(Dr(r))),d=v([this.options.project.id,this.options.identity.sessionKey,i].join("|")).slice(0,24),c=new Date().toISOString(),a={version:1,id:d,projectId:this.options.project.id,projectName:this.options.project.name,createdAt:c,reason:e,sourceSession:{agent:this.options.identity.agent,nativeSessionId:this.options.identity.nativeSessionId,sessionKey:this.options.identity.sessionKey,sequence:n},goal:r.goal,plan:r.plan,progress:r.progress,currentPhase:r.currentPhase,currentTask:r.currentTask,incompletePhases:r.phases.filter(g=>g.status!=="completed"&&g.status!=="cancelled"),incompleteTasks:r.tasks.filter(g=>g.status!=="completed"&&g.status!=="cancelled"),nextActions:r.nextActions.slice(0,10),blockers:r.blockers.slice(0,10),decisions:r.decisions.slice(-10),warnings:r.warnings.slice(-10),attention:[...o,...r.warnings].slice(0,20),filesTouched:r.filesTouched.slice(-20),tests:r.tests.slice(-15),stateDigest:i},f=`projects/${this.options.project.id}/work/handoffs/${d}.json`;await this.options.storage.exists(f)||await this.options.storage.put(f,JSON.stringify(a,null,2)+`
|
|
67
|
+
`,"application/json"),await this.options.storage.put(`projects/${this.options.project.id}/work/handoff-latest.json`,JSON.stringify(a,null,2)+`
|
|
68
|
+
`,"application/json");let u=ke(this.options.project.rootPath,".toolnet","work","handoffs");return Fr(u,{recursive:!0}),x(ke(u,`${d}.json`),a),x(ke(this.options.project.rootPath,".toolnet","work","handoff-latest.json"),a),a}};async function jt(t,e){let n=await e.getText(`projects/${t.id}/work/handoff-latest.json`);if(!n)return null;try{return JSON.parse(n)}catch{return null}}var pe=class{identity;wal;remote;sanitizer=new D;learner;continuity;semantic;handoff;title;metadata;constructor(e){this.identity=Ye(e.project,e.agent,e.nativeSessionId),this.title=e.title,this.metadata=this.sanitizer.sanitizeValue(e.metadata??{}),this.wal=new X(this.identity),this.remote=new Q(e.storage,e.maxEventsPerChunk??100,e.maxChunkBytes??512*1024),this.learner=new ee({project:e.project,storage:e.storage,identity:this.identity,wal:this.wal}),this.continuity=new oe({project:e.project,storage:e.storage,identity:this.identity,wal:this.wal}),this.semantic=new ae({project:e.project,storage:e.storage,identity:this.identity,wal:this.wal}),this.handoff=new de({project:e.project,storage:e.storage,identity:this.identity})}sanitizeEvent(e){let n=e.provenance?{...e.provenance,metadata:this.sanitizer.sanitizeValue(e.provenance.metadata)}:void 0;return{...e,data:this.sanitizer.sanitizeValue(e.data??{}),provenance:n}}start(e={}){let n=this.wal.loadState();return this.record({type:n.lastSequence===0?"session_start":"session_resume",data:e,provenance:{source:this.identity.agent}})}record(e){return this.wal.append([this.sanitizeEvent(e)])[0]}recordMany(e){return this.wal.append(e.map(n=>this.sanitizeEvent(n)))}setSourceCursor(e,n){this.wal.setSourceCursor(e,n)}async flush(){let e=this.wal.readPending(),n=this.wal.loadState(),r=await this.remote.append(this.identity,e.events,n.sourceCursors,{title:this.title,metadata:this.metadata});if(e.events.length>0){let s=e.events[e.events.length-1];this.wal.markRemote(s.sequence,e.endOffset)}if(process.env.TOOLNET_SESSION_LEARNING!=="0")try{await this.learner.learnNew()}catch{}if(process.env.TOOLNET_WORK_CONTINUITY!=="0")try{await this.continuity.learnNew()}catch{}if(process.env.TOOLNET_SEMANTIC_CONTINUITY!=="0")try{await this.semantic.learnNew()}catch{}if(process.env.TOOLNET_SMART_HANDOFF!=="0"&&e.events.length>0)try{let s=e.events[e.events.length-1],o=["session_idle","session_end","session_compact"].includes(s.type)?s.type:"checkpoint";await this.handoff.capture(o,s.sequence)}catch{}return r}async idle(e={}){return this.record({type:"session_idle",data:e,provenance:{source:this.identity.agent}}),this.flush()}async end(e={}){return this.record({type:"session_end",data:e,provenance:{source:this.identity.agent}}),this.flush()}status(){return this.wal.loadState()}recoverRemote(){return this.remote.recover(this.identity)}};function Pt(){return process.env.OPENCODE_DB_PATH??Ur(Jr(),".local","share","opencode","opencode.db")}function j(t){return typeof t=="string"?t:""}function N(t){if(typeof t=="number"&&Number.isFinite(t))return t;if(typeof t=="bigint")return Number(t);if(typeof t=="string"){let e=Number(t);if(Number.isFinite(e))return e}return 0}function xe(t){if(t&&typeof t=="object"&&!Buffer.isBuffer(t))return t;if(typeof t!="string")return{};try{let e=JSON.parse(t);if(e&&typeof e=="object"&&!Array.isArray(e))return e}catch{}return{}}function B(t){let e=N(t);if(e<=0)return new Date().toISOString();e<1e11&&(e*=1e3);let n=new Date(e);return Number.isNaN(n.getTime())?new Date().toISOString():n.toISOString()}function le(t,e){if(!e)return!1;let n=wt(t),r=wt(e);if(n===r)return!0;let s=Hr(n,r);return s!==""&&s!==".."&&!s.startsWith(`..${process.platform==="win32"?"\\":"/"}`)&&!Vr(s)}function kt(t){if(!t)return{time:-1,id:""};try{let e=JSON.parse(t);return{time:typeof e.time=="number"?e.time:-1,id:typeof e.id=="string"?e.id:""}}catch{return{time:-1,id:""}}}function xt(t){return JSON.stringify(t)}function Et(t){if(!qr(t))throw new Error(`OpenCode database not found: ${t}`);let e=new Yr(t,{readOnly:!0});return e.exec("PRAGMA query_only = ON"),e.exec("PRAGMA busy_timeout = 3000"),e}function Gr(t,e){let n=t.prepare(`
|
|
69
|
+
SELECT *
|
|
70
|
+
FROM "session"
|
|
71
|
+
WHERE id = ?
|
|
72
|
+
LIMIT 1
|
|
73
|
+
`).get(e);if(!n)throw new Error(`OpenCode session not found: ${e}`);return n}function Ct(t,e,n){let r=j(e.directory);if(r&&le(n.rootPath,r))return!0;let s=j(e.project_id);if(s){try{let o=t.prepare(`
|
|
74
|
+
SELECT *
|
|
75
|
+
FROM "project"
|
|
76
|
+
WHERE id = ?
|
|
77
|
+
LIMIT 1
|
|
78
|
+
`).get(s);if(o)for(let i of["worktree","directory","path"]){let d=j(o[i]);if(d&&le(n.rootPath,d))return!0}}catch{}try{if(t.prepare(`
|
|
79
|
+
SELECT directory
|
|
80
|
+
FROM "project_directory"
|
|
81
|
+
WHERE project_id = ?
|
|
82
|
+
`).all(s).some(i=>le(n.rootPath,j(i.directory))))return!0}catch{}}try{let o=t.prepare(`
|
|
83
|
+
SELECT data
|
|
84
|
+
FROM "message"
|
|
85
|
+
WHERE session_id = ?
|
|
86
|
+
ORDER BY time_created DESC
|
|
87
|
+
LIMIT 20
|
|
88
|
+
`).all(j(e.id));for(let i of o){let d=xe(i.data),c=d.path&&typeof d.path=="object"?d.path:{};for(let a of[j(c.cwd),j(c.root)])if(a&&le(n.rootPath,a))return!0}}catch{}return!1}function It(t,e,n,r){let s=`
|
|
89
|
+
SELECT *,
|
|
90
|
+
COALESCE(
|
|
91
|
+
time_updated,
|
|
92
|
+
time_created,
|
|
93
|
+
0
|
|
94
|
+
) AS __clock
|
|
95
|
+
FROM "${e}"
|
|
96
|
+
WHERE session_id = ?
|
|
97
|
+
AND (
|
|
98
|
+
COALESCE(
|
|
99
|
+
time_updated,
|
|
100
|
+
time_created,
|
|
101
|
+
0
|
|
102
|
+
) > ?
|
|
103
|
+
OR (
|
|
104
|
+
COALESCE(
|
|
105
|
+
time_updated,
|
|
106
|
+
time_created,
|
|
107
|
+
0
|
|
108
|
+
) = ?
|
|
109
|
+
AND id > ?
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
ORDER BY
|
|
113
|
+
COALESCE(
|
|
114
|
+
time_updated,
|
|
115
|
+
time_created,
|
|
116
|
+
0
|
|
117
|
+
) ASC,
|
|
118
|
+
id ASC
|
|
119
|
+
`;return t.prepare(s).all(n,r.time,r.time,r.id)}function Ot(t,e){let n=t[t.length-1];return n?{time:N(n.__clock),id:j(n.id)}:e}function Qr(t,e){let n=xe(e.data),r=j(n.role),s=N(e.__clock),o=j(e.id),i="message";return r==="user"?i="user_prompt":r==="assistant"&&(i="assistant_message"),{clock:s,order:0,event:{type:i,timestamp:B(s),role:r||void 0,sourceEventId:`message:${o}:${s}`,sourceSequence:`${s}:${o}`,data:{messageId:o,...n},provenance:{source:"opencode",sourcePath:t,sourceTable:"message",sourceRowId:o,sourceOffset:`${s}:${o}`}}}}function Xr(t,e){let n=xe(e.data),r=j(n.type),s=N(e.__clock),o=j(e.id),i=j(e.message_id),d="message_part";return r==="tool"?d="tool_call":r==="snapshot"&&(d="artifact"),{clock:s,order:1,event:{type:d,timestamp:B(s),sourceEventId:`part:${o}:${s}`,sourceSequence:`${s}:${o}`,data:{partId:o,messageId:i,...n},provenance:{source:"opencode",sourcePath:t,sourceTable:"part",sourceRowId:o,sourceOffset:`${s}:${o}`}}}}async function Ie(t){let e=t.dbPath??Pt(),n=Et(e);try{let r=Gr(n,t.nativeSessionId);if(!Ct(n,r,t.project))throw new Error(["OpenCode session does not belong to current ToolNet project.",`Session: ${t.nativeSessionId}`,`Project: ${t.project.rootPath}`,`Session directory: ${j(r.directory)||"unknown"}`].join(" "));let s=new pe({project:t.project,storage:t.storage,agent:"opencode",nativeSessionId:t.nativeSessionId,title:j(r.title)||void 0,metadata:{source:"opencode.db",openCodeProjectId:j(r.project_id)||void 0,directory:j(r.directory)||void 0}}),o=s.status(),i=kt(o.sourceCursors["opencode.message"]),d=kt(o.sourceCursors["opencode.part"]),c=It(n,"message",t.nativeSessionId,i),a=It(n,"part",t.nativeSessionId,d),f=[];if(o.lastSequence===0){let m=N(r.time_created);f.push({clock:m,order:-1,event:{type:"session_start",timestamp:B(m),sourceEventId:`session:${t.nativeSessionId}:created:${m}`,data:{title:j(r.title)||void 0,directory:j(r.directory)||void 0,openCodeProjectId:j(r.project_id)||void 0},provenance:{source:"opencode",sourcePath:e,sourceTable:"session",sourceRowId:t.nativeSessionId}}})}f.push(...c.map(m=>Qr(e,m))),f.push(...a.map(m=>Xr(e,m)));let u=N(r.time_updated)||N(r.time_created);t.compacted&&f.push({clock:u,order:98,event:{type:"session_compact",timestamp:B(u),sourceEventId:`session:${t.nativeSessionId}:compact:${u}`,data:{},provenance:{source:"opencode"}}}),t.error?f.push({clock:u,order:99,event:{type:"error",timestamp:B(u),sourceEventId:`session:${t.nativeSessionId}:error:${u}`,data:{source:"session.error"},provenance:{source:"opencode"}}}):t.idle&&f.push({clock:u,order:100,event:{type:"session_idle",timestamp:B(u),sourceEventId:`session:${t.nativeSessionId}:idle:${u}`,data:{},provenance:{source:"opencode"}}}),f.sort((m,S)=>m.clock-S.clock||m.order-S.order);let g=s.recordMany(f.map(m=>m.event)),p=Ot(c,i),y=Ot(a,d);s.setSourceCursor("opencode.message",xt(p)),s.setSourceCursor("opencode.part",xt(y));let h=await s.flush();return{nativeSessionId:t.nativeSessionId,importedMessages:c.length,importedParts:a.length,recordedEvents:g.length,eventCount:h.eventCount,chunkCount:h.chunkCount,status:h.status}}finally{n.close()}}async function Rt(t){let e=t.dbPath??Pt(),n=Et(e),r=[];try{let o=n.prepare(`
|
|
120
|
+
SELECT *
|
|
121
|
+
FROM "session"
|
|
122
|
+
ORDER BY
|
|
123
|
+
COALESCE(
|
|
124
|
+
time_updated,
|
|
125
|
+
time_created,
|
|
126
|
+
0
|
|
127
|
+
) DESC
|
|
128
|
+
`).all();for(let i of o){if(!Ct(n,i,t.project))continue;let d=j(i.id);if(d&&r.push(d),r.length>=(t.limit??100))break}}finally{n.close()}let s=[];for(let o of r)s.push(await Ie({project:t.project,storage:t.storage,nativeSessionId:o,dbPath:e}));return s}import{existsSync as Zr,mkdirSync as Mt,readFileSync as es,writeFileSync as At}from"node:fs";import{homedir as Pe}from"node:os";import{join as fe}from"node:path";var Tt="<!-- TOOLNET_MEMORY_BOOTSTRAP_START -->",Oe="<!-- TOOLNET_MEMORY_BOOTSTRAP_END -->";function ts(){let t=fe(Pe(),".config","opencode","AGENTS.md");Mt(fe(Pe(),".config","opencode"),{recursive:!0});let e=`${Tt}
|
|
129
|
+
## ToolNet Memory \u2014 Project Continuity
|
|
130
|
+
|
|
131
|
+
When the current workspace belongs to a ToolNet project, identified by a nearest ancestor containing \`.toolnet/project.json\`:
|
|
132
|
+
|
|
133
|
+
1. Before planning, editing, or running implementation commands, read that project root's \`.toolnet/context/startup.md\`.
|
|
134
|
+
2. Treat its PROJECT RULES, MISSION, CURRENT OBJECTIVE, rationale, current phase/task, Definition of Done, blockers and next actions as project continuity context.
|
|
135
|
+
3. Do not restart work already marked completed unless repository evidence proves it must be revisited.
|
|
136
|
+
4. Verify current repository state before continuing old work.
|
|
137
|
+
5. If \`.toolnet/context/startup.md\` is missing, run \`toolnet-memory context:sync --project <project-root>\`, then read it.
|
|
138
|
+
6. Never invent missing rationale. If ToolNet says a reason was not explicitly recorded, inspect the implementation or ask rather than guessing.
|
|
139
|
+
|
|
140
|
+
This applies automatically to every ToolNet-managed project.
|
|
141
|
+
${Oe}`,n=Zr(t)?es(t,"utf8"):"",r=n.indexOf(Tt),s=n.indexOf(Oe);return r>=0&&s>=r?n=n.slice(0,r)+e+n.slice(s+Oe.length):(n=n.trimEnd(),n&&(n+=`
|
|
142
|
+
|
|
143
|
+
`),n+=e),At(t,n.trimEnd()+`
|
|
144
|
+
`,{encoding:"utf8",mode:384}),t}function Nt(t={}){let e=t.directory??fe(Pe(),".config","opencode","plugins");Mt(e,{recursive:!0}),ts();let n=fe(e,"toolnet-memory.js"),r=t.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",s=`// Generated by ToolNet Memory.
|
|
145
|
+
// OpenCode session capture + continuity bootstrap.
|
|
146
|
+
|
|
147
|
+
function getSessionId(event) {
|
|
148
|
+
const p = event?.properties ?? {}
|
|
149
|
+
|
|
150
|
+
const candidates = [
|
|
151
|
+
p.sessionID,
|
|
152
|
+
p.sessionId,
|
|
153
|
+
p.info?.id,
|
|
154
|
+
p.session?.id,
|
|
155
|
+
]
|
|
156
|
+
|
|
157
|
+
return candidates.find(
|
|
158
|
+
value => typeof value === "string" && value.length > 0
|
|
159
|
+
) ?? null
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export const ToolNetMemoryPlugin = async ({ directory }) => {
|
|
163
|
+
const binary = ${JSON.stringify(r)}
|
|
164
|
+
|
|
165
|
+
async function refreshContext() {
|
|
166
|
+
try {
|
|
167
|
+
const child = Bun.spawn(
|
|
168
|
+
[
|
|
169
|
+
binary,
|
|
170
|
+
"context:sync",
|
|
171
|
+
"--project",
|
|
172
|
+
directory,
|
|
173
|
+
],
|
|
174
|
+
{
|
|
175
|
+
stdin: "ignore",
|
|
176
|
+
stdout: "ignore",
|
|
177
|
+
stderr: "ignore",
|
|
178
|
+
}
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
await child.exited
|
|
182
|
+
} catch {
|
|
183
|
+
// Context sync must never break OpenCode startup.
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function readContext() {
|
|
188
|
+
try {
|
|
189
|
+
const child = Bun.spawn(
|
|
190
|
+
[
|
|
191
|
+
binary,
|
|
192
|
+
"context:print",
|
|
193
|
+
"--project",
|
|
194
|
+
directory,
|
|
195
|
+
"--tokens",
|
|
196
|
+
"900",
|
|
197
|
+
],
|
|
198
|
+
{
|
|
199
|
+
stdin: "ignore",
|
|
200
|
+
stdout: "pipe",
|
|
201
|
+
stderr: "ignore",
|
|
202
|
+
}
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
const text =
|
|
206
|
+
await new Response(
|
|
207
|
+
child.stdout
|
|
208
|
+
).text()
|
|
209
|
+
|
|
210
|
+
await child.exited
|
|
211
|
+
|
|
212
|
+
return text.trim()
|
|
213
|
+
} catch {
|
|
214
|
+
return ""
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function sync(sessionId, flag) {
|
|
219
|
+
if (!sessionId) return
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
const args = [
|
|
223
|
+
binary,
|
|
224
|
+
"session:opencode-sync",
|
|
225
|
+
sessionId,
|
|
226
|
+
"--project",
|
|
227
|
+
directory,
|
|
228
|
+
]
|
|
229
|
+
|
|
230
|
+
if (flag) {
|
|
231
|
+
args.push(flag)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const child = Bun.spawn(args, {
|
|
235
|
+
stdin: "ignore",
|
|
236
|
+
stdout: "ignore",
|
|
237
|
+
stderr: "ignore",
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
if (
|
|
241
|
+
typeof child.unref === "function"
|
|
242
|
+
) {
|
|
243
|
+
child.unref()
|
|
244
|
+
}
|
|
245
|
+
} catch {
|
|
246
|
+
// Memory capture must never break OpenCode.
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/*
|
|
251
|
+
* Pull the latest project brief from ToolNet remote storage
|
|
252
|
+
* before the new OpenCode session starts doing work.
|
|
253
|
+
*/
|
|
254
|
+
await refreshContext()
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
event: async ({ event }) => {
|
|
258
|
+
const sessionId =
|
|
259
|
+
getSessionId(event)
|
|
260
|
+
|
|
261
|
+
if (!sessionId) {
|
|
262
|
+
return
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (event.type === "session.idle") {
|
|
266
|
+
sync(sessionId, "--idle")
|
|
267
|
+
return
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (event.type === "session.compacted") {
|
|
271
|
+
sync(sessionId, "--compacted")
|
|
272
|
+
return
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (event.type === "session.error") {
|
|
276
|
+
sync(sessionId, "--error")
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
|
|
280
|
+
/*
|
|
281
|
+
* Official OpenCode compaction hook.
|
|
282
|
+
* Preserve ToolNet's semantic state after context compaction.
|
|
283
|
+
*/
|
|
284
|
+
"experimental.session.compacting": async (_input, output) => {
|
|
285
|
+
const context =
|
|
286
|
+
await readContext()
|
|
287
|
+
|
|
288
|
+
if (
|
|
289
|
+
context &&
|
|
290
|
+
Array.isArray(
|
|
291
|
+
output.context
|
|
292
|
+
)
|
|
293
|
+
) {
|
|
294
|
+
output.context.push(
|
|
295
|
+
context
|
|
296
|
+
)
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
`;return At(n,s,{encoding:"utf8",mode:384}),n}import{existsSync as Ta,mkdirSync as os,readFileSync as Ma,writeFileSync as is}from"node:fs";import{dirname as as,join as $t}from"node:path";function Ee(t){if(!t)return 0;let e=Array.from(t).length,n=t.trim().split(/\s+/u).filter(Boolean).length;return Math.ceil(Math.max(e/3.5,n*1.3))}function b(t,e){let n=t.replace(/\s+/g," ").trim();return n.length<=e?n:n.slice(0,Math.max(0,e-1)).trimEnd()+"\u2026"}function ns(t){let e=[],n=!1;for(let r of t.split(/\r?\n/u)){let s=r.trim();if(s.includes("<!--")&&(n=!0),n){s.includes("-->")&&(n=!1);continue}let o=s.toLowerCase();if(!(!s||s.startsWith("#")||s==="```"||o.startsWith("- [enforce]")||o.startsWith("* [enforce]")||o.startsWith("- [advisory]")||o.startsWith("* [advisory]"))&&(s=s.replace(/^[-*]\s+/u,""),s&&e.push(b(s,280)),e.length>=16))break}return e}function rs(t){let e=[],n=[];for(let r of t.split(/\\r?\\n/u)){let s=r.trim(),o=s.toLowerCase(),d=["- [enforce]","* [enforce]","- [advisory]","* [advisory]"].find(a=>o.startsWith(a));if(!d)continue;let c=s.slice(d.length).trim();c&&(d.includes("enforce")?e.push(c):n.push(c))}return{enforce:e,advisory:n}}function ss(t,e){let n=[];for(let r of t){let s=[...n,r].join(`
|
|
302
|
+
`);if(Ee(s)<=e){n.push(r);continue}let o=Ee(n.join(`
|
|
303
|
+
`)),i=Math.max(0,e-o);if(i>=16){let d=Math.floor(i*3.2),c=b(r,d);c&&n.push(c)}break}return n.join(`
|
|
304
|
+
`).trim()}async function _t(t){let e=Math.max(256,Math.min(2e3,t.maxTokens??1e3)),n=ue(t.project,!1),r=n?.content??"";r||(r=await t.storage.getText(`projects/${t.project.id}/project/manual.md`)??"");let s=rs(r),o=n?n.rules.filter(p=>p.mode==="enforce").map(p=>p.text):s.enforce,i=n?n.rules.filter(p=>p.mode==="advisory").map(p=>p.text):s.advisory,d=r?ns(r):[],c=await se(t.project,t.storage),a=await yt(t.project,t.storage),f=await jt(t.project,t.storage),u=[];if(u.push("[TOOLNET PROJECT CONTEXT]"),u.push(`Project: ${t.project.name}`),u.push("Continue existing project state. Do not restart completed work unless evidence shows it is necessary."),r&&u.push(`Full operating manual: ${ce(t.project)}`),o.length){u.push("","PROJECT RULES \u2014 MUST FOLLOW");for(let p of o.slice(0,24))u.push(`- [ENFORCE] ${b(p,240)}`)}if(i.length){u.push("","PROJECT PREFERENCES");for(let p of i.slice(0,10))u.push(`- ${b(p,220)}`)}if(a&&(a.mission&&u.push("","MISSION",b(a.mission.value,420)),a.activeObjective&&u.push("","CURRENT OBJECTIVE",b(a.activeObjective.value,420)),a.why&&u.push("","WHY THIS WORK MATTERS",b(a.why.value,420)),a.desiredOutcome&&u.push("","DESIRED OUTCOME",b(a.desiredOutcome.value,420)),a.planRationale&&u.push("","WHY THIS APPROACH",b(a.planRationale.value,420))),c){if(u.push("","ACTIVE WORK"),c.goal&&u.push(`Goal: ${b(c.goal,320)}`),c.plan&&u.push(`Plan: ${b(c.plan,320)}`),u.push(`Progress: phases ${c.progress.phasesCompleted}/${c.progress.phasesTotal}; tasks ${c.progress.tasksCompleted}/${c.progress.tasksTotal}; blocked ${c.progress.blocked}`),c.currentPhase&&u.push(`Current phase: ${c.currentPhase.title} [${c.currentPhase.status}]`),c.currentPhase&&a){let p=a.phases.find(y=>y.order===c.currentPhase?.order);p&&(p.objective&&u.push(`Phase objective: ${b(p.objective.value,340)}`),p.why?u.push(`Why this phase: ${b(p.why.value,340)}`):u.push("Why this phase: not explicitly recorded. Inspect existing implementation before assuming intent."),p.deliverable&&u.push(`Deliverable: ${b(p.deliverable.value,340)}`),p.dependencies.length&&u.push(`Depends on: ${p.dependencies.slice(0,4).map(y=>b(y.value,180)).join("; ")}`),p.acceptanceCriteria.length&&(u.push("","DEFINITION OF DONE"),p.acceptanceCriteria.slice(0,6).forEach(y=>{u.push(`- ${b(y.value,260)}`)})),p.openQuestions.length&&(u.push("","OPEN QUESTIONS FOR CURRENT PHASE"),p.openQuestions.slice(0,4).forEach(y=>{u.push(`- ${b(y.value,260)}`)})))}c.currentTask&&u.push(`Current task: ${c.currentTask.title} [${c.currentTask.status}]`),c.nextActions.length&&(u.push("","NEXT ACTIONS"),c.nextActions.slice(0,6).forEach((p,y)=>{u.push(`${y+1}. ${b(p,260)}`)})),c.blockers.length&&(u.push("","BLOCKERS"),c.blockers.slice(0,5).forEach(p=>{u.push(`- ${b(p,260)}`)})),c.warnings.length&&(u.push("","ATTENTION"),c.warnings.slice(-5).forEach(p=>{u.push(`- ${b(p,260)}`)})),c.decisions.length&&(u.push("","RECENT DECISIONS"),c.decisions.slice(-5).forEach(p=>{u.push(`- ${b(p,260)}`)})),c.lastSession&&u.push("",`Last work session: ${c.lastSession.agent} / ${c.lastSession.nativeSessionId}`)}if(a&&a.openQuestions.length&&(u.push("","UNRESOLVED QUESTIONS"),a.openQuestions.slice(0,5).forEach(p=>{u.push(`- ${b(p.value,260)}`)})),f&&u.push(`Latest handoff: ${f.reason} / ${f.sourceSession.agent}`),d.length){u.push("","OPERATING NOTES");for(let p of d)u.push(`- ${p}`)}u.push("","Before changing anything: verify the current repository state and continue from the active phase/task above.");let g=ss(u,e);return{version:1,projectId:t.project.id,projectName:t.project.name,text:g,estimatedTokens:Ee(g),maxTokens:e,hasManual:!!r,hasWorkState:!!c,hasHandoff:!!f,generatedAt:new Date().toISOString()}}function cs(t){return $t(t.rootPath,".toolnet","context","startup.md")}function us(t){return $t(t.rootPath,".toolnet","context","startup.json")}function ds(t,e){let n=cs(t);os(as(n),{recursive:!0}),is(n,e.text.endsWith(`
|
|
305
|
+
`)?e.text:e.text+`
|
|
306
|
+
`,{encoding:"utf8",mode:384}),x(us(t),e)}async function Lt(t,e,n=900){let r=await _t({project:t,storage:e,maxTokens:n}),s={version:1,projectId:t.id,projectName:t.name,text:r.text,digest:v(r.text),estimatedTokens:r.estimatedTokens,generatedAt:new Date().toISOString()};return ds(t,s),await e.put(`projects/${t.id}/context/startup.md`,s.text+`
|
|
307
|
+
`,"text/markdown"),await e.put(`projects/${t.id}/context/startup.json`,JSON.stringify(s,null,2)+`
|
|
308
|
+
`,"application/json"),s}function me(t,e){let n=t.indexOf(e);if(!(n<0))return t[n+1]}function Ce(t,e){return t.includes(e)}function ps(t){let e=Me(),n=Ke(Fe({provider:e.storage.provider,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3});return new G(n,t.id,t.name,t.remote??t.name)}async function ls(){let[t="help",...e]=process.argv.slice(2);if(t==="install-plugin"){let i=Nt({binary:me(e,"--bin")});console.log(`\u2705 OpenCode plugin installed: ${i}`),console.log("OpenCode will load it automatically on next start.");return}let n=me(e,"--project")??process.cwd(),r=new U().detect(n),s=ps(r),o=me(e,"--db");if(t==="sync"){let i=e.find(u=>!u.startsWith("--")&&u!==n&&u!==o);if(!i)throw new Error("Usage: session:opencode-sync <session-id>");let d=Ce(e,"--idle"),c=Ce(e,"--error"),a=Ce(e,"--compacted"),f=await Ie({project:r,storage:s,nativeSessionId:i,dbPath:o,idle:d,error:c,compacted:a});if(d||a||c)try{await Lt(r,s,900)}catch{}console.log(JSON.stringify(f,null,2));return}if(t==="recover"){let i=me(e,"--limit"),d=i?Number(i):100,c=await Rt({project:r,storage:s,dbPath:o,limit:Number.isFinite(d)?d:100});console.log(JSON.stringify({project:r.name,sessions:c.length,importedMessages:c.reduce((a,f)=>a+f.importedMessages,0),importedParts:c.reduce((a,f)=>a+f.importedParts,0)},null,2));return}console.log(`OpenCode Session Adapter
|
|
309
|
+
|
|
310
|
+
Commands:
|
|
311
|
+
sync <session-id> [--project PATH] [--idle]
|
|
312
|
+
recover [--project PATH] [--limit N]
|
|
313
|
+
install-plugin [--bin PATH]
|
|
314
|
+
`)}ls().catch(t=>{console.error(t instanceof Error?t.message:t),process.exit(1)});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import{readFileSync as Ne}from"node:fs";import{existsSync as F,readFileSync as B}from"node:fs";import{homedir as H}from"node:os";import{join as U}from"node:path";function Y(r){let e=r.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
|
|
2
|
+
`).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function G(){let r=process.env.TOOLNET_GLOBAL_ENV??U(H(),".config","toolnet-memory",".env");if(!F(r))return;let e=B(r,"utf8");for(let t of e.split(/\r?\n/)){let o=t.trim();if(!o||o.startsWith("#"))continue;o.startsWith("export ")&&(o=o.slice(7));let n=o.indexOf("=");if(n<=0)continue;let s=o.slice(0,n).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)&&process.env[s]===void 0&&(process.env[s]=Y(o.slice(n+1)))}}G();function d(r,e){return r===void 0?e:["1","true","yes","on"].includes(r.toLowerCase())}function u(r,e){if(!r)return e;let t=Number(r);return Number.isFinite(t)?t:e}function R(){return{memory:{autoCapture:d(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:d(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:d(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:d(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:u(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:u(process.env.MEMORY_RERANK_TOP,10),finalContext:u(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:u(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:u(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as W}from"node:crypto";import{existsSync as M,mkdirSync as Z,readFileSync as Q,renameSync as X,writeFileSync as q}from"node:fs";import{basename as ee,dirname as l,join as j,parse as k,resolve as g}from"node:path";var T=".toolnet",te="project.json";function re(r){return W("sha256").update(r).digest("hex").slice(0,16)}function S(r){return j(r,T,te)}function oe(r){return M(S(r))}function ne(r){let e=g(r),t=k(e).root;for(;;){if(oe(e))return e;if(e===t)break;let o=l(e);if(o===e)break;e=o}return null}function se(r){let e=g(r),t=k(e).root,o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(o.some(s=>M(j(e,s))))return e;if(e===t)break;let n=l(e);if(n===e)break;e=n}return g(r)}function ie(r){let e;try{e=JSON.parse(Q(r,"utf8"))}catch(n){throw new Error(`Invalid ToolNet project manifest: ${r}: ${n instanceof Error?n.message:String(n)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${r}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${r}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${r}`);let o=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:l(l(r)),createdAt:typeof t.createdAt=="string"?t.createdAt:o,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:o,graphVersion:typeof t.graphVersion=="number"?t.graphVersion:0,memoryVersion:typeof t.memoryVersion=="number"?t.memoryVersion:0,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0}}function A(r,e){let t=j(r,T);Z(t,{recursive:!0});let o=S(r),n=`${o}.tmp-${process.pid}`;q(n,JSON.stringify(e,null,2)+`
|
|
3
|
+
`,{encoding:"utf8",mode:384}),X(n,o)}function b(r,e){return{id:r.id,name:r.name,remote:r.remote,rootPath:e,createdAt:r.createdAt,updatedAt:r.updatedAt,graphVersion:r.graphVersion,memoryVersion:r.memoryVersion,metadata:r.metadata}}var f=class{detect(e=process.cwd()){let t=g(e),o=ne(t);if(o){let p=S(o),c=ie(p);return c.rootPath!==o&&(c.rootPath=o,c.updatedAt=new Date().toISOString(),A(o,c)),b(c,o)}let n=se(t),s=new Date().toISOString(),i=ee(n),a={version:1,id:re(n),name:i,remote:i,rootPath:n,createdAt:s,updatedAt:s,graphVersion:0,memoryVersion:0};return A(n,a),b(a,n)}};import{homedir as xe}from"node:os";import{join as Pe}from"node:path";import{DeleteObjectCommand as ae,GetObjectCommand as ce,HeadObjectCommand as pe,ListObjectsV2Command as ue,PutObjectCommand as me,S3Client as de}from"@aws-sdk/client-s3";import{getSignedUrl as le}from"@aws-sdk/s3-request-presigner";var y=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new de({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,o="application/octet-stream"){let n=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new me({Bucket:this.bucket,Key:e,Body:n,ContentType:o}))}async get(e){let t=await le(this.client,new ce({Bucket:this.bucket,Key:e}),{expiresIn:60}),o=await fetch(t,{redirect:"follow"});if(o.status===404)return null;if(!o.ok)throw new Error(`HF download failed: ${o.status} ${o.statusText}`);return new Uint8Array(await o.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new pe({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new ae({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],o;do{let n=await this.client.send(new ue({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:o}));for(let s of n.Contents??[])s.Key&&t.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});o=n.IsTruncated?n.NextContinuationToken:void 0}while(o);return t}};import{access as E,mkdir as ge,readFile as fe,readdir as ye,rm as he,stat as O,writeFile as je}from"node:fs/promises";import{dirname as Se,join as ve,relative as C,resolve as we}from"node:path";var m=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return we(this.root,t)}async put(e,t){let o=this.path(e);await ge(Se(o),{recursive:!0}),await je(o,t)}async get(e){try{return await fe(this.path(e))}catch(t){if(typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT")return null;throw t}}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await E(this.path(e)),!0}catch{return!1}}async delete(e){await he(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),o=[];try{await E(t)}catch{return o}let n=async i=>{let a=await ye(i,{withFileTypes:!0});for(let p of a){let c=ve(i,p.name);if(p.isDirectory()){await n(c);continue}let P=await O(c);o.push({key:C(this.root,c),size:P.size,updatedAt:P.mtime.toISOString()})}},s=await O(t);return s.isDirectory()?await n(t):o.push({key:C(this.root,t),size:s.size,updatedAt:s.mtime.toISOString()}),o}};function I(r){let e=r.localRoot??Pe(xe(),".toolnet-memory","storage");if(r.provider==="huggingface"){let t=r.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new y({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):(console.warn("[storage] Hugging Face credentials missing. Using local fallback."),new m(e))}return new m(e)}function Re(r){return new Promise(e=>setTimeout(e,r))}async function _(r,e={}){let t=Math.max(1,e.attempts??3),o=e.baseDelayMs??150,n=e.maxDelayMs??2e3,s;for(let i=1;i<=t;i++)try{return await r()}catch(a){if(s=a,i>=t)break;let p=Math.min(n,o*2**(i-1)),c=Math.floor(Math.random()*Math.max(1,p*.2));await Re(p+c)}throw s}var Ae=new Set(["put","get","getText","delete","list"]);function N(r,e={}){return new Proxy(r,{get(t,o){let n=Reflect.get(t,o,t);return typeof n!="function"?n:Ae.has(o)?(...s)=>_(()=>Promise.resolve(n.apply(t,s)),e):n.bind(t)}})}function $(r){let e=r.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function z(r){let e=r.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let o=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(o)return`${o[1]}memory/records/${o[2]}`;if(o=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),o)return`${o[1]}memory/vectors/${o[2]}`;if(o=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),o)return`${o[1]}code/graph/${o[2]}`;let n=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return n?`${n[1]}memory/records/${n[2]}`:(n=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),n?`${n[1]}memory/vectors/${n[2]}`:(n=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),n?`${n[1]}code/graph/${n[2]}`:e))}var h=class{constructor(e,t,o,n){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=o,this.folder=$(n??o),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),o=t,n=await this.provider.getText(e);if(n){let i;try{i=JSON.parse(n)}catch(a){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${a instanceof Error?a.message:String(a)}`)}if(typeof i.id=="string"&&i.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${i.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof i.createdAt=="string"&&(o=i.createdAt)}let s={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:o,updatedAt:t};await this.provider.put(e,JSON.stringify(s,null,2)+`
|
|
4
|
+
`,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=z(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,o){return await this.ensureRegistered(),this.provider.put(this.key(e),t,o)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import{existsSync as V,mkdirSync as Me,readFileSync as ke,statSync as D,writeFileSync as Te}from"node:fs";import{dirname as Ee,join as Oe}from"node:path";import{createHash as be}from"node:crypto";function v(r){return be("sha256").update(r).digest("hex")}var L=64*1024,Ce=`# ToolNet Project Operating Manual
|
|
5
|
+
|
|
6
|
+
This file contains persistent instructions for AI agents working on this project.
|
|
7
|
+
|
|
8
|
+
## Critical Rules
|
|
9
|
+
|
|
10
|
+
<!--
|
|
11
|
+
Examples:
|
|
12
|
+
|
|
13
|
+
- [enforce] Never edit production files directly.
|
|
14
|
+
- [enforce] Edit source only in /path/to/source.
|
|
15
|
+
- [enforce] Deploy only with ./deploy.sh --apply
|
|
16
|
+
-->
|
|
17
|
+
|
|
18
|
+
## Workflow
|
|
19
|
+
|
|
20
|
+
<!--
|
|
21
|
+
Describe the correct working process.
|
|
22
|
+
-->
|
|
23
|
+
|
|
24
|
+
## Architecture
|
|
25
|
+
|
|
26
|
+
<!--
|
|
27
|
+
Important architecture conventions.
|
|
28
|
+
-->
|
|
29
|
+
|
|
30
|
+
## Verification
|
|
31
|
+
|
|
32
|
+
<!--
|
|
33
|
+
Tests / QA required after changes.
|
|
34
|
+
-->
|
|
35
|
+
|
|
36
|
+
## Known Gotchas
|
|
37
|
+
|
|
38
|
+
<!--
|
|
39
|
+
Things an AI agent should always remember.
|
|
40
|
+
-->
|
|
41
|
+
|
|
42
|
+
## Preferences
|
|
43
|
+
|
|
44
|
+
<!--
|
|
45
|
+
- [advisory] Prefer small focused files.
|
|
46
|
+
-->
|
|
47
|
+
`;function J(r){return Oe(r.rootPath,".toolnet","PROJECT.md")}function Ie(r){return r.normalize("NFKC").replace(/\s+/g," ").trim()}function _e(r){let e=[],t=new Set,o=/^\s*[-*]\s+\[(enforce|advisory)\]\s+(.+?)\s*$/gimu,n;for(;n=o.exec(r);){let s=n[1].toLowerCase(),i=Ie(n[2]);if(!i)continue;let a=`${s}:${i.toLowerCase()}`;t.has(a)||(t.add(a),e.push({id:v(a).slice(0,24),mode:s,text:i,source:"manual"}))}return e}function w(r){let e=J(r);return V(e)||(Me(Ee(e),{recursive:!0}),Te(e,Ce,{encoding:"utf8",mode:384})),e}function x(r,e=!1){let t=e?w(r):J(r);if(!V(t))return null;if(D(t).size>L)throw new Error(`PROJECT.md exceeds ${L} bytes`);let n=ke(t,"utf8");return{path:t,content:n,digest:v(n),rules:_e(n),bytes:Buffer.byteLength(n,"utf8"),updatedAt:new Date(D(t).mtimeMs).toISOString()}}async function K(r,e){let t=x(r,!0);if(!t)throw new Error("Project manual unavailable");let o=`projects/${r.id}/project`;return await e.put(`${o}/manual.md`,t.content,"text/markdown"),await e.put(`${o}/manual.json`,JSON.stringify({version:1,projectId:r.id,projectName:r.name,digest:t.digest,bytes:t.bytes,ruleCount:t.rules.length,enforceRules:t.rules.filter(n=>n.mode==="enforce").length,advisoryRules:t.rules.filter(n=>n.mode==="advisory").length,updatedAt:t.updatedAt},null,2)+`
|
|
48
|
+
`,"application/json"),t}function $e(r,e){let t=r.indexOf(e);return t>=0?r[t+1]:void 0}function ze(r){let e=R(),t=N(I({provider:e.storage.provider,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3});return new h(t,r.id,r.name,r.remote??r.name)}async function De(){let[r="help",...e]=process.argv.slice(2),t=new f().detect($e(e,"--project")??process.cwd());if(r==="init"){let o=w(t);console.log(`\u2705 Project manual: ${o}`);return}if(r==="show"){let o=x(t,!1);if(!o){console.log("No .toolnet/PROJECT.md yet.");return}process.stdout.write(Ne(o.path,"utf8"));return}if(r==="sync"){let o=await K(t,ze(t));console.log(JSON.stringify({project:t.name,path:o.path,digest:o.digest,bytes:o.bytes,rules:o.rules.length,enforce:o.rules.filter(n=>n.mode==="enforce").length,advisory:o.rules.filter(n=>n.mode==="advisory").length},null,2));return}console.log(`Project Manual
|
|
49
|
+
|
|
50
|
+
Commands:
|
|
51
|
+
init [--project PATH]
|
|
52
|
+
show [--project PATH]
|
|
53
|
+
sync [--project PATH]
|
|
54
|
+
`)}De().catch(r=>{console.error(r instanceof Error?r.message:r),process.exit(1)});
|
package/bundle/setup.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import g from"node:fs";import T from"node:os";import R from"node:path";import O from"node:readline/promises";import{stdin as s,stdout as l}from"node:process";var u=R.join(T.homedir(),".config","toolnet-memory"),E=R.join(u,".env");function Y(o){let e=new Map;for(let n of o.split(/\r?\n/)){let i=n.trim();if(!i||i.startsWith("#"))continue;let t=i.indexOf("=");t!==-1&&e.set(i.slice(0,t).trim(),i.slice(t+1).trim())}return e}function a(o){g.mkdirSync(u,{recursive:!0,mode:448});let e=`# ==========================================================
|
|
2
|
+
# TOOLNET MEMORY
|
|
3
|
+
# ==========================================================
|
|
4
|
+
|
|
5
|
+
MEMORY_STORAGE_PROVIDER=huggingface
|
|
6
|
+
|
|
7
|
+
# Hugging Face
|
|
8
|
+
HF_NAMESPACE=${o.get("HF_NAMESPACE")??""}
|
|
9
|
+
HF_BUCKET=${o.get("HF_BUCKET")??"toolnet-memory"}
|
|
10
|
+
HF_S3_ACCESS_KEY_ID=${o.get("HF_S3_ACCESS_KEY_ID")??""}
|
|
11
|
+
HF_S3_SECRET_ACCESS_KEY=${o.get("HF_S3_SECRET_ACCESS_KEY")??""}
|
|
12
|
+
|
|
13
|
+
# Local cache
|
|
14
|
+
MEMORY_LOCAL_STORAGE_PATH=${o.get("MEMORY_LOCAL_STORAGE_PATH")??""}
|
|
15
|
+
MEMORY_LOCAL_CACHE_MB=${o.get("MEMORY_LOCAL_CACHE_MB")??"200"}
|
|
16
|
+
|
|
17
|
+
# Automation
|
|
18
|
+
MEMORY_AUTO_CAPTURE=${o.get("MEMORY_AUTO_CAPTURE")??"true"}
|
|
19
|
+
MEMORY_AUTO_RETRIEVE=${o.get("MEMORY_AUTO_RETRIEVE")??"true"}
|
|
20
|
+
MEMORY_AUTO_SUMMARIZE=${o.get("MEMORY_AUTO_SUMMARIZE")??"true"}
|
|
21
|
+
MEMORY_AUTO_SYNC=${o.get("MEMORY_AUTO_SYNC")??"true"}
|
|
22
|
+
|
|
23
|
+
# Retrieval
|
|
24
|
+
MEMORY_MAX_CANDIDATES=${o.get("MEMORY_MAX_CANDIDATES")??"50"}
|
|
25
|
+
MEMORY_RERANK_TOP=${o.get("MEMORY_RERANK_TOP")??"10"}
|
|
26
|
+
MEMORY_FINAL_CONTEXT=${o.get("MEMORY_FINAL_CONTEXT")??"5"}
|
|
27
|
+
MEMORY_TOKEN_BUDGET=${o.get("MEMORY_TOKEN_BUDGET")??"2000"}
|
|
28
|
+
`;g.writeFileSync(E,e,{encoding:"utf8",mode:384}),g.chmodSync(u,448),g.chmodSync(E,384)}function A(o,e=!0){let n=o.trim().toLowerCase();return n?n==="y"||n==="yes":e}async function F(o){return s.isTTY?(l.write(o),new Promise(e=>{let n="",i=()=>{s.off("data",t),s.setRawMode?.(!1),s.pause(),l.write(`
|
|
29
|
+
`),e(n)},t=_=>{for(let r of _.toString("utf8")){if(r==="\r"||r===`
|
|
30
|
+
`){i();return}if(r===""&&(s.setRawMode?.(!1),l.write(`
|
|
31
|
+
`),process.exit(130)),r==="\x7F"){n=n.slice(0,-1);continue}n+=r}};s.resume(),s.setRawMode?.(!0),s.on("data",t)})):""}async function d(){let o=g.existsSync(E),e=o?Y(g.readFileSync(E,"utf8")):new Map,n=["HF_NAMESPACE","HF_BUCKET","HF_S3_ACCESS_KEY_ID","HF_S3_SECRET_ACCESS_KEY"],i=n.every(c=>!!e.get(c)?.trim());if(console.log(""),console.log("TOOLNET MEMORY SETUP"),console.log("===================="),console.log(""),console.log(`Config: ${E}`),console.log(""),!s.isTTY||!l.isTTY){o||a(e),i?console.log("\u2713 Hugging Face storage already configured"):(console.log("Configuration pending."),console.log("Run:"),console.log(" toolnet-memory setup"));return}let t=O.createInterface({input:s,output:l});if(i){console.log("\u2713 Hugging Face storage already configured"),console.log("");let c=await t.question("Use existing configuration? (Y/n) [Y]: ");if(A(c)){t.close(),console.log(""),console.log("\u2713 Existing Hugging Face configuration kept"),console.log(""),console.log("Next:"),console.log(" toolnet-memory doctor");return}console.log("")}else{let c=await t.question("Configure Hugging Face storage now? (Y/n) [Y]: ");if(!A(c)){t.close(),o||a(e),console.log(""),console.log("Configuration pending."),console.log("Run later:"),console.log(" toolnet-memory setup");return}console.log("")}let _=e.get("HF_NAMESPACE")||"",r=e.get("HF_BUCKET")||"toolnet-memory",f=await t.question(_?`Hugging Face namespace [${_}]: `:"Hugging Face namespace: "),M=await t.question(`Bucket [${r}]: `),S=await t.question(e.get("HF_S3_ACCESS_KEY_ID")?"S3 Access Key ID [configured]: ":"S3 Access Key ID: ");t.close();let m=await F(e.get("HF_S3_SECRET_ACCESS_KEY")?"S3 Secret Access Key [configured]: ":"S3 Secret Access Key: ");f.trim()&&e.set("HF_NAMESPACE",f.trim()),M.trim()?e.set("HF_BUCKET",M.trim()):e.get("HF_BUCKET")||e.set("HF_BUCKET","toolnet-memory"),S.trim()&&e.set("HF_S3_ACCESS_KEY_ID",S.trim()),m.trim()&&e.set("HF_S3_SECRET_ACCESS_KEY",m.trim()),a(e);let C=n.filter(c=>!e.get(c)?.trim());if(console.log(""),console.log("\u2713 Configuration saved"),console.log(` ${E}`),console.log(""),C.length){console.log("Missing configuration:");for(let c of C)console.log(` - ${c}`);console.log(""),console.log("Run setup again:"),console.log(" toolnet-memory setup");return}console.log("\u2713 Hugging Face configuration complete"),console.log(""),console.log("Next:"),console.log(" toolnet-memory doctor")}d().catch(o=>{console.error(o instanceof Error?o.message:String(o)),process.exit(1)});
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import"dotenv/config";import{existsSync as L,readFileSync as V}from"node:fs";import{homedir as K}from"node:os";import{join as J}from"node:path";function B(s){let e=s.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
|
|
2
|
+
`).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function F(){let s=process.env.TOOLNET_GLOBAL_ENV??J(K(),".config","toolnet-memory",".env");if(!L(s))return;let e=V(s,"utf8");for(let t of e.split(/\r?\n/)){let r=t.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let o=r.indexOf("=");if(o<=0)continue;let n=r.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(n)&&process.env[n]===void 0&&(process.env[n]=B(r.slice(o+1)))}}F();function l(s,e){return s===void 0?e:["1","true","yes","on"].includes(s.toLowerCase())}function u(s,e){if(!s)return e;let t=Number(s);return Number.isFinite(t)?t:e}function P(){return{memory:{autoCapture:l(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:l(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:l(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:l(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:u(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:u(process.env.MEMORY_RERANK_TOP,10),finalContext:u(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:u(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:u(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as U}from"node:crypto";import{existsSync as A,mkdirSync as H,readFileSync as Y,renameSync as G,writeFileSync as W}from"node:fs";import{basename as Z,dirname as f,join as S,parse as k,resolve as y}from"node:path";var M=".toolnet",Q="project.json";function X(s){return U("sha256").update(s).digest("hex").slice(0,16)}function w(s){return S(s,M,Q)}function q(s){return A(w(s))}function ee(s){let e=y(s),t=k(e).root;for(;;){if(q(e))return e;if(e===t)break;let r=f(e);if(r===e)break;e=r}return null}function te(s){let e=y(s),t=k(e).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(n=>A(S(e,n))))return e;if(e===t)break;let o=f(e);if(o===e)break;e=o}return y(s)}function re(s){let e;try{e=JSON.parse(Y(s,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${s}: ${o instanceof Error?o.message:String(o)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${s}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${s}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${s}`);let r=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:f(f(s)),createdAt:typeof t.createdAt=="string"?t.createdAt:r,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:r,graphVersion:typeof t.graphVersion=="number"?t.graphVersion:0,memoryVersion:typeof t.memoryVersion=="number"?t.memoryVersion:0,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0}}function b(s,e){let t=S(s,M);H(t,{recursive:!0});let r=w(s),o=`${r}.tmp-${process.pid}`;W(o,JSON.stringify(e,null,2)+`
|
|
3
|
+
`,{encoding:"utf8",mode:384}),G(o,r)}function R(s,e){return{id:s.id,name:s.name,remote:s.remote,rootPath:e,createdAt:s.createdAt,updatedAt:s.updatedAt,graphVersion:s.graphVersion,memoryVersion:s.memoryVersion,metadata:s.metadata}}var h=class{detect(e=process.cwd()){let t=y(e),r=ee(t);if(r){let p=w(r),c=re(p);return c.rootPath!==r&&(c.rootPath=r,c.updatedAt=new Date().toISOString(),b(r,c)),R(c,r)}let o=te(t),n=new Date().toISOString(),i=Z(o),a={version:1,id:X(o),name:i,remote:i,rootPath:o,createdAt:n,updatedAt:n,graphVersion:0,memoryVersion:0};return b(o,a),R(a,o)}};import{homedir as je}from"node:os";import{join as ve}from"node:path";import{DeleteObjectCommand as oe,GetObjectCommand as se,HeadObjectCommand as ne,ListObjectsV2Command as ie,PutObjectCommand as ae,S3Client as ce}from"@aws-sdk/client-s3";import{getSignedUrl as pe}from"@aws-sdk/s3-request-presigner";var j=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new ce({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new ae({Bucket:this.bucket,Key:e,Body:o,ContentType:r}))}async get(e){let t=await pe(this.client,new se({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`HF download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new ne({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new oe({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let o=await this.client.send(new ie({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let n of o.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return t}};import{access as T,mkdir as me,readFile as de,readdir as ue,rm as ge,stat as E,writeFile as le}from"node:fs/promises";import{dirname as fe,join as ye,relative as O,resolve as he}from"node:path";var g=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return he(this.root,t)}async put(e,t){let r=this.path(e);await me(fe(r),{recursive:!0}),await le(r,t)}async get(e){try{return await de(this.path(e))}catch(t){if(typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT")return null;throw t}}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await T(this.path(e)),!0}catch{return!1}}async delete(e){await ge(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),r=[];try{await T(t)}catch{return r}let o=async i=>{let a=await ue(i,{withFileTypes:!0});for(let p of a){let c=ye(i,p.name);if(p.isDirectory()){await o(c);continue}let m=await E(c);r.push({key:O(this.root,c),size:m.size,updatedAt:m.mtime.toISOString()})}},n=await E(t);return n.isDirectory()?await o(t):r.push({key:O(this.root,t),size:n.size,updatedAt:n.mtime.toISOString()}),r}};function C(s){let e=s.localRoot??ve(je(),".toolnet-memory","storage");if(s.provider==="huggingface"){let t=s.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new j({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):(console.warn("[storage] Hugging Face credentials missing. Using local fallback."),new g(e))}return new g(e)}function Se(s){return new Promise(e=>setTimeout(e,s))}async function I(s,e={}){let t=Math.max(1,e.attempts??3),r=e.baseDelayMs??150,o=e.maxDelayMs??2e3,n;for(let i=1;i<=t;i++)try{return await s()}catch(a){if(n=a,i>=t)break;let p=Math.min(o,r*2**(i-1)),c=Math.floor(Math.random()*Math.max(1,p*.2));await Se(p+c)}throw n}var we=new Set(["put","get","getText","delete","list"]);function _(s,e={}){return new Proxy(s,{get(t,r){let o=Reflect.get(t,r,t);return typeof o!="function"?o:we.has(r)?(...n)=>I(()=>Promise.resolve(o.apply(t,n)),e):o.bind(t)}})}function $(s){let e=s.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function N(s){let e=s.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[2]}`;let o=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return o?`${o[1]}memory/records/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),o?`${o[1]}memory/vectors/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),o?`${o[1]}code/graph/${o[2]}`:e))}var v=class{constructor(e,t,r,o){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=r,this.folder=$(o??r),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),r=t,o=await this.provider.getText(e);if(o){let i;try{i=JSON.parse(o)}catch(a){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${a instanceof Error?a.message:String(a)}`)}if(typeof i.id=="string"&&i.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${i.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof i.createdAt=="string"&&(r=i.createdAt)}let n={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:t};await this.provider.put(e,JSON.stringify(n,null,2)+`
|
|
4
|
+
`,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=N(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,r){return await this.ensureRegistered(),this.provider.put(this.key(e),t,r)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};var xe=["memories/current.json","vectors/current.json","graph/current.json","graph/manifest.json","graph/resolution/current.json","code/chunks/current.json","code/vectors/current.json","code/architecture/current.json","code/architecture/entry-points.json","code/architecture/hotspots.json","code/architecture/layers.json","code/architecture/clusters.json","code/analysis/current.json","code/analysis/dead-code.json","code/analysis/dependencies.json","code/visualization/graph.json"],d=class{constructor(e){this.storage=e}storage;projectPrefix(e){return`projects/${e}`}snapshotPrefix(e,t){return[this.projectPrefix(e),"snapshots",t].join("/")}async create(e,t="automatic",r){let o=new Date().toISOString(),n=o.replace(/[:.]/g,"-"),i=this.projectPrefix(e),a=this.snapshotPrefix(e,n),p=[];for(let m of xe){let D=`${i}/${m}`,x=await this.storage.get(D);x&&(await this.storage.put(`${a}/${m}`,x,"application/json"),p.push(m))}if(p.length===0)return null;let c={version:1,id:n,projectId:e,createdAt:o,reason:t,objects:p,metadata:r};return await this.storage.put(`${a}/snapshot.json`,JSON.stringify(c,null,2),"application/json"),c}async list(e){let t=[this.projectPrefix(e),"snapshots"].join("/"),o=(await this.storage.list(t)).filter(i=>i.key.endsWith("/snapshot.json")),n=[];for(let i of o){let a=await this.storage.getText(i.key);if(a)try{n.push(JSON.parse(a))}catch{}}return n.sort((i,a)=>a.createdAt.localeCompare(i.createdAt))}async restore(e,t){let r=this.snapshotPrefix(e,t),o=await this.storage.getText(`${r}/snapshot.json`);if(!o)throw new Error(`Snapshot not found: ${t}`);let n=JSON.parse(o);if(n.projectId!==e)throw new Error("Snapshot project mismatch");let i=this.projectPrefix(e),a=[];for(let p of n.objects){let c=await this.storage.get(`${r}/${p}`);c&&(await this.storage.put(`${i}/${p}`,c,"application/json"),a.push(p))}return{restored:a}}async remove(e,t){let r=this.snapshotPrefix(e,t),o=await this.storage.list(r);for(let n of o)await this.storage.delete(n.key);return o.length}async prune(e,t=10){let o=(await this.list(e)).slice(t),n=0;for(let i of o)await this.remove(e,i.id),n++;return n}};async function z(s,e){let t=new d(s),o=(await t.list(e))[0];if(!o)throw new Error("No snapshot available");let n=await t.restore(e,o.id);return{snapshot:o.id,createdAt:o.createdAt,...n}}async function Pe(){let s=process.argv[2],e=P(),t=new h().detect(),r=_(C({provider:e.storage.provider,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3}),o=new v(r,t.id,t.name,t.remote??t.name),n=new d(o);if(s==="list"){let i=await n.list(t.id);console.log(i.map(a=>({id:a.id,createdAt:a.createdAt,reason:a.reason})));return}if(s==="create"){let i=process.argv.slice(3).join(" ")||"manual-cli";console.log(await n.create(t.id,i));return}if(s==="restore"){let i=process.argv[3];if(!i)throw new Error("snapshot id required");await n.create(t.id,"before-cli-restore"),console.log(await n.restore(t.id,i));return}if(s==="recover-latest"){console.log(await z(o,t.id));return}throw new Error("Usage: snapshot-cli list|create|restore|recover-latest")}Pe().catch(s=>{console.error(s instanceof Error?s.message:s),process.exit(1)});
|