mortgram-hook 1.0.1 → 1.0.3
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/mortgram-observer/handler.js +9 -35
- package/package.json +1 -1
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
// ═══════════════════════════════════════════════════════
|
|
2
2
|
// MORTGRAM Ghost Bridge — Native OpenClaw Hook
|
|
3
3
|
// ═══════════════════════════════════════════════════════
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// Environment Variables Required:
|
|
8
|
-
// MORTGRAM_API_KEY — Your MORTGRAM API key (from dashboard)
|
|
9
|
-
// MORTGRAM_API_URL — API endpoint (defaults to https://mortgram.com/api/ingest)
|
|
10
|
-
// MG_AGENT_ID — Agent identifier (defaults to "ghost-agent")
|
|
4
|
+
// Zero dependencies. Uses only fetch() (Node.js 18+).
|
|
5
|
+
// The MORTGRAM API handles Ably publishing server-side.
|
|
11
6
|
// ═══════════════════════════════════════════════════════
|
|
12
7
|
|
|
13
8
|
export const name = "mortgram-observer";
|
|
14
9
|
|
|
15
|
-
// Track session state for efficiency calculation
|
|
16
10
|
let totalTurns = 0;
|
|
17
11
|
let successfulTools = 0;
|
|
18
12
|
let latestThought = "Ghost Bridge Active";
|
|
@@ -23,9 +17,9 @@ const API_URL = process.env.MORTGRAM_API_URL || "https://mortgram.com/api/ingest
|
|
|
23
17
|
const AGENT_ID = process.env.MG_AGENT_ID || "ghost-agent";
|
|
24
18
|
|
|
25
19
|
// Silently POST to the MORTGRAM ingest endpoint
|
|
20
|
+
// The API publishes to Ably server-side, so no Ably client needed here.
|
|
26
21
|
async function send(type, content, metadata = {}) {
|
|
27
22
|
if (!API_KEY) return;
|
|
28
|
-
|
|
29
23
|
try {
|
|
30
24
|
await fetch(API_URL, {
|
|
31
25
|
method: "POST",
|
|
@@ -52,12 +46,9 @@ async function send(type, content, metadata = {}) {
|
|
|
52
46
|
}
|
|
53
47
|
}
|
|
54
48
|
|
|
55
|
-
// ───
|
|
56
|
-
// OpenClaw fires events for every lifecycle step.
|
|
57
|
-
// We intercept them invisibly and stream to MORTGRAM.
|
|
49
|
+
// ─── Main Event Handler ───
|
|
58
50
|
export async function onEvent({ event, data }) {
|
|
59
51
|
switch (event) {
|
|
60
|
-
// ── Agent Thought / Reasoning ──
|
|
61
52
|
case "agent:thought":
|
|
62
53
|
case "agent:reasoning":
|
|
63
54
|
totalTurns++;
|
|
@@ -65,39 +56,30 @@ export async function onEvent({ event, data }) {
|
|
|
65
56
|
await send("thought", latestThought, { event });
|
|
66
57
|
break;
|
|
67
58
|
|
|
68
|
-
// ── Tool Calls (Actions) ──
|
|
69
59
|
case "tool:call":
|
|
70
60
|
case "tool:result":
|
|
71
61
|
successfulTools++;
|
|
72
62
|
await send("action", data?.name || data?.tool || "Tool Call", {
|
|
73
|
-
event,
|
|
74
|
-
tool: data?.name,
|
|
75
|
-
args: data?.args
|
|
63
|
+
event, tool: data?.name, args: data?.args
|
|
76
64
|
});
|
|
77
65
|
break;
|
|
78
66
|
|
|
79
|
-
// ── Errors ──
|
|
80
67
|
case "agent:error":
|
|
81
68
|
case "tool:error":
|
|
82
69
|
if (successfulTools > 0) successfulTools--;
|
|
83
70
|
await send("error", data?.message || data?.text || "Error occurred", {
|
|
84
|
-
event,
|
|
85
|
-
stack: data?.stack
|
|
71
|
+
event, stack: data?.stack
|
|
86
72
|
});
|
|
87
73
|
break;
|
|
88
74
|
|
|
89
|
-
// ── Session Usage / Cost ──
|
|
90
75
|
case "session:usage":
|
|
91
76
|
case "session:cost":
|
|
92
77
|
dailyCost = data?.total_cost || data?.cost || dailyCost;
|
|
93
78
|
await send("cost_update", `Daily spend: $${dailyCost.toFixed(4)}`, {
|
|
94
|
-
event,
|
|
95
|
-
daily_cost: dailyCost,
|
|
96
|
-
tokens: data?.tokens
|
|
79
|
+
event, daily_cost: dailyCost, tokens: data?.tokens
|
|
97
80
|
});
|
|
98
81
|
break;
|
|
99
82
|
|
|
100
|
-
// ── Session Start ──
|
|
101
83
|
case "session:start":
|
|
102
84
|
case "agent:start":
|
|
103
85
|
totalTurns = 0;
|
|
@@ -105,31 +87,23 @@ export async function onEvent({ event, data }) {
|
|
|
105
87
|
dailyCost = 0;
|
|
106
88
|
latestThought = "Session started";
|
|
107
89
|
await send("system", "Ghost Bridge: Session started", {
|
|
108
|
-
event,
|
|
109
|
-
status: "online"
|
|
90
|
+
event, status: "online"
|
|
110
91
|
});
|
|
111
92
|
break;
|
|
112
93
|
|
|
113
|
-
// ── Session End ──
|
|
114
94
|
case "session:end":
|
|
115
95
|
case "agent:end":
|
|
116
96
|
await send("system", "Ghost Bridge: Session ended", {
|
|
117
|
-
event,
|
|
118
|
-
status: "offline",
|
|
97
|
+
event, status: "offline",
|
|
119
98
|
final_cost: dailyCost,
|
|
120
99
|
final_efficiency: totalTurns > 0 ? (successfulTools / totalTurns) : 0
|
|
121
100
|
});
|
|
122
101
|
break;
|
|
123
102
|
|
|
124
|
-
// ── Agent Response / Output ──
|
|
125
103
|
case "agent:response":
|
|
126
104
|
case "agent:message":
|
|
127
105
|
latestThought = data?.text?.slice(0, 200) || "Response generated";
|
|
128
106
|
await send("thought", latestThought, { event });
|
|
129
107
|
break;
|
|
130
|
-
|
|
131
|
-
default:
|
|
132
|
-
// Skip unrecognized events silently
|
|
133
|
-
break;
|
|
134
108
|
}
|
|
135
109
|
}
|