@remnic/core 9.15.2 → 9.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-admin-ops-surface.d.ts +8 -8
- package/dist/access-admin-ops-surface.js +7 -7
- package/dist/access-boundary.d.ts +8 -8
- package/dist/access-boundary.js +7 -7
- package/dist/access-cli.js +31 -30
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +8 -8
- package/dist/access-http.js +11 -11
- package/dist/access-identity-continuity-surface.d.ts +6 -6
- package/dist/access-identity-continuity-surface.js +7 -7
- package/dist/access-lcm-surface.d.ts +8 -8
- package/dist/access-lcm-surface.js +7 -7
- package/dist/access-mcp.d.ts +8 -8
- package/dist/access-mcp.js +10 -10
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/{access-namespace-preflight-B9e5GnyZ.d.ts → access-namespace-preflight-DwhEFYy3.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +7 -7
- package/dist/access-observe-write-surface.d.ts +8 -8
- package/dist/access-observe-write-surface.js +7 -7
- package/dist/access-operations-batch.js +8 -8
- package/dist/access-operations.d.ts +11 -11
- package/dist/access-operations.js +9 -9
- package/dist/access-recall-concurrency.d.ts +8 -8
- package/dist/access-recall-concurrency.js +7 -7
- package/dist/access-recall-response.d.ts +8 -8
- package/dist/access-recall-response.js +7 -7
- package/dist/access-recall-surface.d.ts +8 -8
- package/dist/access-recall-surface.js +7 -7
- package/dist/access-schema.d.ts +76 -76
- package/dist/{access-service-DgmJd_A-.d.ts → access-service-CSq1Wp2Z.d.ts} +6 -6
- package/dist/access-service.d.ts +8 -8
- package/dist/access-service.js +7 -7
- package/dist/access-surface-catalog.d.ts +8 -8
- 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 +2 -1
- package/dist/active-recall.js.map +1 -1
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +6 -6
- package/dist/briefing.d.ts +2 -2
- package/dist/briefing.js +3 -3
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer.d.ts +2 -2
- package/dist/bulk-import/index.d.ts +3 -3
- package/dist/calibration.d.ts +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/{catalog-g_dfT6oL.d.ts → catalog-B9uga4sG.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +4 -4
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-IRX5UCGN.js → chunk-2OH3ZRYH.js} +3 -3
- package/dist/{chunk-YXTSUIXC.js → chunk-5EKOBRCA.js} +6 -1
- package/dist/chunk-5EKOBRCA.js.map +1 -0
- package/dist/{chunk-42DJZZUB.js → chunk-7LXSPIVG.js} +2 -2
- package/dist/{chunk-47WFEKT7.js → chunk-7PK4W4RN.js} +2 -2
- package/dist/{chunk-O7ACJAHR.js → chunk-7YJLCNPF.js} +2 -2
- package/dist/{chunk-6EJL2O3G.js → chunk-AFH7PIFU.js} +2 -2
- package/dist/{chunk-JN4N2TMQ.js → chunk-ANGUOE2N.js} +7 -7
- package/dist/{chunk-GQOKO77V.js → chunk-AWT7SCWQ.js} +2 -2
- package/dist/{chunk-PXJEL55X.js → chunk-BIRPR5N7.js} +2 -2
- package/dist/{chunk-NAY25BGS.js → chunk-BTK2PIFL.js} +6 -6
- package/dist/{chunk-SYCVU556.js → chunk-C2RMQRPN.js} +3 -3
- package/dist/{chunk-T4EAQPQQ.js → chunk-G2WCVFDC.js} +2 -2
- package/dist/{chunk-UJIBXOJA.js → chunk-GEDBTSYU.js} +5 -5
- package/dist/{chunk-CXFVZ3PK.js → chunk-GRJ5UOFO.js} +84 -658
- package/dist/chunk-GRJ5UOFO.js.map +1 -0
- package/dist/chunk-GSQASASV.js +429 -0
- package/dist/chunk-GSQASASV.js.map +1 -0
- package/dist/{chunk-WIKOHCQZ.js → chunk-I6ODVWQB.js} +2 -2
- package/dist/{chunk-H4MDUPER.js → chunk-IZHXSLO7.js} +2 -2
- package/dist/{chunk-RRS5CDB6.js → chunk-JOCXI2O5.js} +2 -2
- package/dist/{chunk-EEUZQQRJ.js → chunk-KGKYTCDO.js} +2 -2
- package/dist/{chunk-JTW4TGPV.js → chunk-L3HAUO7K.js} +2 -2
- package/dist/{chunk-P53HGVJS.js → chunk-LMVAIZR5.js} +2 -2
- package/dist/{chunk-KLXG4BBO.js → chunk-M6TZQDTX.js} +2 -2
- package/dist/{chunk-FJ5IQ3SI.js → chunk-N7DK67GZ.js} +1 -1
- package/dist/chunk-N7DK67GZ.js.map +1 -0
- package/dist/{chunk-U6QWUFSM.js → chunk-OXT5TQDS.js} +2 -2
- package/dist/{chunk-T4OTDMX4.js → chunk-QDTEH6BS.js} +2 -2
- package/dist/{chunk-5MDE5MIC.js → chunk-SESF64QR.js} +2 -2
- package/dist/{chunk-4E5H4FPC.js → chunk-TUNI4GB3.js} +3 -3
- package/dist/{chunk-XJT6LNLY.js → chunk-V6LYAXGW.js} +834 -25
- package/dist/chunk-V6LYAXGW.js.map +1 -0
- package/dist/{chunk-5HFIHWOF.js → chunk-VLIY55Q5.js} +17 -17
- package/dist/{chunk-BNH2VVWP.js → chunk-WIS5JTJR.js} +2 -2
- package/dist/{chunk-OOGGUXAG.js → chunk-WQ2I6ODB.js} +2 -2
- package/dist/{cli-BXCd2cgK.d.ts → cli-DYLGXxuu.d.ts} +5 -5
- package/dist/cli.d.ts +10 -10
- package/dist/cli.js +25 -24
- package/dist/compounding/engine.d.ts +2 -2
- 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 +2 -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 +3 -3
- package/dist/consolidation-provenance-check.d.ts +2 -2
- package/dist/consolidation-undo.d.ts +2 -2
- package/dist/contradiction/index.d.ts +2 -2
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- 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 +2 -2
- package/dist/entity-retrieval.js +3 -3
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +6 -6
- package/dist/extraction-faithfulness.d.ts +1 -1
- 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.d.ts +1 -1
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/graph-dashboard-diff.d.ts +1 -1
- package/dist/graph-dashboard-key.d.ts +1 -1
- package/dist/graph-dashboard-parser.d.ts +1 -1
- package/dist/graph-edge-reinforcement.d.ts +1 -1
- package/dist/graph-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/importers/index.d.ts +1 -1
- package/dist/in-flight-reads.d.ts +1 -1
- package/dist/index.d.ts +693 -503
- package/dist/index.js +70 -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-model-endpoint.d.ts +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.d.ts +2 -2
- 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 +8 -8
- 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 +2 -2
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +3 -3
- package/dist/namespaces/migrate.js +4 -4
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/storage.d.ts +3 -3
- package/dist/namespaces/storage.js +3 -3
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/offline-sync-impression-drain.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +2 -2
- package/dist/operator-toolkit.js +9 -8
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/maintenance.d.ts +10 -7
- package/dist/orchestration/maintenance.js +7 -6
- package/dist/{orchestrator-BHfWtvEJ.d.ts → orchestrator-BHx-DWCa.d.ts} +7 -5
- package/dist/orchestrator.d.ts +6 -6
- package/dist/orchestrator.js +31 -30
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-CBYcnmDC.d.ts → pipeline-DDHSE876.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/proactive-contention.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/{qmd-BIq-jrV4.d.ts → qmd-C4WVh6BM.d.ts} +1 -1
- package/dist/qmd-preflight.d.ts +2 -2
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +2 -2
- package/dist/recall-concurrency-config.d.ts +1 -1
- 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-memory-map.d.ts +1 -1
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-timings.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/replay/normalizers/chatgpt.d.ts +1 -1
- package/dist/replay/normalizers/claude.d.ts +1 -1
- package/dist/replay/normalizers/openclaw.d.ts +1 -1
- package/dist/replay/normalizers/shared.d.ts +1 -1
- package/dist/replay/runner.d.ts +1 -1
- package/dist/replay/types.d.ts +1 -1
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +3 -2
- package/dist/retrieval-agents.d.ts +2 -2
- 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/salvage-envelope.d.ts +1 -1
- package/dist/schemas.d.ts +76 -76
- package/dist/{scope-profiles-DDno-uvy.d.ts → scope-profiles-EqMXwzCz.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/index.d.ts +1 -1
- 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-7RIJFDwY.d.ts → semantic-consolidation-CYpvwmQc.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 +9 -9
- package/dist/signal.d.ts +1 -1
- package/dist/{storage-DkphZiOJ.d.ts → storage-Dxd7zVZH.d.ts} +1 -1
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +2 -2
- package/dist/summarizer.d.ts +1 -1
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/temporal-supersession.d.ts +2 -2
- package/dist/temporal-timeline-recall.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 +2 -2
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/transfer/types.d.ts +66 -66
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-2q_MRFcM.d.ts → types-OUCJALBK.d.ts} +98 -1
- 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/dist/write-envelope.d.ts +1 -1
- package/package.json +2 -2
- package/src/activity/config.test.ts +113 -0
- package/src/activity/config.ts +111 -0
- package/src/activity/digest.ts +2 -2
- package/src/activity/index.ts +6 -0
- package/src/activity/pipeline.test.ts +379 -0
- package/src/activity/pipeline.ts +223 -0
- package/src/activity/reindex.test.ts +97 -0
- package/src/activity/reindex.ts +40 -0
- package/src/activity/runner.test.ts +452 -0
- package/src/activity/runner.ts +237 -0
- package/src/activity/scheduler.test.ts +303 -0
- package/src/activity/scheduler.ts +147 -0
- package/src/activity/source-client.test.ts +208 -0
- package/src/activity/source-client.ts +159 -0
- package/src/activity/sync-registration.test.ts +134 -0
- package/src/activity/sync-registration.ts +139 -0
- package/src/activity/types.ts +17 -0
- package/src/config.test.ts +19 -0
- package/src/config.ts +3 -0
- package/src/orchestration/maintenance.test.ts +42 -42
- package/src/orchestration/maintenance.ts +37 -5
- package/src/orchestration/orchestrator-helpers.ts +36 -1
- package/src/orchestration/recall-internal.ts +6 -6
- package/src/orchestration/recall-search-pipeline.ts +15 -7
- package/src/orchestrator-background-writes.test.ts +2 -2
- package/src/orchestrator.ts +3 -3
- package/src/types.ts +4 -1
- package/dist/chunk-CXFVZ3PK.js.map +0 -1
- package/dist/chunk-FJ5IQ3SI.js.map +0 -1
- package/dist/chunk-XJT6LNLY.js.map +0 -1
- package/dist/chunk-YXTSUIXC.js.map +0 -1
- /package/dist/{chunk-IRX5UCGN.js.map → chunk-2OH3ZRYH.js.map} +0 -0
- /package/dist/{chunk-42DJZZUB.js.map → chunk-7LXSPIVG.js.map} +0 -0
- /package/dist/{chunk-47WFEKT7.js.map → chunk-7PK4W4RN.js.map} +0 -0
- /package/dist/{chunk-O7ACJAHR.js.map → chunk-7YJLCNPF.js.map} +0 -0
- /package/dist/{chunk-6EJL2O3G.js.map → chunk-AFH7PIFU.js.map} +0 -0
- /package/dist/{chunk-JN4N2TMQ.js.map → chunk-ANGUOE2N.js.map} +0 -0
- /package/dist/{chunk-GQOKO77V.js.map → chunk-AWT7SCWQ.js.map} +0 -0
- /package/dist/{chunk-PXJEL55X.js.map → chunk-BIRPR5N7.js.map} +0 -0
- /package/dist/{chunk-NAY25BGS.js.map → chunk-BTK2PIFL.js.map} +0 -0
- /package/dist/{chunk-SYCVU556.js.map → chunk-C2RMQRPN.js.map} +0 -0
- /package/dist/{chunk-T4EAQPQQ.js.map → chunk-G2WCVFDC.js.map} +0 -0
- /package/dist/{chunk-UJIBXOJA.js.map → chunk-GEDBTSYU.js.map} +0 -0
- /package/dist/{chunk-WIKOHCQZ.js.map → chunk-I6ODVWQB.js.map} +0 -0
- /package/dist/{chunk-H4MDUPER.js.map → chunk-IZHXSLO7.js.map} +0 -0
- /package/dist/{chunk-RRS5CDB6.js.map → chunk-JOCXI2O5.js.map} +0 -0
- /package/dist/{chunk-EEUZQQRJ.js.map → chunk-KGKYTCDO.js.map} +0 -0
- /package/dist/{chunk-JTW4TGPV.js.map → chunk-L3HAUO7K.js.map} +0 -0
- /package/dist/{chunk-P53HGVJS.js.map → chunk-LMVAIZR5.js.map} +0 -0
- /package/dist/{chunk-KLXG4BBO.js.map → chunk-M6TZQDTX.js.map} +0 -0
- /package/dist/{chunk-U6QWUFSM.js.map → chunk-OXT5TQDS.js.map} +0 -0
- /package/dist/{chunk-T4OTDMX4.js.map → chunk-QDTEH6BS.js.map} +0 -0
- /package/dist/{chunk-5MDE5MIC.js.map → chunk-SESF64QR.js.map} +0 -0
- /package/dist/{chunk-4E5H4FPC.js.map → chunk-TUNI4GB3.js.map} +0 -0
- /package/dist/{chunk-5HFIHWOF.js.map → chunk-VLIY55Q5.js.map} +0 -0
- /package/dist/{chunk-BNH2VVWP.js.map → chunk-WIS5JTJR.js.map} +0 -0
- /package/dist/{chunk-OOGGUXAG.js.map → chunk-WQ2I6ODB.js.map} +0 -0
|
@@ -9,11 +9,20 @@ import {
|
|
|
9
9
|
} from "./chunk-666A3MOW.js";
|
|
10
10
|
import {
|
|
11
11
|
rebuildMemoryLifecycleLedger
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-LMVAIZR5.js";
|
|
13
13
|
import {
|
|
14
14
|
planNamespaceMaintenance,
|
|
15
15
|
runNamespaceMaintenanceBatchPlan
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-SESF64QR.js";
|
|
17
|
+
import {
|
|
18
|
+
activityDayWindow,
|
|
19
|
+
activityDigestPath,
|
|
20
|
+
composeActivityDigestBody,
|
|
21
|
+
composeActivityDigestMeta,
|
|
22
|
+
isValidActivityDate,
|
|
23
|
+
serializeActivityDigest,
|
|
24
|
+
validateActivityBaseUrl
|
|
25
|
+
} from "./chunk-GSQASASV.js";
|
|
17
26
|
import {
|
|
18
27
|
STATE_FILE_MAX_DECRYPT_BYTES,
|
|
19
28
|
drainPendingLifecycleLedgerIfAny,
|
|
@@ -26,6 +35,10 @@ import {
|
|
|
26
35
|
assertPathInsideRoot,
|
|
27
36
|
listContainedSpillFiles
|
|
28
37
|
} from "./chunk-GCASGYIO.js";
|
|
38
|
+
import {
|
|
39
|
+
displayErrorDetail,
|
|
40
|
+
openBetterSqlite3
|
|
41
|
+
} from "./chunk-RL6NEVXT.js";
|
|
29
42
|
import {
|
|
30
43
|
resolveHomeDir
|
|
31
44
|
} from "./chunk-JUC24CTX.js";
|
|
@@ -51,8 +64,770 @@ import {
|
|
|
51
64
|
|
|
52
65
|
// src/orchestration/maintenance.ts
|
|
53
66
|
import { existsSync } from "fs";
|
|
54
|
-
import { appendFile, lstat, mkdir, readFile, readdir, realpath, stat, writeFile } from "fs/promises";
|
|
67
|
+
import { appendFile, lstat, mkdir as mkdir3, readFile as readFile2, readdir, realpath, stat, writeFile as writeFile2 } from "fs/promises";
|
|
68
|
+
import path3 from "path";
|
|
69
|
+
|
|
70
|
+
// src/activity/reindex.ts
|
|
71
|
+
async function refreshActivityIndex(qmd, collection, signal) {
|
|
72
|
+
const execution = signal === void 0 ? void 0 : { signal };
|
|
73
|
+
if (typeof qmd.updateCollectionStrict === "function" && collection.trim().length > 0) {
|
|
74
|
+
await qmd.updateCollectionStrict(collection, execution);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (typeof qmd.updateStrict === "function") {
|
|
78
|
+
await qmd.updateStrict(execution);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
await qmd.update(execution);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/activity/store.ts
|
|
85
|
+
import { mkdirSync } from "fs";
|
|
86
|
+
import { mkdir } from "fs/promises";
|
|
55
87
|
import path from "path";
|
|
88
|
+
var ACTIVITY_SCHEMA_VERSION = 1;
|
|
89
|
+
var MAX_SEARCH_RESULTS = 100;
|
|
90
|
+
function activityDatabasePath(memoryDir) {
|
|
91
|
+
return path.join(memoryDir, "state", "activity.sqlite");
|
|
92
|
+
}
|
|
93
|
+
async function ensureActivityStateDir(memoryDir) {
|
|
94
|
+
await mkdir(path.join(memoryDir, "state"), { recursive: true });
|
|
95
|
+
}
|
|
96
|
+
function openActivityDatabase(memoryDir) {
|
|
97
|
+
mkdirSync(path.join(memoryDir, "state"), { recursive: true });
|
|
98
|
+
const db = openBetterSqlite3(activityDatabasePath(memoryDir));
|
|
99
|
+
db.pragma("journal_mode = WAL");
|
|
100
|
+
db.pragma("busy_timeout = 5000");
|
|
101
|
+
db.pragma("synchronous = NORMAL");
|
|
102
|
+
applySchema(db);
|
|
103
|
+
return db;
|
|
104
|
+
}
|
|
105
|
+
function applyActivitySchema(db) {
|
|
106
|
+
applySchema(db);
|
|
107
|
+
}
|
|
108
|
+
function applySchema(db) {
|
|
109
|
+
db.exec(`
|
|
110
|
+
CREATE TABLE IF NOT EXISTS activity_meta (
|
|
111
|
+
key TEXT PRIMARY KEY,
|
|
112
|
+
value TEXT NOT NULL
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
CREATE TABLE IF NOT EXISTS activity_snapshots (
|
|
116
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
117
|
+
machine TEXT NOT NULL,
|
|
118
|
+
captured_at_utc TEXT NOT NULL,
|
|
119
|
+
app_name TEXT NOT NULL,
|
|
120
|
+
window_title TEXT NOT NULL,
|
|
121
|
+
browser_url TEXT,
|
|
122
|
+
text TEXT NOT NULL,
|
|
123
|
+
text_source TEXT NOT NULL,
|
|
124
|
+
content_hash TEXT NOT NULL,
|
|
125
|
+
simhash TEXT
|
|
126
|
+
);
|
|
127
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_activity_snapshots_dedup
|
|
128
|
+
ON activity_snapshots(machine, captured_at_utc, content_hash);
|
|
129
|
+
CREATE INDEX IF NOT EXISTS idx_activity_snapshots_time
|
|
130
|
+
ON activity_snapshots(captured_at_utc, id);
|
|
131
|
+
|
|
132
|
+
CREATE TABLE IF NOT EXISTS activity_sync_state (
|
|
133
|
+
machine TEXT PRIMARY KEY,
|
|
134
|
+
cursor TEXT,
|
|
135
|
+
updated_at_utc TEXT NOT NULL
|
|
136
|
+
);
|
|
137
|
+
`);
|
|
138
|
+
const hasFts = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='activity_snapshots_fts'").get();
|
|
139
|
+
if (!hasFts) {
|
|
140
|
+
db.exec(`
|
|
141
|
+
CREATE VIRTUAL TABLE activity_snapshots_fts USING fts5(
|
|
142
|
+
text, app_name, window_title, browser_url
|
|
143
|
+
);
|
|
144
|
+
`);
|
|
145
|
+
}
|
|
146
|
+
db.prepare("INSERT OR REPLACE INTO activity_meta (key, value) VALUES ('schema_version', ?)").run(
|
|
147
|
+
String(ACTIVITY_SCHEMA_VERSION)
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
function canonicalizeUtc(iso) {
|
|
151
|
+
const parsed = Date.parse(iso);
|
|
152
|
+
return Number.isFinite(parsed) ? new Date(parsed).toISOString() : iso;
|
|
153
|
+
}
|
|
154
|
+
function assertRequiredSnapshotFields(s) {
|
|
155
|
+
const required = [
|
|
156
|
+
["machine", s.machine],
|
|
157
|
+
["app", s.app],
|
|
158
|
+
["windowTitle", s.windowTitle],
|
|
159
|
+
["text", s.text],
|
|
160
|
+
["textSource", s.textSource],
|
|
161
|
+
["contentHash", s.contentHash]
|
|
162
|
+
];
|
|
163
|
+
for (const [field, value] of required) {
|
|
164
|
+
if (typeof value !== "string") {
|
|
165
|
+
throw new RangeError(`activity: snapshot field "${field}" is required (got ${value === null ? "null" : typeof value}).`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (s.machine.length === 0 || s.contentHash.length === 0) {
|
|
169
|
+
throw new RangeError('activity: snapshot "machine" and "contentHash" must be non-empty.');
|
|
170
|
+
}
|
|
171
|
+
if (s.textSource !== "ax" && s.textSource !== "ocr") {
|
|
172
|
+
throw new RangeError(`activity: snapshot "textSource" must be "ax" or "ocr" (got "${s.textSource}").`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function assertValidUtcInstant(iso, what) {
|
|
176
|
+
const m = iso.match(
|
|
177
|
+
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:Z|[+-](?:0\d:[0-5]\d|1[0-3]:[0-5]\d|14:00))$/
|
|
178
|
+
);
|
|
179
|
+
if (m === null || !Number.isFinite(Date.parse(iso))) {
|
|
180
|
+
throw new RangeError(`activity: invalid ${what} "${iso}".`);
|
|
181
|
+
}
|
|
182
|
+
const [year, month, day, hour, minute, second] = m.slice(1).map(Number);
|
|
183
|
+
const daysInMonth = month >= 1 && month <= 12 ? new Date(Date.UTC(year, month, 0)).getUTCDate() : 0;
|
|
184
|
+
if (day < 1 || day > daysInMonth || hour > 23 || minute > 59 || second > 59) {
|
|
185
|
+
throw new RangeError(`activity: ${what} "${iso}" is not a real calendar instant.`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function isRecord(value) {
|
|
189
|
+
return typeof value === "object" && value !== null;
|
|
190
|
+
}
|
|
191
|
+
function str(value) {
|
|
192
|
+
return typeof value === "string" ? value : "";
|
|
193
|
+
}
|
|
194
|
+
function optStr(value) {
|
|
195
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
196
|
+
}
|
|
197
|
+
function ftsMatchFor(query) {
|
|
198
|
+
if (typeof query !== "string") return null;
|
|
199
|
+
const tokens = query.match(/[\p{L}\p{N}_]+/gu);
|
|
200
|
+
if (!tokens || tokens.length === 0) return null;
|
|
201
|
+
return tokens.map((token) => `"${token}"`).join(" ");
|
|
202
|
+
}
|
|
203
|
+
function rowToSnapshot(row) {
|
|
204
|
+
if (!isRecord(row)) {
|
|
205
|
+
throw new Error("activity store: unexpected non-object row");
|
|
206
|
+
}
|
|
207
|
+
const textSource = row.text_source === "ocr" ? "ocr" : "ax";
|
|
208
|
+
return {
|
|
209
|
+
id: typeof row.id === "number" ? row.id : void 0,
|
|
210
|
+
machine: str(row.machine),
|
|
211
|
+
capturedAtUtc: str(row.captured_at_utc),
|
|
212
|
+
app: str(row.app_name),
|
|
213
|
+
windowTitle: str(row.window_title),
|
|
214
|
+
...optStr(row.browser_url) !== void 0 ? { browserUrl: optStr(row.browser_url) } : {},
|
|
215
|
+
text: str(row.text),
|
|
216
|
+
textSource,
|
|
217
|
+
contentHash: str(row.content_hash),
|
|
218
|
+
...optStr(row.simhash) !== void 0 ? { simhash: optStr(row.simhash) } : {}
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
var ActivityStore = class _ActivityStore {
|
|
222
|
+
db;
|
|
223
|
+
constructor(db) {
|
|
224
|
+
this.db = db;
|
|
225
|
+
}
|
|
226
|
+
static open(memoryDir) {
|
|
227
|
+
return new _ActivityStore(openActivityDatabase(memoryDir));
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Insert a snapshot, idempotent on (machine, captured_at_utc, content_hash):
|
|
231
|
+
* the same screen content recurring at a *different* time is kept; only an
|
|
232
|
+
* exact re-ingestion of the same capture dedups. Returns `inserted: false`
|
|
233
|
+
* for a duplicate. The FTS row is written only on a real insert (atomically),
|
|
234
|
+
* so it never drifts from the base table.
|
|
235
|
+
*/
|
|
236
|
+
insertSnapshot(snapshot) {
|
|
237
|
+
const runInsert = this.db.transaction((s) => {
|
|
238
|
+
assertValidUtcInstant(s.capturedAtUtc, "capture timestamp");
|
|
239
|
+
assertRequiredSnapshotFields(s);
|
|
240
|
+
const capturedAtUtc = canonicalizeUtc(s.capturedAtUtc);
|
|
241
|
+
const info = this.db.prepare(
|
|
242
|
+
`INSERT OR IGNORE INTO activity_snapshots
|
|
243
|
+
(machine, captured_at_utc, app_name, window_title, browser_url, text, text_source, content_hash, simhash)
|
|
244
|
+
VALUES (@machine, @captured_at_utc, @app_name, @window_title, @browser_url, @text, @text_source, @content_hash, @simhash)`
|
|
245
|
+
).run({
|
|
246
|
+
machine: s.machine,
|
|
247
|
+
captured_at_utc: capturedAtUtc,
|
|
248
|
+
app_name: s.app,
|
|
249
|
+
window_title: s.windowTitle,
|
|
250
|
+
browser_url: s.browserUrl ?? null,
|
|
251
|
+
text: s.text,
|
|
252
|
+
text_source: s.textSource,
|
|
253
|
+
content_hash: s.contentHash,
|
|
254
|
+
simhash: s.simhash ?? null
|
|
255
|
+
});
|
|
256
|
+
if (info.changes === 0) {
|
|
257
|
+
const existing = this.db.prepare("SELECT id FROM activity_snapshots WHERE machine = ? AND captured_at_utc = ? AND content_hash = ?").get(s.machine, capturedAtUtc, s.contentHash);
|
|
258
|
+
const id2 = isRecord(existing) && typeof existing.id === "number" ? existing.id : -1;
|
|
259
|
+
return { inserted: false, id: id2 };
|
|
260
|
+
}
|
|
261
|
+
const id = Number(info.lastInsertRowid);
|
|
262
|
+
this.db.prepare(
|
|
263
|
+
`INSERT INTO activity_snapshots_fts (rowid, text, app_name, window_title, browser_url)
|
|
264
|
+
VALUES (?, ?, ?, ?, ?)`
|
|
265
|
+
).run(id, s.text, s.app, s.windowTitle, s.browserUrl ?? "");
|
|
266
|
+
return { inserted: true, id };
|
|
267
|
+
});
|
|
268
|
+
return runInsert(snapshot);
|
|
269
|
+
}
|
|
270
|
+
/** Snapshots whose capture instant is in the half-open [start, end) window. */
|
|
271
|
+
listSnapshotsForDay(machine, startUtcInclusive, endUtcExclusive) {
|
|
272
|
+
assertValidUtcInstant(startUtcInclusive, "range start");
|
|
273
|
+
assertValidUtcInstant(endUtcExclusive, "range end");
|
|
274
|
+
const start = canonicalizeUtc(startUtcInclusive);
|
|
275
|
+
const end = canonicalizeUtc(endUtcExclusive);
|
|
276
|
+
const rows = machine === null ? this.db.prepare(
|
|
277
|
+
`SELECT * FROM activity_snapshots
|
|
278
|
+
WHERE captured_at_utc >= ? AND captured_at_utc < ?
|
|
279
|
+
ORDER BY captured_at_utc ASC, id ASC`
|
|
280
|
+
).all(start, end) : this.db.prepare(
|
|
281
|
+
`SELECT * FROM activity_snapshots
|
|
282
|
+
WHERE machine = ? AND captured_at_utc >= ? AND captured_at_utc < ?
|
|
283
|
+
ORDER BY captured_at_utc ASC, id ASC`
|
|
284
|
+
).all(machine, start, end);
|
|
285
|
+
return rows.map(rowToSnapshot);
|
|
286
|
+
}
|
|
287
|
+
getCursor(machine) {
|
|
288
|
+
const row = this.db.prepare("SELECT cursor FROM activity_sync_state WHERE machine = ?").get(machine);
|
|
289
|
+
return isRecord(row) && typeof row.cursor === "string" ? row.cursor : null;
|
|
290
|
+
}
|
|
291
|
+
setCursor(machine, cursor, updatedAtUtc = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
292
|
+
this.db.prepare(
|
|
293
|
+
`INSERT INTO activity_sync_state (machine, cursor, updated_at_utc)
|
|
294
|
+
VALUES (?, ?, ?)
|
|
295
|
+
ON CONFLICT(machine) DO UPDATE SET cursor = excluded.cursor, updated_at_utc = excluded.updated_at_utc`
|
|
296
|
+
).run(machine, cursor, updatedAtUtc);
|
|
297
|
+
}
|
|
298
|
+
/** Full-text search over snapshot text/app/window/url; newest first. */
|
|
299
|
+
searchSnapshots(query, limit) {
|
|
300
|
+
const capped = Number.isInteger(limit) && limit > 0 ? Math.min(limit, MAX_SEARCH_RESULTS) : 20;
|
|
301
|
+
const match = ftsMatchFor(query);
|
|
302
|
+
if (match === null) return [];
|
|
303
|
+
const rows = this.db.prepare(
|
|
304
|
+
`SELECT s.* FROM activity_snapshots_fts f
|
|
305
|
+
JOIN activity_snapshots s ON s.id = f.rowid
|
|
306
|
+
WHERE activity_snapshots_fts MATCH ?
|
|
307
|
+
ORDER BY s.captured_at_utc DESC, s.id DESC
|
|
308
|
+
LIMIT ?`
|
|
309
|
+
).all(match, capped);
|
|
310
|
+
return rows.map(rowToSnapshot);
|
|
311
|
+
}
|
|
312
|
+
/** Retention: drop snapshots captured strictly before `cutoffUtc`. */
|
|
313
|
+
pruneOlderThan(cutoffUtc) {
|
|
314
|
+
assertValidUtcInstant(cutoffUtc, "prune cutoff");
|
|
315
|
+
const ids = this.db.prepare("SELECT id FROM activity_snapshots WHERE captured_at_utc < ?").all(canonicalizeUtc(cutoffUtc)).map((row) => isRecord(row) && typeof row.id === "number" ? row.id : -1).filter((id) => id >= 0);
|
|
316
|
+
const deleteFts = this.db.prepare("DELETE FROM activity_snapshots_fts WHERE rowid = ?");
|
|
317
|
+
const deleteRow = this.db.prepare("DELETE FROM activity_snapshots WHERE id = ?");
|
|
318
|
+
const tx = this.db.transaction((rowIds) => {
|
|
319
|
+
for (const id of rowIds) {
|
|
320
|
+
deleteFts.run(id);
|
|
321
|
+
deleteRow.run(id);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
tx(ids);
|
|
325
|
+
return ids.length;
|
|
326
|
+
}
|
|
327
|
+
close() {
|
|
328
|
+
this.db.close();
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
// src/activity/pipeline.ts
|
|
333
|
+
import { mkdir as mkdir2, readFile, rename, writeFile } from "fs/promises";
|
|
334
|
+
import { randomUUID } from "crypto";
|
|
335
|
+
import path2 from "path";
|
|
336
|
+
var MAX_SYNC_PAGES = 1e4;
|
|
337
|
+
function activityCursorKey(machine, date) {
|
|
338
|
+
return `${machine}\0${date}`;
|
|
339
|
+
}
|
|
340
|
+
var digestLocks = /* @__PURE__ */ new Map();
|
|
341
|
+
function withDigestLock(key, fn) {
|
|
342
|
+
const prior = digestLocks.get(key) ?? Promise.resolve();
|
|
343
|
+
const next = prior.then(fn, fn);
|
|
344
|
+
const tail = next.then(
|
|
345
|
+
() => void 0,
|
|
346
|
+
() => void 0
|
|
347
|
+
);
|
|
348
|
+
digestLocks.set(key, tail);
|
|
349
|
+
void tail.then(() => {
|
|
350
|
+
if (digestLocks.get(key) === tail) digestLocks.delete(key);
|
|
351
|
+
});
|
|
352
|
+
return next;
|
|
353
|
+
}
|
|
354
|
+
function snapshotForMachine(snapshot, machine) {
|
|
355
|
+
return {
|
|
356
|
+
machine,
|
|
357
|
+
capturedAtUtc: snapshot.capturedAtUtc,
|
|
358
|
+
app: snapshot.app,
|
|
359
|
+
windowTitle: snapshot.windowTitle,
|
|
360
|
+
...snapshot.browserUrl === void 0 ? {} : { browserUrl: snapshot.browserUrl },
|
|
361
|
+
text: snapshot.text,
|
|
362
|
+
textSource: snapshot.textSource,
|
|
363
|
+
contentHash: snapshot.contentHash,
|
|
364
|
+
...snapshot.simhash === void 0 ? {} : { simhash: snapshot.simhash }
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
async function writeDigestIfChanged(memoryDir, date, serialized) {
|
|
368
|
+
const target = activityDigestPath(memoryDir, date);
|
|
369
|
+
try {
|
|
370
|
+
if (await readFile(target, "utf8") === serialized) return false;
|
|
371
|
+
} catch (error) {
|
|
372
|
+
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") throw error;
|
|
373
|
+
}
|
|
374
|
+
await mkdir2(path2.dirname(target), { recursive: true, mode: 448 });
|
|
375
|
+
const temporary = `${target}.${process.pid}.${randomUUID()}.tmp`;
|
|
376
|
+
await writeFile(temporary, serialized, { encoding: "utf8", mode: 384 });
|
|
377
|
+
await rename(temporary, target);
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
async function syncActivitySource(source, options) {
|
|
381
|
+
if (!isValidActivityDate(options.date)) {
|
|
382
|
+
throw new RangeError(`Invalid activity date "${options.date}"; expected a real YYYY-MM-DD day.`);
|
|
383
|
+
}
|
|
384
|
+
if (source.machineLabel.trim().length === 0) {
|
|
385
|
+
throw new RangeError("activity source machine label must not be empty");
|
|
386
|
+
}
|
|
387
|
+
const maxPages = options.maxPages ?? MAX_SYNC_PAGES;
|
|
388
|
+
if (!Number.isInteger(maxPages) || maxPages < 1) {
|
|
389
|
+
throw new RangeError("activity sync maxPages must be a positive integer");
|
|
390
|
+
}
|
|
391
|
+
const cursorKey = activityCursorKey(source.machineLabel, options.date);
|
|
392
|
+
let cursor = options.store.getCursor(cursorKey);
|
|
393
|
+
let pages = 0;
|
|
394
|
+
let fetched = 0;
|
|
395
|
+
let inserted = 0;
|
|
396
|
+
let duplicates = 0;
|
|
397
|
+
const seenCursors = /* @__PURE__ */ new Set();
|
|
398
|
+
let completed = false;
|
|
399
|
+
while (pages < maxPages) {
|
|
400
|
+
options.signal?.throwIfAborted();
|
|
401
|
+
const page = await source.fetchSnapshots({
|
|
402
|
+
date: options.date,
|
|
403
|
+
timezone: options.timezone,
|
|
404
|
+
cursor,
|
|
405
|
+
signal: options.signal
|
|
406
|
+
});
|
|
407
|
+
pages += 1;
|
|
408
|
+
fetched += page.snapshots.length;
|
|
409
|
+
for (const snapshot of page.snapshots) {
|
|
410
|
+
const result = options.store.insertSnapshot(snapshotForMachine(snapshot, source.machineLabel));
|
|
411
|
+
if (result.inserted) inserted += 1;
|
|
412
|
+
else duplicates += 1;
|
|
413
|
+
}
|
|
414
|
+
if (page.nextCursor === null) {
|
|
415
|
+
completed = true;
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
if (typeof page.nextCursor !== "string" || page.nextCursor.length === 0) {
|
|
419
|
+
throw new TypeError("activity source returned an invalid cursor");
|
|
420
|
+
}
|
|
421
|
+
if (seenCursors.has(page.nextCursor)) {
|
|
422
|
+
throw new Error("activity source returned a repeated cursor");
|
|
423
|
+
}
|
|
424
|
+
seenCursors.add(page.nextCursor);
|
|
425
|
+
cursor = page.nextCursor;
|
|
426
|
+
}
|
|
427
|
+
if (!completed) {
|
|
428
|
+
throw new Error(`activity source exceeded ${maxPages} pages`);
|
|
429
|
+
}
|
|
430
|
+
options.signal?.throwIfAborted();
|
|
431
|
+
const digestWritten = await withDigestLock(`${options.memoryDir}\0${options.date}`, async () => {
|
|
432
|
+
options.signal?.throwIfAborted();
|
|
433
|
+
const { startUtc, endUtc } = activityDayWindow(options.date, options.timezone);
|
|
434
|
+
const snapshots = options.store.listSnapshotsForDay(null, startUtc, endUtc);
|
|
435
|
+
const body = composeActivityDigestBody(options.date, options.timezone, snapshots);
|
|
436
|
+
const machines = [...new Set(snapshots.map((snapshot) => snapshot.machine))].sort(
|
|
437
|
+
(a, b) => a < b ? -1 : a > b ? 1 : 0
|
|
438
|
+
);
|
|
439
|
+
const serialized = serializeActivityDigest(composeActivityDigestMeta(options.date, machines, snapshots, body), body);
|
|
440
|
+
return writeDigestIfChanged(options.memoryDir, options.date, serialized);
|
|
441
|
+
});
|
|
442
|
+
options.signal?.throwIfAborted();
|
|
443
|
+
options.store.setCursor(cursorKey, cursor);
|
|
444
|
+
let reindexError;
|
|
445
|
+
if (digestWritten && options.afterWrites !== void 0) {
|
|
446
|
+
try {
|
|
447
|
+
await options.afterWrites(options.signal);
|
|
448
|
+
} catch (error) {
|
|
449
|
+
reindexError = error instanceof Error && error.message.startsWith("QMD ") ? error.message : displayErrorDetail(error) || "reindex failed";
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return {
|
|
453
|
+
machine: source.machineLabel,
|
|
454
|
+
fetched,
|
|
455
|
+
inserted,
|
|
456
|
+
duplicates,
|
|
457
|
+
cursor,
|
|
458
|
+
digestWritten,
|
|
459
|
+
...reindexError === void 0 ? {} : { reindexError }
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// src/activity/source-client.ts
|
|
464
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
465
|
+
function isRecord2(value) {
|
|
466
|
+
return typeof value === "object" && value !== null;
|
|
467
|
+
}
|
|
468
|
+
function stringField(value, field) {
|
|
469
|
+
const result = value[field];
|
|
470
|
+
if (typeof result !== "string" || result.length === 0) {
|
|
471
|
+
throw new TypeError(`activity source response has no valid ${field}`);
|
|
472
|
+
}
|
|
473
|
+
return result;
|
|
474
|
+
}
|
|
475
|
+
function contentStringField(value, field) {
|
|
476
|
+
const result = value[field];
|
|
477
|
+
if (typeof result !== "string") {
|
|
478
|
+
throw new TypeError(`activity source response has invalid ${field}`);
|
|
479
|
+
}
|
|
480
|
+
return result;
|
|
481
|
+
}
|
|
482
|
+
function optionalStringField(value, field) {
|
|
483
|
+
const result = value[field];
|
|
484
|
+
if (result === void 0 || result === null) return void 0;
|
|
485
|
+
if (typeof result !== "string") throw new TypeError(`activity source response has invalid ${field}`);
|
|
486
|
+
return result;
|
|
487
|
+
}
|
|
488
|
+
function snapshotFromWire(value, machine) {
|
|
489
|
+
if (!isRecord2(value)) throw new TypeError("activity source returned a non-object snapshot");
|
|
490
|
+
const textSource = stringField(value, "textSource");
|
|
491
|
+
if (textSource !== "ax" && textSource !== "ocr") {
|
|
492
|
+
throw new TypeError("activity source response has invalid textSource");
|
|
493
|
+
}
|
|
494
|
+
const browserUrl = optionalStringField(value, "browserUrl");
|
|
495
|
+
const simhash = optionalStringField(value, "simhash");
|
|
496
|
+
return {
|
|
497
|
+
machine,
|
|
498
|
+
capturedAtUtc: stringField(value, "capturedAtUtc"),
|
|
499
|
+
app: contentStringField(value, "app"),
|
|
500
|
+
windowTitle: contentStringField(value, "windowTitle"),
|
|
501
|
+
...browserUrl === void 0 ? {} : { browserUrl },
|
|
502
|
+
text: contentStringField(value, "text"),
|
|
503
|
+
textSource,
|
|
504
|
+
contentHash: stringField(value, "contentHash"),
|
|
505
|
+
...simhash === void 0 ? {} : { simhash }
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
function responsePage(value, machine) {
|
|
509
|
+
if (!isRecord2(value) || !Array.isArray(value.snapshots)) {
|
|
510
|
+
throw new TypeError("activity source response has no snapshots array");
|
|
511
|
+
}
|
|
512
|
+
const nextCursor = value.nextCursor === void 0 ? null : value.nextCursor;
|
|
513
|
+
if (nextCursor !== null && typeof nextCursor !== "string") {
|
|
514
|
+
throw new TypeError("activity source response has invalid nextCursor");
|
|
515
|
+
}
|
|
516
|
+
return {
|
|
517
|
+
snapshots: value.snapshots.map((snapshot) => snapshotFromWire(snapshot, machine)),
|
|
518
|
+
nextCursor
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
function requestUrl(baseUrl, path4, query) {
|
|
522
|
+
const url = new URL(path4, baseUrl);
|
|
523
|
+
const parts = Object.entries(query).filter((entry) => entry[1] !== void 0).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
|
|
524
|
+
url.search = parts.join("&");
|
|
525
|
+
return url.toString();
|
|
526
|
+
}
|
|
527
|
+
var ActivityHttpSourceClient = class {
|
|
528
|
+
machineLabel;
|
|
529
|
+
baseUrl;
|
|
530
|
+
token;
|
|
531
|
+
timeoutMs;
|
|
532
|
+
constructor(options) {
|
|
533
|
+
if (options.machineLabel.trim().length === 0) throw new RangeError("activity source machine label must not be empty");
|
|
534
|
+
if (options.timeoutMs !== void 0 && (!Number.isFinite(options.timeoutMs) || options.timeoutMs <= 0)) {
|
|
535
|
+
throw new RangeError("activity source timeoutMs must be a positive number");
|
|
536
|
+
}
|
|
537
|
+
this.machineLabel = options.machineLabel;
|
|
538
|
+
this.baseUrl = validateActivityBaseUrl(options.baseUrl).toString();
|
|
539
|
+
this.token = options.token;
|
|
540
|
+
this.timeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
541
|
+
}
|
|
542
|
+
async request(url, signal) {
|
|
543
|
+
const timeout = AbortSignal.timeout(this.timeoutMs);
|
|
544
|
+
const composed = signal === void 0 ? timeout : AbortSignal.any([signal, timeout]);
|
|
545
|
+
const response = await fetch(url, {
|
|
546
|
+
headers: this.token === void 0 ? void 0 : { authorization: `Bearer ${this.token}` },
|
|
547
|
+
signal: composed
|
|
548
|
+
});
|
|
549
|
+
if (!response.ok) throw new Error(`activity source HTTP ${response.status}`);
|
|
550
|
+
return response;
|
|
551
|
+
}
|
|
552
|
+
async verify(signal) {
|
|
553
|
+
try {
|
|
554
|
+
await this.request(requestUrl(this.baseUrl, "/v1/health", {}), signal);
|
|
555
|
+
return { ok: true };
|
|
556
|
+
} catch (error) {
|
|
557
|
+
const detail = error instanceof Error && error.message.startsWith("activity source HTTP ") ? error.message.slice("activity source ".length) : displayErrorDetail(error) || "request failed";
|
|
558
|
+
return { ok: false, detail };
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
async fetchSnapshots(opts) {
|
|
562
|
+
const response = await this.request(
|
|
563
|
+
requestUrl(this.baseUrl, "/v1/snapshots", {
|
|
564
|
+
date: opts.date,
|
|
565
|
+
timezone: opts.timezone,
|
|
566
|
+
cursor: opts.cursor ?? void 0
|
|
567
|
+
}),
|
|
568
|
+
opts.signal
|
|
569
|
+
);
|
|
570
|
+
return responsePage(await response.json(), this.machineLabel);
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
// src/activity/runner.ts
|
|
575
|
+
function localDateInTimezone(instant, timezone) {
|
|
576
|
+
try {
|
|
577
|
+
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
578
|
+
timeZone: timezone,
|
|
579
|
+
year: "numeric",
|
|
580
|
+
month: "2-digit",
|
|
581
|
+
day: "2-digit"
|
|
582
|
+
}).formatToParts(instant);
|
|
583
|
+
const get = (type) => parts.find((part) => part.type === type)?.value ?? "";
|
|
584
|
+
return `${get("year")}-${get("month")}-${get("day")}`;
|
|
585
|
+
} catch {
|
|
586
|
+
return instant.toISOString().slice(0, 10);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
function previousIsoDate(date) {
|
|
590
|
+
const parsed = /* @__PURE__ */ new Date(`${date}T00:00:00Z`);
|
|
591
|
+
parsed.setUTCDate(parsed.getUTCDate() - 1);
|
|
592
|
+
return parsed.toISOString().slice(0, 10);
|
|
593
|
+
}
|
|
594
|
+
function resolveActivitySyncDates(syncDays, timezone, now) {
|
|
595
|
+
const dates = [];
|
|
596
|
+
let cursor = localDateInTimezone(now, timezone);
|
|
597
|
+
for (let count = 0; count < syncDays; count++) {
|
|
598
|
+
dates.unshift(cursor);
|
|
599
|
+
cursor = previousIsoDate(cursor);
|
|
600
|
+
}
|
|
601
|
+
return dates;
|
|
602
|
+
}
|
|
603
|
+
function sanitizedSyncError(error) {
|
|
604
|
+
return error instanceof Error && /^(activity|Invalid activity)/.test(error.message) ? error.message : displayErrorDetail(error) || "activity sync failed";
|
|
605
|
+
}
|
|
606
|
+
async function runActivitySyncOnce(options) {
|
|
607
|
+
const ranAt = typeof options.now === "function" ? options.now() : options.now ?? /* @__PURE__ */ new Date();
|
|
608
|
+
if (!options.config.enabled) {
|
|
609
|
+
return {
|
|
610
|
+
ranAt: ranAt.toISOString(),
|
|
611
|
+
enabled: false,
|
|
612
|
+
ranCount: 0,
|
|
613
|
+
errorCount: 0,
|
|
614
|
+
reindexErrorCount: 0,
|
|
615
|
+
totalInserted: 0,
|
|
616
|
+
results: []
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
const dates = resolveActivitySyncDates(options.config.syncDays, options.config.timezone, ranAt);
|
|
620
|
+
const createSourceClient = options.createSourceClient ?? ((source) => new ActivityHttpSourceClient({
|
|
621
|
+
machineLabel: source.machineLabel,
|
|
622
|
+
baseUrl: source.baseUrl,
|
|
623
|
+
...source.token === void 0 ? {} : { token: source.token }
|
|
624
|
+
}));
|
|
625
|
+
const ownStore = options.store === void 0;
|
|
626
|
+
const store = options.store ?? ActivityStore.open(options.memoryDir);
|
|
627
|
+
const results = [];
|
|
628
|
+
try {
|
|
629
|
+
for (const sourceConfig of options.config.sources) {
|
|
630
|
+
const item = {
|
|
631
|
+
machineLabel: sourceConfig.machineLabel,
|
|
632
|
+
ran: false,
|
|
633
|
+
fetched: 0,
|
|
634
|
+
inserted: 0,
|
|
635
|
+
duplicates: 0,
|
|
636
|
+
digestsWritten: 0,
|
|
637
|
+
// Cursors are now per (machine, date); report the last synced day's
|
|
638
|
+
// cursor. Null until a day advances it.
|
|
639
|
+
cursor: null
|
|
640
|
+
};
|
|
641
|
+
try {
|
|
642
|
+
const client = createSourceClient(sourceConfig);
|
|
643
|
+
for (const date of dates) {
|
|
644
|
+
try {
|
|
645
|
+
const result = await syncActivitySource(client, {
|
|
646
|
+
date,
|
|
647
|
+
timezone: options.config.timezone,
|
|
648
|
+
memoryDir: options.memoryDir,
|
|
649
|
+
store,
|
|
650
|
+
signal: options.signal,
|
|
651
|
+
...options.reindexSearch === void 0 ? {} : { afterWrites: options.reindexSearch }
|
|
652
|
+
});
|
|
653
|
+
item.fetched += result.fetched;
|
|
654
|
+
item.inserted += result.inserted;
|
|
655
|
+
item.duplicates += result.duplicates;
|
|
656
|
+
if (result.digestWritten) item.digestsWritten += 1;
|
|
657
|
+
item.cursor = result.cursor;
|
|
658
|
+
item.ran = true;
|
|
659
|
+
if (result.reindexError !== void 0) item.reindexError = result.reindexError;
|
|
660
|
+
} catch (dateError) {
|
|
661
|
+
item.error = sanitizedSyncError(dateError);
|
|
662
|
+
if (options.signal?.aborted) break;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
} catch (error) {
|
|
666
|
+
item.error = sanitizedSyncError(error);
|
|
667
|
+
}
|
|
668
|
+
results.push(item);
|
|
669
|
+
if (options.signal?.aborted) break;
|
|
670
|
+
}
|
|
671
|
+
} finally {
|
|
672
|
+
if (ownStore) store.close();
|
|
673
|
+
}
|
|
674
|
+
return {
|
|
675
|
+
ranAt: ranAt.toISOString(),
|
|
676
|
+
enabled: true,
|
|
677
|
+
ranCount: results.filter((item) => item.ran).length,
|
|
678
|
+
errorCount: results.filter((item) => item.error !== void 0).length,
|
|
679
|
+
reindexErrorCount: results.filter((item) => item.reindexError !== void 0).length,
|
|
680
|
+
totalInserted: results.reduce((sum, item) => sum + item.inserted, 0),
|
|
681
|
+
results
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// src/activity/scheduler.ts
|
|
686
|
+
var ACTIVITY_SYNC_DEFAULT_INTERVAL_MS = 9e5;
|
|
687
|
+
var ActivitySyncScheduler = class {
|
|
688
|
+
config;
|
|
689
|
+
memoryDir;
|
|
690
|
+
intervalMs;
|
|
691
|
+
invoke;
|
|
692
|
+
setTimer;
|
|
693
|
+
clearTimer;
|
|
694
|
+
onRun;
|
|
695
|
+
onError;
|
|
696
|
+
handle = null;
|
|
697
|
+
stopped = false;
|
|
698
|
+
inFlight = false;
|
|
699
|
+
inFlightPromise = null;
|
|
700
|
+
abortController = new AbortController();
|
|
701
|
+
constructor(options) {
|
|
702
|
+
this.config = options.config;
|
|
703
|
+
this.memoryDir = options.memoryDir;
|
|
704
|
+
this.intervalMs = options.intervalMs ?? ACTIVITY_SYNC_DEFAULT_INTERVAL_MS;
|
|
705
|
+
this.invoke = options.invoke ?? ((signal) => runActivitySyncOnce({
|
|
706
|
+
config: this.config,
|
|
707
|
+
memoryDir: this.memoryDir,
|
|
708
|
+
signal,
|
|
709
|
+
...options.reindexSearch === void 0 ? {} : { reindexSearch: options.reindexSearch }
|
|
710
|
+
}));
|
|
711
|
+
this.setTimer = options.setTimer ?? ((fn, ms) => {
|
|
712
|
+
const timer = setInterval(fn, ms);
|
|
713
|
+
timer.unref?.();
|
|
714
|
+
return timer;
|
|
715
|
+
});
|
|
716
|
+
this.clearTimer = options.clearTimer ?? ((handle) => clearInterval(handle));
|
|
717
|
+
this.onRun = options.onRun;
|
|
718
|
+
this.onError = options.onError;
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Arm the periodic sync. Disabled config (default-off) registers nothing.
|
|
722
|
+
* Idempotent: a second call while running (or after stop) re-arms nothing.
|
|
723
|
+
*/
|
|
724
|
+
start() {
|
|
725
|
+
if (this.handle !== null || this.stopped || !this.config.enabled) {
|
|
726
|
+
return { registered: this.handle !== null, intervalMs: this.intervalMs };
|
|
727
|
+
}
|
|
728
|
+
this.handle = this.setTimer(() => this.tick(), this.intervalMs);
|
|
729
|
+
return { registered: true, intervalMs: this.intervalMs };
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Cancel the periodic sync and abort any in-flight tick, then resolve once it
|
|
733
|
+
* has unwound. The abort + timer-clear + stop-latch happen synchronously, so
|
|
734
|
+
* even a caller that cannot await (a sync teardown) still signals the running
|
|
735
|
+
* sync to stop; async callers can await the returned drain. Idempotent.
|
|
736
|
+
*/
|
|
737
|
+
async stop() {
|
|
738
|
+
this.stopped = true;
|
|
739
|
+
this.abortController.abort();
|
|
740
|
+
if (this.handle !== null) {
|
|
741
|
+
this.clearTimer(this.handle);
|
|
742
|
+
this.handle = null;
|
|
743
|
+
}
|
|
744
|
+
await this.inFlightPromise?.catch(() => void 0);
|
|
745
|
+
}
|
|
746
|
+
tick() {
|
|
747
|
+
if (this.stopped || this.inFlight) return;
|
|
748
|
+
this.inFlight = true;
|
|
749
|
+
this.inFlightPromise = this.invoke(this.abortController.signal).then((summary) => this.onRun?.(summary)).catch((error) => this.onError?.(error)).finally(() => {
|
|
750
|
+
this.inFlight = false;
|
|
751
|
+
this.inFlightPromise = null;
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
|
|
756
|
+
// src/activity/sync-registration.ts
|
|
757
|
+
var ActivitySyncRegistrar = class {
|
|
758
|
+
constructor(deps) {
|
|
759
|
+
this.deps = deps;
|
|
760
|
+
this.createScheduler = deps.createScheduler ?? ((options) => new ActivitySyncScheduler(options));
|
|
761
|
+
}
|
|
762
|
+
deps;
|
|
763
|
+
scheduler = null;
|
|
764
|
+
/** Latched by dispose() so a draining tick's onRun cannot re-arm maintenance. */
|
|
765
|
+
disposed = false;
|
|
766
|
+
createScheduler;
|
|
767
|
+
/** True while a scheduler is armed (test/telemetry observability). */
|
|
768
|
+
get armed() {
|
|
769
|
+
return this.scheduler !== null;
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Arm (or re-arm) the periodic sync for the current lifecycle. Idempotent and
|
|
773
|
+
* safe to call repeatedly; always stops a prior scheduler first.
|
|
774
|
+
*/
|
|
775
|
+
async register(signal) {
|
|
776
|
+
const prior = this.scheduler;
|
|
777
|
+
this.scheduler = null;
|
|
778
|
+
await prior?.stop();
|
|
779
|
+
if (signal.aborted) return;
|
|
780
|
+
if (this.deps.secureStoreEnabled && this.deps.config.enabled) {
|
|
781
|
+
log.warn(
|
|
782
|
+
"activity sync disabled: secure store is enabled but activity digests/SQLite are not encrypted at rest"
|
|
783
|
+
);
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
this.disposed = false;
|
|
787
|
+
try {
|
|
788
|
+
const scheduler = this.createScheduler({
|
|
789
|
+
config: this.deps.config,
|
|
790
|
+
memoryDir: this.deps.memoryDir,
|
|
791
|
+
intervalMs: this.deps.config.autoSyncIntervalMinutes * 6e4,
|
|
792
|
+
// Strict refresh after each digest write (rule 31): bypasses the
|
|
793
|
+
// min-interval gate and throws on a real failure.
|
|
794
|
+
reindexSearch: (sig) => refreshActivityIndex(this.deps.getQmd(), this.deps.qmdCollection, sig),
|
|
795
|
+
// A failed reindex (QMD down) leaves the next unchanged-digest tick
|
|
796
|
+
// skipping afterWrites, so route the retry through the durable,
|
|
797
|
+
// throttled QMD path — but never after teardown.
|
|
798
|
+
onRun: (summary) => {
|
|
799
|
+
if (summary.reindexErrorCount > 0 && !this.disposed) {
|
|
800
|
+
log.warn(
|
|
801
|
+
`activity sync: ${summary.reindexErrorCount} source(s) had a failed reindex; queuing a maintenance retry`
|
|
802
|
+
);
|
|
803
|
+
this.deps.requestReindexRetry();
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
});
|
|
807
|
+
this.scheduler = scheduler;
|
|
808
|
+
scheduler.start();
|
|
809
|
+
if (signal.aborted) {
|
|
810
|
+
await scheduler.stop();
|
|
811
|
+
this.scheduler = null;
|
|
812
|
+
}
|
|
813
|
+
} catch (err) {
|
|
814
|
+
log.debug(`activity sync scheduler start failed (non-fatal): ${err}`);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Latch teardown and drain the scheduler. Latching first ensures a draining
|
|
819
|
+
* tick's onRun cannot queue a retry; the drain is awaited so no in-flight
|
|
820
|
+
* tick keeps writing after teardown.
|
|
821
|
+
*/
|
|
822
|
+
async dispose() {
|
|
823
|
+
this.disposed = true;
|
|
824
|
+
const drain = this.scheduler?.stop();
|
|
825
|
+
this.scheduler = null;
|
|
826
|
+
await drain;
|
|
827
|
+
}
|
|
828
|
+
};
|
|
829
|
+
|
|
830
|
+
// src/orchestration/maintenance.ts
|
|
56
831
|
function qmdMaintenanceSkipReasonForError(error) {
|
|
57
832
|
const message = error instanceof Error ? error.message : String(error);
|
|
58
833
|
return /^QMD (?:update|embed) skipped by .*min-interval gate$/.test(message) ? "throttled" : null;
|
|
@@ -60,6 +835,14 @@ function qmdMaintenanceSkipReasonForError(error) {
|
|
|
60
835
|
var MaintenanceScheduler = class {
|
|
61
836
|
constructor(deps) {
|
|
62
837
|
this.deps = deps;
|
|
838
|
+
this.activityRegistrar = new ActivitySyncRegistrar({
|
|
839
|
+
config: deps.config.activity,
|
|
840
|
+
memoryDir: deps.config.memoryDir,
|
|
841
|
+
qmdCollection: deps.config.qmdCollection,
|
|
842
|
+
secureStoreEnabled: resolveRecallAuxiliaryCapabilities(deps.config).secureStore,
|
|
843
|
+
getQmd: () => deps.getQmd(),
|
|
844
|
+
requestReindexRetry: () => this.requestQmdMaintenanceForTool("activity-reindex-retry")
|
|
845
|
+
});
|
|
63
846
|
}
|
|
64
847
|
deps;
|
|
65
848
|
// ── Consolidation scheduling state ──
|
|
@@ -81,6 +864,9 @@ var MaintenanceScheduler = class {
|
|
|
81
864
|
// the bounding + post-write verification deterministically without a 400MB
|
|
82
865
|
// fixture.
|
|
83
866
|
lifecycleLedgerMaxBytes = STATE_FILE_MAX_DECRYPT_BYTES;
|
|
867
|
+
// ── Activity (screen-capture) sync scheduler (issue #1900) ──
|
|
868
|
+
// The full arm/re-arm/teardown lifecycle lives in ActivitySyncRegistrar.
|
|
869
|
+
activityRegistrar;
|
|
84
870
|
// ───────────────────────────────────────────────────────────────────────
|
|
85
871
|
// Cron auto-registration
|
|
86
872
|
// ───────────────────────────────────────────────────────────────────────
|
|
@@ -93,7 +879,7 @@ var MaintenanceScheduler = class {
|
|
|
93
879
|
* Returns when all eligible registrations have completed so callers that
|
|
94
880
|
* `await deferredReady` can rely on jobs.json being current.
|
|
95
881
|
*/
|
|
96
|
-
async autoRegisterCrons(
|
|
882
|
+
async autoRegisterCrons(signal) {
|
|
97
883
|
if (resolveRecallAuxiliaryCapabilities(this.deps.config).daySummary) {
|
|
98
884
|
try {
|
|
99
885
|
await this.autoRegisterDaySummaryCron();
|
|
@@ -136,10 +922,11 @@ var MaintenanceScheduler = class {
|
|
|
136
922
|
log.debug(`graph edge decay cron auto-register failed (non-fatal): ${err}`);
|
|
137
923
|
}
|
|
138
924
|
}
|
|
925
|
+
await this.activityRegistrar.register(signal);
|
|
139
926
|
}
|
|
140
927
|
async autoRegisterDaySummaryCron() {
|
|
141
928
|
const home = resolveHomeDir();
|
|
142
|
-
const jobsPath =
|
|
929
|
+
const jobsPath = path3.join(home, ".openclaw", "cron", "jobs.json");
|
|
143
930
|
try {
|
|
144
931
|
if (!existsSync(jobsPath)) {
|
|
145
932
|
log.debug("day-summary cron: jobs.json not found, skipping auto-register");
|
|
@@ -190,7 +977,7 @@ var MaintenanceScheduler = class {
|
|
|
190
977
|
}
|
|
191
978
|
async autoRegisterNightlyGovernanceCron() {
|
|
192
979
|
const home = resolveHomeDir();
|
|
193
|
-
const jobsPath =
|
|
980
|
+
const jobsPath = path3.join(home, ".openclaw", "cron", "jobs.json");
|
|
194
981
|
try {
|
|
195
982
|
if (!existsSync(jobsPath)) {
|
|
196
983
|
log.debug("nightly governance cron: jobs.json not found, skipping auto-register");
|
|
@@ -212,7 +999,7 @@ var MaintenanceScheduler = class {
|
|
|
212
999
|
}
|
|
213
1000
|
async autoRegisterProceduralMiningCron() {
|
|
214
1001
|
const home = resolveHomeDir();
|
|
215
|
-
const jobsPath =
|
|
1002
|
+
const jobsPath = path3.join(home, ".openclaw", "cron", "jobs.json");
|
|
216
1003
|
try {
|
|
217
1004
|
if (!existsSync(jobsPath)) {
|
|
218
1005
|
log.debug("procedural mining cron: jobs.json not found, skipping auto-register");
|
|
@@ -232,7 +1019,7 @@ var MaintenanceScheduler = class {
|
|
|
232
1019
|
}
|
|
233
1020
|
async autoRegisterContradictionScanCron() {
|
|
234
1021
|
const home = resolveHomeDir();
|
|
235
|
-
const jobsPath =
|
|
1022
|
+
const jobsPath = path3.join(home, ".openclaw", "cron", "jobs.json");
|
|
236
1023
|
try {
|
|
237
1024
|
if (!existsSync(jobsPath)) {
|
|
238
1025
|
log.debug("contradiction scan cron: jobs.json not found, skipping auto-register");
|
|
@@ -252,7 +1039,7 @@ var MaintenanceScheduler = class {
|
|
|
252
1039
|
}
|
|
253
1040
|
async autoRegisterPatternReinforcementCron() {
|
|
254
1041
|
const home = resolveHomeDir();
|
|
255
|
-
const jobsPath =
|
|
1042
|
+
const jobsPath = path3.join(home, ".openclaw", "cron", "jobs.json");
|
|
256
1043
|
try {
|
|
257
1044
|
if (!existsSync(jobsPath)) {
|
|
258
1045
|
log.debug("pattern reinforcement cron: jobs.json not found, skipping auto-register");
|
|
@@ -272,7 +1059,7 @@ var MaintenanceScheduler = class {
|
|
|
272
1059
|
}
|
|
273
1060
|
async autoRegisterGraphEdgeDecayCron() {
|
|
274
1061
|
const home = resolveHomeDir();
|
|
275
|
-
const jobsPath =
|
|
1062
|
+
const jobsPath = path3.join(home, ".openclaw", "cron", "jobs.json");
|
|
276
1063
|
try {
|
|
277
1064
|
if (!existsSync(jobsPath)) {
|
|
278
1065
|
log.debug("graph edge decay cron: jobs.json not found, skipping auto-register");
|
|
@@ -536,7 +1323,7 @@ var MaintenanceScheduler = class {
|
|
|
536
1323
|
*/
|
|
537
1324
|
async hasOverCapEncryptedLifecycleLedger(targets) {
|
|
538
1325
|
for (const target of targets) {
|
|
539
|
-
const ledgerPath =
|
|
1326
|
+
const ledgerPath = path3.join(target.memoryDir, "state", "memory-lifecycle-ledger.jsonl");
|
|
540
1327
|
let size;
|
|
541
1328
|
try {
|
|
542
1329
|
size = (await stat(ledgerPath)).size;
|
|
@@ -588,7 +1375,7 @@ var MaintenanceScheduler = class {
|
|
|
588
1375
|
* containment guard on every spill file it reads or deletes.
|
|
589
1376
|
*/
|
|
590
1377
|
async drainPendingLifecycleAppendsForPath(memoryDir) {
|
|
591
|
-
const ledgerPath =
|
|
1378
|
+
const ledgerPath = path3.join(memoryDir, "state", "memory-lifecycle-ledger.jsonl");
|
|
592
1379
|
const spillDir = pendingLifecycleLedgerDir(ledgerPath);
|
|
593
1380
|
let spillFiles;
|
|
594
1381
|
let claimedOrphans;
|
|
@@ -602,14 +1389,14 @@ var MaintenanceScheduler = class {
|
|
|
602
1389
|
if (spillFiles.length === 0 && claimedOrphans.length === 0) return;
|
|
603
1390
|
const io = {
|
|
604
1391
|
writeSecure: async (filePath, payload) => {
|
|
605
|
-
await
|
|
606
|
-
await
|
|
1392
|
+
await mkdir3(path3.dirname(filePath), { recursive: true });
|
|
1393
|
+
await writeFile2(filePath, payload, "utf-8");
|
|
607
1394
|
},
|
|
608
1395
|
readSecure: async (filePath) => {
|
|
609
1396
|
if (await probeEncryptedRegularFileHeader(filePath)) {
|
|
610
1397
|
throw new Error(`fallback lifecycle drain deferred: encrypted spill at ${filePath}`);
|
|
611
1398
|
}
|
|
612
|
-
return
|
|
1399
|
+
return readFile2(filePath, "utf-8");
|
|
613
1400
|
}
|
|
614
1401
|
};
|
|
615
1402
|
try {
|
|
@@ -623,7 +1410,7 @@ var MaintenanceScheduler = class {
|
|
|
623
1410
|
await appendFile(ledgerPath, payload, "utf-8");
|
|
624
1411
|
},
|
|
625
1412
|
async () => {
|
|
626
|
-
await
|
|
1413
|
+
await mkdir3(path3.dirname(ledgerPath), { recursive: true });
|
|
627
1414
|
}
|
|
628
1415
|
);
|
|
629
1416
|
} catch (err) {
|
|
@@ -641,7 +1428,7 @@ var MaintenanceScheduler = class {
|
|
|
641
1428
|
const targets = [];
|
|
642
1429
|
const seen = /* @__PURE__ */ new Set();
|
|
643
1430
|
const addTarget = (memoryDir, storage) => {
|
|
644
|
-
const key =
|
|
1431
|
+
const key = path3.resolve(memoryDir);
|
|
645
1432
|
if (seen.has(key)) return;
|
|
646
1433
|
seen.add(key);
|
|
647
1434
|
targets.push({ memoryDir, storage });
|
|
@@ -670,7 +1457,7 @@ var MaintenanceScheduler = class {
|
|
|
670
1457
|
}
|
|
671
1458
|
}
|
|
672
1459
|
if (resolveNamespaceCapabilities(this.deps.config).namespaces) {
|
|
673
|
-
const namespacesBase =
|
|
1460
|
+
const namespacesBase = path3.join(this.deps.config.memoryDir, "namespaces");
|
|
674
1461
|
let memoryDirReal;
|
|
675
1462
|
try {
|
|
676
1463
|
memoryDirReal = await realpath(this.deps.config.memoryDir);
|
|
@@ -695,7 +1482,7 @@ var MaintenanceScheduler = class {
|
|
|
695
1482
|
}
|
|
696
1483
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
697
1484
|
if (!entry.isDirectory() || entry.isSymbolicLink()) continue;
|
|
698
|
-
const childPath =
|
|
1485
|
+
const childPath = path3.join(namespacesBase, entry.name);
|
|
699
1486
|
try {
|
|
700
1487
|
assertPathInsideRoot(memoryDirReal, await realpath(childPath), childPath);
|
|
701
1488
|
} catch (err) {
|
|
@@ -707,7 +1494,7 @@ var MaintenanceScheduler = class {
|
|
|
707
1494
|
if (resolveNamespaceStorage && routeNamespace !== null) {
|
|
708
1495
|
try {
|
|
709
1496
|
const storage = await resolveNamespaceStorage(routeNamespace);
|
|
710
|
-
if (
|
|
1497
|
+
if (path3.resolve(storage.dir) === path3.resolve(childPath)) {
|
|
711
1498
|
addTarget(storage.dir, storage);
|
|
712
1499
|
continue;
|
|
713
1500
|
}
|
|
@@ -738,7 +1525,7 @@ var MaintenanceScheduler = class {
|
|
|
738
1525
|
* an over-cap rewrite never arms the throttle.
|
|
739
1526
|
*/
|
|
740
1527
|
async compactLifecycleLedgerTarget(target, threshold) {
|
|
741
|
-
const ledgerPath =
|
|
1528
|
+
const ledgerPath = path3.join(target.memoryDir, "state", "memory-lifecycle-ledger.jsonl");
|
|
742
1529
|
let size = 0;
|
|
743
1530
|
try {
|
|
744
1531
|
size = (await stat(ledgerPath)).size;
|
|
@@ -823,10 +1610,19 @@ var MaintenanceScheduler = class {
|
|
|
823
1610
|
// Lifecycle
|
|
824
1611
|
// ───────────────────────────────────────────────────────────────────────
|
|
825
1612
|
/**
|
|
826
|
-
* Clear any pending debounce timer
|
|
827
|
-
* destroy() so a late tick does not fire on a
|
|
1613
|
+
* Clear any pending debounce timer and drain the activity sync scheduler.
|
|
1614
|
+
* Called from the orchestrator's destroy() so a late tick does not fire on a
|
|
1615
|
+
* torn-down instance and no in-flight activity tick keeps writing after
|
|
1616
|
+
* teardown. Async: awaits the scheduler's abort+drain before resolving.
|
|
828
1617
|
*/
|
|
829
|
-
dispose() {
|
|
1618
|
+
async dispose() {
|
|
1619
|
+
const activityDrain = this.activityRegistrar.dispose();
|
|
1620
|
+
if (this.qmdMaintenanceTimer) {
|
|
1621
|
+
clearTimeout(this.qmdMaintenanceTimer);
|
|
1622
|
+
this.qmdMaintenanceTimer = null;
|
|
1623
|
+
}
|
|
1624
|
+
this.qmdMaintenancePending = false;
|
|
1625
|
+
await activityDrain;
|
|
830
1626
|
if (this.qmdMaintenanceTimer) {
|
|
831
1627
|
clearTimeout(this.qmdMaintenanceTimer);
|
|
832
1628
|
this.qmdMaintenanceTimer = null;
|
|
@@ -836,6 +1632,19 @@ var MaintenanceScheduler = class {
|
|
|
836
1632
|
};
|
|
837
1633
|
|
|
838
1634
|
export {
|
|
1635
|
+
refreshActivityIndex,
|
|
1636
|
+
activityDatabasePath,
|
|
1637
|
+
ensureActivityStateDir,
|
|
1638
|
+
openActivityDatabase,
|
|
1639
|
+
applyActivitySchema,
|
|
1640
|
+
ActivityStore,
|
|
1641
|
+
activityCursorKey,
|
|
1642
|
+
syncActivitySource,
|
|
1643
|
+
ActivityHttpSourceClient,
|
|
1644
|
+
resolveActivitySyncDates,
|
|
1645
|
+
runActivitySyncOnce,
|
|
1646
|
+
ACTIVITY_SYNC_DEFAULT_INTERVAL_MS,
|
|
1647
|
+
ActivitySyncScheduler,
|
|
839
1648
|
MaintenanceScheduler
|
|
840
1649
|
};
|
|
841
|
-
//# sourceMappingURL=chunk-
|
|
1650
|
+
//# sourceMappingURL=chunk-V6LYAXGW.js.map
|