@plasm_lang/vercel-agent 0.3.120 → 0.3.123

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.
@@ -0,0 +1,97 @@
1
+ import {
2
+ createArchiveStore,
3
+ resolveArchiveBackend,
4
+ type RunSnapshot,
5
+ } from "@plasm_lang/vercel-agent";
6
+
7
+ /** One scan cycle's plasm_run rows — derived from durable operator archives. */
8
+ export interface LastRunMeta {
9
+ at: string;
10
+ status: "ok" | "error";
11
+ message?: string;
12
+ runIds: string[];
13
+ logicalSessionRef?: string;
14
+ okCount: number;
15
+ errorCount: number;
16
+ source: "archive";
17
+ }
18
+
19
+ const SCAN_BATCH_GAP_MS = 15 * 60 * 1000;
20
+
21
+ function radarRunsForIntent(runs: RunSnapshot[], intent: string): RunSnapshot[] {
22
+ return runs
23
+ .filter((run) => run.intent === intent)
24
+ .sort((a, b) => b.archived_at.localeCompare(a.archived_at));
25
+ }
26
+
27
+ /** Latest plasm_run batch for one agent scan (same session, within one workflow window). */
28
+ function latestScanBatch(runs: RunSnapshot[]): RunSnapshot[] {
29
+ if (runs.length === 0) return [];
30
+ const [latest] = runs;
31
+ const latestMs = Date.parse(latest.archived_at);
32
+ const sessionRef = latest.logical_session_ref;
33
+ const batch: RunSnapshot[] = [];
34
+
35
+ for (const run of runs) {
36
+ if (sessionRef && run.logical_session_ref !== sessionRef) continue;
37
+ const runMs = Date.parse(run.archived_at);
38
+ if (latestMs - runMs > SCAN_BATCH_GAP_MS) break;
39
+ batch.push(run);
40
+ }
41
+
42
+ return batch.sort((a, b) => a.archived_at.localeCompare(b.archived_at));
43
+ }
44
+
45
+ export function deriveLastRunFromRuns(
46
+ runs: RunSnapshot[],
47
+ intent: string,
48
+ ): LastRunMeta | null {
49
+ const intentRuns = radarRunsForIntent(runs, intent);
50
+ const batch = latestScanBatch(intentRuns);
51
+ if (batch.length === 0) return null;
52
+
53
+ const latest = batch[batch.length - 1];
54
+ const okCount = batch.filter((run) => run.ok).length;
55
+ const errorCount = batch.length - okCount;
56
+
57
+ return {
58
+ at: latest.archived_at,
59
+ status: latest.ok ? "ok" : "error",
60
+ message: latest.ok ? undefined : latest.message,
61
+ runIds: batch.map((run) => run.run_id),
62
+ logicalSessionRef: latest.logical_session_ref,
63
+ okCount,
64
+ errorCount,
65
+ source: "archive",
66
+ };
67
+ }
68
+
69
+ export async function loadLastRunFromArchives(
70
+ agentRoot: string,
71
+ intent: string,
72
+ ): Promise<LastRunMeta | null> {
73
+ const archive = createArchiveStore(agentRoot);
74
+ const runs = await archive.listRuns(200);
75
+ return deriveLastRunFromRuns(runs, intent);
76
+ }
77
+
78
+ export async function radarArchiveSummary(
79
+ agentRoot: string,
80
+ intent: string,
81
+ ): Promise<{
82
+ archiveBackend: ReturnType<typeof resolveArchiveBackend>;
83
+ totalRuns: number;
84
+ intentRuns: number;
85
+ lastRun: LastRunMeta | null;
86
+ }> {
87
+ const archive = createArchiveStore(agentRoot);
88
+ const runs = await archive.listRuns(200);
89
+ const intentRuns = radarRunsForIntent(runs, intent);
90
+
91
+ return {
92
+ archiveBackend: resolveArchiveBackend(),
93
+ totalRuns: runs.length,
94
+ intentRuns: intentRuns.length,
95
+ lastRun: deriveLastRunFromRuns(runs, intent),
96
+ };
97
+ }
@@ -1,34 +1,9 @@
1
- import { mkdir, readFile, writeFile } from "node:fs/promises";
2
- import path from "node:path";
3
-
4
- export interface LastRunMeta {
5
- at: string;
6
- status: "ok" | "skipped" | "error";
7
- message?: string;
8
- runIds?: string[];
9
- logicalSessionRef?: string;
10
- }
11
-
12
- function researchDir(agentRoot: string): string {
13
- return path.join(agentRoot, ".plasm", "research");
14
- }
15
-
16
- function lastRunPath(agentRoot: string): string {
17
- return path.join(researchDir(agentRoot), "last-run.json");
18
- }
19
-
20
- export async function loadLastRun(agentRoot: string): Promise<LastRunMeta | null> {
21
- try {
22
- const raw = await readFile(lastRunPath(agentRoot), "utf8");
23
- return JSON.parse(raw) as LastRunMeta;
24
- } catch {
25
- return null;
26
- }
27
- }
28
-
29
- export async function saveLastRun(agentRoot: string, meta: LastRunMeta): Promise<void> {
30
- await mkdir(researchDir(agentRoot), { recursive: true });
31
- await writeFile(lastRunPath(agentRoot), `${JSON.stringify(meta, null, 2)}\n`, "utf8");
1
+ function isVercelRuntime(): boolean {
2
+ return (
3
+ process.env.VERCEL === "1" ||
4
+ Boolean(process.env.VERCEL_DEPLOYMENT_ID?.trim()) ||
5
+ Boolean(process.env.VERCEL_ENV?.trim())
6
+ );
32
7
  }
33
8
 
34
9
  /** Host infra — not a substitute for Plasm catalog calls. */
@@ -40,11 +15,7 @@ export function gatewayConfigured(): boolean {
40
15
  ) {
41
16
  return true;
42
17
  }
43
- return (
44
- process.env.VERCEL === "1" ||
45
- Boolean(process.env.VERCEL_DEPLOYMENT_ID?.trim()) ||
46
- Boolean(process.env.VERCEL_ENV?.trim())
47
- );
18
+ return isVercelRuntime();
48
19
  }
49
20
 
50
21
  /** Outbound Tavily auth present on host — agent still calls Tavily via Plasm. */
@@ -1,12 +1,8 @@
1
1
  import { type AuthoringContext } from "@plasm_lang/vercel-agent";
2
2
 
3
+ import { radarArchiveSummary } from "./radar-archive-status.js";
3
4
  import { drainRunAudit, resetRunAudit } from "./run-audit.js";
4
- import {
5
- gatewayConfigured,
6
- loadLastRun,
7
- saveLastRun,
8
- tavilyConfigured,
9
- } from "./radar-state.js";
5
+ import { gatewayConfigured, tavilyConfigured } from "./radar-state.js";
10
6
 
11
7
  /** Stable intent — same string on every radar turn (`plasm_context` session reuse). */
12
8
  export const MCP_RADAR_INTENT =
@@ -52,11 +48,6 @@ export async function runRadar(
52
48
  if (!gatewayConfigured()) {
53
49
  const error =
54
50
  "AI Gateway is not configured (link project on Vercel or set AI_GATEWAY_API_KEY locally)";
55
- await saveLastRun(ctx.agentRoot, {
56
- at: new Date().toISOString(),
57
- status: "error",
58
- message: error,
59
- });
60
51
  return { ok: false, skipped: true, reason: "ai_gateway_missing", error };
61
52
  }
62
53
 
@@ -64,15 +55,7 @@ export async function runRadar(
64
55
  resetRunAudit();
65
56
  const agent = await ctx.getAgent();
66
57
  const turn = await agent.generate(buildRadarGoal(options), { resetConversation: false });
67
- const runAt = new Date().toISOString();
68
- const audit = drainRunAudit();
69
-
70
- await saveLastRun(ctx.agentRoot, {
71
- at: runAt,
72
- status: "ok",
73
- runIds: audit.runIds,
74
- logicalSessionRef: audit.logicalSessionRef,
75
- });
58
+ void drainRunAudit();
76
59
 
77
60
  return {
78
61
  ok: true,
@@ -81,11 +64,6 @@ export async function runRadar(
81
64
  };
82
65
  } catch (err) {
83
66
  const message = String(err);
84
- await saveLastRun(ctx.agentRoot, {
85
- at: new Date().toISOString(),
86
- status: "error",
87
- message,
88
- });
89
67
  return {
90
68
  ok: false,
91
69
  skipped: false,
@@ -95,16 +73,21 @@ export async function runRadar(
95
73
  }
96
74
 
97
75
  export async function radarStatus(agentRoot: string): Promise<Record<string, unknown>> {
98
- const last = await loadLastRun(agentRoot);
76
+ const archive = await radarArchiveSummary(agentRoot, MCP_RADAR_INTENT);
77
+ const last = archive.lastRun;
78
+
99
79
  return {
100
80
  gateway: gatewayConfigured(),
101
81
  tavily: tavilyConfigured(),
102
82
  lastRun: last,
103
83
  intent: MCP_RADAR_INTENT,
104
84
  observability: {
85
+ archiveBackend: archive.archiveBackend,
105
86
  operatorRunsUrl: "/operator/runs",
106
87
  operatorSessionsUrl: "/operator/sessions",
107
88
  operatorArchivesUrl: "/operator/archives",
89
+ totalArchivedRuns: archive.totalRuns,
90
+ intentArchivedRuns: archive.intentRuns,
108
91
  runIds: last?.runIds ?? [],
109
92
  logicalSessionRef: last?.logicalSessionRef,
110
93
  },
@@ -8,18 +8,17 @@
8
8
  "dev": "plasm-agent dev",
9
9
  "dev:interactive": "plasm-agent dev --interactive",
10
10
  "info": "plasm-agent info",
11
- "deploy": "vercel deploy",
11
+ "deploy": "vercel deploy --prod",
12
12
  "eval": "tsx scripts/run-evals.ts",
13
13
  "smoke:channel": "tsx scripts/smoke-channel.ts",
14
- "test:proof-extract": "tsx scripts/test-proof-extract.mjs",
15
- "test:hn-preflight": "tsx scripts/test-hn-algolia-preflight.mjs"
14
+ "test:proof-extract": "tsx scripts/test-proof-extract.mjs"
16
15
  },
17
16
  "dependencies": {
18
17
  "@ai-sdk/otel": "1.0.14",
19
- "@plasm_lang/engine": "0.3.112",
20
- "@plasm_lang/vercel-agent": "file:../../plasm-oss/packages/plasm-agent",
21
- "@vercel/blob": "0.27.3",
18
+ "@plasm_lang/engine": "0.3.121",
19
+ "@plasm_lang/vercel-agent": "0.3.120",
22
20
  "@vercel/functions": "3.7.4",
21
+ "@vercel/oidc": "^3.8.0",
23
22
  "@vercel/otel": "1.14.2",
24
23
  "ai": "7.0.14",
25
24
  "esbuild": "0.25.12",