@memberjunction/ai-agents 5.38.0 → 5.40.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/AgentRunner.d.ts +23 -0
- package/dist/AgentRunner.d.ts.map +1 -1
- package/dist/AgentRunner.js +81 -26
- package/dist/AgentRunner.js.map +1 -1
- package/dist/ArtifactToolManager.d.ts +13 -0
- package/dist/ArtifactToolManager.d.ts.map +1 -1
- package/dist/ArtifactToolManager.js +60 -1
- package/dist/ArtifactToolManager.js.map +1 -1
- package/dist/agent-run-watchdog.d.ts +109 -0
- package/dist/agent-run-watchdog.d.ts.map +1 -0
- package/dist/agent-run-watchdog.js +278 -0
- package/dist/agent-run-watchdog.js.map +1 -0
- package/dist/agent-types/loop-agent-prompt-params.d.ts +13 -0
- package/dist/agent-types/loop-agent-prompt-params.d.ts.map +1 -1
- package/dist/agent-types/loop-agent-prompt-params.js +3 -1
- package/dist/agent-types/loop-agent-prompt-params.js.map +1 -1
- package/dist/agent-types/loop-agent-response-type.d.ts +12 -3
- package/dist/agent-types/loop-agent-response-type.d.ts.map +1 -1
- package/dist/agent-types/loop-agent-response-type.js.map +1 -1
- package/dist/agent-types/loop-agent-type.d.ts.map +1 -1
- package/dist/agent-types/loop-agent-type.js +41 -2
- package/dist/agent-types/loop-agent-type.js.map +1 -1
- package/dist/artifact-tools/DataSnapshotToolLibrary.js +2 -2
- package/dist/artifact-tools/JSONToolLibrary.d.ts.map +1 -1
- package/dist/artifact-tools/JSONToolLibrary.js +7 -1
- package/dist/artifact-tools/JSONToolLibrary.js.map +1 -1
- package/dist/base-agent.d.ts +69 -1
- package/dist/base-agent.d.ts.map +1 -1
- package/dist/base-agent.js +294 -24
- package/dist/base-agent.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/pipeline/coerce.d.ts +43 -0
- package/dist/pipeline/coerce.d.ts.map +1 -0
- package/dist/pipeline/coerce.js +92 -0
- package/dist/pipeline/coerce.js.map +1 -0
- package/dist/pipeline/index.d.ts +20 -0
- package/dist/pipeline/index.d.ts.map +1 -0
- package/dist/pipeline/index.js +20 -0
- package/dist/pipeline/index.js.map +1 -0
- package/dist/pipeline/jsonpath-eval.d.ts +29 -0
- package/dist/pipeline/jsonpath-eval.d.ts.map +1 -0
- package/dist/pipeline/jsonpath-eval.js +144 -0
- package/dist/pipeline/jsonpath-eval.js.map +1 -0
- package/dist/pipeline/operators.d.ts +15 -0
- package/dist/pipeline/operators.d.ts.map +1 -0
- package/dist/pipeline/operators.js +332 -0
- package/dist/pipeline/operators.js.map +1 -0
- package/dist/pipeline/path.d.ts +16 -0
- package/dist/pipeline/path.d.ts.map +1 -0
- package/dist/pipeline/path.js +34 -0
- package/dist/pipeline/path.js.map +1 -0
- package/dist/pipeline/pipeline-docs.d.ts +6 -0
- package/dist/pipeline/pipeline-docs.d.ts.map +1 -0
- package/dist/pipeline/pipeline-docs.js +137 -0
- package/dist/pipeline/pipeline-docs.js.map +1 -0
- package/dist/pipeline/pipeline-executor.d.ts +72 -0
- package/dist/pipeline/pipeline-executor.d.ts.map +1 -0
- package/dist/pipeline/pipeline-executor.js +326 -0
- package/dist/pipeline/pipeline-executor.js.map +1 -0
- package/dist/pipeline/pipeline-registry.d.ts +24 -0
- package/dist/pipeline/pipeline-registry.d.ts.map +1 -0
- package/dist/pipeline/pipeline-registry.js +28 -0
- package/dist/pipeline/pipeline-registry.js.map +1 -0
- package/dist/pipeline/pipeline.types.d.ts +89 -0
- package/dist/pipeline/pipeline.types.d.ts.map +1 -0
- package/dist/pipeline/pipeline.types.js +13 -0
- package/dist/pipeline/pipeline.types.js.map +1 -0
- package/dist/pipeline/predicate.d.ts +23 -0
- package/dist/pipeline/predicate.d.ts.map +1 -0
- package/dist/pipeline/predicate.js +306 -0
- package/dist/pipeline/predicate.js.map +1 -0
- package/dist/pipeline/providers/action-provider.d.ts +21 -0
- package/dist/pipeline/providers/action-provider.d.ts.map +1 -0
- package/dist/pipeline/providers/action-provider.js +24 -0
- package/dist/pipeline/providers/action-provider.js.map +1 -0
- package/dist/pipeline/providers/artifact-tool-provider.d.ts +20 -0
- package/dist/pipeline/providers/artifact-tool-provider.d.ts.map +1 -0
- package/dist/pipeline/providers/artifact-tool-provider.js +27 -0
- package/dist/pipeline/providers/artifact-tool-provider.js.map +1 -0
- package/dist/pipeline/providers/serialize.d.ts +23 -0
- package/dist/pipeline/providers/serialize.d.ts.map +1 -0
- package/dist/pipeline/providers/serialize.js +99 -0
- package/dist/pipeline/providers/serialize.js.map +1 -0
- package/dist/pipeline/template.d.ts +18 -0
- package/dist/pipeline/template.d.ts.map +1 -0
- package/dist/pipeline/template.js +47 -0
- package/dist/pipeline/template.js.map +1 -0
- package/package.json +19 -17
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { BaseSingleton, IShutdownable } from '@memberjunction/global';
|
|
2
|
+
import { DatabaseProviderBase, UserInfo } from '@memberjunction/core';
|
|
3
|
+
/**
|
|
4
|
+
* Tuning for the agent-run watchdog. All durations are in their named units. Defaults are
|
|
5
|
+
* deliberately conservative: the stale threshold is many heartbeat intervals wide, so a
|
|
6
|
+
* momentarily-slow-but-alive process is never mistaken for a dead one.
|
|
7
|
+
*/
|
|
8
|
+
export interface AgentRunWatchdogConfig {
|
|
9
|
+
/** How often the owning process stamps a liveness heartbeat for its in-flight runs. */
|
|
10
|
+
heartbeatIntervalMs: number;
|
|
11
|
+
/** How often this process re-scans for orphaned runs (mid-life, without waiting for a restart). */
|
|
12
|
+
sweepIntervalMs: number;
|
|
13
|
+
/** A Running run whose heartbeat is older than this (or NULL with an older StartedAt) is force-failed. */
|
|
14
|
+
staleThresholdMinutes: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Process-wide watchdog that guarantees no `AIAgentRun` is left stuck in `Status='Running'`
|
|
18
|
+
* after the owning process dies (restart, crash, OOM) or its terminal-state write fails.
|
|
19
|
+
*
|
|
20
|
+
* Three cooperating mechanisms, all anchored to the **database clock** so they remain correct
|
|
21
|
+
* across multiple MJAPI instances behind a load balancer (no instance trusts its own clock):
|
|
22
|
+
*
|
|
23
|
+
* 1. **Heartbeat** — while a run is in flight, the owning process stamps `LastHeartbeatAt`
|
|
24
|
+
* every {@link AgentRunWatchdogConfig.heartbeatIntervalMs} via {@link SP_HEARTBEAT}.
|
|
25
|
+
* 2. **Sweep** — {@link SweepOrphanedRuns} force-fails any `Running` run whose heartbeat has
|
|
26
|
+
* gone stale, via {@link SP_SWEEP} (one atomic, set-based `UPDATE … WHERE Status='Running'
|
|
27
|
+
* AND <stale>` inside the proc — so concurrent sweeps from multiple instances are harmless
|
|
28
|
+
* and a run another instance is still heart-beating can't be caught mid-sweep). Run once on
|
|
29
|
+
* boot (closes restart-orphans) and on a timer (closes mid-life orphans).
|
|
30
|
+
* 3. **Graceful shutdown** — on SIGTERM/SIGINT (via {@link ShutdownRegistry}) this process marks
|
|
31
|
+
* the runs *it* owns `Cancelled` via {@link SP_CANCEL}, closing the deploy case instantly.
|
|
32
|
+
*
|
|
33
|
+
* Every proc filters `Status='Running'` only — `Paused` and `AwaitingFeedback` are
|
|
34
|
+
* legitimately-not-progressing states and are never touched. The watchdog accesses the DB only
|
|
35
|
+
* through these stored procedures (writes) and the base view (reads) — never the base table —
|
|
36
|
+
* so it works as the regular runtime DB user, not just the elevated CodeGen user. Proc calls are
|
|
37
|
+
* built through the provider's {@link Dialect.ProcedureCallSyntax}, so they run on SQL Server and
|
|
38
|
+
* PostgreSQL unchanged.
|
|
39
|
+
*/
|
|
40
|
+
export declare class AgentRunWatchdog extends BaseSingleton<AgentRunWatchdog> implements IShutdownable {
|
|
41
|
+
private _config;
|
|
42
|
+
private _trackedRuns;
|
|
43
|
+
private _provider;
|
|
44
|
+
private _contextUser;
|
|
45
|
+
private _heartbeatTimer;
|
|
46
|
+
private _sweepTimer;
|
|
47
|
+
private _registered;
|
|
48
|
+
protected constructor();
|
|
49
|
+
static get Instance(): AgentRunWatchdog;
|
|
50
|
+
readonly ShutdownName = "AgentRunWatchdog";
|
|
51
|
+
/** Overrides the default tuning. Safe to call before or after runs start tracking. */
|
|
52
|
+
Configure(config: Partial<AgentRunWatchdogConfig>): void;
|
|
53
|
+
/**
|
|
54
|
+
* Begin guarding a run. The owning agent calls this immediately after the run row is saved
|
|
55
|
+
* (so it has a stable ID). Idempotent per run ID. Capturing the provider/user lets the
|
|
56
|
+
* central heartbeat + shutdown statements run without each run re-supplying them.
|
|
57
|
+
*/
|
|
58
|
+
Track(runID: string, provider: DatabaseProviderBase, contextUser: UserInfo): void;
|
|
59
|
+
/** Stop guarding a run. Optional — the periodic prune drops terminal runs automatically — but
|
|
60
|
+
* callers may invoke it for prompt cleanup right after writing a terminal state. */
|
|
61
|
+
Untrack(runID: string): void;
|
|
62
|
+
/** Number of runs this process is currently guarding (primarily for tests/diagnostics). */
|
|
63
|
+
get TrackedCount(): number;
|
|
64
|
+
/**
|
|
65
|
+
* IShutdownable: on graceful shutdown, mark every run this process still owns as `Cancelled`
|
|
66
|
+
* (via {@link SP_CANCEL}) so it doesn't linger as a phantom `Running` row until the next sweep.
|
|
67
|
+
* Idempotent and never throws — a failed cancel just falls through to the staleness sweep later.
|
|
68
|
+
*/
|
|
69
|
+
Shutdown(): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Force-fail any `Running` agent run whose liveness heartbeat has gone stale (or that never
|
|
72
|
+
* beat and was started long ago), via the dedicated {@link SP_SWEEP} proc. The proc does the
|
|
73
|
+
* whole thing as one atomic, set-based `UPDATE` and returns the count, so it's safe to run on
|
|
74
|
+
* every boot and on a timer, on every instance: the predicate is staleness-based (never "fail
|
|
75
|
+
* all Running") and re-evaluated at write time, so it cannot touch a run a healthy instance is
|
|
76
|
+
* still heart-beating. Returns the number of rows failed.
|
|
77
|
+
*/
|
|
78
|
+
static SweepOrphanedRuns(provider: DatabaseProviderBase, contextUser: UserInfo, config?: Partial<AgentRunWatchdogConfig>): Promise<number>;
|
|
79
|
+
/** Start the heartbeat + sweep timers and register for graceful shutdown, exactly once. */
|
|
80
|
+
private ensureStarted;
|
|
81
|
+
private stopTimers;
|
|
82
|
+
/** Stamp LastHeartbeatAt for every run this process is guarding, via {@link SP_HEARTBEAT}.
|
|
83
|
+
* Never throws (runs on a timer); a per-run failure is logged and the rest still beat. */
|
|
84
|
+
private heartbeat;
|
|
85
|
+
/** Periodic tick: force-fail orphans, then prune any terminal runs out of the tracked set. */
|
|
86
|
+
private periodicSweep;
|
|
87
|
+
/**
|
|
88
|
+
* Drop any tracked run that is no longer `Running` so the in-memory set can't grow unbounded
|
|
89
|
+
* over a long-lived process. A single SELECT (against the base VIEW — the runtime user has no
|
|
90
|
+
* table access) of the still-running subset; the rest are pruned.
|
|
91
|
+
*/
|
|
92
|
+
private pruneTerminalRuns;
|
|
93
|
+
/** Validated, lowercased UUIDs this process is guarding (for per-run proc calls). */
|
|
94
|
+
private trackedIds;
|
|
95
|
+
/**
|
|
96
|
+
* Build a single batched SQL string of one proc call per id, newline-separated, so a set of
|
|
97
|
+
* per-run writes goes to the DB as one round-trip instead of N. Each id is already a validated
|
|
98
|
+
* UUID and is quoted as a string literal before interpolation. Runs unchanged on SQL Server and
|
|
99
|
+
* PostgreSQL because the call form comes from {@link Dialect.ProcedureCallSyntax}.
|
|
100
|
+
*/
|
|
101
|
+
private static buildProcBatch;
|
|
102
|
+
/** Comma-joined, quoted, validated UUID list for an `ID IN (...)` clause, or '' if none. */
|
|
103
|
+
private trackedIdList;
|
|
104
|
+
/** Schema that owns the AIAgentRun objects (for qualifying proc calls), or null if unresolved. */
|
|
105
|
+
private static schemaOf;
|
|
106
|
+
/** Fully-qualified, dialect-quoted base VIEW for AIAgentRun reads, or null if unresolved. */
|
|
107
|
+
private static viewOf;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=agent-run-watchdog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-run-watchdog.d.ts","sourceRoot":"","sources":["../src/agent-run-watchdog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAoB,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACxF,OAAO,EAAE,oBAAoB,EAAE,QAAQ,EAAuB,MAAM,sBAAsB,CAAC;AAM3F;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACnC,uFAAuF;IACvF,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mGAAmG;IACnG,eAAe,EAAE,MAAM,CAAC;IACxB,0GAA0G;IAC1G,qBAAqB,EAAE,MAAM,CAAC;CACjC;AAsBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,gBAAiB,SAAQ,aAAa,CAAC,gBAAgB,CAAE,YAAW,aAAa;IAC1F,OAAO,CAAC,OAAO,CAA0C;IACzD,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,SAAS,CAAqC;IACtD,OAAO,CAAC,YAAY,CAAyB;IAC7C,OAAO,CAAC,eAAe,CAA+C;IACtE,OAAO,CAAC,WAAW,CAA+C;IAClE,OAAO,CAAC,WAAW,CAAS;IAE5B,SAAS;IAIT,WAAkB,QAAQ,IAAI,gBAAgB,CAE7C;IAED,SAAgB,YAAY,sBAAsB;IAElD,sFAAsF;IAC/E,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,sBAAsB,CAAC,GAAG,IAAI;IAI/D;;;;OAIG;IACI,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,oBAAoB,EAAE,WAAW,EAAE,QAAQ,GAAG,IAAI;IAUxF;yFACqF;IAC9E,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAMnC,2FAA2F;IAC3F,IAAW,YAAY,IAAI,MAAM,CAEhC;IAED;;;;OAIG;IACU,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAqBtC;;;;;;;OAOG;WACiB,iBAAiB,CACjC,QAAQ,EAAE,oBAAoB,EAC9B,WAAW,EAAE,QAAQ,EACrB,MAAM,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,GACzC,OAAO,CAAC,MAAM,CAAC;IA0BlB,2FAA2F;IAC3F,OAAO,CAAC,aAAa;IAuBrB,OAAO,CAAC,UAAU;IAWlB;+FAC2F;YAC7E,SAAS;IAsBvB,8FAA8F;YAChF,aAAa;IAQ3B;;;;OAIG;YACW,iBAAiB;IAiC/B,qFAAqF;IACrF,OAAO,CAAC,UAAU;IAIlB;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,cAAc;IAI7B,4FAA4F;IAC5F,OAAO,CAAC,aAAa;IAIrB,kGAAkG;IAClG,OAAO,CAAC,MAAM,CAAC,QAAQ;IAKvB,6FAA6F;IAC7F,OAAO,CAAC,MAAM,CAAC,MAAM;CAQxB"}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { BaseSingleton, ShutdownRegistry } from '@memberjunction/global';
|
|
2
|
+
import { LogError, LogStatus } from '@memberjunction/core';
|
|
3
|
+
const DEFAULT_CONFIG = {
|
|
4
|
+
heartbeatIntervalMs: 30_000,
|
|
5
|
+
sweepIntervalMs: 5 * 60_000,
|
|
6
|
+
staleThresholdMinutes: 5,
|
|
7
|
+
};
|
|
8
|
+
/** Matches a canonical UUID. Tracked IDs come from provider-generated PKs, but we validate
|
|
9
|
+
* before ever interpolating into SQL so a proc argument can never become an injection vector. */
|
|
10
|
+
const UUID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
11
|
+
/** The one MJ entity whose runs this watchdog guards. */
|
|
12
|
+
const AGENT_RUN_ENTITY = 'MJ: AI Agent Runs';
|
|
13
|
+
/** Dedicated maintenance stored procedures (see the watchdog sprocs migration). The runtime DB
|
|
14
|
+
* user has EXECUTE on these + SELECT on the base view, but no direct table access — so all of
|
|
15
|
+
* the watchdog's writes go through these procs and its reads go through the view. */
|
|
16
|
+
const SP_HEARTBEAT = 'spStampAIAgentRunHeartbeat';
|
|
17
|
+
const SP_SWEEP = 'spSweepStaleAIAgentRuns';
|
|
18
|
+
const SP_CANCEL = 'spCancelAIAgentRun';
|
|
19
|
+
/**
|
|
20
|
+
* Process-wide watchdog that guarantees no `AIAgentRun` is left stuck in `Status='Running'`
|
|
21
|
+
* after the owning process dies (restart, crash, OOM) or its terminal-state write fails.
|
|
22
|
+
*
|
|
23
|
+
* Three cooperating mechanisms, all anchored to the **database clock** so they remain correct
|
|
24
|
+
* across multiple MJAPI instances behind a load balancer (no instance trusts its own clock):
|
|
25
|
+
*
|
|
26
|
+
* 1. **Heartbeat** — while a run is in flight, the owning process stamps `LastHeartbeatAt`
|
|
27
|
+
* every {@link AgentRunWatchdogConfig.heartbeatIntervalMs} via {@link SP_HEARTBEAT}.
|
|
28
|
+
* 2. **Sweep** — {@link SweepOrphanedRuns} force-fails any `Running` run whose heartbeat has
|
|
29
|
+
* gone stale, via {@link SP_SWEEP} (one atomic, set-based `UPDATE … WHERE Status='Running'
|
|
30
|
+
* AND <stale>` inside the proc — so concurrent sweeps from multiple instances are harmless
|
|
31
|
+
* and a run another instance is still heart-beating can't be caught mid-sweep). Run once on
|
|
32
|
+
* boot (closes restart-orphans) and on a timer (closes mid-life orphans).
|
|
33
|
+
* 3. **Graceful shutdown** — on SIGTERM/SIGINT (via {@link ShutdownRegistry}) this process marks
|
|
34
|
+
* the runs *it* owns `Cancelled` via {@link SP_CANCEL}, closing the deploy case instantly.
|
|
35
|
+
*
|
|
36
|
+
* Every proc filters `Status='Running'` only — `Paused` and `AwaitingFeedback` are
|
|
37
|
+
* legitimately-not-progressing states and are never touched. The watchdog accesses the DB only
|
|
38
|
+
* through these stored procedures (writes) and the base view (reads) — never the base table —
|
|
39
|
+
* so it works as the regular runtime DB user, not just the elevated CodeGen user. Proc calls are
|
|
40
|
+
* built through the provider's {@link Dialect.ProcedureCallSyntax}, so they run on SQL Server and
|
|
41
|
+
* PostgreSQL unchanged.
|
|
42
|
+
*/
|
|
43
|
+
export class AgentRunWatchdog extends BaseSingleton {
|
|
44
|
+
constructor() {
|
|
45
|
+
super();
|
|
46
|
+
this._config = DEFAULT_CONFIG;
|
|
47
|
+
this._trackedRuns = new Set();
|
|
48
|
+
this._provider = null;
|
|
49
|
+
this._contextUser = null;
|
|
50
|
+
this._heartbeatTimer = null;
|
|
51
|
+
this._sweepTimer = null;
|
|
52
|
+
this._registered = false;
|
|
53
|
+
this.ShutdownName = 'AgentRunWatchdog';
|
|
54
|
+
}
|
|
55
|
+
static get Instance() {
|
|
56
|
+
return super.getInstance();
|
|
57
|
+
}
|
|
58
|
+
/** Overrides the default tuning. Safe to call before or after runs start tracking. */
|
|
59
|
+
Configure(config) {
|
|
60
|
+
this._config = { ...this._config, ...config };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Begin guarding a run. The owning agent calls this immediately after the run row is saved
|
|
64
|
+
* (so it has a stable ID). Idempotent per run ID. Capturing the provider/user lets the
|
|
65
|
+
* central heartbeat + shutdown statements run without each run re-supplying them.
|
|
66
|
+
*/
|
|
67
|
+
Track(runID, provider, contextUser) {
|
|
68
|
+
if (!runID || !UUID_RE.test(runID)) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
this._provider = provider;
|
|
72
|
+
this._contextUser = contextUser;
|
|
73
|
+
this._trackedRuns.add(runID.toLowerCase());
|
|
74
|
+
this.ensureStarted();
|
|
75
|
+
}
|
|
76
|
+
/** Stop guarding a run. Optional — the periodic prune drops terminal runs automatically — but
|
|
77
|
+
* callers may invoke it for prompt cleanup right after writing a terminal state. */
|
|
78
|
+
Untrack(runID) {
|
|
79
|
+
if (runID) {
|
|
80
|
+
this._trackedRuns.delete(runID.toLowerCase());
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/** Number of runs this process is currently guarding (primarily for tests/diagnostics). */
|
|
84
|
+
get TrackedCount() {
|
|
85
|
+
return this._trackedRuns.size;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* IShutdownable: on graceful shutdown, mark every run this process still owns as `Cancelled`
|
|
89
|
+
* (via {@link SP_CANCEL}) so it doesn't linger as a phantom `Running` row until the next sweep.
|
|
90
|
+
* Idempotent and never throws — a failed cancel just falls through to the staleness sweep later.
|
|
91
|
+
*/
|
|
92
|
+
async Shutdown() {
|
|
93
|
+
this.stopTimers();
|
|
94
|
+
const ids = this.trackedIds();
|
|
95
|
+
this._trackedRuns.clear();
|
|
96
|
+
if (!ids.length || !this._provider) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const schema = AgentRunWatchdog.schemaOf(this._provider);
|
|
100
|
+
if (!schema) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const d = this._provider.Dialect;
|
|
104
|
+
// One batched round-trip (one EXEC per id, newline-separated) rather than a call per run.
|
|
105
|
+
const sql = AgentRunWatchdog.buildProcBatch(d, schema, SP_CANCEL, ids);
|
|
106
|
+
try {
|
|
107
|
+
await this._provider.ExecuteSQL(sql, undefined, { isMutation: true, description: 'AgentRunWatchdog graceful-shutdown cancel' }, this._contextUser ?? undefined);
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
LogError(`AgentRunWatchdog.Shutdown failed to cancel runs [${ids.join(', ')}]: ${err instanceof Error ? err.message : String(err)}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Force-fail any `Running` agent run whose liveness heartbeat has gone stale (or that never
|
|
115
|
+
* beat and was started long ago), via the dedicated {@link SP_SWEEP} proc. The proc does the
|
|
116
|
+
* whole thing as one atomic, set-based `UPDATE` and returns the count, so it's safe to run on
|
|
117
|
+
* every boot and on a timer, on every instance: the predicate is staleness-based (never "fail
|
|
118
|
+
* all Running") and re-evaluated at write time, so it cannot touch a run a healthy instance is
|
|
119
|
+
* still heart-beating. Returns the number of rows failed.
|
|
120
|
+
*/
|
|
121
|
+
static async SweepOrphanedRuns(provider, contextUser, config) {
|
|
122
|
+
const threshold = config?.staleThresholdMinutes ?? DEFAULT_CONFIG.staleThresholdMinutes;
|
|
123
|
+
const schema = AgentRunWatchdog.schemaOf(provider);
|
|
124
|
+
if (!schema) {
|
|
125
|
+
LogError('AgentRunWatchdog.SweepOrphanedRuns: could not resolve the AIAgentRun schema from metadata; skipping sweep');
|
|
126
|
+
return 0;
|
|
127
|
+
}
|
|
128
|
+
const d = provider.Dialect;
|
|
129
|
+
try {
|
|
130
|
+
// The proc runs the atomic UPDATE on the DB clock and returns RunsFailed.
|
|
131
|
+
const sql = d.ProcedureCallSyntax(schema, SP_SWEEP, [String(Math.trunc(threshold))]) + ';';
|
|
132
|
+
const rows = await provider.ExecuteSQL(sql, undefined, { isMutation: true, description: 'AgentRunWatchdog stale-run sweep' }, contextUser);
|
|
133
|
+
const failCount = (Array.isArray(rows) && rows[0] && typeof rows[0].RunsFailed === 'number') ? rows[0].RunsFailed : 0;
|
|
134
|
+
if (failCount > 0) {
|
|
135
|
+
LogStatus(`[AgentRunWatchdog] Swept ${failCount} orphaned agent run(s) -> Failed (no heartbeat for >${threshold}m)`);
|
|
136
|
+
}
|
|
137
|
+
return failCount;
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
LogError(`AgentRunWatchdog.SweepOrphanedRuns failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
141
|
+
return 0;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// ----- internals -------------------------------------------------------------------------
|
|
145
|
+
/** Start the heartbeat + sweep timers and register for graceful shutdown, exactly once. */
|
|
146
|
+
ensureStarted() {
|
|
147
|
+
if (!this._registered) {
|
|
148
|
+
ShutdownRegistry.Instance.Register(this);
|
|
149
|
+
this._registered = true;
|
|
150
|
+
}
|
|
151
|
+
if (!this._heartbeatTimer) {
|
|
152
|
+
this._heartbeatTimer = setInterval(() => void this.heartbeat(), this._config.heartbeatIntervalMs);
|
|
153
|
+
// Don't keep the event loop alive solely for the watchdog.
|
|
154
|
+
this._heartbeatTimer.unref?.();
|
|
155
|
+
}
|
|
156
|
+
if (!this._sweepTimer) {
|
|
157
|
+
// The periodic sweep is an always-on in-process timer rather than an MJ Scheduled Job
|
|
158
|
+
// (MJ: Scheduled Jobs / SchedulingEngine) on purpose. This is a reliability floor for
|
|
159
|
+
// orphaned runs, and ScheduledJobsService is gated by `scheduledJobs.enabled` in config
|
|
160
|
+
// — moving the sweep there would silently disable it in any deployment that runs with
|
|
161
|
+
// scheduled jobs off, exactly where the safety net still needs to work. A Scheduled Job
|
|
162
|
+
// could be added on top later as an observability/audit layer (it would call the same
|
|
163
|
+
// idempotent SweepOrphanedRuns), but it must not be the only thing that runs it.
|
|
164
|
+
this._sweepTimer = setInterval(() => void this.periodicSweep(), this._config.sweepIntervalMs);
|
|
165
|
+
this._sweepTimer.unref?.();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
stopTimers() {
|
|
169
|
+
if (this._heartbeatTimer) {
|
|
170
|
+
clearInterval(this._heartbeatTimer);
|
|
171
|
+
this._heartbeatTimer = null;
|
|
172
|
+
}
|
|
173
|
+
if (this._sweepTimer) {
|
|
174
|
+
clearInterval(this._sweepTimer);
|
|
175
|
+
this._sweepTimer = null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/** Stamp LastHeartbeatAt for every run this process is guarding, via {@link SP_HEARTBEAT}.
|
|
179
|
+
* Never throws (runs on a timer); a per-run failure is logged and the rest still beat. */
|
|
180
|
+
async heartbeat() {
|
|
181
|
+
if (!this._provider) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const ids = this.trackedIds();
|
|
185
|
+
if (!ids.length) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const schema = AgentRunWatchdog.schemaOf(this._provider);
|
|
189
|
+
if (!schema) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const d = this._provider.Dialect;
|
|
193
|
+
// One batched round-trip (one EXEC per id, newline-separated) rather than a call per run.
|
|
194
|
+
const sql = AgentRunWatchdog.buildProcBatch(d, schema, SP_HEARTBEAT, ids);
|
|
195
|
+
try {
|
|
196
|
+
await this._provider.ExecuteSQL(sql, undefined, { isMutation: true, ignoreLogging: true, description: 'AgentRunWatchdog heartbeat' }, this._contextUser ?? undefined);
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
LogError(`AgentRunWatchdog heartbeat failed for runs [${ids.join(', ')}]: ${err instanceof Error ? err.message : String(err)}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/** Periodic tick: force-fail orphans, then prune any terminal runs out of the tracked set. */
|
|
203
|
+
async periodicSweep() {
|
|
204
|
+
if (!this._provider || !this._contextUser) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
await AgentRunWatchdog.SweepOrphanedRuns(this._provider, this._contextUser, this._config);
|
|
208
|
+
await this.pruneTerminalRuns();
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Drop any tracked run that is no longer `Running` so the in-memory set can't grow unbounded
|
|
212
|
+
* over a long-lived process. A single SELECT (against the base VIEW — the runtime user has no
|
|
213
|
+
* table access) of the still-running subset; the rest are pruned.
|
|
214
|
+
*/
|
|
215
|
+
async pruneTerminalRuns() {
|
|
216
|
+
const ids = this.trackedIdList();
|
|
217
|
+
if (!ids || !this._provider) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const view = AgentRunWatchdog.viewOf(this._provider);
|
|
221
|
+
if (!view) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const d = this._provider.Dialect;
|
|
225
|
+
try {
|
|
226
|
+
const rows = await this._provider.ExecuteSQL(`SELECT ${d.QuoteIdentifier('ID')} FROM ${view} ` +
|
|
227
|
+
`WHERE ${d.QuoteIdentifier('Status')} = 'Running' AND ${d.QuoteIdentifier('ID')} IN (${ids});`, undefined, { ignoreLogging: true, description: 'AgentRunWatchdog tracked-set prune' }, this._contextUser ?? undefined);
|
|
228
|
+
const stillRunning = new Set();
|
|
229
|
+
if (Array.isArray(rows)) {
|
|
230
|
+
for (const row of rows) {
|
|
231
|
+
if (typeof row?.ID === 'string') {
|
|
232
|
+
stillRunning.add(row.ID.toLowerCase());
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
for (const tracked of Array.from(this._trackedRuns)) {
|
|
237
|
+
if (!stillRunning.has(tracked)) {
|
|
238
|
+
this._trackedRuns.delete(tracked);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
catch (err) {
|
|
243
|
+
LogError(`AgentRunWatchdog prune failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/** Validated, lowercased UUIDs this process is guarding (for per-run proc calls). */
|
|
247
|
+
trackedIds() {
|
|
248
|
+
return Array.from(this._trackedRuns).filter(id => UUID_RE.test(id));
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Build a single batched SQL string of one proc call per id, newline-separated, so a set of
|
|
252
|
+
* per-run writes goes to the DB as one round-trip instead of N. Each id is already a validated
|
|
253
|
+
* UUID and is quoted as a string literal before interpolation. Runs unchanged on SQL Server and
|
|
254
|
+
* PostgreSQL because the call form comes from {@link Dialect.ProcedureCallSyntax}.
|
|
255
|
+
*/
|
|
256
|
+
static buildProcBatch(d, schema, proc, ids) {
|
|
257
|
+
return ids.map(id => d.ProcedureCallSyntax(schema, proc, [d.QuoteStringLiteral(id)]) + ';').join('\n');
|
|
258
|
+
}
|
|
259
|
+
/** Comma-joined, quoted, validated UUID list for an `ID IN (...)` clause, or '' if none. */
|
|
260
|
+
trackedIdList() {
|
|
261
|
+
return this.trackedIds().map(id => `'${id}'`).join(',');
|
|
262
|
+
}
|
|
263
|
+
/** Schema that owns the AIAgentRun objects (for qualifying proc calls), or null if unresolved. */
|
|
264
|
+
static schemaOf(provider) {
|
|
265
|
+
const entity = provider.EntityByName(AGENT_RUN_ENTITY);
|
|
266
|
+
return entity?.SchemaName || null;
|
|
267
|
+
}
|
|
268
|
+
/** Fully-qualified, dialect-quoted base VIEW for AIAgentRun reads, or null if unresolved. */
|
|
269
|
+
static viewOf(provider) {
|
|
270
|
+
const entity = provider.EntityByName(AGENT_RUN_ENTITY);
|
|
271
|
+
if (!entity || !entity.BaseView) {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
const d = provider.Dialect;
|
|
275
|
+
return entity.SchemaName ? d.QuoteSchema(entity.SchemaName, entity.BaseView) : d.QuoteIdentifier(entity.BaseView);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=agent-run-watchdog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-run-watchdog.js","sourceRoot":"","sources":["../src/agent-run-watchdog.ts"],"names":[],"mappings":"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"}
|
|
@@ -79,6 +79,12 @@ export interface ResponseTypeInclusionRules {
|
|
|
79
79
|
* @default true
|
|
80
80
|
*/
|
|
81
81
|
artifactToolCalls?: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Include the pipeline field in the response interface.
|
|
84
|
+
* Auto-aligns with includePipelineDocs unless explicitly set.
|
|
85
|
+
* @default true
|
|
86
|
+
*/
|
|
87
|
+
pipeline?: boolean;
|
|
82
88
|
}
|
|
83
89
|
/**
|
|
84
90
|
* Default values for ResponseTypeInclusionRules.
|
|
@@ -237,6 +243,13 @@ export interface LoopAgentTypePromptParams {
|
|
|
237
243
|
* @default true
|
|
238
244
|
*/
|
|
239
245
|
includeArtifactToolsDocs?: boolean;
|
|
246
|
+
/**
|
|
247
|
+
* Include tool-pipeline documentation in the prompt (the `_PIPELINE_TOOLS` block).
|
|
248
|
+
* Only emitted when at least one pipeline source — an Action or artifact tool — is available.
|
|
249
|
+
* Disable for agents that should never compose pipelines.
|
|
250
|
+
* @default true
|
|
251
|
+
*/
|
|
252
|
+
includePipelineDocs?: boolean;
|
|
240
253
|
/**
|
|
241
254
|
* Maximum number of sub-agents to include in prompt details.
|
|
242
255
|
* -1 = include all (default)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop-agent-prompt-params.d.ts","sourceRoot":"","sources":["../../src/agent-types/loop-agent-prompt-params.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,0BAA0B;IACvC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"loop-agent-prompt-params.d.ts","sourceRoot":"","sources":["../../src/agent-types/loop-agent-prompt-params.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,0BAA0B;IACvC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;GAGG;AACH,eAAO,MAAM,qCAAqC,EAAE,QAAQ,CAAC,0BAA0B,CAStF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,MAAM,WAAW,yBAAyB;IAGtC;;;;;;;;;OASG;IACH,6BAA6B,CAAC,EAAE,0BAA0B,CAAC;IAE3D;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B;;;;;;OAMG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAElC;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B;;;;;;;OAOG;IACH,2BAA2B,CAAC,EAAE,OAAO,CAAC;IAEtC;;;;;OAKG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAElC;;;;;;OAMG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC;;;;;;OAMG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAElC;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAEhC;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;;;;;OAMG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAEnC;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAI9B;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B;;;;;;;OAOG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;GAGG;AACH,eAAO,MAAM,gCAAgC,EAAE,QAAQ,CAAC,yBAAyB,CAgBhF,CAAC"}
|
|
@@ -30,7 +30,8 @@ export const DEFAULT_RESPONSE_TYPE_INCLUSION_RULES = {
|
|
|
30
30
|
forEach: true,
|
|
31
31
|
while: true,
|
|
32
32
|
scratchpad: true,
|
|
33
|
-
artifactToolCalls: true
|
|
33
|
+
artifactToolCalls: true,
|
|
34
|
+
pipeline: true
|
|
34
35
|
};
|
|
35
36
|
/**
|
|
36
37
|
* Default values for LoopAgentTypePromptParams.
|
|
@@ -49,6 +50,7 @@ export const DEFAULT_LOOP_AGENT_PROMPT_PARAMS = {
|
|
|
49
50
|
includeScratchpadDocs: true,
|
|
50
51
|
scratchpadMaxTasks: 50,
|
|
51
52
|
includeArtifactToolsDocs: true,
|
|
53
|
+
includePipelineDocs: true,
|
|
52
54
|
maxSubAgentsInPrompt: -1,
|
|
53
55
|
maxActionsInPrompt: -1
|
|
54
56
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop-agent-prompt-params.js","sourceRoot":"","sources":["../../src/agent-types/loop-agent-prompt-params.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;
|
|
1
|
+
{"version":3,"file":"loop-agent-prompt-params.js","sourceRoot":"","sources":["../../src/agent-types/loop-agent-prompt-params.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AA+EH;;;GAGG;AACH,MAAM,CAAC,MAAM,qCAAqC,GAAyC;IACvF,OAAO,EAAE,IAAI;IACb,aAAa,EAAE,IAAI;IACnB,QAAQ,EAAE,IAAI;IACd,OAAO,EAAE,IAAI;IACb,KAAK,EAAE,IAAI;IACX,UAAU,EAAE,IAAI;IAChB,iBAAiB,EAAE,IAAI;IACvB,QAAQ,EAAE,IAAI;CACjB,CAAC;AAuMF;;;GAGG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAwC;IACjF,6BAA6B,EAAE,EAAE,GAAG,qCAAqC,EAAE;IAC3E,kBAAkB,EAAE,IAAI;IACxB,gBAAgB,EAAE,IAAI;IACtB,uBAAuB,EAAE,IAAI;IAC7B,kBAAkB,EAAE,IAAI;IACxB,2BAA2B,EAAE,IAAI;IACjC,uBAAuB,EAAE,IAAI;IAC7B,sBAAsB,EAAE,IAAI;IAC5B,uBAAuB,EAAE,IAAI;IAC7B,qBAAqB,EAAE,IAAI;IAC3B,kBAAkB,EAAE,EAAE;IACtB,wBAAwB,EAAE,IAAI;IAC9B,mBAAmB,EAAE,IAAI;IACzB,oBAAoB,EAAE,CAAC,CAAC;IACxB,kBAAkB,EAAE,CAAC,CAAC;CACzB,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AgentPayloadChangeRequest, ForEachOperation, WhileOperation, AgentResponseForm, ActionableCommand, AutomaticCommand, AgentScratchpad } from "@memberjunction/ai-core-plus";
|
|
1
|
+
import { AgentPayloadChangeRequest, ForEachOperation, WhileOperation, AgentResponseForm, ActionableCommand, AutomaticCommand, AgentScratchpad, AgentPipelineRequest } from "@memberjunction/ai-core-plus";
|
|
2
2
|
import { ArtifactToolCall } from "../ArtifactToolManager.js";
|
|
3
3
|
export type { ForEachOperation, WhileOperation, ArtifactToolCall };
|
|
4
4
|
/**
|
|
@@ -62,9 +62,9 @@ export interface LoopAgentResponse<P = any> {
|
|
|
62
62
|
*/
|
|
63
63
|
nextStep?: {
|
|
64
64
|
/**
|
|
65
|
-
* Operation type: 'Actions' | 'ClientTools' | 'Sub-Agent' | 'Chat' | 'Retry' | 'ForEach' | 'While'
|
|
65
|
+
* Operation type: 'Actions' | 'ClientTools' | 'Sub-Agent' | 'Chat' | 'Retry' | 'ForEach' | 'While' | 'Pipeline'
|
|
66
66
|
*/
|
|
67
|
-
type: 'Actions' | 'ClientTools' | 'Sub-Agent' | 'Chat' | 'Retry' | 'ForEach' | 'While';
|
|
67
|
+
type: 'Actions' | 'ClientTools' | 'Sub-Agent' | 'Chat' | 'Retry' | 'ForEach' | 'While' | 'Pipeline';
|
|
68
68
|
/**
|
|
69
69
|
* Actions to execute (when type='Actions')
|
|
70
70
|
*/
|
|
@@ -72,6 +72,15 @@ export interface LoopAgentResponse<P = any> {
|
|
|
72
72
|
name: string;
|
|
73
73
|
params: Record<string, unknown>;
|
|
74
74
|
}>;
|
|
75
|
+
/**
|
|
76
|
+
* Tool pipeline to run server-side (when type='Pipeline'). Like client tools, it is a
|
|
77
|
+
* yield/await action: the agent cannot know the result until it executes, so the loop runs
|
|
78
|
+
* the pipeline inline, injects the final output, and forces one more turn. Only the final
|
|
79
|
+
* step's output enters the context window. Modeling it as a `nextStep.type` (rather than a
|
|
80
|
+
* top-level field) makes it structurally mutually exclusive with Actions/Chat/etc. — the LLM
|
|
81
|
+
* cannot accidentally request a pipeline AND another step in the same turn.
|
|
82
|
+
*/
|
|
83
|
+
pipeline?: AgentPipelineRequest;
|
|
75
84
|
/**
|
|
76
85
|
* Index of a compacted message to expand (when type='Retry').
|
|
77
86
|
* The message is restored to full content and the prompt re-runs.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop-agent-response-type.d.ts","sourceRoot":"","sources":["../../src/agent-types/loop-agent-response-type.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,cAAc,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"loop-agent-response-type.d.ts","sourceRoot":"","sources":["../../src/agent-types/loop-agent-response-type.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop-agent-response-type.js","sourceRoot":"","sources":["../../src/agent-types/loop-agent-response-type.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"loop-agent-response-type.js","sourceRoot":"","sources":["../../src/agent-types/loop-agent-response-type.ts"],"names":[],"mappings":";AA2KA,oEAAoE;AACpE,sDAAsD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop-agent-type.d.ts","sourceRoot":"","sources":["../../src/agent-types/loop-agent-type.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,cAAc,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,WAAW,EAA6B,MAAM,8BAA8B,CAAC;AAEpL,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAIxE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,qBACa,aAAc,SAAQ,aAAa;IAC5C,OAAO,CAAC,UAAU,CAAiC;IAEtC,wBAAwB,CAAC,GAAG,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,MAAM,EAAE,kBAAkB,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC;IAM3G;;;;;;;;;;;OAWG;IACU,iBAAiB,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,GAAG,GAAG,EAC7C,YAAY,EAAE,iBAAiB,GAAG,IAAI,EACtC,MAAM,EAAE,kBAAkB,CAAC,GAAG,EAAE,CAAC,CAAC,EAClC,OAAO,EAAE,CAAC,EACV,cAAc,EAAE,GAAG,GACpB,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"loop-agent-type.d.ts","sourceRoot":"","sources":["../../src/agent-types/loop-agent-type.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,cAAc,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,WAAW,EAA6B,MAAM,8BAA8B,CAAC;AAEpL,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAIxE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,qBACa,aAAc,SAAQ,aAAa;IAC5C,OAAO,CAAC,UAAU,CAAiC;IAEtC,wBAAwB,CAAC,GAAG,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,MAAM,EAAE,kBAAkB,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC;IAM3G;;;;;;;;;;;OAWG;IACU,iBAAiB,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,GAAG,GAAG,EAC7C,YAAY,EAAE,iBAAiB,GAAG,IAAI,EACtC,MAAM,EAAE,kBAAkB,CAAC,GAAG,EAAE,CAAC,CAAC,EAClC,OAAO,EAAE,CAAC,EACV,cAAc,EAAE,GAAG,GACpB,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;IA0NhC;;;;;;;OAOG;IACH,SAAS,CAAC,mBAAmB,CAAC,cAAc,EAAE,OAAO,GAAG;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAC;IAyI5F;;;;;;;OAOG;IACU,aAAa,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,GAAG,GAAG,EACzC,OAAO,EAAE,CAAC,EACV,cAAc,EAAE,GAAG,EACnB,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GACpD,OAAO,CAAC,IAAI,CAAC;IAShB;;;;;;;;;;OAUG;IACU,oBAAoB,CAAC,CAAC,GAAG,GAAG,EAAE,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAK/G;;;;;;;;;;;OAWG;IACU,kBAAkB,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,GAAG,GAAG,EAC9C,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAC7B,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAC1B,OAAO,EAAE,CAAC,EACV,cAAc,EAAE,GAAG,GACpB,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAKvC;;;;;;;;;;;OAWG;IACU,gBAAgB,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,GAAG,GAAG,EAC5C,MAAM,EAAE,kBAAkB,EAC1B,MAAM,EAAE,kBAAkB,EAC1B,OAAO,EAAE,CAAC,EACV,cAAc,EAAE,GAAG,EACnB,gBAAgB,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,IAAI,GAC/C,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAAC;IAK3C;;;;;;;;;;;;;;;;;OAiBG;IACU,oBAAoB,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,GAAG,GAAG,EAChD,OAAO,EAAE,WAAW,EAAE,EACtB,cAAc,EAAE,CAAC,EACjB,cAAc,EAAE,GAAG,EACnB,WAAW,EAAE,iBAAiB,CAAC,CAAC,CAAC,EACjC,MAAM,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAC/B,OAAO,CAAC,IAAI,CAAC;IAShB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,6BAA6B;CAyBxC"}
|
|
@@ -86,13 +86,41 @@ let LoopAgentType = class LoopAgentType extends BaseAgentType {
|
|
|
86
86
|
// Parse the response using the base class utility
|
|
87
87
|
const response = this.parseJSONResponse(promptResult);
|
|
88
88
|
if (!response) {
|
|
89
|
-
|
|
89
|
+
// Strong, specific corrective directive. The common failure mode here is the model
|
|
90
|
+
// drifting into conversational prose (e.g. "I'm executing the X action with
|
|
91
|
+
// parameters: ...") instead of emitting the JSON envelope. A terse "couldn't parse"
|
|
92
|
+
// message gives the model nothing to correct against, so we spell out exactly what
|
|
93
|
+
// to do and what NOT to do. This is the retry-feedback half of the prose-drift fix;
|
|
94
|
+
// the other half re-roles framework action records so they stop reading as prose
|
|
95
|
+
// assistant exemplars.
|
|
96
|
+
return this.createRetryStep('Your previous message was not valid JSON and could not be parsed. Respond with ONLY a ' +
|
|
97
|
+
'single raw JSON object conforming to the LoopAgentResponse interface — no prose, no ' +
|
|
98
|
+
'markdown code fences, and no natural-language narration such as "I\'m executing the ... ' +
|
|
99
|
+
'action". Do not describe your action in text; express it inside the JSON (e.g. via ' +
|
|
100
|
+
'nextStep). Output the JSON object and nothing else.');
|
|
90
101
|
}
|
|
91
102
|
// Validate the response structure
|
|
92
103
|
const validationResult = this.isValidLoopResponse(response);
|
|
93
104
|
if (!validationResult.success) {
|
|
94
105
|
return this.createRetryStep(validationResult.message);
|
|
95
106
|
}
|
|
107
|
+
// A tool pipeline is a yield/await action (like client tools): the agent can't know
|
|
108
|
+
// its result until it runs. Handle it BEFORE Chat/taskComplete/nextStep-switch checks —
|
|
109
|
+
// the loop executes the pipeline inline, injects the final output, and we force one
|
|
110
|
+
// more turn (Retry, non-terminal) so the LLM can use the result. Pipeline is a singular
|
|
111
|
+
// `nextStep.type`, so the model cannot also request Actions/Chat/etc. this turn; checking
|
|
112
|
+
// it here preserves yield/await semantics (it pre-empts taskComplete, like client tools).
|
|
113
|
+
if (response.nextStep?.type === 'Pipeline' && response.nextStep.pipeline?.steps?.length) {
|
|
114
|
+
return this.createNextStep('Retry', {
|
|
115
|
+
pipeline: response.nextStep.pipeline,
|
|
116
|
+
terminate: false,
|
|
117
|
+
scratchpad: response.scratchpad,
|
|
118
|
+
artifactToolCalls: response.artifactToolCalls,
|
|
119
|
+
payloadChangeRequest: response.payloadChangeRequest,
|
|
120
|
+
reasoning: response.reasoning,
|
|
121
|
+
confidence: response.confidence
|
|
122
|
+
});
|
|
123
|
+
}
|
|
96
124
|
// Check for Chat nextStep BEFORE checking taskComplete
|
|
97
125
|
// This allows agents to ask for user clarification even when taskComplete=true
|
|
98
126
|
if (response.nextStep?.type === 'Chat') {
|
|
@@ -238,6 +266,13 @@ let LoopAgentType = class LoopAgentType extends BaseAgentType {
|
|
|
238
266
|
retVal.messageIndex = response.nextStep.messageIndex;
|
|
239
267
|
retVal.expandReason = response.nextStep.reason;
|
|
240
268
|
break;
|
|
269
|
+
case 'Pipeline':
|
|
270
|
+
// A well-formed pipeline is handled by the early return above; reaching the
|
|
271
|
+
// switch means type='Pipeline' was set with no (or empty) nextStep.pipeline.steps.
|
|
272
|
+
retVal.step = 'Retry';
|
|
273
|
+
retVal.message = 'When nextStep.type == "Pipeline", nextStep.pipeline.steps must contain 1 or more stages';
|
|
274
|
+
retVal.errorMessage = 'Pipeline steps not specified for Pipeline type';
|
|
275
|
+
break;
|
|
241
276
|
default:
|
|
242
277
|
retVal.step = 'Retry';
|
|
243
278
|
retVal.errorMessage = `Unknown next step type: ${response.nextStep.type}`;
|
|
@@ -317,7 +352,7 @@ let LoopAgentType = class LoopAgentType extends BaseAgentType {
|
|
|
317
352
|
}
|
|
318
353
|
// Validate nextStep structure if present
|
|
319
354
|
if (response.nextStep) {
|
|
320
|
-
const validStepTypes = ['actions', 'sub-agent', 'chat', 'retry', 'foreach', 'while', 'clienttools'];
|
|
355
|
+
const validStepTypes = ['actions', 'sub-agent', 'chat', 'retry', 'foreach', 'while', 'clienttools', 'pipeline'];
|
|
321
356
|
let lcaseType = response.nextStep.type?.toLowerCase().trim();
|
|
322
357
|
// allow the AI to mess up the case, but we need to validate it
|
|
323
358
|
// be smart/lenient about missing types. if type is missing but we have a nextStep.subAgent/subAgents, default to sub-agent and if type is missing and we have nextStep.actions, default to actions
|
|
@@ -341,6 +376,10 @@ let LoopAgentType = class LoopAgentType extends BaseAgentType {
|
|
|
341
376
|
response.nextStep.type = 'While'; // update the data structure to have the correct type
|
|
342
377
|
lcaseType = 'while';
|
|
343
378
|
}
|
|
379
|
+
else if (!lcaseType && response.nextStep.pipeline?.steps?.length) {
|
|
380
|
+
response.nextStep.type = 'Pipeline'; // update the data structure to have the correct type
|
|
381
|
+
lcaseType = 'pipeline';
|
|
382
|
+
}
|
|
344
383
|
if (!validStepTypes.includes(lcaseType)) {
|
|
345
384
|
const message = `LoopAgentResponse has invalid nextStep.type: ${response.nextStep.type}`;
|
|
346
385
|
LogError(message);
|