opencode-swarm 7.130.2 → 7.132.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/cli/{config-doctor-489wej17.js → config-doctor-3rssa871.js} +2 -2
- package/dist/cli/{curation-policy-aaz0hap6.js → curation-policy-3914h3g9.js} +2 -2
- package/dist/cli/{curator-s2ynewsr.js → curator-1e77dq6x.js} +13 -13
- package/dist/cli/{curator-llm-factory-mxjcctn8.js → curator-llm-factory-41c17ybs.js} +13 -13
- package/dist/cli/{evidence-summary-service-hf7qq777.js → evidence-summary-service-jfagmvrh.js} +1 -1
- package/dist/cli/{guardrail-explain-s85kv81r.js → guardrail-explain-624brj1y.js} +14 -14
- package/dist/cli/{guardrail-log-2cejc33f.js → guardrail-log-vfka4tcn.js} +3 -3
- package/dist/cli/{hive-promoter-qdgxajht.js → hive-promoter-mw5prfy2.js} +13 -13
- package/dist/cli/{index-kzvwb2se.js → index-03zyn94g.js} +110 -85
- package/dist/cli/{index-xa29259t.js → index-2dtwjx39.js} +1 -1
- package/dist/cli/{index-5xxgeksy.js → index-5x3g1q91.js} +50 -2
- package/dist/cli/{index-zft4hp35.js → index-64z5dvam.js} +1 -1
- package/dist/cli/{index-ts9wh1ba.js → index-6mcx76j9.js} +60 -8
- package/dist/cli/{index-etdgtk3m.js → index-8w3sp149.js} +3502 -1947
- package/dist/cli/{index-6fqj8fcp.js → index-99tq1zd5.js} +2 -2
- package/dist/cli/{index-jvyn76xe.js → index-e630fw43.js} +3 -3
- package/dist/cli/{index-nr5pr54q.js → index-exs5bt14.js} +1 -1
- package/dist/cli/{index-hjar4crm.js → index-fegng5pv.js} +1 -1
- package/dist/cli/{index-jxha73tm.js → index-nsxchdx0.js} +1 -1
- package/dist/cli/{index-3rnkyb60.js → index-ppkp20fp.js} +5 -5
- package/dist/cli/{index-p6qgjkbt.js → index-q0ysv66x.js} +9 -1
- package/dist/cli/{index-jvwkxz8q.js → index-qg1cj1ev.js} +15 -15
- package/dist/cli/{index-s8h2cedv.js → index-sbq3gbjs.js} +1 -1
- package/dist/cli/{index-wy3q73j2.js → index-zah4qws8.js} +18 -14
- package/dist/cli/{index-agte9w1e.js → index-zrh07ykv.js} +2 -2
- package/dist/cli/index.js +13 -13
- package/dist/cli/{knowledge-escalator-1ypz0266.js → knowledge-escalator-5hv2dwhz.js} +3 -3
- package/dist/cli/{knowledge-events-yvcczmja.js → knowledge-events-434663dp.js} +1 -1
- package/dist/cli/{knowledge-store-h8bp9eje.js → knowledge-store-73ykq5j3.js} +3 -1
- package/dist/cli/{knowledge-validator-sz9v1fdq.js → knowledge-validator-tnadtt1h.js} +4 -4
- package/dist/cli/{scan-cursor-k9zyt374.js → scan-cursor-e33n9pej.js} +2 -2
- package/dist/cli/{schema-fkpn2r1s.js → schema-1kwmcayk.js} +5 -1
- package/dist/cli/{skill-generator-a6a9jxfd.js → skill-generator-k111qfdw.js} +5 -5
- package/dist/commands/consolidate.d.ts +14 -0
- package/dist/commands/curate.d.ts +2 -0
- package/dist/commands/promote.d.ts +25 -0
- package/dist/commands/registry.d.ts +2 -2
- package/dist/config/schema.d.ts +101 -0
- package/dist/consensus/contracts.d.ts +479 -0
- package/dist/consensus/corpus.d.ts +261 -0
- package/dist/consensus/miner.d.ts +200 -0
- package/dist/consensus/public-api.d.ts +42 -0
- package/dist/consensus/store.d.ts +119 -0
- package/dist/evidence/immutable-store.d.ts +86 -0
- package/dist/evidence/manager.d.ts +31 -1
- package/dist/hooks/actionability-predicate.d.ts +36 -0
- package/dist/hooks/curator.d.ts +3 -0
- package/dist/hooks/hive-policy.d.ts +11 -0
- package/dist/hooks/hive-promoter.d.ts +27 -1
- package/dist/hooks/knowledge-dedup-sweep.d.ts +145 -0
- package/dist/hooks/knowledge-injector.d.ts +4 -0
- package/dist/hooks/knowledge-store.d.ts +26 -0
- package/dist/hooks/knowledge-types.d.ts +6 -0
- package/dist/hooks/knowledge-validator.d.ts +10 -14
- package/dist/hooks/micro-reflector.d.ts +106 -2
- package/dist/hooks/realtime-learning-nudge.d.ts +19 -0
- package/dist/index.js +464 -456
- package/dist/knowledge/entry-merge.d.ts +112 -0
- package/dist/knowledge/family-migration.d.ts +6 -2
- package/dist/learning/admission.d.ts +154 -0
- package/dist/learning/candidate-queue.d.ts +178 -0
- package/dist/learning/fingerprint.d.ts +56 -0
- package/dist/learning/prm-pattern-support.d.ts +124 -0
- package/dist/learning/provenance.d.ts +86 -0
- package/dist/parallel/index.d.ts +0 -2
- package/dist/plan/parallel-verdict.d.ts +94 -0
- package/dist/prm/index.d.ts +46 -1
- package/dist/services/recommendation-ledger.d.ts +266 -0
- package/dist/services/skill-improver.d.ts +4 -0
- package/dist/services/status-service.d.ts +27 -0
- package/dist/services/trajectory-cluster.d.ts +65 -2
- package/dist/tools/consensus-mine.d.ts +172 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/knowledge-add.d.ts +34 -0
- package/dist/tools/lean-turbo-run-phase.d.ts +7 -0
- package/dist/tools/manifest.d.ts +2 -0
- package/dist/tools/plan-conflict-check.d.ts +61 -0
- package/dist/tools/tool-metadata.d.ts +8 -0
- package/dist/turbo/lean/recovery.d.ts +90 -0
- package/dist/turbo/lean/runner.d.ts +16 -1
- package/dist/worktree/merge.d.ts +5 -0
- package/package.json +1 -1
- package/dist/parallel/dependency-graph.d.ts +0 -34
- package/dist/parallel/meta-indexer.d.ts +0 -32
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared write pipeline for content-addressed, write-once artifacts under
|
|
3
|
+
* `.swarm/` (issue #1821).
|
|
4
|
+
*
|
|
5
|
+
* Several subsystems persist artifacts that must never be silently rewritten:
|
|
6
|
+
* the evaluation store (`.swarm/evolution/{task-sets,runs,decisions,...}`) and
|
|
7
|
+
* the consensus report store (`.swarm/evolution/consensus/`). They all need the
|
|
8
|
+
* identical sequence — create the parent directory, take the evidence lock,
|
|
9
|
+
* re-read whatever is already on disk, treat a byte-identical (or
|
|
10
|
+
* caller-defined equivalent) payload as idempotent, reject a divergent payload,
|
|
11
|
+
* and otherwise commit through the atomic temp-file+rename write.
|
|
12
|
+
*
|
|
13
|
+
* That sequence lives here exactly once. Duplicating it per store is how the
|
|
14
|
+
* two copies drift: one gains a conflict check the other lacks, or one takes
|
|
15
|
+
* the lock under the wrong actor and corrupts lock attribution.
|
|
16
|
+
*
|
|
17
|
+
* Deliberate design points:
|
|
18
|
+
* - `agent` is a **parameter**, not a constant. The lock actor identifies the
|
|
19
|
+
* subsystem doing the write; hard-coding one store's actor would mislabel
|
|
20
|
+
* every other store's lock acquisitions in telemetry and diagnostics.
|
|
21
|
+
* - `serialize` is a **parameter**. The canonical-JSON encoder that the
|
|
22
|
+
* evaluation store uses lives in `src/evaluation/`, and importing it here
|
|
23
|
+
* would tie this module to a sibling subsystem it has no business knowing
|
|
24
|
+
* about. Callers supply their own canonical form.
|
|
25
|
+
* - `conflictError` is a **factory parameter** rather than a hard-coded error
|
|
26
|
+
* class, so each store throws its own conflict type (and keeps `instanceof`
|
|
27
|
+
* identity with its own tests) without this module importing any of them.
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* Read a UTF-8 file, returning `undefined` when it does not exist.
|
|
31
|
+
*
|
|
32
|
+
* A missing immutable artifact is the normal first-write case, not an error, so
|
|
33
|
+
* this collapses `ENOENT` into `undefined` while letting every other filesystem
|
|
34
|
+
* error (EACCES, EISDIR, EIO, ...) propagate. Exported because the same
|
|
35
|
+
* "absent means not-yet-written" semantics apply to the stores' own reads.
|
|
36
|
+
*/
|
|
37
|
+
export declare function readOptionalFile(filePath: string): Promise<string | undefined>;
|
|
38
|
+
/**
|
|
39
|
+
* Why a write to an already-populated path could not proceed.
|
|
40
|
+
*
|
|
41
|
+
* - `corrupt` — a file exists but does not parse/validate as the artifact
|
|
42
|
+
* type, so it cannot be compared against the desired payload.
|
|
43
|
+
* - `divergent` — a valid artifact exists and differs from the desired payload.
|
|
44
|
+
*/
|
|
45
|
+
export type ImmutableArtifactConflict = {
|
|
46
|
+
kind: 'corrupt';
|
|
47
|
+
filePath: string;
|
|
48
|
+
cause: unknown;
|
|
49
|
+
} | {
|
|
50
|
+
kind: 'divergent';
|
|
51
|
+
filePath: string;
|
|
52
|
+
};
|
|
53
|
+
export type WriteImmutableArtifactOptions<T> = {
|
|
54
|
+
/** Project root; the evidence lock is scoped to it. */
|
|
55
|
+
directory: string;
|
|
56
|
+
/** Lock key — the artifact path relative to `<directory>/.swarm/`. */
|
|
57
|
+
relativeLockPath: string;
|
|
58
|
+
/** Absolute path of the artifact file to write. */
|
|
59
|
+
filePath: string;
|
|
60
|
+
/** Lock actor, e.g. the owning store's name. */
|
|
61
|
+
agent: string;
|
|
62
|
+
/** Lock task identifier, for diagnostics. */
|
|
63
|
+
taskId: string;
|
|
64
|
+
/** The artifact to persist. */
|
|
65
|
+
value: T;
|
|
66
|
+
/** Canonical encoder; its output is what lands on disk verbatim. */
|
|
67
|
+
serialize: (value: unknown) => string;
|
|
68
|
+
/** Schema parse/validate for the artifact already on disk. */
|
|
69
|
+
parse: (value: unknown) => T;
|
|
70
|
+
/**
|
|
71
|
+
* Optional equivalence escape hatch for artifacts whose canonical form may
|
|
72
|
+
* legitimately differ (e.g. a decision that carries a decision timestamp).
|
|
73
|
+
*/
|
|
74
|
+
isEquivalent?: (existing: T, desired: T) => boolean;
|
|
75
|
+
/** Builds the store-specific error thrown on a conflict. */
|
|
76
|
+
conflictError: (conflict: ImmutableArtifactConflict) => Error;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Write `value` to `filePath` exactly once.
|
|
80
|
+
*
|
|
81
|
+
* Idempotent: re-writing an equivalent payload returns the artifact already on
|
|
82
|
+
* disk without touching the file. Writing a different payload to the same path
|
|
83
|
+
* throws the caller-supplied conflict error. The whole read-compare-write runs
|
|
84
|
+
* under the evidence lock so concurrent writers cannot interleave.
|
|
85
|
+
*/
|
|
86
|
+
export declare function writeImmutableArtifact<T>(options: WriteImmutableArtifactOptions<T>): Promise<T>;
|
|
@@ -16,6 +16,32 @@ export type LoadEvidenceResult = {
|
|
|
16
16
|
status: 'invalid_schema';
|
|
17
17
|
errors: string[];
|
|
18
18
|
};
|
|
19
|
+
/**
|
|
20
|
+
* Options for {@link loadEvidence}.
|
|
21
|
+
*
|
|
22
|
+
* Additive and backward-compatible: omitting the argument entirely — which every
|
|
23
|
+
* pre-existing caller does — keeps the historical behaviour exactly, including
|
|
24
|
+
* the lazy in-place upgrade of a legacy flat retrospective.
|
|
25
|
+
*/
|
|
26
|
+
export interface LoadEvidenceOptions {
|
|
27
|
+
/**
|
|
28
|
+
* Whether a legacy flat retrospective may be upgraded **in place** on read.
|
|
29
|
+
*
|
|
30
|
+
* `true` (the default, and the historical behaviour) persists the wrapped
|
|
31
|
+
* bundle back to `.swarm/evidence/<taskId>/evidence.json` under the evidence
|
|
32
|
+
* lock, so the repair happens once instead of on every read.
|
|
33
|
+
*
|
|
34
|
+
* `false` makes the call a pure read: the caller still receives the wrapped,
|
|
35
|
+
* validated bundle, but nothing is written, no lock is taken, and no
|
|
36
|
+
* `evidence-loader` actor appears in lock telemetry. Callers that advertise a
|
|
37
|
+
* read-only contract — the consensus corpus (`src/consensus/corpus.ts`) is the
|
|
38
|
+
* first — MUST pass `false`. A mining run that silently rewrote the evidence
|
|
39
|
+
* it was merely reading would falsify that contract and, because
|
|
40
|
+
* `LEGACY_TASK_COMPLEXITY_MAP` remaps values on the way through, would also
|
|
41
|
+
* change the stored data.
|
|
42
|
+
*/
|
|
43
|
+
migrate?: boolean;
|
|
44
|
+
}
|
|
19
45
|
/**
|
|
20
46
|
* All valid evidence types (13 total)
|
|
21
47
|
*/
|
|
@@ -67,8 +93,12 @@ declare function wrapFlatRetrospective(flatEntry: Record<string, unknown>, taskI
|
|
|
67
93
|
/**
|
|
68
94
|
* Load evidence bundle for a task.
|
|
69
95
|
* Returns a LoadEvidenceResult discriminated union.
|
|
96
|
+
*
|
|
97
|
+
* By default this may perform a one-time in-place upgrade of a legacy flat
|
|
98
|
+
* retrospective (see {@link LoadEvidenceOptions.migrate}). Pass
|
|
99
|
+
* `{ migrate: false }` for a guaranteed pure read.
|
|
70
100
|
*/
|
|
71
|
-
export declare function loadEvidence(directory: string, taskId: string): Promise<LoadEvidenceResult>;
|
|
101
|
+
export declare function loadEvidence(directory: string, taskId: string, options?: LoadEvidenceOptions): Promise<LoadEvidenceResult>;
|
|
72
102
|
/**
|
|
73
103
|
* List all task IDs that have evidence bundles.
|
|
74
104
|
* Returns sorted array of valid task IDs.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pure actionability predicate (knowledge Layer 5), extracted as a LEAF
|
|
3
|
+
* module (issue #1821 Workstream A3).
|
|
4
|
+
*
|
|
5
|
+
* Why this file exists separately from `knowledge-validator.ts`: the predicate
|
|
6
|
+
* itself has always been pure, but its host module imports `node:fs/promises`
|
|
7
|
+
* and `proper-lockfile` at the top level. `hive-policy.ts` documents that it
|
|
8
|
+
* "performs NO I/O and holds NO module-level mutable state" (invariant 8), so
|
|
9
|
+
* importing the predicate from the validator would have quietly made that
|
|
10
|
+
* comment false. Extracting the predicate keeps the documented purity TRUE
|
|
11
|
+
* rather than weakening the comment.
|
|
12
|
+
*
|
|
13
|
+
* CONTRACT FOR THIS MODULE: no I/O imports, no module-level mutable state, no
|
|
14
|
+
* plugin-init-path import. It may only import types. `knowledge-validator.ts`
|
|
15
|
+
* re-exports everything here, so every existing consumer is unaffected.
|
|
16
|
+
*/
|
|
17
|
+
import type { KnowledgeEntryBase } from './knowledge-types.js';
|
|
18
|
+
export interface ActionabilityResult {
|
|
19
|
+
actionable: boolean;
|
|
20
|
+
/** Present only when not actionable. */
|
|
21
|
+
reason?: 'missing_predicate' | 'missing_scope' | 'missing_predicate_and_scope';
|
|
22
|
+
}
|
|
23
|
+
/** The exact field subset the predicate reads. */
|
|
24
|
+
export type ActionabilityInput = Pick<KnowledgeEntryBase, 'forbidden_actions' | 'required_actions' | 'verification_checks' | 'verification_predicate' | 'applies_to_tools' | 'applies_to_agents'>;
|
|
25
|
+
/**
|
|
26
|
+
* Layer 5: an entry is actionable only when it carries at least one
|
|
27
|
+
* machine-checkable predicate AND at least one scope tag.
|
|
28
|
+
*
|
|
29
|
+
* predicate := forbidden_actions | required_actions | verification_checks
|
|
30
|
+
* | verification_predicate
|
|
31
|
+
* scope := applies_to_tools | applies_to_agents
|
|
32
|
+
*
|
|
33
|
+
* Plain-prose lessons (no predicate, no scope) are NOT actionable and must be
|
|
34
|
+
* quarantined rather than activated.
|
|
35
|
+
*/
|
|
36
|
+
export declare function validateActionability(entry: ActionabilityInput): ActionabilityResult;
|
package/dist/hooks/curator.d.ts
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
* This dual dispatch means agent lists are incomplete — they capture factory-dispatched
|
|
25
25
|
* curators but omit hook-dispatched ones. This is by design for hook-internal operations.
|
|
26
26
|
*/
|
|
27
|
+
import { checkRecommendations, recordEmittedRecommendations } from '../services/recommendation-ledger.js';
|
|
27
28
|
import { listSkills, parseDraftFrontmatter, retireOrMarkStale, retireSkill } from '../services/skill-generator.js';
|
|
28
29
|
import { getSkillVersion, reviseSkill } from '../services/skill-reviser.js';
|
|
29
30
|
import type { ComplianceObservation, CuratorConfig, CuratorInitResult, CuratorPhaseResult, CuratorSummary, KnowledgeRecommendation, PhaseDigestEntry } from './curator-types.js';
|
|
@@ -61,6 +62,8 @@ export declare const _internals: {
|
|
|
61
62
|
reviseSkill: typeof reviseSkill;
|
|
62
63
|
getSkillVersion: typeof getSkillVersion;
|
|
63
64
|
readLatestPostMortemDigest: (directory: string) => string | null;
|
|
65
|
+
checkRecommendations: typeof checkRecommendations;
|
|
66
|
+
recordEmittedRecommendations: typeof recordEmittedRecommendations;
|
|
64
67
|
};
|
|
65
68
|
export interface RecommendationParseDiagnostic {
|
|
66
69
|
section: 'OBSERVATIONS' | 'RECOMMENDATION_ID';
|
|
@@ -21,8 +21,19 @@
|
|
|
21
21
|
* (it neither credits nor blocks) and current behavior is preserved. Operators
|
|
22
22
|
* raise the thresholds to activate application-evidence gating.
|
|
23
23
|
*
|
|
24
|
+
* Actionability floor (#1821 A3): the `actionability_floor` gate refuses to
|
|
25
|
+
* promote a plain-prose lesson — a promotion candidate must carry at least one
|
|
26
|
+
* machine-checkable predicate AND at least one scope tag. It is ON by default
|
|
27
|
+
* (`knowledge.promotion_require_actionable`, schema default `true`); the
|
|
28
|
+
* hand-written `KnowledgeConfig` interface declares the field OPTIONAL, so this
|
|
29
|
+
* module reads it as `?? true`.
|
|
30
|
+
*
|
|
24
31
|
* This module performs NO I/O and holds NO module-level mutable state
|
|
25
32
|
* (invariant 8). It is NOT imported on the plugin-init path (invariant 1).
|
|
33
|
+
* `validateActionability` is imported from the LEAF module
|
|
34
|
+
* `./actionability-predicate.js` rather than from `./knowledge-validator.js`,
|
|
35
|
+
* which imports `node:fs/promises` + `proper-lockfile` — importing the
|
|
36
|
+
* validator here would have made the no-I/O statement above false.
|
|
26
37
|
*/
|
|
27
38
|
import type { KnowledgeConfig, PromotionEvidenceRecord, SwarmKnowledgeEntry } from './knowledge-types.js';
|
|
28
39
|
/** A single named policy gate and whether it passed. */
|
|
@@ -126,6 +126,7 @@ export declare const _internals: {
|
|
|
126
126
|
promoted_demotion_signal_threshold: number;
|
|
127
127
|
promotion_min_terminal_applications: number;
|
|
128
128
|
promotion_min_distinct_cohorts: number;
|
|
129
|
+
promotion_require_actionable: boolean;
|
|
129
130
|
realtime_learning_nudge: {
|
|
130
131
|
enabled: boolean;
|
|
131
132
|
first_after_tool_calls: number;
|
|
@@ -156,6 +157,26 @@ export interface ManualPromotionOptions {
|
|
|
156
157
|
force?: boolean;
|
|
157
158
|
reason?: string;
|
|
158
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* Actionability fields supplied on the DIRECT-TEXT promote path (#1821 A3).
|
|
162
|
+
*
|
|
163
|
+
* `/swarm promote "<text>"` has no swarm entry to inherit predicates/scope
|
|
164
|
+
* from, so the command passes them explicitly. Without this parameter the
|
|
165
|
+
* default-on `actionability_floor` gate would be permanently unsatisfiable on
|
|
166
|
+
* that path: every direct-text promotion would return "Promotion blocked by
|
|
167
|
+
* policy" and operators would be pushed into `--force`, poisoning the
|
|
168
|
+
* override-audit signal that #1847 exists to keep honest.
|
|
169
|
+
*
|
|
170
|
+
* `promoteFromSwarm` does NOT take these — it inherits the source swarm entry's
|
|
171
|
+
* own actionable-directive fields via `carryActionableFields`.
|
|
172
|
+
*/
|
|
173
|
+
export interface ManualActionabilityFields {
|
|
174
|
+
applies_to_tools?: string[];
|
|
175
|
+
applies_to_agents?: string[];
|
|
176
|
+
required_actions?: string[];
|
|
177
|
+
forbidden_actions?: string[];
|
|
178
|
+
verification_checks?: string[];
|
|
179
|
+
}
|
|
159
180
|
/**
|
|
160
181
|
* Promote a lesson directly to the hive (manual promotion).
|
|
161
182
|
*
|
|
@@ -168,8 +189,13 @@ export interface ManualPromotionOptions {
|
|
|
168
189
|
* On a policy PASS → promotes with actor='manual'.
|
|
169
190
|
*
|
|
170
191
|
* An exact entry id / direct text alone is NEVER authorization to bypass policy.
|
|
192
|
+
*
|
|
193
|
+
* `actionable` (#1821 A3) carries the predicate/scope fields the direct-text
|
|
194
|
+
* path has no swarm entry to inherit from. They land on the synthetic stand-in
|
|
195
|
+
* (so the `actionability_floor` gate can see them) AND on the written hive
|
|
196
|
+
* entry (so a correctly-supplied field is not dropped on write).
|
|
171
197
|
*/
|
|
172
|
-
export declare function promoteToHive(directory: string, lesson: string, category?: string, options?: ManualPromotionOptions, config?: KnowledgeConfig): Promise<string>;
|
|
198
|
+
export declare function promoteToHive(directory: string, lesson: string, category?: string, options?: ManualPromotionOptions, config?: KnowledgeConfig, actionable?: ManualActionabilityFields): Promise<string>;
|
|
173
199
|
/**
|
|
174
200
|
* Promote a lesson from swarm knowledge to hive.
|
|
175
201
|
*
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Active-store near-duplicate dedup sweep (issue #1821 Lane A).
|
|
3
|
+
*
|
|
4
|
+
* The swarm knowledge store accretes near-duplicate lessons: the same insight
|
|
5
|
+
* arrives from a curator pass, a retrospective, and a micro-reflection, each
|
|
6
|
+
* with its own id. The `/swarm link` family migration already merges
|
|
7
|
+
* near-duplicates when two cohort stores are unified, but nothing ever did it
|
|
8
|
+
* for the ACTIVE store of a single worktree. This sweep closes that gap using
|
|
9
|
+
* the SAME merge helpers (`knowledge/entry-merge.ts`), so a lesson merged by a
|
|
10
|
+
* sweep and a lesson merged by a link end up in the same shape.
|
|
11
|
+
*
|
|
12
|
+
* ## Contract
|
|
13
|
+
*
|
|
14
|
+
* - ACTIVE entries only (`candidate` / `established` / `promoted`). Archived and
|
|
15
|
+
* quarantined entries are neither merge targets nor merge sources.
|
|
16
|
+
* - Bucketed by `category` so the pairwise comparison count is bounded by the
|
|
17
|
+
* sum of per-category squares rather than the whole store squared, and hard
|
|
18
|
+
* capped by `learning.dedup_sweep.max_comparisons`.
|
|
19
|
+
* - Deterministic: entries are compared in `id` order and clusters are resolved
|
|
20
|
+
* in `id` order, so two concurrent sweeps converge on the same winner rather
|
|
21
|
+
* than racing to different survivors.
|
|
22
|
+
* - All merges plus all loser archivals commit inside ONE `transactKnowledge`
|
|
23
|
+
* transaction, so a crash cannot leave a loser archived with its evidence
|
|
24
|
+
* never carried to the winner.
|
|
25
|
+
* - IDEMPOTENT. Losers are archived and only active entries are considered, so
|
|
26
|
+
* a second sweep over an unchanged store is a no-op. See the CLUSTERING note
|
|
27
|
+
* below for why that holds transitively, and
|
|
28
|
+
* `tests/unit/hooks/knowledge-dedup-sweep.test.ts` for the assertion — the
|
|
29
|
+
* underlying `mergeStoreEntries` is NOT idempotent (it re-merges and doubles
|
|
30
|
+
* counters on a repeat), so this property is earned here, not inherited.
|
|
31
|
+
*
|
|
32
|
+
* ## CLUSTERING (why a second sweep is a no-op)
|
|
33
|
+
*
|
|
34
|
+
* Near-duplication is not transitive: A~B and B~C does not imply A~C. A greedy
|
|
35
|
+
* "compare against the surviving representative" pass would therefore leave C
|
|
36
|
+
* active next to a winner it is a near-duplicate of, and the NEXT sweep would
|
|
37
|
+
* merge them — the sweep would never reach a fixed point.
|
|
38
|
+
*
|
|
39
|
+
* Instead every in-bucket pair is compared and near-duplicate pairs are unioned
|
|
40
|
+
* into connected components (union-find). Each component elects ONE winner that
|
|
41
|
+
* absorbs every other member. Because the winner's surviving lesson is always
|
|
42
|
+
* one of its own component's member lessons, and no member of one component is
|
|
43
|
+
* a near-duplicate of any member of another (that is exactly what the
|
|
44
|
+
* transitive closure guarantees), the surviving winners are pairwise non-
|
|
45
|
+
* duplicate. The next sweep finds nothing.
|
|
46
|
+
*
|
|
47
|
+
* The only ways a follow-up sweep does more work are the explicit budgets
|
|
48
|
+
* (`max_comparisons`, `max_merges_per_sweep`) — that is bounded progress, not
|
|
49
|
+
* non-convergence.
|
|
50
|
+
*
|
|
51
|
+
* ## Configuration
|
|
52
|
+
*
|
|
53
|
+
* The sweep reads its OWN config via `loadPluginConfigWithMeta(directory)`.
|
|
54
|
+
* `runCuratorPhase`'s 5th parameter is `knowledgeConfig: { directory?: string }`
|
|
55
|
+
* and cannot carry thresholds, and widening that signature would ripple through
|
|
56
|
+
* all three curator entry points. The dynamic `import('../config/index.js')`
|
|
57
|
+
* matches the established hook pattern (`knowledge-curator.ts`,
|
|
58
|
+
* `knowledge-escalator.ts`, `phase-monitor.ts`) and keeps `hooks → config` off
|
|
59
|
+
* the static import graph.
|
|
60
|
+
*/
|
|
61
|
+
import type { KnowledgeEntryBase } from './knowledge-types.js';
|
|
62
|
+
export interface DedupSweepOptions {
|
|
63
|
+
/**
|
|
64
|
+
* Knowledge-store directory override — the curator's
|
|
65
|
+
* `knowledgeConfig.directory`. Defaults to `directory`. Audit surfaces
|
|
66
|
+
* (tombstone events, rewrite history) always use `directory`, matching the
|
|
67
|
+
* curator's own archive-invalidation call.
|
|
68
|
+
*/
|
|
69
|
+
knowledgeDirectory?: string;
|
|
70
|
+
}
|
|
71
|
+
/** One applied merge: `loserId` was absorbed into `winnerId` and archived. */
|
|
72
|
+
export interface DedupSweepMerge {
|
|
73
|
+
winnerId: string;
|
|
74
|
+
loserId: string;
|
|
75
|
+
category: string;
|
|
76
|
+
}
|
|
77
|
+
export interface DedupSweepResult {
|
|
78
|
+
/** False when `learning.dedup_sweep.enabled` is off (nothing was read). */
|
|
79
|
+
enabled: boolean;
|
|
80
|
+
/** Number of ACTIVE entries considered. */
|
|
81
|
+
scanned: number;
|
|
82
|
+
/** Pairwise near-duplicate comparisons actually performed. */
|
|
83
|
+
comparisons: number;
|
|
84
|
+
/** True when `max_comparisons` cut the scan short. */
|
|
85
|
+
comparisonBudgetExhausted: boolean;
|
|
86
|
+
/** True when `max_merges_per_sweep` cut the merge set short. */
|
|
87
|
+
mergeBudgetExhausted: boolean;
|
|
88
|
+
/** Merges that actually committed. */
|
|
89
|
+
merges: DedupSweepMerge[];
|
|
90
|
+
}
|
|
91
|
+
declare function isActive(entry: KnowledgeEntryBase): boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Evidence mass used as the third winner-selection tiebreak. Counts the
|
|
94
|
+
* outcome signals that represent a real interaction with the entry plus the
|
|
95
|
+
* confirmation records.
|
|
96
|
+
*/
|
|
97
|
+
declare function evidenceWeight(entry: KnowledgeEntryBase): number;
|
|
98
|
+
/**
|
|
99
|
+
* Winner-selection order (negative result ⇒ `a` wins):
|
|
100
|
+
* 1. actionable beats non-actionable — an actionable directive is enforceable
|
|
101
|
+
* and a non-actionable near-duplicate is not; losing the actionable one
|
|
102
|
+
* would silently downgrade the store even though `mergeEntryFields` now
|
|
103
|
+
* carries the predicates across.
|
|
104
|
+
* 2. higher confidence
|
|
105
|
+
* 3. more evidence
|
|
106
|
+
* 4. older `created_at` (the original, not the restatement)
|
|
107
|
+
* 5. lexicographic `id` — a total order, so concurrent sweeps converge.
|
|
108
|
+
*/
|
|
109
|
+
declare function compareCandidates(a: KnowledgeEntryBase, b: KnowledgeEntryBase): number;
|
|
110
|
+
interface PlannedMerge {
|
|
111
|
+
winner: KnowledgeEntryBase;
|
|
112
|
+
loser: KnowledgeEntryBase;
|
|
113
|
+
category: string;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Build the merge plan from a read-only snapshot. Pure (no I/O), so it is
|
|
117
|
+
* cheap to reason about and the transaction below only has to re-validate.
|
|
118
|
+
*/
|
|
119
|
+
declare function planMerges(active: KnowledgeEntryBase[], threshold: number, maxComparisons: number, maxMerges: number): {
|
|
120
|
+
plan: PlannedMerge[];
|
|
121
|
+
comparisons: number;
|
|
122
|
+
comparisonBudgetExhausted: boolean;
|
|
123
|
+
mergeBudgetExhausted: boolean;
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Merge active near-duplicate knowledge entries in `directory`'s swarm store.
|
|
127
|
+
*
|
|
128
|
+
* Never throws: every failure path is logged and returns a result describing
|
|
129
|
+
* what did happen. The caller (`runCuratorPhase`) must never be blocked by a
|
|
130
|
+
* background learning loop.
|
|
131
|
+
*/
|
|
132
|
+
export declare function sweepActiveNearDuplicates(directory: string, options?: DedupSweepOptions): Promise<DedupSweepResult>;
|
|
133
|
+
/**
|
|
134
|
+
* Tier-0 pure-function test seam (see `.opencode/skills/writing-tests`). These
|
|
135
|
+
* are deterministic and dependency-free at the I/O boundary, so their tests need
|
|
136
|
+
* no mocks at all — planning, winner selection, and evidence weighting are
|
|
137
|
+
* exercised directly instead of through a temp-directory round trip.
|
|
138
|
+
*/
|
|
139
|
+
export declare const _test_exports: {
|
|
140
|
+
compareCandidates: typeof compareCandidates;
|
|
141
|
+
evidenceWeight: typeof evidenceWeight;
|
|
142
|
+
isActive: typeof isActive;
|
|
143
|
+
planMerges: typeof planMerges;
|
|
144
|
+
};
|
|
145
|
+
export {};
|
|
@@ -63,6 +63,10 @@ export interface InjectForDelegateResult {
|
|
|
63
63
|
entries: RankedEntry[];
|
|
64
64
|
trace_id: string;
|
|
65
65
|
}
|
|
66
|
+
/** Invalidate memoized injections. Called by the admission path after a write. */
|
|
67
|
+
export declare function bumpKnowledgeGeneration(): number;
|
|
68
|
+
/** Current corpus generation. Part of the architect injection cache key. */
|
|
69
|
+
export declare function getKnowledgeGeneration(): number;
|
|
66
70
|
/**
|
|
67
71
|
* Retrieve the subset of active knowledge directives scoped to a delegated
|
|
68
72
|
* subagent's role + expected tools (Change 1, Task 1.2). Emits a single
|
|
@@ -42,6 +42,32 @@ export declare function appendRewriteHistory(directory: string, record: RewriteH
|
|
|
42
42
|
* Read rewrite/merge history records (issue #1848 §3). Used for audit/recovery.
|
|
43
43
|
*/
|
|
44
44
|
export declare function readRewriteHistory(directory: string): Promise<RewriteHistoryRecord[]>;
|
|
45
|
+
/**
|
|
46
|
+
* Normalize a loosely-typed knowledge array field: keep only strings,
|
|
47
|
+
* optionally truncate each item, deduplicate case-insensitively, then cap.
|
|
48
|
+
*
|
|
49
|
+
* The steps run in EXACTLY this order and the order is observable:
|
|
50
|
+
*
|
|
51
|
+
* 1. non-array input → `[]`
|
|
52
|
+
* 2. drop non-string items
|
|
53
|
+
* 3. truncate each item to `itemMaxChars` (when provided)
|
|
54
|
+
* 4. deduplicate on a case-insensitive key, PRESERVING the first
|
|
55
|
+
* occurrence's original casing
|
|
56
|
+
* 5. cap to `opts.cap`, keeping the first N survivors
|
|
57
|
+
*
|
|
58
|
+
* Truncation happens BEFORE deduplication, so two items that differ only past
|
|
59
|
+
* `itemMaxChars` collapse into one. Deduplication happens BEFORE the cap, so a
|
|
60
|
+
* run of duplicates can no longer evict distinct values off the end — that
|
|
61
|
+
* eviction was the defect this helper eradicates (a bare positional cap of 20
|
|
62
|
+
* with no dedup, repeated at six call sites — issue #1821 Lane 0b).
|
|
63
|
+
*
|
|
64
|
+
* `values` is typed `unknown` because every call site receives untrusted or
|
|
65
|
+
* loosely-typed input (LLM output, tool arguments, on-disk records).
|
|
66
|
+
*/
|
|
67
|
+
export declare function dedupeCapped(values: unknown, opts: {
|
|
68
|
+
cap: number;
|
|
69
|
+
itemMaxChars?: number;
|
|
70
|
+
}): string[];
|
|
45
71
|
export declare function appendKnowledge<T>(filePath: string, entry: T): Promise<void>;
|
|
46
72
|
export declare function rewriteKnowledge<T>(filePath: string, entries: T[]): Promise<void>;
|
|
47
73
|
export declare function transactFile<T>(filePath: string, read: (filePath: string) => Promise<T>, write: (filePath: string, data: T) => Promise<void>, mutate: (data: T) => T | null): Promise<boolean>;
|
|
@@ -490,6 +490,12 @@ export interface KnowledgeConfig {
|
|
|
490
490
|
* `validated_terminal_applications` gate. Default 0 (conservative; see
|
|
491
491
|
* `promotion_min_terminal_applications`). */
|
|
492
492
|
promotion_min_distinct_cohorts: number;
|
|
493
|
+
/** #1821: require a promotion candidate to carry an actionable directive
|
|
494
|
+
* before it may be promoted. Default true (see `KnowledgeConfigSchema`).
|
|
495
|
+
* Declared OPTIONAL here on purpose: this hand-written interface is what the
|
|
496
|
+
* hooks layer imports, and hundreds of full config literals in tests would
|
|
497
|
+
* otherwise need updating. The Zod inference keeps it required. */
|
|
498
|
+
promotion_require_actionable?: boolean;
|
|
493
499
|
/** Change 5: retrieval-upgrade tuning (MMR / cold-start / synonyms). */
|
|
494
500
|
retrieval?: {
|
|
495
501
|
mmr_lambda?: number;
|
|
@@ -79,23 +79,19 @@ export declare function validateSkillCandidatePath(p: unknown): boolean;
|
|
|
79
79
|
/** Validate the optional ActionableDirectiveFields block on a knowledge entry. */
|
|
80
80
|
export declare function validateActionableFields(fields: ActionableDirectiveFields | undefined): ActionableValidationResult;
|
|
81
81
|
export type { ActionableDirectiveFields, DirectivePriority };
|
|
82
|
-
export interface ActionabilityResult {
|
|
83
|
-
actionable: boolean;
|
|
84
|
-
/** Present only when not actionable. */
|
|
85
|
-
reason?: 'missing_predicate' | 'missing_scope' | 'missing_predicate_and_scope';
|
|
86
|
-
}
|
|
87
82
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* predicate := forbidden_actions | required_actions | verification_checks
|
|
92
|
-
* | verification_predicate
|
|
93
|
-
* scope := applies_to_tools | applies_to_agents
|
|
83
|
+
* The predicate itself now lives in the leaf module
|
|
84
|
+
* `./actionability-predicate.js` (issue #1821 Workstream A3). It is re-exported
|
|
85
|
+
* here so every historical consumer of `knowledge-validator` is unaffected.
|
|
94
86
|
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
87
|
+
* Why it moved: this module imports `node:fs/promises` and `proper-lockfile` at
|
|
88
|
+
* the top level, while `hive-policy.ts` documents that it performs NO I/O
|
|
89
|
+
* (invariant 8). The promotion `actionability_floor` gate needs the predicate,
|
|
90
|
+
* so the predicate moved to a leaf with no I/O imports instead of the purity
|
|
91
|
+
* comment being weakened.
|
|
97
92
|
*/
|
|
98
|
-
export
|
|
93
|
+
export type { ActionabilityInput, ActionabilityResult, } from './actionability-predicate.js';
|
|
94
|
+
export { validateActionability } from './actionability-predicate.js';
|
|
99
95
|
/** Returns the knowledge-unactionable.jsonl path for the given directory (link-aware). */
|
|
100
96
|
export declare function resolveUnactionablePath(directory: string): string;
|
|
101
97
|
/** One quarantined-unactionable record. */
|
|
@@ -24,11 +24,18 @@ export type MicroOutcome = 'success' | 'failure_test' | 'failure_lint' | 'failur
|
|
|
24
24
|
export declare const MICRO_PROMPT_INPUT_CAP = 1800;
|
|
25
25
|
/** One v3-schema candidate insight written to the queue. */
|
|
26
26
|
export interface InsightCandidate extends ActionableDirectiveFields {
|
|
27
|
+
/**
|
|
28
|
+
* Deterministic candidate identity (`ic_<16hex>`), issue #1821 Workstream B
|
|
29
|
+
* D1. OPTIONAL on purpose: legacy id-less lines already on disk recompute
|
|
30
|
+
* the identical value through `resolveInsightCandidateId`, so no migration
|
|
31
|
+
* of `.swarm/insight-candidates.jsonl` is required.
|
|
32
|
+
*/
|
|
33
|
+
id?: string;
|
|
27
34
|
lesson: string;
|
|
28
35
|
category: string;
|
|
29
36
|
tags: string[];
|
|
30
37
|
source: {
|
|
31
|
-
kind: 'micro_reflection';
|
|
38
|
+
kind: 'micro_reflection' | 'prm_pattern';
|
|
32
39
|
task_id?: string;
|
|
33
40
|
agent: string;
|
|
34
41
|
outcome: MicroOutcome;
|
|
@@ -36,6 +43,75 @@ export interface InsightCandidate extends ActionableDirectiveFields {
|
|
|
36
43
|
};
|
|
37
44
|
created_at: string;
|
|
38
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Compute the deterministic identity of an insight candidate.
|
|
48
|
+
*
|
|
49
|
+
* Hashes exactly `{lesson, taskId, createdAt}`. `canonicalJson` DROPS
|
|
50
|
+
* `undefined` keys, so a candidate with no `source.task_id` hashes
|
|
51
|
+
* consistently whether the key is absent or explicitly undefined — which is
|
|
52
|
+
* what makes the value reproducible from a legacy durable line that never
|
|
53
|
+
* carried an `id`.
|
|
54
|
+
*/
|
|
55
|
+
export declare function computeInsightCandidateId(cand: {
|
|
56
|
+
lesson: string;
|
|
57
|
+
source?: {
|
|
58
|
+
task_id?: string;
|
|
59
|
+
};
|
|
60
|
+
created_at: string;
|
|
61
|
+
}): string;
|
|
62
|
+
/**
|
|
63
|
+
* Resolve a candidate's identity by ALWAYS deriving it from content.
|
|
64
|
+
*
|
|
65
|
+
* The stamped `id` is deliberately NOT trusted. `.swarm/insight-candidates.jsonl`
|
|
66
|
+
* is read back through a bare `JSON.parse(...) as InsightCandidate`, and the
|
|
67
|
+
* curator explicitly treats that file as tamper-suspect (it re-applies both
|
|
68
|
+
* write-time gates on read). A trusted `id` would be an unvalidated field on
|
|
69
|
+
* that same untrusted line: setting it to an already-admitted candidate's id
|
|
70
|
+
* would make the fold-in silently DROP a distinct new lesson, and rotating it
|
|
71
|
+
* every run would let the same lesson be re-confirmed every phase, inflating
|
|
72
|
+
* confidence toward hive auto-promotion.
|
|
73
|
+
*
|
|
74
|
+
* Recomputation is a single sha256 over a three-field object, bounded by the
|
|
75
|
+
* meso batch limit — far cheaper than the failure it prevents. The stamped `id`
|
|
76
|
+
* survives as a diagnostic and round-trip aid only.
|
|
77
|
+
*/
|
|
78
|
+
export declare function resolveInsightCandidateId(cand: InsightCandidate): string;
|
|
79
|
+
/**
|
|
80
|
+
* The marker recorded in an admitted entry's EXISTING `source_knowledge_ids`
|
|
81
|
+
* field so the phase-boundary fold-in can recognise a candidate real-time
|
|
82
|
+
* admission already handled. 27 chars, matching the 64-char per-id ceiling
|
|
83
|
+
* `validateActionableFields` applies wherever that field IS validated (the
|
|
84
|
+
* admission/fold-in paths pass an explicit field subset that omits it, so the
|
|
85
|
+
* length is a construction guarantee here rather than a checked one).
|
|
86
|
+
*/
|
|
87
|
+
export declare function insightAdmissionMarker(candidateId: string): string;
|
|
88
|
+
/**
|
|
89
|
+
* Cap on `source_knowledge_ids` for an entry stamped by real-time admission.
|
|
90
|
+
*
|
|
91
|
+
* The knowledge store deliberately EXCLUDES `source_knowledge_ids` from its
|
|
92
|
+
* 20-item write normalization (see `WRITE_NORMALIZED_ARRAY_FIELDS` in
|
|
93
|
+
* `knowledge-store.ts`) because `skill-invalidator.ts` walks the full list.
|
|
94
|
+
* That exclusion means nothing else bounds the array, so an entry reinforced by
|
|
95
|
+
* many distinct candidates would grow it forever (AGENTS.md invariant 8). 50
|
|
96
|
+
* matches the cap the curator's own producer already applies.
|
|
97
|
+
*/
|
|
98
|
+
export declare const MAX_SOURCE_KNOWLEDGE_IDS = 50;
|
|
99
|
+
/**
|
|
100
|
+
* Union an insight marker into an entry's `source_knowledge_ids`, bounded.
|
|
101
|
+
*
|
|
102
|
+
* Eviction drops the OLDEST `insight:` markers only. Non-insight ids (`task:`
|
|
103
|
+
* ids and real knowledge ids) are NEVER dropped: `skill-invalidator.ts` walks
|
|
104
|
+
* them to retire skills whose source entry was archived, so silently losing one
|
|
105
|
+
* would leave a stale skill live. If the array is over cap purely because of
|
|
106
|
+
* non-insight ids it is left over cap — correctness beats the bound here, and
|
|
107
|
+
* that case is already bounded by the producers of those ids.
|
|
108
|
+
*
|
|
109
|
+
* Losing a very old insight marker can at worst allow ONE extra confirmation
|
|
110
|
+
* for a long-superseded candidate; unbounded growth has no such ceiling.
|
|
111
|
+
*/
|
|
112
|
+
export declare function unionInsightMarker(existing: string[] | undefined, marker: string): string[];
|
|
113
|
+
/** Extract the insight marker carried by an entry, if any. */
|
|
114
|
+
export declare function findInsightAdmissionMarker(sourceKnowledgeIds: string[] | undefined): string | undefined;
|
|
39
115
|
/** Returns `.swarm/insight-candidates.jsonl` for a project directory. */
|
|
40
116
|
export declare function resolveInsightCandidatesPath(directory: string): string;
|
|
41
117
|
/**
|
|
@@ -47,6 +123,15 @@ export declare function classifyOutcome(transcript: string, trajectory: Trajecto
|
|
|
47
123
|
/** Read a task's trajectory slice. Fail-open: [] when absent/corrupt. */
|
|
48
124
|
export declare function readTaskTrajectory(directory: string, taskId: string): Promise<TrajectoryEntry[]>;
|
|
49
125
|
export declare const INSIGHT_CANDIDATES_MAX_ENTRIES = 500;
|
|
126
|
+
/** Append validated candidates to the insight queue (best-effort, fail-open).
|
|
127
|
+
* Uses transactFile for consistency with consumeInsightCandidates and enforces
|
|
128
|
+
* a FIFO cap to prevent unbounded growth between phase completions.
|
|
129
|
+
*
|
|
130
|
+
* EXPORTED (#1821) so the PRM pattern producer can write the SAME durable
|
|
131
|
+
* crash backstop the micro-reflector writes. Any producer that only enqueues
|
|
132
|
+
* in memory loses its candidate on process death, on queue overflow, and on a
|
|
133
|
+
* drain failure — with no phase-boundary fold-in to recover it. */
|
|
134
|
+
export declare function appendInsightCandidates(directory: string, candidates: InsightCandidate[]): Promise<void>;
|
|
50
135
|
/** Build the bounded micro-reflection prompt (≤ MICRO_PROMPT_INPUT_CAP chars). */
|
|
51
136
|
export declare function buildMicroPrompt(params: {
|
|
52
137
|
agent: string;
|
|
@@ -75,6 +160,13 @@ export declare function runMicroReflection(params: {
|
|
|
75
160
|
trajectory: TrajectoryEntry[];
|
|
76
161
|
llmDelegate?: CuratorLLMDelegate;
|
|
77
162
|
quota?: EnrichmentQuotaOptions;
|
|
163
|
+
/** Session that produced the reflection — required for real-time admission. */
|
|
164
|
+
sessionID?: string;
|
|
165
|
+
/** `learning.realtime_admission` knobs the enqueue side needs. */
|
|
166
|
+
admission?: {
|
|
167
|
+
enabled?: boolean;
|
|
168
|
+
maxQueueSize?: number;
|
|
169
|
+
};
|
|
78
170
|
}): Promise<MicroReflectionResult>;
|
|
79
171
|
export interface MicroReflectorInput {
|
|
80
172
|
tool: unknown;
|
|
@@ -87,10 +179,22 @@ export interface MicroReflectorInput {
|
|
|
87
179
|
export interface MicroReflectorOutput {
|
|
88
180
|
output?: unknown;
|
|
89
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* True for the delegation tool under either casing.
|
|
184
|
+
*
|
|
185
|
+
* EXPORTED (issue #1821 Workstream B) so the real-time admission drain adapter
|
|
186
|
+
* can self-gate on exactly the same predicate this hook uses. Six private
|
|
187
|
+
* copies of this two-line check already exist across the hook layer; the drain
|
|
188
|
+
* reuses this one rather than adding a seventh.
|
|
189
|
+
*/
|
|
190
|
+
export declare function isTaskTool(tool: unknown): boolean;
|
|
90
191
|
/**
|
|
91
192
|
* `tool.execute.after` adapter for the `Task` tool. Resolves the delegate, the
|
|
92
193
|
* transcript, the task id, and the trajectory slice, then runs micro-reflection.
|
|
93
194
|
* The LLM delegate is provided by the caller (so tests can inject one); when
|
|
94
195
|
* absent, classification still runs but no LLM call is made.
|
|
95
196
|
*/
|
|
96
|
-
export declare function microReflectorAfter(directory: string, input: MicroReflectorInput, output: MicroReflectorOutput, llmDelegate?: CuratorLLMDelegate, quota?: EnrichmentQuotaOptions
|
|
197
|
+
export declare function microReflectorAfter(directory: string, input: MicroReflectorInput, output: MicroReflectorOutput, llmDelegate?: CuratorLLMDelegate, quota?: EnrichmentQuotaOptions, admission?: {
|
|
198
|
+
enabled?: boolean;
|
|
199
|
+
maxQueueSize?: number;
|
|
200
|
+
}): Promise<void>;
|