@hasna/skills 0.10.83 → 0.10.89
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 +166 -15
- package/bin/index.js +5829 -2913
- package/bin/mcp.js +1766 -835
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/cli/commands/agent-integration.d.ts +55 -0
- package/dist/cli/commands/context.d.ts +8 -0
- package/dist/cli/commands/runtime-launcher.d.ts +4 -9
- package/dist/index.js +3039 -1531
- package/dist/lib/agent-adapters.d.ts +5 -2
- package/dist/lib/agent-discovery.d.ts +22 -4
- package/dist/lib/agent-hermes.d.ts +7 -1
- package/dist/lib/agent-integration.d.ts +31 -2
- package/dist/lib/agent-opencode.d.ts +4 -0
- package/dist/lib/claude-hook-disable.d.ts +32 -0
- package/dist/lib/claude-settings-witness.d.ts +28 -0
- package/dist/lib/codex-settings-witness.d.ts +20 -0
- package/dist/lib/guarded-agent-config.d.ts +3 -0
- package/dist/lib/hook-diagnostics.d.ts +3 -1
- package/dist/lib/narrowed-witness-ledger.d.ts +24 -0
- package/dist/lib/native-discovery-roots.d.ts +8 -0
- package/dist/lib/native-drift-receipt.d.ts +55 -0
- package/dist/lib/opencode-hook-protocol.d.ts +5 -0
- package/dist/lib/profile-client.d.ts +1 -0
- package/dist/lib/selection-cache.d.ts +47 -2
- package/dist/lib/selection-resolver.d.ts +13 -1
- package/dist/lib/session-background-renewal.d.ts +118 -0
- package/dist/lib/session-reconciliation.d.ts +82 -2
- package/dist/lib/skill-context.d.ts +50 -1
- package/dist/lib/skill-relevance.d.ts +95 -0
- package/dist/runtime-prerequisites.js +570 -165
- package/dist/sdk/index.js +2629 -1396
- package/docs/plugin-admission.md +132 -3
- package/package.json +1 -1
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { SkillSelectionError, type SelectionCacheOptions } from "./selection-cache.js";
|
|
2
|
+
import { type SessionReconciliationOptions } from "./session-reconciliation.js";
|
|
3
|
+
/** The longest an expired-pin prompt waits for its background renewal. */
|
|
4
|
+
export declare const BACKGROUND_RENEWAL_WAIT_MS = 3000;
|
|
5
|
+
/** A lock older than this belongs to no live worker (the worker's own budget plus margin). */
|
|
6
|
+
export declare const BACKGROUND_RENEWAL_LOCK_TTL_MS: number;
|
|
7
|
+
/** A failed attempt for the same pin is reported again, not retried, within these windows. */
|
|
8
|
+
export declare const EXPIRED_RENEWAL_RETRY_MS = 30000;
|
|
9
|
+
export declare const AHEAD_RENEWAL_RETRY_MS: number;
|
|
10
|
+
export type BackgroundRenewalMode = "expired" | "ahead";
|
|
11
|
+
export type BackgroundRenewalStatus = "running" | "renewed" | "migrated" | "unchanged" | "failed";
|
|
12
|
+
export interface BackgroundRenewalState {
|
|
13
|
+
schemaVersion: 1;
|
|
14
|
+
kind: "session-pin-background-renewal";
|
|
15
|
+
sessionId: string;
|
|
16
|
+
profileId: string;
|
|
17
|
+
operationId: string;
|
|
18
|
+
/** verifiedAt of the exact pin this attempt renews. */
|
|
19
|
+
pinVerifiedAt: string;
|
|
20
|
+
mode: BackgroundRenewalMode;
|
|
21
|
+
status: BackgroundRenewalStatus;
|
|
22
|
+
startedAt: string;
|
|
23
|
+
finishedAt?: string;
|
|
24
|
+
workerPid?: number;
|
|
25
|
+
code?: string;
|
|
26
|
+
migrationTimeout?: true;
|
|
27
|
+
/** No worker could be started; such a record never delays the next attempt. */
|
|
28
|
+
notStarted?: true;
|
|
29
|
+
/**
|
|
30
|
+
* The exact request this attempt answers: the receipt bytes, mode,
|
|
31
|
+
* migration setting and prompt directory. A recorded failure is reused only
|
|
32
|
+
* for the same request; any change (a rewritten receipt, migration turned
|
|
33
|
+
* on, another directory) asks again at once.
|
|
34
|
+
*/
|
|
35
|
+
requestDigest?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface BackgroundRenewalRequest {
|
|
38
|
+
sessionId: string;
|
|
39
|
+
profileId: string;
|
|
40
|
+
pinVerifiedAt: string;
|
|
41
|
+
mode: BackgroundRenewalMode;
|
|
42
|
+
/** SHA-256 of the exact receipt bytes this request read. */
|
|
43
|
+
receiptSha256: string;
|
|
44
|
+
/** Expired pins only: the managed hook's automatic migration of a refused renewal. */
|
|
45
|
+
migrate?: boolean;
|
|
46
|
+
/** The prompt's directory, which migration requires (project locks keep the refusal). */
|
|
47
|
+
projectDir?: string;
|
|
48
|
+
}
|
|
49
|
+
export interface BackgroundRenewalOptions extends SelectionCacheOptions {
|
|
50
|
+
/**
|
|
51
|
+
* Tests replace the detached worker; production spawns `skills sessions
|
|
52
|
+
* renew-background`. `notStarted` is called if the worker fails to start
|
|
53
|
+
* after this returns (an asynchronous spawn error such as EAGAIN).
|
|
54
|
+
*/
|
|
55
|
+
spawnWorker?: (args: string[], notStarted: () => void) => void;
|
|
56
|
+
}
|
|
57
|
+
/** The identity of one renewal request; equal digests ask the authority the same question. */
|
|
58
|
+
export declare function backgroundRenewalRequestDigest(request: BackgroundRenewalRequest): string;
|
|
59
|
+
/**
|
|
60
|
+
* The last recorded attempt for this session, or null when absent or not
|
|
61
|
+
* exact. Only validated fields are returned; anything else in the file is
|
|
62
|
+
* dropped, so no unvalidated value reaches a caller's output.
|
|
63
|
+
*/
|
|
64
|
+
export declare function readBackgroundRenewalState(sessionId: string, options?: SelectionCacheOptions): BackgroundRenewalState | null;
|
|
65
|
+
export type BackgroundRenewalStart =
|
|
66
|
+
/** `operationId` names the attempt to wait for, when its lock is readable. */
|
|
67
|
+
{
|
|
68
|
+
status: "started" | "running";
|
|
69
|
+
operationId?: string;
|
|
70
|
+
}
|
|
71
|
+
/** `notStarted`: no worker could be started, so the caller may renew inline instead. */
|
|
72
|
+
| {
|
|
73
|
+
status: "failed";
|
|
74
|
+
state: BackgroundRenewalState;
|
|
75
|
+
notStarted?: true;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Start the session's background renewal unless one is running, or report a
|
|
79
|
+
* recent failure of the same pin without starting another.
|
|
80
|
+
*/
|
|
81
|
+
export declare function startBackgroundRenewal(request: BackgroundRenewalRequest, options?: BackgroundRenewalOptions): BackgroundRenewalStart;
|
|
82
|
+
export type BackgroundRenewalWait = {
|
|
83
|
+
status: "running" | "changed";
|
|
84
|
+
} | {
|
|
85
|
+
status: Exclude<BackgroundRenewalStatus, "running">;
|
|
86
|
+
state: BackgroundRenewalState;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Wait up to waitMs for the attempt renewing this exact pin to finish, or for
|
|
90
|
+
* the session receipt to stop holding that pin. With an operation id, only
|
|
91
|
+
* that attempt's outcome counts.
|
|
92
|
+
*/
|
|
93
|
+
export declare function awaitBackgroundRenewal(sessionId: string, pinVerifiedAt: string, waitMs: number, options?: SelectionCacheOptions, operationId?: string): Promise<BackgroundRenewalWait>;
|
|
94
|
+
export interface BackgroundRenewalRun {
|
|
95
|
+
status: BackgroundRenewalStatus | "superseded";
|
|
96
|
+
code?: string;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The worker: renew (or, for an expired pin with migration on, migrate) one
|
|
100
|
+
* session pin with the background budget, record the outcome, release the lock.
|
|
101
|
+
*/
|
|
102
|
+
export declare function runBackgroundRenewal(sessionId: string, profileId: string, operationId: string, request: {
|
|
103
|
+
migrate?: boolean;
|
|
104
|
+
projectDir?: string;
|
|
105
|
+
}, options?: SessionReconciliationOptions): Promise<BackgroundRenewalRun>;
|
|
106
|
+
/**
|
|
107
|
+
* An expired pin whose background renewal is still running. It keeps the
|
|
108
|
+
* renewal timeout's code (fixed codes cross the hook boundary) and a fixed
|
|
109
|
+
* marker so the hook can say the renewal continues instead of restarting.
|
|
110
|
+
*/
|
|
111
|
+
export declare class SessionRenewalInBackground extends SkillSelectionError {
|
|
112
|
+
readonly renewal: "background";
|
|
113
|
+
constructor();
|
|
114
|
+
}
|
|
115
|
+
/** The recorded failure of a background attempt, as the error the inline renewal would have raised. */
|
|
116
|
+
export declare function backgroundRenewalError(state: BackgroundRenewalState): SkillSelectionError;
|
|
117
|
+
/** The pin's age class, from its verifiedAt; null when it is neither due nor expired. */
|
|
118
|
+
export declare function renewalModeFor(verifiedAt: string, now: number): BackgroundRenewalMode | null;
|
|
@@ -49,8 +49,71 @@ export declare function inspectSkillSession(sessionId: string, options?: Selecti
|
|
|
49
49
|
bundleDigest: string;
|
|
50
50
|
}[];
|
|
51
51
|
};
|
|
52
|
-
/**
|
|
53
|
-
export declare const
|
|
52
|
+
/** The managed user-prompt hook's own deadline, measured from hook start. */
|
|
53
|
+
export declare const MANAGED_HOOK_DEADLINE_MS = 12000;
|
|
54
|
+
/**
|
|
55
|
+
* The managed context child's own ceiling. It stays above
|
|
56
|
+
* SESSION_RENEWAL_TIMEOUT_MS so the parent never kills a renewal that is still
|
|
57
|
+
* inside its own budget, and below MANAGED_HOOK_DEADLINE_MS.
|
|
58
|
+
*/
|
|
59
|
+
export declare const SESSION_CONTEXT_CHILD_CEILING_MS = 11000;
|
|
60
|
+
/**
|
|
61
|
+
* Kept by the managed hook after its context child's limit: the hook's own
|
|
62
|
+
* startup before it set its deadline (an outer supervisor's timer was already
|
|
63
|
+
* running) and writing its diagnostic, so the hook reports before it is killed.
|
|
64
|
+
*/
|
|
65
|
+
export declare const MANAGED_HOOK_EXIT_RESERVE_MS = 1000;
|
|
66
|
+
/**
|
|
67
|
+
* Kept by the context child around its renewal: its own startup before the
|
|
68
|
+
* renewal deadline starts and writing its result. A renewal at a 9 s budget
|
|
69
|
+
* measured about 9.25 s of child time in cli.session-renewal-budget.test.ts.
|
|
70
|
+
*/
|
|
71
|
+
export declare const SESSION_CONTEXT_CHILD_RESERVE_MS = 1000;
|
|
72
|
+
/**
|
|
73
|
+
* SessionStart refreshes the station profile before its context. That
|
|
74
|
+
* refresh is bounded so it cannot use the window verified context needs: it
|
|
75
|
+
* ends by this budget, and always leaves SESSION_START_CONTEXT_RESERVE_MS of
|
|
76
|
+
* the hook's window (the context child's startup, a background renewal's
|
|
77
|
+
* bounded wait and the hook's report). A refresh that times out or cannot
|
|
78
|
+
* reach the authority is reported in one line, and the session uses its last
|
|
79
|
+
* verified profile within the usual 24-hour bound.
|
|
80
|
+
*/
|
|
81
|
+
export declare const SESSION_START_SYNC_BUDGET_MS = 5000;
|
|
82
|
+
export declare const SESSION_START_CONTEXT_RESERVE_MS = 6000;
|
|
83
|
+
export declare function sessionStartSyncBudget(remainingMs: number): number;
|
|
84
|
+
/**
|
|
85
|
+
* Limits for one managed context child, from the time the hook has left. On a
|
|
86
|
+
* full prompt window the renewal keeps its whole budget; after a lifecycle
|
|
87
|
+
* sync has used part of the window, the renewal shrinks first, so each layer
|
|
88
|
+
* times out inside the one that encloses it and reports its own code.
|
|
89
|
+
*/
|
|
90
|
+
export declare function managedContextChildLimits(remainingMs: number): {
|
|
91
|
+
childMs: number;
|
|
92
|
+
renewalBudgetMs: number;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Renewal budget for one managed-hook prompt. A renewal re-reads every changed
|
|
96
|
+
* pinned selection at SESSION_RENEWAL_CONCURRENCY, so this has to cover the
|
|
97
|
+
* profile's real churn and not just the profile read. Measured on the live
|
|
98
|
+
* 831-selection profile with a pin carrying 807 selections, 85 of them changed:
|
|
99
|
+
* the exact-version reads alone took ~6.1 s cold and ~4.6 s warm, before the
|
|
100
|
+
* client, the two profile resolves and the receipt write. A 4-second budget sat
|
|
101
|
+
* inside that range, so one pin renewed or timed out on authority latency alone.
|
|
102
|
+
*/
|
|
103
|
+
export declare const SESSION_RENEWAL_TIMEOUT_MS = 9000;
|
|
104
|
+
/**
|
|
105
|
+
* Budget of one background renewal (`skills sessions renew-background`),
|
|
106
|
+
* which managed hooks start instead of renewing on the prompt's critical
|
|
107
|
+
* path. It covers a large pin's historical reads on a slow authority; the
|
|
108
|
+
* authorization checks are the inline renewal's own.
|
|
109
|
+
*/
|
|
110
|
+
export declare const BACKGROUND_SESSION_RENEWAL_BUDGET_MS = 120000;
|
|
111
|
+
/**
|
|
112
|
+
* A root session's pin is renewed ahead of expiry once it is this old, while
|
|
113
|
+
* it is still valid, so an active session normally never reaches expiry and
|
|
114
|
+
* its prompts keep receiving context from the valid pin meanwhile.
|
|
115
|
+
*/
|
|
116
|
+
export declare const SESSION_RENEW_AHEAD_AGE_MS: number;
|
|
54
117
|
/**
|
|
55
118
|
* A definitive automatic renewal refusal is remembered briefly so the managed
|
|
56
119
|
* hook does not resolve the whole profile again on every prompt. The record
|
|
@@ -112,6 +175,23 @@ export interface SessionPinRenewalOptions extends SessionReconciliationOptions {
|
|
|
112
175
|
* does not run.
|
|
113
176
|
*/
|
|
114
177
|
projectDir?: string;
|
|
178
|
+
/**
|
|
179
|
+
* Managed hooks only: the time the caller has left for this renewal. The
|
|
180
|
+
* renewal uses the smaller of this and SESSION_RENEWAL_TIMEOUT_MS, so it
|
|
181
|
+
* reports its own SESSION_RENEWAL_TIMEOUT before an outer layer kills it.
|
|
182
|
+
*/
|
|
183
|
+
budgetMs?: number;
|
|
184
|
+
/**
|
|
185
|
+
* Background renewal only: the budget ceiling is
|
|
186
|
+
* BACKGROUND_SESSION_RENEWAL_BUDGET_MS instead of SESSION_RENEWAL_TIMEOUT_MS.
|
|
187
|
+
*/
|
|
188
|
+
background?: boolean;
|
|
189
|
+
/**
|
|
190
|
+
* Renew once the pin is at least this old (default: only after it expired).
|
|
191
|
+
* Background renew-ahead passes SESSION_RENEW_AHEAD_AGE_MS. The renewal
|
|
192
|
+
* itself and every check it makes are unchanged; only when it may start is.
|
|
193
|
+
*/
|
|
194
|
+
renewAfterMs?: number;
|
|
115
195
|
}
|
|
116
196
|
/**
|
|
117
197
|
* A refused renewal whose automatic migration could not finish within the
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ResolvedSkillSelection } from "../types/skill-selection.js";
|
|
2
|
-
import { type SelectionResolverOptions } from "./selection-resolver.js";
|
|
2
|
+
import { type ResolvedSelectionContext, type SelectionResolverOptions } from "./selection-resolver.js";
|
|
3
3
|
export interface SkillContextInput {
|
|
4
4
|
prompt?: string;
|
|
5
5
|
cwd?: string;
|
|
@@ -15,6 +15,25 @@ export interface SkillContextInput {
|
|
|
15
15
|
export interface SkillContextOptions extends SelectionResolverOptions {
|
|
16
16
|
maxChars?: number;
|
|
17
17
|
maxSkills?: number;
|
|
18
|
+
/**
|
|
19
|
+
* An unnamed prompt match whose longest matched phrase has fewer words than
|
|
20
|
+
* this is offered as a load command instead of being injected, however many
|
|
21
|
+
* such words matched. Named, explicit, path and required selections are not
|
|
22
|
+
* affected. Unset, every counted match may be injected.
|
|
23
|
+
*/
|
|
24
|
+
minPromptPhraseWords?: number;
|
|
25
|
+
/** Print load pointers with --cache-first even though this context didn't come from the cache: a managed hook that fell back to the service. */
|
|
26
|
+
pointerCacheFirst?: boolean;
|
|
27
|
+
/** Bodies a child session receives from its parent's loaded skills; the rest are offered. Unset, HISTORY_BODY_LIMITS applies. */
|
|
28
|
+
inheritedBodies?: number;
|
|
29
|
+
/** Called with the resolved pin before any payload is read (again if a new child is resolved again); metadata only. */
|
|
30
|
+
onResolved?: (resolved: ResolvedSelectionContext) => void;
|
|
31
|
+
/**
|
|
32
|
+
* performance.now() time after which a new child whose parent receipt
|
|
33
|
+
* changed while it resolved is not resolved again (the managed hook passes
|
|
34
|
+
* its renewal deadline). Without it the single retry is unconditional.
|
|
35
|
+
*/
|
|
36
|
+
resolveAgainBefore?: number;
|
|
18
37
|
}
|
|
19
38
|
export interface ContextSelectionReceipt extends ResolvedSkillSelection {
|
|
20
39
|
reason: string;
|
|
@@ -42,4 +61,34 @@ export interface SkillContextResult {
|
|
|
42
61
|
selections: ContextSelectionReceipt[];
|
|
43
62
|
};
|
|
44
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Re-delivered history (a subagent's inheritance and a restore after
|
|
66
|
+
* compaction or resume) carries no prompt, so its relevance is recency: the
|
|
67
|
+
* most recently loaded skills first, a few bodies, then load commands. Older
|
|
68
|
+
* history stays loadable with `skills load` and is not re-listed.
|
|
69
|
+
*/
|
|
70
|
+
export declare const HISTORY_BODY_LIMITS: Readonly<Record<"subagent-inherit" | "session-restore", number>>;
|
|
71
|
+
export declare const HISTORY_CANDIDATE_LIMIT = 10;
|
|
72
|
+
/**
|
|
73
|
+
* The one fixed line that says a context was limited because this session's
|
|
74
|
+
* record could not be updated: newly read skills are offered as load commands
|
|
75
|
+
* instead of being delivered unrecorded.
|
|
76
|
+
*/
|
|
77
|
+
export declare const SESSION_WRITE_BUSY_NOTICE = "Skills context was limited [SESSION_WRITE_BUSY]: another Skills process was updating this session's record, so newly selected skills are listed as load commands instead of being delivered. Load them with those commands when the task needs them.";
|
|
78
|
+
/**
|
|
79
|
+
* Select and read context for one prompt, recording newly delivered skills in
|
|
80
|
+
* the session receipt.
|
|
81
|
+
*
|
|
82
|
+
* A new child's first receipt write fences its parent's exact receipt. When
|
|
83
|
+
* the parent recorded a load or committed a renewal while the child resolved
|
|
84
|
+
* (its pin itself unchanged), that write refuses with SESSION_PARENT_CHANGED;
|
|
85
|
+
* as the refusal prescribes, the child is resolved once more from the current
|
|
86
|
+
* parent pin. A parent whose pin changed (a migration or reconciliation), a
|
|
87
|
+
* second change, or no time left before options.resolveAgainBefore keeps the
|
|
88
|
+
* refusal.
|
|
89
|
+
*
|
|
90
|
+
* Receipt writes run synchronously and, under contention, block the calling
|
|
91
|
+
* thread for at most SESSION_WRITE_LOCK_WAIT_MS while they wait for another
|
|
92
|
+
* live writer's lock.
|
|
93
|
+
*/
|
|
45
94
|
export declare function buildSkillContext(input: SkillContextInput, options?: SkillContextOptions): Promise<SkillContextResult>;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic prompt relevance for selected skills. Pure metadata: it reads
|
|
3
|
+
* slugs, aliases and trigger keywords, never payloads, and builds no regular
|
|
4
|
+
* expression from profile data.
|
|
5
|
+
*
|
|
6
|
+
* - A keyword is a phrase: all of its words, in order and adjacent, in the
|
|
7
|
+
* prompt. Duplicated keywords count once.
|
|
8
|
+
* - A selection without keywords is matched by its slug's words as a phrase:
|
|
9
|
+
* the whole slug, or a shorter run of adjacent slug words that carries the
|
|
10
|
+
* slug's own distinguishing word ("seating plan" for event-seating-plan, not
|
|
11
|
+
* "compliance check" for grant-compliance-check or "run of" for
|
|
12
|
+
* live-event-run-of-show), scored by the longest run that matched. A shorter
|
|
13
|
+
* run needs two words of at least SLUG_CONTENT_WORD_MIN_LENGTH letters and
|
|
14
|
+
* one of the slug's least-used words in this profile's triggers. Single slug
|
|
15
|
+
* words never match on their own, except a one-word slug.
|
|
16
|
+
* - A one-word phrase counts only when it discriminates in this profile: a
|
|
17
|
+
* word used by the triggers of more than DISCRIMINATING_WORD_MAX_SELECTIONS
|
|
18
|
+
* selections (review, plan, check) says nothing on its own about which skill
|
|
19
|
+
* a prompt needs.
|
|
20
|
+
* - A selection is named when the prompt contains its kebab-case slug or alias
|
|
21
|
+
* with at least one hyphen, or a one-word name separated only by whitespace
|
|
22
|
+
* from the word "skill" or "skills". A name raises priority and lets the
|
|
23
|
+
* selection's own one-word triggers count, but never selects on its own.
|
|
24
|
+
* - A match much weaker than the prompt's best match (less than
|
|
25
|
+
* WEAK_MATCH_FRACTION of its score) is marked weak: the prompt is about
|
|
26
|
+
* something else, so it is offered as a load command, not injected. Named
|
|
27
|
+
* selections are never weak.
|
|
28
|
+
* - An empty keyword list still disables prompt matching.
|
|
29
|
+
*/
|
|
30
|
+
import type { ResolvedSkillSelection } from "../types/skill-selection.js";
|
|
31
|
+
/** A one-word trigger shared by more selections than this selects nothing alone. */
|
|
32
|
+
export declare const DISCRIMINATING_WORD_MAX_SELECTIONS = 3;
|
|
33
|
+
/** One matched phrase word is worth this much; a candidate needs at least one. */
|
|
34
|
+
export declare const PHRASE_WORD_SCORE = 100;
|
|
35
|
+
/** A match scoring below this fraction of the prompt's best match is weak. */
|
|
36
|
+
export declare const WEAK_MATCH_FRACTION = 0.5;
|
|
37
|
+
/** Shorter slug words (of, to, a) neither distinguish a slug nor fill a partial slug run. */
|
|
38
|
+
export declare const SLUG_CONTENT_WORD_MIN_LENGTH = 3;
|
|
39
|
+
export interface PromptRelevance {
|
|
40
|
+
selection: ResolvedSkillSelection;
|
|
41
|
+
/** 2 when the prompt names the selection, otherwise 1. */
|
|
42
|
+
priority: 1 | 2;
|
|
43
|
+
score: number;
|
|
44
|
+
/** The distinct phrases that counted, as normalized words. */
|
|
45
|
+
phrases: string[];
|
|
46
|
+
/** Much weaker than the prompt's best match; offered, not injected. */
|
|
47
|
+
weak: boolean;
|
|
48
|
+
}
|
|
49
|
+
export declare function promptWords(value: string): string[];
|
|
50
|
+
interface SelectionPhrases {
|
|
51
|
+
phrases: string[][];
|
|
52
|
+
fromSlug: boolean;
|
|
53
|
+
}
|
|
54
|
+
/** The selection's distinct trigger phrases, or its slug's word runs when it has no keywords. */
|
|
55
|
+
export declare function selectionPhrases(selection: Pick<ResolvedSkillSelection, "slug" | "triggers">): SelectionPhrases;
|
|
56
|
+
/** How many selections use each word in any trigger phrase (each selection counted once). */
|
|
57
|
+
export declare function triggerWordFrequency(selections: readonly Pick<ResolvedSkillSelection, "slug" | "triggers">[]): Map<string, number>;
|
|
58
|
+
/**
|
|
59
|
+
* The slug's distinguishing words: among its words of at least
|
|
60
|
+
* SLUG_CONTENT_WORD_MIN_LENGTH letters, those used by the fewest selections'
|
|
61
|
+
* triggers in this profile (all of them when tied).
|
|
62
|
+
*/
|
|
63
|
+
export declare function slugAnchors(slugWords: readonly string[], frequency: ReadonlyMap<string, number>): Set<string>;
|
|
64
|
+
/**
|
|
65
|
+
* Rank selections by prompt relevance. Only candidates with at least one
|
|
66
|
+
* counted phrase are returned, ordered by priority, then score, then slug.
|
|
67
|
+
*/
|
|
68
|
+
export declare function rankPromptRelevance(prompt: string, selections: readonly ResolvedSkillSelection[], frequency?: Map<string, number>): PromptRelevance[];
|
|
69
|
+
/** At most this many weak prompt pointers are offered as load commands. */
|
|
70
|
+
export declare const PROMPT_OFFER_LIMIT = 3;
|
|
71
|
+
export type PromptOfferReason = "partial-match" | "common-word";
|
|
72
|
+
export interface PromptOffer {
|
|
73
|
+
selection: ResolvedSkillSelection;
|
|
74
|
+
reason: PromptOfferReason;
|
|
75
|
+
evidence: number;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Selections the prompt points at too weakly to select, offered as `skills
|
|
79
|
+
* load` commands and never injected. Only trigger keywords count, never slug
|
|
80
|
+
* runs, and a selection the caller excludes (already selected, ranked or
|
|
81
|
+
* loaded) is skipped.
|
|
82
|
+
*
|
|
83
|
+
* - partial-match: all content words (at least SLUG_CONTENT_WORD_MIN_LENGTH
|
|
84
|
+
* letters, two or more) of a multi-word keyword appear in the prompt, though
|
|
85
|
+
* not as the phrase, and one of them discriminates in this profile
|
|
86
|
+
* ("designs review" in "push the screens to the designs app for review").
|
|
87
|
+
* - common-word: a one-word keyword used by more than
|
|
88
|
+
* DISCRIMINATING_WORD_MAX_SELECTIONS selections appears in the prompt.
|
|
89
|
+
*
|
|
90
|
+
* Partial matches rank before common words, then more matching keywords,
|
|
91
|
+
* then slug. A tie that does not fit the remaining offers is left out
|
|
92
|
+
* entirely: an alphabetical slug is no reason to prefer one.
|
|
93
|
+
*/
|
|
94
|
+
export declare function rankPromptOffers(prompt: string, selections: readonly ResolvedSkillSelection[], frequency?: Map<string, number>, exclude?: (selection: ResolvedSkillSelection) => boolean): PromptOffer[];
|
|
95
|
+
export {};
|