@remnic/core 9.3.624 → 9.3.626
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-cli.js +18 -16
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +12 -5
- package/dist/access-http.js +10 -9
- package/dist/access-mcp.d.ts +5 -5
- package/dist/access-mcp.js +8 -8
- package/dist/access-schema.d.ts +5 -5
- package/dist/{access-service-CBNEKjzN.d.ts → access-service-C_sfOHsX.d.ts} +26 -3
- package/dist/access-service.d.ts +5 -5
- package/dist/access-service.js +7 -7
- 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 +4 -4
- package/dist/briefing.d.ts +1 -1
- package/dist/briefing.js +3 -3
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer.d.ts +1 -1
- package/dist/calibration.d.ts +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +4 -4
- package/dist/{chunk-7TPH6UZL.js → chunk-2RHI3FGV.js} +540 -17
- package/dist/chunk-2RHI3FGV.js.map +1 -0
- package/dist/{chunk-GYTVOLNX.js → chunk-3MNBW7R7.js} +2 -2
- package/dist/{chunk-QFQQFX2H.js → chunk-3R2UZV3U.js} +2 -2
- package/dist/{chunk-O4UNM6OR.js → chunk-532VCWYW.js} +2 -2
- package/dist/{chunk-2UFQYU5F.js → chunk-57QXN2CS.js} +2 -2
- package/dist/chunk-7WV3F5DQ.js +22 -0
- package/dist/chunk-7WV3F5DQ.js.map +1 -0
- package/dist/{chunk-RKW6QR7W.js → chunk-AZ4RI3QD.js} +1461 -78
- package/dist/chunk-AZ4RI3QD.js.map +1 -0
- package/dist/{chunk-KQFQ3IS5.js → chunk-F3FY3D3S.js} +43 -7
- package/dist/chunk-F3FY3D3S.js.map +1 -0
- package/dist/{chunk-4R4KTDIE.js → chunk-FPNQF475.js} +1 -1
- package/dist/chunk-FPNQF475.js.map +1 -0
- package/dist/{chunk-UGEBPVNI.js → chunk-GE7Q7KXP.js} +2 -2
- package/dist/{chunk-GLWW3EJQ.js → chunk-KB4MFBF5.js} +3 -3
- package/dist/{chunk-5GOMXHLC.js → chunk-KKTXCFD7.js} +255 -1
- package/dist/chunk-KKTXCFD7.js.map +1 -0
- package/dist/{chunk-FH3PPO42.js → chunk-KVFYTRMV.js} +2 -2
- package/dist/{chunk-BNW5NJJH.js → chunk-LQYTQCXM.js} +2 -2
- package/dist/{chunk-AYHXQR53.js → chunk-MVQN73GT.js} +2 -2
- package/dist/{chunk-ZZPIJPPD.js → chunk-N5RGXWLQ.js} +2 -2
- package/dist/chunk-NDAH7BJ5.js +213 -0
- package/dist/chunk-NDAH7BJ5.js.map +1 -0
- package/dist/{chunk-R3OQGYOU.js → chunk-P2D2MM47.js} +2 -2
- package/dist/{chunk-PSUB67YB.js → chunk-PW6GURU3.js} +2 -2
- package/dist/{chunk-W3BKVM64.js → chunk-QDV6VAD4.js} +2 -2
- package/dist/{chunk-3QSU4NFF.js → chunk-QHXW3LZV.js} +3 -3
- package/dist/{chunk-I6UCUHLK.js → chunk-SHV5Y2WU.js} +182 -3
- package/dist/chunk-SHV5Y2WU.js.map +1 -0
- package/dist/{chunk-OZXVGYGZ.js → chunk-STDAAGH7.js} +2 -2
- package/dist/{chunk-FMGWXIES.js → chunk-TZDSNIRO.js} +5 -5
- package/dist/{chunk-2L54V4ZO.js → chunk-UELS6WWF.js} +2 -2
- package/dist/{chunk-PJGB7XRR.js → chunk-UGHUNQ74.js} +502 -134
- package/dist/chunk-UGHUNQ74.js.map +1 -0
- package/dist/{chunk-FG76RDVI.js → chunk-Y3TMFC6I.js} +136 -4
- package/dist/chunk-Y3TMFC6I.js.map +1 -0
- package/dist/{chunk-BPSGLMQ4.js → chunk-YQNADJCT.js} +2 -2
- package/dist/{cli-Cw729yLf.d.ts → cli-EZv6YE6_.d.ts} +3 -3
- package/dist/cli.d.ts +6 -6
- package/dist/cli.js +23 -21
- package/dist/compounding/engine.d.ts +1 -1
- package/dist/compounding/engine.js +3 -3
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +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 +1 -1
- package/dist/consolidation-undo.d.ts +1 -1
- 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 +1 -1
- package/dist/entity-retrieval.js +3 -3
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +4 -4
- 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/identity-continuity.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/index.d.ts +307 -9
- package/dist/index.js +155 -29
- package/dist/index.js.map +1 -1
- 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/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +3 -3
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
- package/dist/maintenance/rebuild-memory-projection.js +4 -4
- package/dist/mcp-memory-inspector-app.d.ts +5 -5
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-worth-outcomes.d.ts +1 -1
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +1 -1
- 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 +1 -1
- package/dist/namespaces/storage.js +3 -3
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +1 -1
- package/dist/operator-toolkit.js +8 -7
- package/dist/{orchestrator-CqWOjfgl.d.ts → orchestrator-CEycaY3M.d.ts} +361 -4
- package/dist/orchestrator.d.ts +4 -4
- package/dist/orchestrator.js +13 -11
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +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-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-xray-cli.d.ts +1 -1
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +1 -1
- package/dist/recall-xray.js +2 -2
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +3 -2
- package/dist/retrieval-agents.d.ts +1 -1
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/schemas.d.ts +10 -10
- 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-SLAa_prH.d.ts → semantic-DJR8_DMQ.d.ts} +1 -1
- package/dist/{semantic-consolidation-4HkHWgeI.d.ts → semantic-consolidation-FbhPeJjB.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 +5 -5
- package/dist/signal.d.ts +1 -1
- package/dist/storage.d.ts +19 -1
- 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 +1 -1
- package/dist/temporal-validity.d.ts +1 -1
- package/dist/threading.d.ts +1 -1
- package/dist/tier-migration.d.ts +1 -1
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/types-D5VRAI04.d.ts +3134 -0
- package/dist/types.d.ts +3 -2862
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +3 -3
- package/package.json +1 -1
- package/src/access-http.ts +167 -0
- package/src/access-mcp.ts +198 -0
- package/src/access-service.ts +65 -0
- package/src/cli.ts +187 -0
- package/src/config.ts +7 -0
- package/src/index.ts +7 -0
- package/src/orchestrator.ts +42 -0
- package/src/storage.ts +106 -0
- package/src/types.ts +5 -0
- package/src/wearables/cleanup.test.ts +134 -0
- package/src/wearables/cleanup.ts +188 -0
- package/src/wearables/cli.test.ts +170 -0
- package/src/wearables/cli.ts +441 -0
- package/src/wearables/config.test.ts +143 -0
- package/src/wearables/config.ts +332 -0
- package/src/wearables/corrections.test.ts +118 -0
- package/src/wearables/corrections.ts +211 -0
- package/src/wearables/day-store.test.ts +143 -0
- package/src/wearables/day-store.ts +238 -0
- package/src/wearables/errors.test.ts +32 -0
- package/src/wearables/errors.ts +29 -0
- package/src/wearables/index.ts +114 -0
- package/src/wearables/memory-gen.test.ts +342 -0
- package/src/wearables/memory-gen.ts +413 -0
- package/src/wearables/pipeline.test.ts +608 -0
- package/src/wearables/pipeline.ts +519 -0
- package/src/wearables/redaction.test.ts +94 -0
- package/src/wearables/redaction.ts +156 -0
- package/src/wearables/registry.test.ts +62 -0
- package/src/wearables/registry.ts +133 -0
- package/src/wearables/service.test.ts +425 -0
- package/src/wearables/service.ts +691 -0
- package/src/wearables/speakers.test.ts +110 -0
- package/src/wearables/speakers.ts +174 -0
- package/src/wearables/storage-io.test.ts +105 -0
- package/src/wearables/sync-state.test.ts +134 -0
- package/src/wearables/sync-state.ts +186 -0
- package/src/wearables/types.ts +285 -0
- package/dist/chunk-4R4KTDIE.js.map +0 -1
- package/dist/chunk-5GOMXHLC.js.map +0 -1
- package/dist/chunk-7TPH6UZL.js.map +0 -1
- package/dist/chunk-FG76RDVI.js.map +0 -1
- package/dist/chunk-I6UCUHLK.js.map +0 -1
- package/dist/chunk-KQFQ3IS5.js.map +0 -1
- package/dist/chunk-PJGB7XRR.js.map +0 -1
- package/dist/chunk-RKW6QR7W.js.map +0 -1
- /package/dist/{chunk-GYTVOLNX.js.map → chunk-3MNBW7R7.js.map} +0 -0
- /package/dist/{chunk-QFQQFX2H.js.map → chunk-3R2UZV3U.js.map} +0 -0
- /package/dist/{chunk-O4UNM6OR.js.map → chunk-532VCWYW.js.map} +0 -0
- /package/dist/{chunk-2UFQYU5F.js.map → chunk-57QXN2CS.js.map} +0 -0
- /package/dist/{chunk-UGEBPVNI.js.map → chunk-GE7Q7KXP.js.map} +0 -0
- /package/dist/{chunk-GLWW3EJQ.js.map → chunk-KB4MFBF5.js.map} +0 -0
- /package/dist/{chunk-FH3PPO42.js.map → chunk-KVFYTRMV.js.map} +0 -0
- /package/dist/{chunk-BNW5NJJH.js.map → chunk-LQYTQCXM.js.map} +0 -0
- /package/dist/{chunk-AYHXQR53.js.map → chunk-MVQN73GT.js.map} +0 -0
- /package/dist/{chunk-ZZPIJPPD.js.map → chunk-N5RGXWLQ.js.map} +0 -0
- /package/dist/{chunk-R3OQGYOU.js.map → chunk-P2D2MM47.js.map} +0 -0
- /package/dist/{chunk-PSUB67YB.js.map → chunk-PW6GURU3.js.map} +0 -0
- /package/dist/{chunk-W3BKVM64.js.map → chunk-QDV6VAD4.js.map} +0 -0
- /package/dist/{chunk-3QSU4NFF.js.map → chunk-QHXW3LZV.js.map} +0 -0
- /package/dist/{chunk-OZXVGYGZ.js.map → chunk-STDAAGH7.js.map} +0 -0
- /package/dist/{chunk-FMGWXIES.js.map → chunk-TZDSNIRO.js.map} +0 -0
- /package/dist/{chunk-2L54V4ZO.js.map → chunk-UELS6WWF.js.map} +0 -0
- /package/dist/{chunk-BPSGLMQ4.js.map → chunk-YQNADJCT.js.map} +0 -0
package/src/cli.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { createHash } from "node:crypto";
|
|
|
5
5
|
import type { Readable, Writable } from "node:stream";
|
|
6
6
|
import type { Orchestrator } from "./orchestrator.js";
|
|
7
7
|
import { ThreadingManager } from "./threading.js";
|
|
8
|
+
import { runWearablesCliCommand } from "./wearables/cli.js";
|
|
8
9
|
import type {
|
|
9
10
|
BehaviorSignalEvent,
|
|
10
11
|
ContinuityIncidentRecord,
|
|
@@ -5017,6 +5018,192 @@ export function registerCli(
|
|
|
5017
5018
|
}
|
|
5018
5019
|
});
|
|
5019
5020
|
|
|
5021
|
+
// Wearable transcript sources (Limitless / Bee / Omi). Every
|
|
5022
|
+
// subcommand forwards to the shared runner in wearables/cli.ts so
|
|
5023
|
+
// this host CLI and the standalone `remnic` CLI never fork
|
|
5024
|
+
// behavior or formatting.
|
|
5025
|
+
{
|
|
5026
|
+
const wearablesCmd = cmd
|
|
5027
|
+
.command("wearables")
|
|
5028
|
+
.description(
|
|
5029
|
+
"Wearable transcript sources (Limitless / Bee / Omi): sync, transcripts, search, speakers, corrections",
|
|
5030
|
+
);
|
|
5031
|
+
const forwardWearables = async (argv: string[]): Promise<void> => {
|
|
5032
|
+
const code = await runWearablesCliCommand(
|
|
5033
|
+
orchestrator.getWearablesService(),
|
|
5034
|
+
argv,
|
|
5035
|
+
{ stdout: process.stdout, stderr: process.stderr },
|
|
5036
|
+
);
|
|
5037
|
+
if (code !== 0) process.exitCode = code;
|
|
5038
|
+
};
|
|
5039
|
+
const stringOpt = (options: Record<string, unknown>, key: string, flag: string): string[] =>
|
|
5040
|
+
typeof options[key] === "string" && (options[key] as string).length > 0
|
|
5041
|
+
? [flag, options[key] as string]
|
|
5042
|
+
: [];
|
|
5043
|
+
|
|
5044
|
+
wearablesCmd
|
|
5045
|
+
.command("status")
|
|
5046
|
+
.description("Show configured sources, connector availability, and last sync")
|
|
5047
|
+
.option("--json", "JSON output")
|
|
5048
|
+
.action(async (...args: unknown[]) => {
|
|
5049
|
+
const options = (args[0] ?? {}) as Record<string, unknown>;
|
|
5050
|
+
await forwardWearables(["status", ...(options.json === true ? ["--json"] : [])]);
|
|
5051
|
+
});
|
|
5052
|
+
|
|
5053
|
+
wearablesCmd
|
|
5054
|
+
.command("check <source>")
|
|
5055
|
+
.description("Verify credentials/connectivity for a source")
|
|
5056
|
+
.action(async (...args: unknown[]) => {
|
|
5057
|
+
await forwardWearables(["check", String(args[0] ?? "")]);
|
|
5058
|
+
});
|
|
5059
|
+
|
|
5060
|
+
wearablesCmd
|
|
5061
|
+
.command("sync")
|
|
5062
|
+
.description("Pull, clean, and store wearable transcripts (and trust-gated memories)")
|
|
5063
|
+
.option("--source <id>", "Only this source (default: all enabled)")
|
|
5064
|
+
.option("--date <date>", "Sync exactly this day (YYYY-MM-DD)")
|
|
5065
|
+
.option("--days <n>", "Lookback window in days ending today (default 2)")
|
|
5066
|
+
.option("--force-memories", "Re-run memory extraction for unchanged days")
|
|
5067
|
+
.option("--json", "JSON output")
|
|
5068
|
+
.action(async (...args: unknown[]) => {
|
|
5069
|
+
const options = (args[0] ?? {}) as Record<string, unknown>;
|
|
5070
|
+
await forwardWearables([
|
|
5071
|
+
"sync",
|
|
5072
|
+
...stringOpt(options, "source", "--source"),
|
|
5073
|
+
...stringOpt(options, "date", "--date"),
|
|
5074
|
+
...stringOpt(options, "days", "--days"),
|
|
5075
|
+
...(options.forceMemories === true ? ["--force-memories"] : []),
|
|
5076
|
+
...(options.json === true ? ["--json"] : []),
|
|
5077
|
+
]);
|
|
5078
|
+
});
|
|
5079
|
+
|
|
5080
|
+
wearablesCmd
|
|
5081
|
+
.command("transcript")
|
|
5082
|
+
.description("Print the stored transcript(s) for a day")
|
|
5083
|
+
.requiredOption("--date <date>", "Day to read (YYYY-MM-DD)")
|
|
5084
|
+
.option("--source <id>", "Scope to one source")
|
|
5085
|
+
.action(async (...args: unknown[]) => {
|
|
5086
|
+
const options = (args[0] ?? {}) as Record<string, unknown>;
|
|
5087
|
+
await forwardWearables([
|
|
5088
|
+
"transcript",
|
|
5089
|
+
...stringOpt(options, "date", "--date"),
|
|
5090
|
+
...stringOpt(options, "source", "--source"),
|
|
5091
|
+
]);
|
|
5092
|
+
});
|
|
5093
|
+
|
|
5094
|
+
wearablesCmd
|
|
5095
|
+
.command("search <query>")
|
|
5096
|
+
.description("Search stored wearable transcripts")
|
|
5097
|
+
.option("--source <id>", "Source id filter")
|
|
5098
|
+
.option("--from <date>", "Inclusive start date (YYYY-MM-DD)")
|
|
5099
|
+
.option("--to <date>", "Inclusive end date (YYYY-MM-DD)")
|
|
5100
|
+
.option("--limit <n>", "Maximum results (default 10)")
|
|
5101
|
+
.option("--json", "JSON output")
|
|
5102
|
+
.action(async (...args: unknown[]) => {
|
|
5103
|
+
const query = typeof args[0] === "string" ? args[0] : "";
|
|
5104
|
+
const options = (args[1] ?? {}) as Record<string, unknown>;
|
|
5105
|
+
await forwardWearables([
|
|
5106
|
+
"search",
|
|
5107
|
+
query,
|
|
5108
|
+
...stringOpt(options, "source", "--source"),
|
|
5109
|
+
...stringOpt(options, "from", "--from"),
|
|
5110
|
+
...stringOpt(options, "to", "--to"),
|
|
5111
|
+
...stringOpt(options, "limit", "--limit"),
|
|
5112
|
+
...(options.json === true ? ["--json"] : []),
|
|
5113
|
+
]);
|
|
5114
|
+
});
|
|
5115
|
+
|
|
5116
|
+
wearablesCmd
|
|
5117
|
+
.command("memories")
|
|
5118
|
+
.description("List memories created from wearable transcripts")
|
|
5119
|
+
.option("--source <id>", "Source id filter")
|
|
5120
|
+
.option("--date <date>", "Transcript day filter (YYYY-MM-DD)")
|
|
5121
|
+
.option("--limit <n>", "Maximum results (default 50)")
|
|
5122
|
+
.option("--json", "JSON output")
|
|
5123
|
+
.action(async (...args: unknown[]) => {
|
|
5124
|
+
const options = (args[0] ?? {}) as Record<string, unknown>;
|
|
5125
|
+
await forwardWearables([
|
|
5126
|
+
"memories",
|
|
5127
|
+
...stringOpt(options, "source", "--source"),
|
|
5128
|
+
...stringOpt(options, "date", "--date"),
|
|
5129
|
+
...stringOpt(options, "limit", "--limit"),
|
|
5130
|
+
...(options.json === true ? ["--json"] : []),
|
|
5131
|
+
]);
|
|
5132
|
+
});
|
|
5133
|
+
|
|
5134
|
+
const speakersCmd = wearablesCmd
|
|
5135
|
+
.command("speakers")
|
|
5136
|
+
.description("Manage the speaker registry");
|
|
5137
|
+
speakersCmd
|
|
5138
|
+
.command("list")
|
|
5139
|
+
.description("Show stored speaker mappings")
|
|
5140
|
+
.action(async () => forwardWearables(["speakers", "list"]));
|
|
5141
|
+
speakersCmd
|
|
5142
|
+
.command("self <name>")
|
|
5143
|
+
.description("Set the wearer's display name")
|
|
5144
|
+
.action(async (...args: unknown[]) =>
|
|
5145
|
+
forwardWearables(["speakers", "self", String(args[0] ?? "")]),
|
|
5146
|
+
);
|
|
5147
|
+
speakersCmd
|
|
5148
|
+
.command("set <source> <speakerKey> <name>")
|
|
5149
|
+
.description("Map a provider speaker label to a display name")
|
|
5150
|
+
.option("--self", "Mark this speaker as the wearer")
|
|
5151
|
+
.action(async (...args: unknown[]) => {
|
|
5152
|
+
const options = (args[3] ?? {}) as Record<string, unknown>;
|
|
5153
|
+
await forwardWearables([
|
|
5154
|
+
"speakers",
|
|
5155
|
+
"set",
|
|
5156
|
+
String(args[0] ?? ""),
|
|
5157
|
+
String(args[1] ?? ""),
|
|
5158
|
+
String(args[2] ?? ""),
|
|
5159
|
+
...(options.self === true ? ["--self"] : []),
|
|
5160
|
+
]);
|
|
5161
|
+
});
|
|
5162
|
+
speakersCmd
|
|
5163
|
+
.command("remove <source> <speakerKey>")
|
|
5164
|
+
.description("Remove a speaker mapping")
|
|
5165
|
+
.action(async (...args: unknown[]) =>
|
|
5166
|
+
forwardWearables([
|
|
5167
|
+
"speakers",
|
|
5168
|
+
"remove",
|
|
5169
|
+
String(args[0] ?? ""),
|
|
5170
|
+
String(args[1] ?? ""),
|
|
5171
|
+
]),
|
|
5172
|
+
);
|
|
5173
|
+
|
|
5174
|
+
const correctionsCmd = wearablesCmd
|
|
5175
|
+
.command("corrections")
|
|
5176
|
+
.description("Manage user-specific transcript correction rules");
|
|
5177
|
+
correctionsCmd
|
|
5178
|
+
.command("list")
|
|
5179
|
+
.description("Show correction rules from config and state")
|
|
5180
|
+
.action(async () => forwardWearables(["corrections", "list"]));
|
|
5181
|
+
correctionsCmd
|
|
5182
|
+
.command("add <match> <replace>")
|
|
5183
|
+
.description("Add a correction rule (quote multi-word values)")
|
|
5184
|
+
.option("--regex", "Treat <match> as a regular expression")
|
|
5185
|
+
.option("--case-sensitive", "Match case-sensitively")
|
|
5186
|
+
.option("--source <id>", "Restrict the rule to one source")
|
|
5187
|
+
.action(async (...args: unknown[]) => {
|
|
5188
|
+
const options = (args[2] ?? {}) as Record<string, unknown>;
|
|
5189
|
+
await forwardWearables([
|
|
5190
|
+
"corrections",
|
|
5191
|
+
"add",
|
|
5192
|
+
String(args[0] ?? ""),
|
|
5193
|
+
String(args[1] ?? ""),
|
|
5194
|
+
...(options.regex === true ? ["--regex"] : []),
|
|
5195
|
+
...(options.caseSensitive === true ? ["--case-sensitive"] : []),
|
|
5196
|
+
...stringOpt(options, "source", "--source"),
|
|
5197
|
+
]);
|
|
5198
|
+
});
|
|
5199
|
+
correctionsCmd
|
|
5200
|
+
.command("remove <index>")
|
|
5201
|
+
.description("Remove a state correction rule by index")
|
|
5202
|
+
.action(async (...args: unknown[]) =>
|
|
5203
|
+
forwardWearables(["corrections", "remove", String(args[0] ?? "")]),
|
|
5204
|
+
);
|
|
5205
|
+
}
|
|
5206
|
+
|
|
5020
5207
|
cmd
|
|
5021
5208
|
.command("benchmark-status")
|
|
5022
5209
|
.description("Show benchmark/evaluation harness status, benchmark packs, and latest run summary")
|
package/src/config.ts
CHANGED
|
@@ -35,6 +35,7 @@ import { expandTildePath } from "./utils/path.js";
|
|
|
35
35
|
// lives in connectors/coerce.ts (a tiny, dependency-free module) so neither
|
|
36
36
|
// config.ts → connectors/index.ts nor the reverse circular import arises.
|
|
37
37
|
import { coerceBool, coerceInstallExtension, coerceNumber } from "./connectors/coerce.js";
|
|
38
|
+
import { parseWearablesConfig } from "./wearables/config.js";
|
|
38
39
|
|
|
39
40
|
const DEFAULT_MEMORY_DIR = path.join(
|
|
40
41
|
resolveHomeDir(),
|
|
@@ -1142,6 +1143,11 @@ export function parseConfig(raw: unknown): PluginConfig {
|
|
|
1142
1143
|
recallMaxProcedures,
|
|
1143
1144
|
};
|
|
1144
1145
|
|
|
1146
|
+
// Wearable transcript ingestion (Limitless / Bee / Omi). Parsing is
|
|
1147
|
+
// delegated to the wearables module; it follows the same loud-reject
|
|
1148
|
+
// conventions as the `procedural` block above.
|
|
1149
|
+
const wearables = parseWearablesConfig(cfg.wearables);
|
|
1150
|
+
|
|
1145
1151
|
// Coding-agent project/branch scoping (issue #569)
|
|
1146
1152
|
const rawCodingMode =
|
|
1147
1153
|
cfg.codingMode && typeof cfg.codingMode === "object" && !Array.isArray(cfg.codingMode)
|
|
@@ -2018,6 +2024,7 @@ export function parseConfig(raw: unknown): PluginConfig {
|
|
|
2018
2024
|
dreaming,
|
|
2019
2025
|
dreamsPhases,
|
|
2020
2026
|
procedural,
|
|
2027
|
+
wearables,
|
|
2021
2028
|
// At-rest encryption (issue #690 PR 3/4)
|
|
2022
2029
|
// coerceBool handles CLI string inputs: `--config secureStoreEnabled=true`
|
|
2023
2030
|
// arrives as the string "true" which `=== true` would reject (CLAUDE.md #36).
|
package/src/index.ts
CHANGED
|
@@ -1038,6 +1038,13 @@ export {
|
|
|
1038
1038
|
type BulkImportCliCommandOptions,
|
|
1039
1039
|
} from "./cli.js";
|
|
1040
1040
|
|
|
1041
|
+
// ---------------------------------------------------------------------------
|
|
1042
|
+
// Wearable transcript subsystem (Limitless / Bee / Omi connectors).
|
|
1043
|
+
// Connector packages import the registry + types from here.
|
|
1044
|
+
// ---------------------------------------------------------------------------
|
|
1045
|
+
|
|
1046
|
+
export * from "./wearables/index.js";
|
|
1047
|
+
|
|
1041
1048
|
// ---------------------------------------------------------------------------
|
|
1042
1049
|
// Shared importer base (issue #568)
|
|
1043
1050
|
// ---------------------------------------------------------------------------
|
package/src/orchestrator.ts
CHANGED
|
@@ -262,6 +262,7 @@ import {
|
|
|
262
262
|
} from "./native-knowledge.js";
|
|
263
263
|
import { normalizeReplaySessionKey, type ReplayTurn } from "./replay/types.js";
|
|
264
264
|
import type { ImportTurn } from "./bulk-import/types.js";
|
|
265
|
+
import { WearablesService } from "./wearables/service.js";
|
|
265
266
|
import {
|
|
266
267
|
type AgentPersonaModelConfig,
|
|
267
268
|
confidenceTier,
|
|
@@ -1694,6 +1695,7 @@ export class Orchestrator {
|
|
|
1694
1695
|
(observation: ConsolidationObservation) => Promise<void> | void
|
|
1695
1696
|
>();
|
|
1696
1697
|
private qmdMaintenanceTimer: NodeJS.Timeout | null = null;
|
|
1698
|
+
private wearablesServiceInstance: WearablesService | null = null;
|
|
1697
1699
|
private qmdMaintenancePending = false;
|
|
1698
1700
|
private qmdMaintenanceInFlight = false;
|
|
1699
1701
|
private lastQmdEmbedAtMs = 0;
|
|
@@ -10765,6 +10767,46 @@ export class Orchestrator {
|
|
|
10765
10767
|
return this.config.defaultNamespace;
|
|
10766
10768
|
}
|
|
10767
10769
|
|
|
10770
|
+
/**
|
|
10771
|
+
* Lazily-constructed wearables service (Limitless / Bee / Omi
|
|
10772
|
+
* transcript ingestion). All wearables surfaces — CLI, MCP tools,
|
|
10773
|
+
* HTTP routes — share this one instance so sync state, search, and
|
|
10774
|
+
* memory writes stay consistent. Writes are pinned to the same
|
|
10775
|
+
* deterministic namespace bulk-import uses.
|
|
10776
|
+
*/
|
|
10777
|
+
getWearablesService(): WearablesService {
|
|
10778
|
+
if (!this.wearablesServiceInstance) {
|
|
10779
|
+
this.wearablesServiceInstance = new WearablesService({
|
|
10780
|
+
config: this.config.wearables,
|
|
10781
|
+
getStorage: async () =>
|
|
10782
|
+
await this.getStorageForNamespace(this.bulkImportWriteNamespace()),
|
|
10783
|
+
extract: (turns) => this.extraction.extract(turns),
|
|
10784
|
+
searchBackend: {
|
|
10785
|
+
search: async (query, maxResults) => {
|
|
10786
|
+
if (!this.qmd.isAvailable()) return null;
|
|
10787
|
+
try {
|
|
10788
|
+
const results = await this.qmd.search(query, undefined, maxResults);
|
|
10789
|
+
return results.map((result) => ({
|
|
10790
|
+
path: result.path,
|
|
10791
|
+
score: result.score,
|
|
10792
|
+
preview: result.snippet,
|
|
10793
|
+
}));
|
|
10794
|
+
} catch {
|
|
10795
|
+
// Backend hiccup → tell the service "unavailable" so it
|
|
10796
|
+
// runs its bounded scan fallback instead of returning a
|
|
10797
|
+
// silent empty result (CLAUDE.md rule 34).
|
|
10798
|
+
return null;
|
|
10799
|
+
}
|
|
10800
|
+
},
|
|
10801
|
+
},
|
|
10802
|
+
reindexSearch: async () => {
|
|
10803
|
+
await this.qmd.update();
|
|
10804
|
+
},
|
|
10805
|
+
});
|
|
10806
|
+
}
|
|
10807
|
+
return this.wearablesServiceInstance;
|
|
10808
|
+
}
|
|
10809
|
+
|
|
10768
10810
|
/**
|
|
10769
10811
|
* Ingest a batch of bulk-import turns (#460). Like ingestReplayBatch, this
|
|
10770
10812
|
* normalizes user/assistant turns into the extraction buffer and awaits
|
package/src/storage.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { getCachedEntities, invalidateCachedEntities, setCachedEntities } from "
|
|
|
8
8
|
import { rotateMarkdownFileToArchive } from "./hygiene.js";
|
|
9
9
|
import { sanitizeMemoryContent } from "./sanitize.js";
|
|
10
10
|
import { createVersion as createPageVersion, type VersioningConfig, type VersionTrigger } from "./page-versioning.js";
|
|
11
|
+
import { isValidTranscriptDate, WEARABLES_DIR_NAME } from "./wearables/day-store.js";
|
|
11
12
|
import {
|
|
12
13
|
SecureStoreLockedError,
|
|
13
14
|
MAGIC_HEADER_SIZE,
|
|
@@ -2490,6 +2491,111 @@ export class StorageManager {
|
|
|
2490
2491
|
return this.readSharedVersion("artifact-write", StorageManager.artifactWriteVersionByDir);
|
|
2491
2492
|
}
|
|
2492
2493
|
|
|
2494
|
+
// -------------------------------------------------------------------------
|
|
2495
|
+
// Wearable day transcripts
|
|
2496
|
+
//
|
|
2497
|
+
// Stored under `<baseDir>/wearables/<source>/<YYYY-MM-DD>.md` — outside
|
|
2498
|
+
// the memory scan roots (never surfaces as a memory) but inside the QMD
|
|
2499
|
+
// collection root (full-text searchable). IO lives here so transcripts
|
|
2500
|
+
// inherit the same encrypted-at-rest + atomic-write semantics as
|
|
2501
|
+
// memories.
|
|
2502
|
+
// -------------------------------------------------------------------------
|
|
2503
|
+
|
|
2504
|
+
private get wearablesDir(): string {
|
|
2505
|
+
return path.join(this.baseDir, WEARABLES_DIR_NAME);
|
|
2506
|
+
}
|
|
2507
|
+
|
|
2508
|
+
/**
|
|
2509
|
+
* Resolve the on-disk path for a source/day transcript. Throws on
|
|
2510
|
+
* malformed inputs — source ids and dates reach this from CLI/MCP/HTTP
|
|
2511
|
+
* surfaces and must never become path traversal.
|
|
2512
|
+
*/
|
|
2513
|
+
wearableTranscriptPath(sourceId: string, date: string): string {
|
|
2514
|
+
if (typeof sourceId !== "string" || !/^[a-z][a-z0-9-]{0,63}$/.test(sourceId)) {
|
|
2515
|
+
throw new Error(
|
|
2516
|
+
`invalid wearable source id '${String(sourceId)}' — expected lowercase letters, digits, and dashes`,
|
|
2517
|
+
);
|
|
2518
|
+
}
|
|
2519
|
+
if (!isValidTranscriptDate(date)) {
|
|
2520
|
+
throw new Error(
|
|
2521
|
+
`invalid wearable transcript date '${String(date)}' — expected YYYY-MM-DD`,
|
|
2522
|
+
);
|
|
2523
|
+
}
|
|
2524
|
+
return path.join(this.wearablesDir, sourceId, `${date}.md`);
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
async writeWearableDayTranscript(
|
|
2528
|
+
sourceId: string,
|
|
2529
|
+
date: string,
|
|
2530
|
+
serialized: string,
|
|
2531
|
+
): Promise<void> {
|
|
2532
|
+
const targetPath = this.wearableTranscriptPath(sourceId, date);
|
|
2533
|
+
// writeMaybeEncryptedFile handles mkdir + atomic temp→rename.
|
|
2534
|
+
await writeMaybeEncryptedFile(targetPath, serialized, this.resolveWriteKey(), {}, this.baseDir);
|
|
2535
|
+
}
|
|
2536
|
+
|
|
2537
|
+
/** Read a stored day transcript; null when the day has no file. */
|
|
2538
|
+
async readWearableDayTranscript(
|
|
2539
|
+
sourceId: string,
|
|
2540
|
+
date: string,
|
|
2541
|
+
): Promise<string | null> {
|
|
2542
|
+
const targetPath = this.wearableTranscriptPath(sourceId, date);
|
|
2543
|
+
try {
|
|
2544
|
+
return await readMaybeEncryptedFile(targetPath, this._secureStoreKey, this.baseDir);
|
|
2545
|
+
} catch (err) {
|
|
2546
|
+
if ((err as NodeJS.ErrnoException).code === "ENOENT") return null;
|
|
2547
|
+
throw err;
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
|
|
2551
|
+
/**
|
|
2552
|
+
* List stored transcript days, newest first, optionally scoped to one
|
|
2553
|
+
* source. Non-transcript files in the tree are ignored.
|
|
2554
|
+
*/
|
|
2555
|
+
async listWearableTranscriptDays(
|
|
2556
|
+
sourceId?: string,
|
|
2557
|
+
): Promise<Array<{ source: string; date: string }>> {
|
|
2558
|
+
const days: Array<{ source: string; date: string }> = [];
|
|
2559
|
+
let sources: string[];
|
|
2560
|
+
if (sourceId !== undefined) {
|
|
2561
|
+
sources = [sourceId];
|
|
2562
|
+
} else {
|
|
2563
|
+
try {
|
|
2564
|
+
const entries = await readdir(this.wearablesDir, { withFileTypes: true });
|
|
2565
|
+
sources = entries
|
|
2566
|
+
.filter((entry) => entry.isDirectory())
|
|
2567
|
+
.map((entry) => entry.name);
|
|
2568
|
+
} catch (err) {
|
|
2569
|
+
if ((err as NodeJS.ErrnoException).code === "ENOENT") return [];
|
|
2570
|
+
throw err;
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
for (const source of sources) {
|
|
2574
|
+
if (!/^[a-z][a-z0-9-]{0,63}$/.test(source)) continue;
|
|
2575
|
+
let entries: string[];
|
|
2576
|
+
try {
|
|
2577
|
+
entries = await readdir(path.join(this.wearablesDir, source));
|
|
2578
|
+
} catch (err) {
|
|
2579
|
+
if ((err as NodeJS.ErrnoException).code === "ENOENT") continue;
|
|
2580
|
+
throw err;
|
|
2581
|
+
}
|
|
2582
|
+
for (const entry of entries) {
|
|
2583
|
+
if (!entry.endsWith(".md")) continue;
|
|
2584
|
+
const date = entry.slice(0, -3);
|
|
2585
|
+
if (!isValidTranscriptDate(date)) continue;
|
|
2586
|
+
days.push({ source, date });
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
days.sort((a, b) => {
|
|
2590
|
+
if (a.date > b.date) return -1;
|
|
2591
|
+
if (a.date < b.date) return 1;
|
|
2592
|
+
if (a.source < b.source) return -1;
|
|
2593
|
+
if (a.source > b.source) return 1;
|
|
2594
|
+
return 0;
|
|
2595
|
+
});
|
|
2596
|
+
return days;
|
|
2597
|
+
}
|
|
2598
|
+
|
|
2493
2599
|
private get factsDir(): string {
|
|
2494
2600
|
return path.join(this.baseDir, "facts");
|
|
2495
2601
|
}
|
package/src/types.ts
CHANGED
|
@@ -871,6 +871,11 @@ export interface PluginConfig {
|
|
|
871
871
|
*/
|
|
872
872
|
dreamsPhases: DreamsPhasesConfig;
|
|
873
873
|
procedural: ProceduralConfig;
|
|
874
|
+
/**
|
|
875
|
+
* Wearable transcript ingestion (Limitless / Bee / Omi connectors).
|
|
876
|
+
* Disabled by default; see docs/wearables.md.
|
|
877
|
+
*/
|
|
878
|
+
wearables: import("./wearables/types.js").WearablesConfig;
|
|
874
879
|
/**
|
|
875
880
|
* At-rest encryption configuration (issue #690 PR 3/4).
|
|
876
881
|
*
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { test } from "node:test";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
cleanConversation,
|
|
6
|
+
collapseImmediateRepeats,
|
|
7
|
+
isLowQualitySegment,
|
|
8
|
+
stripFillerTokens,
|
|
9
|
+
} from "./cleanup.js";
|
|
10
|
+
import type { WearableCleanupSettings, WearableConversation } from "./types.js";
|
|
11
|
+
|
|
12
|
+
const ALL_ON: WearableCleanupSettings = {
|
|
13
|
+
mergeSameSpeaker: true,
|
|
14
|
+
stripFillers: true,
|
|
15
|
+
collapseRepeats: true,
|
|
16
|
+
dropLowQuality: true,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function conversation(
|
|
20
|
+
segments: Array<{ speakerKey: string; text: string; startIso?: string; endIso?: string }>,
|
|
21
|
+
): WearableConversation {
|
|
22
|
+
return {
|
|
23
|
+
id: "c1",
|
|
24
|
+
source: "testsource",
|
|
25
|
+
startIso: "2026-06-10T10:00:00Z",
|
|
26
|
+
segments,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
test("stripFillerTokens removes standalone fillers but not words containing them", () => {
|
|
31
|
+
assert.equal(stripFillerTokens("Um, so we should ship it"), "so we should ship it");
|
|
32
|
+
assert.equal(stripFillerTokens("Grab the umbrella, uh, before noon"), "Grab the umbrella, before noon");
|
|
33
|
+
assert.equal(stripFillerTokens("hmm"), "");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("collapseImmediateRepeats collapses word and phrase stutters fully", () => {
|
|
37
|
+
assert.equal(collapseImmediateRepeats("I I I think so"), "I think so");
|
|
38
|
+
assert.equal(collapseImmediateRepeats("we should we should go"), "we should go");
|
|
39
|
+
assert.equal(collapseImmediateRepeats("that's a really really really good point"), "that's a really good point");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("collapseImmediateRepeats never collapses pure digit sequences", () => {
|
|
43
|
+
assert.equal(collapseImmediateRepeats("call 555 555 1234 now"), "call 555 555 1234 now");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("isLowQualitySegment flags garbage and keeps real speech", () => {
|
|
47
|
+
assert.equal(isLowQualitySegment("aaaaaaaaaa"), true);
|
|
48
|
+
assert.equal(isLowQualitySegment("%$#@! ---- ////"), true);
|
|
49
|
+
assert.equal(isLowQualitySegment("yeah yeah yeah yeah yeah"), true);
|
|
50
|
+
assert.equal(isLowQualitySegment("Let's review the budget tomorrow."), false);
|
|
51
|
+
assert.equal(isLowQualitySegment("ok"), false);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("merges consecutive same-speaker segments within the gap", () => {
|
|
55
|
+
const result = cleanConversation(
|
|
56
|
+
conversation([
|
|
57
|
+
{
|
|
58
|
+
speakerKey: "a",
|
|
59
|
+
text: "First part.",
|
|
60
|
+
startIso: "2026-06-10T10:00:00Z",
|
|
61
|
+
endIso: "2026-06-10T10:00:05Z",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
speakerKey: "a",
|
|
65
|
+
text: "Second part.",
|
|
66
|
+
startIso: "2026-06-10T10:00:10Z",
|
|
67
|
+
endIso: "2026-06-10T10:00:15Z",
|
|
68
|
+
},
|
|
69
|
+
{ speakerKey: "b", text: "Reply.", startIso: "2026-06-10T10:00:20Z" },
|
|
70
|
+
]),
|
|
71
|
+
ALL_ON,
|
|
72
|
+
);
|
|
73
|
+
assert.equal(result.conversation.segments.length, 2);
|
|
74
|
+
assert.equal(result.conversation.segments[0].text, "First part. Second part.");
|
|
75
|
+
assert.equal(result.conversation.segments[0].endIso, "2026-06-10T10:00:15Z");
|
|
76
|
+
assert.equal(result.mergedSegments, 1);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("does not merge across a long silence gap", () => {
|
|
80
|
+
const result = cleanConversation(
|
|
81
|
+
conversation([
|
|
82
|
+
{
|
|
83
|
+
speakerKey: "a",
|
|
84
|
+
text: "Before lunch.",
|
|
85
|
+
startIso: "2026-06-10T10:00:00Z",
|
|
86
|
+
endIso: "2026-06-10T10:00:05Z",
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
speakerKey: "a",
|
|
90
|
+
text: "After lunch.",
|
|
91
|
+
startIso: "2026-06-10T11:30:00Z",
|
|
92
|
+
endIso: "2026-06-10T11:30:05Z",
|
|
93
|
+
},
|
|
94
|
+
]),
|
|
95
|
+
ALL_ON,
|
|
96
|
+
);
|
|
97
|
+
assert.equal(result.conversation.segments.length, 2);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("drops low-quality segments and counts them", () => {
|
|
101
|
+
const result = cleanConversation(
|
|
102
|
+
conversation([
|
|
103
|
+
{ speakerKey: "a", text: "Real sentence about plans." },
|
|
104
|
+
{ speakerKey: "a", text: "zzzzzzzzz" },
|
|
105
|
+
]),
|
|
106
|
+
{ ...ALL_ON, mergeSameSpeaker: false },
|
|
107
|
+
);
|
|
108
|
+
assert.equal(result.conversation.segments.length, 1);
|
|
109
|
+
assert.equal(result.droppedSegments, 1);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("respects disabled passes", () => {
|
|
113
|
+
const result = cleanConversation(
|
|
114
|
+
conversation([
|
|
115
|
+
{ speakerKey: "a", text: "Um, well well well" },
|
|
116
|
+
{ speakerKey: "a", text: "Um, again" },
|
|
117
|
+
]),
|
|
118
|
+
{
|
|
119
|
+
mergeSameSpeaker: false,
|
|
120
|
+
stripFillers: false,
|
|
121
|
+
collapseRepeats: false,
|
|
122
|
+
dropLowQuality: false,
|
|
123
|
+
},
|
|
124
|
+
);
|
|
125
|
+
assert.equal(result.conversation.segments.length, 2);
|
|
126
|
+
assert.equal(result.conversation.segments[0].text, "Um, well well well");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("input conversation is not mutated", () => {
|
|
130
|
+
const input = conversation([{ speakerKey: "a", text: "Um, hello there" }]);
|
|
131
|
+
const before = JSON.stringify(input);
|
|
132
|
+
cleanConversation(input, ALL_ON);
|
|
133
|
+
assert.equal(JSON.stringify(input), before);
|
|
134
|
+
});
|