@peers-app/peers-sdk 0.28.6 → 0.28.7

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/README.md CHANGED
@@ -32,7 +32,7 @@ NaCl-based key management, message signing and verification, box encryption, and
32
32
 
33
33
  ### Tools Framework
34
34
 
35
- Define AI-callable tools with `ITool` and `IToolInstance`. Tools are registered at runtime and available to built-in and user-defined AI assistants and workflows.
35
+ Declare AI-callable tools with `definePackage()` and provide their implementations through contract resolvers. Use `IContractToolImplementation` for provider implementations.
36
36
 
37
37
  ### Identity and Groups
38
38
 
@@ -58,7 +58,7 @@ The fastest way to build a Peers package is to start from the template:
58
58
  | `DataQuery` / `DataFilter` | Mongo-style query filters translated to SQL |
59
59
  | `observable()` / `computed()` | Reactive state primitives |
60
60
  | `deviceVar` / `userVar` / `groupVar` | Persistent variables — observables backed by the database |
61
- | `ITool` / `IToolInstance` | AI-callable tool definitions |
61
+ | `ITool` / `IContractToolImplementation` | AI-callable tool definitions |
62
62
  | `IPeersUI` / `IPeersUIRoute` | Screen and route declarations for package UIs |
63
63
  | `newid()` | Generate 25-character time-sortable peer IDs |
64
64
  | `newKeys()` / `hydrateKeys()` | Cryptographic key pair generation and hydration |
@@ -18,7 +18,6 @@ export declare class ContractBuilder {
18
18
  private _events;
19
19
  private _alsoImplements;
20
20
  private _exposes;
21
- private warnedAboutToolInstances;
22
21
  /** Full table definitions for this contract (used by the install flow). */
23
22
  private _tableDefinitions;
24
23
  constructor(contractId: string, version: number, name: string, description?: string | undefined);
@@ -35,11 +34,6 @@ export declare class ContractBuilder {
35
34
  */
36
35
  set events(events: IContractEvent[]);
37
36
  get events(): IContractEvent[];
38
- /**
39
- * Discard executable assignments from bundles compiled against an older SDK.
40
- * @deprecated Implement tools with registerContractResolver instead.
41
- */
42
- set toolInstances(_instances: never);
43
37
  /** Full table definitions for runtime table registration. */
44
38
  set tableDefinitions(defs: ITableDefinition[]);
45
39
  get tableDefinitions(): ITableDefinition[];
@@ -26,7 +26,6 @@ class ContractBuilder {
26
26
  _events = [];
27
27
  _alsoImplements = [];
28
28
  _exposes = [];
29
- warnedAboutToolInstances = false;
30
29
  /** Full table definitions for this contract (used by the install flow). */
31
30
  _tableDefinitions = [];
32
31
  constructor(contractId, version, name, description) {
@@ -64,16 +63,6 @@ class ContractBuilder {
64
63
  get events() {
65
64
  return this._events;
66
65
  }
67
- /**
68
- * Discard executable assignments from bundles compiled against an older SDK.
69
- * @deprecated Implement tools with registerContractResolver instead.
70
- */
71
- set toolInstances(_instances) {
72
- if (this.warnedAboutToolInstances)
73
- return;
74
- this.warnedAboutToolInstances = true;
75
- console.warn(`Contract ${this.name} (${this.contractId}@${this.version}) assigned toolInstances, which no longer run; implement these tools with registerContractResolver (docs/Packages/contracts.md#migrating-from-toolinstances)`);
76
- }
77
66
  /** Full table definitions for runtime table registration. */
78
67
  set tableDefinitions(defs) {
79
68
  this._tableDefinitions = defs;
@@ -1,6 +1,7 @@
1
+ import type { z } from "zod";
1
2
  import type { DataContext } from "../context/data-context";
2
3
  import type { Table } from "../data/orm/table";
3
- import type { ITool, IToolInstance } from "../data/tools";
4
+ import type { ITool } from "../data/tools";
4
5
  import type { Event, ISubscriptionResult } from "../events";
5
6
  import { type Observable } from "../observable";
6
7
  import { type IIsolatedCallPrincipal } from "../package-loader/isolated-runtime";
@@ -359,15 +360,15 @@ export declare function createRemoteContractConsumer(contract: IContractDefiniti
359
360
  dataContextId?: string;
360
361
  }): IRemoteContractConsumer<IContractConsumer>;
361
362
  /**
362
- * Provider-local executable metadata. Existing IToolInstance values remain assignable;
363
- * new implementations need no catalog ID, persisted row, or executable registration.
363
+ * Provider-local executable metadata for contract tools.
364
+ * Implementations need no catalog ID, persisted row, or executable registration.
364
365
  */
365
366
  export interface IContractToolImplementation {
366
367
  /** Provider metadata; the contract member name supplies identity, without a Tools row. */
367
368
  tool: Pick<ITool, "name" | "usageDescription"> & Partial<Pick<ITool, "toolId" | "detailedDescription" | "inputSchema" | "outputSchema" | "accessLevel" | "authorityPropagation" | "remoteAccessLevel">>;
368
- toolFn: IToolInstance["toolFn"];
369
- inputSchema?: IToolInstance["inputSchema"];
370
- outputSchema?: IToolInstance["outputSchema"];
369
+ toolFn: (...args: any[]) => any;
370
+ inputSchema?: z.ZodObject<any>;
371
+ outputSchema?: z.ZodObject<any>;
371
372
  }
372
373
  /** Context-bound live members of a provider; executable ownership stays with the contract. */
373
374
  export interface IContractResolution {
@@ -18,7 +18,7 @@ export interface IExtractableTableDef {
18
18
  }
19
19
  /**
20
20
  * Minimal tool-instance shape accepted by extractToolShape.
21
- * Mirrors the subset of IToolInstance we need.
21
+ * Mirrors the subset of IContractToolImplementation we need.
22
22
  */
23
23
  export interface IExtractableToolInstance {
24
24
  /** Host-only workflow policy carried into the contract definition. */
@@ -18,14 +18,12 @@ exports.hasDeviceFollowOverride = hasDeviceFollowOverride;
18
18
  exports.readStoredPackageLocalPath = readStoredPackageLocalPath;
19
19
  const user_context_singleton_1 = require("../context/user-context-singleton");
20
20
  const keys_1 = require("../keys");
21
- const assistants_1 = require("./assistants");
22
21
  const file_types_1 = require("./files/file.types");
23
22
  const group_member_roles_1 = require("./group-member-roles");
24
23
  const group_permissions_1 = require("./group-permissions");
25
24
  const package_versions_1 = require("./package-versions");
26
25
  const packages_1 = require("./packages");
27
26
  const persistent_vars_1 = require("./persistent-vars");
28
- const workflows_1 = require("./workflows");
29
27
  /**
30
28
  * Reactive, cached observable holding device-local preferences for a single
31
29
  * package. Backed by a `groupDeviceVar` so the UI can subscribe via
@@ -344,9 +342,7 @@ async function resolveDevicePackageVersion(pkg, dataContext, opts) {
344
342
  }
345
343
  return { loaded: false, upgraded: false };
346
344
  }
347
- // Load succeeded - install assistants/workflows, then persist the activation
348
- // either as a group-level active version or as device-local state.
349
- await installPackageContents(dataContext, pkg, instance);
345
+ // Load succeeded; persist activation as a group default or device-local state.
350
346
  // When the device is following group settings (no device-level follow
351
347
  // override) and the upgraded version is non-dev, also advance the group's
352
348
  // activePackageVersionId so other devices stay in sync. Only admin+ users
@@ -436,20 +432,6 @@ async function loadOlderCompatiblePackageVersion(pkg, dataContext, unavailableVe
436
432
  }
437
433
  return undefined;
438
434
  }
439
- /**
440
- * Save a loaded package's assistants and workflows into the data context.
441
- * Contract-installed packages are skipped because their content is already
442
- * managed by the contract loader.
443
- */
444
- async function installPackageContents(dataContext, pkg, instance) {
445
- if (dataContext.packageLoader.isContractInstalled(pkg.packageId))
446
- return;
447
- const saves = [
448
- ...(instance.assistants?.map((a) => (0, assistants_1.Assistants)(dataContext).save(a)) ?? []),
449
- ...(instance.workflows?.map((w) => (0, workflows_1.Workflows)(dataContext).save(w)) ?? []),
450
- ];
451
- await Promise.all(saves);
452
- }
453
435
  /**
454
436
  * Returns true when the device has an explicit follow-policy override
455
437
  * (`followRange`, `followTags`, or `pinned`). Used by the UI to drive the
@@ -1,3 +1,7 @@
1
1
  import type { DataContext } from "../context/data-context";
2
2
  import { type IPackage } from "./packages";
3
+ /**
4
+ * Copy a package and its active version's bundles into another context.
5
+ * Restores an uninstalled package; uninstall keeps version and file rows.
6
+ */
3
7
  export declare function copyPackageToAnotherDataContext(packageId: string, fromDataContext: DataContext, toDataContext: DataContext): Promise<IPackage>;
@@ -15,6 +15,10 @@ async function copyBundleFile(fileId, fromFiles, toFiles, label) {
15
15
  console.warn(`${label} file ${fileId} not found in source context`);
16
16
  }
17
17
  }
18
+ /**
19
+ * Copy a package and its active version's bundles into another context.
20
+ * Restores an uninstalled package; uninstall keeps version and file rows.
21
+ */
18
22
  async function copyPackageToAnotherDataContext(packageId, fromDataContext, toDataContext) {
19
23
  const fromPackages = (0, packages_1.Packages)(fromDataContext);
20
24
  const toPackages = (0, packages_1.Packages)(toDataContext);
@@ -35,5 +39,5 @@ async function copyPackageToAnotherDataContext(packageId, fromDataContext, toDat
35
39
  await toPvTable.signAndSave(pv, { saveAsSnapshot: true });
36
40
  }
37
41
  }
38
- return toPackages.signAndSave(pkg);
42
+ return toPackages.signAndSave(pkg, { restoreIfDeleted: true, saveAsSnapshot: true });
39
43
  }
@@ -172,16 +172,6 @@ export declare const toolSchema: z.ZodObject<{
172
172
  }>;
173
173
  /** A tool definition stored in the Tools table. */
174
174
  export type ITool = z.infer<typeof toolSchema>;
175
- /**
176
- * Historical tool instance shape used by older packages.
177
- * @deprecated Use IContractToolImplementation with a contract resolver.
178
- */
179
- export interface IToolInstance {
180
- tool: ITool;
181
- toolFn: (...args: any[]) => any;
182
- inputSchema?: z.ZodObject<any>;
183
- outputSchema?: z.ZodObject<any>;
184
- }
185
175
  /**
186
176
  * Accessor for the Tools table.
187
177
  *
@@ -14,7 +14,7 @@ export interface IPackageProviderOptions {
14
14
  providerPackageId?: string;
15
15
  }
16
16
  /**
17
- * The host functions the Runtime gives one handler call (guest API 2 and 3).
17
+ * The host functions the Runtime gives one guest API 3 handler call.
18
18
  *
19
19
  * Every tool handler gets its own `ctx`, and host calls made through it act for that call's
20
20
  * caller, even while other calls run. The table and contract handles in this module take a
@@ -75,15 +75,13 @@ export interface IPackageContext {
75
75
  watchOwnedTable(tableName: string, handler: (event: IDataChangedEvent<unknown>) => unknown): Promise<PackageUnsubscribe>;
76
76
  /**
77
77
  * Set the value of an in-memory observable this package provides (one declared without a
78
- * `persistentVar`). The host checks it against the observable's `valueType`, keeps it for
79
- * readers, and pushes it to subscribers. Allowed at any time while the worker runs.
78
+ * `persistentVar`). The host checks it against the observable's `valueType` and pushes it
79
+ * to subscribers. Allowed at any time while the worker runs.
80
80
  */
81
81
  publishOwnedObservable(contractId: string, version: number, observableName: string, value: unknown): Promise<unknown>;
82
82
  /**
83
- * Take guest API 2 consumers' writes to a writable in-memory observable this package
84
- * provides. `handler` gets the value with no caller authority, stores and publishes it, and
85
- * returns what it stored. A later call replaces the handler. Guest API 3 instead routes
86
- * writes through the contract member handler under the caller's authority.
83
+ * Compatibility no-op retained for built guest API 3 bundles. Consumer writes are
84
+ * served through the contract member handler under the caller's authority.
87
85
  */
88
86
  onOwnedObservableSet(contractId: string, version: number, observableName: string, handler: (value: unknown) => unknown): void;
89
87
  }
@@ -162,8 +162,7 @@ async function serveObservable(observable, targets, ctx) {
162
162
  });
163
163
  serving.set(observable, () => subscription.dispose());
164
164
  if (definition.writable) {
165
- // Guest API 3 calls this from its member handler under the caller's authority; guest
166
- // API 2 uses onOwnedObservableSet without a caller frame. Both parse before writing.
165
+ // The member handler runs this under the caller's authority and parses before writing.
167
166
  const write = async (value) => {
168
167
  const next = schema ? schema.parse((0, empty_fields_1.withoutEmptySchemaFields)(schema, value)) : value;
169
168
  applying = true;
@@ -177,9 +176,6 @@ async function serveObservable(observable, targets, ctx) {
177
176
  return publish(source());
178
177
  };
179
178
  writers.set(observable, write);
180
- for (const target of targets) {
181
- ctx.onOwnedObservableSet(target.contractId, target.version, name, write);
182
- }
183
179
  }
184
180
  // A successful start guarantees readers have a published value. Later background failures
185
181
  // are logged, but a failed initial publish must fail startup rather than silently serve null.
@@ -48,34 +48,17 @@ export interface IIsolatedContractTool extends Omit<IContractTool, "inputFields"
48
48
  * {@link ISOLATED_TOOL_TIMEOUT_MAX_MS}. Host calls made during the invoke,
49
49
  * including System HTTP, count against this deadline, as does time a handler spends
50
50
  * waiting on the package's own locks. A call that times out is rejected; its handler keeps
51
- * running unless the worker is stuck, in either guest API.
51
+ * running unless the worker is stuck.
52
52
  */
53
53
  timeoutMs?: number;
54
54
  }
55
55
  /**
56
- * The latest guest API version an isolated manifest targets.
57
- *
58
- * - `3`: as `2`, and the package serves its own contract members. The host sends consumers'
59
- * table calls, observable reads and writes, and subscriptions to the provider's worker
60
- * (`contractMemberHandlerName`), and passes every table change, event, and observable
61
- * value bound for a subscriber through it first (`contractDeliveryHandlerName`).
62
- * - `2`: handlers receive `(args, ctx)` and calls run concurrently; the package orders any
63
- * state it shares between calls itself. The host serves contract tables and stored
64
- * observables from storage, without the worker.
65
- * - Omitted: the legacy global helpers, with one call at a time.
66
- *
67
- * Hosts that predate a version reject it.
56
+ * The guest API version built and run by Peers. Consumers' contract member calls and
57
+ * notifications pass through the provider's worker under the caller's authority.
68
58
  */
69
59
  export declare const ISOLATED_GUEST_API_VERSION = 3;
70
- /** Guest API versions a manifest may declare. */
71
- export type IsolatedGuestApi = 2 | 3;
72
- /**
73
- * True when the package serves its contract members itself (guest API 3): every consumer call
74
- * and outgoing message goes through its worker.
75
- *
76
- * @param manifest - The package's manifest.
77
- */
78
- export declare function servesContractMembers(manifest: Pick<IIsolatedPackageManifest, "guestApi">): boolean;
60
+ /** Guest API version for packages built with peers-package. */
61
+ export type IsolatedGuestApi = 3;
79
62
  /** Longest per-tool invoke deadline an isolated manifest may declare. */
80
63
  export declare const ISOLATED_TOOL_TIMEOUT_MAX_MS = 120000;
81
64
  /**
@@ -152,9 +135,9 @@ export interface IIsolatedContractPersistentVarBinding {
152
135
  }
153
136
  /**
154
137
  * Observable contract member. With `persistentVar`, a package-owned persistent variable holds
155
- * the value. Without it (guest API 2 or later), the worker holds the value in memory and
156
- * publishes it with `publishOwnedObservable`. Guest API 3 serves all consumer calls and
157
- * deliveries through the worker; older packages serve stored values directly from the host.
138
+ * the value. Without it, the worker holds the value in memory and
139
+ * publishes it with `publishOwnedObservable`. All consumer calls and deliveries pass
140
+ * through the worker.
158
141
  */
159
142
  export interface IIsolatedContractObservable extends IContractObservable {
160
143
  persistentVar?: IIsolatedContractPersistentVarBinding;
@@ -224,9 +207,8 @@ export interface IIsolatedPackageManifest {
224
207
  network?: IIsolatedPackageNetwork;
225
208
  appNavs?: IAppNav[];
226
209
  /**
227
- * Guest API version ({@link ISOLATED_GUEST_API_VERSION}). `2` for handlers that take
228
- * `(args, ctx)`; `3` when the package also serves its own contract members; omit for the
229
- * legacy global helpers, which run one call at a time.
210
+ * Guest API version ({@link ISOLATED_GUEST_API_VERSION}). Required by the runtime;
211
+ * optional here so build tools can inspect historical bundles without running them.
230
212
  */
231
213
  guestApi?: IsolatedGuestApi;
232
214
  }
@@ -311,15 +293,20 @@ export declare function looksLikeIsolatedPackageArtifact(bundle: string): boolea
311
293
  */
312
294
  export declare function validateIsolatedPackageArtifact(artifact: IIsolatedPackageArtifact, options?: {
313
295
  strict?: boolean;
296
+ /** Refuse unsupported guest APIs before validating their contents for execution. */
297
+ requireCurrentGuestApi?: boolean;
298
+ /** Display name to include alongside the package ID in a runtime refusal. */
299
+ packageName?: string;
314
300
  }): IIsolatedPackageManifest;
315
301
  /**
316
302
  * Parse a magic-header + JSON isolated package bundle.
317
303
  *
318
304
  * @param bundle - Full package-bundle text.
305
+ * @param options - Runtime loaders require the current guest API; build tools may inspect older bundles.
319
306
  * @returns The validated artifact.
320
307
  * @throws If the magic header is present but the envelope is malformed.
321
308
  */
322
- export declare function parseIsolatedPackageArtifact(bundle: string): IIsolatedPackageArtifact;
309
+ export declare function parseIsolatedPackageArtifact(bundle: string, options?: Parameters<typeof validateIsolatedPackageArtifact>[1]): IIsolatedPackageArtifact;
323
310
  /**
324
311
  * Serialize an isolated artifact into the package-bundle slot format.
325
312
  *
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ISOLATED_MAX_FIELD_DEPTH = exports.ISOLATED_OBSERVABLE_HOST_MEMBER = exports.ISOLATED_EVENT_HOST_MEMBER = exports.ISOLATED_PERSISTENT_VAR_HOST_MEMBER = exports.ISOLATED_TABLE_HOST_MEMBER = exports.ISOLATED_CONSUME_HOST_MEMBER = exports.ISOLATED_TOOL_TIMEOUT_MAX_MS = exports.ISOLATED_GUEST_API_VERSION = exports.ISOLATED_PACKAGE_MAGIC = void 0;
4
- exports.servesContractMembers = servesContractMembers;
5
4
  exports.enumValueFits = enumValueFits;
6
5
  exports.looksLikeIsolatedPackageArtifact = looksLikeIsolatedPackageArtifact;
7
6
  exports.validateIsolatedPackageArtifact = validateIsolatedPackageArtifact;
@@ -30,29 +29,10 @@ const utils_1 = require("../utils");
30
29
  */
31
30
  exports.ISOLATED_PACKAGE_MAGIC = "PEERS_ISOLATED_PACKAGE_V1";
32
31
  /**
33
- * The latest guest API version an isolated manifest targets.
34
- *
35
- * - `3`: as `2`, and the package serves its own contract members. The host sends consumers'
36
- * table calls, observable reads and writes, and subscriptions to the provider's worker
37
- * (`contractMemberHandlerName`), and passes every table change, event, and observable
38
- * value bound for a subscriber through it first (`contractDeliveryHandlerName`).
39
- * - `2`: handlers receive `(args, ctx)` and calls run concurrently; the package orders any
40
- * state it shares between calls itself. The host serves contract tables and stored
41
- * observables from storage, without the worker.
42
- * - Omitted: the legacy global helpers, with one call at a time.
43
- *
44
- * Hosts that predate a version reject it.
32
+ * The guest API version built and run by Peers. Consumers' contract member calls and
33
+ * notifications pass through the provider's worker under the caller's authority.
45
34
  */
46
35
  exports.ISOLATED_GUEST_API_VERSION = 3;
47
- /**
48
- * True when the package serves its contract members itself (guest API 3): every consumer call
49
- * and outgoing message goes through its worker.
50
- *
51
- * @param manifest - The package's manifest.
52
- */
53
- function servesContractMembers(manifest) {
54
- return manifest.guestApi === 3;
55
- }
56
36
  /** Longest per-tool invoke deadline an isolated manifest may declare. */
57
37
  exports.ISOLATED_TOOL_TIMEOUT_MAX_MS = 120_000;
58
38
  /** Host-contract member guests use to request a consumed-contract tool call. */
@@ -340,7 +320,7 @@ function validatePackageTables(manifest, strict) {
340
320
  (0, package_table_identity_1.validateTableIndexes)(table.indexes, new Set(table.fields.map((field) => field.name)), `${tableLabel}.indexes`, table.localOnly);
341
321
  }
342
322
  }
343
- function validateProvidedContract(contract, index, packageTables, guestApi, strict) {
323
+ function validateProvidedContract(contract, index, packageTables, strict) {
344
324
  const label = `provides[${index}]`;
345
325
  if (!contract || typeof contract !== "object" || Array.isArray(contract)) {
346
326
  throw new Error(`${label} must be an object`);
@@ -488,13 +468,8 @@ function validateProvidedContract(contract, index, packageTables, guestApi, stri
488
468
  throw new Error(`${observableLabel}.writable must be a boolean`);
489
469
  }
490
470
  const binding = observable.persistentVar;
491
- if (binding === undefined) {
492
- // Held by the worker, which publishes it: that needs guest API 2 or later.
493
- if (guestApi === undefined) {
494
- throw new Error(`${observableLabel}.persistentVar is required unless the package uses guestApi 2 or later`);
495
- }
471
+ if (binding === undefined)
496
472
  continue;
497
- }
498
473
  if (!binding || typeof binding !== "object" || Array.isArray(binding)) {
499
474
  throw new Error(`${observableLabel}.persistentVar must be an object`);
500
475
  }
@@ -777,6 +752,15 @@ function validateIsolatedPackageArtifact(artifact, options = {}) {
777
752
  if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
778
753
  throw new Error("Package artifact.manifest is required");
779
754
  }
755
+ if (options.requireCurrentGuestApi && manifest.guestApi !== exports.ISOLATED_GUEST_API_VERSION) {
756
+ const identity = options.packageName
757
+ ? `'${options.packageName}' (${manifest.packageId})`
758
+ : manifest.packageId;
759
+ if (typeof manifest.guestApi === "number" && manifest.guestApi > exports.ISOLATED_GUEST_API_VERSION) {
760
+ throw new Error(`Package ${identity} needs a newer version of Peers; update Peers to use it.`);
761
+ }
762
+ throw new Error(`Package ${identity} has an unsupported guest API. Rebuild it with peers-package; guest API 2 and packages without guestApi are no longer supported.`);
763
+ }
780
764
  assertOnlyKeys(manifest, [
781
765
  "packageId",
782
766
  "version",
@@ -802,7 +786,9 @@ function validateIsolatedPackageArtifact(artifact, options = {}) {
802
786
  manifest.executionMode !== "privileged") {
803
787
  throw new Error('manifest.executionMode must be "sandbox" or "privileged"');
804
788
  }
805
- if (manifest.guestApi !== undefined && manifest.guestApi !== 2 && manifest.guestApi !== 3) {
789
+ const guestApi = manifest.guestApi;
790
+ // Contract import/freezing tools still inspect historical bundles without executing them.
791
+ if (guestApi !== undefined && guestApi !== 2 && guestApi !== 3) {
806
792
  throw new Error("manifest.guestApi must be 2 or 3 when set");
807
793
  }
808
794
  assertPeerId(manifest.packageId, "packageId");
@@ -820,7 +806,7 @@ function validateIsolatedPackageArtifact(artifact, options = {}) {
820
806
  const packageTables = manifest.tables ?? [];
821
807
  const providedKeys = new Set();
822
808
  for (const [index, contract] of manifest.provides.entries()) {
823
- validateProvidedContract(contract, index, packageTables, manifest.guestApi, strict);
809
+ validateProvidedContract(contract, index, packageTables, strict);
824
810
  const key = `${contract.contractId}@${contract.version}`;
825
811
  if (providedKeys.has(key)) {
826
812
  throw new Error(`Duplicate provided contract ${key}`);
@@ -857,10 +843,11 @@ function validateIsolatedPackageArtifact(artifact, options = {}) {
857
843
  * Parse a magic-header + JSON isolated package bundle.
858
844
  *
859
845
  * @param bundle - Full package-bundle text.
846
+ * @param options - Runtime loaders require the current guest API; build tools may inspect older bundles.
860
847
  * @returns The validated artifact.
861
848
  * @throws If the magic header is present but the envelope is malformed.
862
849
  */
863
- function parseIsolatedPackageArtifact(bundle) {
850
+ function parseIsolatedPackageArtifact(bundle, options = {}) {
864
851
  if (!looksLikeIsolatedPackageArtifact(bundle)) {
865
852
  throw new Error("Bundle is not an isolated package artifact");
866
853
  }
@@ -878,7 +865,7 @@ function parseIsolatedPackageArtifact(bundle) {
878
865
  throw new Error(`Isolated package artifact JSON is invalid: ${err instanceof Error ? err.message : String(err)}`);
879
866
  }
880
867
  const artifact = parsed;
881
- validateIsolatedPackageArtifact(artifact);
868
+ validateIsolatedPackageArtifact(artifact, options);
882
869
  return artifact;
883
870
  }
884
871
  /**
@@ -30,7 +30,7 @@ export interface IPackageContractEndpointOptions {
30
30
  * Loads package versions for one {@link DataContext}.
31
31
  *
32
32
  * Isolated artifacts (magic-header JSON) are installed without host
33
- * evaluation. Legacy `package.bundle.js` files still evaluate in-process.
33
+ * evaluation. Host-backed `definePackage()` bundles evaluate in-process.
34
34
  */
35
35
  export declare class PackageLoader {
36
36
  readonly dataContext: DataContext;
@@ -75,7 +75,7 @@ export declare class PackageLoader {
75
75
  get catalogReady(): Promise<void>;
76
76
  /** Optional isolated-provider supervisor created from the host factory. */
77
77
  readonly isolatedRuntime?: IIsolatedPackageRuntime;
78
- /** Optional host `require` used when reading local bundles or evaluating legacy packages. */
78
+ /** Optional host `require` used when reading local bundles or evaluating host-backed packages. */
79
79
  require: (<T>(module: string) => T) | undefined;
80
80
  /**
81
81
  * @param dataContext - Owning personal or group data context. Its
@@ -116,7 +116,7 @@ export declare class PackageLoader {
116
116
  */
117
117
  private recordLoadFailure;
118
118
  private dispatchToolFromProvider;
119
- /** Project a legacy caller's declared tool reach and authority for advisory catalog listings. */
119
+ /** Project a host caller's declared tool reach and authority for advisory catalog listings. */
120
120
  hostToolPrincipal(principal: IIsolatedCallPrincipal, packageId: string, target: {
121
121
  contractId: string;
122
122
  version: number;
@@ -146,7 +146,7 @@ export declare class PackageLoader {
146
146
  resolveContractEndpoint(request: IContractProviderEndpointRequest, opts?: IPackageContractEndpointOptions): Promise<IContractProviderEndpoint>;
147
147
  private guardResolutionTools;
148
148
  /**
149
- * Bind a legacy package's declared consume to its selected provider.
149
+ * Bind a host-backed package's declared consume to its selected provider.
150
150
  *
151
151
  * The returned proxy can target either a resolver-backed or isolated provider.
152
152
  * Its provider choice is captured from {@link ContractRegistry.selectProvider}
@@ -158,7 +158,7 @@ export declare class PackageLoader {
158
158
  */
159
159
  resolveConsumedContract(dependency: IConsumedContract, callerPackageId: string): Promise<IContractConsumer | undefined>;
160
160
  /**
161
- * Bind a legacy package's `remote()` declaration to a per-device consumer factory.
161
+ * Bind a host-backed package's `remote()` declaration to a per-device consumer factory.
162
162
  *
163
163
  * No local provider is selected: every device consumer stamps `targetDeviceId` and
164
164
  * this loader's own router forwards the call through
@@ -46,7 +46,7 @@ function isolatedContractLifecycle(packageVersion) {
46
46
  * Loads package versions for one {@link DataContext}.
47
47
  *
48
48
  * Isolated artifacts (magic-header JSON) are installed without host
49
- * evaluation. Legacy `package.bundle.js` files still evaluate in-process.
49
+ * evaluation. Host-backed `definePackage()` bundles evaluate in-process.
50
50
  */
51
51
  class PackageLoader {
52
52
  dataContext;
@@ -93,7 +93,7 @@ class PackageLoader {
93
93
  }
94
94
  /** Optional isolated-provider supervisor created from the host factory. */
95
95
  isolatedRuntime;
96
- /** Optional host `require` used when reading local bundles or evaluating legacy packages. */
96
+ /** Optional host `require` used when reading local bundles or evaluating host-backed packages. */
97
97
  require = undefined;
98
98
  /**
99
99
  * @param dataContext - Owning personal or group data context. Its
@@ -264,7 +264,7 @@ class PackageLoader {
264
264
  endpoint.dispose();
265
265
  }
266
266
  }
267
- /** Project a legacy caller's declared tool reach and authority for advisory catalog listings. */
267
+ /** Project a host caller's declared tool reach and authority for advisory catalog listings. */
268
268
  hostToolPrincipal(principal, packageId, target) {
269
269
  if (!principal.userId || this.isolatedRuntime?.isInstalled(packageId))
270
270
  return undefined;
@@ -473,7 +473,7 @@ class PackageLoader {
473
473
  };
474
474
  }
475
475
  /**
476
- * Bind a legacy package's declared consume to its selected provider.
476
+ * Bind a host-backed package's declared consume to its selected provider.
477
477
  *
478
478
  * The returned proxy can target either a resolver-backed or isolated provider.
479
479
  * Its provider choice is captured from {@link ContractRegistry.selectProvider}
@@ -514,7 +514,7 @@ class PackageLoader {
514
514
  return consumer;
515
515
  }
516
516
  /**
517
- * Bind a legacy package's `remote()` declaration to a per-device consumer factory.
517
+ * Bind a host-backed package's `remote()` declaration to a per-device consumer factory.
518
518
  *
519
519
  * No local provider is selected: every device consumer stamps `targetDeviceId` and
520
520
  * this loader's own router forwards the call through
@@ -910,7 +910,10 @@ class PackageLoader {
910
910
  assertCurrent();
911
911
  if ((0, isolated_artifact_1.looksLikeIsolatedPackageArtifact)(bundleCode)) {
912
912
  try {
913
- const artifact = (0, isolated_artifact_1.parseIsolatedPackageArtifact)(bundleCode);
913
+ const artifact = (0, isolated_artifact_1.parseIsolatedPackageArtifact)(bundleCode, {
914
+ requireCurrentGuestApi: true,
915
+ packageName: pkg.name,
916
+ });
914
917
  return await this._installIsolated(pkg, artifact, selectedPackageVersion, usingLocalBundle, force, assertCurrent);
915
918
  }
916
919
  catch (err) {
@@ -920,7 +923,7 @@ class PackageLoader {
920
923
  await this.catalogReady;
921
924
  assertCurrent();
922
925
  const replacingIsolated = this.isolatedRuntime?.isInstalled(pkg.packageId) ?? false;
923
- const instance = await this._evaluateBundle(pkg, bundleCode, selectedPackageVersion, force, assertCurrent);
926
+ const instance = await this._evaluateBundle(pkg, bundleCode, selectedPackageVersion, assertCurrent);
924
927
  if (!instance) {
925
928
  return undefined;
926
929
  }
@@ -995,7 +998,7 @@ class PackageLoader {
995
998
  });
996
999
  return instance;
997
1000
  }
998
- async _evaluateBundle(pkg, bundleCode, selectedPackageVersion, force, assertCurrent = this.guardLoad(pkg.packageId)) {
1001
+ async _evaluateBundle(pkg, bundleCode, selectedPackageVersion, assertCurrent = this.guardLoad(pkg.packageId)) {
999
1002
  // Node.js built-in modules that do not exist in React Native (and would cause
1000
1003
  // a fatal native error if passed through to RN's require). We block them here
1001
1004
  // and throw a descriptive JS Error instead, which the surrounding try/catch
@@ -1063,19 +1066,16 @@ class PackageLoader {
1063
1066
  const bundleFunction = new Function("module", "exports", "require", bundleCode);
1064
1067
  const activateResolvers = (0, contract_resolvers_1.stagePackageContractResolvers)(this.dataContext, pkg.packageId, () => bundleFunction(module, moduleExports, customRequire));
1065
1068
  const bundleExports = module.exports;
1066
- // Check for contract-based package definition (new path)
1069
+ // Host bundles must export a definePackage() definition.
1067
1070
  const packageDefinition = (0, contract_package_loader_1.extractPackageDefinition)(bundleExports);
1068
1071
  if (packageDefinition) {
1069
1072
  if (packageDefinition.packageId !== pkg.packageId) {
1070
1073
  console.warn(`[PackageLoader] packageId mismatch: definition has "${packageDefinition.packageId}" but DB record has "${pkg.packageId}"`);
1071
1074
  }
1072
- // Build a proper IPeersPackage from the contract definition so callers
1073
- // (e.g. package-installer) can read appNavs, assistants, etc.
1075
+ // Expose loaded identity and navigation to callers such as package-installer.
1074
1076
  const contractPackageInstance = {
1075
1077
  packageId: packageDefinition.packageId,
1076
1078
  appNavs: packageDefinition.appNavs,
1077
- assistants: packageDefinition.assistants,
1078
- tableDefinitions: packageDefinition.tableDefinitions,
1079
1079
  };
1080
1080
  await (0, contract_package_loader_1.installContractPackage)(this.dataContext, packageDefinition, {
1081
1081
  registry: this.contractRegistry,
@@ -1100,36 +1100,7 @@ class PackageLoader {
1100
1100
  assertCurrent();
1101
1101
  return contractPackageInstance;
1102
1102
  }
1103
- // Legacy path: extract IPeersPackage for backward compat
1104
- const packageInstance = bundleExports?.exports || bundleExports?.package || bundleExports?.default || bundleExports;
1105
- if (packageInstance &&
1106
- typeof packageInstance === "object" &&
1107
- typeof packageInstance.packageId === "string" &&
1108
- packageInstance.packageId.length > 0) {
1109
- const tools = packageInstance.toolInstances;
1110
- if (Array.isArray(tools) && tools.length)
1111
- throw new Error(`Package '${pkg.name}' (${pkg.packageId}) ships legacy executable tools, which this runtime no longer runs; upgrade it to definePackage() contract tools (docs/Packages/contracts.md#migrating-from-toolinstances)`);
1112
- await this.toolCatalog.publish(pkg.packageId, [], async () => assertCurrent(), () => {
1113
- assertCurrent();
1114
- // Final catalog writes must succeed before changing active host tables.
1115
- if (packageInstance.tableDefinitions?.length) {
1116
- this.dataContext.tableContainer.registerTableDefinitions(packageInstance.tableDefinitions, { overwrite: true });
1117
- }
1118
- activateResolvers();
1119
- this.retireHostEndpoints(pkg.packageId);
1120
- if (force && this.contractInstalledPackages.has(pkg.packageId)) {
1121
- this.contractRegistry.unregister(pkg.packageId);
1122
- this.contractInstalledPackages.delete(pkg.packageId);
1123
- }
1124
- this.packageInstances[pkg.packageId] = packageInstance;
1125
- this.recordRunning(pkg.packageId, {
1126
- ...runningVersionFields(selectedPackageVersion),
1127
- kind: "legacy",
1128
- });
1129
- });
1130
- return packageInstance;
1131
- }
1132
- throw new Error("Package bundle exports nothing usable");
1103
+ throw new Error(`Package '${pkg.name}' (${pkg.packageId}) has an unsupported bundle format. Rebuild it with definePackage() and peers-package.`);
1133
1104
  }
1134
1105
  catch (err) {
1135
1106
  this.recordLoadFailure(pkg.packageId, selectedPackageVersion, err, assertCurrent);
@@ -24,9 +24,8 @@ export type PackageRuntimeStatus = "running" | "failed" | "unavailable" | "unsup
24
24
  *
25
25
  * - `isolated` — an isolated artifact served by the host's isolated runtime.
26
26
  * - `contract` — an in-process `definePackage()` bundle.
27
- * - `legacy` — an in-process legacy bundle without a package definition.
28
27
  */
29
- export type PackageRuntimeKind = "isolated" | "contract" | "legacy";
28
+ export type PackageRuntimeKind = "isolated" | "contract";
30
29
  /** A contract a running package provides. */
31
30
  export interface IPackageRuntimeProvidedContract {
32
31
  /** Provided contract identity. */
@@ -1,16 +1,10 @@
1
- import type { IAssistant } from "../data";
2
- import type { ITableDefinition } from "../data/orm/table-definitions.type";
3
1
  import type { IPeersUI } from "../peers-ui/peers-ui";
4
2
  import type { IPeersUIRoute } from "../peers-ui/peers-ui.types";
5
3
  import type { IAppNav } from "./app-nav";
6
- import type { IWorkflow } from "./workflow";
7
- /** Host package tables, assistants and UI; tools are provided by contracts. */
4
+ /** Identity and navigation of a loaded package; content is installed from its definition. */
8
5
  export interface IPeersPackage {
9
6
  packageId: string;
10
- assistants?: IAssistant[];
11
- workflows?: IWorkflow[];
12
7
  appNavs?: IAppNav[];
13
- tableDefinitions?: ITableDefinition[];
14
8
  }
15
9
  /** Routes exported by a host package. */
16
10
  export interface IPeersPackageRoutes {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peers-app/peers-sdk",
3
- "version": "0.28.6",
3
+ "version": "0.28.7",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/peers-app/peers-sdk.git"