@pseolint/core 0.4.3 → 0.5.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.
- package/README.md +264 -169
- package/dist/ai/manifest/diff.d.ts +78 -0
- package/dist/ai/manifest/diff.d.ts.map +1 -0
- package/dist/ai/manifest/diff.js +139 -0
- package/dist/ai/manifest/diff.js.map +1 -0
- package/dist/ai/manifest/index.d.ts +18 -0
- package/dist/ai/manifest/index.d.ts.map +1 -0
- package/dist/ai/manifest/index.js +15 -0
- package/dist/ai/manifest/index.js.map +1 -0
- package/dist/ai/manifest/validate-manifest.d.ts +37 -0
- package/dist/ai/manifest/validate-manifest.d.ts.map +1 -0
- package/dist/ai/manifest/validate-manifest.js +67 -0
- package/dist/ai/manifest/validate-manifest.js.map +1 -0
- package/dist/ai/manifest/validators/domain-patches.d.ts +15 -0
- package/dist/ai/manifest/validators/domain-patches.d.ts.map +1 -0
- package/dist/ai/manifest/validators/domain-patches.js +110 -0
- package/dist/ai/manifest/validators/domain-patches.js.map +1 -0
- package/dist/ai/manifest/validators/index.d.ts +5 -0
- package/dist/ai/manifest/validators/index.d.ts.map +1 -0
- package/dist/ai/manifest/validators/index.js +4 -0
- package/dist/ai/manifest/validators/index.js.map +1 -0
- package/dist/ai/manifest/validators/page-changes.d.ts +36 -0
- package/dist/ai/manifest/validators/page-changes.d.ts.map +1 -0
- package/dist/ai/manifest/validators/page-changes.js +221 -0
- package/dist/ai/manifest/validators/page-changes.js.map +1 -0
- package/dist/ai/manifest/validators/types.d.ts +17 -0
- package/dist/ai/manifest/validators/types.d.ts.map +1 -0
- package/dist/ai/manifest/validators/types.js +5 -0
- package/dist/ai/manifest/validators/types.js.map +1 -0
- package/dist/ai/orchestrate.d.ts +74 -0
- package/dist/ai/orchestrate.d.ts.map +1 -0
- package/dist/ai/orchestrate.js +54 -0
- package/dist/ai/orchestrate.js.map +1 -0
- package/dist/ai/orchestrator/budget.d.ts +57 -0
- package/dist/ai/orchestrator/budget.d.ts.map +1 -0
- package/dist/ai/orchestrator/budget.js +114 -0
- package/dist/ai/orchestrator/budget.js.map +1 -0
- package/dist/ai/orchestrator/finish-tool.d.ts +568 -0
- package/dist/ai/orchestrator/finish-tool.d.ts.map +1 -0
- package/dist/ai/orchestrator/finish-tool.js +114 -0
- package/dist/ai/orchestrator/finish-tool.js.map +1 -0
- package/dist/ai/orchestrator/index.d.ts +25 -0
- package/dist/ai/orchestrator/index.d.ts.map +1 -0
- package/dist/ai/orchestrator/index.js +21 -0
- package/dist/ai/orchestrator/index.js.map +1 -0
- package/dist/ai/orchestrator/log.d.ts +24 -0
- package/dist/ai/orchestrator/log.d.ts.map +1 -0
- package/dist/ai/orchestrator/log.js +48 -0
- package/dist/ai/orchestrator/log.js.map +1 -0
- package/dist/ai/orchestrator/page-cache.d.ts +64 -0
- package/dist/ai/orchestrator/page-cache.d.ts.map +1 -0
- package/dist/ai/orchestrator/page-cache.js +127 -0
- package/dist/ai/orchestrator/page-cache.js.map +1 -0
- package/dist/ai/orchestrator/prompt.d.ts +16 -0
- package/dist/ai/orchestrator/prompt.d.ts.map +1 -0
- package/dist/ai/orchestrator/prompt.js +52 -0
- package/dist/ai/orchestrator/prompt.js.map +1 -0
- package/dist/ai/orchestrator/runner.d.ts +65 -0
- package/dist/ai/orchestrator/runner.d.ts.map +1 -0
- package/dist/ai/orchestrator/runner.js +223 -0
- package/dist/ai/orchestrator/runner.js.map +1 -0
- package/dist/ai/orchestrator/session.d.ts +44 -0
- package/dist/ai/orchestrator/session.d.ts.map +1 -0
- package/dist/ai/orchestrator/session.js +64 -0
- package/dist/ai/orchestrator/session.js.map +1 -0
- package/dist/ai/orchestrator/types.d.ts +99 -0
- package/dist/ai/orchestrator/types.d.ts.map +1 -0
- package/dist/ai/orchestrator/types.js +8 -0
- package/dist/ai/orchestrator/types.js.map +1 -0
- package/dist/ai/probes/cache.d.ts +12 -0
- package/dist/ai/probes/cache.d.ts.map +1 -0
- package/dist/ai/probes/cache.js +46 -0
- package/dist/ai/probes/cache.js.map +1 -0
- package/dist/ai/tools/ask-ai-engine.d.ts +77 -0
- package/dist/ai/tools/ask-ai-engine.d.ts.map +1 -0
- package/dist/ai/tools/ask-ai-engine.js +253 -0
- package/dist/ai/tools/ask-ai-engine.js.map +1 -0
- package/dist/ai/tools/check-domain-crawler-access.d.ts +71 -0
- package/dist/ai/tools/check-domain-crawler-access.d.ts.map +1 -0
- package/dist/ai/tools/check-domain-crawler-access.js +76 -0
- package/dist/ai/tools/check-domain-crawler-access.js.map +1 -0
- package/dist/ai/tools/check-domain-llms-txt.d.ts +70 -0
- package/dist/ai/tools/check-domain-llms-txt.d.ts.map +1 -0
- package/dist/ai/tools/check-domain-llms-txt.js +75 -0
- package/dist/ai/tools/check-domain-llms-txt.js.map +1 -0
- package/dist/ai/tools/check-indexability.d.ts +58 -0
- package/dist/ai/tools/check-indexability.d.ts.map +1 -0
- package/dist/ai/tools/check-indexability.js +64 -0
- package/dist/ai/tools/check-indexability.js.map +1 -0
- package/dist/ai/tools/check-robots.d.ts +68 -0
- package/dist/ai/tools/check-robots.d.ts.map +1 -0
- package/dist/ai/tools/check-robots.js +90 -0
- package/dist/ai/tools/check-robots.js.map +1 -0
- package/dist/ai/tools/check-rule-answer-first.d.ts +54 -0
- package/dist/ai/tools/check-rule-answer-first.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-answer-first.js +50 -0
- package/dist/ai/tools/check-rule-answer-first.js.map +1 -0
- package/dist/ai/tools/check-rule-canonical-consistency.d.ts +66 -0
- package/dist/ai/tools/check-rule-canonical-consistency.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-canonical-consistency.js +51 -0
- package/dist/ai/tools/check-rule-canonical-consistency.js.map +1 -0
- package/dist/ai/tools/check-rule-citable-facts.d.ts +58 -0
- package/dist/ai/tools/check-rule-citable-facts.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-citable-facts.js +41 -0
- package/dist/ai/tools/check-rule-citable-facts.js.map +1 -0
- package/dist/ai/tools/check-rule-content-modularity.d.ts +58 -0
- package/dist/ai/tools/check-rule-content-modularity.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-content-modularity.js +45 -0
- package/dist/ai/tools/check-rule-content-modularity.js.map +1 -0
- package/dist/ai/tools/check-rule-faq-coverage.d.ts +54 -0
- package/dist/ai/tools/check-rule-faq-coverage.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-faq-coverage.js +39 -0
- package/dist/ai/tools/check-rule-faq-coverage.js.map +1 -0
- package/dist/ai/tools/check-rule-freshness-signals.d.ts +54 -0
- package/dist/ai/tools/check-rule-freshness-signals.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-freshness-signals.js +45 -0
- package/dist/ai/tools/check-rule-freshness-signals.js.map +1 -0
- package/dist/ai/tools/check-rule-json-ld-valid.d.ts +54 -0
- package/dist/ai/tools/check-rule-json-ld-valid.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-json-ld-valid.js +44 -0
- package/dist/ai/tools/check-rule-json-ld-valid.js.map +1 -0
- package/dist/ai/tools/check-rule-missing-author.d.ts +54 -0
- package/dist/ai/tools/check-rule-missing-author.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-missing-author.js +45 -0
- package/dist/ai/tools/check-rule-missing-author.js.map +1 -0
- package/dist/ai/tools/check-rule-near-duplicate.d.ts +82 -0
- package/dist/ai/tools/check-rule-near-duplicate.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-near-duplicate.js +63 -0
- package/dist/ai/tools/check-rule-near-duplicate.js.map +1 -0
- package/dist/ai/tools/check-rule-required-fields.d.ts +50 -0
- package/dist/ai/tools/check-rule-required-fields.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-required-fields.js +38 -0
- package/dist/ai/tools/check-rule-required-fields.js.map +1 -0
- package/dist/ai/tools/check-rule-schema-consistency.d.ts +54 -0
- package/dist/ai/tools/check-rule-schema-consistency.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-schema-consistency.js +44 -0
- package/dist/ai/tools/check-rule-schema-consistency.js.map +1 -0
- package/dist/ai/tools/check-rule-summary-bait.d.ts +54 -0
- package/dist/ai/tools/check-rule-summary-bait.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-summary-bait.js +39 -0
- package/dist/ai/tools/check-rule-summary-bait.js.map +1 -0
- package/dist/ai/tools/check-rule-thin-content.d.ts +66 -0
- package/dist/ai/tools/check-rule-thin-content.d.ts.map +1 -0
- package/dist/ai/tools/check-rule-thin-content.js +58 -0
- package/dist/ai/tools/check-rule-thin-content.js.map +1 -0
- package/dist/ai/tools/detect-templates.d.ts +60 -0
- package/dist/ai/tools/detect-templates.d.ts.map +1 -0
- package/dist/ai/tools/detect-templates.js +43 -0
- package/dist/ai/tools/detect-templates.js.map +1 -0
- package/dist/ai/tools/fetch-page.d.ts +70 -0
- package/dist/ai/tools/fetch-page.d.ts.map +1 -0
- package/dist/ai/tools/fetch-page.js +93 -0
- package/dist/ai/tools/fetch-page.js.map +1 -0
- package/dist/ai/tools/fetch-sitemap.d.ts +60 -0
- package/dist/ai/tools/fetch-sitemap.d.ts.map +1 -0
- package/dist/ai/tools/fetch-sitemap.js +116 -0
- package/dist/ai/tools/fetch-sitemap.js.map +1 -0
- package/dist/ai/tools/index.d.ts +1555 -0
- package/dist/ai/tools/index.d.ts.map +1 -0
- package/dist/ai/tools/index.js +119 -0
- package/dist/ai/tools/index.js.map +1 -0
- package/dist/ai/tools/parse-page.d.ts +94 -0
- package/dist/ai/tools/parse-page.d.ts.map +1 -0
- package/dist/ai/tools/parse-page.js +108 -0
- package/dist/ai/tools/parse-page.js.map +1 -0
- package/dist/ai/tools/query-serp.d.ts +113 -0
- package/dist/ai/tools/query-serp.d.ts.map +1 -0
- package/dist/ai/tools/query-serp.js +131 -0
- package/dist/ai/tools/query-serp.js.map +1 -0
- package/dist/ai/tools/sample-template.d.ts +67 -0
- package/dist/ai/tools/sample-template.d.ts.map +1 -0
- package/dist/ai/tools/sample-template.js +75 -0
- package/dist/ai/tools/sample-template.js.map +1 -0
- package/dist/ai/tools/types.d.ts +73 -0
- package/dist/ai/tools/types.d.ts.map +1 -0
- package/dist/ai/tools/types.js +64 -0
- package/dist/ai/tools/types.js.map +1 -0
- package/dist/ai/tools/validate-jsonld.d.ts +62 -0
- package/dist/ai/tools/validate-jsonld.d.ts.map +1 -0
- package/dist/ai/tools/validate-jsonld.js +84 -0
- package/dist/ai/tools/validate-jsonld.js.map +1 -0
- package/dist/auditor.d.ts +4 -0
- package/dist/auditor.d.ts.map +1 -1
- package/dist/auditor.js +629 -64
- package/dist/auditor.js.map +1 -1
- package/dist/backpressure.d.ts.map +1 -1
- package/dist/backpressure.js +10 -3
- package/dist/backpressure.js.map +1 -1
- package/dist/enrich-findings.d.ts.map +1 -1
- package/dist/enrich-findings.js +15 -1
- package/dist/enrich-findings.js.map +1 -1
- package/dist/formatters/console.d.ts.map +1 -1
- package/dist/formatters/console.js +13 -0
- package/dist/formatters/console.js.map +1 -1
- package/dist/formatters/markdown.d.ts.map +1 -1
- package/dist/formatters/markdown.js +20 -2
- package/dist/formatters/markdown.js.map +1 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/rule-references.d.ts.map +1 -1
- package/dist/rule-references.js +5 -0
- package/dist/rule-references.js.map +1 -1
- package/dist/rules/content/heading-structure.d.ts +21 -0
- package/dist/rules/content/heading-structure.d.ts.map +1 -0
- package/dist/rules/content/heading-structure.js +56 -0
- package/dist/rules/content/heading-structure.js.map +1 -0
- package/dist/rules/content/image-alt-text.d.ts +18 -0
- package/dist/rules/content/image-alt-text.d.ts.map +1 -0
- package/dist/rules/content/image-alt-text.js +77 -0
- package/dist/rules/content/image-alt-text.js.map +1 -0
- package/dist/rules/content/title-uniqueness.d.ts +18 -0
- package/dist/rules/content/title-uniqueness.d.ts.map +1 -0
- package/dist/rules/content/title-uniqueness.js +70 -0
- package/dist/rules/content/title-uniqueness.js.map +1 -0
- package/dist/rules/links/host-section-divergence.d.ts +3 -0
- package/dist/rules/links/host-section-divergence.d.ts.map +1 -0
- package/dist/rules/links/host-section-divergence.js +158 -0
- package/dist/rules/links/host-section-divergence.js.map +1 -0
- package/dist/rules/links/link-depth.d.ts +12 -1
- package/dist/rules/links/link-depth.d.ts.map +1 -1
- package/dist/rules/links/link-depth.js +25 -12
- package/dist/rules/links/link-depth.js.map +1 -1
- package/dist/rules/scope.d.ts.map +1 -1
- package/dist/rules/scope.js +5 -0
- package/dist/rules/scope.js.map +1 -1
- package/dist/rules/spam/doorway-pattern.d.ts.map +1 -1
- package/dist/rules/spam/doorway-pattern.js +27 -4
- package/dist/rules/spam/doorway-pattern.js.map +1 -1
- package/dist/rules/spam/publication-velocity.d.ts +1 -1
- package/dist/rules/spam/publication-velocity.d.ts.map +1 -1
- package/dist/rules/spam/publication-velocity.js +9 -4
- package/dist/rules/spam/publication-velocity.js.map +1 -1
- package/dist/rules/spam/template-coverage.js +1 -1
- package/dist/rules/spam/template-coverage.js.map +1 -1
- package/dist/rules/spam/template-diversity.js +1 -1
- package/dist/rules/spam/template-diversity.js.map +1 -1
- package/dist/rules/tech/hreflang-consistency.d.ts.map +1 -1
- package/dist/rules/tech/hreflang-consistency.js +33 -4
- package/dist/rules/tech/hreflang-consistency.js.map +1 -1
- package/dist/rules/tech/og-completeness.d.ts +11 -0
- package/dist/rules/tech/og-completeness.d.ts.map +1 -1
- package/dist/rules/tech/og-completeness.js +22 -23
- package/dist/rules/tech/og-completeness.js.map +1 -1
- package/dist/ruleset-version.d.ts +8 -0
- package/dist/ruleset-version.d.ts.map +1 -0
- package/dist/ruleset-version.js +8 -0
- package/dist/ruleset-version.js.map +1 -0
- package/dist/scrape-strategy.d.ts +42 -0
- package/dist/scrape-strategy.d.ts.map +1 -0
- package/dist/scrape-strategy.js +101 -0
- package/dist/scrape-strategy.js.map +1 -0
- package/dist/site-classifier.d.ts.map +1 -1
- package/dist/site-classifier.js +1 -0
- package/dist/site-classifier.js.map +1 -1
- package/dist/state.d.ts +36 -1
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +3 -1
- package/dist/state.js.map +1 -1
- package/dist/stratified-sample.d.ts +9 -1
- package/dist/stratified-sample.d.ts.map +1 -1
- package/dist/stratified-sample.js +23 -6
- package/dist/stratified-sample.js.map +1 -1
- package/dist/types.d.ts +135 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/url-normalize.d.ts.map +1 -1
- package/dist/url-normalize.js +13 -1
- package/dist/url-normalize.js.map +1 -1
- package/package.json +90 -90
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { generateText } from "ai";
|
|
2
|
+
import { estimateCostUsd } from "../cost.js";
|
|
3
|
+
import { orchestratorTools } from "../tools/index.js";
|
|
4
|
+
import { finishAuditTool, manifestSchema } from "./finish-tool.js";
|
|
5
|
+
import { PageCache, withPageCache } from "./page-cache.js";
|
|
6
|
+
import { buildSystemPrompt } from "./prompt.js";
|
|
7
|
+
import { SessionState } from "./session.js";
|
|
8
|
+
const FINISH_TOOL_NAME = "finish_audit";
|
|
9
|
+
/**
|
|
10
|
+
* Run a single orchestrator session end-to-end. Returns the final
|
|
11
|
+
* `SessionResult` once the LLM calls `finish_audit`, the budget is
|
|
12
|
+
* exhausted, the abort signal fires, or the model errors out.
|
|
13
|
+
*
|
|
14
|
+
* No streaming yet — uses `generateText` for simpler test ergonomics with
|
|
15
|
+
* the existing `MockModel` (which only implements `doGenerate`). Phase 2
|
|
16
|
+
* follow-up swaps to `streamText` once we have a stream-capable mock and a
|
|
17
|
+
* web-app SSE endpoint to wire up.
|
|
18
|
+
*/
|
|
19
|
+
export async function runOrchestrator(opts) {
|
|
20
|
+
const session = new SessionState({
|
|
21
|
+
domain: opts.domain,
|
|
22
|
+
userId: opts.userId,
|
|
23
|
+
providerId: opts.providerId,
|
|
24
|
+
modelId: opts.modelId,
|
|
25
|
+
budget: opts.budget,
|
|
26
|
+
ndjsonPath: opts.ndjsonPath,
|
|
27
|
+
onEvent: opts.onEvent,
|
|
28
|
+
});
|
|
29
|
+
await session.start();
|
|
30
|
+
const baseTools = opts.tools ?? orchestratorTools;
|
|
31
|
+
const aiTools = {};
|
|
32
|
+
for (const [name, tool] of Object.entries(baseTools)) {
|
|
33
|
+
aiTools[name] = tool.toAiTool();
|
|
34
|
+
}
|
|
35
|
+
// The runner detects finish_audit by name and pulls the manifest from
|
|
36
|
+
// its tool-call input — the AI SDK ferries the rest through normally.
|
|
37
|
+
aiTools[FINISH_TOOL_NAME] = finishAuditTool.toAiTool();
|
|
38
|
+
const systemPrompt = buildSystemPrompt(session.caps);
|
|
39
|
+
let stopReason = "no_finish_called";
|
|
40
|
+
let modelError;
|
|
41
|
+
// Closure for per-step timing. prepareStep records start; onStepFinish
|
|
42
|
+
// reads + emits duration. generateText is sequential so the
|
|
43
|
+
// single-cell pattern is race-free.
|
|
44
|
+
let currentStepStartedAt = 0;
|
|
45
|
+
// Last tool-call count at which we injected a watchdog message. Used to
|
|
46
|
+
// suppress duplicate firings within the same multiple-of-N window when
|
|
47
|
+
// the orchestrator emits multiple tool calls per step.
|
|
48
|
+
let lastWatchdogAtToolCount = 0;
|
|
49
|
+
// Per-session page cache. `fetch_page` writes HTML here keyed by pageId;
|
|
50
|
+
// page-content-consuming tools read by pageId. AsyncLocalStorage threads
|
|
51
|
+
// it through the AI SDK's tool loop without expanding the tool API.
|
|
52
|
+
const pageCache = new PageCache();
|
|
53
|
+
try {
|
|
54
|
+
await withPageCache(pageCache, () => generateText({
|
|
55
|
+
model: opts.model,
|
|
56
|
+
system: systemPrompt,
|
|
57
|
+
tools: aiTools,
|
|
58
|
+
messages: [
|
|
59
|
+
{
|
|
60
|
+
role: "user",
|
|
61
|
+
content: `Audit ${opts.domain}. Produce a fix manifest covering integrity, discoverability, citation, and data per the methodology in the system prompt. Call finish_audit when done.`,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
stopWhen: ({ steps }) => {
|
|
65
|
+
if (session.isFinished())
|
|
66
|
+
return true;
|
|
67
|
+
if (opts.signal?.aborted)
|
|
68
|
+
return true;
|
|
69
|
+
if (session.budget.nextStopReason() !== null)
|
|
70
|
+
return true;
|
|
71
|
+
// Pre-flight: refuse the next step if it would breach the USD or
|
|
72
|
+
// input-token cap based on the max-observed per-step cost so far.
|
|
73
|
+
// Reactive enforcement (nextStopReason) above catches breaches *after*
|
|
74
|
+
// they happen; this catches them *before*. Both fences exist because
|
|
75
|
+
// pre-flight is conservative (uses max-observed, not avg) and may
|
|
76
|
+
// miss anomalous future steps the average wouldn't predict.
|
|
77
|
+
if (session.budget.projectedNextStopReason() !== null)
|
|
78
|
+
return true;
|
|
79
|
+
// Hard fallback against runaway loops; budget caps are the real guard.
|
|
80
|
+
return steps.length >= session.caps.maxToolCalls + 5;
|
|
81
|
+
},
|
|
82
|
+
prepareStep: async ({ messages }) => {
|
|
83
|
+
currentStepStartedAt = Date.now();
|
|
84
|
+
const interval = session.caps.watchdogIntervalCalls;
|
|
85
|
+
const toolCount = session.budget.snapshot().toolCallCount;
|
|
86
|
+
// Fire watchdog when we've crossed a multiple-of-interval boundary
|
|
87
|
+
// since the last firing. `lastWatchdogAtToolCount` rises monotonically
|
|
88
|
+
// so we never re-inject for the same window.
|
|
89
|
+
const lastBoundary = Math.floor(lastWatchdogAtToolCount / interval);
|
|
90
|
+
const currentBoundary = Math.floor(toolCount / interval);
|
|
91
|
+
if (interval > 0 && toolCount > 0 && currentBoundary > lastBoundary) {
|
|
92
|
+
lastWatchdogAtToolCount = toolCount;
|
|
93
|
+
await session.emit({ kind: "watchdog_injected", toolCallCount: toolCount });
|
|
94
|
+
// Inject as a `user` role message, not `system`. Anthropic's API
|
|
95
|
+
// rejects multiple system messages interspersed with user/assistant
|
|
96
|
+
// turns ("Multiple system messages that are separated by user/
|
|
97
|
+
// assistant messages functionality not supported"), and other
|
|
98
|
+
// providers vary in tolerance. A user-role nudge is universally
|
|
99
|
+
// accepted and reads natural ("hey, are you converging?").
|
|
100
|
+
return {
|
|
101
|
+
messages: [
|
|
102
|
+
...messages,
|
|
103
|
+
{
|
|
104
|
+
role: "user",
|
|
105
|
+
content: `[watchdog] You've made ${toolCount} tool calls. ` +
|
|
106
|
+
`Hard cap is ${session.caps.maxToolCalls}. ` +
|
|
107
|
+
`Are you converging on a manifest? If not, summarize remaining gaps now and call finish_audit. ` +
|
|
108
|
+
`Partial manifests are still useful — prefer breadth over depth.`,
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
return undefined;
|
|
114
|
+
},
|
|
115
|
+
onStepFinish: async ({ toolCalls, toolResults, text, usage }) => {
|
|
116
|
+
const stepDurationMs = currentStepStartedAt > 0 ? Date.now() - currentStepStartedAt : undefined;
|
|
117
|
+
if (text) {
|
|
118
|
+
await session.emit({ kind: "thought", text });
|
|
119
|
+
}
|
|
120
|
+
for (const c of toolCalls) {
|
|
121
|
+
await session.emit({
|
|
122
|
+
kind: "tool_call",
|
|
123
|
+
toolName: c.toolName,
|
|
124
|
+
input: c.input,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
for (const r of toolResults) {
|
|
128
|
+
await session.emit({
|
|
129
|
+
kind: "tool_result",
|
|
130
|
+
toolName: r.toolName,
|
|
131
|
+
result: r.output,
|
|
132
|
+
});
|
|
133
|
+
// External-probe cost ingestion. Tools that hit billable APIs
|
|
134
|
+
// (query_serp, ask_ai_engine) include `apiCostUsd` in their data
|
|
135
|
+
// payload. The orchestrator's USD cap would otherwise be fiction
|
|
136
|
+
// for sessions that lean on probes — this is the only place where
|
|
137
|
+
// non-LLM costs get folded into the session budget.
|
|
138
|
+
const probeCost = extractProbeCost(r.output);
|
|
139
|
+
if (probeCost > 0)
|
|
140
|
+
session.budget.recordExternalCost(probeCost);
|
|
141
|
+
if (r.toolName === FINISH_TOOL_NAME) {
|
|
142
|
+
const call = toolCalls.find((c) => c.toolCallId === r.toolCallId);
|
|
143
|
+
// The AI SDK validates input via the tool's Zod schema before
|
|
144
|
+
// dispatching to execute(), so by the time the result fires the
|
|
145
|
+
// manifest shape is already known-good. Re-validating here gives
|
|
146
|
+
// us the typed shape and a defensive guard against weird SDK
|
|
147
|
+
// edge cases (missing call, unexpected input shape) without
|
|
148
|
+
// assuming the cast.
|
|
149
|
+
const parsed = call ? manifestSchema.safeParse(call.input?.manifest) : null;
|
|
150
|
+
session.markFinished(parsed?.success ? parsed.data : null);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
session.budget.recordToolCalls(toolCalls.length);
|
|
154
|
+
const stepInputTokens = usage.inputTokens ?? 0;
|
|
155
|
+
const stepOutputTokens = usage.outputTokens ?? 0;
|
|
156
|
+
session.budget.recordStepUsage({
|
|
157
|
+
inputTokens: stepInputTokens,
|
|
158
|
+
outputTokens: stepOutputTokens,
|
|
159
|
+
});
|
|
160
|
+
await session.emit({
|
|
161
|
+
kind: "step_usage",
|
|
162
|
+
inputTokens: stepInputTokens,
|
|
163
|
+
outputTokens: stepOutputTokens,
|
|
164
|
+
estimatedUsd: estimateCostUsd(opts.providerId, opts.modelId, {
|
|
165
|
+
input: stepInputTokens,
|
|
166
|
+
output: stepOutputTokens,
|
|
167
|
+
}) ?? 0,
|
|
168
|
+
durationMs: stepDurationMs,
|
|
169
|
+
});
|
|
170
|
+
const breach = session.budget.nextStopReason();
|
|
171
|
+
if (breach !== null) {
|
|
172
|
+
stopReason = breach;
|
|
173
|
+
await session.emit({
|
|
174
|
+
kind: "budget_warning",
|
|
175
|
+
reason: breach,
|
|
176
|
+
usage: session.budget.snapshot(),
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
abortSignal: opts.signal,
|
|
181
|
+
maxRetries: opts.maxRetries ?? 5,
|
|
182
|
+
}));
|
|
183
|
+
}
|
|
184
|
+
catch (e) {
|
|
185
|
+
modelError = e instanceof Error ? e.message : String(e);
|
|
186
|
+
stopReason = opts.signal?.aborted ? "aborted" : "model_error";
|
|
187
|
+
}
|
|
188
|
+
if (session.isFinished()) {
|
|
189
|
+
stopReason = "completed";
|
|
190
|
+
}
|
|
191
|
+
else if (stopReason === "no_finish_called" && opts.signal?.aborted) {
|
|
192
|
+
stopReason = "aborted";
|
|
193
|
+
}
|
|
194
|
+
const usage = session.budget.snapshot();
|
|
195
|
+
await session.emit({ kind: "session_finished", reason: stopReason, usage });
|
|
196
|
+
return {
|
|
197
|
+
sessionId: session.id,
|
|
198
|
+
domain: session.domain,
|
|
199
|
+
reason: stopReason,
|
|
200
|
+
manifest: session.getManifest(),
|
|
201
|
+
usage,
|
|
202
|
+
events: session.events(),
|
|
203
|
+
error: modelError,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Best-effort extraction of `apiCostUsd` from a tool result. Tool outputs
|
|
208
|
+
* are wrapped as `{ ok: true, data }` or `{ ok: false, error }` by
|
|
209
|
+
* `defineTool`. We only bill for ok results carrying a non-negative
|
|
210
|
+
* numeric `apiCostUsd` field — every other shape returns 0.
|
|
211
|
+
*/
|
|
212
|
+
function extractProbeCost(output) {
|
|
213
|
+
if (output === null || typeof output !== "object")
|
|
214
|
+
return 0;
|
|
215
|
+
const o = output;
|
|
216
|
+
if (o.ok !== true || o.data === null || typeof o.data !== "object")
|
|
217
|
+
return 0;
|
|
218
|
+
const usd = o.data.apiCostUsd;
|
|
219
|
+
if (typeof usd !== "number" || !Number.isFinite(usd) || usd < 0)
|
|
220
|
+
return 0;
|
|
221
|
+
return usd;
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../../src/ai/orchestrator/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { BudgetTracker } from "./budget.js";
|
|
2
|
+
import type { FixManifest } from "./finish-tool.js";
|
|
3
|
+
import { SessionLog, type EventSink } from "./log.js";
|
|
4
|
+
import { type BudgetCaps, type SessionEvent } from "./types.js";
|
|
5
|
+
export interface SessionStateOptions {
|
|
6
|
+
domain: string;
|
|
7
|
+
userId: string;
|
|
8
|
+
providerId: string;
|
|
9
|
+
modelId: string;
|
|
10
|
+
budget?: Partial<BudgetCaps>;
|
|
11
|
+
ndjsonPath?: string;
|
|
12
|
+
onEvent?: EventSink;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Bundles the per-session pieces (budget + log + identity) so the runner has
|
|
16
|
+
* a single object to thread through `streamText` callbacks. Keeps the runner
|
|
17
|
+
* itself focused on the AI-SDK glue.
|
|
18
|
+
*/
|
|
19
|
+
export declare class SessionState {
|
|
20
|
+
readonly id: string;
|
|
21
|
+
readonly domain: string;
|
|
22
|
+
readonly userId: string;
|
|
23
|
+
readonly budget: BudgetTracker;
|
|
24
|
+
readonly log: SessionLog;
|
|
25
|
+
readonly caps: BudgetCaps;
|
|
26
|
+
private finished;
|
|
27
|
+
private finishedManifest;
|
|
28
|
+
constructor(opts: SessionStateOptions);
|
|
29
|
+
start(): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Mark `finish_audit` was called and capture its manifest. The runner
|
|
32
|
+
* reads this to detect completion. `manifest` may be null only in the
|
|
33
|
+
* pathological case where the AI SDK fired a finish_audit tool_result
|
|
34
|
+
* event but the matching tool_call's input couldn't be located — keep
|
|
35
|
+
* the session as "finished" so the loop terminates, but the result will
|
|
36
|
+
* carry a null manifest and the caller can detect the partial state.
|
|
37
|
+
*/
|
|
38
|
+
markFinished(manifest: FixManifest | null): void;
|
|
39
|
+
isFinished(): boolean;
|
|
40
|
+
getManifest(): FixManifest | null;
|
|
41
|
+
emit(event: SessionEvent): Promise<void>;
|
|
42
|
+
events(): SessionEvent[];
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/ai/orchestrator/session.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,UAAU,CAAC;AACtD,OAAO,EAAkB,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAEhF,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB;AAED;;;;GAIG;AACH,qBAAa,YAAY;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAE1B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,gBAAgB,CAA4B;gBAExC,IAAI,EAAE,mBAAmB;IAY/B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAU5B;;;;;;;OAOG;IACH,YAAY,CAAC,QAAQ,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI;IAKhD,UAAU,IAAI,OAAO;IAIrB,WAAW,IAAI,WAAW,GAAG,IAAI;IAI3B,IAAI,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9C,MAAM,IAAI,YAAY,EAAE;CAGzB"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { BudgetTracker } from "./budget.js";
|
|
3
|
+
import { SessionLog } from "./log.js";
|
|
4
|
+
import { DEFAULT_BUDGET } from "./types.js";
|
|
5
|
+
/**
|
|
6
|
+
* Bundles the per-session pieces (budget + log + identity) so the runner has
|
|
7
|
+
* a single object to thread through `streamText` callbacks. Keeps the runner
|
|
8
|
+
* itself focused on the AI-SDK glue.
|
|
9
|
+
*/
|
|
10
|
+
export class SessionState {
|
|
11
|
+
id;
|
|
12
|
+
domain;
|
|
13
|
+
userId;
|
|
14
|
+
budget;
|
|
15
|
+
log;
|
|
16
|
+
caps;
|
|
17
|
+
finished = false;
|
|
18
|
+
finishedManifest = null;
|
|
19
|
+
constructor(opts) {
|
|
20
|
+
this.id = randomUUID();
|
|
21
|
+
this.domain = opts.domain;
|
|
22
|
+
this.userId = opts.userId;
|
|
23
|
+
this.caps = { ...DEFAULT_BUDGET, ...opts.budget };
|
|
24
|
+
this.budget = new BudgetTracker(this.caps, opts.providerId, opts.modelId);
|
|
25
|
+
this.log = new SessionLog({
|
|
26
|
+
ndjsonPath: opts.ndjsonPath,
|
|
27
|
+
onEvent: opts.onEvent,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
async start() {
|
|
31
|
+
this.budget.start();
|
|
32
|
+
await this.log.emit({
|
|
33
|
+
kind: "session_started",
|
|
34
|
+
sessionId: this.id,
|
|
35
|
+
domain: this.domain,
|
|
36
|
+
startedAt: new Date().toISOString(),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Mark `finish_audit` was called and capture its manifest. The runner
|
|
41
|
+
* reads this to detect completion. `manifest` may be null only in the
|
|
42
|
+
* pathological case where the AI SDK fired a finish_audit tool_result
|
|
43
|
+
* event but the matching tool_call's input couldn't be located — keep
|
|
44
|
+
* the session as "finished" so the loop terminates, but the result will
|
|
45
|
+
* carry a null manifest and the caller can detect the partial state.
|
|
46
|
+
*/
|
|
47
|
+
markFinished(manifest) {
|
|
48
|
+
this.finished = true;
|
|
49
|
+
this.finishedManifest = manifest;
|
|
50
|
+
}
|
|
51
|
+
isFinished() {
|
|
52
|
+
return this.finished;
|
|
53
|
+
}
|
|
54
|
+
getManifest() {
|
|
55
|
+
return this.finishedManifest;
|
|
56
|
+
}
|
|
57
|
+
async emit(event) {
|
|
58
|
+
await this.log.emit(event);
|
|
59
|
+
}
|
|
60
|
+
events() {
|
|
61
|
+
return this.log.events();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/ai/orchestrator/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,UAAU,EAAkB,MAAM,UAAU,CAAC;AACtD,OAAO,EAAE,cAAc,EAAsC,MAAM,YAAY,CAAC;AAYhF;;;;GAIG;AACH,MAAM,OAAO,YAAY;IACd,EAAE,CAAS;IACX,MAAM,CAAS;IACf,MAAM,CAAS;IACf,MAAM,CAAgB;IACtB,GAAG,CAAa;IAChB,IAAI,CAAa;IAElB,QAAQ,GAAG,KAAK,CAAC;IACjB,gBAAgB,GAAuB,IAAI,CAAC;IAEpD,YAAY,IAAyB;QACnC,IAAI,CAAC,EAAE,GAAG,UAAU,EAAE,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAClD,IAAI,CAAC,MAAM,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1E,IAAI,CAAC,GAAG,GAAG,IAAI,UAAU,CAAC;YACxB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACpB,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;YAClB,IAAI,EAAE,iBAAiB;YACvB,SAAS,EAAE,IAAI,CAAC,EAAE;YAClB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;OAOG;IACH,YAAY,CAAC,QAA4B;QACvC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,gBAAgB,GAAG,QAAQ,CAAC;IACnC,CAAC;IAED,UAAU;QACR,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,gBAAgB,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,KAAmB;QAC5B,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;IAC3B,CAAC;CACF"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { OrchestratorToolName } from "../tools/index.js";
|
|
2
|
+
import type { FixManifest } from "./finish-tool.js";
|
|
3
|
+
/**
|
|
4
|
+
* Hard caps on a single orchestrator session. Pre-flight checks against these
|
|
5
|
+
* happen on every step boundary; exceeding any cap aborts the session and
|
|
6
|
+
* returns the partial manifest accumulated so far.
|
|
7
|
+
*/
|
|
8
|
+
export interface BudgetCaps {
|
|
9
|
+
/** Max number of tool calls before forced termination. Default 100. */
|
|
10
|
+
maxToolCalls: number;
|
|
11
|
+
/** Max cumulative input tokens across all LLM calls. Default 500_000. */
|
|
12
|
+
maxInputTokensTotal: number;
|
|
13
|
+
/** Max estimated USD spend for this session. Default $5. */
|
|
14
|
+
maxSessionUsd: number;
|
|
15
|
+
/** Max wall-clock seconds before forced termination. Default 300 (5 min). */
|
|
16
|
+
maxWallSeconds: number;
|
|
17
|
+
/**
|
|
18
|
+
* Inject a watchdog system message every N tool calls reminding the model
|
|
19
|
+
* to converge on a manifest. Defaults to 20 — i.e. on calls 20, 40, 60,
|
|
20
|
+
* 80, the model gets a "are you converging?" nudge. Set to 0 to disable.
|
|
21
|
+
*/
|
|
22
|
+
watchdogIntervalCalls: number;
|
|
23
|
+
}
|
|
24
|
+
export declare const DEFAULT_BUDGET: BudgetCaps;
|
|
25
|
+
/** Cumulative usage so far in a session. */
|
|
26
|
+
export interface UsageSnapshot {
|
|
27
|
+
inputTokens: number;
|
|
28
|
+
outputTokens: number;
|
|
29
|
+
estimatedUsd: number;
|
|
30
|
+
toolCallCount: number;
|
|
31
|
+
elapsedMs: number;
|
|
32
|
+
}
|
|
33
|
+
/** Reason a session terminated, present on every SessionResult. */
|
|
34
|
+
export type StopReason = "completed" | "tool_call_limit" | "input_token_limit" | "usd_limit" | "wall_time_limit" | "model_error" | "aborted" | "no_finish_called";
|
|
35
|
+
/**
|
|
36
|
+
* Discriminated event union streamed during a session. Consumers (web app's
|
|
37
|
+
* SSE channel, R2 log writer, vitest assertions) subscribe via the
|
|
38
|
+
* orchestrator's `onEvent` callback. The same events end up in the durable
|
|
39
|
+
* NDJSON log so the session can be replayed offline.
|
|
40
|
+
*/
|
|
41
|
+
export type SessionEvent = {
|
|
42
|
+
kind: "session_started";
|
|
43
|
+
sessionId: string;
|
|
44
|
+
domain: string;
|
|
45
|
+
startedAt: string;
|
|
46
|
+
} | {
|
|
47
|
+
kind: "tool_call";
|
|
48
|
+
toolName: OrchestratorToolName | "finish_audit";
|
|
49
|
+
input: unknown;
|
|
50
|
+
} | {
|
|
51
|
+
kind: "tool_result";
|
|
52
|
+
toolName: OrchestratorToolName | "finish_audit";
|
|
53
|
+
result: unknown;
|
|
54
|
+
} | {
|
|
55
|
+
kind: "thought";
|
|
56
|
+
text: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Per-step delta. inputTokens/outputTokens are this step alone (not
|
|
60
|
+
* cumulative); estimatedUsd is the per-step cost (sum these to get total).
|
|
61
|
+
* `durationMs` is wall-clock time from the start of this step to when
|
|
62
|
+
* its onStepFinish fired — undefined when prepareStep didn't run for the
|
|
63
|
+
* very first step. Cumulative state lives in `session_finished.usage`.
|
|
64
|
+
*/
|
|
65
|
+
| {
|
|
66
|
+
kind: "step_usage";
|
|
67
|
+
inputTokens: number;
|
|
68
|
+
outputTokens: number;
|
|
69
|
+
estimatedUsd: number;
|
|
70
|
+
durationMs?: number;
|
|
71
|
+
} | {
|
|
72
|
+
kind: "watchdog_injected";
|
|
73
|
+
toolCallCount: number;
|
|
74
|
+
} | {
|
|
75
|
+
kind: "budget_warning";
|
|
76
|
+
reason: string;
|
|
77
|
+
usage: UsageSnapshot;
|
|
78
|
+
} | {
|
|
79
|
+
kind: "session_finished";
|
|
80
|
+
reason: StopReason;
|
|
81
|
+
usage: UsageSnapshot;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Final result returned by `runOrchestrator`. `manifest` is whatever
|
|
85
|
+
* `finish_audit` was called with — null when the session terminated for any
|
|
86
|
+
* reason other than the model calling finish_audit.
|
|
87
|
+
*/
|
|
88
|
+
export interface SessionResult {
|
|
89
|
+
sessionId: string;
|
|
90
|
+
domain: string;
|
|
91
|
+
reason: StopReason;
|
|
92
|
+
manifest: FixManifest | null;
|
|
93
|
+
usage: UsageSnapshot;
|
|
94
|
+
/** Full event log from the session, in order. */
|
|
95
|
+
events: SessionEvent[];
|
|
96
|
+
/** Error message when reason indicates failure. */
|
|
97
|
+
error?: string;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/ai/orchestrator/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,uEAAuE;IACvE,YAAY,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,mBAAmB,EAAE,MAAM,CAAC;IAC5B,4DAA4D;IAC5D,aAAa,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,cAAc,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,eAAO,MAAM,cAAc,EAAE,UAM5B,CAAC;AAEF,4CAA4C;AAC5C,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,mEAAmE;AACnE,MAAM,MAAM,UAAU,GAClB,WAAW,GACX,iBAAiB,GACjB,mBAAmB,GACnB,WAAW,GACX,iBAAiB,GACjB,aAAa,GACb,SAAS,GACT,kBAAkB,CAAC;AAEvB;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACjF;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,oBAAoB,GAAG,cAAc,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GACtF;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,EAAE,oBAAoB,GAAG,cAAc,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GACzF;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AACnC;;;;;;GAMG;GACD;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5G;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GACpD;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,aAAa,CAAA;CAAE,GAChE;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,MAAM,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,aAAa,CAAA;CAAE,CAAC;AAE3E;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,aAAa,CAAC;IACrB,iDAAiD;IACjD,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/ai/orchestrator/types.ts"],"names":[],"mappings":"AAyBA,MAAM,CAAC,MAAM,cAAc,GAAe;IACxC,YAAY,EAAE,GAAG;IACjB,mBAAmB,EAAE,OAAO;IAC5B,aAAa,EAAE,CAAC;IAChB,cAAc,EAAE,GAAG;IACnB,qBAAqB,EAAE,EAAE;CAC1B,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare function probeCacheKey(...parts: string[]): string;
|
|
2
|
+
/**
|
|
3
|
+
* Lightweight content-addressable cache for external probe responses
|
|
4
|
+
* (SerpAPI, ask_ai_engine). Simpler than the cachedFetch HTTP cache —
|
|
5
|
+
* no ETag/Last-Modified, no negative cache, just key → JSON with TTL.
|
|
6
|
+
*
|
|
7
|
+
* Misses (file absent, malformed, expired) all return null silently —
|
|
8
|
+
* the caller fetches fresh and writes back.
|
|
9
|
+
*/
|
|
10
|
+
export declare function readProbeCache<T>(dir: string, key: string, ttlMs: number): Promise<T | null>;
|
|
11
|
+
export declare function writeProbeCache<T>(dir: string, key: string, data: T): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/ai/probes/cache.ts"],"names":[],"mappings":"AASA,wBAAgB,aAAa,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAExD;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAkBlG;AAED,wBAAsB,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAOzF"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
export function probeCacheKey(...parts) {
|
|
5
|
+
return createHash("sha256").update(parts.join("|")).digest("hex");
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Lightweight content-addressable cache for external probe responses
|
|
9
|
+
* (SerpAPI, ask_ai_engine). Simpler than the cachedFetch HTTP cache —
|
|
10
|
+
* no ETag/Last-Modified, no negative cache, just key → JSON with TTL.
|
|
11
|
+
*
|
|
12
|
+
* Misses (file absent, malformed, expired) all return null silently —
|
|
13
|
+
* the caller fetches fresh and writes back.
|
|
14
|
+
*/
|
|
15
|
+
export async function readProbeCache(dir, key, ttlMs) {
|
|
16
|
+
const path = join(dir, `${key}.json`);
|
|
17
|
+
let raw;
|
|
18
|
+
try {
|
|
19
|
+
raw = await readFile(path, "utf8");
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
let entry;
|
|
25
|
+
try {
|
|
26
|
+
entry = JSON.parse(raw);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
if (typeof entry.cachedAt !== "string")
|
|
32
|
+
return null;
|
|
33
|
+
const ageMs = Date.now() - new Date(entry.cachedAt).getTime();
|
|
34
|
+
if (Number.isNaN(ageMs) || ageMs > ttlMs)
|
|
35
|
+
return null;
|
|
36
|
+
return entry.data;
|
|
37
|
+
}
|
|
38
|
+
export async function writeProbeCache(dir, key, data) {
|
|
39
|
+
await mkdir(dir, { recursive: true });
|
|
40
|
+
const path = join(dir, `${key}.json`);
|
|
41
|
+
const tmp = `${path}.tmp`;
|
|
42
|
+
const entry = { cachedAt: new Date().toISOString(), data };
|
|
43
|
+
await writeFile(tmp, JSON.stringify(entry), "utf8");
|
|
44
|
+
await rename(tmp, path);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../src/ai/probes/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAOjC,MAAM,UAAU,aAAa,CAAC,GAAG,KAAe;IAC9C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACpE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAI,GAAW,EAAE,GAAW,EAAE,KAAa;IAC7E,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC;IACtC,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,KAAqB,CAAC;IAC1B,IAAI,CAAC;QACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAmB,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACpD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;IAC9D,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,KAAK;QAAE,OAAO,IAAI,CAAC;IACtD,OAAO,KAAK,CAAC,IAAI,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAI,GAAW,EAAE,GAAW,EAAE,IAAO;IACxE,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC;IACtC,MAAM,GAAG,GAAG,GAAG,IAAI,MAAM,CAAC;IAC1B,MAAM,KAAK,GAAmB,EAAE,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC;IAC3E,MAAM,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC;IACpD,MAAM,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Probe an AI answer engine with a query. The whole point of this tool —
|
|
4
|
+
* the AEO citability check — is to ask the same engines users will ask
|
|
5
|
+
* and see whether they cite the page being audited. No proxies, no
|
|
6
|
+
* heuristics. The cited-URLs list comes back, and if `candidateUrl` is
|
|
7
|
+
* provided we report whether it (by hostname) appears.
|
|
8
|
+
*
|
|
9
|
+
* Provider key resolution: explicit `apiKey` arg → engine-specific env
|
|
10
|
+
* var. Cached for 24h on `engine + query` — same query against the same
|
|
11
|
+
* engine reliably hits the same answer for the cache window.
|
|
12
|
+
*/
|
|
13
|
+
export declare const askAiEngineTool: {
|
|
14
|
+
name: string;
|
|
15
|
+
description: string;
|
|
16
|
+
inputSchema: z.ZodType<{
|
|
17
|
+
engine: "anthropic" | "perplexity" | "gemini";
|
|
18
|
+
query: string;
|
|
19
|
+
candidateUrl?: string | undefined;
|
|
20
|
+
apiKey?: string | undefined;
|
|
21
|
+
cacheDir?: string | undefined;
|
|
22
|
+
}, unknown, z.core.$ZodTypeInternals<{
|
|
23
|
+
engine: "anthropic" | "perplexity" | "gemini";
|
|
24
|
+
query: string;
|
|
25
|
+
candidateUrl?: string | undefined;
|
|
26
|
+
apiKey?: string | undefined;
|
|
27
|
+
cacheDir?: string | undefined;
|
|
28
|
+
}, unknown>>;
|
|
29
|
+
outputSchema: z.ZodType<{
|
|
30
|
+
engine: "anthropic" | "perplexity" | "gemini";
|
|
31
|
+
query: string;
|
|
32
|
+
answer: string;
|
|
33
|
+
citedUrls: string[];
|
|
34
|
+
candidateCited: boolean | null;
|
|
35
|
+
fromCache: boolean;
|
|
36
|
+
apiCostUsd: number;
|
|
37
|
+
}, unknown, z.core.$ZodTypeInternals<{
|
|
38
|
+
engine: "anthropic" | "perplexity" | "gemini";
|
|
39
|
+
query: string;
|
|
40
|
+
answer: string;
|
|
41
|
+
citedUrls: string[];
|
|
42
|
+
candidateCited: boolean | null;
|
|
43
|
+
fromCache: boolean;
|
|
44
|
+
apiCostUsd: number;
|
|
45
|
+
}, unknown>>;
|
|
46
|
+
toAiTool(): import("ai").Tool<{
|
|
47
|
+
engine: "anthropic" | "perplexity" | "gemini";
|
|
48
|
+
query: string;
|
|
49
|
+
candidateUrl?: string | undefined;
|
|
50
|
+
apiKey?: string | undefined;
|
|
51
|
+
cacheDir?: string | undefined;
|
|
52
|
+
}, import("./types.js").ToolResult<{
|
|
53
|
+
engine: "anthropic" | "perplexity" | "gemini";
|
|
54
|
+
query: string;
|
|
55
|
+
answer: string;
|
|
56
|
+
citedUrls: string[];
|
|
57
|
+
candidateCited: boolean | null;
|
|
58
|
+
fromCache: boolean;
|
|
59
|
+
apiCostUsd: number;
|
|
60
|
+
}>>;
|
|
61
|
+
run(input: {
|
|
62
|
+
engine: "anthropic" | "perplexity" | "gemini";
|
|
63
|
+
query: string;
|
|
64
|
+
candidateUrl?: string | undefined;
|
|
65
|
+
apiKey?: string | undefined;
|
|
66
|
+
cacheDir?: string | undefined;
|
|
67
|
+
}, ctx?: import("./types.js").ToolExecuteContext): Promise<import("./types.js").ToolResult<{
|
|
68
|
+
engine: "anthropic" | "perplexity" | "gemini";
|
|
69
|
+
query: string;
|
|
70
|
+
answer: string;
|
|
71
|
+
citedUrls: string[];
|
|
72
|
+
candidateCited: boolean | null;
|
|
73
|
+
fromCache: boolean;
|
|
74
|
+
apiCostUsd: number;
|
|
75
|
+
}>>;
|
|
76
|
+
};
|
|
77
|
+
//# sourceMappingURL=ask-ai-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ask-ai-engine.d.ts","sourceRoot":"","sources":["../../../src/ai/tools/ask-ai-engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAiOxB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoE1B,CAAC"}
|