import { delay, extractErrorMsg, pickRandom_Letters, pretty } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs'; import { UUID_REGEXP } from '@ibgib/helper-gib/dist/constants.mjs'; import { IbGibAddr, TransformResult } from '@ibgib/ts-gib/dist/types.mjs'; import { IbGib_V1, } from '@ibgib/ts-gib/dist/V1/types.mjs'; import { rel8 } from '@ibgib/ts-gib/dist/V1/transforms/rel8.mjs'; import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs'; import { GIB, IB, ROOT, ROOT_ADDR } from '@ibgib/ts-gib/dist/V1/constants.mjs'; import { mut8 } from '@ibgib/ts-gib/dist/V1/transforms/mut8.mjs'; import { getGibInfo } from '@ibgib/ts-gib/dist/V1/transforms/transform-helper.mjs'; import { WitnessData_V1, WitnessRel8ns_V1, } from '@ibgib/core-gib/dist/witness/witness-types.mjs'; import { CommentIbGib_V1 } from '@ibgib/core-gib/dist/common/comment/comment-types.mjs'; import { createCommentIbGib, isComment, parseCommentIb } from '@ibgib/core-gib/dist/common/comment/comment-helper.mjs'; import { LightWitnessBase_V1 } from '@ibgib/core-gib/dist/witness/light-witness-base-v1.mjs'; import { SpaceId } from '@ibgib/core-gib/dist/witness/space/space-types.mjs'; import { IbGibSpaceAny } from '@ibgib/core-gib/dist/witness/space/space-base-v1.mjs'; import { MetaspaceService } from '@ibgib/core-gib/dist/witness/space/metaspace/metaspace-types.mjs'; import { getTjpAddr } from '@ibgib/core-gib/dist/common/other/ibgib-helper.mjs'; import { fnObs } from '@ibgib/core-gib/dist/common/pubsub/observer/observer-helper.mjs'; import { execInSpaceWithLocking, } from '@ibgib/core-gib/dist/witness/space/space-helper.mjs'; import { GLOBAL_LOG_A_LOT } from '../../constants.mjs'; import { AGENT_ATOM, AGENT_DESC_REGEXP, AGENT_NAME_REGEXP, DEFAULT_DESCRIPTION_AGENT, DEFAULT_NAME_AGENT, DEFAULT_UUID_AGENT, FUNCTION_CALL_REQUEST_COMMENT_TAG, TextSource, } from './agent-constants.mjs'; import { APIFunctionInfo } from '../../api/api-types.mjs'; import { getAllFunctionInfos, } from '../../api/api-index.mjs'; import { FUNCTION_INFO_REL8N_NAME, FunctionInfoIbGib_V1, createFunctionInfoIbGib } from './function-info/function-info-one-file.mjs'; import { GeminiModel } from './gemini/gemini-constants.mjs'; import { PromptAPIResult_Gemini } from './gemini/gemini-types.mjs'; import { getGlobalMetaspace_waitIfNeeded } from '../../helpers.mjs'; import { getAddlMetadataTextForAgentText, parseAddlMetadataTextForAgentText, taggifyForPrompt, updateAgentIndex } from './agent-helpers.mjs'; import { getAgentsSvc } from './agents-service-v1.mjs'; import { LiveProxyIbGib } from '../live-proxy-ibgib/live-proxy-ibgib-one-file.mjs'; import { AddTextsOpts, AgentAPI, AgentModel, AgentWitnessData_V1, AgentWitnessIbGib_V1, AgentWitnessRel8ns_V1, PromptAPIResult, PromptInfo, PromptOneOffOpts, SetActiveContextOpts } from './agent-types.mjs'; const logalot = GLOBAL_LOG_A_LOT; /** * @extends LightWitnessBase_V1 * * ## notes * * This is far from complete. I don't know about performance, but it feels like * what we need to add: * * * initialization * * an agent should have a reference to the ibgib metaspace, as well as the agent's working local space. * * if no local space exists yet, one should be created. * * we will have to create an index ("special ibgib") that tracks our agents */ export abstract class AgentWitness_V1 extends LightWitnessBase_V1 // extends WitnessWithContextBase_V1< // IbGibData_V1, IbGibRel8ns_V1, IbGib_V1, // options // IbGibData_V1, IbGibRel8ns_V1, IbGib_V1, // results // AgentWitnessData_V1, // AgentWitnessRel8ns_V1 // > implements AgentWitnessIbGib_V1 { protected lc: string = `[${AgentWitness_V1.name}]`; protected availableFunctions: Map = new Map(); protected commentIbGibs_system: CommentIbGib_V1[] = []; protected commentIbGibs_chat: CommentIbGib_V1[] = []; protected _metaspace: MetaspaceService | undefined; // /** // * public helper to set the metaspace // * @param metaspace // */ // setMetaspace(metaspace: MetaspaceService): void { // if (this._metaspace) { throw new Error(`(UNEXPECTED) this.metaspace already set? (E: 60062ee8c69e773b96f8ddc38f227b25)`); } // this._metaspace = metaspace; // } /** * provider function for api key. */ fnGetAPIKey: () => Promise = () => { return Promise.reject(`not implemented. you need to set this after initializing the agent. (E: a14cbe903d08821126bbbb1d063d9825)`); } /** * convenience getter for this.data.name */ get name(): string { return this.data?.name ?? ''; } /** * convenience getter for this.data.name */ get uuid(): string { return this.data?.uuid ?? ''; } protected _genAI: TAPIModel | undefined; /** * @protected * @property transientDecompressionPromptText - A temporary string that is * added to the prompt on the next LLM call ONLY, i.e. for a single prompt. * * This should NOT be part of any text history, but is rather a temporary * way of including text in a chat in a one-off manner. * * ## driving use case * * We're using this right now in relation with Function Call Requests from * this agent's model. We don't want to spam the context window with the * details of these function calls, but sometimes the model needs the * results of these functions. */ protected transientDecompressionPromptText: string | undefined; /** * @property fnOutputText - A property on the AgentWitness_V1 that provides a way to output text. * * Default implementation to do nothing. This is to avoid a null pointer. * ATOW (01/2025) This is a hack right now to get feedback from the agent that is not a function call. */ fnOutputText: (textResponse: string) => void = (tr) => { console.log(`${this.lc}[fnOutputText] this hack does nothing by default. set this to do something when text is returned by the model LLM. textResponse: ${tr} (W: 3007e5d69dafdd7e42157fa7370d7f25)`) } // protected instanceId: string; /** * we need this agent witness to basically update as a singleton. enforcing * singleton instances of agents is difficult even just locally, but * impossible when we consider that we may have multiple tabs/apps sharing * some space. * * So instead, we'll try having multiple witness instances wrapping the * proxy. */ protected ibGibProxy: LiveProxyIbGib | undefined; constructor(initialData?: AgentWitnessData_V1, initialRel8ns?: AgentWitnessRel8ns_V1) { super(initialData, initialRel8ns); this.instanceId = pickRandom_Letters({ count: 32 }); } protected override async validateThis(): Promise { const lc = `${this.lc}[${this.validateThis.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: 641e211473fc54d5a9ebb4ea44944d25)`); } const errors = await super.validateThis(); // todo: refactor this into a non-class-method validation helper function for data if (this.data) { if (!this.data?.subSpaceId) { errors.push(`agent.data.subSpaceId falsy (E: d4ea57fd6b1c5cbc1e18a084de2f7725)`) } if (!this.data?.subSpaceId.match(UUID_REGEXP)) { errors.push(`agent.data.subSpaceId must match regexp: ${UUID_REGEXP}. (E: f381eff533aa3f4713316f93f7f1dc25)`); } if (this.data.name) { if (!this.data.name.match(AGENT_NAME_REGEXP)) { errors.push(`agent name must match regexp: ${AGENT_NAME_REGEXP}`); } } else { errors.push(`agent name falsy (E: 1351325107e5ac5ea6b7b789b73de125)`); } if (this.data.description) { if (!this.data.description.match(AGENT_DESC_REGEXP)) { errors.push(`agent description must match regexp: ${AGENT_DESC_REGEXP}`); } } // todo: add validation for available functions // todo: add other validation // todo: break out data validation and rel8ns validation into separate functions } else { errors.push(`this.data falsy (E: f5f151e11b12cad814b37ecad7c74a25)`) } return errors; } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } protected override async initialize(): Promise { const lc = `${this.lc}[${this.initialize.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: ce38519ac4bef52844a558fe31cda825)`); } await super.initialize(); // I'm not sure how to handle metaspace init...for now, it is up to // a factory function this._metaspace = await getGlobalMetaspace_waitIfNeeded(); const ibGibProxy = new LiveProxyIbGib(); await ibGibProxy.initialized; if (!ibGibProxy.contextUpdated$) { throw new Error(`(UNEXPECTED) ibGibProxy.contextUpdated$ falsy? (E: e8840870af18091e6e4d52490ddca825)`); } ibGibProxy.contextUpdated$.subscribe(fnObs({ next: async (nextIbGib) => { // new agent dto const lcNext = `${lc}[ibGibProxy.contextUpdated$][next]`; if (logalot) { console.log(`${lcNext} nextIbGib: ${pretty(nextIbGib)} (I: 96c846357b94fda2cefa615aed88c325)`); } await this.handleContextUpdated(); }, error: async (e) => { debugger; // error in component.ibGibProxy.contextUpdated$ observable? console.error(`${lc}[ibGibProxy.contextUpdated$][error] what up? ${extractErrorMsg(e)}`); }, })); // while (!this._metaspace) { // console.log(`${lc} this.metaspace falsy for instance ${this.instanceId} (W: 3a89c85b6bb34c1c53a92ded9f66a925)`); // await delay(30); // } // this will populate the properties that are driven by data/rel8ns, // e.g., availableFunctions, commentIbGibs_system/chat, etc. if (this.ib && this.gib && this.data) { const dto = this.toIbGibDto(); await this.loadIbGibDto(dto); const space = await this._metaspace.getLocalUserSpace({ localSpaceId: dto.data?.superSpaceId, }); if (!space) { throw new Error(`couldn't get local space with agent.data.superSpaceId (${dto.data?.superSpaceId})? (E: 0fc1b8a8ffa26f39d8a15218ec818725)`); } await this.ibGibProxy?.setWrappedIbGib({ ibGib: dto, space }); } else { // this happens when you just new up empty ctor, e.g. new // AgentWitnessGemini_V1(), so the caller will be responsible // for calling loadIbGibDto if (logalot) { console.log(`${lc} agent is "empty". not loading dto at this time, caller is responsible for calling loadIbGibDto (I: 06dba19d74d5695fe7d1957889283725)`); } } } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } /** * loads the new context (agent witness ibgib) if it's different than what * is already loaded in this agent instance. */ protected async handleContextUpdated(): Promise { const lc = `${this.lc}[${this.handleContextUpdated.name}]`; try { if (!this.ibGibProxy) { throw new Error(`(UNEXPECTED) this.ibGibProxy falsy but we're within handleContextUpdated handler? (E: a88fa85e8f27bd7f72c207389ebfbc25)`); } if (!this.ibGibProxy.ibGib) { throw new Error(`(UNEXPECTED) this.ibGibProxy.ibGib falsy but we're within the handleContextUpdated handler? (E: ca60b8f0a60a3e3d652c3018102d1825)`); } debugger; // want to see this early runs if (!this.gib) { debugger; // error state? this.gib falsy? want to see this console.error(`${lc} not sure if error, but this.gib is falsy when we get a new context down the pipeline? (E: 8501d8aee4db7b8461587f38f0bbb125)`); } if (getIbGibAddr({ ibGib: this }) !== getIbGibAddr({ ibGib: this.ibGibProxy.ibGib })) { const nProxy = this.ibGibProxy!.ibGib!.data!.n ?? 0; const nThis = this.data?.n ?? 0; if (nProxy <= nThis) { debugger; // no change but different address? console.error(`${lc} issue with "new" proxy context and the current this.data.n with different addresses. divergent timelines? (E: 15c698eb4488cbae2db0e778fec15825)`) } // we have not loaded the proxy's new ibGib await this.loadIbGibDto(this.ibGibProxy.ibGib); await delay(200); // hack to reduce the possibility of the proxy not updating elsewhere } else { // already loaded this ibGib, i.e., context updated is not new console.error(`${lc} ibgibProxy new context is no different than current. this is NOT necessarily an error, I just want to see this when it happens. nothing is being done here, we're just not loading the "new" ibgib dto. (E: eff668b2e858ded448366d58e4dd6825)`) } } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } } // protected override async handleContextUpdate({ update }: { update: IbGibTimelineUpdateInfo; }): Promise { // const lc = `${this.lc}[${this.handleContextUpdate.name}]`; // try { // if (logalot) { console.log(`${lc} starting... (I: 86abc8464278b3dcb8d078b830853825)`); } // // super.handleContextUpdate // to nav into the super function // super.handleContextUpdate // debugger; // just want to see if this is hit on updates? // } catch (error) { // console.error(`${lc} ${extractErrorMsg(error)}`); // throw error; // } finally { // if (logalot) { console.log(`${lc} complete.`); } // } // } /** * each agent has access to its own local ibgib space for its internal contents. this gets the local * space that corresponds to the agent's spaceId, or if it does not already * exist, it creates the space (and updates the bootstrap). * @returns space object that the agent will use as its local space. * * todo: refactor this with options to possibly return other spaces as well (metaspace, subSpace, superSpace). if options is undefined, do existing behavior of getting only the subspace */ protected async getSubSpace(): Promise { const lc = `[${this.getSubSpace.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: a2ec2c5e8f5fdf95c8c571b2b1b9e925)`); } // await this.initialized; // what if this fn is called within initialize? const { _metaspace: metaspace } = this; if (!metaspace) { throw new Error(`(UNEXPECTED) this.metaspace falsy? after init this.metaspace is expected to be set. (E: 4519a8feaf4489389be679598f274425)`); } let space = await metaspace.getLocalUserSpace({ localSpaceId: this.data!.subSpaceId, }); if (!space) { const validationErrors = await this.validateThis(); if (validationErrors.length > 0) { throw new Error(`this agent has validation errors: ${validationErrors} (E: 9a0c6125cf89789ddb1610cf5a752225)`); } if (!this.data) { throw new Error(`(UNEXPECTED) this.data falsy? (E: 06e30e1d53fd4935333c7cf7e10db425)`); } space = await metaspace.createLocalSpaceAndUpdateBootstrap({ allowCancel: false, zeroSpace: metaspace.zeroSpace, spaceName: this.name + this.uuid.substring(0, 5), // arbitrary createBootstrap: false, }); } if (!space) { throw new Error(`(UNEXPECTED) space still falsy after creating locally? (E: 037ef1e6f54a675d0367227fdae16525)`); } return space; } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } /** * helper that retrieves this agent's local space given by * this.data.superSpaceId */ protected async getSuperSpace(): Promise { const lc = `[${this.getSuperSpace.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: 55fba9bcaf9539cc2219e03cda200525)`); } // await this.initialized; // what if this fn is called within initialize? const { _metaspace: metaspace } = this; if (!metaspace) { throw new Error(`(UNEXPECTED) this.metaspace falsy? after init this.metaspace is expected to be set. (E: 107f670c99832c573a7e5de18d58c325)`); } if (!this.data) { throw new Error(`(UNEXPECTED) this.data falsy? (E: a4341b8753678fcba7877b6319769b25)`); } if (!this.data.superSpaceId) { throw new Error(`(UNEXPECTED) this.data.superSpaceId falsy? (E: 0429fe44df5b56bf2ac67d38cde56325)`); } const space = await metaspace.getLocalUserSpace({ localSpaceId: this.data!.superSpaceId, }); if (!space) { throw new Error(`(UNEXPECTED) superSpace falsy? this.data.superSpaceId: ${this.data.superSpaceId} (E: fd9435212e8b87e4b60034bf9e797f25)`); } return space; } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } abstract addTexts(opts: AddTextsOpts): Promise; /** * quick and dirty, does not */ abstract promptOneOff(opts: PromptOneOffOpts): Promise; protected _activeContext: LiveProxyIbGib | undefined = undefined; /** * Updates the agent.data to have a soft link to an ibgib's timeline (via * its tjp addr) that defines the agent's current context. * * This is kinda like describing what "directory" the agent is "in", though * remember, ibgib's contain both intrinsic meaning (like a file) and * extrinsic meaning (like a folder/dir). * * @see {@link SetActiveContextOpts} */ async setActiveContext(opts: SetActiveContextOpts): Promise { const lc = `${this.lc}[${this.setActiveContext.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: d8646db1a33f980a88a2296644cbdf26)`); } const { contextIbGib, loggingInfo } = opts; // #region init/validation if (!this.data) { throw new Error(`(UNEXPECTED) this.data falsy? (E: ab5377fda6bc324638722d9832bab925)`); } if (!contextIbGib) { throw new Error(`(UNEXPECTED) contextIbGib falsy? (E: 694525e512cfdd12c14e1213dbaab925)`); } if (!contextIbGib.data) { throw new Error(`(UNEXPECTED) contextIbGib.data falsy? (E: f337252a23c822bcbd76144540050825)`); } const contextIbGibAddr = getIbGibAddr({ ibGib: contextIbGib }); const contextTjpAddr = getTjpAddr({ ibGib: contextIbGib }); if (!contextTjpAddr) { throw new Error(`(UNEXPECTED) contextTjpAddr falsy? contextIbGib (${contextIbGibAddr}) is expected to be a living ibgib with a tjp address (i.e. a timeline). This means it should have a rel8ns.tjp or data.isTjp should be truthy. (E: d756d1e862d2cccfed4a5807737e9a25)`); } // #endregion init/validation // update this agent's internals if applicable if (this.data?.['@currentContextTjpAddr'] === contextTjpAddr) { if (logalot) { console.log(`${lc} this.data.@currentContextTjpAddr already set to the same addr. (I: 9f0d58ec65eee3ca2aec528f1328c325)`); } // return; /* <<<< returns early */ } else { // we're guaranteed to have a different context await this.mut8Self({ dataToAddOrPatch: { "@currentContextTjpAddr": contextTjpAddr, } }); } // subscribe to updates to context if (!this._activeContext) { // create the proxy itself const proxy = new LiveProxyIbGib(); await proxy.initialized; // subscribe to context updates - these are just when newer // versions come down the pipe, which i don't think we're too // interested in at the moment. we're more interested in the new // children const lcContextUpdated = `${lc}[contextUpdated]`; if (!proxy.contextUpdated$) { throw new Error(`(UNEXPECTED) proxy.contextUpdated$ falsy? (E: 868c5e3bf025baa6d1ae727ab1160c25)`); } proxy.contextUpdated$.subscribe(fnObs({ next: async (nextIbGib) => { console.log(`${lcContextUpdated}[next] nextIbGib: ${pretty(nextIbGib)}`); }, complete: async () => { debugger; // complete? in agent subscribe context update throw new Error(`${lcContextUpdated}[complete] (UNEXPECTED) agent active context subscription complete? i honestly don't know if this hits. (E: 786d26ab23e4313db7ae2e08c70a7425)`); }, error: async (e) => { debugger; // error in agent subscribe context update console.error(`${lcContextUpdated}[error] ${extractErrorMsg(e)}`); }, })); // subscribe to new children to add text/pics to the agent's // awareness (context window/dynamic prompt when next prompt // occurs) and act accordingly const lcNewContextChild = `${lc}[newContextChild]`; if (!proxy.newContextChild$) { throw new Error(`(UNEXPECTED) proxy.newContextChild$ falsy? (E: 123cf5624c3d2a6308aafc0c41f8a925)`); } proxy.newContextChild$.subscribe(fnObs({ next: async (newChild) => { // await delay(500); // magic delay temporary hack due to answering before the new child propagates to other event listeners on the timeline. namely, when user inputs text, it adds to the context ibgib. then this agent hears that and then imediately adds to the context ibgib before the chronologys has a chance to answer. shouldn't matter, but what can ya do on no fing funding. looks like this delay still happens console.log(`${lcNewContextChild}[next] newChild: ${pretty(newChild)}`); if (!newChild.data) { throw new Error(`newChild.data falsy? (E: a654134a2695c176971b0cd1426ced25)`); } if (isComment({ ibGib: newChild })) { /** * idempotent, otherwise we get repeats */ const alreadyDoneCommentTjpAddrs = this.commentIbGibs_chat.map(x => { return getTjpAddr({ ibGib: x }) ?? getIbGibAddr({ ibGib: x }); }); const newChildTjpAddr = getTjpAddr({ ibGib: newChild }) ?? getIbGibAddr({ ibGib: newChild }); if (alreadyDoneCommentTjpAddrs.includes(newChildTjpAddr)) { console.log(`${lc} already did this comment (${newChildTjpAddr}) (I: bce18642bb9babeb553e6188d7ae2825)`); return; /* <<<< returns early */ } const { safeIbCommentMetadataText } = parseCommentIb({ ib: newChild.ib }); const { textSrc, } = parseAddlMetadataTextForAgentText({ addlMetadataText: safeIbCommentMetadataText ?? '', ifError: 'warn', }); const isSystem = newChild.data.text.startsWith('system: '); await this.addTexts({ infos: [ { textSrc: textSrc ?? TextSource.UNKNOWN, commentIbGib: newChild as CommentIbGib_V1, isSystem, } ] }); // trigger doPrompt? for now we'll just do it when // the text comes directly from a human, but in the // future we either should pass this through a // smaller model or have some defined triggers like // if the text contains "@[agent.data.name]" if (textSrc === TextSource.HUMAN) { if (!isSystem) { await this.doPrompt(); } else { console.log(`${lc} system prompt has been modified. (I: d21c26f0a3fa12e2a874925882f39825)`); } } } else { console.warn(`${lcNewContextChild} non-comment newChild. probably not a warning proper, but at this stage I'm only expecting comments ignoring... (W: e664834af4791ddcde19bfd4f7063e25)`); } }, complete: async () => { debugger; // complete? in agent newContextChild throw new Error(`${lcNewContextChild}[complete] (UNEXPECTED) agent active context subscription complete? i honestly don't know if this hits. (E: 88202d1f7554330d3dfca0237c2d6525)`); }, error: async (e) => { debugger; // error in agent newContextChild console.error(`${lcNewContextChild}[error] ${extractErrorMsg(e)}`); }, })); // set the backing property this._activeContext = proxy; // is this enough if the proxy changes its internal wrapped ibgib? } if (!this._activeContext) { throw new Error(`(UNEXPECTED) this._activeContext still falsy after we just initialized it? (E: 584f2f672b9ae7c979dde693eafb7a25)`); } await this._activeContext.setWrappedIbGib({ ibGib: contextIbGib }); } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } // protected async subscribeToContextUpdates(): Promise { // const lc = `[${this.subscribeToContextUpdates.name}]`; // try { // if (logalot) { console.log(`${lc} starting... (I: 871a18a5a0abd7e1177fde3270c12425)`); } // } catch (error) { // console.error(`${lc} ${extractErrorMsg(error)}`); // throw error; // } finally { // if (logalot) { console.log(`${lc} complete.`); } // } // } async updateName({ name }: { name: string }): Promise { const lc = `${this.lc}[${this.updateName.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: 0cc02edeb5e8eb7babbf373924fffd26)`); } if (name === this.name) { console.warn(`${lc} agent (${this.uuid}) already is named ${name}. returning early... (W: aa2527831ed82377b881e7d8de9ca526)`); return; /* <<<< returns early */ } else { if (AGENT_NAME_REGEXP.test(name)) { // we're guaranteed to have a different name await this.mut8Self({ dataToAddOrPatch: { name: name, } }); } else { throw new Error(`invalid name ${name}. must match regexp: ${AGENT_NAME_REGEXP.source} (E: 507a2bfc0aaa60bd7bb608d253239e25)`); } } } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } async updateAvailableFunctions({ availableFunctions, }: { availableFunctions: APIFunctionInfo[], }): Promise { const lc = `${this.lc}[${this.updateAvailableFunctions.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: 68f1bfa343925a657f8c0545b191c725)`); } // // first prune existing available functions... // await this.pruneDeprecatedAvailableFunctions_ifApplicable({ // dto: this.toIbGibDto() as AgentWitnessIbGib_V1, // metaspace: this._metaspace!, // superSpace: await this.getSuperSpace(), // }); const functionsToAdd: APIFunctionInfo[] = []; // const removedFunctions: string[] = []; // implement later as needed for (const incomingFn of availableFunctions) { if (!this.availableFunctions.has(incomingFn.nameOrId)) { const functionInfo = getAllFunctionInfos().get(incomingFn.nameOrId); if (functionInfo) { functionsToAdd.push(functionInfo); } else { console.error(`(UNEXPECTED) ${incomingFn} function not registered with getAllFunctionInfos()? skipping adding this function to the agent... (E: e860fe13c02677b274ac0e7df2587a25)`); continue; } } } if (functionsToAdd.length > 0) { console.log(`${lc} adding newly found functions to agent: ${functionsToAdd.map(x => x.nameOrId)}. (I: ab2b0ae6a62b3ced6a5f1e2e8cec4f25)`) await this.addAvailableFunctions(functionsToAdd); } } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } /** * I'm taking this out temporarily because it's non-trivial to figure out * where/when this should happen. We can just do the deprecated list and when * we go to populate the actual availble function infos, we check the * deprecated list and skip the ones that are deprecated. So in the agent's * dto ibgib (data.availableFunctions) the function name will still be * listed, but at runtime it will not be populated. */ // protected async pruneDeprecatedAvailableFunctions_ifApplicable({ // dto, // metaspace, // superSpace, // }: { // dto: AgentWitnessIbGib_V1, // metaspace: MetaspaceService, // superSpace: IbGibSpaceAny, // }): Promise { // const lc = `${this.lc}[${this.pruneDeprecatedAvailableFunctions_ifApplicable.name}]`; // try { // if (logalot) { console.log(`${lc} starting... (I: e6d5f13a3c4892f5c878239744a00825)`); } // if (!dto.data) { throw new Error(`(UNEXPECTED) this.data falsy? (E: 83c531982725611401ebe3ecc7d1c825)`); } // if (!dto.data.availableFunctionNameOrIds || dto.data.availableFunctionNameOrIds.length === 0) { // console.warn(`${lc} dto agent (${getIbGibAddr({ ibGib: dto })} has falsy/empty availableFunctionNameOrIds? maybe this is normal, but here writing this code, i think this seems unexpected. (W: 6a15d8e2fdc8e5d38a9045d39bb06825))`); // return; /* <<<< returns early */ // } // // deprecated, so modify the data and reload the dto // const deprecatedNameOrIds = dto.data.availableFunctionNameOrIds.filter(x => DeprecatedFunctionInfoNames.includes(x)); // if (deprecatedNameOrIds.length > 0) { // console.log(`${lc} deprecatedNameOrIds.length > 0, so we're going to mut8 this agent to exclude these.`); // const newAgentIbGibDto = await mut8Timeline({ // timeline: dto, // mut8Opts: { // dataToAddOrPatch: { // availableFunctionNameOrIds: // dto.data.availableFunctionNameOrIds.filter(x => !deprecatedNameOrIds.includes(x)), // } // }, // metaspace, // space: superSpace, // timelineIndexInfo: { // rel8nName: AGENT_REL8N_NAME, // type: dto.data.type, // }, // }); // await super.loadIbGibDto(newAgentIbGibDto); // } // } catch (error) { // console.error(`${lc} ${extractErrorMsg(error)}`); // throw error; // } finally { // if (logalot) { console.log(`${lc} complete.`); } // } // } /** * Performs the {@link mut8} transform on this agent and does extra * plumbing. */ protected async mut8Self({ dataToAddOrPatch, }: { dataToAddOrPatch: Partial, }): Promise { const lc = `${this.lc}[${this.mut8Self.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: f4618832a0fe07ff8c89cea889d20725)`); } // get lock on self const metaspace = this._metaspace; if (!metaspace) { throw new Error(`(UNEXPECTED) metaspace falsy? (E: 1bf3b72fef222e26789de2187d7c0926)`); } const superSpace = await metaspace.getLocalUserSpace({ lock: false, localSpaceId: this.data!.superSpaceId, }); if (!superSpace) { throw new Error(`couldn't get local superSpace given by this.data.superSpaceId (${this.data!.superSpaceId}) (E: b224117415e860bb3365709c9afd9b26)`); } const { tjpGib } = getGibInfo({ gib: this.gib }); if (!tjpGib) { throw new Error(`(UNEXPECTED) this.gib (${this.gib}) has no tjpGib? (E: 75b8dcb82b58d3b0db40b39149023e25)`); } await execInSpaceWithLocking({ fn: async () => { const agentsSvc = getAgentsSvc(); /** * always update at least in memory latest */ await agentsSvc.updateOrSetLatestAgent({ agent: this, throwIfNewerFound: false, }); const resMut8 = await mut8({ type: 'mut8', src: this.toIbGibDto(), dataToAddOrPatch, dna: true, nCounter: true, }) as TransformResult; await metaspace.persistTransformResult({ resTransform: resMut8, space: superSpace, }); const newAgentIbGib = resMut8.newIbGib; await this.loadIbGibDto(newAgentIbGib); await agentsSvc.updateOrSetLatestAgent({ agent: this, throwIfNewerFound: true, // we **just** created what should be the latest, so throw if this isn't the case }); await metaspace.registerNewIbGib({ ibGib: newAgentIbGib, space: superSpace, }); // update special index // this will do its own locking on the agent's type await updateAgentIndex({ newAgentIbGib, metaspace, space: superSpace, type: this.data!.type, }); }, scope: tjpGib, secondsValid: 60, maxDelayMs: 100, maxLockAttempts: Number.MAX_SAFE_INTEGER, // drive by seconds valid space: superSpace, callerInstanceId: this.instanceId, }); } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } /** * gets a list of api function infos available as tools to this agent, i.e., atow this are available for function calling in Gemini API. implementation is a WIP. * * @returns array of api function infos available as tools to this agent */ protected getAvailableFunctions(): readonly APIFunctionInfo[] { return Array.from(this.availableFunctions.values()); } protected getComments_System(): readonly CommentIbGib_V1[] { return [...this.commentIbGibs_system]; } protected getComments_Chat(): readonly CommentIbGib_V1[] { return [...this.commentIbGibs_chat]; } // abstract addAvailableFunctions(functionInfos: APIFunctionInfo[]): Promise; // override async addAvailableFunctions(functionInfos: APIFunctionInfo[]): Promise { async addAvailableFunctions(functionInfos: APIFunctionInfo[]): Promise { const lc = `[${this.addAvailableFunctions.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: 273525464ec540fc9cba565e608db925)`); } if (!functionInfos) { throw new Error(`(UNEXPECTED) functionInfos falsy? (E: 19cf2b7c9ee1115f0a3f7f62fbf77825)`); } if (functionInfos.length === 0) { console.warn(`${lc} functionInfos.length === 0? why is this being called with no functions? returning early without doing anything. (W: 86c6dc4fad3c1ddcac697d5636b1eb25)`); return; /* <<<< returns early */ } const agentsSvc = getAgentsSvc(); /** * always update at least in memory latest */ await agentsSvc.updateOrSetLatestAgent({ agent: this, throwIfNewerFound: false, }); functionInfos.forEach(x => { if (this.availableFunctions.has(x.nameOrId)) { console.warn(`${lc} this.availableFunctions already has an entry for ${x.nameOrId}. replacing anyway. (W: ef2372a27be67174cb3605eac2c85c25)`) } this.availableFunctions.set(x.nameOrId, x); }); await this.mut8Self({ dataToAddOrPatch: { availableFunctionNameOrIds: Array.from(this.availableFunctions.keys()), } }) } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } /** * Generates the prompt text for the LLM by concatenating system and chat * comments. This method is meant to be overridden by descendant classes if * needed. * * @returns A {@link TPromptInfo} object that will be used to prompt {@link _genAI} * @throws {Error} If there is an error parsing the addlMetadataText, or if no metaspace is set, or if there are no chat comment ibgibs. (why are we prompting?) */ abstract composePromptInfo(): Promise; /** * Calls the text-based API to get a response based on the given prompt. * This method is meant to be overridden by descendant classes if needed. * * @param {string} prompt - The text prompt to send to the API. * @returns {Promise} - The response from the API. * @throws {Error} If the api is not supported or if there is an error with the Gemini API call. */ protected abstract callTextAPI(info: PromptInfo): Promise; // #region doPrompt /** * Orchestrates the prompting of the agent, combining system and chat history, * and processing any function calling that is returned from the API. * This method is called via the witness function when it receives a ROOT input. * @returns {Promise} * @throws {Error} If there is an error getting the prompt text, calling the text API, or processing the response. */ async doPrompt(): Promise { const lc = `[${this.doPrompt.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: e7f1a7845cdf1bdce1f12d74c71a6425)`); } if (!this.data) { throw new Error(`(UNEXPECTED) this.data falsy? (E: 3c528e2376a2f2600b347b2c183a5225)`); } const promptTextInfo = await this.composePromptInfo(); if (logalot) { console.log(`${lc} promptTextInfo: ${pretty(promptTextInfo)} (I: b769b5f2a47623a99f78210994d90525)`); } const apiResult = await this.callTextAPI(promptTextInfo); if (logalot) { console.log(`${lc} api result:`, apiResult, `(I: e4d2c767eb3d6e38d83123d684a07425)`); } if ("text" in apiResult) { await this.doPrompt_text(apiResult.text); } else { const { functionCalls } = apiResult; // assert here that functionCalls is present await this.doPrompt_functionCalls({ functionCalls }); // will call recursively if applicable } } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } /** * handles text when there are no function calls * @param text string */ protected async doPrompt_text(text: string): Promise { const lc = `${this.lc}[${this.doPrompt_text.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: a2b9c21f3b2b058d355d16421e2b4425)`); } text ||= '[no text returned from the model]'; if (this.fnOutputText) { // this indeed is occasionally hit, including when an agent // hasn't actually been initialized and just text is returned. this.fnOutputText(text); } else { console.log(`${lc} no fnOutputText set. Text Response: ${text} (I: 5936517474b10b214257781a24b10425)`); } } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } /** * @protected * Handles the prompt function calls when there are one or more calls * requested by the LLM. * * * parses, validates, executes those function calls * * creates associated function info ibgibs * * creates Function Call Request comment ibgib and relates this to this agent * * recursively calls doPrompt if indicated by the LLM (if they want to be * reprompted after the function calls) * * @param {string[]} functionCalls are raw stringified JSON fn calls requested by the LLM */ protected async doPrompt_functionCalls({ functionCalls }: { functionCalls: string[] }): Promise { const lc = `${this.lc}[${this.doPrompt_functionCalls.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: a4f1b581517a9c2a9187e9f8f3519925)`); } if (functionCalls.length === 0) { throw new Error(`(UNEXPECTED) functionCalls.length is zero? (E: 6434b8515a435a59b331452b563f4425)`); } // we must first create the function info ibgibs, because the FCR // comment ibgib requires the ibgibs' addresses in both the // data.text and rel8ns const functionInfoIbGibs = await this.doPrompt_functionCalls_createFunctionInfoIbGibsAndExecFunctionCalls({ functionCalls }); await this.doPrompt_functionCalls_addFunctionCallRequestComment({ functionInfoIbGibs }); // if the model wants a reprompt on **ANY** of the functions, we set the "decompression" text here const repromptRequested = functionInfoIbGibs.some(x => { return !!x.data?.repromptWithResult && x.data.fnName !== 'tellUser'; }, ); if (repromptRequested) { const transientDecompressionPromptText = await this.doPrompt_functionCalls_getTransientDecompressionPromptForNextPrompt({ functionInfoIbGibs }); if (transientDecompressionPromptText.length > 0) { console.log(`${lc} transientDecompressionPromptText: ${transientDecompressionPromptText} (I: 11c11d544b7c8f8f8e7e5f7c1a9e3625)`); this.transientDecompressionPromptText = transientDecompressionPromptText; await this.rePrompt(); } else { console.warn(`${lc} (UNEXPECTED) there is no decompression text? (W: 6ce5da95c1c89a7ec48a00fbc1d11825)`) } } else { if (logalot) { console.log(`${lc} no repromptWithResult. returning early. (I: 9c0113c958c7404534291f6700325925)`); } } } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } protected async doPrompt_functionCalls_createFunctionInfoIbGibsAndExecFunctionCalls({ functionCalls, }: { functionCalls: string[], }): Promise { const lc = `${this.lc}[${this.doPrompt_functionCalls_createFunctionInfoIbGibsAndExecFunctionCalls.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: adc094bc98cb076a4aaf729f70adce25)`); } if (!this.data) { throw new Error(`(UNEXPECTED) this.data falsy? (E: d071328283b2cf880ff0ccd499c22b25)`); } if (!this.data.uuid) { throw new Error(`(UNEXPECTED) this.data.uuid falsy? (E: 8acd2f793e4b5a368dbcf1d8d1e6e825)`); } const metaspace = this._metaspace; if (!metaspace) { throw new Error(`(UNEXPECTED) metaspace falsy? (E: acf8a69e853eb8663f1dd0f79a823e25)`); } const functionInfoIbGibs: FunctionInfoIbGib_V1[] = []; for (const functionCallStr of functionCalls) { try { if (logalot) { console.log(`${lc} processing functionCall: ${functionCallStr} (I: a0c0b22b3c6b6c4b674a5a9c8c8b9e25)`); } let functionInfoIbGib: FunctionInfoIbGib_V1 | undefined = undefined; let functionCallParsedObj: any | undefined = undefined; let parseErrorMsg: string | undefined = undefined; let functionInfo: APIFunctionInfo | undefined = undefined; let functionArgs: any = undefined; let functionName: string = ''; // #region get functionCallParsedObj // can we turn this into a helper? This doesn't require this agent state, right? try { functionCallParsedObj = JSON.parse(functionCallStr); functionName = functionCallParsedObj?.name ?? ''; if (!functionName) { throw new Error(`functionName (functionCallParsedObj.name) falsy (E: e2397d675863a12b7609495b0007a025)`); } if (logalot) { console.log(`${lc} functionCallParsedObj:`, pretty(functionCallParsedObj), `(I: 750f097243b85644d111971e0f929c25)`); } functionInfo = getAllFunctionInfos().get(functionCallParsedObj.name); if (!functionInfo) { throw new Error(`no APIFunctionInfo found that matches functionName (${functionName}) (E: 9d36b6472ec5cdf95e4f498292449925)`); } // todo: validate function call json against the OpenAPI parameters defined in the api function functionArgs = functionCallParsedObj.args; // todo: validate args of function call if (logalot) { console.log(`${lc} valid params (I: a4d5c13c79a2405c8d20464b42083325)`) }; } catch (error: any) { parseErrorMsg = `${lc} invalid functionCallParsedObj. error: ${extractErrorMsg(error)}. functionCallStr: ${functionCallStr}. (E: 1687a24c5a3667d34689a09f8f753125)`; console.error(parseErrorMsg); functionCallParsedObj = undefined; } // #endregion get functionCallParsedObj if (functionInfo) { if (logalot) { console.log(`${lc} found functionInfo: ${functionInfo.nameOrId} (I: 837c7a8200689a0b2314b73057812b25)`); } let fnResult: any | undefined = undefined; let fnComplete = false; let execErrorMsg: string | undefined = undefined; // #region execute the functionInfo.functionImpl try { if (logalot) { console.log(`${lc} calling function impl for ${functionInfo.nameOrId} (I: b145d08186782089c18564305f0f1125)`); } fnResult = await functionInfo.fnViaCmd(functionArgs); fnComplete = true; if (logalot) { console.log(`${lc} functionImpl complete for ${functionInfo.nameOrId}. result:`, pretty(fnResult), `(I: e19d476f489261263165914750472d25)`); } } catch (error) { execErrorMsg = `${lc} could not call functionImpl for ${functionInfo.nameOrId}: ${extractErrorMsg(error)} (E: 9c0e97f29a9290f9d57637b7053f0c25)`; console.error(execErrorMsg); } // #endregion execute the functionInfo.functionImpl const resCreate = await createFunctionInfoIbGib({ api: this.data.api, model: this.data.model, agentId: this.data.uuid!, fnRawRequest: functionCallStr, fnName: functionName, fnArgs: functionCallParsedObj.args, repromptWithResult: functionCallParsedObj.args?.repromptWithResult ?? false, fnResult, fnComplete, execErrorMsg, parseErrorMsg: undefined, saveInSpace: true, metaspace, space: await this.getSuperSpace(), }); functionInfoIbGib = resCreate.newIbGib; } else { const resCreateErrored = await createFunctionInfoIbGib({ api: this.data.api, model: this.data.model, agentId: this.data.uuid, fnRawRequest: functionCallStr, fnName: functionName, fnArgs: functionCallParsedObj.args, repromptWithResult: functionCallParsedObj.args?.repromptWithResult ?? false, fnResult: undefined, fnComplete: undefined, execErrorMsg: undefined, parseErrorMsg, saveInSpace: true, metaspace, space: await this.getSuperSpace(), }); functionInfoIbGib = resCreateErrored.newIbGib; } if (!functionInfoIbGib) { throw new Error(`(UNEXPECTED) functionInfoIbGib falsy? we should have guaranteed to create one at this point. (E: 97c4ce12c7372adfeebfd45181b73f25)`); } functionInfoIbGibs.push(functionInfoIbGib); } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } // end iterate functionCalls (raw JSON strings) if (functionInfoIbGibs.length === 0) { throw new Error(`(UNEXPECTED) functionInfoIbGibs.length === 0? We expect at least one to be created (E: a8f81c56377cdbdeb468af6b21879825)`); } return functionInfoIbGibs; } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } protected async doPrompt_functionCalls_addFunctionCallRequestComment({ functionInfoIbGibs }: { functionInfoIbGibs: FunctionInfoIbGib_V1[] }): Promise { const lc = `${this.lc}[${this.doPrompt_functionCalls_addFunctionCallRequestComment.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: 5000adac5e8c4a7158a720c38f56b325)`); } const metaspace = this._metaspace; if (!metaspace) { throw new Error(`(UNEXPECTED) metaspace falsy? (E: d7275731461d8d0526af3088553ae525)`); } const superSpace = await this.getSuperSpace(); // this is the actual data.text string const fcrText = taggifyForPrompt({ tagText: FUNCTION_CALL_REQUEST_COMMENT_TAG, contentText: functionInfoIbGibs.map(x => getIbGibAddr({ ibGib: x })).join('\n'), randomIdLength: 4, }); const addlMetadataText = getAddlMetadataTextForAgentText({ textSrc: 'ai', other: 'functionCallRequest' }); const resComment: TransformResult = await createCommentIbGib({ text: fcrText, addlMetadataText, saveInSpace: true, space: superSpace, }); // now we have the starting comment ibgib, but we want to relate the // actual function info ibgibs via the function info rel8nName. const resRel8FunctionInfosToFCR = await rel8({ type: 'rel8', src: resComment.newIbGib, rel8nsToAddByAddr: { [FUNCTION_INFO_REL8N_NAME]: functionInfoIbGibs.map(x => getIbGibAddr({ ibGib: x })), }, dna: true, nCounter: true, }) as TransformResult; const functionCallRequestComment = resRel8FunctionInfosToFCR.newIbGib; await metaspace.persistTransformResult({ resTransform: resRel8FunctionInfosToFCR, space: superSpace, }); await metaspace.registerNewIbGib({ ibGib: functionCallRequestComment, space: superSpace, }); await this.addTexts({ infos: [{ textSrc: 'ai', commentIbGib: functionCallRequestComment }], }); } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } protected async doPrompt_functionCalls_getTransientDecompressionPromptForNextPrompt({ functionInfoIbGibs }: { functionInfoIbGibs: FunctionInfoIbGib_V1[] }): Promise { const lc = `${this.lc}[${this.doPrompt_functionCalls_getTransientDecompressionPromptForNextPrompt.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: c245f78f6c78a98d62375a9f14b3d625)`); } let contentTexts: string[] = []; for (const functionInfoIbGib of functionInfoIbGibs) { try { const functionInfoAddr = getIbGibAddr({ ibGib: functionInfoIbGib }); if (!functionInfoIbGib.data) { throw new Error(`(UNEXPECTED) !functionInfoIbGib.data? functionInfoAddr: ${functionInfoAddr} (E: 7815e4854513c26b889517a9bd7e0d25)`); } const { fnRawRequest, fnComplete, fnResult } = functionInfoIbGib.data; const dataSlice = { fnRawRequest, fnComplete, fnResult }; const contentText = taggifyForPrompt({ tagText: functionInfoAddr, contentText: pretty(dataSlice), randomIdLength: undefined, // don't need an id since we have a unique ibgib addr as the tag }); contentTexts.push(contentText); } catch (error) { console.error(`${lc} could not call functionImpl: ${extractErrorMsg(error)} (E: 2a3b8b37e146c524b67119e3a74e3d25)`) } } return contentTexts.join('\n'); } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } // #endregion doPrompt /** * naive counter for reprompt asked for by the model */ repromptCount: number = 0; /** * naive cap for reprompt asked for by the model */ repromptMax: number = 12; /** * recursively calls doPrompt depending on {@link repromptCount} and {@link repromptMax} */ async rePrompt(): Promise { const lc = `${this.lc}[${this.rePrompt.name}]`; try { if (logalot) { console.log(`${lc} starting... (I: b6af34214acf07f9ca8c1d03c0011525)`); } if (this.repromptCount < this.repromptMax) { this.repromptCount++; await this.doPrompt(); } else { console.log(`${lc} already reached repromptMax (${this.repromptMax}). reprompt aborting. $(I: dacefb501dab2a1ee173ce8558657725)`); this.repromptCount = 0; } } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } async witness(arg: IbGib_V1): Promise { if (arg.ib === IB && arg.gib === GIB) { // root called, which in this class is a request to doPrompt. if (this.getComments_Chat().length > 0) { await this.doPrompt(); } return ROOT; } else { // TODO: Implement other witness logic for the agent. for now we're just ignoring this, but in the future, we need to redesign this class more towards ibgib style: functions create args that are funneled to this, or at the very least, the functions, if successful, are memoized via this witness function after-the-fact and this witness gets updated. its internal state (`this.data` and `this.rel8ns`) is changed, so therefore `this.gib` needs to be updated and most likely this witness needs to be updated/persisted in its local space. but since we're still feeling out the overall architecture, this would be too much overhead for something that might change in the near future. throw new Error(`arg (${pretty(arg)}) not implemented. only expect ${ROOT} right now to trigger doPrompt. (E: 24da0ad7333b6bbb359d1be425db3c25)`); } } } export type AgentWitnessAny = AgentWitness_V1;