@remnic/core 9.15.3 → 9.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { validateActivityBaseUrl } from "./config.js";
|
|
2
|
+
import { displayErrorDetail } from "../runtime/better-sqlite.js";
|
|
3
|
+
import type { ActivitySnapshot, ActivitySnapshotPage, ActivitySourceCheck, ActivitySourceClient } from "./types.js";
|
|
4
|
+
|
|
5
|
+
/** Abort a stalled capture-daemon request rather than hang the whole sync. */
|
|
6
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
|
|
7
|
+
|
|
8
|
+
export interface ActivityHttpSourceClientOptions {
|
|
9
|
+
machineLabel: string;
|
|
10
|
+
baseUrl: string;
|
|
11
|
+
token?: string;
|
|
12
|
+
/** Per-request timeout in ms; defaults to DEFAULT_REQUEST_TIMEOUT_MS. */
|
|
13
|
+
timeoutMs?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
17
|
+
return typeof value === "object" && value !== null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function stringField(value: Record<string, unknown>, field: string): string {
|
|
21
|
+
const result = value[field];
|
|
22
|
+
if (typeof result !== "string" || result.length === 0) {
|
|
23
|
+
throw new TypeError(`activity source response has no valid ${field}`);
|
|
24
|
+
}
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Content fields (app/windowTitle/text) may be legitimately empty: a foreground
|
|
30
|
+
* window can be untitled or have no extractable text. Require the string TYPE
|
|
31
|
+
* (a missing/non-string field still fails loudly) but tolerate "", matching the
|
|
32
|
+
* durable store's contract — else a blank window poisons the day cursor by
|
|
33
|
+
* failing every replay of the same page.
|
|
34
|
+
*/
|
|
35
|
+
function contentStringField(value: Record<string, unknown>, field: string): string {
|
|
36
|
+
const result = value[field];
|
|
37
|
+
if (typeof result !== "string") {
|
|
38
|
+
throw new TypeError(`activity source response has invalid ${field}`);
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function optionalStringField(value: Record<string, unknown>, field: string): string | undefined {
|
|
44
|
+
const result = value[field];
|
|
45
|
+
if (result === undefined || result === null) return undefined;
|
|
46
|
+
if (typeof result !== "string") throw new TypeError(`activity source response has invalid ${field}`);
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function snapshotFromWire(value: unknown, machine: string): ActivitySnapshot {
|
|
51
|
+
if (!isRecord(value)) throw new TypeError("activity source returned a non-object snapshot");
|
|
52
|
+
const textSource = stringField(value, "textSource");
|
|
53
|
+
if (textSource !== "ax" && textSource !== "ocr") {
|
|
54
|
+
throw new TypeError("activity source response has invalid textSource");
|
|
55
|
+
}
|
|
56
|
+
const browserUrl = optionalStringField(value, "browserUrl");
|
|
57
|
+
const simhash = optionalStringField(value, "simhash");
|
|
58
|
+
return {
|
|
59
|
+
machine,
|
|
60
|
+
capturedAtUtc: stringField(value, "capturedAtUtc"),
|
|
61
|
+
app: contentStringField(value, "app"),
|
|
62
|
+
windowTitle: contentStringField(value, "windowTitle"),
|
|
63
|
+
...(browserUrl === undefined ? {} : { browserUrl }),
|
|
64
|
+
text: contentStringField(value, "text"),
|
|
65
|
+
textSource,
|
|
66
|
+
contentHash: stringField(value, "contentHash"),
|
|
67
|
+
...(simhash === undefined ? {} : { simhash }),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function responsePage(value: unknown, machine: string): ActivitySnapshotPage {
|
|
72
|
+
if (!isRecord(value) || !Array.isArray(value.snapshots)) {
|
|
73
|
+
throw new TypeError("activity source response has no snapshots array");
|
|
74
|
+
}
|
|
75
|
+
// A missing/undefined nextCursor (field omitted, not JSON null) is normal
|
|
76
|
+
// end-of-pagination — only a present non-string value is invalid.
|
|
77
|
+
const nextCursor = value.nextCursor === undefined ? null : value.nextCursor;
|
|
78
|
+
if (nextCursor !== null && typeof nextCursor !== "string") {
|
|
79
|
+
throw new TypeError("activity source response has invalid nextCursor");
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
snapshots: value.snapshots.map((snapshot) => snapshotFromWire(snapshot, machine)),
|
|
83
|
+
nextCursor,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function requestUrl(baseUrl: string, path: string, query: Record<string, string | undefined>): string {
|
|
88
|
+
const url = new URL(path, baseUrl);
|
|
89
|
+
const parts = Object.entries(query)
|
|
90
|
+
.filter((entry): entry is [string, string] => entry[1] !== undefined)
|
|
91
|
+
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
|
|
92
|
+
url.search = parts.join("&");
|
|
93
|
+
return url.toString();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export class ActivityHttpSourceClient implements ActivitySourceClient {
|
|
97
|
+
readonly machineLabel: string;
|
|
98
|
+
private readonly baseUrl: string;
|
|
99
|
+
private readonly token: string | undefined;
|
|
100
|
+
private readonly timeoutMs: number;
|
|
101
|
+
|
|
102
|
+
constructor(options: ActivityHttpSourceClientOptions) {
|
|
103
|
+
if (options.machineLabel.trim().length === 0) throw new RangeError("activity source machine label must not be empty");
|
|
104
|
+
if (options.timeoutMs !== undefined && (!Number.isFinite(options.timeoutMs) || options.timeoutMs <= 0)) {
|
|
105
|
+
throw new RangeError("activity source timeoutMs must be a positive number");
|
|
106
|
+
}
|
|
107
|
+
this.machineLabel = options.machineLabel;
|
|
108
|
+
this.baseUrl = validateActivityBaseUrl(options.baseUrl).toString();
|
|
109
|
+
this.token = options.token;
|
|
110
|
+
this.timeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private async request(url: string, signal?: AbortSignal): Promise<Response> {
|
|
114
|
+
// Bound every request so a stalled daemon aborts instead of hanging the
|
|
115
|
+
// sync forever; honor the caller's signal too when one is supplied.
|
|
116
|
+
const timeout = AbortSignal.timeout(this.timeoutMs);
|
|
117
|
+
const composed = signal === undefined ? timeout : AbortSignal.any([signal, timeout]);
|
|
118
|
+
const response = await fetch(url, {
|
|
119
|
+
headers: this.token === undefined ? undefined : { authorization: `Bearer ${this.token}` },
|
|
120
|
+
signal: composed,
|
|
121
|
+
});
|
|
122
|
+
if (!response.ok) throw new Error(`activity source HTTP ${response.status}`);
|
|
123
|
+
return response;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async verify(signal?: AbortSignal): Promise<ActivitySourceCheck> {
|
|
127
|
+
try {
|
|
128
|
+
await this.request(requestUrl(this.baseUrl, "/v1/health", {}), signal);
|
|
129
|
+
return { ok: true };
|
|
130
|
+
} catch (error: unknown) {
|
|
131
|
+
// The controlled `HTTP <status>` message is safe to surface verbatim;
|
|
132
|
+
// anything else (network/runtime fetch errors that can embed hostnames or
|
|
133
|
+
// absolute paths) is reduced to a sanitized name+code via
|
|
134
|
+
// displayErrorDetail, matching the wearables operator-facing path.
|
|
135
|
+
const detail =
|
|
136
|
+
error instanceof Error && error.message.startsWith("activity source HTTP ")
|
|
137
|
+
? error.message.slice("activity source ".length)
|
|
138
|
+
: displayErrorDetail(error) || "request failed";
|
|
139
|
+
return { ok: false, detail };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async fetchSnapshots(opts: {
|
|
144
|
+
date: string;
|
|
145
|
+
timezone: string;
|
|
146
|
+
cursor?: string | null;
|
|
147
|
+
signal?: AbortSignal;
|
|
148
|
+
}): Promise<ActivitySnapshotPage> {
|
|
149
|
+
const response = await this.request(
|
|
150
|
+
requestUrl(this.baseUrl, "/v1/snapshots", {
|
|
151
|
+
date: opts.date,
|
|
152
|
+
timezone: opts.timezone,
|
|
153
|
+
cursor: opts.cursor ?? undefined,
|
|
154
|
+
}),
|
|
155
|
+
opts.signal,
|
|
156
|
+
);
|
|
157
|
+
return responsePage(await response.json(), this.machineLabel);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
|
|
4
|
+
import type { ActivitySyncRunSummary } from "./runner.js";
|
|
5
|
+
import type { ActivitySyncSchedulerOptions } from "./scheduler.js";
|
|
6
|
+
import {
|
|
7
|
+
ActivitySyncRegistrar,
|
|
8
|
+
type ActivitySyncRegistrarDeps,
|
|
9
|
+
type ActivitySyncSchedulerLike,
|
|
10
|
+
} from "./sync-registration.js";
|
|
11
|
+
|
|
12
|
+
class FakeScheduler implements ActivitySyncSchedulerLike {
|
|
13
|
+
starts = 0;
|
|
14
|
+
stops = 0;
|
|
15
|
+
readonly options: ActivitySyncSchedulerOptions;
|
|
16
|
+
constructor(options: ActivitySyncSchedulerOptions) {
|
|
17
|
+
this.options = options;
|
|
18
|
+
}
|
|
19
|
+
start(): unknown {
|
|
20
|
+
this.starts += 1;
|
|
21
|
+
return { registered: true, intervalMs: this.options.intervalMs ?? 0 };
|
|
22
|
+
}
|
|
23
|
+
async stop(): Promise<void> {
|
|
24
|
+
this.stops += 1;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function makeDeps(overrides: Partial<ActivitySyncRegistrarDeps> = {}): {
|
|
29
|
+
deps: ActivitySyncRegistrarDeps;
|
|
30
|
+
created: FakeScheduler[];
|
|
31
|
+
state: { retries: number };
|
|
32
|
+
} {
|
|
33
|
+
const created: FakeScheduler[] = [];
|
|
34
|
+
const state = { retries: 0 };
|
|
35
|
+
const deps: ActivitySyncRegistrarDeps = {
|
|
36
|
+
config: { enabled: true, timezone: "UTC", syncDays: 1, autoSyncIntervalMinutes: 15, sources: [] },
|
|
37
|
+
memoryDir: "/tmp/remnic-activity-registrar",
|
|
38
|
+
qmdCollection: "openclaw-engram",
|
|
39
|
+
secureStoreEnabled: false,
|
|
40
|
+
getQmd: () => ({ update: async () => undefined }),
|
|
41
|
+
requestReindexRetry: () => {
|
|
42
|
+
state.retries += 1;
|
|
43
|
+
},
|
|
44
|
+
createScheduler: (options) => {
|
|
45
|
+
const s = new FakeScheduler(options);
|
|
46
|
+
created.push(s);
|
|
47
|
+
return s;
|
|
48
|
+
},
|
|
49
|
+
...overrides,
|
|
50
|
+
};
|
|
51
|
+
return { deps, created, state };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
test("register arms a scheduler on a live signal", async () => {
|
|
55
|
+
const { deps, created } = makeDeps();
|
|
56
|
+
const registrar = new ActivitySyncRegistrar(deps);
|
|
57
|
+
await registrar.register(new AbortController().signal);
|
|
58
|
+
assert.equal(created.length, 1, "exactly one scheduler is constructed");
|
|
59
|
+
assert.equal(created[0]?.starts, 1, "the scheduler is started");
|
|
60
|
+
assert.ok(registrar.armed, "the registrar reports armed");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("register on an aborted signal never arms and stops any prior scheduler", async () => {
|
|
64
|
+
const { deps, created } = makeDeps();
|
|
65
|
+
const registrar = new ActivitySyncRegistrar(deps);
|
|
66
|
+
await registrar.register(new AbortController().signal);
|
|
67
|
+
const prior = created[0];
|
|
68
|
+
const aborted = AbortSignal.abort();
|
|
69
|
+
await registrar.register(aborted);
|
|
70
|
+
assert.equal(created.length, 1, "no replacement scheduler is constructed while aborted");
|
|
71
|
+
assert.equal(prior?.stops, 1, "the prior scheduler is stopped before bailing");
|
|
72
|
+
assert.equal(registrar.armed, false, "the registrar is disarmed after an aborted re-init");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("register re-arms by stopping the prior scheduler first", async () => {
|
|
76
|
+
const { deps, created } = makeDeps();
|
|
77
|
+
const registrar = new ActivitySyncRegistrar(deps);
|
|
78
|
+
await registrar.register(new AbortController().signal);
|
|
79
|
+
await registrar.register(new AbortController().signal);
|
|
80
|
+
assert.equal(created.length, 2, "a fresh scheduler is armed on re-register");
|
|
81
|
+
assert.equal(created[0]?.stops, 1, "the prior scheduler is drained");
|
|
82
|
+
assert.equal(created[1]?.starts, 1, "the replacement scheduler is started");
|
|
83
|
+
assert.ok(registrar.armed);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("register refuses to arm under a secure store", async () => {
|
|
87
|
+
const { deps, created } = makeDeps({ secureStoreEnabled: true });
|
|
88
|
+
const registrar = new ActivitySyncRegistrar(deps);
|
|
89
|
+
await registrar.register(new AbortController().signal);
|
|
90
|
+
assert.equal(created.length, 0, "no scheduler is armed when at-rest encryption is on");
|
|
91
|
+
assert.equal(registrar.armed, false);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("dispose latches teardown and drains the scheduler", async () => {
|
|
95
|
+
const { deps, created } = makeDeps();
|
|
96
|
+
const registrar = new ActivitySyncRegistrar(deps);
|
|
97
|
+
await registrar.register(new AbortController().signal);
|
|
98
|
+
await registrar.dispose();
|
|
99
|
+
assert.equal(created[0]?.stops, 1, "dispose drains the armed scheduler");
|
|
100
|
+
assert.equal(registrar.armed, false, "the registrar is disarmed after dispose");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("a reindex failure queues a retry, but never after teardown", async () => {
|
|
104
|
+
const box = makeDeps();
|
|
105
|
+
const registrar = new ActivitySyncRegistrar(box.deps);
|
|
106
|
+
await registrar.register(new AbortController().signal);
|
|
107
|
+
const onRun = box.created[0]?.options.onRun as (s: ActivitySyncRunSummary) => void;
|
|
108
|
+
const failed = { machine: "m", ran: true, fetched: 0, inserted: 0, duplicates: 0, reindexErrorCount: 2 } as unknown as ActivitySyncRunSummary;
|
|
109
|
+
onRun(failed);
|
|
110
|
+
assert.equal(box.state.retries, 1, "a failed reindex queues one maintenance retry");
|
|
111
|
+
await registrar.dispose();
|
|
112
|
+
onRun(failed);
|
|
113
|
+
assert.equal(box.state.retries, 1, "a draining tick's onRun does not re-arm maintenance after teardown");
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("abort landing during construction stops the freshly-started scheduler", async () => {
|
|
117
|
+
const controller = new AbortController();
|
|
118
|
+
const created: FakeScheduler[] = [];
|
|
119
|
+
const { deps } = makeDeps({
|
|
120
|
+
createScheduler: (options) => {
|
|
121
|
+
// Simulate the abort arriving in the window between the guard and start().
|
|
122
|
+
controller.abort();
|
|
123
|
+
const s = new FakeScheduler(options);
|
|
124
|
+
created.push(s);
|
|
125
|
+
return s;
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
const registrar = new ActivitySyncRegistrar(deps);
|
|
129
|
+
await registrar.register(controller.signal);
|
|
130
|
+
assert.equal(created.length, 1, "the scheduler is constructed before the abort is observed");
|
|
131
|
+
assert.equal(created[0]?.starts, 1, "it is started");
|
|
132
|
+
assert.equal(created[0]?.stops, 1, "then immediately stopped once the abort is seen");
|
|
133
|
+
assert.equal(registrar.armed, false, "the registrar does not retain an aborted scheduler");
|
|
134
|
+
});
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Activity (screen-capture) sync lifecycle owner (issue #1900).
|
|
3
|
+
*
|
|
4
|
+
* The MaintenanceScheduler used to inline the arm / re-arm / teardown wiring
|
|
5
|
+
* for the periodic activity sync, which made the lifecycle invariants easy to
|
|
6
|
+
* break one edit at a time. This module centralizes them in one testable place:
|
|
7
|
+
*
|
|
8
|
+
* - Re-arm and teardown BOTH stop any prior scheduler first, so a repeated
|
|
9
|
+
* registration never orphans a running interval.
|
|
10
|
+
* - An aborted deferred-init signal means teardown began. `destroy()` aborts
|
|
11
|
+
* the init signal *before* it disposes, so an aborted signal here also
|
|
12
|
+
* implies `dispose()` may already have latched teardown. `register()` never
|
|
13
|
+
* arms nor clears the latch while aborted — even when the abort lands
|
|
14
|
+
* mid-drain — so a late re-init cannot resurrect a torn-down instance.
|
|
15
|
+
* - The teardown latch gates the reindex-failure retry, so a draining tick's
|
|
16
|
+
* `onRun` cannot queue QMD maintenance after teardown.
|
|
17
|
+
* - Activity digests + SQLite are written in the clear, so the registrar
|
|
18
|
+
* refuses to arm under a secure store rather than silently bypass opted-in
|
|
19
|
+
* at-rest encryption.
|
|
20
|
+
*
|
|
21
|
+
* Host-agnostic: the scheduler factory is injectable, so tests drive the whole
|
|
22
|
+
* lifecycle against a fake scheduler without real timers.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { refreshActivityIndex, type ActivityIndexRefresher } from "./reindex.js";
|
|
26
|
+
import type { ActivitySyncRunSummary } from "./runner.js";
|
|
27
|
+
import { ActivitySyncScheduler, type ActivitySyncSchedulerOptions } from "./scheduler.js";
|
|
28
|
+
import type { ActivityConfig } from "./types.js";
|
|
29
|
+
import { log } from "../logger.js";
|
|
30
|
+
|
|
31
|
+
/** The scheduler surface the registrar drives (injectable for tests). */
|
|
32
|
+
export interface ActivitySyncSchedulerLike {
|
|
33
|
+
start(): unknown;
|
|
34
|
+
stop(): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ActivitySyncRegistrarDeps {
|
|
38
|
+
/** Activity sync settings; the master default-off flag lives here. */
|
|
39
|
+
readonly config: ActivityConfig;
|
|
40
|
+
/** Memory root the digests + SQLite store live under. */
|
|
41
|
+
readonly memoryDir: string;
|
|
42
|
+
/** QMD collection the day digest is (strictly) reindexed into. */
|
|
43
|
+
readonly qmdCollection: string;
|
|
44
|
+
/**
|
|
45
|
+
* True when at-rest encryption is configured. Activity writes are not yet
|
|
46
|
+
* encrypted, so the registrar refuses to arm rather than leak plaintext.
|
|
47
|
+
*/
|
|
48
|
+
readonly secureStoreEnabled: boolean;
|
|
49
|
+
/** Live QMD accessor — the host may swap backends after construction. */
|
|
50
|
+
readonly getQmd: () => ActivityIndexRefresher;
|
|
51
|
+
/** Debounced/singleflighted QMD retry used when a tick's reindex fails. */
|
|
52
|
+
readonly requestReindexRetry: () => void;
|
|
53
|
+
/** Scheduler factory (injectable for tests); defaults to the real scheduler. */
|
|
54
|
+
readonly createScheduler?: (options: ActivitySyncSchedulerOptions) => ActivitySyncSchedulerLike;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export class ActivitySyncRegistrar {
|
|
58
|
+
private scheduler: ActivitySyncSchedulerLike | null = null;
|
|
59
|
+
/** Latched by dispose() so a draining tick's onRun cannot re-arm maintenance. */
|
|
60
|
+
private disposed = false;
|
|
61
|
+
private readonly createScheduler: (options: ActivitySyncSchedulerOptions) => ActivitySyncSchedulerLike;
|
|
62
|
+
|
|
63
|
+
constructor(private readonly deps: ActivitySyncRegistrarDeps) {
|
|
64
|
+
this.createScheduler = deps.createScheduler ?? ((options) => new ActivitySyncScheduler(options));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** True while a scheduler is armed (test/telemetry observability). */
|
|
68
|
+
get armed(): boolean {
|
|
69
|
+
return this.scheduler !== null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Arm (or re-arm) the periodic sync for the current lifecycle. Idempotent and
|
|
74
|
+
* safe to call repeatedly; always stops a prior scheduler first.
|
|
75
|
+
*/
|
|
76
|
+
async register(signal: AbortSignal): Promise<void> {
|
|
77
|
+
// Re-arm or teardown: stop any prior scheduler before anything else, so a
|
|
78
|
+
// repeated registration never orphans a running interval.
|
|
79
|
+
const prior = this.scheduler;
|
|
80
|
+
this.scheduler = null;
|
|
81
|
+
await prior?.stop();
|
|
82
|
+
// Teardown began (possibly during the drain above). Never arm nor clear the
|
|
83
|
+
// latch: dispose() may already have set it, and it must stay set.
|
|
84
|
+
if (signal.aborted) return;
|
|
85
|
+
// Opted-in at-rest encryption must not be silently bypassed: activity
|
|
86
|
+
// digests + SQLite are written in the clear, so refuse to run under it.
|
|
87
|
+
if (this.deps.secureStoreEnabled && this.deps.config.enabled) {
|
|
88
|
+
log.warn(
|
|
89
|
+
"activity sync disabled: secure store is enabled but activity digests/SQLite are not encrypted at rest",
|
|
90
|
+
);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
// Committed to a fresh lifecycle: clear the latch so this scheduler's
|
|
94
|
+
// reindex retries fire again.
|
|
95
|
+
this.disposed = false;
|
|
96
|
+
try {
|
|
97
|
+
const scheduler = this.createScheduler({
|
|
98
|
+
config: this.deps.config,
|
|
99
|
+
memoryDir: this.deps.memoryDir,
|
|
100
|
+
intervalMs: this.deps.config.autoSyncIntervalMinutes * 60_000,
|
|
101
|
+
// Strict refresh after each digest write (rule 31): bypasses the
|
|
102
|
+
// min-interval gate and throws on a real failure.
|
|
103
|
+
reindexSearch: (sig) => refreshActivityIndex(this.deps.getQmd(), this.deps.qmdCollection, sig),
|
|
104
|
+
// A failed reindex (QMD down) leaves the next unchanged-digest tick
|
|
105
|
+
// skipping afterWrites, so route the retry through the durable,
|
|
106
|
+
// throttled QMD path — but never after teardown.
|
|
107
|
+
onRun: (summary: ActivitySyncRunSummary) => {
|
|
108
|
+
if (summary.reindexErrorCount > 0 && !this.disposed) {
|
|
109
|
+
log.warn(
|
|
110
|
+
`activity sync: ${summary.reindexErrorCount} source(s) had a failed reindex; queuing a maintenance retry`,
|
|
111
|
+
);
|
|
112
|
+
this.deps.requestReindexRetry();
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
this.scheduler = scheduler;
|
|
117
|
+
scheduler.start();
|
|
118
|
+
// Close the race where abort fires between the guard above and start().
|
|
119
|
+
if (signal.aborted) {
|
|
120
|
+
await scheduler.stop();
|
|
121
|
+
this.scheduler = null;
|
|
122
|
+
}
|
|
123
|
+
} catch (err) {
|
|
124
|
+
log.debug(`activity sync scheduler start failed (non-fatal): ${err}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Latch teardown and drain the scheduler. Latching first ensures a draining
|
|
130
|
+
* tick's onRun cannot queue a retry; the drain is awaited so no in-flight
|
|
131
|
+
* tick keeps writing after teardown.
|
|
132
|
+
*/
|
|
133
|
+
async dispose(): Promise<void> {
|
|
134
|
+
this.disposed = true;
|
|
135
|
+
const drain = this.scheduler?.stop();
|
|
136
|
+
this.scheduler = null;
|
|
137
|
+
await drain;
|
|
138
|
+
}
|
|
139
|
+
}
|
package/src/activity/types.ts
CHANGED
|
@@ -84,3 +84,20 @@ export interface ActivitySourceClient {
|
|
|
84
84
|
signal?: AbortSignal;
|
|
85
85
|
}): Promise<ActivitySnapshotPage>;
|
|
86
86
|
}
|
|
87
|
+
|
|
88
|
+
/** One trusted capture daemon that feeds this memory directory. */
|
|
89
|
+
export interface ActivitySourceConfig {
|
|
90
|
+
machineLabel: string;
|
|
91
|
+
baseUrl: string;
|
|
92
|
+
token?: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Opt-in activity synchronization settings. */
|
|
96
|
+
export interface ActivityConfig {
|
|
97
|
+
enabled: boolean;
|
|
98
|
+
timezone: string;
|
|
99
|
+
syncDays: number;
|
|
100
|
+
/** Periodic auto-sync cadence in minutes (default 15). */
|
|
101
|
+
autoSyncIntervalMinutes: number;
|
|
102
|
+
sources: ActivitySourceConfig[];
|
|
103
|
+
}
|
package/src/config.test.ts
CHANGED
|
@@ -2348,3 +2348,22 @@ test("parseConfig bounded-state knobs coerce valid strings, preserve 0, and reje
|
|
|
2348
2348
|
assert.throws(() => parseConfig({ recallImpressionsRotateKeep: 1001 }), /between 1 and 1000/);
|
|
2349
2349
|
assert.throws(() => parseConfig({ recallImpressionsRotateKeep: "1000000" }), /between 1 and 1000/);
|
|
2350
2350
|
}));
|
|
2351
|
+
|
|
2352
|
+
test("parseConfig forwards activity source settings", () => {
|
|
2353
|
+
const config = parseConfig({
|
|
2354
|
+
activity: {
|
|
2355
|
+
enabled: "true",
|
|
2356
|
+
timezone: "America/Chicago",
|
|
2357
|
+
syncDays: "2",
|
|
2358
|
+
sources: [{ machineLabel: "fixture-machine", baseUrl: "http://127.0.0.1:4319" }],
|
|
2359
|
+
},
|
|
2360
|
+
});
|
|
2361
|
+
|
|
2362
|
+
assert.deepEqual(config.activity, {
|
|
2363
|
+
enabled: true,
|
|
2364
|
+
timezone: "America/Chicago",
|
|
2365
|
+
syncDays: 2,
|
|
2366
|
+
autoSyncIntervalMinutes: 15,
|
|
2367
|
+
sources: [{ machineLabel: "fixture-machine", baseUrl: "http://127.0.0.1:4319" }],
|
|
2368
|
+
});
|
|
2369
|
+
});
|
package/src/config.ts
CHANGED
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
resolveNamespaceCatalogEnabled,
|
|
52
52
|
} from "./emit-legacy-tools.js";
|
|
53
53
|
import { parseWearablesConfig } from "./wearables/config.js";
|
|
54
|
+
import { parseActivityConfig } from "./activity/config.js";
|
|
54
55
|
import { parseProvenanceConfig } from "./provenance.js";
|
|
55
56
|
import { parseBoundedJsonlStateConfig } from "./bounded-jsonl-state.js";
|
|
56
57
|
import { parseCodingKnowledgeConfig } from "./coding/coding-knowledge-config.js";
|
|
@@ -1172,6 +1173,7 @@ export function parseConfig(
|
|
|
1172
1173
|
|
|
1173
1174
|
// Wearable transcript ingestion (Limitless / Bee / Omi) — delegated to the wearables module.
|
|
1174
1175
|
const wearables = parseWearablesConfig(cfg.wearables);
|
|
1176
|
+
const activity = parseActivityConfig(cfg.activity);
|
|
1175
1177
|
|
|
1176
1178
|
const provenance = parseProvenanceConfig(cfg.provenance);
|
|
1177
1179
|
// Coding-agent project/branch scoping (issue #569)
|
|
@@ -2335,6 +2337,7 @@ export function parseConfig(
|
|
|
2335
2337
|
dreamsPhases,
|
|
2336
2338
|
procedural,
|
|
2337
2339
|
wearables,
|
|
2340
|
+
activity,
|
|
2338
2341
|
provenance,
|
|
2339
2342
|
// At-rest encryption (issue #690 PR 3/4)
|
|
2340
2343
|
// coerceBool handles CLI string inputs: `--config secureStoreEnabled=true`
|