# Hooks, plugins, timeline, and AI

Runtime extensions and deferred subsystems with precise limits on execution, registration, and authority.

<span id="middleware-de-lifecycle"></span>

<span class="section-anchor" id="section-1"></span>

## Lifecycle middleware

Hooks receive `(context, next)`. Calling `next()` continues the chain; omitting it cancels the chain. Higher priority runs first. Registration returns an unsubscribe function that you must retain if middleware has a shorter lifecycle than the shell. The TypeScript declaration of `use` says `void`; the runtime does provide this cleanup.

```js
const remove = wu.hooks.use('beforeMount', async (context, next) => {
  console.log('Montando', context.appName);
  await next();
}, { name: 'mount-observer', priority: 10 });

// Después de observar el escenario:
remove();
```

The phases are `beforeInit`, `afterInit`, `beforeLoad`, `afterLoad`, `beforeMount`, `afterMount`, `beforeUpdate`, `afterUpdate`, `beforeUnmount`, `afterUnmount`, `beforeDestroy`, and `afterDestroy`. Context depends on the phase; not every hook receives lifecycle, sandbox, or props.

Middleware throwing before `next` cancels by default; `failOpen: true` allows continuation after that failure. Exported helpers `createSimpleHook`, `createConditionalHook`, `createGuardHook`, `createTransformHook`, and `createTimedHook` build middleware. The timed helper logs its error and calls next: it is unsuitable for a control that must deny access on timeout.

<span id="plugin-con-grants-explícitos"></span>

<span class="section-anchor" id="section-2"></span>

## Plugin with explicit grants

```js
let stop = () => {};

await wu.pluginSystem.use({
  name: 'catalog-observer',
  permissions: ['events'],
  install(api) {
    stop = api.on('catalog:selected', (event) => {
      console.info('Selección observada', event.data);
    });
  },
  uninstall() { stop(); },
}, { grants: ['events'] });

// Al retirarlo:
await wu.pluginSystem.uninstall('catalog-observer');
```

`use` accepts an object or factory, awaits installation, and validates permissions. `ready()` lets you await pending installations; `init` already waits for this barrier. The default plugin-hook timeout is 5000 ms.

Factories receive options, not the core. Host grants are copied before calling the factory so it cannot expand its options and authorize itself. However, a plugin executes in the host: the filtered API does not isolate closures or global access that code already has.

<span id="timeline-y-tiempo-de-carga"></span>

<span class="section-anchor" id="section-3"></span>

## Timeline and loading time

```js
await wu.timelineReady();
wu.timeline.record({ snapshotEvery: 64, maxEntries: 5000 });

wu.store.set('demo.counter', 1);
wu.store.set('demo.counter', 2);

await wu.timeline.stepBack();
console.log(wu.store.get('demo.counter'));
await wu.timeline.live();

const recording = await wu.timeline.export();
wu.timeline.stop();
```

`timelineReady()` loads the chunk before starting. If you call `record()` for the first time and immediately write, the write may occur before deferred recording is armed. The facade preserves record/stop intent while the module loads; call ready first to capture the next write unambiguously.

The journal records store writes, events, and lifecycle/props passing through WU. Its default limit is 5000 entries, with snapshots every 64. `seek`, `stepBack`, `stepForward`, `live`, `export`, `import`, `entries`, and `status` let you inspect that record.

Going back rematerializes projections; it does not rewind apps' internal timers, HTTP requests, or infrastructure. While you are in the past, new writes are not added to the journal, and returning to live discards them. There is no history branching or implicit durable audit.

<span id="ia-optativa"></span>

<span class="section-anchor" id="section-4"></span>

## Optional AI

The main import installs a lazy proxy. Chainable methods such as `provider`, `action`, `trigger`, `capability`, and `workflow` can queue configuration while the chunk loads; value-returning operations require waiting for the real module. `aiReady()` obtains it explicitly.

```js
const ai = await wu.aiReady();
ai.init({ permissions: { executeActions: true } });

ai.action('sum_local', {
  description: 'Suma dos valores de demostración sin efectos externos',
  parameters: {
    type: 'object',
    properties: {
      a: { type: 'number' },
      b: { type: 'number' },
    },
    required: ['a', 'b'],
  },
  handler: async ({ a, b }) => ({ total: a + b }),
});

const result = await ai.execute('sum_local', { a: 2, b: 3 });
console.log(result);
```

Registering and executing this local action does not require sending data to a model. Conversation uses providers, `send`, `stream`, `json`, `agent`, triggers, and workflows, with their own permissions and configuration. Do not put a private service key in a public bundle.

Actions validate parameters, check `executeActions`, consult additional/conditional permissions, and may request confirmation. `dangerous: true` requires confirmation in the current code. Execution results use an envelope `{ success, result? , reason? }`: permission rejection may be a negative result rather than an exception.

Defaults allow `readStore` and `emitEvents`; `writeStore`, `readDOM`, `modifyDOM`, `readDiagnostics`, `navigate`, `executeActions`, `network`, and `allowDirectKey` start disabled. These are AI-module flags, independent of app grants and server authorization.

<span id="mcp-y-automatización"></span>

<span class="section-anchor" id="section-5"></span>

## MCP and automation

`wu.mcp.connect(url, options)`, `disconnect()`, and `isConnected()` expose the deferred client bridge. The package also includes the `wu-mcp-server` binary and its subpath. The bridge can inspect and mutate state/interface according to module controls; it does not automatically add binding dry runs, idempotency, domain authorization, rollback, or permanent records.

For an agent operating real resources, use a service that receives validated commands and returns receipts. The store can project its progress; an action should not become a closure executing privileges without revalidation.

<span id="referencia-de-implementación"></span>

<span class="section-anchor" id="section-6"></span>

## Implementation references

Hooks: `src/core/wu-hooks.js`; plugins: `src/core/wu-plugin.js`; timeline facade: `src/index.js`; journal: `src/core/wu-timeline.js`; AI: `src/ai/wu-ai.js`, `src/ai/wu-ai-actions.js`, `src/ai/wu-ai-actions.js`; defaults: `src/ai/wu-ai-permissions.js`; MCP: `src/index.js`. Tests: `tests/unit/plugin-security.test.js`, `tests/unit/timeline-facade.test.js`, `tests/unit/lazy-ai-proxy.test.js`, `tests/unit/ai-actions.test.js`, `tests/unit/mcp-bridge-security.test.js`.