chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,551 @@
|
||||
//#region src/state/schema.ts
|
||||
const KV = {
|
||||
sessions: "mem:sessions",
|
||||
observations: (sessionId) => `mem:obs:${sessionId}`,
|
||||
memories: "mem:memories",
|
||||
summaries: "mem:summaries",
|
||||
config: "mem:config",
|
||||
metrics: "mem:metrics",
|
||||
health: "mem:health",
|
||||
embeddings: (obsId) => `mem:emb:${obsId}`,
|
||||
bm25Index: "mem:index:bm25",
|
||||
relations: "mem:relations",
|
||||
profiles: "mem:profiles",
|
||||
claudeBridge: "mem:claude-bridge",
|
||||
graphNodes: "mem:graph:nodes",
|
||||
graphEdges: "mem:graph:edges",
|
||||
semantic: "mem:semantic",
|
||||
procedural: "mem:procedural",
|
||||
teamShared: (teamId) => `mem:team:${teamId}:shared`,
|
||||
teamUsers: (teamId, userId) => `mem:team:${teamId}:users:${userId}`,
|
||||
teamProfile: (teamId) => `mem:team:${teamId}:profile`,
|
||||
audit: "mem:audit",
|
||||
actions: "mem:actions",
|
||||
actionEdges: "mem:action-edges",
|
||||
leases: "mem:leases",
|
||||
routines: "mem:routines",
|
||||
routineRuns: "mem:routine-runs",
|
||||
signals: "mem:signals",
|
||||
checkpoints: "mem:checkpoints",
|
||||
mesh: "mem:mesh",
|
||||
sketches: "mem:sketches",
|
||||
facets: "mem:facets",
|
||||
sentinels: "mem:sentinels",
|
||||
crystals: "mem:crystals"
|
||||
};
|
||||
|
||||
//#endregion
|
||||
//#region src/state/keyed-mutex.ts
|
||||
const locks = /* @__PURE__ */ new Map();
|
||||
function withKeyedLock(key, fn) {
|
||||
const next = (locks.get(key) ?? Promise.resolve()).then(fn, fn);
|
||||
const cleanup = next.then(() => {}, () => {});
|
||||
locks.set(key, cleanup);
|
||||
cleanup.then(() => {
|
||||
if (locks.get(key) === cleanup) locks.delete(key);
|
||||
});
|
||||
return next;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region src/functions/diagnostics.ts
|
||||
const ALL_CATEGORIES = [
|
||||
"actions",
|
||||
"leases",
|
||||
"sentinels",
|
||||
"sketches",
|
||||
"signals",
|
||||
"sessions",
|
||||
"memories",
|
||||
"mesh"
|
||||
];
|
||||
const TWENTY_FOUR_HOURS_MS = 1440 * 60 * 1e3;
|
||||
const ONE_HOUR_MS = 3600 * 1e3;
|
||||
function registerDiagnosticsFunction(sdk, kv) {
|
||||
sdk.registerFunction("mem::diagnose", async (data) => {
|
||||
const categories = data.categories && data.categories.length > 0 ? data.categories.filter((c) => ALL_CATEGORIES.includes(c)) : ALL_CATEGORIES;
|
||||
const checks = [];
|
||||
const now = Date.now();
|
||||
if (categories.includes("actions")) {
|
||||
const actions = await kv.list(KV.actions);
|
||||
const allEdges = await kv.list(KV.actionEdges);
|
||||
const leases = await kv.list(KV.leases);
|
||||
const actionMap = new Map(actions.map((a) => [a.id, a]));
|
||||
for (const action of actions) {
|
||||
if (action.status === "active") {
|
||||
if (!leases.some((l) => l.actionId === action.id && l.status === "active" && new Date(l.expiresAt).getTime() > now)) checks.push({
|
||||
name: `active-no-lease:${action.id}`,
|
||||
category: "actions",
|
||||
status: "warn",
|
||||
message: `Action "${action.title}" is active but has no active lease`,
|
||||
fixable: false
|
||||
});
|
||||
}
|
||||
if (action.status === "blocked") {
|
||||
const deps = allEdges.filter((e) => e.sourceActionId === action.id && e.type === "requires");
|
||||
if (deps.length > 0) {
|
||||
if (deps.every((d) => {
|
||||
const target = actionMap.get(d.targetActionId);
|
||||
return target && target.status === "done";
|
||||
})) checks.push({
|
||||
name: `blocked-deps-done:${action.id}`,
|
||||
category: "actions",
|
||||
status: "fail",
|
||||
message: `Action "${action.title}" is blocked but all dependencies are done`,
|
||||
fixable: true
|
||||
});
|
||||
}
|
||||
}
|
||||
if (action.status === "pending") {
|
||||
const deps = allEdges.filter((e) => e.sourceActionId === action.id && e.type === "requires");
|
||||
if (deps.length > 0) {
|
||||
if (deps.some((d) => {
|
||||
const target = actionMap.get(d.targetActionId);
|
||||
return !target || target.status !== "done";
|
||||
})) checks.push({
|
||||
name: `pending-unsatisfied-deps:${action.id}`,
|
||||
category: "actions",
|
||||
status: "fail",
|
||||
message: `Action "${action.title}" is pending but has unsatisfied dependencies`,
|
||||
fixable: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!checks.some((c) => c.category === "actions" && c.status !== "pass")) checks.push({
|
||||
name: "actions-ok",
|
||||
category: "actions",
|
||||
status: "pass",
|
||||
message: `All ${actions.length} actions are consistent`,
|
||||
fixable: false
|
||||
});
|
||||
}
|
||||
if (categories.includes("leases")) {
|
||||
const leases = await kv.list(KV.leases);
|
||||
const actions = await kv.list(KV.actions);
|
||||
const actionIds = new Set(actions.map((a) => a.id));
|
||||
let leaseIssues = 0;
|
||||
for (const lease of leases) {
|
||||
if (lease.status === "active" && new Date(lease.expiresAt).getTime() <= now) {
|
||||
checks.push({
|
||||
name: `expired-lease:${lease.id}`,
|
||||
category: "leases",
|
||||
status: "fail",
|
||||
message: `Lease ${lease.id} for action ${lease.actionId} expired at ${lease.expiresAt}`,
|
||||
fixable: true
|
||||
});
|
||||
leaseIssues++;
|
||||
}
|
||||
if (!actionIds.has(lease.actionId)) {
|
||||
checks.push({
|
||||
name: `orphaned-lease:${lease.id}`,
|
||||
category: "leases",
|
||||
status: "fail",
|
||||
message: `Lease ${lease.id} references non-existent action ${lease.actionId}`,
|
||||
fixable: true
|
||||
});
|
||||
leaseIssues++;
|
||||
}
|
||||
}
|
||||
if (leaseIssues === 0) checks.push({
|
||||
name: "leases-ok",
|
||||
category: "leases",
|
||||
status: "pass",
|
||||
message: `All ${leases.length} leases are healthy`,
|
||||
fixable: false
|
||||
});
|
||||
}
|
||||
if (categories.includes("sentinels")) {
|
||||
const sentinels = await kv.list(KV.sentinels);
|
||||
const actions = await kv.list(KV.actions);
|
||||
const actionIds = new Set(actions.map((a) => a.id));
|
||||
let sentinelIssues = 0;
|
||||
for (const sentinel of sentinels) {
|
||||
if (sentinel.status === "watching" && sentinel.expiresAt && new Date(sentinel.expiresAt).getTime() <= now) {
|
||||
checks.push({
|
||||
name: `expired-sentinel:${sentinel.id}`,
|
||||
category: "sentinels",
|
||||
status: "fail",
|
||||
message: `Sentinel "${sentinel.name}" expired at ${sentinel.expiresAt}`,
|
||||
fixable: true
|
||||
});
|
||||
sentinelIssues++;
|
||||
}
|
||||
for (const actionId of sentinel.linkedActionIds) if (!actionIds.has(actionId)) {
|
||||
checks.push({
|
||||
name: `sentinel-missing-action:${sentinel.id}:${actionId}`,
|
||||
category: "sentinels",
|
||||
status: "warn",
|
||||
message: `Sentinel "${sentinel.name}" references non-existent action ${actionId}`,
|
||||
fixable: false
|
||||
});
|
||||
sentinelIssues++;
|
||||
}
|
||||
}
|
||||
if (sentinelIssues === 0) checks.push({
|
||||
name: "sentinels-ok",
|
||||
category: "sentinels",
|
||||
status: "pass",
|
||||
message: `All ${sentinels.length} sentinels are healthy`,
|
||||
fixable: false
|
||||
});
|
||||
}
|
||||
if (categories.includes("sketches")) {
|
||||
const sketches = await kv.list(KV.sketches);
|
||||
let sketchIssues = 0;
|
||||
for (const sketch of sketches) if (sketch.status === "active" && new Date(sketch.expiresAt).getTime() <= now) {
|
||||
checks.push({
|
||||
name: `expired-sketch:${sketch.id}`,
|
||||
category: "sketches",
|
||||
status: "fail",
|
||||
message: `Sketch "${sketch.title}" expired at ${sketch.expiresAt}`,
|
||||
fixable: true
|
||||
});
|
||||
sketchIssues++;
|
||||
}
|
||||
if (sketchIssues === 0) checks.push({
|
||||
name: "sketches-ok",
|
||||
category: "sketches",
|
||||
status: "pass",
|
||||
message: `All ${sketches.length} sketches are healthy`,
|
||||
fixable: false
|
||||
});
|
||||
}
|
||||
if (categories.includes("signals")) {
|
||||
const signals = await kv.list(KV.signals);
|
||||
let signalIssues = 0;
|
||||
for (const signal of signals) if (signal.expiresAt && new Date(signal.expiresAt).getTime() <= now) {
|
||||
checks.push({
|
||||
name: `expired-signal:${signal.id}`,
|
||||
category: "signals",
|
||||
status: "fail",
|
||||
message: `Signal from "${signal.from}" expired at ${signal.expiresAt}`,
|
||||
fixable: true
|
||||
});
|
||||
signalIssues++;
|
||||
}
|
||||
if (signalIssues === 0) checks.push({
|
||||
name: "signals-ok",
|
||||
category: "signals",
|
||||
status: "pass",
|
||||
message: `All ${signals.length} signals are healthy`,
|
||||
fixable: false
|
||||
});
|
||||
}
|
||||
if (categories.includes("sessions")) {
|
||||
const sessions = await kv.list(KV.sessions);
|
||||
let sessionIssues = 0;
|
||||
for (const session of sessions) if (session.status === "active" && now - new Date(session.startedAt).getTime() > TWENTY_FOUR_HOURS_MS) {
|
||||
checks.push({
|
||||
name: `abandoned-session:${session.id}`,
|
||||
category: "sessions",
|
||||
status: "warn",
|
||||
message: `Session ${session.id} has been active for over 24 hours`,
|
||||
fixable: false
|
||||
});
|
||||
sessionIssues++;
|
||||
}
|
||||
if (sessionIssues === 0) checks.push({
|
||||
name: "sessions-ok",
|
||||
category: "sessions",
|
||||
status: "pass",
|
||||
message: `All ${sessions.length} sessions are healthy`,
|
||||
fixable: false
|
||||
});
|
||||
}
|
||||
if (categories.includes("memories")) {
|
||||
const memories = await kv.list(KV.memories);
|
||||
const memoryIds = new Set(memories.map((m) => m.id));
|
||||
const supersededBy = /* @__PURE__ */ new Map();
|
||||
let memoryIssues = 0;
|
||||
for (const memory of memories) if (memory.supersedes && memory.supersedes.length > 0) for (const sid of memory.supersedes) {
|
||||
if (!memoryIds.has(sid)) {
|
||||
checks.push({
|
||||
name: `memory-missing-supersedes:${memory.id}:${sid}`,
|
||||
category: "memories",
|
||||
status: "warn",
|
||||
message: `Memory "${memory.title}" supersedes non-existent memory ${sid}`,
|
||||
fixable: false
|
||||
});
|
||||
memoryIssues++;
|
||||
}
|
||||
supersededBy.set(sid, memory.id);
|
||||
}
|
||||
for (const memory of memories) if (memory.isLatest && supersededBy.has(memory.id)) {
|
||||
checks.push({
|
||||
name: `memory-stale-latest:${memory.id}`,
|
||||
category: "memories",
|
||||
status: "fail",
|
||||
message: `Memory "${memory.title}" has isLatest=true but is superseded by ${supersededBy.get(memory.id)}`,
|
||||
fixable: true
|
||||
});
|
||||
memoryIssues++;
|
||||
}
|
||||
if (memoryIssues === 0) checks.push({
|
||||
name: "memories-ok",
|
||||
category: "memories",
|
||||
status: "pass",
|
||||
message: `All ${memories.length} memories are consistent`,
|
||||
fixable: false
|
||||
});
|
||||
}
|
||||
if (categories.includes("mesh")) {
|
||||
const peers = await kv.list(KV.mesh);
|
||||
let meshIssues = 0;
|
||||
for (const peer of peers) {
|
||||
if (peer.lastSyncAt && now - new Date(peer.lastSyncAt).getTime() > ONE_HOUR_MS) {
|
||||
checks.push({
|
||||
name: `stale-peer:${peer.id}`,
|
||||
category: "mesh",
|
||||
status: "warn",
|
||||
message: `Peer "${peer.name}" last synced over 1 hour ago`,
|
||||
fixable: false
|
||||
});
|
||||
meshIssues++;
|
||||
}
|
||||
if (peer.status === "error") {
|
||||
checks.push({
|
||||
name: `error-peer:${peer.id}`,
|
||||
category: "mesh",
|
||||
status: "warn",
|
||||
message: `Peer "${peer.name}" is in error state`,
|
||||
fixable: false
|
||||
});
|
||||
meshIssues++;
|
||||
}
|
||||
}
|
||||
if (meshIssues === 0) checks.push({
|
||||
name: "mesh-ok",
|
||||
category: "mesh",
|
||||
status: "pass",
|
||||
message: `All ${peers.length} mesh peers are healthy`,
|
||||
fixable: false
|
||||
});
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
checks,
|
||||
summary: {
|
||||
pass: checks.filter((c) => c.status === "pass").length,
|
||||
warn: checks.filter((c) => c.status === "warn").length,
|
||||
fail: checks.filter((c) => c.status === "fail").length,
|
||||
fixable: checks.filter((c) => c.fixable).length
|
||||
}
|
||||
};
|
||||
});
|
||||
sdk.registerFunction("mem::heal", async (data) => {
|
||||
const dryRun = data.dryRun ?? false;
|
||||
const categories = data.categories && data.categories.length > 0 ? data.categories.filter((c) => ALL_CATEGORIES.includes(c)) : ALL_CATEGORIES;
|
||||
let fixed = 0;
|
||||
let skipped = 0;
|
||||
const details = [];
|
||||
const now = Date.now();
|
||||
if (categories.includes("actions")) {
|
||||
const actions = await kv.list(KV.actions);
|
||||
const allEdges = await kv.list(KV.actionEdges);
|
||||
const actionMap = new Map(actions.map((a) => [a.id, a]));
|
||||
for (const action of actions) {
|
||||
if (action.status === "blocked") {
|
||||
const deps = allEdges.filter((e) => e.sourceActionId === action.id && e.type === "requires");
|
||||
if (deps.length > 0) {
|
||||
if (deps.every((d) => {
|
||||
const target = actionMap.get(d.targetActionId);
|
||||
return target && target.status === "done";
|
||||
})) {
|
||||
if (dryRun) {
|
||||
details.push(`[dry-run] Would unblock action "${action.title}" (${action.id})`);
|
||||
fixed++;
|
||||
continue;
|
||||
}
|
||||
if (await withKeyedLock(`mem:action:${action.id}`, async () => {
|
||||
const fresh = await kv.get(KV.actions, action.id);
|
||||
if (!fresh || fresh.status !== "blocked") return false;
|
||||
const freshDeps = (await kv.list(KV.actionEdges)).filter((e) => e.sourceActionId === fresh.id && e.type === "requires");
|
||||
const freshActions = await kv.list(KV.actions);
|
||||
const freshMap = new Map(freshActions.map((a) => [a.id, a]));
|
||||
if (!freshDeps.every((d) => {
|
||||
const target = freshMap.get(d.targetActionId);
|
||||
return target && target.status === "done";
|
||||
})) return false;
|
||||
fresh.status = "pending";
|
||||
fresh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
||||
await kv.set(KV.actions, fresh.id, fresh);
|
||||
return true;
|
||||
})) {
|
||||
details.push(`Unblocked action "${action.title}" (${action.id})`);
|
||||
fixed++;
|
||||
} else skipped++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (action.status === "pending") {
|
||||
const deps = allEdges.filter((e) => e.sourceActionId === action.id && e.type === "requires");
|
||||
if (deps.length > 0) {
|
||||
if (deps.some((d) => {
|
||||
const target = actionMap.get(d.targetActionId);
|
||||
return !target || target.status !== "done";
|
||||
})) {
|
||||
if (dryRun) {
|
||||
details.push(`[dry-run] Would block action "${action.title}" (${action.id})`);
|
||||
fixed++;
|
||||
continue;
|
||||
}
|
||||
if (await withKeyedLock(`mem:action:${action.id}`, async () => {
|
||||
const fresh = await kv.get(KV.actions, action.id);
|
||||
if (!fresh || fresh.status !== "pending") return false;
|
||||
const freshDeps = (await kv.list(KV.actionEdges)).filter((e) => e.sourceActionId === fresh.id && e.type === "requires");
|
||||
const freshActions = await kv.list(KV.actions);
|
||||
const freshMap = new Map(freshActions.map((a) => [a.id, a]));
|
||||
if (!freshDeps.some((d) => {
|
||||
const target = freshMap.get(d.targetActionId);
|
||||
return !target || target.status !== "done";
|
||||
})) return false;
|
||||
fresh.status = "blocked";
|
||||
fresh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
||||
await kv.set(KV.actions, fresh.id, fresh);
|
||||
return true;
|
||||
})) {
|
||||
details.push(`Blocked action "${action.title}" (${action.id})`);
|
||||
fixed++;
|
||||
} else skipped++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (categories.includes("leases")) {
|
||||
const leases = await kv.list(KV.leases);
|
||||
const actions = await kv.list(KV.actions);
|
||||
const actionIds = new Set(actions.map((a) => a.id));
|
||||
for (const lease of leases) {
|
||||
if (lease.status === "active" && new Date(lease.expiresAt).getTime() <= now) {
|
||||
if (dryRun) {
|
||||
details.push(`[dry-run] Would expire lease ${lease.id} for action ${lease.actionId}`);
|
||||
fixed++;
|
||||
continue;
|
||||
}
|
||||
if (await withKeyedLock(`mem:action:${lease.actionId}`, async () => {
|
||||
const fresh = await kv.get(KV.leases, lease.id);
|
||||
if (!fresh || fresh.status !== "active" || new Date(fresh.expiresAt).getTime() > Date.now()) return false;
|
||||
fresh.status = "expired";
|
||||
await kv.set(KV.leases, fresh.id, fresh);
|
||||
const action = await kv.get(KV.actions, fresh.actionId);
|
||||
if (action && action.status === "active" && action.assignedTo === fresh.agentId) {
|
||||
action.status = "pending";
|
||||
action.assignedTo = void 0;
|
||||
action.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
||||
await kv.set(KV.actions, action.id, action);
|
||||
}
|
||||
return true;
|
||||
})) {
|
||||
details.push(`Expired lease ${lease.id} for action ${lease.actionId}`);
|
||||
fixed++;
|
||||
} else skipped++;
|
||||
continue;
|
||||
}
|
||||
if (!actionIds.has(lease.actionId)) {
|
||||
if (dryRun) {
|
||||
details.push(`[dry-run] Would delete orphaned lease ${lease.id}`);
|
||||
fixed++;
|
||||
continue;
|
||||
}
|
||||
await kv.delete(KV.leases, lease.id);
|
||||
details.push(`Deleted orphaned lease ${lease.id}`);
|
||||
fixed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (categories.includes("sentinels")) {
|
||||
const sentinels = await kv.list(KV.sentinels);
|
||||
for (const sentinel of sentinels) if (sentinel.status === "watching" && sentinel.expiresAt && new Date(sentinel.expiresAt).getTime() <= now) {
|
||||
if (dryRun) {
|
||||
details.push(`[dry-run] Would expire sentinel "${sentinel.name}" (${sentinel.id})`);
|
||||
fixed++;
|
||||
continue;
|
||||
}
|
||||
if (await withKeyedLock(`mem:sentinel:${sentinel.id}`, async () => {
|
||||
const fresh = await kv.get(KV.sentinels, sentinel.id);
|
||||
if (!fresh || fresh.status !== "watching") return false;
|
||||
if (!fresh.expiresAt || new Date(fresh.expiresAt).getTime() > Date.now()) return false;
|
||||
fresh.status = "expired";
|
||||
await kv.set(KV.sentinels, fresh.id, fresh);
|
||||
return true;
|
||||
})) {
|
||||
details.push(`Expired sentinel "${sentinel.name}" (${sentinel.id})`);
|
||||
fixed++;
|
||||
} else skipped++;
|
||||
}
|
||||
}
|
||||
if (categories.includes("sketches")) {
|
||||
const sketches = await kv.list(KV.sketches);
|
||||
for (const sketch of sketches) if (sketch.status === "active" && new Date(sketch.expiresAt).getTime() <= now) {
|
||||
if (dryRun) {
|
||||
details.push(`[dry-run] Would discard expired sketch "${sketch.title}" (${sketch.id})`);
|
||||
fixed++;
|
||||
continue;
|
||||
}
|
||||
if (await withKeyedLock(`mem:sketch:${sketch.id}`, async () => {
|
||||
const fresh = await kv.get(KV.sketches, sketch.id);
|
||||
if (!fresh || fresh.status !== "active" || new Date(fresh.expiresAt).getTime() > Date.now()) return false;
|
||||
const allEdges = await kv.list(KV.actionEdges);
|
||||
const actionIdSet = new Set(fresh.actionIds);
|
||||
for (const edge of allEdges) if (actionIdSet.has(edge.sourceActionId) || actionIdSet.has(edge.targetActionId)) await kv.delete(KV.actionEdges, edge.id);
|
||||
for (const actionId of fresh.actionIds) await kv.delete(KV.actions, actionId);
|
||||
fresh.status = "discarded";
|
||||
fresh.discardedAt = (/* @__PURE__ */ new Date()).toISOString();
|
||||
await kv.set(KV.sketches, fresh.id, fresh);
|
||||
return true;
|
||||
})) {
|
||||
details.push(`Discarded expired sketch "${sketch.title}" (${sketch.id})`);
|
||||
fixed++;
|
||||
} else skipped++;
|
||||
}
|
||||
}
|
||||
if (categories.includes("signals")) {
|
||||
const signals = await kv.list(KV.signals);
|
||||
for (const signal of signals) if (signal.expiresAt && new Date(signal.expiresAt).getTime() <= now) {
|
||||
if (dryRun) {
|
||||
details.push(`[dry-run] Would delete expired signal ${signal.id}`);
|
||||
fixed++;
|
||||
continue;
|
||||
}
|
||||
await kv.delete(KV.signals, signal.id);
|
||||
details.push(`Deleted expired signal ${signal.id}`);
|
||||
fixed++;
|
||||
}
|
||||
}
|
||||
if (categories.includes("memories")) {
|
||||
const memories = await kv.list(KV.memories);
|
||||
const supersededBy = /* @__PURE__ */ new Map();
|
||||
for (const memory of memories) if (memory.supersedes && memory.supersedes.length > 0) for (const sid of memory.supersedes) supersededBy.set(sid, memory.id);
|
||||
for (const memory of memories) if (memory.isLatest && supersededBy.has(memory.id)) {
|
||||
if (dryRun) {
|
||||
details.push(`[dry-run] Would set isLatest=false on memory "${memory.title}" (${memory.id})`);
|
||||
fixed++;
|
||||
continue;
|
||||
}
|
||||
if (await withKeyedLock(`mem:memory:${memory.id}`, async () => {
|
||||
const fresh = await kv.get(KV.memories, memory.id);
|
||||
if (!fresh || !fresh.isLatest) return false;
|
||||
fresh.isLatest = false;
|
||||
fresh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
||||
await kv.set(KV.memories, fresh.id, fresh);
|
||||
return true;
|
||||
})) {
|
||||
details.push(`Set isLatest=false on memory "${memory.title}" (${memory.id})`);
|
||||
fixed++;
|
||||
} else skipped++;
|
||||
}
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
fixed,
|
||||
skipped,
|
||||
details
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
//#endregion
|
||||
export { registerDiagnosticsFunction };
|
||||
//# sourceMappingURL=diagnostics.mjs.map
|
||||
Executable
+76
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env node
|
||||
import { execSync } from "node:child_process";
|
||||
import { basename } from "node:path";
|
||||
|
||||
//#region src/hooks/_project.ts
|
||||
function resolveProject(cwd) {
|
||||
const explicit = process.env["AGENTMEMORY_PROJECT_NAME"];
|
||||
if (explicit && explicit.trim()) return explicit.trim();
|
||||
const dir = cwd && cwd.trim() ? cwd : process.cwd();
|
||||
try {
|
||||
const top = execSync("git rev-parse --show-toplevel", {
|
||||
cwd: dir,
|
||||
stdio: [
|
||||
"ignore",
|
||||
"pipe",
|
||||
"ignore"
|
||||
],
|
||||
timeout: 500
|
||||
}).toString().trim();
|
||||
if (top) return basename(top);
|
||||
} catch {}
|
||||
return basename(dir);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region src/hooks/notification.ts
|
||||
function isSdkChildContext(payload) {
|
||||
if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true;
|
||||
if (!payload || typeof payload !== "object") return false;
|
||||
return payload.entrypoint === "sdk-ts";
|
||||
}
|
||||
const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
|
||||
const SECRET = process.env["AGENTMEMORY_SECRET"] || "";
|
||||
function authHeaders() {
|
||||
const h = { "Content-Type": "application/json" };
|
||||
if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
|
||||
return h;
|
||||
}
|
||||
async function main() {
|
||||
let input = "";
|
||||
for await (const chunk of process.stdin) input += chunk;
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(input);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
if (isSdkChildContext(data)) return;
|
||||
const notificationType = data.notification_type ?? data.notificationType;
|
||||
if (notificationType !== "permission_prompt") return;
|
||||
const rawSessionId = data.session_id ?? data.sessionId;
|
||||
const sessionId = typeof rawSessionId === "string" && rawSessionId.length > 0 ? rawSessionId : "unknown";
|
||||
fetch(`${REST_URL}/agentmemory/observe`, {
|
||||
method: "POST",
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({
|
||||
hookType: "notification",
|
||||
sessionId,
|
||||
project: resolveProject(data.cwd),
|
||||
cwd: data.cwd || process.cwd(),
|
||||
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
||||
data: {
|
||||
notification_type: notificationType,
|
||||
title: data.title,
|
||||
message: data.message
|
||||
}
|
||||
}),
|
||||
signal: AbortSignal.timeout(2e3)
|
||||
}).catch(() => {});
|
||||
setTimeout(() => process.exit(0), 500).unref();
|
||||
}
|
||||
main();
|
||||
|
||||
//#endregion
|
||||
export { };
|
||||
//# sourceMappingURL=notification.mjs.map
|
||||
Executable
+102
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env node
|
||||
import { execFile } from "node:child_process";
|
||||
import { promisify } from "node:util";
|
||||
|
||||
//#region src/hooks/post-commit.ts
|
||||
const exec = promisify(execFile);
|
||||
function isSdkChildContext(payload) {
|
||||
if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true;
|
||||
if (!payload || typeof payload !== "object") return false;
|
||||
return payload.entrypoint === "sdk-ts";
|
||||
}
|
||||
const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
|
||||
const SECRET = process.env["AGENTMEMORY_SECRET"] || "";
|
||||
const TIMEOUT_MS = 1500;
|
||||
function authHeaders() {
|
||||
const h = { "Content-Type": "application/json" };
|
||||
if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
|
||||
return h;
|
||||
}
|
||||
async function git(args, cwd) {
|
||||
try {
|
||||
const { stdout } = await exec("git", args, {
|
||||
cwd,
|
||||
timeout: 1500
|
||||
});
|
||||
return stdout.trim();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
async function main() {
|
||||
let input = "";
|
||||
for await (const chunk of process.stdin) input += chunk;
|
||||
let data = {};
|
||||
if (input.trim()) try {
|
||||
data = JSON.parse(input);
|
||||
} catch {}
|
||||
if (isSdkChildContext(data)) return;
|
||||
const cwd = data.cwd || process.env["AGENTMEMORY_CWD"] || process.cwd();
|
||||
const sessionId = data.session_id || process.env["AGENTMEMORY_SESSION_ID"] || void 0;
|
||||
const sha = process.env["AGENTMEMORY_COMMIT_SHA"] || await git(["rev-parse", "HEAD"], cwd);
|
||||
if (!sha) return;
|
||||
const branch = await git([
|
||||
"rev-parse",
|
||||
"--abbrev-ref",
|
||||
"HEAD"
|
||||
], cwd);
|
||||
const repo = await git([
|
||||
"config",
|
||||
"--get",
|
||||
"remote.origin.url"
|
||||
], cwd);
|
||||
const message = await git([
|
||||
"log",
|
||||
"-1",
|
||||
"--pretty=%B",
|
||||
sha
|
||||
], cwd);
|
||||
const author = await git([
|
||||
"log",
|
||||
"-1",
|
||||
"--pretty=%an <%ae>",
|
||||
sha
|
||||
], cwd);
|
||||
const authoredAt = await git([
|
||||
"log",
|
||||
"-1",
|
||||
"--pretty=%aI",
|
||||
sha
|
||||
], cwd);
|
||||
const filesRaw = await git([
|
||||
"diff-tree",
|
||||
"--no-commit-id",
|
||||
"--name-only",
|
||||
"-r",
|
||||
sha
|
||||
], cwd);
|
||||
const files = filesRaw ? filesRaw.split("\n").filter(Boolean) : void 0;
|
||||
const body = {
|
||||
sessionId,
|
||||
sha,
|
||||
branch: branch || void 0,
|
||||
repo: repo || void 0,
|
||||
message: message || void 0,
|
||||
author: author || void 0,
|
||||
authoredAt: authoredAt || void 0,
|
||||
files
|
||||
};
|
||||
try {
|
||||
await fetch(`${REST_URL}/agentmemory/session/commit`, {
|
||||
method: "POST",
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify(body),
|
||||
signal: AbortSignal.timeout(TIMEOUT_MS)
|
||||
});
|
||||
} catch {}
|
||||
}
|
||||
main();
|
||||
|
||||
//#endregion
|
||||
export { };
|
||||
//# sourceMappingURL=post-commit.mjs.map
|
||||
Executable
+77
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env node
|
||||
import { execSync } from "node:child_process";
|
||||
import { basename } from "node:path";
|
||||
|
||||
//#region src/hooks/_project.ts
|
||||
function resolveProject(cwd) {
|
||||
const explicit = process.env["AGENTMEMORY_PROJECT_NAME"];
|
||||
if (explicit && explicit.trim()) return explicit.trim();
|
||||
const dir = cwd && cwd.trim() ? cwd : process.cwd();
|
||||
try {
|
||||
const top = execSync("git rev-parse --show-toplevel", {
|
||||
cwd: dir,
|
||||
stdio: [
|
||||
"ignore",
|
||||
"pipe",
|
||||
"ignore"
|
||||
],
|
||||
timeout: 500
|
||||
}).toString().trim();
|
||||
if (top) return basename(top);
|
||||
} catch {}
|
||||
return basename(dir);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region src/hooks/post-tool-failure.ts
|
||||
function isSdkChildContext(payload) {
|
||||
if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true;
|
||||
if (!payload || typeof payload !== "object") return false;
|
||||
return payload.entrypoint === "sdk-ts";
|
||||
}
|
||||
const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
|
||||
const SECRET = process.env["AGENTMEMORY_SECRET"] || "";
|
||||
function authHeaders() {
|
||||
const h = { "Content-Type": "application/json" };
|
||||
if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
|
||||
return h;
|
||||
}
|
||||
async function main() {
|
||||
let input = "";
|
||||
for await (const chunk of process.stdin) input += chunk;
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(input);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
if (isSdkChildContext(data)) return;
|
||||
if (data.is_interrupt || data.isInterrupt) return;
|
||||
const sessionId = data.session_id || data.sessionId || "unknown";
|
||||
const toolName = data.tool_name ?? data.toolName;
|
||||
const toolInput = data.tool_input ?? data.toolArgs;
|
||||
const error = data.error ?? data.errorMessage;
|
||||
fetch(`${REST_URL}/agentmemory/observe`, {
|
||||
method: "POST",
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({
|
||||
hookType: "post_tool_failure",
|
||||
sessionId,
|
||||
project: resolveProject(data.cwd),
|
||||
cwd: data.cwd || process.cwd(),
|
||||
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
||||
data: {
|
||||
tool_name: toolName,
|
||||
tool_input: typeof toolInput === "string" ? toolInput.slice(0, 4e3) : JSON.stringify(toolInput ?? "").slice(0, 4e3),
|
||||
error: typeof error === "string" ? error.slice(0, 4e3) : JSON.stringify(error ?? "").slice(0, 4e3)
|
||||
}
|
||||
}),
|
||||
signal: AbortSignal.timeout(3e3)
|
||||
}).catch(() => {});
|
||||
setTimeout(() => process.exit(0), 500).unref();
|
||||
}
|
||||
main();
|
||||
|
||||
//#endregion
|
||||
export { };
|
||||
//# sourceMappingURL=post-tool-failure.mjs.map
|
||||
Executable
+122
@@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env node
|
||||
import { execSync } from "node:child_process";
|
||||
import { basename } from "node:path";
|
||||
|
||||
//#region src/hooks/_project.ts
|
||||
function resolveProject(cwd) {
|
||||
const explicit = process.env["AGENTMEMORY_PROJECT_NAME"];
|
||||
if (explicit && explicit.trim()) return explicit.trim();
|
||||
const dir = cwd && cwd.trim() ? cwd : process.cwd();
|
||||
try {
|
||||
const top = execSync("git rev-parse --show-toplevel", {
|
||||
cwd: dir,
|
||||
stdio: [
|
||||
"ignore",
|
||||
"pipe",
|
||||
"ignore"
|
||||
],
|
||||
timeout: 500
|
||||
}).toString().trim();
|
||||
if (top) return basename(top);
|
||||
} catch {}
|
||||
return basename(dir);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region src/hooks/post-tool-use.ts
|
||||
function isSdkChildContext(payload) {
|
||||
if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true;
|
||||
if (!payload || typeof payload !== "object") return false;
|
||||
return payload.entrypoint === "sdk-ts";
|
||||
}
|
||||
const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
|
||||
const SECRET = process.env["AGENTMEMORY_SECRET"] || "";
|
||||
function authHeaders() {
|
||||
const h = { "Content-Type": "application/json" };
|
||||
if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
|
||||
return h;
|
||||
}
|
||||
async function main() {
|
||||
let input = "";
|
||||
for await (const chunk of process.stdin) input += chunk;
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(input);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
if (isSdkChildContext(data)) return;
|
||||
const sessionId = data.session_id || data.sessionId || "unknown";
|
||||
const toolName = data.tool_name ?? data.toolName;
|
||||
const toolInput = data.tool_input ?? data.toolArgs;
|
||||
const { imageData, cleanOutput } = extractImageData(toolOutput(data));
|
||||
fetch(`${REST_URL}/agentmemory/observe`, {
|
||||
method: "POST",
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({
|
||||
hookType: "post_tool_use",
|
||||
sessionId,
|
||||
project: resolveProject(data.cwd),
|
||||
cwd: data.cwd || process.cwd(),
|
||||
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
||||
data: {
|
||||
tool_name: toolName,
|
||||
tool_input: toolInput,
|
||||
tool_output: truncate(cleanOutput, 8e3),
|
||||
...imageData ? { image_data: imageData } : {}
|
||||
}
|
||||
}),
|
||||
signal: AbortSignal.timeout(3e3)
|
||||
}).catch(() => {});
|
||||
setTimeout(() => process.exit(0), 500).unref();
|
||||
}
|
||||
function toolOutput(data) {
|
||||
if (data.tool_response !== void 0) return data.tool_response;
|
||||
if (data.tool_output !== void 0) return data.tool_output;
|
||||
const result = data.tool_result ?? data.toolResult;
|
||||
if (typeof result === "object" && result !== null) {
|
||||
const obj = result;
|
||||
return obj.text_result_for_llm ?? obj.textResultForLlm ?? result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function isBase64Image(val) {
|
||||
return typeof val === "string" && (val.startsWith("data:image/") || val.startsWith("iVBORw0KGgo") || val.startsWith("/9j/"));
|
||||
}
|
||||
function extractImageData(output) {
|
||||
if (isBase64Image(output)) return {
|
||||
imageData: output,
|
||||
cleanOutput: "[image data extracted]"
|
||||
};
|
||||
if (typeof output === "object" && output !== null && !Array.isArray(output)) {
|
||||
const obj = output;
|
||||
let imageData;
|
||||
const clean = {};
|
||||
for (const [key, val] of Object.entries(obj)) if (!imageData && isBase64Image(val)) {
|
||||
imageData = val;
|
||||
clean[key] = "[image data extracted]";
|
||||
} else clean[key] = val;
|
||||
return {
|
||||
imageData,
|
||||
cleanOutput: clean
|
||||
};
|
||||
}
|
||||
return {
|
||||
imageData: void 0,
|
||||
cleanOutput: output
|
||||
};
|
||||
}
|
||||
function truncate(value, max) {
|
||||
if (typeof value === "string" && value.length > max) return value.slice(0, max) + "\n[...truncated]";
|
||||
if (typeof value === "object" && value !== null) {
|
||||
const str = JSON.stringify(value);
|
||||
if (str.length > max) return str.slice(0, max) + "...[truncated]";
|
||||
return value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
main();
|
||||
|
||||
//#endregion
|
||||
export { };
|
||||
//# sourceMappingURL=post-tool-use.mjs.map
|
||||
Executable
+80
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env node
|
||||
import { execSync } from "node:child_process";
|
||||
import { basename } from "node:path";
|
||||
|
||||
//#region src/hooks/_project.ts
|
||||
function resolveProject(cwd) {
|
||||
const explicit = process.env["AGENTMEMORY_PROJECT_NAME"];
|
||||
if (explicit && explicit.trim()) return explicit.trim();
|
||||
const dir = cwd && cwd.trim() ? cwd : process.cwd();
|
||||
try {
|
||||
const top = execSync("git rev-parse --show-toplevel", {
|
||||
cwd: dir,
|
||||
stdio: [
|
||||
"ignore",
|
||||
"pipe",
|
||||
"ignore"
|
||||
],
|
||||
timeout: 500
|
||||
}).toString().trim();
|
||||
if (top) return basename(top);
|
||||
} catch {}
|
||||
return basename(dir);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region src/hooks/pre-compact.ts
|
||||
function isSdkChildContext(payload) {
|
||||
if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true;
|
||||
if (!payload || typeof payload !== "object") return false;
|
||||
return payload.entrypoint === "sdk-ts";
|
||||
}
|
||||
const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
|
||||
const SECRET = process.env["AGENTMEMORY_SECRET"] || "";
|
||||
function authHeaders() {
|
||||
const h = { "Content-Type": "application/json" };
|
||||
if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
|
||||
return h;
|
||||
}
|
||||
async function main() {
|
||||
let input = "";
|
||||
for await (const chunk of process.stdin) input += chunk;
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(input);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
if (isSdkChildContext(data)) return;
|
||||
const sessionId = data.session_id || data.sessionId || "unknown";
|
||||
const project = resolveProject(data.cwd);
|
||||
if (process.env["CLAUDE_MEMORY_BRIDGE"] === "true") try {
|
||||
await fetch(`${REST_URL}/agentmemory/claude-bridge/sync`, {
|
||||
method: "POST",
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({}),
|
||||
signal: AbortSignal.timeout(5e3)
|
||||
});
|
||||
} catch {}
|
||||
try {
|
||||
const res = await fetch(`${REST_URL}/agentmemory/context`, {
|
||||
method: "POST",
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({
|
||||
sessionId,
|
||||
project,
|
||||
budget: 1500
|
||||
}),
|
||||
signal: AbortSignal.timeout(5e3)
|
||||
});
|
||||
if (res.ok) {
|
||||
const result = await res.json();
|
||||
if (result.context) process.stdout.write(result.context);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
main();
|
||||
|
||||
//#endregion
|
||||
export { };
|
||||
//# sourceMappingURL=pre-compact.mjs.map
|
||||
Executable
+84
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env node
|
||||
//#region src/hooks/pre-tool-use.ts
|
||||
function isSdkChildContext(payload) {
|
||||
if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true;
|
||||
if (!payload || typeof payload !== "object") return false;
|
||||
return payload.entrypoint === "sdk-ts";
|
||||
}
|
||||
const INJECT_CONTEXT = process.env["AGENTMEMORY_INJECT_CONTEXT"] === "true";
|
||||
const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
|
||||
const SECRET = process.env["AGENTMEMORY_SECRET"] || "";
|
||||
function authHeaders() {
|
||||
const h = { "Content-Type": "application/json" };
|
||||
if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
|
||||
return h;
|
||||
}
|
||||
async function main() {
|
||||
if (!INJECT_CONTEXT) return;
|
||||
let input = "";
|
||||
for await (const chunk of process.stdin) input += chunk;
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(input);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
if (isSdkChildContext(data)) return;
|
||||
const toolName = typeof data.tool_name === "string" ? data.tool_name : typeof data.toolName === "string" ? data.toolName : void 0;
|
||||
if (!toolName) return;
|
||||
const normalizedToolName = toolName.toLowerCase();
|
||||
if (![
|
||||
"edit",
|
||||
"write",
|
||||
"create",
|
||||
"read",
|
||||
"view",
|
||||
"glob",
|
||||
"grep"
|
||||
].includes(normalizedToolName)) return;
|
||||
const rawToolInput = data.tool_input ?? data.toolArgs;
|
||||
const toolInput = typeof rawToolInput === "object" && rawToolInput !== null && !Array.isArray(rawToolInput) ? rawToolInput : {};
|
||||
const files = [];
|
||||
const fileKeys = normalizedToolName === "grep" ? ["path", "file"] : [
|
||||
"file_path",
|
||||
"path",
|
||||
"file",
|
||||
"pattern"
|
||||
];
|
||||
for (const key of fileKeys) {
|
||||
const val = toolInput[key];
|
||||
if (typeof val === "string" && val.length > 0) files.push(val);
|
||||
}
|
||||
if (files.length === 0) return;
|
||||
const terms = [];
|
||||
if (normalizedToolName === "grep" || normalizedToolName === "glob") {
|
||||
const pattern = toolInput["pattern"];
|
||||
if (typeof pattern === "string" && pattern.length > 0) terms.push(pattern);
|
||||
}
|
||||
const rawSessionId = data.session_id || data.sessionId;
|
||||
const sessionId = typeof rawSessionId === "string" && rawSessionId.length > 0 ? rawSessionId : "unknown";
|
||||
const project = typeof data.project === "string" && data.project.trim().length > 0 ? data.project.trim() : void 0;
|
||||
try {
|
||||
const res = await fetch(`${REST_URL}/agentmemory/enrich`, {
|
||||
method: "POST",
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({
|
||||
sessionId,
|
||||
files,
|
||||
terms,
|
||||
toolName,
|
||||
...project !== void 0 && { project }
|
||||
}),
|
||||
signal: AbortSignal.timeout(2e3)
|
||||
});
|
||||
if (res.ok) {
|
||||
const result = await res.json();
|
||||
if (result.context) process.stdout.write(result.context);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
main();
|
||||
|
||||
//#endregion
|
||||
export { };
|
||||
//# sourceMappingURL=pre-tool-use.mjs.map
|
||||
Executable
+69
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env node
|
||||
import { execSync } from "node:child_process";
|
||||
import { basename } from "node:path";
|
||||
|
||||
//#region src/hooks/_project.ts
|
||||
function resolveProject(cwd) {
|
||||
const explicit = process.env["AGENTMEMORY_PROJECT_NAME"];
|
||||
if (explicit && explicit.trim()) return explicit.trim();
|
||||
const dir = cwd && cwd.trim() ? cwd : process.cwd();
|
||||
try {
|
||||
const top = execSync("git rev-parse --show-toplevel", {
|
||||
cwd: dir,
|
||||
stdio: [
|
||||
"ignore",
|
||||
"pipe",
|
||||
"ignore"
|
||||
],
|
||||
timeout: 500
|
||||
}).toString().trim();
|
||||
if (top) return basename(top);
|
||||
} catch {}
|
||||
return basename(dir);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region src/hooks/prompt-submit.ts
|
||||
function isSdkChildContext(payload) {
|
||||
if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true;
|
||||
if (!payload || typeof payload !== "object") return false;
|
||||
return payload.entrypoint === "sdk-ts";
|
||||
}
|
||||
const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
|
||||
const SECRET = process.env["AGENTMEMORY_SECRET"] || "";
|
||||
function authHeaders() {
|
||||
const h = { "Content-Type": "application/json" };
|
||||
if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
|
||||
return h;
|
||||
}
|
||||
async function main() {
|
||||
let input = "";
|
||||
for await (const chunk of process.stdin) input += chunk;
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(input);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
if (isSdkChildContext(data)) return;
|
||||
const sessionId = data.session_id || data.sessionId || "unknown";
|
||||
fetch(`${REST_URL}/agentmemory/observe`, {
|
||||
method: "POST",
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({
|
||||
hookType: "prompt_submit",
|
||||
sessionId,
|
||||
project: resolveProject(data.cwd),
|
||||
cwd: data.cwd || process.cwd(),
|
||||
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
||||
data: { prompt: data.prompt ?? data.userPrompt }
|
||||
}),
|
||||
signal: AbortSignal.timeout(3e3)
|
||||
}).catch(() => {});
|
||||
setTimeout(() => process.exit(0), 500).unref();
|
||||
}
|
||||
main();
|
||||
|
||||
//#endregion
|
||||
export { };
|
||||
//# sourceMappingURL=prompt-submit.mjs.map
|
||||
Executable
+60
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env node
|
||||
//#region src/hooks/session-end.ts
|
||||
function isSdkChildContext(payload) {
|
||||
if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true;
|
||||
if (!payload || typeof payload !== "object") return false;
|
||||
return payload.entrypoint === "sdk-ts";
|
||||
}
|
||||
const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
|
||||
const SECRET = process.env["AGENTMEMORY_SECRET"] || "";
|
||||
function authHeaders() {
|
||||
const h = { "Content-Type": "application/json" };
|
||||
if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
|
||||
return h;
|
||||
}
|
||||
async function main() {
|
||||
let input = "";
|
||||
for await (const chunk of process.stdin) input += chunk;
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(input);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
if (isSdkChildContext(data)) return;
|
||||
const sessionId = data.session_id || data.sessionId || "unknown";
|
||||
fetch(`${REST_URL}/agentmemory/session/end`, {
|
||||
method: "POST",
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({ sessionId }),
|
||||
signal: AbortSignal.timeout(3e4)
|
||||
}).catch(() => {});
|
||||
if (process.env["CONSOLIDATION_ENABLED"] === "true") {
|
||||
fetch(`${REST_URL}/agentmemory/crystals/auto`, {
|
||||
method: "POST",
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({ olderThanDays: 0 }),
|
||||
signal: AbortSignal.timeout(6e4)
|
||||
}).catch(() => {});
|
||||
fetch(`${REST_URL}/agentmemory/consolidate-pipeline`, {
|
||||
method: "POST",
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({
|
||||
tier: "all",
|
||||
force: true
|
||||
}),
|
||||
signal: AbortSignal.timeout(12e4)
|
||||
}).catch(() => {});
|
||||
}
|
||||
if (process.env["CLAUDE_MEMORY_BRIDGE"] === "true") fetch(`${REST_URL}/agentmemory/claude-bridge/sync`, {
|
||||
method: "POST",
|
||||
headers: authHeaders(),
|
||||
signal: AbortSignal.timeout(3e4)
|
||||
}).catch(() => {});
|
||||
setTimeout(() => process.exit(0), 1500).unref();
|
||||
}
|
||||
main();
|
||||
|
||||
//#endregion
|
||||
export { };
|
||||
//# sourceMappingURL=session-end.mjs.map
|
||||
Executable
+87
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env node
|
||||
import { execSync } from "node:child_process";
|
||||
import { basename } from "node:path";
|
||||
|
||||
//#region src/hooks/_project.ts
|
||||
function resolveProject(cwd) {
|
||||
const explicit = process.env["AGENTMEMORY_PROJECT_NAME"];
|
||||
if (explicit && explicit.trim()) return explicit.trim();
|
||||
const dir = cwd && cwd.trim() ? cwd : process.cwd();
|
||||
try {
|
||||
const top = execSync("git rev-parse --show-toplevel", {
|
||||
cwd: dir,
|
||||
stdio: [
|
||||
"ignore",
|
||||
"pipe",
|
||||
"ignore"
|
||||
],
|
||||
timeout: 500
|
||||
}).toString().trim();
|
||||
if (top) return basename(top);
|
||||
} catch {}
|
||||
return basename(dir);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region src/hooks/session-start.ts
|
||||
function isSdkChildContext(payload) {
|
||||
if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true;
|
||||
if (!payload || typeof payload !== "object") return false;
|
||||
return payload.entrypoint === "sdk-ts";
|
||||
}
|
||||
const INJECT_CONTEXT = process.env["AGENTMEMORY_INJECT_CONTEXT"] === "true";
|
||||
const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
|
||||
const SECRET = process.env["AGENTMEMORY_SECRET"] || "";
|
||||
const INJECT_TIMEOUT_MS = 1500;
|
||||
const REGISTER_TIMEOUT_MS = 800;
|
||||
function authHeaders() {
|
||||
const h = { "Content-Type": "application/json" };
|
||||
if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
|
||||
return h;
|
||||
}
|
||||
async function main() {
|
||||
let input = "";
|
||||
for await (const chunk of process.stdin) input += chunk;
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(input);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
if (isSdkChildContext(data)) return;
|
||||
const sessionId = data.session_id || data.sessionId || `ses_${Date.now().toString(36)}`;
|
||||
const cwd = data.cwd || process.cwd();
|
||||
const project = resolveProject(data.cwd);
|
||||
const url = `${REST_URL}/agentmemory/session/start`;
|
||||
const init = {
|
||||
method: "POST",
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({
|
||||
sessionId,
|
||||
project,
|
||||
cwd
|
||||
})
|
||||
};
|
||||
if (!INJECT_CONTEXT) {
|
||||
fetch(url, {
|
||||
...init,
|
||||
signal: AbortSignal.timeout(REGISTER_TIMEOUT_MS)
|
||||
}).catch(() => {});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
...init,
|
||||
signal: AbortSignal.timeout(INJECT_TIMEOUT_MS)
|
||||
});
|
||||
if (res.ok) {
|
||||
const result = await res.json();
|
||||
if (result.context) process.stdout.write(result.context);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
main();
|
||||
|
||||
//#endregion
|
||||
export { };
|
||||
//# sourceMappingURL=session-start.mjs.map
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env node
|
||||
//#region src/hooks/stop.ts
|
||||
function isSdkChildContext(payload) {
|
||||
if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true;
|
||||
if (!payload || typeof payload !== "object") return false;
|
||||
return payload.entrypoint === "sdk-ts";
|
||||
}
|
||||
const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
|
||||
const SECRET = process.env["AGENTMEMORY_SECRET"] || "";
|
||||
function authHeaders() {
|
||||
const h = { "Content-Type": "application/json" };
|
||||
if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
|
||||
return h;
|
||||
}
|
||||
async function main() {
|
||||
let input = "";
|
||||
for await (const chunk of process.stdin) input += chunk;
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(input);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
if (isSdkChildContext(data)) return;
|
||||
const sessionId = data.session_id || data.sessionId || "unknown";
|
||||
fetch(`${REST_URL}/agentmemory/summarize`, {
|
||||
method: "POST",
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({ sessionId }),
|
||||
signal: AbortSignal.timeout(12e4)
|
||||
}).catch(() => {});
|
||||
fetch(`${REST_URL}/agentmemory/session/end`, {
|
||||
method: "POST",
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({ sessionId }),
|
||||
signal: AbortSignal.timeout(5e3)
|
||||
}).catch(() => {});
|
||||
setTimeout(() => process.exit(0), 1500).unref();
|
||||
}
|
||||
main();
|
||||
|
||||
//#endregion
|
||||
export { };
|
||||
//# sourceMappingURL=stop.mjs.map
|
||||
Executable
+75
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env node
|
||||
import { execSync } from "node:child_process";
|
||||
import { basename } from "node:path";
|
||||
|
||||
//#region src/hooks/_project.ts
|
||||
function resolveProject(cwd) {
|
||||
const explicit = process.env["AGENTMEMORY_PROJECT_NAME"];
|
||||
if (explicit && explicit.trim()) return explicit.trim();
|
||||
const dir = cwd && cwd.trim() ? cwd : process.cwd();
|
||||
try {
|
||||
const top = execSync("git rev-parse --show-toplevel", {
|
||||
cwd: dir,
|
||||
stdio: [
|
||||
"ignore",
|
||||
"pipe",
|
||||
"ignore"
|
||||
],
|
||||
timeout: 500
|
||||
}).toString().trim();
|
||||
if (top) return basename(top);
|
||||
} catch {}
|
||||
return basename(dir);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region src/hooks/subagent-start.ts
|
||||
function isSdkChildContext(payload) {
|
||||
if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true;
|
||||
if (!payload || typeof payload !== "object") return false;
|
||||
return payload.entrypoint === "sdk-ts";
|
||||
}
|
||||
const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
|
||||
const SECRET = process.env["AGENTMEMORY_SECRET"] || "";
|
||||
const TIMEOUT_MS = 800;
|
||||
function authHeaders() {
|
||||
const h = { "Content-Type": "application/json" };
|
||||
if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
|
||||
return h;
|
||||
}
|
||||
async function main() {
|
||||
let input = "";
|
||||
for await (const chunk of process.stdin) input += chunk;
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(input);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
if (isSdkChildContext(data)) return;
|
||||
const sessionId = data.session_id || data.sessionId || "unknown";
|
||||
const agentId = data.agent_id || data.agentName;
|
||||
const agentType = data.agent_type || data.agentDisplayName || data.agentName;
|
||||
fetch(`${REST_URL}/agentmemory/observe`, {
|
||||
method: "POST",
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({
|
||||
hookType: "subagent_start",
|
||||
sessionId,
|
||||
project: resolveProject(data.cwd),
|
||||
cwd: data.cwd || process.cwd(),
|
||||
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
||||
data: {
|
||||
agent_id: agentId,
|
||||
agent_type: agentType
|
||||
}
|
||||
}),
|
||||
signal: AbortSignal.timeout(TIMEOUT_MS)
|
||||
}).catch(() => {});
|
||||
setTimeout(() => process.exit(0), 500).unref();
|
||||
}
|
||||
main();
|
||||
|
||||
//#endregion
|
||||
export { };
|
||||
//# sourceMappingURL=subagent-start.mjs.map
|
||||
Executable
+76
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env node
|
||||
import { execSync } from "node:child_process";
|
||||
import { basename } from "node:path";
|
||||
|
||||
//#region src/hooks/_project.ts
|
||||
function resolveProject(cwd) {
|
||||
const explicit = process.env["AGENTMEMORY_PROJECT_NAME"];
|
||||
if (explicit && explicit.trim()) return explicit.trim();
|
||||
const dir = cwd && cwd.trim() ? cwd : process.cwd();
|
||||
try {
|
||||
const top = execSync("git rev-parse --show-toplevel", {
|
||||
cwd: dir,
|
||||
stdio: [
|
||||
"ignore",
|
||||
"pipe",
|
||||
"ignore"
|
||||
],
|
||||
timeout: 500
|
||||
}).toString().trim();
|
||||
if (top) return basename(top);
|
||||
} catch {}
|
||||
return basename(dir);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region src/hooks/subagent-stop.ts
|
||||
function isSdkChildContext(payload) {
|
||||
if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true;
|
||||
if (!payload || typeof payload !== "object") return false;
|
||||
return payload.entrypoint === "sdk-ts";
|
||||
}
|
||||
const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
|
||||
const SECRET = process.env["AGENTMEMORY_SECRET"] || "";
|
||||
function authHeaders() {
|
||||
const h = { "Content-Type": "application/json" };
|
||||
if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
|
||||
return h;
|
||||
}
|
||||
async function main() {
|
||||
let input = "";
|
||||
for await (const chunk of process.stdin) input += chunk;
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(input);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
if (isSdkChildContext(data)) return;
|
||||
const sessionId = data.session_id || data.sessionId || "unknown";
|
||||
const agentId = data.agent_id || data.agentName;
|
||||
const agentType = data.agent_type || data.agentDisplayName || data.agentName;
|
||||
const lastMsg = typeof data.last_assistant_message === "string" ? data.last_assistant_message.slice(0, 4e3) : "";
|
||||
fetch(`${REST_URL}/agentmemory/observe`, {
|
||||
method: "POST",
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({
|
||||
hookType: "subagent_stop",
|
||||
sessionId,
|
||||
project: resolveProject(data.cwd),
|
||||
cwd: data.cwd || process.cwd(),
|
||||
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
||||
data: {
|
||||
agent_id: agentId,
|
||||
agent_type: agentType,
|
||||
last_message: lastMsg
|
||||
}
|
||||
}),
|
||||
signal: AbortSignal.timeout(2e3)
|
||||
}).catch(() => {});
|
||||
setTimeout(() => process.exit(0), 500).unref();
|
||||
}
|
||||
main();
|
||||
|
||||
//#endregion
|
||||
export { };
|
||||
//# sourceMappingURL=subagent-stop.mjs.map
|
||||
Executable
+75
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env node
|
||||
import { execSync } from "node:child_process";
|
||||
import { basename } from "node:path";
|
||||
|
||||
//#region src/hooks/_project.ts
|
||||
function resolveProject(cwd) {
|
||||
const explicit = process.env["AGENTMEMORY_PROJECT_NAME"];
|
||||
if (explicit && explicit.trim()) return explicit.trim();
|
||||
const dir = cwd && cwd.trim() ? cwd : process.cwd();
|
||||
try {
|
||||
const top = execSync("git rev-parse --show-toplevel", {
|
||||
cwd: dir,
|
||||
stdio: [
|
||||
"ignore",
|
||||
"pipe",
|
||||
"ignore"
|
||||
],
|
||||
timeout: 500
|
||||
}).toString().trim();
|
||||
if (top) return basename(top);
|
||||
} catch {}
|
||||
return basename(dir);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region src/hooks/task-completed.ts
|
||||
function isSdkChildContext(payload) {
|
||||
if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true;
|
||||
if (!payload || typeof payload !== "object") return false;
|
||||
return payload.entrypoint === "sdk-ts";
|
||||
}
|
||||
const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
|
||||
const SECRET = process.env["AGENTMEMORY_SECRET"] || "";
|
||||
function authHeaders() {
|
||||
const h = { "Content-Type": "application/json" };
|
||||
if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
|
||||
return h;
|
||||
}
|
||||
async function main() {
|
||||
let input = "";
|
||||
for await (const chunk of process.stdin) input += chunk;
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(input);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
if (isSdkChildContext(data)) return;
|
||||
const sessionId = data.session_id || "unknown";
|
||||
fetch(`${REST_URL}/agentmemory/observe`, {
|
||||
method: "POST",
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({
|
||||
hookType: "task_completed",
|
||||
sessionId,
|
||||
project: resolveProject(data.cwd),
|
||||
cwd: data.cwd || process.cwd(),
|
||||
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
||||
data: {
|
||||
task_id: data.task_id,
|
||||
task_subject: data.task_subject,
|
||||
task_description: typeof data.task_description === "string" ? data.task_description.slice(0, 2e3) : "",
|
||||
teammate_name: data.teammate_name,
|
||||
team_name: data.team_name
|
||||
}
|
||||
}),
|
||||
signal: AbortSignal.timeout(2e3)
|
||||
}).catch(() => {});
|
||||
setTimeout(() => process.exit(0), 500).unref();
|
||||
}
|
||||
main();
|
||||
|
||||
//#endregion
|
||||
export { };
|
||||
//# sourceMappingURL=task-completed.mjs.map
|
||||
Reference in New Issue
Block a user