@vidge/dsh-agent-hub 0.1.0-rc1

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 (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +133 -0
  3. package/README.zh.md +115 -0
  4. package/cordis.patch.yml +22 -0
  5. package/lib/client.js +1494 -0
  6. package/lib/index.js +5045 -0
  7. package/lib/invariant.js +96 -0
  8. package/lib/types/client/LoopEngineComposerSelect.d.ts +73 -0
  9. package/lib/types/client/LoopEngineSection.d.ts +43 -0
  10. package/lib/types/client/engine-rpc.d.ts +74 -0
  11. package/lib/types/client/index.d.ts +28 -0
  12. package/lib/types/client/locales.d.ts +44 -0
  13. package/lib/types/client/session-location.d.ts +63 -0
  14. package/lib/types/client/store.d.ts +58 -0
  15. package/lib/types/commands.d.ts +69 -0
  16. package/lib/types/driver-core/context-files.d.ts +62 -0
  17. package/lib/types/driver-core/ownership.d.ts +40 -0
  18. package/lib/types/driver-core/permission-knobs.d.ts +26 -0
  19. package/lib/types/driver-core/prompt.d.ts +23 -0
  20. package/lib/types/driver-core/skill-inject.d.ts +59 -0
  21. package/lib/types/engine-claude/agent.d.ts +116 -0
  22. package/lib/types/engine-claude/loop.d.ts +99 -0
  23. package/lib/types/engine-claude/mapping.d.ts +84 -0
  24. package/lib/types/engine-claude/permission.d.ts +41 -0
  25. package/lib/types/engine-claude/process.d.ts +59 -0
  26. package/lib/types/engine-claude/provider-env.d.ts +50 -0
  27. package/lib/types/engine-claude/sdk.d.ts +101 -0
  28. package/lib/types/engine-claude/types.d.ts +28 -0
  29. package/lib/types/engine-codex/agent.d.ts +109 -0
  30. package/lib/types/engine-codex/appserver/client.d.ts +49 -0
  31. package/lib/types/engine-codex/appserver/mapping.d.ts +67 -0
  32. package/lib/types/engine-codex/appserver/thread.d.ts +66 -0
  33. package/lib/types/engine-codex/appserver/types.d.ts +215 -0
  34. package/lib/types/engine-codex/loop.d.ts +92 -0
  35. package/lib/types/engine-codex/permission.d.ts +32 -0
  36. package/lib/types/engine-codex/skills.d.ts +29 -0
  37. package/lib/types/engine-codex/types.d.ts +19 -0
  38. package/lib/types/engine-pi/agent.d.ts +125 -0
  39. package/lib/types/engine-pi/loop.d.ts +96 -0
  40. package/lib/types/engine-pi/permission.d.ts +43 -0
  41. package/lib/types/engine-pi/rpc/client.d.ts +105 -0
  42. package/lib/types/engine-pi/rpc/mapping.d.ts +37 -0
  43. package/lib/types/engine-pi/rpc/types.d.ts +235 -0
  44. package/lib/types/engine-pi/skills.d.ts +55 -0
  45. package/lib/types/engine-pi/types.d.ts +27 -0
  46. package/lib/types/engine-record.d.ts +124 -0
  47. package/lib/types/index.d.ts +138 -0
  48. package/lib/types/invariant.d.ts +23 -0
  49. package/lib/types/llm-compat.d.ts +32 -0
  50. package/lib/types/namespace.d.ts +19 -0
  51. package/lib/types/patch-manager.d.ts +78 -0
  52. package/lib/types/router.d.ts +189 -0
  53. package/lib/types/rpc.d.ts +113 -0
  54. package/lib/types/settings.d.ts +29 -0
  55. package/lib/types/skills.d.ts +93 -0
  56. package/package.json +107 -0
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Managed-block editing for a profile's `cordis.patch.yml`.
3
+ *
4
+ * The plugin owns one contiguous block inside the user's patch file, delimited
5
+ * by a begin/end marker pair, and rewrites only that span — everything else the
6
+ * user wrote (other patches, their comments) survives byte for byte. The
7
+ * block's content is the loader patch that takes the loop engine over: it
8
+ * disables the base bundle's `agent-loop` row so this plugin's factory can
9
+ * register without colliding, because the harness admits exactly one
10
+ * AgentFactory:
11
+ *
12
+ * # -- dsh-loop-engine managed block --
13
+ * - id: agent-loop
14
+ * disabled: true
15
+ * # -- /dsh-loop-engine managed block --
16
+ *
17
+ * The block is now PERMANENT and engine-independent. Under per-session routing
18
+ * this plugin always owns the slot — it registers a router that dispatches to
19
+ * whichever engine a session belongs to, and it hosts the base in-process loop
20
+ * itself as one of those engines. The block therefore no longer carries an
21
+ * engine name and no longer changes when the user picks a different engine;
22
+ * that choice is now runtime state, not boot state.
23
+ *
24
+ * All functions here are pure string transforms — file I/O and durability live
25
+ * in the plugin's apply.
26
+ *
27
+ * @module dsh-agent-hub/patch-manager
28
+ */
29
+ /**
30
+ * Begin marker of the plugin-managed span inside a profile patch file.
31
+ *
32
+ * Deliberately still spelled `dsh-loop-engine` after the package was renamed to
33
+ * `dsh-agent-hub`: this string is not a brand, it is the key by which the
34
+ * plugin recognizes a block it already wrote into the *user's* file. Renaming
35
+ * it would make every existing install fail to find its own span and append a
36
+ * second one, leaving two `agent-loop: disabled` rows in the patch list.
37
+ */
38
+ export declare const MANAGED_BLOCK_BEGIN = "# -- dsh-loop-engine managed block";
39
+ /** End marker of the plugin-managed span inside a profile patch file. */
40
+ export declare const MANAGED_BLOCK_END = "# -- /dsh-loop-engine managed block --";
41
+ /**
42
+ * Render the permanent managed block. The plugin always owns the AgentFactory
43
+ * slot, so the base `agent-loop` row is always disabled.
44
+ */
45
+ export declare function renderManagedBlock(): string;
46
+ /** Whether a patch-file text contains the managed block span. */
47
+ export declare function hasManagedBlock(text: string): boolean;
48
+ /**
49
+ * Ensure the permanent managed block is present, preserving every byte outside
50
+ * the managed span. Appends the span when absent; rewrites it in place when
51
+ * present, which also upgrades a legacy engine-tagged marker from the era when
52
+ * the block encoded the selected engine.
53
+ *
54
+ * This is a belt-and-braces layer, not the mechanism that disables the base
55
+ * loop. Disabling `agent-loop` is a boot-time fact and only the bundle patch can
56
+ * state it: the loader applies bundle layers while expanding entries, whereas
57
+ * this profile layer is applied afterwards and this very write happens inside
58
+ * apply() — by which point `AgentLoop`'s constructor has already claimed the
59
+ * sole AgentFactory slot. The row therefore lives in this package's own
60
+ * `cordis.patch.yml`, and what lands here merely re-disables an already-disabled
61
+ * row, which is idempotent. It is kept so a profile that pins an older bundle,
62
+ * or that lists `agent-loop` itself, still boots.
63
+ *
64
+ * The file must always parse as a top-level YAML *array*: app-boot's
65
+ * `parsePatchList` throws `must be a top-level YAML array of loader patch
66
+ * entries` on anything else, which fails the whole plugin tree — including this
67
+ * plugin's own `insert` row, so no agent factory registers at all.
68
+ *
69
+ * A fresh profile's file is `[]`, a complete flow-style document. Block
70
+ * sequence items cannot follow it, so the `[]` is dropped when the block goes
71
+ * in. The reverse direction no longer exists: the block is permanent, so it is
72
+ * never removed and can never leave a comments-only file behind.
73
+ *
74
+ * @param text - current patch-file text.
75
+ * @returns the rewritten patch-file text.
76
+ */
77
+ export declare function applyManagedBlock(text: string): string;
78
+ //# sourceMappingURL=patch-manager.d.ts.map
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Per-session engine routing: the single AgentFactory slot owner, plus the
3
+ * service shadows that let every engine keep its unmodified registration calls
4
+ * while its three siblings are mounted alongside it.
5
+ *
6
+ * The harness admits exactly one AgentFactory (`ctx.agents.setFactory` throws
7
+ * on a second registration), which historically forced the engine to be a
8
+ * process-wide choice. This module inverts that: one {@link LoopEngineRouter}
9
+ * occupies the slot for the life of the process and dispatches each call to
10
+ * the engine that owns the session, so four engines coexist and a session's
11
+ * engine is fixed at creation.
12
+ *
13
+ * The engines themselves are untouched. Each is a `Service` whose constructor
14
+ * runs `ctx.effect(() => ctx.agents.setFactory(this))`; mounting one through a
15
+ * context whose `agents` member is {@link shadowAgents} redirects that call
16
+ * into router registration instead. `ctx.isolate('agents')` cannot do this —
17
+ * a second `AgentRegistry` calls `ctx.accessor('agent', …)`, which is not
18
+ * isolate-scoped, and the duplicate declaration fails the fiber — so the shadow
19
+ * wraps the *same* registry and overrides exactly one method.
20
+ *
21
+ * {@link shadowSystemPrompt} resolves the second collision the same way, for
22
+ * the prompt variables every engine constructor registers under the same names.
23
+ *
24
+ * @module dsh-agent-hub/router
25
+ */
26
+ import type { Context } from '@deepseek-ai/cordis';
27
+ import type { AgentFactory, AgentHandle, CreateAgentOptions, ResumeAgentOptions } from '@deepseek-ai/dsh-agent';
28
+ import type { LoopEngineId } from './settings.ts';
29
+ /**
30
+ * The subset of the agent registry the shadow must reproduce. Only
31
+ * `setFactory` is intercepted; everything else forwards to the real service.
32
+ */
33
+ interface AgentsLike {
34
+ setFactory(factory: AgentFactory): () => void;
35
+ }
36
+ /**
37
+ * Wrap the real `agents` service so `setFactory` registers with the router
38
+ * instead of the harness slot.
39
+ *
40
+ * Every other member forwards to the real registry. Methods are bound to the
41
+ * target rather than the proxy: `AgentRegistry` keeps its state in private
42
+ * fields, and an unbound method invoked with the proxy as receiver would trip
43
+ * the brand check on those fields.
44
+ *
45
+ * @param agents - the real `ctx.agents` service.
46
+ * @param capture - receives the factory an engine tries to register; its return is handed back as the disposer.
47
+ * @returns a stand-in service to install via `ctx.extend({ agents })`.
48
+ */
49
+ export declare function shadowAgents<T extends AgentsLike>(agents: T, capture: (factory: AgentFactory) => () => void): T;
50
+ /** Resolve the engine that owns a session being resumed. */
51
+ export type EngineResolver = (sessionId: string) => Promise<LoopEngineId> | LoopEngineId;
52
+ /**
53
+ * The subset of the system-prompt service the shadow must reproduce. Only
54
+ * `variable` is intercepted; everything else forwards to the real service.
55
+ */
56
+ interface SystemPromptLike {
57
+ variable(name: string, provider: (context: any) => string | undefined): () => void;
58
+ }
59
+ /**
60
+ * Wrap the real `systemPrompt` service so a prompt-variable name that a sibling
61
+ * engine already claimed is ignored instead of throwing.
62
+ *
63
+ * Every engine constructor registers `provider`, `model`, and `cwd` on its
64
+ * mount context. That context carries no scope tag, so all three registrations
65
+ * target the one global prompt layer and the second engine to mount dies with
66
+ * `prompt variable "provider" is already registered`. Under the old design only
67
+ * one engine was ever mounted, so the collision could not arise; hosting all
68
+ * four makes it certain.
69
+ *
70
+ * Scoping the mount contexts would not help: an agent mints its own scope with
71
+ * `createScope(loopCtx, this)` and no `parent`, so `scopeChainOf(agent)` is just
72
+ * `[agent]` — a variable parked on an engine-level scope would be invisible to
73
+ * that engine's own agents. Deduplicating on the global layer is therefore the
74
+ * correct level.
75
+ *
76
+ * Collapsing the three registrations to one is sound because they are
77
+ * byte-identical across the engines and engine-independent by construction:
78
+ * each reads the *assembling* agent's own options (`context.agent?.options`) or
79
+ * its session header, so whichever engine registered first serves every
80
+ * engine's agents with that agent's own values.
81
+ *
82
+ * @param systemPrompt - the real `ctx.systemPrompt` service.
83
+ * @param claim - decides whether this context may own `name`; false means a sibling already did.
84
+ * @returns a stand-in service to install via `ctx.extend({ systemPrompt })`.
85
+ */
86
+ export declare function shadowSystemPrompt<T extends SystemPromptLike>(systemPrompt: T, claim: (name: string) => boolean): T;
87
+ /** Compose extra per-agent registrations into the caller's setup callback. */
88
+ export type SetupDecorator = (engine: LoopEngineId, agentCtx: Context) => void;
89
+ /** Wiring the router needs from the plugin that owns it. */
90
+ export interface RouterOptions {
91
+ /**
92
+ * The engine a new session gets when it carries no reservation: the profile
93
+ * default from settings.
94
+ */
95
+ engineForNewSession(): LoopEngineId;
96
+ /**
97
+ * Record a new session's engine durably, before the engine creates it.
98
+ *
99
+ * Takes the session's `meta` rather than its id alone: the persistence
100
+ * backend keys a session's artifact directory by `cwd`, so a record written
101
+ * without it lands in a directory no resume will look in.
102
+ */
103
+ remember(meta: {
104
+ readonly id: string;
105
+ readonly cwd?: string;
106
+ }, engine: LoopEngineId): Promise<void>;
107
+ /** Recover a persisted session's engine. */
108
+ resolve: EngineResolver;
109
+ /**
110
+ * Registers engine-specific per-agent contributions (skill providers) into
111
+ * the agent's own scope layer. Invoked with the scope-tagged `agentCtx` the
112
+ * engine passes to `setup`, so the harness's layered skill registry filters
113
+ * them to that session automatically.
114
+ */
115
+ decorateSetup?: SetupDecorator;
116
+ }
117
+ /**
118
+ * The process's sole AgentFactory. Holds one candidate per engine and forwards
119
+ * `createAgent` / `resume` to the right one.
120
+ *
121
+ * Forwarding is contract-compliant by construction: both factory methods are
122
+ * per-call and take the caller's `ownerCtx`, which the contract requires the
123
+ * implementation to attach lifecycle to ("it must not infer ownership from the
124
+ * factory object's registration context"). The router passes it through
125
+ * verbatim, so the engine owns the transaction exactly as if it had been
126
+ * registered directly.
127
+ */
128
+ export declare class LoopEngineRouter implements AgentFactory {
129
+ private readonly options;
130
+ private readonly engines;
131
+ /**
132
+ * Engines claimed for session ids that do not exist yet.
133
+ *
134
+ * Agent creation is eager — the harness calls `createAgent` when a session is
135
+ * opened, not when its first prompt is sent — so a client that wants a
136
+ * specific engine cannot ask for it after the fact. Instead it mints the
137
+ * session id itself, reserves the engine here, and only then asks the host to
138
+ * create that id. Insertion order is the eviction order (Map preserves it).
139
+ */
140
+ private readonly reservations;
141
+ constructor(options: RouterOptions);
142
+ /**
143
+ * Claim an engine for a session id the caller is about to create.
144
+ *
145
+ * The reservation is consumed by the first `createAgent` for that id, so it
146
+ * cannot leak into a later session that happens to reuse the id.
147
+ *
148
+ * @param sessionId - id the caller will pass to session creation.
149
+ * @param engine - engine that session must run on.
150
+ */
151
+ reserve(sessionId: string, engine: LoopEngineId): void;
152
+ /**
153
+ * Take the engine a new session should run on: its reservation when it has
154
+ * one, the profile default otherwise.
155
+ */
156
+ private claim;
157
+ /**
158
+ * Register one engine's factory as a routing candidate.
159
+ * @param engine - the engine id this factory implements.
160
+ * @param factory - the engine's factory, captured from its `setFactory` call.
161
+ * @returns a disposer that withdraws the candidate.
162
+ */
163
+ register(engine: LoopEngineId, factory: AgentFactory): () => void;
164
+ /** The shadow to install on the context that mounts `engine`. */
165
+ shadowFor<T extends AgentsLike>(engine: LoopEngineId, agents: T): T;
166
+ /**
167
+ * Look up a mounted engine, failing loud when it is selected but absent.
168
+ * A silent fallback would run the session on an engine that cannot read its
169
+ * history, which is the exact defect per-session routing exists to remove.
170
+ */
171
+ private factoryFor;
172
+ /** Wrap the caller's setup so the engine's per-agent registrations land in the agent's scope. */
173
+ private withSetup;
174
+ /**
175
+ * Create a session on the engine it reserved, or on the profile default when
176
+ * it made no reservation. The engine is recorded durably *before* delegation:
177
+ * a crash between the two leaves an orphan record (harmless — no session ever
178
+ * reads it), whereas the reverse order would leave a session whose engine
179
+ * cannot be recovered.
180
+ *
181
+ * `options.meta.cwd` is forwarded to the record so the sidecar lands in the
182
+ * same per-project directory the backend will write the session into.
183
+ */
184
+ createAgent(ownerCtx: Context, options: CreateAgentOptions): Promise<AgentHandle>;
185
+ /** Resume a session on the engine that created it. */
186
+ resume(ownerCtx: Context, options: ResumeAgentOptions): Promise<AgentHandle>;
187
+ }
188
+ export {};
189
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1,113 @@
1
+ /**
2
+ * The `/loop-engine` Connection RPC channel: the browser's only route to the
3
+ * node half's per-session engine state.
4
+ *
5
+ * Two facts about the harness make this channel necessary rather than merely
6
+ * convenient:
7
+ *
8
+ * - **Agent creation is eager.** `createAgent` fires when a session is
9
+ * *opened*, not when its first prompt is sent, so by the time a user can
10
+ * click anything in the composer the engine is already chosen. A client that
11
+ * wants a specific engine must therefore say so *before* creating the
12
+ * session — hence `bind`, which reserves an engine for a session id the
13
+ * client mints itself.
14
+ * - **The engine lives in a node-side sidecar.** Nothing in the session log,
15
+ * header, or settings carries it (see {@link ../engine-record.ts} for why
16
+ * each of those is closed), so the browser cannot read it from any existing
17
+ * projection — hence `resolve`.
18
+ *
19
+ * A custom Connection channel is the smallest seam that provides both. The
20
+ * alternative, a `@Remote` service on the typert gateway, would work on the
21
+ * host but requires hand-written strict zod descriptors on the client
22
+ * (`requireStrictDescriptor` rejects reflection-derived ones), which is a large
23
+ * contract surface for two internal methods. This plugin owns both ends of this
24
+ * channel, so it validates its own payloads and needs no codec at all.
25
+ *
26
+ * @module dsh-agent-hub/rpc
27
+ */
28
+ import { type LoopEngineId } from './settings.ts';
29
+ /** Channel path this plugin owns. Reserved names (`/api`) are refused by the host. */
30
+ export declare const LOOP_ENGINE_RPC_CHANNEL = "/loop-engine";
31
+ /** Carrier-neutral result shape the Connection RPC transport expects back. */
32
+ export type RpcResult<T> = {
33
+ readonly ok: true;
34
+ readonly value: T;
35
+ } | {
36
+ readonly ok: false;
37
+ readonly error: {
38
+ readonly code: string;
39
+ readonly message: string;
40
+ readonly details: object;
41
+ };
42
+ };
43
+ /** One logical endpoint handler, as `connection.rpc.handle` supplies it. */
44
+ export type RpcHandler = (endpoint: string, payload: unknown, signal: AbortSignal) => Promise<RpcResult<unknown>>;
45
+ /**
46
+ * Trust fence the host applies before a request reaches this channel's handler.
47
+ *
48
+ * `trusted-host` accepts any authority in the deployment's configured
49
+ * `trustedHosts`; `loopback` narrows to loopback only. This channel takes
50
+ * `trusted-host` because it must work wherever the composer works — a dsh
51
+ * reachable over a LAN authority serves the same UI, and a stricter fence would
52
+ * 403 the seat's own requests on exactly those deployments.
53
+ */
54
+ export type ConnectionRpcAuthority = 'trusted-host' | 'loopback';
55
+ /** Registration policy `rpc.handle` requires for a channel. */
56
+ export interface ConnectionRpcHandlerOptions {
57
+ /** Browser authority accepted by every endpoint in this channel. */
58
+ readonly authority: ConnectionRpcAuthority;
59
+ }
60
+ /**
61
+ * The host Connection surface this module borrows. Declared structurally so the
62
+ * plugin needs no peer dependency on the connection package, and so a profile
63
+ * without a Connection (headless, SDK) degrades instead of failing to mount.
64
+ *
65
+ * `options` is **required**, and getting that wrong fails in a way worth
66
+ * spelling out: the host reads `options.authority` on the first line of
67
+ * `register`, so omitting the argument throws `Cannot read properties of
68
+ * undefined (reading 'authority')` *inside the effect*. Cordis swallows that
69
+ * into the fiber, the plugin keeps running, every engine still works — and the
70
+ * route is simply never added, so every request to the channel falls through to
71
+ * the plugin-bundle server and comes back 405. Nothing logs at the default
72
+ * level. Structural typing is what let this compile: the declaration below is
73
+ * the plugin's own, so it cannot disagree with the installed package unless it
74
+ * is kept in step with it by hand.
75
+ */
76
+ export interface ConnectionLike {
77
+ readonly rpc: {
78
+ handle(channel: string, handler: RpcHandler, options: ConnectionRpcHandlerOptions): () => Promise<void>;
79
+ };
80
+ }
81
+ /** The router operations this channel drives. */
82
+ export interface EngineRpcTarget {
83
+ /** Claim an engine for a session id the client is about to create. */
84
+ reserve(sessionId: string, engine: LoopEngineId): void;
85
+ /** Read back the engine a session is actually bound to. */
86
+ recall(sessionId: string): Promise<LoopEngineId>;
87
+ /** The profile default, for a session that has no record yet. */
88
+ fallback(): LoopEngineId;
89
+ }
90
+ /**
91
+ * Build the `/loop-engine` handler.
92
+ *
93
+ * Exported separately from {@link installEngineRpc} so the endpoints are
94
+ * testable without a Connection.
95
+ *
96
+ * @param target - the router and record store operations to drive.
97
+ * @returns the handler to register on the channel.
98
+ */
99
+ export declare function createEngineRpcHandler(target: EngineRpcTarget): RpcHandler;
100
+ /**
101
+ * Register the channel when the profile has a Connection.
102
+ *
103
+ * The Connection is injected conditionally by the gateway, so a headless or SDK
104
+ * profile legitimately has none. There the plugin keeps working — per-session
105
+ * routing is a node-side property — and only the browser controls degrade.
106
+ *
107
+ * @param connection - the host connection, or undefined when absent.
108
+ * @param register - installs the handler under the plugin's effect lifecycle.
109
+ * @param target - the router and record store operations to drive.
110
+ * @returns whether the channel was registered.
111
+ */
112
+ export declare function installEngineRpc(connection: ConnectionLike | undefined, register: (install: () => () => Promise<void>, label: string) => void, target: EngineRpcTarget): boolean;
113
+ //# sourceMappingURL=rpc.d.ts.map
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Shared loop-engine identity, namespace, and schema.
3
+ *
4
+ * The namespace literal lives in the zero-import `./namespace.ts` so both
5
+ * halves agree on the section name: the node half brands it through
6
+ * `settingsNamespace()` (a runtime value), while the browser half imports the
7
+ * same literal without pulling the host-side `dsh-settings` service into the
8
+ * client bundle (cross-plugin value imports go through cordis services, and
9
+ * `settings-scope.ts` follows the same discipline).
10
+ *
11
+ * @module dsh-agent-hub/settings
12
+ */
13
+ import z from '@deepseek-ai/schemastery';
14
+ import type { SettingsNamespace } from '@deepseek-ai/dsh-settings';
15
+ import { type LoopEngineId } from './namespace.ts';
16
+ export { LOOP_ENGINE_SETTINGS_NAMESPACE_LITERAL } from './namespace.ts';
17
+ export { LOOP_ENGINE_IDS, type LoopEngineId } from './namespace.ts';
18
+ /** Stored and composed loop engine selection. */
19
+ export interface LoopEngineSettings {
20
+ /** The engine future Agents are created on. */
21
+ engine: LoopEngineId;
22
+ /** Whether the composer's loop engine picker is shown on the chat page. */
23
+ showInComposer: boolean;
24
+ }
25
+ /** Schema of the loop engine settings section. */
26
+ export declare const LOOP_ENGINE_SETTINGS_SCHEMA: z<LoopEngineSettings>;
27
+ /** Brand the shared literal through the settings API on the node side. */
28
+ export declare function loopEngineSettingsNamespace(): SettingsNamespace;
29
+ //# sourceMappingURL=settings.d.ts.map
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Claude Code skill provider: discovers skills from the project's `.claude/`
3
+ * directory, the user-level `~/.claude/skills/` directory, and the project's
4
+ * `CLAUDE.md` file, using the same YAML-frontmatter + markdown format as DSH
5
+ * skills.
6
+ *
7
+ * @module dsh-agent-hub/skills
8
+ */
9
+ export interface SkillInvocationPolicy {
10
+ readonly modelInvocable: boolean;
11
+ readonly userInvocable: boolean;
12
+ }
13
+ export type SkillSource = string;
14
+ export interface SkillCandidate {
15
+ readonly name: string;
16
+ readonly description: string;
17
+ readonly whenToUse?: string;
18
+ readonly invocation: SkillInvocationPolicy;
19
+ readonly source: SkillSource;
20
+ readonly provider: string;
21
+ readonly rank: number;
22
+ readonly locator: unknown;
23
+ readonly path?: string;
24
+ readonly resourceBase?: {
25
+ readonly kind: string;
26
+ readonly path: string;
27
+ };
28
+ }
29
+ export interface SkillDefinition {
30
+ readonly name: string;
31
+ readonly description: string;
32
+ readonly whenToUse?: string;
33
+ readonly invocation: SkillInvocationPolicy;
34
+ readonly source: SkillSource;
35
+ readonly provider: string;
36
+ readonly content: string;
37
+ readonly path?: string;
38
+ readonly resourceBase?: {
39
+ readonly kind: string;
40
+ readonly path: string;
41
+ };
42
+ }
43
+ export interface SkillLookupOptions {
44
+ readonly cwd?: string;
45
+ readonly signal?: AbortSignal;
46
+ }
47
+ export interface SkillProvider {
48
+ readonly name: string;
49
+ list(options: SkillLookupOptions): Promise<readonly SkillCandidate[] | {
50
+ candidates: readonly SkillCandidate[];
51
+ complete: boolean;
52
+ }>;
53
+ get(candidate: SkillCandidate, options: SkillLookupOptions): Promise<SkillDefinition | undefined>;
54
+ }
55
+ export interface SkillProviderControl {
56
+ readonly signal: AbortSignal;
57
+ invalidate(): void;
58
+ }
59
+ /** One parsed agents-skill standard skill: frontmatter metadata plus the body. */
60
+ export interface ParsedSkill {
61
+ name: string;
62
+ description: string;
63
+ whenToUse?: string;
64
+ invocation: SkillInvocationPolicy;
65
+ content: string;
66
+ }
67
+ /**
68
+ * Parse one agents-skill standard file: YAML frontmatter (`name`,
69
+ * `description`, optional `whenToUse`/`disable-model-invocation`/
70
+ * `user-invocable`) plus the markdown body.
71
+ * @param raw - the raw file content.
72
+ * @returns the parsed skill, or `undefined` when the file is not a skill.
73
+ */
74
+ export declare function parseSkillFile(raw: string): ParsedSkill | undefined;
75
+ /**
76
+ * Skill provider that discovers skills from Claude Code's standard locations:
77
+ * - `<project>/.claude/skills/` — project skills
78
+ * - `~/.claude/skills/` — personal skills
79
+ * Each location accepts both Claude Code layouts: a `<name>/SKILL.md`
80
+ * directory (its directory becomes the resource base) and a flat `<name>.md`
81
+ * file. `CLAUDE.md` in the project root is also read when it carries skill
82
+ * frontmatter.
83
+ */
84
+ export declare class ClaudeCodeSkillProvider implements SkillProvider {
85
+ private readonly control;
86
+ readonly name = "claude-code";
87
+ constructor(control: SkillProviderControl);
88
+ list(options: SkillLookupOptions): Promise<readonly SkillCandidate[]>;
89
+ get(candidate: SkillCandidate, _options: SkillLookupOptions): Promise<SkillDefinition | undefined>;
90
+ }
91
+ export declare function findProjectRoot(cwd: string): Promise<string>;
92
+ export default ClaudeCodeSkillProvider;
93
+ //# sourceMappingURL=skills.d.ts.map
package/package.json ADDED
@@ -0,0 +1,107 @@
1
+ {
2
+ "name": "@vidge/dsh-agent-hub",
3
+ "description": "Per-session agent loop engine router for DSH — run Claude Code, Codex, pi, or the in-process loop per session, all on DSH's unified session, LLM, and tracing substrate",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "version": "0.1.0-rc1",
8
+ "repository": "github:vidgewong/dsh-agent-hub",
9
+ "type": "module",
10
+ "main": "lib/index.js",
11
+ "types": "lib/types/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./lib/types/index.d.ts",
15
+ "default": "./lib/index.js"
16
+ },
17
+ "./invariant": {
18
+ "types": "./lib/types/invariant.d.ts",
19
+ "default": "./lib/invariant.js"
20
+ },
21
+ "./client": {
22
+ "types": "./lib/types/client/index.d.ts",
23
+ "default": "./lib/client.js"
24
+ },
25
+ "./src/*": "./src/*",
26
+ "./package.json": "./package.json"
27
+ },
28
+ "files": [
29
+ "LICENSE",
30
+ "README.md",
31
+ "lib/index.js",
32
+ "lib/invariant.js",
33
+ "lib/client.js",
34
+ "lib/types/**/*.d.ts",
35
+ "cordis.patch.yml"
36
+ ],
37
+ "license": "MIT",
38
+ "scripts": {
39
+ "build": "node build.mjs",
40
+ "typecheck": "tsc -p tsconfig.build.json --noEmit",
41
+ "test": "vitest run",
42
+ "test:coverage": "vitest run --coverage"
43
+ },
44
+ "dsh": {
45
+ "bundle": {
46
+ "patch": "./cordis.patch.yml"
47
+ },
48
+ "client": {
49
+ "inject": [
50
+ "@deepseek-ai/dsh-client-runtime",
51
+ "@deepseek-ai/dsh-client-locale",
52
+ "@deepseek-ai/dsh-client-ui-settings",
53
+ "@deepseek-ai/dsh-client-ui-conversation",
54
+ "@deepseek-ai/dsh-client-connection",
55
+ "@deepseek-ai/dsh-api-remotes"
56
+ ],
57
+ "platform": "web"
58
+ }
59
+ },
60
+ "dependencies": {
61
+ "@anthropic-ai/sdk": "0.93.0",
62
+ "@deepseek-ai/schemastery": "3.18.1"
63
+ },
64
+ "optionalDependencies": {
65
+ "@anthropic-ai/claude-agent-sdk": "0.3.220",
66
+ "@earendil-works/pi-coding-agent": "0.84.3",
67
+ "@openai/codex": "0.149.1"
68
+ },
69
+ "peerDependencies": {
70
+ "@deepseek-ai/cordis": "^4.0.1",
71
+ "@deepseek-ai/dsh-agent": "0.1.1-rc.2",
72
+ "@deepseek-ai/dsh-agent-loop": "0.1.1-rc.2",
73
+ "@deepseek-ai/dsh-invariants": "0.1.1-rc.2",
74
+ "@deepseek-ai/dsh-llm": "0.1.1-rc.2 || 0.1.2-alpha.2",
75
+ "@deepseek-ai/dsh-scope": "0.1.1-rc.2",
76
+ "@deepseek-ai/dsh-session": "0.1.1-rc.2",
77
+ "@deepseek-ai/dsh-session-persistence": "0.1.1-rc.2",
78
+ "@deepseek-ai/dsh-settings": "0.1.1-rc.2",
79
+ "@deepseek-ai/dsh-subprocess": "0.1.1-rc.2",
80
+ "@deepseek-ai/dsh-timeout": "0.1.1-rc.2"
81
+ },
82
+ "devDependencies": {
83
+ "@deepseek-ai/dsh-attachment": "0.1.1-rc.2",
84
+ "@deepseek-ai/dsh-agent-loop": "0.1.1-rc.2",
85
+ "@deepseek-ai/dsh-client-locale": "0.1.1-rc.2",
86
+ "@deepseek-ai/dsh-client-runtime": "0.1.1-rc.2",
87
+ "@deepseek-ai/dsh-client-store": "0.1.2-alpha.2",
88
+ "@deepseek-ai/dsh-client-ui-conversation": "0.1.1-rc.2",
89
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.1-rc.2",
90
+ "@deepseek-ai/dsh-client-ui-settings": "0.1.1-rc.2",
91
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.1-rc.2",
92
+ "@deepseek-ai/dsh-home-paths": "0.1.1-rc.2",
93
+ "@deepseek-ai/dsh-session-persistence-jsonl": "0.1.1-rc.2",
94
+ "@deepseek-ai/dsh-subprocess-local": "0.1.1-rc.2",
95
+ "@deepseek-ai/dsh-system-prompt": "0.1.1-rc.2",
96
+ "@types/node": "^22.20.0",
97
+ "@types/react": "^19.0.0",
98
+ "@vitest/coverage-v8": "^4.1.8",
99
+ "esbuild": "^0.28.2",
100
+ "tsx": "^4.22.4",
101
+ "typescript": "^6.0.3",
102
+ "vite": "^7.0.0",
103
+ "vite-tsconfig-paths": "^6.1.1",
104
+ "vitest": "^4.1.8",
105
+ "yaml": "^2.9.0"
106
+ }
107
+ }