dsh-keys-manager 0.0.0 → 0.1.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.
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Minimal Host service contracts used by the plugin.
3
+ *
4
+ * These interfaces name only the methods Keys Manager calls. The real
5
+ * implementations are the shipped DSH services; keeping the contract local
6
+ * avoids coupling the plugin to adapter internals while still type-checking
7
+ * every call site.
8
+ */
9
+ import type { Context } from '@deepseek-ai/cordis';
10
+ import type { KeyRecord, ManagerSettings, ModelRecord } from './domain.js';
11
+ /** One provider route from `llm.listConfigurableProviders()`. */
12
+ export interface DirectoryEntry {
13
+ provider: string;
14
+ displayName: string;
15
+ settingsNs: string;
16
+ settingsPath: readonly string[];
17
+ declared?: boolean;
18
+ defaultBaseURL?: string;
19
+ baseURLEnv?: readonly string[];
20
+ nativeAuth?: {
21
+ env: readonly string[];
22
+ oauth: boolean;
23
+ };
24
+ error?: string;
25
+ }
26
+ /** One model an endpoint disclosed during discovery. */
27
+ export interface DiscoveredModel {
28
+ id: string;
29
+ name?: string;
30
+ contextWindow?: number;
31
+ maxTokens?: number;
32
+ inputModalities?: ReadonlyArray<'text' | 'image'>;
33
+ }
34
+ /** Request sent to the adapter's model-discovery operation. */
35
+ export interface DiscoveryRequest {
36
+ provider?: string;
37
+ baseURL?: string;
38
+ api?: string;
39
+ apiKey?: string;
40
+ }
41
+ /** The LLM runtime methods Keys Manager uses. */
42
+ export interface LlmRuntimeLike {
43
+ listConfigurableProviders(): DirectoryEntry[];
44
+ listProviders(): Array<{
45
+ id: string;
46
+ name: string;
47
+ }>;
48
+ discoverModels(settingsNs: string, request: DiscoveryRequest, signal: AbortSignal): Promise<DiscoveredModel[]>;
49
+ }
50
+ /** Credential value/source facts returned by the credential seam. */
51
+ export interface CredentialHit {
52
+ value: string;
53
+ source: string;
54
+ }
55
+ /** Credential presence facts returned by the credential seam. */
56
+ export interface CredentialDescription {
57
+ configured: boolean;
58
+ source?: string;
59
+ writable: boolean;
60
+ }
61
+ /** The credential provider methods the broker wraps. */
62
+ export interface CredentialsLike {
63
+ resolve(ref: string): Promise<CredentialHit | undefined>;
64
+ describe(ref: string): Promise<CredentialDescription>;
65
+ set(ref: string, value: string): Promise<void>;
66
+ unset(ref: string): Promise<void>;
67
+ }
68
+ /** One path-addressed settings edit. */
69
+ export type SettingsPathOp = {
70
+ op: 'set';
71
+ path: readonly string[];
72
+ value: unknown;
73
+ } | {
74
+ op: 'unset';
75
+ path: readonly string[];
76
+ };
77
+ /** One live settings namespace descriptor. */
78
+ export interface SettingsDescriptorLike {
79
+ ns: string;
80
+ value: unknown;
81
+ revision: number;
82
+ }
83
+ /** The settings service methods Keys Manager uses. */
84
+ export interface SettingsLike {
85
+ readonly writable: boolean;
86
+ describe(options?: {
87
+ redactSecrets?: boolean;
88
+ }): SettingsDescriptorLike[];
89
+ mutate(ns: string, ops: readonly SettingsPathOp[], expectedRevision?: number): Promise<void>;
90
+ }
91
+ /** The Typert registry method Keys Manager uses. */
92
+ export interface TypertLike {
93
+ register(contribution: unknown): () => void;
94
+ }
95
+ /** Host context surface this plugin requires. */
96
+ export type HostContext = Context & {
97
+ llm: LlmRuntimeLike;
98
+ credentials: CredentialsLike;
99
+ settings?: SettingsLike;
100
+ typert: TypertLike;
101
+ };
102
+ /** Serializable provider failure facts relevant to key health. */
103
+ export interface LlmFailureLike {
104
+ code: string;
105
+ message?: string;
106
+ providerRetryAfterMs?: number;
107
+ }
108
+ /** One stream chunk; only `finish` is inspected by the broker. */
109
+ export interface StreamChunkLike {
110
+ type: string;
111
+ reason?: {
112
+ kind: string;
113
+ failure?: LlmFailureLike;
114
+ };
115
+ }
116
+ /** Minimal `GenerateOptions` surface observed by the stream listener. */
117
+ export interface GenerateOptionsLike {
118
+ provider: string;
119
+ model: string;
120
+ sessionId?: string;
121
+ /** Host-side bypass purposes; any value opts the stream out of retry pins. */
122
+ purpose?: 'compaction' | 'session-title';
123
+ messages: readonly unknown[];
124
+ }
125
+ /** Payload of the `agent/request` waterfall (subset used here). */
126
+ export interface AgentRequestPayload {
127
+ agent?: {
128
+ session?: {
129
+ id?: string;
130
+ };
131
+ };
132
+ turn: number;
133
+ step: number;
134
+ }
135
+ /** Payload of the `agent/request-error` waterfall. */
136
+ export interface RequestErrorPayload {
137
+ agent: {
138
+ session: {
139
+ id: string;
140
+ };
141
+ };
142
+ turn: number;
143
+ step: number;
144
+ provider: string;
145
+ failure: LlmFailureLike;
146
+ retryPolicy?: unknown;
147
+ signal: AbortSignal;
148
+ }
149
+ /** Return value of the request-error waterfall. */
150
+ export type RequestErrorAction = {
151
+ kind: 'retry';
152
+ } | undefined;
153
+ /** Selection result for one request. */
154
+ export interface KeySelection {
155
+ key: KeyRecord | undefined;
156
+ value: string | undefined;
157
+ }
158
+ /** Attempt bookkeeping for retry/failover decisions. */
159
+ export interface AttemptState {
160
+ keyId: string | undefined;
161
+ retries: number;
162
+ failure?: LlmFailureLike;
163
+ at: number;
164
+ turn?: number;
165
+ step?: number;
166
+ /** Whether the request actually resolved a managed key through the broker. */
167
+ usedManagedKey?: boolean;
168
+ /** Monotonic stream-sequence attribution for this step. */
169
+ lastSequence?: number;
170
+ /** Retry budget consumed per selected key. */
171
+ keyRetries?: Record<string, number>;
172
+ }
173
+ /** Provider support verdict used by the UI. */
174
+ export interface ProviderSupport {
175
+ supported: boolean;
176
+ reason?: string;
177
+ }
178
+ export type { ManagerSettings, ModelRecord };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=host-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-types.js","sourceRoot":"","sources":["../src/host-types.ts"],"names":[],"mappings":""}
package/lib/index.d.ts ADDED
@@ -0,0 +1,181 @@
1
+ import type { StateView } from './domain.js';
2
+ import type { CredentialsLike, GenerateOptionsLike, HostContext, KeySelection, RequestErrorAction, AgentRequestPayload, RequestErrorPayload, SettingsLike, StreamChunkLike } from './host-types.js';
3
+ import { KeysManagerStore } from './store.js';
4
+ export declare const name = "keys-manager";
5
+ export declare const inject: string[];
6
+ export declare const provide: string[];
7
+ /** Service keybinding consumed by the Typert Gateway. */
8
+ interface TypertRemoteBinding {
9
+ service: KeysManagerService;
10
+ serviceKey: string;
11
+ namespace: string;
12
+ }
13
+ export declare class KeysManagerService {
14
+ readonly ctx: HostContext;
15
+ readonly store: KeysManagerStore;
16
+ private readonly als;
17
+ private readonly attempts;
18
+ private readonly stepAttempts;
19
+ private readonly streamAttempts;
20
+ private readonly linkedAttempts;
21
+ private readonly retryBindings;
22
+ private readonly requestSteps;
23
+ private attemptSequence;
24
+ private readonly switches;
25
+ private warnings;
26
+ settings: SettingsLike | undefined;
27
+ private rawCredentials;
28
+ private readonly credentialOriginals;
29
+ private readonly credentialWrappers;
30
+ private brokerOwners;
31
+ private readonly bindingCache;
32
+ private lastBindingPruneAt;
33
+ private readonly selectionQueues;
34
+ private queue;
35
+ private closing;
36
+ readonly typertRemote: TypertRemoteBinding;
37
+ constructor(ctx: HostContext, store: KeysManagerStore);
38
+ /** Log without ever echoing secret material. */
39
+ warn(message: string): void;
40
+ /** Rebuild the broker-reference → provider index. */
41
+ private rebuildBrokerMap;
42
+ /** Serialize mutations, rejecting submissions that arrive during shutdown. */
43
+ private run;
44
+ /**
45
+ * Periodically drop expired transient state: retry pins after `PIN_TTL_MS`,
46
+ * the remaining attempt/step records after `ATTEMPT_TTL_MS`.
47
+ */
48
+ pruneTransient(now?: number): void;
49
+ /** Stop accepting mutations, drain the queue, then persist the final snapshot. */
50
+ close(): Promise<void>;
51
+ /** Push a user-facing warning that rides the next state response. */
52
+ private pushWarning;
53
+ private takeWarnings;
54
+ /** Current settings service, if the composition mounted one. */
55
+ private settingsService;
56
+ /** Current raw credential provider. */
57
+ private credentialsRaw;
58
+ /** Install the broker layer on one credential provider instance. */
59
+ attachCredentials(credentials: CredentialsLike): void;
60
+ /** Restore the credential provider methods when our fiber unloads. */
61
+ detachCredentials(credentials: CredentialsLike): void;
62
+ /** Resolve a credential through the unwrapped provider. */
63
+ private resolveOriginal;
64
+ /**
65
+ * Probe one credential reference while distinguishing absence from a
66
+ * transient credential-store failure. Selection must never persist a
67
+ * permanent `valueMissing` marker for an error.
68
+ */
69
+ private probeKey;
70
+ /** Write one key literal through the unwrapped provider. */
71
+ private setOriginal;
72
+ /**
73
+ * Remove one key literal through the unwrapped provider. A rejected unset is
74
+ * only ignored when the credential is confirmed absent; a transient or
75
+ * permission failure must keep the metadata so a later retry can finish.
76
+ */
77
+ private unsetOriginal;
78
+ /** All settings descriptors, with an explicit unreadable state. */
79
+ private describeSettings;
80
+ /** Find one live settings descriptor by entry id. */
81
+ private namespace;
82
+ /** Apply one path-addressed settings write through the live revision. */
83
+ private mutateSettings;
84
+ /** Prune expired bindings at most once a minute, without a restart. */
85
+ private pruneBindings;
86
+ /** Build the complete UI state. */
87
+ buildState(): StateView;
88
+ /** Remote: complete manager state. */
89
+ getState(): StateView;
90
+ /** Find one directory entry by route id. */
91
+ private directoryEntry;
92
+ /**
93
+ * Classify the live binding. An unreadable settings snapshot is `unknown`,
94
+ * not a confirmed mismatch: transient settings failures must not disable
95
+ * key-pool recovery for a provider that is still managed.
96
+ */
97
+ private bindingState;
98
+ /** Candidate credential refs from which an existing key can be imported. */
99
+ private importRefs;
100
+ /** Create and store one key record without adding it to a provider. */
101
+ private createKeyRecord;
102
+ /** Find the first existing key value worth importing. */
103
+ private findImport;
104
+ /** Remote: take over one existing provider route. */
105
+ adoptProvider(provider: string): Promise<StateView>;
106
+ /** Discover models for a new extra provider, falling back to the seed catalog. */
107
+ private discoverModels;
108
+ /** Remote: install and adopt one extra built-in provider. */
109
+ addExtraProvider(extraId: string, alias: string, value: string): Promise<StateView>;
110
+ /** Add one key; caller already owns the mutation queue. */
111
+ private addKeyLocked;
112
+ /** Remote: add one key to a managed provider. */
113
+ addKey(provider: string, alias: string, value: string): Promise<StateView>;
114
+ /** Remote: rename or pause/resume one key. */
115
+ updateKey(provider: string, keyId: string, patch: unknown): Promise<StateView>;
116
+ /** Remote: delete one key, releasing the provider when it was the last one. */
117
+ deleteKey(provider: string, keyId: string): Promise<StateView>;
118
+ /** Read one live profile for release-time cleanup decisions. */
119
+ private profileAt;
120
+ /** Restore the exact live profile captured before release mutated it. */
121
+ private restoreProfileSnapshot;
122
+ /** Remove every managed credential, recording progress before a retry. */
123
+ private deleteManagedCredentials;
124
+ /** Restore the provider profile and drop management; caller owns the queue. */
125
+ private releaseLocked;
126
+ /** Remote: stop managing a provider and restore its original key reference. */
127
+ releaseProvider(provider: string): Promise<StateView>;
128
+ /** Remote: update manager policy. */
129
+ setSettings(patch: unknown): Promise<StateView>;
130
+ /**
131
+ * Serialize the first selection for a sticky session/provider pair so two
132
+ * concurrent requests cannot advance the cursor and bind different keys.
133
+ */
134
+ selectKey(providerId: string, sessionId: string | undefined, step?: {
135
+ turn: number;
136
+ step: number;
137
+ }): Promise<KeySelection>;
138
+ /** Pin the key a retry must use, scoped to the failing step. */
139
+ private pinRetry;
140
+ /** Record the turn/step the agent loop is about to request for one provider. */
141
+ noteRequestStep(payload: AgentRequestPayload, providerId: string): void;
142
+ /** Drop one step's retry pin once its attempt settles. */
143
+ private clearRetry;
144
+ /** Selection body; callers reach it through {@link selectKey}. */
145
+ private doSelectKey;
146
+ /** Record a terminal failure without deciding policy yet. */
147
+ private recordFailure;
148
+ /** Record a successful terminal finish. */
149
+ private recordSuccess;
150
+ /** Mark one key cooling, preserving escalation across recovery windows. */
151
+ private markFailure;
152
+ /** Prune stale attempt records. */
153
+ private pruneAttempts;
154
+ /** Waterfall listener: bind a key to the request and observe its terminal chunk. */
155
+ streamListener(options: GenerateOptionsLike, next: () => AsyncIterable<StreamChunkLike>): AsyncIterable<StreamChunkLike>;
156
+ /** Wrap one managed stream so credential resolution sees this key. */
157
+ private wrapStream;
158
+ /**
159
+ * Delegate recovery to the next agent/request-error listener. Preserve the
160
+ * current key only when that listener actually requests a retry; a declined
161
+ * (undefined) recovery is terminal and must still cool the exhausted key.
162
+ */
163
+ private delegateAndMaybeCool;
164
+ /** Record one failed stream attempt for turn/step attribution. */
165
+ private recordStreamAttempt;
166
+ /** Consume the stream record linked to this exact failure object. */
167
+ private takeLinkedAttempt;
168
+ /** Claim the next unclaimed stream attempt after a step's last sequence. */
169
+ private claimStreamAttempt;
170
+ /**
171
+ * Find a metadata-selectable alternate whose credential actually resolves.
172
+ * A key whose credential was removed outside the manager must not cause the
173
+ * current, working key to be cooled and the session rebound.
174
+ */
175
+ private findVerifiedAlternate;
176
+ /** Waterfall listener: bounded retry, then key switch and cool-down. */
177
+ requestErrorListener(payload: RequestErrorPayload, next: () => Promise<RequestErrorAction>): Promise<RequestErrorAction>;
178
+ }
179
+ /** Host entry. */
180
+ export declare function apply(ctx: HostContext): void;
181
+ export {};