@llblab/pi-actors 0.48.0 → 0.48.1

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/CHANGELOG.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  > Each release keeps at most 8 outcome records of at most 512 characters.
4
4
 
5
+ ## 0.48.1: Persistent Skill Composition
6
+
7
+ - `Persistent Skill Composition`: Made `register_tool from=<skill>/<recipe>` use Pi's authoritative active-session Skill snapshot across every admitted Skill location and activate synchronous or asynchronous tools from the resolved effective contract. Compact user Recipes retain logical delegation without copied contracts, absolute helper paths, symlinks, or ambient runtime re-resolution.
8
+
5
9
  ## 0.48.0: Host-Coordinated Swarms
6
10
 
7
11
  - `Coordinator And Swarm Methodology`: Defined gatewayless host coordination with companion transports as presence only; the coordinator accepts declarative outcomes, creates explicit Runs, stays available, and owns integration/final validation. Reasoning is role-allocated: bounded authors default off, independent reviewers/integrators use medium, and the coordinator selects evidence-worthy fanout. Swarm retains overhead admission, disjoint ownership, isolation, mutation freeze, and event/timer observation.
@@ -127,6 +127,9 @@ export function createActorExtensionRuntime(pi) {
127
127
  Pi.registerToolDefinitions(pi, Tools.createCoreActorToolDefinitions({
128
128
  configPath: Paths.EXTENSION_RUNTIME_PATHS.configPath,
129
129
  getActiveTools: () => pi.getActiveTools(),
130
+ getRecipeResolutionContext: () => activeRunContext
131
+ ? getRecipeResolutionContext(activeRunContext)
132
+ : undefined,
130
133
  getRuntimeTool: (name) => Tools.resolveActiveRuntimeTool(name, runtime.getTools(), (activeName) => actorToolDefinitions.get(activeName)),
131
134
  getRuntimeToolStatus: runtime.getToolStatus,
132
135
  handleRuntimeControl: automaticReview.handleControl,
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import * as CommandTemplates from "./command-templates.ts";
7
7
  import * as Config from "./config.ts";
8
+ import * as RecipesContext from "./recipes-context.ts";
8
9
  export interface RegisterToolInput {
9
10
  name?: string;
10
11
  description?: string;
@@ -55,6 +56,7 @@ export interface RegisterToolRuntimeDeps<TContext> {
55
56
  recipeRoot?: string;
56
57
  getToolNameBlocker: (name: string) => string | undefined;
57
58
  getTools: () => Map<string, Config.RegisteredTool>;
59
+ getRecipeResolutionContext?: () => RecipesContext.RecipeResolutionContext | undefined;
58
60
  getActiveTools: () => string[];
59
61
  notify: (ctx: TContext, message: string, type: "info" | "warning" | "error") => void;
60
62
  registerRuntimeTool: (cfg: Config.RegisteredTool) => RuntimeActivation | void;
@@ -233,9 +233,10 @@ function getInputTemplate(value) {
233
233
  throw new Error(ExecutionOutput.formatToolText("Tool template must be a string, object, or sequence."));
234
234
  }
235
235
  function getRegistrationResolutionContext(ctx, deps) {
236
- if (ctx &&
237
- typeof ctx === "object" &&
238
- "recipeResolutionContext" in ctx) {
236
+ const runtimeContext = deps.getRecipeResolutionContext?.();
237
+ if (runtimeContext)
238
+ return runtimeContext;
239
+ if (ctx && typeof ctx === "object" && "recipeResolutionContext" in ctx) {
239
240
  const resolutionContext = ctx.recipeResolutionContext;
240
241
  if (resolutionContext)
241
242
  return resolutionContext;
@@ -415,15 +416,18 @@ function executeRegisterToolUnlocked(params, ctx, deps) {
415
416
  let persisted = false;
416
417
  let activation;
417
418
  let cfg;
419
+ let transactionStage = "persist";
418
420
  try {
419
421
  persistToolRecipe(deps, name, authoredRecipe);
420
422
  persisted = true;
423
+ transactionStage = "persisted_admission";
421
424
  const admitted = RecipesDiscovery.admitUserRecipe(recipePath, resolutionContext);
422
425
  if (!admitted.validated || !admitted.tool) {
423
426
  throw new Error(`Persisted tool recipe admission failed: ${admitted.diagnostics.join("; ")}`);
424
427
  }
425
428
  cfg = admitted.tool;
426
429
  tools.set(name, cfg);
430
+ transactionStage = "runtime_activation";
427
431
  activation = deps.registerRuntimeTool(cfg) ?? undefined;
428
432
  if (activation &&
429
433
  (!activation.host_registered ||
@@ -447,7 +451,7 @@ function executeRegisterToolUnlocked(params, ctx, deps) {
447
451
  tools.delete(name);
448
452
  }
449
453
  deps.setActiveTools(activeBefore);
450
- throw new Error(ExecutionOutput.formatToolText(`Tool registration transaction failed: ${error instanceof Error ? error.message : String(error)}`));
454
+ throw new Error(ExecutionOutput.formatToolText(`Tool registration transaction failed at ${transactionStage} (${resolutionContext.generation}; active Skills: ${activeSkillSummary(resolutionContext)}): ${error instanceof Error ? error.message : String(error)}`));
451
455
  }
452
456
  deps.notify(ctx, `Tool activated: ${name}`, "info");
453
457
  const templateWarnings = CommandTemplates.getCommandTemplateWarnings(typeof cfg.recipe?.template === "object" && !Array.isArray(cfg.recipe.template)
@@ -93,8 +93,9 @@ export function createRuntimeToolDefinition(cfg, exec) {
93
93
  const paramSchema = {};
94
94
  const required = [];
95
95
  const isRecipe = RecipesReferences.isRecipeTool(cfg.template, cfg.recipe);
96
- const isAsyncRecipe = cfg.recipe?.async === true ||
97
- RecipesReferences.isAsyncRecipeReference(cfg.template);
96
+ const isAsyncRecipe = cfg.recipe
97
+ ? cfg.recipe.async === true
98
+ : RecipesReferences.isAsyncRecipeReference(cfg.template);
98
99
  const recipeTemplate = cfg.recipe?.template ?? RecipesReferences.getRecipeTemplate(cfg.template);
99
100
  const requiredTemplate = recipeTemplate ?? cfg.template;
100
101
  const requiredTemplateConfig = typeof requiredTemplate === "object" && !Array.isArray(requiredTemplate)
@@ -112,7 +113,7 @@ export function createRuntimeToolDefinition(cfg, exec) {
112
113
  const requiredArgs = isRecipe && cfg.storedArgs !== undefined
113
114
  ? new Set(cfg.args.filter((arg) => !Object.hasOwn(cfg.defaults, arg) &&
114
115
  !Object.hasOwn(recipeInlineDefaults, arg)))
115
- : RecipesReferences.isRecipeReference(cfg.template) && !recipeTemplate
116
+ : !cfg.recipe && RecipesReferences.isRecipeReference(cfg.template) && !recipeTemplate
116
117
  ? new Set(cfg.args.filter((arg) => !Object.hasOwn(cfg.defaults, arg)))
117
118
  : Schema.getRequiredToolArgNames(requiredTemplateConfig);
118
119
  for (const arg of cfg.args) {
@@ -14,6 +14,7 @@ export interface ActorToolDefinition {
14
14
  export interface CoreActorToolDefinitionDeps<TContext extends RuntimeToolContext> {
15
15
  configPath: string;
16
16
  getActiveTools: () => string[];
17
+ getRecipeResolutionContext: () => import("./recipes-context.ts").RecipeResolutionContext | undefined;
17
18
  getRuntimeTool: (name: string) => unknown;
18
19
  getRuntimeToolStatus: (name: string) => Record<string, unknown> | undefined;
19
20
  handleRuntimeControl?: (action: string, input: unknown) => Record<string, unknown>;
package/dist/lib/tools.js CHANGED
@@ -30,6 +30,7 @@ export function createCoreActorToolDefinitions(deps) {
30
30
  getActiveTools: deps.getActiveTools,
31
31
  getToolNameBlocker: deps.registryRuntime.getToolNameBlocker,
32
32
  getTools: deps.registryRuntime.getTools,
33
+ getRecipeResolutionContext: deps.getRecipeResolutionContext,
33
34
  notify: deps.registryRuntime.notify,
34
35
  registerRuntimeTool: deps.registryRuntime.registerRuntimeTool,
35
36
  reservedToolNames: RESERVED_TOOL_NAMES,
@@ -52,7 +52,7 @@ direct delegation ≠ named import composition
52
52
  Run Control ≠ actor chat
53
53
  ```
54
54
 
55
- A Skill Recipe is a maintained component addressed by `<skill>/<recipe>`. `spawn` creates a Run from a Recipe. `register_tool` creates or updates a persistent user tool. A tool is callable in the current session only when activation evidence says `callable_now: true`.
55
+ A Skill Recipe is a maintained component addressed by `<skill>/<recipe>`. `spawn` creates a Run from a Recipe. `register_tool from=<skill>/<recipe>` persists compact logical delegation and activates from the resolved effective contract without copying, symlinking, or ambient re-resolution. A tool is callable in the current session only when activation evidence says `callable_now: true`.
56
56
 
57
57
  `actors` owns generic Recipe/tool/Run mechanics. The owning capability Skill owns capability-specific selection and constraints. `swarm` owns multi-actor decomposition and integration methodology.
58
58
 
@@ -28,6 +28,8 @@ register_tool
28
28
 
29
29
  `from` means logical direct delegation. The source remains authoritative for async behavior, caller args and types, source defaults, artifacts, Control, and runtime-owned origins. The persistent user Recipe stores only the compact specialization; do not copy inherited fields.
30
30
 
31
+ Resolution uses Pi's authoritative active-session Skill snapshot across every Skill location Pi admits. Registration resolves and validates the maintained source, persists only its logical `<skill>/<recipe>` reference plus caller specialization, and projects the already-resolved effective contract into the runtime tool. Never replace this composition with a copied Recipe, absolute helper path, symlink, or ambient runtime re-resolution.
32
+
31
33
  Use `description` to narrow agent-facing intent when useful. Every supplied default must name a caller-owned source arg and satisfy its type or enum. Never default runtime-owned origins.
32
34
 
33
35
  ## Prove registration
@@ -73,7 +73,7 @@ Usage and lineage live in locked metadata ledgers rather than authored Recipe fi
73
73
 
74
74
  ## Specializing Existing Recipes
75
75
 
76
- Use `register_tool from=<skill>/<recipe> defaults={...}` for one maintained capability under a persistent name or narrower defaults. The stored user Recipe remains compact direct delegation; it does not copy async, args/types, Control, artifacts, helpers, or runtime-owned `{recipe_dir}`/`{skill_dir}`. Named imports remain for multi-node Recipe composition, not one-source specialization. Skill Recipes remain components and are never exposed merely because their Skill is active. Install only specific capabilities; internal automatic-review Recipes must not become user-callable tools.
76
+ Use `register_tool from=<skill>/<recipe> defaults={...}` for one maintained capability under a persistent name or narrower defaults. Pi supplies one authoritative active-session Skill snapshot across global, project, package, settings, CLI, and extension-contributed locations. Registration resolves the source from that snapshot, stores only the logical direct-delegation reference plus caller specialization, and activates the tool from the already-resolved effective contract without ambient re-resolution. It does not copy async, args/types, Control, artifacts, helpers, or runtime-owned `{recipe_dir}`/`{skill_dir}`; symlinks and absolute helper paths are not substitutes. Named imports remain for multi-node Recipe composition, not one-source specialization. Skill Recipes remain components and are never exposed merely because their Skill is active. Install only specific capabilities; internal automatic-review Recipes must not become user-callable tools.
77
77
 
78
78
  ## Safety
79
79
 
@@ -160,6 +160,10 @@ export function createActorExtensionRuntime(
160
160
  Tools.createCoreActorToolDefinitions<Pi.ExtensionContext>({
161
161
  configPath: Paths.EXTENSION_RUNTIME_PATHS.configPath,
162
162
  getActiveTools: () => pi.getActiveTools(),
163
+ getRecipeResolutionContext: () =>
164
+ activeRunContext
165
+ ? getRecipeResolutionContext(activeRunContext)
166
+ : undefined,
163
167
  getRuntimeTool: (name) =>
164
168
  Tools.resolveActiveRuntimeTool(
165
169
  name,
package/lib/registry.ts CHANGED
@@ -73,6 +73,7 @@ export interface RegisterToolRuntimeDeps<TContext> {
73
73
  recipeRoot?: string;
74
74
  getToolNameBlocker: (name: string) => string | undefined;
75
75
  getTools: () => Map<string, Config.RegisteredTool>;
76
+ getRecipeResolutionContext?: () => RecipesContext.RecipeResolutionContext | undefined;
76
77
  getActiveTools: () => string[];
77
78
  notify: (
78
79
  ctx: TContext,
@@ -422,11 +423,9 @@ function getRegistrationResolutionContext<TContext>(
422
423
  ctx: TContext,
423
424
  deps: RegisterToolRuntimeDeps<TContext>,
424
425
  ): RecipesContext.RecipeResolutionContext {
425
- if (
426
- ctx &&
427
- typeof ctx === "object" &&
428
- "recipeResolutionContext" in ctx
429
- ) {
426
+ const runtimeContext = deps.getRecipeResolutionContext?.();
427
+ if (runtimeContext) return runtimeContext;
428
+ if (ctx && typeof ctx === "object" && "recipeResolutionContext" in ctx) {
430
429
  const resolutionContext = (ctx as {
431
430
  recipeResolutionContext?: RecipesContext.RecipeResolutionContext;
432
431
  }).recipeResolutionContext;
@@ -697,9 +696,11 @@ function executeRegisterToolUnlocked<TContext>(
697
696
  let persisted = false;
698
697
  let activation: RuntimeActivation | undefined;
699
698
  let cfg: Config.RegisteredTool;
699
+ let transactionStage = "persist";
700
700
  try {
701
701
  persistToolRecipe(deps, name, authoredRecipe);
702
702
  persisted = true;
703
+ transactionStage = "persisted_admission";
703
704
  const admitted = RecipesDiscovery.admitUserRecipe(
704
705
  recipePath,
705
706
  resolutionContext,
@@ -711,6 +712,7 @@ function executeRegisterToolUnlocked<TContext>(
711
712
  }
712
713
  cfg = admitted.tool;
713
714
  tools.set(name, cfg);
715
+ transactionStage = "runtime_activation";
714
716
  activation = deps.registerRuntimeTool(cfg) ?? undefined;
715
717
  if (
716
718
  activation &&
@@ -737,7 +739,7 @@ function executeRegisterToolUnlocked<TContext>(
737
739
  deps.setActiveTools(activeBefore);
738
740
  throw new Error(
739
741
  ExecutionOutput.formatToolText(
740
- `Tool registration transaction failed: ${error instanceof Error ? error.message : String(error)}`,
742
+ `Tool registration transaction failed at ${transactionStage} (${resolutionContext.generation}; active Skills: ${activeSkillSummary(resolutionContext)}): ${error instanceof Error ? error.message : String(error)}`,
741
743
  ),
742
744
  );
743
745
  }
@@ -147,9 +147,9 @@ export function createRuntimeToolDefinition(
147
147
  const paramSchema: Record<string, JsonSchema> = {};
148
148
  const required: string[] = [];
149
149
  const isRecipe = RecipesReferences.isRecipeTool(cfg.template, cfg.recipe);
150
- const isAsyncRecipe =
151
- cfg.recipe?.async === true ||
152
- RecipesReferences.isAsyncRecipeReference(cfg.template);
150
+ const isAsyncRecipe = cfg.recipe
151
+ ? cfg.recipe.async === true
152
+ : RecipesReferences.isAsyncRecipeReference(cfg.template);
153
153
  const recipeTemplate =
154
154
  cfg.recipe?.template ?? RecipesReferences.getRecipeTemplate(cfg.template);
155
155
  const requiredTemplate = recipeTemplate ?? cfg.template!;
@@ -177,7 +177,7 @@ export function createRuntimeToolDefinition(
177
177
  !Object.hasOwn(recipeInlineDefaults, arg),
178
178
  ),
179
179
  )
180
- : RecipesReferences.isRecipeReference(cfg.template) && !recipeTemplate
180
+ : !cfg.recipe && RecipesReferences.isRecipeReference(cfg.template) && !recipeTemplate
181
181
  ? new Set(cfg.args.filter((arg) => !Object.hasOwn(cfg.defaults, arg)))
182
182
  : Schema.getRequiredToolArgNames(requiredTemplateConfig);
183
183
  for (const arg of cfg.args) {
package/lib/tools.ts CHANGED
@@ -24,6 +24,7 @@ export interface CoreActorToolDefinitionDeps<
24
24
  > {
25
25
  configPath: string;
26
26
  getActiveTools: () => string[];
27
+ getRecipeResolutionContext: () => import("./recipes-context.ts").RecipeResolutionContext | undefined;
27
28
  getRuntimeTool: (name: string) => unknown;
28
29
  getRuntimeToolStatus: (name: string) => Record<string, unknown> | undefined;
29
30
  handleRuntimeControl?: (
@@ -68,6 +69,7 @@ export function createCoreActorToolDefinitions<
68
69
  getActiveTools: deps.getActiveTools,
69
70
  getToolNameBlocker: deps.registryRuntime.getToolNameBlocker,
70
71
  getTools: deps.registryRuntime.getTools,
72
+ getRecipeResolutionContext: deps.getRecipeResolutionContext,
71
73
  notify: deps.registryRuntime.notify,
72
74
  registerRuntimeTool: deps.registryRuntime.registerRuntimeTool,
73
75
  reservedToolNames: RESERVED_TOOL_NAMES,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-actors",
3
- "version": "0.48.0",
3
+ "version": "0.48.1",
4
4
  "private": false,
5
5
  "description": "Local Actor Kernel for Pi",
6
6
  "keywords": [
@@ -52,7 +52,7 @@ direct delegation ≠ named import composition
52
52
  Run Control ≠ actor chat
53
53
  ```
54
54
 
55
- A Skill Recipe is a maintained component addressed by `<skill>/<recipe>`. `spawn` creates a Run from a Recipe. `register_tool` creates or updates a persistent user tool. A tool is callable in the current session only when activation evidence says `callable_now: true`.
55
+ A Skill Recipe is a maintained component addressed by `<skill>/<recipe>`. `spawn` creates a Run from a Recipe. `register_tool from=<skill>/<recipe>` persists compact logical delegation and activates from the resolved effective contract without copying, symlinking, or ambient re-resolution. A tool is callable in the current session only when activation evidence says `callable_now: true`.
56
56
 
57
57
  `actors` owns generic Recipe/tool/Run mechanics. The owning capability Skill owns capability-specific selection and constraints. `swarm` owns multi-actor decomposition and integration methodology.
58
58
 
@@ -28,6 +28,8 @@ register_tool
28
28
 
29
29
  `from` means logical direct delegation. The source remains authoritative for async behavior, caller args and types, source defaults, artifacts, Control, and runtime-owned origins. The persistent user Recipe stores only the compact specialization; do not copy inherited fields.
30
30
 
31
+ Resolution uses Pi's authoritative active-session Skill snapshot across every Skill location Pi admits. Registration resolves and validates the maintained source, persists only its logical `<skill>/<recipe>` reference plus caller specialization, and projects the already-resolved effective contract into the runtime tool. Never replace this composition with a copied Recipe, absolute helper path, symlink, or ambient runtime re-resolution.
32
+
31
33
  Use `description` to narrow agent-facing intent when useful. Every supplied default must name a caller-owned source arg and satisfy its type or enum. Never default runtime-owned origins.
32
34
 
33
35
  ## Prove registration