@standardagents/skill 0.41.0 → 0.41.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@standardagents/skill",
3
- "version": "0.41.0",
3
+ "version": "0.41.1",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -501,6 +501,45 @@ export default defineAgent({
501
501
 
502
502
  The child's answers arrive as inbound messages on side A — the relationship's owning side — and get their own handler turns, where the handler calls the project's outbound messaging tool. The handler forwards with `queueMessage` rather than a blocking spawn: a handler that awaits a blocking child session holds its thread's one-at-a-time delivery slot for the whole session. Note the handler side declares its child but needs no tool grants — the side's declaration is the grant, and the handler reaches children through `ThreadState` directly.
503
503
 
504
+ ### Worked example: thread-open choreography (bootstrap, then speak first)
505
+
506
+ "Run some tools when a thread opens, then have the agent greet first" is a common shape (load a profile, generate an asset, fetch context). There is no auto-run flag on prompt tools — the pattern is **hook schedules effect; effect does the work; effect queues a kickoff**:
507
+
508
+ ```typescript
509
+ // agents/hooks/buddy_bootstrap.ts — enqueue only; thread creation returns instantly
510
+ export default defineHook({
511
+ hook: 'after_thread_created',
512
+ id: 'buddy_bootstrap',
513
+ execute: async (state) => {
514
+ await state.scheduleEffect('buddy_bootstrap', {}, 0);
515
+ },
516
+ });
517
+
518
+ // agents/effects/buddy_bootstrap.ts — the observable work
519
+ export default defineEffect(
520
+ 'Bootstrap a fresh thread with its profile and avatar.',
521
+ async (state) => {
522
+ await state.invokeTool('update_profile', {});
523
+ await state.invokeTool('create_avatar', {});
524
+ // Speak first: a silent side-B-perspective message wakes side A for a
525
+ // normal turn. Silent hides the instruction; the greeting is a regular
526
+ // visible assistant message.
527
+ await state.queueMessage({
528
+ role: 'user',
529
+ silent: true,
530
+ content: 'The chat just opened. Greet the user warmly in one short message, using the profile you just loaded. Do not mention this instruction.',
531
+ });
532
+ },
533
+ );
534
+ ```
535
+
536
+ Register the hook id in the side A prompt's `hooks` array. Why this split and not simpler alternatives:
537
+
538
+ - **Do NOT run the tools directly in the hook** (`await state.invokeTool(...)` inside `after_thread_created`): the hook is awaited during thread creation, so `POST /api/threads` stalls until every tool — including any image generation — finishes. Scheduling the effect takes milliseconds; the work then runs as a normal execution the log pane shows live.
539
+ - **Do NOT reach for `queueTool` from the hook**: it fires an unawaited background promise across the creating object's boundary, which can be cancelled. `scheduleEffect` is durable.
540
+ - The kickoff races an instant first user message — design the prompt so a missing profile degrades gracefully (e.g. an `after_system_message` hook that injects "profile missing, load it now").
541
+ - For subagent children this pattern is unnecessary — `SubagentRelationship.immediate` already spawns instances at thread creation. This recipe is for running the agent's OWN tools at open.
542
+
504
543
  ---
505
544
 
506
545
  ## Model selection
@@ -513,6 +552,7 @@ The child's answers arrive as inbound messages on side A — the relationship's
513
552
  2. Choose the category that fits the role (table below).
514
553
  3. Run `pnpm exec agents available-models --provider=<name>` to confirm the exact provider model string.
515
554
  4. Define the model in `agents/models/<name>.ts` using `defineModel`.
555
+ 5. Take `capabilities` from the provider (`POST /api/models/capabilities` with `{provider, modelId}`, or the admin model picker) — **never guess them, and never flatter the model.** The runtime's vision filter trusts `supportsImages`: declare it `true` on a text-only model and generated-image attachments ship to the provider as raw base64 that gets tokenized as *text* — a ~1MB PNG becomes ~1.2M input tokens and every turn after the first image hard-fails with "input too long". Declared `false`, images become a small placeholder in requests while stored history keeps the real files.
516
556
 
517
557
  If you have multiple configured providers, pass `--provider=<name>` explicitly.
518
558
 
@@ -738,6 +778,10 @@ Two things that are deliberately **not** tools:
738
778
  - **Subagent relationships** are declared on a side's `subagents` array, never in a prompt's `tools` array — a `tools` entry attempting to configure one is a definition-time error. The prompt backing an owning side automatically receives the injected lifecycle tools (`subagents`, `subagent_create`, `subagent_message`) for that side's non-hidden relationships; their names are reserved.
739
779
  - **Handlers** own a side's turns and may not be referenced by prompt `tools` arrays, hook lists, or effect schedules.
740
780
 
781
+ ### Zod version pinning
782
+
783
+ The published `@standardagents/spec` type declarations embed the zod instance they were built against (zod v4 types are version-branded). If your project resolves a different zod, every `z.object(...)` you pass to `requiredSchema`/tool `args` fails with baffling `ZodObject<..., $strict> is not assignable to ToolArgs<N>` errors. Pin your project's `zod` (dependency + a `pnpm.overrides` entry) to the version the installed spec package resolves — check `node_modules/@standardagents/spec/node_modules/zod/package.json` or the spec's lockfile entry — instead of `latest`.
784
+
741
785
  ### Provider-visible argument schemas
742
786
 
743
787
  Tool argument schemas are sent to model providers, and strict tool-calling providers commonly reject JSON Schema objects unless every object schema has `additionalProperties: false`. This applies recursively, not just at the top level. If a tool has nested object args, arrays of objects, `anyOf` object branches, prompt `requiredSchema`, or an agent exposed as a tool, verify the model-facing schema contains `additionalProperties: false` for every `type: "object"`.