@axiom-lattice/core 3.0.3 → 3.0.4

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/dist/index.mjs CHANGED
@@ -11023,6 +11023,10 @@ Always set metadata on agent creation. At minimum:
11023
11023
  - verified: "unverified" (upgraded after eval passes)
11024
11024
  - version: "1.0" (bump on each update_agent)
11025
11025
  - source: the material name or "user-description"
11026
+ - role: "orchestrator" | "sub-agent" \u2014 set when the agent is part of a
11027
+ parent+subAgents structure (role clarity, User Interaction Rules).
11028
+ The eval project is NOT recorded \u2014 it is derived by naming convention
11029
+ (eval-{agent-id}, see [[eval-verify]] Setup).
11026
11030
  When trust upgrades, update both the skill's verified frontmatter and
11027
11031
  the agent's metadata.verified \u2014 they must stay in sync.
11028
11032
 
@@ -11144,8 +11148,14 @@ verified: unverified
11144
11148
  ## Setup
11145
11149
 
11146
11150
  0. Load [[eval-design-tests]] for case design guidance
11147
- 1. \`read_eval list_projects\` \u2192 find "eval-{domain}"
11148
- Exists \u2192 reuse projectId. New \u2192 create_project(name: "eval-{domain}")
11151
+ 1. **One eval project per agent**, named \`eval-{agent-id}\`:
11152
+ \`read_eval list_projects\` \u2192 find "eval-{agent-id}"
11153
+ Exists \u2192 reuse projectId. New \u2192 create_project(name: "eval-{agent-id}")
11154
+ - The agent-id is the eval project's subject. Observability: from an
11155
+ agent's id you can find its eval project by naming convention.
11156
+ - Orchestrator + subAgents \u2192 one eval project per sub-agent
11157
+ (eval-{sub-agent-id}) PLUS one integration eval project for the
11158
+ parent (eval-{parent-id}) \u2014 see Layered verification below.
11149
11159
  2. \`manage_eval create_case(suiteId, ...)\` per sample or scenario
11150
11160
  Required: inputMessage, steps=[{agent_id}], outputType, contentAssertion
11151
11161
 
@@ -21632,6 +21642,7 @@ Your sub-skills (accessible via the MOC or direct loading):
21632
21642
  - [[learn-capability]] \u2014 Learn from any source material \u2192 skills + agents
21633
21643
  - [[agent-build]] \u2014 Design and build single agents (REACT/DEEP_AGENT)
21634
21644
  - [[design-workflow]] \u2014 Design workflow agents
21645
+ - [[eval-verify]] \u2014 Run evaluations with fix loop, hold-out, trust upgrade
21635
21646
  - [[task-tracking]] \u2014 Manage persistent tasks (manage_task)
21636
21647
  - [[completion-gate]] \u2014 THE rule: no agent is "done" without eval
21637
21648
  - [[domain-moc]] \u2014 Create the domain MOC (mandatory per learning run)
@@ -28261,7 +28272,26 @@ Use read_eval get_run_results for multiple runs and present comparison.
28261
28272
  `
28262
28273
  };
28263
28274
 
28275
+ // src/tool_lattice/withToolTimeout.ts
28276
+ function withToolTimeout(executor, timeoutMs = 18e4) {
28277
+ return async (input, exeConfig) => {
28278
+ return new Promise((resolve4, reject) => {
28279
+ const timer = setTimeout(() => {
28280
+ reject(new Error(`Tool execution timed out after ${timeoutMs}ms`));
28281
+ }, timeoutMs);
28282
+ executor(input, exeConfig).then((result) => {
28283
+ clearTimeout(timer);
28284
+ resolve4(result);
28285
+ }).catch((err) => {
28286
+ clearTimeout(timer);
28287
+ reject(err);
28288
+ });
28289
+ });
28290
+ };
28291
+ }
28292
+
28264
28293
  // src/middlewares/evalMiddleware.ts
28294
+ var RUN_EVAL_SYNC_WAIT_MS = 15e4;
28265
28295
  function getStore() {
28266
28296
  return getStoreLattice("default", "eval").store;
28267
28297
  }
@@ -28288,6 +28318,23 @@ function sanitize(obj) {
28288
28318
  }
28289
28319
  return out;
28290
28320
  }
28321
+ function aggregateHoldoutResults(results) {
28322
+ const passed = results.filter((r) => r.pass).length;
28323
+ return {
28324
+ holdout: true,
28325
+ passedCases: passed,
28326
+ failedCases: results.length - passed,
28327
+ passRate: results.length > 0 ? passed / results.length : 0,
28328
+ totalCases: results.length
28329
+ };
28330
+ }
28331
+ async function runWithResults(tid, store, svc, run, runnerAlive) {
28332
+ const results = run.status === "completed" ? await store.getResultsByRun(tid, run.id) : void 0;
28333
+ if (run.holdout && results) {
28334
+ return { ...run, runnerAlive, results: aggregateHoldoutResults(results) };
28335
+ }
28336
+ return { ...run, runnerAlive, results };
28337
+ }
28291
28338
  function createReadEvalTool() {
28292
28339
  const schema6 = z66.object({
28293
28340
  action: z66.enum([
@@ -28522,100 +28569,119 @@ function createRunEvalTool() {
28522
28569
  projectId: z66.string().optional().describe("Required for start"),
28523
28570
  suiteIds: z66.array(z66.string()).optional().describe("Optional for start \u2014 only run these suites (e.g. dev set only). Omit to run all."),
28524
28571
  caseIds: z66.array(z66.string()).optional().describe("Optional for start \u2014 only run these cases across the selected suites. Omit to run all cases in those suites."),
28525
- runId: z66.string().optional().describe("Required for status, resume, abort")
28572
+ runId: z66.string().optional().describe("Required for status, resume, abort"),
28573
+ sleepMs: z66.number().int().min(0).max(12e4).optional().describe("Optional for status \u2014 sleep this many ms BEFORE checking the run, to pace polling (e.g. 15000 \u2192 30000 \u2192 60000 \u2192 120000). Omit to check immediately."),
28574
+ wait: z66.boolean().optional().describe("Optional for start \u2014 defaults to true: block synchronously (up to ~150s) and return final results in one call. Set false to return the runId immediately and poll.")
28526
28575
  });
28527
28576
  return tool62(
28528
- async (input, exeConfig) => {
28529
- const tid = tenantId(exeConfig);
28530
- if (!tid) {
28531
- return JSON.stringify({ success: false, error: "No tenant context. Agent must be invoked through gateway." });
28532
- }
28533
- try {
28534
- const store = getStore();
28535
- const svc = getEvalRunService();
28536
- let data;
28537
- switch (input.action) {
28538
- case "start": {
28539
- const ctx = workspaceContext(exeConfig);
28540
- const runId = await svc.startRun(tid, input.projectId, input.suiteIds, input.caseIds, {
28541
- workspaceId: ctx.workspaceId,
28542
- projectId: ctx.projectId
28543
- });
28544
- data = sanitize({ runId, message: "Run started. Poll with run_eval status (backoff: 15s\u219230s\u219260s\u2192max 120s)." });
28545
- break;
28546
- }
28547
- case "status": {
28548
- const run = await store.getRunById(tid, input.runId);
28549
- if (!run) return JSON.stringify({ success: false, error: "Run not found" });
28550
- data = sanitize({ ...run, runnerAlive: svc.isRunning(input.runId) });
28551
- break;
28552
- }
28553
- case "resume": {
28554
- const run = await store.getRunById(tid, input.runId);
28555
- if (!run) return JSON.stringify({ success: false, error: "Run not found" });
28556
- const runnerAlive = svc.isRunning(input.runId);
28557
- if (run.status === "running" && !runnerAlive) {
28558
- await store.updateRunStatus(tid, run.id, {
28559
- status: "failed",
28560
- error: "Gateway restarted \u2014 run orphaned",
28561
- completedAt: /* @__PURE__ */ new Date()
28562
- });
28563
- data = sanitize({
28564
- ...run,
28565
- status: "failed",
28566
- runnerAlive: false,
28567
- message: "Run was orphaned \u2014 marked failed. Start a new run."
28568
- });
28577
+ withToolTimeout(
28578
+ async (input, exeConfig) => {
28579
+ const tid = tenantId(exeConfig);
28580
+ if (!tid) {
28581
+ return JSON.stringify({ success: false, error: "No tenant context. Agent must be invoked through gateway." });
28582
+ }
28583
+ try {
28584
+ const store = getStore();
28585
+ const svc = getEvalRunService();
28586
+ let data;
28587
+ switch (input.action) {
28588
+ case "start": {
28589
+ const ctx = workspaceContext(exeConfig);
28590
+ const runId = await svc.startRun(tid, input.projectId, input.suiteIds, input.caseIds, ctx);
28591
+ if (input.wait === false) {
28592
+ data = sanitize({ runId, message: "Run started. Poll with run_eval status (backoff: 15s\u219230s\u219260s\u2192max 120s)." });
28593
+ break;
28594
+ }
28595
+ let timer;
28596
+ try {
28597
+ await Promise.race([
28598
+ svc.waitForRun(runId).catch(() => {
28599
+ }),
28600
+ new Promise((resolve4) => {
28601
+ timer = setTimeout(resolve4, RUN_EVAL_SYNC_WAIT_MS);
28602
+ })
28603
+ ]);
28604
+ } finally {
28605
+ if (timer) clearTimeout(timer);
28606
+ }
28607
+ const run = await store.getRunById(tid, runId);
28608
+ if (!run) return JSON.stringify({ success: false, error: "Run not found" });
28609
+ if (run.status === "running") {
28610
+ data = sanitize({
28611
+ runId,
28612
+ status: "running",
28613
+ runnerAlive: svc.isRunning(runId),
28614
+ message: `Run not finished within ${RUN_EVAL_SYNC_WAIT_MS / 1e3}s \u2014 poll with run_eval status(runId, sleepMs) (e.g. 15000, doubling up to 120000), or abort with run_eval abort.`
28615
+ });
28616
+ break;
28617
+ }
28618
+ data = sanitize({ synced: true, ...await runWithResults(tid, store, svc, run, svc.isRunning(runId)) });
28569
28619
  break;
28570
28620
  }
28571
- const results = run.status === "completed" ? await store.getResultsByRun(tid, run.id) : void 0;
28572
- if (run.holdout && results) {
28573
- const passed = results.filter((r) => r.pass).length;
28574
- data = sanitize({
28575
- ...run,
28576
- runnerAlive,
28577
- results: {
28578
- holdout: true,
28579
- passedCases: passed,
28580
- failedCases: results.length - passed,
28581
- passRate: results.length > 0 ? passed / results.length : 0,
28582
- totalCases: results.length
28583
- }
28584
- });
28621
+ case "status": {
28622
+ if (input.sleepMs && input.sleepMs > 0) {
28623
+ await new Promise((resolve4) => setTimeout(resolve4, input.sleepMs));
28624
+ }
28625
+ const run = await store.getRunById(tid, input.runId);
28626
+ if (!run) return JSON.stringify({ success: false, error: "Run not found" });
28627
+ data = sanitize({ ...run, runnerAlive: svc.isRunning(input.runId) });
28585
28628
  break;
28586
28629
  }
28587
- data = sanitize({ ...run, runnerAlive, results });
28588
- break;
28589
- }
28590
- case "abort": {
28591
- const run = await store.getRunById(tid, input.runId);
28592
- if (!run) return JSON.stringify({ success: false, error: "Run not found" });
28593
- const ok = await svc.abortRun(input.runId);
28594
- data = sanitize({ aborted: ok });
28595
- break;
28630
+ case "resume": {
28631
+ const run = await store.getRunById(tid, input.runId);
28632
+ if (!run) return JSON.stringify({ success: false, error: "Run not found" });
28633
+ const runnerAlive = svc.isRunning(input.runId);
28634
+ if (run.status === "running" && !runnerAlive) {
28635
+ await store.updateRunStatus(tid, run.id, {
28636
+ status: "failed",
28637
+ error: "Gateway restarted \u2014 run orphaned",
28638
+ completedAt: /* @__PURE__ */ new Date()
28639
+ });
28640
+ data = sanitize({
28641
+ ...run,
28642
+ status: "failed",
28643
+ runnerAlive: false,
28644
+ message: "Run was orphaned \u2014 marked failed. Start a new run."
28645
+ });
28646
+ break;
28647
+ }
28648
+ data = sanitize(await runWithResults(tid, store, svc, run, runnerAlive));
28649
+ break;
28650
+ }
28651
+ case "abort": {
28652
+ const run = await store.getRunById(tid, input.runId);
28653
+ if (!run) return JSON.stringify({ success: false, error: "Run not found" });
28654
+ const ok = await svc.abortRun(input.runId);
28655
+ data = sanitize({ aborted: ok });
28656
+ break;
28657
+ }
28658
+ default:
28659
+ return JSON.stringify({ success: false, error: `Unknown action: ${input.action}` });
28596
28660
  }
28597
- default:
28598
- return JSON.stringify({ success: false, error: `Unknown action: ${input.action}` });
28661
+ return JSON.stringify({ success: true, data });
28662
+ } catch (e) {
28663
+ return JSON.stringify({ success: false, error: e.message });
28599
28664
  }
28600
- return JSON.stringify({ success: true, data });
28601
- } catch (e) {
28602
- return JSON.stringify({ success: false, error: e.message });
28603
28665
  }
28604
- },
28666
+ ),
28605
28667
  {
28606
28668
  name: "run_eval",
28607
- description: `Execute and manage evaluation runs. ASYNCHRONOUS \u2014 may take minutes.
28669
+ description: `Execute and manage evaluation runs.
28608
28670
 
28609
28671
  ACTIONS:
28610
- - start(projectId, suiteIds?, caseIds?) \u2014 begin evaluation (optionally only the listed suites, and/or only the listed cases). Returns runId.
28611
- - status(runId) \u2014 current status + runnerAlive flag:
28612
- \u2022 runnerAlive=true, status=running: keep polling
28672
+ - start(projectId, suiteIds?, caseIds?, wait?) \u2014 begin evaluation (optionally only the listed suites, and/or only the listed cases).
28673
+ wait=true (DEFAULT): SYNCHRONOUS \u2014 blocks up to ~150s and returns the FINAL RESULTS in one call:
28674
+ { status, results } with per-case pass/score (hold-out/validation runs: aggregates only \u2014 per-case details withheld by design).
28675
+ If the run exceeds 150s: returns { runId, status: "running" } \u2014 NOT an error \u2014 then poll with status/resume until completed.
28676
+ wait=false: fire-and-forget \u2014 returns { runId } immediately; poll with status.
28677
+ - status(runId, sleepMs?) \u2014 sleep sleepMs first (to pace polling), then return current status + runnerAlive flag:
28678
+ \u2022 runnerAlive=true, status=running: keep polling \u2014 call status(runId, sleepMs) with backoff 15s\u219230s\u219260s\u2192max 120s
28613
28679
  \u2022 runnerAlive=false, status=running: ORPHANED \u2014 resume marks it failed automatically; then start a new run
28614
28680
  \u2022 status=completed: get results with read_eval get_run_results or run_eval resume
28615
28681
  - resume(runId) \u2014 reconnect from new conversation. Returns status + results if completed.
28616
28682
  - abort(runId) \u2014 cancel running evaluation.
28617
28683
 
28618
- Polling: start at 15s, double each time, max 120s between polls. Batch reports.`,
28684
+ Polling (only needed with wait=false or after a sync timeout): call status(runId, sleepMs) so the tool sleeps before checking; start at 15s, double each time, max 120s between polls. Batch reports.`,
28619
28685
  schema: schema6
28620
28686
  }
28621
28687
  );
@@ -29295,7 +29361,8 @@ Both modes use the same agent type \u2014 skill only, no domain tools:
29295
29361
  verified: "unverified", # upgraded after eval passes
29296
29362
  version: "1.0", # bump on each update_agent
29297
29363
  source: "{material name}", # provenance
29298
- skill: "skill-name"
29364
+ skill: "skill-name",
29365
+ role: "orchestrator" | "sub-agent" # only for parent+subAgents structure
29299
29366
  }
29300
29367
  )
29301
29368
 
@@ -29391,6 +29458,11 @@ Run evaluation, fix loop, hold-out validation, trust upgrade. See
29391
29458
  [[eval-verify]] for the full workflow. The eval-design-tests and
29392
29459
  eval-run-and-govern skills cover case design and run governance.
29393
29460
 
29461
+ **One eval project per agent**, named \`eval-{agent-id}\` \u2014 every agent
29462
+ built by this workflow gets its own eval project (see eval-verify
29463
+ Setup). Orchestrator + subAgents \u2192 one eval project per sub-agent plus
29464
+ one integration eval for the parent.
29465
+
29394
29466
  Learning-specific suite guidance:
29395
29467
  - 0.2 \u2461 \u2192 {skill}-user-sample; samples \u22658 \u2192 also {skill}-validation
29396
29468
  - 0.2 \u2460 \u2192 {skill}-api-verified (single step, \xA74.1)
@@ -29429,11 +29501,12 @@ base is wanted (it is extra work beyond the skill).
29429
29501
  ## Fallback
29430
29502
 
29431
29503
  - All engines fail \u2192 suggest text version or different format.
29432
- - No eval agent \u2192 judge-only scoring, or build a production agent via \xA75
29433
- (generic or API-verified variant, thin prompt) \u2014 never reuse an agent
29434
- whose knowledge derives from the learning material.
29435
- Judge-only scoring does NOT unlock machine-confirmed \u2014 trust caps at
29436
- human-reviewed; say so explicitly.
29504
+ - Eval runtime unavailable (no eval agent / service down) \u2192 still
29505
+ DESIGN and CREATE the eval project with test cases (every agent MUST
29506
+ have an eval \u2014 no skip). If the eval cannot RUN now, deliver with
29507
+ trust capped at human-reviewed and state: "Test framework created;
29508
+ run the evaluation once the eval service is available." Judge-only
29509
+ scoring (when run) does NOT unlock machine-confirmed.
29437
29510
  - No test files \u2192 user-described scenarios as contentAssertion.
29438
29511
  - run_eval orphaned (resume shows runnerAlive=false) \u2192 \`run_eval resume(runId)\`
29439
29512
  marks it failed automatically; then \`run_eval start(projectId)\` to restart.