@slopus/happy-agent-base 0.0.2 → 0.0.3
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 +20 -635
- package/dist/Agent.d.ts +7 -0
- package/dist/Agent.d.ts.map +1 -1
- package/dist/Agent.js +11 -0
- package/dist/Agent.js.map +1 -1
- package/dist/AgentBase.d.ts +15 -17
- package/dist/AgentBase.d.ts.map +1 -1
- package/dist/AgentBase.js +130 -229
- package/dist/AgentBase.js.map +1 -1
- package/dist/AgentBaseHooks.d.ts +1 -5
- package/dist/AgentBaseHooks.d.ts.map +1 -1
- package/dist/AgentBasePending.d.ts +3 -8
- package/dist/AgentBasePending.d.ts.map +1 -1
- package/dist/AgentBasePending.js +4 -13
- package/dist/AgentBasePending.js.map +1 -1
- package/dist/AgentFeature.d.ts +14 -1
- package/dist/AgentFeature.d.ts.map +1 -1
- package/dist/AgentKV.d.ts +0 -5
- package/dist/AgentKV.d.ts.map +1 -1
- package/dist/AgentKV.js +0 -8
- package/dist/AgentKV.js.map +1 -1
- package/dist/AgentPersistence.d.ts +0 -30
- package/dist/AgentPersistence.d.ts.map +1 -1
- package/dist/AgentProviders.d.ts +19 -9
- package/dist/AgentProviders.d.ts.map +1 -1
- package/dist/AgentProviders.js +15 -12
- package/dist/AgentProviders.js.map +1 -1
- package/dist/AgentStorage.d.ts +20 -0
- package/dist/AgentStorage.d.ts.map +1 -1
- package/dist/AgentStorage.js +33 -0
- package/dist/AgentStorage.js.map +1 -1
- package/dist/AgentSystem.d.ts +2 -0
- package/dist/AgentSystem.d.ts.map +1 -1
- package/dist/AgentSystemLocal.d.ts +12 -11
- package/dist/AgentSystemLocal.d.ts.map +1 -1
- package/dist/AgentSystemLocal.js +186 -67
- package/dist/AgentSystemLocal.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/AgentBaseStoreLock.d.ts +0 -16
- package/dist/AgentBaseStoreLock.d.ts.map +0 -1
- package/dist/AgentBaseStoreLock.js +0 -37
- package/dist/AgentBaseStoreLock.js.map +0 -1
package/dist/AgentProviders.js
CHANGED
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Mutable registry of
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* conversation must reset. Providers can be added and removed while the process runs; lookups
|
|
6
|
-
* return the live reference or null when absent.
|
|
2
|
+
* Mutable registry of model-aware provider sources keyed by caller-supplied IDs. Each entry carries
|
|
3
|
+
* its compatibility type, which decides how far a model change can go before the conversation
|
|
4
|
+
* must reset.
|
|
7
5
|
*/
|
|
8
6
|
export class AgentProviders {
|
|
9
|
-
/** Registered
|
|
7
|
+
/** Registered provider sources keyed by their caller-supplied ID and compatibility type. */
|
|
10
8
|
#providers = new Map();
|
|
11
|
-
/** Register a provider under `id`. Throws if that ID is already registered. */
|
|
12
|
-
add(id,
|
|
9
|
+
/** Register a provider source under `id`. Throws if that ID is already registered. */
|
|
10
|
+
add(id, source, type) {
|
|
13
11
|
if (this.#providers.has(id)) {
|
|
14
12
|
throw new Error(`Provider "${id}" is already registered.`);
|
|
15
13
|
}
|
|
16
|
-
this.#providers.set(id, {
|
|
14
|
+
this.#providers.set(id, { source, type });
|
|
17
15
|
}
|
|
18
16
|
/** Unregister the provider at `id`. Returns whether one was actually removed. */
|
|
19
17
|
remove(id) {
|
|
20
18
|
return this.#providers.delete(id);
|
|
21
19
|
}
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
/** Resolve the provider suitable for `model`, or null when `id` is not registered. */
|
|
21
|
+
async resolve(id, model) {
|
|
22
|
+
const entry = this.#providers.get(id);
|
|
23
|
+
if (entry === undefined)
|
|
24
|
+
return null;
|
|
25
|
+
return typeof entry.source === "function"
|
|
26
|
+
? await entry.source({ id, model })
|
|
27
|
+
: entry.source;
|
|
25
28
|
}
|
|
26
29
|
/** The compatibility type the provider at `id` was registered with, or null when absent. */
|
|
27
30
|
typeOf(id) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentProviders.js","sourceRoot":"","sources":["../sources/AgentProviders.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AgentProviders.js","sourceRoot":"","sources":["../sources/AgentProviders.ts"],"names":[],"mappings":"AAkBA;;;;GAIG;AACH,MAAM,OAAO,cAAc;IACvB,4FAA4F;IACnF,UAAU,GAAG,IAAI,GAAG,EAG1B,CAAC;IAEJ,sFAAsF;IACtF,GAAG,CAAC,EAAU,EAAE,MAA2B,EAAE,IAAoC;QAC7E,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,aAAa,EAAE,0BAA0B,CAAC,CAAC;QAC/D,CAAC;QACD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,CAAC;IAED,iFAAiF;IACjF,MAAM,CAAC,EAAU;QACb,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,sFAAsF;IACtF,KAAK,CAAC,OAAO,CAAC,EAAU,EAAE,KAAyB;QAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACtC,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACrC,OAAO,OAAO,KAAK,CAAC,MAAM,KAAK,UAAU;YACrC,CAAC,CAAC,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;YACnC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC;IACvB,CAAC;IAED,4FAA4F;IAC5F,MAAM,CAAC,EAAU;QACb,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,IAAI,IAAI,IAAI,CAAC;IACjD,CAAC;IAED,8CAA8C;IAC9C,IAAI,GAAG;QACH,OAAO,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;IACvC,CAAC;CACJ"}
|
package/dist/AgentStorage.d.ts
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
|
+
import type { Context } from "@steve.kite/stdlib";
|
|
1
2
|
import type { AgentKV } from "./AgentKV.js";
|
|
2
3
|
import type { AgentPersistence } from "./AgentPersistence.js";
|
|
4
|
+
/**
|
|
5
|
+
* The exclusive ownership of one durable agent store. The storage adapter must back this with
|
|
6
|
+
* the database's hard process-level lock rather than an in-memory convention.
|
|
7
|
+
*/
|
|
8
|
+
export interface AgentStorageLock {
|
|
9
|
+
/** Release the hard lock after the owning AgentSystem has stopped every agent. */
|
|
10
|
+
release(ctx: Context): Promise<void>;
|
|
11
|
+
}
|
|
3
12
|
/** What an `AgentStorage` is built from. */
|
|
4
13
|
export interface AgentStorageOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Acquire exclusive ownership of the whole store. This must fail while any other process or
|
|
16
|
+
* AgentSystem owns the same durable store.
|
|
17
|
+
*/
|
|
18
|
+
readonly acquireLock: (ctx: Context) => Promise<AgentStorageLock>;
|
|
5
19
|
/** Shared key-value storage used for state spanning all agents. */
|
|
6
20
|
readonly kv: AgentKV;
|
|
7
21
|
/** Produce the isolated persistence used by one agent. */
|
|
@@ -13,6 +27,12 @@ export declare class AgentStorage {
|
|
|
13
27
|
/** Shared key-value storage used for state spanning all agents. */
|
|
14
28
|
readonly kv: AgentKV;
|
|
15
29
|
constructor(options: AgentStorageOptions);
|
|
30
|
+
/**
|
|
31
|
+
* Acquire exclusive ownership until the returned lock is released. The adapter's lock
|
|
32
|
+
* enforces this across processes; the local guard also rejects accidental reuse of this
|
|
33
|
+
* object.
|
|
34
|
+
*/
|
|
35
|
+
acquireLock(ctx: Context): Promise<AgentStorageLock>;
|
|
16
36
|
/** The isolated persistence for the given agent. */
|
|
17
37
|
persistence(agentId: string): AgentPersistence;
|
|
18
38
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentStorage.d.ts","sourceRoot":"","sources":["../sources/AgentStorage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,4CAA4C;AAC5C,MAAM,WAAW,mBAAmB;IAChC,mEAAmE;IACnE,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,0DAA0D;IAC1D,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,gBAAgB,CAAC;CAC/D;AAED,gEAAgE;AAChE,qBAAa,YAAY;;IACrB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"AgentStorage.d.ts","sourceRoot":"","sources":["../sources/AgentStorage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC7B,kFAAkF;IAClF,OAAO,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC;AAED,4CAA4C;AAC5C,MAAM,WAAW,mBAAmB;IAChC;;;OAGG;IACH,QAAQ,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAClE,mEAAmE;IACnE,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,0DAA0D;IAC1D,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,gBAAgB,CAAC;CAC/D;AAED,gEAAgE;AAChE,qBAAa,YAAY;;IACrB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IAQrB,YAAY,OAAO,EAAE,mBAAmB,EAIvC;IAED;;;;OAIG;IACG,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAmBzD;IAED,oDAAoD;IACpD,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAE7C;CACJ"}
|
package/dist/AgentStorage.js
CHANGED
|
@@ -2,12 +2,45 @@
|
|
|
2
2
|
export class AgentStorage {
|
|
3
3
|
/** Shared key-value storage used for state spanning all agents. */
|
|
4
4
|
kv;
|
|
5
|
+
/** Acquires the database-backed exclusive lock for this store. */
|
|
6
|
+
#acquireLock;
|
|
5
7
|
/** Produces the isolated persistence used by one agent. */
|
|
6
8
|
#persistence;
|
|
9
|
+
/** Prevents two systems from sharing even one AgentStorage instance. */
|
|
10
|
+
#owned = false;
|
|
7
11
|
constructor(options) {
|
|
8
12
|
this.kv = options.kv;
|
|
13
|
+
this.#acquireLock = options.acquireLock;
|
|
9
14
|
this.#persistence = options.persistence;
|
|
10
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Acquire exclusive ownership until the returned lock is released. The adapter's lock
|
|
18
|
+
* enforces this across processes; the local guard also rejects accidental reuse of this
|
|
19
|
+
* object.
|
|
20
|
+
*/
|
|
21
|
+
async acquireLock(ctx) {
|
|
22
|
+
if (this.#owned)
|
|
23
|
+
throw new Error("The agent store is already owned by another system.");
|
|
24
|
+
this.#owned = true;
|
|
25
|
+
let lock;
|
|
26
|
+
try {
|
|
27
|
+
lock = await this.#acquireLock(ctx);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
this.#owned = false;
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
let released = false;
|
|
34
|
+
return {
|
|
35
|
+
release: async (releaseCtx) => {
|
|
36
|
+
if (released)
|
|
37
|
+
return;
|
|
38
|
+
await lock.release(releaseCtx);
|
|
39
|
+
released = true;
|
|
40
|
+
this.#owned = false;
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
11
44
|
/** The isolated persistence for the given agent. */
|
|
12
45
|
persistence(agentId) {
|
|
13
46
|
return this.#persistence(agentId);
|
package/dist/AgentStorage.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentStorage.js","sourceRoot":"","sources":["../sources/AgentStorage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AgentStorage.js","sourceRoot":"","sources":["../sources/AgentStorage.ts"],"names":[],"mappings":"AA2BA,gEAAgE;AAChE,MAAM,OAAO,YAAY;IACrB,mEAAmE;IAC1D,EAAE,CAAU;IACrB,kEAAkE;IACzD,YAAY,CAA8C;IACnE,2DAA2D;IAClD,YAAY,CAAwC;IAC7D,wEAAwE;IACxE,MAAM,GAAG,KAAK,CAAC;IAEf,YAAY,OAA4B;QACpC,IAAI,CAAC,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC;QACrB,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC;QACxC,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC;IAC5C,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,WAAW,CAAC,GAAY;QAC1B,IAAI,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;QACxF,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,IAAsB,CAAC;QAC3B,IAAI,CAAC;YACD,IAAI,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QACxC,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACtB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;YACpB,MAAM,KAAK,CAAC;QAChB,CAAC;QACD,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,OAAO;YACH,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE;gBAC1B,IAAI,QAAQ;oBAAE,OAAO;gBACrB,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;gBAC/B,QAAQ,GAAG,IAAI,CAAC;gBAChB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;YACxB,CAAC;SACJ,CAAC;IACN,CAAC;IAED,oDAAoD;IACpD,WAAW,CAAC,OAAe;QACvB,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IACtC,CAAC;CACJ"}
|
package/dist/AgentSystem.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ export interface AgentInitialContext {
|
|
|
23
23
|
export interface AgentSystem {
|
|
24
24
|
/** The models this collection offers its agents. */
|
|
25
25
|
readonly models: readonly AgentModel[];
|
|
26
|
+
/** Stop every agent and release this system's exclusive ownership of its durable store. */
|
|
27
|
+
close(ctx: Context): Promise<void>;
|
|
26
28
|
/**
|
|
27
29
|
* Create an agent with a new system-generated cuid2 identity and the configuration it keeps
|
|
28
30
|
* for its whole life. The configuration and optional initial context are persisted before
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentSystem.d.ts","sourceRoot":"","sources":["../sources/AgentSystem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AACrF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,mFAAmF;AACnF,MAAM,WAAW,mBAAmB;IAChC,oFAAoF;IACpF,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAC;CAChD;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,WAAW;IACxB,oDAAoD;IACpD,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAC;IAEvC;;;;OAIG;IACH,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAEhG,oFAAoF;IACpF,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAErD,6FAA6F;IAC7F,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAAC;IAExE,gFAAgF;IAChF,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAEvD;;;OAGG;IACH,KAAK,CACD,GAAG,EAAE,OAAO,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,kBAAkB,EAC3B,OAAO,CAAC,EAAE,uBAAuB,GAAG,qBAAqB,GAC1D,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB,+FAA+F;IAC/F,IAAI,CACA,GAAG,EAAE,OAAO,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,kBAAkB,EAC3B,OAAO,CAAC,EAAE,uBAAuB,GAAG,qBAAqB,GAC1D,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB,2FAA2F;IAC3F,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAErF,wFAAwF;IACxF,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1F"}
|
|
1
|
+
{"version":3,"file":"AgentSystem.d.ts","sourceRoot":"","sources":["../sources/AgentSystem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AACrF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,mFAAmF;AACnF,MAAM,WAAW,mBAAmB;IAChC,oFAAoF;IACpF,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAC;CAChD;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,WAAW;IACxB,oDAAoD;IACpD,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAC;IAEvC,2FAA2F;IAC3F,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnC;;;;OAIG;IACH,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAEhG,oFAAoF;IACpF,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAErD,6FAA6F;IAC7F,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAAC;IAExE,gFAAgF;IAChF,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAEvD;;;OAGG;IACH,KAAK,CACD,GAAG,EAAE,OAAO,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,kBAAkB,EAC3B,OAAO,CAAC,EAAE,uBAAuB,GAAG,qBAAqB,GAC1D,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB,+FAA+F;IAC/F,IAAI,CACA,GAAG,EAAE,OAAO,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,kBAAkB,EAC3B,OAAO,CAAC,EAAE,uBAAuB,GAAG,qBAAqB,GAC1D,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB,2FAA2F;IAC3F,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAErF,wFAAwF;IACxF,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1F"}
|
|
@@ -29,10 +29,8 @@ export interface AgentSystemLocalOptions {
|
|
|
29
29
|
* the identities its storage holds. Concurrent resolutions of the same ID share one load, while a
|
|
30
30
|
* failed load is forgotten so a later resolution can retry.
|
|
31
31
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* collection-wide lock would make one agent's feature load block every other agent, including one
|
|
35
|
-
* that load itself resolves — and treats storage, not memory, as the authority on who exists.
|
|
32
|
+
* Work serializes per agent rather than per collection: a collection-wide lock would make one
|
|
33
|
+
* agent's feature load block every other agent, including one that load itself resolves.
|
|
36
34
|
*
|
|
37
35
|
* This is the owner's handle, and some of what it offers waits for an agent to reach a point only
|
|
38
36
|
* that agent's run loop can bring it to. Hand an `AgentSystemRef` to anything running inside an
|
|
@@ -49,8 +47,7 @@ export declare class AgentSystemLocal implements AgentSystem {
|
|
|
49
47
|
* was running when the previous process ended is still owed an answer, and this is what
|
|
50
48
|
* makes it happen. Every identity the storage holds is examined, and each one that owes work
|
|
51
49
|
* is resolved and resumed, so by the time this returns the collection is not merely built
|
|
52
|
-
* but running.
|
|
53
|
-
* creation commits there is no agent to resume.
|
|
50
|
+
* but running.
|
|
54
51
|
*/
|
|
55
52
|
static create(ctx: Context, storage: AgentStorage, config: AgentSystemLocalOptions): Promise<AgentSystemLocal>;
|
|
56
53
|
/**
|
|
@@ -59,6 +56,11 @@ export declare class AgentSystemLocal implements AgentSystem {
|
|
|
59
56
|
* the storage was left holding — building one without that is building half of it.
|
|
60
57
|
*/
|
|
61
58
|
private constructor();
|
|
59
|
+
/**
|
|
60
|
+
* Stop every live agent, wait for operations already admitted by this owner, and only then
|
|
61
|
+
* release the database lock. Repeated callers join the same shutdown.
|
|
62
|
+
*/
|
|
63
|
+
close(ctx: Context): Promise<void>;
|
|
62
64
|
/**
|
|
63
65
|
* Create an agent with the configuration it keeps for its whole life, and resolve it. The
|
|
64
66
|
* identity is allocated here and nowhere else: it is a cuid2, globally unique, so a new
|
|
@@ -66,11 +68,10 @@ export declare class AgentSystemLocal implements AgentSystem {
|
|
|
66
68
|
* The configuration is persisted before the agent runs, so every later process resolves the
|
|
67
69
|
* agent exactly as it was created.
|
|
68
70
|
*
|
|
69
|
-
* Nothing here is
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* compensation that can itself fail is not a guarantee.
|
|
71
|
+
* Nothing here is undone: an agent whose features refuse to load leaves an identity that
|
|
72
|
+
* exists, is resolvable, and will be built the next time something wants it. Taking a
|
|
73
|
+
* provisional identity back after a failed build would add a compensation that can itself
|
|
74
|
+
* fail.
|
|
74
75
|
*/
|
|
75
76
|
create(ctx: Context, config: AgentConfig, initialContext?: AgentInitialContext): Promise<Agent>;
|
|
76
77
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentSystemLocal.d.ts","sourceRoot":"","sources":["../sources/AgentSystemLocal.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAkB,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAElF,OAAO,EAA6B,KAAK,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7E,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,KAAK,qBAAqB,EAAE,KAAK,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAI1F,OAAO,EAAsC,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACxF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"AgentSystemLocal.d.ts","sourceRoot":"","sources":["../sources/AgentSystemLocal.ts"],"names":[],"mappings":"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"}
|
package/dist/AgentSystemLocal.js
CHANGED
|
@@ -3,7 +3,7 @@ import { Value } from "@sinclair/typebox/value";
|
|
|
3
3
|
import { asyncLock } from "@steve.kite/stdlib";
|
|
4
4
|
import { Agent } from "./Agent.js";
|
|
5
5
|
import {} from "./AgentBase.js";
|
|
6
|
-
import {
|
|
6
|
+
import { agentId as agentIdOf } from "./AgentContexts.js";
|
|
7
7
|
import { agentConfigSchema, withAgentConfig } from "./AgentConfig.js";
|
|
8
8
|
import { withAgentSystem } from "./AgentSystemContext.js";
|
|
9
9
|
import { AgentSystemRef } from "./AgentSystemRef.js";
|
|
@@ -12,10 +12,8 @@ import { AgentSystemRef } from "./AgentSystemRef.js";
|
|
|
12
12
|
* the identities its storage holds. Concurrent resolutions of the same ID share one load, while a
|
|
13
13
|
* failed load is forgotten so a later resolution can retry.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* collection-wide lock would make one agent's feature load block every other agent, including one
|
|
18
|
-
* that load itself resolves — and treats storage, not memory, as the authority on who exists.
|
|
15
|
+
* Work serializes per agent rather than per collection: a collection-wide lock would make one
|
|
16
|
+
* agent's feature load block every other agent, including one that load itself resolves.
|
|
19
17
|
*
|
|
20
18
|
* This is the owner's handle, and some of what it offers waits for an agent to reach a point only
|
|
21
19
|
* that agent's run loop can bring it to. Hand an `AgentSystemRef` to anything running inside an
|
|
@@ -24,6 +22,8 @@ import { AgentSystemRef } from "./AgentSystemRef.js";
|
|
|
24
22
|
export class AgentSystemLocal {
|
|
25
23
|
/** The models this collection offers its agents. */
|
|
26
24
|
models;
|
|
25
|
+
/** The lifetime context retained by this system and its storage lock. */
|
|
26
|
+
#ctx;
|
|
27
27
|
/**
|
|
28
28
|
* What this collection looks like from inside one of its agents, and the only form of it a
|
|
29
29
|
* derived context ever carries.
|
|
@@ -37,6 +37,8 @@ export class AgentSystemLocal {
|
|
|
37
37
|
#providers;
|
|
38
38
|
/** The registry ID of the provider new agents are created with. */
|
|
39
39
|
#provider;
|
|
40
|
+
/** Exclusive database-backed ownership of this collection's whole durable store. */
|
|
41
|
+
#storageLock;
|
|
40
42
|
/** The configuration each identity was created with. */
|
|
41
43
|
#configs;
|
|
42
44
|
/**
|
|
@@ -51,6 +53,12 @@ export class AgentSystemLocal {
|
|
|
51
53
|
#persistences = new Map();
|
|
52
54
|
/** Per-agent locks handed out by `#lockFor`, created lazily the first time an ID is touched. */
|
|
53
55
|
#locks = new Map();
|
|
56
|
+
/** Public operations admitted before shutdown and therefore allowed to finish. */
|
|
57
|
+
#admitted = new Set();
|
|
58
|
+
/** No agent operation is admitted until every feature has finished its beforeStart hook. */
|
|
59
|
+
#lifecycle = "initializing";
|
|
60
|
+
/** The shared shutdown, including release of the hard storage lock. */
|
|
61
|
+
#closePromise;
|
|
54
62
|
/**
|
|
55
63
|
* Bring up a collection over one storage and carry on where the last process left off.
|
|
56
64
|
*
|
|
@@ -58,28 +66,109 @@ export class AgentSystemLocal {
|
|
|
58
66
|
* was running when the previous process ended is still owed an answer, and this is what
|
|
59
67
|
* makes it happen. Every identity the storage holds is examined, and each one that owes work
|
|
60
68
|
* is resolved and resumed, so by the time this returns the collection is not merely built
|
|
61
|
-
* but running.
|
|
62
|
-
* creation commits there is no agent to resume.
|
|
69
|
+
* but running.
|
|
63
70
|
*/
|
|
64
71
|
static async create(ctx, storage, config) {
|
|
65
|
-
const
|
|
66
|
-
await
|
|
67
|
-
|
|
72
|
+
const systemCtx = ctx;
|
|
73
|
+
const storageLock = await storage.acquireLock(systemCtx);
|
|
74
|
+
const system = new AgentSystemLocal(systemCtx, storage, storageLock, config);
|
|
75
|
+
try {
|
|
76
|
+
await system.#beforeStart(systemCtx);
|
|
77
|
+
const active = await system.#start(systemCtx);
|
|
78
|
+
system.#lifecycle = "open";
|
|
79
|
+
for (const agent of active)
|
|
80
|
+
agent.start();
|
|
81
|
+
await system.#afterStart(systemCtx);
|
|
82
|
+
return system;
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
await system.close(systemCtx).catch(() => undefined);
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
68
88
|
}
|
|
69
89
|
/**
|
|
70
90
|
* Wire this collection to its storage, providers, and feature configuration, without reading
|
|
71
91
|
* any of it. Private: a collection is brought up by `create`, which also resumes the work
|
|
72
92
|
* the storage was left holding — building one without that is building half of it.
|
|
73
93
|
*/
|
|
74
|
-
constructor(storage, options) {
|
|
94
|
+
constructor(ctx, storage, storageLock, options) {
|
|
95
|
+
this.#ctx = ctx;
|
|
75
96
|
this.#features = options.features ?? [];
|
|
76
97
|
this.#storage = storage;
|
|
98
|
+
this.#storageLock = storageLock;
|
|
77
99
|
this.#providers = options.providers;
|
|
78
100
|
this.#provider = options.provider;
|
|
79
101
|
this.models = [...options.models];
|
|
80
102
|
this.#configs = storage.kv.scoped("config");
|
|
81
103
|
this.#sharedFeatureKV = storage.kv.scoped("features");
|
|
82
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Stop every live agent, wait for operations already admitted by this owner, and only then
|
|
107
|
+
* release the database lock. Repeated callers join the same shutdown.
|
|
108
|
+
*/
|
|
109
|
+
async close(ctx) {
|
|
110
|
+
if (this.#closePromise === undefined) {
|
|
111
|
+
this.#lifecycle = "closing";
|
|
112
|
+
this.#closePromise = this.#shutdown();
|
|
113
|
+
}
|
|
114
|
+
const closing = this.#closePromise;
|
|
115
|
+
const caller = agentIdOf(ctx);
|
|
116
|
+
if (caller !== undefined && this.#agents.has(caller)) {
|
|
117
|
+
void closing.catch(() => undefined);
|
|
118
|
+
throw new Error("Closing the agent system from inside one of its own agents would wait for " +
|
|
119
|
+
"that agent's turn. Shutdown will finish and release the store after this " +
|
|
120
|
+
"caller returns.");
|
|
121
|
+
}
|
|
122
|
+
await closing;
|
|
123
|
+
}
|
|
124
|
+
/** The real shutdown barrier, which keeps the hard store lock until every agent is closed. */
|
|
125
|
+
async #shutdown() {
|
|
126
|
+
try {
|
|
127
|
+
while (this.#admitted.size > 0) {
|
|
128
|
+
await Promise.allSettled(this.#admitted);
|
|
129
|
+
}
|
|
130
|
+
const closed = [...this.#agents.values()].map((agent) => {
|
|
131
|
+
void agent.close().catch(() => undefined);
|
|
132
|
+
return agent.waitForClosed();
|
|
133
|
+
});
|
|
134
|
+
await Promise.allSettled(closed);
|
|
135
|
+
this.#agents.clear();
|
|
136
|
+
this.#persistences.clear();
|
|
137
|
+
this.#locks.clear();
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
try {
|
|
141
|
+
await this.#storageLock.release(this.#ctx);
|
|
142
|
+
}
|
|
143
|
+
finally {
|
|
144
|
+
this.#lifecycle = "closed";
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Admit one public operation while this system still owns the store. Shutdown rejects new
|
|
150
|
+
* admissions and waits for every earlier one before releasing the hard lock.
|
|
151
|
+
*/
|
|
152
|
+
#admit(operation) {
|
|
153
|
+
if (this.#lifecycle !== "open") {
|
|
154
|
+
return Promise.reject(new Error(this.#lifecycle === "initializing"
|
|
155
|
+
? "The agent system is not ready."
|
|
156
|
+
: "The agent system is closed."));
|
|
157
|
+
}
|
|
158
|
+
let running;
|
|
159
|
+
try {
|
|
160
|
+
// Begin synchronously so ownership-transfer boundaries such as create(config) copy
|
|
161
|
+
// caller-owned input before the caller can mutate it after receiving the promise.
|
|
162
|
+
running = operation();
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
return Promise.reject(error);
|
|
166
|
+
}
|
|
167
|
+
const settled = running.then(() => undefined, () => undefined);
|
|
168
|
+
this.#admitted.add(settled);
|
|
169
|
+
void settled.finally(() => this.#admitted.delete(settled));
|
|
170
|
+
return running;
|
|
171
|
+
}
|
|
83
172
|
/**
|
|
84
173
|
* Create an agent with the configuration it keeps for its whole life, and resolve it. The
|
|
85
174
|
* identity is allocated here and nowhere else: it is a cuid2, globally unique, so a new
|
|
@@ -87,29 +176,30 @@ export class AgentSystemLocal {
|
|
|
87
176
|
* The configuration is persisted before the agent runs, so every later process resolves the
|
|
88
177
|
* agent exactly as it was created.
|
|
89
178
|
*
|
|
90
|
-
* Nothing here is
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
* compensation that can itself fail is not a guarantee.
|
|
179
|
+
* Nothing here is undone: an agent whose features refuse to load leaves an identity that
|
|
180
|
+
* exists, is resolvable, and will be built the next time something wants it. Taking a
|
|
181
|
+
* provisional identity back after a failed build would add a compensation that can itself
|
|
182
|
+
* fail.
|
|
95
183
|
*/
|
|
96
184
|
async create(ctx, config, initialContext) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
// The caller keeps its own object, and may go on editing it. What was created is what
|
|
102
|
-
// was passed at this moment, so storage and this agent's context both get a copy.
|
|
103
|
-
const owned = structuredClone(config);
|
|
104
|
-
return await this.#lockFor(agentId).runInLock(ctx, async (lockCtx) => {
|
|
105
|
-
await this.#configs.write(lockCtx, agentId, owned);
|
|
106
|
-
if (initialContext !== undefined) {
|
|
107
|
-
await this.#seedInitialContext(lockCtx, agentId, initialContext.messages);
|
|
185
|
+
return await this.#admit(async () => {
|
|
186
|
+
const agentId = createId();
|
|
187
|
+
if (!Value.Check(agentConfigSchema, config)) {
|
|
188
|
+
throw new Error(`The configuration for agent "${agentId}" is not valid.`);
|
|
108
189
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return
|
|
190
|
+
// The caller keeps its own object, and may go on editing it. What was created is what
|
|
191
|
+
// was passed at this moment, so storage and this agent's context both get a copy.
|
|
192
|
+
const owned = structuredClone(config);
|
|
193
|
+
return await this.#lockFor(agentId).runInLock(ctx, async (lockCtx) => {
|
|
194
|
+
await this.#configs.write(lockCtx, agentId, owned);
|
|
195
|
+
if (initialContext !== undefined) {
|
|
196
|
+
await this.#seedInitialContext(lockCtx, agentId, initialContext.messages);
|
|
197
|
+
}
|
|
198
|
+
const agent = await this.#instantiate(lockCtx, agentId, owned);
|
|
199
|
+
if (agent === undefined)
|
|
200
|
+
throw new Error(`Agent "${agentId}" could not be built.`);
|
|
201
|
+
return agent;
|
|
202
|
+
});
|
|
113
203
|
});
|
|
114
204
|
}
|
|
115
205
|
/**
|
|
@@ -123,12 +213,14 @@ export class AgentSystemLocal {
|
|
|
123
213
|
* what clears it.
|
|
124
214
|
*/
|
|
125
215
|
async delete(ctx, agentId) {
|
|
126
|
-
await this.#
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
216
|
+
await this.#admit(async () => {
|
|
217
|
+
await this.#lockFor(agentId).runInLock(ctx, async (lockCtx) => {
|
|
218
|
+
const agent = this.#agents.get(agentId);
|
|
219
|
+
this.#agents.delete(agentId);
|
|
220
|
+
await agent?.close();
|
|
221
|
+
await this.#configs.delete(lockCtx, agentId);
|
|
222
|
+
this.#persistences.delete(agentId);
|
|
223
|
+
});
|
|
132
224
|
});
|
|
133
225
|
}
|
|
134
226
|
/** Install a projected conversation before the new agent is started. */
|
|
@@ -136,15 +228,19 @@ export class AgentSystemLocal {
|
|
|
136
228
|
if (messages.length === 0)
|
|
137
229
|
return;
|
|
138
230
|
const persistence = this.#persistenceFor(agentId);
|
|
139
|
-
await
|
|
231
|
+
await persistence.transaction(ctx, async (txCtx) => {
|
|
140
232
|
await persistence.append(txCtx, {
|
|
141
233
|
type: "compaction",
|
|
142
234
|
messages: structuredClone(messages),
|
|
143
235
|
});
|
|
144
|
-
})
|
|
236
|
+
});
|
|
145
237
|
}
|
|
146
238
|
/** The configuration an agent was created with, or undefined when there is no such agent. */
|
|
147
239
|
async config(ctx, agentId) {
|
|
240
|
+
return await this.#admit(async () => await this.#config(ctx, agentId));
|
|
241
|
+
}
|
|
242
|
+
/** Read one stored configuration while its owning operation is already admitted. */
|
|
243
|
+
async #config(ctx, agentId) {
|
|
148
244
|
const stored = await this.#configs.read(ctx, agentId);
|
|
149
245
|
if (stored === undefined)
|
|
150
246
|
return undefined;
|
|
@@ -158,6 +254,10 @@ export class AgentSystemLocal {
|
|
|
158
254
|
* Concurrent resolutions of the same ID share one load.
|
|
159
255
|
*/
|
|
160
256
|
async resolve(ctx, agentId) {
|
|
257
|
+
return await this.#admit(async () => await this.#resolve(ctx, agentId));
|
|
258
|
+
}
|
|
259
|
+
/** Resolve one agent while its owning public operation is already admitted. */
|
|
260
|
+
async #resolve(ctx, agentId) {
|
|
161
261
|
const existing = this.#agents.get(agentId);
|
|
162
262
|
if (existing !== undefined)
|
|
163
263
|
return existing;
|
|
@@ -165,23 +265,13 @@ export class AgentSystemLocal {
|
|
|
165
265
|
const resolved = this.#agents.get(agentId);
|
|
166
266
|
if (resolved !== undefined)
|
|
167
267
|
return resolved;
|
|
168
|
-
const config = await this
|
|
268
|
+
const config = await this.#config(lockCtx, agentId);
|
|
169
269
|
if (config === undefined) {
|
|
170
270
|
throw new Error(`Agent "${agentId}" has not been created.`);
|
|
171
271
|
}
|
|
172
272
|
const agent = await this.#instantiate(lockCtx, agentId, config);
|
|
173
273
|
if (agent === undefined)
|
|
174
274
|
throw new Error(`Agent "${agentId}" could not be built.`);
|
|
175
|
-
// Building an agent takes as long as its features do, and identity is not this
|
|
176
|
-
// collection's to hold still for that: another owner may have deleted the ID, or a
|
|
177
|
-
// creation this resolution overtook may have rolled it back. Handing back an agent
|
|
178
|
-
// for an identity that no longer exists strands its caller with a live object no
|
|
179
|
-
// restart would ever reproduce, so the answer is checked before it is given.
|
|
180
|
-
if ((await this.config(lockCtx, agentId)) === undefined) {
|
|
181
|
-
this.#agents.delete(agentId);
|
|
182
|
-
await agent.close();
|
|
183
|
-
throw new Error(`Agent "${agentId}" was deleted while it was being resolved.`);
|
|
184
|
-
}
|
|
185
275
|
return agent;
|
|
186
276
|
});
|
|
187
277
|
}
|
|
@@ -189,7 +279,7 @@ export class AgentSystemLocal {
|
|
|
189
279
|
* Build one agent and put it to work. Called with the agent's lock held, and only for an ID
|
|
190
280
|
* that has no live instance yet.
|
|
191
281
|
*/
|
|
192
|
-
async #instantiate(ctx, agentId, config, onlyIfActive = false) {
|
|
282
|
+
async #instantiate(ctx, agentId, config, onlyIfActive = false, start = true) {
|
|
193
283
|
const agentCtx = withAgentConfig(withAgentSystem(ctx, this.#ref), config);
|
|
194
284
|
const options = {
|
|
195
285
|
id: agentId,
|
|
@@ -205,13 +295,14 @@ export class AgentSystemLocal {
|
|
|
205
295
|
// resolves through — so the agent is loaded rather than created, and knows whether it
|
|
206
296
|
// has work left before anything asks it. Bringing a collection up asks only for the
|
|
207
297
|
// agents that do; anything else resolving an agent wants it whether it owes work or not.
|
|
208
|
-
const agent =
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if (agent === undefined)
|
|
298
|
+
const agent = await Agent.load(agentCtx, options);
|
|
299
|
+
if (onlyIfActive && !agent.active) {
|
|
300
|
+
await agent.close();
|
|
212
301
|
return undefined;
|
|
213
|
-
|
|
302
|
+
}
|
|
214
303
|
this.#agents.set(agentId, agent);
|
|
304
|
+
if (start)
|
|
305
|
+
agent.start();
|
|
215
306
|
return agent;
|
|
216
307
|
}
|
|
217
308
|
/**
|
|
@@ -228,15 +319,29 @@ export class AgentSystemLocal {
|
|
|
228
319
|
*/
|
|
229
320
|
async #start(ctx) {
|
|
230
321
|
const created = await this.#configs.list(ctx);
|
|
231
|
-
await Promise.
|
|
322
|
+
const results = await Promise.allSettled(created.map(async ({ key: agentId, value }) => {
|
|
232
323
|
if (!Value.Check(agentConfigSchema, value))
|
|
233
|
-
return;
|
|
234
|
-
await this.#lockFor(agentId).runInLock(ctx, async (lockCtx) => {
|
|
324
|
+
return undefined;
|
|
325
|
+
return await this.#lockFor(agentId).runInLock(ctx, async (lockCtx) => {
|
|
235
326
|
if (this.#agents.has(agentId))
|
|
236
|
-
return;
|
|
237
|
-
await this.#instantiate(lockCtx, agentId, value, true);
|
|
327
|
+
return undefined;
|
|
328
|
+
return await this.#instantiate(lockCtx, agentId, value, true, false);
|
|
238
329
|
});
|
|
239
330
|
}));
|
|
331
|
+
throwFirstStartFailure(results);
|
|
332
|
+
return results.flatMap((result) => result.status === "fulfilled" && result.value !== undefined ? [result.value] : []);
|
|
333
|
+
}
|
|
334
|
+
/** Initialize every feature before any active agent is restored or started. */
|
|
335
|
+
async #beforeStart(ctx) {
|
|
336
|
+
const startCtx = withAgentSystem(ctx, this.#ref);
|
|
337
|
+
const results = await Promise.allSettled(this.#features.map(async (feature) => await feature.beforeStart?.(startCtx, this.#ref)));
|
|
338
|
+
throwFirstStartFailure(results);
|
|
339
|
+
}
|
|
340
|
+
/** Notify every feature after all active agents have been restored and started. */
|
|
341
|
+
async #afterStart(ctx) {
|
|
342
|
+
const startCtx = withAgentSystem(ctx, this.#ref);
|
|
343
|
+
const results = await Promise.allSettled(this.#features.map(async (feature) => await feature.afterStart?.(startCtx, this.#ref)));
|
|
344
|
+
throwFirstStartFailure(results);
|
|
240
345
|
}
|
|
241
346
|
/** The durable store for one agent, created once and reused for the life of the collection. */
|
|
242
347
|
#persistenceFor(agentId) {
|
|
@@ -262,21 +367,35 @@ export class AgentSystemLocal {
|
|
|
262
367
|
}
|
|
263
368
|
/** Queue a steered message for an agent. */
|
|
264
369
|
async steer(ctx, agentId, message, options) {
|
|
265
|
-
|
|
266
|
-
|
|
370
|
+
await this.#admit(async () => {
|
|
371
|
+
const agent = await this.#resolve(ctx, agentId);
|
|
372
|
+
await agent.steer(ctx, message, options);
|
|
373
|
+
});
|
|
267
374
|
}
|
|
268
375
|
/** Queue a message for an agent. */
|
|
269
376
|
async send(ctx, agentId, message, options) {
|
|
270
|
-
|
|
271
|
-
|
|
377
|
+
await this.#admit(async () => {
|
|
378
|
+
const agent = await this.#resolve(ctx, agentId);
|
|
379
|
+
await agent.send(ctx, message, options);
|
|
380
|
+
});
|
|
272
381
|
}
|
|
273
382
|
/** Cancel an agent's active turn, leaving its queued messages durable for the next one. */
|
|
274
383
|
async abort(ctx, agentId, options) {
|
|
275
|
-
await (
|
|
384
|
+
await this.#admit(async () => {
|
|
385
|
+
await (await this.#resolve(ctx, agentId)).abort(ctx, options);
|
|
386
|
+
});
|
|
276
387
|
}
|
|
277
388
|
/** Ask an agent for its conversation to be replaced by the provider's summary of it. */
|
|
278
389
|
async compact(ctx, agentId, options) {
|
|
279
|
-
await (
|
|
390
|
+
await this.#admit(async () => {
|
|
391
|
+
await (await this.#resolve(ctx, agentId)).compact(ctx, options);
|
|
392
|
+
});
|
|
280
393
|
}
|
|
281
394
|
}
|
|
395
|
+
/** Start every feature even when one fails, then surface the first failure in feature order. */
|
|
396
|
+
function throwFirstStartFailure(results) {
|
|
397
|
+
const failure = results.find((result) => result.status === "rejected");
|
|
398
|
+
if (failure !== undefined)
|
|
399
|
+
throw failure.reason;
|
|
400
|
+
}
|
|
282
401
|
//# sourceMappingURL=AgentSystemLocal.js.map
|