@gamaze/hicortex 0.18.3 → 0.19.1
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 +76 -2
- package/assets/dashboard.html +103 -7
- package/assets/identity.html +48 -1
- package/assets/viz.html +52 -1
- package/dist/backup.d.ts +107 -0
- package/dist/backup.js +343 -0
- package/dist/capture.d.ts +20 -0
- package/dist/capture.js +9 -1
- package/dist/cli.js +30 -0
- package/dist/config-read.d.ts +28 -2
- package/dist/config-read.js +32 -2
- package/dist/consolidate.js +2 -4
- package/dist/dashboard.d.ts +71 -6
- package/dist/dashboard.js +61 -7
- package/dist/index.d.ts +25 -1
- package/dist/index.js +108 -2
- package/dist/mcp-server.d.ts +18 -0
- package/dist/mcp-server.js +60 -2
- package/dist/memory-instructions.js +1 -1
- package/dist/nightly.js +104 -2
- package/dist/prompts.js +19 -4
- package/dist/telemetry.d.ts +14 -0
- package/dist/token-budget.js +10 -2
- package/dist/type-classify.js +7 -4
- package/dist/types.d.ts +40 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +6 -4
package/dist/nightly.js
CHANGED
|
@@ -75,6 +75,7 @@ const capture_js_1 = require("./capture.js");
|
|
|
75
75
|
const dashboard_js_1 = require("./dashboard.js");
|
|
76
76
|
const telemetry_js_1 = require("./telemetry.js");
|
|
77
77
|
const init_js_1 = require("./init.js");
|
|
78
|
+
const backup_js_1 = require("./backup.js");
|
|
78
79
|
const HICORTEX_HOME = (0, paths_js_1.hicortexHome)();
|
|
79
80
|
function readNightlyConfig(stateDir) {
|
|
80
81
|
const configPath = (0, node_path_1.join)(stateDir, "config.json");
|
|
@@ -168,7 +169,16 @@ function makeRemotePost(serverUrl, authToken) {
|
|
|
168
169
|
async function normalizePostResult(resp) {
|
|
169
170
|
if (resp.status === 201) {
|
|
170
171
|
const data = (await resp.json().catch(() => ({})));
|
|
171
|
-
|
|
172
|
+
// #287: the daemon reports the segment's metered usage. Shape-validated
|
|
173
|
+
// so a partial payload can't NaN the run's totals; a pre-#287 daemon
|
|
174
|
+
// simply omits it → capture sums zero (snapshot stays consolidation-only).
|
|
175
|
+
const usage = parseUsage(data.usage);
|
|
176
|
+
return {
|
|
177
|
+
status: 201,
|
|
178
|
+
distilled: data.distilled ?? 0,
|
|
179
|
+
dropped: data.dropped ?? [],
|
|
180
|
+
...(usage ? { usage } : {}),
|
|
181
|
+
};
|
|
172
182
|
}
|
|
173
183
|
if (resp.status === 200) {
|
|
174
184
|
const data = (await resp.json().catch(() => ({})));
|
|
@@ -177,6 +187,18 @@ async function normalizePostResult(resp) {
|
|
|
177
187
|
const data = (await resp.json().catch(() => ({})));
|
|
178
188
|
return { status: resp.status, error: data.error ?? "unknown error" };
|
|
179
189
|
}
|
|
190
|
+
/** Strict {prompt, completion, total} parser for /distill's usage field (#287);
|
|
191
|
+
* undefined on anything malformed — the caller then treats it as unmetered. */
|
|
192
|
+
function parseUsage(u) {
|
|
193
|
+
if (typeof u !== "object" || u === null)
|
|
194
|
+
return undefined;
|
|
195
|
+
const { prompt, completion, total } = u;
|
|
196
|
+
const num = (n) => typeof n === "number" && Number.isFinite(n) && n >= 0 ? n : null;
|
|
197
|
+
const p = num(prompt), c = num(completion), t = num(total);
|
|
198
|
+
if (p === null || c === null || t === null)
|
|
199
|
+
return undefined;
|
|
200
|
+
return { prompt: p, completion: c, total: t };
|
|
201
|
+
}
|
|
180
202
|
function writeLastRun(stateDir = HICORTEX_HOME) {
|
|
181
203
|
(0, state_js_1.updateState)((s) => {
|
|
182
204
|
s.lastNightly = new Date().toISOString();
|
|
@@ -347,6 +369,13 @@ async function runNightly(options = {}) {
|
|
|
347
369
|
let batches = [];
|
|
348
370
|
let memoriesIngested = 0;
|
|
349
371
|
let hadTransientFailure = false;
|
|
372
|
+
// #287: distill tokens metered by the daemon across this run's segment
|
|
373
|
+
// POSTs (summed from the /distill responses by the capture loop).
|
|
374
|
+
// Forwarded to the dashboard snapshot so new_this_run.tokens is the run's
|
|
375
|
+
// TRUE total (distill + consolidation) and the distill share lands in
|
|
376
|
+
// tokens_by_stage. Zero when the daemon predates the usage field — the
|
|
377
|
+
// snapshot writer gates on total > 0, so old servers keep today's shape.
|
|
378
|
+
let distillUsage;
|
|
350
379
|
// consolidateOnly (hosted service): skip capture entirely. The hosted
|
|
351
380
|
// consolidation timer uses this so per-tenant nightly runs don't ingest the
|
|
352
381
|
// operator's local sessions into the tenant's DB.
|
|
@@ -423,6 +452,7 @@ async function runNightly(options = {}) {
|
|
|
423
452
|
sourceDomain: savedConfig?.sourceDomain,
|
|
424
453
|
});
|
|
425
454
|
memoriesIngested = result.memoriesIngested;
|
|
455
|
+
distillUsage = result.distillUsage;
|
|
426
456
|
// A 429/401 stop must hold the watermark too (fix 1): the loop abandoned
|
|
427
457
|
// the remaining sessions, and mtime discovery would never re-find them.
|
|
428
458
|
hadTransientFailure = result.hadTransientFailure || result.stopped !== undefined;
|
|
@@ -430,7 +460,12 @@ async function runNightly(options = {}) {
|
|
|
430
460
|
finally {
|
|
431
461
|
releaseLock();
|
|
432
462
|
}
|
|
433
|
-
console.log(`[hicortex] Capture complete: ${memoriesIngested} new memories`
|
|
463
|
+
console.log(`[hicortex] Capture complete: ${memoriesIngested} new memories` +
|
|
464
|
+
// #287: distill tokens the daemon metered for those segments (absent
|
|
465
|
+
// when the daemon predates the usage field or nothing distilled).
|
|
466
|
+
(distillUsage && distillUsage.total > 0
|
|
467
|
+
? ` · ${distillUsage.total.toLocaleString()} distill tokens`
|
|
468
|
+
: ""));
|
|
434
469
|
// Prune aged-out cursors (90d) — only on a clean run so a transient
|
|
435
470
|
// failure doesn't drop a still-needed cursor.
|
|
436
471
|
if (!dryRun && !hadTransientFailure) {
|
|
@@ -630,6 +665,53 @@ async function runNightly(options = {}) {
|
|
|
630
665
|
}
|
|
631
666
|
}
|
|
632
667
|
console.log(`[hicortex] Nightly pipeline complete.`);
|
|
668
|
+
// Backup stage (#6, Phase 0B) — a transactionally-consistent snapshot of
|
|
669
|
+
// the irreplaceable data (DB + identity + state), packaged as one tar.gz
|
|
670
|
+
// the operator ships offsite via the optional `backupCommand` hook. Runs
|
|
671
|
+
// ONLY on a full nightly (capture-only is frequent + stateless; dry-run
|
|
672
|
+
// writes nothing). Backup failure must NOT fail the nightly — capture +
|
|
673
|
+
// consolidation have already succeeded; the snapshot is on disk and the
|
|
674
|
+
// failure surfaces as `backupOk:false` in the dashboard snapshot + telemetry
|
|
675
|
+
// for alerting (the operator's hook owns active alerting; no in-product
|
|
676
|
+
// channel yet — Phase 3).
|
|
677
|
+
let backupPath;
|
|
678
|
+
let backupBytes;
|
|
679
|
+
let backupOk;
|
|
680
|
+
if (!dryRun && !captureOnly) {
|
|
681
|
+
try {
|
|
682
|
+
const backupDir = typeof savedConfig?.backupDir === "string" && savedConfig.backupDir.trim()
|
|
683
|
+
? savedConfig.backupDir
|
|
684
|
+
: undefined;
|
|
685
|
+
const bRes = await (0, backup_js_1.createBackup)({ db, home: stateDir, outDir: backupDir });
|
|
686
|
+
backupPath = bRes.path;
|
|
687
|
+
backupBytes = bRes.bytes;
|
|
688
|
+
backupOk = true;
|
|
689
|
+
console.log(`[hicortex] Backup: ${bRes.files} files, ${bRes.bytes.toLocaleString()} bytes -> ${bRes.path}`);
|
|
690
|
+
const cmd = typeof savedConfig?.backupCommand === "string" && savedConfig.backupCommand.trim()
|
|
691
|
+
? savedConfig.backupCommand
|
|
692
|
+
: undefined;
|
|
693
|
+
if (cmd && backupPath) {
|
|
694
|
+
const hook = await (0, backup_js_1.runBackupHook)(backupPath, cmd);
|
|
695
|
+
if (!hook.ok) {
|
|
696
|
+
// The artifact is on disk; only the offsite copy failed. Keep
|
|
697
|
+
// backupPath/backupBytes (the snapshot records what was produced)
|
|
698
|
+
// but flip backupOk so the aggregate can alert.
|
|
699
|
+
backupOk = false;
|
|
700
|
+
console.error(`[hicortex] Backup hook failed (exit ${hook.exitCode ?? "n/a"}). ` +
|
|
701
|
+
`Artifact is on disk; offsite copy did NOT complete.`);
|
|
702
|
+
}
|
|
703
|
+
else {
|
|
704
|
+
console.log(`[hicortex] Backup hook ok (exit 0).`);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
catch (err) {
|
|
709
|
+
// The whole backup stage failed (snapshot, tar, or write). Do NOT
|
|
710
|
+
// propagate — capture/consolidation already succeeded. Surface + continue.
|
|
711
|
+
backupOk = false;
|
|
712
|
+
console.error(`[hicortex] Backup FAILED: ${err instanceof Error ? err.message : String(err)}`);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
633
715
|
// Dashboard snapshot (#224) — full nightly only. The snapshot reflects
|
|
634
716
|
// corpus state regardless of whether consolidation/LLM ran, so it is
|
|
635
717
|
// ALWAYS written here (the use case is history; an LLM-less install still
|
|
@@ -673,6 +755,12 @@ async function runNightly(options = {}) {
|
|
|
673
755
|
// when consolidation didn't run or made no metered calls).
|
|
674
756
|
tokensThisRun,
|
|
675
757
|
tokensByStage,
|
|
758
|
+
// #287: the capture phase's distill tokens (from the /distill
|
|
759
|
+
// responses). The writer merges them into `tokens` +
|
|
760
|
+
// `tokens_by_stage.distill` so the customer-facing total is the
|
|
761
|
+
// run's TRUE spend; zero (old daemon / nothing distilled) is a no-op
|
|
762
|
+
// and the row keeps its consolidation-only shape.
|
|
763
|
+
distillUsage,
|
|
676
764
|
// #255 CR: always-on budget usage — undefined when consolidation
|
|
677
765
|
// didn't run (capture-only / no_llm / throttled). Forwarded whenever
|
|
678
766
|
// consolidation ran so the digest renders a continuous used/max bar.
|
|
@@ -682,6 +770,13 @@ async function runNightly(options = {}) {
|
|
|
682
770
|
// (capture-only / no_llm / throttled) or didn't exhaust.
|
|
683
771
|
budgetExhausted,
|
|
684
772
|
budgetDeferredByStage,
|
|
773
|
+
// #6 backup stage — hoisted from the block above. Present whenever
|
|
774
|
+
// the backup stage ran (full nightly); undefined on capture-only /
|
|
775
|
+
// dry-run. backupOk flips to false on snapshot OR hook failure so the
|
|
776
|
+
// dashboard digest can flag a night the offsite copy didn't complete.
|
|
777
|
+
backupPath,
|
|
778
|
+
backupBytes,
|
|
779
|
+
backupOk,
|
|
685
780
|
}, memorySoftCapResolved);
|
|
686
781
|
}
|
|
687
782
|
catch (snapErr) {
|
|
@@ -728,6 +823,13 @@ async function runNightly(options = {}) {
|
|
|
728
823
|
// exhausted (false is omitted to keep the ping minimal; the aggregate
|
|
729
824
|
// treats absent as "not exhausted / not measurable").
|
|
730
825
|
...(budgetExhausted ? { budget_exhausted: true } : {}),
|
|
826
|
+
// #6 backup stage outcome — forwarded only when the backup stage ran
|
|
827
|
+
// (full nightly). `ok` is false on snapshot OR hook failure; the fleet
|
|
828
|
+
// aggregate surfaces a sustained drop in backup_ok as a data-loss risk.
|
|
829
|
+
// Absent on capture-only / dry-run / client runs (no backup ran).
|
|
830
|
+
...(backupOk !== undefined
|
|
831
|
+
? { backup: { ok: backupOk === true, bytes: backupBytes ?? 0 } }
|
|
832
|
+
: {}),
|
|
731
833
|
sessions: batches.length,
|
|
732
834
|
ok: !hadTransientFailure,
|
|
733
835
|
shown: adoption.shown,
|
package/dist/prompts.js
CHANGED
|
@@ -120,6 +120,7 @@ EXTRACT into this markdown format:
|
|
|
120
120
|
|
|
121
121
|
### Decisions Made
|
|
122
122
|
- [D] [SUBJECT]: [decision] — [reasoning] (${date})
|
|
123
|
+
(ONLY decisions the user explicitly made or confirmed — never an AI proposal)
|
|
123
124
|
|
|
124
125
|
### Knowledge Learned
|
|
125
126
|
- [K] [SUBJECT]: [knowledge] — [context/source] (${date})
|
|
@@ -145,10 +146,19 @@ TYPE TAG (critical — prefix EVERY bullet with exactly one letter + space):
|
|
|
145
146
|
- [K] KNOWLEDGE — a durable truth that will hold across sessions: "the API is at
|
|
146
147
|
:8787", "uv is used for packages", "config lives in ~/.hicortex/". Not tied
|
|
147
148
|
to a single moment.
|
|
148
|
-
- [D] DECISIONS — a choice
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
149
|
+
- [D] DECISIONS — a choice the USER explicitly made or confirmed in the
|
|
150
|
+
transcript: they said "do X / agreed / ok / go ahead", approved the plan, or
|
|
151
|
+
the transcript shows the change actually being carried out. It must be a
|
|
152
|
+
choice future work builds on and that a later decision can SUPERSEDE:
|
|
153
|
+
"switched from gemma4 to qwen3.5", "adopted the graded-schema tag model".
|
|
154
|
+
Not knowledge (it can change) and not experience (it persists and constrains).
|
|
155
|
+
- An AI recommendation or proposal is NEVER a decision, however detailed or
|
|
156
|
+
well-reasoned — even if the user seemed receptive. If the user declined,
|
|
157
|
+
deferred ("hold", "later", "wait for X"), or did not answer: record it as
|
|
158
|
+
[E] EXPERIENCE with proposal framing ("AI proposed X → user declined/held
|
|
159
|
+
because Y"), or under Corrections & Rejections.
|
|
160
|
+
- SELF-CHECK: if an item's text says "AI recommended/proposed" or the user
|
|
161
|
+
"has not (yet) confirmed" it, that item is NOT [D] — re-tag it [E].
|
|
152
162
|
- NEVER use [L] (learnings). Learnings are extracted by a SEPARATE reflection stage,
|
|
153
163
|
not here. If the model emits [L], it is wrong — re-tag as experience/knowledge/decisions.
|
|
154
164
|
The type tag goes BEFORE the subject, never as a section/category bracket.
|
|
@@ -161,6 +171,8 @@ entity. The subject is what a future reader would search for.
|
|
|
161
171
|
- NOT: "[E] User rejected AI's bundling of unknown loads"
|
|
162
172
|
- Write: "[K] Nightly capture (Hermes): cron sessions are excluded — source='cron' is skipped before distillation"
|
|
163
173
|
- NOT: "[K] Discovered that cron sessions are filtered out"
|
|
174
|
+
- Write: "[E] Qwen3.8-27B swap: AI proposed switching → user held on Qwen3.6-35B-A3B until an MoE variant ships"
|
|
175
|
+
- NOT: "[D] Qwen3.8-27B: switch from Qwen3.6-27B — drop-in upgrade"
|
|
164
176
|
Reason: each item's first words (after the type tag) become the memory's one-line
|
|
165
177
|
index entry AND dominate its search embedding. An item that opens with a category
|
|
166
178
|
label, a sentiment ("Strong Negative"), or "User rejected…" is unfindable — it
|
|
@@ -169,6 +181,9 @@ the subject; put reaction, intensity and reasoning AFTER it.
|
|
|
169
181
|
|
|
170
182
|
RULES:
|
|
171
183
|
- Extract MAX 20 items total (quality over quantity)
|
|
184
|
+
- Use EXACT names/versions/paths/numbers as they appear in the transcript —
|
|
185
|
+
never substitute what seems current or more standard (writing "Qwen3.6-27B"
|
|
186
|
+
when the transcript says "Qwen3.6-35B-A3B" is a fabrication)
|
|
172
187
|
- Each must be useful if recalled in a future session
|
|
173
188
|
- Skip: routine code edits, standard tool usage, trivial fixes
|
|
174
189
|
- Include: architectural decisions, debugging breakthroughs, user preferences,
|
package/dist/telemetry.d.ts
CHANGED
|
@@ -116,6 +116,20 @@ export interface TelemetryPayload {
|
|
|
116
116
|
* the dashboard snapshot, not on the wire (mirrors `tokens_this_run`).
|
|
117
117
|
*/
|
|
118
118
|
budget_exhausted?: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Backup stage outcome (#6, Phase 0B). Present on every full nightly (absent
|
|
121
|
+
* on capture-only / dry-run / client runs — no backup stage runs there).
|
|
122
|
+
* `ok` is false when EITHER the snapshot write OR the operator's
|
|
123
|
+
* `backupCommand` hook failed — the aggregate data-loss-risk signal (a
|
|
124
|
+
* sustained drop in `backup.ok` means offsite copies are silently not
|
|
125
|
+
* landing). `bytes` is the compressed artifact size (0 when the snapshot
|
|
126
|
+
* itself failed before producing a file). The operator's hook owns active
|
|
127
|
+
* alerting (email/Discord); this field is the passive fleet-health signal.
|
|
128
|
+
*/
|
|
129
|
+
backup?: {
|
|
130
|
+
ok: boolean;
|
|
131
|
+
bytes: number;
|
|
132
|
+
};
|
|
119
133
|
}
|
|
120
134
|
/**
|
|
121
135
|
* Check if telemetry is enabled. Disabled by:
|
package/dist/token-budget.js
CHANGED
|
@@ -92,7 +92,14 @@ function isTokenBudgetExceeded(stateDir) {
|
|
|
92
92
|
* consistent with total (distill + consolidation).
|
|
93
93
|
*/
|
|
94
94
|
function recordDistillUsage(stateDir, usage) {
|
|
95
|
-
|
|
95
|
+
// Record ALWAYS; the cap only governs ENFORCEMENT (isTokenBudgetExceeded)
|
|
96
|
+
// and the 80% warning below. Without this, an uncapped install (self-hosted
|
|
97
|
+
// default) never accrues distill tokens into the period meter, so the
|
|
98
|
+
// dashboard's monthly headline read consolidation-only while the per-run
|
|
99
|
+
// chart next to it showed true (distill-inclusive) totals — numbers on the
|
|
100
|
+
// same card telling different stories (#287 CR). Always-recording makes the
|
|
101
|
+
// meter the honest "what did this install spend" number everywhere.
|
|
102
|
+
if (usage.total <= 0)
|
|
96
103
|
return;
|
|
97
104
|
let newTotal = 0;
|
|
98
105
|
let periodStart = "";
|
|
@@ -123,7 +130,8 @@ function recordDistillUsage(stateDir, usage) {
|
|
|
123
130
|
periodStart = s.llmTokensThisPeriod.periodStart;
|
|
124
131
|
}, stateDir);
|
|
125
132
|
// 80% warning — dedup per period (once per month per threshold crossing).
|
|
126
|
-
|
|
133
|
+
// Cap-gated by design: no cap → no percentage to warn about.
|
|
134
|
+
if (cap > 0 && periodStart && warnedPeriod !== periodStart && newTotal >= cap * 0.8) {
|
|
127
135
|
warnedPeriod = periodStart;
|
|
128
136
|
const pct = Math.round((newTotal / cap) * 100);
|
|
129
137
|
console.warn(`[hicortex] Token usage at ${pct}% of monthly cap (${newTotal.toLocaleString()}/${cap.toLocaleString()}).`);
|
package/dist/type-classify.js
CHANGED
|
@@ -78,10 +78,13 @@ function buildTypeClassifyPrompt(content) {
|
|
|
78
78
|
`occurrence ("tried X, failed because Y", a correction, a debugging session).\n` +
|
|
79
79
|
`- knowledge: a durable truth that holds across sessions, not tied to a ` +
|
|
80
80
|
`single moment ("the API is at :8787", "uv is used for packages").\n` +
|
|
81
|
-
`- decisions: a choice
|
|
82
|
-
`
|
|
83
|
-
`
|
|
84
|
-
`
|
|
81
|
+
`- decisions: a choice the user explicitly made or confirmed (or one the ` +
|
|
82
|
+
`memory records as actually carried out/applied) that future work builds ` +
|
|
83
|
+
`on and a later decision can supersede ("switched from gemma4 to qwen3.5", ` +
|
|
84
|
+
`"adopted the graded-schema tag model"). Not knowledge (it can change) and ` +
|
|
85
|
+
`not an experience (it persists). A bare AI recommendation or proposal is ` +
|
|
86
|
+
`NEVER a decision — "AI proposed X → user declined/held" is experience ` +
|
|
87
|
+
`(#290).\n\n` +
|
|
85
88
|
`IMPORTANCE (0.0–1.0):\n` +
|
|
86
89
|
`- 0.8–1.0: load-bearing — a core piece of knowledge or a decision the ` +
|
|
87
90
|
`agent must know.\n` +
|
package/dist/types.d.ts
CHANGED
|
@@ -434,6 +434,46 @@ export interface HicortexConfig {
|
|
|
434
434
|
* set it. Period resets monthly (state.json `llmTokensThisPeriod.periodStart`).
|
|
435
435
|
*/
|
|
436
436
|
llmTokensPerMonth?: number;
|
|
437
|
+
/**
|
|
438
|
+
* Max request body size in MB accepted by the REST/MCP server (#7). The body
|
|
439
|
+
* is fully JSON-parsed into memory before the distiller truncates content to
|
|
440
|
+
* 80K chars, so an unbounded body is an OOM vector — a tenant (hosted) or a
|
|
441
|
+
* misbehaving client could POST a huge payload to exhaust RAM. Default: 25 MB
|
|
442
|
+
* self-hosted (unchanged), 5 MB hosted (the spec §6.3 figure — ~25× the 200KB
|
|
443
|
+
* segment max, so legitimate capture never approaches it; it's a pure
|
|
444
|
+
* abuse/OOM backstop). Oversized bodies get HTTP 413.
|
|
445
|
+
*/
|
|
446
|
+
distillBodyLimitMb?: number;
|
|
447
|
+
/**
|
|
448
|
+
* Output directory for backup artifacts (#6). Default `<HICORTEX_HOME>/backups`.
|
|
449
|
+
* Both the CLI (`hicortex backup`) and the nightly backup stage resolve this
|
|
450
|
+
* before calling createBackup; an unset value falls back to the home dir.
|
|
451
|
+
* Operator-owned: point at a mounted backup volume, a tmpfs, etc.
|
|
452
|
+
*/
|
|
453
|
+
backupDir?: string;
|
|
454
|
+
/**
|
|
455
|
+
* Post-backup offsite hook (#6). When set, `hicortex backup` and the nightly
|
|
456
|
+
* backup stage invoke this command with the artifact path appended as the LAST
|
|
457
|
+
* arg (e.g. `"rclone copyto"` → `rclone copyto <path> remote:bucket/`). Cloud
|
|
458
|
+
* credentials + active alerting (email/Discord) stay in the operator's wrapper
|
|
459
|
+
* script, out of the product. The hook is split on whitespace (no shell); a
|
|
460
|
+
* command with quoted args containing spaces should be a wrapper script. A
|
|
461
|
+
* failing/missing/timed-out hook reports `{ok:false}` and never throws —
|
|
462
|
+
* capture/consolidation have already succeeded, so a backup-hook failure must
|
|
463
|
+
* not fail the nightly. 5 min timeout.
|
|
464
|
+
*/
|
|
465
|
+
backupCommand?: string;
|
|
466
|
+
/**
|
|
467
|
+
* Account identity shown in the dashboard header (hosted). When ALL three
|
|
468
|
+
* are absent the header renders no account element (self-hosted default —
|
|
469
|
+
* nothing changes). Strings only; read via readStringConfig, null when
|
|
470
|
+
* absent/not a string. Set per-tenant by provision-tenant.sh.
|
|
471
|
+
*/
|
|
472
|
+
displayName?: string;
|
|
473
|
+
/** Organization name — rendered alongside displayName as "Name · Org". */
|
|
474
|
+
orgName?: string;
|
|
475
|
+
/** Plan/tier label rendered as a small badge (e.g. "Cloud · Early bird"). */
|
|
476
|
+
planLabel?: string;
|
|
437
477
|
}
|
|
438
478
|
/** A config-owned life-sphere domain (see HicortexConfig.domains). */
|
|
439
479
|
export interface DomainDef {
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "hicortex",
|
|
3
3
|
"name": "Hicortex — Long-term Memory That Learns",
|
|
4
4
|
"description": "Your agents remember past decisions, avoid repeated mistakes, and get smarter every day. Nightly reflection generates actionable lessons that automatically update agent behavior.",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.19.1",
|
|
6
6
|
"kind": "lifecycle",
|
|
7
7
|
"skills": ["./skills/hicortex-memory"],
|
|
8
8
|
"configSchema": {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gamaze/hicortex",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Persistent agent identity for AI agents
|
|
3
|
+
"version": "0.19.1",
|
|
4
|
+
"description": "Persistent agent identity for AI agents \u2014 a hand-edited identity layer, nightly-distilled experience, and lessons injected every session, shared across your whole fleet. Works with Hermes, OpenClaw, Claude Code, and Pi.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"hicortex": "dist/cli.js"
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"@types/better-sqlite3": "^7.6.0",
|
|
48
48
|
"@types/express": "^5.0.6",
|
|
49
49
|
"@types/node": "^22.0.0",
|
|
50
|
+
"@types/tar-stream": "^3.1.4",
|
|
50
51
|
"typescript": "^5.4.0",
|
|
51
52
|
"vitest": "^3.0.0"
|
|
52
53
|
},
|
|
@@ -69,6 +70,7 @@
|
|
|
69
70
|
"@modelcontextprotocol/sdk": "^1.28.0",
|
|
70
71
|
"better-sqlite3": "^12.11.1",
|
|
71
72
|
"express": "^4.21.0",
|
|
72
|
-
"sqlite-vec": "^0.1.7"
|
|
73
|
+
"sqlite-vec": "^0.1.7",
|
|
74
|
+
"tar-stream": "^2.2.0"
|
|
73
75
|
}
|
|
74
|
-
}
|
|
76
|
+
}
|