@sparkelf/dsh-knowledge-base 0.1.4 → 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 (60) hide show
  1. package/README.md +129 -2
  2. package/SKILL.md +86 -18
  3. package/lib/client.js +13915 -8694
  4. package/lib/client.js.map +1 -1
  5. package/lib/index.js +8098 -256
  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/dropTargetPath.d.ts +33 -0
  18. package/lib/types/client/extractionHistory.d.ts +43 -0
  19. package/lib/types/client/index.d.ts +32 -14
  20. package/lib/types/client/listingFilter.d.ts +58 -0
  21. package/lib/types/client/locales.d.ts +126 -0
  22. package/lib/types/client/openInSidebar.d.ts +2 -2
  23. package/lib/types/client/progressRules.d.ts +198 -17
  24. package/lib/types/client/sanitizeExplorerTabs.d.ts +20 -0
  25. package/lib/types/client/settingsGate.d.ts +65 -0
  26. package/lib/types/client/uploadTransport.d.ts +65 -0
  27. package/lib/types/client/useTreeSort.d.ts +72 -0
  28. package/lib/types/client/workflows/WorkflowPlaza.d.ts +146 -0
  29. package/lib/types/client/workflows/cron.d.ts +125 -0
  30. package/lib/types/client/workflows/dedupe.d.ts +70 -0
  31. package/lib/types/client/workflows/deps.d.ts +101 -0
  32. package/lib/types/client/workflows/modelChoice.d.ts +106 -0
  33. package/lib/types/client/workflows/published.d.ts +43 -0
  34. package/lib/types/client/workflows/registry.d.ts +98 -0
  35. package/lib/types/client/workflows/subscribe.d.ts +179 -0
  36. package/lib/types/embeddings.d.ts +62 -0
  37. package/lib/types/externalChange.d.ts +25 -0
  38. package/lib/types/extract.d.ts +32 -0
  39. package/lib/types/extractionQueue.d.ts +23 -2
  40. package/lib/types/ftsIndex.d.ts +497 -0
  41. package/lib/types/index.d.ts +266 -8
  42. package/lib/types/initialize.d.ts +105 -0
  43. package/lib/types/knowledgeTools.d.ts +239 -0
  44. package/lib/types/localStoragePath.d.ts +6 -0
  45. package/lib/types/localStorageSettings.d.ts +33 -0
  46. package/lib/types/managementTools.d.ts +184 -0
  47. package/lib/types/outlineGuard.d.ts +119 -0
  48. package/lib/types/outlineText.d.ts +134 -0
  49. package/lib/types/protocol.d.ts +34 -0
  50. package/lib/types/scopeAliases.d.ts +20 -0
  51. package/lib/types/trash.d.ts +14 -0
  52. package/lib/types/vectorIndex.d.ts +113 -0
  53. package/lib/types/views.d.ts +33 -1
  54. package/lib/types/workflowPublish.d.ts +114 -0
  55. package/package.json +7 -7
  56. package/skills/kb-summarize/SKILL.md +158 -0
  57. package/skills/kb-summarize/i18n/zh.json +10 -0
  58. package/skills/kb-vectorize/SKILL.md +57 -0
  59. package//344/270/207/347/233/270AI/344/275/277/347/224/250/346/225/231/347/250/213.html +294 -0
  60. package/lib/types/client/KnowledgeProgressPanel.d.ts +0 -53
@@ -0,0 +1,65 @@
1
+ /**
2
+ * The transport an upload actually uses.
3
+ *
4
+ * Uploading with `fetch` is one request with no visibility into it: the browser reports
5
+ * nothing until the whole body has gone, so a progress bar built on it can only be a
6
+ * spinner. `XMLHttpRequest` exposes `upload.onprogress`, which is the only browser API
7
+ * that reports bytes sent -- that is why this exists despite `fetch` being the modern
8
+ * choice.
9
+ *
10
+ * Everything about the request other than the transport is unchanged: same URL, same
11
+ * JSON body, same content type, same failure codes. The response is returned as a small
12
+ * result object rather than a `Response`, because `XHR` does not produce one and every
13
+ * caller here only needs "did it work, with which status, and what did the body say".
14
+ *
15
+ * @module @sparkelf/dsh-knowledge-base/client
16
+ */
17
+ /** What one upload attempt reported. */
18
+ export interface UploadAttempt {
19
+ /** HTTP status, or 0 when the request never completed (offline, DNS, abort). */
20
+ status: number;
21
+ /** Parsed JSON body when it parsed, else undefined. */
22
+ body: unknown;
23
+ /** Why the attempt did not complete, when the transport itself failed. */
24
+ transportError?: string;
25
+ }
26
+ /**
27
+ * Report progress no more often than this.
28
+ *
29
+ * `upload.onprogress` can fire hundreds of times for a large file, and each event that
30
+ * reaches React is a render of the whole task list. 100 ms keeps the bar smooth (10
31
+ * updates a second is well past what an eye resolves) while bounding the work to ten
32
+ * renders per second instead of hundreds.
33
+ */
34
+ export declare const PROGRESS_INTERVAL_MS = 100;
35
+ /**
36
+ * Compute the integer percentage of bytes sent.
37
+ *
38
+ * Returns undefined when the total is unknown, which is a real case: a body sent without
39
+ * a `Content-Length` reports `lengthComputable: false`, and inventing a percentage from
40
+ * a missing denominator is exactly the dishonesty this module avoids.
41
+ * @param loaded - bytes sent so far.
42
+ * @param total - total bytes, when the browser knows it.
43
+ * @param computable - whether the browser reported a usable total.
44
+ * @returns 0-100, or undefined when it cannot be known.
45
+ */
46
+ export declare function uploadPercent(loaded: number, total: number, computable: boolean): number | undefined;
47
+ /**
48
+ * Whether enough time has passed to report another progress sample.
49
+ *
50
+ * Kept as a pure predicate so the throttle can be tested without a timer or a network.
51
+ * @param now - the current timestamp in milliseconds.
52
+ * @param last - when the last sample was reported, or undefined if none was.
53
+ * @returns Whether this sample should be reported.
54
+ */
55
+ export declare function shouldReportProgress(now: number, last: number | undefined): boolean;
56
+ /**
57
+ * Upload one JSON body, reporting byte progress as it goes.
58
+ * @param url - the route to post to.
59
+ * @param payload - the JSON-serialisable body.
60
+ * @param onProgress - called with 0-100 while sending; only called when a percentage is
61
+ * actually known, and throttled to {@link PROGRESS_INTERVAL_MS}.
62
+ * @returns The attempt's outcome. Never rejects: a transport failure is a result, because
63
+ * the caller has to show it either way.
64
+ */
65
+ export declare function uploadJsonWithProgress(url: string, payload: unknown, onProgress: (percent: number) => void): Promise<UploadAttempt>;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * The knowledge tree's row ordering, remembered per browser.
3
+ *
4
+ * Sorting is a VIEW preference: it decides how one reader scans a list, not what is in the
5
+ * tree. That makes the browser the right place for it -- there is nothing to share with
6
+ * another machine, and a round trip before the list reorders would make a one-click control
7
+ * feel broken. (The list/grid layout, by contrast, lives in the view directory because it
8
+ * was built as a document-level preference; sorting deliberately does not copy that.)
9
+ *
10
+ * The stored key carries the panel id so two different knowledge panels -- or two
11
+ * deployments mounted on one origin -- cannot read each other's choice.
12
+ *
13
+ * @module @sparkelf/dsh-knowledge-base/client
14
+ */
15
+ /**
16
+ * The orderings the tree offers.
17
+ *
18
+ * `mtime` first because it is the default: a folder is usually opened to find what was
19
+ * just worked on. `name` is the host's own order, `type` groups by extension, `size`
20
+ * surfaces the large documents.
21
+ */
22
+ export type TreeSortKey = 'mtime' | 'name' | 'type' | 'size';
23
+ /** Every ordering, in the order the menu lists them. */
24
+ export declare const TREE_SORT_KEYS: readonly TreeSortKey[];
25
+ /** Most recently modified first. */
26
+ export declare const DEFAULT_TREE_SORT: TreeSortKey;
27
+ /** What the rail gets back. */
28
+ export interface TreeSortStore {
29
+ /** The current ordering. */
30
+ key: TreeSortKey;
31
+ /** Choose another ordering; written through to storage. */
32
+ setKey: (key: TreeSortKey) => void;
33
+ }
34
+ /**
35
+ * Whether a value is one of the orderings.
36
+ * @param value - the candidate.
37
+ * @returns Whether it is a known key.
38
+ */
39
+ export declare function isTreeSortKey(value: unknown): value is TreeSortKey;
40
+ /**
41
+ * Build the storage key for one panel.
42
+ *
43
+ * Exported so the test can assert the shape rather than duplicate the string.
44
+ * @param panelId - the mounting panel's identity, may be empty.
45
+ * @returns The localStorage key.
46
+ */
47
+ export declare function treeSortStorageKey(panelId: string): string;
48
+ /**
49
+ * Read the stored ordering.
50
+ *
51
+ * Any failure -- no storage (a privacy mode, a server render), an unknown value from an
52
+ * older build -- answers the default rather than throwing: a preference that cannot be read
53
+ * must not take the tree down with it.
54
+ * @param panelId - the mounting panel's identity.
55
+ * @returns The stored ordering, or the default.
56
+ */
57
+ export declare function readTreeSort(panelId: string): TreeSortKey;
58
+ /**
59
+ * Persist the ordering.
60
+ *
61
+ * A write failure is swallowed for the same reason: the reader's click should still take
62
+ * effect for this session even when the browser refuses to remember it.
63
+ * @param panelId - the mounting panel's identity.
64
+ * @param key - the ordering to store.
65
+ */
66
+ export declare function writeTreeSort(panelId: string, key: TreeSortKey): void;
67
+ /**
68
+ * Read and write the tree's ordering.
69
+ * @param panelId - the mounting panel's identity, used to scope the stored key.
70
+ * @returns The store.
71
+ */
72
+ export declare function useTreeSort(panelId: string): TreeSortStore;
@@ -0,0 +1,146 @@
1
+ /**
2
+ * The 「工作流广场」 body: built-in workflow templates and their subscribe flow.
3
+ *
4
+ * The plaza is a TEMPLATE SHELF. It renders cards from the static registry and turns a
5
+ * subscription into an ordinary DSH Schedule through the existing agent tool, following
6
+ * the same path the product's own 「新建任务」 button uses (create a Session, ask the model
7
+ * to schedule the work, confirm the task exists). Nothing here schedules anything
8
+ * itself, and nothing here talks to DataOps: a workflow that touches knowledge
9
+ * documents goes through the knowledge API when it runs, where permissions already
10
+ * apply.
11
+ *
12
+ * @module @sparkelf/dsh-knowledge-base/client/workflows/WorkflowPlaza
13
+ */
14
+ import { type ReactNode } from 'react';
15
+ import { type WorkflowDefinition } from './registry.ts';
16
+ import { type SubscribedTaskView } from './dedupe.ts';
17
+ import { type SubscribeFailure } from './subscribe.ts';
18
+ import { type PublishedWorkflowView } from './published.ts';
19
+ import { type WorkflowModelCatalogView, type WorkflowModelSelection } from './modelChoice.ts';
20
+ /** Copy the plaza needs, injected so this module owns no dictionary. */
21
+ export interface WorkflowPlazaCopy {
22
+ readonly title: string;
23
+ readonly subscribe: string;
24
+ readonly subscribed: string;
25
+ readonly unsubscribe: string;
26
+ readonly frequency: string;
27
+ readonly daily: string;
28
+ readonly weekly: string;
29
+ readonly monthly: string;
30
+ readonly custom: string;
31
+ readonly cronLabel: string;
32
+ readonly cronInvalid: string;
33
+ /** Labels for the two interval shortcuts. */
34
+ readonly everyHours: string;
35
+ readonly everyMinutes: string;
36
+ /** Count-field labels, one per interval unit. */
37
+ readonly hoursCount: string;
38
+ readonly minutesCount: string;
39
+ /** Reasons a cron expression cannot be submitted, by rejection code. */
40
+ readonly cronReason: (reason: string) => string;
41
+ /** Range message for an out-of-bounds or non-integer interval count. */
42
+ readonly intervalInvalid: (min: number, max: number) => string;
43
+ readonly subscribing: string;
44
+ readonly plazaTab: string;
45
+ readonly reviewTab: string;
46
+ readonly mineTab: string;
47
+ readonly mineEmpty: string;
48
+ readonly mineUpdated: string;
49
+ readonly mineReviewNote: string;
50
+ readonly mineResubmit: string;
51
+ readonly mineUnpublish: string;
52
+ readonly mineDetails: string;
53
+ readonly mineActionFailed: string;
54
+ readonly mineStatusPending: string;
55
+ readonly mineStatusApproved: string;
56
+ readonly mineStatusRejected: string;
57
+ readonly mineStatusUnpublished: string;
58
+ readonly mineScopeGlobal: string;
59
+ readonly mineScopePrivate: string;
60
+ readonly mineScopeDepartment: (name: string) => string;
61
+ readonly reviewEmpty: string;
62
+ readonly reviewLoading: string;
63
+ readonly reviewFailed: string;
64
+ readonly reviewAuthor: string;
65
+ readonly reviewCreated: string;
66
+ readonly reviewPromptLabel: string;
67
+ readonly reviewNotePlaceholder: string;
68
+ readonly reviewApprove: string;
69
+ readonly reviewReject: string;
70
+ readonly model: string;
71
+ readonly modelLoading: string;
72
+ readonly modelUnavailable: string;
73
+ readonly modelDefault: string;
74
+ readonly effort: string;
75
+ readonly dialogTitle: string;
76
+ readonly dialogConfirm: string;
77
+ readonly dialogCancel: string;
78
+ readonly retry: string;
79
+ readonly alreadySubscribed: string;
80
+ readonly failure: (reason: string) => string;
81
+ readonly reason: (failure: SubscribeFailure) => string;
82
+ /** Card title and description, resolved per workflow id by the host. */
83
+ readonly cardTitle: (workflow: WorkflowDefinition) => string;
84
+ readonly cardDescription: (workflow: WorkflowDefinition) => string;
85
+ }
86
+ /** What the plaza needs from the Host, all injected so the component stays testable. */
87
+ export interface WorkflowPlazaDeps {
88
+ /** Read the authoritative task list. */
89
+ readonly listTasks: () => Promise<readonly SubscribedTaskView[]>;
90
+ /** Create a Session and return its id. */
91
+ readonly createSession: () => Promise<string>;
92
+ /** Send one prompt into a Session. */
93
+ readonly sendPrompt: (sessionId: string, text: string) => Promise<boolean>;
94
+ /** Remove one task. */
95
+ readonly removeTask: (taskId: string, sessionId: string) => Promise<boolean>;
96
+ /** The Session's own turn failure, so a failed attempt states the real cause. */
97
+ readonly readSessionFailure?: (sessionId: string) => Promise<string | undefined>;
98
+ /** Open a Session, so a failed attempt can be inspected. */
99
+ readonly openSession?: (sessionId: string) => void;
100
+ /** Load the Host model catalog; omitted in tests that do not exercise model choice. */
101
+ readonly loadModels?: () => Promise<WorkflowModelCatalogView>;
102
+ /** Install the chosen model on a newly created Session. */
103
+ readonly selectModel?: (sessionId: string, selection: WorkflowModelSelection) => Promise<boolean>;
104
+ /** Persist the choice for the next dialog open. */
105
+ readonly rememberModel?: (selection: WorkflowModelSelection) => void;
106
+ /** Read the previous choice; defaults to localStorage. */
107
+ readonly readRememberedModel?: () => WorkflowModelSelection | undefined;
108
+ /** Approved published workflows to append to the built-in shelf. */
109
+ readonly loadPublished?: () => Promise<readonly PublishedWorkflowView[]>;
110
+ /** Whether this caller may see the review tab. */
111
+ readonly loadReviewAccess?: () => Promise<boolean>;
112
+ /** Pending workflows for the review tab. */
113
+ readonly loadPending?: () => Promise<readonly PublishedWorkflowView[]>;
114
+ /** Approve or reject one pending workflow. */
115
+ readonly reviewPublished?: (id: string, decision: 'approve' | 'reject', note?: string) => Promise<boolean>;
116
+ /** The caller's own shared workflows (all statuses). */
117
+ readonly loadMine?: () => Promise<readonly PublishedWorkflowView[]>;
118
+ /** Unpublish one of the caller's approved workflows. */
119
+ readonly unpublishMine?: (id: string) => Promise<boolean>;
120
+ /** Resubmit one rejected/unpublished workflow for review. */
121
+ readonly resubmitMine?: (id: string) => Promise<boolean>;
122
+ }
123
+ /** Props for the plaza body. */
124
+ export interface WorkflowPlazaProps {
125
+ readonly copy: WorkflowPlazaCopy;
126
+ readonly deps: WorkflowPlazaDeps;
127
+ /**
128
+ * Child-slot renderer supplied by the slot system when this component's registration
129
+ * declares `workflow-plaza.actions`. Tests and non-slot uses omit it; the header
130
+ * actions simply do not render.
131
+ */
132
+ readonly renderSlot?: (key: 'workflow-plaza.actions', owner: Record<string, never>) => ReactNode;
133
+ }
134
+ /** What the slot's `inject` supplies: the copy and the Host wiring. */
135
+ export type WorkflowPlazaInjected = WorkflowPlazaProps;
136
+ /** The card grid plus the subscribe dialog. */
137
+ export declare function WorkflowPlaza({ copy, deps, renderSlot }: WorkflowPlazaProps): ReactNode;
138
+ /**
139
+ * The plaza as a standalone panel page.
140
+ *
141
+ * The automation-task page's `schedule.manager.tabs` seat is contributed at runtime;
142
+ * on a Host that does not declare that child slot the registration is simply inert.
143
+ * This page gives the same body a sidebar-reachable home on every Host, so the feature
144
+ * is never invisible just because the surrounding page is older.
145
+ */
146
+ export declare function WorkflowPlazaPage({ copy, deps, renderSlot }: WorkflowPlazaInjected): ReactNode;
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Cron handling for the workflow plaza: shortcut mapping and shape checking ONLY.
3
+ *
4
+ * This module deliberately does NOT parse or interpret cron. The DSH Schedule owns the
5
+ * expression's meaning, and a second implementation here would be a second opinion
6
+ * about when a task runs -- exactly the "two answers for one fact" defect this codebase
7
+ * keeps removing. What the dialog needs is narrower:
8
+ *
9
+ * - map a frequency shortcut to the expression it stands for, and
10
+ * - tell the reader whether what they typed has the right SHAPE, so a typo is caught
11
+ * while they are still looking at the field instead of failing a minute later.
12
+ *
13
+ * Anything beyond that is handed to the Schedule verbatim.
14
+ *
15
+ * @module @sparkelf/dsh-knowledge-base/client/workflows/cron
16
+ */
17
+ /** Number of space-separated fields a standard five-field cron expression has. */
18
+ export declare const CRON_FIELD_COUNT = 5;
19
+ /** Why a custom expression was rejected, or that it is usable. */
20
+ export type CronCheck = {
21
+ readonly ok: true;
22
+ readonly cron: string;
23
+ } | {
24
+ readonly ok: false;
25
+ readonly reason: CronRejection;
26
+ };
27
+ /** Why an expression cannot be submitted. */
28
+ export type CronRejection = 'empty' | 'fieldCount'
29
+ /** An interval count that is not a whole number. */
30
+ | 'notInteger'
31
+ /** An interval count outside the range the shortcut allows. */
32
+ | 'outOfRange';
33
+ /**
34
+ * Check the SHAPE of a custom cron expression.
35
+ *
36
+ * Only the field count is checked: it is the mistake a reader actually makes (typing
37
+ * four fields, or pasting a six-field expression with seconds), and it is the one that
38
+ * the Schedule rejects outright. Ranges, steps and names are left alone -- judging those
39
+ * would mean interpreting the expression, which is the Schedule's job.
40
+ *
41
+ * Whitespace is normalised to single spaces so the value stored matches what the reader
42
+ * would see in the task detail.
43
+ *
44
+ * @param raw - exactly what the reader typed.
45
+ * @returns the normalised expression, or the reason it cannot be submitted.
46
+ */
47
+ export declare function checkCustomCron(raw: string): CronCheck;
48
+ /**
49
+ * The expression a frequency shortcut stands for.
50
+ *
51
+ * @param frequencyId - shortcut chosen in the dialog.
52
+ * @param workflow - whose frequencies are offered.
53
+ * @param customRaw - the custom field's text, used only for `custom`.
54
+ * @param intervalRaw - the count field's text, used only for the interval shortcuts.
55
+ * @returns the expression to submit, or undefined when a custom value is not usable yet.
56
+ */
57
+ export declare function resolveCron(frequencyId: string, workflow: {
58
+ readonly frequencies: readonly {
59
+ readonly id: string;
60
+ readonly cron?: string;
61
+ }[];
62
+ }, customRaw: string, intervalRaw?: string): CronCheck;
63
+ /** Which counter an interval shortcut edits. */
64
+ export type IntervalUnit = 'hours' | 'minutes';
65
+ /** The id of the two interval shortcuts, as stored in a workflow definition. */
66
+ export type IntervalFrequencyId = 'everyHours' | 'everyMinutes';
67
+ /** One interval shortcut: its bounds and the count it starts from. */
68
+ export interface IntervalSpec {
69
+ readonly id: IntervalFrequencyId;
70
+ readonly unit: IntervalUnit;
71
+ /** Smallest accepted count, inclusive. */
72
+ readonly min: number;
73
+ /** Largest accepted count, inclusive. */
74
+ readonly max: number;
75
+ /** Count the dialog starts on; never blank, so the field is always usable. */
76
+ readonly defaultCount: number;
77
+ }
78
+ /**
79
+ * The interval shortcuts every built-in workflow offers.
80
+ *
81
+ * Hourly stops at 23 and minutely at 59 because a larger count is not a shorter
82
+ * interval -- it is a different rule, and a 90-minute step would silently mean something
83
+ * the reader did not ask for. Rejecting keeps the generated expression honest.
84
+ *
85
+ * (Cron examples are kept out of these comments on purpose: the two-character sequence
86
+ * that begins a step value also ends a block comment.)
87
+ */
88
+ export declare const INTERVAL_SPECS: Readonly<Record<IntervalFrequencyId, IntervalSpec>>;
89
+ /** Whether a frequency id selects an interval shortcut. */
90
+ export declare function isIntervalFrequency(id: string): id is IntervalFrequencyId;
91
+ /**
92
+ * Build the expression an interval stands for.
93
+ *
94
+ * Hourly pins the minute field to 0, so the task runs on the hour rather than sixty
95
+ * times at an arbitrary offset. Minutely has no such anchor and steps the minute field
96
+ * directly.
97
+ *
98
+ * The count is validated rather than clamped: a reader who types 25 hours gets told the
99
+ * range instead of quietly being given 23, because a silently altered schedule is a
100
+ * schedule they did not choose.
101
+ *
102
+ * @param spec - which shortcut, and the bounds it accepts.
103
+ * @param raw - the count exactly as it appears in the field.
104
+ * @returns the expression, or why the count cannot be used.
105
+ */
106
+ export declare function intervalCron(spec: IntervalSpec, raw: string): CronCheck;
107
+ /** A frequency shortcut as the dialog renders it: id, cron, and a locale key for its label. */
108
+ export interface FrequencyChoice {
109
+ readonly id: string;
110
+ readonly cron: string | undefined;
111
+ readonly labelKey: string;
112
+ }
113
+ /**
114
+ * Build the dialog's frequency radio list for one workflow.
115
+ *
116
+ * @param workflow - whose frequencies to present.
117
+ * @param defaultCron - the workflow's prefilled expression, used for `custom`'s field.
118
+ * @returns one entry per shortcut, in the workflow's declared order.
119
+ */
120
+ export declare function frequencyChoices(workflow: {
121
+ readonly frequencies: readonly {
122
+ readonly id: string;
123
+ readonly cron?: string;
124
+ }[];
125
+ }, defaultCron: string): FrequencyChoice[];
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Deciding whether a workflow is already subscribed.
3
+ *
4
+ * The plaza must not offer a second subscription for a workflow that already has a
5
+ * task, and the answer has to survive a page refresh -- so it is read from the
6
+ * authoritative Schedule list every time, never from component state. A local flag
7
+ * would be a second opinion that starts lying the moment the page reloads.
8
+ *
9
+ * ## Why two ways of recognising a task
10
+ *
11
+ * A subscription is marked by putting `[workflow:<id>]` inside the Schedule's prompt.
12
+ * That marker is written by our own code, but the prompt also passes through a model
13
+ * that is asked to reproduce it verbatim -- and a model sometimes paraphrases. If
14
+ * recognition depended on the marker alone, one paraphrased prompt would make a
15
+ * subscribed workflow look unsubscribed, and the reader could subscribe twice.
16
+ *
17
+ * So recognition is deliberately generous: the marker OR the workflow's own title
18
+ * appearing in the task's prompt/name counts. A false "already subscribed" is a much
19
+ * smaller problem than a duplicate nightly job that reorganises someone's documents.
20
+ *
21
+ * @module @sparkelf/dsh-knowledge-base/client/workflows/dedupe
22
+ */
23
+ import { type WorkflowDefinition } from './registry.ts';
24
+ /** The part of a Schedule task this module needs, so it can be tested without one. */
25
+ export interface SubscribedTaskView {
26
+ /** Task identity, used to unsubscribe. */
27
+ readonly id: string;
28
+ /** Session the task belongs to; deletion is routed through it. */
29
+ readonly sessionId?: string;
30
+ /** The task's prompt, which is where a subscription leaves its mark. */
31
+ readonly prompt?: string;
32
+ /** The task's display name, when the list reports one. */
33
+ readonly name?: string;
34
+ }
35
+ /** One workflow found to be already subscribed. */
36
+ export interface ExistingSubscription<T extends SubscribedTaskView = SubscribedTaskView> {
37
+ /** The workflow id that was found. */
38
+ readonly workflowId: string;
39
+ /** The task that represents it. */
40
+ readonly task: T;
41
+ /**
42
+ * How it was recognised. `marker` is the reliable path; `title` means the marker was
43
+ * lost and the title match carried it. Presented so a reader can be told the
44
+ * subscription was found by name rather than by its tag.
45
+ */
46
+ readonly via: 'marker' | 'title';
47
+ }
48
+ /**
49
+ * Find every workflow already represented among the scheduled tasks.
50
+ *
51
+ * @param tasks - the authoritative task list.
52
+ * @param workflows - workflows the plaza offers.
53
+ * @returns one entry per subscribed workflow, in the workflows' own order.
54
+ */
55
+ export declare function findSubscriptions<T extends SubscribedTaskView>(tasks: readonly T[], workflows: readonly WorkflowDefinition[]): Array<ExistingSubscription<T>>;
56
+ /**
57
+ * Whether one workflow is already subscribed, and by which task.
58
+ *
59
+ * @param workflow - workflow the reader wants to subscribe.
60
+ * @param tasks - the authoritative task list, read immediately before subscribing.
61
+ * @returns the existing subscription, or undefined when there is none.
62
+ */
63
+ export declare function findSubscription<T extends SubscribedTaskView>(workflow: WorkflowDefinition, tasks: readonly T[]): ExistingSubscription<T> | undefined;
64
+ /**
65
+ * Index subscriptions by workflow id, for rendering card state.
66
+ *
67
+ * @param subscriptions - result of {@link findSubscriptions}.
68
+ * @returns workflow id -> subscription, so a card asks one question instead of scanning.
69
+ */
70
+ export declare function subscriptionIndex<T extends SubscribedTaskView>(subscriptions: readonly ExistingSubscription<T>[]): Map<string, ExistingSubscription<T>>;
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Wiring the workflow plaza to the Host's real services.
3
+ *
4
+ * Split out from the component so the component stays a pure view: everything that
5
+ * touches sessions or Schedules lives here, and the orchestration in `subscribe.ts`
6
+ * only ever sees plain functions.
7
+ *
8
+ * ## The asymmetry this module exists to absorb
9
+ *
10
+ * DSH exposes Schedules to the browser as READ and DELETE only; creating one is an
11
+ * agent tool (`schedule_create`). So "is this workflow already subscribed" is answered
12
+ * from `remote.schedule.catalog()`, "unsubscribe" is `remote.schedule.delete`, and
13
+ * "subscribe" is a prompt into a new Session. That is not a shortcut taken here: the
14
+ * task manager page documents the same design ("The page deliberately has no creation
15
+ * form of its own").
16
+ *
17
+ * `remote.schedule.list` is deliberately NOT used: it is scoped to one Session
18
+ * (`{ sessionId }`), so it cannot answer "does any task already represent this
19
+ * workflow". `catalog()` is the cross-Session read and is what deduplication needs.
20
+ *
21
+ * @module @sparkelf/dsh-knowledge-base/client/workflows/deps
22
+ */
23
+ import type { WorkflowPlazaDeps } from './WorkflowPlaza.tsx';
24
+ import type { PublishedWorkflowView } from './published.ts';
25
+ import { type RawModelCatalog, type WorkflowModelSelection } from './modelChoice.ts';
26
+ /** One Schedule row as the catalog reports it, narrowed to what dedupe reads. */
27
+ export interface ScheduleCatalogRow {
28
+ readonly id: string;
29
+ /** Session that owns the task; required to delete it. */
30
+ readonly sessionId?: string;
31
+ /** The standing instruction this task delivers; the marker lives here. */
32
+ readonly prompt?: string;
33
+ /** Human title, when the row carries one. */
34
+ readonly name?: string;
35
+ }
36
+ /** The parts of a remote result this module reads. */
37
+ interface RemoteResultLike<T> {
38
+ readonly ok: boolean;
39
+ readonly value?: T;
40
+ }
41
+ /**
42
+ * The narrow slice of DSH client services this module needs.
43
+ *
44
+ * Typed structurally rather than importing the concrete service classes, so a test can
45
+ * pass plain objects and a Host upgrade that reshapes a class does not silently change
46
+ * what this file promises.
47
+ */
48
+ export interface WorkflowPlazaHost {
49
+ /** Create a Session in the current workspace; resolves its id. */
50
+ readonly createSession: () => Promise<string>;
51
+ /** Send one text prompt into an existing Session; resolves false when refused. */
52
+ readonly sendPrompt: (sessionId: string, text: string) => Promise<boolean>;
53
+ /** Read every retained task, across Sessions. */
54
+ readonly readCatalog: () => Promise<RemoteResultLike<readonly ScheduleCatalogRow[]>>;
55
+ /** Delete one task by id. */
56
+ readonly deleteSchedule: (taskId: string, sessionId: string) => Promise<RemoteResultLike<unknown>>;
57
+ /**
58
+ * Read the Session's own turn failure, once it has one.
59
+ *
60
+ * Optional because only a Host holding the `sessions` service can answer it; a caller
61
+ * without that wiring keeps the plain verification poll.
62
+ */
63
+ readonly readSessionFailure?: (sessionId: string) => Promise<string | undefined>;
64
+ /** Focus an existing Session, used to explain a failed attempt. */
65
+ readonly openSession?: (sessionId: string) => void;
66
+ /** Read the Host model catalog, grouped by provider. */
67
+ readonly readModelCatalog?: () => Promise<RemoteResultLike<RawModelCatalog>>;
68
+ /** Install one model selection on a Session before its first prompt. */
69
+ readonly selectSessionModel?: (sessionId: string, selection: WorkflowModelSelection) => Promise<boolean>;
70
+ /** Approved published workflows. */
71
+ readonly readPublished?: () => Promise<RemoteResultLike<{
72
+ workflows: readonly PublishedWorkflowView[];
73
+ }>>;
74
+ /** Whether the caller may review pending workflows. */
75
+ readonly readReviewAccess?: () => Promise<boolean>;
76
+ /** Pending workflows for the review tab. */
77
+ readonly readPending?: () => Promise<RemoteResultLike<{
78
+ workflows: readonly PublishedWorkflowView[];
79
+ }>>;
80
+ /** Approve or reject one pending workflow. */
81
+ readonly reviewWorkflow?: (id: string, decision: 'approve' | 'reject', note?: string) => Promise<boolean>;
82
+ /** The caller's own shared workflows. */
83
+ readonly readMine?: () => Promise<RemoteResultLike<{
84
+ workflows: readonly PublishedWorkflowView[];
85
+ }>>;
86
+ /** Unpublish one of the caller's workflows. */
87
+ readonly unpublishMine?: (id: string) => Promise<boolean>;
88
+ /** Resubmit one rejected/unpublished workflow. */
89
+ readonly resubmitMine?: (id: string) => Promise<boolean>;
90
+ }
91
+ /**
92
+ * Build the plaza's dependencies from Host services.
93
+ *
94
+ * Both reads are total: a failed remote call becomes an empty-or-unchanged answer the
95
+ * caller can react to, never a thrown error inside a render.
96
+ *
97
+ * @param host - the wiring described by {@link WorkflowPlazaHost}.
98
+ * @returns dependencies the plaza and its orchestration consume.
99
+ */
100
+ export declare function workflowPlazaDeps(host: WorkflowPlazaHost): WorkflowPlazaDeps;
101
+ export {};