@hasna/skills 0.8.2 → 0.8.3

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/bin/worker.js CHANGED
@@ -76164,11 +76164,11 @@ var init_dist_es9 = __esm(() => {
76164
76164
  });
76165
76165
 
76166
76166
  // src/server/worker.ts
76167
- import { randomUUID as randomUUID5 } from "crypto";
76167
+ import { randomUUID as randomUUID6 } from "crypto";
76168
76168
  // package.json
76169
76169
  var package_default = {
76170
76170
  name: "@hasna/skills",
76171
- version: "0.8.2",
76171
+ version: "0.8.3",
76172
76172
  description: "Skills library for AI coding agents",
76173
76173
  type: "module",
76174
76174
  bin: {
@@ -87454,11 +87454,82 @@ function looksLikeSqlitePath(value) {
87454
87454
  return SQLITE_EXTENSIONS.some((extension) => value.toLowerCase().endsWith(extension));
87455
87455
  }
87456
87456
 
87457
+ // src/server/execution-grant-store.ts
87458
+ import { randomUUID } from "crypto";
87459
+ function next(p2, id, grants, expected) {
87460
+ return {
87461
+ schema: "hasna.skills-execution-grants.v1",
87462
+ profileId: id,
87463
+ workspaceId: p2.orgId,
87464
+ revision: randomUUID(),
87465
+ previousRevision: expected,
87466
+ updatedAt: new Date().toISOString(),
87467
+ actorId: p2.userId,
87468
+ grants: structuredClone(grants)
87469
+ };
87470
+ }
87471
+ function document(row) {
87472
+ return row ? typeof row.document_json === "string" ? JSON.parse(row.document_json) : structuredClone(row.document_json) : null;
87473
+ }
87474
+
87475
+ class MemoryExecutionGrantStore {
87476
+ current = new Map;
87477
+ history = new Map;
87478
+ async get(p2, id, revision) {
87479
+ return structuredClone((revision ? this.history.get(JSON.stringify([p2.orgId, id, revision])) : this.current.get(JSON.stringify([p2.orgId, id]))) ?? null);
87480
+ }
87481
+ async save(p2, id, grants, expected) {
87482
+ const key = JSON.stringify([p2.orgId, id]);
87483
+ if ((this.current.get(key)?.revision ?? null) !== expected)
87484
+ return null;
87485
+ const value = next(p2, id, grants, expected);
87486
+ this.current.set(key, value);
87487
+ this.history.set(JSON.stringify([p2.orgId, id, value.revision]), value);
87488
+ return structuredClone(value);
87489
+ }
87490
+ }
87491
+
87492
+ class SqliteExecutionGrantStore {
87493
+ db;
87494
+ constructor(db) {
87495
+ this.db = db;
87496
+ }
87497
+ async get(p2, id, revision) {
87498
+ return document(revision ? this.db.query("SELECT document_json FROM skills_execution_grant_revisions WHERE org_id=? AND profile_id=? AND revision=?").get(p2.orgId, id, revision) : this.db.query("SELECT document_json FROM skills_execution_grants WHERE org_id=? AND profile_id=?").get(p2.orgId, id));
87499
+ }
87500
+ async save(p2, id, grants, expected) {
87501
+ return this.db.transaction(() => {
87502
+ const value = next(p2, id, grants, expected), json = JSON.stringify(value);
87503
+ const row = expected === null ? this.db.query("INSERT INTO skills_execution_grants(org_id,profile_id,revision,document_json) VALUES(?,?,?,?) ON CONFLICT(org_id,profile_id) DO NOTHING RETURNING document_json").get(p2.orgId, id, value.revision, json) : this.db.query("UPDATE skills_execution_grants SET revision=?,document_json=? WHERE org_id=? AND profile_id=? AND revision=? RETURNING document_json").get(value.revision, json, p2.orgId, id, expected);
87504
+ if (!row)
87505
+ return null;
87506
+ this.db.query("INSERT INTO skills_execution_grant_revisions(org_id,profile_id,revision,document_json) VALUES(?,?,?,?)").run(p2.orgId, id, value.revision, json);
87507
+ return document(row);
87508
+ })();
87509
+ }
87510
+ }
87511
+
87512
+ class PostgresExecutionGrantStore {
87513
+ sql;
87514
+ constructor(sql) {
87515
+ this.sql = sql;
87516
+ }
87517
+ async get(p2, id, revision) {
87518
+ const rows = revision ? await this.sql`SELECT document_json FROM skills_execution_grant_revisions WHERE org_id=${p2.orgId} AND profile_id=${id} AND revision=${revision}` : await this.sql`SELECT document_json FROM skills_execution_grants WHERE org_id=${p2.orgId} AND profile_id=${id}`;
87519
+ return document(rows[0]);
87520
+ }
87521
+ async save(p2, id, grants, expected) {
87522
+ const value = next(p2, id, grants, expected), json = JSON.stringify(value);
87523
+ const rows = expected === null ? await this.sql`WITH changed AS (INSERT INTO skills_execution_grants(org_id,profile_id,revision,document_json) VALUES(${p2.orgId},${id},${value.revision},${json}::jsonb) ON CONFLICT(org_id,profile_id) DO NOTHING RETURNING *) INSERT INTO skills_execution_grant_revisions(org_id,profile_id,revision,document_json) SELECT org_id,profile_id,revision,document_json FROM changed RETURNING document_json` : await this.sql`WITH changed AS (UPDATE skills_execution_grants SET revision=${value.revision},document_json=${json}::jsonb WHERE org_id=${p2.orgId} AND profile_id=${id} AND revision=${expected} RETURNING *) INSERT INTO skills_execution_grant_revisions(org_id,profile_id,revision,document_json) SELECT org_id,profile_id,revision,document_json FROM changed RETURNING document_json`;
87524
+ return document(rows[0]);
87525
+ }
87526
+ }
87527
+
87457
87528
  // src/server/sqlite-store.ts
87458
87529
  import { Database } from "bun:sqlite";
87459
87530
 
87460
87531
  // src/server/selection-store.ts
87461
- import { randomUUID } from "crypto";
87532
+ import { randomUUID as randomUUID2 } from "crypto";
87462
87533
  function selections(value) {
87463
87534
  return typeof value === "string" ? JSON.parse(value) : structuredClone(value);
87464
87535
  }
@@ -87499,15 +87570,15 @@ class MemorySkillSelectionStore {
87499
87570
  const key = this.key(p2.orgId, id), previous = this.profiles.get(key);
87500
87571
  if ((previous?.revision ?? null) !== expected)
87501
87572
  return null;
87502
- const next = {
87573
+ const next2 = {
87503
87574
  id,
87504
87575
  workspaceId: p2.orgId,
87505
- revision: randomUUID(),
87576
+ revision: randomUUID2(),
87506
87577
  selections: structuredClone(selected),
87507
87578
  updatedAt: new Date().toISOString()
87508
87579
  };
87509
- this.profiles.set(key, next);
87510
- return structuredClone(next);
87580
+ this.profiles.set(key, next2);
87581
+ return structuredClone(next2);
87511
87582
  }
87512
87583
  async getStationState(p2, id) {
87513
87584
  return structuredClone(this.states.get(this.key(p2.orgId, p2.userId, id)) ?? null);
@@ -87515,15 +87586,15 @@ class MemorySkillSelectionStore {
87515
87586
  async saveStationState(p2, id, input) {
87516
87587
  if (this.profiles.get(this.key(p2.orgId, input.profileId))?.revision !== input.profileRevision)
87517
87588
  return null;
87518
- const next = {
87589
+ const next2 = {
87519
87590
  ...structuredClone(input),
87520
87591
  stationId: id,
87521
87592
  workspaceId: p2.orgId,
87522
87593
  actorId: p2.userId,
87523
87594
  appliedAt: new Date().toISOString()
87524
87595
  };
87525
- this.states.set(this.key(p2.orgId, p2.userId, id), next);
87526
- return structuredClone(next);
87596
+ this.states.set(this.key(p2.orgId, p2.userId, id), next2);
87597
+ return structuredClone(next2);
87527
87598
  }
87528
87599
  }
87529
87600
 
@@ -87537,7 +87608,7 @@ class SqliteSkillSelectionStore {
87537
87608
  return row ? profile(row) : null;
87538
87609
  }
87539
87610
  async saveProfile(p2, id, selected, expected) {
87540
- const revision = randomUUID(), timestamp = new Date().toISOString();
87611
+ const revision = randomUUID2(), timestamp = new Date().toISOString();
87541
87612
  const row = expected === null ? this.db.query("INSERT INTO skills_profiles(org_id,profile_id,revision,selections_json,updated_at) VALUES(?,?,?,?,?) ON CONFLICT(org_id,profile_id) DO NOTHING RETURNING *").get(p2.orgId, id, revision, JSON.stringify(selected), timestamp) : this.db.query("UPDATE skills_profiles SET revision=?,selections_json=?,updated_at=? WHERE org_id=? AND profile_id=? AND revision=? RETURNING *").get(revision, JSON.stringify(selected), timestamp, p2.orgId, id, expected);
87542
87613
  return row ? profile(row) : null;
87543
87614
  }
@@ -87563,7 +87634,7 @@ class PostgresSkillSelectionStore {
87563
87634
  return rows[0] ? profile(rows[0]) : null;
87564
87635
  }
87565
87636
  async saveProfile(p2, id, selected, expected) {
87566
- const revision = randomUUID(), timestamp = new Date().toISOString();
87637
+ const revision = randomUUID2(), timestamp = new Date().toISOString();
87567
87638
  const rows = expected === null ? await this.sql`INSERT INTO skills_profiles(org_id,profile_id,revision,selections_json,updated_at) VALUES(${p2.orgId},${id},${revision},${JSON.stringify(selected)}::jsonb,${timestamp}) ON CONFLICT(org_id,profile_id) DO NOTHING RETURNING *` : await this.sql`UPDATE skills_profiles SET revision=${revision},selections_json=${JSON.stringify(selected)}::jsonb,updated_at=${timestamp} WHERE org_id=${p2.orgId} AND profile_id=${id} AND revision=${expected} RETURNING *`;
87568
87639
  return rows[0] ? profile(rows[0]) : null;
87569
87640
  }
@@ -87581,7 +87652,7 @@ class PostgresSkillSelectionStore {
87581
87652
  }
87582
87653
 
87583
87654
  // src/server/sqlite-store.ts
87584
- import { randomUUID as randomUUID3 } from "crypto";
87655
+ import { randomUUID as randomUUID4 } from "crypto";
87585
87656
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, readdirSync } from "fs";
87586
87657
  import { dirname as dirname3, join as join5 } from "path";
87587
87658
 
@@ -87642,7 +87713,7 @@ function defaultStartDirs() {
87642
87713
  }
87643
87714
 
87644
87715
  // src/server/rows.ts
87645
- import { randomUUID as randomUUID2 } from "crypto";
87716
+ import { randomUUID as randomUUID3 } from "crypto";
87646
87717
  function nowIso() {
87647
87718
  return new Date().toISOString();
87648
87719
  }
@@ -87650,7 +87721,7 @@ function normalizeLimit(limit) {
87650
87721
  return Number.isFinite(limit) ? Math.max(0, Math.trunc(limit)) : 0;
87651
87722
  }
87652
87723
  function runId() {
87653
- return `run_${Date.now().toString(36)}_${randomUUID2().slice(0, 8)}`;
87724
+ return `run_${Date.now().toString(36)}_${randomUUID3().slice(0, 8)}`;
87654
87725
  }
87655
87726
  function rowToRun(row) {
87656
87727
  return {
@@ -87873,6 +87944,9 @@ class SqliteSkillsStore {
87873
87944
  get selectionStore() {
87874
87945
  return new SqliteSkillSelectionStore(this.db);
87875
87946
  }
87947
+ get executionGrantStore() {
87948
+ return new SqliteExecutionGrantStore(this.db);
87949
+ }
87876
87950
  backend;
87877
87951
  db;
87878
87952
  closed = false;
@@ -87974,7 +88048,7 @@ class SqliteSkillsStore {
87974
88048
  JSON.stringify(input.input),
87975
88049
  JSON.stringify(input.args),
87976
88050
  input.idempotencyKey ?? null,
87977
- randomUUID3(),
88051
+ randomUUID4(),
87978
88052
  nowIso()
87979
88053
  ]);
87980
88054
  return rowToRun(row);
@@ -88284,21 +88358,21 @@ class SqliteSkillsStore {
88284
88358
  if (expectedRevisionId !== current.revisionId) {
88285
88359
  throw new SkillRevisionConflictError(slug, expectedRevisionId, current.revisionId);
88286
88360
  }
88287
- const next = { ...current, ...patch };
88361
+ const next2 = { ...current, ...patch };
88288
88362
  return this.db.transaction(() => {
88289
- const revisionId = revisionIdOfRecord(next);
88363
+ const revisionId = revisionIdOfRecord(next2);
88290
88364
  const row = this.get(`UPDATE skills_registry
88291
88365
  SET display_name = ?, description = ?, category = ?, tags_json = ?, kind = ?, version = ?, skill_md = ?,
88292
88366
  revision_id = ?, revision_number = revision_number + 1, updated_at = ?
88293
88367
  WHERE org_id = ? AND slug = ? AND tombstoned_at IS NULL AND revision_id = ?
88294
88368
  RETURNING *`, [
88295
- next.displayName,
88296
- next.description,
88297
- next.category,
88298
- JSON.stringify(next.tags),
88299
- next.kind,
88300
- next.version ?? null,
88301
- next.skillMd ?? null,
88369
+ next2.displayName,
88370
+ next2.description,
88371
+ next2.category,
88372
+ JSON.stringify(next2.tags),
88373
+ next2.kind,
88374
+ next2.version ?? null,
88375
+ next2.skillMd ?? null,
88302
88376
  revisionId,
88303
88377
  nowIso(),
88304
88378
  principal.orgId,
@@ -88318,7 +88392,7 @@ class SqliteSkillsStore {
88318
88392
  }
88319
88393
  this.db.run("DELETE FROM skills_tags WHERE org_id = ? AND slug = ?", [principal.orgId, slug]);
88320
88394
  const insertTag = this.db.prepare("INSERT OR IGNORE INTO skills_tags (org_id, slug, tag) VALUES (?, ?, ?)");
88321
- for (const tag of next.tags) {
88395
+ for (const tag of next2.tags) {
88322
88396
  if (!tag.trim())
88323
88397
  continue;
88324
88398
  insertTag.run(principal.orgId, slug, tag);
@@ -88494,8 +88568,245 @@ function parseScopes(value) {
88494
88568
  }
88495
88569
  }
88496
88570
 
88497
- // node_modules/@hasna/contracts/dist/client/transport.js
88571
+ // node_modules/@hasna/secrets/dist/sdk.js
88498
88572
  import { createRequire } from "module";
88573
+ import { createRequire as createRequire2 } from "module";
88574
+ var __esm2 = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
88575
+ var CredentialResolutionError;
88576
+ var CredentialFileUnsafeError;
88577
+ var MAX_CREDENTIAL_FILE_BYTES;
88578
+ var SAFE_APP_SLUG;
88579
+ var SAFE_PROFILE;
88580
+ var ILLEGAL_IN_HEADER_VALUE;
88581
+ var VAULT_POINTER_SHAPE;
88582
+ var CREDENTIAL_SHAPED_KEY;
88583
+ var INSPECT_CUSTOM;
88584
+ var CREDENTIAL_SEAL;
88585
+ var AMBIENT_ENVIRONMENT;
88586
+ var SECRETS_PACKAGE_SPECIFIER;
88587
+ var requireSecretsSdk;
88588
+ var ASCII_CONTROL_PATTERN;
88589
+ var DNS_LABEL_PATTERN;
88590
+ var ClientTransportConfigurationError;
88591
+ var HasnaHttpError;
88592
+ var DEFAULT_RETRY_STATUSES;
88593
+ var IDEMPOTENT_METHODS;
88594
+ var AUTHORITY_OVERRIDE_HEADERS;
88595
+ var init_transport = __esm2(() => {
88596
+ CredentialResolutionError = class CredentialResolutionError2 extends Error {
88597
+ appName;
88598
+ attempted;
88599
+ constructor(appName, message, attempted) {
88600
+ super(message);
88601
+ this.name = "CredentialResolutionError";
88602
+ this.appName = appName;
88603
+ this.attempted = attempted;
88604
+ }
88605
+ };
88606
+ CredentialFileUnsafeError = class CredentialFileUnsafeError2 extends Error {
88607
+ path;
88608
+ constructor(path, reason) {
88609
+ super(`Refusing unsafe credential/config file ${path}: ${reason}.`);
88610
+ this.name = "CredentialFileUnsafeError";
88611
+ this.path = path;
88612
+ }
88613
+ };
88614
+ MAX_CREDENTIAL_FILE_BYTES = 64 * 1024;
88615
+ SAFE_APP_SLUG = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
88616
+ SAFE_PROFILE = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/;
88617
+ ILLEGAL_IN_HEADER_VALUE = /[^\t\x20-\x7e]/;
88618
+ VAULT_POINTER_SHAPE = /^[a-z0-9][a-z0-9-]*(?:\/[a-z0-9][a-z0-9-_.]*){2,}$/;
88619
+ CREDENTIAL_SHAPED_KEY = /(?:^|_)(?:API_KEY|KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|AUTH)(?:_|$)/;
88620
+ INSPECT_CUSTOM = Symbol.for("nodejs.util.inspect.custom");
88621
+ CREDENTIAL_SEAL = Symbol.for("hasna:contracts:sealedCredential");
88622
+ AMBIENT_ENVIRONMENT = Symbol.for("hasna:contracts:ambientClientEnvironment");
88623
+ SECRETS_PACKAGE_SPECIFIER = "@hasna/" + "secrets";
88624
+ requireSecretsSdk = createRequire(import.meta.url);
88625
+ ASCII_CONTROL_PATTERN = /[\u0000-\u001f\u007f]/;
88626
+ DNS_LABEL_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
88627
+ ClientTransportConfigurationError = class ClientTransportConfigurationError2 extends Error {
88628
+ appName;
88629
+ sources;
88630
+ constructor(appName, message, sources = []) {
88631
+ super(message);
88632
+ this.name = "ClientTransportConfigurationError";
88633
+ this.appName = appName;
88634
+ this.sources = Object.freeze([...sources]);
88635
+ }
88636
+ };
88637
+ HasnaHttpError = class HasnaHttpError2 extends Error {
88638
+ status;
88639
+ method;
88640
+ path;
88641
+ credentialSource;
88642
+ credentialTier;
88643
+ constructor(method, path, status, body, credential) {
88644
+ const guidance = credential ? `. ${credential.guidance}` : "";
88645
+ super(`Hasna cloud request failed: ${method} ${path} -> ${status}${guidance}`);
88646
+ this.name = "HasnaHttpError";
88647
+ this.status = status;
88648
+ this.method = method;
88649
+ this.path = path;
88650
+ Object.defineProperty(this, "body", {
88651
+ value: body,
88652
+ enumerable: status !== 401 && status !== 403,
88653
+ writable: false,
88654
+ configurable: false
88655
+ });
88656
+ this.credentialSource = credential?.source ?? null;
88657
+ this.credentialTier = credential?.tier ?? null;
88658
+ }
88659
+ };
88660
+ DEFAULT_RETRY_STATUSES = [408, 425, 429, 500, 502, 503, 504];
88661
+ IDEMPOTENT_METHODS = new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
88662
+ AUTHORITY_OVERRIDE_HEADERS = new Set([
88663
+ "host",
88664
+ ":authority",
88665
+ "forwarded",
88666
+ "x-forwarded-host",
88667
+ "x-original-host"
88668
+ ]);
88669
+ });
88670
+ var MAX_CREDENTIAL_FILE_BYTES2;
88671
+ var INSPECT_CUSTOM2;
88672
+ var CREDENTIAL_SEAL2;
88673
+ var AMBIENT_ENVIRONMENT2;
88674
+ var SECRETS_PACKAGE_SPECIFIER2;
88675
+ var requireSecretsSdk2;
88676
+ var IDEMPOTENT_METHODS2;
88677
+ var AUTHORITY_OVERRIDE_HEADERS2;
88678
+ var init_storage = __esm2(() => {
88679
+ MAX_CREDENTIAL_FILE_BYTES2 = 64 * 1024;
88680
+ INSPECT_CUSTOM2 = Symbol.for("nodejs.util.inspect.custom");
88681
+ CREDENTIAL_SEAL2 = Symbol.for("hasna:contracts:sealedCredential");
88682
+ AMBIENT_ENVIRONMENT2 = Symbol.for("hasna:contracts:ambientClientEnvironment");
88683
+ SECRETS_PACKAGE_SPECIFIER2 = "@hasna/" + "secrets";
88684
+ requireSecretsSdk2 = createRequire2(import.meta.url);
88685
+ IDEMPOTENT_METHODS2 = new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
88686
+ AUTHORITY_OVERRIDE_HEADERS2 = new Set([
88687
+ "host",
88688
+ ":authority",
88689
+ "forwarded",
88690
+ "x-forwarded-host",
88691
+ "x-original-host"
88692
+ ]);
88693
+ });
88694
+ var PATHS_RESOLVER_APP_SLUG_RE2;
88695
+ var SQLITE_SIDECAR_FILES;
88696
+ var LEGACY_SERVICE_FILES;
88697
+ var init_data_dir = __esm2(() => {
88698
+ PATHS_RESOLVER_APP_SLUG_RE2 = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
88699
+ SQLITE_SIDECAR_FILES = ["vault.db-wal", "vault.db-shm", "vault.db-journal"];
88700
+ LEGACY_SERVICE_FILES = [
88701
+ "vault.key",
88702
+ "vault.key.enc",
88703
+ "kms.json",
88704
+ "aws.json",
88705
+ ".serve-token"
88706
+ ];
88707
+ });
88708
+ var SecretsTestIsolationError;
88709
+ var TEST_ENTRYPOINT_RE;
88710
+ var init_test_isolation = __esm2(() => {
88711
+ init_data_dir();
88712
+ SecretsTestIsolationError = class SecretsTestIsolationError2 extends Error {
88713
+ constructor(message) {
88714
+ super(message);
88715
+ this.name = "SecretsTestIsolationError";
88716
+ }
88717
+ };
88718
+ TEST_ENTRYPOINT_RE = /\.(test|spec)\.[cm]?[jt]sx?$/;
88719
+ });
88720
+ var init_client = __esm2(() => {
88721
+ init_transport();
88722
+ init_storage();
88723
+ init_test_isolation();
88724
+ init_transport();
88725
+ });
88726
+ init_client();
88727
+ init_client();
88728
+
88729
+ // src/lib/profile-limits.ts
88730
+ var MAX_PROFILE_SELECTIONS = 4096;
88731
+ var MAX_PROFILE_DOCUMENT_BYTES = 8 * 1024 * 1024;
88732
+ var MAX_SKILL_SESSION_ID_CHARS = 256;
88733
+ var PROFILE_SESSION_ENVELOPE_BYTES = MAX_PROFILE_SELECTIONS * 67 + MAX_SKILL_SESSION_ID_CHARS * 6 + 256;
88734
+ var MAX_RESOLVED_PROFILE_BYTES = MAX_PROFILE_DOCUMENT_BYTES - PROFILE_SESSION_ENVELOPE_BYTES;
88735
+
88736
+ // src/lib/selection-cache.ts
88737
+ var MAX_CACHED_PROFILE_AGE_MS = 24 * 60 * 60 * 1000;
88738
+
88739
+ // src/lib/execution-grants.ts
88740
+ var MAX_EXECUTION_GRANT_BYTES = 2 * 1024 * 1024;
88741
+
88742
+ // src/lib/hosted-skill-set.ts
88743
+ var HOSTED_RUNTIMES = new Set(["hosted"]);
88744
+ var HOSTED_SOURCES = new Set(["remote", "private-hosted"]);
88745
+ var HOSTED_METADATA_SET_EMPTY_ERROR = [
88746
+ "The hosted metadata skill set is empty, but the packaging guards that depend on it",
88747
+ "only mean anything while it is non-empty: an empty set makes every one of them pass",
88748
+ "vacuously and silently removes the protection that keeps hosted implementation source",
88749
+ "out of the published package.",
88750
+ "",
88751
+ "If emptying the set is intentional (every hosted skill was deleted or converted to a",
88752
+ "runnable in-repo skill), retire the derived guards in the SAME change \u2014 do not let them",
88753
+ "stay green and empty."
88754
+ ].join(`
88755
+ `);
88756
+ var SLUG = "[^,{}/]+";
88757
+ var BRACE_SOURCE_EXCLUSION = new RegExp(`^!skills/\\{(${SLUG}(?:,${SLUG})+)\\}/src$`);
88758
+ var SINGLE_SOURCE_EXCLUSION = new RegExp(`^!skills/(${SLUG})/src$`);
88759
+
88760
+ // src/lib/skill-validation.ts
88761
+ var RESERVED_SKILL_ENTRIES = new Set([
88762
+ ".env",
88763
+ ".npmrc",
88764
+ ".pypirc",
88765
+ ".netrc",
88766
+ "id_rsa",
88767
+ "id_ed25519"
88768
+ ]);
88769
+ var KNOWN_TOP_LEVEL_ENTRIES = new Set([
88770
+ ".claude",
88771
+ ".env.example",
88772
+ ".gitignore",
88773
+ ".skills",
88774
+ "CLAUDE.md",
88775
+ "AGENTS.md",
88776
+ "LICENSE",
88777
+ "PROJECT_OVERVIEW.md",
88778
+ "QUICKSTART.md",
88779
+ "README.md",
88780
+ "SKILL.md",
88781
+ "api-docs-list.json",
88782
+ "auth.ts",
88783
+ "bun.lock",
88784
+ "bunfig.toml",
88785
+ "data",
88786
+ "dist",
88787
+ "examples",
88788
+ "exports",
88789
+ "http-client.ts",
88790
+ "index.ts",
88791
+ "install.sh",
88792
+ "installer.ts",
88793
+ "logs",
88794
+ "node_modules",
88795
+ "package.json",
88796
+ "scripts",
88797
+ "skill-install.ts",
88798
+ "skill.json",
88799
+ "src",
88800
+ "tests",
88801
+ "references",
88802
+ "assets",
88803
+ "tsconfig.json",
88804
+ "vision.ts"
88805
+ ]);
88806
+ var VALID_PROVENANCE_SOURCES = new Set(["official", "custom", "remote", "private", "private-hosted", "upstream", "extension"]);
88807
+
88808
+ // node_modules/@hasna/contracts/dist/client/transport.js
88809
+ import { createRequire as createRequire3 } from "module";
88499
88810
  function envToken(name) {
88500
88811
  return name.toUpperCase().replace(/-/g, "_");
88501
88812
  }
@@ -88507,13 +88818,13 @@ function clientTransportEnvKeys(name) {
88507
88818
  };
88508
88819
  }
88509
88820
  var MAX_PACKAGE_BYTES = 1024 * 1024;
88510
- var MAX_CREDENTIAL_FILE_BYTES = 64 * 1024;
88511
- var INSPECT_CUSTOM = Symbol.for("nodejs.util.inspect.custom");
88512
- var CREDENTIAL_SEAL = Symbol.for("hasna:contracts:sealedCredential");
88513
- var AMBIENT_ENVIRONMENT = Symbol.for("hasna:contracts:ambientClientEnvironment");
88514
- var requireSecretsSdk = createRequire(import.meta.url);
88515
- var IDEMPOTENT_METHODS = new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
88516
- var AUTHORITY_OVERRIDE_HEADERS = new Set([
88821
+ var MAX_CREDENTIAL_FILE_BYTES3 = 64 * 1024;
88822
+ var INSPECT_CUSTOM3 = Symbol.for("nodejs.util.inspect.custom");
88823
+ var CREDENTIAL_SEAL3 = Symbol.for("hasna:contracts:sealedCredential");
88824
+ var AMBIENT_ENVIRONMENT3 = Symbol.for("hasna:contracts:ambientClientEnvironment");
88825
+ var requireSecretsSdk3 = createRequire3(import.meta.url);
88826
+ var IDEMPOTENT_METHODS3 = new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
88827
+ var AUTHORITY_OVERRIDE_HEADERS3 = new Set([
88517
88828
  "host",
88518
88829
  ":authority",
88519
88830
  "forwarded",
@@ -88535,13 +88846,6 @@ var MAX_MANIFEST_BYTES = MAX_SKILL_MD_BYTES + 64000;
88535
88846
  var ALLOWED_PUBLISH_PARTS = new Set(["manifest", "bundle"]);
88536
88847
  var MAX_VERSION_MANIFEST_BYTES = 512 * 1024;
88537
88848
 
88538
- // src/lib/profile-limits.ts
88539
- var MAX_PROFILE_SELECTIONS = 4096;
88540
- var MAX_PROFILE_DOCUMENT_BYTES = 8 * 1024 * 1024;
88541
- var MAX_SKILL_SESSION_ID_CHARS = 256;
88542
- var PROFILE_SESSION_ENVELOPE_BYTES = MAX_PROFILE_SELECTIONS * 67 + MAX_SKILL_SESSION_ID_CHARS * 6 + 256;
88543
- var MAX_RESOLVED_PROFILE_BYTES = MAX_PROFILE_DOCUMENT_BYTES - PROFILE_SESSION_ENVELOPE_BYTES;
88544
-
88545
88849
  // src/sdk/execution/storage.ts
88546
88850
  class MemoryRunExecutionStore {
88547
88851
  durable = false;
@@ -88619,15 +88923,15 @@ class MemoryRunExecutionStore {
88619
88923
  if (attempt.leaseGeneration !== input.expectedLeaseGeneration) {
88620
88924
  return { ok: false, reason: "STALE_GENERATION" };
88621
88925
  }
88622
- const next = {
88926
+ const next2 = {
88623
88927
  ...attempt,
88624
88928
  leaseGeneration: attempt.leaseGeneration + 1,
88625
88929
  workerId: input.workerId,
88626
88930
  claimedAt: new Date().toISOString(),
88627
88931
  status: "leased"
88628
88932
  };
88629
- this.attempts.set(attempt.attemptId, next);
88630
- return { ok: true, attempt: next, leaseGeneration: next.leaseGeneration };
88933
+ this.attempts.set(attempt.attemptId, next2);
88934
+ return { ok: true, attempt: next2, leaseGeneration: next2.leaseGeneration };
88631
88935
  }
88632
88936
  async recordLaunchIntent(input) {
88633
88937
  const run = this.runs.get(input.runId);
@@ -88639,28 +88943,28 @@ class MemoryRunExecutionStore {
88639
88943
  return { ok: false, reason: "NO_SUCH_ATTEMPT" };
88640
88944
  if (attempt.status === "terminal")
88641
88945
  return { ok: false, reason: "ATTEMPT_TERMINAL" };
88642
- const next = {
88946
+ const next2 = {
88643
88947
  ...attempt,
88644
88948
  clientToken: input.clientToken,
88645
88949
  requestDigest: input.requestDigest,
88646
88950
  startedBy: input.startedBy,
88647
88951
  launchState: "launching"
88648
88952
  };
88649
- this.attempts.set(attempt.attemptId, next);
88953
+ this.attempts.set(attempt.attemptId, next2);
88650
88954
  this.runs.set(input.runId, { ...run, currentAttemptId: attempt.attemptId, updatedAt: new Date().toISOString() });
88651
- return { ok: true, attempt: next };
88955
+ return { ok: true, attempt: next2 };
88652
88956
  }
88653
88957
  async recordLaunchState(input) {
88654
88958
  const attempt = this.attempts.get(input.attemptId);
88655
88959
  if (!attempt)
88656
88960
  return null;
88657
- const next = {
88961
+ const next2 = {
88658
88962
  ...attempt,
88659
88963
  launchState: input.launchState,
88660
88964
  ...input.taskId === undefined ? {} : { taskId: input.taskId }
88661
88965
  };
88662
- this.attempts.set(attempt.attemptId, next);
88663
- return next;
88966
+ this.attempts.set(attempt.attemptId, next2);
88967
+ return next2;
88664
88968
  }
88665
88969
  async recordTransition(transition) {
88666
88970
  this.transitions.push(transition);
@@ -88669,9 +88973,9 @@ class MemoryRunExecutionStore {
88669
88973
  const attempt = this.attempts.get(attemptId);
88670
88974
  if (!attempt)
88671
88975
  return null;
88672
- const next = { ...attempt, status: "terminal" };
88673
- this.attempts.set(attemptId, next);
88674
- return next;
88976
+ const next2 = { ...attempt, status: "terminal" };
88977
+ this.attempts.set(attemptId, next2);
88978
+ return next2;
88675
88979
  }
88676
88980
  async writeReceipt(receipt) {
88677
88981
  this.receipts.set(receipt.runId + "\x00" + receipt.attemptId, receipt);
@@ -88689,22 +88993,22 @@ class MemoryRunExecutionStore {
88689
88993
  this.attempts.set(attemptId, { ...attempt, status: "terminal" });
88690
88994
  }
88691
88995
  }
88692
- const next = {
88996
+ const next2 = {
88693
88997
  ...run,
88694
88998
  status,
88695
88999
  terminalReceiptId: receiptId,
88696
89000
  updatedAt: new Date().toISOString()
88697
89001
  };
88698
- this.runs.set(runId2, next);
88699
- return next;
89002
+ this.runs.set(runId2, next2);
89003
+ return next2;
88700
89004
  }
88701
89005
  async setRunStatus(runId2, status) {
88702
89006
  const run = this.runs.get(runId2);
88703
89007
  if (!run)
88704
89008
  return null;
88705
- const next = { ...run, status, updatedAt: new Date().toISOString() };
88706
- this.runs.set(runId2, next);
88707
- return next;
89009
+ const next2 = { ...run, status, updatedAt: new Date().toISOString() };
89010
+ this.runs.set(runId2, next2);
89011
+ return next2;
88708
89012
  }
88709
89013
  }
88710
89014
  function isMemoryTerminal(status) {
@@ -88782,28 +89086,28 @@ async function failRun(store, run, code, message) {
88782
89086
  try {
88783
89087
  await store.appendLog(run.id, run.orgId, "error", message);
88784
89088
  } catch {}
88785
- const next = await fencedTransition(store, run, {
89089
+ const next2 = await fencedTransition(store, run, {
88786
89090
  status: "failed",
88787
89091
  errorCode: code,
88788
89092
  errorMessage: message,
88789
89093
  completedAt: new Date().toISOString()
88790
89094
  });
88791
- return next ?? run;
89095
+ return next2 ?? run;
88792
89096
  }
88793
89097
  async function fencedTransition(store, run, patch) {
88794
89098
  if (!store.transitionRun)
88795
89099
  return store.updateRun(run.id, patch);
88796
- const next = await store.transitionRun(run.id, patch, run.leaseGeneration);
88797
- if (!next) {
89100
+ const next2 = await store.transitionRun(run.id, patch, run.leaseGeneration);
89101
+ if (!next2) {
88798
89102
  try {
88799
89103
  await store.appendLog(run.id, run.orgId, "warn", `late write rejected: run no longer owned at lease_generation ${run.leaseGeneration}`);
88800
89104
  } catch {}
88801
89105
  }
88802
- return next;
89106
+ return next2;
88803
89107
  }
88804
89108
 
88805
89109
  // src/server/store.ts
88806
- import { randomUUID as randomUUID4 } from "crypto";
89110
+ import { randomUUID as randomUUID5 } from "crypto";
88807
89111
  function recordFieldsOf(input, carriedBundle, carriedSkillMd) {
88808
89112
  return {
88809
89113
  slug: input.slug,
@@ -88846,6 +89150,7 @@ function instantiateStore(target, sqliteOptions) {
88846
89150
 
88847
89151
  class MemorySkillsStore {
88848
89152
  selectionStore = new MemorySkillSelectionStore;
89153
+ executionGrantStore = new MemoryExecutionGrantStore;
88849
89154
  backend = { kind: "memory", durable: false, label: "memory (non-durable)" };
88850
89155
  apiKeys = new Map;
88851
89156
  runs = new Map;
@@ -88890,7 +89195,7 @@ class MemorySkillsStore {
88890
89195
  input: input.input,
88891
89196
  args: input.args,
88892
89197
  ...idemKey ? { idempotencyKey: idemKey } : {},
88893
- correlationId: randomUUID4(),
89198
+ correlationId: randomUUID5(),
88894
89199
  costCents: 0,
88895
89200
  leaseGeneration: 0,
88896
89201
  createdAt: now
@@ -88939,11 +89244,11 @@ class MemorySkillsStore {
88939
89244
  return run ? [...this.logs.get(runId2) ?? []] : [];
88940
89245
  }
88941
89246
  async addArtifact(artifact) {
88942
- const next = { ...artifact, createdAt: nowIso() };
89247
+ const next2 = { ...artifact, createdAt: nowIso() };
88943
89248
  const artifacts = this.artifacts.get(artifact.runId) ?? [];
88944
- artifacts.push(next);
89249
+ artifacts.push(next2);
88945
89250
  this.artifacts.set(artifact.runId, artifacts);
88946
- return next;
89251
+ return next2;
88947
89252
  }
88948
89253
  async listArtifacts(principal, runId2) {
88949
89254
  const run = await this.getRun(principal, runId2);
@@ -89049,15 +89354,15 @@ class MemorySkillsStore {
89049
89354
  if (latest.revisionId !== current.revisionId) {
89050
89355
  throw new SkillRevisionConflictError(slug, expectedRevisionId, latest.revisionId);
89051
89356
  }
89052
- const next = {
89357
+ const next2 = {
89053
89358
  ...latest,
89054
89359
  ...patch,
89055
89360
  updatedAt: nowIso(),
89056
89361
  revisionId: revisionIdOfRecord({ ...latest, ...patch }),
89057
89362
  revisionNumber: latest.revisionNumber + 1
89058
89363
  };
89059
- this.skills.set(skillKey(principal.orgId, slug), next);
89060
- return next;
89364
+ this.skills.set(skillKey(principal.orgId, slug), next2);
89365
+ return next2;
89061
89366
  }
89062
89367
  async deleteSkill(principal, slug, tombstoneWindowMs) {
89063
89368
  const current = await this.getSkill(principal, slug);
@@ -89066,9 +89371,9 @@ class MemorySkillsStore {
89066
89371
  if (!current.tombstonedAt) {
89067
89372
  const tombstoned = nowIso();
89068
89373
  const purgeAfter = new Date(Date.now() + tombstoneWindowMs).toISOString();
89069
- const next = { ...current, tombstonedAt: tombstoned, tombstonePurgeAfter: purgeAfter, updatedAt: tombstoned };
89070
- this.skills.set(skillKey(principal.orgId, slug), next);
89071
- return next;
89374
+ const next2 = { ...current, tombstonedAt: tombstoned, tombstonePurgeAfter: purgeAfter, updatedAt: tombstoned };
89375
+ this.skills.set(skillKey(principal.orgId, slug), next2);
89376
+ return next2;
89072
89377
  }
89073
89378
  return current;
89074
89379
  }
@@ -89140,9 +89445,9 @@ class MemorySkillsStore {
89140
89445
  const run = this.runs.get(runId2);
89141
89446
  if (!run)
89142
89447
  return null;
89143
- const next = { ...run, ...patch };
89144
- this.runs.set(runId2, next);
89145
- return next;
89448
+ const next2 = { ...run, ...patch };
89449
+ this.runs.set(runId2, next2);
89450
+ return next2;
89146
89451
  }
89147
89452
  }
89148
89453
  function skillKey(orgId, slug) {
@@ -89159,6 +89464,9 @@ class PostgresSkillsStore {
89159
89464
  get selectionStore() {
89160
89465
  return new PostgresSkillSelectionStore(this.sql);
89161
89466
  }
89467
+ get executionGrantStore() {
89468
+ return new PostgresExecutionGrantStore(this.sql);
89469
+ }
89162
89470
  backend = { kind: "postgres", durable: true, label: "postgres" };
89163
89471
  sql;
89164
89472
  constructor(databaseUrl) {
@@ -89258,7 +89566,7 @@ class PostgresSkillsStore {
89258
89566
  const id = runId();
89259
89567
  const rows = await tx`
89260
89568
  INSERT INTO skills_runs (id, org_id, user_id, skill_slug, requested_slug, status, input_json, args_json, idempotency_key, correlation_id)
89261
- VALUES (${id}, ${input.principal.orgId}, ${input.principal.userId}, ${input.slug}, ${input.slug}, ${"queued"}, ${JSON.stringify(input.input)}::jsonb, ${JSON.stringify(input.args)}::jsonb, ${input.idempotencyKey ?? null}, ${randomUUID4()})
89569
+ VALUES (${id}, ${input.principal.orgId}, ${input.principal.userId}, ${input.slug}, ${input.slug}, ${"queued"}, ${JSON.stringify(input.input)}::jsonb, ${JSON.stringify(input.args)}::jsonb, ${input.idempotencyKey ?? null}, ${randomUUID5()})
89262
89570
  RETURNING *
89263
89571
  `;
89264
89572
  return rowToRun(rows[0]);
@@ -89564,14 +89872,14 @@ class PostgresSkillsStore {
89564
89872
  if (expectedRevisionId !== current.revisionId) {
89565
89873
  throw new SkillRevisionConflictError(slug, expectedRevisionId, current.revisionId);
89566
89874
  }
89567
- const next = { ...current, ...patch };
89875
+ const next2 = { ...current, ...patch };
89568
89876
  return await this.sql.begin(async (tx) => {
89569
- const revisionId = revisionIdOfRecord(next);
89877
+ const revisionId = revisionIdOfRecord(next2);
89570
89878
  const updated = await tx`
89571
89879
  UPDATE skills_registry
89572
- SET display_name = ${next.displayName}, description = ${next.description}, category = ${next.category},
89573
- tags_json = ${JSON.stringify(next.tags)}::jsonb, kind = ${next.kind}, version = ${next.version ?? null},
89574
- skill_md = ${next.skillMd ?? null}, revision_id = ${revisionId}, revision_number = revision_number + 1, updated_at = now()
89880
+ SET display_name = ${next2.displayName}, description = ${next2.description}, category = ${next2.category},
89881
+ tags_json = ${JSON.stringify(next2.tags)}::jsonb, kind = ${next2.kind}, version = ${next2.version ?? null},
89882
+ skill_md = ${next2.skillMd ?? null}, revision_id = ${revisionId}, revision_number = revision_number + 1, updated_at = now()
89575
89883
  WHERE org_id = ${principal.orgId} AND slug = ${slug} AND tombstoned_at IS NULL AND revision_id = ${current.revisionId}
89576
89884
  RETURNING *
89577
89885
  `;
@@ -89586,7 +89894,7 @@ class PostgresSkillsStore {
89586
89894
  return null;
89587
89895
  }
89588
89896
  await tx`DELETE FROM skills_tags WHERE org_id = ${principal.orgId} AND slug = ${slug}`;
89589
- for (const tag of next.tags) {
89897
+ for (const tag of next2.tags) {
89590
89898
  if (!tag.trim())
89591
89899
  continue;
89592
89900
  await tx`
@@ -89761,7 +90069,7 @@ function assertDurableTarget(target, config) {
89761
90069
  }
89762
90070
 
89763
90071
  // src/server/worker.ts
89764
- async function runWorkerOnce(store, workerId = `worker_${randomUUID5().slice(0, 8)}`, storage = new ArtifactStorage) {
90072
+ async function runWorkerOnce(store, workerId = `worker_${randomUUID6().slice(0, 8)}`, storage = new ArtifactStorage) {
89765
90073
  const run = await store.claimNextRun({ workerId });
89766
90074
  if (!run)
89767
90075
  return false;
@@ -89799,7 +90107,7 @@ Environment:
89799
90107
  assertDurableStore(store, config);
89800
90108
  const storage = new ArtifactStorage({ bucket: config.artifactBucket, prefix: config.artifactPrefix, runPrefix: config.runArtifactPrefix });
89801
90109
  console.log(`skills worker ${process.env.HASNA_SKILLS_WORKER_ID || ""} storage: ${resolveDatabaseTarget(config.databaseUrl).label}`);
89802
- const workerId = process.env.HASNA_SKILLS_WORKER_ID || `worker_${randomUUID5().slice(0, 8)}`;
90110
+ const workerId = process.env.HASNA_SKILLS_WORKER_ID || `worker_${randomUUID6().slice(0, 8)}`;
89803
90111
  const once = process.argv.includes("--once");
89804
90112
  const idleMs = Number.parseInt(process.env.HASNA_SKILLS_WORKER_IDLE_MS || "1000", 10);
89805
90113
  const pause = Number.isFinite(idleMs) ? idleMs : 1000;