@stackmemoryai/stackmemory 1.10.4 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +104 -23
- package/dist/src/cli/claude-sm.js +266 -84
- package/dist/src/cli/codex-sm.js +185 -33
- package/dist/src/cli/commands/bench.js +209 -2
- package/dist/src/cli/commands/cache.js +126 -0
- package/dist/src/cli/commands/daemon.js +41 -0
- package/dist/src/cli/commands/handoff.js +40 -9
- package/dist/src/cli/commands/onboard.js +70 -3
- package/dist/src/cli/commands/optimize.js +117 -0
- package/dist/src/cli/commands/orchestrate.js +230 -5
- package/dist/src/cli/commands/orchestrator.js +312 -24
- package/dist/src/cli/commands/pack.js +322 -0
- package/dist/src/cli/commands/search.js +40 -1
- package/dist/src/cli/commands/setup.js +177 -7
- package/dist/src/cli/commands/skills.js +10 -1
- package/dist/src/cli/commands/state.js +265 -0
- package/dist/src/cli/commands/wiki.js +33 -0
- package/dist/src/cli/gemini-sm.js +19 -29
- package/dist/src/cli/index.js +90 -29
- package/dist/src/cli/opencode-sm.js +38 -21
- package/dist/src/cli/utils/determinism-watcher.js +66 -0
- package/dist/src/cli/utils/real-cli-bin.js +44 -0
- package/dist/src/core/cache/content-cache.js +238 -0
- package/dist/src/core/cache/index.js +11 -0
- package/dist/src/core/cache/token-estimator.js +16 -0
- package/dist/src/core/context/frame-database.js +38 -30
- package/dist/src/core/cross-search/cross-project-search.js +269 -0
- package/dist/src/core/{merge → cross-search}/index.js +6 -4
- package/dist/src/core/database/sqlite-adapter.js +0 -83
- package/dist/src/core/extensions/provider-adapter.js +5 -0
- package/dist/src/core/models/model-router.js +22 -2
- package/dist/src/core/monitoring/logger.js +2 -1
- package/dist/src/core/optimization/trace-optimizer.js +413 -0
- package/dist/src/core/provenance/confidence-scorer.js +128 -0
- package/dist/src/core/provenance/index.js +40 -0
- package/dist/src/core/provenance/provenance-store.js +194 -0
- package/dist/src/core/provenance/types.js +82 -0
- package/dist/src/core/session/project-handoff.js +64 -0
- package/dist/src/core/session/session-manager.js +28 -0
- package/dist/src/core/shared-state/canonical-store.js +564 -0
- package/dist/src/core/skill-packs/index.js +18 -0
- package/dist/src/core/skill-packs/parser.js +42 -0
- package/dist/src/core/skill-packs/registry.js +224 -0
- package/dist/src/core/skill-packs/types.js +66 -0
- package/dist/src/core/trace/trace-event-store.js +282 -0
- package/dist/src/core/trace/trace-event.js +4 -0
- package/dist/src/core/wiki/wiki-compiler.js +219 -0
- package/dist/src/daemon/daemon-config.js +7 -0
- package/dist/src/daemon/services/github-service.js +126 -0
- package/dist/src/daemon/unified-daemon.js +30 -0
- package/dist/src/features/sweep/pty-wrapper.js +13 -5
- package/dist/src/hooks/schemas.js +2 -0
- package/dist/src/integrations/claude-code/subagent-client.js +89 -0
- package/dist/src/integrations/github/pr-state.js +158 -0
- package/dist/src/integrations/linear/client.js +4 -1
- package/dist/src/integrations/mcp/handlers/cross-search-handlers.js +188 -0
- package/dist/src/integrations/mcp/handlers/index.js +40 -59
- package/dist/src/integrations/mcp/server.js +425 -311
- package/dist/src/integrations/mcp/tool-alias-registry.js +370 -0
- package/dist/src/integrations/mcp/tool-definitions.js +98 -229
- package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +3 -40
- package/dist/src/integrations/ralph/learning/pattern-learner.js +1 -20
- package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +0 -2
- package/dist/src/mcp/stackmemory-mcp-server.js +315 -0
- package/dist/src/orchestrators/multimodal/determinism.js +243 -0
- package/dist/src/orchestrators/multimodal/harness.js +147 -77
- package/dist/src/orchestrators/multimodal/providers.js +44 -3
- package/dist/src/utils/hook-installer.js +8 -8
- package/package.json +10 -1
- package/packs/coding/python-fastapi/instructions.md +60 -0
- package/packs/coding/python-fastapi/pack.yaml +28 -0
- package/packs/coding/typescript-react/instructions.md +47 -0
- package/packs/coding/typescript-react/pack.yaml +28 -0
- package/packs/core/commands/capture.md +32 -0
- package/packs/core/commands/learn.md +73 -0
- package/packs/core/commands/next.md +36 -0
- package/packs/core/commands/restart.md +58 -0
- package/packs/core/commands/restore.md +29 -0
- package/packs/core/commands/start.md +57 -0
- package/packs/core/commands/stop.md +65 -0
- package/packs/core/commands/summary.md +40 -0
- package/packs/core/manifest.json +24 -0
- package/packs/ops/decision-recovery/instructions.md +65 -0
- package/packs/ops/decision-recovery/pack.yaml +89 -0
- package/templates/claude-hooks/doc-ingest.js +76 -0
- package/dist/src/cli/commands/team.js +0 -168
- package/dist/src/core/context/shared-context-layer.js +0 -620
- package/dist/src/core/context/stack-merge-resolver.js +0 -748
- package/dist/src/core/merge/conflict-detector.js +0 -430
- package/dist/src/core/merge/resolution-engine.js +0 -557
- package/dist/src/core/merge/stack-diff.js +0 -531
- package/dist/src/core/merge/unified-merge-resolver.js +0 -302
- package/dist/src/integrations/mcp/handlers/cord-handlers.js +0 -397
- package/dist/src/integrations/mcp/handlers/team-handlers.js +0 -211
- /package/dist/src/core/{merge → cache}/types.js +0 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
import { logger } from "../monitoring/logger.js";
|
|
6
|
+
import { TraceEventSchema } from "./types.js";
|
|
7
|
+
class ProvenanceStore {
|
|
8
|
+
db;
|
|
9
|
+
constructor(db) {
|
|
10
|
+
this.db = db;
|
|
11
|
+
this.initializeSchema();
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Initialize database schema for trace events
|
|
15
|
+
*/
|
|
16
|
+
initializeSchema() {
|
|
17
|
+
this.db.exec(`
|
|
18
|
+
CREATE TABLE IF NOT EXISTS trace_events (
|
|
19
|
+
trace_id TEXT PRIMARY KEY,
|
|
20
|
+
session_id TEXT NOT NULL,
|
|
21
|
+
parent_trace_id TEXT,
|
|
22
|
+
tenant_id TEXT NOT NULL,
|
|
23
|
+
timestamp TEXT NOT NULL,
|
|
24
|
+
actor TEXT NOT NULL,
|
|
25
|
+
operation TEXT NOT NULL,
|
|
26
|
+
inputs TEXT NOT NULL,
|
|
27
|
+
outputs TEXT NOT NULL,
|
|
28
|
+
tokens_in INTEGER NOT NULL DEFAULT 0,
|
|
29
|
+
tokens_out INTEGER NOT NULL DEFAULT 0,
|
|
30
|
+
cost_usd REAL NOT NULL DEFAULT 0,
|
|
31
|
+
score REAL,
|
|
32
|
+
feedback TEXT,
|
|
33
|
+
provenance TEXT NOT NULL,
|
|
34
|
+
created_at INTEGER DEFAULT (unixepoch())
|
|
35
|
+
)
|
|
36
|
+
`);
|
|
37
|
+
this.db.exec(`
|
|
38
|
+
CREATE INDEX IF NOT EXISTS idx_trace_events_session_id ON trace_events(session_id);
|
|
39
|
+
CREATE INDEX IF NOT EXISTS idx_trace_events_tenant_id ON trace_events(tenant_id);
|
|
40
|
+
CREATE INDEX IF NOT EXISTS idx_trace_events_operation ON trace_events(operation);
|
|
41
|
+
CREATE INDEX IF NOT EXISTS idx_trace_events_timestamp ON trace_events(timestamp);
|
|
42
|
+
CREATE INDEX IF NOT EXISTS idx_trace_events_parent_trace_id ON trace_events(parent_trace_id);
|
|
43
|
+
`);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Record a trace event
|
|
47
|
+
*/
|
|
48
|
+
record(event) {
|
|
49
|
+
const stmt = this.db.prepare(`
|
|
50
|
+
INSERT OR REPLACE INTO trace_events (
|
|
51
|
+
trace_id, session_id, parent_trace_id, tenant_id, timestamp,
|
|
52
|
+
actor, operation, inputs, outputs,
|
|
53
|
+
tokens_in, tokens_out, cost_usd, score, feedback, provenance
|
|
54
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
55
|
+
`);
|
|
56
|
+
try {
|
|
57
|
+
stmt.run(
|
|
58
|
+
event.traceId,
|
|
59
|
+
event.sessionId,
|
|
60
|
+
event.parentTraceId ?? null,
|
|
61
|
+
event.tenantId,
|
|
62
|
+
event.timestamp,
|
|
63
|
+
JSON.stringify(event.actor),
|
|
64
|
+
event.operation,
|
|
65
|
+
JSON.stringify(event.inputs),
|
|
66
|
+
JSON.stringify(event.outputs),
|
|
67
|
+
event.tokensIn,
|
|
68
|
+
event.tokensOut,
|
|
69
|
+
event.costUsd,
|
|
70
|
+
event.score ?? null,
|
|
71
|
+
event.feedback ?? null,
|
|
72
|
+
JSON.stringify(event.provenance)
|
|
73
|
+
);
|
|
74
|
+
logger.debug(`Recorded trace event ${event.traceId}`);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
logger.error(
|
|
77
|
+
`Failed to record trace event ${event.traceId}:`,
|
|
78
|
+
error
|
|
79
|
+
);
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get a trace event by traceId
|
|
85
|
+
*/
|
|
86
|
+
get(traceId) {
|
|
87
|
+
const row = this.db.prepare("SELECT * FROM trace_events WHERE trace_id = ?").get(traceId);
|
|
88
|
+
if (!row) return void 0;
|
|
89
|
+
return this.rowToEvent(row);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Query trace events with filters
|
|
93
|
+
*/
|
|
94
|
+
query(opts = {}) {
|
|
95
|
+
const conditions = [];
|
|
96
|
+
const params = [];
|
|
97
|
+
if (opts.sessionId) {
|
|
98
|
+
conditions.push("session_id = ?");
|
|
99
|
+
params.push(opts.sessionId);
|
|
100
|
+
}
|
|
101
|
+
if (opts.tenantId) {
|
|
102
|
+
conditions.push("tenant_id = ?");
|
|
103
|
+
params.push(opts.tenantId);
|
|
104
|
+
}
|
|
105
|
+
if (opts.operation) {
|
|
106
|
+
conditions.push("operation = ?");
|
|
107
|
+
params.push(opts.operation);
|
|
108
|
+
}
|
|
109
|
+
if (opts.since) {
|
|
110
|
+
conditions.push("timestamp >= ?");
|
|
111
|
+
params.push(opts.since);
|
|
112
|
+
}
|
|
113
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
114
|
+
const limit = opts.limit ?? 100;
|
|
115
|
+
const rows = this.db.prepare(
|
|
116
|
+
`SELECT * FROM trace_events ${where} ORDER BY timestamp DESC LIMIT ?`
|
|
117
|
+
).all(...params, limit);
|
|
118
|
+
return rows.map((row) => this.rowToEvent(row));
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Mark a trace event as superseded by another
|
|
122
|
+
*/
|
|
123
|
+
supersede(traceId, supersededBy) {
|
|
124
|
+
const row = this.db.prepare("SELECT provenance FROM trace_events WHERE trace_id = ?").get(traceId);
|
|
125
|
+
if (!row) return;
|
|
126
|
+
const provenance = JSON.parse(row.provenance);
|
|
127
|
+
provenance.supersededBy = supersededBy;
|
|
128
|
+
this.db.prepare("UPDATE trace_events SET provenance = ? WHERE trace_id = ?").run(JSON.stringify(provenance), traceId);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Follow parentTraceId chain to build lineage
|
|
132
|
+
*/
|
|
133
|
+
getLineage(traceId) {
|
|
134
|
+
const lineage = [];
|
|
135
|
+
let currentId = traceId;
|
|
136
|
+
while (currentId) {
|
|
137
|
+
const event = this.get(currentId);
|
|
138
|
+
if (!event) break;
|
|
139
|
+
lineage.push(event);
|
|
140
|
+
currentId = event.parentTraceId;
|
|
141
|
+
}
|
|
142
|
+
return lineage;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Aggregate stats across trace events
|
|
146
|
+
*/
|
|
147
|
+
getStats(tenantId) {
|
|
148
|
+
const where = tenantId ? "WHERE tenant_id = ?" : "";
|
|
149
|
+
const params = tenantId ? [tenantId] : [];
|
|
150
|
+
const row = this.db.prepare(
|
|
151
|
+
`
|
|
152
|
+
SELECT
|
|
153
|
+
COUNT(*) as total_events,
|
|
154
|
+
COALESCE(SUM(tokens_in), 0) as total_tokens_in,
|
|
155
|
+
COALESCE(SUM(tokens_out), 0) as total_tokens_out,
|
|
156
|
+
COALESCE(SUM(cost_usd), 0) as total_cost_usd,
|
|
157
|
+
COALESCE(AVG(json_extract(provenance, '$.confidence')), 0) as avg_confidence
|
|
158
|
+
FROM trace_events ${where}
|
|
159
|
+
`
|
|
160
|
+
).get(...params);
|
|
161
|
+
return {
|
|
162
|
+
totalEvents: row.total_events,
|
|
163
|
+
totalTokensIn: row.total_tokens_in,
|
|
164
|
+
totalTokensOut: row.total_tokens_out,
|
|
165
|
+
totalCostUsd: row.total_cost_usd,
|
|
166
|
+
avgConfidence: row.avg_confidence
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Convert a database row to a TraceEvent
|
|
171
|
+
*/
|
|
172
|
+
rowToEvent(row) {
|
|
173
|
+
return TraceEventSchema.parse({
|
|
174
|
+
timestamp: row.timestamp,
|
|
175
|
+
sessionId: row.session_id,
|
|
176
|
+
traceId: row.trace_id,
|
|
177
|
+
parentTraceId: row.parent_trace_id ?? void 0,
|
|
178
|
+
tenantId: row.tenant_id,
|
|
179
|
+
actor: JSON.parse(row.actor),
|
|
180
|
+
operation: row.operation,
|
|
181
|
+
inputs: JSON.parse(row.inputs),
|
|
182
|
+
outputs: JSON.parse(row.outputs),
|
|
183
|
+
tokensIn: row.tokens_in,
|
|
184
|
+
tokensOut: row.tokens_out,
|
|
185
|
+
costUsd: row.cost_usd,
|
|
186
|
+
score: row.score ?? void 0,
|
|
187
|
+
feedback: row.feedback ?? void 0,
|
|
188
|
+
provenance: JSON.parse(row.provenance)
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
export {
|
|
193
|
+
ProvenanceStore
|
|
194
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
const SourceRefSchema = z.object({
|
|
7
|
+
system: z.string(),
|
|
8
|
+
// e.g., "linear", "github", "slack", "manual"
|
|
9
|
+
externalId: z.string(),
|
|
10
|
+
// ID in source system
|
|
11
|
+
url: z.string().optional(),
|
|
12
|
+
// link to source
|
|
13
|
+
fetchedAt: z.string().datetime(),
|
|
14
|
+
// ISO8601
|
|
15
|
+
hash: z.string().optional()
|
|
16
|
+
// content hash for change detection
|
|
17
|
+
});
|
|
18
|
+
const ProvenanceRecordSchema = z.object({
|
|
19
|
+
sources: z.array(SourceRefSchema),
|
|
20
|
+
derivation: z.array(z.string()),
|
|
21
|
+
// chain of transformations
|
|
22
|
+
confidence: z.number().min(0).max(1),
|
|
23
|
+
supersededBy: z.string().optional(),
|
|
24
|
+
// ID of superseding record
|
|
25
|
+
programVersion: z.string().optional()
|
|
26
|
+
// version that produced this
|
|
27
|
+
});
|
|
28
|
+
const ActorSchema = z.object({
|
|
29
|
+
host: z.string(),
|
|
30
|
+
// e.g., "claude-code", "cursor", "codex"
|
|
31
|
+
agent: z.string(),
|
|
32
|
+
// agent identifier
|
|
33
|
+
user: z.string()
|
|
34
|
+
// user identifier
|
|
35
|
+
});
|
|
36
|
+
const TraceEventSchema = z.object({
|
|
37
|
+
timestamp: z.string().datetime(),
|
|
38
|
+
// ISO8601
|
|
39
|
+
sessionId: z.string(),
|
|
40
|
+
traceId: z.string(),
|
|
41
|
+
parentTraceId: z.string().optional(),
|
|
42
|
+
tenantId: z.string(),
|
|
43
|
+
actor: ActorSchema,
|
|
44
|
+
operation: z.string(),
|
|
45
|
+
// what happened
|
|
46
|
+
inputs: z.unknown(),
|
|
47
|
+
outputs: z.unknown(),
|
|
48
|
+
tokensIn: z.number().int().min(0),
|
|
49
|
+
tokensOut: z.number().int().min(0),
|
|
50
|
+
costUsd: z.number().min(0),
|
|
51
|
+
score: z.number().optional(),
|
|
52
|
+
// numeric eval (ASI-shaped)
|
|
53
|
+
feedback: z.string().optional(),
|
|
54
|
+
// textual feedback for GEPA
|
|
55
|
+
provenance: ProvenanceRecordSchema
|
|
56
|
+
});
|
|
57
|
+
const ConfidenceClassificationSchema = z.enum([
|
|
58
|
+
"accept",
|
|
59
|
+
"review",
|
|
60
|
+
"discard"
|
|
61
|
+
]);
|
|
62
|
+
const ConfidenceScoreSchema = z.object({
|
|
63
|
+
confidence: z.number().min(0).max(1),
|
|
64
|
+
signals: z.record(z.string(), z.unknown()),
|
|
65
|
+
classification: ConfidenceClassificationSchema
|
|
66
|
+
});
|
|
67
|
+
const ConfidenceConfigSchema = z.object({
|
|
68
|
+
thresholds: z.object({
|
|
69
|
+
accept: z.number().min(0).max(1),
|
|
70
|
+
review: z.number().min(0).max(1)
|
|
71
|
+
}),
|
|
72
|
+
weights: z.record(z.string(), z.number())
|
|
73
|
+
});
|
|
74
|
+
export {
|
|
75
|
+
ActorSchema,
|
|
76
|
+
ConfidenceClassificationSchema,
|
|
77
|
+
ConfidenceConfigSchema,
|
|
78
|
+
ConfidenceScoreSchema,
|
|
79
|
+
ProvenanceRecordSchema,
|
|
80
|
+
SourceRefSchema,
|
|
81
|
+
TraceEventSchema
|
|
82
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
import { existsSync, readFileSync } from "fs";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
function getProjectHandoffPaths(projectRoot) {
|
|
8
|
+
return {
|
|
9
|
+
handoffPath: join(projectRoot, ".stackmemory", "last-handoff.md"),
|
|
10
|
+
metadataPath: join(projectRoot, ".stackmemory", "last-handoff-meta.json")
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function parseBranchFromHandoffContent(content) {
|
|
14
|
+
const compactMatch = content.match(/^# Handoff:\s+.+?@([^\n]+)$/m);
|
|
15
|
+
if (compactMatch?.[1]) {
|
|
16
|
+
return compactMatch[1].trim();
|
|
17
|
+
}
|
|
18
|
+
const verboseMatch = content.match(/^\*\*Branch\*\*:\s+([^\n]+)$/m);
|
|
19
|
+
if (verboseMatch?.[1]) {
|
|
20
|
+
return verboseMatch[1].trim();
|
|
21
|
+
}
|
|
22
|
+
const ultraMatch = content.match(/^\[H\].+?@([^|\n]+)\|/m);
|
|
23
|
+
if (ultraMatch?.[1]) {
|
|
24
|
+
return ultraMatch[1].trim();
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
function loadProjectHandoff(projectRoot, currentBranch) {
|
|
29
|
+
const { handoffPath, metadataPath } = getProjectHandoffPaths(projectRoot);
|
|
30
|
+
if (!existsSync(handoffPath)) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const content = readFileSync(handoffPath, "utf8").trim();
|
|
34
|
+
if (!content) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
let metadata = null;
|
|
38
|
+
if (existsSync(metadataPath)) {
|
|
39
|
+
try {
|
|
40
|
+
metadata = JSON.parse(readFileSync(metadataPath, "utf8"));
|
|
41
|
+
} catch {
|
|
42
|
+
metadata = null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const branch = metadata?.branch || parseBranchFromHandoffContent(content);
|
|
46
|
+
if (currentBranch && branch && branch !== currentBranch) {
|
|
47
|
+
return {
|
|
48
|
+
content,
|
|
49
|
+
branch,
|
|
50
|
+
compatible: false,
|
|
51
|
+
mismatchReason: `handoff is for branch ${branch}, current branch is ${currentBranch}`
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
content,
|
|
56
|
+
branch: branch || null,
|
|
57
|
+
compatible: true
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export {
|
|
61
|
+
getProjectHandoffPaths,
|
|
62
|
+
loadProjectHandoff,
|
|
63
|
+
parseBranchFromHandoffContent
|
|
64
|
+
};
|
|
@@ -7,6 +7,7 @@ import * as fs from "fs/promises";
|
|
|
7
7
|
import * as path from "path";
|
|
8
8
|
import { logger } from "../monitoring/logger.js";
|
|
9
9
|
import { SystemError, ErrorCode } from "../errors/index.js";
|
|
10
|
+
import { canonicalStateStore } from "../shared-state/canonical-store.js";
|
|
10
11
|
function _getEnv(key, defaultValue) {
|
|
11
12
|
const value = process.env[key];
|
|
12
13
|
if (value === void 0) {
|
|
@@ -119,6 +120,16 @@ class SessionManager {
|
|
|
119
120
|
};
|
|
120
121
|
await this.saveSession(session);
|
|
121
122
|
await this.setProjectActiveSession(params.projectId, session.sessionId);
|
|
123
|
+
await canonicalStateStore.appendEvent({
|
|
124
|
+
type: "session_created",
|
|
125
|
+
tool: "stackmemory",
|
|
126
|
+
sessionId: session.sessionId,
|
|
127
|
+
projectId: session.projectId,
|
|
128
|
+
branch: session.branch,
|
|
129
|
+
payload: {
|
|
130
|
+
state: session.state
|
|
131
|
+
}
|
|
132
|
+
});
|
|
122
133
|
this.currentSession = session;
|
|
123
134
|
logger.info("Created new session", {
|
|
124
135
|
sessionId: session.sessionId,
|
|
@@ -152,6 +163,14 @@ class SessionManager {
|
|
|
152
163
|
`${session.sessionId}.json`
|
|
153
164
|
);
|
|
154
165
|
await fs.writeFile(sessionPath, JSON.stringify(session, null, 2));
|
|
166
|
+
await canonicalStateStore.upsertSession({
|
|
167
|
+
sessionId: session.sessionId,
|
|
168
|
+
tool: "stackmemory",
|
|
169
|
+
projectId: session.projectId,
|
|
170
|
+
branch: session.branch,
|
|
171
|
+
status: session.state,
|
|
172
|
+
metadata: session.metadata
|
|
173
|
+
});
|
|
155
174
|
}
|
|
156
175
|
async suspendSession(sessionId) {
|
|
157
176
|
const id = sessionId || this.currentSession?.sessionId;
|
|
@@ -193,6 +212,15 @@ class SessionManager {
|
|
|
193
212
|
`${session.sessionId}.json`
|
|
194
213
|
);
|
|
195
214
|
await fs.rename(sessionPath, historyPath);
|
|
215
|
+
await canonicalStateStore.endSession(session.sessionId, "closed");
|
|
216
|
+
await canonicalStateStore.appendEvent({
|
|
217
|
+
type: "session_closed",
|
|
218
|
+
tool: "stackmemory",
|
|
219
|
+
sessionId: session.sessionId,
|
|
220
|
+
projectId: session.projectId,
|
|
221
|
+
branch: session.branch,
|
|
222
|
+
payload: {}
|
|
223
|
+
});
|
|
196
224
|
}
|
|
197
225
|
}
|
|
198
226
|
async listSessions(filter) {
|