hyperstack-core 1.3.0 → 1.5.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/LICENSE +21 -21
- package/README.md +247 -63
- package/SKILL.md +679 -40
- package/adapters/openclaw.js +221 -221
- package/cli.js +631 -500
- package/examples/before-after.js +110 -110
- package/examples/openclaw-multiagent.js +214 -214
- package/index.js +19 -18
- package/package.json +2 -1
- package/src/client.js +320 -267
- package/src/parser.js +305 -0
- package/templates/openclaw-multiagent.json +98 -98
package/adapters/openclaw.js
CHANGED
|
@@ -1,221 +1,221 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* hyperstack-core/adapters/openclaw.js
|
|
3
|
-
*
|
|
4
|
-
* OpenClaw adapter for HyperStack.
|
|
5
|
-
* Replaces GOALS.md / DECISIONS.md with typed graph memory.
|
|
6
|
-
*
|
|
7
|
-
* Usage in OpenClaw config (openclaw.json):
|
|
8
|
-
* {
|
|
9
|
-
* "skills": ["hyperstack-core/adapters/openclaw"],
|
|
10
|
-
* "env": {
|
|
11
|
-
* "HYPERSTACK_API_KEY": "hs_your_key"
|
|
12
|
-
* }
|
|
13
|
-
* }
|
|
14
|
-
*
|
|
15
|
-
* Or use programmatically:
|
|
16
|
-
* import { createOpenClawAdapter } from "hyperstack-core/adapters/openclaw";
|
|
17
|
-
* const adapter = createOpenClawAdapter({ agentId: "researcher" });
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import { HyperStackClient } from "../src/client.js";
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Create an OpenClaw-compatible adapter that provides tools
|
|
24
|
-
* for multi-agent coordination via HyperStack.
|
|
25
|
-
*
|
|
26
|
-
* @param {object} opts
|
|
27
|
-
* @param {string} opts.agentId — this agent's unique ID
|
|
28
|
-
* @param {string} [opts.apiKey] — HyperStack API key
|
|
29
|
-
* @param {string} [opts.workspace] — workspace slug
|
|
30
|
-
*/
|
|
31
|
-
function createOpenClawAdapter(opts = {}) {
|
|
32
|
-
const agentId = opts.agentId || process.env.OPENCLAW_AGENT_ID || "main";
|
|
33
|
-
|
|
34
|
-
const client = new HyperStackClient({
|
|
35
|
-
apiKey: opts.apiKey,
|
|
36
|
-
workspace: opts.workspace,
|
|
37
|
-
agentId,
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
return {
|
|
41
|
-
client,
|
|
42
|
-
agentId,
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Tools that get exposed to the OpenClaw agent.
|
|
46
|
-
* These can be called via OpenClaw's tool system.
|
|
47
|
-
*/
|
|
48
|
-
tools: {
|
|
49
|
-
/**
|
|
50
|
-
* Search the shared knowledge graph.
|
|
51
|
-
*/
|
|
52
|
-
async hs_search({ query }) {
|
|
53
|
-
const result = await client.search(query);
|
|
54
|
-
const cards = result.results || [];
|
|
55
|
-
if (!cards.length) return { text: "No matching cards found." };
|
|
56
|
-
|
|
57
|
-
return {
|
|
58
|
-
text: cards.slice(0, 5).map(c => {
|
|
59
|
-
let line = `[${c.slug}] ${c.title} (${c.cardType || "general"})`;
|
|
60
|
-
if (c.body) line += `\n ${c.body.slice(0, 200)}`;
|
|
61
|
-
if (c.links?.length) {
|
|
62
|
-
line += `\n Links: ${c.links.map(l => `${l.relation}→${l.target}`).join(", ")}`;
|
|
63
|
-
}
|
|
64
|
-
return line;
|
|
65
|
-
}).join("\n\n"),
|
|
66
|
-
cards,
|
|
67
|
-
};
|
|
68
|
-
},
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Store a card in the shared graph. Auto-tags with agent ID.
|
|
72
|
-
*/
|
|
73
|
-
async hs_store({ slug, title, body, type, links, keywords }) {
|
|
74
|
-
const parsedLinks = [];
|
|
75
|
-
if (links) {
|
|
76
|
-
// Accept "target:relation,target:relation" format
|
|
77
|
-
for (const l of (typeof links === "string" ? links.split(",") : links)) {
|
|
78
|
-
if (typeof l === "string") {
|
|
79
|
-
const [target, relation] = l.trim().split(":");
|
|
80
|
-
parsedLinks.push({ target: target.trim(), relation: (relation || "related").trim() });
|
|
81
|
-
} else {
|
|
82
|
-
parsedLinks.push(l);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const result = await client.store({
|
|
88
|
-
slug,
|
|
89
|
-
title,
|
|
90
|
-
body: body || "",
|
|
91
|
-
cardType: type || "general",
|
|
92
|
-
keywords: typeof keywords === "string" ? keywords.split(",").map(k => k.trim()) : (keywords || []),
|
|
93
|
-
links: parsedLinks,
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
return {
|
|
97
|
-
text: `${result.updated ? "Updated" : "Created"} [${slug}]: ${title}`,
|
|
98
|
-
result,
|
|
99
|
-
};
|
|
100
|
-
},
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Record a decision with full provenance.
|
|
104
|
-
*/
|
|
105
|
-
async hs_decide({ slug, title, rationale, affects, blocks }) {
|
|
106
|
-
const result = await client.decide({
|
|
107
|
-
slug,
|
|
108
|
-
title,
|
|
109
|
-
body: rationale,
|
|
110
|
-
decidedBy: `agent-${agentId}`,
|
|
111
|
-
affects: affects ? (typeof affects === "string" ? affects.split(",").map(s => s.trim()) : affects) : [],
|
|
112
|
-
blocks: blocks ? (typeof blocks === "string" ? blocks.split(",").map(s => s.trim()) : blocks) : [],
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
return {
|
|
116
|
-
text: `Decision recorded: [${slug}] ${title} (by ${agentId})`,
|
|
117
|
-
result,
|
|
118
|
-
};
|
|
119
|
-
},
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Check what blocks a task/card.
|
|
123
|
-
*/
|
|
124
|
-
async hs_blockers({ slug }) {
|
|
125
|
-
const result = await client.blockers(slug);
|
|
126
|
-
const blockers = result.blockers || [];
|
|
127
|
-
|
|
128
|
-
if (!blockers.length) {
|
|
129
|
-
return { text: `Nothing blocks [${slug}].`, blockers: [] };
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return {
|
|
133
|
-
text: `${blockers.length} blocker(s) for [${slug}]:\n` +
|
|
134
|
-
blockers.map(b => ` [${b.slug}] ${b.title || "?"}`).join("\n"),
|
|
135
|
-
blockers,
|
|
136
|
-
};
|
|
137
|
-
},
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Traverse the graph from a card.
|
|
141
|
-
*/
|
|
142
|
-
async hs_graph({ from, depth, relation }) {
|
|
143
|
-
const result = await client.graph(from, {
|
|
144
|
-
depth: depth || 2,
|
|
145
|
-
relation: relation || undefined,
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
const nodes = result.nodes || [];
|
|
149
|
-
const edges = result.edges || [];
|
|
150
|
-
|
|
151
|
-
if (!nodes.length) {
|
|
152
|
-
return { text: `No graph found from [${from}].`, nodes: [], edges: [] };
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
let text = `Graph from [${from}]: ${nodes.length} nodes, ${edges.length} edges\n\n`;
|
|
156
|
-
text += "Nodes:\n" + nodes.map(n =>
|
|
157
|
-
` [${n.slug}] ${n.title || "?"} (${n.cardType || "?"})`
|
|
158
|
-
).join("\n");
|
|
159
|
-
text += "\n\nEdges:\n" + edges.map(e =>
|
|
160
|
-
` ${e.from} --${e.relation}--> ${e.to}`
|
|
161
|
-
).join("\n");
|
|
162
|
-
|
|
163
|
-
return { text, nodes, edges };
|
|
164
|
-
},
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* List all cards by this agent.
|
|
168
|
-
*/
|
|
169
|
-
async hs_my_cards() {
|
|
170
|
-
const result = await client.agentCards(agentId);
|
|
171
|
-
const cards = result.results || [];
|
|
172
|
-
|
|
173
|
-
return {
|
|
174
|
-
text: `${cards.length} cards by agent "${agentId}":\n` +
|
|
175
|
-
cards.map(c => ` [${c.slug}] ${c.title}`).join("\n"),
|
|
176
|
-
cards,
|
|
177
|
-
};
|
|
178
|
-
},
|
|
179
|
-
},
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Hook: called when agent session starts.
|
|
183
|
-
* Registers the agent and loads relevant context.
|
|
184
|
-
*/
|
|
185
|
-
async onSessionStart({ agentName, agentRole }) {
|
|
186
|
-
// Register this agent in the graph
|
|
187
|
-
await client.registerAgent({
|
|
188
|
-
id: agentId,
|
|
189
|
-
name: agentName || agentId,
|
|
190
|
-
role: agentRole || "OpenClaw agent",
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
// Load recent context for this agent
|
|
194
|
-
const context = await client.search(`agent:${agentId}`);
|
|
195
|
-
return {
|
|
196
|
-
cards: context.results || [],
|
|
197
|
-
text: `HyperStack: ${(context.results || []).length} cards loaded for agent "${agentId}"`,
|
|
198
|
-
};
|
|
199
|
-
},
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Hook: called when agent session ends.
|
|
203
|
-
* Good place to save working state.
|
|
204
|
-
*/
|
|
205
|
-
async onSessionEnd({ summary }) {
|
|
206
|
-
if (summary) {
|
|
207
|
-
await client.store({
|
|
208
|
-
slug: `session-${agentId}-${Date.now()}`,
|
|
209
|
-
title: `Session summary (${agentId})`,
|
|
210
|
-
body: summary.slice(0, 500),
|
|
211
|
-
cardType: "event",
|
|
212
|
-
stack: "general",
|
|
213
|
-
keywords: ["session", "summary"],
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
},
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
export { createOpenClawAdapter };
|
|
221
|
-
export default createOpenClawAdapter;
|
|
1
|
+
/**
|
|
2
|
+
* hyperstack-core/adapters/openclaw.js
|
|
3
|
+
*
|
|
4
|
+
* OpenClaw adapter for HyperStack.
|
|
5
|
+
* Replaces GOALS.md / DECISIONS.md with typed graph memory.
|
|
6
|
+
*
|
|
7
|
+
* Usage in OpenClaw config (openclaw.json):
|
|
8
|
+
* {
|
|
9
|
+
* "skills": ["hyperstack-core/adapters/openclaw"],
|
|
10
|
+
* "env": {
|
|
11
|
+
* "HYPERSTACK_API_KEY": "hs_your_key"
|
|
12
|
+
* }
|
|
13
|
+
* }
|
|
14
|
+
*
|
|
15
|
+
* Or use programmatically:
|
|
16
|
+
* import { createOpenClawAdapter } from "hyperstack-core/adapters/openclaw";
|
|
17
|
+
* const adapter = createOpenClawAdapter({ agentId: "researcher" });
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { HyperStackClient } from "../src/client.js";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Create an OpenClaw-compatible adapter that provides tools
|
|
24
|
+
* for multi-agent coordination via HyperStack.
|
|
25
|
+
*
|
|
26
|
+
* @param {object} opts
|
|
27
|
+
* @param {string} opts.agentId — this agent's unique ID
|
|
28
|
+
* @param {string} [opts.apiKey] — HyperStack API key
|
|
29
|
+
* @param {string} [opts.workspace] — workspace slug
|
|
30
|
+
*/
|
|
31
|
+
function createOpenClawAdapter(opts = {}) {
|
|
32
|
+
const agentId = opts.agentId || process.env.OPENCLAW_AGENT_ID || "main";
|
|
33
|
+
|
|
34
|
+
const client = new HyperStackClient({
|
|
35
|
+
apiKey: opts.apiKey,
|
|
36
|
+
workspace: opts.workspace,
|
|
37
|
+
agentId,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
client,
|
|
42
|
+
agentId,
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Tools that get exposed to the OpenClaw agent.
|
|
46
|
+
* These can be called via OpenClaw's tool system.
|
|
47
|
+
*/
|
|
48
|
+
tools: {
|
|
49
|
+
/**
|
|
50
|
+
* Search the shared knowledge graph.
|
|
51
|
+
*/
|
|
52
|
+
async hs_search({ query }) {
|
|
53
|
+
const result = await client.search(query);
|
|
54
|
+
const cards = result.results || [];
|
|
55
|
+
if (!cards.length) return { text: "No matching cards found." };
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
text: cards.slice(0, 5).map(c => {
|
|
59
|
+
let line = `[${c.slug}] ${c.title} (${c.cardType || "general"})`;
|
|
60
|
+
if (c.body) line += `\n ${c.body.slice(0, 200)}`;
|
|
61
|
+
if (c.links?.length) {
|
|
62
|
+
line += `\n Links: ${c.links.map(l => `${l.relation}→${l.target}`).join(", ")}`;
|
|
63
|
+
}
|
|
64
|
+
return line;
|
|
65
|
+
}).join("\n\n"),
|
|
66
|
+
cards,
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Store a card in the shared graph. Auto-tags with agent ID.
|
|
72
|
+
*/
|
|
73
|
+
async hs_store({ slug, title, body, type, links, keywords }) {
|
|
74
|
+
const parsedLinks = [];
|
|
75
|
+
if (links) {
|
|
76
|
+
// Accept "target:relation,target:relation" format
|
|
77
|
+
for (const l of (typeof links === "string" ? links.split(",") : links)) {
|
|
78
|
+
if (typeof l === "string") {
|
|
79
|
+
const [target, relation] = l.trim().split(":");
|
|
80
|
+
parsedLinks.push({ target: target.trim(), relation: (relation || "related").trim() });
|
|
81
|
+
} else {
|
|
82
|
+
parsedLinks.push(l);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const result = await client.store({
|
|
88
|
+
slug,
|
|
89
|
+
title,
|
|
90
|
+
body: body || "",
|
|
91
|
+
cardType: type || "general",
|
|
92
|
+
keywords: typeof keywords === "string" ? keywords.split(",").map(k => k.trim()) : (keywords || []),
|
|
93
|
+
links: parsedLinks,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
text: `${result.updated ? "Updated" : "Created"} [${slug}]: ${title}`,
|
|
98
|
+
result,
|
|
99
|
+
};
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Record a decision with full provenance.
|
|
104
|
+
*/
|
|
105
|
+
async hs_decide({ slug, title, rationale, affects, blocks }) {
|
|
106
|
+
const result = await client.decide({
|
|
107
|
+
slug,
|
|
108
|
+
title,
|
|
109
|
+
body: rationale,
|
|
110
|
+
decidedBy: `agent-${agentId}`,
|
|
111
|
+
affects: affects ? (typeof affects === "string" ? affects.split(",").map(s => s.trim()) : affects) : [],
|
|
112
|
+
blocks: blocks ? (typeof blocks === "string" ? blocks.split(",").map(s => s.trim()) : blocks) : [],
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
text: `Decision recorded: [${slug}] ${title} (by ${agentId})`,
|
|
117
|
+
result,
|
|
118
|
+
};
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Check what blocks a task/card.
|
|
123
|
+
*/
|
|
124
|
+
async hs_blockers({ slug }) {
|
|
125
|
+
const result = await client.blockers(slug);
|
|
126
|
+
const blockers = result.blockers || [];
|
|
127
|
+
|
|
128
|
+
if (!blockers.length) {
|
|
129
|
+
return { text: `Nothing blocks [${slug}].`, blockers: [] };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
text: `${blockers.length} blocker(s) for [${slug}]:\n` +
|
|
134
|
+
blockers.map(b => ` [${b.slug}] ${b.title || "?"}`).join("\n"),
|
|
135
|
+
blockers,
|
|
136
|
+
};
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Traverse the graph from a card.
|
|
141
|
+
*/
|
|
142
|
+
async hs_graph({ from, depth, relation }) {
|
|
143
|
+
const result = await client.graph(from, {
|
|
144
|
+
depth: depth || 2,
|
|
145
|
+
relation: relation || undefined,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const nodes = result.nodes || [];
|
|
149
|
+
const edges = result.edges || [];
|
|
150
|
+
|
|
151
|
+
if (!nodes.length) {
|
|
152
|
+
return { text: `No graph found from [${from}].`, nodes: [], edges: [] };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
let text = `Graph from [${from}]: ${nodes.length} nodes, ${edges.length} edges\n\n`;
|
|
156
|
+
text += "Nodes:\n" + nodes.map(n =>
|
|
157
|
+
` [${n.slug}] ${n.title || "?"} (${n.cardType || "?"})`
|
|
158
|
+
).join("\n");
|
|
159
|
+
text += "\n\nEdges:\n" + edges.map(e =>
|
|
160
|
+
` ${e.from} --${e.relation}--> ${e.to}`
|
|
161
|
+
).join("\n");
|
|
162
|
+
|
|
163
|
+
return { text, nodes, edges };
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* List all cards by this agent.
|
|
168
|
+
*/
|
|
169
|
+
async hs_my_cards() {
|
|
170
|
+
const result = await client.agentCards(agentId);
|
|
171
|
+
const cards = result.results || [];
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
text: `${cards.length} cards by agent "${agentId}":\n` +
|
|
175
|
+
cards.map(c => ` [${c.slug}] ${c.title}`).join("\n"),
|
|
176
|
+
cards,
|
|
177
|
+
};
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Hook: called when agent session starts.
|
|
183
|
+
* Registers the agent and loads relevant context.
|
|
184
|
+
*/
|
|
185
|
+
async onSessionStart({ agentName, agentRole }) {
|
|
186
|
+
// Register this agent in the graph
|
|
187
|
+
await client.registerAgent({
|
|
188
|
+
id: agentId,
|
|
189
|
+
name: agentName || agentId,
|
|
190
|
+
role: agentRole || "OpenClaw agent",
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// Load recent context for this agent
|
|
194
|
+
const context = await client.search(`agent:${agentId}`);
|
|
195
|
+
return {
|
|
196
|
+
cards: context.results || [],
|
|
197
|
+
text: `HyperStack: ${(context.results || []).length} cards loaded for agent "${agentId}"`,
|
|
198
|
+
};
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Hook: called when agent session ends.
|
|
203
|
+
* Good place to save working state.
|
|
204
|
+
*/
|
|
205
|
+
async onSessionEnd({ summary }) {
|
|
206
|
+
if (summary) {
|
|
207
|
+
await client.store({
|
|
208
|
+
slug: `session-${agentId}-${Date.now()}`,
|
|
209
|
+
title: `Session summary (${agentId})`,
|
|
210
|
+
body: summary.slice(0, 500),
|
|
211
|
+
cardType: "event",
|
|
212
|
+
stack: "general",
|
|
213
|
+
keywords: ["session", "summary"],
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export { createOpenClawAdapter };
|
|
221
|
+
export default createOpenClawAdapter;
|