claude-memory-layer 1.0.30 → 1.0.32
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 +12 -5
- package/dist/cli/index.js +4 -3
- package/dist/cli/index.js.map +2 -2
- package/dist/core/index.js +3 -2
- package/dist/core/index.js.map +2 -2
- package/dist/hooks/post-tool-use.js +3 -2
- package/dist/hooks/post-tool-use.js.map +2 -2
- package/dist/hooks/semantic-daemon.js +3 -2
- package/dist/hooks/semantic-daemon.js.map +2 -2
- package/dist/hooks/session-end.js +3 -2
- package/dist/hooks/session-end.js.map +2 -2
- package/dist/hooks/session-start.js +3 -2
- package/dist/hooks/session-start.js.map +2 -2
- package/dist/hooks/stop.js +3 -2
- package/dist/hooks/stop.js.map +2 -2
- package/dist/hooks/user-prompt-submit.js +3 -2
- package/dist/hooks/user-prompt-submit.js.map +2 -2
- package/dist/index.js +3 -2
- package/dist/index.js.map +2 -2
- package/dist/mcp/index.js +3 -2
- package/dist/mcp/index.js.map +2 -2
- package/dist/server/api/index.js +3 -2
- package/dist/server/api/index.js.map +2 -2
- package/dist/server/index.js +3 -2
- package/dist/server/index.js.map +2 -2
- package/dist/services/memory-service.js +3 -2
- package/dist/services/memory-service.js.map +2 -2
- package/package.json +10 -3
- package/scripts/postinstall-embedding-backend.cjs +18 -16
- package/AGENTS.md +0 -71
- package/CLAUDE.md +0 -30
- package/HANDOFF.md +0 -92
- package/Memo.txt +0 -558
- package/benchmarks/replay/anonymized-real-sessions.json +0 -48
- package/config/kpi-thresholds.json +0 -7
- package/context.md +0 -636
- package/docs/ARCHITECTURE_COMPARISON_AND_RECOMMENDATIONS.md +0 -627
- package/docs/HERMES_MEMORY_INGESTION_ANALYSIS.md +0 -440
- package/docs/MCP_MEMORY_SERVICE_COMPARATIVE_REVIEW.md +0 -271
- package/docs/MEMORY_USEFULNESS_AUDIT.md +0 -371
- package/docs/MEMORY_USEFULNESS_AUDIT_RAW.json +0 -80
- package/docs/MEMSEARCH_PROJECT_STRUCTURE_ANALYSIS.md +0 -333
- package/docs/MEMU_ADOPTION.md +0 -40
- package/docs/OPERATIONS.md +0 -18
- package/docs/PRODUCT_VALIDATION_MATRIX.md +0 -82
- package/docs/PROJECT_STRUCTURE_ANALYSIS.md +0 -421
- package/docs/REFACTORING_MILESTONES_AND_ISSUES.md +0 -501
- package/docs/REFACTORING_PLAN_THIN_CORE.md +0 -414
- package/docs/REFERENCE_PROJECT_ANALYSES.md +0 -25
- package/docs/SUPERLOCALMEMORY_PROJECT_STRUCTURE_ANALYSIS.md +0 -452
- package/docs/TARGET_ARCHITECTURE_AND_FOLDER_STRUCTURE.md +0 -446
- package/docs/architecture/comparison-index.md +0 -47
- package/docs/reports/codex-real-data-validation-20260505T040447Z.md +0 -46
- package/plan.md +0 -1642
- package/scripts/build.ts +0 -159
- package/scripts/bump-patch-version.sh +0 -18
- package/scripts/delete-unknown-projects.js +0 -154
- package/scripts/fix-sync-gap.js +0 -32
- package/scripts/generate-session-qrels.ts +0 -126
- package/scripts/heartbeat-memory-orchestrator.sh +0 -28
- package/scripts/replay-retrieval-benchmark.ts +0 -69
- package/scripts/report-sync-gap.js +0 -26
- package/scripts/review-queue-auto-resolve.js +0 -21
- package/scripts/sync-gap-auto-heal.sh +0 -17
- package/spec.md +0 -624
- package/specs/20260207-dashboard-upgrade/context.md +0 -38
- package/specs/20260207-dashboard-upgrade/spec.md +0 -96
- package/specs/citations-system/context.md +0 -243
- package/specs/citations-system/plan.md +0 -495
- package/specs/citations-system/spec.md +0 -371
- package/specs/endless-mode/context.md +0 -305
- package/specs/endless-mode/plan.md +0 -620
- package/specs/endless-mode/spec.md +0 -455
- package/specs/entity-edge-model/context.md +0 -401
- package/specs/entity-edge-model/plan.md +0 -459
- package/specs/entity-edge-model/spec.md +0 -391
- package/specs/evidence-aligner-v2/context.md +0 -401
- package/specs/evidence-aligner-v2/plan.md +0 -303
- package/specs/evidence-aligner-v2/spec.md +0 -312
- package/specs/mcp-desktop-integration/context.md +0 -278
- package/specs/mcp-desktop-integration/plan.md +0 -550
- package/specs/mcp-desktop-integration/spec.md +0 -494
- package/specs/memory-utilization-improvements/context.md +0 -145
- package/specs/memory-utilization-improvements/plan.md +0 -361
- package/specs/memory-utilization-improvements/spec.md +0 -361
- package/specs/post-tool-use-hook/context.md +0 -319
- package/specs/post-tool-use-hook/plan.md +0 -469
- package/specs/post-tool-use-hook/spec.md +0 -364
- package/specs/private-tags/context.md +0 -288
- package/specs/private-tags/plan.md +0 -412
- package/specs/private-tags/spec.md +0 -345
- package/specs/progressive-disclosure/context.md +0 -346
- package/specs/progressive-disclosure/plan.md +0 -663
- package/specs/progressive-disclosure/spec.md +0 -415
- package/specs/selective-tool-observation/context.md +0 -100
- package/specs/selective-tool-observation/plan.md +0 -158
- package/specs/selective-tool-observation/spec.md +0 -127
- package/specs/task-entity-system/context.md +0 -297
- package/specs/task-entity-system/plan.md +0 -301
- package/specs/task-entity-system/spec.md +0 -314
- package/specs/thin-core-refactor/context.md +0 -275
- package/specs/thin-core-refactor/plan.md +0 -536
- package/specs/thin-core-refactor/spec.md +0 -465
- package/specs/vector-outbox-v2/context.md +0 -470
- package/specs/vector-outbox-v2/plan.md +0 -562
- package/specs/vector-outbox-v2/spec.md +0 -466
- package/specs/web-viewer-ui/context.md +0 -384
- package/specs/web-viewer-ui/plan.md +0 -797
- package/specs/web-viewer-ui/spec.md +0 -516
- package/src/adapters/claude/capture/index.ts +0 -3
- package/src/adapters/claude/context/index.ts +0 -3
- package/src/adapters/claude/hooks/index.ts +0 -21
- package/src/adapters/claude/hooks/post-tool-use.ts +0 -239
- package/src/adapters/claude/hooks/prompt-injection-policy.ts +0 -104
- package/src/adapters/claude/hooks/semantic-daemon-client.ts +0 -209
- package/src/adapters/claude/hooks/semantic-daemon.ts +0 -283
- package/src/adapters/claude/hooks/session-end.ts +0 -59
- package/src/adapters/claude/hooks/session-start.ts +0 -73
- package/src/adapters/claude/hooks/stop.ts +0 -128
- package/src/adapters/claude/hooks/user-prompt-submit.ts +0 -361
- package/src/adapters/claude/index.ts +0 -4
- package/src/adapters/claude/transcript/index.ts +0 -4
- package/src/adapters/claude/transcript/transcript-reader.ts +0 -57
- package/src/adapters/claude/transcript/turn-reconstructor.ts +0 -65
- package/src/apps/cli/claude-settings-hooks.ts +0 -138
- package/src/apps/cli/codex-import-runner.ts +0 -125
- package/src/apps/cli/codex-validation-output.ts +0 -95
- package/src/apps/cli/hermes-import-runner.ts +0 -130
- package/src/apps/cli/hermes-validation-output.ts +0 -91
- package/src/apps/cli/index.ts +0 -1735
- package/src/apps/cli/mcp-install.ts +0 -106
- package/src/apps/cli/retrieval-disclosure-output.ts +0 -196
- package/src/apps/dashboard/assets/js/bootstrap.js +0 -244
- package/src/apps/dashboard/assets/js/chat.js +0 -373
- package/src/apps/dashboard/assets/js/disclosure.js +0 -232
- package/src/apps/dashboard/assets/js/modals.js +0 -298
- package/src/apps/dashboard/assets/js/overview.js +0 -655
- package/src/apps/dashboard/assets/js/state.js +0 -72
- package/src/apps/dashboard/assets/js/views.js +0 -468
- package/src/apps/dashboard/index.html +0 -543
- package/src/apps/dashboard/index.ts +0 -3
- package/src/apps/dashboard/style.css +0 -1750
- package/src/apps/index.ts +0 -5
- package/src/apps/server/api/chat.ts +0 -244
- package/src/apps/server/api/citations.ts +0 -105
- package/src/apps/server/api/events.ts +0 -137
- package/src/apps/server/api/health.ts +0 -53
- package/src/apps/server/api/index.ts +0 -26
- package/src/apps/server/api/projects.ts +0 -74
- package/src/apps/server/api/search.ts +0 -184
- package/src/apps/server/api/sessions.ts +0 -115
- package/src/apps/server/api/stats.ts +0 -723
- package/src/apps/server/api/turns.ts +0 -143
- package/src/apps/server/api/utils.ts +0 -65
- package/src/apps/server/index.ts +0 -111
- package/src/cli/index.ts +0 -3
- package/src/cli/retrieval-disclosure-output.ts +0 -2
- package/src/compat/index.ts +0 -5
- package/src/core/canonical-key.ts +0 -186
- package/src/core/citation-generator.ts +0 -63
- package/src/core/consolidated-store.ts +0 -356
- package/src/core/consolidation-worker.ts +0 -493
- package/src/core/context-formatter.ts +0 -276
- package/src/core/continuity-manager.ts +0 -341
- package/src/core/db-wrapper.ts +0 -64
- package/src/core/derive/fact-deriver.ts +0 -170
- package/src/core/derive/index.ts +0 -2
- package/src/core/derive/summary-deriver.ts +0 -76
- package/src/core/edge-repo.ts +0 -333
- package/src/core/embedder.ts +0 -4
- package/src/core/engine/embedding-maintenance-service.ts +0 -187
- package/src/core/engine/endless-memory-services.ts +0 -4
- package/src/core/engine/index.ts +0 -19
- package/src/core/engine/memory-engine-services.ts +0 -170
- package/src/core/engine/memory-ingest-service.ts +0 -317
- package/src/core/engine/memory-query-service.ts +0 -173
- package/src/core/engine/memory-runtime-service.ts +0 -162
- package/src/core/engine/memory-service-composition.ts +0 -231
- package/src/core/engine/retrieval-analytics-service.ts +0 -181
- package/src/core/engine/retrieval-disclosure-service.ts +0 -420
- package/src/core/engine/retrieval-orchestrator.ts +0 -377
- package/src/core/engine/retrieval-services.ts +0 -176
- package/src/core/engine/shared-memory-services.ts +0 -4
- package/src/core/entity-repo.ts +0 -349
- package/src/core/event-store.ts +0 -779
- package/src/core/evidence-aligner.ts +0 -635
- package/src/core/external-market-context.ts +0 -582
- package/src/core/graduation-worker.ts +0 -171
- package/src/core/graduation.ts +0 -377
- package/src/core/index.ts +0 -64
- package/src/core/ingest-interceptor.ts +0 -80
- package/src/core/markdown-mirror.ts +0 -70
- package/src/core/matcher.ts +0 -208
- package/src/core/md-mirror.ts +0 -92
- package/src/core/metadata-extractor.ts +0 -203
- package/src/core/model/memory-fact.ts +0 -30
- package/src/core/model/memory-rule.ts +0 -14
- package/src/core/model/memory-summary.ts +0 -21
- package/src/core/model/raw-event.ts +0 -28
- package/src/core/model/retrieval-result.ts +0 -35
- package/src/core/mongo-sync-config.ts +0 -165
- package/src/core/mongo-sync-worker.ts +0 -381
- package/src/core/privacy/filter.ts +0 -190
- package/src/core/privacy/index.ts +0 -20
- package/src/core/privacy/tag-parser.ts +0 -145
- package/src/core/product-validation-matrix.ts +0 -314
- package/src/core/progressive-retriever.ts +0 -414
- package/src/core/registry/project-path.ts +0 -54
- package/src/core/registry/session-registry.ts +0 -69
- package/src/core/replay-evaluator.ts +0 -625
- package/src/core/retrieval-benchmark.ts +0 -117
- package/src/core/retrieval-quality.ts +0 -109
- package/src/core/retriever.ts +0 -800
- package/src/core/session-qrels.ts +0 -360
- package/src/core/shared-event-store.ts +0 -114
- package/src/core/shared-promoter.ts +0 -249
- package/src/core/shared-store.ts +0 -289
- package/src/core/shared-vector-store.ts +0 -203
- package/src/core/sqlite-event-store.ts +0 -1846
- package/src/core/sqlite-wrapper.ts +0 -116
- package/src/core/sync-worker.ts +0 -228
- package/src/core/tag-taxonomy.ts +0 -51
- package/src/core/task/blocker-resolver.ts +0 -333
- package/src/core/task/index.ts +0 -9
- package/src/core/task/task-matcher.ts +0 -240
- package/src/core/task/task-projector.ts +0 -358
- package/src/core/task/task-resolver.ts +0 -421
- package/src/core/turn-state.ts +0 -207
- package/src/core/types.ts +0 -952
- package/src/core/vector-outbox.ts +0 -299
- package/src/core/vector-store.ts +0 -231
- package/src/core/vector-worker.ts +0 -521
- package/src/core/working-set-store.ts +0 -257
- package/src/extensions/endless-memory/endless-memory-services.ts +0 -350
- package/src/extensions/endless-memory/index.ts +0 -1
- package/src/extensions/index.ts +0 -5
- package/src/extensions/mcp/handlers.ts +0 -960
- package/src/extensions/mcp/index.ts +0 -48
- package/src/extensions/mcp/tools.ts +0 -252
- package/src/extensions/shared-memory/index.ts +0 -1
- package/src/extensions/shared-memory/shared-memory-services.ts +0 -211
- package/src/extensions/vector/embedder.ts +0 -233
- package/src/extensions/vector/index.ts +0 -1
- package/src/hooks/post-tool-use.ts +0 -9
- package/src/hooks/semantic-daemon-client.ts +0 -1
- package/src/hooks/semantic-daemon.ts +0 -11
- package/src/hooks/session-end.ts +0 -9
- package/src/hooks/session-start.ts +0 -9
- package/src/hooks/stop.ts +0 -9
- package/src/hooks/user-prompt-submit.ts +0 -9
- package/src/index.ts +0 -13
- package/src/mcp/handlers.ts +0 -2
- package/src/mcp/index.ts +0 -4
- package/src/mcp/tools.ts +0 -2
- package/src/server/api/chat.ts +0 -2
- package/src/server/api/citations.ts +0 -2
- package/src/server/api/events.ts +0 -2
- package/src/server/api/health.ts +0 -2
- package/src/server/api/index.ts +0 -2
- package/src/server/api/projects.ts +0 -2
- package/src/server/api/search.ts +0 -2
- package/src/server/api/sessions.ts +0 -2
- package/src/server/api/stats.ts +0 -2
- package/src/server/api/turns.ts +0 -2
- package/src/server/api/utils.ts +0 -2
- package/src/server/index.ts +0 -2
- package/src/services/bootstrap-organizer.ts +0 -463
- package/src/services/codex-session-history-importer.ts +0 -966
- package/src/services/hermes-session-history-importer.ts +0 -733
- package/src/services/memory-service-config.ts +0 -36
- package/src/services/memory-service-registry.ts +0 -150
- package/src/services/memory-service.ts +0 -688
- package/src/services/session-history-importer.ts +0 -629
- package/tests/README.md +0 -23
- package/tests/adapters/claude/claude-semantic-daemon-adapter.test.ts +0 -54
- package/tests/adapters/claude/claude-transcript-reconstructor.test.ts +0 -98
- package/tests/adapters/claude-hook-prompt-injection-policy.test.ts +0 -99
- package/tests/apps/app-layer-boundary.test.ts +0 -48
- package/tests/apps/claude-settings-hooks.test.ts +0 -107
- package/tests/apps/cli-disclosure-output.test.ts +0 -212
- package/tests/apps/codex-import-runner.test.ts +0 -99
- package/tests/apps/codex-validation-output.test.ts +0 -100
- package/tests/apps/hermes-import-runner.test.ts +0 -99
- package/tests/apps/mcp-install-command.test.ts +0 -59
- package/tests/apps/package-build-entrypoints.test.ts +0 -30
- package/tests/apps/postinstall-embedding-backend.test.ts +0 -175
- package/tests/apps/search-api-disclosure.test.ts +0 -162
- package/tests/apps/stats-api-lightweight.test.ts +0 -67
- package/tests/apps/ui-disclosure-output.test.ts +0 -140
- package/tests/core/bootstrap-organizer.test.ts +0 -111
- package/tests/core/canonical-key.test.ts +0 -101
- package/tests/core/codex-session-history-importer-validation.test.ts +0 -185
- package/tests/core/consolidation-worker.test.ts +0 -75
- package/tests/core/embedding-maintenance-service.test.ts +0 -282
- package/tests/core/evidence-aligner.test.ts +0 -152
- package/tests/core/external-market-context.test.ts +0 -209
- package/tests/core/fact-deriver.test.ts +0 -79
- package/tests/core/hermes-session-history-importer-validation.test.ts +0 -609
- package/tests/core/ingest-interceptor.test.ts +0 -38
- package/tests/core/markdown-mirror.test.ts +0 -85
- package/tests/core/matcher.test.ts +0 -112
- package/tests/core/md-mirror.test.ts +0 -50
- package/tests/core/memory-engine-services.test.ts +0 -240
- package/tests/core/memory-ingest-service.test.ts +0 -296
- package/tests/core/memory-query-service.test.ts +0 -129
- package/tests/core/memory-runtime-service.test.ts +0 -201
- package/tests/core/memory-service-composition.test.ts +0 -192
- package/tests/core/memory-service-config.test.ts +0 -41
- package/tests/core/memory-service-facade.test.ts +0 -30
- package/tests/core/memory-service-registry.test.ts +0 -206
- package/tests/core/product-validation-matrix.test.ts +0 -61
- package/tests/core/project-registry.test.ts +0 -78
- package/tests/core/replay-evaluator.test.ts +0 -181
- package/tests/core/retrieval-analytics-service.test.ts +0 -210
- package/tests/core/retrieval-benchmark.test.ts +0 -93
- package/tests/core/retrieval-disclosure-service.test.ts +0 -264
- package/tests/core/retrieval-orchestrator.test.ts +0 -403
- package/tests/core/retrieval-quality.test.ts +0 -31
- package/tests/core/retrieval-services.test.ts +0 -185
- package/tests/core/retriever-fallback-chain.test.ts +0 -223
- package/tests/core/retriever-strategy-scope.test.ts +0 -164
- package/tests/core/retriever.memu-adoption.test.ts +0 -122
- package/tests/core/session-history-importer-filter.test.ts +0 -78
- package/tests/core/session-qrels.test.ts +0 -250
- package/tests/core/sqlite-event-store-replication.test.ts +0 -127
- package/tests/core/summary-deriver.test.ts +0 -66
- package/tests/extensions/embedder-warning-suppression.test.ts +0 -83
- package/tests/extensions/endless-memory-extension-boundary.test.ts +0 -17
- package/tests/extensions/endless-memory-services.test.ts +0 -325
- package/tests/extensions/mcp-context-tools.test.ts +0 -905
- package/tests/extensions/mcp-extension-boundary.test.ts +0 -21
- package/tests/extensions/mcp-package-build.test.ts +0 -22
- package/tests/extensions/mcp-project-aware-tools.test.ts +0 -102
- package/tests/extensions/shared-memory-extension-boundary.test.ts +0 -24
- package/tests/extensions/shared-memory-services.test.ts +0 -309
- package/tests/extensions/vector-extension-boundary.test.ts +0 -21
- package/tsconfig.json +0 -24
- package/vitest.config.ts +0 -15
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Turns API
|
|
3
|
-
* Endpoints for viewing events grouped by conversation turn
|
|
4
|
-
*
|
|
5
|
-
* A "turn" groups a user_prompt with its associated tool_observations
|
|
6
|
-
* and the final agent_response into a single logical unit.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { Hono } from 'hono';
|
|
10
|
-
import { getServiceFromQuery } from './utils.js';
|
|
11
|
-
|
|
12
|
-
export const turnsRouter = new Hono();
|
|
13
|
-
|
|
14
|
-
// GET /api/turns?sessionId=xxx - List turns for a session
|
|
15
|
-
turnsRouter.get('/', async (c) => {
|
|
16
|
-
const sessionId = c.req.query('sessionId');
|
|
17
|
-
const limit = parseInt(c.req.query('limit') || '20', 10);
|
|
18
|
-
const offset = parseInt(c.req.query('offset') || '0', 10);
|
|
19
|
-
|
|
20
|
-
if (!sessionId) {
|
|
21
|
-
return c.json({ error: 'sessionId is required' }, 400);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const memoryService = getServiceFromQuery(c);
|
|
25
|
-
|
|
26
|
-
try {
|
|
27
|
-
await memoryService.initialize();
|
|
28
|
-
|
|
29
|
-
const turns = await memoryService.getSessionTurns(sessionId, { limit, offset });
|
|
30
|
-
const totalTurns = await memoryService.countSessionTurns(sessionId);
|
|
31
|
-
|
|
32
|
-
return c.json({
|
|
33
|
-
turns: turns.map(t => ({
|
|
34
|
-
turnId: t.turnId,
|
|
35
|
-
startedAt: t.startedAt.toISOString(),
|
|
36
|
-
promptPreview: t.promptPreview,
|
|
37
|
-
eventCount: t.eventCount,
|
|
38
|
-
toolCount: t.toolCount,
|
|
39
|
-
hasResponse: t.hasResponse,
|
|
40
|
-
events: t.events.map(e => ({
|
|
41
|
-
id: e.id,
|
|
42
|
-
eventType: e.eventType,
|
|
43
|
-
timestamp: e.timestamp instanceof Date ? e.timestamp.toISOString() : e.timestamp,
|
|
44
|
-
preview: e.content.slice(0, 300) + (e.content.length > 300 ? '...' : ''),
|
|
45
|
-
contentLength: e.content.length
|
|
46
|
-
}))
|
|
47
|
-
})),
|
|
48
|
-
total: totalTurns,
|
|
49
|
-
limit,
|
|
50
|
-
offset,
|
|
51
|
-
hasMore: offset + limit < totalTurns
|
|
52
|
-
});
|
|
53
|
-
} catch (error) {
|
|
54
|
-
return c.json({ error: (error as Error).message }, 500);
|
|
55
|
-
} finally {
|
|
56
|
-
await memoryService.shutdown();
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
// GET /api/turns/:turnId - Get full turn details
|
|
61
|
-
turnsRouter.get('/:turnId', async (c) => {
|
|
62
|
-
const { turnId } = c.req.param();
|
|
63
|
-
const memoryService = getServiceFromQuery(c);
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
await memoryService.initialize();
|
|
67
|
-
|
|
68
|
-
const events = await memoryService.getEventsByTurn(turnId);
|
|
69
|
-
|
|
70
|
-
if (events.length === 0) {
|
|
71
|
-
return c.json({ error: 'Turn not found' }, 404);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const promptEvent = events.find(e => e.eventType === 'user_prompt');
|
|
75
|
-
const toolEvents = events.filter(e => e.eventType === 'tool_observation');
|
|
76
|
-
const responseEvents = events.filter(e => e.eventType === 'agent_response');
|
|
77
|
-
|
|
78
|
-
return c.json({
|
|
79
|
-
turnId,
|
|
80
|
-
sessionId: events[0].sessionId,
|
|
81
|
-
startedAt: events[0].timestamp instanceof Date
|
|
82
|
-
? events[0].timestamp.toISOString()
|
|
83
|
-
: events[0].timestamp,
|
|
84
|
-
prompt: promptEvent ? {
|
|
85
|
-
id: promptEvent.id,
|
|
86
|
-
content: promptEvent.content,
|
|
87
|
-
timestamp: promptEvent.timestamp instanceof Date
|
|
88
|
-
? promptEvent.timestamp.toISOString()
|
|
89
|
-
: promptEvent.timestamp
|
|
90
|
-
} : null,
|
|
91
|
-
tools: toolEvents.map(e => {
|
|
92
|
-
let toolName = '';
|
|
93
|
-
let success = true;
|
|
94
|
-
try {
|
|
95
|
-
const parsed = JSON.parse(e.content);
|
|
96
|
-
toolName = parsed.toolName || '';
|
|
97
|
-
success = parsed.success !== false;
|
|
98
|
-
} catch { /* ignore */ }
|
|
99
|
-
|
|
100
|
-
return {
|
|
101
|
-
id: e.id,
|
|
102
|
-
toolName,
|
|
103
|
-
success,
|
|
104
|
-
timestamp: e.timestamp instanceof Date ? e.timestamp.toISOString() : e.timestamp,
|
|
105
|
-
preview: e.content.slice(0, 500) + (e.content.length > 500 ? '...' : '')
|
|
106
|
-
};
|
|
107
|
-
}),
|
|
108
|
-
responses: responseEvents.map(e => ({
|
|
109
|
-
id: e.id,
|
|
110
|
-
content: e.content,
|
|
111
|
-
timestamp: e.timestamp instanceof Date ? e.timestamp.toISOString() : e.timestamp
|
|
112
|
-
})),
|
|
113
|
-
totalEvents: events.length
|
|
114
|
-
});
|
|
115
|
-
} catch (error) {
|
|
116
|
-
return c.json({ error: (error as Error).message }, 500);
|
|
117
|
-
} finally {
|
|
118
|
-
await memoryService.shutdown();
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
// POST /api/turns/backfill - Backfill turn_ids from metadata
|
|
123
|
-
turnsRouter.post('/backfill', async (c) => {
|
|
124
|
-
const memoryService = getServiceFromQuery(c);
|
|
125
|
-
|
|
126
|
-
try {
|
|
127
|
-
await memoryService.initialize();
|
|
128
|
-
const updated = await memoryService.backfillTurnIds();
|
|
129
|
-
|
|
130
|
-
return c.json({
|
|
131
|
-
success: true,
|
|
132
|
-
updated,
|
|
133
|
-
message: `Backfilled turn_id for ${updated} events`
|
|
134
|
-
});
|
|
135
|
-
} catch (error) {
|
|
136
|
-
return c.json({
|
|
137
|
-
success: false,
|
|
138
|
-
error: (error as Error).message
|
|
139
|
-
}, 500);
|
|
140
|
-
} finally {
|
|
141
|
-
await memoryService.shutdown();
|
|
142
|
-
}
|
|
143
|
-
});
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* API Utilities
|
|
3
|
-
* Shared helpers for API endpoints
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { Context } from 'hono';
|
|
7
|
-
import {
|
|
8
|
-
DISABLED_SHARED_STORE_CONFIG,
|
|
9
|
-
getReadOnlyMemoryService,
|
|
10
|
-
MemoryService
|
|
11
|
-
} from '../../../services/memory-service.js';
|
|
12
|
-
import { resolveProjectStoragePath } from '../../../core/registry/project-path.js';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Get the appropriate MemoryService based on the ?project= query parameter.
|
|
16
|
-
* - If ?project=<hash> is set (8 hex chars), resolves directly to project storage
|
|
17
|
-
* - If ?project=<path> is set, computes hash from path
|
|
18
|
-
* - Otherwise, returns the global read-only service
|
|
19
|
-
*
|
|
20
|
-
* Always creates read-only services for the dashboard API to avoid
|
|
21
|
-
* VectorWorker lifecycle issues with per-request services.
|
|
22
|
-
*/
|
|
23
|
-
export function getServiceFromQuery(c: Context): MemoryService {
|
|
24
|
-
const project = c.req.query('project') || c.req.query('projectId');
|
|
25
|
-
if (project) {
|
|
26
|
-
const storagePath = resolveProjectStoragePath(project);
|
|
27
|
-
|
|
28
|
-
return new MemoryService({
|
|
29
|
-
storagePath,
|
|
30
|
-
readOnly: true,
|
|
31
|
-
analyticsEnabled: false,
|
|
32
|
-
sharedStoreConfig: DISABLED_SHARED_STORE_CONFIG
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
return getReadOnlyMemoryService();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Read-only lightweight service for API paths that only need sqlite/keyword reads.
|
|
41
|
-
* This avoids per-request vector/embedder/shared-store initialization for stats and
|
|
42
|
-
* explicit fast searches while preserving the same project query resolution rules.
|
|
43
|
-
*/
|
|
44
|
-
export function getLightweightServiceFromQuery(c: Context): MemoryService {
|
|
45
|
-
const project = c.req.query('project') || c.req.query('projectId');
|
|
46
|
-
if (project) {
|
|
47
|
-
const storagePath = resolveProjectStoragePath(project);
|
|
48
|
-
|
|
49
|
-
return new MemoryService({
|
|
50
|
-
storagePath,
|
|
51
|
-
readOnly: true,
|
|
52
|
-
lightweightMode: true,
|
|
53
|
-
analyticsEnabled: false,
|
|
54
|
-
sharedStoreConfig: DISABLED_SHARED_STORE_CONFIG
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return new MemoryService({
|
|
59
|
-
storagePath: '~/.claude-code/memory',
|
|
60
|
-
readOnly: true,
|
|
61
|
-
lightweightMode: true,
|
|
62
|
-
analyticsEnabled: false,
|
|
63
|
-
sharedStoreConfig: DISABLED_SHARED_STORE_CONFIG
|
|
64
|
-
});
|
|
65
|
-
}
|
package/src/apps/server/index.ts
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Web Viewer HTTP Server
|
|
3
|
-
* Provides REST API and serves static UI files
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { Hono } from 'hono';
|
|
7
|
-
import { cors } from 'hono/cors';
|
|
8
|
-
import { logger } from 'hono/logger';
|
|
9
|
-
import { serve } from '@hono/node-server';
|
|
10
|
-
import { serveStatic } from '@hono/node-server/serve-static';
|
|
11
|
-
import * as path from 'path';
|
|
12
|
-
import * as fs from 'fs';
|
|
13
|
-
import { fileURLToPath as fileUrlToPath } from 'url';
|
|
14
|
-
|
|
15
|
-
import { apiRouter } from './api/index.js';
|
|
16
|
-
|
|
17
|
-
const app = new Hono();
|
|
18
|
-
const moduleDir = path.dirname(fileUrlToPath(import.meta.url));
|
|
19
|
-
|
|
20
|
-
// Middleware
|
|
21
|
-
app.use('/*', cors());
|
|
22
|
-
app.use('/*', logger());
|
|
23
|
-
|
|
24
|
-
// API routes
|
|
25
|
-
app.route('/api', apiRouter);
|
|
26
|
-
|
|
27
|
-
// Health check
|
|
28
|
-
app.get('/health', (c) => c.json({ status: 'ok', timestamp: new Date().toISOString() }));
|
|
29
|
-
|
|
30
|
-
function resolveUiPath(): string {
|
|
31
|
-
const candidates = [
|
|
32
|
-
// Built server: dist/server/index.js -> dist/ui
|
|
33
|
-
path.resolve(moduleDir, '../ui'),
|
|
34
|
-
// Source/dev server: src/apps/server/index.ts -> src/apps/dashboard
|
|
35
|
-
path.resolve(moduleDir, '../dashboard'),
|
|
36
|
-
// Fallback when running from a repository root.
|
|
37
|
-
path.resolve(process.cwd(), 'dist/ui'),
|
|
38
|
-
path.resolve(process.cwd(), 'src/apps/dashboard')
|
|
39
|
-
];
|
|
40
|
-
|
|
41
|
-
return candidates.find((candidate) => fs.existsSync(path.join(candidate, 'index.html'))) ?? candidates[0];
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Static files (UI)
|
|
45
|
-
const uiPath = resolveUiPath();
|
|
46
|
-
if (fs.existsSync(uiPath)) {
|
|
47
|
-
app.use('/*', serveStatic({ root: uiPath }));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Fallback for SPA routing
|
|
51
|
-
app.get('*', (c) => {
|
|
52
|
-
const indexPath = path.join(uiPath, 'index.html');
|
|
53
|
-
if (fs.existsSync(indexPath)) {
|
|
54
|
-
return c.html(fs.readFileSync(indexPath, 'utf-8'));
|
|
55
|
-
}
|
|
56
|
-
return c.text('UI not built. Run "npm run build:ui" first.', 404);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
export { app };
|
|
60
|
-
|
|
61
|
-
let serverInstance: ReturnType<typeof serve> | null = null;
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Start the HTTP server
|
|
65
|
-
*/
|
|
66
|
-
export function startServer(port: number = 37777): NonNullable<ReturnType<typeof serve>> {
|
|
67
|
-
if (serverInstance) {
|
|
68
|
-
return serverInstance;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
serverInstance = serve({
|
|
72
|
-
fetch: app.fetch,
|
|
73
|
-
port,
|
|
74
|
-
hostname: '127.0.0.1'
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
console.log(`🧠 Code Memory viewer started at http://localhost:${port}`);
|
|
78
|
-
|
|
79
|
-
return serverInstance;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Stop the HTTP server
|
|
84
|
-
*/
|
|
85
|
-
export function stopServer(): void {
|
|
86
|
-
if (serverInstance) {
|
|
87
|
-
serverInstance.close();
|
|
88
|
-
serverInstance = null;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Check if server is running on given port
|
|
94
|
-
*/
|
|
95
|
-
export async function isServerRunning(port: number = 37777): Promise<boolean> {
|
|
96
|
-
try {
|
|
97
|
-
const response = await fetch(`http://127.0.0.1:${port}/health`);
|
|
98
|
-
return response.ok;
|
|
99
|
-
} catch {
|
|
100
|
-
return false;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Start server if run directly
|
|
105
|
-
// Check if this file is being run directly (not imported)
|
|
106
|
-
const isMainModule = process.argv[1]?.includes('server/index') ||
|
|
107
|
-
process.argv[1]?.endsWith('server.js');
|
|
108
|
-
if (isMainModule) {
|
|
109
|
-
const port = parseInt(process.env.PORT || '37777', 10);
|
|
110
|
-
startServer(port);
|
|
111
|
-
}
|
package/src/cli/index.ts
DELETED
package/src/compat/index.ts
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AXIOMMIND canonical_key.py port
|
|
3
|
-
* Deterministic normalization ensuring identical titles always map to same keys
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { createHash } from 'crypto';
|
|
7
|
-
|
|
8
|
-
const MAX_KEY_LENGTH = 200;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Convert text to a normalized canonical key
|
|
12
|
-
*
|
|
13
|
-
* Normalization steps:
|
|
14
|
-
* 1. NFKC unicode normalization
|
|
15
|
-
* 2. Lowercase conversion
|
|
16
|
-
* 3. Punctuation removal
|
|
17
|
-
* 4. Consecutive whitespace cleanup
|
|
18
|
-
* 5. Context addition (optional)
|
|
19
|
-
* 6. Long key truncation with MD5
|
|
20
|
-
*/
|
|
21
|
-
export function makeCanonicalKey(
|
|
22
|
-
title: string,
|
|
23
|
-
context?: { project?: string; sessionId?: string }
|
|
24
|
-
): string {
|
|
25
|
-
// Step 1: NFKC normalization
|
|
26
|
-
let normalized = title.normalize('NFKC');
|
|
27
|
-
|
|
28
|
-
// Step 2: Lowercase conversion
|
|
29
|
-
normalized = normalized.toLowerCase();
|
|
30
|
-
|
|
31
|
-
// Step 3: Punctuation removal (unicode compatible)
|
|
32
|
-
normalized = normalized.replace(/[^\p{L}\p{N}\s]/gu, '');
|
|
33
|
-
|
|
34
|
-
// Step 4: Consecutive whitespace cleanup
|
|
35
|
-
normalized = normalized.replace(/\s+/g, ' ').trim();
|
|
36
|
-
|
|
37
|
-
// Step 5: Context addition
|
|
38
|
-
let key = normalized;
|
|
39
|
-
if (context?.project) {
|
|
40
|
-
key = `${context.project}::${key}`;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Step 6: Long key handling
|
|
44
|
-
if (key.length > MAX_KEY_LENGTH) {
|
|
45
|
-
const hashSuffix = createHash('md5').update(key).digest('hex').slice(0, 8);
|
|
46
|
-
key = key.slice(0, MAX_KEY_LENGTH - 9) + '_' + hashSuffix;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return key;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Check if two texts have the same canonical key
|
|
54
|
-
*/
|
|
55
|
-
export function isSameCanonicalKey(a: string, b: string): boolean {
|
|
56
|
-
return makeCanonicalKey(a) === makeCanonicalKey(b);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Generate dedupe key (content + session for uniqueness)
|
|
61
|
-
* AXIOMMIND Principle 3: Idempotency guarantee
|
|
62
|
-
*/
|
|
63
|
-
export function makeDedupeKey(content: string, sessionId: string): string {
|
|
64
|
-
const contentHash = createHash('sha256').update(content).digest('hex');
|
|
65
|
-
return `${sessionId}:${contentHash}`;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Generate content hash for deduplication
|
|
70
|
-
*/
|
|
71
|
-
export function hashContent(content: string): string {
|
|
72
|
-
return createHash('sha256').update(content).digest('hex');
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// ============================================================
|
|
76
|
-
// Entity Canonical Keys (Task Entity System)
|
|
77
|
-
// ============================================================
|
|
78
|
-
|
|
79
|
-
export type EntityKeyType = 'task' | 'condition' | 'artifact';
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Normalize text for entity key generation
|
|
83
|
-
*/
|
|
84
|
-
function normalizeForKey(text: string): string {
|
|
85
|
-
return text
|
|
86
|
-
.normalize('NFKC')
|
|
87
|
-
.toLowerCase()
|
|
88
|
-
.replace(/[^\p{L}\p{N}\s]/gu, '')
|
|
89
|
-
.replace(/\s+/g, '_')
|
|
90
|
-
.trim();
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Generate canonical key for entities
|
|
95
|
-
* Format: {type}:{project}:{normalized_identifier}
|
|
96
|
-
*/
|
|
97
|
-
export function makeEntityCanonicalKey(
|
|
98
|
-
entityType: EntityKeyType,
|
|
99
|
-
identifier: string,
|
|
100
|
-
context?: { project?: string }
|
|
101
|
-
): string {
|
|
102
|
-
const project = context?.project ?? 'default';
|
|
103
|
-
|
|
104
|
-
switch (entityType) {
|
|
105
|
-
case 'task':
|
|
106
|
-
return `task:${project}:${normalizeForKey(identifier)}`;
|
|
107
|
-
case 'condition':
|
|
108
|
-
return `cond:${project}:${normalizeForKey(identifier)}`;
|
|
109
|
-
case 'artifact':
|
|
110
|
-
return makeArtifactKey(identifier);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Generate canonical key for artifacts based on identifier pattern
|
|
116
|
-
* - URL: art:url:{sha1(url)}
|
|
117
|
-
* - JIRA key: art:jira:{key}
|
|
118
|
-
* - GitHub issue: art:gh_issue:{repo}:{num}
|
|
119
|
-
* - Generic: art:generic:{sha1(identifier)}
|
|
120
|
-
*/
|
|
121
|
-
export function makeArtifactKey(identifier: string): string {
|
|
122
|
-
// URL pattern
|
|
123
|
-
if (/^https?:\/\//.test(identifier)) {
|
|
124
|
-
const hash = createHash('sha1').update(identifier).digest('hex').slice(0, 12);
|
|
125
|
-
return `art:url:${hash}`;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// JIRA key pattern (e.g., PROJ-123)
|
|
129
|
-
const jiraMatch = identifier.match(/^([A-Z]+-\d+)$/);
|
|
130
|
-
if (jiraMatch) {
|
|
131
|
-
return `art:jira:${jiraMatch[1].toLowerCase()}`;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// GitHub issue pattern (e.g., owner/repo#123)
|
|
135
|
-
const ghMatch = identifier.match(/^([^\/]+\/[^#]+)#(\d+)$/);
|
|
136
|
-
if (ghMatch) {
|
|
137
|
-
return `art:gh_issue:${ghMatch[1]}:${ghMatch[2]}`;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Generic identifier
|
|
141
|
-
const hash = createHash('sha1').update(identifier).digest('hex').slice(0, 12);
|
|
142
|
-
return `art:generic:${hash}`;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Generate dedupe key for task events
|
|
147
|
-
*/
|
|
148
|
-
export function makeTaskEventDedupeKey(
|
|
149
|
-
eventType: string,
|
|
150
|
-
taskId: string,
|
|
151
|
-
sessionId: string,
|
|
152
|
-
additionalContext?: string
|
|
153
|
-
): string {
|
|
154
|
-
const parts = [eventType, taskId, sessionId];
|
|
155
|
-
if (additionalContext) {
|
|
156
|
-
parts.push(additionalContext);
|
|
157
|
-
}
|
|
158
|
-
const combined = parts.join(':');
|
|
159
|
-
return createHash('sha256').update(combined).digest('hex');
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Parse entity canonical key to extract type and identifier
|
|
164
|
-
*/
|
|
165
|
-
export function parseEntityCanonicalKey(canonicalKey: string): {
|
|
166
|
-
entityType: EntityKeyType;
|
|
167
|
-
project?: string;
|
|
168
|
-
identifier: string;
|
|
169
|
-
} | null {
|
|
170
|
-
const taskMatch = canonicalKey.match(/^task:([^:]+):(.+)$/);
|
|
171
|
-
if (taskMatch) {
|
|
172
|
-
return { entityType: 'task', project: taskMatch[1], identifier: taskMatch[2] };
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const condMatch = canonicalKey.match(/^cond:([^:]+):(.+)$/);
|
|
176
|
-
if (condMatch) {
|
|
177
|
-
return { entityType: 'condition', project: condMatch[1], identifier: condMatch[2] };
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const artMatch = canonicalKey.match(/^art:([^:]+):(.+)$/);
|
|
181
|
-
if (artMatch) {
|
|
182
|
-
return { entityType: 'artifact', identifier: artMatch[2] };
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return null;
|
|
186
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Citation Generator
|
|
3
|
-
* Generates unique, short citation IDs for memory references
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { createHash } from 'crypto';
|
|
7
|
-
|
|
8
|
-
const ID_LENGTH = 6;
|
|
9
|
-
const CHARSET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Generate a citation ID from an event ID using SHA256
|
|
13
|
-
*/
|
|
14
|
-
export function generateCitationId(eventId: string): string {
|
|
15
|
-
const hash = createHash('sha256')
|
|
16
|
-
.update(eventId)
|
|
17
|
-
.digest();
|
|
18
|
-
|
|
19
|
-
let id = '';
|
|
20
|
-
for (let i = 0; i < ID_LENGTH; i++) {
|
|
21
|
-
id += CHARSET[hash[i] % CHARSET.length];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return id;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Generate a unique citation ID with collision handling
|
|
29
|
-
*/
|
|
30
|
-
export async function generateUniqueCitationId(
|
|
31
|
-
eventId: string,
|
|
32
|
-
existsCheck: (id: string) => Promise<boolean>
|
|
33
|
-
): Promise<string> {
|
|
34
|
-
let id = generateCitationId(eventId);
|
|
35
|
-
let attempt = 0;
|
|
36
|
-
|
|
37
|
-
while (await existsCheck(id) && attempt < 10) {
|
|
38
|
-
// Add salt and regenerate
|
|
39
|
-
id = generateCitationId(`${eventId}:${attempt}`);
|
|
40
|
-
attempt++;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (attempt >= 10) {
|
|
44
|
-
throw new Error('Failed to generate unique citation ID after 10 attempts');
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return id;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Format a citation ID for display
|
|
52
|
-
*/
|
|
53
|
-
export function formatCitationId(citationId: string): string {
|
|
54
|
-
return `[mem:${citationId}]`;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Parse a citation ID from formatted string
|
|
59
|
-
*/
|
|
60
|
-
export function parseCitationId(formatted: string): string | null {
|
|
61
|
-
const match = formatted.match(/\[?mem:([A-Za-z0-9]{6})\]?/);
|
|
62
|
-
return match ? match[1] : null;
|
|
63
|
-
}
|