anbaric-tsapi 1.7.0 → 1.8.1

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/README.md CHANGED
@@ -11,12 +11,10 @@ synchronous.
11
11
 
12
12
  ## Value classes
13
13
 
14
- **`Job`** — a unit of work moving through a workflow.
15
- `new Job(id, properties = new Map(), initialState, workflowId?, startedBy = "system", startedAt = new Date(), lastUpdated = startedAt, transitions = [])`.
16
- `stateId` is a getter; state only changes through
17
- `transition(transition, actor = workflowId ?? "state-machine")`, which
18
- records `{from, to, actor}` into `transitions` and bumps `lastUpdated` when
19
- the transition's predicate accepts the job.
14
+ **`Job`** — a unit of work moving through a workflow. Immutable.
15
+ `new Job(id, properties = new Map(), state, workflowId?, startedBy = "system", startedAt = new Date(), lastUpdated = startedAt)`.
16
+ `state` is a readonly string; a job never changes in place — `JobPersistence.save`
17
+ constructs a new `Job` at the target `state` and stamps `lastUpdated`.
20
18
 
21
19
  **`State`** — `new State(id, actions = [], transitions = [])`, plus
22
20
  `subscribe(action)` to add an action later.
@@ -27,7 +25,7 @@ accept) gates whether the action runs, and
27
25
  `run : (job) => Promise<Map<string, any>>` (defaults to an empty map)
28
26
  returns the property changes the action wants. `run` never mutates the job.
29
27
 
30
- **`Actor`** — an interface, pure identity: `{ type : "HUMAN" | "CODE" | "AGENT", id : string, role : string }`.
28
+ **`Actor`** — an interface, pure identity: `{ type : "HUMAN" | "CODE" | "AGENT" | "SYSTEM", id : string, role : string }`.
31
29
  Concrete `Human`, `Code`, `Agent` classes live in `anbaric-state-machine`.
32
30
 
33
31
  **`Transition`** — `new Transition(to, predicate)`; the first accepting
@@ -37,18 +35,18 @@ transition in a state wins.
37
35
  `required : boolean` (default false) and `validation : (value) => boolean`
38
36
  (default accept).
39
37
 
40
- **`JobTransition`** — `{ from : string, to : string, actor : string }`.
41
-
42
38
  **Serialization** — `serializeJob(job) : SerializedJob` and
43
39
  `deserializeJob(serialized) : Job` define the wire shape used by the cloud
44
40
  clients and platform (dates as ISO strings, properties as a plain object).
45
41
 
46
42
  ## Contracts
47
43
 
48
- **`JobPersistence`** — `save(job)`, `retrieve(id)` (throws
49
- `No job found with id "x"`), `delete(id)`, `list(pageSize = 100, page = 0)`,
50
- `updateProperties(id, properties)` (merge; implementations stamp
51
- `lastUpdated`).
44
+ **`JobPersistence`** — an abstract class taking an `Auditor`; every method takes
45
+ the acting `Actor` and audits before deferring to an abstract `…Internal`.
46
+ `create(actor, job)`, `save(actor, changeDescription, job, properties?, state?)`
47
+ (applies the property/state change and stamps `lastUpdated`),
48
+ `retrieve(id, actor)` (throws `No job found with id "x"`), `delete(id, actor)`,
49
+ `list(actor, pageSize?, page?)`.
52
50
 
53
51
  **`Queue`** — `enqueue(jobId, workflowId)`, `schedule(jobId, workflowId, due)`.
54
52
  **`Dequeue extends Queue`** adds `dequeueSome() : Promise<Array<QueueMessage>>`;
@@ -58,10 +56,15 @@ Delivery is at-least-once: consumers must tolerate redelivery.
58
56
  **`Consumer`** — `subscribe(workflowId, processJob)` routes deliveries for
59
57
  one workflow to a callback; `cleanUp()` releases resources.
60
58
 
61
- **`JsonStore`** — `save(id, document)`, `retrieve(id)`, `delete(id)`,
62
- `list()`; implementations validate against a `JsonSchema` when one is given.
59
+ **`JsonStore`** — abstract, `Auditor`-backed and actor-audited like
60
+ `JobPersistence`: `create(actor, id, document)`,
61
+ `save(actor, changeDescription, id, document)`, `retrieve(id, actor)`,
62
+ `delete(id, actor)`, `list(actor, pageSize?, page?)` (returns the document
63
+ values, not ids); implementations validate against a `JsonSchema` when one is given.
63
64
 
64
- **`SecretStore`** — `save(name, value)`, `retrieve(name)`, `list()`.
65
+ **`SecretStore`** — abstract, `Auditor`-backed: `create(actor, name, value)`,
66
+ `save(actor, changeDescription, name, value)`, `retrieve(name, actor)`,
67
+ `delete(name, actor)`, `list(actor)`. Secret values never appear in audit details.
65
68
 
66
69
  **`QueueMessage`** — `{ jobId, workflowId }`; part of the platform's private
67
70
  wire protocol (in `api/cloud/`), exported because `Dequeue` returns it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anbaric-tsapi",
3
- "version": "1.7.0",
3
+ "version": "1.8.1",
4
4
  "description": "These are the shared libraries - types, classes, etc - that both the public state-machine and hosting services share",
5
5
  "license": "MIT",
6
6
  "author": "chris@anbaric.ai",
@@ -0,0 +1,24 @@
1
+ import {Agent} from "../actors/agents/Agent";
2
+ import {AgentRequest} from "../actors/agents/AgentRequest";
3
+ import {Job} from "../jobs/Job";
4
+ import {Action} from "./Action";
5
+
6
+ /* An action whose properties are generated by an agent. run builds a request
7
+ from the job and hands it to the agent's client; the structured result
8
+ becomes the property map. Subclasses decide how the request is composed. */
9
+ abstract class AgenticAction extends Action {
10
+
11
+ protected agent : Agent;
12
+
13
+ constructor(name : string, agent : Agent, description : string = "", id? : string) {
14
+ super(name, agent, description, id);
15
+ this.agent = agent;
16
+ this.run = async (job : Job) =>
17
+ new Map(Object.entries(await this.agent.client.generate(this.requestFor(job))));
18
+ }
19
+
20
+ protected abstract requestFor(job : Job) : AgentRequest;
21
+
22
+ }
23
+
24
+ export { AgenticAction }
@@ -1,4 +1,4 @@
1
- type ActorType = "HUMAN" | "CODE" | "AGENT";
1
+ type ActorType = "HUMAN" | "CODE" | "AGENT" | "SYSTEM";
2
2
 
3
3
  interface Actor {
4
4
 
@@ -0,0 +1,13 @@
1
+ import {Actor, ActorType} from "./Actor";
2
+
3
+ class SystemActor implements Actor {
4
+
5
+ type : ActorType = "SYSTEM";
6
+ id = "_SYSTEM";
7
+ role = "_SYSTEM";
8
+
9
+ static actor = new SystemActor();
10
+
11
+ }
12
+
13
+ export { SystemActor }
@@ -0,0 +1,32 @@
1
+ import {Actor} from "../Actor";
2
+ import {AgentRequest} from "./AgentRequest";
3
+
4
+ /* An agent is a light actor: identity only, used for auditing and
5
+ authorization like any other actor. The functional model detail lives in
6
+ its Client collaborator, keeping the actor itself pure data. */
7
+ class Agent implements Actor {
8
+
9
+ readonly type = "AGENT" as const;
10
+ readonly id : string;
11
+ readonly role : string;
12
+ readonly client : Agent.Client;
13
+
14
+ constructor(id : string, role : string, client : Agent.Client) {
15
+ this.id = id;
16
+ this.role = role;
17
+ this.client = client;
18
+ }
19
+
20
+ }
21
+
22
+ namespace Agent {
23
+
24
+ export abstract class Client {
25
+
26
+ abstract generate(request : AgentRequest) : Promise<Record<string, any>>;
27
+
28
+ }
29
+
30
+ }
31
+
32
+ export { Agent }
@@ -0,0 +1,8 @@
1
+ type AgentMessage = {
2
+
3
+ role : "system" | "user" | "assistant";
4
+ content : string;
5
+
6
+ };
7
+
8
+ export type { AgentMessage }
@@ -0,0 +1,10 @@
1
+ import {AgentMessage} from "./AgentMessage";
2
+
3
+ type AgentRequest = {
4
+
5
+ messages : Array<AgentMessage>;
6
+ outputSchema : object;
7
+
8
+ };
9
+
10
+ export type { AgentRequest }
@@ -1,12 +1,19 @@
1
1
  import {Actor} from "../actors/Actor";
2
2
 
3
- type AuditChange = "CREATE" | "UPDATE_PROPERTIES" | "CHANGE_STATE" | "DELETE";
3
+ enum AuditInteraction {
4
+ CREATE = "CREATE",
5
+ UPDATE_PROPERTIES = "UPDATE_PROPERTIES",
6
+ CHANGE_STATE = "CHANGE_STATE",
7
+ DELETE = "DELETE",
8
+ READ = "READ",
9
+ LIST = "LIST",
10
+ }
4
11
 
5
12
  interface Auditor {
6
13
 
7
- audit(jobId : string, actor : Actor, change : AuditChange,
8
- changeDescription : string, details : any) : Promise<void>;
14
+ audit(resourceType : string, resourceId : string, actor : Actor, interaction : AuditInteraction[],
15
+ description : string, details : any) : Promise<void>;
9
16
 
10
17
  }
11
18
 
12
- export type { AuditChange, Auditor }
19
+ export { AuditInteraction, Auditor }
@@ -0,0 +1,15 @@
1
+ import {Actor} from "../actors/Actor";
2
+ import {AuditInteraction, Auditor} from "./Auditor";
3
+
4
+ /* An auditor that records nothing. Used by the persistence instances that sit
5
+ behind the HTTP API, where the app-facing store on the other side has
6
+ already audited the interaction. */
7
+ class NoOpAuditor implements Auditor {
8
+
9
+ async audit(_resourceType : string, _resourceId : string, _actor : Actor, _interaction : AuditInteraction[],
10
+ _description : string, _details : any) : Promise<void> {
11
+ }
12
+
13
+ }
14
+
15
+ export { NoOpAuditor }
@@ -1,11 +1,12 @@
1
- import {AuditChange} from "../auditing/Auditor";
1
+ import {AuditInteraction} from "../auditing/Auditor";
2
2
 
3
3
  type AuditRecord = {
4
4
  id? : string,
5
- jobId : string,
5
+ resourceType : string,
6
+ resourceId : string,
6
7
  actorId : string,
7
8
  actorType : string,
8
- change : AuditChange,
9
+ interaction : AuditInteraction[],
9
10
  description : string,
10
11
  details : any,
11
12
  at? : string,
@@ -1,9 +1,42 @@
1
- interface JsonStore {
1
+ import {Actor} from "../actors/Actor";
2
+ import {AuditInteraction, Auditor} from "../auditing/Auditor";
2
3
 
3
- save(id : string, document : any) : Promise<void>;
4
- retrieve(id : string) : Promise<any>;
5
- delete(id : string) : Promise<void>;
6
- list(pageSize? : number, page? : number) : Promise<Array<any>>;
4
+ /* A schema-validated JSON document store that audits every interaction, keyed
5
+ by collection and id. Public methods audit then defer to ...Internal. */
6
+ abstract class JsonStore {
7
+
8
+ constructor(protected auditor : Auditor, protected collection : string = "documents") {
9
+ }
10
+
11
+ async create(actor : Actor, id : string, document : any) : Promise<void> {
12
+ await this.auditor.audit("document", `${this.collection}/${id}`, actor, [AuditInteraction.CREATE], "Document created", document);
13
+ await this.saveInternal(id, document);
14
+ }
15
+
16
+ async save(actor : Actor, changeDescription : string, id : string, document : any) : Promise<void> {
17
+ await this.auditor.audit("document", `${this.collection}/${id}`, actor, [AuditInteraction.UPDATE_PROPERTIES], changeDescription, document);
18
+ await this.saveInternal(id, document);
19
+ }
20
+
21
+ async retrieve(id : string, actor : Actor) : Promise<any> {
22
+ await this.auditor.audit("document", `${this.collection}/${id}`, actor, [AuditInteraction.READ], "", null);
23
+ return this.retrieveInternal(id);
24
+ }
25
+
26
+ async delete(id : string, actor : Actor) : Promise<void> {
27
+ await this.auditor.audit("document", `${this.collection}/${id}`, actor, [AuditInteraction.DELETE], "", null);
28
+ await this.deleteInternal(id);
29
+ }
30
+
31
+ async list(actor : Actor, pageSize? : number, page? : number) : Promise<Array<any>> {
32
+ await this.auditor.audit("document", `${this.collection}/*`, actor, [AuditInteraction.LIST], "", null);
33
+ return this.listInternal(pageSize, page);
34
+ }
35
+
36
+ protected abstract saveInternal(id : string, document : any) : Promise<void>;
37
+ protected abstract retrieveInternal(id : string) : Promise<any>;
38
+ protected abstract deleteInternal(id : string) : Promise<void>;
39
+ protected abstract listInternal(pageSize? : number, page? : number) : Promise<Array<any>>;
7
40
 
8
41
  }
9
42
 
@@ -1,48 +1,26 @@
1
1
  import {Transition} from "../transitions/Transition";
2
- import {JobTransition} from "./JobTransition";
3
2
 
4
3
  class Job {
5
4
 
6
5
  readonly id : string;
7
- private state: string;
8
- properties : Map<string, any>;
6
+ readonly state: string;
7
+ readonly properties : Map<string, any>;
9
8
  readonly workflowId? : string;
10
9
  readonly startedAt : Date;
11
10
  readonly startedBy : string;
12
- lastUpdated : Date;
13
- readonly transitions : Array<JobTransition>;
11
+ readonly lastUpdated : Date;
14
12
 
15
- constructor(id : string, properties : Map<string, any> = new Map(), initialState: string, workflowId? : string,
13
+ constructor(id : string, properties : Map<string, any> = new Map(), state: string, workflowId? : string,
16
14
  startedBy : string = "system", startedAt : Date = new Date(),
17
- lastUpdated : Date = startedAt, transitions : Array<JobTransition> = []) {
15
+ lastUpdated : Date = startedAt) {
16
+
18
17
  this.id = id;
19
18
  this.properties = properties;
20
- this.state = initialState;
19
+ this.state = state;
21
20
  this.workflowId = workflowId;
22
21
  this.startedBy = startedBy;
23
22
  this.startedAt = startedAt;
24
23
  this.lastUpdated = lastUpdated;
25
- this.transitions = transitions;
26
- }
27
-
28
- private setState(stateId : string) : void {
29
- this.state = stateId;
30
- }
31
-
32
- get stateId() : string | undefined {
33
- return this.state;
34
- }
35
-
36
- transition(transition : Transition, actor : string = this.workflowId ?? "state-machine") : boolean {
37
-
38
- if (transition.predicate(this)) {
39
- this.transitions.push({ from: this.state, to: transition.to, actor });
40
- this.setState(transition.to);
41
- this.lastUpdated = new Date();
42
- return true;
43
- }
44
-
45
- return false;
46
24
  }
47
25
 
48
26
  }
@@ -1,12 +1,90 @@
1
+ import {Actor} from "../actors/Actor";
2
+ import {AuditInteraction, Auditor} from "../auditing/Auditor";
1
3
  import {Job} from "./Job";
4
+ import {SystemActor} from "../actors/SystemActor";
2
5
 
3
- interface JobPersistence {
6
+ /* Persists jobs and audits every interaction. Public methods record the
7
+ interaction against the injected auditor and then defer to the abstract
8
+ ...Internal methods a concrete store implements. */
9
+ abstract class JobPersistence {
4
10
 
5
- save(job : Job) : Promise<void>;
6
- retrieve(id : string) : Promise<Job>;
7
- delete(id : string) : Promise<void>;
8
- list(pageSize? : number, page? : number) : Promise<Array<Job>>;
9
- updateProperties(id : string, properties : Map<string, any>) : Promise<void>;
11
+ constructor(protected auditor : Auditor) {
12
+ }
13
+
14
+ async create(actor : Actor,
15
+ job : Job) : Promise<void> {
16
+
17
+ await this.auditor.audit("job", job.id, actor, [AuditInteraction.CREATE], "Job created", job);
18
+
19
+ this.saveInternal(job);
20
+ }
21
+
22
+ async save(actor : Actor,
23
+ changeDescription : string,
24
+ job : Job,
25
+ properties? : Map<string, any>,
26
+ state? : string) : Promise<void> {
27
+
28
+ const change = {
29
+ properties: properties ? this.generatePropertiesDiff(job.properties, properties) : undefined,
30
+ state: state ? {from : job.state, to : state} : undefined
31
+ }
32
+
33
+ const interaction = [];
34
+ if (properties) interaction.push(AuditInteraction.UPDATE_PROPERTIES);
35
+ if (state) interaction.push(AuditInteraction.CHANGE_STATE);
36
+
37
+ await this.auditor.audit("job", job.id, actor, interaction, changeDescription, change);
38
+
39
+ const updatedJob = new Job(
40
+ job.id,
41
+ properties ? this.updateProperties(job.properties, properties) : job.properties,
42
+ state ? state : job.state,
43
+ job.workflowId,
44
+ job.startedBy,
45
+ job.startedAt,
46
+ new Date()
47
+ )
48
+
49
+ this.saveInternal(updatedJob);
50
+ }
51
+
52
+ private generatePropertiesDiff(oldVersion : Map<string, any>, newVersion : Map<string, any>) : Map<string, {from: any, to: any}> {
53
+ const diff: Map<string, { from: any; to: any; }> = new Map();
54
+
55
+ for (const [key, value] of newVersion) {
56
+ diff.set(key, {from: oldVersion.get(key), to: value});
57
+ }
58
+ return diff;
59
+ }
60
+
61
+ private updateProperties(oldVersion : Map<string, any>, newVersion : Map<string, any>) : Map<string, any> {
62
+ const updated = new Map(oldVersion);
63
+ for (const [key, value] of newVersion) {
64
+ updated.set(key, value);
65
+ }
66
+ return updated;
67
+ }
68
+
69
+ async retrieve(id : string, actor : Actor) : Promise<Job> {
70
+ await this.auditor.audit("job", id, actor, [AuditInteraction.READ], "", null);
71
+ return this.retrieveInternal(id);
72
+ }
73
+
74
+ async delete(id : string, actor : Actor) : Promise<void> {
75
+ await this.auditor.audit("job", id, actor, [AuditInteraction.DELETE], "", null);
76
+ await this.deleteInternal(id);
77
+ }
78
+
79
+ async list(actor : Actor, pageSize? : number, page? : number) : Promise<Array<Job>> {
80
+ await this.auditor.audit("job", "*", actor, [AuditInteraction.LIST], "", null);
81
+ return this.listInternal(pageSize, page);
82
+ }
83
+
84
+ protected abstract saveInternal(job : Job) : Promise<void>;
85
+ protected abstract retrieveInternal(id : string) : Promise<Job>;
86
+ protected abstract deleteInternal(id : string) : Promise<void>;
87
+ protected abstract listInternal(pageSize? : number, page? : number) : Promise<Array<Job>>;
10
88
 
11
89
  }
12
90
 
@@ -1,5 +1,4 @@
1
1
  import {Job} from "./Job";
2
- import {JobTransition} from "./JobTransition";
3
2
 
4
3
  type SerializedJob = {
5
4
  id : string,
@@ -9,26 +8,23 @@ type SerializedJob = {
9
8
  startedAt? : string,
10
9
  startedBy? : string,
11
10
  lastUpdated? : string,
12
- transitions? : Array<JobTransition>,
13
11
  };
14
12
 
15
13
  const serializeJob = (job : Job) : SerializedJob => ({
16
14
  id: job.id,
17
- state: job.stateId!,
15
+ state: job.state,
18
16
  properties: Object.fromEntries(job.properties),
19
17
  workflowId: job.workflowId,
20
18
  startedAt: job.startedAt.toISOString(),
21
19
  startedBy: job.startedBy,
22
20
  lastUpdated: job.lastUpdated.toISOString(),
23
- transitions: job.transitions,
24
21
  });
25
22
 
26
23
  const deserializeJob = (serialized : SerializedJob) : Job => {
27
24
  const startedAt = serialized.startedAt ? new Date(serialized.startedAt) : new Date();
28
25
  return new Job(serialized.id, new Map(Object.entries(serialized.properties)), serialized.state,
29
26
  serialized.workflowId, serialized.startedBy ?? "system", startedAt,
30
- serialized.lastUpdated ? new Date(serialized.lastUpdated) : startedAt,
31
- serialized.transitions ?? []);
27
+ serialized.lastUpdated ? new Date(serialized.lastUpdated) : startedAt);
32
28
  };
33
29
 
34
30
  export { serializeJob, deserializeJob };
@@ -1,9 +1,43 @@
1
- interface SecretStore {
1
+ import {Actor} from "../actors/Actor";
2
+ import {AuditInteraction, Auditor} from "../auditing/Auditor";
2
3
 
3
- save(name : string, value : string) : Promise<void>;
4
- retrieve(name : string) : Promise<string>;
5
- delete(name : string) : Promise<void>;
6
- list() : Promise<Array<string>>;
4
+ /* Stores named secrets and audits every interaction. The secret value is
5
+ never included in an audit record. Public methods audit then defer to the
6
+ abstract ...Internal methods. */
7
+ abstract class SecretStore {
8
+
9
+ constructor(protected auditor : Auditor) {
10
+ }
11
+
12
+ async create(actor : Actor, name : string, value : string) : Promise<void> {
13
+ await this.auditor.audit("secret", name, actor, [AuditInteraction.CREATE], "Secret created", null);
14
+ await this.saveInternal(name, value);
15
+ }
16
+
17
+ async save(actor : Actor, changeDescription : string, name : string, value : string) : Promise<void> {
18
+ await this.auditor.audit("secret", name, actor, [AuditInteraction.UPDATE_PROPERTIES], changeDescription, null);
19
+ await this.saveInternal(name, value);
20
+ }
21
+
22
+ async retrieve(name : string, actor : Actor) : Promise<string> {
23
+ await this.auditor.audit("secret", name, actor, [AuditInteraction.READ], "", null);
24
+ return this.retrieveInternal(name);
25
+ }
26
+
27
+ async delete(name : string, actor : Actor) : Promise<void> {
28
+ await this.auditor.audit("secret", name, actor, [AuditInteraction.DELETE], "", null);
29
+ await this.deleteInternal(name);
30
+ }
31
+
32
+ async list(actor : Actor) : Promise<Array<string>> {
33
+ await this.auditor.audit("secret", "*", actor, [AuditInteraction.LIST], "", null);
34
+ return this.listInternal();
35
+ }
36
+
37
+ protected abstract saveInternal(name : string, value : string) : Promise<void>;
38
+ protected abstract retrieveInternal(name : string) : Promise<string>;
39
+ protected abstract deleteInternal(name : string) : Promise<void>;
40
+ protected abstract listInternal() : Promise<Array<string>>;
7
41
 
8
42
  }
9
43
 
package/src/index.ts CHANGED
@@ -1,13 +1,18 @@
1
1
  export * from "./api/jobs/Job";
2
2
  export * from "./api/jobs/JobPersistence"
3
3
  export * from "./api/jobs/JobSerialization"
4
- export * from "./api/jobs/JobTransition"
5
4
  export * from "./api/jobs/PropertyDefinition"
6
5
  export * from "./api/states/State"
7
6
  export * from "./api/actions/Action"
7
+ export * from "./api/actions/AgenticAction"
8
+ export * from "./api/actors/agents/AgentMessage"
9
+ export * from "./api/actors/agents/AgentRequest"
10
+ export * from "./api/actors/agents/Agent"
8
11
  export * from "./api/auditing/Auditor"
12
+ export * from "./api/auditing/NoOpAuditor"
9
13
  export * from "./api/transitions/Transition"
10
14
  export * from "./api/actors/Actor"
15
+ export * from "./api/actors/SystemActor"
11
16
  export * from "./api/documents/JsonStore"
12
17
  export * from "./api/secrets/SecretStore"
13
18
  export * from "./api/documents/JsonSchema"
@@ -1,7 +0,0 @@
1
- type JobTransition = {
2
- from : string,
3
- to : string,
4
- actor : string,
5
- };
6
-
7
- export type { JobTransition }