akm-cli 0.9.17-alpha.2 → 0.9.17-alpha.4
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/CHANGELOG.md +756 -0
- package/dist/akm +94 -196
- package/dist/cli/shared.js +6 -2
- package/dist/cli.js +22 -9
- package/dist/commands/agent/agent-dispatch.js +1 -1
- package/dist/commands/command/command-execution.js +24 -62
- package/dist/commands/feedback-cli.js +0 -1
- package/dist/commands/health/accept-rate.js +2 -2
- package/dist/commands/health/checks.js +30 -75
- package/dist/commands/health/config-skew.js +38 -0
- package/dist/commands/health/egress.js +54 -0
- package/dist/commands/health/html-report.js +0 -38
- package/dist/commands/health/improve-metrics.js +123 -562
- package/dist/commands/health/plugin-staleness.js +53 -3
- package/dist/commands/health/renderers.js +12 -4
- package/dist/commands/health/report-view-model.js +11 -106
- package/dist/commands/health/types-improve.js +4 -19
- package/dist/commands/health/windows.js +64 -73
- package/dist/commands/health.js +122 -143
- package/dist/commands/improve/consolidate/chunking.js +25 -100
- package/dist/commands/improve/consolidate/sanitize.js +54 -149
- package/dist/commands/improve/consolidate.js +538 -1075
- package/dist/commands/improve/content-hash.js +16 -24
- package/dist/commands/improve/distill/content-repair.js +18 -100
- package/dist/commands/improve/distill-guards.js +20 -81
- package/dist/commands/improve/distill-promotion-policy.js +23 -243
- package/dist/commands/improve/distill.js +608 -1075
- package/dist/commands/improve/eligibility.js +126 -400
- package/dist/commands/improve/execution.js +3 -5
- package/dist/commands/improve/extract.js +487 -1046
- package/dist/commands/improve/feedback-valence.js +0 -25
- package/dist/commands/improve/improve-cli.js +29 -166
- package/dist/commands/improve/improve-result-file.js +10 -66
- package/dist/commands/improve/improve-strategies.js +12 -7
- package/dist/commands/improve/improve-usage-report.js +18 -64
- package/dist/commands/improve/improve.js +443 -1063
- package/dist/commands/improve/ledger.js +114 -0
- package/dist/commands/improve/locks.js +2 -8
- package/dist/commands/improve/loop-stages.js +459 -1172
- package/dist/commands/improve/memory/derived-ref.js +12 -77
- package/dist/commands/improve/memory/memory-belief.js +14 -118
- package/dist/commands/improve/memory/memory-improve.js +4 -3
- package/dist/commands/improve/outcome-loop.js +28 -156
- package/dist/commands/improve/planner.js +5 -10
- package/dist/commands/improve/preparation.js +851 -2339
- package/dist/commands/improve/proactive-maintenance.js +34 -101
- package/dist/commands/improve/reflect-noise.js +104 -280
- package/dist/commands/improve/reflect.js +621 -1367
- package/dist/commands/improve/salience.js +46 -232
- package/dist/commands/improve/session-asset.js +19 -100
- package/dist/commands/improve/stage.js +323 -0
- package/dist/commands/proposal/drain.js +251 -644
- package/dist/commands/proposal/proposal-cli.js +3 -18
- package/dist/commands/proposal/proposal-types.js +20 -41
- package/dist/commands/proposal/proposal.js +1 -2
- package/dist/commands/proposal/propose.js +134 -160
- package/dist/commands/proposal/repository.js +502 -1487
- package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
- package/dist/commands/proposal/validators/proposal-validators.js +1 -1
- package/dist/commands/proposal/validators/proposals.js +13 -89
- package/dist/commands/read/curate.js +63 -413
- package/dist/commands/read/search-cli.js +16 -33
- package/dist/commands/read/search.js +17 -23
- package/dist/commands/read/show.js +2 -13
- package/dist/commands/sources/bundle-cli.js +25 -2
- package/dist/commands/sources/bundle-config-ops.js +7 -0
- package/dist/commands/sources/dangerous-env-audit.js +1 -2
- package/dist/commands/sources/info.js +2 -11
- package/dist/commands/sources/installed-stashes.js +197 -746
- package/dist/commands/sources/schema-repair.js +98 -129
- package/dist/commands/sources/source-add.js +62 -12
- package/dist/commands/sources/stash-cli.js +1 -1
- package/dist/commands/tasks/explain.js +10 -13
- package/dist/commands/tasks/tasks-cli.js +9 -8
- package/dist/commands/tasks/tasks.js +326 -930
- package/dist/commands/tasks/validate.js +42 -21
- package/dist/commands/workflow/plan.js +22 -29
- package/dist/commands/workflow-cli.js +4 -4
- package/dist/core/adapter/adapters/akm-adapter.js +0 -1
- package/dist/core/adapter/adapters/akm-lint.js +2 -3
- package/dist/core/adapter/adapters/akm-metadata.js +11 -12
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
- package/dist/core/adapter/execution-source.js +17 -29
- package/dist/core/asset/resolve-ref.js +1 -1
- package/dist/core/bundle-id.js +42 -5
- package/dist/core/bundle-rename.js +291 -0
- package/dist/core/config/config-io.js +1 -2
- package/dist/core/config/config-schema.js +1 -33
- package/dist/core/config/config-walker.js +1 -1
- package/dist/core/config/config.js +163 -68
- package/dist/core/config/legacy-source-shape-shim.js +38 -9
- package/dist/core/config/schema/embedding.js +20 -5
- package/dist/core/config/schema/engines.js +5 -0
- package/dist/core/config/schema/execution.js +1 -1
- package/dist/core/config/schema/experimental.js +1 -1
- package/dist/core/config/schema/improve-processes.js +21 -95
- package/dist/core/config/schema/improve.js +4 -42
- package/dist/core/config/schema/scheduler.js +12 -12
- package/dist/core/config/schema/search.js +6 -22
- package/dist/core/env-secret-ref.js +0 -1
- package/dist/core/errors.js +8 -9
- package/dist/core/file-lock.js +76 -173
- package/dist/core/logs-db.js +2 -2
- package/dist/core/paths.js +0 -27
- package/dist/core/redaction.js +109 -2
- package/dist/core/run-lock.js +2 -5
- package/dist/core/spawn-env.js +1 -1
- package/dist/core/state/migrations.js +108 -61
- package/dist/core/state-db-scope.js +2 -4
- package/dist/core/state-db.js +126 -692
- package/dist/core/type-presentation.js +1 -9
- package/dist/core/write-source.js +293 -1012
- package/dist/execution/input-contract.js +1 -1
- package/dist/execution/resolved-request.js +135 -689
- package/dist/execution/source.js +63 -257
- package/dist/execution/target-ref.js +1 -1
- package/dist/indexer/bundle-identity-guard.js +2 -2
- package/dist/indexer/db/graph-db.js +106 -46
- package/dist/indexer/ensure-index.js +44 -85
- package/dist/indexer/graph/graph-extraction.js +340 -562
- package/dist/indexer/graph/graph-related.js +130 -0
- package/dist/indexer/index-rebuild-lock.js +3 -11
- package/dist/indexer/index-writer-lock.js +8 -17
- package/dist/indexer/index-written-assets.js +139 -151
- package/dist/indexer/indexer.js +524 -846
- package/dist/indexer/materialize-embeddings.js +60 -397
- package/dist/indexer/passes/memory-inference.js +81 -90
- package/dist/indexer/passes/metadata.js +132 -200
- package/dist/indexer/read-preflight.js +0 -7
- package/dist/indexer/scan/doc-to-entry.js +1 -3
- package/dist/indexer/scan/drain-dir.js +1 -1
- package/dist/indexer/search/db-search.js +181 -590
- package/dist/indexer/search/fts-query.js +30 -41
- package/dist/indexer/search/ranking.js +28 -154
- package/dist/indexer/search/search-attribution.js +12 -32
- package/dist/indexer/search/search-fields.js +11 -15
- package/dist/indexer/search/search-hit-enrichers.js +54 -85
- package/dist/indexer/search/search-source.js +1 -4
- package/dist/indexer/usage/usage-events.js +2 -7
- package/dist/integrations/agent/engine-fallback.js +23 -40
- package/dist/integrations/agent/engine-resolution.js +93 -183
- package/dist/integrations/agent/execution.js +507 -0
- package/dist/integrations/agent/model-map.js +28 -156
- package/dist/integrations/agent/request-lowering.js +66 -141
- package/dist/integrations/agent/runner-dispatch.js +143 -321
- package/dist/integrations/agent/runner.js +54 -14
- package/dist/integrations/lockfile.js +53 -101
- package/dist/llm/embedders/deterministic.js +2 -3
- package/dist/llm/embedders/profile.js +71 -0
- package/dist/llm/embedders/remote.js +10 -15
- package/dist/llm/graph-extract.js +3 -12
- package/dist/llm/index-passes.js +3 -5
- package/dist/llm/memory-infer.js +1 -2
- package/dist/llm/metadata-enhance.js +1 -2
- package/dist/llm/structured-call.js +5 -24
- package/dist/output/generic-render.js +23 -11
- package/dist/output/html-render.js +13 -10
- package/dist/output/render-registry.js +3 -32
- package/dist/output/shapes/helpers.js +2 -34
- package/dist/output/shapes/passthrough.js +1 -9
- package/dist/{indexer/search/ranking-types.js → output/text/bundle-rename.js} +4 -1
- package/dist/output/text/command-format.js +60 -23
- package/dist/output/text/helpers.js +1 -1
- package/dist/output/text/migrate.js +5 -14
- package/dist/output/text/proposal-format.js +1 -2
- package/dist/output/text/workflow-format.js +0 -32
- package/dist/output/text.js +2 -0
- package/dist/registry/factory.js +4 -19
- package/dist/registry/network.js +66 -220
- package/dist/registry/providers/index.js +0 -2
- package/dist/registry/providers/skills-sh.js +3 -14
- package/dist/registry/providers/static-index.js +24 -26
- package/dist/registry/resolve.js +55 -131
- package/dist/scripts/akm-migrate-node.js +43937 -93313
- package/dist/scripts/akm-migrate.js +43697 -93071
- package/dist/setup/registry-stash-loader.js +4 -13
- package/dist/setup/semantic-assets.js +3 -44
- package/dist/setup/setup.js +1 -1
- package/dist/setup/steps/tasks.js +25 -15
- package/dist/sources/provider-factory.js +17 -18
- package/dist/sources/providers/filesystem.js +2 -3
- package/dist/sources/providers/git-install.js +7 -1
- package/dist/sources/providers/git-provider.js +0 -3
- package/dist/sources/providers/git-stash.js +0 -17
- package/dist/sources/providers/npm.js +2 -4
- package/dist/sources/providers/provider-utils.js +5 -10
- package/dist/sources/providers/website.js +0 -2
- package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
- package/dist/sources/website-url.js +2 -2
- package/dist/storage/database.js +9 -35
- package/dist/storage/repositories/improve-ledger-repository.js +168 -0
- package/dist/storage/repositories/index-connection.js +34 -70
- package/dist/storage/repositories/index-entries-repository.js +69 -111
- package/dist/storage/repositories/index-entry-mapper.js +1 -2
- package/dist/storage/repositories/index-entry-schema.js +83 -269
- package/dist/storage/repositories/index-fts-repository.js +86 -256
- package/dist/storage/repositories/index-llm-cache-repository.js +17 -0
- package/dist/storage/repositories/index-meta-repository.js +6 -4
- package/dist/storage/repositories/index-schema.js +192 -220
- package/dist/storage/repositories/index-utility-repository.js +8 -29
- package/dist/storage/repositories/index-vec-repository.js +133 -414
- package/dist/storage/repositories/outcome-repository.js +2 -1
- package/dist/storage/repositories/proposals-repository.js +35 -0
- package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
- package/dist/storage/repositories/task-history-repository.js +26 -4
- package/dist/storage/repositories/workflow-runs-repository.js +53 -244
- package/dist/storage/sqlite-migrations.js +136 -0
- package/dist/storage/sqlite-pragmas.js +11 -9
- package/dist/storage/sqlite-transaction.js +170 -0
- package/dist/storage/state-db-integrity.js +34 -27
- package/dist/tasks/activation-config.js +134 -62
- package/dist/tasks/backends/cron.js +129 -277
- package/dist/tasks/backends/exec-utils.js +2 -5
- package/dist/tasks/backends/launchd.js +125 -745
- package/dist/tasks/backends/schtasks.js +101 -620
- package/dist/tasks/prepare/prepare-support.js +5 -15
- package/dist/tasks/prepare/prepare.js +0 -2
- package/dist/tasks/resolve-akm-bin.js +20 -79
- package/dist/tasks/run/attempt-lifecycle.js +0 -1
- package/dist/tasks/scheduler-binding.js +18 -238
- package/dist/tasks/scheduler-invocation.js +52 -52
- package/dist/tasks/scheduler-lock.js +53 -0
- package/dist/tasks/scheduler-sync.js +363 -679
- package/dist/tasks/source/parse-task-source.js +160 -10
- package/dist/tasks/source/task-source-v3-frozen.js +3 -4
- package/dist/tasks/source/task-to-v4.js +2 -2
- package/dist/workflows/authoring/authoring.js +3 -12
- package/dist/workflows/compile.js +211 -0
- package/dist/workflows/concurrency-policy.js +13 -74
- package/dist/workflows/exec/child-invocation.js +3 -17
- package/dist/workflows/exec/child-workflow.js +32 -141
- package/dist/workflows/exec/dispatch-redaction.js +13 -53
- package/dist/workflows/exec/environment.js +98 -0
- package/dist/workflows/exec/exec-unit.js +33 -140
- package/dist/workflows/exec/frozen-judge.js +7 -59
- package/dist/workflows/exec/native-executor.js +82 -341
- package/dist/workflows/exec/param-secrets.js +29 -47
- package/dist/workflows/exec/run-workflow.js +154 -387
- package/dist/workflows/exec/scheduler.js +9 -36
- package/dist/workflows/exec/step-work.js +127 -430
- package/dist/workflows/exec/unit-dispatch.js +11 -63
- package/dist/workflows/exec/unit-writer.js +8 -52
- package/dist/workflows/exec/worktree.js +39 -273
- package/dist/workflows/freeze/child-output-references.js +4 -15
- package/dist/workflows/freeze/environment.js +99 -92
- package/dist/workflows/freeze/freeze.js +172 -0
- package/dist/workflows/freeze/step-values.js +19 -21
- package/dist/workflows/freeze/targets/child-workflow.js +23 -92
- package/dist/workflows/freeze/targets/command.js +10 -33
- package/dist/workflows/freeze/targets/script.js +5 -12
- package/dist/workflows/freeze/targets/shell.js +3 -6
- package/dist/workflows/freeze/targets/task.js +25 -80
- package/dist/workflows/freeze/task-bindings.js +20 -67
- package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
- package/dist/workflows/ir/params.js +6 -51
- package/dist/workflows/ir/plan-hash.js +2 -34
- package/dist/workflows/parser.js +140 -43
- package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
- package/dist/workflows/renderer.js +36 -69
- package/dist/workflows/resource-limits.js +12 -120
- package/dist/workflows/runtime/agent-identity.js +8 -40
- package/dist/workflows/runtime/run-outputs.js +3 -6
- package/dist/workflows/runtime/run-plan.js +316 -0
- package/dist/workflows/runtime/runs.js +48 -200
- package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
- package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
- package/dist/workflows/validate-summary.js +2 -7
- package/docs/integration/bundling-akm.md +49 -42
- package/docs/migration/README.md +1 -0
- package/docs/migration/release-notes/0.9.17.md +41 -0
- package/docs/migration/v0.9.1-to-v0.9.2.md +19 -7
- package/docs/reference/cli.md +182 -125
- package/docs/reference/configuration.md +49 -56
- package/docs/reference/data-and-telemetry.md +19 -20
- package/docs/reference/tasks.md +86 -38
- package/docs/reference/workflow-schema.md +14 -18
- package/docs/reference/workflows.md +6 -9
- package/package.json +1 -1
- package/schemas/akm-config.json +87 -406
- package/dist/commands/health/advisories.js +0 -150
- package/dist/commands/health/metrics.js +0 -329
- package/dist/commands/health/surfaces.js +0 -102
- package/dist/commands/improve/anti-collapse.js +0 -83
- package/dist/commands/improve/collapse-detector.js +0 -432
- package/dist/commands/improve/consolidate/eligibility.js +0 -48
- package/dist/commands/improve/consolidate/merge.js +0 -146
- package/dist/commands/improve/distill/promote-memory.js +0 -329
- package/dist/commands/improve/distill/quality-gate.js +0 -500
- package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
- package/dist/commands/improve/proposal-envelope.js +0 -31
- package/dist/commands/improve/run-context.js +0 -123
- package/dist/commands/improve/shared.js +0 -21
- package/dist/commands/improve/source-identity.js +0 -28
- package/dist/commands/improve/triage.js +0 -96
- package/dist/commands/proposal/drain-policies.js +0 -151
- package/dist/commands/sources/update-transaction.js +0 -220
- package/dist/core/action-contributors.js +0 -28
- package/dist/core/config/config-version-shim.js +0 -101
- package/dist/core/config/retired-experimental-keys-shim.js +0 -62
- package/dist/core/fs-txn.js +0 -405
- package/dist/core/lexical-score.js +0 -25
- package/dist/core/maintenance-barrier.js +0 -167
- package/dist/execution/executable-identity.js +0 -105
- package/dist/execution/guarded-source.js +0 -427
- package/dist/indexer/graph/graph-boost.js +0 -427
- package/dist/indexer/graph/graph-dedup.js +0 -95
- package/dist/indexer/search/name-match.js +0 -35
- package/dist/indexer/search/ranking-contributors.js +0 -515
- package/dist/indexer/walk/project-context.js +0 -192
- package/dist/integrations/agent/execution-cascade.js +0 -566
- package/dist/integrations/agent/execution-definitions.js +0 -202
- package/dist/integrations/agent/execution-lowering.js +0 -841
- package/dist/integrations/agent/execution-preparation.js +0 -98
- package/dist/integrations/agent/inline-execution.js +0 -74
- package/dist/registry/create-provider-registry.js +0 -29
- package/dist/registry/pinned-request-helper.js +0 -247
- package/dist/registry/pinned-transport.js +0 -717
- package/dist/sources/providers/index.js +0 -14
- package/dist/storage/engines/sqlite-migrations.js +0 -271
- package/dist/storage/repositories/canaries-repository.js +0 -107
- package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
- package/dist/storage/repositories/registry-cache.js +0 -113
- package/dist/tasks/scheduler-sync-preview.js +0 -52
- package/dist/workflows/freeze/resolve-steps.js +0 -86
- package/dist/workflows/freeze/source-freeze.js +0 -64
- package/dist/workflows/ir/compile.js +0 -321
- package/dist/workflows/ir/environment-v4.js +0 -330
- package/dist/workflows/ir/freeze-v4.js +0 -153
- package/dist/workflows/ir/schema-v4.js +0 -745
- package/dist/workflows/ir/schema.js +0 -354
- package/dist/workflows/program/schema.js +0 -78
- package/dist/workflows/runtime/checkin.js +0 -57
- package/dist/workflows/runtime/plan-classifier.js +0 -196
- package/dist/workflows/runtime/unit-checkin.js +0 -45
- package/dist/workflows/runtime/unit-phases.js +0 -20
- package/dist/workflows/schema.js +0 -4
- package/dist/workflows/source-ir/compile.js +0 -200
- package/dist/workflows/source-ir/program.js +0 -50
- package/dist/workflows/source-ir/result.js +0 -26
- package/dist/workflows/source-ir/schema.js +0 -786
- package/dist/workflows/source-ir/triggers.js +0 -79
- package/dist/workflows/source-ir/uses.js +0 -40
- package/dist/workflows/validator.js +0 -60
|
@@ -4,71 +4,25 @@
|
|
|
4
4
|
import { getAllEntries } from "../../storage/repositories/index-entries-repository.js";
|
|
5
5
|
import { getUtilityScoresByIds } from "../../storage/repositories/index-utility-repository.js";
|
|
6
6
|
import { WARM_START_CAP } from "./outcome-loop.js";
|
|
7
|
-
// ── One day in ms ─────────────────────────────────────────────────────────────
|
|
8
7
|
const DAY_MS = 86_400_000;
|
|
9
|
-
// ── Recency decay half-life (mirrors the proactive-maintenance prototype) ─────
|
|
10
8
|
const RECENCY_HALFLIFE_DAYS = 21;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// floor. This replaces the deleted homeostatic demotion pass (which was
|
|
16
|
-
// default-off and self-undoing — every salience recompute clobbered it);
|
|
17
|
-
// folding the decay into the always-applied recency term makes it persist by
|
|
18
|
-
// construction. At 180 days the floor halves; a 1-year-stale asset sits at
|
|
19
|
-
// ~0.025 instead of 0.1.
|
|
9
|
+
/**
|
|
10
|
+
* The recency floor itself halves every 180 days, so an asset nobody uses keeps
|
|
11
|
+
* drifting down instead of parking at the floor.
|
|
12
|
+
*/
|
|
20
13
|
const RECENCY_FLOOR_HALFLIFE_DAYS = 180;
|
|
21
|
-
|
|
22
|
-
// for assets whose last-use timestamp is unknown (utility_scores has no
|
|
23
|
-
// last_used_at) — without it their retrieval salience collapses to exactly 0
|
|
24
|
-
// and frequency ordering is lost for maintenance selection.
|
|
14
|
+
/** Keeps frequency ordinal for assets whose last use is unknown. */
|
|
25
15
|
const RECENCY_EPSILON = 0.01;
|
|
26
|
-
// ── Size proxy floor (avoids log10(0)) ────────────────────────────────────────
|
|
27
16
|
const SIZE_FLOOR_BYTES = 200;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
// measures use; outcome provides a quality signal proportional to usefulness.
|
|
38
|
-
//
|
|
39
|
-
// Re-tune via the Part-V measurement protocol if the throughput/quality gate
|
|
40
|
-
// shows regression after enabling the outcome weight.
|
|
41
|
-
export const W_ENCODING = 0.25; // WS-2 target encoding weight (w_e)
|
|
42
|
-
export const W_OUTCOME = 0.15; // WS-2 target outcome weight (w_o)
|
|
43
|
-
export const W_RETRIEVAL = 0.6; // WS-2 target retrieval weight (w_r)
|
|
44
|
-
// Compile-time guard: weights must sum to 1.0 (±ε). The TS initializer runs
|
|
45
|
-
// at module load, not build time, so this acts as a startup assertion.
|
|
46
|
-
if (Math.abs(W_ENCODING + W_OUTCOME + W_RETRIEVAL - 1.0) > 1e-9) {
|
|
47
|
-
throw new Error(`salience.ts: W_ENCODING + W_OUTCOME + W_RETRIEVAL must equal 1.0 (got ${W_ENCODING + W_OUTCOME + W_RETRIEVAL})`);
|
|
48
|
-
}
|
|
49
|
-
// ── WS-1 parity weights ───────────────────────────────────────────────────────
|
|
50
|
-
//
|
|
51
|
-
// These constants reflect the WS-1 parity weights used when the operator
|
|
52
|
-
// explicitly opts out (`outcomeWeightEnabled: false`). They preserve the
|
|
53
|
-
// WS-1 two-way split (w_e=0.30, w_r=0.70) with w_o=0 so outcome does not
|
|
54
|
-
// affect rankScore in the opt-out mode.
|
|
55
|
-
//
|
|
56
|
-
// Named here (rather than inline literals in the else branch) so a future
|
|
57
|
-
// re-tune has a single source of truth and the sum-to-1 guard below catches
|
|
58
|
-
// any accidental mis-edit.
|
|
59
|
-
export const W_ENCODING_PARITY = 0.3; // WS-1 parity encoding weight
|
|
60
|
-
export const W_OUTCOME_PARITY = 0; // WS-1 parity outcome weight (0 = disabled)
|
|
61
|
-
export const W_RETRIEVAL_PARITY = 0.7; // WS-1 parity retrieval weight
|
|
62
|
-
// Startup guard: parity triple must also sum to 1.0 (±ε).
|
|
63
|
-
if (Math.abs(W_ENCODING_PARITY + W_OUTCOME_PARITY + W_RETRIEVAL_PARITY - 1.0) > 1e-9) {
|
|
64
|
-
throw new Error(`salience.ts: W_ENCODING_PARITY + W_OUTCOME_PARITY + W_RETRIEVAL_PARITY must equal 1.0 (got ${W_ENCODING_PARITY + W_OUTCOME_PARITY + W_RETRIEVAL_PARITY})`);
|
|
65
|
-
}
|
|
66
|
-
// ── Type-importance fallback weights (#608 landed) ────────────────────────────
|
|
67
|
-
//
|
|
68
|
-
// The real encoding salience estimator is `scoreEncodingSalience` in
|
|
69
|
-
// `encoding-salience.ts` (#608). These weights are the fallback used when
|
|
70
|
-
// `SalienceInputs.encodingSalience` is absent (pre-#608 assets without a
|
|
71
|
-
// frontmatter `salience:` field or a state.db row seeded by distill).
|
|
17
|
+
/** Default weights: the outcome term is on (opt out with `improve.salience.outcomeWeightEnabled: false`). */
|
|
18
|
+
export const W_ENCODING = 0.25;
|
|
19
|
+
export const W_OUTCOME = 0.15;
|
|
20
|
+
export const W_RETRIEVAL = 0.6;
|
|
21
|
+
/** Weights with the outcome term off. */
|
|
22
|
+
export const W_ENCODING_PARITY = 0.3;
|
|
23
|
+
export const W_OUTCOME_PARITY = 0;
|
|
24
|
+
export const W_RETRIEVAL_PARITY = 0.7;
|
|
25
|
+
/** Encoding salience by type, for assets with no content-derived score yet. */
|
|
72
26
|
export const DEFAULT_TYPE_ENCODING_WEIGHTS = Object.freeze({
|
|
73
27
|
skill: 0.9,
|
|
74
28
|
agent: 0.9,
|
|
@@ -79,218 +33,78 @@ export const DEFAULT_TYPE_ENCODING_WEIGHTS = Object.freeze({
|
|
|
79
33
|
script: 0.6,
|
|
80
34
|
memory: 0.5,
|
|
81
35
|
});
|
|
82
|
-
/** Default encoding salience for types not in the table above. */
|
|
83
36
|
export const DEFAULT_ENCODING_SALIENCE = 0.5;
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Compute the salience vector for one asset.
|
|
87
|
-
*
|
|
88
|
-
* Pure function — no I/O. All inputs are pre-fetched by the caller.
|
|
89
|
-
*/
|
|
37
|
+
const clamp01 = (value) => Math.min(1, Math.max(0, value));
|
|
38
|
+
/** The salience vector for one asset (pure). */
|
|
90
39
|
export function computeSalience(inputs) {
|
|
91
40
|
const now = inputs.now ?? Date.now();
|
|
92
|
-
// ── Encoding salience ────────────────────────────────────────────────────────
|
|
93
|
-
//
|
|
94
|
-
// When `inputs.encodingSalience` is provided (computed by `scoreEncodingSalience`
|
|
95
|
-
// in encoding-salience.ts at extract/distill time, #608), use it directly.
|
|
96
|
-
// Fall back to the type-importance stub only when the caller has not yet
|
|
97
|
-
// computed a content-based score (e.g. on older assets before the first
|
|
98
|
-
// staleness refresh runs).
|
|
99
41
|
const encodingSource = inputs.encodingSalience !== undefined ? "content" : "type-stub";
|
|
100
42
|
const encoding = inputs.encodingSalience !== undefined
|
|
101
|
-
?
|
|
43
|
+
? clamp01(inputs.encodingSalience)
|
|
102
44
|
: (DEFAULT_TYPE_ENCODING_WEIGHTS[inputs.type] ?? DEFAULT_ENCODING_SALIENCE);
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
// from `utilityScore` clipped to [0, WARM_START_CAP], matching the seed
|
|
111
|
-
// value that `updateAssetOutcome` writes on first row creation. This ensures
|
|
112
|
-
// `outcomeSalience` is non-zero at launch for assets with utility history
|
|
113
|
-
// (avoiding the starvation problem described in the plan §WS-2 warm start).
|
|
114
|
-
let outcome;
|
|
115
|
-
if (inputs.outcomeSalience !== undefined) {
|
|
116
|
-
// Direct pass-through — caller already normalised via outcomeScoreToSalience.
|
|
117
|
-
outcome = Math.min(1, Math.max(0, inputs.outcomeSalience));
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
// Warm-start fallback: clip utility to [0, WARM_START_CAP] so the
|
|
121
|
-
// outcomeSalience term contributes a modest non-zero baseline.
|
|
122
|
-
outcome = Math.min(WARM_START_CAP, Math.max(0, inputs.utilityScore ?? 0));
|
|
123
|
-
}
|
|
124
|
-
// ── Retrieval salience ─────────────────────────────────────────────────────
|
|
125
|
-
//
|
|
126
|
-
// Formula: log(1 + freq) × recencyDecay
|
|
127
|
-
// log(1+freq): sub-linear frequency term (same as proactive-maintenance prototype).
|
|
128
|
-
// recencyDecay: max(ε, 0.1·0.5^(useAgeDays/180) + 0.5^(useAgeDays/21)) —
|
|
129
|
-
// the fast term halves every 21 days; the 0.1 floor itself halves every
|
|
130
|
-
// 180 days (R4: SHY-style continuous downscaling — an unreviewed-forever
|
|
131
|
-
// asset keeps drifting down instead of parking at the floor). The ε=0.01
|
|
132
|
-
// epsilon keeps the frequency term ordinal for unknown-last-use assets.
|
|
133
|
-
// lastUseMs=0/undefined → useAgeDays=9999 → recencyDecay=ε.
|
|
134
|
-
//
|
|
135
|
-
// The recency term is MANDATORY (plan requirement §WS-1 step 2). Without it
|
|
136
|
-
// retrievalSalience degenerates to a non-decaying frequency count. This
|
|
137
|
-
// always-applied decay replaces the deleted homeostatic demotion pass.
|
|
45
|
+
// Without an outcome row, utility (capped) seeds the outcome term — the same
|
|
46
|
+
// seed the first outcome row gets — so it is not zero at launch.
|
|
47
|
+
const outcome = inputs.outcomeSalience !== undefined
|
|
48
|
+
? clamp01(inputs.outcomeSalience)
|
|
49
|
+
: Math.min(WARM_START_CAP, Math.max(0, inputs.utilityScore ?? 0));
|
|
50
|
+
// log(1 + freq) × recency, where recency halves every 21 days above a floor
|
|
51
|
+
// that itself halves every 180 days; soft-capped to [0, 1).
|
|
138
52
|
const lastUseMs = inputs.lastUseMs ?? 0;
|
|
139
53
|
const useAgeDays = lastUseMs > 0 ? (now - lastUseMs) / DAY_MS : 9999;
|
|
140
54
|
const recencyDecay = Math.max(RECENCY_EPSILON, 0.1 * 0.5 ** (useAgeDays / RECENCY_FLOOR_HALFLIFE_DAYS) + 0.5 ** (useAgeDays / RECENCY_HALFLIFE_DAYS));
|
|
141
55
|
const rawRetrieval = Math.log(1 + inputs.retrievalFreq) * recencyDecay;
|
|
142
|
-
// ── Size penalty ─────────────────────────────────────────────────────────────
|
|
143
|
-
// 1/log10(size): larger assets are slightly deprioritized (same as proactive prototype).
|
|
144
|
-
const sizeProxy = Math.max(SIZE_FLOOR_BYTES, inputs.sizeBytes ?? 0);
|
|
145
|
-
const sizePenalty = 1 / Math.log10(sizeProxy);
|
|
146
|
-
// ── Projection → rankScore ────────────────────────────────────────────────
|
|
147
|
-
//
|
|
148
|
-
// Raw projection may be > 1 (log retrieval terms can exceed 1 for high freq + fresh use).
|
|
149
|
-
// Normalize by the theoretical maximum of the retrieval component:
|
|
150
|
-
// max retrievalRaw = log(1 + Infinity) × (0.1 + 1.0) = Infinity, so we
|
|
151
|
-
// cap instead — rankScore is clamped to [0,1] after applying the size penalty.
|
|
152
|
-
//
|
|
153
|
-
// Normalization approach: we scale the combined linear sum to [0,1] by clamping,
|
|
154
|
-
// after applying the size penalty. The encoding term is already in [0,1]; the
|
|
155
|
-
// retrieval term is open-ended but bounded in practice by log(1+N)×1.1 where N
|
|
156
|
-
// is the retrieval count. We normalize `retrieval` to [0,1] using a soft cap:
|
|
157
|
-
// retrieval_normalized = rawRetrieval / (rawRetrieval + 1)
|
|
158
|
-
// which asymptotes to 1 and equals 0.5 at rawRetrieval=1. This is the same
|
|
159
|
-
// formula used for MemRL utility updates.
|
|
160
56
|
const retrieval = rawRetrieval / (rawRetrieval + 1);
|
|
161
|
-
//
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
//
|
|
168
|
-
// When `outcomeWeightEnabled` is explicitly false (operator opt-out via
|
|
169
|
-
// `improve.salience.outcomeWeightEnabled: false`): fall back to the WS-1
|
|
170
|
-
// parity weights (w_e=0.30, w_r=0.70, w_o=0). The `outcome` sub-score is
|
|
171
|
-
// still computed and stored for observability in that mode.
|
|
172
|
-
let we;
|
|
173
|
-
let wo;
|
|
174
|
-
let wr;
|
|
175
|
-
if (inputs.outcomeWeightEnabled !== false) {
|
|
176
|
-
// WS-2 active (default): three-way split.
|
|
177
|
-
we = W_ENCODING; // 0.25
|
|
178
|
-
wo = W_OUTCOME; // 0.15
|
|
179
|
-
wr = W_RETRIEVAL; // 0.60
|
|
180
|
-
}
|
|
181
|
-
else {
|
|
182
|
-
// WS-1 parity (opt-out): w_o=0, redistribute to WS-1 proportions.
|
|
183
|
-
// Original WS-1 split was w_e=0.30, w_r=0.70.
|
|
184
|
-
we = W_ENCODING_PARITY;
|
|
185
|
-
wo = W_OUTCOME_PARITY;
|
|
186
|
-
wr = W_RETRIEVAL_PARITY;
|
|
187
|
-
}
|
|
188
|
-
const rawRankScore = (we * encoding + wo * outcome + wr * retrieval) * sizePenalty;
|
|
189
|
-
const rankScore = Math.min(1, Math.max(0, rawRankScore));
|
|
57
|
+
// Larger assets rank slightly lower.
|
|
58
|
+
const sizePenalty = 1 / Math.log10(Math.max(SIZE_FLOOR_BYTES, inputs.sizeBytes ?? 0));
|
|
59
|
+
const [we, wo, wr] = inputs.outcomeWeightEnabled !== false
|
|
60
|
+
? [W_ENCODING, W_OUTCOME, W_RETRIEVAL]
|
|
61
|
+
: [W_ENCODING_PARITY, W_OUTCOME_PARITY, W_RETRIEVAL_PARITY];
|
|
62
|
+
const rankScore = clamp01((we * encoding + wo * outcome + wr * retrieval) * sizePenalty);
|
|
190
63
|
return { encoding, outcome, retrieval, rankScore, encodingSource };
|
|
191
64
|
}
|
|
192
|
-
// ── state.db persistence ─────────────────────────────────────────────────────
|
|
193
|
-
//
|
|
194
|
-
// The three sub-scores live in state.db::asset_salience. Raw SQL now lives in
|
|
195
|
-
// storage/repositories/salience-repository.ts (#672 part 2) — extracted
|
|
196
|
-
// verbatim, only relocated behind the repository boundary. Re-exported here so
|
|
197
|
-
// existing importers of this module resolve unchanged. Migrations live in
|
|
198
|
-
// state-db.ts (migration 009).
|
|
199
65
|
export { getAllRankScores, getAssetSalience, getConsecutiveNoOps, recordNoOp, resetConsecutiveNoOps, upsertAssetSalience, } from "../../storage/repositories/salience-repository.js";
|
|
200
|
-
/**
|
|
201
|
-
* Does this row carry a genuine content-derived `encoding_salience` (#644)?
|
|
202
|
-
*
|
|
203
|
-
* Unknown provenance is not treated as content-derived.
|
|
204
|
-
*/
|
|
66
|
+
/** Whether a stored row's encoding salience is content-derived (unknown provenance is not). */
|
|
205
67
|
export function isContentEncodingRow(row) {
|
|
206
68
|
return row.encoding_source === "content";
|
|
207
69
|
}
|
|
208
|
-
// ── Consolidation-selection dampener constants ────────────────────────────────
|
|
209
|
-
//
|
|
210
|
-
// Assets with consecutive_no_ops >= THRESHOLD are deprioritised in the
|
|
211
|
-
// SELECTION ORDER only. The persisted rank_score is intentionally left
|
|
212
|
-
// unchanged so stable assets remain fully retrievable by other callers.
|
|
213
|
-
//
|
|
214
|
-
// Tuning guidance:
|
|
215
|
-
// THRESHOLD — how many consecutive no-op runs before dampening kicks in.
|
|
216
|
-
// 3 means "skipped three times in a row", which signals the
|
|
217
|
-
// LLM consistently has nothing to say about this asset.
|
|
218
|
-
// FACTOR — multiplicative penalty on the effective selection score.
|
|
219
|
-
// 0.5 halves the apparent score so a dampened asset sorts
|
|
220
|
-
// after any peer with >= half its rankScore.
|
|
221
|
-
export const SALIENCE_NO_OP_DAMPEN_THRESHOLD = 3;
|
|
222
|
-
export const SALIENCE_NO_OP_DAMPEN_FACTOR = 0.5;
|
|
223
70
|
/**
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
* Compares the provided `newRanks` (Map<ref, position (1-indexed)>) against
|
|
227
|
-
* the provided `oldRanks` and flags refs that were in the old top-200 but
|
|
228
|
-
* are now below position 500 as "forgetting candidates".
|
|
229
|
-
*
|
|
230
|
-
* Caller is responsible for computing old/new rank positions before and after
|
|
231
|
-
* the WS-1 formula cutover. Called once at cutover, not every run.
|
|
232
|
-
*
|
|
233
|
-
* @param oldRanks - Map<ref, 1-indexed rank position> under the OLD formula.
|
|
234
|
-
* @param newRanks - Map<ref, 1-indexed rank position> under the NEW formula.
|
|
235
|
-
* @param oldTopN - Assets in old top-N to guard (default: 200).
|
|
236
|
-
* @param forgettingThreshold - New rank position below which a fall is flagged (default: 500).
|
|
71
|
+
* Consolidation selection only: an asset whose last three runs were no-ops
|
|
72
|
+
* sorts at half its score. The stored rank is untouched.
|
|
237
73
|
*/
|
|
74
|
+
export const SALIENCE_NO_OP_DAMPEN_THRESHOLD = 3;
|
|
75
|
+
export const SALIENCE_NO_OP_DAMPEN_FACTOR = 0.5;
|
|
76
|
+
/** Forgetting safety: compare 1-indexed rank positions between two rankings. */
|
|
238
77
|
export function buildRankChangeReport(oldRanks, newRanks, oldTopN = 200, forgettingThreshold = 500) {
|
|
239
78
|
const allChanges = [];
|
|
240
|
-
const forgettingCandidates = [];
|
|
241
79
|
for (const [ref, oldRank] of oldRanks) {
|
|
242
80
|
const newRank = newRanks.get(ref);
|
|
243
|
-
if (newRank
|
|
244
|
-
|
|
245
|
-
const rankDelta = newRank - oldRank; // positive = fell in rank
|
|
246
|
-
allChanges.push({ ref, oldRank, newRank, rankDelta });
|
|
247
|
-
if (oldRank <= oldTopN && newRank > forgettingThreshold) {
|
|
248
|
-
forgettingCandidates.push({ ref, oldRank, newRank, rankDelta });
|
|
249
|
-
}
|
|
81
|
+
if (newRank !== undefined)
|
|
82
|
+
allChanges.push({ ref, oldRank, newRank, rankDelta: newRank - oldRank });
|
|
250
83
|
}
|
|
251
|
-
|
|
252
|
-
|
|
84
|
+
const forgettingCandidates = allChanges
|
|
85
|
+
.filter((c) => c.oldRank <= oldTopN && c.newRank > forgettingThreshold)
|
|
86
|
+
.sort((a, b) => b.rankDelta - a.rankDelta);
|
|
253
87
|
return { forgettingCandidates, allChanges };
|
|
254
88
|
}
|
|
255
|
-
|
|
256
|
-
//
|
|
257
|
-
// Wraps the index DB query to retrieve the last-retrieval timestamp per ref,
|
|
258
|
-
// so callers do not need to import the raw db helpers directly. Returns a Map
|
|
259
|
-
// keyed by the same ref strings passed in.
|
|
260
|
-
//
|
|
261
|
-
// Source: `utility_scores.last_used_at` (ISO-8601 string) joined to entries
|
|
262
|
-
// via entry_id. WS-2 may later supersede this with `asset_outcome.last_retrieved_at`.
|
|
263
|
-
/**
|
|
264
|
-
* Build a Map<ref, lastUseMs> from the index database's utility_scores table.
|
|
265
|
-
*
|
|
266
|
-
* Returns only refs that have a non-null `last_used_at`. Refs absent from the
|
|
267
|
-
* map should be treated as never retrieved (lastUseMs = 0).
|
|
268
|
-
*
|
|
269
|
-
* @param indexDb - An open read-capable index database connection.
|
|
270
|
-
* @param candidates - Planned refs carrying their canonical durable item_ref.
|
|
271
|
-
*/
|
|
89
|
+
/** `ref → last retrieval (ms)` from the index's utility scores; absent means never retrieved. */
|
|
272
90
|
export function getLastUseMsByRef(indexDb, candidates) {
|
|
273
91
|
const result = new Map();
|
|
274
92
|
if (candidates.length === 0)
|
|
275
93
|
return result;
|
|
276
94
|
const refByItemRef = new Map(candidates.flatMap((candidate) => (candidate.itemRef ? [[candidate.itemRef, candidate.ref]] : [])));
|
|
277
|
-
const allEntries = getAllEntries(indexDb);
|
|
278
95
|
const idToRef = new Map();
|
|
279
|
-
for (const indexed of
|
|
96
|
+
for (const indexed of getAllEntries(indexDb)) {
|
|
280
97
|
const ref = refByItemRef.get(indexed.itemRef);
|
|
281
98
|
if (ref)
|
|
282
99
|
idToRef.set(indexed.id, ref);
|
|
283
100
|
}
|
|
284
|
-
|
|
285
|
-
if (ids.length === 0)
|
|
101
|
+
if (idToRef.size === 0)
|
|
286
102
|
return result;
|
|
287
|
-
const
|
|
103
|
+
const scores = getUtilityScoresByIds(indexDb, [...idToRef.keys()]);
|
|
288
104
|
for (const [id, row] of scores) {
|
|
289
105
|
const ref = idToRef.get(id);
|
|
290
|
-
if (!ref)
|
|
291
|
-
continue;
|
|
292
106
|
const lastUsedAt = row.lastUsedAt;
|
|
293
|
-
if (!lastUsedAt)
|
|
107
|
+
if (!ref || !lastUsedAt)
|
|
294
108
|
continue;
|
|
295
109
|
const ms = typeof lastUsedAt === "number" ? lastUsedAt : Date.parse(lastUsedAt);
|
|
296
110
|
if (ms > 0)
|
|
@@ -2,37 +2,18 @@
|
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
/**
|
|
5
|
-
* Session
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* — on any harness — can discover prior work via `akm search` / `akm curate`.
|
|
10
|
-
*
|
|
11
|
-
* Design constraints (see #561):
|
|
12
|
-
* - ADDITIVE + FAIL-OPEN + CONFIG-GATED. Disabled (or no LLM provider) →
|
|
13
|
-
* extract behaves EXACTLY as before. Nothing is written.
|
|
14
|
-
* - The LLM summary call routes through the injectable {@link SessionSummaryGenerator}
|
|
15
|
-
* seam so tests never touch a real provider, and so production wraps the
|
|
16
|
-
* call in the existing `tryLlmFeature` fail-open pattern.
|
|
17
|
-
* - The `log_path` + `access` frontmatter fields are the durable correlation
|
|
18
|
-
* key — they survive index rebuilds (the body is re-derived from disk).
|
|
19
|
-
*
|
|
20
|
-
* The asset is written to `sessions/<harness>/<session-id>.md`; the registered
|
|
21
|
-
* `session` asset type (see `asset-spec.ts`) makes the normal index pass pick it
|
|
22
|
-
* up for FTS + vector search with no special-casing.
|
|
5
|
+
* Session assets (#561): besides its memory proposals, extract writes each
|
|
6
|
+
* session to `sessions/<harness>/<session-id>.md` as a searchable `session`
|
|
7
|
+
* asset. Additive and fail-open — no summary means nothing is written — and
|
|
8
|
+
* `log_path` + `access` in the frontmatter tell any agent how to read the raw log.
|
|
23
9
|
*/
|
|
24
10
|
import fs from "node:fs";
|
|
25
11
|
import path from "node:path";
|
|
26
12
|
import { stashDirFor } from "../../core/asset/asset-placement.js";
|
|
27
13
|
import { assembleAsset } from "../../core/asset/asset-serialize.js";
|
|
28
14
|
import { conceptIdFromTypeName } from "../../core/asset/resolve-ref.js";
|
|
15
|
+
import { parseEmbeddedJsonResponse } from "../../core/parse.js";
|
|
29
16
|
import { recordWrittenPath } from "../../core/write-provenance.js";
|
|
30
|
-
/**
|
|
31
|
-
* JSON Schema for the session-summary LLM call. Strict so providers that
|
|
32
|
-
* support schema enforcement constrain the output upstream; the parser only
|
|
33
|
-
* has to handle the happy path. `additionalProperties: false` drops any
|
|
34
|
-
* hallucinated keys before parsing.
|
|
35
|
-
*/
|
|
36
17
|
export const SESSION_SUMMARY_JSON_SCHEMA = {
|
|
37
18
|
type: "object",
|
|
38
19
|
required: ["summary", "key_topics"],
|
|
@@ -43,11 +24,7 @@ export const SESSION_SUMMARY_JSON_SCHEMA = {
|
|
|
43
24
|
tags: { type: "array", items: { type: "string" } },
|
|
44
25
|
},
|
|
45
26
|
};
|
|
46
|
-
/**
|
|
47
|
-
* Render a compact transcript snippet from session events for the summary
|
|
48
|
-
* prompt. Mirrors the extract transcript format but caps total length so the
|
|
49
|
-
* summary prompt stays bounded regardless of session size.
|
|
50
|
-
*/
|
|
27
|
+
/** The transcript for the summary prompt, capped at `maxChars`. */
|
|
51
28
|
function renderTranscriptForSummary(events, maxChars = 12_000) {
|
|
52
29
|
if (events.length === 0)
|
|
53
30
|
return "(empty — no events)";
|
|
@@ -66,11 +43,6 @@ function renderTranscriptForSummary(events, maxChars = 12_000) {
|
|
|
66
43
|
}
|
|
67
44
|
return lines.join("\n\n") || "(empty — no textual events)";
|
|
68
45
|
}
|
|
69
|
-
/**
|
|
70
|
-
* Build the user prompt for the session-summary LLM call. Pure — no IO. The
|
|
71
|
-
* model is asked for a dense 2–4 sentence summary plus key topics, optimised
|
|
72
|
-
* for semantic search recall.
|
|
73
|
-
*/
|
|
74
46
|
export function buildSessionSummaryPrompt(data) {
|
|
75
47
|
const ref = data.ref;
|
|
76
48
|
const startedAt = isoOrUndefined(ref.startedAt) ?? "unknown";
|
|
@@ -92,33 +64,13 @@ export function buildSessionSummaryPrompt(data) {
|
|
|
92
64
|
'Respond as JSON: {"summary": string, "key_topics": string[], "tags"?: string[]}.',
|
|
93
65
|
].join("\n");
|
|
94
66
|
}
|
|
95
|
-
/**
|
|
96
|
-
* Parse the session-summary LLM response into a {@link SessionSummaryResult}.
|
|
97
|
-
* Defensive: tolerates prose preamble/postamble around the JSON, and returns
|
|
98
|
-
* `undefined` when nothing usable parses (fail-open: no asset is written).
|
|
99
|
-
*/
|
|
67
|
+
/** The summary JSON, tolerating prose around it; `undefined` when nothing usable parses. */
|
|
100
68
|
export function parseSessionSummary(raw) {
|
|
101
69
|
if (!raw || raw.trim().length === 0)
|
|
102
70
|
return undefined;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
parsed = JSON.parse(raw);
|
|
106
|
-
}
|
|
107
|
-
catch {
|
|
108
|
-
const start = raw.indexOf("{");
|
|
109
|
-
const end = raw.lastIndexOf("}");
|
|
110
|
-
if (start === -1 || end <= start)
|
|
111
|
-
return undefined;
|
|
112
|
-
try {
|
|
113
|
-
parsed = JSON.parse(raw.slice(start, end + 1));
|
|
114
|
-
}
|
|
115
|
-
catch {
|
|
116
|
-
return undefined;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
if (!parsed || typeof parsed !== "object")
|
|
71
|
+
const obj = parseEmbeddedJsonResponse(raw);
|
|
72
|
+
if (!obj || typeof obj !== "object" || Array.isArray(obj))
|
|
120
73
|
return undefined;
|
|
121
|
-
const obj = parsed;
|
|
122
74
|
const summary = typeof obj.summary === "string" ? obj.summary.trim() : "";
|
|
123
75
|
if (summary.length === 0)
|
|
124
76
|
return undefined;
|
|
@@ -130,62 +82,40 @@ export function parseSessionSummary(raw) {
|
|
|
130
82
|
: undefined;
|
|
131
83
|
return { summary, keyTopics, ...(tags && tags.length > 0 ? { tags } : {}) };
|
|
132
84
|
}
|
|
133
|
-
/**
|
|
134
|
-
* Decide whether a session is long enough to index. `minDurationMinutes <= 0`
|
|
135
|
-
* disables the gate. When either timestamp is missing we DON'T gate it out —
|
|
136
|
-
* fail-open toward indexing, since a missing timestamp is not evidence of a
|
|
137
|
-
* trivial session.
|
|
138
|
-
*/
|
|
85
|
+
/** Long enough to index (`<= 0` disables; a missing timestamp is no evidence of a trivial session). */
|
|
139
86
|
export function sessionMeetsDurationGate(data, minDurationMinutes) {
|
|
140
87
|
if (!Number.isFinite(minDurationMinutes) || minDurationMinutes <= 0)
|
|
141
88
|
return true;
|
|
142
89
|
const { startedAt, endedAt } = data.ref;
|
|
143
90
|
if (typeof startedAt !== "number" || typeof endedAt !== "number")
|
|
144
91
|
return true;
|
|
145
|
-
|
|
146
|
-
return durationMinutes >= minDurationMinutes;
|
|
92
|
+
return (endedAt - startedAt) / 60_000 >= minDurationMinutes;
|
|
147
93
|
}
|
|
148
|
-
/**
|
|
149
|
-
* Build per-harness `access` instructions for reading the raw session log.
|
|
150
|
-
*
|
|
151
|
-
* Documented convention (#561, checklist item "Document `access` field
|
|
152
|
-
* convention per harness"): the string tells a downstream agent exactly how to
|
|
153
|
-
* read and parse the source at `log_path`. New harnesses fall back to a generic
|
|
154
|
-
* `cat <log_path>` hint for file-backed logs.
|
|
155
|
-
*/
|
|
94
|
+
/** How an agent reads and parses the raw log at `log_path`, per harness (`cat` otherwise). */
|
|
156
95
|
export function buildSessionAccessInstructions(harness, logPath, sessionId) {
|
|
157
|
-
|
|
158
|
-
if (canonical === "claude") {
|
|
96
|
+
if (harness === "claude") {
|
|
159
97
|
return [
|
|
160
98
|
`Read with: cat ${logPath}`,
|
|
161
99
|
`Parse messages: jq -r 'select(.type=="message") | .message.content[]? | select(.type=="text") | .text' ${logPath}`,
|
|
162
100
|
].join("\n");
|
|
163
101
|
}
|
|
164
|
-
if (
|
|
102
|
+
if (harness === "opencode") {
|
|
165
103
|
return [
|
|
166
104
|
`Open the SQLite database at ${JSON.stringify(logPath)} in read-only mode.`,
|
|
167
105
|
"Query: SELECT m.data, p.data FROM message AS m JOIN part AS p ON p.message_id = m.id WHERE m.session_id = ? AND p.session_id = ? ORDER BY m.time_created, p.time_created;",
|
|
168
106
|
`Bind both parameters to ${JSON.stringify(sessionId)}.`,
|
|
169
107
|
].join("\n");
|
|
170
108
|
}
|
|
171
|
-
// Generic fallback — file-backed logs are always readable with cat.
|
|
172
109
|
return `Read with: cat ${logPath}`;
|
|
173
110
|
}
|
|
174
|
-
/** ISO-8601 (UTC) from a ms-epoch, or undefined when absent/non-finite. */
|
|
175
111
|
function isoOrUndefined(ms) {
|
|
176
112
|
return typeof ms === "number" && Number.isFinite(ms) ? new Date(ms).toISOString() : undefined;
|
|
177
113
|
}
|
|
178
114
|
/** Default session-name slug: `<harness>-session-<yyyy-mm-dd>-<shortId>`. */
|
|
179
115
|
export function buildSessionAssetName(harness, sessionId, startedAtMs) {
|
|
180
|
-
|
|
181
|
-
const datePart = isoOrUndefined(startedAtMs)?.slice(0, 10) ?? "unknown-date";
|
|
182
|
-
const shortId = sessionId.slice(0, 8);
|
|
183
|
-
return `${canonical}-session-${datePart}-${shortId}`;
|
|
116
|
+
return `${harness}-session-${isoOrUndefined(startedAtMs)?.slice(0, 10) ?? "unknown-date"}-${sessionId.slice(0, 8)}`;
|
|
184
117
|
}
|
|
185
|
-
/**
|
|
186
|
-
* Assemble the full session asset (frontmatter + `## Summary` / `## Key topics`).
|
|
187
|
-
* Pure — no IO. Returns the serialized markdown string.
|
|
188
|
-
*/
|
|
118
|
+
/** The session asset: frontmatter plus `## Summary` and `## Key topics`. */
|
|
189
119
|
export function buildSessionAssetContent(data, summary) {
|
|
190
120
|
const ref = data.ref;
|
|
191
121
|
const harness = ref.harness;
|
|
@@ -213,8 +143,7 @@ export function buildSessionAssetContent(data, summary) {
|
|
|
213
143
|
.map((t) => `- ${t.trim()}`)
|
|
214
144
|
.join("\n");
|
|
215
145
|
const body = `## Summary\n\n${summary.summary.trim()}\n\n## Key topics\n\n${topics || "- (none extracted)"}\n`;
|
|
216
|
-
//
|
|
217
|
-
// it without re-reading the body — matches how other content types behave.
|
|
146
|
+
// The summary doubles as the description, as for other types.
|
|
218
147
|
const content = assembleAsset({ ...frontmatter, description: summary.summary.trim() }, body);
|
|
219
148
|
return { name, frontmatter, content };
|
|
220
149
|
}
|
|
@@ -223,13 +152,7 @@ export function resolveSessionAssetPath(stashDir, harness, sessionId) {
|
|
|
223
152
|
const dir = stashDirFor("session") ?? "sessions";
|
|
224
153
|
return path.join(stashDir, dir, harness, `${sessionId}.md`);
|
|
225
154
|
}
|
|
226
|
-
/**
|
|
227
|
-
* Generate (via the injected summarizer) and write a session asset to the stash.
|
|
228
|
-
*
|
|
229
|
-
* FAIL-OPEN: when the summarizer returns `undefined` (disabled / no LLM /
|
|
230
|
-
* error), NOTHING is written and `{ written: false }` is returned. Any write
|
|
231
|
-
* error is swallowed by the caller — session indexing must NEVER break extract.
|
|
232
|
-
*/
|
|
155
|
+
/** Summarize and write a session asset; nothing without a summary. The caller swallows write errors. */
|
|
233
156
|
export async function writeSessionAsset(data, stashDir, generate) {
|
|
234
157
|
const summary = await generate(data);
|
|
235
158
|
if (!summary?.summary || summary.summary.trim().length === 0) {
|
|
@@ -241,15 +164,11 @@ export async function writeSessionAsset(data, stashDir, generate) {
|
|
|
241
164
|
const filePath = resolveSessionAssetPath(stashDir, harness, sessionId);
|
|
242
165
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
243
166
|
fs.writeFileSync(filePath, content, "utf8");
|
|
244
|
-
//
|
|
245
|
-
// journal it so the run's auto-sync stages it as one of its own writes.
|
|
167
|
+
// Written outside the proposal queue: journal it so auto-sync commits it (#652).
|
|
246
168
|
recordWrittenPath(filePath);
|
|
247
169
|
return {
|
|
248
170
|
written: true,
|
|
249
171
|
filePath,
|
|
250
|
-
// Canonical 0.9.0 conceptId (`sessions/<harness>/<id>`, D-R3) — the same
|
|
251
|
-
// spelling the xrefs / usage-event readers now expect. Historical
|
|
252
|
-
// `session:<harness>/<id>` rows persist un-migrated and are tolerated.
|
|
253
172
|
ref: conceptIdFromTypeName("session", `${harness}/${sessionId}`),
|
|
254
173
|
logPath: data.ref.filePath,
|
|
255
174
|
};
|