@hasna/skills 0.8.9 → 0.8.11

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 (37) hide show
  1. package/README.md +103 -1
  2. package/bin/index.js +26509 -24016
  3. package/bin/mcp.js +596 -222
  4. package/bin/migrate.js +7 -5
  5. package/bin/server.js +12313 -73626
  6. package/bin/worker.js +7636 -60520
  7. package/dist/cli/commands/plugin-admission.d.ts +2 -0
  8. package/dist/index.d.ts +9 -2
  9. package/dist/index.js +23559 -21779
  10. package/dist/lib/agent-adapters.d.ts +1 -0
  11. package/dist/lib/agent-codex-trust.d.ts +42 -0
  12. package/dist/lib/agent-discovery.d.ts +4 -2
  13. package/dist/lib/claude-marketplace-registry.d.ts +17 -0
  14. package/dist/lib/codex-hook-rpc.d.ts +13 -0
  15. package/dist/lib/codex-hook-trust-files.d.ts +15 -0
  16. package/dist/lib/codex-hook-trust-identity.d.ts +16 -0
  17. package/dist/lib/codex-hook-trust-layout.d.ts +3 -0
  18. package/dist/lib/fleet-credentials.d.ts +18 -0
  19. package/dist/lib/hook-diagnostics.d.ts +7 -0
  20. package/dist/lib/plugin-admission.d.ts +101 -0
  21. package/dist/lib/plugin-discovery.d.ts +13 -0
  22. package/dist/lib/plugin-projection-store.d.ts +14 -0
  23. package/dist/lib/plugin-projection.d.ts +80 -0
  24. package/dist/lib/profile-client.d.ts +10 -0
  25. package/dist/lib/recurring-recovery.d.ts +2 -2
  26. package/dist/lib/recurring-surface.d.ts +1 -1
  27. package/dist/lib/remote-client.d.ts +14 -4
  28. package/dist/lib/remote-credit-checkout.d.ts +22 -0
  29. package/dist/lib/remote-customer-operations.d.ts +3 -4
  30. package/dist/lib/remote-permissions.d.ts +24 -0
  31. package/dist/lib/selection-cache.d.ts +32 -0
  32. package/dist/lib/selection-resolver.d.ts +3 -1
  33. package/dist/lib/session-reconciliation.d.ts +100 -0
  34. package/dist/sdk/index.d.ts +5 -2
  35. package/dist/sdk/index.js +18528 -78089
  36. package/docs/plugin-admission.md +221 -0
  37. package/package.json +7 -5
@@ -1,5 +1,6 @@
1
1
  export declare const INTEGRATION_AGENTS: readonly ["claude", "codex", "gemini", "opencode", "cursor", "hermes"];
2
2
  export type IntegrationAgent = typeof INTEGRATION_AGENTS[number];
3
+ export declare function renderAgentHookCommand(command: string, agent: IntegrationAgent, profileId: string, event: string): string;
3
4
  export declare const AGENT_ADAPTERS: {
4
5
  readonly claude: {
5
6
  readonly root: ".claude/skills";
@@ -0,0 +1,42 @@
1
+ import { connectCodexHookRpc } from "./codex-hook-rpc.js";
2
+ import { type ReviewedSkillsCli } from "./codex-hook-trust-identity.js";
3
+ export interface CodexNativeHookTrustOptions {
4
+ home?: string;
5
+ dataDir?: string;
6
+ codexCommand?: string;
7
+ apply?: boolean;
8
+ reviewedPlanDigest?: string;
9
+ reviewedSkillsCli?: ReviewedSkillsCli;
10
+ }
11
+ type Connect = typeof connectCodexHookRpc;
12
+ /** Deliberately separate from filesystem hook installation: native trust is an
13
+ * explicit authorization of exact owned commands, never a broad trust bypass. */
14
+ export declare function enrollCodexNativeHooks(options?: CodexNativeHookTrustOptions, connect?: Connect): Promise<{
15
+ planDigest: string;
16
+ skillsCli: {
17
+ path: string;
18
+ version: string;
19
+ sha256: string;
20
+ manifestSha256: string;
21
+ };
22
+ nativeVersion: string;
23
+ ownedNativeProcessId: number | undefined;
24
+ agent: string;
25
+ applied: boolean;
26
+ planned: {
27
+ key: any;
28
+ event: string;
29
+ command: string;
30
+ handlerType: any;
31
+ timeoutSec: any;
32
+ sourcePath: any;
33
+ currentHash: any;
34
+ enabled: any;
35
+ trustStatus: any;
36
+ }[];
37
+ nativeEligible: boolean;
38
+ transport: string;
39
+ existingSessionsReloaded: boolean;
40
+ nativeExecutionVerified: boolean;
41
+ }>;
42
+ export {};
@@ -1,10 +1,12 @@
1
1
  import type { IntegrationAgent } from "./agent-adapters.js";
2
2
  import { type DiscoveryDirectory } from "./agent-discovery-directories.js";
3
+ import { type ManagedPluginRegistrationWitness } from "./plugin-discovery.js";
3
4
  export { captureDiscoveryDirectories, type DiscoveryDirectory } from "./agent-discovery-directories.js";
4
5
  export interface DiscoverySource {
5
6
  path: string;
6
7
  sha256: string | null;
7
- hashMode?: "bytes" | "path-bytes";
8
+ hashMode?: "bytes" | "path-bytes" | "claude-plugin-registry" | "claude-marketplace-registry";
9
+ managedPlugins?: ManagedPluginRegistrationWitness[];
8
10
  format?: "json" | "toml" | "yaml";
9
11
  fields?: string[];
10
12
  }
@@ -35,7 +37,7 @@ export declare function captureDiscoveryPathSources(paths: string[]): DiscoveryS
35
37
  /** Project settings can introduce a higher-precedence discovery source. Until
36
38
  * its native merge format is supported, refuse that layer rather than certify
37
39
  * the home-only inventory. Ordinary unrelated project settings remain usable. */
38
- export declare function assertProjectDiscovery(agent: IntegrationAgent, directories: string[], home: string, canonical?: (path: string) => string): void;
40
+ export declare function assertProjectDiscovery(agent: IntegrationAgent, directories: string[], home: string, canonical?: (path: string) => string, reviewed?: AgentDiscoveryBinding): void;
39
41
  /** Resolve configured plugin discovery without starting an agent or loading a plugin.
40
42
  * Unknown runtime registrations require explicit reviewed inputs, never a cache guess. */
41
43
  export declare function resolveAgentDiscovery(options: {
@@ -0,0 +1,17 @@
1
+ export declare const CLAUDE_MARKETPLACE_REGISTRY_LIMITS: Readonly<{
2
+ bytes: number;
3
+ rows: 1024;
4
+ depth: 32;
5
+ nodes: 65536;
6
+ stringCharacters: 16384;
7
+ pathCharacters: 4096;
8
+ }>;
9
+ export interface ClaudeMarketplaceRegistryBudget {
10
+ remaining: number;
11
+ }
12
+ /** Capture explicitly reviewed registry structure; never infer this mode from a raw-byte witness. */
13
+ export declare function captureClaudeMarketplaceRegistry(path: string, budget?: ClaudeMarketplaceRegistryBudget): {
14
+ path: string;
15
+ hashMode: "claude-marketplace-registry";
16
+ sha256: string;
17
+ };
@@ -0,0 +1,13 @@
1
+ export declare const SUPPORTED_CODEX_HOOK_VERSIONS: readonly ["codex-cli 0.153.0", "codex-cli 0.154.0", "codex-cli 0.155.0"];
2
+ export interface CodexHookRpc {
3
+ version: string;
4
+ processId?: number;
5
+ request(method: string, params: unknown): Promise<any>;
6
+ close(): Promise<void>;
7
+ }
8
+ /** An owned, short-lived native client. It cannot reload other app servers. */
9
+ export declare function connectCodexHookRpc(options: {
10
+ command: string;
11
+ home: string;
12
+ codexHome: string;
13
+ }): Promise<CodexHookRpc>;
@@ -0,0 +1,15 @@
1
+ import { type BigIntStats } from "node:fs";
2
+ export declare function need(value: unknown, code: string): asserts value;
3
+ export declare function snapshot(file: string, privateFile?: boolean, options?: {
4
+ readOnlyPackage?: true;
5
+ }): {
6
+ file: string;
7
+ privateFile: boolean;
8
+ readOnlyPackage: boolean;
9
+ text: string;
10
+ bytes: Buffer<ArrayBuffer>;
11
+ stat: BigIntStats;
12
+ sha256: string;
13
+ };
14
+ export declare function unchanged(before: ReturnType<typeof snapshot>): void;
15
+ export declare function save(file: string, content: string | Buffer): void;
@@ -0,0 +1,16 @@
1
+ /** A private release adapter supplies a reviewed registry artifact identity;
2
+ * the normal CLI binds to its own installed entrypoint instead. */
3
+ export interface ReviewedSkillsCli {
4
+ path: string;
5
+ version: string;
6
+ sha256: string;
7
+ }
8
+ export declare function bindSkillsCli(command: string, reviewed?: ReviewedSkillsCli): {
9
+ receipt: {
10
+ path: string;
11
+ version: string;
12
+ sha256: string;
13
+ manifestSha256: string;
14
+ };
15
+ recheck(): void;
16
+ };
@@ -0,0 +1,3 @@
1
+ /** Conservative admission and text witness, never a TOML writer. Native Codex
2
+ * performs the versioned write; unrecognized inline/dotted trust shapes refuse. */
3
+ export declare function codexTrustTextWitness(text: string, keys: string[]): string;
@@ -147,6 +147,24 @@ export declare class SkillsFleetCredentialError extends Error {
147
147
  * on the error's NAME rather than on a class identity a bundle may not share.
148
148
  */
149
149
  export declare function isSkillsFleetCredentialError(error: unknown): error is SkillsFleetCredentialError;
150
+ /**
151
+ * Translate the shared seam's `CredentialResolutionError` into this package's
152
+ * own refusal, or return null for anything else.
153
+ *
154
+ * A DELIBERATE selection that cannot be honoured — `HASNA_PROFILE` naming a
155
+ * profile that has no entry, an override or pointer that resolves to nothing, a
156
+ * corrupt credentials file — is thrown by `@hasna/contracts`, not by the
157
+ * transport resolver. Left untranslated it escaped every helper in this file:
158
+ * `skillsCredentialOrReason` and `resolveConfiguredRunRouting` recognise only
159
+ * `SkillsFleetCredentialError`, so a `--json` command or an MCP tool got an
160
+ * unhandled exception where the structured refusal was the whole point.
161
+ *
162
+ * The seam's message already names what was attempted and never carries a
163
+ * credential value. Pointer-completion messages can still carry the selected
164
+ * vault item identifier, so that exact identifier is removed before the error
165
+ * reaches any user-visible surface.
166
+ */
167
+ export declare const REDACTED_VAULT_REFERENCE = "[redacted vault reference]";
150
168
  /**
151
169
  * Normalize a configured Skills authority to the origin the client dials.
152
170
  *
@@ -0,0 +1,7 @@
1
+ export declare class HookDiagnosticError extends Error {
2
+ readonly code: string;
3
+ readonly stage: "context" | "sync";
4
+ constructor(code: string, stage: "context" | "sync");
5
+ }
6
+ export declare function hookChildError(stdout: string, stage: "context" | "sync"): HookDiagnosticError;
7
+ export declare function hookFailureReason(error: unknown, profileId?: string): string;
@@ -0,0 +1,101 @@
1
+ import { type ProfileClient } from "./profile-client.js";
2
+ import { type SelectionCacheOptions } from "./selection-cache.js";
3
+ import { type PluginFileWitness, type PluginProjectionManifest } from "./plugin-projection.js";
4
+ import type { ResolvedSkillSelection } from "../types/skill-selection.js";
5
+ /** Version 1 is certified against native Claude 2.1.274 and 2.1.276; new hosts require a contract revision/test. */
6
+ export interface PluginAdmissionTarget {
7
+ schemaVersion: 1;
8
+ pluginId: string;
9
+ /** One marketplace command serves all explicitly reviewed native registration scopes. */
10
+ registrations: Array<{
11
+ scope: "user" | "project";
12
+ projectPath: string | null;
13
+ }>;
14
+ native: {
15
+ version: "2.1.274" | "2.1.276";
16
+ executable: string;
17
+ digest: string;
18
+ };
19
+ resolver: {
20
+ executable: string;
21
+ digest: string;
22
+ };
23
+ }
24
+ export interface PluginAdmissionBinding {
25
+ schemaVersion: 2;
26
+ authority: string;
27
+ workspaceId: string;
28
+ /** Stable account subject. Raw credential/key identities are never persisted. */
29
+ principal: PluginAdmissionPrincipal;
30
+ profileId: string;
31
+ bundleSlug: string;
32
+ target: PluginAdmissionTarget;
33
+ }
34
+ export type PluginBundleIdentity = Pick<ResolvedSkillSelection, "slug" | "version" | "bundleDigest">;
35
+ export interface PluginAdmissionPrincipal {
36
+ userId: string;
37
+ accountId: string;
38
+ role: "owner";
39
+ }
40
+ export interface PluginRoutingIdentity extends PluginBundleIdentity {
41
+ aliases?: string[];
42
+ triggers?: {
43
+ keywords?: string[];
44
+ paths?: string[];
45
+ always?: boolean;
46
+ };
47
+ }
48
+ /** Every field that can route or activate the admitted plugin participates in approval. */
49
+ export interface PluginAdmissionIdentity {
50
+ schemaVersion: 3;
51
+ binding: PluginAdmissionBinding;
52
+ bindingId: string;
53
+ profileRevision: string;
54
+ selection: PluginRoutingIdentity;
55
+ payloadSelections: PluginRoutingIdentity[];
56
+ manifest: PluginProjectionManifest;
57
+ originalTreeDigest: string;
58
+ projectionTreeDigest: string;
59
+ files: PluginFileWitness[];
60
+ removed: PluginFileWitness[];
61
+ sourceCommand: string;
62
+ /** Approved digest of the authenticated routing evidence snapshot. */
63
+ evidenceDigest: string;
64
+ }
65
+ export interface PluginAdmissionPlan extends PluginAdmissionIdentity {
66
+ observation: {
67
+ principal: PluginAdmissionPrincipal;
68
+ profileRevision: string;
69
+ integration: PluginRoutingIdentity;
70
+ payloads: PluginRoutingIdentity[];
71
+ };
72
+ planDigest: string;
73
+ }
74
+ export interface PluginAdmissionReceipt {
75
+ schemaVersion: 3;
76
+ plan: PluginAdmissionPlan;
77
+ materializedPath: string;
78
+ admittedAt: string;
79
+ }
80
+ /** Client/store injections are for embedding and isolated tests. The CLI exposes neither. */
81
+ export interface PluginAdmissionOptions extends Pick<SelectionCacheOptions, "now"> {
82
+ client?: ProfileClient;
83
+ storeRoot?: string;
84
+ timeoutMs?: number;
85
+ }
86
+ export declare function pluginAdmissionRoot(): string;
87
+ export declare function validatePluginTarget(value: unknown): asserts value is PluginAdmissionTarget;
88
+ /** The accepted native command opens and hashes one inode, then executes that same descriptor. */
89
+ export declare function pluginResolverCommand(binding: PluginAdmissionBinding): string;
90
+ export declare function pluginBindingPath(storeRoot: string, id: string): string;
91
+ export declare function pluginReceiptPath(storeRoot: string, id: string, digest: string): string;
92
+ export declare function readPluginBinding(storeRoot: string, id: string): PluginAdmissionBinding;
93
+ export declare function readPluginAdmissionReceipt(storeRoot: string, id: string, digest: string): PluginAdmissionReceipt;
94
+ /** Command sources inherit an untrusted shell environment; authority comes from normal owner credentials. */
95
+ export declare function assertPluginAuthorityEnvironment(env?: Record<string, string | undefined>): void;
96
+ /** API reads only: a plan neither writes caches nor admits or executes package code. */
97
+ export declare function planPluginAdmission(spec: string, profileId: string, target: PluginAdmissionTarget, options?: PluginAdmissionOptions): Promise<PluginAdmissionPlan>;
98
+ /** Explicit local admission. Original archive and migrated payloads are already privately published. */
99
+ export declare function admitPlugin(spec: string, profileId: string, target: PluginAdmissionTarget, approvedDigest: string, approvedEvidenceDigest: string, options?: PluginAdmissionOptions): Promise<PluginAdmissionReceipt>;
100
+ /** Fresh authority on every invocation. No receipt, API failure or revocation ever returns a stale directory. */
101
+ export declare function resolveAdmittedPlugin(id: string, options?: PluginAdmissionOptions): Promise<string>;
@@ -0,0 +1,13 @@
1
+ export interface ManagedPluginRegistrationWitness {
2
+ bindingId: string;
3
+ storeRoot: string;
4
+ }
5
+ export declare function validateManagedPluginWitnesses(value: unknown): asserts value is ManagedPluginRegistrationWitness[];
6
+ /** This is a structured witness, not an exemption for a plugin ID or its cache directory. */
7
+ export declare function hashManagedPluginRegistry(path: string, managed: ManagedPluginRegistrationWitness[]): string;
8
+ export declare function captureManagedPluginRegistry(path: string, managedPlugins: ManagedPluginRegistrationWitness[]): {
9
+ path: string;
10
+ hashMode: "claude-plugin-registry";
11
+ managedPlugins: ManagedPluginRegistrationWitness[];
12
+ sha256: string;
13
+ };
@@ -0,0 +1,14 @@
1
+ import type { SkillBundleEntry } from "./skill-bundle.js";
2
+ import { type PluginFileWitness } from "./plugin-projection.js";
3
+ /** Snapshot every file, rejecting empty directories and any membership or metadata change. */
4
+ export declare function snapshotPluginTree(root: string, options?: {
5
+ nativeRuntime?: "claude-2.1.274" | "claude-2.1.276";
6
+ }): SkillBundleEntry[];
7
+ export declare function verifyPluginTree(root: string, witnesses: PluginFileWitness[]): void;
8
+ /** Sibling staging and an exclusive, nonwaiting lock serialize cooperative publishers. */
9
+ export declare function materializePluginTree(root: string, entries: SkillBundleEntry[]): void;
10
+ export declare function readPluginJson(path: string): unknown | null;
11
+ /** Atomic link publication never overwrites an existing receipt. */
12
+ export declare function writePluginJsonImmutable(path: string, value: unknown): void;
13
+ /** Hash the reviewed executable without executing it or allocating the entire binary. */
14
+ export declare function pluginExecutableDigest(path: string): string;
@@ -0,0 +1,80 @@
1
+ import type { SkillBundleEntry } from "./skill-bundle.js";
2
+ import type { SkillSelection } from "../types/skill-selection.js";
3
+ export declare const PLUGIN_PROJECTION_MANIFEST = "plugin-projection.json";
4
+ export declare const PLUGIN_PROJECTION_LIMITS: Readonly<{
5
+ files: 1024;
6
+ bytes: number;
7
+ fileBytes: number;
8
+ metadataBytes: number;
9
+ registrations: 64;
10
+ history: 128;
11
+ }>;
12
+ export interface PluginPayloadMapping {
13
+ path: string;
14
+ kind: "skill" | "command";
15
+ /** Digest of the original one-file tree, including its path and mode. */
16
+ sourceDigest: string;
17
+ target: Pick<SkillSelection, "slug" | "version" | "bundleDigest">;
18
+ }
19
+ interface PluginProjectionFields {
20
+ agent: "claude";
21
+ pluginId: string;
22
+ payloads: PluginPayloadMapping[];
23
+ }
24
+ interface PluginUpstream {
25
+ source: string;
26
+ revision: string;
27
+ license: string;
28
+ treeDigest: string;
29
+ }
30
+ interface PluginReview {
31
+ hooks: "reviewed-no-skill-injection";
32
+ dependencies: "reviewed-no-retired-payload-dependency";
33
+ }
34
+ export type PluginProjectionManifest = PluginProjectionFields & ({
35
+ schemaVersion: 1;
36
+ upstream: PluginUpstream & {
37
+ version: string;
38
+ };
39
+ review: PluginReview;
40
+ } | {
41
+ schemaVersion: 2;
42
+ upstream: PluginUpstream & {
43
+ version: string | null;
44
+ };
45
+ review: PluginReview & {
46
+ documentation: Array<{
47
+ path: string;
48
+ sourceDigest: string;
49
+ }>;
50
+ };
51
+ });
52
+ export interface PluginFileWitness {
53
+ path: string;
54
+ mode: number;
55
+ size: number;
56
+ sha256: string;
57
+ }
58
+ export interface PluginProjection {
59
+ manifest: PluginProjectionManifest;
60
+ payloads: PluginPayloadMapping[];
61
+ files: SkillBundleEntry[];
62
+ witnesses: PluginFileWitness[];
63
+ originalTreeDigest: string;
64
+ projectionTreeDigest: string;
65
+ removed: PluginFileWitness[];
66
+ }
67
+ export declare function pluginRefusal(message: string): never;
68
+ export declare function pluginNeed(value: unknown, message: string): asserts value;
69
+ export declare const pluginHash: (bytes: string | Uint8Array) => string;
70
+ export declare function pluginObject(value: unknown): value is Record<string, unknown>;
71
+ export declare function pluginKeys(value: unknown, keys: string[]): asserts value is Record<string, unknown>;
72
+ export declare function pluginText(value: unknown, limit?: number): asserts value is string;
73
+ export declare function pluginId(value: unknown): asserts value is string;
74
+ export declare function pluginDigest(value: unknown): asserts value is string;
75
+ export declare function validatePluginManifest(value: unknown): asserts value is PluginProjectionManifest;
76
+ export declare function pluginFileWitnesses(entries: SkillBundleEntry[]): PluginFileWitness[];
77
+ export declare function pluginTreeDigest(entries: SkillBundleEntry[]): string;
78
+ /** Pure projection: no filesystem access, upstream commands, package installation or publication. */
79
+ export declare function buildPluginProjection(entries: SkillBundleEntry[]): PluginProjection;
80
+ export {};
@@ -1,18 +1,28 @@
1
1
  import type { ResolvedSkillProfile, StationSkillState, StationSkillStateInput } from "../types/skill-selection.js";
2
2
  export interface ProfileClient {
3
3
  readonly authority: string;
4
+ /** Stable whoami subject; raw credential/key identities are intentionally excluded. */
5
+ resolvePrincipal?(): Promise<AuthenticatedProfilePrincipal>;
4
6
  resolveProfile(id: string): Promise<ResolvedSkillProfile>;
5
7
  recordStation(id: string, state: StationSkillStateInput): Promise<StationSkillState>;
6
8
  getBundle(slug: string, version: string): Promise<Response | null>;
7
9
  }
10
+ export type AuthenticatedProfileRole = "owner" | "admin" | "member" | "viewer";
11
+ export interface AuthenticatedProfilePrincipal {
12
+ userId: string;
13
+ accountId: string;
14
+ role: AuthenticatedProfileRole;
15
+ }
8
16
  export declare class HttpProfileClient implements ProfileClient {
9
17
  private key;
10
18
  readonly authority: string;
11
19
  private origin;
12
20
  constructor(key: string, origin: string);
21
+ private requestRoute;
13
22
  private request;
14
23
  private read;
15
24
  resolveProfile(id: string): Promise<ResolvedSkillProfile>;
25
+ resolvePrincipal(): Promise<AuthenticatedProfilePrincipal>;
16
26
  recordStation(id: string, input: StationSkillStateInput): Promise<StationSkillState>;
17
27
  getBundle(slug: string, version: string): Promise<Response | null>;
18
28
  }
@@ -62,7 +62,7 @@ export declare function readRecurringRecovery(directory: string): {
62
62
  operation: "revoke";
63
63
  consentId: string;
64
64
  };
65
- phase: "pending" | "prepared" | "observed";
65
+ phase: "prepared" | "pending" | "observed";
66
66
  consentId: string | null;
67
67
  };
68
68
  export interface RecurringRecoveryOptions {
@@ -76,7 +76,7 @@ export declare function continueRecurringRecovery(customer: RecurringCustomer, d
76
76
  recoveryDirectory: string;
77
77
  operation: "activate" | "revoke";
78
78
  outcomeUnknown: boolean;
79
- phase: "pending" | "prepared" | "observed";
79
+ phase: "prepared" | "pending" | "observed";
80
80
  result: unknown;
81
81
  }>;
82
82
  export {};
@@ -219,7 +219,7 @@ export declare function executeRecurringSurface(action: RecurringSurfaceAction,
219
219
  recoveryDirectory: string;
220
220
  operation: "activate" | "revoke";
221
221
  outcomeUnknown: boolean;
222
- phase: "pending" | "prepared" | "observed";
222
+ phase: "prepared" | "pending" | "observed";
223
223
  result: unknown;
224
224
  } | {
225
225
  cancelled: boolean;
@@ -7,8 +7,10 @@ import { type RemoteWorkspaceMemberErrorCode, type SetRemoteWorkspaceMemberRole,
7
7
  import { type RemoteSkillRunContract } from "./remote-run-contract.js";
8
8
  import { type RemoteCreditPack, type RemoteRunApproval, type RemoteRunQuote } from "./remote-account.js";
9
9
  import { type RemoteInputFile, type RemoteInputFileDescriptor } from "./remote-files.js";
10
+ import { type RemoteCreditCheckout, type RemoteCreditCheckoutOptions, type RemoteCreditCheckoutErrorCode } from "./remote-credit-checkout.js";
10
11
  import { type UpdateRemoteProfile, type UpdateRemoteWorkspace } from "./remote-profile.js";
11
12
  import { type RemoteQuoteUnavailableCode } from "./remote-quote-errors.js";
13
+ import { type RemoteSkillsAccess } from "./remote-permissions.js";
12
14
  export type { RemoteQuoteUnavailableCode } from "./remote-quote-errors.js";
13
15
  /**
14
16
  * A server that predates this client's pin/tag/incremental-sync routes answered
@@ -28,6 +30,13 @@ export declare class RemoteRequestError extends Error {
28
30
  readonly status: number;
29
31
  constructor(path: string, status: number, _statusText?: string);
30
32
  }
33
+ /** Bounded checkout outcome; the key is caller-owned, never copied from a server error. */
34
+ export declare class RemoteCreditCheckoutError extends RemoteRequestError {
35
+ readonly code: RemoteCreditCheckoutErrorCode;
36
+ readonly requestIdempotencyKey: string;
37
+ readonly retryAfterSeconds?: number | undefined;
38
+ constructor(code: RemoteCreditCheckoutErrorCode, status: number, requestIdempotencyKey: string, retryAfterSeconds?: number | undefined);
39
+ }
31
40
  /** A recognized unavailable quote; status compatibility and client-owned copy. */
32
41
  export declare class RemoteQuoteUnavailableError extends RemoteRequestError {
33
42
  readonly code: RemoteQuoteUnavailableCode;
@@ -123,7 +132,9 @@ export declare class RemoteSkillsClient {
123
132
  /** Low-level admission transport; interactive surfaces use submitQuotedRun. */
124
133
  submitRun(slug: string, input?: Record<string, unknown>, args?: string[], approval?: RemoteRunApproval): Promise<RemoteSkillRunContract>;
125
134
  quoteRun(slug: string, input?: Record<string, unknown>, args?: string[], files?: RemoteInputFileDescriptor[]): Promise<RemoteRunQuote>;
126
- getCapabilities(): Promise<{
135
+ getCapabilities(options?: {
136
+ refresh?: boolean;
137
+ }): Promise<RemoteSkillsAccess & {
127
138
  contractVersion: 1;
128
139
  apiVersion: 1;
129
140
  capabilities: string[];
@@ -193,9 +204,8 @@ export declare class RemoteSkillsClient {
193
204
  formattedCreditBalance: string;
194
205
  }>;
195
206
  listCreditPacks(): Promise<RemoteCreditPack[]>;
196
- createCreditCheckout(packId: string): Promise<{
197
- url: string;
198
- }>;
207
+ /** One checkout POST. Retain an explicit key before calling to recover even a lost process. */
208
+ createCreditCheckout(packId: string, options?: RemoteCreditCheckoutOptions): Promise<RemoteCreditCheckout>;
199
209
  getUsage(): Promise<Record<string, unknown>[]>;
200
210
  listInvoices(): Promise<Record<string, unknown>[]>;
201
211
  createBillingCheckout(): Promise<{
@@ -0,0 +1,22 @@
1
+ /** Caller-owned checkout identity. Reuse only for the same server, account and pack. */
2
+ export interface RemoteCreditCheckoutOptions {
3
+ idempotencyKey?: string;
4
+ }
5
+ export interface RemoteCreditCheckout {
6
+ url: string;
7
+ requestIdempotencyKey: string;
8
+ }
9
+ export declare const creditCheckoutKeyPattern: RegExp;
10
+ export declare function creditCheckoutRequestKey(value: unknown): string;
11
+ export declare const creditCheckoutMessages: {
12
+ readonly CREDIT_CHECKOUT_UNCONFIRMED: "Checkout creation could not be confirmed. Keep the request idempotency key, inspect billing, and use the same key for an explicit retry on the same server, account and pack. No automatic retry was made.";
13
+ readonly CREDIT_CHECKOUT_IN_PROGRESS: "This checkout is still being created. Wait before explicitly retrying the same server, account and pack with the same request idempotency key. No automatic retry was made.";
14
+ readonly CREDIT_CHECKOUT_EXPIRED: "This checkout has expired. Inspect billing before deliberately starting a new purchase with a new request idempotency key. Do not retry this checkout automatically.";
15
+ readonly CREDIT_CHECKOUT_FULFILLED: "This checkout is already fulfilled. Inspect the account balance; do not create another checkout to recover this purchase.";
16
+ };
17
+ export type RemoteCreditCheckoutErrorCode = keyof typeof creditCheckoutMessages;
18
+ /** Only recognized server states become typed outcomes; never retain server text. */
19
+ export declare function creditCheckoutFailure(value: unknown, status: number, key: string): {
20
+ code: RemoteCreditCheckoutErrorCode;
21
+ retryAfterSeconds?: number;
22
+ };
@@ -1,3 +1,4 @@
1
+ import type { RemoteCreditCheckoutOptions } from "./remote-credit-checkout.js";
1
2
  import type { RemoteSkillsClient } from "./remote-client.js";
2
3
  /** Shared customer operation declarations for MCP registration and its discovery contract. */
3
4
  export declare const REMOTE_CUSTOMER_OPERATIONS: readonly [{
@@ -11,7 +12,7 @@ export declare const REMOTE_CUSTOMER_OPERATIONS: readonly [{
11
12
  readonly title: "Get Server Capabilities";
12
13
  readonly parameter: null;
13
14
  readonly mutates: false;
14
- readonly invoke: (client: RemoteSkillsClient) => Promise<{
15
+ readonly invoke: (client: RemoteSkillsClient) => Promise<import("./remote-permissions.js").RemoteSkillsAccess & {
15
16
  contractVersion: 1;
16
17
  apiVersion: 1;
17
18
  capabilities: string[];
@@ -49,9 +50,7 @@ export declare const REMOTE_CUSTOMER_OPERATIONS: readonly [{
49
50
  readonly title: "Create Credit Checkout";
50
51
  readonly parameter: "pack_id";
51
52
  readonly mutates: true;
52
- readonly invoke: (client: RemoteSkillsClient, value: string) => Promise<{
53
- url: string;
54
- }>;
53
+ readonly invoke: (client: RemoteSkillsClient, value: string, options?: RemoteCreditCheckoutOptions) => Promise<import("./remote-credit-checkout.js").RemoteCreditCheckout>;
55
54
  }, {
56
55
  readonly name: "get_billing_usage";
57
56
  readonly title: "Get Billing Usage";
@@ -0,0 +1,24 @@
1
+ /** Effective access belongs to the credential, independently of workspace role. */
2
+ export interface RemoteSkillsPermissions {
3
+ read?: boolean;
4
+ publish?: boolean;
5
+ profilesWrite?: boolean;
6
+ stationStateWrite?: boolean;
7
+ cloudSubmit?: boolean;
8
+ executionGrantsRead?: boolean;
9
+ executionGrantsWrite?: boolean;
10
+ executionGrantsResolve?: boolean;
11
+ }
12
+ export interface RemoteSkillsAccess {
13
+ scopes?: string[];
14
+ permissions?: RemoteSkillsPermissions;
15
+ }
16
+ /** Pick known fields only; never reflect additive provider fields into diagnostics. */
17
+ export declare function parseSkillsAccess(value: unknown): RemoteSkillsAccess;
18
+ export declare class RemoteSkillsPermissionError extends Error {
19
+ readonly permission: "publish" | "profilesWrite";
20
+ readonly code = "SKILLS_PERMISSION_DENIED";
21
+ readonly status = 403;
22
+ constructor(permission: "publish" | "profilesWrite");
23
+ }
24
+ export declare function assertSkillsPermission(access: RemoteSkillsAccess, permission: "publish" | "profilesWrite"): void;
@@ -13,9 +13,28 @@ export interface CachedSelectionProfile {
13
13
  verifiedAt: string;
14
14
  profile: ResolvedSkillProfile;
15
15
  }
16
+ export interface SkillSessionParentBinding {
17
+ sessionId: string;
18
+ generation: number;
19
+ receiptSha256: string;
20
+ }
16
21
  export interface SkillSessionReceipt extends CachedSelectionProfile {
17
22
  sessionId: string;
18
23
  loaded: string[];
24
+ /** Monotonic across sanctioned receipt writes. Legacy receipts without it are generation zero. */
25
+ generation?: number;
26
+ /** Private local metadata binding a child receipt to the exact parent snapshot it inherited. */
27
+ parent?: SkillSessionParentBinding;
28
+ }
29
+ export interface SkillSessionSnapshot extends SkillSessionParentBinding {
30
+ path: string;
31
+ bytes: Uint8Array;
32
+ receipt: SkillSessionReceipt;
33
+ sha256: string;
34
+ }
35
+ export interface SkillSessionWritePrecondition {
36
+ current: SkillSessionParentBinding | null;
37
+ parent?: SkillSessionParentBinding;
19
38
  }
20
39
  export declare const MAX_CACHED_PROFILE_AGE_MS: number;
21
40
  export declare const SELECTION_LOCK_FILE = "selection.lock.json";
@@ -38,3 +57,16 @@ export declare function projectSelectionLockPath(projectDir: string): string;
38
57
  export declare function readProjectSelection(projectDir: string): CachedSelectionProfile | null;
39
58
  export declare function sessionReceiptPath(sessionId: string, options?: SelectionCacheOptions): string;
40
59
  export declare function readSkillSession(sessionId: string, options?: SelectionCacheOptions): SkillSessionReceipt | null;
60
+ export declare function readSkillSessionSnapshotIfExists(sessionId: string, options?: SelectionCacheOptions): SkillSessionSnapshot | null;
61
+ export declare function readSkillSessionSnapshot(sessionId: string, options?: SelectionCacheOptions): SkillSessionSnapshot;
62
+ export declare function skillSessionSnapshotBinding(snapshot: SkillSessionSnapshot): SkillSessionParentBinding;
63
+ export declare function nextSkillSessionGeneration(generation: number): number;
64
+ /** Receipt creation and updates are exact-snapshot CAS operations. Child creation also fences its parent. */
65
+ export declare function writeSkillSession(receipt: SkillSessionReceipt, expected: SkillSessionWritePrecondition, options?: SelectionCacheOptions): void;
66
+ /** Preserve both sides before the single atomic replacement; failures retain recovery evidence. */
67
+ export declare function replaceSkillSession(expected: SkillSessionParentBinding, replacement: SkillSessionReceipt, plan: unknown, options?: SelectionCacheOptions, beforeCommit?: () => void): {
68
+ archivePath: string;
69
+ receiptPath: string;
70
+ beforeSha256: string;
71
+ afterSha256: string;
72
+ };
@@ -2,7 +2,7 @@
2
2
  import { type ProfileClient } from "./profile-client.js";
3
3
  import type { ResolvedSkillProfile, ResolvedSkillSelection } from "../types/skill-selection.js";
4
4
  import type { SkillBundleEntry } from "./skill-bundle.js";
5
- import { type CachedSelectionProfile, type SelectionCacheOptions, type SkillSessionReceipt } from "./selection-cache.js";
5
+ import { type CachedSelectionProfile, type SelectionCacheOptions, type SkillSessionParentBinding, type SkillSessionReceipt } from "./selection-cache.js";
6
6
  export interface SelectionResolverOptions extends SelectionCacheOptions {
7
7
  client?: ProfileClient;
8
8
  cached?: boolean;
@@ -31,6 +31,8 @@ export declare function syncSelectionProfile(profileId: string, options?: SyncSe
31
31
  export interface ResolvedSelectionContext {
32
32
  receipt: CachedSelectionProfile;
33
33
  session: SkillSessionReceipt | null;
34
+ sessionSnapshot: SkillSessionParentBinding | null;
35
+ parentSnapshot?: SkillSessionParentBinding;
34
36
  inheritedLoaded?: string[];
35
37
  client?: ProfileClient;
36
38
  }