@getbourdon/participants 0.5.5 → 0.6.0

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.
package/dist/index.d.cts CHANGED
@@ -1,6 +1,7 @@
1
- import { L5ManifestModel, SessionModel, VisibilityPolicyModel, EntityModel, Visibility } from '@getbourdon/l5';
2
- export { EntityModel, L5ManifestModel, SessionModel, Visibility, VisibilityPolicyModel, applyVisibility, filterForFederation, toDict, validateManifest } from '@getbourdon/l5';
1
+ import { L5ManifestModel, SessionModel, SkillRefModel, SkillOriginModel, VisibilityPolicyModel, EntityModel, Visibility } from '@getbourdon/l5';
2
+ export { EntityModel, L5ManifestModel, SessionModel, SkillRefModel, Visibility, VisibilityPolicyModel, applyVisibility, filterForFederation, toDict, validateManifest } from '@getbourdon/l5';
3
3
  import DatabaseT from 'better-sqlite3';
4
+ import { FederationAudit } from '@getbourdon/federation';
4
5
 
5
6
  /**
6
7
  * Bourdon participant base — the Protocol → TS interface, errors, and the
@@ -317,6 +318,297 @@ declare class HermesParticipant implements BourdonParticipant {
317
318
  healthCheck(): HealthStatus;
318
319
  }
319
320
 
321
+ /**
322
+ * Skill readers: advertise the local skills an agent can see in its L5
323
+ * manifest (plan 005, discovery side). Port of `participants/_skills.py`.
324
+ *
325
+ * A participant that knows where its agent keeps skills (a folder per skill
326
+ * holding a `SKILL.md` with `name` / `description` front matter) lists them as
327
+ * `SkillRef` rows: name, description and a content hash. Nothing is shared or
328
+ * promoted here; the body never enters the manifest.
329
+ *
330
+ * Two kinds of skill directory:
331
+ * - **The agent's own** (`~/.claude/skills`, `$CODEX_HOME/skills`,
332
+ * `~/.cursor/skills` and their project forms).
333
+ * - **The cross-agent store**, `~/.agents/skills` (and `<project>/.agents/skills`):
334
+ * a vendor-neutral directory no single agent owns. The cross-agent skills
335
+ * installer CLI keeps its installs there and records each one in
336
+ * `~/.agents/.skill-lock.json`; people also write skills there by hand. Refs
337
+ * from it carry `store: cross_agent`. Every skill-reading participant reads
338
+ * it with its own directories; a reader of several manifests collapses the
339
+ * copies (`collectAdvertisedSkills` in @getbourdon/federation).
340
+ *
341
+ * One skill, one ref per manifest: when an agent's own folder holds a skill
342
+ * that is also in the cross-agent store, the agent-folder ref is kept and
343
+ * records the store copy in `also_in` (`via: copy` for a copy with the same
344
+ * `content_sha256`, `via: link` for a link into the store); the store's own ref
345
+ * is then left out. A link counts only when it resolves to a direct child
346
+ * folder of the store at the same scope, and the skill is then read through
347
+ * the store's own path, never through the link. Any other link is skipped.
348
+ *
349
+ * Provenance (user-level store only): a skill with an entry in the installer
350
+ * lock carries `origin: {kind: installed, ...}`; one without carries
351
+ * `origin: {kind: authored}`. Nothing else is read from the lock, and a
352
+ * missing, linked, oversized or malformed lock never fails a scan.
353
+ *
354
+ * Scope: one skill at a time. By default a participant lists only the skills
355
+ * the person approved one by one with `bourdon skills share` (stored locally
356
+ * in `<library>/.skill-shares.json`, see skill-shares.ts), and only while the
357
+ * folder still hashes to the approved content; no other skill folder is read.
358
+ * `BOURDON_SKILLS_ADVERTISE` set to an on value lists every skill; an off
359
+ * value or an unrecognised one lists none, shared or not. A caller that builds
360
+ * a participant with explicit `skillSources` lists every skill in them
361
+ * ({@link skillsListingMode}). The directories: user-level skill directories,
362
+ * and a project's directories
363
+ * only when the project is named in `BOURDON_SKILL_PROJECTS` (absolute paths
364
+ * separated by the platform's path delimiter). Disallowed files are skipped
365
+ * and flagged, never fatal; no link is followed; no absolute path reaches the
366
+ * manifest (`path_hint` starts at a symbolic root); refs are emitted as
367
+ * `team`.
368
+ */
369
+
370
+ /** Opt-in list of project roots whose skill directories may be indexed. */
371
+ declare const SKILL_PROJECTS_ENV = "BOURDON_SKILL_PROJECTS";
372
+ /**
373
+ * The listing switch for every participant. Skill descriptions are free text
374
+ * and can name private projects, clients or infrastructure, so by default
375
+ * (unset or empty) only the skills approved one by one are listed. `1` /
376
+ * `true` / `yes` / `on` list every skill; `0` / `false` / `no` / `off` list
377
+ * none, approved or not, and so does any other value (fail closed): a typo
378
+ * never lists anything.
379
+ */
380
+ declare const SKILLS_ADVERTISE_ENV = "BOURDON_SKILLS_ADVERTISE";
381
+ /**
382
+ * Turns named participants off in every mode: agent ids separated by commas
383
+ * (or whitespace), e.g. `codex,cursor`. Matching ignores case.
384
+ */
385
+ declare const SKILLS_NO_ADVERTISE_ENV = "BOURDON_SKILLS_NO_ADVERTISE";
386
+ /** {@link skillsListingMode} results. */
387
+ declare const LIST_ALL = "all";
388
+ declare const LIST_APPROVED = "approved";
389
+ declare const LIST_OFF = "off";
390
+ type SkillsListingMode = typeof LIST_ALL | typeof LIST_APPROVED | typeof LIST_OFF;
391
+ /** Every advertised ref carries this visibility. */
392
+ declare const SKILL_REF_VISIBILITY: "team";
393
+ /** The L5 schema bounds (`skills.maxItems` and the `SkillRef` fields). */
394
+ declare const MAX_SKILL_REFS = 512;
395
+ declare const MAX_SKIPPED_FILES = 32;
396
+ declare const MAX_PATH_HINT_CHARS = 255;
397
+ /** An installer lock larger than this is not read (origin is then omitted). */
398
+ declare const MAX_LOCK_BYTES: number;
399
+ declare const SCOPE_USER = "user";
400
+ declare const SCOPE_PROJECT = "project";
401
+ /** Whose directory a skill folder sits in; `agent` is the default and never written. */
402
+ declare const STORE_AGENT = "agent";
403
+ declare const STORE_CROSS_AGENT = "cross_agent";
404
+ declare const VIA_COPY = "copy";
405
+ declare const VIA_LINK = "link";
406
+ declare const ORIGIN_INSTALLED = "installed";
407
+ declare const ORIGIN_AUTHORED = "authored";
408
+ /** The cross-agent installer's lock file, beside the user-level store. */
409
+ declare const CROSS_AGENT_LOCK_NAME = ".skill-lock.json";
410
+ /**
411
+ * One directory a reader indexes, one skill per child folder. `label` is the
412
+ * symbolic form of `root` that `path_hint` starts with; it never contains an
413
+ * absolute path. `store` says whose directory it is; `lockFile` is the
414
+ * installer lock that records where the store's skills came from.
415
+ */
416
+ interface SkillSource {
417
+ root: string;
418
+ scope: string;
419
+ label: string;
420
+ project?: string | null;
421
+ store?: string;
422
+ lockFile?: string | null;
423
+ }
424
+ /** A skill (or a whole skills directory) that was not advertised, and why. */
425
+ interface SkippedSkill {
426
+ pathHint: string;
427
+ scope: string;
428
+ reason: string;
429
+ project?: string | null;
430
+ }
431
+ interface SkillScanReport {
432
+ refs: SkillRefModel[];
433
+ skipped: SkippedSkill[];
434
+ }
435
+ /** The home directory user-level skill directories are found under. */
436
+ declare function userHome(): string;
437
+ /**
438
+ * Project roots opted in through {@link SKILL_PROJECTS_ENV}, in order. Only
439
+ * absolute paths count: a relative entry is ignored (and logged) rather than
440
+ * resolved against whatever directory the export happens to run in.
441
+ */
442
+ declare function skillProjects(): string[];
443
+ interface SkillSourceOptions {
444
+ /** The home directory to use instead of {@link userHome}. */
445
+ home?: string;
446
+ /** Project roots instead of {@link skillProjects}. */
447
+ projects?: readonly string[];
448
+ }
449
+ /**
450
+ * Claude Code: `<config dir>/skills` and, on opt-in, `<project>/.claude/skills`.
451
+ * The config directory is `CLAUDE_CONFIG_DIR` (Claude Code's own override),
452
+ * else `CLAUDE_HOME`, else `~/.claude`.
453
+ */
454
+ declare function claudeCodeSkillSources(options?: SkillSourceOptions): SkillSource[];
455
+ /**
456
+ * Codex: `$CODEX_HOME/skills`. `codexHome` is an explicit Codex home, if one
457
+ * was given; otherwise `CODEX_HOME`, else `~/.codex`. Codex keeps its bundled
458
+ * skills in a hidden `.system` folder there, which is not indexed. The mirror
459
+ * has no Codex participant yet; this is the source a Codex reader would use.
460
+ */
461
+ declare function codexSkillSources(options?: {
462
+ codexHome?: string | null;
463
+ home?: string;
464
+ }): SkillSource[];
465
+ /**
466
+ * Cursor: `~/.cursor/skills` and, on opt-in, `<project>/.cursor/skills`.
467
+ * `CURSOR_HOME` overrides `~/.cursor`. `~/.cursor/skills-cursor` holds
468
+ * Cursor's own built-in skills and is not indexed.
469
+ */
470
+ declare function cursorSkillSources(options?: SkillSourceOptions): SkillSource[];
471
+ /**
472
+ * The vendor-neutral store: `~/.agents/skills` and, on opt-in,
473
+ * `<project>/.agents/skills`. The user-level store comes with the installer
474
+ * lock for provenance; no lock is read for a project store.
475
+ */
476
+ declare function crossAgentSkillSources(options?: SkillSourceOptions): SkillSource[];
477
+ type LockEntry = Record<string, unknown>;
478
+ /**
479
+ * The `skills` table of the installer lock, keyed by skill name. An empty map
480
+ * when there is no lock (every store skill is then `authored`); `null` when a
481
+ * lock exists but cannot be trusted or parsed (a link, not a regular file, too
482
+ * large, not JSON, or without a `skills` mapping), in which case origin is left
483
+ * off rather than guessed.
484
+ */
485
+ declare function readInstallLock(path: string): Map<string, LockEntry> | null;
486
+ /**
487
+ * `installed` with the lock entry's sanitised source fields, else `authored`.
488
+ * Reads only `source`, `sourceType`, `sourceUrl` and `updatedAt`; a field
489
+ * that is missing, of the wrong type or fails its check is left out.
490
+ */
491
+ declare function originFromLockEntry(entry: LockEntry | null | undefined): SkillOriginModel;
492
+ /** Which child folders of a source to read: absent reads every one. */
493
+ type SkillFolderFilter = (source: SkillSource, folderName: string) => boolean;
494
+ interface ScanSkillSourcesOptions {
495
+ /**
496
+ * Limits the scan to the child folders it accepts; every other folder is
497
+ * passed over unread. A store folder that an accepted agent folder links to
498
+ * is read too, so the link resolves as in a full scan. An accepted agent
499
+ * folder that is a copy of a store folder the scan did not read carries no
500
+ * `also_in`.
501
+ */
502
+ include?: SkillFolderFilter | null;
503
+ }
504
+ /**
505
+ * Index every source; one ref per skill, first occurrence of a name wins per
506
+ * store and scope. Cross-agent stores are scanned first so the agent's own
507
+ * folders can be matched against them (`also_in`). Refs come back with the
508
+ * agent's own folders first, in source order, then the cross-agent store
509
+ * skills that no advertised agent folder already represents.
510
+ */
511
+ declare function scanSkillSources(sources: Iterable<SkillSource>, options?: ScanSkillSourcesOptions): SkillScanReport;
512
+ /** A participant that advertises skills (plan 005). */
513
+ interface SkillAdvertiser {
514
+ agentId: string;
515
+ exportSkills(): SkillRefModel[];
516
+ /**
517
+ * Whether the participant was built with explicit `skillSources`, which
518
+ * lists every skill in them ({@link skillsListingMode}). Absent = no.
519
+ */
520
+ hasExplicitSkillSources?(): boolean;
521
+ /**
522
+ * The directories the reader indexes. When present, the default
523
+ * (approved-only) mode reads just the approved folders in them; without it
524
+ * the reader's own scan runs and only approved refs are kept.
525
+ */
526
+ skillSources?(): SkillSource[];
527
+ }
528
+ /**
529
+ * Which skills the participant `agentId` may list in its manifest.
530
+ *
531
+ * - {@link LIST_APPROVED}: {@link SKILLS_ADVERTISE_ENV} unset or empty: only
532
+ * the skills approved one by one (`bourdon skills share`) whose folder still
533
+ * hashes to the approved content.
534
+ * - {@link LIST_ALL}: an on value; or, while the variable is unset, a
535
+ * participant built with explicit `skillSources` (the caller chose what to
536
+ * index).
537
+ * - {@link LIST_OFF}: an off value, or any unrecognised one (fail closed):
538
+ * nothing is listed, approved or not.
539
+ *
540
+ * {@link SKILLS_NO_ADVERTISE_ENV} turns named participants off in every mode.
541
+ */
542
+ declare function skillsListingMode(agentId: string, options?: {
543
+ explicitSources?: boolean;
544
+ }): SkillsListingMode;
545
+ /**
546
+ * Whether the participant `agentId` lists every skill it can see: true only
547
+ * in {@link LIST_ALL} mode ({@link skillsListingMode}). In the default mode a
548
+ * participant lists only the skills shared one by one.
549
+ */
550
+ declare function skillsAdvertisingEnabled(agentId: string, options?: {
551
+ explicitSources?: boolean;
552
+ }): boolean;
553
+ /**
554
+ * The refs `participant`'s manifest carries, for `exportL5`.
555
+ *
556
+ * Follows {@link skillsListingMode}: nothing, and no skill directory read,
557
+ * when off; by default only the skills with a current approval, reading only
558
+ * their folders; every skill the reader sees when listing all. A participant
559
+ * built with explicit `skillSources` lists all unless turned off. A failure
560
+ * here never fails an export: skills are an addition to the manifest, not a
561
+ * reason to lose its memory rows. Private refs are dropped, as private
562
+ * entities are.
563
+ */
564
+ declare function advertisedSkills(participant: SkillAdvertiser): SkillRefModel[];
565
+ /**
566
+ * The default skill directories of a built-in skill-reading participant.
567
+ *
568
+ * The same directories the participant's own export reads (its user-level
569
+ * directory honouring its home override, the opted-in projects, and the
570
+ * cross-agent store). `null` for an agent with no skill reader here, so a skill
571
+ * it advertises can be listed but not proposed from this machine.
572
+ */
573
+ declare function skillSourcesForAgent(agentId: string): SkillSource[] | null;
574
+ /**
575
+ * The folder an advertised ref was scanned from, found through `sources` only.
576
+ *
577
+ * `ref` is a SkillRef as it sits in a manifest. Its `path_hint` is matched
578
+ * against each source's symbolic label (same scope and project), so the result
579
+ * is always one direct child of a directory the reader indexes; a path from
580
+ * anywhere else cannot be produced. A ref that is a link into the cross-agent
581
+ * store resolves to the store's own folder, as the reader read it. `null` when
582
+ * nothing matches.
583
+ */
584
+ declare function resolveAdvertisedFolder(sources: readonly SkillSource[], ref: Readonly<Record<string, unknown>>): string | null;
585
+ /** The built-in skill readers, in listing order. */
586
+ declare const SKILL_READERS: readonly ["claude-code", "codex", "cursor"];
587
+ /**
588
+ * One skill folder a built-in reader sees, for `bourdon skills available|share`.
589
+ *
590
+ * `agent` is the reader that owns the folder, or `null` for a cross-agent
591
+ * store folder; `agents` names every reader that sees it. Local use only:
592
+ * `folder` is an absolute path and never leaves the terminal command that
593
+ * asked for it.
594
+ */
595
+ interface LocalSkill {
596
+ ref: SkillRefModel;
597
+ agent: string | null;
598
+ agents: string[];
599
+ folder: string;
600
+ /** `agent` or `cross_agent`. */
601
+ store: string;
602
+ }
603
+ /**
604
+ * Every skill folder the built-in readers see on this machine (each one is
605
+ * read). The same directories the readers' exports use: user-level, opted-in
606
+ * projects and the cross-agent store. One row per folder; a store folder seen
607
+ * by several readers is one row naming them all. `agent` keeps the folders
608
+ * that one reader sees.
609
+ */
610
+ declare function localSkills(agent?: string | null): LocalSkill[];
611
+
320
612
  /**
321
613
  * Claude Code participant — reads agent-brain + auto-memory + MCP knowledge
322
614
  * graph. Port of `participants/claude_code.py`.
@@ -346,7 +638,25 @@ declare class ClaudeCodeParticipant implements BourdonParticipant {
346
638
  autoMemoryPath: string | null;
347
639
  knowledgeGraphPath: string | null;
348
640
  static defaultNativePath(home?: string): string;
349
- constructor();
641
+ /**
642
+ * Explicit skill directories; `null` = the defaults (see skills.ts), read
643
+ * only for shared skills unless `BOURDON_SKILLS_ADVERTISE` lists all.
644
+ * Explicit sources list every skill in them.
645
+ */
646
+ private readonly explicitSkillSources;
647
+ constructor(options?: {
648
+ skillSources?: readonly SkillSource[] | null;
649
+ });
650
+ /**
651
+ * `~/.claude/skills` (or the config-dir override) and the cross-agent store,
652
+ * plus the opted-in projects' `.claude/skills` and `.agents/skills`.
653
+ */
654
+ /** Built with explicit skill sources: this participant lists all of them. */
655
+ hasExplicitSkillSources(): boolean;
656
+ skillSources(): SkillSource[];
657
+ /** Advertised skills plus the skills that could not be advertised. */
658
+ scanSkills(): SkillScanReport;
659
+ exportSkills(): SkillRefModel[];
350
660
  discover(): AgentStore;
351
661
  exportL5(since?: Date): L5ManifestModel;
352
662
  exportSessions(since?: Date, limit?: number): SessionModel[];
@@ -421,7 +731,27 @@ declare class CursorParticipant implements BourdonParticipant {
421
731
  * extraction intentionally use different anchors.
422
732
  */
423
733
  static defaultNativePath(home?: string): string;
424
- constructor(cursorDir?: string | null, workspaceRoot?: string | null);
734
+ /**
735
+ * Explicit skill directories; `null` = the defaults (see skills.ts), read
736
+ * only for shared skills unless `BOURDON_SKILLS_ADVERTISE` lists all.
737
+ * Explicit sources list every skill in them.
738
+ * `workspaceRoot` does not opt a project in: that takes an explicit
739
+ * `BOURDON_SKILL_PROJECTS` entry, or explicit sources.
740
+ */
741
+ private readonly explicitSkillSources;
742
+ constructor(cursorDir?: string | null, workspaceRoot?: string | null, options?: {
743
+ skillSources?: readonly SkillSource[] | null;
744
+ });
745
+ /**
746
+ * `~/.cursor/skills` (not the built-in `skills-cursor`) and the cross-agent
747
+ * store, plus the opted-in projects' `.cursor/skills` and `.agents/skills`.
748
+ */
749
+ /** Built with explicit skill sources: this participant lists all of them. */
750
+ hasExplicitSkillSources(): boolean;
751
+ skillSources(): SkillSource[];
752
+ /** Advertised skills plus the skills that could not be advertised. */
753
+ scanSkills(): SkillScanReport;
754
+ exportSkills(): SkillRefModel[];
425
755
  get nativePath(): string;
426
756
  discover(): AgentStore;
427
757
  exportSessions(since?: Date, limit?: number): SessionModel[];
@@ -1040,6 +1370,625 @@ declare function recentSessionThreads(codexHome?: string | null, opts?: {
1040
1370
  limit?: number;
1041
1371
  }): RecentThread[];
1042
1372
 
1373
+ /**
1374
+ * Per-skill share approvals: which local skills a participant may list. Port
1375
+ * of the engine's `core/skill_shares.py`.
1376
+ *
1377
+ * Like approving a hook, a skill is listed in an agent's L5 manifest only
1378
+ * after the person at this machine approves that one skill (`bourdon skills
1379
+ * share`). Each approval pins the exact content approved: a skill whose folder
1380
+ * has changed since is not listed again until it is re-approved.
1381
+ *
1382
+ * Layout (interoperable with the engine: either side reads what the other wrote):
1383
+ *
1384
+ * <library>/.skill-shares.json
1385
+ *
1386
+ * {
1387
+ * "version": 1,
1388
+ * "approvals": [
1389
+ * {
1390
+ * "kind": "skill",
1391
+ * "source": {
1392
+ * "agent": "alice", // null: any reader of a cross-agent store skill
1393
+ * "store": "agent", // "agent" | "cross_agent"
1394
+ * "scope": "user", // "user" | "project"
1395
+ * "project": null, // the project's folder name, for scope "project"
1396
+ * "path_hint": "~/.alice/skills/release-checklist"
1397
+ * },
1398
+ * "item": "release-checklist",
1399
+ * "content_sha256": "<64 hex>",
1400
+ * "approved_at": "2026-10-15T08:00:00Z",
1401
+ * "actor": "operator"
1402
+ * }
1403
+ * ]
1404
+ * }
1405
+ *
1406
+ * An approval is identified by `(kind, source, item, content_sha256)`. Only
1407
+ * `kind: "skill"` is written today; an entry of a kind this version does not
1408
+ * know is kept untouched on rewrite (and never treated as an approval).
1409
+ * Approving an item again from the same source replaces its earlier approval,
1410
+ * so a re-approved edit does not leave the old content approved.
1411
+ *
1412
+ * Load-bearing rules:
1413
+ *
1414
+ * - **Local only.** The file sits beside the library's other local state and
1415
+ * is never synced: nothing in this package stages a library for the network,
1416
+ * and the engine's sync stages only `agents/*.l5.yaml`.
1417
+ * - **No absolute paths.** A share names its folder by the reader's symbolic
1418
+ * `path_hint` (`~/...`, `$ENV_VAR/...` or project-relative), never by where
1419
+ * it sits on disk.
1420
+ * - **Corruption means no approvals.** A missing, linked, oversized or
1421
+ * unparseable file reads as no shares at all, and a malformed entry is
1422
+ * dropped; reading never throws, so an export never fails here.
1423
+ * - **Writes are locked** with the same cross-process lock the library's other
1424
+ * writers use, and replace the file atomically.
1425
+ *
1426
+ * Nothing here is reachable over MCP; the terminal commands are the only
1427
+ * writers, and they ask the person at the keyboard to confirm.
1428
+ */
1429
+ declare const SHARES_FILENAME = ".skill-shares.json";
1430
+ declare const SHARES_VERSION = 1;
1431
+ /** The only approval kind written by this version. */
1432
+ declare const KIND_SKILL = "skill";
1433
+ /** Who approved a share. Only the operator's terminal writes this file. */
1434
+ declare const SHARE_ACTOR = "operator";
1435
+ /** A shares file larger than this is not read (it then holds no approvals). */
1436
+ declare const MAX_SHARES_BYTES: number;
1437
+ type Dict$3 = Record<string, unknown>;
1438
+ /** A share cannot be recorded (the file is unsafe to write, or the share is invalid). */
1439
+ declare class SkillShareError extends Error {
1440
+ constructor(message: string);
1441
+ }
1442
+ /** One approval: this skill folder, with exactly this content, may be listed. */
1443
+ interface SkillShare {
1444
+ name: string;
1445
+ agent: string | null;
1446
+ store: string;
1447
+ scope: string;
1448
+ project: string | null;
1449
+ pathHint: string;
1450
+ contentSha256: string;
1451
+ approvedAt: string;
1452
+ actor: string;
1453
+ }
1454
+ /** The skill folder a share is about, whoever reads it (a comparable key). */
1455
+ declare function shareFolderKey(share: SkillShare): string;
1456
+ /** The reader source: the same folder as read by the same reader. */
1457
+ declare function shareSourceKey(share: SkillShare): string;
1458
+ /** The approval's identity: `(kind, source, item, content_sha256)`. */
1459
+ declare function shareKey(share: SkillShare): string;
1460
+ /**
1461
+ * Whether the participant `agentId` may list the skill under `share`. A share
1462
+ * of an agent's own folder applies to that agent; a share of a cross-agent
1463
+ * store skill with no `agent` applies to every reader of the store.
1464
+ */
1465
+ declare function shareAppliesTo(share: SkillShare, agentId: string): boolean;
1466
+ /** The on-disk record, in the engine's key order. */
1467
+ declare function shareToDict(share: SkillShare): Dict$3;
1468
+ /**
1469
+ * The library whose shares the participants' exports read. Resolved at call
1470
+ * time (not import time) so a relocated home is honoured.
1471
+ */
1472
+ declare function defaultSharesLibrary(): string;
1473
+ declare function sharesPath(libraryPath?: string | null): string;
1474
+ /**
1475
+ * Every readable approval, in file order. Never throws.
1476
+ *
1477
+ * A missing file is no approvals. So is a file that is a link, not a regular
1478
+ * file, too large, not JSON or of an unknown layout; a malformed entry is
1479
+ * dropped and the rest are kept.
1480
+ */
1481
+ declare function loadShares(libraryPath?: string | null): SkillShare[];
1482
+ interface AddShareOptions {
1483
+ name: string;
1484
+ agent: string | null;
1485
+ store: string;
1486
+ scope: string;
1487
+ project: string | null;
1488
+ pathHint: string;
1489
+ contentSha256: string;
1490
+ approvedAt?: string | null;
1491
+ }
1492
+ /**
1493
+ * Record one approval, replacing earlier approvals of the same item from the
1494
+ * same source. An unreadable shares file is replaced: it held no usable
1495
+ * approvals.
1496
+ */
1497
+ declare function addShare(libraryPath: string | null | undefined, options: AddShareOptions): SkillShare;
1498
+ /** Remove the shares whose {@link shareKey} is in `keys`; how many went. */
1499
+ declare function removeShares(libraryPath: string | null | undefined, keys: Iterable<string>): number;
1500
+
1501
+ /**
1502
+ * Reader-side skill folder scan (plan 005). Port of the parts of the engine's
1503
+ * `core/skill_index.py` a participant needs to advertise a local skill: the
1504
+ * text-only content rules, the no-follow folder walk, front-matter parsing,
1505
+ * the `content_sha256` framing and `scan_local_skill` (skip and flag).
1506
+ *
1507
+ * `readSkillFolder` is the promotion-side twin of `scanLocalSkill`: it refuses
1508
+ * a folder that breaks any rule instead of skipping files. The library-level
1509
+ * shared store (index rows, versions, promotion) is in `skill-store.ts`. Both
1510
+ * use the same limits and name rule, so a hash computed here compares
1511
+ * directly with a shared skill's.
1512
+ *
1513
+ * Rules a v1 shared skill must meet (anything else is skipped and flagged):
1514
+ * - `SKILL.md` plus markdown, plain text and template files only;
1515
+ * - no links, no hidden entries, bounded depth and path length, no paths that
1516
+ * differ only in case;
1517
+ * - UTF-8 text without NUL bytes, no interpreter line, no credential-like text;
1518
+ * - per-file and total size caps, at most {@link MAX_REFERENCE_FILES} reference files.
1519
+ */
1520
+
1521
+ declare const SKILL_BODY_NAME = "SKILL.md";
1522
+ /** Reference-file types a v1 shared skill may carry: markdown, plain text and templates. */
1523
+ declare const TEXT_FILE_SUFFIXES: ReadonlySet<string>;
1524
+ declare const MAX_SKILL_BODY_BYTES: number;
1525
+ declare const MAX_REFERENCE_FILE_BYTES: number;
1526
+ declare const MAX_REFERENCE_FILES = 64;
1527
+ declare const MAX_SKILL_TOTAL_BYTES: number;
1528
+ /**
1529
+ * Why a reader left a file out of an advertised skill. `limit` means the
1530
+ * file-count or total-size cap was reached; every other code names the content
1531
+ * rule the file breaks.
1532
+ */
1533
+ declare const SKIP_REASONS: ReadonlySet<string>;
1534
+ /**
1535
+ * A skill folder cannot be advertised (or shared) as it stands. `problems`
1536
+ * lists every offending path with the reason; content is never echoed back.
1537
+ */
1538
+ declare class SkillContentError extends Error {
1539
+ readonly problems: string[];
1540
+ constructor(problems: string[]);
1541
+ }
1542
+ /** Python `str.strip()` with no argument. */
1543
+ declare function pyStrip(text: string): string;
1544
+ /** The first `limit` code points, as Python's `text[:limit]`. */
1545
+ declare function codePointSlice(text: string, limit: number): string;
1546
+ interface SkillFrontMatter {
1547
+ name: string;
1548
+ description: string;
1549
+ }
1550
+ /** Extract `name` and `description` from a `SKILL.md` body. Throws {@link SkillContentError}. */
1551
+ declare function parseFrontMatter(body: Uint8Array | string): SkillFrontMatter;
1552
+ /**
1553
+ * Hash a skill's files as the schema's `content_sha256` defines: entries sorted
1554
+ * by the UTF-8 bytes of their POSIX path relative to the skill folder, each
1555
+ * framed as `path \0 len(bytes) \0 bytes`.
1556
+ */
1557
+ declare function computeContentSha256(entries: Iterable<readonly [string, Uint8Array]>): string;
1558
+ /** One file a reader left out of a local skill, and why (a {@link SKIP_REASONS} code). */
1559
+ interface SkippedFile {
1560
+ path: string;
1561
+ reason: string;
1562
+ }
1563
+ /**
1564
+ * What a participant reader can advertise about one local skill folder. `files`
1565
+ * is the shareable part (`SKILL.md` plus every reference file that passes the
1566
+ * v1 content rules); `skipped` lists the rest. The hash covers `files` only.
1567
+ */
1568
+ interface LocalSkillScan {
1569
+ frontMatter: SkillFrontMatter;
1570
+ files: ReadonlyArray<readonly [string, Uint8Array]>;
1571
+ skipped: readonly SkippedFile[];
1572
+ contentSha256: string;
1573
+ }
1574
+ /**
1575
+ * Scan one local skill folder for discovery: skip and flag, don't refuse.
1576
+ *
1577
+ * A disallowed reference file (a script, a binary, a link, a hidden file,
1578
+ * credential-like text) is left out and reported in `skipped` instead of making
1579
+ * the whole skill disappear. Only a `SKILL.md` that is missing, unreadable or
1580
+ * itself breaks a content rule throws {@link SkillContentError}, because then
1581
+ * there is nothing safe to advertise. Nothing is followed through a link and
1582
+ * nothing is modified.
1583
+ */
1584
+ declare function scanLocalSkill(folder: string): LocalSkillScan;
1585
+ /** A validated skill: front matter plus every file, `SKILL.md` included, sorted by path. */
1586
+ interface SkillContent {
1587
+ frontMatter: SkillFrontMatter;
1588
+ files: ReadonlyArray<readonly [string, Uint8Array]>;
1589
+ }
1590
+ /**
1591
+ * Read a skill folder and enforce every v1 content rule (the oracle's
1592
+ * `read_skill_folder`). Throws {@link SkillContentError} listing every problem
1593
+ * found. The folder is read in place and never modified; links are reported,
1594
+ * never followed.
1595
+ */
1596
+ declare function readSkillFolder(folder: string): SkillContent;
1597
+
1598
+ /**
1599
+ * Shared skill store: the library-level home of federated procedural memory.
1600
+ * Port of the store half of the engine's `core/skill_index.py` (plan 005,
1601
+ * phase 1); the content rules it relies on are in `skill-index.ts`.
1602
+ *
1603
+ * Layout (one folder per shared skill, beside the per-agent manifests):
1604
+ *
1605
+ * <library>/skills/<name>/
1606
+ * SKILL.md verbatim body, as promoted
1607
+ * skill.yaml index row (the skill index schema)
1608
+ * files/... reference files, verbatim, text-only, size-capped
1609
+ * .versions/<n>/ the same three entries for each replaced version
1610
+ *
1611
+ * Load-bearing rules:
1612
+ *
1613
+ * - **Org-tier write semantics.** The store is readable by every caller allowed
1614
+ * to read the federation and writable only through {@link promoteSkill}. The
1615
+ * ordinary federation write path never touches `<library>/skills`.
1616
+ * - **One namespace, rename at promotion.** A shared name belongs to one skill.
1617
+ * Promoting under a taken name throws {@link SkillNameCollisionError} with a
1618
+ * free suggestion, unless the caller updates that skill by passing the
1619
+ * current version as `expectedVersion` (also the compare-and-swap that stops
1620
+ * two promotions from silently overwriting each other). A renamed skill keeps
1621
+ * its front-matter name in `source_name`.
1622
+ * - **Fail closed on reads.** A row that does not validate is not served.
1623
+ */
1624
+
1625
+ type Dict$2 = Record<string, unknown>;
1626
+ declare const SKILL_INDEX_SPEC_VERSION = "0.1";
1627
+ declare const SKILLS_DIRNAME = "skills";
1628
+ declare const INDEX_ROW_NAME = "skill.yaml";
1629
+ declare const FILES_DIRNAME = "files";
1630
+ declare const VERSIONS_DIRNAME = ".versions";
1631
+ /** The shared store behaves as org-tier memory: readable by every federation
1632
+ * reader, writable by no one through the ordinary write API. */
1633
+ declare const SKILL_STORE_WRITE_POLICY = "read-only";
1634
+ /** Promotion widens nothing unless the promoting caller asks it to. */
1635
+ declare const DEFAULT_SKILL_VISIBILITY = "private";
1636
+ declare const MAX_INDEX_ROW_BYTES: number;
1637
+ /** Base class for every refusal the skill store raises. */
1638
+ declare class SkillIndexError extends Error {
1639
+ constructor(message: string);
1640
+ }
1641
+ /** An index row does not conform to the skill index schema. */
1642
+ declare class InvalidSkillRowError extends SkillIndexError {
1643
+ readonly errors: string[];
1644
+ constructor(errors: string[]);
1645
+ }
1646
+ /** The shared name is already taken and the caller did not ask to update it. */
1647
+ declare class SkillNameCollisionError extends SkillIndexError {
1648
+ readonly skillName: string;
1649
+ readonly existingVersion: number;
1650
+ readonly suggestedName: string | null;
1651
+ constructor(name: string, existingVersion: number, suggestedName: string | null);
1652
+ }
1653
+ /** `expectedVersion` does not match what is in the store. */
1654
+ declare class SkillVersionConflictError extends SkillIndexError {
1655
+ readonly skillName: string;
1656
+ readonly expected: number | null;
1657
+ readonly current: number | null;
1658
+ constructor(name: string, expected: number | null, current: number | null);
1659
+ }
1660
+ /** No shared skill (or no such version of it) exists. */
1661
+ declare class SkillNotFoundError extends SkillIndexError {
1662
+ constructor(message: string);
1663
+ }
1664
+ /** Return `name` if it is a valid shared skill name, else throw {@link SkillIndexError}. */
1665
+ declare function validateSkillName(name: unknown): string;
1666
+ /**
1667
+ * Check a parsed `skill.yaml` mapping against the schema's rules. Returns every
1668
+ * violation found (empty means valid), in the oracle's order and wording.
1669
+ */
1670
+ declare function validateIndexRow(row: unknown): string[];
1671
+ /** One validated `skill.yaml`. */
1672
+ declare class SkillIndexRow {
1673
+ readonly specVersion: string;
1674
+ readonly name: string;
1675
+ readonly sourceName: string | null;
1676
+ readonly description: string;
1677
+ readonly version: number;
1678
+ readonly contentSha256: string;
1679
+ readonly sourceAgent: string;
1680
+ readonly promotedBy: string;
1681
+ readonly promotedAt: string;
1682
+ readonly proposalId: string | null;
1683
+ readonly visibility: string;
1684
+ readonly validTo: string | null;
1685
+ readonly supersedes: number | null;
1686
+ private constructor();
1687
+ /** Validate `data` and build a row; throws {@link InvalidSkillRowError}. */
1688
+ static fromDict(data: unknown): SkillIndexRow;
1689
+ /** Serialize in schema order, omitting absent optional fields. */
1690
+ toDict(): Dict$2;
1691
+ /** False once `valid_to` is on or before `today` (UTC today by default, `YYYY-MM-DD`). */
1692
+ isActive(today?: string): boolean;
1693
+ }
1694
+ /** Read and validate one `skill.yaml`. */
1695
+ declare function loadIndexRow(path: string): SkillIndexRow;
1696
+ /** Validate `row` and write it atomically to `path`. */
1697
+ declare function writeIndexRow(path: string, row: SkillIndexRow): void;
1698
+ /** `<library>/skills` -- the shared skill store. */
1699
+ declare function skillsRoot(libraryPath: string): string;
1700
+ /** The folder of one shared skill; `name` is validated first. */
1701
+ declare function skillDir(libraryPath: string, name: string): string;
1702
+ /**
1703
+ * The index row for `name` (the live one, or an archived `version`). Throws
1704
+ * {@link SkillNotFoundError} when absent and {@link InvalidSkillRowError} when
1705
+ * present but invalid.
1706
+ */
1707
+ declare function getSkillRow(libraryPath: string, name: string, version?: number | null): SkillIndexRow;
1708
+ /**
1709
+ * Every valid live index row, sorted by name. Retired rows are omitted unless
1710
+ * `includeRetired`. Rows that fail validation are skipped (and logged) rather
1711
+ * than served.
1712
+ */
1713
+ declare function listSkillRows(libraryPath: string, options?: {
1714
+ includeRetired?: boolean;
1715
+ today?: string;
1716
+ }): SkillIndexRow[];
1717
+ /** Every version number on disk for `name`, oldest first. */
1718
+ declare function listSkillVersions(libraryPath: string, name: string): number[];
1719
+ /** The row and the stored files of one shared skill version. */
1720
+ declare function readSharedSkill(libraryPath: string, name: string, version?: number | null): {
1721
+ row: SkillIndexRow;
1722
+ content: SkillContent;
1723
+ };
1724
+ /** Whether the stored files still hash to the row's `content_sha256`. */
1725
+ declare function verifySharedSkill(libraryPath: string, name: string, version?: number | null): boolean;
1726
+ /**
1727
+ * A free shared name derived from `name` (`name-2`, `name-3`...). Returns null
1728
+ * only if no candidate within the length limit is free.
1729
+ */
1730
+ declare function suggestAvailableName(libraryPath: string, name: string): string | null;
1731
+ interface PromotionResult {
1732
+ row: SkillIndexRow;
1733
+ /** False when the content already matched the live version (nothing written). */
1734
+ changed: boolean;
1735
+ }
1736
+ interface PromoteSkillOptions {
1737
+ sourceAgent: string;
1738
+ promotedBy: string;
1739
+ /** Shared name; defaults to the front-matter name. Another name renames at promotion. */
1740
+ name?: string | null;
1741
+ /** The live version when replacing a skill; null/absent for a new one. */
1742
+ expectedVersion?: number | null;
1743
+ visibility?: string;
1744
+ proposalId?: string | null;
1745
+ /** ISO 8601 with a UTC offset; defaults to now, to the second. */
1746
+ promotedAt?: string | null;
1747
+ }
1748
+ /**
1749
+ * Write `content` into the shared store as a new skill or a new version.
1750
+ *
1751
+ * This is the store's only write path and performs no authorization: the
1752
+ * caller (the promotion gate) must already have established that a trusted
1753
+ * caller approved it. `content` must come from `readSkillFolder`.
1754
+ *
1755
+ * - `name` defaults to the front-matter name. Another name renames at
1756
+ * promotion; the front-matter name is then kept as `source_name`.
1757
+ * - If the name is free, `expectedVersion` must be null.
1758
+ * - If the name is taken, `expectedVersion` must equal the live version; null
1759
+ * throws {@link SkillNameCollisionError} (with a free name to suggest) and
1760
+ * any other value throws {@link SkillVersionConflictError}.
1761
+ * - Content identical to the live version is a no-op (`changed: false`).
1762
+ *
1763
+ * The previous version moves to `.versions/<n>/`. `skill.yaml` is moved out
1764
+ * first and written last, so a reader never sees a row next to the wrong body;
1765
+ * an interrupted promotion leaves the skill unlisted, with the old version
1766
+ * intact under `.versions/`.
1767
+ */
1768
+ declare function promoteSkill(libraryPath: string, content: SkillContent, options: PromoteSkillOptions): PromotionResult;
1769
+ /**
1770
+ * Set (retire) or clear (restore) the live row's `valid_to`.
1771
+ *
1772
+ * Retirement is a row edit, not a new version: the body and its hash are
1773
+ * untouched, so every version stays fetchable by number. Like
1774
+ * {@link promoteSkill} this performs no authorization; the caller is the gate.
1775
+ * `expectedVersion` must equal the live version (compare-and-swap, under the
1776
+ * same lock promotion writes under).
1777
+ */
1778
+ declare function setSkillValidTo(libraryPath: string, name: string, validTo: string | null, options: {
1779
+ expectedVersion: number;
1780
+ }): SkillIndexRow;
1781
+
1782
+ /**
1783
+ * Skill proposals: the approval gate in front of the shared skill library.
1784
+ * Port of the engine's `core/skill_proposals.py` (plan 005, phase 4).
1785
+ *
1786
+ * An agent *proposes* one of the skills it advertises; a person *approves* it;
1787
+ * only then does {@link promoteSkill} write it into `<library>/skills`.
1788
+ *
1789
+ * Layout (interoperable with the engine: either side reads what the other wrote):
1790
+ *
1791
+ * <library>/.skill-proposals/<proposal_id>/
1792
+ * proposal.yaml the record: what was proposed, by whom, and the decision
1793
+ * content/ a snapshot of the skill folder, taken at proposal time
1794
+ *
1795
+ * Load-bearing rules:
1796
+ *
1797
+ * - **The snapshot is what gets approved.** Proposing copies the skill's files
1798
+ * into `content/` and records their `content_sha256`. Approval re-reads that
1799
+ * snapshot, re-checks every text-only rule and refuses unless it still hashes
1800
+ * to the recorded value, so editing the agent's folder after proposing cannot
1801
+ * change what lands in the library.
1802
+ * - **One human-confirmed approval promotes.** Nothing in this module is
1803
+ * reachable over MCP except {@link createProposal}. Approval, rejection,
1804
+ * retirement and rollback are called by the operator's terminal command,
1805
+ * which asks the person at the keyboard to confirm. Consensus votes on a
1806
+ * proposal's ballot are advisory: they are shown to the approver and recorded
1807
+ * beside the decision, and never promote anything on their own.
1808
+ * - **Every decision is audited before it happens, fail-closed**, in the same
1809
+ * federation audit log the quarantine staging decisions use.
1810
+ *
1811
+ * Nothing here performs caller authorization; the callers (the MCP tool for
1812
+ * proposing, the CLI for deciding) are the gates.
1813
+ */
1814
+
1815
+ type Dict$1 = Record<string, unknown>;
1816
+ declare const PROPOSALS_DIRNAME = ".skill-proposals";
1817
+ declare const RECORD_NAME = "proposal.yaml";
1818
+ declare const CONTENT_DIRNAME = "content";
1819
+ declare const STATUS_PENDING = "pending";
1820
+ declare const STATUS_APPROVED = "approved";
1821
+ declare const STATUS_REJECTED = "rejected";
1822
+ declare const PROPOSER_TRUSTED = "trusted";
1823
+ declare const PROPOSER_QUARANTINED = "quarantined";
1824
+ /** The advisory ballot's options. A ballot only informs the person deciding. */
1825
+ declare const BALLOT_OPTIONS: readonly ["approve", "reject"];
1826
+ /** Tunable limits. `maxPendingPerProposer`: pending proposals one proposer may
1827
+ * hold at once, so a caller cannot fill the library with snapshots nobody
1828
+ * asked for. */
1829
+ declare const SKILL_PROPOSAL_LIMITS: {
1830
+ maxPendingPerProposer: number;
1831
+ maxReasonChars: number;
1832
+ };
1833
+ /** Base class for refusals raised by the proposal gate. */
1834
+ declare class SkillProposalError extends SkillIndexError {
1835
+ constructor(message: string);
1836
+ }
1837
+ /** No proposal with that id. */
1838
+ declare class ProposalNotFoundError extends SkillProposalError {
1839
+ constructor(message: string);
1840
+ }
1841
+ /** The proposal is not pending (it was already decided). */
1842
+ declare class ProposalStateError extends SkillProposalError {
1843
+ constructor(message: string);
1844
+ }
1845
+ /** The snapshot no longer hashes to what was proposed, or to what the approver saw. */
1846
+ declare class ProposalDigestMismatchError extends SkillProposalError {
1847
+ constructor(message: string);
1848
+ }
1849
+ /** One proposal record (`proposal.yaml`). Field names follow the on-disk keys. */
1850
+ interface SkillProposal {
1851
+ proposal_id: string;
1852
+ name: string;
1853
+ description: string;
1854
+ content_sha256: string;
1855
+ source_agent: string;
1856
+ proposed_by: string;
1857
+ proposer_tier: string;
1858
+ created_at: string;
1859
+ path_hint: string;
1860
+ scope: string;
1861
+ visibility: string;
1862
+ project: string | null;
1863
+ origin: Dict$1 | null;
1864
+ files: Dict$1[];
1865
+ ballot: boolean;
1866
+ status: string;
1867
+ decision: Dict$1 | null;
1868
+ }
1869
+ /** The record as written: the engine's key order, absent optionals omitted. */
1870
+ declare function proposalToDict(proposal: SkillProposal): Dict$1;
1871
+ /** Parse a record; throws {@link SkillProposalError} on anything malformed. */
1872
+ declare function proposalFromDict(data: unknown): SkillProposal;
1873
+ /** The row a listing shows: no file list, no decision detail. */
1874
+ declare function proposalSummary(proposal: SkillProposal): Dict$1;
1875
+ declare function isProposalId(value: unknown): value is string;
1876
+ /** `<library>/.skill-proposals`. */
1877
+ declare function proposalsRoot(libraryPath: string): string;
1878
+ /** Read one proposal; throws {@link ProposalNotFoundError} or {@link SkillProposalError}. */
1879
+ declare function loadProposal(libraryPath: string, proposalId: string): SkillProposal;
1880
+ /** Every readable proposal (optionally of one `status`), oldest first. */
1881
+ declare function listProposals(libraryPath: string, status?: string | null): SkillProposal[];
1882
+ interface CreateProposalOptions {
1883
+ /** The advertised SkillRef, as it sits in the agent's manifest. */
1884
+ ref: Readonly<Dict$1>;
1885
+ sourceAgent: string;
1886
+ proposedBy: string;
1887
+ proposerTier: string;
1888
+ createdAt?: string | null;
1889
+ }
1890
+ /**
1891
+ * Snapshot `content` into a new pending proposal.
1892
+ *
1893
+ * `content` must come from `readSkillFolder` over the folder the agent's own
1894
+ * reader resolved for `ref` (the advertised SkillRef). Returns
1895
+ * `{ proposal, created }`: proposing the same bytes of the same skill again
1896
+ * while a proposal is pending returns that proposal unchanged.
1897
+ */
1898
+ declare function createProposal(libraryPath: string, content: SkillContent, options: CreateProposalOptions): {
1899
+ proposal: SkillProposal;
1900
+ created: boolean;
1901
+ };
1902
+ /** The snapshot, re-checked against every content rule and its recorded hash. */
1903
+ declare function readProposalContent(libraryPath: string, proposal: SkillProposal): SkillContent;
1904
+ /** The shared skill already using `name`, if any (retired included). */
1905
+ declare function collisionFor(libraryPath: string, name: string): SkillIndexRow | null;
1906
+ /**
1907
+ * Open a consensus ballot under the proposal's id. Votes on it only advise.
1908
+ *
1909
+ * Returns false (and leaves the proposal without a ballot) when one cannot be
1910
+ * opened; a proposal never fails over its advisory ballot.
1911
+ */
1912
+ declare function openAdvisoryBallot(libraryPath: string, proposal: SkillProposal): boolean;
1913
+ type AdvisoryTally = Record<(typeof BALLOT_OPTIONS)[number], {
1914
+ votes: number;
1915
+ voters: string[];
1916
+ }>;
1917
+ /** Vote counts and voters on the proposal's ballot; null without one. */
1918
+ declare function advisoryTally(libraryPath: string, proposal: SkillProposal): AdvisoryTally | null;
1919
+ interface ApprovalResult {
1920
+ proposal: SkillProposal;
1921
+ row: SkillIndexRow;
1922
+ changed: boolean;
1923
+ renamedFrom: string | null;
1924
+ advisory: AdvisoryTally | null;
1925
+ }
1926
+ interface ApproveProposalOptions {
1927
+ approvedBy: string;
1928
+ channel: string;
1929
+ /** At least 8 hex characters: pins the decision to the bytes reviewed. */
1930
+ expectedSha256?: string | null;
1931
+ name?: string | null;
1932
+ update?: boolean;
1933
+ visibility?: string | null;
1934
+ audit?: FederationAudit | null;
1935
+ }
1936
+ /**
1937
+ * Promote a pending proposal's snapshot into the shared library.
1938
+ *
1939
+ * This is the one human-confirmed approval. It is not exposed over MCP; the
1940
+ * operator's terminal command calls it after asking the person at the keyboard
1941
+ * to confirm (`channel` records how).
1942
+ *
1943
+ * Naming: the skill's own name by default, or `name`. When that name is taken,
1944
+ * `update: true` promotes the snapshot as the next version of the existing
1945
+ * skill; otherwise the skill is renamed to a free name and the original name is
1946
+ * reported in `renamedFrom`.
1947
+ */
1948
+ declare function approveProposal(libraryPath: string, proposalId: string, options: ApproveProposalOptions): ApprovalResult;
1949
+ interface RejectProposalOptions {
1950
+ rejectedBy: string;
1951
+ channel: string;
1952
+ reason?: string;
1953
+ expectedSha256?: string | null;
1954
+ audit?: FederationAudit | null;
1955
+ }
1956
+ /** Mark a pending proposal rejected. The snapshot is kept for the record. */
1957
+ declare function rejectProposal(libraryPath: string, proposalId: string, options: RejectProposalOptions): SkillProposal;
1958
+ /**
1959
+ * Retire the live version (`valid_to`, default today). Versions are kept.
1960
+ *
1961
+ * A retired skill leaves the default index and `get_skill` without a version;
1962
+ * every version stays fetchable by number, and {@link rollbackSharedSkill}
1963
+ * brings one back.
1964
+ */
1965
+ declare function retireSharedSkill(libraryPath: string, name: string, options: {
1966
+ retiredBy: string;
1967
+ channel: string;
1968
+ validTo?: string | null;
1969
+ audit?: FederationAudit | null;
1970
+ }): SkillIndexRow;
1971
+ interface RollbackResult {
1972
+ row: SkillIndexRow;
1973
+ /** True when a new version was written. */
1974
+ changed: boolean;
1975
+ /** True when a retirement was lifted. */
1976
+ restored: boolean;
1977
+ }
1978
+ /**
1979
+ * Make version `toVersion`'s content current again, as a NEW version.
1980
+ *
1981
+ * History is never rewritten: rolling back to version 2 of a skill at version 4
1982
+ * writes version 5 with version 2's files (re-hashed before they are used), so
1983
+ * every version stays fetchable. Rolling back a retired skill also lifts the
1984
+ * retirement; rolling back to the live version of a retired skill only lifts it.
1985
+ */
1986
+ declare function rollbackSharedSkill(libraryPath: string, name: string, toVersion: number, options: {
1987
+ promotedBy: string;
1988
+ channel: string;
1989
+ audit?: FederationAudit | null;
1990
+ }): RollbackResult;
1991
+
1043
1992
  /**
1044
1993
  * Participant discovery — a STATIC TS registry replacing Python's `pkgutil`
1045
1994
  * package scan + entry-point duck-type (`participants/__init__.py`).
@@ -1248,4 +2197,4 @@ declare class V0Participant extends ExportStoreParticipant {
1248
2197
  static defaultNativePath(home?: string): string;
1249
2198
  }
1250
2199
 
1251
- export { type AgentStore, type AuthProvider, BOLT_CONFIG, BoltParticipant, type BourdonParticipant, CONTRACT_VERSION, CacheEntry, CascadeParticipant, ClaudeCodeAutomationsParticipant, ClaudeCodeParticipant, ClaudeDesktopCodeParticipant, ClaudeDesktopCoworkParticipant, CodexAutomationsParticipant, CopilotAutomationsParticipant, CopilotCliParticipant, CopilotParticipant, CopilotVscodeParticipant, CursorAutomationsParticipant, CursorParticipant, DEFAULT_CACHE_TTL_SECONDS, DEFAULT_RECENT_THREAD_LIMIT, DEFAULT_TITLE_LIMIT, type ExportStoreConfig, ExportStoreParticipant, FIRST_PARTY, GitHubCopilotParticipant, type HealthStatus, HermesParticipant, LOVABLE_CONFIG, LovableParticipant, MIN_PATCHED_VERSION, NETWORK_CONTRACT_VERSION, NetworkParticipant, type NetworkParticipantOptions, NetworkUnavailable, OpenClawApiClient, OpenClawParticipant, type OpenReadonlyOptions, PROJECT_HINTS_ENV, ParticipantAuthError, type ParticipantCtor, ParticipantDiscoveryError, ParticipantError, ParticipantExportError, ParticipantVersionMismatchError, PayloadCache, type RecentThread, SPEC_VERSION, type SqliteDatabase, V0Participant, V0_CONFIG, defaultCacheRoot, discoverParticipants, envAuthProvider, epochToIsoDate, epochToIsoDatetime, friendlyLabel, ghTokenProvider, isParticipant, isoDate, loadProjectHints, openReadonly, parseSessionIndex, parseVisibility, projectKeyFromCwd, recentSessionThreads, resolveCodexHome, resolvePluginParticipants, safeStr, safeStrList, sessionThreadTitles, sqliteAvailable, tableColumns, tableExists, tryOpenReadonly, verifyInstance };
2200
+ export { type AddShareOptions, type AdvisoryTally, type AgentStore, type ApprovalResult, type ApproveProposalOptions, type AuthProvider, BALLOT_OPTIONS, BOLT_CONFIG, BoltParticipant, type BourdonParticipant, CONTENT_DIRNAME, CONTRACT_VERSION, CROSS_AGENT_LOCK_NAME, CacheEntry, CascadeParticipant, ClaudeCodeAutomationsParticipant, ClaudeCodeParticipant, ClaudeDesktopCodeParticipant, ClaudeDesktopCoworkParticipant, CodexAutomationsParticipant, CopilotAutomationsParticipant, CopilotCliParticipant, CopilotParticipant, CopilotVscodeParticipant, type CreateProposalOptions, CursorAutomationsParticipant, CursorParticipant, DEFAULT_CACHE_TTL_SECONDS, DEFAULT_RECENT_THREAD_LIMIT, DEFAULT_SKILL_VISIBILITY, DEFAULT_TITLE_LIMIT, type ExportStoreConfig, ExportStoreParticipant, FILES_DIRNAME, FIRST_PARTY, GitHubCopilotParticipant, type HealthStatus, HermesParticipant, INDEX_ROW_NAME, InvalidSkillRowError, KIND_SKILL, LIST_ALL, LIST_APPROVED, LIST_OFF, LOVABLE_CONFIG, type LocalSkill, type LocalSkillScan, LovableParticipant, MAX_INDEX_ROW_BYTES, MAX_LOCK_BYTES, MAX_PATH_HINT_CHARS, MAX_REFERENCE_FILES, MAX_REFERENCE_FILE_BYTES, MAX_SHARES_BYTES, MAX_SKILL_BODY_BYTES, MAX_SKILL_REFS, MAX_SKILL_TOTAL_BYTES, MAX_SKIPPED_FILES, MIN_PATCHED_VERSION, NETWORK_CONTRACT_VERSION, NetworkParticipant, type NetworkParticipantOptions, NetworkUnavailable, ORIGIN_AUTHORED, ORIGIN_INSTALLED, OpenClawApiClient, OpenClawParticipant, type OpenReadonlyOptions, PROJECT_HINTS_ENV, PROPOSALS_DIRNAME, PROPOSER_QUARANTINED, PROPOSER_TRUSTED, ParticipantAuthError, type ParticipantCtor, ParticipantDiscoveryError, ParticipantError, ParticipantExportError, ParticipantVersionMismatchError, PayloadCache, type PromoteSkillOptions, type PromotionResult, ProposalDigestMismatchError, ProposalStateError, RECORD_NAME, type RecentThread, type RejectProposalOptions, type RollbackResult, SCOPE_PROJECT, SCOPE_USER, SHARES_FILENAME, SHARES_VERSION, SHARE_ACTOR, SKILLS_ADVERTISE_ENV, SKILLS_DIRNAME, SKILLS_NO_ADVERTISE_ENV, SKILL_BODY_NAME, SKILL_INDEX_SPEC_VERSION, SKILL_PROJECTS_ENV, SKILL_PROPOSAL_LIMITS, SKILL_READERS, SKILL_REF_VISIBILITY, SKILL_STORE_WRITE_POLICY, SKIP_REASONS, SPEC_VERSION, STATUS_APPROVED, STATUS_PENDING, STATUS_REJECTED, STORE_AGENT, STORE_CROSS_AGENT, type ScanSkillSourcesOptions, type SkillAdvertiser, type SkillContent, SkillContentError, type SkillFolderFilter, type SkillFrontMatter, SkillIndexError, SkillIndexRow, SkillNameCollisionError, SkillNotFoundError, type SkillProposal, SkillProposalError, ProposalNotFoundError as SkillProposalNotFoundError, type SkillScanReport, type SkillShare, SkillShareError, type SkillSource, type SkillSourceOptions, SkillVersionConflictError, type SkillsListingMode, type SkippedFile, type SkippedSkill, type SqliteDatabase, TEXT_FILE_SUFFIXES, V0Participant, V0_CONFIG, VERSIONS_DIRNAME, VIA_COPY, VIA_LINK, addShare, advertisedSkills, advisoryTally, approveProposal, claudeCodeSkillSources, codePointSlice, codexSkillSources, collisionFor, computeContentSha256, createProposal, crossAgentSkillSources, cursorSkillSources, defaultCacheRoot, defaultSharesLibrary, discoverParticipants, envAuthProvider, epochToIsoDate, epochToIsoDatetime, friendlyLabel, getSkillRow, ghTokenProvider, isParticipant, isProposalId, isoDate, listProposals, listSkillRows, listSkillVersions, loadIndexRow, loadProjectHints, loadProposal, loadShares, localSkills, openAdvisoryBallot, openReadonly, originFromLockEntry, parseFrontMatter, parseSessionIndex, parseVisibility, projectKeyFromCwd, promoteSkill, proposalFromDict, proposalSummary, proposalToDict, proposalsRoot, pyStrip, readInstallLock, readProposalContent, readSharedSkill, readSkillFolder, recentSessionThreads, rejectProposal, removeShares, resolveAdvertisedFolder, resolveCodexHome, resolvePluginParticipants, retireSharedSkill, rollbackSharedSkill, safeStr, safeStrList, scanLocalSkill, scanSkillSources, sessionThreadTitles, setSkillValidTo, shareAppliesTo, shareFolderKey, shareKey, shareSourceKey, shareToDict, sharesPath, skillDir, skillProjects, skillSourcesForAgent, skillsAdvertisingEnabled, skillsListingMode, skillsRoot, sqliteAvailable, suggestAvailableName, tableColumns, tableExists, tryOpenReadonly, userHome, validateIndexRow, validateSkillName, verifyInstance, verifySharedSkill, writeIndexRow };