@agimon-ai/po-web-search 0.0.0-alpha.2 → 0.0.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"server.cjs","names":["defineServerTool","createWebRunTool","defineServerPlugin","toolWebRun"],"sources":["../../src/extensions/workspaces/sessions/(backend)/tool/webRun.server.ts","../../generated/server.ts"],"sourcesContent":["import { defineServerTool } from '@agimon-ai/po-core/extensionFile';\n\nimport { createWebRunTool } from '../../../../../services/webRunTool';\n\nexport default defineServerTool(() => createWebRunTool());\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\n\nimport toolWebRun from '../src/extensions/workspaces/sessions/(backend)/tool/webRun.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-web-search',\n session: (context) => ({\n get tools(): DoomServerSessionPlugin['tools'] { return [via({ name: 'web_run' }, at(toolWebRun, context))]; },\n }),\n});\n\nexport default facet;\n"],"mappings":";;;;;;;AAIA,IAAA,yBAAA,6CAAeA,CAAAA,CAAAA,iBAAAA,OAAuBC,mBAAAA,iBAAiB,CAAC;;;ACExD,MAAM,MAAY,OAA0B,YAC1C,OAAO,UAAU,aAAc,MAAwB,OAAO,IAAI;AACpE,MAAM,OAAU,UAAmC,UAChD,UAAU,KAAA,IAAY,KAAA,IAAY;CAAE,GAAG;CAAU,GAAI;AAAiB;AAEzE,MAAa,SAAA,GAAQC,+BAAAA,mBAAAA,CAAmB;CACtC,MAAM;CACN,UAAU,aAAa,EACrB,IAAI,QAA0C;EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,UAAU,GAAG,GAAGC,uBAAY,OAAO,CAAC,CAAC;CAAG,EAC9G;AACF,CAAC"}
1
+ {"version":3,"file":"server.cjs","names":["defineServerTool","createWebRunTool","defineServerPlugin","toolWebRun"],"sources":["../../src/extensions/workspaces/sessions/(backend)/tool/webRun.server.ts","../../generated/server.ts"],"sourcesContent":["import { defineServerTool } from '@agimon-ai/po-core/extensionFile';\n\nimport { createWebRunTool } from '../../../../../services/webRunTool';\n\nexport default defineServerTool(() => createWebRunTool());\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\n\nimport toolWebRun from '../src/extensions/workspaces/sessions/(backend)/tool/webRun.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-web-search',\n session: (context) => ({\n get tools(): PoServerSessionPlugin['tools'] { return [via({ name: 'web_run' }, at(toolWebRun, context))]; },\n }),\n});\n\nexport default facet;\n"],"mappings":";;;;;;;AAIA,IAAA,yBAAA,6CAAeA,CAAAA,CAAAA,iBAAAA,OAAuBC,mBAAAA,iBAAiB,CAAC;;;ACExD,MAAM,MAAY,OAA0B,YAC1C,OAAO,UAAU,aAAc,MAAwB,OAAO,IAAI;AACpE,MAAM,OAAU,UAAmC,UAChD,UAAU,KAAA,IAAY,KAAA,IAAY;CAAE,GAAG;CAAU,GAAI;AAAiB;AAEzE,MAAa,SAAA,GAAQC,+BAAAA,mBAAAA,CAAmB;CACtC,MAAM;CACN,UAAU,aAAa,EACrB,IAAI,QAAwC;EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,UAAU,GAAG,GAAGC,uBAAY,OAAO,CAAC,CAAC;CAAG,EAC5G;AACF,CAAC"}
@@ -1,5 +1,5 @@
1
1
  //#region generated/server.d.ts
2
- export declare const facet: import("@agimon-ai/po-core/serverFacet").DoomServerFacet;
2
+ export declare const facet: import("@agimon-ai/po-core/serverFacet").PoServerFacet;
3
3
  //#endregion
4
4
  export { facet as default };
5
5
  //# sourceMappingURL=server.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.mjs","names":["toolWebRun"],"sources":["../../src/extensions/workspaces/sessions/(backend)/tool/webRun.server.ts","../../generated/server.ts"],"sourcesContent":["import { defineServerTool } from '@agimon-ai/po-core/extensionFile';\n\nimport { createWebRunTool } from '../../../../../services/webRunTool';\n\nexport default defineServerTool(() => createWebRunTool());\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\n\nimport toolWebRun from '../src/extensions/workspaces/sessions/(backend)/tool/webRun.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-web-search',\n session: (context) => ({\n get tools(): DoomServerSessionPlugin['tools'] { return [via({ name: 'web_run' }, at(toolWebRun, context))]; },\n }),\n});\n\nexport default facet;\n"],"mappings":";;;;AAIA,IAAA,wBAAe,uBAAuB,iBAAiB,CAAC;;;ACExD,MAAM,MAAY,OAA0B,YAC1C,OAAO,UAAU,aAAc,MAAwB,OAAO,IAAI;AACpE,MAAM,OAAU,UAAmC,UAChD,UAAU,KAAA,IAAY,KAAA,IAAY;CAAE,GAAG;CAAU,GAAI;AAAiB;AAEzE,MAAa,QAAQ,mBAAmB;CACtC,MAAM;CACN,UAAU,aAAa,EACrB,IAAI,QAA0C;EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,UAAU,GAAG,GAAGA,uBAAY,OAAO,CAAC,CAAC;CAAG,EAC9G;AACF,CAAC"}
1
+ {"version":3,"file":"server.mjs","names":["toolWebRun"],"sources":["../../src/extensions/workspaces/sessions/(backend)/tool/webRun.server.ts","../../generated/server.ts"],"sourcesContent":["import { defineServerTool } from '@agimon-ai/po-core/extensionFile';\n\nimport { createWebRunTool } from '../../../../../services/webRunTool';\n\nexport default defineServerTool(() => createWebRunTool());\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\n\nimport toolWebRun from '../src/extensions/workspaces/sessions/(backend)/tool/webRun.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-web-search',\n session: (context) => ({\n get tools(): PoServerSessionPlugin['tools'] { return [via({ name: 'web_run' }, at(toolWebRun, context))]; },\n }),\n});\n\nexport default facet;\n"],"mappings":";;;;AAIA,IAAA,wBAAe,uBAAuB,iBAAiB,CAAC;;;ACExD,MAAM,MAAY,OAA0B,YAC1C,OAAO,UAAU,aAAc,MAAwB,OAAO,IAAI;AACpE,MAAM,OAAU,UAAmC,UAChD,UAAU,KAAA,IAAY,KAAA,IAAY;CAAE,GAAG;CAAU,GAAI;AAAiB;AAEzE,MAAa,QAAQ,mBAAmB;CACtC,MAAM;CACN,UAAU,aAAa,EACrB,IAAI,QAAwC;EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,UAAU,GAAG,GAAGA,uBAAY,OAAO,CAAC,CAAC;CAAG,EAC5G;AACF,CAAC"}
@@ -98,10 +98,10 @@ function WebRunToolMessage(props) {
98
98
  title: "web search",
99
99
  children: [/* @__PURE__ */ jsx("span", {
100
100
  "data-testid": "tool-call-web_run",
101
- className: "min-w-0 flex-1 truncate text-doom-text",
101
+ className: "min-w-0 flex-1 truncate text-po-text",
102
102
  children: view.summary
103
103
  }), label ? /* @__PURE__ */ jsx("span", {
104
- className: "shrink-0 text-doom-faint",
104
+ className: "shrink-0 text-po-faint",
105
105
  children: label
106
106
  }) : null]
107
107
  }), expanded ? /* @__PURE__ */ jsxs(MessageItemBody, {
@@ -125,13 +125,13 @@ function WebRunToolMessage(props) {
125
125
  href: source.url,
126
126
  target: "_blank",
127
127
  rel: "noreferrer",
128
- className: "text-doom-blue underline decoration-doom-blue/50",
128
+ className: "text-po-blue underline decoration-po-blue/50",
129
129
  children: source.label
130
130
  })
131
131
  }, source.url))
132
132
  }) : null,
133
133
  resultTextLines(result, output).map((line, index) => /* @__PURE__ */ jsx("span", {
134
- className: `whitespace-pre-wrap break-words ${isError ? "text-doom-red" : "text-doom-dim"}`,
134
+ className: `whitespace-pre-wrap break-words ${isError ? "text-po-red" : "text-po-dim"}`,
135
135
  children: line
136
136
  }, `${String(index)}-${line}`))
137
137
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"web.mjs","names":["toolWebRun"],"sources":["../../src/extensions/workspaces/sessions/(frontend)/tool/_lib/webRunToolView.ts","../../src/extensions/workspaces/sessions/(frontend)/tool/_components/WebRunToolMessage.tsx","../../src/extensions/workspaces/sessions/(frontend)/tool/webRun.web.tsx","../../generated/web.ts"],"sourcesContent":["// Copyright 2025 OpenAI\n// SPDX-License-Identifier: Apache-2.0\n// Modified for Po from codex-rs ext/web-search/src/tool.rs (command_action), core/src/web_search.rs\n// (web_search_action_detail) and tui/src/history_cell/search.rs (summary wording).\n\nimport type { ToolMessageRenderProps } from '@agimon-ai/po-core/web';\n\ntype Action =\n | { kind: 'search'; detail: string }\n | { kind: 'open'; detail: string }\n | { kind: 'find'; detail: string; pattern: boolean }\n | { kind: 'other' };\n\nexport interface WebRunSourceView {\n url: string;\n label: string;\n}\n\nconst ACCESS_LABELS = ['cached', 'indexed', 'live'] as const;\nconst MAX_URL = 2048;\n\nfunction entries(value: unknown): Record<string, unknown>[] {\n return Array.isArray(value)\n ? value.filter((item): item is Record<string, unknown> => typeof item === 'object' && item !== null)\n : [];\n}\n\nfunction string(value: unknown): string {\n return typeof value === 'string' ? value : '';\n}\n\nfunction literalUrl(refId: string): string {\n return URL.canParse(refId) ? refId : '';\n}\n\nfunction queryAction(value: unknown): Action | undefined {\n const queries = entries(value).map((query) => string(query.q));\n if (queries.length === 0) return undefined;\n const first = queries[0]!;\n return { kind: 'search', detail: queries.length > 1 && first ? `${first} ...` : first };\n}\n\nfunction action(args: Record<string, unknown>): Action {\n const search = queryAction(args.search_query) ?? queryAction(args.image_query);\n if (search) return search;\n const url = literalUrl(string(entries(args.open)[0]?.ref_id));\n if (url) return { kind: 'open', detail: url };\n const find = entries(args.find)[0];\n if (find) {\n const pattern = string(find.pattern);\n const page = literalUrl(string(find.ref_id));\n const detail = pattern && page ? `'${pattern}' in ${page}` : pattern ? `'${pattern}'` : page;\n return { kind: 'find', detail, pattern: !!pattern };\n }\n return { kind: 'other' };\n}\n\nfunction summary(current: Action, completed: boolean): string {\n if (current.kind === 'open') return `${completed ? 'Opened' : 'Opening'} ${current.detail}`;\n if (current.kind === 'find') {\n const verb = current.pattern\n ? completed\n ? 'Searched for'\n : 'Searching for'\n : completed\n ? 'Searched page'\n : 'Searching page';\n return current.detail ? `${verb} ${current.detail}` : verb;\n }\n const detail = current.kind === 'search' ? current.detail : '';\n if (!detail)\n return completed ? 'Searched the web' : current.kind === 'other' ? 'Browsing the web' : 'Searching the web';\n return `${completed ? 'Searched the web' : 'Searching the web'} for ${detail}`;\n}\n\n/** `details` is re-validated here: only http(s) URLs become links, everything else is ignored. */\nfunction sources(details: unknown): WebRunSourceView[] {\n if (typeof details !== 'object' || details === null) return [];\n return entries((details as { sources?: unknown }).sources).flatMap((source) => {\n const url = string(source.url);\n if (url.length > MAX_URL || !URL.canParse(url)) return [];\n const parsed = new URL(url);\n if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') return [];\n return [{ url: parsed.href, label: string(source.title).trim() || parsed.hostname }];\n });\n}\n\nexport function webRunToolView({ args, result, running }: Pick<ToolMessageRenderProps, 'args' | 'result' | 'running'>) {\n const details: unknown = result?.details;\n const record = typeof details === 'object' && details !== null ? (details as Record<string, unknown>) : {};\n return {\n summary: summary(action(args), !running),\n access: ACCESS_LABELS.find((label) => label === record.access),\n sharedConversation: record.sharedConversation === true,\n sources: sources(details),\n };\n}\n","import type { ToolMessageRenderProps } from '@agimon-ai/po-core/web';\nimport {\n MessageItem,\n MessageItemBody,\n MessageItemHeader,\n MessageItemStatus,\n resultTextLines,\n toolTone,\n} from '@agimon-ai/po-web-components';\n\nimport { webRunToolView } from '../_lib/webRunToolView';\n\n/** Collapsed by default; `defaultExpanded` lets a story show the body. */\nexport function WebRunToolMessage(props: ToolMessageRenderProps & { defaultExpanded?: boolean }) {\n const { result, output, running, isError, defaultExpanded = false } = props;\n const view = webRunToolView(props);\n const label = view.access ? `${view.access}${view.sharedConversation ? ' · with recent conversation' : ''}` : '';\n return (\n <MessageItem tone={toolTone({ running, isError })} expandable defaultExpanded={defaultExpanded}>\n {({ expanded }) => (\n <>\n <MessageItemHeader title=\"web search\">\n <span data-testid=\"tool-call-web_run\" className=\"min-w-0 flex-1 truncate text-doom-text\">\n {view.summary}\n </span>\n {label ? <span className=\"shrink-0 text-doom-faint\">{label}</span> : null}\n </MessageItemHeader>\n {expanded ? (\n <MessageItemBody data-testid=\"tool-result-web_run\" className=\"flex flex-col gap-2\">\n {running ? <MessageItemStatus tone=\"running\">searching</MessageItemStatus> : null}\n {isError ? <MessageItemStatus tone=\"error\">failed</MessageItemStatus> : null}\n {!isError && view.sources.length > 0 ? (\n <ul aria-label=\"Sources\" className=\"flex flex-col gap-1\">\n {view.sources.map((source) => (\n <li key={source.url} className=\"min-w-0 truncate\">\n <a\n href={source.url}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"text-doom-blue underline decoration-doom-blue/50\"\n >\n {source.label}\n </a>\n </li>\n ))}\n </ul>\n ) : null}\n {resultTextLines(result, output).map((line, index) => (\n <span\n key={`${String(index)}-${line}`}\n className={`whitespace-pre-wrap break-words ${isError ? 'text-doom-red' : 'text-doom-dim'}`}\n >\n {line}\n </span>\n ))}\n </MessageItemBody>\n ) : null}\n </>\n )}\n </MessageItem>\n );\n}\n","import { defineToolRenderer } from '@agimon-ai/po-core/web';\n\nimport { WebRunToolMessage } from './_components/WebRunToolMessage';\n\nexport default defineToolRenderer({ message: WebRunToolMessage });\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineWebPlugin, type WebPluginContributions } from '@agimon-ai/po-core/web';\n\nimport toolWebRun from '../src/extensions/workspaces/sessions/(frontend)/tool/webRun.web';\n\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const webPlugin = defineWebPlugin({\n id: 'web-search',\n session: {\n get toolRenderers(): WebPluginContributions['toolRenderers'] { return [via({ tools: ['web_run'] }, toolWebRun)]; },\n },\n});\n"],"mappings":";;;;AAkBA,MAAM,gBAAgB;CAAC;CAAU;CAAW;AAAM;AAClD,MAAM,UAAU;AAEhB,SAAS,QAAQ,OAA2C;CAC1D,OAAO,MAAM,QAAQ,KAAK,IACtB,MAAM,QAAQ,SAA0C,OAAO,SAAS,YAAY,SAAS,IAAI,IACjG,CAAC;AACP;AAEA,SAAS,OAAO,OAAwB;CACtC,OAAO,OAAO,UAAU,WAAW,QAAQ;AAC7C;AAEA,SAAS,WAAW,OAAuB;CACzC,OAAO,IAAI,SAAS,KAAK,IAAI,QAAQ;AACvC;AAEA,SAAS,YAAY,OAAoC;CACvD,MAAM,UAAU,QAAQ,KAAK,CAAC,CAAC,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;CAC7D,IAAI,QAAQ,WAAW,GAAG,OAAO,KAAA;CACjC,MAAM,QAAQ,QAAQ;CACtB,OAAO;EAAE,MAAM;EAAU,QAAQ,QAAQ,SAAS,KAAK,QAAQ,GAAG,MAAM,QAAQ;CAAM;AACxF;AAEA,SAAS,OAAO,MAAuC;CACrD,MAAM,SAAS,YAAY,KAAK,YAAY,KAAK,YAAY,KAAK,WAAW;CAC7E,IAAI,QAAQ,OAAO;CACnB,MAAM,MAAM,WAAW,OAAO,QAAQ,KAAK,IAAI,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC;CAC5D,IAAI,KAAK,OAAO;EAAE,MAAM;EAAQ,QAAQ;CAAI;CAC5C,MAAM,OAAO,QAAQ,KAAK,IAAI,CAAC,CAAC;CAChC,IAAI,MAAM;EACR,MAAM,UAAU,OAAO,KAAK,OAAO;EACnC,MAAM,OAAO,WAAW,OAAO,KAAK,MAAM,CAAC;EAE3C,OAAO;GAAE,MAAM;GAAQ,QADR,WAAW,OAAO,IAAI,QAAQ,OAAO,SAAS,UAAU,IAAI,QAAQ,KAAK;GACzD,SAAS,CAAC,CAAC;EAAQ;CACpD;CACA,OAAO,EAAE,MAAM,QAAQ;AACzB;AAEA,SAAS,QAAQ,SAAiB,WAA4B;CAC5D,IAAI,QAAQ,SAAS,QAAQ,OAAO,GAAG,YAAY,WAAW,UAAU,GAAG,QAAQ;CACnF,IAAI,QAAQ,SAAS,QAAQ;EAC3B,MAAM,OAAO,QAAQ,UACjB,YACE,iBACA,kBACF,YACE,kBACA;EACN,OAAO,QAAQ,SAAS,GAAG,KAAK,GAAG,QAAQ,WAAW;CACxD;CACA,MAAM,SAAS,QAAQ,SAAS,WAAW,QAAQ,SAAS;CAC5D,IAAI,CAAC,QACH,OAAO,YAAY,qBAAqB,QAAQ,SAAS,UAAU,qBAAqB;CAC1F,OAAO,GAAG,YAAY,qBAAqB,oBAAoB,OAAO;AACxE;;AAGA,SAAS,QAAQ,SAAsC;CACrD,IAAI,OAAO,YAAY,YAAY,YAAY,MAAM,OAAO,CAAC;CAC7D,OAAO,QAAS,QAAkC,OAAO,CAAC,CAAC,SAAS,WAAW;EAC7E,MAAM,MAAM,OAAO,OAAO,GAAG;EAC7B,IAAI,IAAI,SAAS,WAAW,CAAC,IAAI,SAAS,GAAG,GAAG,OAAO,CAAC;EACxD,MAAM,SAAS,IAAI,IAAI,GAAG;EAC1B,IAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SAAS,OAAO,CAAC;EACzE,OAAO,CAAC;GAAE,KAAK,OAAO;GAAM,OAAO,OAAO,OAAO,KAAK,CAAC,CAAC,KAAK,KAAK,OAAO;EAAS,CAAC;CACrF,CAAC;AACH;AAEA,SAAgB,eAAe,EAAE,MAAM,QAAQ,WAAwE;CACrH,MAAM,UAAmB,QAAQ;CACjC,MAAM,SAAS,OAAO,YAAY,YAAY,YAAY,OAAQ,UAAsC,CAAC;CACzG,OAAO;EACL,SAAS,QAAQ,OAAO,IAAI,GAAG,CAAC,OAAO;EACvC,QAAQ,cAAc,MAAM,UAAU,UAAU,OAAO,MAAM;EAC7D,oBAAoB,OAAO,uBAAuB;EAClD,SAAS,QAAQ,OAAO;CAC1B;AACF;;;;ACnFA,SAAgB,kBAAkB,OAA+D;CAC/F,MAAM,EAAE,QAAQ,QAAQ,SAAS,SAAS,kBAAkB,UAAU;CACtE,MAAM,OAAO,eAAe,KAAK;CACjC,MAAM,QAAQ,KAAK,SAAS,GAAG,KAAK,SAAS,KAAK,qBAAqB,gCAAgC,OAAO;CAC9G,OACE,oBAAC,aAAD;EAAa,MAAM,SAAS;GAAE;GAAS;EAAQ,CAAC;EAAG,YAAA;EAA4B;EAC3E,WAAA,EAAE,eACF,qBAAA,UAAA,EAAA,UAAA,CACE,qBAAC,mBAAD;GAAmB,OAAM;GAAzB,UAAA,CACE,oBAAC,QAAD;IAAM,eAAY;IAAoB,WAAU;IAC7C,UAAA,KAAK;GACF,CAAA,GACL,QAAQ,oBAAC,QAAD;IAAM,WAAU;IAA4B,UAAA;GAAY,CAAA,IAAI,IACpD;EAClB,CAAA,GAAA,WACC,qBAAC,iBAAD;GAAiB,eAAY;GAAsB,WAAU;GAA7D,UAAA;IACG,UAAU,oBAAC,mBAAD;KAAmB,MAAK;KAAU,UAAA;IAA4B,CAAA,IAAI;IAC5E,UAAU,oBAAC,mBAAD;KAAmB,MAAK;KAAQ,UAAA;IAAyB,CAAA,IAAI;IACvE,CAAC,WAAW,KAAK,QAAQ,SAAS,IACjC,oBAAC,MAAD;KAAI,cAAW;KAAU,WAAU;KAChC,UAAA,KAAK,QAAQ,KAAK,WACjB,oBAAC,MAAD;MAAqB,WAAU;MAC7B,UAAA,oBAAC,KAAD;OACE,MAAM,OAAO;OACb,QAAO;OACP,KAAI;OACJ,WAAU;OAET,UAAA,OAAO;MACP,CAAA;KACD,GATK,OAAO,GASZ,CACL;IACC,CAAA,IACF;IACH,gBAAgB,QAAQ,MAAM,CAAC,CAAC,KAAK,MAAM,UAC1C,oBAAC,QAAD;KAEE,WAAW,mCAAmC,UAAU,kBAAkB;KAEzE,UAAA;IACG,GAJC,GAAG,OAAO,KAAK,EAAE,GAAG,MAIrB,CACP;GACc;EACf,CAAA,IAAA,IACJ,EAAA,CAAA;CAEO,CAAA;AAEjB;;;ACzDA,IAAA,qBAAe,mBAAmB,EAAE,SAAS,kBAAkB,CAAC;;;ACChE,MAAM,OAAU,UAAmC,UAChD,UAAU,KAAA,IAAY,KAAA,IAAY;CAAE,GAAG;CAAU,GAAI;AAAiB;AAEzE,MAAa,YAAY,gBAAgB;CACvC,IAAI;CACJ,SAAS,EACP,IAAI,gBAAyD;EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,SAAS,EAAE,GAAGA,kBAAU,CAAC;CAAG,EACnH;AACF,CAAC"}
1
+ {"version":3,"file":"web.mjs","names":["toolWebRun"],"sources":["../../src/extensions/workspaces/sessions/(frontend)/tool/_lib/webRunToolView.ts","../../src/extensions/workspaces/sessions/(frontend)/tool/_components/WebRunToolMessage.tsx","../../src/extensions/workspaces/sessions/(frontend)/tool/webRun.web.tsx","../../generated/web.ts"],"sourcesContent":["// Copyright 2025 OpenAI\n// SPDX-License-Identifier: Apache-2.0\n// Modified for Po from codex-rs ext/web-search/src/tool.rs (command_action), core/src/web_search.rs\n// (web_search_action_detail) and tui/src/history_cell/search.rs (summary wording).\n\nimport type { ToolMessageRenderProps } from '@agimon-ai/po-core/web';\n\ntype Action =\n | { kind: 'search'; detail: string }\n | { kind: 'open'; detail: string }\n | { kind: 'find'; detail: string; pattern: boolean }\n | { kind: 'other' };\n\nexport interface WebRunSourceView {\n url: string;\n label: string;\n}\n\nconst ACCESS_LABELS = ['cached', 'indexed', 'live'] as const;\nconst MAX_URL = 2048;\n\nfunction entries(value: unknown): Record<string, unknown>[] {\n return Array.isArray(value)\n ? value.filter((item): item is Record<string, unknown> => typeof item === 'object' && item !== null)\n : [];\n}\n\nfunction string(value: unknown): string {\n return typeof value === 'string' ? value : '';\n}\n\nfunction literalUrl(refId: string): string {\n return URL.canParse(refId) ? refId : '';\n}\n\nfunction queryAction(value: unknown): Action | undefined {\n const queries = entries(value).map((query) => string(query.q));\n if (queries.length === 0) return undefined;\n const first = queries[0]!;\n return { kind: 'search', detail: queries.length > 1 && first ? `${first} ...` : first };\n}\n\nfunction action(args: Record<string, unknown>): Action {\n const search = queryAction(args.search_query) ?? queryAction(args.image_query);\n if (search) return search;\n const url = literalUrl(string(entries(args.open)[0]?.ref_id));\n if (url) return { kind: 'open', detail: url };\n const find = entries(args.find)[0];\n if (find) {\n const pattern = string(find.pattern);\n const page = literalUrl(string(find.ref_id));\n const detail = pattern && page ? `'${pattern}' in ${page}` : pattern ? `'${pattern}'` : page;\n return { kind: 'find', detail, pattern: !!pattern };\n }\n return { kind: 'other' };\n}\n\nfunction summary(current: Action, completed: boolean): string {\n if (current.kind === 'open') return `${completed ? 'Opened' : 'Opening'} ${current.detail}`;\n if (current.kind === 'find') {\n const verb = current.pattern\n ? completed\n ? 'Searched for'\n : 'Searching for'\n : completed\n ? 'Searched page'\n : 'Searching page';\n return current.detail ? `${verb} ${current.detail}` : verb;\n }\n const detail = current.kind === 'search' ? current.detail : '';\n if (!detail)\n return completed ? 'Searched the web' : current.kind === 'other' ? 'Browsing the web' : 'Searching the web';\n return `${completed ? 'Searched the web' : 'Searching the web'} for ${detail}`;\n}\n\n/** `details` is re-validated here: only http(s) URLs become links, everything else is ignored. */\nfunction sources(details: unknown): WebRunSourceView[] {\n if (typeof details !== 'object' || details === null) return [];\n return entries((details as { sources?: unknown }).sources).flatMap((source) => {\n const url = string(source.url);\n if (url.length > MAX_URL || !URL.canParse(url)) return [];\n const parsed = new URL(url);\n if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') return [];\n return [{ url: parsed.href, label: string(source.title).trim() || parsed.hostname }];\n });\n}\n\nexport function webRunToolView({ args, result, running }: Pick<ToolMessageRenderProps, 'args' | 'result' | 'running'>) {\n const details: unknown = result?.details;\n const record = typeof details === 'object' && details !== null ? (details as Record<string, unknown>) : {};\n return {\n summary: summary(action(args), !running),\n access: ACCESS_LABELS.find((label) => label === record.access),\n sharedConversation: record.sharedConversation === true,\n sources: sources(details),\n };\n}\n","import type { ToolMessageRenderProps } from '@agimon-ai/po-core/web';\nimport {\n MessageItem,\n MessageItemBody,\n MessageItemHeader,\n MessageItemStatus,\n resultTextLines,\n toolTone,\n} from '@agimon-ai/po-web-components';\n\nimport { webRunToolView } from '../_lib/webRunToolView';\n\n/** Collapsed by default; `defaultExpanded` lets a story show the body. */\nexport function WebRunToolMessage(props: ToolMessageRenderProps & { defaultExpanded?: boolean }) {\n const { result, output, running, isError, defaultExpanded = false } = props;\n const view = webRunToolView(props);\n const label = view.access ? `${view.access}${view.sharedConversation ? ' · with recent conversation' : ''}` : '';\n return (\n <MessageItem tone={toolTone({ running, isError })} expandable defaultExpanded={defaultExpanded}>\n {({ expanded }) => (\n <>\n <MessageItemHeader title=\"web search\">\n <span data-testid=\"tool-call-web_run\" className=\"min-w-0 flex-1 truncate text-po-text\">\n {view.summary}\n </span>\n {label ? <span className=\"shrink-0 text-po-faint\">{label}</span> : null}\n </MessageItemHeader>\n {expanded ? (\n <MessageItemBody data-testid=\"tool-result-web_run\" className=\"flex flex-col gap-2\">\n {running ? <MessageItemStatus tone=\"running\">searching</MessageItemStatus> : null}\n {isError ? <MessageItemStatus tone=\"error\">failed</MessageItemStatus> : null}\n {!isError && view.sources.length > 0 ? (\n <ul aria-label=\"Sources\" className=\"flex flex-col gap-1\">\n {view.sources.map((source) => (\n <li key={source.url} className=\"min-w-0 truncate\">\n <a\n href={source.url}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"text-po-blue underline decoration-po-blue/50\"\n >\n {source.label}\n </a>\n </li>\n ))}\n </ul>\n ) : null}\n {resultTextLines(result, output).map((line, index) => (\n <span\n key={`${String(index)}-${line}`}\n className={`whitespace-pre-wrap break-words ${isError ? 'text-po-red' : 'text-po-dim'}`}\n >\n {line}\n </span>\n ))}\n </MessageItemBody>\n ) : null}\n </>\n )}\n </MessageItem>\n );\n}\n","import { defineToolRenderer } from '@agimon-ai/po-core/web';\n\nimport { WebRunToolMessage } from './_components/WebRunToolMessage';\n\nexport default defineToolRenderer({ message: WebRunToolMessage });\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineWebPlugin, type WebPluginContributions } from '@agimon-ai/po-core/web';\n\nimport toolWebRun from '../src/extensions/workspaces/sessions/(frontend)/tool/webRun.web';\n\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const webPlugin = defineWebPlugin({\n id: 'web-search',\n session: {\n get toolRenderers(): WebPluginContributions['toolRenderers'] { return [via({ tools: ['web_run'] }, toolWebRun)]; },\n },\n});\n"],"mappings":"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"}
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Type } from "typebox";
2
- import { DoomHeadlessTool } from "@agimon-ai/po-core/headless";
2
+ import { PoHeadlessTool } from "@agimon-ai/po-core/headless";
3
3
  //#region src/schemas/webRun.d.ts
4
4
  declare const WebRunParamsSchema: Type.TObject<{
5
5
  search_query: Type.TOptional<Type.TArray<Type.TObject<{
@@ -56,7 +56,7 @@ declare const WebRunParamsSchema: Type.TObject<{
56
56
  }>;
57
57
  //#endregion
58
58
  //#region src/services/webRunTool/index.d.ts
59
- export declare function createWebRunTool(): DoomHeadlessTool<typeof WebRunParamsSchema>;
59
+ export declare function createWebRunTool(): PoHeadlessTool<typeof WebRunParamsSchema>;
60
60
  //#endregion
61
61
  //#region src/services/webRunTool/type.d.ts
62
62
  type WebSearchAccess = 'cached' | 'indexed' | 'live';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/schemas/webRun.ts","../src/services/webRunTool/index.ts","../src/services/webRunTool/type.ts"],"mappings":";;;cAgCa,oBAAkB,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBCiBf,oBAAoB,wBAAwB;;;KCjDhD;UAEK;EACf;EACA;;UAGe;EACf,QAAQ;EACR;EACA,SAAS"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/schemas/webRun.ts","../src/services/webRunTool/index.ts","../src/services/webRunTool/type.ts"],"mappings":";;;cAgCa,oBAAkB,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBCiBf,oBAAoB,sBAAsB;;;KCjD9C;UAEK;EACf;EACA;;UAGe;EACf,QAAQ;EACR;EACA,SAAS"}
@@ -1 +1 @@
1
- {"version":3,"file":"webRunTool-BkYexFJr.mjs","names":[],"sources":["../src/constants/webRun.ts","../src/schemas/webRun.ts","../src/services/recentConversation/index.ts","../src/services/webRunTool/index.ts"],"sourcesContent":["// Copyright 2025 OpenAI\n// SPDX-License-Identifier: Apache-2.0\n// Modified for Po from codex-rs ext/web-search/web_run_description.md: `web.run` renamed to `web_run`, error guidance added.\n\nexport const WEB_RUN_NAME = 'web_run';\nexport const WEB_RUN_DESCRIPTION = `Tool for accessing the internet.\n\n\n---\n\n## Examples of different commands available in this tool\n\nExamples of different commands available in this tool:\n* \\`search_query\\`: {\"search_query\": [{\"q\": \"What is the capital of France?\"}, {\"q\": \"What is the capital of belgium?\"}]}. Searches the internet for a given query (and optionally with a domain or recency filter)\n* \\`image_query\\`: {\"image_query\":[{\"q\": \"waterfalls\"}]}.\n* \\`open\\`: {\"open\": [{\"ref_id\": \"turn0search0\"}, {\"ref_id\": \"https://www.openai.com\", \"lineno\": 120}]}\n* \\`click\\`: {\"click\": [{\"ref_id\": \"turn0fetch3\", \"id\": 17}]}\n* \\`find\\`: {\"find\": [{\"ref_id\": \"turn0fetch3\", \"pattern\": \"Annie Case\"}]}\n* \\`screenshot\\`: {\"screenshot\": [{\"ref_id\": \"turn1view0\", \"pageno\": 0}, {\"ref_id\": \"turn1view0\", \"pageno\": 3}]}\n* \\`finance\\`: {\"finance\":[{\"ticker\":\"AMD\",\"type\":\"equity\",\"market\":\"USA\"}]}, {\"finance\":[{\"ticker\":\"BTC\",\"type\":\"crypto\",\"market\":\"\"}]}\n* \\`weather\\`: {\"weather\":[{\"location\":\"San Francisco, CA\"}]}\n* \\`sports\\`: {\"sports\":[{\"fn\":\"standings\",\"league\":\"nfl\"}, {\"fn\":\"schedule\",\"league\":\"nba\",\"team\":\"GSW\",\"date_from\":\"2025-02-24\"}]}\n* \\`time\\`: {\"time\":[{\"utc_offset\":\"+03:00\"}]}\n\n---\n\n## Usage hints\nTo use this tool efficiently:\n* Use multiple commands and queries in one call to get more results faster; e.g. {\"search_query\": [{\"q\": \"bitcoin news\"}], \"finance\":[{\"ticker\":\"BTC\",\"type\":\"crypto\",\"market\":\"\"}], \"find\": [{\"ref_id\": \"turn0search0\", \"pattern\": \"Annie Case\"}, {\"ref_id\": \"turn0search1\", \"pattern\": \"John Smith\"}]}\n* Use \"response_length\" to control the number of results returned by this tool, omit it if you intend to pass \"short\" in\n* Only write required parameters; do not write empty lists or nulls where they could be omitted.\n* \\`search_query\\` must have length at most 4 in each call. If it has length > 3, response_length must be medium or long\n* If you find yourself in a situation where you accidentally call the \\`web_run\\` tool, it's best just to send an empty query: {\"search_query\": [{\"q\": \"\"}]}.\n\n---\n\n## Decision boundary\n\nIf the user makes an explicit request to search the internet, find latest information, look up, etc (or to not do so), you must obey their request.\nWhen you make an assumption, always consider whether it is temporally stable; i.e. whether there's even a small (>10%) chance it has changed. If it is unstable, you must verify with browsing the internet for verification.\n\n<situations_where_you_must_browse_the_internet>\nBelow is a list of scenarios where browsing the internet MUST be used. PAY CLOSE ATTENTION: you MUST browse the internet in these cases. If you're unsure or on the fence, you MUST bias towards browsing the internet.\n- The information could have changed recently: for example news; prices; laws; schedules; product specs; sports scores; economic indicators; political/public/company figures (e.g. the question relates to 'the president of country A' or 'the CEO of company B', which might change over time); rules; regulations; standards; software libraries that could be updated; exchange rates; recommendations (i.e., recommendations about various topics or things might be informed by what currently exists / is popular / is safe / is unsafe / is in the zeitgeist / etc.); and many many many more categories -- again, if you're on the fence, you MUST browse the internet!\n - For news queries, prioritize more recent events, ensuring you compare publish dates and the date that the event happened.\n- The user is seeking recommendations that could lead them to spend substantial time or money -- researching products, restaurants, travel plans, etc.\n- The user wants (or would benefit from) direct quotes, links, or precise source attribution.\n- A specific page, paper, dataset, PDF, or site is referenced and you haven't been given its contents.\n- You're unsure about a fact, the topic is niche or emerging, or you suspect there's at least a 10% chance you will incorrectly recall it\n- High-stakes accuracy matters (medical, legal, financial guidance). For these you generally should search by default because this information is highly temporally unstable\n- The user explicitly says to search, browse, verify, or look it up.\n</situations_where_you_must_browse_the_internet>\n\n---\n\n## Citations\n\nResults from \\`web_run\\` include internal reference IDs such as \\`turn2search5\\`. Use\nthose reference IDs only in calls to \\`web_run\\`; do not expose them in the final\nresponse.\n\nCite sources in the final response using Markdown links:\n\n- Cite a single source as \\`[descriptive source title](https://example.com/page)\\`.\n- Cite multiple sources with separate Markdown links, for example\n \\`[first source](https://example.com/one), [second source](https://example.com/two)\\`.\n- Link directly to the page that supports the claim. Do not link to search result\n pages or use bare URLs.\n\nFormatting of citations:\n\n- Place each citation as near as possible to the claim it supports, normally at\n the end of the sentence or paragraph and after punctuation.\n- Do not place citations inside code fences.\n- Do not put citations on a line by themselves or collect all citations at the\n end of the response.\n\nIf you browse the internet, cite statements supported by web sources. Each cited\nsource must directly support the associated claim. Prefer primary and\nauthoritative sources, and use sources from different domains when the response\nbenefits from multiple perspectives.\n\n---\n\n## Special cases\nIf these conflict with any other instructions, these should take precedence.\n\n<special_cases>\n- When the user asks for information about how to use OpenAI products, (ChatGPT, the OpenAI API, etc.), you should check the code in local env and only browse as fallback, when you browse restrict your sources to official OpenAI websites using the domains filter, unless otherwise requested.\n- When using search to answer technical questions, you must only rely on primary sources (research papers, official documentation, etc.)\n- Clearly indicate when you are making an inference from sources.\n</special_cases>\n\n---\n\n## Word limits\nResponses may not excessively quote or draw on a specific source. There are several limits here:\n- **Limit on verbatim quotes:**\n - You may not quote more than 25 words verbatim from any single non-lyrical source, unless the source is reddit.\n - For song lyrics, verbatim quotes must be limited to at most 10 words.\n - Long quotes from reddit are allowed, as long as you indicate that those are direct quotes via a markdown blockquote starting with \">\", copy verbatim, and link the source.\n- **Word limits:**\n - Each webpage source in the sources has a word limit label formatted like \"[wordlim N]\", in which N is the maximum number of words in the whole response that are attributed to that source. If omitted, the word limit is 200 words.\n - Non-contiguous words derived from a given source must be counted to the word limit.\n - The summarization limit N is a maximum for each source.\n - When using multiple sources, their summarization limits add together. However, each article used must be relevant to the response.\n- **Copyright compliance:**\n - You must avoid providing full articles, long verbatim passages, or extensive direct quotes due to copyright concerns.\n - If the user asked for a verbatim quote, the response should provide a short compliant excerpt and then answer with paraphrases and summaries.\n - Again, this limit does not apply to reddit content, as long as it's appropriately indicated that those are direct quotes and you link to the source.\n\n---\n\n## Errors\n\nIf \\`web_run\\` reports a sign-in, permission, or quota error, tell the user and do not call it again this turn.\n`;\n/** Session environment variable selecting `cached`, `indexed` or `live` access. */\nexport const WEB_SEARCH_ACCESS_ENV = 'PO_WEB_SEARCH_ACCESS';\nexport const DEFAULT_WEB_SEARCH_ACCESS = 'cached';\nexport const WEB_SEARCH_LOGIN_MESSAGE =\n 'Web search needs a ChatGPT sign-in. Sign in to ChatGPT for Po features under Settings > Providers, or with /login.';\n","// Copyright 2025 OpenAI\n// SPDX-License-Identifier: Apache-2.0\n// Modified for Po from codex-rs codex-api/src/search.rs SearchCommands: closed objects and size bounds added.\n\nimport { type Static, type TSchema, Type } from 'typebox';\n\nconst MAX_TEXT = 2048;\nconst MAX_OPERATIONS = 10;\n\nconst text = (description: string) => Type.String({ maxLength: MAX_TEXT, description });\nconst count = (description: string) => Type.Integer({ minimum: 0, description });\nconst closed = <T extends Record<string, TSchema>>(properties: T) =>\n Type.Object(properties, { additionalProperties: false });\nconst operations = <T extends TSchema>(item: T, description: string, maxItems = MAX_OPERATIONS) =>\n Type.Optional(Type.Array(item, { maxItems, description }));\nconst oneOf = <const T extends readonly string[]>(values: T, description: string) =>\n Type.Union(\n values.map((value) => Type.Literal(value)),\n { description },\n );\n\nconst SearchQuery = closed({\n q: text('Search query.'),\n recency: Type.Optional(count('Whether to filter by recency, as a number of recent days.')),\n domains: Type.Optional(\n Type.Array(Type.String({ maxLength: 253 }), {\n maxItems: 20,\n description: 'Whether to filter by a specific list of domains.',\n }),\n ),\n});\n\nexport const WebRunParamsSchema = Type.Object(\n {\n search_query: operations(SearchQuery, 'Query the internet search engine for a given list of queries.', 4),\n image_query: operations(SearchQuery, 'Query the image search engine for a given list of queries.', 4),\n open: operations(\n closed({\n ref_id: text('Reference id or URL to open.'),\n lineno: Type.Optional(count('Line number to position the page at.')),\n }),\n 'Open pages by reference id or URL.',\n ),\n click: operations(\n closed({\n ref_id: text('Reference id containing the numbered link.'),\n id: count('Numbered link id to open.'),\n }),\n 'Open links from previously opened pages.',\n ),\n find: operations(\n closed({ ref_id: text('Reference id or URL to search within.'), pattern: text('Text pattern to find.') }),\n 'Find text patterns in pages.',\n ),\n screenshot: operations(\n closed({ ref_id: text('Reference id or URL to screenshot.'), pageno: count('Zero-indexed PDF page number.') }),\n 'Take screenshots of PDF pages.',\n ),\n finance: operations(\n closed({\n ticker: text('Ticker symbol to look up.'),\n type: oneOf(['equity', 'fund', 'crypto', 'index'], 'Asset type to look up.'),\n market: Type.Optional(text('ISO 3166-1 alpha-3 country code, \"OTC\", or \"\" for cryptocurrency.')),\n }),\n 'Look up prices for the given stock symbols.',\n ),\n weather: operations(\n closed({\n location: text('Location in \"Country, Area, City\" format.'),\n start: Type.Optional(text('Start date in YYYY-MM-DD format. Defaults to today.')),\n duration: Type.Optional(count('Number of days to return. Defaults to 7.')),\n }),\n 'Look up weather forecasts.',\n ),\n sports: operations(\n closed({\n tool: Type.Optional(oneOf(['sports'], 'Tool name for sports requests.')),\n fn: oneOf(['schedule', 'standings'], 'Sports function to call.'),\n league: oneOf(['nba', 'wnba', 'nfl', 'nhl', 'mlb', 'epl', 'ncaamb', 'ncaawb', 'ipl'], 'League to look up.'),\n team: Type.Optional(text('Team to look up, using the common 3 or 4 letter alias used in broadcasts.')),\n opponent: Type.Optional(text('Opponent to use with `team` when narrowing the lookup.')),\n date_from: Type.Optional(text('Start date in YYYY-MM-DD format.')),\n date_to: Type.Optional(text('End date in YYYY-MM-DD format.')),\n num_games: Type.Optional(count('Number of games to return.')),\n locale: Type.Optional(text('Locale for the lookup.')),\n }),\n 'Look up sports schedules and standings.',\n ),\n time: operations(\n closed({ utc_offset: text('UTC offset formatted like \"+03:00\".') }),\n 'Get time for the given UTC offsets.',\n ),\n response_length: Type.Optional(\n oneOf(['short', 'medium', 'long'], 'Set the length of the response to be returned.'),\n ),\n },\n { additionalProperties: false, minProperties: 1 },\n);\n\nexport type WebRunParams = Static<typeof WebRunParamsSchema>;\n","// Copyright 2025 OpenAI\n// SPDX-License-Identifier: Apache-2.0\n// Modified for Po from codex-rs ext/web-search/src/history.rs and tools/src/response_history.rs:\n// reads Pi messages, drops custom (injected) messages, and budgets characters instead of tokens.\n\n/** About Codex's 1,000-token assistant budget. */\nconst ASSISTANT_CHARACTERS = 4000;\nconst USER_CHARACTERS = 8000;\nconst USER_TURNS = 2;\n/** The host accepts at most 16 conversation items: two user messages plus this many assistant messages. */\nconst ASSISTANT_MESSAGES = 14;\n\ninterface ConversationMessage {\n role: 'user' | 'assistant';\n text: string;\n}\n\nfunction textParts(content: unknown): string[] {\n if (typeof content === 'string') return [content];\n if (!Array.isArray(content)) return [];\n return content.flatMap((part: unknown) =>\n typeof part === 'object' &&\n part !== null &&\n (part as { type?: unknown }).type === 'text' &&\n typeof (part as { text?: unknown }).text === 'string'\n ? [(part as { text: string }).text]\n : [],\n );\n}\n\n/**\n * The previous user text message, the assistant text that followed it (shared budget, oldest first),\n * and the latest user text message. Tool traffic, thinking, images and custom messages are never included.\n */\nexport function recentConversation(messages: readonly unknown[]): ConversationMessage[] | undefined {\n const visible: ConversationMessage[] = [];\n for (const message of messages) {\n if (typeof message !== 'object' || message === null) continue;\n const { role, content } = message as { role?: unknown; content?: unknown };\n if (role !== 'user' && role !== 'assistant') continue;\n const text = textParts(content).join('\\n');\n if (text.trim()) visible.push({ role, text });\n }\n const latest = visible.findLastIndex((message) => message.role === 'user');\n if (latest < 0) return undefined;\n const userIndexes = visible.flatMap((message, index) => (message.role === 'user' && index <= latest ? [index] : []));\n const tail = visible.slice(userIndexes.at(-USER_TURNS) ?? latest, latest + 1);\n let budget = ASSISTANT_CHARACTERS;\n let assistants = 0;\n return tail.flatMap<ConversationMessage>((message) => {\n if (message.role === 'user') return [{ role: 'user' as const, text: message.text.slice(0, USER_CHARACTERS) }];\n if (budget === 0 || assistants === ASSISTANT_MESSAGES) return [];\n const text = message.text.slice(0, budget);\n budget -= text.length;\n assistants += 1;\n return [{ role: 'assistant' as const, text }];\n });\n}\n","import type { DoomHeadlessExecutionContext, DoomHeadlessTool } from '@agimon-ai/po-core/headless';\n\nimport {\n DEFAULT_WEB_SEARCH_ACCESS,\n WEB_RUN_DESCRIPTION,\n WEB_RUN_NAME,\n WEB_SEARCH_ACCESS_ENV,\n WEB_SEARCH_LOGIN_MESSAGE,\n} from '../../constants/webRun';\nimport { WebRunParamsSchema } from '../../schemas/webRun';\nimport { recentConversation } from '../recentConversation';\nimport type { WebRunDetails, WebRunSource, WebSearchAccess } from './type';\n\nconst ACCESS_MODES: readonly WebSearchAccess[] = ['cached', 'indexed', 'live'];\nconst MAX_SOURCES = 20;\nconst MAX_URL = 2048;\nconst MAX_TITLE = 300;\n\nfunction accessMode(environment: Readonly<Record<string, string | undefined>>): WebSearchAccess {\n const value = environment[WEB_SEARCH_ACCESS_ENV]?.trim() || DEFAULT_WEB_SEARCH_ACCESS;\n const access = ACCESS_MODES.find((mode) => mode === value);\n if (!access) throw new Error(`${WEB_SEARCH_ACCESS_ENV} must be cached, indexed, or live.`);\n return access;\n}\n\n/** Only the excerpt sent to the same OpenAI account the chat already uses; other providers send commands only. */\nasync function conversationFor(context: DoomHeadlessExecutionContext) {\n if (context.model?.provider !== 'openai-codex' || !context.session.contextMessages) return undefined;\n try {\n return recentConversation(await context.session.contextMessages());\n } catch {\n // Documented fallback: search still runs, without conversation context, when history is unreadable.\n return undefined;\n }\n}\n\nfunction sources(results: readonly Record<string, unknown>[] | undefined): WebRunSource[] {\n const safe: WebRunSource[] = [];\n for (const result of results ?? []) {\n if (safe.length === MAX_SOURCES) break;\n if (typeof result.url !== 'string' || result.url.length > MAX_URL || !URL.canParse(result.url)) continue;\n const url = new URL(result.url);\n if (url.protocol !== 'https:' && url.protocol !== 'http:') continue;\n const title = typeof result.title === 'string' ? result.title.trim().slice(0, MAX_TITLE) : '';\n safe.push(title ? { url: url.href, title } : { url: url.href });\n }\n return safe;\n}\n\nexport function createWebRunTool(): DoomHeadlessTool<typeof WebRunParamsSchema> {\n return {\n name: WEB_RUN_NAME,\n label: WEB_RUN_NAME,\n description: WEB_RUN_DESCRIPTION,\n parameters: WebRunParamsSchema,\n executionMode: 'parallel',\n async execute(_toolCallId, params, signal, _onUpdate, context) {\n signal?.throwIfAborted();\n const capability = context.webSearch;\n if (!capability?.available()) throw new Error(WEB_SEARCH_LOGIN_MESSAGE);\n const access = accessMode(context.environment);\n const conversation = await conversationFor(context);\n signal?.throwIfAborted();\n const result = await capability.search({\n commands: params,\n access,\n ...(conversation ? { conversation } : {}),\n ...(signal ? { signal } : {}),\n });\n const details: WebRunDetails = {\n access,\n sharedConversation: conversation !== undefined,\n sources: sources(result.results),\n };\n return { content: [{ type: 'text', text: result.output }], details };\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"webRunTool-BkYexFJr.mjs","names":[],"sources":["../src/constants/webRun.ts","../src/schemas/webRun.ts","../src/services/recentConversation/index.ts","../src/services/webRunTool/index.ts"],"sourcesContent":["// Copyright 2025 OpenAI\n// SPDX-License-Identifier: Apache-2.0\n// Modified for Po from codex-rs ext/web-search/web_run_description.md: `web.run` renamed to `web_run`, error guidance added.\n\nexport const WEB_RUN_NAME = 'web_run';\nexport const WEB_RUN_DESCRIPTION = `Tool for accessing the internet.\n\n\n---\n\n## Examples of different commands available in this tool\n\nExamples of different commands available in this tool:\n* \\`search_query\\`: {\"search_query\": [{\"q\": \"What is the capital of France?\"}, {\"q\": \"What is the capital of belgium?\"}]}. Searches the internet for a given query (and optionally with a domain or recency filter)\n* \\`image_query\\`: {\"image_query\":[{\"q\": \"waterfalls\"}]}.\n* \\`open\\`: {\"open\": [{\"ref_id\": \"turn0search0\"}, {\"ref_id\": \"https://www.openai.com\", \"lineno\": 120}]}\n* \\`click\\`: {\"click\": [{\"ref_id\": \"turn0fetch3\", \"id\": 17}]}\n* \\`find\\`: {\"find\": [{\"ref_id\": \"turn0fetch3\", \"pattern\": \"Annie Case\"}]}\n* \\`screenshot\\`: {\"screenshot\": [{\"ref_id\": \"turn1view0\", \"pageno\": 0}, {\"ref_id\": \"turn1view0\", \"pageno\": 3}]}\n* \\`finance\\`: {\"finance\":[{\"ticker\":\"AMD\",\"type\":\"equity\",\"market\":\"USA\"}]}, {\"finance\":[{\"ticker\":\"BTC\",\"type\":\"crypto\",\"market\":\"\"}]}\n* \\`weather\\`: {\"weather\":[{\"location\":\"San Francisco, CA\"}]}\n* \\`sports\\`: {\"sports\":[{\"fn\":\"standings\",\"league\":\"nfl\"}, {\"fn\":\"schedule\",\"league\":\"nba\",\"team\":\"GSW\",\"date_from\":\"2025-02-24\"}]}\n* \\`time\\`: {\"time\":[{\"utc_offset\":\"+03:00\"}]}\n\n---\n\n## Usage hints\nTo use this tool efficiently:\n* Use multiple commands and queries in one call to get more results faster; e.g. {\"search_query\": [{\"q\": \"bitcoin news\"}], \"finance\":[{\"ticker\":\"BTC\",\"type\":\"crypto\",\"market\":\"\"}], \"find\": [{\"ref_id\": \"turn0search0\", \"pattern\": \"Annie Case\"}, {\"ref_id\": \"turn0search1\", \"pattern\": \"John Smith\"}]}\n* Use \"response_length\" to control the number of results returned by this tool, omit it if you intend to pass \"short\" in\n* Only write required parameters; do not write empty lists or nulls where they could be omitted.\n* \\`search_query\\` must have length at most 4 in each call. If it has length > 3, response_length must be medium or long\n* If you find yourself in a situation where you accidentally call the \\`web_run\\` tool, it's best just to send an empty query: {\"search_query\": [{\"q\": \"\"}]}.\n\n---\n\n## Decision boundary\n\nIf the user makes an explicit request to search the internet, find latest information, look up, etc (or to not do so), you must obey their request.\nWhen you make an assumption, always consider whether it is temporally stable; i.e. whether there's even a small (>10%) chance it has changed. If it is unstable, you must verify with browsing the internet for verification.\n\n<situations_where_you_must_browse_the_internet>\nBelow is a list of scenarios where browsing the internet MUST be used. PAY CLOSE ATTENTION: you MUST browse the internet in these cases. If you're unsure or on the fence, you MUST bias towards browsing the internet.\n- The information could have changed recently: for example news; prices; laws; schedules; product specs; sports scores; economic indicators; political/public/company figures (e.g. the question relates to 'the president of country A' or 'the CEO of company B', which might change over time); rules; regulations; standards; software libraries that could be updated; exchange rates; recommendations (i.e., recommendations about various topics or things might be informed by what currently exists / is popular / is safe / is unsafe / is in the zeitgeist / etc.); and many many many more categories -- again, if you're on the fence, you MUST browse the internet!\n - For news queries, prioritize more recent events, ensuring you compare publish dates and the date that the event happened.\n- The user is seeking recommendations that could lead them to spend substantial time or money -- researching products, restaurants, travel plans, etc.\n- The user wants (or would benefit from) direct quotes, links, or precise source attribution.\n- A specific page, paper, dataset, PDF, or site is referenced and you haven't been given its contents.\n- You're unsure about a fact, the topic is niche or emerging, or you suspect there's at least a 10% chance you will incorrectly recall it\n- High-stakes accuracy matters (medical, legal, financial guidance). For these you generally should search by default because this information is highly temporally unstable\n- The user explicitly says to search, browse, verify, or look it up.\n</situations_where_you_must_browse_the_internet>\n\n---\n\n## Citations\n\nResults from \\`web_run\\` include internal reference IDs such as \\`turn2search5\\`. Use\nthose reference IDs only in calls to \\`web_run\\`; do not expose them in the final\nresponse.\n\nCite sources in the final response using Markdown links:\n\n- Cite a single source as \\`[descriptive source title](https://example.com/page)\\`.\n- Cite multiple sources with separate Markdown links, for example\n \\`[first source](https://example.com/one), [second source](https://example.com/two)\\`.\n- Link directly to the page that supports the claim. Do not link to search result\n pages or use bare URLs.\n\nFormatting of citations:\n\n- Place each citation as near as possible to the claim it supports, normally at\n the end of the sentence or paragraph and after punctuation.\n- Do not place citations inside code fences.\n- Do not put citations on a line by themselves or collect all citations at the\n end of the response.\n\nIf you browse the internet, cite statements supported by web sources. Each cited\nsource must directly support the associated claim. Prefer primary and\nauthoritative sources, and use sources from different domains when the response\nbenefits from multiple perspectives.\n\n---\n\n## Special cases\nIf these conflict with any other instructions, these should take precedence.\n\n<special_cases>\n- When the user asks for information about how to use OpenAI products, (ChatGPT, the OpenAI API, etc.), you should check the code in local env and only browse as fallback, when you browse restrict your sources to official OpenAI websites using the domains filter, unless otherwise requested.\n- When using search to answer technical questions, you must only rely on primary sources (research papers, official documentation, etc.)\n- Clearly indicate when you are making an inference from sources.\n</special_cases>\n\n---\n\n## Word limits\nResponses may not excessively quote or draw on a specific source. There are several limits here:\n- **Limit on verbatim quotes:**\n - You may not quote more than 25 words verbatim from any single non-lyrical source, unless the source is reddit.\n - For song lyrics, verbatim quotes must be limited to at most 10 words.\n - Long quotes from reddit are allowed, as long as you indicate that those are direct quotes via a markdown blockquote starting with \">\", copy verbatim, and link the source.\n- **Word limits:**\n - Each webpage source in the sources has a word limit label formatted like \"[wordlim N]\", in which N is the maximum number of words in the whole response that are attributed to that source. If omitted, the word limit is 200 words.\n - Non-contiguous words derived from a given source must be counted to the word limit.\n - The summarization limit N is a maximum for each source.\n - When using multiple sources, their summarization limits add together. However, each article used must be relevant to the response.\n- **Copyright compliance:**\n - You must avoid providing full articles, long verbatim passages, or extensive direct quotes due to copyright concerns.\n - If the user asked for a verbatim quote, the response should provide a short compliant excerpt and then answer with paraphrases and summaries.\n - Again, this limit does not apply to reddit content, as long as it's appropriately indicated that those are direct quotes and you link to the source.\n\n---\n\n## Errors\n\nIf \\`web_run\\` reports a sign-in, permission, or quota error, tell the user and do not call it again this turn.\n`;\n/** Session environment variable selecting `cached`, `indexed` or `live` access. */\nexport const WEB_SEARCH_ACCESS_ENV = 'PO_WEB_SEARCH_ACCESS';\nexport const DEFAULT_WEB_SEARCH_ACCESS = 'cached';\nexport const WEB_SEARCH_LOGIN_MESSAGE =\n 'Web search needs a ChatGPT sign-in. Sign in to ChatGPT for Po features under Settings > Providers, or with /login.';\n","// Copyright 2025 OpenAI\n// SPDX-License-Identifier: Apache-2.0\n// Modified for Po from codex-rs codex-api/src/search.rs SearchCommands: closed objects and size bounds added.\n\nimport { type Static, type TSchema, Type } from 'typebox';\n\nconst MAX_TEXT = 2048;\nconst MAX_OPERATIONS = 10;\n\nconst text = (description: string) => Type.String({ maxLength: MAX_TEXT, description });\nconst count = (description: string) => Type.Integer({ minimum: 0, description });\nconst closed = <T extends Record<string, TSchema>>(properties: T) =>\n Type.Object(properties, { additionalProperties: false });\nconst operations = <T extends TSchema>(item: T, description: string, maxItems = MAX_OPERATIONS) =>\n Type.Optional(Type.Array(item, { maxItems, description }));\nconst oneOf = <const T extends readonly string[]>(values: T, description: string) =>\n Type.Union(\n values.map((value) => Type.Literal(value)),\n { description },\n );\n\nconst SearchQuery = closed({\n q: text('Search query.'),\n recency: Type.Optional(count('Whether to filter by recency, as a number of recent days.')),\n domains: Type.Optional(\n Type.Array(Type.String({ maxLength: 253 }), {\n maxItems: 20,\n description: 'Whether to filter by a specific list of domains.',\n }),\n ),\n});\n\nexport const WebRunParamsSchema = Type.Object(\n {\n search_query: operations(SearchQuery, 'Query the internet search engine for a given list of queries.', 4),\n image_query: operations(SearchQuery, 'Query the image search engine for a given list of queries.', 4),\n open: operations(\n closed({\n ref_id: text('Reference id or URL to open.'),\n lineno: Type.Optional(count('Line number to position the page at.')),\n }),\n 'Open pages by reference id or URL.',\n ),\n click: operations(\n closed({\n ref_id: text('Reference id containing the numbered link.'),\n id: count('Numbered link id to open.'),\n }),\n 'Open links from previously opened pages.',\n ),\n find: operations(\n closed({ ref_id: text('Reference id or URL to search within.'), pattern: text('Text pattern to find.') }),\n 'Find text patterns in pages.',\n ),\n screenshot: operations(\n closed({ ref_id: text('Reference id or URL to screenshot.'), pageno: count('Zero-indexed PDF page number.') }),\n 'Take screenshots of PDF pages.',\n ),\n finance: operations(\n closed({\n ticker: text('Ticker symbol to look up.'),\n type: oneOf(['equity', 'fund', 'crypto', 'index'], 'Asset type to look up.'),\n market: Type.Optional(text('ISO 3166-1 alpha-3 country code, \"OTC\", or \"\" for cryptocurrency.')),\n }),\n 'Look up prices for the given stock symbols.',\n ),\n weather: operations(\n closed({\n location: text('Location in \"Country, Area, City\" format.'),\n start: Type.Optional(text('Start date in YYYY-MM-DD format. Defaults to today.')),\n duration: Type.Optional(count('Number of days to return. Defaults to 7.')),\n }),\n 'Look up weather forecasts.',\n ),\n sports: operations(\n closed({\n tool: Type.Optional(oneOf(['sports'], 'Tool name for sports requests.')),\n fn: oneOf(['schedule', 'standings'], 'Sports function to call.'),\n league: oneOf(['nba', 'wnba', 'nfl', 'nhl', 'mlb', 'epl', 'ncaamb', 'ncaawb', 'ipl'], 'League to look up.'),\n team: Type.Optional(text('Team to look up, using the common 3 or 4 letter alias used in broadcasts.')),\n opponent: Type.Optional(text('Opponent to use with `team` when narrowing the lookup.')),\n date_from: Type.Optional(text('Start date in YYYY-MM-DD format.')),\n date_to: Type.Optional(text('End date in YYYY-MM-DD format.')),\n num_games: Type.Optional(count('Number of games to return.')),\n locale: Type.Optional(text('Locale for the lookup.')),\n }),\n 'Look up sports schedules and standings.',\n ),\n time: operations(\n closed({ utc_offset: text('UTC offset formatted like \"+03:00\".') }),\n 'Get time for the given UTC offsets.',\n ),\n response_length: Type.Optional(\n oneOf(['short', 'medium', 'long'], 'Set the length of the response to be returned.'),\n ),\n },\n { additionalProperties: false, minProperties: 1 },\n);\n\nexport type WebRunParams = Static<typeof WebRunParamsSchema>;\n","// Copyright 2025 OpenAI\n// SPDX-License-Identifier: Apache-2.0\n// Modified for Po from codex-rs ext/web-search/src/history.rs and tools/src/response_history.rs:\n// reads Pi messages, drops custom (injected) messages, and budgets characters instead of tokens.\n\n/** About Codex's 1,000-token assistant budget. */\nconst ASSISTANT_CHARACTERS = 4000;\nconst USER_CHARACTERS = 8000;\nconst USER_TURNS = 2;\n/** The host accepts at most 16 conversation items: two user messages plus this many assistant messages. */\nconst ASSISTANT_MESSAGES = 14;\n\ninterface ConversationMessage {\n role: 'user' | 'assistant';\n text: string;\n}\n\nfunction textParts(content: unknown): string[] {\n if (typeof content === 'string') return [content];\n if (!Array.isArray(content)) return [];\n return content.flatMap((part: unknown) =>\n typeof part === 'object' &&\n part !== null &&\n (part as { type?: unknown }).type === 'text' &&\n typeof (part as { text?: unknown }).text === 'string'\n ? [(part as { text: string }).text]\n : [],\n );\n}\n\n/**\n * The previous user text message, the assistant text that followed it (shared budget, oldest first),\n * and the latest user text message. Tool traffic, thinking, images and custom messages are never included.\n */\nexport function recentConversation(messages: readonly unknown[]): ConversationMessage[] | undefined {\n const visible: ConversationMessage[] = [];\n for (const message of messages) {\n if (typeof message !== 'object' || message === null) continue;\n const { role, content } = message as { role?: unknown; content?: unknown };\n if (role !== 'user' && role !== 'assistant') continue;\n const text = textParts(content).join('\\n');\n if (text.trim()) visible.push({ role, text });\n }\n const latest = visible.findLastIndex((message) => message.role === 'user');\n if (latest < 0) return undefined;\n const userIndexes = visible.flatMap((message, index) => (message.role === 'user' && index <= latest ? [index] : []));\n const tail = visible.slice(userIndexes.at(-USER_TURNS) ?? latest, latest + 1);\n let budget = ASSISTANT_CHARACTERS;\n let assistants = 0;\n return tail.flatMap<ConversationMessage>((message) => {\n if (message.role === 'user') return [{ role: 'user' as const, text: message.text.slice(0, USER_CHARACTERS) }];\n if (budget === 0 || assistants === ASSISTANT_MESSAGES) return [];\n const text = message.text.slice(0, budget);\n budget -= text.length;\n assistants += 1;\n return [{ role: 'assistant' as const, text }];\n });\n}\n","import type { PoHeadlessExecutionContext, PoHeadlessTool } from '@agimon-ai/po-core/headless';\n\nimport {\n DEFAULT_WEB_SEARCH_ACCESS,\n WEB_RUN_DESCRIPTION,\n WEB_RUN_NAME,\n WEB_SEARCH_ACCESS_ENV,\n WEB_SEARCH_LOGIN_MESSAGE,\n} from '../../constants/webRun';\nimport { WebRunParamsSchema } from '../../schemas/webRun';\nimport { recentConversation } from '../recentConversation';\nimport type { WebRunDetails, WebRunSource, WebSearchAccess } from './type';\n\nconst ACCESS_MODES: readonly WebSearchAccess[] = ['cached', 'indexed', 'live'];\nconst MAX_SOURCES = 20;\nconst MAX_URL = 2048;\nconst MAX_TITLE = 300;\n\nfunction accessMode(environment: Readonly<Record<string, string | undefined>>): WebSearchAccess {\n const value = environment[WEB_SEARCH_ACCESS_ENV]?.trim() || DEFAULT_WEB_SEARCH_ACCESS;\n const access = ACCESS_MODES.find((mode) => mode === value);\n if (!access) throw new Error(`${WEB_SEARCH_ACCESS_ENV} must be cached, indexed, or live.`);\n return access;\n}\n\n/** Only the excerpt sent to the same OpenAI account the chat already uses; other providers send commands only. */\nasync function conversationFor(context: PoHeadlessExecutionContext) {\n if (context.model?.provider !== 'openai-codex' || !context.session.contextMessages) return undefined;\n try {\n return recentConversation(await context.session.contextMessages());\n } catch {\n // Documented fallback: search still runs, without conversation context, when history is unreadable.\n return undefined;\n }\n}\n\nfunction sources(results: readonly Record<string, unknown>[] | undefined): WebRunSource[] {\n const safe: WebRunSource[] = [];\n for (const result of results ?? []) {\n if (safe.length === MAX_SOURCES) break;\n if (typeof result.url !== 'string' || result.url.length > MAX_URL || !URL.canParse(result.url)) continue;\n const url = new URL(result.url);\n if (url.protocol !== 'https:' && url.protocol !== 'http:') continue;\n const title = typeof result.title === 'string' ? result.title.trim().slice(0, MAX_TITLE) : '';\n safe.push(title ? { url: url.href, title } : { url: url.href });\n }\n return safe;\n}\n\nexport function createWebRunTool(): PoHeadlessTool<typeof WebRunParamsSchema> {\n return {\n name: WEB_RUN_NAME,\n label: WEB_RUN_NAME,\n description: WEB_RUN_DESCRIPTION,\n parameters: WebRunParamsSchema,\n executionMode: 'parallel',\n async execute(_toolCallId, params, signal, _onUpdate, context) {\n signal?.throwIfAborted();\n const capability = context.webSearch;\n if (!capability?.available()) throw new Error(WEB_SEARCH_LOGIN_MESSAGE);\n const access = accessMode(context.environment);\n const conversation = await conversationFor(context);\n signal?.throwIfAborted();\n const result = await capability.search({\n commands: params,\n access,\n ...(conversation ? { conversation } : {}),\n ...(signal ? { signal } : {}),\n });\n const details: WebRunDetails = {\n access,\n sharedConversation: conversation !== undefined,\n sources: sources(result.results),\n };\n return { content: [{ type: 'text', text: result.output }], details };\n },\n };\n}\n"],"mappings":";;AAIA,MAAa,eAAe;AAC5B,MAAa,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiHnC,MAAa,wBAAwB;AAErC,MAAa,2BACX;;;ACnHF,MAAM,WAAW;AACjB,MAAM,iBAAiB;AAEvB,MAAM,QAAQ,gBAAwB,KAAK,OAAO;CAAE,WAAW;CAAU;AAAY,CAAC;AACtF,MAAM,SAAS,gBAAwB,KAAK,QAAQ;CAAE,SAAS;CAAG;AAAY,CAAC;AAC/E,MAAM,UAA6C,eACjD,KAAK,OAAO,YAAY,EAAE,sBAAsB,MAAM,CAAC;AACzD,MAAM,cAAiC,MAAS,aAAqB,WAAW,mBAC9E,KAAK,SAAS,KAAK,MAAM,MAAM;CAAE;CAAU;AAAY,CAAC,CAAC;AAC3D,MAAM,SAA4C,QAAW,gBAC3D,KAAK,MACH,OAAO,KAAK,UAAU,KAAK,QAAQ,KAAK,CAAC,GACzC,EAAE,YAAY,CAChB;AAEF,MAAM,cAAc,OAAO;CACzB,GAAG,KAAK,eAAe;CACvB,SAAS,KAAK,SAAS,MAAM,2DAA2D,CAAC;CACzF,SAAS,KAAK,SACZ,KAAK,MAAM,KAAK,OAAO,EAAE,WAAW,IAAI,CAAC,GAAG;EAC1C,UAAU;EACV,aAAa;CACf,CAAC,CACH;AACF,CAAC;AAED,MAAa,qBAAqB,KAAK,OACrC;CACE,cAAc,WAAW,aAAa,iEAAiE,CAAC;CACxG,aAAa,WAAW,aAAa,8DAA8D,CAAC;CACpG,MAAM,WACJ,OAAO;EACL,QAAQ,KAAK,8BAA8B;EAC3C,QAAQ,KAAK,SAAS,MAAM,sCAAsC,CAAC;CACrE,CAAC,GACD,oCACF;CACA,OAAO,WACL,OAAO;EACL,QAAQ,KAAK,4CAA4C;EACzD,IAAI,MAAM,2BAA2B;CACvC,CAAC,GACD,0CACF;CACA,MAAM,WACJ,OAAO;EAAE,QAAQ,KAAK,uCAAuC;EAAG,SAAS,KAAK,uBAAuB;CAAE,CAAC,GACxG,8BACF;CACA,YAAY,WACV,OAAO;EAAE,QAAQ,KAAK,oCAAoC;EAAG,QAAQ,MAAM,+BAA+B;CAAE,CAAC,GAC7G,gCACF;CACA,SAAS,WACP,OAAO;EACL,QAAQ,KAAK,2BAA2B;EACxC,MAAM,MAAM;GAAC;GAAU;GAAQ;GAAU;EAAO,GAAG,wBAAwB;EAC3E,QAAQ,KAAK,SAAS,KAAK,uEAAmE,CAAC;CACjG,CAAC,GACD,6CACF;CACA,SAAS,WACP,OAAO;EACL,UAAU,KAAK,6CAA2C;EAC1D,OAAO,KAAK,SAAS,KAAK,qDAAqD,CAAC;EAChF,UAAU,KAAK,SAAS,MAAM,0CAA0C,CAAC;CAC3E,CAAC,GACD,4BACF;CACA,QAAQ,WACN,OAAO;EACL,MAAM,KAAK,SAAS,MAAM,CAAC,QAAQ,GAAG,gCAAgC,CAAC;EACvE,IAAI,MAAM,CAAC,YAAY,WAAW,GAAG,0BAA0B;EAC/D,QAAQ,MAAM;GAAC;GAAO;GAAQ;GAAO;GAAO;GAAO;GAAO;GAAU;GAAU;EAAK,GAAG,oBAAoB;EAC1G,MAAM,KAAK,SAAS,KAAK,2EAA2E,CAAC;EACrG,UAAU,KAAK,SAAS,KAAK,wDAAwD,CAAC;EACtF,WAAW,KAAK,SAAS,KAAK,kCAAkC,CAAC;EACjE,SAAS,KAAK,SAAS,KAAK,gCAAgC,CAAC;EAC7D,WAAW,KAAK,SAAS,MAAM,4BAA4B,CAAC;EAC5D,QAAQ,KAAK,SAAS,KAAK,wBAAwB,CAAC;CACtD,CAAC,GACD,yCACF;CACA,MAAM,WACJ,OAAO,EAAE,YAAY,KAAK,uCAAqC,EAAE,CAAC,GAClE,qCACF;CACA,iBAAiB,KAAK,SACpB,MAAM;EAAC;EAAS;EAAU;CAAM,GAAG,gDAAgD,CACrF;AACF,GACA;CAAE,sBAAsB;CAAO,eAAe;AAAE,CAClD;;;;AC3FA,MAAM,uBAAuB;AAC7B,MAAM,kBAAkB;;AAGxB,MAAM,qBAAqB;AAO3B,SAAS,UAAU,SAA4B;CAC7C,IAAI,OAAO,YAAY,UAAU,OAAO,CAAC,OAAO;CAChD,IAAI,CAAC,MAAM,QAAQ,OAAO,GAAG,OAAO,CAAC;CACrC,OAAO,QAAQ,SAAS,SACtB,OAAO,SAAS,YAChB,SAAS,QACR,KAA4B,SAAS,UACtC,OAAQ,KAA4B,SAAS,WACzC,CAAE,KAA0B,IAAI,IAChC,CAAC,CACP;AACF;;;;;AAMA,SAAgB,mBAAmB,UAAiE;CAClG,MAAM,UAAiC,CAAC;CACxC,KAAK,MAAM,WAAW,UAAU;EAC9B,IAAI,OAAO,YAAY,YAAY,YAAY,MAAM;EACrD,MAAM,EAAE,MAAM,YAAY;EAC1B,IAAI,SAAS,UAAU,SAAS,aAAa;EAC7C,MAAM,OAAO,UAAU,OAAO,CAAC,CAAC,KAAK,IAAI;EACzC,IAAI,KAAK,KAAK,GAAG,QAAQ,KAAK;GAAE;GAAM;EAAK,CAAC;CAC9C;CACA,MAAM,SAAS,QAAQ,eAAe,YAAY,QAAQ,SAAS,MAAM;CACzE,IAAI,SAAS,GAAG,OAAO,KAAA;CACvB,MAAM,cAAc,QAAQ,SAAS,SAAS,UAAW,QAAQ,SAAS,UAAU,SAAS,SAAS,CAAC,KAAK,IAAI,CAAC,CAAE;CACnH,MAAM,OAAO,QAAQ,MAAM,YAAY,GAAG,EAAW,KAAK,QAAQ,SAAS,CAAC;CAC5E,IAAI,SAAS;CACb,IAAI,aAAa;CACjB,OAAO,KAAK,SAA8B,YAAY;EACpD,IAAI,QAAQ,SAAS,QAAQ,OAAO,CAAC;GAAE,MAAM;GAAiB,MAAM,QAAQ,KAAK,MAAM,GAAG,eAAe;EAAE,CAAC;EAC5G,IAAI,WAAW,KAAK,eAAe,oBAAoB,OAAO,CAAC;EAC/D,MAAM,OAAO,QAAQ,KAAK,MAAM,GAAG,MAAM;EACzC,UAAU,KAAK;EACf,cAAc;EACd,OAAO,CAAC;GAAE,MAAM;GAAsB;EAAK,CAAC;CAC9C,CAAC;AACH;;;AC5CA,MAAM,eAA2C;CAAC;CAAU;CAAW;AAAM;AAC7E,MAAM,cAAc;AACpB,MAAM,UAAU;AAChB,MAAM,YAAY;AAElB,SAAS,WAAW,aAA4E;CAC9F,MAAM,QAAQ,YAAA,uBAAkC,EAAE,KAAK,KAAA;CACvD,MAAM,SAAS,aAAa,MAAM,SAAS,SAAS,KAAK;CACzD,IAAI,CAAC,QAAQ,MAAM,IAAI,MAAM,GAAG,sBAAsB,mCAAmC;CACzF,OAAO;AACT;;AAGA,eAAe,gBAAgB,SAAqC;CAClE,IAAI,QAAQ,OAAO,aAAa,kBAAkB,CAAC,QAAQ,QAAQ,iBAAiB,OAAO,KAAA;CAC3F,IAAI;EACF,OAAO,mBAAmB,MAAM,QAAQ,QAAQ,gBAAgB,CAAC;CACnE,QAAQ;EAEN;CACF;AACF;AAEA,SAAS,QAAQ,SAAyE;CACxF,MAAM,OAAuB,CAAC;CAC9B,KAAK,MAAM,UAAU,WAAW,CAAC,GAAG;EAClC,IAAI,KAAK,WAAW,aAAa;EACjC,IAAI,OAAO,OAAO,QAAQ,YAAY,OAAO,IAAI,SAAS,WAAW,CAAC,IAAI,SAAS,OAAO,GAAG,GAAG;EAChG,MAAM,MAAM,IAAI,IAAI,OAAO,GAAG;EAC9B,IAAI,IAAI,aAAa,YAAY,IAAI,aAAa,SAAS;EAC3D,MAAM,QAAQ,OAAO,OAAO,UAAU,WAAW,OAAO,MAAM,KAAK,CAAC,CAAC,MAAM,GAAG,SAAS,IAAI;EAC3F,KAAK,KAAK,QAAQ;GAAE,KAAK,IAAI;GAAM;EAAM,IAAI,EAAE,KAAK,IAAI,KAAK,CAAC;CAChE;CACA,OAAO;AACT;AAEA,SAAgB,mBAA8D;CAC5E,OAAO;EACL,MAAM;EACN,OAAO;EACP,aAAa;EACb,YAAY;EACZ,eAAe;EACf,MAAM,QAAQ,aAAa,QAAQ,QAAQ,WAAW,SAAS;GAC7D,QAAQ,eAAe;GACvB,MAAM,aAAa,QAAQ;GAC3B,IAAI,CAAC,YAAY,UAAU,GAAG,MAAM,IAAI,MAAM,wBAAwB;GACtE,MAAM,SAAS,WAAW,QAAQ,WAAW;GAC7C,MAAM,eAAe,MAAM,gBAAgB,OAAO;GAClD,QAAQ,eAAe;GACvB,MAAM,SAAS,MAAM,WAAW,OAAO;IACrC,UAAU;IACV;IACA,GAAI,eAAe,EAAE,aAAa,IAAI,CAAC;IACvC,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;GAC7B,CAAC;GACD,MAAM,UAAyB;IAC7B;IACA,oBAAoB,iBAAiB,KAAA;IACrC,SAAS,QAAQ,OAAO,OAAO;GACjC;GACA,OAAO;IAAE,SAAS,CAAC;KAAE,MAAM;KAAQ,MAAM,OAAO;IAAO,CAAC;IAAG;GAAQ;EACrE;CACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"webRunTool-CuCm5OZL.cjs","names":["Type"],"sources":["../src/constants/webRun.ts","../src/schemas/webRun.ts","../src/services/recentConversation/index.ts","../src/services/webRunTool/index.ts"],"sourcesContent":["// Copyright 2025 OpenAI\n// SPDX-License-Identifier: Apache-2.0\n// Modified for Po from codex-rs ext/web-search/web_run_description.md: `web.run` renamed to `web_run`, error guidance added.\n\nexport const WEB_RUN_NAME = 'web_run';\nexport const WEB_RUN_DESCRIPTION = `Tool for accessing the internet.\n\n\n---\n\n## Examples of different commands available in this tool\n\nExamples of different commands available in this tool:\n* \\`search_query\\`: {\"search_query\": [{\"q\": \"What is the capital of France?\"}, {\"q\": \"What is the capital of belgium?\"}]}. Searches the internet for a given query (and optionally with a domain or recency filter)\n* \\`image_query\\`: {\"image_query\":[{\"q\": \"waterfalls\"}]}.\n* \\`open\\`: {\"open\": [{\"ref_id\": \"turn0search0\"}, {\"ref_id\": \"https://www.openai.com\", \"lineno\": 120}]}\n* \\`click\\`: {\"click\": [{\"ref_id\": \"turn0fetch3\", \"id\": 17}]}\n* \\`find\\`: {\"find\": [{\"ref_id\": \"turn0fetch3\", \"pattern\": \"Annie Case\"}]}\n* \\`screenshot\\`: {\"screenshot\": [{\"ref_id\": \"turn1view0\", \"pageno\": 0}, {\"ref_id\": \"turn1view0\", \"pageno\": 3}]}\n* \\`finance\\`: {\"finance\":[{\"ticker\":\"AMD\",\"type\":\"equity\",\"market\":\"USA\"}]}, {\"finance\":[{\"ticker\":\"BTC\",\"type\":\"crypto\",\"market\":\"\"}]}\n* \\`weather\\`: {\"weather\":[{\"location\":\"San Francisco, CA\"}]}\n* \\`sports\\`: {\"sports\":[{\"fn\":\"standings\",\"league\":\"nfl\"}, {\"fn\":\"schedule\",\"league\":\"nba\",\"team\":\"GSW\",\"date_from\":\"2025-02-24\"}]}\n* \\`time\\`: {\"time\":[{\"utc_offset\":\"+03:00\"}]}\n\n---\n\n## Usage hints\nTo use this tool efficiently:\n* Use multiple commands and queries in one call to get more results faster; e.g. {\"search_query\": [{\"q\": \"bitcoin news\"}], \"finance\":[{\"ticker\":\"BTC\",\"type\":\"crypto\",\"market\":\"\"}], \"find\": [{\"ref_id\": \"turn0search0\", \"pattern\": \"Annie Case\"}, {\"ref_id\": \"turn0search1\", \"pattern\": \"John Smith\"}]}\n* Use \"response_length\" to control the number of results returned by this tool, omit it if you intend to pass \"short\" in\n* Only write required parameters; do not write empty lists or nulls where they could be omitted.\n* \\`search_query\\` must have length at most 4 in each call. If it has length > 3, response_length must be medium or long\n* If you find yourself in a situation where you accidentally call the \\`web_run\\` tool, it's best just to send an empty query: {\"search_query\": [{\"q\": \"\"}]}.\n\n---\n\n## Decision boundary\n\nIf the user makes an explicit request to search the internet, find latest information, look up, etc (or to not do so), you must obey their request.\nWhen you make an assumption, always consider whether it is temporally stable; i.e. whether there's even a small (>10%) chance it has changed. If it is unstable, you must verify with browsing the internet for verification.\n\n<situations_where_you_must_browse_the_internet>\nBelow is a list of scenarios where browsing the internet MUST be used. PAY CLOSE ATTENTION: you MUST browse the internet in these cases. If you're unsure or on the fence, you MUST bias towards browsing the internet.\n- The information could have changed recently: for example news; prices; laws; schedules; product specs; sports scores; economic indicators; political/public/company figures (e.g. the question relates to 'the president of country A' or 'the CEO of company B', which might change over time); rules; regulations; standards; software libraries that could be updated; exchange rates; recommendations (i.e., recommendations about various topics or things might be informed by what currently exists / is popular / is safe / is unsafe / is in the zeitgeist / etc.); and many many many more categories -- again, if you're on the fence, you MUST browse the internet!\n - For news queries, prioritize more recent events, ensuring you compare publish dates and the date that the event happened.\n- The user is seeking recommendations that could lead them to spend substantial time or money -- researching products, restaurants, travel plans, etc.\n- The user wants (or would benefit from) direct quotes, links, or precise source attribution.\n- A specific page, paper, dataset, PDF, or site is referenced and you haven't been given its contents.\n- You're unsure about a fact, the topic is niche or emerging, or you suspect there's at least a 10% chance you will incorrectly recall it\n- High-stakes accuracy matters (medical, legal, financial guidance). For these you generally should search by default because this information is highly temporally unstable\n- The user explicitly says to search, browse, verify, or look it up.\n</situations_where_you_must_browse_the_internet>\n\n---\n\n## Citations\n\nResults from \\`web_run\\` include internal reference IDs such as \\`turn2search5\\`. Use\nthose reference IDs only in calls to \\`web_run\\`; do not expose them in the final\nresponse.\n\nCite sources in the final response using Markdown links:\n\n- Cite a single source as \\`[descriptive source title](https://example.com/page)\\`.\n- Cite multiple sources with separate Markdown links, for example\n \\`[first source](https://example.com/one), [second source](https://example.com/two)\\`.\n- Link directly to the page that supports the claim. Do not link to search result\n pages or use bare URLs.\n\nFormatting of citations:\n\n- Place each citation as near as possible to the claim it supports, normally at\n the end of the sentence or paragraph and after punctuation.\n- Do not place citations inside code fences.\n- Do not put citations on a line by themselves or collect all citations at the\n end of the response.\n\nIf you browse the internet, cite statements supported by web sources. Each cited\nsource must directly support the associated claim. Prefer primary and\nauthoritative sources, and use sources from different domains when the response\nbenefits from multiple perspectives.\n\n---\n\n## Special cases\nIf these conflict with any other instructions, these should take precedence.\n\n<special_cases>\n- When the user asks for information about how to use OpenAI products, (ChatGPT, the OpenAI API, etc.), you should check the code in local env and only browse as fallback, when you browse restrict your sources to official OpenAI websites using the domains filter, unless otherwise requested.\n- When using search to answer technical questions, you must only rely on primary sources (research papers, official documentation, etc.)\n- Clearly indicate when you are making an inference from sources.\n</special_cases>\n\n---\n\n## Word limits\nResponses may not excessively quote or draw on a specific source. There are several limits here:\n- **Limit on verbatim quotes:**\n - You may not quote more than 25 words verbatim from any single non-lyrical source, unless the source is reddit.\n - For song lyrics, verbatim quotes must be limited to at most 10 words.\n - Long quotes from reddit are allowed, as long as you indicate that those are direct quotes via a markdown blockquote starting with \">\", copy verbatim, and link the source.\n- **Word limits:**\n - Each webpage source in the sources has a word limit label formatted like \"[wordlim N]\", in which N is the maximum number of words in the whole response that are attributed to that source. If omitted, the word limit is 200 words.\n - Non-contiguous words derived from a given source must be counted to the word limit.\n - The summarization limit N is a maximum for each source.\n - When using multiple sources, their summarization limits add together. However, each article used must be relevant to the response.\n- **Copyright compliance:**\n - You must avoid providing full articles, long verbatim passages, or extensive direct quotes due to copyright concerns.\n - If the user asked for a verbatim quote, the response should provide a short compliant excerpt and then answer with paraphrases and summaries.\n - Again, this limit does not apply to reddit content, as long as it's appropriately indicated that those are direct quotes and you link to the source.\n\n---\n\n## Errors\n\nIf \\`web_run\\` reports a sign-in, permission, or quota error, tell the user and do not call it again this turn.\n`;\n/** Session environment variable selecting `cached`, `indexed` or `live` access. */\nexport const WEB_SEARCH_ACCESS_ENV = 'PO_WEB_SEARCH_ACCESS';\nexport const DEFAULT_WEB_SEARCH_ACCESS = 'cached';\nexport const WEB_SEARCH_LOGIN_MESSAGE =\n 'Web search needs a ChatGPT sign-in. Sign in to ChatGPT for Po features under Settings > Providers, or with /login.';\n","// Copyright 2025 OpenAI\n// SPDX-License-Identifier: Apache-2.0\n// Modified for Po from codex-rs codex-api/src/search.rs SearchCommands: closed objects and size bounds added.\n\nimport { type Static, type TSchema, Type } from 'typebox';\n\nconst MAX_TEXT = 2048;\nconst MAX_OPERATIONS = 10;\n\nconst text = (description: string) => Type.String({ maxLength: MAX_TEXT, description });\nconst count = (description: string) => Type.Integer({ minimum: 0, description });\nconst closed = <T extends Record<string, TSchema>>(properties: T) =>\n Type.Object(properties, { additionalProperties: false });\nconst operations = <T extends TSchema>(item: T, description: string, maxItems = MAX_OPERATIONS) =>\n Type.Optional(Type.Array(item, { maxItems, description }));\nconst oneOf = <const T extends readonly string[]>(values: T, description: string) =>\n Type.Union(\n values.map((value) => Type.Literal(value)),\n { description },\n );\n\nconst SearchQuery = closed({\n q: text('Search query.'),\n recency: Type.Optional(count('Whether to filter by recency, as a number of recent days.')),\n domains: Type.Optional(\n Type.Array(Type.String({ maxLength: 253 }), {\n maxItems: 20,\n description: 'Whether to filter by a specific list of domains.',\n }),\n ),\n});\n\nexport const WebRunParamsSchema = Type.Object(\n {\n search_query: operations(SearchQuery, 'Query the internet search engine for a given list of queries.', 4),\n image_query: operations(SearchQuery, 'Query the image search engine for a given list of queries.', 4),\n open: operations(\n closed({\n ref_id: text('Reference id or URL to open.'),\n lineno: Type.Optional(count('Line number to position the page at.')),\n }),\n 'Open pages by reference id or URL.',\n ),\n click: operations(\n closed({\n ref_id: text('Reference id containing the numbered link.'),\n id: count('Numbered link id to open.'),\n }),\n 'Open links from previously opened pages.',\n ),\n find: operations(\n closed({ ref_id: text('Reference id or URL to search within.'), pattern: text('Text pattern to find.') }),\n 'Find text patterns in pages.',\n ),\n screenshot: operations(\n closed({ ref_id: text('Reference id or URL to screenshot.'), pageno: count('Zero-indexed PDF page number.') }),\n 'Take screenshots of PDF pages.',\n ),\n finance: operations(\n closed({\n ticker: text('Ticker symbol to look up.'),\n type: oneOf(['equity', 'fund', 'crypto', 'index'], 'Asset type to look up.'),\n market: Type.Optional(text('ISO 3166-1 alpha-3 country code, \"OTC\", or \"\" for cryptocurrency.')),\n }),\n 'Look up prices for the given stock symbols.',\n ),\n weather: operations(\n closed({\n location: text('Location in \"Country, Area, City\" format.'),\n start: Type.Optional(text('Start date in YYYY-MM-DD format. Defaults to today.')),\n duration: Type.Optional(count('Number of days to return. Defaults to 7.')),\n }),\n 'Look up weather forecasts.',\n ),\n sports: operations(\n closed({\n tool: Type.Optional(oneOf(['sports'], 'Tool name for sports requests.')),\n fn: oneOf(['schedule', 'standings'], 'Sports function to call.'),\n league: oneOf(['nba', 'wnba', 'nfl', 'nhl', 'mlb', 'epl', 'ncaamb', 'ncaawb', 'ipl'], 'League to look up.'),\n team: Type.Optional(text('Team to look up, using the common 3 or 4 letter alias used in broadcasts.')),\n opponent: Type.Optional(text('Opponent to use with `team` when narrowing the lookup.')),\n date_from: Type.Optional(text('Start date in YYYY-MM-DD format.')),\n date_to: Type.Optional(text('End date in YYYY-MM-DD format.')),\n num_games: Type.Optional(count('Number of games to return.')),\n locale: Type.Optional(text('Locale for the lookup.')),\n }),\n 'Look up sports schedules and standings.',\n ),\n time: operations(\n closed({ utc_offset: text('UTC offset formatted like \"+03:00\".') }),\n 'Get time for the given UTC offsets.',\n ),\n response_length: Type.Optional(\n oneOf(['short', 'medium', 'long'], 'Set the length of the response to be returned.'),\n ),\n },\n { additionalProperties: false, minProperties: 1 },\n);\n\nexport type WebRunParams = Static<typeof WebRunParamsSchema>;\n","// Copyright 2025 OpenAI\n// SPDX-License-Identifier: Apache-2.0\n// Modified for Po from codex-rs ext/web-search/src/history.rs and tools/src/response_history.rs:\n// reads Pi messages, drops custom (injected) messages, and budgets characters instead of tokens.\n\n/** About Codex's 1,000-token assistant budget. */\nconst ASSISTANT_CHARACTERS = 4000;\nconst USER_CHARACTERS = 8000;\nconst USER_TURNS = 2;\n/** The host accepts at most 16 conversation items: two user messages plus this many assistant messages. */\nconst ASSISTANT_MESSAGES = 14;\n\ninterface ConversationMessage {\n role: 'user' | 'assistant';\n text: string;\n}\n\nfunction textParts(content: unknown): string[] {\n if (typeof content === 'string') return [content];\n if (!Array.isArray(content)) return [];\n return content.flatMap((part: unknown) =>\n typeof part === 'object' &&\n part !== null &&\n (part as { type?: unknown }).type === 'text' &&\n typeof (part as { text?: unknown }).text === 'string'\n ? [(part as { text: string }).text]\n : [],\n );\n}\n\n/**\n * The previous user text message, the assistant text that followed it (shared budget, oldest first),\n * and the latest user text message. Tool traffic, thinking, images and custom messages are never included.\n */\nexport function recentConversation(messages: readonly unknown[]): ConversationMessage[] | undefined {\n const visible: ConversationMessage[] = [];\n for (const message of messages) {\n if (typeof message !== 'object' || message === null) continue;\n const { role, content } = message as { role?: unknown; content?: unknown };\n if (role !== 'user' && role !== 'assistant') continue;\n const text = textParts(content).join('\\n');\n if (text.trim()) visible.push({ role, text });\n }\n const latest = visible.findLastIndex((message) => message.role === 'user');\n if (latest < 0) return undefined;\n const userIndexes = visible.flatMap((message, index) => (message.role === 'user' && index <= latest ? [index] : []));\n const tail = visible.slice(userIndexes.at(-USER_TURNS) ?? latest, latest + 1);\n let budget = ASSISTANT_CHARACTERS;\n let assistants = 0;\n return tail.flatMap<ConversationMessage>((message) => {\n if (message.role === 'user') return [{ role: 'user' as const, text: message.text.slice(0, USER_CHARACTERS) }];\n if (budget === 0 || assistants === ASSISTANT_MESSAGES) return [];\n const text = message.text.slice(0, budget);\n budget -= text.length;\n assistants += 1;\n return [{ role: 'assistant' as const, text }];\n });\n}\n","import type { DoomHeadlessExecutionContext, DoomHeadlessTool } from '@agimon-ai/po-core/headless';\n\nimport {\n DEFAULT_WEB_SEARCH_ACCESS,\n WEB_RUN_DESCRIPTION,\n WEB_RUN_NAME,\n WEB_SEARCH_ACCESS_ENV,\n WEB_SEARCH_LOGIN_MESSAGE,\n} from '../../constants/webRun';\nimport { WebRunParamsSchema } from '../../schemas/webRun';\nimport { recentConversation } from '../recentConversation';\nimport type { WebRunDetails, WebRunSource, WebSearchAccess } from './type';\n\nconst ACCESS_MODES: readonly WebSearchAccess[] = ['cached', 'indexed', 'live'];\nconst MAX_SOURCES = 20;\nconst MAX_URL = 2048;\nconst MAX_TITLE = 300;\n\nfunction accessMode(environment: Readonly<Record<string, string | undefined>>): WebSearchAccess {\n const value = environment[WEB_SEARCH_ACCESS_ENV]?.trim() || DEFAULT_WEB_SEARCH_ACCESS;\n const access = ACCESS_MODES.find((mode) => mode === value);\n if (!access) throw new Error(`${WEB_SEARCH_ACCESS_ENV} must be cached, indexed, or live.`);\n return access;\n}\n\n/** Only the excerpt sent to the same OpenAI account the chat already uses; other providers send commands only. */\nasync function conversationFor(context: DoomHeadlessExecutionContext) {\n if (context.model?.provider !== 'openai-codex' || !context.session.contextMessages) return undefined;\n try {\n return recentConversation(await context.session.contextMessages());\n } catch {\n // Documented fallback: search still runs, without conversation context, when history is unreadable.\n return undefined;\n }\n}\n\nfunction sources(results: readonly Record<string, unknown>[] | undefined): WebRunSource[] {\n const safe: WebRunSource[] = [];\n for (const result of results ?? []) {\n if (safe.length === MAX_SOURCES) break;\n if (typeof result.url !== 'string' || result.url.length > MAX_URL || !URL.canParse(result.url)) continue;\n const url = new URL(result.url);\n if (url.protocol !== 'https:' && url.protocol !== 'http:') continue;\n const title = typeof result.title === 'string' ? result.title.trim().slice(0, MAX_TITLE) : '';\n safe.push(title ? { url: url.href, title } : { url: url.href });\n }\n return safe;\n}\n\nexport function createWebRunTool(): DoomHeadlessTool<typeof WebRunParamsSchema> {\n return {\n name: WEB_RUN_NAME,\n label: WEB_RUN_NAME,\n description: WEB_RUN_DESCRIPTION,\n parameters: WebRunParamsSchema,\n executionMode: 'parallel',\n async execute(_toolCallId, params, signal, _onUpdate, context) {\n signal?.throwIfAborted();\n const capability = context.webSearch;\n if (!capability?.available()) throw new Error(WEB_SEARCH_LOGIN_MESSAGE);\n const access = accessMode(context.environment);\n const conversation = await conversationFor(context);\n signal?.throwIfAborted();\n const result = await capability.search({\n commands: params,\n access,\n ...(conversation ? { conversation } : {}),\n ...(signal ? { signal } : {}),\n });\n const details: WebRunDetails = {\n access,\n sharedConversation: conversation !== undefined,\n sources: sources(result.results),\n };\n return { content: [{ type: 'text', text: result.output }], details };\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"webRunTool-CuCm5OZL.cjs","names":["Type"],"sources":["../src/constants/webRun.ts","../src/schemas/webRun.ts","../src/services/recentConversation/index.ts","../src/services/webRunTool/index.ts"],"sourcesContent":["// Copyright 2025 OpenAI\n// SPDX-License-Identifier: Apache-2.0\n// Modified for Po from codex-rs ext/web-search/web_run_description.md: `web.run` renamed to `web_run`, error guidance added.\n\nexport const WEB_RUN_NAME = 'web_run';\nexport const WEB_RUN_DESCRIPTION = `Tool for accessing the internet.\n\n\n---\n\n## Examples of different commands available in this tool\n\nExamples of different commands available in this tool:\n* \\`search_query\\`: {\"search_query\": [{\"q\": \"What is the capital of France?\"}, {\"q\": \"What is the capital of belgium?\"}]}. Searches the internet for a given query (and optionally with a domain or recency filter)\n* \\`image_query\\`: {\"image_query\":[{\"q\": \"waterfalls\"}]}.\n* \\`open\\`: {\"open\": [{\"ref_id\": \"turn0search0\"}, {\"ref_id\": \"https://www.openai.com\", \"lineno\": 120}]}\n* \\`click\\`: {\"click\": [{\"ref_id\": \"turn0fetch3\", \"id\": 17}]}\n* \\`find\\`: {\"find\": [{\"ref_id\": \"turn0fetch3\", \"pattern\": \"Annie Case\"}]}\n* \\`screenshot\\`: {\"screenshot\": [{\"ref_id\": \"turn1view0\", \"pageno\": 0}, {\"ref_id\": \"turn1view0\", \"pageno\": 3}]}\n* \\`finance\\`: {\"finance\":[{\"ticker\":\"AMD\",\"type\":\"equity\",\"market\":\"USA\"}]}, {\"finance\":[{\"ticker\":\"BTC\",\"type\":\"crypto\",\"market\":\"\"}]}\n* \\`weather\\`: {\"weather\":[{\"location\":\"San Francisco, CA\"}]}\n* \\`sports\\`: {\"sports\":[{\"fn\":\"standings\",\"league\":\"nfl\"}, {\"fn\":\"schedule\",\"league\":\"nba\",\"team\":\"GSW\",\"date_from\":\"2025-02-24\"}]}\n* \\`time\\`: {\"time\":[{\"utc_offset\":\"+03:00\"}]}\n\n---\n\n## Usage hints\nTo use this tool efficiently:\n* Use multiple commands and queries in one call to get more results faster; e.g. {\"search_query\": [{\"q\": \"bitcoin news\"}], \"finance\":[{\"ticker\":\"BTC\",\"type\":\"crypto\",\"market\":\"\"}], \"find\": [{\"ref_id\": \"turn0search0\", \"pattern\": \"Annie Case\"}, {\"ref_id\": \"turn0search1\", \"pattern\": \"John Smith\"}]}\n* Use \"response_length\" to control the number of results returned by this tool, omit it if you intend to pass \"short\" in\n* Only write required parameters; do not write empty lists or nulls where they could be omitted.\n* \\`search_query\\` must have length at most 4 in each call. If it has length > 3, response_length must be medium or long\n* If you find yourself in a situation where you accidentally call the \\`web_run\\` tool, it's best just to send an empty query: {\"search_query\": [{\"q\": \"\"}]}.\n\n---\n\n## Decision boundary\n\nIf the user makes an explicit request to search the internet, find latest information, look up, etc (or to not do so), you must obey their request.\nWhen you make an assumption, always consider whether it is temporally stable; i.e. whether there's even a small (>10%) chance it has changed. If it is unstable, you must verify with browsing the internet for verification.\n\n<situations_where_you_must_browse_the_internet>\nBelow is a list of scenarios where browsing the internet MUST be used. PAY CLOSE ATTENTION: you MUST browse the internet in these cases. If you're unsure or on the fence, you MUST bias towards browsing the internet.\n- The information could have changed recently: for example news; prices; laws; schedules; product specs; sports scores; economic indicators; political/public/company figures (e.g. the question relates to 'the president of country A' or 'the CEO of company B', which might change over time); rules; regulations; standards; software libraries that could be updated; exchange rates; recommendations (i.e., recommendations about various topics or things might be informed by what currently exists / is popular / is safe / is unsafe / is in the zeitgeist / etc.); and many many many more categories -- again, if you're on the fence, you MUST browse the internet!\n - For news queries, prioritize more recent events, ensuring you compare publish dates and the date that the event happened.\n- The user is seeking recommendations that could lead them to spend substantial time or money -- researching products, restaurants, travel plans, etc.\n- The user wants (or would benefit from) direct quotes, links, or precise source attribution.\n- A specific page, paper, dataset, PDF, or site is referenced and you haven't been given its contents.\n- You're unsure about a fact, the topic is niche or emerging, or you suspect there's at least a 10% chance you will incorrectly recall it\n- High-stakes accuracy matters (medical, legal, financial guidance). For these you generally should search by default because this information is highly temporally unstable\n- The user explicitly says to search, browse, verify, or look it up.\n</situations_where_you_must_browse_the_internet>\n\n---\n\n## Citations\n\nResults from \\`web_run\\` include internal reference IDs such as \\`turn2search5\\`. Use\nthose reference IDs only in calls to \\`web_run\\`; do not expose them in the final\nresponse.\n\nCite sources in the final response using Markdown links:\n\n- Cite a single source as \\`[descriptive source title](https://example.com/page)\\`.\n- Cite multiple sources with separate Markdown links, for example\n \\`[first source](https://example.com/one), [second source](https://example.com/two)\\`.\n- Link directly to the page that supports the claim. Do not link to search result\n pages or use bare URLs.\n\nFormatting of citations:\n\n- Place each citation as near as possible to the claim it supports, normally at\n the end of the sentence or paragraph and after punctuation.\n- Do not place citations inside code fences.\n- Do not put citations on a line by themselves or collect all citations at the\n end of the response.\n\nIf you browse the internet, cite statements supported by web sources. Each cited\nsource must directly support the associated claim. Prefer primary and\nauthoritative sources, and use sources from different domains when the response\nbenefits from multiple perspectives.\n\n---\n\n## Special cases\nIf these conflict with any other instructions, these should take precedence.\n\n<special_cases>\n- When the user asks for information about how to use OpenAI products, (ChatGPT, the OpenAI API, etc.), you should check the code in local env and only browse as fallback, when you browse restrict your sources to official OpenAI websites using the domains filter, unless otherwise requested.\n- When using search to answer technical questions, you must only rely on primary sources (research papers, official documentation, etc.)\n- Clearly indicate when you are making an inference from sources.\n</special_cases>\n\n---\n\n## Word limits\nResponses may not excessively quote or draw on a specific source. There are several limits here:\n- **Limit on verbatim quotes:**\n - You may not quote more than 25 words verbatim from any single non-lyrical source, unless the source is reddit.\n - For song lyrics, verbatim quotes must be limited to at most 10 words.\n - Long quotes from reddit are allowed, as long as you indicate that those are direct quotes via a markdown blockquote starting with \">\", copy verbatim, and link the source.\n- **Word limits:**\n - Each webpage source in the sources has a word limit label formatted like \"[wordlim N]\", in which N is the maximum number of words in the whole response that are attributed to that source. If omitted, the word limit is 200 words.\n - Non-contiguous words derived from a given source must be counted to the word limit.\n - The summarization limit N is a maximum for each source.\n - When using multiple sources, their summarization limits add together. However, each article used must be relevant to the response.\n- **Copyright compliance:**\n - You must avoid providing full articles, long verbatim passages, or extensive direct quotes due to copyright concerns.\n - If the user asked for a verbatim quote, the response should provide a short compliant excerpt and then answer with paraphrases and summaries.\n - Again, this limit does not apply to reddit content, as long as it's appropriately indicated that those are direct quotes and you link to the source.\n\n---\n\n## Errors\n\nIf \\`web_run\\` reports a sign-in, permission, or quota error, tell the user and do not call it again this turn.\n`;\n/** Session environment variable selecting `cached`, `indexed` or `live` access. */\nexport const WEB_SEARCH_ACCESS_ENV = 'PO_WEB_SEARCH_ACCESS';\nexport const DEFAULT_WEB_SEARCH_ACCESS = 'cached';\nexport const WEB_SEARCH_LOGIN_MESSAGE =\n 'Web search needs a ChatGPT sign-in. Sign in to ChatGPT for Po features under Settings > Providers, or with /login.';\n","// Copyright 2025 OpenAI\n// SPDX-License-Identifier: Apache-2.0\n// Modified for Po from codex-rs codex-api/src/search.rs SearchCommands: closed objects and size bounds added.\n\nimport { type Static, type TSchema, Type } from 'typebox';\n\nconst MAX_TEXT = 2048;\nconst MAX_OPERATIONS = 10;\n\nconst text = (description: string) => Type.String({ maxLength: MAX_TEXT, description });\nconst count = (description: string) => Type.Integer({ minimum: 0, description });\nconst closed = <T extends Record<string, TSchema>>(properties: T) =>\n Type.Object(properties, { additionalProperties: false });\nconst operations = <T extends TSchema>(item: T, description: string, maxItems = MAX_OPERATIONS) =>\n Type.Optional(Type.Array(item, { maxItems, description }));\nconst oneOf = <const T extends readonly string[]>(values: T, description: string) =>\n Type.Union(\n values.map((value) => Type.Literal(value)),\n { description },\n );\n\nconst SearchQuery = closed({\n q: text('Search query.'),\n recency: Type.Optional(count('Whether to filter by recency, as a number of recent days.')),\n domains: Type.Optional(\n Type.Array(Type.String({ maxLength: 253 }), {\n maxItems: 20,\n description: 'Whether to filter by a specific list of domains.',\n }),\n ),\n});\n\nexport const WebRunParamsSchema = Type.Object(\n {\n search_query: operations(SearchQuery, 'Query the internet search engine for a given list of queries.', 4),\n image_query: operations(SearchQuery, 'Query the image search engine for a given list of queries.', 4),\n open: operations(\n closed({\n ref_id: text('Reference id or URL to open.'),\n lineno: Type.Optional(count('Line number to position the page at.')),\n }),\n 'Open pages by reference id or URL.',\n ),\n click: operations(\n closed({\n ref_id: text('Reference id containing the numbered link.'),\n id: count('Numbered link id to open.'),\n }),\n 'Open links from previously opened pages.',\n ),\n find: operations(\n closed({ ref_id: text('Reference id or URL to search within.'), pattern: text('Text pattern to find.') }),\n 'Find text patterns in pages.',\n ),\n screenshot: operations(\n closed({ ref_id: text('Reference id or URL to screenshot.'), pageno: count('Zero-indexed PDF page number.') }),\n 'Take screenshots of PDF pages.',\n ),\n finance: operations(\n closed({\n ticker: text('Ticker symbol to look up.'),\n type: oneOf(['equity', 'fund', 'crypto', 'index'], 'Asset type to look up.'),\n market: Type.Optional(text('ISO 3166-1 alpha-3 country code, \"OTC\", or \"\" for cryptocurrency.')),\n }),\n 'Look up prices for the given stock symbols.',\n ),\n weather: operations(\n closed({\n location: text('Location in \"Country, Area, City\" format.'),\n start: Type.Optional(text('Start date in YYYY-MM-DD format. Defaults to today.')),\n duration: Type.Optional(count('Number of days to return. Defaults to 7.')),\n }),\n 'Look up weather forecasts.',\n ),\n sports: operations(\n closed({\n tool: Type.Optional(oneOf(['sports'], 'Tool name for sports requests.')),\n fn: oneOf(['schedule', 'standings'], 'Sports function to call.'),\n league: oneOf(['nba', 'wnba', 'nfl', 'nhl', 'mlb', 'epl', 'ncaamb', 'ncaawb', 'ipl'], 'League to look up.'),\n team: Type.Optional(text('Team to look up, using the common 3 or 4 letter alias used in broadcasts.')),\n opponent: Type.Optional(text('Opponent to use with `team` when narrowing the lookup.')),\n date_from: Type.Optional(text('Start date in YYYY-MM-DD format.')),\n date_to: Type.Optional(text('End date in YYYY-MM-DD format.')),\n num_games: Type.Optional(count('Number of games to return.')),\n locale: Type.Optional(text('Locale for the lookup.')),\n }),\n 'Look up sports schedules and standings.',\n ),\n time: operations(\n closed({ utc_offset: text('UTC offset formatted like \"+03:00\".') }),\n 'Get time for the given UTC offsets.',\n ),\n response_length: Type.Optional(\n oneOf(['short', 'medium', 'long'], 'Set the length of the response to be returned.'),\n ),\n },\n { additionalProperties: false, minProperties: 1 },\n);\n\nexport type WebRunParams = Static<typeof WebRunParamsSchema>;\n","// Copyright 2025 OpenAI\n// SPDX-License-Identifier: Apache-2.0\n// Modified for Po from codex-rs ext/web-search/src/history.rs and tools/src/response_history.rs:\n// reads Pi messages, drops custom (injected) messages, and budgets characters instead of tokens.\n\n/** About Codex's 1,000-token assistant budget. */\nconst ASSISTANT_CHARACTERS = 4000;\nconst USER_CHARACTERS = 8000;\nconst USER_TURNS = 2;\n/** The host accepts at most 16 conversation items: two user messages plus this many assistant messages. */\nconst ASSISTANT_MESSAGES = 14;\n\ninterface ConversationMessage {\n role: 'user' | 'assistant';\n text: string;\n}\n\nfunction textParts(content: unknown): string[] {\n if (typeof content === 'string') return [content];\n if (!Array.isArray(content)) return [];\n return content.flatMap((part: unknown) =>\n typeof part === 'object' &&\n part !== null &&\n (part as { type?: unknown }).type === 'text' &&\n typeof (part as { text?: unknown }).text === 'string'\n ? [(part as { text: string }).text]\n : [],\n );\n}\n\n/**\n * The previous user text message, the assistant text that followed it (shared budget, oldest first),\n * and the latest user text message. Tool traffic, thinking, images and custom messages are never included.\n */\nexport function recentConversation(messages: readonly unknown[]): ConversationMessage[] | undefined {\n const visible: ConversationMessage[] = [];\n for (const message of messages) {\n if (typeof message !== 'object' || message === null) continue;\n const { role, content } = message as { role?: unknown; content?: unknown };\n if (role !== 'user' && role !== 'assistant') continue;\n const text = textParts(content).join('\\n');\n if (text.trim()) visible.push({ role, text });\n }\n const latest = visible.findLastIndex((message) => message.role === 'user');\n if (latest < 0) return undefined;\n const userIndexes = visible.flatMap((message, index) => (message.role === 'user' && index <= latest ? [index] : []));\n const tail = visible.slice(userIndexes.at(-USER_TURNS) ?? latest, latest + 1);\n let budget = ASSISTANT_CHARACTERS;\n let assistants = 0;\n return tail.flatMap<ConversationMessage>((message) => {\n if (message.role === 'user') return [{ role: 'user' as const, text: message.text.slice(0, USER_CHARACTERS) }];\n if (budget === 0 || assistants === ASSISTANT_MESSAGES) return [];\n const text = message.text.slice(0, budget);\n budget -= text.length;\n assistants += 1;\n return [{ role: 'assistant' as const, text }];\n });\n}\n","import type { PoHeadlessExecutionContext, PoHeadlessTool } from '@agimon-ai/po-core/headless';\n\nimport {\n DEFAULT_WEB_SEARCH_ACCESS,\n WEB_RUN_DESCRIPTION,\n WEB_RUN_NAME,\n WEB_SEARCH_ACCESS_ENV,\n WEB_SEARCH_LOGIN_MESSAGE,\n} from '../../constants/webRun';\nimport { WebRunParamsSchema } from '../../schemas/webRun';\nimport { recentConversation } from '../recentConversation';\nimport type { WebRunDetails, WebRunSource, WebSearchAccess } from './type';\n\nconst ACCESS_MODES: readonly WebSearchAccess[] = ['cached', 'indexed', 'live'];\nconst MAX_SOURCES = 20;\nconst MAX_URL = 2048;\nconst MAX_TITLE = 300;\n\nfunction accessMode(environment: Readonly<Record<string, string | undefined>>): WebSearchAccess {\n const value = environment[WEB_SEARCH_ACCESS_ENV]?.trim() || DEFAULT_WEB_SEARCH_ACCESS;\n const access = ACCESS_MODES.find((mode) => mode === value);\n if (!access) throw new Error(`${WEB_SEARCH_ACCESS_ENV} must be cached, indexed, or live.`);\n return access;\n}\n\n/** Only the excerpt sent to the same OpenAI account the chat already uses; other providers send commands only. */\nasync function conversationFor(context: PoHeadlessExecutionContext) {\n if (context.model?.provider !== 'openai-codex' || !context.session.contextMessages) return undefined;\n try {\n return recentConversation(await context.session.contextMessages());\n } catch {\n // Documented fallback: search still runs, without conversation context, when history is unreadable.\n return undefined;\n }\n}\n\nfunction sources(results: readonly Record<string, unknown>[] | undefined): WebRunSource[] {\n const safe: WebRunSource[] = [];\n for (const result of results ?? []) {\n if (safe.length === MAX_SOURCES) break;\n if (typeof result.url !== 'string' || result.url.length > MAX_URL || !URL.canParse(result.url)) continue;\n const url = new URL(result.url);\n if (url.protocol !== 'https:' && url.protocol !== 'http:') continue;\n const title = typeof result.title === 'string' ? result.title.trim().slice(0, MAX_TITLE) : '';\n safe.push(title ? { url: url.href, title } : { url: url.href });\n }\n return safe;\n}\n\nexport function createWebRunTool(): PoHeadlessTool<typeof WebRunParamsSchema> {\n return {\n name: WEB_RUN_NAME,\n label: WEB_RUN_NAME,\n description: WEB_RUN_DESCRIPTION,\n parameters: WebRunParamsSchema,\n executionMode: 'parallel',\n async execute(_toolCallId, params, signal, _onUpdate, context) {\n signal?.throwIfAborted();\n const capability = context.webSearch;\n if (!capability?.available()) throw new Error(WEB_SEARCH_LOGIN_MESSAGE);\n const access = accessMode(context.environment);\n const conversation = await conversationFor(context);\n signal?.throwIfAborted();\n const result = await capability.search({\n commands: params,\n access,\n ...(conversation ? { conversation } : {}),\n ...(signal ? { signal } : {}),\n });\n const details: WebRunDetails = {\n access,\n sharedConversation: conversation !== undefined,\n sources: sources(result.results),\n };\n return { content: [{ type: 'text', text: result.output }], details };\n },\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/po-web-search",
3
- "version": "0.0.0-alpha.2",
3
+ "version": "0.0.0-alpha.3",
4
4
  "description": "Search the web using a ChatGPT subscription.",
5
5
  "keywords": [
6
6
  "coding-agent",
@@ -58,24 +58,24 @@
58
58
  "access": "public"
59
59
  },
60
60
  "dependencies": {
61
- "@agimon-ai/po-core": "0.0.0-alpha.3",
62
- "@agimon-ai/po-web-components": "0.0.0-alpha.3",
61
+ "@agimon-ai/po-core": "0.0.0-alpha.4",
62
+ "@agimon-ai/po-web-components": "0.0.0-alpha.4",
63
63
  "@deepseek-ai/cordis": "4.0.4",
64
64
  "react": "19.3.0",
65
- "typebox": "1.3.36"
65
+ "typebox": "1.3.37"
66
66
  },
67
67
  "devDependencies": {
68
- "@agimon-ai/po-build": "0.0.0-alpha.3",
69
- "@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.0-alpha.3",
70
- "@agimon-ai/vibe-lint-plugin-doom-web": "0.0.0-alpha.3",
68
+ "@agimon-ai/po-build": "0.0.0-alpha.4",
69
+ "@agimon-ai/vibe-lint-plugin-po-extension": "0.0.0-alpha.4",
70
+ "@agimon-ai/vibe-lint-plugin-po-web": "0.0.0-alpha.4",
71
71
  "@earendil-works/pi-coding-agent": "1.2.0",
72
- "@types/node": "25.9.9",
72
+ "@types/node": "26.6.5",
73
73
  "@types/react": "19.3.0",
74
- "@vitest/coverage-v8": "5.0.3",
74
+ "@vitest/coverage-v8": "5.0.4",
75
75
  "react-dom": "19.3.0",
76
76
  "tsdown": "0.23.1",
77
77
  "typescript": "7.0.2",
78
- "vitest": "5.0.3"
78
+ "vitest": "5.0.4"
79
79
  },
80
80
  "peerDependencies": {
81
81
  "@earendil-works/pi-coding-agent": "1.2.0"