qlogicagent 2.15.6 → 2.15.8

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.
@@ -22,6 +22,12 @@ export declare function handleSkillsActivate(this: SkillsHandlerHost, msg: Agent
22
22
  export declare function handleSkillsDeactivate(this: SkillsHandlerHost, msg: AgentRpcRequest): void;
23
23
  export declare function handleSkillsDelete(this: SkillsHandlerHost, msg: AgentRpcRequest): Promise<void>;
24
24
  export declare function handleSkillsPromote(this: SkillsHandlerHost, msg: AgentRpcRequest): void;
25
+ /**
26
+ * Per-project opt-out: mute (or un-mute) a globally-installed skill in ONE project so the agent
27
+ * doesn't see it there — cuts context and mis-triggering without uninstalling the skill. The file
28
+ * stays in the global store and stays active in every other project. This is the "本项目技能" toggle.
29
+ */
30
+ export declare function handleSkillsSetProjectDisabled(this: SkillsHandlerHost, msg: AgentRpcRequest): void;
25
31
  export declare function handleSkillsStats(this: SkillsHandlerHost, msg: AgentRpcRequest): void;
26
32
  export declare function handleSkillsPin(this: SkillsHandlerHost, msg: AgentRpcRequest): void;
27
33
  export declare function handleSkillsUnpin(this: SkillsHandlerHost, msg: AgentRpcRequest): void;
@@ -2,10 +2,12 @@ export interface SkillListEntry {
2
2
  id: string;
3
3
  name: string;
4
4
  path: string;
5
- /** Effective in this project: globally-scoped, or enabled via the project manifest. */
5
+ /** Effective in this project: not globally muted and not muted in this project. */
6
6
  active: boolean;
7
- /** Enablement scope (= lifecycle enabledScope). "global" is active everywhere. */
8
- scope: "project" | "global";
7
+ /** Muted everywhere (the plugin card's off switch). */
8
+ globallyDisabled: boolean;
9
+ /** Muted in THIS project only (the project opt-out). */
10
+ projectDisabled: boolean;
9
11
  category: "automation";
10
12
  displayName: {
11
13
  key: string;
@@ -0,0 +1,19 @@
1
+ export interface RmWithRetryOptions {
2
+ /** Retries after the first attempt before giving up. Default 5. */
3
+ retries?: number;
4
+ /** Base backoff in ms; doubles each retry (100, 200, 400, …). Default 100. */
5
+ baseDelayMs?: number;
6
+ /** Removal primitive — injectable so the retry/backoff logic is unit-testable. */
7
+ remove?: (target: string) => void;
8
+ }
9
+ /**
10
+ * Recursively remove a path, retrying with exponential backoff when Windows
11
+ * reports a transient file lock.
12
+ *
13
+ * Cleanup is best-effort: a leaked directory under the OS temp dir is harmless
14
+ * and the OS reaps it, so exhausting the retries warns (loud and visible) instead
15
+ * of throwing — a teardown race must never fail a test that already verified the
16
+ * behaviour under test. A *non-transient* error (e.g. a bad path) is a real bug
17
+ * and is rethrown immediately.
18
+ */
19
+ export declare function rmWithRetry(target: string, options?: RmWithRetryOptions): Promise<void>;
@@ -5,7 +5,7 @@
5
5
  * external ACP-compatible agent CLIs alongside qlogicagent's internal agents.
6
6
  */
7
7
  import type { AgentCapabilities, CustomAgentDef, AgentConfig, ProductTaskStatus, ProductInstanceDef, ProductTaskDef } from "./agent-methods.js";
8
- export type { AgentCategory, AgentProtocol, AgentStatus, AgentCapabilities, AgentDescriptor, CustomAgentDef, AgentConfig, SoloState, SoloAgentState, SoloAgentResult, SoloStatus, ProductPhase, ProductTaskStatus, ProductInstanceDef, ProductTaskDef, ProductStatus, ProductSummary, } from "./agent-methods.js";
8
+ export type { AgentCategory, AgentProtocol, AgentStatus, AgentCapabilities, AgentDescriptor, CustomAgentDef, AgentConfig, AuthPreference, SoloState, SoloAgentState, SoloAgentResult, SoloStatus, ProductPhase, ProductTaskStatus, ProductInstanceDef, ProductTaskDef, ProductStatus, ProductSummary, } from "./agent-methods.js";
9
9
  /** Pre-defined configuration for a known ACP backend. */
10
10
  export interface AcpBackendConfig {
11
11
  /** Backend ID, e.g. "claude" | "codex" | "goose". */
@@ -71,6 +71,13 @@ export interface ExternalAgentDescriptor {
71
71
  acpArgs: string[];
72
72
  /** Extra environment variables for spawn. */
73
73
  env?: Record<string, string>;
74
+ /**
75
+ * Env var names the spawn layer must DELETE from the merged child env (after Object.assign of `env`),
76
+ * so a forwarded/stale shell key cannot override the CLI's own native login. Set in `account` mode
77
+ * (the load-bearing fix for the env-injection-override bug — buildBackendEnv alone cannot unset a
78
+ * whitelisted forwarded key). See acp-detector.resolveSuppressEnvKeys.
79
+ */
80
+ suppressEnvKeys?: string[];
74
81
  /** Communication protocol. */
75
82
  protocol: "acp";
76
83
  }
@@ -46,6 +46,15 @@ export interface CustomAgentDef {
46
46
  skillsDirs?: string[];
47
47
  supportsBaseUrlOverride?: boolean;
48
48
  }
49
+ /**
50
+ * Per-agent credential source preference (BYO-account/key axis).
51
+ * - "auto" : ladder — detected native account → user's own key → managed gateway (default; reproduces legacy behavior).
52
+ * - "managed" : force the platform gateway (sk-qllm); guarantees a first conversation, ignores any user key.
53
+ * - "account" : use the CLI's own native account/subscription login; inject NO key (the spawn layer also deletes any
54
+ * forwarded key) so the CLI's own credential store wins. Not available for claude (Anthropic bans 3rd-party OAuth).
55
+ * - "userKey" : use the user's own provider API key against the official endpoint (no gateway baseUrl override).
56
+ */
57
+ export type AuthPreference = "auto" | "managed" | "account" | "userKey";
49
58
  /** Persisted per-agent configuration. */
50
59
  export interface AgentConfig {
51
60
  apiKey?: string;
@@ -55,6 +64,8 @@ export interface AgentConfig {
55
64
  customArgs?: string[];
56
65
  customCliPath?: string;
57
66
  modelIdMap?: Record<string, string>;
67
+ /** Credential-source preference (undefined ⇒ "auto"; see AuthPreference). No migration needed. */
68
+ authPreference?: AuthPreference;
58
69
  }
59
70
  export type SoloState = "running" | "evaluating" | "completed" | "cancelled" | "failed";
60
71
  export type SoloAgentState = "pending" | "running" | "completed" | "failed" | "idle";
@@ -1620,26 +1631,35 @@ export interface AgentRpcMethodMap extends GatewayRpcMethodMap {
1620
1631
  "skills.delete": {
1621
1632
  params: {
1622
1633
  name: string;
1623
- scope?: "project" | "global";
1624
1634
  };
1625
1635
  result: {
1626
1636
  ok: true;
1627
1637
  deleted: string;
1628
- scope: string;
1629
1638
  signalStatus?: string;
1630
1639
  };
1631
1640
  };
1632
1641
  "skills.promote": {
1633
1642
  params: {
1634
1643
  name: string;
1635
- keepLocal?: boolean;
1636
1644
  };
1637
1645
  result: {
1638
1646
  ok: true;
1639
1647
  promoted: string;
1640
1648
  scope: "global";
1641
1649
  globalPath: string;
1642
- removedFromProject: boolean;
1650
+ };
1651
+ };
1652
+ "skills.setProjectDisabled": {
1653
+ params: {
1654
+ name: string;
1655
+ disabled: boolean;
1656
+ projectId?: string;
1657
+ };
1658
+ result: {
1659
+ ok: true;
1660
+ name: string;
1661
+ disabled: boolean;
1662
+ changed: boolean;
1643
1663
  };
1644
1664
  };
1645
1665
  "skills.stats": {
@@ -438,7 +438,10 @@ export interface GatewayRpcMethodMap {
438
438
  name: string;
439
439
  path: string;
440
440
  active: boolean;
441
- scope: "project" | "global";
441
+ /** Muted everywhere (the plugin card's off switch). */
442
+ globallyDisabled: boolean;
443
+ /** Muted in THIS project only (the per-project opt-out). */
444
+ projectDisabled: boolean;
442
445
  category: CapabilityCategoryWire;
443
446
  displayName: LocalizedTextWire;
444
447
  displayDescription: LocalizedTextWire;
@@ -446,6 +449,7 @@ export interface GatewayRpcMethodMap {
446
449
  fallbackDescription: string;
447
450
  systemGenerated: boolean;
448
451
  version?: string;
452
+ registryVersion?: string;
449
453
  description?: string;
450
454
  }>;
451
455
  };
@@ -639,6 +643,16 @@ export interface AgentCallOptions {
639
643
  idempotencyKey?: string;
640
644
  traceId?: string;
641
645
  }
642
- export type AgentCall = <M extends keyof GatewayRpcMethodMap>(method: M, params: GatewayRpcMethodMap[M]["params"], options?: AgentCallOptions) => Promise<GatewayRpcMethodMap[M]["result"]>;
646
+ /**
647
+ * Gateway → agent RPC. The gateway is a GENERIC proxy: alongside the strongly-typed methods in
648
+ * GatewayRpcMethodMap (first call signature — params/result inferred), it must forward arbitrary
649
+ * methods by name — dynamic dispatch, and methods not (yet) in the map — via the second signature
650
+ * (opaque params/result). The typed signature is listed first so known method literals resolve to
651
+ * it; a plain `string` method falls through to the proxy signature.
652
+ */
653
+ export type AgentCall = {
654
+ <M extends keyof GatewayRpcMethodMap>(method: M, params: GatewayRpcMethodMap[M]["params"], options?: AgentCallOptions): Promise<GatewayRpcMethodMap[M]["result"]>;
655
+ (method: string, params?: Record<string, unknown>, options?: AgentCallOptions): Promise<unknown>;
656
+ };
643
657
  /** All gateway-facing RPC method names (runtime array). */
644
658
  export declare const GATEWAY_RPC_METHODS: (keyof GatewayRpcMethodMap)[];
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * Reference: AionUI AcpDetector + ACP_BACKENDS_ALL registry.
8
8
  */
9
- import type { AcpBackendConfig, AgentDescriptor, AgentConfigStoreData } from "../../protocol/wire/acp-agent-management.js";
9
+ import type { AcpBackendConfig, AgentDescriptor, AgentConfigStoreData, AuthPreference } from "../../protocol/wire/acp-agent-management.js";
10
10
  export declare const ACP_BACKENDS: Record<string, AcpBackendConfig>;
11
11
  export declare const AGENT_CATALOG_IDS: readonly ["claude", "codex", "gemini", "copilot", "cursor", "qwen", "kimi", "glm", "opencode", "kiro", "qoder", "openclaw", "hermes"];
12
12
  /** Merged catalog entry (base ACP config + grid/install metadata) for one id. */
@@ -85,16 +85,40 @@ export interface CatalogKeySources {
85
85
  baseUrl?: string;
86
86
  model?: string;
87
87
  };
88
+ /** Probe: does this agent have a usable native account/subscription login (e.g. ~/.codex/auth.json,
89
+ * present + not expired)? Drives `account`-mode readiness and `auto`'s account-first preference.
90
+ * Tri-state: true = detected login, false = probeable but not logged in (blocks readiness), undefined
91
+ * = no probe for this agent (treated as ready-if-installed; auto does NOT pick account). Wired in Cut 4. */
92
+ hasSubscription?: (agentId: string) => boolean | undefined;
88
93
  }
94
+ /** Resolve the effective mode, coercing unsupported combinations (claude.account → auto). */
95
+ export declare function effectiveAuthPreference(entry: AcpBackendConfig, mode: AuthPreference): AuthPreference;
89
96
  /**
90
- * Resolve the login-free key env for a catalog agent from the unified key sources.
91
- * "set key once in our system → all agents" the user never logs into each CLI.
92
- * - authMode "oauth" / keySource "none" {} (no bypass; account required)
93
- * - keySource "llmrouter" gateway sk-qllm + gateway baseUrl (agent calls our gateway)
94
- * - keySource "direct" → the user's provider key (agent calls the provider directly)
95
- * Only emits env vars the backend actually declares (apiKeyEnvVar / baseUrlEnvVar).
97
+ * The credential-source modes the UI should offer for an agent (derived from catalog metadata, the
98
+ * single source of truthspec §4.2/§8.2/§15). Never includes a permanently-dead option:
99
+ * - oauth / keyless (copilot/cursor/kimi/…): ["account"] only (no gateway endpoint to repoint)
100
+ * - claude (Anthropic bans subscription): ["managed","userKey"] no account
101
+ * - codex (llmrouter + account-capable): ["account","userKey","managed"]
102
+ * - other llmrouter agents: ["userKey","managed"]
103
+ * - direct agents (qwen/glm/…): ["userKey"] (managed wiring for qwen/opencode is Cut 7)
96
104
  */
97
- export declare function resolveCatalogKeyEnv(entry: AcpBackendConfig | undefined, sources: CatalogKeySources): Record<string, string>;
105
+ export declare function getAvailableAuthModes(entry: AcpBackendConfig): AuthPreference[];
106
+ /**
107
+ * Resolve the login-free key env for a catalog agent, honoring the per-agent `authPreference`.
108
+ * - "account" → {} (inject nothing; the spawn layer also deletes the forwarded key, see resolveSuppressEnvKeys)
109
+ * - "userKey" → the user's own provider key against the official endpoint (no gateway baseUrl/model)
110
+ * - "managed" → force the gateway sk-qllm + baseUrl (ignores any user key) — guarantees a first conversation
111
+ * - "auto" → ladder: user's own key (if present) → managed gateway → host-model passthrough
112
+ * oauth / keyless agents never get a key injected. Only emits env vars the backend declares.
113
+ */
114
+ export declare function resolveCatalogKeyEnv(entry: AcpBackendConfig | undefined, sources: CatalogKeySources, mode?: AuthPreference): Record<string, string>;
115
+ /**
116
+ * Env var names the spawn layer must DELETE from the merged child env (account mode), so a
117
+ * forwarded/stale shell key cannot override the CLI's own native login. This is the load-bearing
118
+ * half of the §1.3 override-bug fix: buildBackendEnv alone cannot unset a whitelist-forwarded key.
119
+ * Returns [] when not in account mode (or the agent has no account path — claude is coerced to auto).
120
+ */
121
+ export declare function resolveSuppressEnvKeys(entry: AcpBackendConfig | undefined, mode: AuthPreference, sources?: CatalogKeySources): string[];
98
122
  export declare class AcpDetector {
99
123
  private cache;
100
124
  private configStore;
@@ -62,7 +62,30 @@ export interface AcpAuthMethod {
62
62
  id?: string;
63
63
  name?: string;
64
64
  description?: string;
65
+ /**
66
+ * ACP auth-method discriminator (when the agent follows the discriminated schema):
67
+ * - "agent" : the agent self-drives login (opens its own browser / prints a device code)
68
+ * - "env_var" : the user supplies a key — surface a key dialog (≈ userKey)
69
+ * - "terminal" : run an interactive login command (the agent's own `login` subcommand)
70
+ * Drives how the host routes the login affordance (see classifyAuthMethod). Absent on agents that
71
+ * don't tag their methods — those fall back to guide-only.
72
+ */
73
+ type?: "agent" | "env_var" | "terminal" | string;
74
+ /** For a terminal-type method: the command + args the host runs to start the interactive login. */
75
+ args?: string[];
76
+ /** For a terminal-type method: extra env for the login command. */
77
+ env?: Record<string, string>;
65
78
  }
79
+ /** How the host should surface a login for an advertised auth method (spec §7). */
80
+ export type AuthMethodRouting = "agent" | "key" | "terminal" | "guide";
81
+ /**
82
+ * Route an advertised auth method to the right login affordance:
83
+ * agent → drive the ACP `authenticate` request; env_var → open a key dialog (userKey); terminal →
84
+ * run the agent's interactive login command; otherwise guide-only (show the command, user runs it).
85
+ * Gating login on a concrete classified method (never a heuristic) prevents firing an uncompletable
86
+ * login (e.g. claude's key-only method) — spec §7 risk.
87
+ */
88
+ export declare function classifyAuthMethod(method: AcpAuthMethod | undefined): AuthMethodRouting;
66
89
  /**
67
90
  * Thrown when an agent rejects session setup because the user must authenticate first — e.g. kimi
68
91
  * returns -32000 "Authentication required" with a `kimi login` authMethod. Carries the agent's
@@ -66,8 +66,8 @@ export declare function getProjectAgentDir(cwd: string): string;
66
66
  export declare function getProjectWorkflowsDir(cwd: string): string;
67
67
  /** `<cwd>/.qlogicagent/plugins/` */
68
68
  export declare function getProjectPluginsDir(cwd: string): string;
69
- /** `<cwd>/.qlogicagent/skills-enabled.json` — per-project skill enablement manifest. */
70
- export declare function getProjectSkillManifestPath(cwd: string): string;
69
+ /** `<cwd>/.qlogicagent/skills-disabled.json` — per-project skill opt-out (mute) list. */
70
+ export declare function getProjectSkillDisabledPath(cwd: string): string;
71
71
  /** `<cwd>/.qlogicagent/settings.json` */
72
72
  export declare function getProjectSettingsPath(cwd: string): string;
73
73
  /** `<cwd>/.qlogicagent/INSTRUCTIONS.md` */
@@ -141,6 +141,17 @@ export interface AgentProcessCallbacks {
141
141
  sessionDir?: string;
142
142
  }
143
143
  export declare function ensureLoopbackNoProxy(env: Record<string, string>): Record<string, string>;
144
+ /**
145
+ * Merge an external descriptor's env onto the forwarded base env, then DELETE any `suppressEnvKeys`
146
+ * (account mode). This is the load-bearing half of the env-injection-override fix: `Object.assign` can
147
+ * only add/overwrite, so a whitelist-forwarded `ANTHROPIC_API_KEY`/`OPENAI_API_KEY` (from the gateway's
148
+ * own shell) would otherwise survive into the child and override the CLI's native subscription login.
149
+ * Deleting after the merge guarantees `account` mode hands credential control to the CLI's own store.
150
+ */
151
+ export declare function applyExternalDescriptorEnv(env: Record<string, string>, ext: {
152
+ env?: Record<string, string>;
153
+ suppressEnvKeys?: string[];
154
+ }): Record<string, string>;
144
155
  /**
145
156
  * Build LLM-related environment variables for an external ACP teammate.
146
157
  *
@@ -30,6 +30,13 @@ export interface PooledAgentEntry {
30
30
  current: PooledTurnCtx | null;
31
31
  };
32
32
  lastUsed: number;
33
+ /**
34
+ * Set when a credential-mode change (authPreference / key / login) arrived while this process was
35
+ * mid-turn. The env is baked at spawn, so the switch can only take effect by respawning — but we must
36
+ * not abort the in-flight turn (D2: "下条消息生效"). The turn-runner evicts the entry once the turn
37
+ * settles (settlePooledTurn), so the NEXT turn respawns with the new credentials.
38
+ */
39
+ pendingRecredential?: boolean;
33
40
  }
34
41
  /** Pool key: same agent + workspace + session ⇒ same resident process (and shared memory). */
35
42
  export declare function poolKey(agentId: string, cwd: string, sessionId?: string): string;
@@ -47,6 +54,22 @@ export declare function adoptWarmedAgent(warmKey: string, sessionKey: string): P
47
54
  export declare const WARM_SESSION = "__warm__";
48
55
  /** Drop + dispose a single entry (e.g. after a turn errors and the process is suspect). */
49
56
  export declare function evictPooledAgent(key: string): void;
57
+ /**
58
+ * Invalidate every resident process for an agent after a credential-mode change (authPreference / key /
59
+ * login). The spawn env is baked at spawn time and reused across turns, so a switch only takes effect by
60
+ * respawning. Idle/warm entries are evicted immediately; a busy entry (turn in flight) is flagged
61
+ * `pendingRecredential` and evicted by settlePooledTurn when its turn settles (D2: never abort the
62
+ * in-flight turn — the switch lands on the next message). Returns counts for UI messaging.
63
+ */
64
+ export declare function evictPooledAgentsByAgentId(agentId: string): {
65
+ evicted: number;
66
+ deferred: number;
67
+ };
68
+ /**
69
+ * Call when a pooled turn settles (success or error). If a credential change arrived mid-turn
70
+ * (pendingRecredential), evict now so the next turn respawns with the new credentials.
71
+ */
72
+ export declare function settlePooledTurn(key: string): void;
50
73
  /** Kill + dispose every pooled agent and stop the reaper. Call on agent shutdown. */
51
74
  export declare function disposeAllPooledAgents(): void;
52
75
  export declare function pooledAgentCount(): number;
@@ -1,17 +1,20 @@
1
1
  /**
2
2
  * One-shot migration: fold legacy per-project skill directories
3
- * (`<projectRoot>/.qlogicagent/skills/<name>/`) into the single global store,
4
- * marking them project-scoped and enabling them in the originating project.
3
+ * (`<projectRoot>/.qlogicagent/skills/<name>/`) into the single global store.
5
4
  *
6
- * Idempotent a no-op once the legacy directory is gone, so it is safe to call
7
- * on every project activation. Conflict-safe if a DIFFERENT skill of the same
8
- * name already exists in the store, the legacy copy is LEFT IN PLACE with a loud
9
- * warning and never silently overwritten.
5
+ * Under the opt-out model every skill is global, so a freshly-migrated skill is parked GLOBALLY
6
+ * DISABLED the same default as an agent-learned skill. The user re-enables it deliberately from
7
+ * the plugins page; a once-project-scoped skill must NOT silently light up in every project.
8
+ *
9
+ * Idempotent — a no-op once the legacy directory is gone, so it is safe to call on every project
10
+ * activation. Conflict-safe — if a DIFFERENT skill of the same name already exists in the store, the
11
+ * legacy copy is LEFT IN PLACE with a loud warning and never silently overwritten.
10
12
  */
11
13
  export interface SkillMigrationResult {
14
+ /** Moved into the store fresh (parked globally disabled). */
12
15
  migrated: string[];
13
16
  conflicts: string[];
14
- /** Already present in the store with identical content; legacy copy reconciled. */
17
+ /** Already present in the store with identical content; legacy copy dropped, store entry untouched. */
15
18
  reconciled: string[];
16
19
  }
17
20
  /**
@@ -1,32 +1,20 @@
1
1
  /**
2
- * Project skill enablement manifest — the per-project opt-in list for the
3
- * single-store skill registry.
2
+ * Skill mute registriesunder the single-store model, EVERY skill is global. There is no
3
+ * project-scoped / opt-in concept; instead there are two opt-OUT mute lists:
4
4
  *
5
- * Stored at `<cwd>/.qlogicagent/skills-enabled.json`:
6
- * { "version": 1, "enabled": ["skill-a", "skill-b"] }
5
+ * - owner-level (~/.qlogicagent/profiles/<owner>/skills-disabled.json) — off in EVERY project
6
+ * (the plugin card's on/off switch; also where agent-learned skills land, disabled, until the
7
+ * user reviews and enables them).
8
+ * - per-project (<cwd>/.qlogicagent/skills-disabled.json) — off in THIS project only
9
+ * (the "project skills" opt-out, to cut down which skills an agent sees in a given project).
7
10
  *
8
- * Only project-scoped skills need listing here; globally-scoped skills
9
- * (see skill-lifecycle `enabledScope`) are active everywhere regardless.
11
+ * active(project) = stored skill AND name globally-disabled AND name ∉ project-disabled
10
12
  */
11
- export interface ProjectSkillManifest {
12
- version: 1;
13
- enabled: string[];
14
- }
15
- /** Read the enablement manifest for a project. Returns empty manifest if absent/malformed. */
16
- export declare function readProjectSkillManifest(cwd: string): ProjectSkillManifest;
17
- /** The set of project-scoped skill names enabled in this project. */
18
- export declare function readEnabledSkills(cwd: string): Set<string>;
19
- /** Enable a (project-scoped) skill in this project. Idempotent. Returns true if newly added. */
20
- export declare function enableSkill(cwd: string, name: string): boolean;
21
- /** Disable (un-enable) a skill in this project. Idempotent. Returns true if removed. */
22
- export declare function disableSkill(cwd: string, name: string): boolean;
23
- /** Skills that are project-scoped (active only where a project manifest enables them). */
24
- export declare function readProjectScopedSkills(): Set<string>;
25
- /** Mark a skill project-scoped (learned/created). Idempotent. Returns true if newly added. */
26
- export declare function markProjectScoped(name: string): boolean;
27
- /** Unmark a skill (promote to global). Idempotent. Returns true if removed. */
28
- export declare function unmarkProjectScoped(name: string): boolean;
29
13
  /** Globally muted skills (inactive everywhere). */
30
14
  export declare function readGloballyDisabledSkills(): Set<string>;
31
- /** Mute/unmute a global skill everywhere. Idempotent. Returns true if the registry changed. */
15
+ /** Mute/unmute a skill globally. Idempotent. Returns true if the registry changed. */
32
16
  export declare function setSkillGloballyDisabled(name: string, disabled: boolean): boolean;
17
+ /** Skills muted in this project (inactive here, active elsewhere unless globally muted). */
18
+ export declare function readProjectDisabledSkills(cwd: string): Set<string>;
19
+ /** Mute/unmute a skill in this project. Idempotent. Returns true if the manifest changed. */
20
+ export declare function setProjectSkillDisabled(cwd: string, name: string, disabled: boolean): boolean;
@@ -1,19 +1,15 @@
1
1
  /**
2
- * Skill resolver — single source of truth for "which skills are effective in a
3
- * given project" under the global-single-store + per-project-manifest model.
2
+ * Skill resolver — single source of truth for "which skills are effective in a given project".
4
3
  *
5
- * Storage: ONE global store at getUserSkillsDir(). A skill is project-scoped if
6
- * it appears in the owner-level project-scoped registry (skills-scoped.json);
7
- * otherwise it is global. Project-scoped skills are active only where the
8
- * project manifest (skills-enabled.json) lists them.
4
+ * Storage: ONE global store at getUserSkillsDir(). Every skill is global; two opt-out mute lists
5
+ * decide effectiveness owner-level (off everywhere) and per-project (off in this project). See
6
+ * project-skill-manifest.
9
7
  *
10
- * effective(project) = { store skills NOT marked project-scoped }
11
- * ∪ { project-scoped skills whose name ∈ project manifest }
8
+ * active(project) = stored skill AND name globally-disabled AND name project-disabled
12
9
  *
13
- * All resolution / listing / injection surfaces MUST go through this module so
14
- * precedence and name-collision semantics live in exactly one place. Kept in
15
- * runtime/infra (no skills-layer imports) so both runtime and cli callers may
16
- * use it without crossing the runtime → skills boundary.
10
+ * All resolution / listing / injection surfaces MUST go through this module so precedence lives in
11
+ * exactly one place. Kept in runtime/infra (no skills-layer imports) so both runtime and cli callers
12
+ * may use it without crossing the runtime skills boundary.
17
13
  */
18
14
  export interface ResolvedSkill {
19
15
  name: string;
@@ -21,8 +17,11 @@ export interface ResolvedSkill {
21
17
  filePath: string;
22
18
  /** Absolute path to <store>/<name> */
23
19
  baseDir: string;
24
- enabledScope: "global" | "project";
25
- /** Active in the resolution context: global, or project-enabled via manifest. */
20
+ /** Muted everywhere (the plugin card's off switch / agent-learned-pending-review). */
21
+ globallyDisabled: boolean;
22
+ /** Muted in THIS project's context (the per-project opt-out). */
23
+ projectDisabled: boolean;
24
+ /** Effective here: neither globally nor project muted. */
26
25
  active: boolean;
27
26
  systemGenerated: boolean;
28
27
  description?: string;
@@ -30,12 +29,11 @@ export interface ResolvedSkill {
30
29
  }
31
30
  export declare function isSystemGeneratedSkillName(name: string): boolean;
32
31
  /**
33
- * Resolve all skills in the global store, annotated with enablement scope and
34
- * whether they're active in the given project context.
32
+ * Resolve all skills in the global store, annotated with global/project mute state and whether
33
+ * they're active in the given project context.
35
34
  *
36
- * Returns ALL store skills (active and inactive) so listing surfaces can show
37
- * enable/disable state; callers that only want active skills filter `.active`
38
- * (or use resolveActiveSkills).
35
+ * Returns ALL store skills (active and inactive) so listing surfaces can show enable/disable state;
36
+ * callers that only want active skills filter `.active` (or use resolveActiveSkills).
39
37
  */
40
38
  export declare function resolveSkills(projectRoot?: string): ResolvedSkill[];
41
39
  /** Only the skills active in the given project (for prompt injection / resolution). */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qlogicagent",
3
- "version": "2.15.6",
3
+ "version": "2.15.8",
4
4
  "description": "XiaozhiClaw Agent CLI — subprocess architecture (JSON-RPC over stdio)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",