@vectorize-io/self-driving-agents 0.0.23 → 0.0.26
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 +486 -87
- package/dist/cli.d.ts +9 -1
- package/dist/cli.js +110 -35
- package/dist/skill/SKILL.md +3 -1
- package/dist/tests/cli.test.js +88 -0
- package/dist/tests/lint.test.d.ts +1 -0
- package/dist/tests/lint.test.js +85 -0
- package/hermes-plugin/__init__.py +470 -0
- package/hermes-plugin/plugin.yaml +7 -0
- package/package.json +4 -2
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
"""Self-driving agents Hindsight plugin for Hermes.
|
|
2
|
+
|
|
3
|
+
Registers agent_knowledge_* tools as a regular plugin (not a memory provider),
|
|
4
|
+
so it coexists with the bundled hindsight memory provider or any other provider.
|
|
5
|
+
|
|
6
|
+
Config read from the active Hermes profile's hindsight/config.json (the same file
|
|
7
|
+
the bundled hindsight provider uses), so both share one bank, API URL, and key:
|
|
8
|
+
{ "api_url": "...", "api_key": "...", "bank_id": "..." }
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import glob
|
|
14
|
+
import json
|
|
15
|
+
import logging
|
|
16
|
+
import os
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
import httpx
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
PAGE_DEFAULTS = {
|
|
25
|
+
"mode": "delta",
|
|
26
|
+
"refresh_after_consolidation": True,
|
|
27
|
+
"exclude_mental_models": True,
|
|
28
|
+
"fact_types": ["observation"],
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _get_hermes_home() -> Path:
|
|
33
|
+
"""Resolve the active Hermes home, profile-isolation aware.
|
|
34
|
+
|
|
35
|
+
Prefer Hermes's own resolver: it honors the in-process profile override and
|
|
36
|
+
the active-profile marker, not just the ``HERMES_HOME`` env var. This is what
|
|
37
|
+
makes us read the *same* config.json the bundled hindsight provider reads —
|
|
38
|
+
if we only checked the env var (which is often unset in the agent worker) we
|
|
39
|
+
could silently fall back to the global ~/.hermes config and write to the
|
|
40
|
+
wrong bank. Fall back to the env var, then the default, if Hermes isn't importable.
|
|
41
|
+
"""
|
|
42
|
+
try:
|
|
43
|
+
from hermes_constants import get_hermes_home # type: ignore
|
|
44
|
+
return Path(get_hermes_home())
|
|
45
|
+
except Exception:
|
|
46
|
+
env = os.environ.get("HERMES_HOME")
|
|
47
|
+
if env:
|
|
48
|
+
return Path(env)
|
|
49
|
+
return Path.home() / ".hermes"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _load_config() -> dict | None:
|
|
53
|
+
"""Load Hindsight config from the active Hermes profile.
|
|
54
|
+
|
|
55
|
+
Reads the same config.json the bundled hindsight provider uses,
|
|
56
|
+
so both share the same bank, API URL, and credentials.
|
|
57
|
+
"""
|
|
58
|
+
cfg_path = _get_hermes_home() / "hindsight" / "config.json"
|
|
59
|
+
if not cfg_path.exists():
|
|
60
|
+
return None
|
|
61
|
+
try:
|
|
62
|
+
cfg = json.loads(cfg_path.read_text())
|
|
63
|
+
# Normalize field names (bundled provider uses api_url/api_key,
|
|
64
|
+
# we expose as api_url/api_token for consistency with other harnesses)
|
|
65
|
+
return {
|
|
66
|
+
"api_url": cfg.get("api_url", ""),
|
|
67
|
+
"api_token": cfg.get("api_key", ""),
|
|
68
|
+
"bank_id": cfg.get("bank_id", "hermes"),
|
|
69
|
+
}
|
|
70
|
+
except Exception:
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _api(
|
|
75
|
+
api_url: str,
|
|
76
|
+
path: str,
|
|
77
|
+
method: str = "GET",
|
|
78
|
+
body: dict | None = None,
|
|
79
|
+
token: str | None = None,
|
|
80
|
+
timeout: float = 30.0,
|
|
81
|
+
) -> Any:
|
|
82
|
+
headers: dict[str, str] = {"Content-Type": "application/json"}
|
|
83
|
+
if token:
|
|
84
|
+
headers["Authorization"] = f"Bearer {token}"
|
|
85
|
+
resp = httpx.request(
|
|
86
|
+
method,
|
|
87
|
+
f"{api_url}{path}",
|
|
88
|
+
json=body,
|
|
89
|
+
headers=headers,
|
|
90
|
+
timeout=timeout,
|
|
91
|
+
)
|
|
92
|
+
resp.raise_for_status()
|
|
93
|
+
return resp.json() if resp.content else {}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _is_available() -> bool:
|
|
97
|
+
return _load_config() is not None
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# ── Tool handlers ───────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _handle_list_pages(args: dict, **kwargs: Any) -> str:
|
|
104
|
+
config = _load_config()
|
|
105
|
+
if not config:
|
|
106
|
+
return json.dumps({"error": "Plugin not configured"})
|
|
107
|
+
api_url = config["api_url"].rstrip("/")
|
|
108
|
+
token = config.get("api_token")
|
|
109
|
+
bank_id = config["bank_id"]
|
|
110
|
+
try:
|
|
111
|
+
result = _api(api_url, f"/v1/default/banks/{bank_id}/mental-models?detail=metadata", "GET", token=token)
|
|
112
|
+
return json.dumps(result, indent=2)
|
|
113
|
+
except Exception as e:
|
|
114
|
+
return json.dumps({"error": str(e)})
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _handle_get_page(args: dict, **kwargs: Any) -> str:
|
|
118
|
+
config = _load_config()
|
|
119
|
+
if not config:
|
|
120
|
+
return json.dumps({"error": "Plugin not configured"})
|
|
121
|
+
api_url = config["api_url"].rstrip("/")
|
|
122
|
+
token = config.get("api_token")
|
|
123
|
+
bank_id = config["bank_id"]
|
|
124
|
+
try:
|
|
125
|
+
result = _api(api_url, f"/v1/default/banks/{bank_id}/mental-models/{args['page_id']}", "GET", token=token)
|
|
126
|
+
return json.dumps(result, indent=2)
|
|
127
|
+
except Exception as e:
|
|
128
|
+
return json.dumps({"error": str(e)})
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _handle_create_page(args: dict, **kwargs: Any) -> str:
|
|
132
|
+
config = _load_config()
|
|
133
|
+
if not config:
|
|
134
|
+
return json.dumps({"error": "Plugin not configured"})
|
|
135
|
+
api_url = config["api_url"].rstrip("/")
|
|
136
|
+
token = config.get("api_token")
|
|
137
|
+
bank_id = config["bank_id"]
|
|
138
|
+
try:
|
|
139
|
+
result = _api(
|
|
140
|
+
api_url,
|
|
141
|
+
f"/v1/default/banks/{bank_id}/mental-models",
|
|
142
|
+
"POST",
|
|
143
|
+
body={
|
|
144
|
+
"id": args["page_id"],
|
|
145
|
+
"name": args["name"],
|
|
146
|
+
"source_query": args["source_query"],
|
|
147
|
+
"max_tokens": 4096,
|
|
148
|
+
"trigger": PAGE_DEFAULTS,
|
|
149
|
+
},
|
|
150
|
+
token=token,
|
|
151
|
+
)
|
|
152
|
+
return json.dumps(result, indent=2)
|
|
153
|
+
except Exception as e:
|
|
154
|
+
return json.dumps({"error": str(e)})
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _handle_update_page(args: dict, **kwargs: Any) -> str:
|
|
158
|
+
config = _load_config()
|
|
159
|
+
if not config:
|
|
160
|
+
return json.dumps({"error": "Plugin not configured"})
|
|
161
|
+
api_url = config["api_url"].rstrip("/")
|
|
162
|
+
token = config.get("api_token")
|
|
163
|
+
bank_id = config["bank_id"]
|
|
164
|
+
try:
|
|
165
|
+
body: dict[str, str] = {}
|
|
166
|
+
if args.get("name"):
|
|
167
|
+
body["name"] = args["name"]
|
|
168
|
+
if args.get("source_query"):
|
|
169
|
+
body["source_query"] = args["source_query"]
|
|
170
|
+
result = _api(api_url, f"/v1/default/banks/{bank_id}/mental-models/{args['page_id']}", "PATCH", body=body, token=token)
|
|
171
|
+
return json.dumps(result, indent=2)
|
|
172
|
+
except Exception as e:
|
|
173
|
+
return json.dumps({"error": str(e)})
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _handle_delete_page(args: dict, **kwargs: Any) -> str:
|
|
177
|
+
config = _load_config()
|
|
178
|
+
if not config:
|
|
179
|
+
return json.dumps({"error": "Plugin not configured"})
|
|
180
|
+
api_url = config["api_url"].rstrip("/")
|
|
181
|
+
token = config.get("api_token")
|
|
182
|
+
bank_id = config["bank_id"]
|
|
183
|
+
try:
|
|
184
|
+
_api(api_url, f"/v1/default/banks/{bank_id}/mental-models/{args['page_id']}", "DELETE", token=token)
|
|
185
|
+
return json.dumps({"success": True})
|
|
186
|
+
except Exception as e:
|
|
187
|
+
return json.dumps({"error": str(e)})
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _handle_recall(args: dict, **kwargs: Any) -> str:
|
|
191
|
+
config = _load_config()
|
|
192
|
+
if not config:
|
|
193
|
+
return json.dumps({"error": "Plugin not configured"})
|
|
194
|
+
api_url = config["api_url"].rstrip("/")
|
|
195
|
+
token = config.get("api_token")
|
|
196
|
+
bank_id = config["bank_id"]
|
|
197
|
+
try:
|
|
198
|
+
result = _api(
|
|
199
|
+
api_url,
|
|
200
|
+
f"/v1/default/banks/{bank_id}/memories/recall",
|
|
201
|
+
"POST",
|
|
202
|
+
body={"query": args["query"], "max_results": args.get("max_results", 10)},
|
|
203
|
+
token=token,
|
|
204
|
+
)
|
|
205
|
+
return json.dumps(result, indent=2)
|
|
206
|
+
except Exception as e:
|
|
207
|
+
return json.dumps({"error": str(e)})
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _handle_ingest(args: dict, **kwargs: Any) -> str:
|
|
211
|
+
config = _load_config()
|
|
212
|
+
if not config:
|
|
213
|
+
return json.dumps({"error": "Plugin not configured"})
|
|
214
|
+
api_url = config["api_url"].rstrip("/")
|
|
215
|
+
token = config.get("api_token")
|
|
216
|
+
bank_id = config["bank_id"]
|
|
217
|
+
try:
|
|
218
|
+
doc_id = args["title"].lower().replace(" ", "-")
|
|
219
|
+
result = _api(
|
|
220
|
+
api_url,
|
|
221
|
+
f"/v1/default/banks/{bank_id}/memories",
|
|
222
|
+
"POST",
|
|
223
|
+
body={"items": [{"content": args["content"], "document_id": doc_id}], "async": True},
|
|
224
|
+
token=token,
|
|
225
|
+
)
|
|
226
|
+
return json.dumps(result, indent=2)
|
|
227
|
+
except Exception as e:
|
|
228
|
+
return json.dumps({"error": str(e)})
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _doc_id_for(path: Path) -> str:
|
|
232
|
+
"""Stable, collision-resistant document id from a file path."""
|
|
233
|
+
try:
|
|
234
|
+
rel = path.resolve().relative_to(Path.cwd().resolve())
|
|
235
|
+
except ValueError:
|
|
236
|
+
rel = Path(path.name)
|
|
237
|
+
return str(rel).lower().replace(os.sep, "-").replace(" ", "-").lstrip("-")
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def _handle_ingest_files(args: dict, **kwargs: Any) -> str:
|
|
241
|
+
config = _load_config()
|
|
242
|
+
if not config:
|
|
243
|
+
return json.dumps({"error": "Plugin not configured"})
|
|
244
|
+
api_url = config["api_url"].rstrip("/")
|
|
245
|
+
token = config.get("api_token")
|
|
246
|
+
bank_id = config["bank_id"]
|
|
247
|
+
|
|
248
|
+
patterns = args.get("paths") or []
|
|
249
|
+
if isinstance(patterns, str):
|
|
250
|
+
patterns = [patterns]
|
|
251
|
+
if not patterns:
|
|
252
|
+
return json.dumps({"error": "No paths provided"})
|
|
253
|
+
|
|
254
|
+
# Expand globs (and literal paths), dedupe, keep deterministic order
|
|
255
|
+
resolved: list[Path] = []
|
|
256
|
+
seen: set[str] = set()
|
|
257
|
+
for pat in patterns:
|
|
258
|
+
expanded = os.path.expanduser(pat)
|
|
259
|
+
matches = glob.glob(expanded, recursive=True)
|
|
260
|
+
if not matches and os.path.exists(expanded):
|
|
261
|
+
matches = [expanded]
|
|
262
|
+
for m in sorted(matches):
|
|
263
|
+
p = Path(m)
|
|
264
|
+
key = str(p.resolve())
|
|
265
|
+
if p.is_file() and key not in seen:
|
|
266
|
+
seen.add(key)
|
|
267
|
+
resolved.append(p)
|
|
268
|
+
|
|
269
|
+
if not resolved:
|
|
270
|
+
return json.dumps({"error": "No files matched", "patterns": patterns})
|
|
271
|
+
|
|
272
|
+
items: list[dict] = []
|
|
273
|
+
errors: list[dict] = []
|
|
274
|
+
for p in resolved:
|
|
275
|
+
try:
|
|
276
|
+
content = p.read_text(encoding="utf-8", errors="replace")
|
|
277
|
+
except Exception as e:
|
|
278
|
+
errors.append({"path": str(p), "error": str(e)})
|
|
279
|
+
continue
|
|
280
|
+
if not content.strip():
|
|
281
|
+
errors.append({"path": str(p), "error": "empty file, skipped"})
|
|
282
|
+
continue
|
|
283
|
+
items.append({"content": content, "document_id": _doc_id_for(p)})
|
|
284
|
+
|
|
285
|
+
if not items:
|
|
286
|
+
return json.dumps({"error": "No readable, non-empty files", "errors": errors})
|
|
287
|
+
|
|
288
|
+
try:
|
|
289
|
+
result = _api(
|
|
290
|
+
api_url,
|
|
291
|
+
f"/v1/default/banks/{bank_id}/memories",
|
|
292
|
+
"POST",
|
|
293
|
+
body={"items": items, "async": True},
|
|
294
|
+
token=token,
|
|
295
|
+
)
|
|
296
|
+
return json.dumps(
|
|
297
|
+
{"ingested": len(items), "document_ids": [it["document_id"] for it in items],
|
|
298
|
+
"errors": errors, "result": result},
|
|
299
|
+
indent=2,
|
|
300
|
+
)
|
|
301
|
+
except Exception as e:
|
|
302
|
+
return json.dumps({"error": str(e), "errors": errors})
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
# ── Tool schemas ────────────────────────────────────────
|
|
306
|
+
|
|
307
|
+
_TOOLS = [
|
|
308
|
+
(
|
|
309
|
+
"agent_knowledge_list_pages",
|
|
310
|
+
{
|
|
311
|
+
"name": "agent_knowledge_list_pages",
|
|
312
|
+
"description": "List all your knowledge pages (IDs and names only). Use agent_knowledge_get_page to read the full content of a specific page.",
|
|
313
|
+
"parameters": {"type": "object", "properties": {}},
|
|
314
|
+
},
|
|
315
|
+
_handle_list_pages,
|
|
316
|
+
"📚",
|
|
317
|
+
),
|
|
318
|
+
(
|
|
319
|
+
"agent_knowledge_get_page",
|
|
320
|
+
{
|
|
321
|
+
"name": "agent_knowledge_get_page",
|
|
322
|
+
"description": "Read a specific knowledge page by its ID. Returns the full synthesized content.",
|
|
323
|
+
"parameters": {
|
|
324
|
+
"type": "object",
|
|
325
|
+
"properties": {
|
|
326
|
+
"page_id": {"type": "string", "description": "Page ID (e.g. 'user-preferences')"},
|
|
327
|
+
},
|
|
328
|
+
"required": ["page_id"],
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
_handle_get_page,
|
|
332
|
+
"📖",
|
|
333
|
+
),
|
|
334
|
+
(
|
|
335
|
+
"agent_knowledge_create_page",
|
|
336
|
+
{
|
|
337
|
+
"name": "agent_knowledge_create_page",
|
|
338
|
+
"description": (
|
|
339
|
+
"Create a new knowledge page. The source_query is a question the system "
|
|
340
|
+
"re-asks after each consolidation to rebuild the page from conversation observations."
|
|
341
|
+
),
|
|
342
|
+
"parameters": {
|
|
343
|
+
"type": "object",
|
|
344
|
+
"properties": {
|
|
345
|
+
"page_id": {"type": "string", "description": "Unique page ID, lowercase with hyphens"},
|
|
346
|
+
"name": {"type": "string", "description": "Human-readable page name"},
|
|
347
|
+
"source_query": {"type": "string", "description": "The question that rebuilds this page"},
|
|
348
|
+
},
|
|
349
|
+
"required": ["page_id", "name", "source_query"],
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
_handle_create_page,
|
|
353
|
+
"📝",
|
|
354
|
+
),
|
|
355
|
+
(
|
|
356
|
+
"agent_knowledge_update_page",
|
|
357
|
+
{
|
|
358
|
+
"name": "agent_knowledge_update_page",
|
|
359
|
+
"description": "Update a page's name or source query. Content re-synthesizes on next consolidation.",
|
|
360
|
+
"parameters": {
|
|
361
|
+
"type": "object",
|
|
362
|
+
"properties": {
|
|
363
|
+
"page_id": {"type": "string", "description": "Page ID to update"},
|
|
364
|
+
"name": {"type": "string", "description": "New name (optional)"},
|
|
365
|
+
"source_query": {"type": "string", "description": "New source query (optional)"},
|
|
366
|
+
},
|
|
367
|
+
"required": ["page_id"],
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
_handle_update_page,
|
|
371
|
+
"✏️",
|
|
372
|
+
),
|
|
373
|
+
(
|
|
374
|
+
"agent_knowledge_delete_page",
|
|
375
|
+
{
|
|
376
|
+
"name": "agent_knowledge_delete_page",
|
|
377
|
+
"description": "Permanently delete a knowledge page.",
|
|
378
|
+
"parameters": {
|
|
379
|
+
"type": "object",
|
|
380
|
+
"properties": {
|
|
381
|
+
"page_id": {"type": "string", "description": "Page ID to delete"},
|
|
382
|
+
},
|
|
383
|
+
"required": ["page_id"],
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
_handle_delete_page,
|
|
387
|
+
"🗑️",
|
|
388
|
+
),
|
|
389
|
+
(
|
|
390
|
+
"agent_knowledge_recall",
|
|
391
|
+
{
|
|
392
|
+
"name": "agent_knowledge_recall",
|
|
393
|
+
"description": "Search across all retained conversations and documents for specific facts, numbers, or details not covered by your knowledge pages.",
|
|
394
|
+
"parameters": {
|
|
395
|
+
"type": "object",
|
|
396
|
+
"properties": {
|
|
397
|
+
"query": {"type": "string", "description": "What to search for"},
|
|
398
|
+
"max_results": {"type": "number", "description": "Max results (default 10)"},
|
|
399
|
+
},
|
|
400
|
+
"required": ["query"],
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
_handle_recall,
|
|
404
|
+
"🔍",
|
|
405
|
+
),
|
|
406
|
+
(
|
|
407
|
+
"agent_knowledge_ingest",
|
|
408
|
+
{
|
|
409
|
+
"name": "agent_knowledge_ingest",
|
|
410
|
+
"description": (
|
|
411
|
+
"Upload a document into your memory bank. Pass the full raw content — "
|
|
412
|
+
"never summarize before ingesting. The title becomes the document ID."
|
|
413
|
+
),
|
|
414
|
+
"parameters": {
|
|
415
|
+
"type": "object",
|
|
416
|
+
"properties": {
|
|
417
|
+
"title": {"type": "string", "description": "Document title (becomes the document ID)"},
|
|
418
|
+
"content": {"type": "string", "description": "Full raw document content"},
|
|
419
|
+
},
|
|
420
|
+
"required": ["title", "content"],
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
_handle_ingest,
|
|
424
|
+
"📥",
|
|
425
|
+
),
|
|
426
|
+
(
|
|
427
|
+
"agent_knowledge_ingest_files",
|
|
428
|
+
{
|
|
429
|
+
"name": "agent_knowledge_ingest_files",
|
|
430
|
+
"description": (
|
|
431
|
+
"Ingest one or more files from disk into your memory bank by path. "
|
|
432
|
+
"Accepts explicit file paths or glob patterns (e.g. 'docs/**/*.md'). "
|
|
433
|
+
"Reads each file's full raw content — never summarizes. Each file's path "
|
|
434
|
+
"becomes its document ID. Prefer absolute paths; relative paths resolve "
|
|
435
|
+
"against the agent's working directory. Use this instead of "
|
|
436
|
+
"agent_knowledge_ingest when the content already lives in files."
|
|
437
|
+
),
|
|
438
|
+
"parameters": {
|
|
439
|
+
"type": "object",
|
|
440
|
+
"properties": {
|
|
441
|
+
"paths": {
|
|
442
|
+
"type": "array",
|
|
443
|
+
"items": {"type": "string"},
|
|
444
|
+
"description": "File paths or glob patterns to ingest",
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
"required": ["paths"],
|
|
448
|
+
},
|
|
449
|
+
},
|
|
450
|
+
_handle_ingest_files,
|
|
451
|
+
"📂",
|
|
452
|
+
),
|
|
453
|
+
]
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
# ── Registration ────────────────────────────────────────
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
def register(ctx: Any) -> None:
|
|
460
|
+
"""Register agent_knowledge_* tools. Called once by the Hermes plugin loader."""
|
|
461
|
+
for name, schema, handler, emoji in _TOOLS:
|
|
462
|
+
ctx.register_tool(
|
|
463
|
+
name=name,
|
|
464
|
+
toolset="hindsight-sda",
|
|
465
|
+
schema=schema,
|
|
466
|
+
handler=handler,
|
|
467
|
+
check_fn=_is_available,
|
|
468
|
+
emoji=emoji,
|
|
469
|
+
)
|
|
470
|
+
logger.info("[hindsight-sda] registered %d tools", len(_TOOLS))
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
name: hindsight-sda
|
|
2
|
+
version: 0.2.0
|
|
3
|
+
description: "Self-driving agents knowledge tools powered by Hindsight. Provides agent_knowledge_* tools for managing knowledge pages, recall, and ingestion (raw content or files)."
|
|
4
|
+
kind: standalone
|
|
5
|
+
pip_dependencies:
|
|
6
|
+
- "httpx>=0.27"
|
|
7
|
+
requires_env: []
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vectorize-io/self-driving-agents",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.26",
|
|
4
4
|
"description": "Install self-driving agents with portable memory on any harness",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cli.js",
|
|
@@ -8,11 +8,13 @@
|
|
|
8
8
|
"self-driving-agents": "dist/cli.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
|
-
"dist"
|
|
11
|
+
"dist",
|
|
12
|
+
"hermes-plugin"
|
|
12
13
|
],
|
|
13
14
|
"scripts": {
|
|
14
15
|
"build": "tsc && cp -r src/skill dist/",
|
|
15
16
|
"test": "vitest run src/tests",
|
|
17
|
+
"lint": "node src/scripts/lint-bank-templates.mjs .",
|
|
16
18
|
"prepublishOnly": "npm run build"
|
|
17
19
|
},
|
|
18
20
|
"keywords": [
|