@sparkelf/dsh-knowledge-base 0.1.5 → 0.1.20

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.
Files changed (58) hide show
  1. package/README.md +129 -2
  2. package/SKILL.md +86 -18
  3. package/lib/client.js +14766 -9663
  4. package/lib/client.js.map +1 -1
  5. package/lib/index.js +8082 -258
  6. package/lib/types/api.d.ts +36 -1
  7. package/lib/types/client/KnowledgeCreateDialog.d.ts +25 -0
  8. package/lib/types/client/KnowledgeFileTree.d.ts +143 -0
  9. package/lib/types/client/KnowledgeHome.d.ts +58 -0
  10. package/lib/types/client/KnowledgeListing.d.ts +7 -0
  11. package/lib/types/client/KnowledgeNewDocumentDialog.d.ts +32 -0
  12. package/lib/types/client/KnowledgeProgressPopover.d.ts +69 -0
  13. package/lib/types/client/KnowledgeSettings.d.ts +4 -3
  14. package/lib/types/client/KnowledgeTemplates.d.ts +4 -0
  15. package/lib/types/client/VectorSettingsGroup.d.ts +17 -0
  16. package/lib/types/client/WorkflowMenuIcon.d.ts +22 -0
  17. package/lib/types/client/extractionHistory.d.ts +43 -0
  18. package/lib/types/client/index.d.ts +32 -14
  19. package/lib/types/client/listingFilter.d.ts +58 -0
  20. package/lib/types/client/locales.d.ts +123 -0
  21. package/lib/types/client/openInSidebar.d.ts +2 -2
  22. package/lib/types/client/progressRules.d.ts +198 -17
  23. package/lib/types/client/sanitizeExplorerTabs.d.ts +20 -0
  24. package/lib/types/client/settingsGate.d.ts +65 -0
  25. package/lib/types/client/uploadTransport.d.ts +65 -0
  26. package/lib/types/client/useTreeSort.d.ts +72 -0
  27. package/lib/types/client/workflows/WorkflowPlaza.d.ts +146 -0
  28. package/lib/types/client/workflows/cron.d.ts +125 -0
  29. package/lib/types/client/workflows/dedupe.d.ts +70 -0
  30. package/lib/types/client/workflows/deps.d.ts +101 -0
  31. package/lib/types/client/workflows/modelChoice.d.ts +106 -0
  32. package/lib/types/client/workflows/published.d.ts +43 -0
  33. package/lib/types/client/workflows/registry.d.ts +98 -0
  34. package/lib/types/client/workflows/subscribe.d.ts +179 -0
  35. package/lib/types/embeddings.d.ts +62 -0
  36. package/lib/types/externalChange.d.ts +25 -0
  37. package/lib/types/extract.d.ts +32 -0
  38. package/lib/types/extractionQueue.d.ts +23 -2
  39. package/lib/types/ftsIndex.d.ts +497 -0
  40. package/lib/types/index.d.ts +266 -8
  41. package/lib/types/initialize.d.ts +105 -0
  42. package/lib/types/knowledgeTools.d.ts +239 -0
  43. package/lib/types/localStoragePath.d.ts +6 -0
  44. package/lib/types/localStorageSettings.d.ts +33 -0
  45. package/lib/types/managementTools.d.ts +184 -0
  46. package/lib/types/outlineGuard.d.ts +119 -0
  47. package/lib/types/outlineText.d.ts +134 -0
  48. package/lib/types/protocol.d.ts +34 -0
  49. package/lib/types/scopeAliases.d.ts +20 -0
  50. package/lib/types/vectorIndex.d.ts +113 -0
  51. package/lib/types/views.d.ts +33 -1
  52. package/lib/types/workflowPublish.d.ts +114 -0
  53. package/package.json +7 -7
  54. package/skills/kb-summarize/SKILL.md +158 -0
  55. package/skills/kb-summarize/i18n/zh.json +10 -0
  56. package/skills/kb-vectorize/SKILL.md +57 -0
  57. package//344/270/207/347/233/270AI/344/275/277/347/224/250/346/225/231/347/250/213.html +294 -0
  58. package/lib/types/client/KnowledgeProgressPanel.d.ts +0 -53
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Model choices for the workflow subscription dialog.
3
+ *
4
+ * The Host owns the catalog (`remote.session.modelCatalog()`); this module only narrows
5
+ * it for rendering and remembers the reader's last pick. Keeping the mapping pure makes
6
+ * the "catalog unavailable" fallback testable without a browser or a Host.
7
+ *
8
+ * @module @sparkelf/dsh-knowledge-base/client/workflows/modelChoice
9
+ */
10
+ /** One exact model route the Host can serve. */
11
+ export interface WorkflowModelSelection {
12
+ readonly provider: string;
13
+ readonly model: string;
14
+ readonly reasoningEffort?: string;
15
+ }
16
+ /** One reasoning level an adapter exposes for a model. */
17
+ export interface WorkflowReasoningEffort {
18
+ readonly id: string;
19
+ readonly name: string;
20
+ }
21
+ /** One model row rendered inside its provider group. */
22
+ export interface WorkflowModelOption extends WorkflowModelSelection {
23
+ /** Stable `<optgroup>`/`<option>` key: provider + model. */
24
+ readonly key: string;
25
+ readonly providerName: string;
26
+ readonly modelName: string;
27
+ readonly description?: string;
28
+ readonly reasoningEfforts: readonly WorkflowReasoningEffort[];
29
+ /** The effort the Host would use when none is chosen. */
30
+ readonly defaultEffort?: string;
31
+ }
32
+ /** One provider group. */
33
+ export interface WorkflowModelGroup {
34
+ readonly provider: string;
35
+ readonly providerName: string;
36
+ readonly models: readonly WorkflowModelOption[];
37
+ }
38
+ /** The catalog as the dialog consumes it. */
39
+ export interface WorkflowModelCatalogView {
40
+ readonly default?: WorkflowModelSelection;
41
+ readonly groups: readonly WorkflowModelGroup[];
42
+ /** Providers whose catalog could not be loaded; shown as a quiet fallback note. */
43
+ readonly failures: readonly {
44
+ readonly provider: string;
45
+ readonly message: string;
46
+ }[];
47
+ }
48
+ /** The raw shape `remote.session.modelCatalog()` returns (narrowed structurally). */
49
+ export interface RawModelCatalog {
50
+ readonly default?: WorkflowModelSelection;
51
+ readonly groups?: readonly {
52
+ readonly id: string;
53
+ readonly name: string;
54
+ readonly models: readonly {
55
+ readonly id: string;
56
+ readonly name: string;
57
+ readonly description?: string;
58
+ readonly reasoning?: {
59
+ readonly efforts: readonly WorkflowReasoningEffort[];
60
+ readonly defaultEffort?: string;
61
+ };
62
+ }[];
63
+ }[];
64
+ readonly failures?: readonly {
65
+ readonly id: string;
66
+ readonly name: string;
67
+ readonly message: string;
68
+ }[];
69
+ }
70
+ /** The localStorage key; per browser profile, which is the DSH workspace's boundary. */
71
+ export declare const WORKFLOW_MODEL_STORAGE_KEY = "dsh-kb-workflow-model";
72
+ /** Stable key for one provider/model pair. */
73
+ export declare const modelKeyOf: (selection: WorkflowModelSelection) => string;
74
+ /** Narrow a Host catalog into renderable groups. */
75
+ export declare function toWorkflowModelCatalog(raw: RawModelCatalog | undefined): WorkflowModelCatalogView;
76
+ /** Find one option by its key. */
77
+ export declare function findModelOption(catalog: WorkflowModelCatalogView, key: string): WorkflowModelOption | undefined;
78
+ /** Find the option matching a remembered/default selection, when the catalog still serves it. */
79
+ export declare function findSelection(catalog: WorkflowModelCatalogView, selection: WorkflowModelSelection | undefined): WorkflowModelOption | undefined;
80
+ /**
81
+ * Whether a route is one of the known-bad providers that made the old default fail.
82
+ *
83
+ * `tokensfree_ds` was the concrete 502 in the report: its models are listed, but a
84
+ * subscription whose first turn runs there fails. The check is intentionally narrow
85
+ * (provider/model id contains "tokensfree") rather than a general health probe; a
86
+ * catalog that stops listing the route is already handled by `findSelection`.
87
+ */
88
+ export declare function isObviousBadProvider(selection: WorkflowModelSelection): boolean;
89
+ /**
90
+ * Initial selection for a dialog open.
91
+ *
92
+ * Priority is **workspace default → remembered choice → first usable model**. The
93
+ * workspace default must win over a browser memory: the reported bug was a remembered
94
+ * `tokensfree_ds` selection silently overriding a healthy `dashscope` default and
95
+ * reproducing the 502. A remembered route is only considered while it still exists in
96
+ * the catalog and is not a known-bad provider; a bad workspace default is skipped for
97
+ * a usable remembered route, and only used when there is nothing better.
98
+ *
99
+ * Returns undefined when the catalog is empty, which the dialog renders as
100
+ * "workspace default".
101
+ */
102
+ export declare function initialModelOption(catalog: WorkflowModelCatalogView, remembered: WorkflowModelSelection | undefined): WorkflowModelOption | undefined;
103
+ /** Read the last choice this browser made. Never throws (storage can be disabled). */
104
+ export declare function readRememberedModel(storage?: Pick<Storage, 'getItem'>): WorkflowModelSelection | undefined;
105
+ /** Remember one choice for the next dialog open. Never throws. */
106
+ export declare function rememberModel(selection: WorkflowModelSelection, storage?: Pick<Storage, 'setItem'>): void;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Approved workflows from the profile-local publish store, as plaza cards.
3
+ *
4
+ * The server decides what is public; this module only turns an approved record into the
5
+ * same inert definition the built-ins use. Keeping it pure makes the merge testable
6
+ * without a browser, and keeps the plaza from knowing where the store lives.
7
+ *
8
+ * @module @sparkelf/dsh-knowledge-base/client/workflows/published
9
+ */
10
+ import { type WorkflowDefinition } from './registry.ts';
11
+ /** One approved workflow as the HTTP route reports it. */
12
+ export interface PublishedWorkflowView {
13
+ readonly id: string;
14
+ readonly title: string;
15
+ readonly description: string;
16
+ readonly instruction: string;
17
+ readonly cronHint: string;
18
+ readonly category: string;
19
+ readonly authorId?: string;
20
+ readonly authorName?: string;
21
+ readonly createdAt?: string;
22
+ readonly status?: 'pending' | 'approved' | 'rejected' | 'unpublished';
23
+ readonly reviewStatus?: 'pending' | 'approved' | 'rejected';
24
+ readonly reviewedByName?: string;
25
+ readonly reviewNote?: string;
26
+ readonly visibility?: 'global' | 'department' | 'private';
27
+ readonly departmentCode?: string | null;
28
+ readonly departmentName?: string | null;
29
+ readonly updatedAt?: string;
30
+ readonly reviewedAt?: string;
31
+ readonly unpublishedAt?: string | null;
32
+ readonly submittedAt?: string;
33
+ readonly ownerId?: string;
34
+ }
35
+ /**
36
+ * Turn one approved record into a plaza definition.
37
+ *
38
+ * The record id is the identity: the subscription marker embeds it, so a re-reviewed
39
+ * record keeps its dedupe identity while its copy can change.
40
+ */
41
+ export declare function publishedDefinition(record: PublishedWorkflowView): WorkflowDefinition;
42
+ /** Merge built-ins with approved records, keeping built-ins first and ids unique. */
43
+ export declare function mergePublishedWorkflows(builtIns: readonly WorkflowDefinition[], approved: readonly PublishedWorkflowView[]): WorkflowDefinition[];
@@ -0,0 +1,98 @@
1
+ /**
2
+ * The built-in workflow registry behind the 「工作流广场」 tab.
3
+ *
4
+ * A workflow is a TEMPLATE, not a scheduler. Every field here is static data: the
5
+ * plaza renders these cards, and subscribing turns one of them into an ordinary DSH
6
+ * Schedule using the existing `schedule_create` tool. Nothing in this module runs
7
+ * anything, parses a cron expression, or talks to a server -- keeping the definitions
8
+ * inert is what makes the plaza cheap to extend and impossible to break by adding a
9
+ * card.
10
+ *
11
+ * ## Why the definitions live in this plugin
12
+ *
13
+ * DataOps owns sharing: which buckets a person can see. It does not schedule work and
14
+ * does not execute it, so it has no say in what a workflow is. A workflow that needs
15
+ * to read or write knowledge documents still goes through the knowledge API at run
16
+ * time, which is where DataOps' permissions already apply.
17
+ *
18
+ * @module @sparkelf/dsh-knowledge-base/client/workflows/registry
19
+ */
20
+ /** Which icon a plaza card shows. Mapped to a concrete glyph by the component. */
21
+ export type WorkflowIcon = 'organize' | 'index' | 'archive';
22
+ /** One frequency shortcut offered in the subscribe dialog. */
23
+ export interface WorkflowFrequency {
24
+ /**
25
+ * Stable id, also used as the radio button's DOM hook.
26
+ *
27
+ * `everyHours` / `everyMinutes` are interval shortcuts: their expression depends on a
28
+ * count the reader edits, so they carry no `cron` here and are resolved by `cron.ts`
29
+ * from `INTERVAL_SPECS` instead.
30
+ */
31
+ readonly id: 'daily' | 'weekly' | 'monthly' | 'everyHours' | 'everyMinutes' | 'custom';
32
+ /** The cron expression this shortcut fills in, or `undefined` for `custom`. */
33
+ readonly cron?: string;
34
+ }
35
+ /** One built-in workflow, as the plaza renders and subscribes it. */
36
+ export interface WorkflowDefinition {
37
+ /**
38
+ * Stable identity, and the ONLY thing deduplication is allowed to key on.
39
+ *
40
+ * Titles are copy and will be edited; ids are the contract. A subscribed task is
41
+ * recognised by this string appearing in its prompt, so changing an id orphans
42
+ * existing subscriptions -- treat the value as frozen once shipped.
43
+ */
44
+ readonly id: string;
45
+ /** Locale key for the card title. */
46
+ readonly titleKey: string;
47
+ /** Locale key for the card description. */
48
+ readonly descriptionKey: string;
49
+ /** Card glyph. */
50
+ readonly icon: WorkflowIcon;
51
+ /** Published records carry their own copy; built-ins resolve it from locale keys. */
52
+ readonly title?: string;
53
+ readonly description?: string;
54
+ /** Category/author shown on a published card; absent for built-ins. */
55
+ readonly category?: string;
56
+ readonly authorName?: string;
57
+ /** Cron prefilled in the subscribe dialog before the reader touches anything. */
58
+ readonly defaultCron: string;
59
+ /** Frequency shortcuts, in the order the dialog shows them. */
60
+ readonly frequencies: readonly WorkflowFrequency[];
61
+ /**
62
+ * What the scheduled session should actually do.
63
+ *
64
+ * This is the body of the prompt handed to a new session, and it becomes the
65
+ * Schedule's own prompt, so it must read as a standing instruction rather than as
66
+ * a one-off request: it runs again every time the schedule fires.
67
+ */
68
+ readonly instruction: string;
69
+ }
70
+ /**
71
+ * The marker a subscribed task carries, so its workflow can be recognised later.
72
+ *
73
+ * Deliberately plain text inside the prompt: DSH Schedules have no user-data field,
74
+ * and the prompt is the one part of a Schedule this plugin controls. A marker that is
75
+ * visible in the prompt is also honest -- opening the task detail shows which workflow
76
+ * produced it.
77
+ *
78
+ * @param id - workflow id to mark.
79
+ * @returns the marker string, e.g. `[workflow:kb-organize-personal]`.
80
+ */
81
+ export declare function workflowMarker(id: string): string;
82
+ /** Frequency shortcuts every built-in workflow offers. */
83
+ export declare const STANDARD_FREQUENCIES: readonly WorkflowFrequency[];
84
+ /**
85
+ * The built-in workflows, in the order the plaza shows them.
86
+ *
87
+ * Each one names a capability the knowledge plugin already has, so subscribing cannot
88
+ * promise something the deployment cannot do. Adding a card means adding a definition
89
+ * here and its copy keys; no other wiring is required.
90
+ */
91
+ export declare const BUILT_IN_WORKFLOWS: readonly WorkflowDefinition[];
92
+ /**
93
+ * Find one built-in workflow by id.
94
+ *
95
+ * @param id - workflow id to look up.
96
+ * @returns the definition, or undefined when no such workflow is built in.
97
+ */
98
+ export declare function findWorkflow(id: string): WorkflowDefinition | undefined;
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Turning one plaza card into a real DSH Schedule.
3
+ *
4
+ * ## Why this goes through a Session
5
+ *
6
+ * DSH exposes Schedules to the browser as list-and-delete only; creating one is an
7
+ * agent tool (`schedule_create`). That is deliberate -- the task manager page says so
8
+ * in its own type docs ("Start a new Session, where a reminder is created by asking
9
+ * the model to schedule it. The page deliberately has no creation form of its own.")
10
+ * -- so subscribing follows exactly the path the product already uses for its own
11
+ * 「新建任务」 button: create a Session, ask the model to schedule the work, then confirm
12
+ * it actually happened.
13
+ *
14
+ * ## Why the result is verified instead of assumed
15
+ *
16
+ * The model is a participant, not a function call. It can misread the request, ask a
17
+ * clarifying question, or reply with prose and call nothing. Reporting "subscribed" on
18
+ * the back of a successful `prompt` would be reporting that a message was delivered,
19
+ * not that a task exists. So the last step re-reads the authoritative task list and
20
+ * only then reports success; anything else is a failure with a reason the reader can
21
+ * act on.
22
+ *
23
+ * Every dependency is injected, because the interesting part is the decision sequence
24
+ * and a test should be able to drive it without a browser, a model, or a clock.
25
+ *
26
+ * @module @sparkelf/dsh-knowledge-base/client/workflows/subscribe
27
+ */
28
+ import { type SubscribedTaskView } from './dedupe.ts';
29
+ import type { WorkflowModelSelection } from './modelChoice.ts';
30
+ import { type WorkflowDefinition } from './registry.ts';
31
+ /** Where a subscription attempt currently is. */
32
+ export type SubscribePhase = 'idle' | 'creating-session' | 'selecting-model' | 'prompting' | 'verifying' | 'opened' | 'subscribed' | 'cancelling' | 'cancelled' | 'failed';
33
+ /** Why an attempt failed, in terms the dialog can explain. */
34
+ export type SubscribeFailure =
35
+ /** The workflow already has a task; nothing was created. */
36
+ 'already-subscribed'
37
+ /** The Host refused to create a Session. */
38
+ | 'session-failed'
39
+ /** The new Session could not be addressed, so no prompt could be sent. */
40
+ | 'session-unaddressable'
41
+ /** The prompt was refused (e.g. the Session was not ready to accept it). */
42
+ | 'prompt-failed'
43
+ /** The selected model could not be installed on the new Session. */
44
+ | 'model-failed'
45
+ /** The prompt was accepted but no matching task appeared within the budget. */
46
+ | 'not-created'
47
+ /** Removing the task failed. */
48
+ | 'cancel-failed';
49
+ /** One snapshot of an attempt. */
50
+ export interface SubscribeState {
51
+ readonly phase: SubscribePhase;
52
+ /** Set when `phase` is `failed`. */
53
+ readonly failure?: SubscribeFailure;
54
+ /** The task representing the subscription, once known. */
55
+ readonly taskId?: string;
56
+ /** The Session created for the attempt, so a failed attempt can say where it went. */
57
+ readonly sessionId?: string;
58
+ }
59
+ /** Everything the orchestration needs from the outside world. */
60
+ export interface SubscribeDeps<T extends SubscribedTaskView> {
61
+ /** Read the authoritative task list. */
62
+ readonly listTasks: () => Promise<readonly T[]>;
63
+ /** Create a Session and return its id. */
64
+ readonly createSession: () => Promise<string>;
65
+ /** Send one text prompt into a Session. Resolves false when it was refused. */
66
+ readonly sendPrompt: (sessionId: string, text: string) => Promise<boolean>;
67
+ /** Remove one task (unsubscribe). */
68
+ /**
69
+ * Remove one task. The owning Session travels with the id because a Schedule belongs
70
+ * to the Session that created it, and the Host routes deletion through that binding.
71
+ */
72
+ readonly removeTask: (taskId: string, sessionId: string) => Promise<boolean>;
73
+ /**
74
+ * The Session's own failure, once its turn has one; undefined while it is still working.
75
+ *
76
+ * A turn that ended in an error (an expired provider key, a 5xx from the route, a model
77
+ * that never called the tool) can never produce a task, so waiting out the whole budget
78
+ * only delays a failure the Session already knows about. Optional so a caller without a
79
+ * session face keeps the plain poll; the Host wiring supplies it.
80
+ */
81
+ readonly readSessionFailure?: (sessionId: string) => Promise<string | undefined>;
82
+ /** Wait for one poll interval; injected so tests do not sleep. */
83
+ readonly sleep: (ms: number) => Promise<void>;
84
+ /** Called on every state change, so the dialog can render progress. */
85
+ readonly onChange?: (state: SubscribeState) => void;
86
+ }
87
+ /** How a subscribe attempt ended. */
88
+ export interface SubscribeOutcome {
89
+ readonly ok: boolean;
90
+ /** Present on success: the task that now exists. */
91
+ readonly taskId?: string;
92
+ /** Present on failure: why. */
93
+ readonly failure?: SubscribeFailure;
94
+ /** The Session created, when one was; useful for a failure message. */
95
+ readonly sessionId?: string;
96
+ /** Set when the Session was opened immediately (no verification wait). */
97
+ readonly opened?: boolean;
98
+ /**
99
+ * Present on `session-failed`: the message the Session's turn ended with, so a caller can
100
+ * show the real cause instead of pointing the reader at the new Session.
101
+ */
102
+ readonly detail?: string;
103
+ }
104
+ /**
105
+ * How long the verification waits for the model to create the task.
106
+ *
107
+ * The prompt has to reach the model, the model has to call `schedule_create`, and the catalog
108
+ * has to reflect it. Measured on 2026-10-23 against `dashscope/deepseek-v4.1-flash-1017`: the
109
+ * tool call landed 29 s after the prompt and the task was visible by 33-39 s. The previous
110
+ * 10 x 1 s budget therefore reported "not created" for subscriptions that succeeded moments
111
+ * later, which is the false negative the dialog used to show.
112
+ */
113
+ export declare const VERIFY_ATTEMPTS = 30;
114
+ export declare const VERIFY_INTERVAL_MS = 3000;
115
+ /**
116
+ * The exact instruction handed to the new Session.
117
+ *
118
+ * Written as a repeatable order rather than a conversation opener: it names the tool,
119
+ * gives the cron, supplies the standing instruction, and quotes the marker line to
120
+ * reproduce. The marker is quoted as literal text because it is what later recognition
121
+ * keys on; a paraphrase would still be caught by the title fallback, but the reliable
122
+ * path is worth asking for directly.
123
+ *
124
+ * @param workflow - the workflow being subscribed.
125
+ * @param cron - the expression the reader chose.
126
+ * @returns the prompt text.
127
+ */
128
+ export declare function subscriptionPrompt(workflow: WorkflowDefinition, cron: string): string;
129
+ /**
130
+ * Subscribe one workflow.
131
+ *
132
+ * The sequence is: refuse early if already subscribed, create a Session, ask it to
133
+ * schedule the work, then prove the task exists. Success is only ever reported from a
134
+ * verified read.
135
+ *
136
+ * @param workflow - workflow to subscribe.
137
+ * @param cron - the expression chosen in the dialog.
138
+ * @param deps - injected Host operations.
139
+ * @returns the outcome, including the created task id on success.
140
+ */
141
+ export declare function subscribeWorkflow<T extends SubscribedTaskView>(workflow: WorkflowDefinition, cron: string, deps: SubscribeDeps<T>): Promise<SubscribeOutcome>;
142
+ /**
143
+ * Everything the immediate-open subscription needs.
144
+ *
145
+ * Deliberately no `sleep`/`readSessionFailure`: this path never waits for the task. It
146
+ * ends as soon as the prompt is accepted and the Session is opened; the turn continues
147
+ * in the conversation and the plaza re-reads the catalog when the reader returns.
148
+ */
149
+ export interface CreateSubscriptionDeps<T extends SubscribedTaskView> extends Omit<SubscribeDeps<T>, 'sleep' | 'readSessionFailure'> {
150
+ /** Install the chosen model on the new Session, before the prompt is sent. */
151
+ readonly selectModel?: (sessionId: string, selection: WorkflowModelSelection) => Promise<boolean>;
152
+ /** Focus the new Session immediately. */
153
+ readonly openSession?: (sessionId: string) => void;
154
+ }
155
+ /**
156
+ * Create the subscription Session and open it without waiting for the task.
157
+ *
158
+ * The old flow verified `schedule_create` inside the dialog, which kept "订阅中…" on
159
+ * screen for up to 90 s and made a slow model look like a failed subscription. This
160
+ * path does only what the reader asked for: create the Session, install the chosen
161
+ * model, deliver the scheduling prompt, then open the conversation. The task appears
162
+ * there; the plaza's next catalog read turns the card into 「退订」.
163
+ *
164
+ * @param workflow - workflow to subscribe.
165
+ * @param cron - the frequency chosen in the dialog.
166
+ * @param model - the selected model, or undefined to keep the workspace default.
167
+ * @param deps - injected Host operations.
168
+ * @returns `ok` once the prompt was accepted; no verification wait.
169
+ */
170
+ export declare function createSubscriptionSession<T extends SubscribedTaskView>(workflow: WorkflowDefinition, cron: string, model: WorkflowModelSelection | undefined, deps: CreateSubscriptionDeps<T>): Promise<SubscribeOutcome>;
171
+ /**
172
+ * Unsubscribe one workflow by removing the task that represents it.
173
+ *
174
+ * @param taskId - task to remove, from a previous {@link findSubscription}.
175
+ * @param sessionId - Session that owns the task.
176
+ * @param deps - injected Host operations (only `removeTask` is used).
177
+ * @returns whether the task was removed.
178
+ */
179
+ export declare function unsubscribeWorkflow<T extends SubscribedTaskView>(taskId: string, sessionId: string, deps: Pick<SubscribeDeps<T>, 'removeTask' | 'onChange'>): Promise<boolean>;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Embedding provider calls for optional vector retrieval.
3
+ *
4
+ * The provider is deliberately small and deployment-owned: an OpenAI-compatible
5
+ * `/v1/embeddings` endpoint or an Ollama `/api/embeddings` endpoint. The API key is
6
+ * never part of configuration or a stored record; callers pass the resolved value for
7
+ * one request, and the settings page only ever shows the credential reference.
8
+ *
9
+ * @module @sparkelf/dsh-knowledge-base/embeddings
10
+ */
11
+ /** Providers this plugin can call. */
12
+ export type EmbeddingProvider = 'openai-compatible' | 'ollama';
13
+ /** Vector retrieval settings. Every switch defaults off. */
14
+ export interface VectorRetrievalConfig {
15
+ /** Whether vector retrieval and background embedding are enabled. */
16
+ enabled: boolean;
17
+ /** Embedding API shape. */
18
+ provider: EmbeddingProvider;
19
+ /** Provider base URL, without a trailing slash requirement. */
20
+ baseUrl: string;
21
+ /** Embedding model name. */
22
+ model: string;
23
+ /** Expected vector width; `0` accepts and records the provider's width. */
24
+ dimensions: number;
25
+ /** Credential-store reference for an API key; never the key itself. */
26
+ credentialRef: string;
27
+ /** RRF constant used by hybrid fusion. */
28
+ rrfK: number;
29
+ /** How many FTS and vector candidates each side contributes before fusion. */
30
+ candidateK: number;
31
+ /** RRF weight of the FTS component. */
32
+ ftsWeight: number;
33
+ /** RRF weight of the vector component. */
34
+ vectorWeight: number;
35
+ }
36
+ /** Defaults: no network calls, no vector storage, and the standard RRF shape. */
37
+ export declare const DEFAULT_VECTOR_RETRIEVAL: VectorRetrievalConfig;
38
+ /** A structured provider failure that callers can report without exposing a key. */
39
+ export declare class EmbeddingProviderError extends Error {
40
+ readonly status?: number | undefined;
41
+ readonly name = "EmbeddingProviderError";
42
+ constructor(message: string, status?: number | undefined);
43
+ }
44
+ /** Normalize a base URL and append the provider's embedding path. */
45
+ export declare function embeddingEndpoint(config: Pick<VectorRetrievalConfig, 'provider' | 'baseUrl'>): string;
46
+ /**
47
+ * Embed one batch of texts.
48
+ *
49
+ * OpenAI-compatible providers accept a batch and return one row per input. Ollama's
50
+ * documented `/api/embeddings` endpoint is singular, so its inputs are requested in
51
+ * order; callers still control the batch size and keep provider concurrency bounded.
52
+ */
53
+ export declare function embedTexts(config: Pick<VectorRetrievalConfig, 'provider' | 'baseUrl' | 'model' | 'dimensions'>, texts: readonly string[], apiKey?: string): Promise<number[][]>;
54
+ /**
55
+ * Validate one stored retrieval object.
56
+ *
57
+ * Unknown values fall back field by field rather than invalidating the group, so a
58
+ * hand-edited file cannot switch a provider on with a half-valid configuration.
59
+ * @param raw - parsed settings value.
60
+ * @returns A complete, safe vector configuration.
61
+ */
62
+ export declare function normalizeVectorRetrieval(raw: unknown): VectorRetrievalConfig;
@@ -0,0 +1,25 @@
1
+ /** Record a path this process is about to write, so the watcher can ignore its event. */
2
+ export declare const markInternalMutation: (relPath: string) => void;
3
+ /** Whether an event belongs to a write this process already audited. */
4
+ export declare const isInternalMutation: (relPath: string, now?: number) => boolean;
5
+ /** Clear the suppression map between tests. */
6
+ export declare const resetInternalMutationsForTests: () => void;
7
+ export type ExternalChangeDetail = 'updated' | 'renamed' | 'deleted';
8
+ export interface ExternalChangeWatcherOptions {
9
+ /** The authoritative tree, not the per-user view. */
10
+ actualRoot: string;
11
+ /** Called once per debounced path. May be async; rejections are logged, not thrown. */
12
+ onChange: (relPath: string, detail: ExternalChangeDetail) => void | Promise<void>;
13
+ /** Optional warning sink; defaults to silent. */
14
+ log?: (message: string) => void;
15
+ debounceMs?: number;
16
+ retryMs?: number;
17
+ }
18
+ /**
19
+ * Start observing the authoritative tree.
20
+ *
21
+ * Returns a disposer. The watcher retries when the root does not exist yet (the
22
+ * plugin can load before a workspace provisions its tree) and is closed on dispose.
23
+ * `persistent: false` keeps the watcher from holding the process alive on its own.
24
+ */
25
+ export declare const startExternalChangeWatcher: (options: ExternalChangeWatcherOptions) => (() => void);
@@ -33,6 +33,34 @@
33
33
  export declare const EXTRACTABLE_EXTENSIONS: readonly [".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".odt", ".ods", ".odp", ".rtf"];
34
34
  /** One extractable extension. */
35
35
  export type ExtractableExtension = typeof EXTRACTABLE_EXTENSIONS[number];
36
+ /**
37
+ * Formats that are already plain text, so there is nothing to extract.
38
+ *
39
+ * A `.md` document IS the Markdown an extraction would produce. Asking for one to be
40
+ * extracted is not a failure and not an unsupported format: the honest answer is that no
41
+ * work is needed. Without this list such a request fell through to "this type has no
42
+ * extractor", which told a reader their own Markdown file was unreadable.
43
+ */
44
+ export declare const MARKDOWN_EXTENSIONS: readonly [".md", ".markdown", ".mdown", ".mkd"];
45
+ /**
46
+ * Whether this path is already Markdown.
47
+ * @param relPath - the document's path.
48
+ * @returns Whether no extraction is needed.
49
+ */
50
+ export declare function isAlreadyMarkdown(relPath: string): boolean;
51
+ /** One MinerU layout block, normalized for the SQLite provenance tables. */
52
+ export interface MineruBlock {
53
+ page: number;
54
+ bbox: [number, number, number, number];
55
+ text: string;
56
+ type: string;
57
+ blockIndex: number;
58
+ occurrence: number;
59
+ confidence?: number;
60
+ pageWidth?: number;
61
+ pageHeight?: number;
62
+ coordSpace: 'normalized' | 'points';
63
+ }
36
64
  /** How one request to the extraction authorities ended. */
37
65
  export interface ExtractResult {
38
66
  /** Markdown text, when extraction produced any. */
@@ -48,6 +76,8 @@ export interface ExtractResult {
48
76
  via?: 'mineru' | 'mineru-ocr' | 'libreoffice' | 'python' | 'native';
49
77
  /** Why no markdown was produced. Present whenever {@link markdown} is absent. */
50
78
  reason?: string;
79
+ /** Layout blocks from MinerU (`middle_json`), used only for provenance metadata. */
80
+ blocks?: MineruBlock[];
51
81
  }
52
82
  /**
53
83
  * Whether this path names a document worth extracting.
@@ -107,6 +137,8 @@ export declare function prefersRemoteParser(ext: string): boolean;
107
137
  * @returns true when the answer is short enough that a classifier mistake is likely.
108
138
  */
109
139
  export declare function looksTooThinToSearch(markdown: string): boolean;
140
+ /** Parse MinerU's middle_json pages/blocks into provenance rows. */
141
+ export declare function parseMineruBlocks(value: unknown): MineruBlock[];
110
142
  /** Services and commands extraction may use, as configured for this deployment. */
111
143
  export interface ExtractOptions {
112
144
  /** MinerU base URL. Empty disables the remote parser. */
@@ -17,13 +17,25 @@
17
17
  * @module @sparkelf/dsh-knowledge-base/extractionQueue
18
18
  */
19
19
  /** Lifecycle of one extraction task. */
20
- export type ExtractionState = 'pending' | 'running' | 'succeeded' | 'failed' | 'unsupported';
20
+ export type ExtractionState = 'pending' | 'running' | 'succeeded' | 'failed' | 'unsupported'
21
+ /**
22
+ * The document is already Markdown, so no extraction was needed.
23
+ *
24
+ * Distinct from `succeeded` (a sibling WAS written) and from `unsupported` (no reader
25
+ * exists for the format). Reporting a `.md` file as `unsupported` told a reader their own
26
+ * Markdown was unreadable, which is both wrong and alarming.
27
+ */
28
+ | 'already-markdown';
29
+ /** The visible stage after the parser accepted a document. */
30
+ export type ExtractionStep = 'parse' | 'chunk' | 'index' | 'done';
21
31
  /** One extraction task, as a reader observes it. */
22
32
  export interface ExtractionTask {
23
33
  /** Knowledge-relative path of the SOURCE document. */
24
34
  relPath: string;
25
35
  /** Where the task is in its lifecycle. */
26
36
  state: ExtractionState;
37
+ /** Current Agentic RAG stage: parse → chunk → index → done. */
38
+ step?: ExtractionStep;
27
39
  /** When the task was accepted, ISO-8601. */
28
40
  queuedAt: string;
29
41
  /** When the run started, ISO-8601. */
@@ -77,7 +89,7 @@ export interface ExtractionQueueOptions {
77
89
  * @param mineruBaseUrl - the caller's own parser address, when set.
78
90
  * @returns the terminal state of the run.
79
91
  */
80
- run: (relPath: string, mineruBaseUrl: string | undefined) => Promise<ExtractionOutcome>;
92
+ run: (relPath: string, mineruBaseUrl: string | undefined, reportStep?: (step: ExtractionStep) => void) => Promise<ExtractionOutcome>;
81
93
  /**
82
94
  * Whether this path can be extracted at all.
83
95
  *
@@ -85,6 +97,15 @@ export interface ExtractionQueueOptions {
85
97
  * occupying the single worker slot to discover it has no reader.
86
98
  */
87
99
  isExtractable: (relPath: string) => boolean;
100
+ /**
101
+ * Whether this path is already Markdown, so no extraction is needed.
102
+ *
103
+ * Separate from `isExtractable` because the two answer different questions: `false`
104
+ * from `isExtractable` means "no reader exists" (a failure), while `true` here means
105
+ * "there is nothing to read" (a success). Collapsing them is what reported a reader's
106
+ * own `.md` file as an unreadable format.
107
+ */
108
+ isAlreadyMarkdown: (relPath: string) => boolean;
88
109
  }
89
110
  /** One extraction queue. One instance per plugin registration. */
90
111
  export interface ExtractionQueue {