@remnic/core 9.3.638 → 9.3.640
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/dist/access-cli.js +31 -31
- package/dist/access-http.d.ts +56 -5
- package/dist/access-http.js +17 -17
- package/dist/access-mcp.d.ts +4 -4
- package/dist/access-mcp.js +16 -16
- package/dist/access-schema.d.ts +3 -0
- package/dist/access-schema.js +1 -1
- package/dist/{access-service-DvEvBv9_.d.ts → access-service-jyYute38.d.ts} +3 -2
- package/dist/access-service.d.ts +4 -4
- package/dist/access-service.js +14 -14
- package/dist/action-confidence.d.ts +1 -1
- package/dist/active-memory-bridge.d.ts +1 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +1 -1
- package/dist/{auto-sync-RFADEHIQ.js → auto-sync-54QQHOG5.js} +4 -4
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +3 -3
- package/dist/briefing.d.ts +1 -1
- package/dist/briefing.js +3 -3
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer.d.ts +1 -1
- package/dist/calibration.d.ts +1 -1
- package/dist/calibration.js +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +5 -5
- package/dist/{chunk-3RDYU3JS.js → chunk-3AZDLWDW.js} +3 -3
- package/dist/{chunk-FPNQF475.js → chunk-3F7RMHC4.js} +1 -1
- package/dist/chunk-3F7RMHC4.js.map +1 -0
- package/dist/{chunk-KXTLEB4L.js → chunk-3SF7QNKF.js} +2 -2
- package/dist/{chunk-U2YPUWRB.js → chunk-4GYGEKUB.js} +53 -7
- package/dist/chunk-4GYGEKUB.js.map +1 -0
- package/dist/{chunk-6BDVBBBY.js → chunk-666A3MOW.js} +88 -30
- package/dist/chunk-666A3MOW.js.map +1 -0
- package/dist/{chunk-EDQVAMQI.js → chunk-6GIKAUTN.js} +2 -2
- package/dist/{chunk-4HFJQCJZ.js → chunk-7HYPN2GC.js} +2 -2
- package/dist/{chunk-Q6YIJGXJ.js → chunk-7OGJQP7T.js} +2 -2
- package/dist/{chunk-242XFZ36.js → chunk-7OH7KPF5.js} +2 -2
- package/dist/{chunk-RW5DGAGO.js → chunk-7V36WCUW.js} +2 -2
- package/dist/{chunk-7A2QKUUA.js → chunk-7XRSLMUD.js} +2 -2
- package/dist/{chunk-723OMPUI.js → chunk-A3BS64GV.js} +7 -1
- package/dist/chunk-A3BS64GV.js.map +1 -0
- package/dist/{chunk-JCLECECB.js → chunk-A3UPMOJZ.js} +2 -2
- package/dist/{chunk-QHXW3LZV.js → chunk-C3EJ3UIS.js} +3 -3
- package/dist/{chunk-YCX45GOQ.js → chunk-C4YAIWDG.js} +3 -3
- package/dist/{chunk-A3HKTPPV.js → chunk-C63WC454.js} +12 -1
- package/dist/chunk-C63WC454.js.map +1 -0
- package/dist/{chunk-4S3N6HFG.js → chunk-C6XO6ONP.js} +2 -2
- package/dist/{chunk-WD2W4234.js → chunk-CDHRUNBX.js} +11 -2
- package/dist/chunk-CDHRUNBX.js.map +1 -0
- package/dist/{chunk-YQNADJCT.js → chunk-CMZLMIZI.js} +2 -2
- package/dist/{chunk-KVDUDYEN.js → chunk-DDRNDPX4.js} +2 -2
- package/dist/{chunk-ORFGK3XI.js → chunk-E6ZDCOHM.js} +2 -2
- package/dist/{chunk-TKWGAOLV.js → chunk-EEC4PCG5.js} +2 -2
- package/dist/{chunk-PW6GURU3.js → chunk-EKIQBGAY.js} +2 -2
- package/dist/{chunk-YR6GIWWY.js → chunk-GQL52GQ5.js} +2 -2
- package/dist/{chunk-77NAFXUD.js → chunk-H3HDXD3U.js} +2 -2
- package/dist/{chunk-X7EJF46S.js → chunk-H4RWKZE2.js} +2 -2
- package/dist/{chunk-LASA46TQ.js → chunk-HRXCPKN4.js} +2 -2
- package/dist/{chunk-PUEAEQSN.js → chunk-ISMZIMD3.js} +2 -2
- package/dist/{chunk-LEG7XWS2.js → chunk-IXSTQGYV.js} +2 -2
- package/dist/{chunk-CMN5AWAZ.js → chunk-J2KFC47S.js} +2 -2
- package/dist/{chunk-OSPVAP56.js → chunk-JFHEEDRY.js} +20 -12
- package/dist/chunk-JFHEEDRY.js.map +1 -0
- package/dist/{chunk-RAGXUVQW.js → chunk-KVUG7FF7.js} +4 -4
- package/dist/{chunk-K47C6M2C.js → chunk-M75TBFKQ.js} +2 -2
- package/dist/{chunk-DEDTFRH2.js → chunk-MUKXANAM.js} +14 -3
- package/dist/chunk-MUKXANAM.js.map +1 -0
- package/dist/chunk-O75CRYGF.js +15 -0
- package/dist/chunk-O75CRYGF.js.map +1 -0
- package/dist/{chunk-YROCXMCK.js → chunk-RJGPH2Q3.js} +2 -2
- package/dist/{chunk-QDAWVW75.js → chunk-SBHQBC37.js} +2 -2
- package/dist/{chunk-NRST7W5Q.js → chunk-SEOTBRO4.js} +3 -3
- package/dist/{chunk-QYGIQ5NM.js → chunk-SHHT4JTC.js} +2 -2
- package/dist/{chunk-MNDFVT4F.js → chunk-UDZYZ5Q5.js} +12 -10
- package/dist/chunk-UDZYZ5Q5.js.map +1 -0
- package/dist/{chunk-C4KKM62E.js → chunk-UU5LHD6J.js} +2 -2
- package/dist/{chunk-I3YAA4BI.js → chunk-UZBK2JR6.js} +19 -19
- package/dist/{chunk-TJRV4MWX.js → chunk-WAKPKNEX.js} +172 -51
- package/dist/chunk-WAKPKNEX.js.map +1 -0
- package/dist/{chunk-7KRYJUTB.js → chunk-Y2XKJJEI.js} +3 -3
- package/dist/{chunk-2JIYTTQJ.js → chunk-YMI4NRV7.js} +5 -5
- package/dist/{cli-BJBKNWIS.d.ts → cli-A4AIhv9e.d.ts} +3 -3
- package/dist/cli.d.ts +5 -5
- package/dist/cli.js +31 -31
- package/dist/compat/checks.js +2 -2
- package/dist/compounding/engine.d.ts +1 -1
- package/dist/compounding/engine.js +3 -3
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +3 -3
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +5 -5
- package/dist/consolidation-provenance-check.d.ts +1 -1
- package/dist/consolidation-undo.d.ts +1 -1
- package/dist/contradiction/index.d.ts +1 -1
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/backend.js +3 -3
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.js +2 -2
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/day-summary.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer.d.ts +1 -1
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +1 -1
- package/dist/entity-retrieval.js +3 -3
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +3 -3
- package/dist/extraction-judge-telemetry.d.ts +1 -1
- package/dist/extraction-judge-training.d.ts +1 -1
- package/dist/extraction-judge.d.ts +1 -1
- package/dist/extraction-judge.js +2 -2
- package/dist/extraction.d.ts +1 -1
- package/dist/extraction.js +4 -4
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/fallback-llm.js +1 -1
- package/dist/identity-continuity.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/index.d.ts +9 -9
- package/dist/index.js +45 -45
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/tools.d.ts +1 -1
- package/dist/lifecycle.d.ts +1 -1
- package/dist/live-connectors-runner.d.ts +1 -1
- package/dist/local-llm.d.ts +1 -1
- package/dist/local-llm.js +2 -2
- package/dist/maintenance/memory-governance-cron.d.ts +8 -0
- package/dist/maintenance/memory-governance-cron.js +1 -1
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +3 -3
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
- package/dist/maintenance/rebuild-memory-projection.js +4 -4
- package/dist/mcp-memory-inspector-app.d.ts +4 -4
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-worth-outcomes.d.ts +1 -1
- package/dist/migrate/from-engram.js +2 -2
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +1 -1
- package/dist/namespaces/migrate.js +10 -10
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +6 -6
- package/dist/namespaces/storage.d.ts +1 -1
- package/dist/namespaces/storage.js +3 -3
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/network/tailscale.js +2 -2
- package/dist/operator-toolkit.d.ts +1 -1
- package/dist/operator-toolkit.js +13 -13
- package/dist/{orchestrator-ibTTeoUa.d.ts → orchestrator-BEDTiuEO.d.ts} +11 -5
- package/dist/orchestrator.d.ts +3 -3
- package/dist/orchestrator.js +27 -27
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +4 -1
- package/dist/qmd.js +2 -2
- package/dist/recall-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +1 -1
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-planner-llm.js +1 -1
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-xray-cli.d.ts +1 -1
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +1 -1
- package/dist/recall-xray.js +2 -2
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +2 -2
- package/dist/retrieval-agents.d.ts +1 -1
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/runtime/child-process.js +1 -1
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +5 -5
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +5 -5
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-BPs6BURk.d.ts → semantic-consolidation-Byut1ufK.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +4 -4
- package/dist/semantic-rule-promotion.js +3 -3
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +3 -3
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +1 -1
- package/dist/signal.d.ts +1 -1
- package/dist/storage.d.ts +1 -1
- package/dist/storage.js +2 -2
- package/dist/summarizer.d.ts +1 -1
- package/dist/summarizer.js +4 -4
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/temporal-supersession.d.ts +1 -1
- package/dist/temporal-validity.d.ts +1 -1
- package/dist/threading.d.ts +1 -1
- package/dist/tier-migration.d.ts +1 -1
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/{types-CpMPD8xl.d.ts → types-BzA1FKbg.d.ts} +4 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +3 -3
- package/package.json +3 -1
- package/src/access-http.test.ts +110 -2
- package/src/access-http.ts +117 -1
- package/src/access-mcp.ts +15 -6
- package/src/access-schema.ts +14 -0
- package/src/access-service.ts +4 -1
- package/src/config.ts +14 -0
- package/src/fallback-llm.ts +16 -0
- package/src/index.ts +9 -1
- package/src/maintenance/memory-governance-cron.ts +113 -30
- package/src/orchestrator.ts +197 -32
- package/src/qmd.test.ts +35 -0
- package/src/qmd.ts +16 -1
- package/src/runtime/child-process.test.ts +15 -0
- package/src/runtime/child-process.ts +9 -22
- package/src/types.ts +4 -0
- package/dist/chunk-6BDVBBBY.js.map +0 -1
- package/dist/chunk-723OMPUI.js.map +0 -1
- package/dist/chunk-A3HKTPPV.js.map +0 -1
- package/dist/chunk-DEDTFRH2.js.map +0 -1
- package/dist/chunk-FPNQF475.js.map +0 -1
- package/dist/chunk-MNDFVT4F.js.map +0 -1
- package/dist/chunk-NNVTUXEB.js +0 -23
- package/dist/chunk-NNVTUXEB.js.map +0 -1
- package/dist/chunk-OSPVAP56.js.map +0 -1
- package/dist/chunk-TJRV4MWX.js.map +0 -1
- package/dist/chunk-U2YPUWRB.js.map +0 -1
- package/dist/chunk-WD2W4234.js.map +0 -1
- /package/dist/{auto-sync-RFADEHIQ.js.map → auto-sync-54QQHOG5.js.map} +0 -0
- /package/dist/{chunk-3RDYU3JS.js.map → chunk-3AZDLWDW.js.map} +0 -0
- /package/dist/{chunk-KXTLEB4L.js.map → chunk-3SF7QNKF.js.map} +0 -0
- /package/dist/{chunk-EDQVAMQI.js.map → chunk-6GIKAUTN.js.map} +0 -0
- /package/dist/{chunk-4HFJQCJZ.js.map → chunk-7HYPN2GC.js.map} +0 -0
- /package/dist/{chunk-Q6YIJGXJ.js.map → chunk-7OGJQP7T.js.map} +0 -0
- /package/dist/{chunk-242XFZ36.js.map → chunk-7OH7KPF5.js.map} +0 -0
- /package/dist/{chunk-RW5DGAGO.js.map → chunk-7V36WCUW.js.map} +0 -0
- /package/dist/{chunk-7A2QKUUA.js.map → chunk-7XRSLMUD.js.map} +0 -0
- /package/dist/{chunk-JCLECECB.js.map → chunk-A3UPMOJZ.js.map} +0 -0
- /package/dist/{chunk-QHXW3LZV.js.map → chunk-C3EJ3UIS.js.map} +0 -0
- /package/dist/{chunk-YCX45GOQ.js.map → chunk-C4YAIWDG.js.map} +0 -0
- /package/dist/{chunk-4S3N6HFG.js.map → chunk-C6XO6ONP.js.map} +0 -0
- /package/dist/{chunk-YQNADJCT.js.map → chunk-CMZLMIZI.js.map} +0 -0
- /package/dist/{chunk-KVDUDYEN.js.map → chunk-DDRNDPX4.js.map} +0 -0
- /package/dist/{chunk-ORFGK3XI.js.map → chunk-E6ZDCOHM.js.map} +0 -0
- /package/dist/{chunk-TKWGAOLV.js.map → chunk-EEC4PCG5.js.map} +0 -0
- /package/dist/{chunk-PW6GURU3.js.map → chunk-EKIQBGAY.js.map} +0 -0
- /package/dist/{chunk-YR6GIWWY.js.map → chunk-GQL52GQ5.js.map} +0 -0
- /package/dist/{chunk-77NAFXUD.js.map → chunk-H3HDXD3U.js.map} +0 -0
- /package/dist/{chunk-X7EJF46S.js.map → chunk-H4RWKZE2.js.map} +0 -0
- /package/dist/{chunk-LASA46TQ.js.map → chunk-HRXCPKN4.js.map} +0 -0
- /package/dist/{chunk-PUEAEQSN.js.map → chunk-ISMZIMD3.js.map} +0 -0
- /package/dist/{chunk-LEG7XWS2.js.map → chunk-IXSTQGYV.js.map} +0 -0
- /package/dist/{chunk-CMN5AWAZ.js.map → chunk-J2KFC47S.js.map} +0 -0
- /package/dist/{chunk-RAGXUVQW.js.map → chunk-KVUG7FF7.js.map} +0 -0
- /package/dist/{chunk-K47C6M2C.js.map → chunk-M75TBFKQ.js.map} +0 -0
- /package/dist/{chunk-YROCXMCK.js.map → chunk-RJGPH2Q3.js.map} +0 -0
- /package/dist/{chunk-QDAWVW75.js.map → chunk-SBHQBC37.js.map} +0 -0
- /package/dist/{chunk-NRST7W5Q.js.map → chunk-SEOTBRO4.js.map} +0 -0
- /package/dist/{chunk-QYGIQ5NM.js.map → chunk-SHHT4JTC.js.map} +0 -0
- /package/dist/{chunk-C4KKM62E.js.map → chunk-UU5LHD6J.js.map} +0 -0
- /package/dist/{chunk-I3YAA4BI.js.map → chunk-UZBK2JR6.js.map} +0 -0
- /package/dist/{chunk-7KRYJUTB.js.map → chunk-Y2XKJJEI.js.map} +0 -0
- /package/dist/{chunk-2JIYTTQJ.js.map → chunk-YMI4NRV7.js.map} +0 -0
package/src/orchestrator.ts
CHANGED
|
@@ -78,6 +78,7 @@ import { LocalLlmClient } from "./local-llm.js";
|
|
|
78
78
|
import {
|
|
79
79
|
FallbackLlmClient,
|
|
80
80
|
fallbackLlmRuntimeContextFromConfig,
|
|
81
|
+
gatewayTaskChainOptions,
|
|
81
82
|
} from "./fallback-llm.js";
|
|
82
83
|
import {
|
|
83
84
|
ensureDaySummaryCron,
|
|
@@ -765,6 +766,105 @@ function qmdResultPathCandidates(
|
|
|
765
766
|
const COMPACTION_SIGNAL_MAX_AGE_MS = 60 * 60 * 1000; // 1 hour
|
|
766
767
|
const DEFAULT_QMD_STARTUP_COLLECTION_CHECK_TIMEOUT_MS = 10_000;
|
|
767
768
|
|
|
769
|
+
type DaySummaryGatherOptions = {
|
|
770
|
+
timeZone?: string;
|
|
771
|
+
now?: Date;
|
|
772
|
+
};
|
|
773
|
+
|
|
774
|
+
function normalizeIanaTimeZone(value: unknown): string | undefined {
|
|
775
|
+
if (typeof value !== "string") return undefined;
|
|
776
|
+
const trimmed = value.trim();
|
|
777
|
+
if (!trimmed) return undefined;
|
|
778
|
+
try {
|
|
779
|
+
Intl.DateTimeFormat(undefined, { timeZone: trimmed });
|
|
780
|
+
return trimmed;
|
|
781
|
+
} catch {
|
|
782
|
+
return undefined;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
function formatDateInTimeZone(date: Date, timeZone: string): string {
|
|
787
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
788
|
+
timeZone,
|
|
789
|
+
year: "numeric",
|
|
790
|
+
month: "2-digit",
|
|
791
|
+
day: "2-digit",
|
|
792
|
+
}).formatToParts(date);
|
|
793
|
+
const get = (type: string): string => parts.find((part) => part.type === type)?.value ?? "";
|
|
794
|
+
return `${get("year")}-${get("month")}-${get("day")}`;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
function utcDateKey(date: Date): string {
|
|
798
|
+
return date.toISOString().slice(0, 10);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
function utcDateKeysAround(date: Date): string[] {
|
|
802
|
+
const dayMs = 86_400_000;
|
|
803
|
+
const keys = [
|
|
804
|
+
utcDateKey(new Date(date.getTime() - dayMs)),
|
|
805
|
+
utcDateKey(date),
|
|
806
|
+
utcDateKey(new Date(date.getTime() + dayMs)),
|
|
807
|
+
];
|
|
808
|
+
return keys.filter((value, index, array) => array.indexOf(value) === index);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
function utcDateKeysForLocalDay(date: Date, timeZone: string): string[] {
|
|
812
|
+
const targetLocalDate = formatDateInTimeZone(date, timeZone);
|
|
813
|
+
const keys = new Set<string>();
|
|
814
|
+
const hourMs = 3_600_000;
|
|
815
|
+
const scanStart = date.getTime() - 48 * hourMs;
|
|
816
|
+
const scanEnd = date.getTime() + 48 * hourMs;
|
|
817
|
+
for (let ms = scanStart; ms <= scanEnd; ms += hourMs) {
|
|
818
|
+
const candidate = new Date(ms);
|
|
819
|
+
if (formatDateInTimeZone(candidate, timeZone) === targetLocalDate) {
|
|
820
|
+
keys.add(utcDateKey(candidate));
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
return keys.size > 0 ? [...keys].sort() : utcDateKeysAround(date);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
function parseFiniteDate(value: unknown): Date | null {
|
|
827
|
+
if (typeof value !== "string" || value.trim().length === 0) return null;
|
|
828
|
+
const parsed = new Date(value);
|
|
829
|
+
return Number.isFinite(parsed.getTime()) ? parsed : null;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
function filterHourlySummaryMarkdownForLocalDay(
|
|
833
|
+
raw: string,
|
|
834
|
+
utcDate: string,
|
|
835
|
+
timeZone: string,
|
|
836
|
+
targetLocalDate: string,
|
|
837
|
+
): string | null {
|
|
838
|
+
const hourHeaderPattern = /^## ([01]\d|2[0-3]):00[ \t]*$/gm;
|
|
839
|
+
const matches = Array.from(raw.matchAll(hourHeaderPattern));
|
|
840
|
+
if (matches.length === 0) return null;
|
|
841
|
+
|
|
842
|
+
const firstSectionStart = matches[0]?.index ?? 0;
|
|
843
|
+
const preamble = raw.slice(0, firstSectionStart).trim();
|
|
844
|
+
const sections: string[] = [];
|
|
845
|
+
for (let index = 0; index < matches.length; index += 1) {
|
|
846
|
+
const match = matches[index];
|
|
847
|
+
const hour = match[1];
|
|
848
|
+
if (!hour) continue;
|
|
849
|
+
const sectionTimestamp = parseFiniteDate(`${utcDate}T${hour}:00:00.000Z`);
|
|
850
|
+
if (
|
|
851
|
+
!sectionTimestamp ||
|
|
852
|
+
formatDateInTimeZone(sectionTimestamp, timeZone) !== targetLocalDate
|
|
853
|
+
) {
|
|
854
|
+
continue;
|
|
855
|
+
}
|
|
856
|
+
const sectionStart = match.index ?? 0;
|
|
857
|
+
const sectionEnd = matches[index + 1]?.index ?? raw.length;
|
|
858
|
+
const section = raw.slice(sectionStart, sectionEnd).trim();
|
|
859
|
+
if (section.length > 0) sections.push(section);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
if (sections.length === 0) return null;
|
|
863
|
+
return [preamble, ...sections]
|
|
864
|
+
.filter((section) => section.length > 0)
|
|
865
|
+
.join("\n\n");
|
|
866
|
+
}
|
|
867
|
+
|
|
768
868
|
type SearchCollectionState = "present" | "missing" | "unknown" | "skipped";
|
|
769
869
|
|
|
770
870
|
function qmdStartupCollectionCheckTimeoutMs(): number {
|
|
@@ -2294,10 +2394,12 @@ export class Orchestrator {
|
|
|
2294
2394
|
? await this._fastGatewayLlm.chatCompletion(messages, {
|
|
2295
2395
|
maxTokens: targetTokens * 2,
|
|
2296
2396
|
timeoutMs: this.config.localLlmFastTimeoutMs,
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2397
|
+
// LCM is latency-sensitive. A configured fast persona is the
|
|
2398
|
+
// explicit fast-tier route; otherwise use taskModelChain so LCM
|
|
2399
|
+
// avoids falling to an expensive gateway default. Issue #1473.
|
|
2400
|
+
...(this.config.fastGatewayAgentId
|
|
2401
|
+
? { agentId: this.config.fastGatewayAgentId }
|
|
2402
|
+
: gatewayTaskChainOptions(this.config)),
|
|
2301
2403
|
})
|
|
2302
2404
|
: await this.localLlm.chatCompletion(messages, {
|
|
2303
2405
|
maxTokens: targetTokens * 2,
|
|
@@ -3206,8 +3308,10 @@ export class Orchestrator {
|
|
|
3206
3308
|
}
|
|
3207
3309
|
|
|
3208
3310
|
/**
|
|
3209
|
-
* Auto-register the engram-day-summary cron job in OpenClaw
|
|
3311
|
+
* Auto-register the engram-day-summary cron job in OpenClaw.
|
|
3312
|
+
* Reconciles model and timezone on every startup so config changes propagate.
|
|
3210
3313
|
* Fire-and-forget — never blocks init or crashes on failure.
|
|
3314
|
+
* Issues #1474, #1475.
|
|
3211
3315
|
*/
|
|
3212
3316
|
private async autoRegisterDaySummaryCron(): Promise<void> {
|
|
3213
3317
|
const home = resolveHomeDir();
|
|
@@ -3220,15 +3324,57 @@ export class Orchestrator {
|
|
|
3220
3324
|
);
|
|
3221
3325
|
return;
|
|
3222
3326
|
}
|
|
3223
|
-
|
|
3224
|
-
|
|
3327
|
+
|
|
3328
|
+
// Resolve an OpenClaw cron-routing model only in gateway mode. In plugin
|
|
3329
|
+
// mode, summaryModel is a direct-client model id for Remnic's own LLM
|
|
3330
|
+
// calls and may be unroutable as an OpenClaw agentTurn model.
|
|
3331
|
+
const rawSummaryModel = this.config.summaryModel;
|
|
3332
|
+
const taskPrimary = this.config.taskModelChain?.primary;
|
|
3333
|
+
const isGateway = this.config.modelSource === "gateway";
|
|
3334
|
+
const model = isGateway ? (rawSummaryModel || taskPrimary || undefined) : undefined;
|
|
3335
|
+
// Attach task-chain fallbacks only when the model matches the task-chain
|
|
3336
|
+
// primary. If summaryModel is a distinct override, its fallbacks would
|
|
3337
|
+
// be unrelated to the task chain. Also append gateway default models as
|
|
3338
|
+
// tail fallbacks (de-duped) so a task-chain outage doesn't stop the cron
|
|
3339
|
+
// before reaching the gateway default chain. Mirrors hourly cron pattern.
|
|
3340
|
+
const fallbacks: string[] = [];
|
|
3341
|
+
if (model && taskPrimary && model === taskPrimary) {
|
|
3342
|
+
const seen = new Set<string>(model ? [model] : []);
|
|
3343
|
+
const addUnique = (value: string | undefined) => {
|
|
3344
|
+
if (typeof value !== "string") return;
|
|
3345
|
+
const trimmed = value.trim();
|
|
3346
|
+
if (trimmed.length > 0 && !seen.has(trimmed)) {
|
|
3347
|
+
seen.add(trimmed);
|
|
3348
|
+
fallbacks.push(trimmed);
|
|
3349
|
+
}
|
|
3350
|
+
};
|
|
3351
|
+
for (const fb of this.config.taskModelChain?.fallbacks ?? []) addUnique(fb);
|
|
3352
|
+
const gwDefaults = this.config.gatewayConfig?.agents?.defaults?.model;
|
|
3353
|
+
addUnique(gwDefaults?.primary);
|
|
3354
|
+
if (Array.isArray(gwDefaults?.fallbacks)) {
|
|
3355
|
+
for (const fb of gwDefaults.fallbacks) addUnique(fb);
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
|
|
3359
|
+
// Resolve timezone: configurable override, then server default
|
|
3360
|
+
const timezone = this.config.daySummaryTimezone
|
|
3361
|
+
|| Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
3362
|
+
|
|
3363
|
+
const result = await ensureDaySummaryCron(jobsPath, {
|
|
3364
|
+
timezone,
|
|
3365
|
+
...(model ? { model } : {}),
|
|
3366
|
+
...(fallbacks.length > 0 ? { fallbacks } : {}),
|
|
3225
3367
|
});
|
|
3226
|
-
if (
|
|
3368
|
+
if (result.created) {
|
|
3227
3369
|
log.info(
|
|
3228
|
-
`day-summary cron auto-registered (${
|
|
3370
|
+
`day-summary cron auto-registered (${result.jobId}, 23:47 ${timezone}${model ? `, model: ${model}` : ""})`,
|
|
3371
|
+
);
|
|
3372
|
+
} else if (result.updated) {
|
|
3373
|
+
log.info(
|
|
3374
|
+
`day-summary cron reconciled (${result.jobId}, timezone: ${timezone}${model ? `, model: ${model}` : ""})`,
|
|
3229
3375
|
);
|
|
3230
3376
|
} else {
|
|
3231
|
-
log.debug("day-summary cron already
|
|
3377
|
+
log.debug("day-summary cron already up to date");
|
|
3232
3378
|
}
|
|
3233
3379
|
} catch (err) {
|
|
3234
3380
|
log.debug(`day-summary cron auto-register error: ${err}`);
|
|
@@ -4149,8 +4295,9 @@ export class Orchestrator {
|
|
|
4149
4295
|
*/
|
|
4150
4296
|
async generateDaySummaryAuto(
|
|
4151
4297
|
namespace?: string,
|
|
4298
|
+
options: DaySummaryGatherOptions = {},
|
|
4152
4299
|
): Promise<DaySummaryResult | null> {
|
|
4153
|
-
const gathered = await this.gatherTodayFacts(namespace);
|
|
4300
|
+
const gathered = await this.gatherTodayFacts(namespace, options);
|
|
4154
4301
|
if (!gathered || !gathered.trim()) {
|
|
4155
4302
|
log.warn("generateDaySummaryAuto: no facts found for today, skipping");
|
|
4156
4303
|
return null;
|
|
@@ -4162,24 +4309,27 @@ export class Orchestrator {
|
|
|
4162
4309
|
* Read today's facts and hourly summaries from storage, returning them
|
|
4163
4310
|
* as a formatted string suitable for generateDaySummary().
|
|
4164
4311
|
*/
|
|
4165
|
-
async gatherTodayFacts(
|
|
4312
|
+
async gatherTodayFacts(
|
|
4313
|
+
namespace?: string,
|
|
4314
|
+
options: DaySummaryGatherOptions = {},
|
|
4315
|
+
): Promise<string> {
|
|
4166
4316
|
const ns =
|
|
4167
4317
|
namespace && namespace.length > 0
|
|
4168
4318
|
? namespace
|
|
4169
4319
|
: this.config.defaultNamespace;
|
|
4170
4320
|
const storage = await this.storageRouter.storageFor(ns);
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
const now =
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
);
|
|
4321
|
+
const configuredTimeZone = normalizeIanaTimeZone(options.timeZone)
|
|
4322
|
+
?? normalizeIanaTimeZone(this.config.daySummaryTimezone);
|
|
4323
|
+
const timeZone =
|
|
4324
|
+
configuredTimeZone ?? Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
4325
|
+
const now = options.now instanceof Date && Number.isFinite(options.now.getTime())
|
|
4326
|
+
? options.now
|
|
4327
|
+
: new Date();
|
|
4328
|
+
const targetLocalDate = formatDateInTimeZone(now, timeZone);
|
|
4329
|
+
// Facts are stored under UTC date directories, while the summary target is
|
|
4330
|
+
// a local calendar day. Scan the UTC-date envelope that overlaps the local
|
|
4331
|
+
// day, then filter parseable fact timestamps to that configured local day.
|
|
4332
|
+
const datesToScan = utcDateKeysForLocalDay(now, timeZone);
|
|
4183
4333
|
const factsBaseDir = path.join(storage.dir, "facts");
|
|
4184
4334
|
const MAX_CHARS = 100_000;
|
|
4185
4335
|
|
|
@@ -4206,13 +4356,21 @@ export class Orchestrator {
|
|
|
4206
4356
|
.slice(colonIdx + 1)
|
|
4207
4357
|
.trim();
|
|
4208
4358
|
}
|
|
4359
|
+
const created = fm.created || "unknown";
|
|
4360
|
+
const createdAt = parseFiniteDate(created);
|
|
4361
|
+
if (
|
|
4362
|
+
createdAt &&
|
|
4363
|
+
formatDateInTimeZone(createdAt, timeZone) !== targetLocalDate
|
|
4364
|
+
) {
|
|
4365
|
+
continue;
|
|
4366
|
+
}
|
|
4209
4367
|
facts.push({
|
|
4210
4368
|
path: fullPath,
|
|
4211
4369
|
frontmatter: {
|
|
4212
4370
|
id: fm.id || path.basename(entry.name, ".md"),
|
|
4213
4371
|
category: (fm.category as any) || "fact",
|
|
4214
|
-
created
|
|
4215
|
-
updated: fm.updated ||
|
|
4372
|
+
created,
|
|
4373
|
+
updated: fm.updated || created,
|
|
4216
4374
|
source: fm.source || "unknown",
|
|
4217
4375
|
confidence: parseFloat(fm.confidence || "0.8"),
|
|
4218
4376
|
confidenceTier: (fm.confidenceTier as any) || "implied",
|
|
@@ -4230,9 +4388,10 @@ export class Orchestrator {
|
|
|
4230
4388
|
}
|
|
4231
4389
|
|
|
4232
4390
|
// Sort facts by created timestamp (most recent last) so truncation keeps newest
|
|
4233
|
-
facts.sort((a, b) =>
|
|
4234
|
-
a.frontmatter.created
|
|
4235
|
-
|
|
4391
|
+
facts.sort((a, b) => {
|
|
4392
|
+
if (a.frontmatter.created === b.frontmatter.created) return 0;
|
|
4393
|
+
return a.frontmatter.created < b.frontmatter.created ? -1 : 1;
|
|
4394
|
+
});
|
|
4236
4395
|
|
|
4237
4396
|
// --- Read hourly summaries for the scanned dates ---
|
|
4238
4397
|
const hourlySummaries: string[] = [];
|
|
@@ -4245,8 +4404,14 @@ export class Orchestrator {
|
|
|
4245
4404
|
const summaryFile = path.join(hourlyBaseDir, sk.name, `${date}.md`);
|
|
4246
4405
|
try {
|
|
4247
4406
|
const raw = await readFile(summaryFile, "utf-8");
|
|
4248
|
-
|
|
4249
|
-
|
|
4407
|
+
const filtered = filterHourlySummaryMarkdownForLocalDay(
|
|
4408
|
+
raw,
|
|
4409
|
+
date,
|
|
4410
|
+
timeZone,
|
|
4411
|
+
targetLocalDate,
|
|
4412
|
+
);
|
|
4413
|
+
if (filtered) {
|
|
4414
|
+
hourlySummaries.push(filtered);
|
|
4250
4415
|
}
|
|
4251
4416
|
} catch {
|
|
4252
4417
|
// No summary file for this session/date
|
|
@@ -4284,7 +4449,7 @@ export class Orchestrator {
|
|
|
4284
4449
|
}
|
|
4285
4450
|
|
|
4286
4451
|
log.info(
|
|
4287
|
-
`gatherTodayFacts: collected ${facts.length} facts, ${hourlySummaries.length} hourly summaries (${formatted.length} chars)`,
|
|
4452
|
+
`gatherTodayFacts: collected ${facts.length} facts, ${hourlySummaries.length} hourly summaries for ${targetLocalDate} (${timeZone}, ${formatted.length} chars)`,
|
|
4288
4453
|
);
|
|
4289
4454
|
|
|
4290
4455
|
return formatted;
|
package/src/qmd.test.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
2
4
|
import test from "node:test";
|
|
3
5
|
|
|
4
6
|
import { parseConfig } from "./config.js";
|
|
@@ -148,6 +150,39 @@ test("post-install auto-upgrade probes PATH qmd before stale fallback paths", ()
|
|
|
148
150
|
]);
|
|
149
151
|
});
|
|
150
152
|
|
|
153
|
+
test("QmdClient preserves configured qmdPath diagnostics when all probes fail", async () => {
|
|
154
|
+
const { QmdClient } = await import("./qmd.js");
|
|
155
|
+
const originalPath = process.env.PATH;
|
|
156
|
+
const originalWindowsPath = process.env.Path;
|
|
157
|
+
const missingQmdPath = path.join(
|
|
158
|
+
os.tmpdir(),
|
|
159
|
+
`remnic-missing-qmd-${process.pid}-${Date.now()}`,
|
|
160
|
+
"qmd.cmd",
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
process.env.PATH = "";
|
|
164
|
+
process.env.Path = "";
|
|
165
|
+
try {
|
|
166
|
+
const client = new QmdClient("test-collection", 10, {
|
|
167
|
+
qmdPath: missingQmdPath,
|
|
168
|
+
qmdFallbackPaths: [],
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
assert.equal(await client.probe(), false);
|
|
172
|
+
const status = client.debugStatus();
|
|
173
|
+
|
|
174
|
+
assert.ok(status.includes(`cliPath=${missingQmdPath}`), status);
|
|
175
|
+
assert.ok(status.includes("cliPathSource=configured"), status);
|
|
176
|
+
assert.ok(status.includes("cliProbeError="), status);
|
|
177
|
+
assert.ok(!status.includes("/opt/homebrew/bin/qmd"), status);
|
|
178
|
+
} finally {
|
|
179
|
+
if (originalPath === undefined) delete process.env.PATH;
|
|
180
|
+
else process.env.PATH = originalPath;
|
|
181
|
+
if (originalWindowsPath === undefined) delete process.env.Path;
|
|
182
|
+
else process.env.Path = originalWindowsPath;
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
151
186
|
test("QmdClient applies chunk strategy to normal and forced embed args", async () => {
|
|
152
187
|
const { QmdClient } = await import("./qmd.js");
|
|
153
188
|
const client = new QmdClient("test", 5, {
|
package/src/qmd.ts
CHANGED
|
@@ -42,6 +42,8 @@ export interface QmdClientOptions {
|
|
|
42
42
|
qmdSearchStrategy?: QmdSearchStrategy;
|
|
43
43
|
/** Subprocess fallback command; default "query" keeps LLM expansion. Issue #1335. */
|
|
44
44
|
qmdSubprocessStrategy?: QmdSubprocessStrategy;
|
|
45
|
+
/** Override automatic qmd fallback probes; defaults to known install paths. */
|
|
46
|
+
qmdFallbackPaths?: string[];
|
|
45
47
|
/** Per-call daemon search timeout in ms; default 8000. Issue #1335. */
|
|
46
48
|
qmdDaemonTimeoutMs?: number;
|
|
47
49
|
}
|
|
@@ -1248,6 +1250,7 @@ export class QmdClient implements SearchBackend {
|
|
|
1248
1250
|
private readonly qmdIndexName?: string;
|
|
1249
1251
|
private readonly qmdSearchStrategy: QmdSearchStrategy;
|
|
1250
1252
|
private readonly qmdSubprocessStrategy: QmdSubprocessStrategy;
|
|
1253
|
+
private readonly qmdFallbackPaths: string[];
|
|
1251
1254
|
private readonly daemonTimeoutMs: number;
|
|
1252
1255
|
private readonly qmdRuntimeEnv: QmdRuntimeEnv;
|
|
1253
1256
|
private qmdPathSource: "auto-path" | "auto-fallback" | "configured" = "auto-path";
|
|
@@ -1303,6 +1306,9 @@ export class QmdClient implements SearchBackend {
|
|
|
1303
1306
|
: "hybrid";
|
|
1304
1307
|
// Default "query" keeps `qmd query` (LLM expansion + rerank) per gotcha #7. Issue #1335.
|
|
1305
1308
|
this.qmdSubprocessStrategy = opts?.qmdSubprocessStrategy === "search" ? "search" : "query";
|
|
1309
|
+
this.qmdFallbackPaths = opts?.qmdFallbackPaths
|
|
1310
|
+
? opts.qmdFallbackPaths.map((candidate) => candidate.trim()).filter(Boolean)
|
|
1311
|
+
: QMD_FALLBACK_PATHS;
|
|
1306
1312
|
// Default 8000ms preserves the historical hardcoded daemon timeout. Issue #1335.
|
|
1307
1313
|
// Floor of 1000ms avoids absurdly small values; callers wanting CPU-only HyDE
|
|
1308
1314
|
// headroom can raise this (e.g. 20000) without code changes.
|
|
@@ -1404,9 +1410,16 @@ export class QmdClient implements SearchBackend {
|
|
|
1404
1410
|
}
|
|
1405
1411
|
|
|
1406
1412
|
private async probeCli(): Promise<boolean> {
|
|
1413
|
+
let configuredProbeFailure: string | null = null;
|
|
1407
1414
|
const markProbeFailure = (err: unknown): void => {
|
|
1408
1415
|
this.lastCliProbeError = err instanceof Error ? err.message : String(err);
|
|
1409
1416
|
};
|
|
1417
|
+
const restoreConfiguredProbeFailure = (): void => {
|
|
1418
|
+
if (!this.configuredQmdPath || !configuredProbeFailure) return;
|
|
1419
|
+
this.qmdPath = this.configuredQmdPath;
|
|
1420
|
+
this.qmdPathSource = "configured";
|
|
1421
|
+
this.lastCliProbeError = configuredProbeFailure;
|
|
1422
|
+
};
|
|
1410
1423
|
const recordProbeSuccess = async (
|
|
1411
1424
|
result: { stdout: string; stderr: string },
|
|
1412
1425
|
qmdPath: string,
|
|
@@ -1428,6 +1441,7 @@ export class QmdClient implements SearchBackend {
|
|
|
1428
1441
|
return true;
|
|
1429
1442
|
} catch (err) {
|
|
1430
1443
|
markProbeFailure(err);
|
|
1444
|
+
configuredProbeFailure = this.lastCliProbeError;
|
|
1431
1445
|
// Do not hard-fail here: fall through to PATH/fallback probing.
|
|
1432
1446
|
// This keeps recall healthy even when configured path is stale.
|
|
1433
1447
|
this.logCliProbeWarning(
|
|
@@ -1444,7 +1458,7 @@ export class QmdClient implements SearchBackend {
|
|
|
1444
1458
|
} catch (err) {
|
|
1445
1459
|
markProbeFailure(err);
|
|
1446
1460
|
// Try fallback paths
|
|
1447
|
-
for (const fallbackPath of
|
|
1461
|
+
for (const fallbackPath of this.qmdFallbackPaths) {
|
|
1448
1462
|
try {
|
|
1449
1463
|
const result = await runQmd(["--version"], QMD_PROBE_TIMEOUT_MS, fallbackPath, undefined, this.qmdRuntimeEnv);
|
|
1450
1464
|
await recordProbeSuccess(result, fallbackPath, "auto-fallback");
|
|
@@ -1456,6 +1470,7 @@ export class QmdClient implements SearchBackend {
|
|
|
1456
1470
|
}
|
|
1457
1471
|
}
|
|
1458
1472
|
this.available = false;
|
|
1473
|
+
restoreConfiguredProbeFailure();
|
|
1459
1474
|
return false;
|
|
1460
1475
|
}
|
|
1461
1476
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
|
|
4
|
+
import { launchProcessSync } from "./child-process.js";
|
|
5
|
+
|
|
6
|
+
test("launchProcessSync executes commands through the shared process wrapper", () => {
|
|
7
|
+
const result = launchProcessSync(
|
|
8
|
+
process.execPath,
|
|
9
|
+
["-e", "process.stdout.write('ok')"],
|
|
10
|
+
{ encoding: "utf8" },
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
assert.equal(result.status, 0);
|
|
14
|
+
assert.equal(result.stdout, "ok");
|
|
15
|
+
});
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import crossSpawn from "cross-spawn";
|
|
2
2
|
|
|
3
|
-
const require = createRequire(import.meta.url);
|
|
4
|
-
const PROCESS_MODULE_NAME = `node:${"child"}_${"process"}`;
|
|
5
|
-
|
|
6
|
-
type ProcessModule = Record<string, unknown>;
|
|
7
3
|
type ProcessOptions = Record<string, unknown>;
|
|
8
4
|
type ProcessStream = {
|
|
9
5
|
destroyed?: boolean;
|
|
@@ -22,9 +18,12 @@ type ProcessResult = {
|
|
|
22
18
|
stderr?: string;
|
|
23
19
|
};
|
|
24
20
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
type SpawnApi = {
|
|
22
|
+
(command: string, args?: readonly string[], options?: ProcessOptions): CommandChildProcess;
|
|
23
|
+
sync: (command: string, args: readonly string[], options: ProcessOptions) => ProcessResult;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const spawnApi = crossSpawn as unknown as SpawnApi;
|
|
28
27
|
|
|
29
28
|
export type CommandChildProcess = {
|
|
30
29
|
exitCode?: number | null;
|
|
@@ -43,13 +42,7 @@ export function launchProcess(
|
|
|
43
42
|
args: string[],
|
|
44
43
|
options?: ProcessOptions,
|
|
45
44
|
): CommandChildProcess {
|
|
46
|
-
|
|
47
|
-
const launch = moduleApi["spawn"] as (
|
|
48
|
-
command: string,
|
|
49
|
-
args?: readonly string[],
|
|
50
|
-
options?: ProcessOptions,
|
|
51
|
-
) => CommandChildProcess;
|
|
52
|
-
return launch(command, args, options);
|
|
45
|
+
return spawnApi(command, args, options);
|
|
53
46
|
}
|
|
54
47
|
|
|
55
48
|
export function launchProcessSync(
|
|
@@ -57,11 +50,5 @@ export function launchProcessSync(
|
|
|
57
50
|
args: string[],
|
|
58
51
|
options: ProcessOptions,
|
|
59
52
|
): ProcessResult {
|
|
60
|
-
|
|
61
|
-
const launchSync = moduleApi["spawnSync"] as (
|
|
62
|
-
command: string,
|
|
63
|
-
args: readonly string[],
|
|
64
|
-
options: ProcessOptions,
|
|
65
|
-
) => ProcessResult;
|
|
66
|
-
return launchSync(command, args, options);
|
|
53
|
+
return spawnApi.sync(command, args, options);
|
|
67
54
|
}
|
package/src/types.ts
CHANGED
|
@@ -945,6 +945,8 @@ export interface PluginConfig {
|
|
|
945
945
|
// Hourly summaries
|
|
946
946
|
hourlySummariesEnabled: boolean;
|
|
947
947
|
daySummaryEnabled: boolean;
|
|
948
|
+
/** Timezone override for the day summary cron (issue #1475). */
|
|
949
|
+
daySummaryTimezone?: string;
|
|
948
950
|
/** If true, Engram may attempt to auto-register an hourly summary cron job (default off). */
|
|
949
951
|
hourlySummaryCronAutoRegister: boolean;
|
|
950
952
|
/** If true, Engram may attempt to auto-register the nightly governance cron job (default off). */
|
|
@@ -1280,6 +1282,8 @@ export interface PluginConfig {
|
|
|
1280
1282
|
* consolidation. When set, this chain is resolved through gateway providers
|
|
1281
1283
|
* instead of using gatewayAgentId or agents.defaults.model. All task paths
|
|
1282
1284
|
* route through the shared `gatewayTaskChainOptions` helper.
|
|
1285
|
+
* LCM summarization is the one fast-tier exception: if `fastGatewayAgentId`
|
|
1286
|
+
* is configured, LCM uses that persona before falling back to this chain.
|
|
1283
1287
|
*
|
|
1284
1288
|
* Only takes effect when `modelSource: "gateway"`; ignored (with a startup
|
|
1285
1289
|
* warning) under `modelSource: "plugin"`. See issue #1365 / PR #1370.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/maintenance/memory-governance-cron.ts"],"sourcesContent":["import { mkdir, readFile, rename, rm, stat, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nconst DAY_SUMMARY_CRON_ID = \"engram-day-summary\";\nconst GOVERNANCE_CRON_ID = \"engram-nightly-governance\";\nconst PROCEDURAL_MINING_CRON_ID = \"engram-procedural-mining\";\nconst CONTRADICTION_SCAN_CRON_ID = \"engram-contradiction-scan\";\nconst PATTERN_REINFORCEMENT_CRON_ID = \"engram-pattern-reinforcement\";\nconst GRAPH_EDGE_DECAY_CRON_ID = \"engram-graph-edge-decay\";\n\ntype CronJobsShape =\n | Array<Record<string, unknown>>\n | {\n jobs: Array<Record<string, unknown>>;\n [key: string]: unknown;\n };\n\nasync function acquireCronJobsLock(jobsPath: string): Promise<() => Promise<void>> {\n const lockPath = `${jobsPath}.lock`;\n const start = Date.now();\n const staleMs = 30_000;\n const timeoutMs = 5_000;\n await mkdir(path.dirname(lockPath), { recursive: true });\n\n while (Date.now() - start < timeoutMs) {\n try {\n await mkdir(lockPath);\n return async () => {\n try {\n await rm(lockPath, { recursive: true, force: true });\n } catch {\n // Lock cleanup should not fail cron registration.\n }\n };\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code !== \"EEXIST\") {\n throw error;\n }\n try {\n const lockStat = await stat(lockPath);\n if (Date.now() - lockStat.mtimeMs > staleMs) {\n await rm(lockPath, { recursive: true, force: true });\n continue;\n }\n } catch {\n // Lock may have been released between stat/rm attempts.\n }\n await new Promise((resolve) => setTimeout(resolve, 25));\n }\n }\n\n throw new Error(`cron jobs lock acquisition timed out after ${timeoutMs}ms`);\n}\n\nfunction parseCronJobsShape(raw: string): { parsed: CronJobsShape; jobs: Array<Record<string, unknown>> } {\n const parsed = JSON.parse(raw) as CronJobsShape;\n const jobs = Array.isArray(parsed) ? parsed : Array.isArray(parsed.jobs) ? parsed.jobs : null;\n if (!jobs) {\n throw new Error(\"jobs.json has unexpected structure\");\n }\n return { parsed, jobs };\n}\n\nasync function writeCronJobsAtomic(jobsPath: string, value: CronJobsShape): Promise<void> {\n const tempPath = `${jobsPath}.${process.pid}.${Date.now()}.tmp`;\n await writeFile(tempPath, JSON.stringify(value, null, 2) + \"\\n\", \"utf-8\");\n await rename(tempPath, jobsPath);\n}\n\nexport async function ensureCronJob(\n jobsPath: string,\n jobId: string,\n buildJob: () => Record<string, unknown>,\n options: { updateExisting?: boolean; updateFields?: string[] } = {},\n): Promise<{ created: boolean; updated: boolean; jobId: string }> {\n const releaseLock = await acquireCronJobsLock(jobsPath);\n try {\n const raw = await readFile(jobsPath, \"utf-8\");\n const { parsed, jobs } = parseCronJobsShape(raw);\n\n const existingIndex = jobs.findIndex((job) => job.id === jobId);\n if (existingIndex >= 0) {\n if (!options.updateExisting) {\n return { created: false, updated: false, jobId };\n }\n\n const desired = buildJob();\n const existing = jobs[existingIndex];\n const next = mergeCronJobUpdate(existing, desired, options.updateFields);\n if (stableJson(existing) === stableJson(next)) {\n return { created: false, updated: false, jobId };\n }\n\n jobs[existingIndex] = next;\n const output = Array.isArray(parsed) ? jobs : { ...parsed, jobs };\n await writeCronJobsAtomic(jobsPath, output);\n return { created: false, updated: true, jobId };\n }\n\n jobs.push(buildJob());\n const output = Array.isArray(parsed) ? jobs : { ...parsed, jobs };\n await writeCronJobsAtomic(jobsPath, output);\n return { created: true, updated: false, jobId };\n } finally {\n await releaseLock();\n }\n}\n\nfunction mergeCronJobUpdate(\n existing: Record<string, unknown>,\n desired: Record<string, unknown>,\n updateFields?: string[],\n): Record<string, unknown> {\n if (!updateFields) {\n return desired;\n }\n\n const next = { ...existing };\n for (const field of updateFields) {\n if (Object.prototype.hasOwnProperty.call(desired, field)) {\n next[field] = desired[field];\n } else {\n delete next[field];\n }\n }\n return next;\n}\n\nfunction stableJson(value: unknown): string {\n if (Array.isArray(value)) {\n return `[${value.map((item) => stableJson(item)).join(\",\")}]`;\n }\n if (value && typeof value === \"object\") {\n const record = value as Record<string, unknown>;\n return `{${Object.keys(record)\n .sort()\n .map((key) => `${JSON.stringify(key)}:${stableJson(record[key])}`)\n .join(\",\")}}`;\n }\n return JSON.stringify(value);\n}\n\nexport async function ensureDaySummaryCron(\n jobsPath: string,\n options: {\n timezone: string;\n agentId?: string;\n },\n): Promise<{ created: boolean; jobId: string }> {\n const agentId =\n typeof options.agentId === \"string\" && options.agentId.trim().length > 0\n ? options.agentId.trim()\n : \"main\";\n\n return ensureCronJob(jobsPath, DAY_SUMMARY_CRON_ID, () => ({\n id: DAY_SUMMARY_CRON_ID,\n agentId,\n name: \"Remnic Day Summary (auto)\",\n enabled: true,\n schedule: {\n kind: \"cron\",\n expr: \"47 23 * * *\",\n tz: options.timezone,\n },\n sessionTarget: \"isolated\",\n wakeMode: \"now\",\n payload: {\n kind: \"agentTurn\",\n timeoutSeconds: 900,\n thinking: \"off\",\n message:\n \"You are OpenClaw automation. Call tool engram.day_summary with empty params (it will auto-gather today's facts). If successful output exactly NO_REPLY. On error output one concise line. Do NOT use message tool.\",\n },\n delivery: { mode: \"none\" },\n }));\n}\n\nexport async function ensureNightlyGovernanceCron(\n jobsPath: string,\n options: {\n timezone: string;\n agentId?: string;\n recentDays?: number;\n maxMemories?: number;\n batchSize?: number;\n scheduleExpr?: string;\n },\n): Promise<{ created: boolean; jobId: string }> {\n const recentDays =\n typeof options.recentDays === \"number\" && Number.isFinite(options.recentDays)\n ? Math.max(1, Math.floor(options.recentDays))\n : 2;\n const maxMemories =\n typeof options.maxMemories === \"number\" && Number.isFinite(options.maxMemories)\n ? Math.max(1, Math.floor(options.maxMemories))\n : 500;\n const batchSize =\n typeof options.batchSize === \"number\" && Number.isFinite(options.batchSize)\n ? Math.max(1, Math.floor(options.batchSize))\n : 100;\n const scheduleExpr =\n typeof options.scheduleExpr === \"string\" && options.scheduleExpr.trim().length > 0\n ? options.scheduleExpr.trim()\n : \"23 2 * * *\";\n const agentId =\n typeof options.agentId === \"string\" && options.agentId.trim().length > 0\n ? options.agentId.trim()\n : \"main\";\n\n return ensureCronJob(jobsPath, GOVERNANCE_CRON_ID, () => ({\n id: GOVERNANCE_CRON_ID,\n agentId,\n name: \"Remnic Nightly Governance (batched)\",\n enabled: true,\n schedule: {\n kind: \"cron\",\n expr: scheduleExpr,\n tz: options.timezone,\n },\n sessionTarget: \"isolated\",\n wakeMode: \"now\",\n payload: {\n kind: \"agentTurn\",\n timeoutSeconds: 900,\n thinking: \"off\",\n message:\n \"You are OpenClaw automation. Call the tool `engram.memory_governance_run` with params \" +\n `{\"mode\": \"apply\", \"recentDays\": ${recentDays}, \"maxMemories\": ${maxMemories}, \"batchSize\": ${batchSize}}` +\n \". If successful output exactly NO_REPLY. On error output one concise line. Do NOT use message tool.\",\n },\n delivery: { mode: \"none\" },\n }));\n}\n\nexport async function ensureProceduralMiningCron(\n jobsPath: string,\n options: {\n timezone: string;\n agentId?: string;\n scheduleExpr?: string;\n },\n): Promise<{ created: boolean; jobId: string }> {\n const scheduleExpr =\n typeof options.scheduleExpr === \"string\" && options.scheduleExpr.trim().length > 0\n ? options.scheduleExpr.trim()\n : \"17 3 * * *\";\n const agentId =\n typeof options.agentId === \"string\" && options.agentId.trim().length > 0\n ? options.agentId.trim()\n : \"main\";\n\n return ensureCronJob(jobsPath, PROCEDURAL_MINING_CRON_ID, () => ({\n id: PROCEDURAL_MINING_CRON_ID,\n agentId,\n name: \"Remnic Procedural Mining (nightly)\",\n enabled: true,\n schedule: {\n kind: \"cron\",\n expr: scheduleExpr,\n tz: options.timezone,\n },\n sessionTarget: \"isolated\",\n wakeMode: \"now\",\n payload: {\n kind: \"agentTurn\",\n timeoutSeconds: 900,\n thinking: \"off\",\n message:\n \"You are OpenClaw automation. Call tool `engram.procedure_mining_run` with empty params. \" +\n \"If successful output exactly NO_REPLY. On error output one concise line. Do NOT use message tool.\",\n },\n delivery: { mode: \"none\" },\n }));\n}\n\nexport async function ensureContradictionScanCron(\n jobsPath: string,\n options: {\n timezone: string;\n agentId?: string;\n scheduleExpr?: string;\n },\n): Promise<{ created: boolean; jobId: string }> {\n const scheduleExpr =\n typeof options.scheduleExpr === \"string\" && options.scheduleExpr.trim().length > 0\n ? options.scheduleExpr.trim()\n : \"37 3 * * *\";\n const agentId =\n typeof options.agentId === \"string\" && options.agentId.trim().length > 0\n ? options.agentId.trim()\n : \"main\";\n\n return ensureCronJob(jobsPath, CONTRADICTION_SCAN_CRON_ID, () => ({\n id: CONTRADICTION_SCAN_CRON_ID,\n agentId,\n name: \"Remnic Contradiction Scan (nightly)\",\n enabled: true,\n schedule: {\n kind: \"cron\",\n expr: scheduleExpr,\n tz: options.timezone,\n },\n sessionTarget: \"isolated\",\n wakeMode: \"now\",\n payload: {\n kind: \"agentTurn\",\n timeoutSeconds: 900,\n thinking: \"off\",\n message:\n \"You are OpenClaw automation. Call tool `engram.contradiction_scan_run` with empty params. \" +\n \"If successful output exactly NO_REPLY. On error output one concise line. Do NOT use message tool.\",\n },\n delivery: { mode: \"none\" },\n }));\n}\n\nexport async function ensurePatternReinforcementCron(\n jobsPath: string,\n options: {\n timezone: string;\n agentId?: string;\n /**\n * Cron expression. Default `\"53 4 * * 0\"` — Sunday 04:53 in the\n * configured timezone, deliberately offset from sibling crons\n * (`23 2 * * *`, `17 3 * * *`, `37 3 * * *`) so the maintenance\n * passes don't pile up on the same minute.\n */\n scheduleExpr?: string;\n },\n): Promise<{ created: boolean; jobId: string }> {\n const scheduleExpr =\n typeof options.scheduleExpr === \"string\" && options.scheduleExpr.trim().length > 0\n ? options.scheduleExpr.trim()\n : \"53 4 * * 0\";\n const agentId =\n typeof options.agentId === \"string\" && options.agentId.trim().length > 0\n ? options.agentId.trim()\n : \"main\";\n\n return ensureCronJob(jobsPath, PATTERN_REINFORCEMENT_CRON_ID, () => ({\n id: PATTERN_REINFORCEMENT_CRON_ID,\n agentId,\n name: \"Remnic Pattern Reinforcement (weekly)\",\n enabled: true,\n schedule: {\n kind: \"cron\",\n expr: scheduleExpr,\n tz: options.timezone,\n },\n sessionTarget: \"isolated\",\n wakeMode: \"now\",\n payload: {\n kind: \"agentTurn\",\n timeoutSeconds: 900,\n thinking: \"off\",\n message:\n \"You are OpenClaw automation. Call tool `engram.pattern_reinforcement_run` with empty params. \" +\n \"If successful output exactly NO_REPLY. On error output one concise line. Do NOT use message tool.\",\n },\n delivery: { mode: \"none\" },\n }));\n}\n\n/**\n * Register a cron job that runs the graph-edge-decay maintenance pass\n * (issue #681 PR 2/3). Default schedule is weekly on Sunday 04:13 — the\n * cadence is configurable via `scheduleExpr`. Cadence is also expressible\n * in milliseconds via `Config.graphEdgeDecayCadenceMs`; the orchestrator\n * picks the right cron expression for sub-daily, daily, or weekly cadence.\n */\nexport async function ensureGraphEdgeDecayCron(\n jobsPath: string,\n options: {\n timezone: string;\n agentId?: string;\n scheduleExpr?: string;\n },\n): Promise<{ created: boolean; jobId: string }> {\n const scheduleExpr =\n typeof options.scheduleExpr === \"string\" && options.scheduleExpr.trim().length > 0\n ? options.scheduleExpr.trim()\n : \"13 4 * * 0\";\n const agentId =\n typeof options.agentId === \"string\" && options.agentId.trim().length > 0\n ? options.agentId.trim()\n : \"main\";\n\n const scheduleLabel = graphEdgeDecayScheduleLabel(scheduleExpr);\n\n return ensureCronJob(\n jobsPath,\n GRAPH_EDGE_DECAY_CRON_ID,\n () => ({\n id: GRAPH_EDGE_DECAY_CRON_ID,\n agentId,\n // Schedule label reflects the actual cron expression (`daily` /\n // `weekly` / `custom`) so cron dashboards do not show \"weekly\"\n // when the schedule is in fact daily — Cursor review on PR #729.\n name: `Remnic Graph Edge Decay (${scheduleLabel})`,\n enabled: true,\n schedule: {\n kind: \"cron\",\n expr: scheduleExpr,\n tz: options.timezone,\n },\n sessionTarget: \"isolated\",\n wakeMode: \"now\",\n payload: {\n kind: \"agentTurn\",\n timeoutSeconds: 900,\n thinking: \"off\",\n message:\n \"You are OpenClaw automation. Call tool `engram.graph_edge_decay_run` with empty params. \" +\n \"If successful output exactly NO_REPLY. On error output one concise line. Do NOT use message tool.\",\n },\n delivery: { mode: \"none\" },\n }),\n {\n updateExisting: true,\n updateFields: [\"name\", \"schedule\"],\n },\n );\n}\n\n/**\n * Pick a cron expression that approximates a cadence in milliseconds.\n *\n * - cadence <= 1 day → daily at 04:13. Sub-daily cadence is not natively\n * expressible in 5-field cron without hour/minute step patterns; daily\n * preserves the previous conservative behavior for those values.\n * - cadence > 1 day → weekly on Sunday 04:13. Multi-day intervals like\n * 2-6 days cannot be represented accurately by a portable 5-field cron\n * expression, and daily would run more often than requested.\n *\n * Operators who need finer-grained control should set `scheduleExpr`\n * directly via `ensureGraphEdgeDecayCron`.\n */\nexport function graphEdgeDecayCadenceToCronExpr(cadenceMs: number): string {\n if (!Number.isFinite(cadenceMs) || cadenceMs <= 0) {\n return \"13 4 * * 0\";\n }\n const day = 24 * 60 * 60 * 1000;\n if (cadenceMs <= day) return \"13 4 * * *\";\n return \"13 4 * * 0\";\n}\n\n/**\n * Derive a human-friendly schedule label (\"daily\" / \"weekly\" / \"custom\")\n * for the given cron expression. Used to keep the cron job name in\n * sync with the actual schedule (Cursor review on PR #729).\n */\nfunction graphEdgeDecayScheduleLabel(scheduleExpr: string): string {\n if (scheduleExpr === \"13 4 * * *\") return \"daily\";\n if (scheduleExpr === \"13 4 * * 0\") return \"weekly\";\n return \"custom\";\n}\n"],"mappings":"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","names":["output"]}
|