Hi, I'm Bill Raiford. I've had a couple sessions with Gemini 2.0 Exp Thinking and I've run out of context window. So here is a recap, including the actual code we've produced.
# Project Goal
* Develop an agent-backed UI framework enabling dynamic and adaptable user interfaces, as a first-step test for a broader agent-centric evolutionary workflow using the ibgib protocol as a new "version control" paradigm for evolving code.
* UX inspired by chat "add message" interface, but capable of rendering arbitrary visual outputs for agent-backed semantic timelines.
* Strict prohibition of the `eval` function, rather agents produce version controlled artifacts that get rendered.
* Canvas-based rendering engine with limited initial primitives.
# General Code Notes
* Incremental Development Strategy
  * Starting towards minimal "Hello World" goal
  * Progressively adding complexity, following SOLID principles.
# Key Architectural Decisions
* *Overall a video-game-like architecture*
  * Key differences:
    * we're focused on simplified in terms of graphics, powerful in terms of mobility, robustness, and fully leveraging the unique properties that the ibgib protocol enables
    * sacrifice blazingly fast perf with millions of renderables, but still maintain solid perf responsiveness
    * the delivered benefit is dynamic, "magical" reimagined UX
    * bootstrap to adapt to, e.g.,
      * a standard chat UI that morphs/adapts to users' and agents' abilities
      * a simplified gaming platform (word games to start),
      * a reimagined meta-development, truly AI-enabled IDE
* *Sync render loop, Async API:*
  * Sync, fast *render* for immediate UI updates
  * Async *API via command queue* for agent communication and state changes
  * See below for current render and API/command code
* *"Version Control"-like State Management:*
  * Utilizing `ibgib` protocol for managing agentic state, like a more powerful git-like notepad for agents.
  * Branching paths of chat evolution can produce branching LLM-based agents, where agents can share a chat history but branch to differentiate.
  * Chat items will also be able to be applied like merging cherry-picked diffs to produce even more agent diversity JIT, as opposed to current chats which are a single linear timeline.
  * Content-addressed state, changes are monotonically increasing.
* *Modal User Experience:*
  * Inspired by Vim, with modes like Meta, Debug, Evolve, Feedback.
  * Commands associated with specific modes, with different agents (different text paths) as "mode specialists" so to speak.
* *Handle-Based Resource Management:*
  * Lightweight identifiers (currently objects with lone `uuid` property) for renderable objects and resources.
  * Separation of concerns, non-rendering code interacts via handles.
* *Centralized Rendering Service:*
  * Core component responsible for drawing on the (currently singleton) canvas.
  * Interactions via handles and a defined command set.
* *API Exposed to Agents*
  * Beginnings of render API implemented
  * Functions build commands and enqueue, sit on top of command system
  * Functions responsible for setting dirty flag in rendering svc
# Render Engine Requirements TODO
* We've deferred event handling so far
* Expand commands
# Current Rendering Implementation
```
import { getIdPool, getUUID, pretty, } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';

// render-constants
/**
 * @constant RENDER_SERVICE_HANDLE_TYPE - The handle type for the rendering service.
 */
export const RENDER_SERVICE_HANDLE_TYPE = 'RenderService';
/**
 * @constant RENDERABLE_HANDLE_TYPE - The handle type for individual renderable objects.
 */
export const RENDERABLE_HANDLE_TYPE = 'Renderable';
/**
 * @constant CANVAS_HANDLE_TYPE - The handle type for the canvas element.
 */
export const CANVAS_HANDLE_TYPE = 'Canvas';

// render-types

/**
 * @typedef ColorRGBA - Represents a color with red, green, blue, and alpha components.
 */
export type ColorRGBA = {
    /**
     * @property r - The red component of the color (0-1).
     */
    r: number;
    /**
     * @property g - The green component of the color (0-1).
     */
    g: number;
    /**
     * @property b - The blue component of the color (0-1).
     */
    b: number;
    /**
     * @property a - The alpha component of the color, representing opacity (0-1).
     */
    a: number;
};

/**
 * @typedef Vector2 - Represents a 2-dimensional vector.
 */
export type Vector2 = {
    /**
     * @property x - The x-component of the vector.
     */
    x: number;
    /**
     * @property y - The y-component of the vector.
     */
    y: number;
};

/**
 * @typedef Vector3 - Represents a 3-dimensional vector.
 */
export type Vector3 = {
    /**
     * @property x - The x-component of the vector.
     */
    x: number;
    /**
     * @property y - The y-component of the vector.
     */
    y: number;
    /**
     * @property z - The z-component of the vector (initially 0 for 2D).
     */
    z: number;
};

// render-geometry

/**
 * @interface GeometryParametersBase - Base interface for all geometry parameters.
 */
export interface GeometryParametersBase {
    /**
     * @property type - The type of geometry ('rect' or 'text').
     */
    type: 'rect' | 'text';
}

/**
 * @interface GeometryParametersRect - Parameters specific to rectangle geometry.
 * @extends GeometryParametersBase
 */
export interface GeometryParametersRect extends GeometryParametersBase {
    /**
     * @inheritdoc
     */
    type: 'rect';
    /**
     * @property width - The width of the rectangle.
     */
    width: number;
    /**
     * @property height - The height of the rectangle.
     */
    height: number;
}

/**
 * @interface GeometryParametersText - Parameters specific to text geometry.
 * @extends GeometryParametersBase
 */
export interface GeometryParametersText extends GeometryParametersBase {
    /**
     * @inheritdoc
     */
    type: 'text';
    /**
     * @property text - The text string to render.
     */
    text: string;
    /**
     * @property fontFamily - The font family for the text.
     */
    fontFamily: string;
    /**
     * @property fontSize - The font size for the text.
     */
    fontSize: number;
}

/**
 * @typedef GeometryParameters - Union type for all specific geometry parameter interfaces.
 */
export type GeometryParameters = GeometryParametersRect | GeometryParametersText;

/**
 * @interface Geometry - Defines the geometry of a renderable object.
 */
export interface Geometry {
    /**
     * @property type - The type of geometry, matching the `type` in `GeometryParameters`.
     */
    type: GeometryParameters['type'];
    /**
     * @property parameters - The parameters specific to the geometry type.
     */
    parameters: GeometryParameters;
}

// render-service.ts

/**
 * @interface RenderService - Interface for the rendering service.
 */
export interface RenderService {
    /**
     * @property initialized - A promise that resolves when the rendering service is fully initialized.
     */
    initialized: Promise<void>;

    /**
     * Creates a new renderable object.
     * @param {object} opts - Options for creating the renderable.
     * @param {RenderableState} opts.initialState - The initial state of the renderable.
     * @returns {Promise<RenderableHandle>} A promise that resolves with the handle to the newly created renderable.
     */
    renderableCreate(opts: {
        initialState: RenderableState;
    }): Promise<RenderableHandle>;

    /**
     * Updates the state of an existing renderable object.
     * @param {object} opts - Options for updating the renderable.
     * @param {RenderableHandle} opts.handle - The handle of the renderable to update.
     * @param {Partial<RenderableState>} opts.updatedState - The partial state to update on the renderable.
     * @returns {Promise<void>} A promise that resolves when the renderable has been updated.
     */
    renderableUpdate(opts: {
        handle: RenderableHandle;
        updatedState: Partial<RenderableState>;
    }): Promise<void>;

    /**
     * Destroys a renderable object, releasing its resources.
     * @param {object} opts - Options for destroying the renderable.
     * @param {RenderableHandle} opts.handle - The handle of the renderable to destroy.
     * @returns {Promise<void>} A promise that resolves when the renderable has been destroyed.
     */
    renderableDestroy(opts: {
        handle: RenderableHandle;
    }): Promise<void>;

    /**
     * Retrieves the current state of a renderable object.
     * @param {object} opts - Options for getting the renderable state.
     * @param {RenderableHandle} opts.handle - The handle of the renderable to retrieve.
     * @returns {Promise<RenderableState | undefined>} A promise that resolves with the current state of the renderable, or undefined if the handle is invalid.
     */
    getRenderableState(opts: {
        handle: RenderableHandle;
    }): Promise<RenderableState | undefined>;

    /**
     * Renders all currently managed renderable objects to the canvas.
     */
    render(): void;

    /**
     * Gets the 2D rendering context of the canvas.
     * @returns {CanvasRenderingContext2D | null} The canvas rendering context, or null if not available.
     */
    getContext(): CanvasRenderingContext2D | null;
}

/**
 * @interface RenderServiceInitializerOpts - Options for initializing the rendering service.
 */
export interface RenderServiceInitializerOpts {
    /**
     * @property canvasHandle - An optional handle to an existing canvas element. If not provided, a new canvas will be created.
     */
    canvasHandle?: CanvasHandle;
}

/**
 * @interface RenderServiceInitializer - Signature for the rendering service initializer (factory function).
 */
export interface RenderServiceInitializer {
    /**
     * Initializes the rendering service.
     * @param {RenderServiceInitializerOpts} [opts] - Initialization options.
     * @returns {RenderService} The initialized rendering service instance.
     */
    getRenderService(opts?: RenderServiceInitializerOpts): RenderService;
}

// render-handles
/**
 * @interface HandleBase - Base interface for all handles.
 */
export interface HandleBase {
    /**
     * @property uuid - A unique identifier for the handle.
     */
    uuid: string;
    /**
     * @property type - The type of the handle (e.g., 'Renderable', 'Canvas').
     */
    type: string;
}

/**
 * @interface RenderServiceHandle - Handle for the rendering service.
 * @extends HandleBase
 */
export interface RenderServiceHandle extends HandleBase {
    /**
     * @inheritdoc
     */
    type: typeof RENDER_SERVICE_HANDLE_TYPE;
}

/**
 * @interface RenderableHandle - Handle for an individual renderable object.
 * @extends HandleBase
 */
export interface RenderableHandle extends HandleBase {
    /**
     * @inheritdoc
     */
    type: typeof RENDERABLE_HANDLE_TYPE;
}

/**
 * @interface CanvasHandle - Handle for the HTMLCanvasElement.
 * @extends HandleBase
 */
export interface CanvasHandle extends HandleBase {
    /**
     * @inheritdoc
     */
    type: typeof CANVAS_HANDLE_TYPE;
}

// render-renderable-state
/**
 * @interface RenderableState - Represents the state of a renderable object.
 */
export interface RenderableState {
    /**
     * @property isVisible - Indicates whether the renderable is currently visible.
     */
    isVisible: boolean;
    /**
     * @property color - The color of the renderable.
     */
    color: ColorRGBA;
    /**
     * @property position - The position of the renderable in 2D space (with Z always 0 initially).
     */
    position: Vector3;
    /**
     * @property scale - The scale of the renderable in the x and y dimensions.
     *
     * TODO: refactor this to a Vector3 like {@link position} with z always 0.
     */
    scale: Vector2;
    /**
     * @property geometry - The geometry definition of the renderable.
     */
    geometry: Geometry;
}

// render-helpers

// render-service-v1
// imports

// render-service-v1
/**
 * @interface RenderServiceCtorOpts - Options for the RenderService constructor.
 */
interface RenderServiceCtorOpts {
    /**
     * @property canvasEl - An optional existing HTMLCanvasElement to use. If not provided, a new one will be created.
     */
    canvasEl?: HTMLCanvasElement;
}

/**
 * @class RenderService_V1 - Implementation of the rendering service.
 * Implements the {@link RenderService} interface.
 */
export class RenderService_V1 {
    protected lc = `[${RenderService_V1.name}]`;
    /**
     * @property initialized - A promise that resolves when the rendering service is fully initialized.
     */
    public readonly initialized: Promise<void>;

    /**
     * @private
     * @property canvas - The HTMLCanvasElement used for rendering.
     */
    private canvas: HTMLCanvasElement;

    /**
     * @private
     * @property context - The 2D rendering context of the canvas.
     */
    private context: CanvasRenderingContext2D | null;

    /**
     * @private
     * @property renderables - A map of renderable handles to their current state.
     */
    private renderables: Map<RenderableHandle, RenderableState> = new Map();

    /**
     * @private
     * @property uuidPool - A pool of pre-generated UUIDs for handles.
     */
    private uuidPool: string[] = [];

    /**
     * @private
     * @property handleToUUID - A map to quickly lookup the UUID string given a handle.
     */
    private handleToUUID: Map<HandleBase, string> = new Map();

    /**
     * @private
     * @property uuidToHandle - A map for reverse lookup from UUID to handle.
     */
    private uuidToHandle: Map<string, HandleBase> = new Map();

    /**
       * @private
       * @property canvasHandle - The handle for the managed canvas element.
       */
    private canvasHandle: CanvasHandle;

    /**
     * @private
     * @property dirty - A flag indicating whether the canvas needs to be re-rendered.
     */
    private dirty: boolean = false;

    /**
     * Constructs a new RenderService instance.
     * @param {RenderServiceCtorOpts} [opts] - Options for initializing the rendering service.
     */
    constructor(opts?: RenderServiceCtorOpts) {
        if (opts?.canvasEl) {
            this.canvas = opts.canvasEl;
        } else {
            this.canvas = document.createElement('canvas');
            document.body.appendChild(this.canvas);
            this.canvas.style.width = '100%';
            this.canvas.style.height = '100%';
            this.canvas.width = this.canvas.offsetWidth;
            this.canvas.height = this.canvas.offsetHeight;
        }

        this.context = this.canvas.getContext('2d');
        this.canvasHandle = this.createNewCanvasHandle(); // Create the canvas handle here
        this.initialized = this.initialize();
        this.startRenderLoop();
    }

    /**
     * Marks the canvas as dirty, triggering a re-render on the next animation frame.
     */
    private markDirty(): void {
        this.dirty = true;
    }

    /**
     * Starts the rendering loop using requestAnimationFrame.
     * @private
     */
    private startRenderLoop(): void {
        const renderFrame = () => {
            if (this.dirty) {
                this.render();
                this.dirty = false;
            }
            requestAnimationFrame(renderFrame);
        };
        requestAnimationFrame(renderFrame);
    }

    /**
     * Returns the handle associated with the managed canvas.
     * @returns {CanvasHandle} The handle for the canvas.
     */
    public getCanvasHandle(): CanvasHandle {
        return this.canvasHandle;
    }

    /**
     * Initializes the rendering service, including pre-generating UUIDs.
     * @private
     * @returns {Promise<void>} A promise that resolves when the service is initialized.
     */
    private async initialize(): Promise<void> {
        await this.generateUUIDs(1000);

        // Any other async initialization can go here.
    }

    /**
     * Generates a batch of UUIDs and adds them to the pool.
     * @private
     * @param {number} count - The number of UUIDs to generate.
     */
    private async generateUUIDs(count: number): Promise<void> {
        const ids = await getIdPool({ n: count });
        this.uuidPool.push(...ids);
    }

    /**
     * Gets a UUID from the pool. If the pool is empty, generates more.
     * @private
     * @returns {string} A UUID.
     */
    private getUUID(): string {
        // arbitrary low threshold. turn this into a constant later
        if (this.uuidPool.length < 50) {
            this.generateUUIDs(1000); // spins off to generate more
        }
        if (this.uuidPool.length === 0) {
            // return an emergency sync uuid using crypto.randomUUID, stripping away the dashes
            return crypto.randomUUID().replace(/-/g, '');
        } else {
            return this.uuidPool.pop()!;
        }
    }

    /**
     * Creates a new renderable handle.
     * @private
     * @returns {RenderableHandle} The newly created renderable handle.
     */
    private createNewRenderableHandle(): RenderableHandle {
        const uuid = this.getUUID();
        const handle: RenderableHandle = { type: RENDERABLE_HANDLE_TYPE, uuid };
        this.handleToUUID.set(handle, uuid);
        this.uuidToHandle.set(uuid, handle);
        return handle;
    }

    /**
     * Creates a new canvas handle.
     * @private
     * @returns {CanvasHandle} The newly created canvas handle.
     */
    private createNewCanvasHandle(): CanvasHandle {
        const uuid = this.getUUID();
        const handle: CanvasHandle = { type: CANVAS_HANDLE_TYPE, uuid };
        this.handleToUUID.set(handle, uuid);
        this.uuidToHandle.set(uuid, handle);
        return handle;
    }

    /**
     * @inheritdoc
     */
    public async renderableCreate(opts: {
        initialState: RenderableState;
    }): Promise<RenderableHandle> {
        const handle = this.createNewRenderableHandle();
        this.renderables.set(handle, opts.initialState);
        this.markDirty();
        return handle;
    }

    /**
     * @inheritdoc
     */
    public async renderableUpdate(opts: {
        handle: RenderableHandle;
        updatedState: Partial<RenderableState>;
    }): Promise<void> {
        const lc = `${this.lc}[${this.renderableUpdate.name}]`;
        const currentState = this.renderables.get(opts.handle);
        if (currentState) {
            this.renderables.set(opts.handle, { ...currentState, ...opts.updatedState });
            this.markDirty();
        } else {
            console.warn(`${lc} Attempting to update non-existent renderable with handle: ${opts.handle.uuid} (W: 8071c40f941a39d39e104f4fe2279d24)`);
        }
    }

    /**
     * @inheritdoc
     */
    public async renderableDestroy(opts: { handle: RenderableHandle }): Promise<void> {
        this.renderables.delete(opts.handle);
        this.handleToUUID.delete(opts.handle);
        this.uuidToHandle.delete(opts.handle.uuid);
        this.markDirty();
    }

    /**
     * @inheritdoc
     */
    public async getRenderableState(opts: {
        handle: RenderableHandle;
    }): Promise<RenderableState | undefined> {
        return this.renderables.get(opts.handle);
    }

    /**
     * @inheritdoc
     */
    public render(): void {
        const lc = `${this.lc}[${this.render.name}]`;
        if (!this.context) {
            console.error(`${lc} Canvas context not initialized. (E: 7d5755ca9a4148e1141298c59ce94b24)`);
            return;
        }

        // Clear the canvas
        this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);

        for (const [_handle, state] of this.renderables) {
            if (state.isVisible) {
                const { geometry, position, scale, color } = state;

                this.context.fillStyle = `rgba(${color.r * 255}, ${color.g * 255}, ${color.b * 255}, ${color.a})`;

                switch (geometry.type) {
                    case 'rect': {
                        const { width, height } = geometry.parameters as GeometryParametersRect;
                        this.context.fillRect(
                            position.x,
                            position.y,
                            width * scale.x,
                            height * scale.y
                        );
                        break;
                    }
                    case 'text': {
                        const { text, fontFamily, fontSize } = geometry.parameters as GeometryParametersText;
                        this.context.font = `${fontSize}px ${fontFamily}`;
                        this.context.fillText(text, position.x, position.y);
                        break;
                    }
                }
            }
        }
    }

    /**
     * @inheritdoc
     */
    public getContext(): CanvasRenderingContext2D | null {
        return this.context;
    }
}
// /**
//  * @interface RenderServiceCtorOpts - Options for the RenderService constructor.
//  */
// interface RenderServiceCtorOpts {
//     /**
//      * @property canvasEl - An optional existing HTMLCanvasElement to use. If not provided, a new one will be created.
//      */
//     canvasEl?: HTMLCanvasElement;
// }

// /**
//  * @class RenderService_V1 - Implementation of the rendering service.
//  * Implements the {@link RenderService} interface.
//  */
// export class RenderService_V1 {
//     protected lc = `[${RenderService_V1.name}]`;
//     /**
//      * @property initialized - A promise that resolves when the rendering service is fully initialized.
//      */
//     public readonly initialized: Promise<void>;

//     /**
//      * @private
//      * @property canvas - The HTMLCanvasElement used for rendering.
//      */
//     private canvas: HTMLCanvasElement;

//     /**
//      * @private
//      * @property context - The 2D rendering context of the canvas.
//      */
//     private context: CanvasRenderingContext2D | null;

//     /**
//      * @private
//      * @property renderables - A map of renderable handles to their current state.
//      */
//     private renderables: Map<RenderableHandle, RenderableState> = new Map();

//     /**
//      * @private
//      * @property uuidPool - A pool of pre-generated UUIDs for handles.
//      */
//     private uuidPool: string[] = [];

//     /**
//      * @private
//      * @property handleToUUID - A map to quickly lookup the UUID string given a handle.
//      */
//     private handleToUUID: Map<HandleBase, string> = new Map();

//     /**
//      * @private
//      * @property uuidToHandle - A map for reverse lookup from UUID to handle.
//      */
//     private uuidToHandle: Map<string, HandleBase> = new Map();

//     /**
//        * @private
//        * @property canvasHandle - The handle for the managed canvas element.
//        */
//     private canvasHandle: CanvasHandle;

//     /**
//      * Constructs a new RenderService instance.
//      * @param {RenderServiceCtorOpts} [opts] - Options for initializing the rendering service.
//      */
//     constructor(opts?: RenderServiceCtorOpts) {
//         if (opts?.canvasEl) {
//             this.canvas = opts.canvasEl;
//         } else {
//             this.canvas = document.createElement('canvas');
//             document.body.appendChild(this.canvas);
//             this.canvas.style.width = '100%';
//             this.canvas.style.height = '100%';
//             this.canvas.width = this.canvas.offsetWidth;
//             this.canvas.height = this.canvas.offsetHeight;
//         }

//         this.context = this.canvas.getContext('2d');
//         this.canvasHandle = this.createNewCanvasHandle(); // Create the canvas handle here
//         this.initialized = this.initialize();
//     }

//     /**
//      * Returns the handle associated with the managed canvas.
//      * @returns {CanvasHandle} The handle for the canvas.
//      */
//     public getCanvasHandle(): CanvasHandle {
//         return this.canvasHandle;
//     }

//     /**
//      * Initializes the rendering service, including pre-generating UUIDs.
//      * @private
//      * @returns {Promise<void>} A promise that resolves when the service is initialized.
//      */
//     private async initialize(): Promise<void> {
//         await this.generateUUIDs(1000);

//         // Any other async initialization can go here.
//     }

//     /**
//      * Generates a batch of UUIDs and adds them to the pool.
//      * @private
//      * @param {number} count - The number of UUIDs to generate.
//      */
//     private async generateUUIDs(count: number): Promise<void> {
//         const ids = await getIdPool({ n: count });
//         this.uuidPool.push(...ids);
//     }

//     /**
//      * Gets a UUID from the pool. If the pool is empty, generates more.
//      * @private
//      * @returns {string} A UUID.
//      */
//     private getUUID(): string {
//         // arbitrary low threshold. turn this into a constant later
//         if (this.uuidPool.length < 50) {
//             this.generateUUIDs(1000); // spins off to generate more
//         }
//         if (this.uuidPool.length === 0) {
//             // return an emergency sync uuid using crypto.randomUUID, stripping away the dashes
//             return crypto.randomUUID().replace(/-/g, '');
//         } else {
//             return this.uuidPool.pop()!;
//         }
//     }

//     /**
//      * Creates a new renderable handle.
//      * @private
//      * @returns {RenderableHandle} The newly created renderable handle.
//      */
//     private createNewRenderableHandle(): RenderableHandle {
//         const uuid = this.getUUID();
//         const handle: RenderableHandle = { type: RENDERABLE_HANDLE_TYPE, uuid };
//         this.handleToUUID.set(handle, uuid);
//         this.uuidToHandle.set(uuid, handle);
//         return handle;
//     }

//     /**
//      * Creates a new canvas handle.
//      * @private
//      * @returns {CanvasHandle} The newly created canvas handle.
//      */
//     private createNewCanvasHandle(): CanvasHandle {
//         const uuid = this.getUUID();
//         const handle: CanvasHandle = { type: CANVAS_HANDLE_TYPE, uuid };
//         this.handleToUUID.set(handle, uuid);
//         this.uuidToHandle.set(uuid, handle);
//         return handle;
//     }

//     /**
//      * @inheritdoc
//      */
//     public async renderableCreate(opts: {
//         initialState: RenderableState;
//     }): Promise<RenderableHandle> {
//         const handle = this.createNewRenderableHandle();
//         this.renderables.set(handle, opts.initialState);
//         return handle;
//     }

//     /**
//      * @inheritdoc
//      */
//     public async renderableUpdate(opts: {
//         handle: RenderableHandle;
//         updatedState: Partial<RenderableState>;
//     }): Promise<void> {
//         const lc = `${this.lc}[${this.renderableUpdate.name}]`;
//         const currentState = this.renderables.get(opts.handle);
//         if (currentState) {
//             this.renderables.set(opts.handle, { ...currentState, ...opts.updatedState });
//         } else {
//             console.warn(`${lc} Attempting to update non-existent renderable with handle: ${opts.handle.uuid} (W: 8071c40f941a39d39e104f4fe2279d24)`);
//         }
//     }

//     /**
//      * @inheritdoc
//      */
//     public async renderableDestroy(opts: { handle: RenderableHandle }): Promise<void> {
//         this.renderables.delete(opts.handle);
//         this.handleToUUID.delete(opts.handle);
//         this.uuidToHandle.delete(opts.handle.uuid);
//     }

//     /**
//      * @inheritdoc
//      */
//     public async getRenderableState(opts: {
//         handle: RenderableHandle;
//     }): Promise<RenderableState | undefined> {
//         return this.renderables.get(opts.handle);
//     }

//     /**
//      * @inheritdoc
//      */
//     public render(): void {
//         const lc = `${this.lc}[${this.render.name}]`;
//         if (!this.context) {
//             console.error(`${lc} Canvas context not initialized. (E: 7d5755ca9a4148e1141298c59ce94b24)`);
//             return;
//         }

//         // Clear the canvas
//         this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);

//         for (const [_handle, state] of this.renderables) {
//             if (state.isVisible) {
//                 const { geometry, position, scale, color } = state;

//                 this.context.fillStyle = `rgba(${color.r * 255}, ${color.g * 255}, ${color.b * 255}, ${color.a})`;

//                 switch (geometry.type) {
//                     case 'rect': {
//                         const { width, height } = geometry.parameters as GeometryParametersRect;
//                         this.context.fillRect(
//                             position.x,
//                             position.y,
//                             width * scale.x,
//                             height * scale.y
//                         );
//                         break;
//                     }
//                     case 'text': {
//                         const { text, fontFamily, fontSize } = geometry.parameters as GeometryParametersText;
//                         this.context.font = `${fontSize}px ${fontFamily}`;
//                         this.context.fillText(text, position.x, position.y);
//                         break;
//                     }
//                 }
//             }
//         }
//     }

//     /**
//      * @inheritdoc
//      */
//     public getContext(): CanvasRenderingContext2D | null {
//         return this.context;
//     }
// }

// #region render-service-factory
/**
 * @private
 * @type {RenderService_V1 | undefined} - A singleton instance of the RenderService.
 */
let globalRenderService: RenderService_V1 | undefined;

/**
 * Options for getting the Render Service.
 */
interface GetRenderServiceOpts {
    /**
     * If true, forces the creation of a new RenderService instance.
     */
    forceNew?: boolean;
    /**
     * Optionally provide an existing canvas element.
     */
    canvasEl?: HTMLCanvasElement;
}

/**
 * Gets the singleton instance of the RenderService.
 * @param {GetRenderServiceOpts} [opts] - Options for getting the service.
 * @returns {RenderService_V1} The RenderService instance.
 */
export function getRenderService(opts?: GetRenderServiceOpts): RenderService_V1 {
    if (!globalRenderService || opts?.forceNew) {
        globalRenderService = new RenderService_V1(opts);
    }
    return globalRenderService;
}
// #endregion render-service-factory

// #region

// #region render-service-api
/**
 * Options for creating a renderable object.
 */
interface CreateRenderablesObjectOpts {
    /**
     * @property initialState - The initial state of the renderable.
     */
    initialState: RenderableState;
}

/**
 * Creates a new renderable object using the rendering service.
 * @param {CreateRenderablesObjectOpts} opts - Options for creating the renderable.
 * @returns {Promise<RenderableHandle>} A promise that resolves with the handle to the newly created renderable.
 */
export async function renderableCreateObject(
    opts: CreateRenderablesObjectOpts
): Promise<RenderableHandle> {
    const service = getRenderService();
    return await service.renderableCreate(opts);
}
// #endregion render-service-api
```
# Current Agent-facing API + Command Implementation
```
import { WitnessCmdData } from "@ibgib/core-gib/dist/witness/witness-cmd/witness-cmd-types.mjs";
import { RenderableHandle, RenderableState, getRenderService } from "../render/render-one-file.mjs";

/**
 * @interface CommandBase - Base interface for all commands.
 * Extends the structure for witness commands.
 */
interface CommandBase<TCmds extends string, TCmdModifiers extends string> extends WitnessCmdData<TCmds, TCmdModifiers> {
}

/**
 * @interface CreateRenderablesCommand - Command for creating a renderable object.
 * @extends CommandBase
 */
interface CreateRenderablesCommand extends CommandBase<'renderable', 'create'> {
    /**
     * @property initialState - The initial state of the renderable to create.
     */
    initialState: RenderableState;
}

/**
 * @interface UpdateRenderableCommand - Command for updating an existing renderable object.
 * @extends CommandBase
 */
interface UpdateRenderableCommand extends CommandBase<'renderable', 'update'> {
    /**
     * @property handle - The handle of the renderable to update.
     */
    handle: RenderableHandle;
    /**
     * @property updatedState - The partial state to update on the renderable.
     */
    updatedState: Partial<RenderableState>;
}

/**
 * @interface RenderableDestroyCommand - Command for destroying a renderable object.
 * @extends CommandBase
 */
interface RenderableDestroyCommand extends CommandBase<'renderable', 'destroy'> {
    /**
     * @property handle - The handle of the renderable to destroy.
     */
    handle: RenderableHandle;
}

/**
 * @interface RenderCommand - A union of all possible render-related commands.
 */
type RenderCommand =
    | CreateRenderablesCommand
    | UpdateRenderableCommand
    | RenderableDestroyCommand;

/**
 * @interface EnqueuedCommand - Represents a command enqueued with its resolve and reject functions.
 */
interface EnqueuedCommand<TCommand extends CommandBase<string, string>> {
    command: TCommand;
    resolve: (result: any) => void;
    reject: (error: Error) => void;
}

/**
 * @interface CommandService - Interface for the command service.
 */
interface CommandService {
    /**
     * Enqueues a command to be executed.
     * @param {EnqueuedCommand<TCommand>} enqueuedCommand - The command to enqueue with its resolve and reject functions.
     */
    enqueueCommand<TCommand extends CommandBase<string, string>>(enqueuedCommand: EnqueuedCommand<TCommand>): void;

    /**
     * Starts processing the command queue.
     * @returns {Promise<void>} A promise that resolves when the queue is empty.
     */
    processQueue(): Promise<void>;
}

/**
 * @class CommandService_V1 - Implementation of the command service.
 * Implements the {@link CommandService} interface.
 */
class CommandService_V1 implements CommandService {
    protected lc: string = `[${CommandService_V1.name}]`;
    private commandQueue: EnqueuedCommand<CommandBase<string, string>>[] = [];
    private isProcessing = false;

    constructor() { }

    enqueueCommand<TCommand extends CommandBase<string, string>>(enqueuedCommand: EnqueuedCommand<TCommand>): void {
        this.commandQueue.push(enqueuedCommand);
        if (!this.isProcessing) {
            this.startProcessing();
        }
    }

    private startProcessing(): void {
        this.isProcessing = true;
        this.processQueue().finally(() => {
            this.isProcessing = false;
            if (this.commandQueue.length > 0) {
                this.startProcessing(); // If more commands were added, process them
            }
        });
    }

    async processQueue(): Promise<void> {
        const lc = `${this.lc}[${this.processQueue.name}]`;
        while (this.commandQueue.length > 0) {
            const enqueuedCommand = this.commandQueue.shift();
            if (enqueuedCommand) {
                try {
                    const result = await this.executeCommand(enqueuedCommand.command);
                    enqueuedCommand.resolve(result);
                } catch (error: any) {
                    console.error(`Error executing command ${enqueuedCommand.command.cmd} ${enqueuedCommand.command.cmdModifiers?.join(',') ?? ''}:`, error);
                    enqueuedCommand.reject(error);
                }
            }
        }
        console.log(`${lc} complete. (I: b79c09b807ea4aa96bb991ab3e8a0524)`)
    }

    private async executeCommand(command: CommandBase<string, string>): Promise<any | void> {
        const lc = `[${CommandService_V1.name}.${this.executeCommand.name}]`;
        const renderService = getRenderService();

        if (command.cmd === 'renderable') {
            if (command.cmdModifiers?.includes('create')) {
                const cmd = command as CreateRenderablesCommand;
                return await renderService.renderableCreate({ initialState: cmd.initialState });
            } else if (command.cmdModifiers?.includes('update')) {
                const cmd = command as UpdateRenderableCommand;
                await renderService.renderableUpdate({ handle: cmd.handle, updatedState: cmd.updatedState });
                return;
            } else if (command.cmdModifiers?.includes('destroy')) {
                const cmd = command as RenderableDestroyCommand;
                await renderService.renderableDestroy({ handle: cmd.handle });
                return;
            } else {
                throw new Error(`${lc} Unknown renderable command modifier(s): ${command.cmdModifiers?.join(',')} (E: 8a9b7c3d1e2f)`);
            }
        } else {
            throw new Error(`${lc} Unknown command: ${command.cmd} (E: 3b2c1a0f9e8d)`);
        }
    }
}

/**
 * @private
 */
let globalCommandService: CommandService | undefined;

/**
 * Gets the singleton instance of the CommandService.
 * @returns {CommandService} The CommandService instance.
 */
function getCommandService(): CommandService {
    if (!globalCommandService) {
        globalCommandService = new CommandService_V1();
    }
    return globalCommandService;
}

/**
 * @interface CreateRenderablesObjectOpts - Options for the renderableCreateObject API function.
 */
interface CreateRenderablesObjectOpts {
    initialState: RenderableState;
}

/**
 * @function renderableCreateObject - API function to create a renderable object.
 * @param {CreateRenderablesObjectOpts} opts - Options for creating the renderable.
 * @returns {Promise<RenderableHandle>} A promise that resolves with the handle to the newly created renderable.
 */
export function renderableCreateObject(
    opts: CreateRenderablesObjectOpts
): Promise<RenderableHandle> {
    const commandService = getCommandService();
    const command: CreateRenderablesCommand = {
        cmd: 'renderable',
        cmdModifiers: ['create'],
        initialState: opts.initialState,
    };

    return new Promise<RenderableHandle>((resolve, reject) => {
        commandService.enqueueCommand({ command, resolve, reject });
    });
}

/**
 * @interface UpdateRenderableObjectOpts - Options for the renderableUpdateObject API function.
 */
interface UpdateRenderableObjectOpts {
    handle: RenderableHandle;
    updatedState: Partial<RenderableState>;
}

/**
 * @function renderableUpdateObject - API function to update a renderable object.
 * @param {UpdateRenderableObjectOpts} opts - Options for updating the renderable.
 */
export function renderableUpdateObject(
    opts: UpdateRenderableObjectOpts
): Promise<void> {
    const commandService = getCommandService();
    const command: UpdateRenderableCommand = {
        cmd: 'renderable',
        cmdModifiers: ['update'],
        handle: opts.handle,
        updatedState: opts.updatedState,
    };
    return new Promise<void>((resolve, reject) => {
        commandService.enqueueCommand({ command, resolve, reject });
    });
}

/**
 * @interface RenderableDestroyObjectOpts - Options for the renderableDestroyObject API function.
 */
interface RenderableDestroyObjectOpts {
    handle: RenderableHandle;
}

/**
 * @function renderableDestroyObject - API function to destroy a renderable object.
 * @param {RenderableDestroyObjectOpts} opts - Options for destroying the renderable.
 */
export function renderableDestroyObject(
    opts: RenderableDestroyObjectOpts
): Promise<void> {
    const commandService = getCommandService();
    const command: RenderableDestroyCommand = {
        cmd: 'renderable',
        cmdModifiers: ['destroy'],
        handle: opts.handle,
    };
    return new Promise<void>((resolve, reject) => {
        commandService.enqueueCommand({ command, resolve, reject });
    });
}
```
# Key Ibgib Architecture Highlights
There's too much code to paste all code, but here are key highlights for now
## @ibgib/ts-gib lowest level
```
/**
 * @module types
 *
 * These are the core types at the lowest level of the ibGib protocol.
 *
 * The core of the simplicity of the ibGib protocol is that fundamentally you
 * are encompassing the idea of an ibgib (similar to a base class "object" or
 * "thing") that has intrinsic data and relationships to extrinsic ibgib.  Each
 * ibgib is content addressed with a unique addressing style called its ib^gib
 * address. This allows us semantics for abstracting the "content addressing"
 * per use case, with various schemas allowed, but in general the `ib` is
 * per-use-case metadata and the `gib` is the more conventional hash of the
 * ibgib record to allow for the cryptographic guarantees one would expect. Each
 * discrete frame in time of the ibgib is its own ibgib, and a timeline stream
 * of that ibgib is created via the "past" relationship (`rel8n`).
 *
 * New ibgib records are created via "transforms", of which atow there are three
 * kinds: `mut8`, `rel8`, `fork`
 *
 * These are actually different aspects of the single function of relationship
 * and time, either extending or creating a timeline.
 *
 * Mut8 is intrinsic, rel8 is extrinsic, fork makes a new timeline.  Mut8
 * changes a timeline, rel8 changes a timeline's link(s), fork creates a new
 * timeline.
 *
 * ## notes
 *
 * I have attempted to make these version independent, though we will not know
 * how successful that is until we do a V2.
 */
/**
 * Type alias of string (or empty string) that is meant to convey that it
 * is used as the ib in an ib^gib address or an `IbGib` class.
 */
export declare type Ib = '' | string;
/**
 * Type alias of string (or empty string) that is meant to convey that it
 * is used as the gib in an ib^gib address or an `IbGib` class.
 */
export declare type Gib = '' | 'gib' | string;
/**
 * Convenient for when destructuring an IbGibAddr
 */
export interface IbAndGib {
    ib: Ib;
    gib: Gib;
}
/**
 * Mostly a marker type of address.
 *
 * Not sure how to enforce schema on demand in TypeScript, but
 * that's to decide for future.
 */
export declare type IbGibAddr = string;
/**
 * Type alias that indicates that this is intended to be a tjp (temporal
 * junction point) address.
 */
export declare type TjpIbGibAddr = IbGibAddr;
export declare type IbGibRel8ns = {
    /**
     * The key is the rel8nName.
     *
     * TS doesnt allow for the indexer to have a non-number/string value
     * even if it's an alias for one of these types to be more readable.
     * Otherwise I would type the `key` here as a `Rel8nName` alias of
     * `string.`
     */
    [key: string]: IbGibAddr[] | null | undefined;
};
/**
 * At the base ibGib, the bare minimum is that an ib (data) is required,
 * with an optional gib (metadata).
 */
export interface IbGib {
    ib: Ib;
    gib?: Gib;
}
/**
 * When adding additional structure to an ibGib, the first representation
 * mechanism (from V1) was having explicit Data and Rel8ns keys.
 *
 * This is, however, not the only way to represent this. As just one example,
 * you could also have a prefix for keys that are meant to be the relationships
 * to other ibGibs.
 *
 * I'm including this shape at the core here and not just in V1 because it could
 * be reused for other versions that are different tweaks with different default
 * relationships and data structures but still adhere to this data/rel8ns split
 * (and naming convention).
 */
export interface IbGibWithDataAndRel8ns<TData = any, TRel8ns extends IbGibRel8ns = IbGibRel8ns> extends IbGib {
    /**
     * Intrinsic data to this ibGib, most likely with primitives e.g. strings or numbers,
     * that will be statically copied from mutation to mutation.
     */
    data?: TData | undefined;
    /**
     * Extrinsic data WRT this ibGib, which can track their own timelines of changes.
     *
     * Note that even if the relationships here point to the same address, i.e. do
     * not change, the other ibGibs can be seen as changing or not. It all depends
     * on how you want to interpret the relationship.
     *
     * For example, if you want to pause/freeze a relationship, then this would be
     * handled by the viewer of the relationship. It just wouldn't check anywhere for
     * the more up-to-date version.
     */
    rel8ns?: TRel8ns | undefined;
}
export declare type TransformType = 'fork' | 'mut8' | 'rel8';
export interface TransformOpts<TSrc extends IbGib = IbGib> {
    /**
     * Fork, mut8, rel8
     */
    type?: TransformType;
    /**
     * If truthy, does NOT add a timestamp to the new ibGib.
     */
    noTimestamp?: boolean;
    /**
     * If truthy, creates dna ibGibs like V1.
     * If falsy, skips the dna and only creates the resultant ibGib.
     */
    dna?: boolean;
    /**
     * Src to apply the transform to.
     *
     * Used at runtime, NOT persisted in space.
     */
    src?: TSrc;
    /**
     * Address of Src. Used in the space persistence.
     */
    srcAddr?: IbGibAddr;
    /**
     * There are two ways of creating rel8ns to other ibgibs:
     *
     * 1) Concat previous ones with new one
     *   * e.g. past: ['a^gib'] --> past: ['a^gib', 'b^gib'] --> past: ['a^gib', 'b^gib', 'c^gib']
     * 2) Only have most recent one, linked list style:
     *   * e.g. past: ['a^gib'] --> past: ['b^gib'] --> past: ['c^gib']
     *
     * If you choose #1, then you have the advantage of not needing to load up all of the previous
     * records to evaluate. So this will give you minimal runtime requirements, but take up more space.
     * If you choose #2, like in a blockchain e.g., then you will have a smaller space footprint but
     * it takes longer to build the entire list.
     *
     * This gives flexibility for having rel8ns that only wish to have a single value at most.
     *
     */
    linkedRel8ns?: string[];
    /**
     * If truthy, this will include a data['n'] value that acts as an incremented
     * counter for a naive versioning tracker.
     *
     * So A0^123.data.n = 0, A1^456.data.n = 1, etc.
     */
    nCounter?: boolean;
}
export interface TemporalJunctionPointOptions {
    timestamp?: boolean;
    uuid?: boolean;
}
export interface TransformOpts_Fork<TSrc extends IbGib = IbGib> extends TransformOpts<TSrc> {
    type?: 'fork';
    /**
     * Destination ib for the transform.
     */
    destIb?: string;
    /**
     * If truthy, creates a UUID to encourage local uniqueness of the fork.
     */
    uuid?: boolean;
    /**
     * If truthy, the forked ibGib will be a temporal junction point using
     * the specified
     * This will provide a UUID to encourage local uniqueness of the fork.
     *
     * For what a temporal junction point is, refer to
     * Back to the Future II (or ask me).
     */
    tjp?: TemporalJunctionPointOptions;
    /**
     * If true, retains parent/source's extrinsic rel8ns.
     */
    cloneRel8ns?: boolean;
    /**
     * If true, retains parent/source's intrinsic data.
     */
    cloneData?: boolean;
}
export interface TransformOpts_Mut8<TSrc extends IbGib = IbGib, TNewData = any> extends TransformOpts<TSrc> {
    type?: 'mut8';
    /**
     * Info to rename keys. Should be an object where each value should either be
     * a string or an object. If it's a string, then the corresponding key will
     * be renamed to that value. If it's an object, then it will recurse at that
     * key.
     *
     * @example
     * data = { a: 'aaa', b: { rename: 'me', ignore: 'me' }}
     * dataToRemove = { a: 'aRenamed', b: { rename: 'renamed' } }
     * data = { aRenamed: 'aaa', b: { renamed: 'me', ignore: 'me' }}
     */
    dataToRename?: {
        [key: string]: any;
    };
    /**
     * Info to remove keys. Should be an object where each value should either be
     * a string or an object. If it's a string, then the corresponding key will
     * be removed (the value is ignored). If it's an object, then it will recurse at that
     * key.
     *
     * @example
     * data = { a: 'aaa', b: { remove: 'me', ignore: 'me' }}
     * dataToRemove = { b: { remove: '' } }
     * data = { a: 'aaa', b: { ignore: 'me' }}
     */
    dataToRemove?: {
        [key: string]: any;
    };
    /**
     * Data object that contains only additive information for ibGib's intrinsic data.
     */
    dataToAddOrPatch?: TNewData;
    /**
     * If given, will mut8 the ib (without forking the entire ibGib).
     *
     * Often this will act as a 'rename', e.g. (if the ib is acting as the 'name')
     */
    mut8Ib?: string;
}
export interface TransformOpts_Rel8<TSrc extends IbGib = IbGib> extends TransformOpts<TSrc> {
    type?: 'rel8';
    /**
     * Relationships to add to an ibGib, either by creating new relationships or by
     * concatenating the arrays to existing ones.
     *
     * NOTES:
     *   - Moving relationships can be performed by combining add/remove rel8ns.
     *   - How the relations are actually represented in the ibGib record is
     *     left to the compiler. The same goes for how this information is
     *     recorded in the dna, if generating dna.
     */
    rel8nsToAddByAddr?: IbGibRel8ns;
    /**
     * Relationships to remove from an ibGib.
     *
     * NOTES:
     *   - Moving relationships can be performed by combining add/remove rel8ns.
     *   - Attempting to remove a non-existing relationship may or may not cause
     *     an error, depending on the compiler.
     */
    rel8nsToRemoveByAddr?: IbGibRel8ns;
}
export interface TransformResult<TOut extends IbGib> {
    /**
     * The final result of the transformation
     */
    newIbGib: TOut;
    /**
     * DNA side effects created in the transformation.
     */
    dnas?: TOut[];
    /**
     * If you're performing a transform with multiple steps,
     * then there will be intermediate ibGibs created.
     */
    intermediateIbGibs?: TOut[];
}
//# sourceMappingURL=types.d.mts.map
```

Note that at this point, this is a **data only** protocol. It's a semi-structure data construct, with no functions on it.  Also note that in the above code, the transform opts are structured that at the lowest level, a transform is applied to a src ibgib (DTO at this point), thus making the transform itself memoized and part of the resultant (Merkle) graph. So new ibgibs are created and added to the Merkle graph, including the "dna" (the transform opts) which are themselves memoized into the ibgib data structure. These transforms are essentially "commands", but we will not be storing our render commands at this low of a level. I'm just pointing out that they are memoized as needed, similar to event sourcing events, but storing not only events but their output, and each artifact is cryptographically content-addressed for those added benefits.
## @ibgib/core-gib witness layer
With that data-only aspect in mind, I incorporated functionality via the concept of a "witness". This is done via a single binding point interface, with the single function called `witness`. This is to anthropomorphize these ibgibs, as the act of "witnessing" is actually what functions do. They do not state fact, they can be "fooled" just like humans, they can be corrupted, etc. Ibgib is focused on this humble belief-based approach to data. Here is the *.d.mts of `witness-types.d.mts`:
```
import { IbGibData_V1, IbGibRel8ns_V1, IbGib_V1 } from '@ibgib/ts-gib/dist/V1/index.mjs';
import { IbGib, IbGibWithDataAndRel8ns, IbGibAddr } from '@ibgib/ts-gib/dist/types.mjs';
import { ErrorIbGib_V1 } from '../common/error/error-types.mjs';
/**
 * basic witness function type.
 *
 * Note atow (11/2023) I have this defined in two places: here and in the
 * {@link Witness} declaration. That declaration does not use this type
 * signature, which maybe I should change it to do so...so many things to do
 * though.
 */
export type WitnessFn<TIbGibIn extends IbGib_V1 = IbGib_V1, TIbGibOut extends IbGib_V1 = IbGib_V1> = (arg: TIbGibIn) => Promise<TIbGibOut | undefined>;
/**
 * A witness is a lot like an ibGib analog for a function.
 * Now remember in FP, a function takes a single arg, and
 * multiple args are actually reified single-arg functions
 * that are curried.
 *
 * So an ibGib witness is an ibGib that has a single function: `witness`.
 * What can it witness? Another ibGib. What does it return? An ibGib.
 *
 * ## notes
 *
 * * I'm not smart enough to descend this from just IbGib, so I'm doing
 *   IbGibwithDataAndRel8ns. This is more of a convenience anyway.
 *
 * * atow (11/2023) I have this defined the witness fn shape in two places: here
 *   and in the {@link WitnessFn} declaration. This interface does not use that
 *   type signature, so there is a possibility of getting out of sync.
 */
export interface Witness<TIbGibIn extends IbGib_V1<any, any>, TIbGibOut extends IbGib_V1<any, any>, TData extends WitnessData_V1 = WitnessData_V1, TRel8ns extends WitnessRel8ns_V1 = WitnessRel8ns_V1> extends IbGib_V1<TData, TRel8ns> {
    witness(arg: TIbGibIn): Promise<TIbGibOut | undefined>;
}
/**
 * Convenient non-generic version of the {@link Witness} interface.
 *
 * It's not quite `any` all the way, as witness arg and results are both typed
 * to `IbGib_V1`.
 */
export interface WitnessAny extends Witness<IbGib_V1, IbGib_V1, any, any> {
}
/**
 * Data that corresponds to all witnesses being implemented in V1.
 *
 * This should be expanded only sparingly, and all properties should be optional.
 */
export interface WitnessData_V1 extends IbGibData_V1 {
    /**
     * @optional string for tracking version control of witness.
     */
    version?: string;
    /**
     * @optional Name for the witness.
     */
    name?: string;
    /**
     * @optional classname of the witness.
     */
    classname?: string;
    /**
     * @optional description of the witness.
     */
    description?: string;
    /**
     * @optional
     *
     * If true, then this will allow primitive args when validating incoming
     * args.
     *
     * so if you want a witness to be able to witness, e.g., "16816^gib", then
     * set this to truthy. Otherwise, this will flag this as a validation error.
     */
    allowPrimitiveArgs?: boolean;
    /**
     * If true, any calls to `witness` should have the opt and result ibGibs
     * persisted, regardless of what the actual opt/result is. This is
     * ultimately up to the witness itself though.
     *
     * This is like providing a persistent logging feature for the witness
     * itself, as opposed to ephemeral logging via {@link trace}.
     *
     * ## implementation
     *
     * In the case of V1, WitnessBase_V1<...> has an empty function
     */
    persistOptsAndResultIbGibs?: boolean;
    /**
     * @optional
     *
     * "Should" be a unique identifier for the witness.
     */
    uuid?: string;
    /**
     * @optional configuration for `witness` call.
     *
     * If true, then this space will not catch an error in `witnessImpl`
     * function.
     *
     * ## notes
     *
     * Descendants of Witness who don't override the base `witness` function
     * (but rather override `witnessImpl` as expected) don't need to check
     * for this explicitly, since it is referenced in the base `witness`
     * function implementation.
     */
    catchAllErrors?: boolean;
    /**
     * @optional arg for verbose logging.
     *
     * Space implementations can check this value directly, or if
     * they descend from `WitnessBase`, then there is a property
     * that safely navigates this value.
     */
    trace?: boolean;
}
/**
 * marker interface atm
 */
export interface WitnessRel8ns_V1 extends IbGibRel8ns_V1 {
}
/**
 * This interface simply types our data and rel8ns to V1 style.
 */
export interface Witness_V1<TDataIn extends any, TRel8nsIn extends IbGibRel8ns_V1, TIbGibIn extends IbGib_V1<TDataIn, TRel8nsIn>, TDataOut extends any, TRel8nsOut extends IbGibRel8ns_V1, TIbGibOut extends IbGib_V1<TDataOut, TRel8nsOut> | ErrorIbGib_V1, TData extends WitnessData_V1 = any, TRel8ns extends WitnessRel8ns_V1 = WitnessRel8ns_V1> extends Witness<TIbGibIn, TIbGibOut, TData, TRel8ns> {
}
/**
 * Base information for cmd with optional modifiers to interact with a witness.
 *
 * Note that it is not necessary for a witness to listen to these types of
 * ibgibs, this is just convenient plumbing for those who wish to listen to
 * command-style ibgibs.
 */
export interface WitnessArgData {
    /**
     * Not really in use atm, but will use in the future.
     */
    version?: string;
    /**
     * Addrs of ibgibs that this witness arg is wrapping.
     */
    ibGibAddrs?: IbGibAddr[];
}
export interface WitnessArgRel8ns extends IbGibRel8ns_V1 {
}
/**
 * Shape of an arg wrapper ibgib with a couple common-use properties in data and
 * the object class itself.
 *
 * This is meant to serve as a base class for others, most notably command arg
 * ibgibs, when using witness ibgibs.
 */
export interface WitnessArgIbGib<TIbGib extends IbGib, TArgData extends WitnessArgData, TArgRel8ns extends WitnessArgRel8ns> extends IbGib_V1<TArgData, TArgRel8ns> {
    /**
     * ibgib(s) that this arg wraps.
     *
     * So when you pass in an arg to a witness, this is used often as the
     * payload. For example, a space witness acts like a repo with puts and
     * gets command args. This will contain the references to the ibgibs being
     * put into the space.
     */
    ibGibs?: TIbGib[];
}
export interface WitnessResultData {
    /**
     * The address of the options ibGib that corresponds this space result.
     *
     * So if you called `space.witness(opts)` which produced this result, this is
     * `getIbGibAddr({ibGib: opts})`.
     *
     * Perhaps I should have this as a rel8n on the actual result ibGib instead of here
     * in the data.
     */
    optsAddr: IbGibAddr;
    /**
     * true if the operation executed successfully.
     *
     * If this is a `canGet` or `canPut` `cmd`, this does NOT indicate if you
     * can or can't. For that, see the `can` property of this interface.
     */
    success?: boolean;
    /**
     * Any error messages go here. If this is populated, then the `success`
     * *should* be false (but obvious the interface can't guarantee implementation).
     */
    errors?: string[];
    /**
     * Any warnings that don't cause this operation to explicitly fail, i.e. `errors`
     * is falsy/empty.
     */
    warnings?: string[];
    /**
     * If getting address(es), they will be here.
     */
    addrs?: IbGibAddr[];
    /**
     * Addresses for ibGibs which had errors.
     */
    addrsErrored?: IbGibAddr[];
}
export interface WitnessResultRel8ns extends IbGibRel8ns_V1 {
}
/**
 * Optional witness result ibgib interface.
 *
 * You do NOT have to use this class when returning results for witness ibgibs.
 * This is provided as convenience plumbing for when you do want a standard-ish
 * special result ibgib.
 */
export interface WitnessResultIbGib<TIbGib extends IbGib, TResultData extends WitnessResultData, TResultRel8ns extends WitnessResultRel8ns> extends IbGibWithDataAndRel8ns<TResultData, TResultRel8ns> {
    /**
     * Reference to list of result ibgibs from witness operation.
     *
     * ## notes
     *
     * * this does not have to be the only property on the concrete result
     *   ibgib. From my experience so far, this is convenient for when there is
     *   one and only one obvious result from a witness operation, like a get
     *   command that returns a list of ibgibs.
     */
    ibGibs?: TIbGib[];
}
//# sourceMappingURL=witness-types.d.mts.map
```
## @ibgib/core-gib cmd-based witnesses
Becaue of perf questions, I'm not enforcing that we use my existing witness plumbing that uses command routing. Every layer has try..catch blocks, which are slow (but I use them to make it easier to locate errors in such a large system). Also they have been designed for aspect-oriented programming (the single `witness` function enables us a good injection point) and there is unnecessary (with regards to rendering perf) interstitial code. But our current command code is informed by these shapes:
```
export interface WitnessCmdData<TCmds, TCmdModifiers> extends WitnessArgData {
    cmd: TCmds | string;
    cmdModifiers?: (TCmdModifiers | string)[];
}

export interface WitnessCmdRel8ns extends IbGibRel8ns_V1 {
}

export interface WitnessCmdIbGib<
    TIbGib extends IbGib,
    TCmds, TCmdModifiers,
    TCmdData extends WitnessCmdData<TCmds, TCmdModifiers>,
    TCmdRel8ns extends WitnessCmdRel8ns,
> extends WitnessArgIbGib<TIbGib, TCmdData, TCmdRel8ns> {
}
```
* remember
  * `ib` schemas are per use case, so you decide what is efficient and required.
  * we don't have to record every command. It will ultimately be up to the agent which data to store in ibgib, so for the commands, we can just have regular non-ibgib classes. But we must have just primitive data (no references to other objects) for when we *do* actually want to store them. (It may turn out that we want to store transactions of an array of commands as a single unit of work, say.)
* Don't worry about error handling at the moment. Just throw an error, and any error you throw should end with a unique uuid, e.g. `throw new Error('some error (E: 6548f5560b07137bd683cb713a4fcc24)';`
# Agents and "Version Control" API Sketch for Agents
* Agents right now entirely LLM-based. So we can think of agents as being coded entities but with natural language, i.e., strings.
  * We can think of "version controlling" chat histories (forking/branching/merging/etc) as evolving agents.
* Need to expose a limited API of ibgib for "memory" for the agents for them to "take notes".
* Also the agent can save renderables' states for use by other agents (conceivably).
* Possible commands: `createBranch`, `checkout`, `applyChange`, `mergeBranch`, `rebaseBranch`, `discardChanges`, `viewHistory`, `diff`, `tag`, `listBranches`, `deleteBranch`, `addMetadata`, `addComment`
  * These should be implemented as base command scope and command modifier, e.g. cmd: branch, modifier: create.
  * Commands should be hierarchical and ultimately easily memoizable in the ibgib `data` property as needed.
* We should be able to use the same API to develop agents themselves, e.g., fork an agent's instructions ("chat") history, merge, etc. to create "new" agents.
# Current goals & options
We're still working towards our initial hello world of an agent-backed, ibgib integrated, events-hooked-up single button that does something. We have the rendering sketched and a command-based API. The rendering is sync, our business logic is async.  I'm not quite sure what to tackle next. Here are some options I think, but there may be something else. We need to choose a concrete, incremental goal.

* Event handling: I have almost no idea how we want to begin incorporating events. I have ideas but not much more.
* Agent witnesses: It may be time to tackle agents before events, and then we can think about
  * Gemini API-based initially using function calling. The Gemini API uses OpenAPI for structured chats basically.
* Ibgib integration: We will need to add ibgib-domain DSL to the existing API which only has a couple render commands.

So please, do *not* generate any code at the moment. Let's talk some more to be on the same page and then we will build up the requirements first with just the interfaces, and *after that* we'll add jsdocs, and *after all of that* we'll do implementations. This should be our general workflow for creating a high-quality code artifact for this highly constrained context window.
