tryassay 0.3.0 → 0.11.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/api/pricing-enforcer.d.ts +45 -0
- package/dist/api/pricing-enforcer.js +144 -0
- package/dist/api/pricing-enforcer.js.map +1 -0
- package/dist/api/server.d.ts +28 -0
- package/dist/api/server.js +265 -0
- package/dist/api/server.js.map +1 -0
- package/dist/api/team-session.d.ts +59 -0
- package/dist/api/team-session.js +240 -0
- package/dist/api/team-session.js.map +1 -0
- package/dist/cli.js +142 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/api.d.ts +4 -0
- package/dist/commands/api.js +50 -0
- package/dist/commands/api.js.map +1 -0
- package/dist/commands/runtime.d.ts +69 -0
- package/dist/commands/runtime.js +673 -0
- package/dist/commands/runtime.js.map +1 -1
- package/dist/runtime/agent-loop.d.ts +6 -0
- package/dist/runtime/agent-loop.js +87 -5
- package/dist/runtime/agent-loop.js.map +1 -1
- package/dist/runtime/agent-spawner.d.ts +56 -0
- package/dist/runtime/agent-spawner.js +217 -0
- package/dist/runtime/agent-spawner.js.map +1 -0
- package/dist/runtime/agents/code-agent.d.ts +11 -0
- package/dist/runtime/agents/code-agent.js +90 -0
- package/dist/runtime/agents/code-agent.js.map +1 -0
- package/dist/runtime/agents/coordinator-agent.d.ts +20 -0
- package/dist/runtime/agents/coordinator-agent.js +182 -0
- package/dist/runtime/agents/coordinator-agent.js.map +1 -0
- package/dist/runtime/agents/ops-agent.d.ts +11 -0
- package/dist/runtime/agents/ops-agent.js +113 -0
- package/dist/runtime/agents/ops-agent.js.map +1 -0
- package/dist/runtime/agents/research-agent.d.ts +11 -0
- package/dist/runtime/agents/research-agent.js +114 -0
- package/dist/runtime/agents/research-agent.js.map +1 -0
- package/dist/runtime/agents/review-agent.d.ts +11 -0
- package/dist/runtime/agents/review-agent.js +96 -0
- package/dist/runtime/agents/review-agent.js.map +1 -0
- package/dist/runtime/agents/test-agent.d.ts +11 -0
- package/dist/runtime/agents/test-agent.js +114 -0
- package/dist/runtime/agents/test-agent.js.map +1 -0
- package/dist/runtime/capability-registry.d.ts +62 -0
- package/dist/runtime/capability-registry.js +191 -0
- package/dist/runtime/capability-registry.js.map +1 -0
- package/dist/runtime/collusion-detector.d.ts +35 -0
- package/dist/runtime/collusion-detector.js +97 -0
- package/dist/runtime/collusion-detector.js.map +1 -0
- package/dist/runtime/composition-verifier.d.ts +22 -0
- package/dist/runtime/composition-verifier.js +265 -0
- package/dist/runtime/composition-verifier.js.map +1 -0
- package/dist/runtime/confidence-calibrator.d.ts +10 -0
- package/dist/runtime/confidence-calibrator.js +95 -0
- package/dist/runtime/confidence-calibrator.js.map +1 -0
- package/dist/runtime/domain-coverage-analyzer.d.ts +24 -0
- package/dist/runtime/domain-coverage-analyzer.js +178 -0
- package/dist/runtime/domain-coverage-analyzer.js.map +1 -0
- package/dist/runtime/enriched-prompt-builder.d.ts +25 -0
- package/dist/runtime/enriched-prompt-builder.js +173 -0
- package/dist/runtime/enriched-prompt-builder.js.map +1 -0
- package/dist/runtime/gap-detector.d.ts +6 -0
- package/dist/runtime/gap-detector.js +111 -0
- package/dist/runtime/gap-detector.js.map +1 -0
- package/dist/runtime/human-escalation.d.ts +41 -0
- package/dist/runtime/human-escalation.js +122 -0
- package/dist/runtime/human-escalation.js.map +1 -0
- package/dist/runtime/kill-switch.d.ts +51 -0
- package/dist/runtime/kill-switch.js +185 -0
- package/dist/runtime/kill-switch.js.map +1 -0
- package/dist/runtime/layer2-guardian.d.ts +81 -0
- package/dist/runtime/layer2-guardian.js +263 -0
- package/dist/runtime/layer2-guardian.js.map +1 -0
- package/dist/runtime/message-bus.d.ts +57 -0
- package/dist/runtime/message-bus.js +115 -0
- package/dist/runtime/message-bus.js.map +1 -0
- package/dist/runtime/multi-agent-loop.d.ts +37 -0
- package/dist/runtime/multi-agent-loop.js +411 -0
- package/dist/runtime/multi-agent-loop.js.map +1 -0
- package/dist/runtime/pattern-extractor.d.ts +20 -0
- package/dist/runtime/pattern-extractor.js +257 -0
- package/dist/runtime/pattern-extractor.js.map +1 -0
- package/dist/runtime/planner.d.ts +2 -2
- package/dist/runtime/planner.js +10 -7
- package/dist/runtime/planner.js.map +1 -1
- package/dist/runtime/prompt-safety-analyzer.d.ts +17 -0
- package/dist/runtime/prompt-safety-analyzer.js +230 -0
- package/dist/runtime/prompt-safety-analyzer.js.map +1 -0
- package/dist/runtime/reasoner.d.ts +2 -2
- package/dist/runtime/reasoner.js +9 -5
- package/dist/runtime/reasoner.js.map +1 -1
- package/dist/runtime/reflector.d.ts +7 -1
- package/dist/runtime/reflector.js.map +1 -1
- package/dist/runtime/rollback-manager.d.ts +50 -0
- package/dist/runtime/rollback-manager.js +157 -0
- package/dist/runtime/rollback-manager.js.map +1 -0
- package/dist/runtime/rule-canary-deployer.d.ts +69 -0
- package/dist/runtime/rule-canary-deployer.js +289 -0
- package/dist/runtime/rule-canary-deployer.js.map +1 -0
- package/dist/runtime/rule-conflict-detector.d.ts +48 -0
- package/dist/runtime/rule-conflict-detector.js +214 -0
- package/dist/runtime/rule-conflict-detector.js.map +1 -0
- package/dist/runtime/rule-meta-verifier.d.ts +18 -0
- package/dist/runtime/rule-meta-verifier.js +275 -0
- package/dist/runtime/rule-meta-verifier.js.map +1 -0
- package/dist/runtime/rule-proposal-manager.d.ts +95 -0
- package/dist/runtime/rule-proposal-manager.js +190 -0
- package/dist/runtime/rule-proposal-manager.js.map +1 -0
- package/dist/runtime/safety-enforcer.d.ts +35 -0
- package/dist/runtime/safety-enforcer.js +165 -0
- package/dist/runtime/safety-enforcer.js.map +1 -0
- package/dist/runtime/safety-status.d.ts +48 -0
- package/dist/runtime/safety-status.js +119 -0
- package/dist/runtime/safety-status.js.map +1 -0
- package/dist/runtime/shadow-runner.d.ts +14 -0
- package/dist/runtime/shadow-runner.js +190 -0
- package/dist/runtime/shadow-runner.js.map +1 -0
- package/dist/runtime/shared-memory.d.ts +47 -0
- package/dist/runtime/shared-memory.js +151 -0
- package/dist/runtime/shared-memory.js.map +1 -0
- package/dist/runtime/specialized-agent.d.ts +72 -0
- package/dist/runtime/specialized-agent.js +123 -0
- package/dist/runtime/specialized-agent.js.map +1 -0
- package/dist/runtime/stall-detector.d.ts +13 -0
- package/dist/runtime/stall-detector.js +121 -0
- package/dist/runtime/stall-detector.js.map +1 -0
- package/dist/runtime/strategy-library.d.ts +11 -0
- package/dist/runtime/strategy-library.js +142 -0
- package/dist/runtime/strategy-library.js.map +1 -0
- package/dist/runtime/supabase-experience-store.d.ts +19 -0
- package/dist/runtime/supabase-experience-store.js +215 -0
- package/dist/runtime/supabase-experience-store.js.map +1 -0
- package/dist/runtime/tool-approval.d.ts +51 -0
- package/dist/runtime/tool-approval.js +148 -0
- package/dist/runtime/tool-approval.js.map +1 -0
- package/dist/runtime/tool-sandbox.d.ts +43 -0
- package/dist/runtime/tool-sandbox.js +394 -0
- package/dist/runtime/tool-sandbox.js.map +1 -0
- package/dist/runtime/tool-verifier.d.ts +18 -0
- package/dist/runtime/tool-verifier.js +323 -0
- package/dist/runtime/tool-verifier.js.map +1 -0
- package/dist/runtime/trust-manager.d.ts +63 -0
- package/dist/runtime/trust-manager.js +212 -0
- package/dist/runtime/trust-manager.js.map +1 -0
- package/dist/runtime/two-agent-loop.d.ts +35 -0
- package/dist/runtime/two-agent-loop.js +208 -0
- package/dist/runtime/two-agent-loop.js.map +1 -0
- package/dist/runtime/types.d.ts +939 -1
- package/dist/runtime/verification-intensity.d.ts +34 -0
- package/dist/runtime/verification-intensity.js +104 -0
- package/dist/runtime/verification-intensity.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Assay Verified Agent Runtime — Shared Memory Store
|
|
3
|
+
// Append-only shared state with conflict detection.
|
|
4
|
+
// Agents write facts, decisions, conventions, findings.
|
|
5
|
+
// No mutable shared state — conflicts are detected, not hidden.
|
|
6
|
+
// ============================================================
|
|
7
|
+
import { randomUUID } from 'node:crypto';
|
|
8
|
+
// ── Shared Memory Store ────────────────────────────────────
|
|
9
|
+
export class SharedMemoryStore {
|
|
10
|
+
entries = [];
|
|
11
|
+
conflicts = [];
|
|
12
|
+
/**
|
|
13
|
+
* Add a new entry to shared memory.
|
|
14
|
+
* Checks for conflicts with existing entries by the same tags.
|
|
15
|
+
* Returns the entry ID and any detected conflicts.
|
|
16
|
+
*/
|
|
17
|
+
add(type, content, author, opts) {
|
|
18
|
+
const entry = {
|
|
19
|
+
id: randomUUID(),
|
|
20
|
+
type,
|
|
21
|
+
content,
|
|
22
|
+
author,
|
|
23
|
+
tags: opts?.tags ?? [],
|
|
24
|
+
verificationStatus: opts?.verificationStatus ?? 'unverified',
|
|
25
|
+
claims: opts?.claims ?? [],
|
|
26
|
+
confidence: opts?.confidence ?? 0.5,
|
|
27
|
+
createdAt: new Date().toISOString(),
|
|
28
|
+
supersedes: opts?.supersedes,
|
|
29
|
+
};
|
|
30
|
+
this.entries.push(entry);
|
|
31
|
+
// Detect conflicts: entries with overlapping tags that make contradictory claims
|
|
32
|
+
const newConflicts = this.detectConflicts(entry);
|
|
33
|
+
this.conflicts.push(...newConflicts);
|
|
34
|
+
return { entryId: entry.id, conflicts: newConflicts };
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Query entries by filter criteria.
|
|
38
|
+
*/
|
|
39
|
+
query(filter) {
|
|
40
|
+
return this.entries.filter(entry => {
|
|
41
|
+
if (filter.type && entry.type !== filter.type)
|
|
42
|
+
return false;
|
|
43
|
+
if (filter.tags && filter.tags.length > 0) {
|
|
44
|
+
const hasTag = filter.tags.some(t => entry.tags.includes(t));
|
|
45
|
+
if (!hasTag)
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
if (filter.minConfidence !== undefined && entry.confidence < filter.minConfidence)
|
|
49
|
+
return false;
|
|
50
|
+
if (filter.verificationStatus && entry.verificationStatus !== filter.verificationStatus)
|
|
51
|
+
return false;
|
|
52
|
+
if (filter.author && entry.author !== filter.author)
|
|
53
|
+
return false;
|
|
54
|
+
if (filter.since && entry.createdAt < filter.since)
|
|
55
|
+
return false;
|
|
56
|
+
return true;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/** Get entries by tag. */
|
|
60
|
+
getByTag(tag) {
|
|
61
|
+
return this.entries.filter(e => e.tags.includes(tag));
|
|
62
|
+
}
|
|
63
|
+
/** Get entries by author agent ID. */
|
|
64
|
+
getByAgent(agentId) {
|
|
65
|
+
return this.entries.filter(e => e.author === agentId);
|
|
66
|
+
}
|
|
67
|
+
/** Get only entries where all claims passed verification. */
|
|
68
|
+
getVerified() {
|
|
69
|
+
return this.entries.filter(e => e.verificationStatus === 'verified' || e.verificationStatus === 'formally_verified');
|
|
70
|
+
}
|
|
71
|
+
/** Get all detected conflicts. */
|
|
72
|
+
getConflicts() {
|
|
73
|
+
return this.conflicts;
|
|
74
|
+
}
|
|
75
|
+
/** Get only open (unresolved) conflicts. */
|
|
76
|
+
getOpenConflicts() {
|
|
77
|
+
return this.conflicts.filter(c => c.status === 'open');
|
|
78
|
+
}
|
|
79
|
+
/** Resolve a conflict. */
|
|
80
|
+
resolveConflict(conflictId, resolution) {
|
|
81
|
+
const conflict = this.conflicts.find(c => c.id === conflictId);
|
|
82
|
+
if (!conflict || conflict.status !== 'open')
|
|
83
|
+
return false;
|
|
84
|
+
// Mutate the conflict record (conflicts are mutable tracking state)
|
|
85
|
+
conflict.resolution = resolution;
|
|
86
|
+
conflict.status = 'resolved';
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
/** Escalate a conflict for human resolution. */
|
|
90
|
+
escalateConflict(conflictId) {
|
|
91
|
+
const conflict = this.conflicts.find(c => c.id === conflictId);
|
|
92
|
+
if (!conflict || conflict.status !== 'open')
|
|
93
|
+
return false;
|
|
94
|
+
conflict.status = 'escalated';
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
/** Get all entries (for snapshots and audit). */
|
|
98
|
+
getAllEntries() {
|
|
99
|
+
return this.entries;
|
|
100
|
+
}
|
|
101
|
+
/** Get entry by ID. */
|
|
102
|
+
getEntry(entryId) {
|
|
103
|
+
return this.entries.find(e => e.id === entryId);
|
|
104
|
+
}
|
|
105
|
+
/** Get the entry that superseded a given entry. */
|
|
106
|
+
getSuperseding(entryId) {
|
|
107
|
+
return this.entries.find(e => e.supersedes === entryId);
|
|
108
|
+
}
|
|
109
|
+
/** Update verification status of an entry after boundary verification. */
|
|
110
|
+
updateVerification(entryId, status, claims, confidence) {
|
|
111
|
+
const entry = this.entries.find(e => e.id === entryId);
|
|
112
|
+
if (!entry)
|
|
113
|
+
return;
|
|
114
|
+
entry.verificationStatus = status;
|
|
115
|
+
if (claims) {
|
|
116
|
+
entry.claims = claims;
|
|
117
|
+
}
|
|
118
|
+
if (confidence !== undefined) {
|
|
119
|
+
entry.confidence = confidence;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// ── Private ─────────────────────────────────────────────
|
|
123
|
+
detectConflicts(newEntry) {
|
|
124
|
+
const detected = [];
|
|
125
|
+
// Only check entries with overlapping tags
|
|
126
|
+
if (newEntry.tags.length === 0)
|
|
127
|
+
return detected;
|
|
128
|
+
// Find entries that share tags and are the same type
|
|
129
|
+
const candidates = this.entries.filter(existing => existing.id !== newEntry.id &&
|
|
130
|
+
existing.type === newEntry.type &&
|
|
131
|
+
existing.tags.some(t => newEntry.tags.includes(t)) &&
|
|
132
|
+
!existing.supersedes && // Don't conflict with superseded entries
|
|
133
|
+
!newEntry.supersedes // If new entry supersedes something, that's resolution, not conflict
|
|
134
|
+
);
|
|
135
|
+
for (const candidate of candidates) {
|
|
136
|
+
// Simple heuristic: if two entries of the same type with the same tags
|
|
137
|
+
// have different content and are from different authors, flag as potential conflict
|
|
138
|
+
if (candidate.author !== newEntry.author &&
|
|
139
|
+
candidate.content !== newEntry.content) {
|
|
140
|
+
detected.push({
|
|
141
|
+
id: randomUUID(),
|
|
142
|
+
entries: [candidate, newEntry],
|
|
143
|
+
detectedBy: 'assay',
|
|
144
|
+
status: 'open',
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return detected;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=shared-memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared-memory.js","sourceRoot":"","sources":["../../src/runtime/shared-memory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { AgentIdentity, AgentSpecialization, AgentCapability, TrustLevel, BoundaryArtifact, AgentMessage } from './types.js';
|
|
2
|
+
import type { MessageBus } from './message-bus.js';
|
|
3
|
+
export interface TaskAssignmentPayload {
|
|
4
|
+
readonly taskId: string;
|
|
5
|
+
readonly goal: string;
|
|
6
|
+
readonly constraints: readonly string[];
|
|
7
|
+
readonly dependencies: readonly string[];
|
|
8
|
+
readonly contextRefs: readonly {
|
|
9
|
+
summary: string;
|
|
10
|
+
content?: string;
|
|
11
|
+
}[];
|
|
12
|
+
}
|
|
13
|
+
export interface TaskResultPayload {
|
|
14
|
+
readonly taskId: string;
|
|
15
|
+
readonly status: 'completed' | 'blocked' | 'failed';
|
|
16
|
+
readonly artifacts: readonly BoundaryArtifact[];
|
|
17
|
+
readonly summary: string;
|
|
18
|
+
readonly claimsAboutArtifacts: readonly string[];
|
|
19
|
+
readonly blockers?: readonly string[];
|
|
20
|
+
}
|
|
21
|
+
export interface FindingPayload {
|
|
22
|
+
readonly severity: 'critical' | 'high' | 'medium' | 'low';
|
|
23
|
+
readonly category: string;
|
|
24
|
+
readonly description: string;
|
|
25
|
+
readonly evidence: string;
|
|
26
|
+
readonly suggestedFix?: string;
|
|
27
|
+
readonly affectedFiles: readonly string[];
|
|
28
|
+
}
|
|
29
|
+
export declare abstract class SpecializedAgent {
|
|
30
|
+
readonly identity: AgentIdentity;
|
|
31
|
+
protected messageBus: MessageBus;
|
|
32
|
+
protected modelId: string;
|
|
33
|
+
constructor(name: string, specialization: AgentSpecialization, capabilities: AgentCapability[], messageBus: MessageBus, opts?: {
|
|
34
|
+
model?: string;
|
|
35
|
+
initialTrust?: TrustLevel;
|
|
36
|
+
});
|
|
37
|
+
/** Handle an incoming message. Subclasses override for specific behavior. */
|
|
38
|
+
protected abstract onMessage(message: AgentMessage): void;
|
|
39
|
+
/**
|
|
40
|
+
* Execute a task assignment. Returns the result as artifacts.
|
|
41
|
+
* Each agent role implements this differently.
|
|
42
|
+
*/
|
|
43
|
+
abstract executeTask(task: TaskAssignmentPayload): Promise<TaskResultPayload>;
|
|
44
|
+
/** Get the agent's identity. */
|
|
45
|
+
getIdentity(): AgentIdentity;
|
|
46
|
+
/**
|
|
47
|
+
* Call Claude with a system prompt and user prompt.
|
|
48
|
+
* Returns the raw response text and token usage.
|
|
49
|
+
*/
|
|
50
|
+
protected callClaude(systemPrompt: string, userPrompt: string): Promise<{
|
|
51
|
+
content: string;
|
|
52
|
+
inputTokens: number;
|
|
53
|
+
outputTokens: number;
|
|
54
|
+
}>;
|
|
55
|
+
/** Send a task result back to the coordinator. */
|
|
56
|
+
protected sendResult(result: TaskResultPayload, threadId: string): void;
|
|
57
|
+
/** Send a finding to another agent or coordinator. */
|
|
58
|
+
protected sendFinding(recipient: string, finding: FindingPayload, threadId: string): void;
|
|
59
|
+
/** Send an escalation to the coordinator. */
|
|
60
|
+
protected sendEscalation(reason: string, context: string, attemptsMade: string[], threadId: string): void;
|
|
61
|
+
/** Create a BoundaryArtifact from produced output. */
|
|
62
|
+
protected makeArtifact(type: BoundaryArtifact['type'], content: string, opts?: {
|
|
63
|
+
path?: string;
|
|
64
|
+
language?: string;
|
|
65
|
+
metadata?: Record<string, unknown>;
|
|
66
|
+
}): BoundaryArtifact;
|
|
67
|
+
}
|
|
68
|
+
export declare function checkModelDiversity(agents: AgentIdentity[]): {
|
|
69
|
+
diverse: boolean;
|
|
70
|
+
providers: Map<string, string[]>;
|
|
71
|
+
recommendation: string | null;
|
|
72
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Assay Verified Agent Runtime — Specialized Agent
|
|
3
|
+
// Base class for all agent roles. Handles Claude API calls,
|
|
4
|
+
// message receiving, and artifact production.
|
|
5
|
+
// ============================================================
|
|
6
|
+
import { randomUUID } from 'node:crypto';
|
|
7
|
+
import { getClient, MODEL } from '../lib/anthropic.js';
|
|
8
|
+
// ── Specialized Agent ───────────────────────────────────────
|
|
9
|
+
export class SpecializedAgent {
|
|
10
|
+
identity;
|
|
11
|
+
messageBus;
|
|
12
|
+
modelId;
|
|
13
|
+
constructor(name, specialization, capabilities, messageBus, opts) {
|
|
14
|
+
this.identity = {
|
|
15
|
+
id: randomUUID(),
|
|
16
|
+
name,
|
|
17
|
+
specialization,
|
|
18
|
+
capabilities,
|
|
19
|
+
trustLevel: opts?.initialTrust ?? 'untrusted',
|
|
20
|
+
model: opts?.model ?? MODEL,
|
|
21
|
+
spawnedAt: new Date().toISOString(),
|
|
22
|
+
verificationHistory: {
|
|
23
|
+
totalHandoffs: 0,
|
|
24
|
+
passRate: 0,
|
|
25
|
+
formalOverrides: 0,
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
this.messageBus = messageBus;
|
|
29
|
+
this.modelId = opts?.model ?? MODEL;
|
|
30
|
+
// Listen for messages addressed to this agent
|
|
31
|
+
messageBus.on(`message:${this.identity.id}`, (msg) => {
|
|
32
|
+
this.onMessage(msg);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/** Get the agent's identity. */
|
|
36
|
+
getIdentity() {
|
|
37
|
+
return this.identity;
|
|
38
|
+
}
|
|
39
|
+
// ── Protected helpers for subclasses ──────────────────────
|
|
40
|
+
/**
|
|
41
|
+
* Call Claude with a system prompt and user prompt.
|
|
42
|
+
* Returns the raw response text and token usage.
|
|
43
|
+
*/
|
|
44
|
+
async callClaude(systemPrompt, userPrompt) {
|
|
45
|
+
const client = getClient();
|
|
46
|
+
let content = '';
|
|
47
|
+
const stream = client.messages.stream({
|
|
48
|
+
model: this.modelId,
|
|
49
|
+
max_tokens: 8_000,
|
|
50
|
+
system: systemPrompt,
|
|
51
|
+
messages: [{ role: 'user', content: userPrompt }],
|
|
52
|
+
});
|
|
53
|
+
stream.on('text', (text) => {
|
|
54
|
+
content += text;
|
|
55
|
+
});
|
|
56
|
+
const finalMessage = await stream.finalMessage();
|
|
57
|
+
return {
|
|
58
|
+
content,
|
|
59
|
+
inputTokens: finalMessage.usage.input_tokens,
|
|
60
|
+
outputTokens: finalMessage.usage.output_tokens,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/** Send a task result back to the coordinator. */
|
|
64
|
+
sendResult(result, threadId) {
|
|
65
|
+
this.messageBus.send(this.identity.id, 'coordinator', 'task_result', result, { threadId });
|
|
66
|
+
}
|
|
67
|
+
/** Send a finding to another agent or coordinator. */
|
|
68
|
+
sendFinding(recipient, finding, threadId) {
|
|
69
|
+
this.messageBus.send(this.identity.id, recipient, 'finding', finding, { threadId });
|
|
70
|
+
}
|
|
71
|
+
/** Send an escalation to the coordinator. */
|
|
72
|
+
sendEscalation(reason, context, attemptsMade, threadId) {
|
|
73
|
+
this.messageBus.send(this.identity.id, 'coordinator', 'escalation', { reason, context, attemptsMade }, { threadId });
|
|
74
|
+
}
|
|
75
|
+
/** Create a BoundaryArtifact from produced output. */
|
|
76
|
+
makeArtifact(type, content, opts) {
|
|
77
|
+
return {
|
|
78
|
+
type,
|
|
79
|
+
content,
|
|
80
|
+
path: opts?.path,
|
|
81
|
+
language: opts?.language,
|
|
82
|
+
metadata: opts?.metadata ?? {},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// ── Model Diversity Checker ────────────────────────────────
|
|
87
|
+
export function checkModelDiversity(agents) {
|
|
88
|
+
const providers = new Map();
|
|
89
|
+
for (const agent of agents) {
|
|
90
|
+
// Extract provider from model name (e.g., "claude-sonnet-4-5" -> "anthropic")
|
|
91
|
+
const provider = inferProvider(agent.model);
|
|
92
|
+
const existing = providers.get(provider) ?? [];
|
|
93
|
+
existing.push(agent.name);
|
|
94
|
+
providers.set(provider, existing);
|
|
95
|
+
}
|
|
96
|
+
// Model diversity is satisfied if agents that verify each other
|
|
97
|
+
// use different providers
|
|
98
|
+
const allSameProvider = providers.size === 1;
|
|
99
|
+
const recommendation = allSameProvider
|
|
100
|
+
? 'All agents use the same provider. Consider using different providers for Code and Review agents to reduce correlated failures.'
|
|
101
|
+
: null;
|
|
102
|
+
return {
|
|
103
|
+
diverse: !allSameProvider,
|
|
104
|
+
providers,
|
|
105
|
+
recommendation,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function inferProvider(model) {
|
|
109
|
+
if (model.startsWith('claude') || model.includes('anthropic'))
|
|
110
|
+
return 'anthropic';
|
|
111
|
+
if (model.startsWith('gpt') || model.includes('openai'))
|
|
112
|
+
return 'openai';
|
|
113
|
+
if (model.startsWith('gemini') || model.includes('google'))
|
|
114
|
+
return 'google';
|
|
115
|
+
if (model.startsWith('llama') || model.includes('meta'))
|
|
116
|
+
return 'meta';
|
|
117
|
+
if (model.startsWith('mistral'))
|
|
118
|
+
return 'mistral';
|
|
119
|
+
if (model.startsWith('deepseek'))
|
|
120
|
+
return 'deepseek';
|
|
121
|
+
return 'unknown';
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=specialized-agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"specialized-agent.js","sourceRoot":"","sources":["../../src/runtime/specialized-agent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { TaskGraph, StallReport } from './types.js';
|
|
2
|
+
export declare class StallDetector {
|
|
3
|
+
/**
|
|
4
|
+
* Detect stalls in a task graph.
|
|
5
|
+
* Returns stall reports for any problematic tasks.
|
|
6
|
+
*/
|
|
7
|
+
detectStalls(graph: TaskGraph, thresholdMs: number): StallReport[];
|
|
8
|
+
/**
|
|
9
|
+
* Detect circular dependencies in the task graph.
|
|
10
|
+
* Returns task IDs involved in deadlock cycles.
|
|
11
|
+
*/
|
|
12
|
+
private detectDeadlocks;
|
|
13
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Assay Verified Agent Runtime — Stall Detector
|
|
3
|
+
// Monitors task graph for timeouts, reject loops,
|
|
4
|
+
// dependency deadlocks, and resource contention.
|
|
5
|
+
// ============================================================
|
|
6
|
+
// ── Stall Detector ─────────────────────────────────────────
|
|
7
|
+
export class StallDetector {
|
|
8
|
+
/**
|
|
9
|
+
* Detect stalls in a task graph.
|
|
10
|
+
* Returns stall reports for any problematic tasks.
|
|
11
|
+
*/
|
|
12
|
+
detectStalls(graph, thresholdMs) {
|
|
13
|
+
const reports = [];
|
|
14
|
+
const now = Date.now();
|
|
15
|
+
for (const task of graph.tasks) {
|
|
16
|
+
// Skip completed or failed tasks
|
|
17
|
+
if (task.status === 'completed' || task.status === 'failed')
|
|
18
|
+
continue;
|
|
19
|
+
// Check for timeout: task in progress for too long
|
|
20
|
+
if (task.status === 'in_progress' || task.status === 'assigned') {
|
|
21
|
+
const taskStart = new Date(task.createdAt).getTime();
|
|
22
|
+
const elapsed = now - taskStart;
|
|
23
|
+
if (elapsed > thresholdMs) {
|
|
24
|
+
reports.push({
|
|
25
|
+
taskId: task.id,
|
|
26
|
+
type: 'timeout',
|
|
27
|
+
duration: elapsed,
|
|
28
|
+
attempts: task.attempts,
|
|
29
|
+
suggestedAction: task.attempts >= task.maxAttempts ? 'escalate' : 'reassign',
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Check for reject loop: task rejected too many times
|
|
34
|
+
if (task.status === 'rejected' && task.attempts >= task.maxAttempts) {
|
|
35
|
+
reports.push({
|
|
36
|
+
taskId: task.id,
|
|
37
|
+
type: 'reject_loop',
|
|
38
|
+
duration: now - new Date(task.createdAt).getTime(),
|
|
39
|
+
attempts: task.attempts,
|
|
40
|
+
suggestedAction: task.attempts >= task.maxAttempts ? 'escalate' : 'simplify',
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
// Check for resource contention: multiple tasks waiting for the same specialization
|
|
44
|
+
if (task.status === 'ready' || task.status === 'pending') {
|
|
45
|
+
const waitingForSameSpec = graph.tasks.filter(t => t.id !== task.id &&
|
|
46
|
+
t.specialization === task.specialization &&
|
|
47
|
+
(t.status === 'in_progress' || t.status === 'assigned'));
|
|
48
|
+
if (waitingForSameSpec.length >= 2) {
|
|
49
|
+
reports.push({
|
|
50
|
+
taskId: task.id,
|
|
51
|
+
type: 'resource_contention',
|
|
52
|
+
duration: now - new Date(task.createdAt).getTime(),
|
|
53
|
+
attempts: task.attempts,
|
|
54
|
+
suggestedAction: 'reassign',
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Check for dependency deadlocks: circular dependencies
|
|
60
|
+
const deadlockTasks = this.detectDeadlocks(graph);
|
|
61
|
+
for (const taskId of deadlockTasks) {
|
|
62
|
+
const task = graph.tasks.find(t => t.id === taskId);
|
|
63
|
+
if (task) {
|
|
64
|
+
reports.push({
|
|
65
|
+
taskId: task.id,
|
|
66
|
+
type: 'dependency_deadlock',
|
|
67
|
+
duration: now - new Date(task.createdAt).getTime(),
|
|
68
|
+
attempts: task.attempts,
|
|
69
|
+
suggestedAction: 'abort',
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return reports;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Detect circular dependencies in the task graph.
|
|
77
|
+
* Returns task IDs involved in deadlock cycles.
|
|
78
|
+
*/
|
|
79
|
+
detectDeadlocks(graph) {
|
|
80
|
+
const deadlocked = new Set();
|
|
81
|
+
// Build adjacency list from blocking edges
|
|
82
|
+
const blockingEdges = graph.edges.filter(e => e.type === 'blocks');
|
|
83
|
+
const adjacency = new Map();
|
|
84
|
+
for (const edge of blockingEdges) {
|
|
85
|
+
const existing = adjacency.get(edge.from) ?? [];
|
|
86
|
+
existing.push(edge.to);
|
|
87
|
+
adjacency.set(edge.from, existing);
|
|
88
|
+
}
|
|
89
|
+
// DFS-based cycle detection
|
|
90
|
+
const visited = new Set();
|
|
91
|
+
const inStack = new Set();
|
|
92
|
+
const dfs = (taskId) => {
|
|
93
|
+
if (inStack.has(taskId)) {
|
|
94
|
+
// Found a cycle — collect all tasks in the cycle
|
|
95
|
+
deadlocked.add(taskId);
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
if (visited.has(taskId))
|
|
99
|
+
return false;
|
|
100
|
+
visited.add(taskId);
|
|
101
|
+
inStack.add(taskId);
|
|
102
|
+
const neighbors = adjacency.get(taskId) ?? [];
|
|
103
|
+
for (const neighbor of neighbors) {
|
|
104
|
+
if (dfs(neighbor)) {
|
|
105
|
+
deadlocked.add(taskId);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
inStack.delete(taskId);
|
|
109
|
+
return false;
|
|
110
|
+
};
|
|
111
|
+
// Only check tasks that are not yet completed
|
|
112
|
+
const activeTasks = graph.tasks
|
|
113
|
+
.filter(t => t.status !== 'completed' && t.status !== 'failed')
|
|
114
|
+
.map(t => t.id);
|
|
115
|
+
for (const taskId of activeTasks) {
|
|
116
|
+
dfs(taskId);
|
|
117
|
+
}
|
|
118
|
+
return Array.from(deadlocked);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=stall-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stall-detector.js","sourceRoot":"","sources":["../../src/runtime/stall-detector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Strategy, Experience, ExperienceOutcome, OperationType } from './types.js';
|
|
2
|
+
export declare class StrategyLibrary {
|
|
3
|
+
private strategies;
|
|
4
|
+
constructor(initial?: Strategy[]);
|
|
5
|
+
rank(domain: string, operationType: OperationType): Promise<Strategy[]>;
|
|
6
|
+
recordOutcome(strategyId: string, outcome: ExperienceOutcome, verificationPassRate: number, durationMs: number): Promise<void>;
|
|
7
|
+
discover(experiences: Experience[]): Promise<Strategy[]>;
|
|
8
|
+
getAll(): Strategy[];
|
|
9
|
+
load(strategies: Strategy[]): void;
|
|
10
|
+
private proposeStrategy;
|
|
11
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Assay Verified Agent Runtime — Strategy Library
|
|
3
|
+
// Maintains named strategies per domain/operation type,
|
|
4
|
+
// ranked by historical success rate.
|
|
5
|
+
// ============================================================
|
|
6
|
+
import { randomUUID } from 'node:crypto';
|
|
7
|
+
import { getClient, MODEL } from '../lib/anthropic.js';
|
|
8
|
+
export class StrategyLibrary {
|
|
9
|
+
strategies = new Map();
|
|
10
|
+
constructor(initial) {
|
|
11
|
+
if (initial) {
|
|
12
|
+
for (const s of initial) {
|
|
13
|
+
this.strategies.set(s.id, s);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
async rank(domain, operationType) {
|
|
18
|
+
const matches = Array.from(this.strategies.values())
|
|
19
|
+
.filter(s => s.domain === domain && s.operationType === operationType)
|
|
20
|
+
.filter(s => s.timesUsed >= 5) // only rank strategies with enough data
|
|
21
|
+
.filter(s => s.successRate >= 0.3) // exclude deprecated strategies
|
|
22
|
+
.sort((a, b) => b.successRate - a.successRate);
|
|
23
|
+
return matches;
|
|
24
|
+
}
|
|
25
|
+
async recordOutcome(strategyId, outcome, verificationPassRate, durationMs) {
|
|
26
|
+
const strategy = this.strategies.get(strategyId);
|
|
27
|
+
if (!strategy)
|
|
28
|
+
return;
|
|
29
|
+
strategy.timesUsed++;
|
|
30
|
+
if (outcome === 'success')
|
|
31
|
+
strategy.timesSucceeded++;
|
|
32
|
+
strategy.successRate = strategy.timesUsed > 0
|
|
33
|
+
? strategy.timesSucceeded / strategy.timesUsed
|
|
34
|
+
: 0;
|
|
35
|
+
// Running average for verification pass rate
|
|
36
|
+
const prevTotal = strategy.avgVerificationPassRate * (strategy.timesUsed - 1);
|
|
37
|
+
strategy.avgVerificationPassRate = (prevTotal + verificationPassRate) / strategy.timesUsed;
|
|
38
|
+
// Running average for duration
|
|
39
|
+
const prevDurTotal = strategy.avgDurationMs * (strategy.timesUsed - 1);
|
|
40
|
+
strategy.avgDurationMs = (prevDurTotal + durationMs) / strategy.timesUsed;
|
|
41
|
+
strategy.lastUsed = new Date().toISOString();
|
|
42
|
+
}
|
|
43
|
+
async discover(experiences) {
|
|
44
|
+
// Group successful experiences by domain + operation type
|
|
45
|
+
const groups = new Map();
|
|
46
|
+
for (const exp of experiences) {
|
|
47
|
+
if (exp.outcome !== 'success')
|
|
48
|
+
continue;
|
|
49
|
+
for (const step of exp.plan.steps) {
|
|
50
|
+
const key = `${exp.domain}:${step.operation.type}`;
|
|
51
|
+
const group = groups.get(key) ?? [];
|
|
52
|
+
group.push(exp);
|
|
53
|
+
groups.set(key, group);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const newStrategies = [];
|
|
57
|
+
for (const [key, exps] of groups) {
|
|
58
|
+
if (exps.length < 3)
|
|
59
|
+
continue; // need at least 3 examples
|
|
60
|
+
const [domain, opType] = key.split(':');
|
|
61
|
+
// Check if we already have strategies for this combination
|
|
62
|
+
const existing = Array.from(this.strategies.values())
|
|
63
|
+
.filter(s => s.domain === domain && s.operationType === opType);
|
|
64
|
+
if (existing.length >= 5)
|
|
65
|
+
continue; // enough strategies already
|
|
66
|
+
try {
|
|
67
|
+
const strategy = await this.proposeStrategy(domain, opType, exps);
|
|
68
|
+
if (strategy) {
|
|
69
|
+
this.strategies.set(strategy.id, strategy);
|
|
70
|
+
newStrategies.push(strategy);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Skip on failure — strategy discovery is best-effort
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return newStrategies;
|
|
78
|
+
}
|
|
79
|
+
getAll() {
|
|
80
|
+
return Array.from(this.strategies.values());
|
|
81
|
+
}
|
|
82
|
+
load(strategies) {
|
|
83
|
+
for (const s of strategies) {
|
|
84
|
+
this.strategies.set(s.id, s);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async proposeStrategy(domain, operationType, experiences) {
|
|
88
|
+
const summaries = experiences.slice(0, 5).map(e => ({
|
|
89
|
+
lessons: e.lessons,
|
|
90
|
+
planSteps: e.plan.steps.map(s => s.description),
|
|
91
|
+
duration: e.execution.totalDurationMs,
|
|
92
|
+
}));
|
|
93
|
+
const client = getClient();
|
|
94
|
+
const response = await client.messages.create({
|
|
95
|
+
model: MODEL,
|
|
96
|
+
max_tokens: 1024,
|
|
97
|
+
system: 'Extract a named strategy from successful agent experiences. Respond with ONLY JSON, no markdown.',
|
|
98
|
+
messages: [{
|
|
99
|
+
role: 'user',
|
|
100
|
+
content: `These ${experiences.length} successful experiences in "${domain}" (operation: ${operationType}) share a common approach. Name and describe the strategy.
|
|
101
|
+
|
|
102
|
+
EXAMPLES:
|
|
103
|
+
${JSON.stringify(summaries, null, 2)}
|
|
104
|
+
|
|
105
|
+
Respond:
|
|
106
|
+
{
|
|
107
|
+
"name": "short-kebab-case-name",
|
|
108
|
+
"description": "when to use this strategy",
|
|
109
|
+
"planTemplate": "natural language template for the planner"
|
|
110
|
+
}`,
|
|
111
|
+
}],
|
|
112
|
+
});
|
|
113
|
+
const text = response.content[0].type === 'text' ? response.content[0].text : '';
|
|
114
|
+
try {
|
|
115
|
+
let cleaned = text.trim();
|
|
116
|
+
if (cleaned.startsWith('```')) {
|
|
117
|
+
cleaned = cleaned.replace(/^```(?:json)?\s*/, '').replace(/\s*```$/, '');
|
|
118
|
+
}
|
|
119
|
+
const parsed = JSON.parse(cleaned);
|
|
120
|
+
return {
|
|
121
|
+
id: randomUUID(),
|
|
122
|
+
domain,
|
|
123
|
+
operationType,
|
|
124
|
+
name: parsed.name,
|
|
125
|
+
description: parsed.description,
|
|
126
|
+
planTemplate: parsed.planTemplate,
|
|
127
|
+
timesUsed: 0,
|
|
128
|
+
timesSucceeded: 0,
|
|
129
|
+
successRate: 0,
|
|
130
|
+
avgVerificationPassRate: 0,
|
|
131
|
+
avgDurationMs: 0,
|
|
132
|
+
lastUsed: '',
|
|
133
|
+
discoveredFrom: experiences.map(e => e.id),
|
|
134
|
+
createdAt: new Date().toISOString(),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=strategy-library.js.map
|