@soleri/core 2.7.0 → 2.9.0
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/extensions/index.d.ts +3 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/extensions/index.js +2 -0
- package/dist/extensions/index.js.map +1 -0
- package/dist/extensions/middleware.d.ts +13 -0
- package/dist/extensions/middleware.d.ts.map +1 -0
- package/dist/extensions/middleware.js +47 -0
- package/dist/extensions/middleware.js.map +1 -0
- package/dist/extensions/types.d.ts +64 -0
- package/dist/extensions/types.d.ts.map +1 -0
- package/dist/extensions/types.js +2 -0
- package/dist/extensions/types.js.map +1 -0
- package/dist/index.d.ts +8 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -16
- package/dist/index.js.map +1 -1
- package/dist/planning/gap-analysis.d.ts +2 -1
- package/dist/planning/gap-analysis.d.ts.map +1 -1
- package/dist/planning/gap-analysis.js +70 -1
- package/dist/planning/gap-analysis.js.map +1 -1
- package/dist/planning/gap-types.d.ts +8 -3
- package/dist/planning/gap-types.d.ts.map +1 -1
- package/dist/planning/gap-types.js +9 -1
- package/dist/planning/gap-types.js.map +1 -1
- package/dist/planning/planner.d.ts.map +1 -1
- package/dist/planning/planner.js +17 -5
- package/dist/planning/planner.js.map +1 -1
- package/dist/runtime/core-ops.d.ts +1 -1
- package/dist/runtime/core-ops.js +1 -1
- package/dist/runtime/facades/admin-facade.d.ts +8 -0
- package/dist/runtime/facades/admin-facade.d.ts.map +1 -0
- package/dist/runtime/facades/admin-facade.js +90 -0
- package/dist/runtime/facades/admin-facade.js.map +1 -0
- package/dist/runtime/facades/brain-facade.d.ts +8 -0
- package/dist/runtime/facades/brain-facade.d.ts.map +1 -0
- package/dist/runtime/facades/brain-facade.js +294 -0
- package/dist/runtime/facades/brain-facade.js.map +1 -0
- package/dist/runtime/facades/cognee-facade.d.ts +8 -0
- package/dist/runtime/facades/cognee-facade.d.ts.map +1 -0
- package/dist/runtime/facades/cognee-facade.js +154 -0
- package/dist/runtime/facades/cognee-facade.js.map +1 -0
- package/dist/runtime/facades/control-facade.d.ts +8 -0
- package/dist/runtime/facades/control-facade.d.ts.map +1 -0
- package/dist/runtime/facades/control-facade.js +244 -0
- package/dist/runtime/facades/control-facade.js.map +1 -0
- package/dist/runtime/facades/curator-facade.d.ts +8 -0
- package/dist/runtime/facades/curator-facade.d.ts.map +1 -0
- package/dist/runtime/facades/curator-facade.js +117 -0
- package/dist/runtime/facades/curator-facade.js.map +1 -0
- package/dist/runtime/facades/index.d.ts +10 -0
- package/dist/runtime/facades/index.d.ts.map +1 -0
- package/dist/runtime/facades/index.js +71 -0
- package/dist/runtime/facades/index.js.map +1 -0
- package/dist/runtime/facades/loop-facade.d.ts +8 -0
- package/dist/runtime/facades/loop-facade.d.ts.map +1 -0
- package/dist/runtime/facades/loop-facade.js +9 -0
- package/dist/runtime/facades/loop-facade.js.map +1 -0
- package/dist/runtime/facades/memory-facade.d.ts +8 -0
- package/dist/runtime/facades/memory-facade.d.ts.map +1 -0
- package/dist/runtime/facades/memory-facade.js +108 -0
- package/dist/runtime/facades/memory-facade.js.map +1 -0
- package/dist/runtime/facades/orchestrate-facade.d.ts +8 -0
- package/dist/runtime/facades/orchestrate-facade.d.ts.map +1 -0
- package/dist/runtime/facades/orchestrate-facade.js +58 -0
- package/dist/runtime/facades/orchestrate-facade.js.map +1 -0
- package/dist/runtime/facades/plan-facade.d.ts +8 -0
- package/dist/runtime/facades/plan-facade.d.ts.map +1 -0
- package/dist/runtime/facades/plan-facade.js +110 -0
- package/dist/runtime/facades/plan-facade.js.map +1 -0
- package/dist/runtime/facades/vault-facade.d.ts +8 -0
- package/dist/runtime/facades/vault-facade.d.ts.map +1 -0
- package/dist/runtime/facades/vault-facade.js +194 -0
- package/dist/runtime/facades/vault-facade.js.map +1 -0
- package/dist/runtime/grading-ops.d.ts +1 -1
- package/dist/runtime/grading-ops.js +2 -2
- package/dist/runtime/grading-ops.js.map +1 -1
- package/dist/runtime/vault-extra-ops.d.ts +2 -2
- package/dist/runtime/vault-extra-ops.d.ts.map +1 -1
- package/dist/runtime/vault-extra-ops.js +37 -2
- package/dist/runtime/vault-extra-ops.js.map +1 -1
- package/dist/streams/index.d.ts +4 -0
- package/dist/streams/index.d.ts.map +1 -0
- package/dist/streams/index.js +3 -0
- package/dist/streams/index.js.map +1 -0
- package/dist/streams/normalize.d.ts +14 -0
- package/dist/streams/normalize.d.ts.map +1 -0
- package/dist/streams/normalize.js +43 -0
- package/dist/streams/normalize.js.map +1 -0
- package/dist/streams/replayable-stream.d.ts +19 -0
- package/dist/streams/replayable-stream.d.ts.map +1 -0
- package/dist/streams/replayable-stream.js +90 -0
- package/dist/streams/replayable-stream.js.map +1 -0
- package/dist/vault/content-hash.d.ts +16 -0
- package/dist/vault/content-hash.d.ts.map +1 -0
- package/dist/vault/content-hash.js +21 -0
- package/dist/vault/content-hash.js.map +1 -0
- package/dist/vault/vault.d.ts +9 -0
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +49 -3
- package/dist/vault/vault.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/content-hash.test.ts +60 -0
- package/src/__tests__/core-ops.test.ts +10 -7
- package/src/__tests__/extensions.test.ts +233 -0
- package/src/__tests__/grading-ops.test.ts +2 -2
- package/src/__tests__/memory-cross-project-ops.test.ts +2 -2
- package/src/__tests__/normalize.test.ts +75 -0
- package/src/__tests__/playbook.test.ts +4 -4
- package/src/__tests__/replayable-stream.test.ts +66 -0
- package/src/__tests__/vault-extra-ops.test.ts +1 -1
- package/src/__tests__/vault.test.ts +72 -0
- package/src/extensions/index.ts +2 -0
- package/src/extensions/middleware.ts +53 -0
- package/src/extensions/types.ts +64 -0
- package/src/index.ts +14 -17
- package/src/planning/gap-analysis.ts +95 -1
- package/src/planning/gap-types.ts +12 -2
- package/src/planning/planner.ts +17 -5
- package/src/runtime/facades/admin-facade.ts +101 -0
- package/src/runtime/facades/brain-facade.ts +331 -0
- package/src/runtime/facades/cognee-facade.ts +162 -0
- package/src/runtime/facades/control-facade.ts +279 -0
- package/src/runtime/facades/curator-facade.ts +132 -0
- package/src/runtime/facades/index.ts +74 -0
- package/src/runtime/facades/loop-facade.ts +12 -0
- package/src/runtime/facades/memory-facade.ts +114 -0
- package/src/runtime/facades/orchestrate-facade.ts +68 -0
- package/src/runtime/facades/plan-facade.ts +119 -0
- package/src/runtime/facades/vault-facade.ts +223 -0
- package/src/runtime/grading-ops.ts +2 -2
- package/src/runtime/vault-extra-ops.ts +38 -2
- package/src/streams/index.ts +3 -0
- package/src/streams/normalize.ts +56 -0
- package/src/streams/replayable-stream.ts +92 -0
- package/src/vault/content-hash.ts +31 -0
- package/src/vault/vault.ts +73 -3
- package/src/runtime/core-ops.ts +0 -1443
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { FacadeConfig } from '../facades/types.js';
|
|
2
|
+
import type { OpMiddleware } from './types.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Wrap all ops in the given facades with middleware.
|
|
6
|
+
*
|
|
7
|
+
* Middleware chain follows the onion model:
|
|
8
|
+
* - before hooks: first middleware → last middleware → handler
|
|
9
|
+
* - after hooks: last middleware → first middleware (reverse)
|
|
10
|
+
*
|
|
11
|
+
* This mutates the facade ops in-place (replaces handlers).
|
|
12
|
+
*/
|
|
13
|
+
export function wrapWithMiddleware(facades: FacadeConfig[], middleware: OpMiddleware[]): void {
|
|
14
|
+
if (middleware.length === 0) return;
|
|
15
|
+
|
|
16
|
+
for (const facade of facades) {
|
|
17
|
+
for (const op of facade.ops) {
|
|
18
|
+
const originalHandler = op.handler;
|
|
19
|
+
|
|
20
|
+
op.handler = async (params: Record<string, unknown>) => {
|
|
21
|
+
// Run before hooks (first → last)
|
|
22
|
+
let currentParams = params;
|
|
23
|
+
for (const mw of middleware) {
|
|
24
|
+
if (mw.before) {
|
|
25
|
+
currentParams = await mw.before({
|
|
26
|
+
facade: facade.name,
|
|
27
|
+
op: op.name,
|
|
28
|
+
params: currentParams,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Run original handler
|
|
34
|
+
let result = await originalHandler(currentParams);
|
|
35
|
+
|
|
36
|
+
// Run after hooks (last → first)
|
|
37
|
+
for (let i = middleware.length - 1; i >= 0; i--) {
|
|
38
|
+
const mw = middleware[i];
|
|
39
|
+
if (mw.after) {
|
|
40
|
+
result = await mw.after({
|
|
41
|
+
facade: facade.name,
|
|
42
|
+
op: op.name,
|
|
43
|
+
params: currentParams,
|
|
44
|
+
result,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return result;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { OpDefinition, FacadeConfig } from '../facades/types.js';
|
|
2
|
+
import type { AgentRuntime } from '../runtime/types.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Middleware that wraps op execution with before/after hooks.
|
|
6
|
+
*
|
|
7
|
+
* - `before` runs before the op handler. Return modified params or throw to reject.
|
|
8
|
+
* - `after` runs after the op handler. Return modified result or throw.
|
|
9
|
+
*
|
|
10
|
+
* Multiple middleware are chained: before hooks run first→last,
|
|
11
|
+
* after hooks run last→first (onion model).
|
|
12
|
+
*/
|
|
13
|
+
export interface OpMiddleware {
|
|
14
|
+
/** Middleware name (for logging/debugging) */
|
|
15
|
+
name: string;
|
|
16
|
+
/** Runs before op handler. Return modified params or throw to reject. */
|
|
17
|
+
before?: (ctx: MiddlewareContext) => Promise<Record<string, unknown>>;
|
|
18
|
+
/** Runs after op handler. Return modified result or throw. */
|
|
19
|
+
after?: (ctx: MiddlewareContext & { result: unknown }) => Promise<unknown>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface MiddlewareContext {
|
|
23
|
+
facade: string;
|
|
24
|
+
op: string;
|
|
25
|
+
params: Record<string, unknown>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* User-defined extensions for a Soleri agent.
|
|
30
|
+
*
|
|
31
|
+
* Extensions live in `src/extensions/` and are auto-discovered by the entry
|
|
32
|
+
* point at startup. Core ops from `@soleri/core` are never modified — extensions
|
|
33
|
+
* are additive (new ops, new facades) or decorative (middleware).
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* // src/extensions/index.ts
|
|
38
|
+
* import type { AgentExtensions } from '@soleri/core';
|
|
39
|
+
* import type { AgentRuntime } from '@soleri/core';
|
|
40
|
+
*
|
|
41
|
+
* export default function loadExtensions(runtime: AgentRuntime): AgentExtensions {
|
|
42
|
+
* return {
|
|
43
|
+
* ops: [myCustomOp(runtime)],
|
|
44
|
+
* facades: [myCustomFacade(runtime)],
|
|
45
|
+
* middleware: [auditLogger],
|
|
46
|
+
* };
|
|
47
|
+
* }
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export interface AgentExtensions {
|
|
51
|
+
/** Extra ops merged into the core facade */
|
|
52
|
+
ops?: OpDefinition[];
|
|
53
|
+
/** New facades registered as separate MCP tools */
|
|
54
|
+
facades?: FacadeConfig[];
|
|
55
|
+
/** Middleware applied to all ops across all facades */
|
|
56
|
+
middleware?: OpMiddleware[];
|
|
57
|
+
/** Lifecycle hooks */
|
|
58
|
+
hooks?: {
|
|
59
|
+
/** Called after runtime init, before MCP server starts */
|
|
60
|
+
onStartup?: (runtime: AgentRuntime) => Promise<void>;
|
|
61
|
+
/** Called on SIGTERM/SIGINT before process exits */
|
|
62
|
+
onShutdown?: (runtime: AgentRuntime) => Promise<void>;
|
|
63
|
+
};
|
|
64
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -29,8 +29,6 @@ export type {
|
|
|
29
29
|
MergedPlaybook,
|
|
30
30
|
PlaybookMatchResult,
|
|
31
31
|
} from './playbooks/index.js';
|
|
32
|
-
export { createPlaybookOps } from './runtime/playbook-ops.js';
|
|
33
|
-
|
|
34
32
|
// ─── Text Utilities ─────────────────────────────────────────────────
|
|
35
33
|
export {
|
|
36
34
|
tokenize,
|
|
@@ -239,6 +237,10 @@ export {
|
|
|
239
237
|
export { KeyPool, loadKeyPoolConfig } from './llm/key-pool.js';
|
|
240
238
|
export type { KeyPoolFiles } from './llm/key-pool.js';
|
|
241
239
|
|
|
240
|
+
// ─── Extensions ──────────────────────────────────────────────────────
|
|
241
|
+
export type { AgentExtensions, OpMiddleware, MiddlewareContext } from './extensions/index.js';
|
|
242
|
+
export { wrapWithMiddleware } from './extensions/index.js';
|
|
243
|
+
|
|
242
244
|
// ─── Facades ─────────────────────────────────────────────────────────
|
|
243
245
|
export { registerFacade, registerAllFacades } from './facades/facade-factory.js';
|
|
244
246
|
export { facadeInputSchema } from './facades/types.js';
|
|
@@ -285,22 +287,8 @@ export type { LogLevel, LogEntry, LogContext, LoggerConfig } from './logging/typ
|
|
|
285
287
|
|
|
286
288
|
// ─── Runtime Factory ────────────────────────────────────────────────
|
|
287
289
|
export { createAgentRuntime } from './runtime/runtime.js';
|
|
288
|
-
export {
|
|
290
|
+
export { createSemanticFacades } from './runtime/facades/index.js';
|
|
289
291
|
export { createDomainFacade, createDomainFacades } from './runtime/domain-ops.js';
|
|
290
|
-
export { createPlanningExtraOps } from './runtime/planning-extra-ops.js';
|
|
291
|
-
export { createMemoryExtraOps } from './runtime/memory-extra-ops.js';
|
|
292
|
-
export { createVaultExtraOps } from './runtime/vault-extra-ops.js';
|
|
293
|
-
export { createAdminOps } from './runtime/admin-ops.js';
|
|
294
|
-
export { createAdminExtraOps } from './runtime/admin-extra-ops.js';
|
|
295
|
-
export { createLoopOps } from './runtime/loop-ops.js';
|
|
296
|
-
export { createOrchestrateOps } from './runtime/orchestrate-ops.js';
|
|
297
|
-
export { createGradingOps } from './runtime/grading-ops.js';
|
|
298
|
-
export { createCaptureOps } from './runtime/capture-ops.js';
|
|
299
|
-
export { createCuratorExtraOps } from './runtime/curator-extra-ops.js';
|
|
300
|
-
export { createProjectOps } from './runtime/project-ops.js';
|
|
301
|
-
export { createMemoryCrossProjectOps } from './runtime/memory-cross-project-ops.js';
|
|
302
|
-
export { createCogneeSyncOps } from './runtime/cognee-sync-ops.js';
|
|
303
|
-
export { createIntakeOps } from './runtime/intake-ops.js';
|
|
304
292
|
export type { AgentRuntimeConfig, AgentRuntime } from './runtime/types.js';
|
|
305
293
|
|
|
306
294
|
// ─── Errors ────────────────────────────────────────────────────────────
|
|
@@ -337,6 +325,15 @@ export type {
|
|
|
337
325
|
FtsSearchOptions,
|
|
338
326
|
} from './persistence/index.js';
|
|
339
327
|
|
|
328
|
+
// ─── Streams ──────────────────────────────────────────────────────────
|
|
329
|
+
export { ReplayableStream } from './streams/index.js';
|
|
330
|
+
export { normalize, collect } from './streams/index.js';
|
|
331
|
+
export type { NestableInput } from './streams/index.js';
|
|
332
|
+
|
|
333
|
+
// ─── Content Hashing ──────────────────────────────────────────────────
|
|
334
|
+
export { computeContentHash } from './vault/content-hash.js';
|
|
335
|
+
export type { HashableEntry } from './vault/content-hash.js';
|
|
336
|
+
|
|
340
337
|
// ─── Prompts ───────────────────────────────────────────────────────────
|
|
341
338
|
export { TemplateManager, parseVariables, resolveIncludes } from './prompts/index.js';
|
|
342
339
|
export type { PromptTemplate, TemplateVariable, RenderOptions } from './prompts/index.js';
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
* Ported from Salvador MCP's plan-gap-content.ts / plan-gap-technical.ts /
|
|
4
4
|
* plan-gap-domain.ts / plan-gap-antipattern.ts.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* 7 built-in passes (always run):
|
|
7
7
|
* 1. Structure — required fields present and sufficiently long
|
|
8
8
|
* 2. Completeness — measurable objectives, decision rationale, scope exclusions
|
|
9
9
|
* 3. Feasibility — overly broad scope, missing dependency awareness
|
|
10
10
|
* 4. Risk — breaking changes without mitigation, missing verification
|
|
11
11
|
* 5. Clarity — ambiguous language, vague criteria
|
|
12
12
|
* 6. Semantic Quality — generic objectives, shallow rationale, non-concrete approach
|
|
13
|
+
* 7. Knowledge Depth — BONUS: vault pattern refs, acceptance criteria, domain indicators
|
|
13
14
|
*
|
|
14
15
|
* Opt-in pass factories (registered via customPasses):
|
|
15
16
|
* - createToolFeasibilityPass — validates tool_chain entries and ordering
|
|
@@ -517,6 +518,98 @@ function analyzeSemanticQuality(plan: Plan): PlanGap[] {
|
|
|
517
518
|
return gaps;
|
|
518
519
|
}
|
|
519
520
|
|
|
521
|
+
// ─── Pass 7: Knowledge Depth (Substance Bonuses) ────────────────
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Patterns that indicate vault/knowledge-informed content in task descriptions.
|
|
525
|
+
* Each match earns a bonus point — rewarding plans that reference specific
|
|
526
|
+
* patterns, anti-patterns, or domain knowledge rather than generic guidance.
|
|
527
|
+
*/
|
|
528
|
+
const KNOWLEDGE_INDICATORS = [
|
|
529
|
+
/vault\s*pattern/i,
|
|
530
|
+
/vault\s*patterns/i,
|
|
531
|
+
/anti-pattern/i,
|
|
532
|
+
/wcag\s*[\d.]+/i,
|
|
533
|
+
/aria-[a-z]+/i,
|
|
534
|
+
/\d+(\.\d+)?:\d+\s*(contrast|ratio)/i,
|
|
535
|
+
/\d+px\s*(touch|target|minimum|min)/i,
|
|
536
|
+
/acceptance\s*criteria/i,
|
|
537
|
+
];
|
|
538
|
+
|
|
539
|
+
/** Checks if task descriptions reference specific named patterns (e.g. "zod-form-validation"). */
|
|
540
|
+
const NAMED_PATTERN_REGEX = /[a-z]+-[a-z]+-[a-z]+/;
|
|
541
|
+
|
|
542
|
+
function analyzeKnowledgeDepth(plan: Plan): PlanGap[] {
|
|
543
|
+
const gaps: PlanGap[] = [];
|
|
544
|
+
const allTaskText = taskText(plan);
|
|
545
|
+
|
|
546
|
+
// Bonus: tasks reference vault patterns by name
|
|
547
|
+
let namedPatternCount = 0;
|
|
548
|
+
for (const task of plan.tasks) {
|
|
549
|
+
const desc = task.description || '';
|
|
550
|
+
const matches = desc.match(/[a-z]+-[a-z]+(-[a-z]+)*/g) || [];
|
|
551
|
+
// Filter to likely pattern IDs (hyphenated, 2+ segments, not common words)
|
|
552
|
+
const patternRefs = matches.filter(
|
|
553
|
+
(m) => m.length > 8 && NAMED_PATTERN_REGEX.test(m) && !['front-end', 'back-end', 'real-time', 'client-side', 'server-side'].includes(m),
|
|
554
|
+
);
|
|
555
|
+
namedPatternCount += patternRefs.length;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
if (namedPatternCount >= 5) {
|
|
559
|
+
gaps.push(
|
|
560
|
+
gap('bonus', 'knowledge-depth', `${namedPatternCount} vault pattern references across tasks — strong knowledge-informed plan.`, '', 'tasks', 'vault_pattern_refs_high'),
|
|
561
|
+
);
|
|
562
|
+
gaps.push(
|
|
563
|
+
gap('bonus', 'knowledge-depth', 'Vault pattern density indicates expert-level domain knowledge.', '', 'tasks', 'vault_pattern_density'),
|
|
564
|
+
);
|
|
565
|
+
} else if (namedPatternCount >= 2) {
|
|
566
|
+
gaps.push(
|
|
567
|
+
gap('bonus', 'knowledge-depth', `${namedPatternCount} vault pattern references across tasks.`, '', 'tasks', 'vault_pattern_refs_medium'),
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// Bonus: tasks have specific acceptance criteria
|
|
572
|
+
let tasksWithCriteria = 0;
|
|
573
|
+
let totalCriteria = 0;
|
|
574
|
+
for (const task of plan.tasks) {
|
|
575
|
+
if (task.acceptanceCriteria && task.acceptanceCriteria.length > 0) {
|
|
576
|
+
tasksWithCriteria++;
|
|
577
|
+
totalCriteria += task.acceptanceCriteria.length;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (plan.tasks.length > 0 && tasksWithCriteria / plan.tasks.length >= 0.8) {
|
|
582
|
+
gaps.push(
|
|
583
|
+
gap('bonus', 'knowledge-depth', `${tasksWithCriteria}/${plan.tasks.length} tasks have acceptance criteria (${totalCriteria} total).`, '', 'tasks', 'high_acceptance_criteria'),
|
|
584
|
+
);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Bonus: knowledge indicator patterns in task text
|
|
588
|
+
let indicatorHits = 0;
|
|
589
|
+
for (const pattern of KNOWLEDGE_INDICATORS) {
|
|
590
|
+
if (pattern.test(allTaskText)) indicatorHits++;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
if (indicatorHits >= 4) {
|
|
594
|
+
gaps.push(
|
|
595
|
+
gap('bonus', 'knowledge-depth', `${indicatorHits} domain-specific knowledge indicators found (WCAG, ARIA, contrast ratios, touch targets, etc.).`, '', 'tasks', 'domain_knowledge_indicators'),
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// Bonus: rich task descriptions (avg > 80 chars per task)
|
|
600
|
+
if (plan.tasks.length > 0) {
|
|
601
|
+
const avgDescLength =
|
|
602
|
+
plan.tasks.reduce((sum, t) => sum + (t.description?.length ?? 0), 0) / plan.tasks.length;
|
|
603
|
+
if (avgDescLength >= 80) {
|
|
604
|
+
gaps.push(
|
|
605
|
+
gap('bonus', 'knowledge-depth', `Task descriptions average ${Math.round(avgDescLength)} chars — detailed and specific.`, '', 'tasks', 'rich_task_descriptions'),
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
return gaps;
|
|
611
|
+
}
|
|
612
|
+
|
|
520
613
|
// ─── Types ───────────────────────────────────────────────────────
|
|
521
614
|
|
|
522
615
|
/** A custom gap analysis pass that agents can register. */
|
|
@@ -762,6 +855,7 @@ export function runGapAnalysis(plan: Plan, options?: GapAnalysisOptions): PlanGa
|
|
|
762
855
|
...analyzeRisk(plan),
|
|
763
856
|
...analyzeClarity(plan),
|
|
764
857
|
...analyzeSemanticQuality(plan),
|
|
858
|
+
...analyzeKnowledgeDepth(plan),
|
|
765
859
|
];
|
|
766
860
|
|
|
767
861
|
// Run custom passes (domain-specific checks like tool-feasibility, UI context, etc.)
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
// ─── Severity & Category ─────────────────────────────────────────
|
|
7
7
|
|
|
8
|
-
export type GapSeverity = 'critical' | 'major' | 'minor' | 'info';
|
|
8
|
+
export type GapSeverity = 'critical' | 'major' | 'minor' | 'info' | 'bonus';
|
|
9
9
|
|
|
10
10
|
export type GapCategory =
|
|
11
11
|
| 'structure'
|
|
@@ -14,6 +14,7 @@ export type GapCategory =
|
|
|
14
14
|
| 'risk'
|
|
15
15
|
| 'clarity'
|
|
16
16
|
| 'semantic-quality'
|
|
17
|
+
| 'knowledge-depth'
|
|
17
18
|
| 'tool-feasibility'
|
|
18
19
|
| 'flow-alignment'
|
|
19
20
|
| 'anti-pattern';
|
|
@@ -32,12 +33,13 @@ export interface PlanGap {
|
|
|
32
33
|
|
|
33
34
|
// ─── Scoring Constants ───────────────────────────────────────────
|
|
34
35
|
|
|
35
|
-
/** Points deducted per gap severity. */
|
|
36
|
+
/** Points deducted per gap severity. Negative = bonus (adds points). */
|
|
36
37
|
export const SEVERITY_WEIGHTS: Record<GapSeverity, number> = {
|
|
37
38
|
critical: 30,
|
|
38
39
|
major: 15,
|
|
39
40
|
minor: 2,
|
|
40
41
|
info: 0,
|
|
42
|
+
bonus: -3,
|
|
41
43
|
};
|
|
42
44
|
|
|
43
45
|
/**
|
|
@@ -48,6 +50,14 @@ export const CATEGORY_PENALTY_CAPS: Record<string, number> = {
|
|
|
48
50
|
clarity: 10,
|
|
49
51
|
};
|
|
50
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Maximum bonus per category.
|
|
55
|
+
* Substance bonuses offset structural penalties but are capped to prevent gaming.
|
|
56
|
+
*/
|
|
57
|
+
export const CATEGORY_BONUS_CAPS: Record<string, number> = {
|
|
58
|
+
'knowledge-depth': 15,
|
|
59
|
+
};
|
|
60
|
+
|
|
51
61
|
// ─── Validation Thresholds ───────────────────────────────────────
|
|
52
62
|
|
|
53
63
|
export const MIN_OBJECTIVE_LENGTH = 10;
|
package/src/planning/planner.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
3
|
import { dirname } from 'node:path';
|
|
4
4
|
import type { PlanGap } from './gap-types.js';
|
|
5
|
-
import { SEVERITY_WEIGHTS, CATEGORY_PENALTY_CAPS } from './gap-types.js';
|
|
5
|
+
import { SEVERITY_WEIGHTS, CATEGORY_PENALTY_CAPS, CATEGORY_BONUS_CAPS } from './gap-types.js';
|
|
6
6
|
import { runGapAnalysis } from './gap-analysis.js';
|
|
7
7
|
import type { GapAnalysisOptions } from './gap-analysis.js';
|
|
8
8
|
|
|
@@ -207,7 +207,8 @@ export interface PlanCheck {
|
|
|
207
207
|
* - Score = max(0, 100 - totalDeductions)
|
|
208
208
|
*/
|
|
209
209
|
export function calculateScore(gaps: PlanGap[], iteration: number = 1): number {
|
|
210
|
-
const
|
|
210
|
+
const categoryDeductions = new Map<string, number>();
|
|
211
|
+
const categoryBonuses = new Map<string, number>();
|
|
211
212
|
|
|
212
213
|
for (const gap of gaps) {
|
|
213
214
|
let weight: number = SEVERITY_WEIGHTS[gap.severity];
|
|
@@ -220,16 +221,27 @@ export function calculateScore(gaps: PlanGap[], iteration: number = 1): number {
|
|
|
220
221
|
}
|
|
221
222
|
|
|
222
223
|
const category = gap.category;
|
|
223
|
-
|
|
224
|
+
if (weight < 0) {
|
|
225
|
+
// Bonus — accumulate as positive value for capping, apply as negative deduction
|
|
226
|
+
categoryBonuses.set(category, (categoryBonuses.get(category) ?? 0) + Math.abs(weight));
|
|
227
|
+
} else {
|
|
228
|
+
categoryDeductions.set(category, (categoryDeductions.get(category) ?? 0) + weight);
|
|
229
|
+
}
|
|
224
230
|
}
|
|
225
231
|
|
|
226
232
|
let deductions = 0;
|
|
227
|
-
for (const [category, total] of
|
|
233
|
+
for (const [category, total] of categoryDeductions) {
|
|
228
234
|
const cap = CATEGORY_PENALTY_CAPS[category];
|
|
229
235
|
deductions += cap !== undefined ? Math.min(total, cap) : total;
|
|
230
236
|
}
|
|
231
237
|
|
|
232
|
-
|
|
238
|
+
let bonuses = 0;
|
|
239
|
+
for (const [category, total] of categoryBonuses) {
|
|
240
|
+
const cap = CATEGORY_BONUS_CAPS[category];
|
|
241
|
+
bonuses += cap !== undefined ? Math.min(total, cap) : total;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return Math.max(0, Math.min(100, 100 - deductions + bonuses));
|
|
233
245
|
}
|
|
234
246
|
|
|
235
247
|
/**
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admin facade — infrastructure ops.
|
|
3
|
+
* health, config, telemetry, tokens, LLM, prompts.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import type { OpDefinition } from '../../facades/types.js';
|
|
8
|
+
import type { AgentRuntime } from '../types.js';
|
|
9
|
+
import { createAdminOps } from '../admin-ops.js';
|
|
10
|
+
import { createAdminExtraOps } from '../admin-extra-ops.js';
|
|
11
|
+
|
|
12
|
+
export function createAdminFacadeOps(runtime: AgentRuntime): OpDefinition[] {
|
|
13
|
+
const { llmClient, keyPool } = runtime;
|
|
14
|
+
|
|
15
|
+
return [
|
|
16
|
+
// ─── LLM (inline from core-ops.ts) ──────────────────────────
|
|
17
|
+
{
|
|
18
|
+
name: 'llm_rotate',
|
|
19
|
+
description:
|
|
20
|
+
'Force rotate the active API key for a provider. Useful when rate-limited or key is failing.',
|
|
21
|
+
auth: 'write',
|
|
22
|
+
schema: z.object({
|
|
23
|
+
provider: z.enum(['openai', 'anthropic']),
|
|
24
|
+
}),
|
|
25
|
+
handler: async (params) => {
|
|
26
|
+
const provider = params.provider as 'openai' | 'anthropic';
|
|
27
|
+
const pool = keyPool[provider];
|
|
28
|
+
if (!pool.hasKeys) return { rotated: false, error: `No ${provider} keys configured` };
|
|
29
|
+
const newKey = pool.rotateOnError();
|
|
30
|
+
return {
|
|
31
|
+
rotated: newKey !== null,
|
|
32
|
+
activeKeyIndex: pool.activeKeyIndex,
|
|
33
|
+
poolSize: pool.poolSize,
|
|
34
|
+
exhausted: pool.exhausted,
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'llm_call',
|
|
40
|
+
description: 'Make an LLM completion call. Uses model routing config and key pool rotation.',
|
|
41
|
+
auth: 'write',
|
|
42
|
+
schema: z.object({
|
|
43
|
+
systemPrompt: z.string().describe('System prompt for the LLM.'),
|
|
44
|
+
userPrompt: z.string().describe('User prompt / task input.'),
|
|
45
|
+
model: z
|
|
46
|
+
.string()
|
|
47
|
+
.optional()
|
|
48
|
+
.describe('Model name. Routed via model-routing.json if omitted.'),
|
|
49
|
+
temperature: z.number().optional().describe('Sampling temperature (0-2). Default 0.3.'),
|
|
50
|
+
maxTokens: z.number().optional().describe('Max output tokens. Default 500.'),
|
|
51
|
+
caller: z.string().optional().describe('Caller name for routing. Default "core-ops".'),
|
|
52
|
+
task: z.string().optional().describe('Task name for routing.'),
|
|
53
|
+
}),
|
|
54
|
+
handler: async (params) => {
|
|
55
|
+
return llmClient.complete({
|
|
56
|
+
provider: 'openai',
|
|
57
|
+
model: (params.model as string) ?? '',
|
|
58
|
+
systemPrompt: params.systemPrompt as string,
|
|
59
|
+
userPrompt: params.userPrompt as string,
|
|
60
|
+
temperature: params.temperature as number | undefined,
|
|
61
|
+
maxTokens: params.maxTokens as number | undefined,
|
|
62
|
+
caller: (params.caller as string) ?? 'core-ops',
|
|
63
|
+
task: params.task as string | undefined,
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
// ─── Prompt Templates (inline from core-ops.ts) ─────────────
|
|
69
|
+
{
|
|
70
|
+
name: 'render_prompt',
|
|
71
|
+
description:
|
|
72
|
+
'Render a prompt template with variable substitution. Templates are .prompt files loaded from the templates directory.',
|
|
73
|
+
auth: 'read' as const,
|
|
74
|
+
schema: z.object({
|
|
75
|
+
template: z.string().describe('Template name (without .prompt extension)'),
|
|
76
|
+
variables: z.record(z.string()).optional().default({}),
|
|
77
|
+
strict: z.boolean().optional().default(true),
|
|
78
|
+
}),
|
|
79
|
+
handler: async (params) => {
|
|
80
|
+
const rendered = runtime.templateManager.render(
|
|
81
|
+
params.template as string,
|
|
82
|
+
(params.variables ?? {}) as Record<string, string>,
|
|
83
|
+
{ strict: params.strict as boolean },
|
|
84
|
+
);
|
|
85
|
+
return { rendered };
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'list_templates',
|
|
90
|
+
description: 'List all loaded prompt templates.',
|
|
91
|
+
auth: 'read' as const,
|
|
92
|
+
handler: async () => ({
|
|
93
|
+
templates: runtime.templateManager.listTemplates(),
|
|
94
|
+
}),
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
// ─── Satellite ops ───────────────────────────────────────────
|
|
98
|
+
...createAdminOps(runtime),
|
|
99
|
+
...createAdminExtraOps(runtime),
|
|
100
|
+
];
|
|
101
|
+
}
|