Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { HttpClient } from './core/http-client.js'
import type { RequestInterceptor, ResponseInterceptor } from './core/types.js'
import { AlertsResource } from './resources/alerts.js'
import { BehaviorsResource } from './resources/behaviors.js'
import { ComplianceResource } from './resources/compliance.js'
import { ContextResource } from './resources/context.js'
import { EventsResource } from './resources/events.js'
Expand Down Expand Up @@ -53,6 +54,7 @@ export interface ThinkFleetMemoryOptions {
export class ThinkFleetMemory {
readonly memory: MemoryResource
readonly lattice: LatticeResource
readonly behaviors: BehaviorsResource
readonly context: ContextResource
readonly events: EventsResource
readonly alerts: AlertsResource
Expand Down Expand Up @@ -82,6 +84,7 @@ export class ThinkFleetMemory {

this.memory = new MemoryResource(http)
this.lattice = new LatticeResource(http)
this.behaviors = new BehaviorsResource(http)
this.context = new ContextResource(http)
this.events = new EventsResource(http)
this.alerts = new AlertsResource(http)
Expand Down
6 changes: 6 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ export type {
ConsentStatus,
} from './resources/consent.js'
export { LatticeResource } from './resources/lattice.js'
export { BehaviorsResource } from './resources/behaviors.js'
export type {
DiscoverParams,
DiscoveredBehavior,
DiscoverResult,
} from './resources/behaviors.js'
export { HealthResource } from './resources/health.js'
export { FinancialResource } from './resources/financial.js'

Expand Down
89 changes: 89 additions & 0 deletions src/resources/behaviors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import type { HttpClient } from '../core/http-client.js'
import type { RequestOptions } from '../core/types.js'
import type { Subject } from '../types/lattice.js'

/** Tuning for a discovery run. All optional; the engine clamps to safe ranges. */
export interface DiscoverParams {
/** Minimum similarity [0,1] for a subject to join a cluster. Higher = tighter,
* more numerous clusters. Default 0.75. */
simThreshold?: number
/** A cluster smaller than this is noise, not a behavior. Default 3. */
minClusterSize?: number
/** Drop clusters whose cohesion (mean intra-cluster similarity) is below this.
* Default 0.6. */
minStability?: number
/** Cap on member subjects returned per behavior. Default 50. */
maxMembers?: number
}

/**
* One emergent behavior — a cohesive cluster of subjects the engine grouped
* together because they behave alike, with the statistics that justify treating
* it as real. Behaviors are discovered from the data, not chosen from a fixed
* menu.
*/
export interface DiscoveredBehavior {
/** Rule-based description (RFM band + frequency + dominant pattern + entity).
* Upgraded to an LLM-authored name in a later engine release. */
label: string
/** Fraction of the analyzed cohort in this cluster, [0,1] — how common it is. */
prevalence: number
/** Cohesion: mean pairwise similarity within the cluster, [0,1] — how tightly
* these subjects actually behave alike. */
stability: number
/** Total subjects in the cluster (may exceed memberSubjects length when capped
* by maxMembers). */
size: number
/** Up to maxMembers subjects, medoid first — provenance for who exhibits it. */
memberSubjects: Subject[]
/** Human-readable signals behind the label (e.g. "pattern: recurring_event"). */
exemplarEvidence: string[]
}

export interface DiscoverResult {
/** Discovered behaviors, sorted by prevalence then stability (most common,
* most cohesive first). Empty when there isn't enough signal — discovery
* abstains structurally rather than inventing weak behaviors. */
behaviors: DiscoveredBehavior[]
/** Total subjects analyzed (the prevalence denominator). */
subjectsAnalyzed: number
generatedAt: string
durationMs: number
}

/**
* Behaviors — emergent behavior discovery.
*
* Where `tf.lattice.predict` answers "what will this subject do?" and
* `getProfile` answers "who is this subject?", `discover` answers a project-wide
* question: **"what behaviors exist in my data that nobody defined?"** It
* clusters subjects by their feature vectors and surfaces the dense, cohesive
* groups as behaviors — each with prevalence, stability, members, and
* explainable evidence.
*
* @example
* ```ts
* const { behaviors } = await tf.behaviors.discover()
* for (const b of behaviors) {
* console.log(`${b.label} — ${(b.prevalence * 100).toFixed(0)}% of subjects, `
* + `stability ${b.stability.toFixed(2)}, ${b.size} members`)
* console.log(' evidence:', b.exemplarEvidence.join(', '))
* }
* ```
*/
export class BehaviorsResource {
constructor(private readonly http: HttpClient) {}

/**
* Discover emergent behaviors across the project. Returns clusters of
* like-behaving subjects, sorted most-common-and-cohesive first. An empty
* result means the engine abstained — not enough signal to assert any
* behavior — never "there are no behaviors".
*/
async discover(
params: DiscoverParams = {},
options?: RequestOptions,
): Promise<DiscoverResult> {
return this.http.post<DiscoverResult>('/lattice/discover', params, options)
}
}
Loading