@fevex/core 0.1.0-alpha.1 → 0.1.0-alpha.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 +424 -6
- package/dist/agents/index.d.mts +1 -1
- package/dist/agents/index.mjs +1 -1
- package/dist/channels/index.d.mts +2 -0
- package/dist/channels/index.mjs +150 -0
- package/dist/evals/index.d.mts +19 -11
- package/dist/evals/index.mjs +125 -122
- package/dist/fevex-Ddyi6z6L.d.mts +60 -0
- package/dist/http/index.d.mts +90 -0
- package/dist/http/index.mjs +470 -0
- package/dist/{index-ojCY3lwm.d.mts → index-B4PIkqOU.d.mts} +5 -4
- package/dist/index-Bg9rr8Qe.d.mts +50 -0
- package/dist/index-Bgcxgy66.d.mts +82 -0
- package/dist/index-CTIwrVf9.d.mts +51 -0
- package/dist/index-CqOjvtQ9.d.mts +132 -0
- package/dist/index-CrOId2px.d.mts +66 -0
- package/dist/index-DeXUtyd9.d.mts +70 -0
- package/dist/index-DviHtsLo.d.mts +58 -0
- package/dist/index-Dx_LotWZ.d.mts +425 -0
- package/dist/{index-QTkd6a7C.d.mts → index-dkFyTNh4.d.mts} +4 -3
- package/dist/index.d.mts +22 -10
- package/dist/index.mjs +2784 -720
- package/dist/knowledge/index.d.mts +2 -0
- package/dist/knowledge/index.mjs +89 -0
- package/dist/models/index.d.mts +2 -46
- package/dist/observability/index.d.mts +1 -1
- package/dist/policies/index.d.mts +1 -1
- package/dist/policies/index.mjs +1 -1
- package/dist/run-error-Cr17wPGH.mjs +23 -0
- package/dist/runtime/index.d.mts +2 -2
- package/dist/runtime/index.mjs +58 -9
- package/dist/sandbox/index.d.mts +2 -0
- package/dist/sandbox/index.mjs +122 -0
- package/dist/teams/index.d.mts +2 -0
- package/dist/teams/index.mjs +2 -0
- package/dist/teams-Ctz676G4.mjs +82 -0
- package/dist/testing/index.d.mts +33 -9
- package/dist/testing/index.mjs +415 -27
- package/dist/tools/index.d.mts +3 -3
- package/dist/tools/index.mjs +222 -2
- package/dist/types-CuoRx12Q.d.mts +333 -0
- package/dist/workflows/index.d.mts +2 -0
- package/dist/workflows/index.mjs +6 -0
- package/package.json +27 -3
- package/dist/fevex-Dz6GBz9o.d.mts +0 -32
- package/dist/index-BzxHfyYV.d.mts +0 -23
- package/dist/index-CZGyhNIQ.d.mts +0 -47
- package/dist/index-C_Zpr13f.d.mts +0 -124
- package/dist/index-DGWOGXzc.d.mts +0 -181
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as MemoryQuery, c as MemoryWrite, d as defineContextProvider, f as defineSkill, i as KnowledgeContext, l as SkillDefinition, n as ContextBlock, o as MemoryRecord, r as ContextProvider, s as MemoryStore, t as InMemoryMemoryStore, u as createMemoryContextProvider } from "../index-DeXUtyd9.mjs";
|
|
2
|
+
export { ContextBlock, ContextProvider, InMemoryMemoryStore, KnowledgeContext, MemoryQuery, MemoryRecord, MemoryStore, MemoryWrite, SkillDefinition, createMemoryContextProvider, defineContextProvider, defineSkill };
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
//#region src/knowledge/in-memory-memory-store.ts
|
|
2
|
+
/** Volatile keyword-search memory store for tests and local development. */
|
|
3
|
+
var InMemoryMemoryStore = class {
|
|
4
|
+
#records = /* @__PURE__ */ new Map();
|
|
5
|
+
async search(query, context) {
|
|
6
|
+
context.signal?.throwIfAborted();
|
|
7
|
+
const terms = query.query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
8
|
+
const scored = [...this.#records.values()].filter((record) => matchesScope(record, query)).map((record) => ({
|
|
9
|
+
record,
|
|
10
|
+
score: score(record.content, terms)
|
|
11
|
+
})).filter(({ score }) => !terms.length || score > 0).sort((left, right) => right.score - left.score || right.record.createdAt.localeCompare(left.record.createdAt));
|
|
12
|
+
return scored.slice(0, query.limit ?? scored.length).map(({ record }) => structuredClone(record));
|
|
13
|
+
}
|
|
14
|
+
async write(record, context) {
|
|
15
|
+
context.signal?.throwIfAborted();
|
|
16
|
+
const saved = {
|
|
17
|
+
id: crypto.randomUUID(),
|
|
18
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19
|
+
...structuredClone(record)
|
|
20
|
+
};
|
|
21
|
+
this.#records.set(saved.id, saved);
|
|
22
|
+
return structuredClone(saved);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
function matchesScope(record, query) {
|
|
26
|
+
return matches(record.agentName, query.agentName) && matches(record.sessionId, query.sessionId) && matches(record.namespace, query.namespace) && matches(record.actor?.id, query.actor?.id);
|
|
27
|
+
}
|
|
28
|
+
function matches(left, right) {
|
|
29
|
+
return right === void 0 || left === void 0 || left === right;
|
|
30
|
+
}
|
|
31
|
+
function score(content, terms) {
|
|
32
|
+
const text = content.toLowerCase();
|
|
33
|
+
return terms.reduce((total, term) => total + (text.includes(term) ? 1 : 0), 0);
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/knowledge/knowledge.ts
|
|
37
|
+
function defineContextProvider(provider) {
|
|
38
|
+
return provider;
|
|
39
|
+
}
|
|
40
|
+
function defineSkill(skill) {
|
|
41
|
+
return defineContextProvider({
|
|
42
|
+
name: skill.name,
|
|
43
|
+
async read() {
|
|
44
|
+
return [{
|
|
45
|
+
id: `${skill.name}:instructions`,
|
|
46
|
+
content: skill.instructions
|
|
47
|
+
}, ...(skill.resources ?? []).map((resource, index) => {
|
|
48
|
+
const normalized = isSkillResource(resource) ? resource : { content: resource };
|
|
49
|
+
return {
|
|
50
|
+
id: normalized.id ?? `${skill.name}:resource:${index + 1}`,
|
|
51
|
+
content: stringifyKnowledge(normalized.content),
|
|
52
|
+
...normalized.metadata === void 0 ? {} : { metadata: normalized.metadata }
|
|
53
|
+
};
|
|
54
|
+
})];
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
function isSkillResource(value) {
|
|
59
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && "content" in value && (value.id === void 0 || typeof value.id === "string");
|
|
60
|
+
}
|
|
61
|
+
function createMemoryContextProvider(options) {
|
|
62
|
+
return defineContextProvider({
|
|
63
|
+
name: options.name ?? "memory",
|
|
64
|
+
async read(context) {
|
|
65
|
+
return (await options.store.search({
|
|
66
|
+
query: context.input,
|
|
67
|
+
limit: options.limit,
|
|
68
|
+
agentName: context.agentName,
|
|
69
|
+
sessionId: context.sessionId,
|
|
70
|
+
namespace: context.context?.namespace,
|
|
71
|
+
actor: context.context?.actor
|
|
72
|
+
}, context)).map((record) => ({
|
|
73
|
+
id: record.id,
|
|
74
|
+
content: record.content,
|
|
75
|
+
metadata: {
|
|
76
|
+
...record.metadata ?? {},
|
|
77
|
+
createdAt: record.createdAt,
|
|
78
|
+
...record.agentName === void 0 ? {} : { agentName: record.agentName },
|
|
79
|
+
...record.sessionId === void 0 ? {} : { sessionId: record.sessionId }
|
|
80
|
+
}
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
function stringifyKnowledge(value) {
|
|
86
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
export { InMemoryMemoryStore, createMemoryContextProvider, defineContextProvider, defineSkill };
|
package/dist/models/index.d.mts
CHANGED
|
@@ -1,46 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
type ReasoningEffort = 'provider-default' | 'none' | 'minimal' | 'low' | 'medium' | 'high';
|
|
4
|
-
interface ModelUsage {
|
|
5
|
-
inputTokens?: number;
|
|
6
|
-
outputTokens?: number;
|
|
7
|
-
totalTokens?: number;
|
|
8
|
-
}
|
|
9
|
-
interface ModelInput {
|
|
10
|
-
messages: AgentMessage[];
|
|
11
|
-
tools?: ToolSpec[];
|
|
12
|
-
reasoning?: ReasoningEffort;
|
|
13
|
-
modelOptions?: Record<string, unknown>;
|
|
14
|
-
outputSchema?: JsonObject;
|
|
15
|
-
maxOutputTokens?: number;
|
|
16
|
-
providerState?: unknown;
|
|
17
|
-
signal?: AbortSignal;
|
|
18
|
-
}
|
|
19
|
-
interface ModelResult<TOutput = unknown> {
|
|
20
|
-
output?: TOutput;
|
|
21
|
-
toolCalls?: ToolCall[];
|
|
22
|
-
usage?: ModelUsage;
|
|
23
|
-
providerState?: unknown;
|
|
24
|
-
}
|
|
25
|
-
interface ModelMetadata {
|
|
26
|
-
provider?: string;
|
|
27
|
-
model?: string;
|
|
28
|
-
}
|
|
29
|
-
interface ModelGateway {
|
|
30
|
-
stream(input: ModelInput): AsyncIterable<ModelStreamEvent>;
|
|
31
|
-
metadata?: ModelMetadata;
|
|
32
|
-
stateCodec?: {
|
|
33
|
-
serialize(state: unknown): JsonValue;
|
|
34
|
-
restore(state: JsonValue): unknown;
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
type ModelStreamEvent<TOutput = unknown> = {
|
|
38
|
-
type: 'output.delta';
|
|
39
|
-
delta: string;
|
|
40
|
-
} | {
|
|
41
|
-
type: 'completed';
|
|
42
|
-
result: ModelResult<TOutput>;
|
|
43
|
-
};
|
|
44
|
-
type ModelRef = string | ModelGateway;
|
|
45
|
-
//#endregion
|
|
46
|
-
export { ModelGateway, ModelInput, ModelMetadata, ModelRef, ModelResult, ModelStreamEvent, ModelUsage, ReasoningEffort };
|
|
1
|
+
import { a as ModelResult, c as ReasoningEffort, i as ModelRef, l as ToolChoice, n as ModelInput, o as ModelStreamEvent, r as ModelMetadata, s as ModelUsage, t as ModelGateway } from "../index-CTIwrVf9.mjs";
|
|
2
|
+
export { ModelGateway, ModelInput, ModelMetadata, ModelRef, ModelResult, ModelStreamEvent, ModelUsage, ReasoningEffort, ToolChoice };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as TraceAnnotation, c as TraceContentPolicy, d as TraceRedactor, f as TraceRedactorInput, h as TraceStatus, i as RunTrace, l as TraceCost, m as TraceSpanKind, n as CostCalculatorInput, o as TraceContent, p as TraceSpan, r as ObservabilityOptions, s as TraceContentKind, t as CostCalculator, u as TraceExporter } from "../index-
|
|
1
|
+
import { a as TraceAnnotation, c as TraceContentPolicy, d as TraceRedactor, f as TraceRedactorInput, h as TraceStatus, i as RunTrace, l as TraceCost, m as TraceSpanKind, n as CostCalculatorInput, o as TraceContent, p as TraceSpan, r as ObservabilityOptions, s as TraceContentKind, t as CostCalculator, u as TraceExporter } from "../index-B4PIkqOU.mjs";
|
|
2
2
|
export { CostCalculator, CostCalculatorInput, ObservabilityOptions, RunTrace, TraceAnnotation, TraceContent, TraceContentKind, TraceContentPolicy, TraceCost, TraceExporter, TraceRedactor, TraceRedactorInput, TraceSpan, TraceSpanKind, TraceStatus };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as definePolicy, n as PolicyDecision, r as PolicyDefinition, t as PolicyAuthorization } from "../index-
|
|
1
|
+
import { i as definePolicy, n as PolicyDecision, r as PolicyDefinition, t as PolicyAuthorization } from "../index-dkFyTNh4.mjs";
|
|
2
2
|
export { PolicyAuthorization, PolicyDecision, PolicyDefinition, definePolicy };
|
package/dist/policies/index.mjs
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/run-error.ts
|
|
2
|
+
var FevexRunError = class extends Error {
|
|
3
|
+
code;
|
|
4
|
+
runId;
|
|
5
|
+
name = "FevexRunError";
|
|
6
|
+
constructor(code, message, runId, options) {
|
|
7
|
+
super(message, options);
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.runId = runId;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var RunPausedError = class extends FevexRunError {
|
|
13
|
+
runId;
|
|
14
|
+
pause;
|
|
15
|
+
name = "RunPausedError";
|
|
16
|
+
constructor(runId, pause) {
|
|
17
|
+
super("RUN_PAUSED", `Run "${runId}" is paused`, runId);
|
|
18
|
+
this.runId = runId;
|
|
19
|
+
this.pause = pause;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
//#endregion
|
|
23
|
+
export { RunPausedError as n, FevexRunError as t };
|
package/dist/runtime/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as isDurableRunStore, S as
|
|
2
|
-
export { AgentRun, ApprovalRequest, ApprovalResolution, CredentialStore, DurableRunStore, ExecutionCommit, InMemoryRunStore, ListEventsOptions, PendingToolExecution, ResumeRunResolution, RunCheckpoint, RunLease, RunPause, RunRequest, RunResult, RunStatus, RunStore, Session, SessionId, ToolExecutionRecord, ToolExecutionResolution, ToolExecutionStatus, isDurableRunStore };
|
|
1
|
+
import { A as RunStore, B as WorkflowBudgetUsage, C as RunCheckpoint, D as RunRequest, E as RunRecord, F as TeamRun, G as WorkflowWaitRecord, H as WorkflowCompensationRecord, I as TimerResolution, K as isDurableRunStore, L as ToolExecutionRecord, M as SessionId, N as StoredRunCheckpoint, O as RunResult, P as TeamCheckpoint, R as ToolExecutionResolution, S as ResumeRunResolution, T as RunPause, U as WorkflowRun, V as WorkflowCheckpoint, W as WorkflowStepRecord, _ as EventResolution, a as AgentRun, b as ListEventsOptions, c as ApprovalRequest, d as CoordinatorRun, f as CredentialStore, g as ElicitationResolution, h as ElicitationRequest, i as InMemoryRunStore, j as Session, k as RunStatus, l as ApprovalResolution, m as ElicitationMode, o as AgentRunPause, p as DurableRunStore, q as validateListEventsOptions, s as ApprovalMode, u as CoordinatorCheckpoint, v as ExecutionCommit, w as RunLease, x as PendingToolExecution, y as ExecutionCreate, z as ToolExecutionStatus } from "../index-Dx_LotWZ.mjs";
|
|
2
|
+
export { AgentRun, AgentRunPause, ApprovalMode, ApprovalRequest, ApprovalResolution, CoordinatorCheckpoint, CoordinatorRun, CredentialStore, DurableRunStore, ElicitationMode, ElicitationRequest, ElicitationResolution, EventResolution, ExecutionCommit, ExecutionCreate, InMemoryRunStore, ListEventsOptions, PendingToolExecution, ResumeRunResolution, RunCheckpoint, RunLease, RunPause, RunRecord, RunRequest, RunResult, RunStatus, RunStore, Session, SessionId, StoredRunCheckpoint, TeamCheckpoint, TeamRun, TimerResolution, ToolExecutionRecord, ToolExecutionResolution, ToolExecutionStatus, WorkflowBudgetUsage, WorkflowCheckpoint, WorkflowCompensationRecord, WorkflowRun, WorkflowStepRecord, WorkflowWaitRecord, isDurableRunStore, validateListEventsOptions };
|
package/dist/runtime/index.mjs
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
|
-
|
|
1
|
+
import { t as FevexRunError } from "../run-error-Cr17wPGH.mjs";
|
|
2
|
+
//#region src/runtime/run-store.ts
|
|
3
|
+
function validateListEventsOptions(options) {
|
|
4
|
+
if (options.limit !== void 0 && (!Number.isSafeInteger(options.limit) || options.limit < 1)) throw new TypeError("Event limit must be a positive safe integer");
|
|
5
|
+
if (options.order !== void 0 && options.order !== "asc" && options.order !== "desc") throw new TypeError("Event order must be asc or desc");
|
|
6
|
+
}
|
|
2
7
|
function isDurableRunStore(store) {
|
|
3
8
|
const value = store;
|
|
4
9
|
return [
|
|
5
10
|
value.getCheckpoint,
|
|
6
11
|
value.getToolExecution,
|
|
12
|
+
value.createExecution,
|
|
7
13
|
value.commitExecution,
|
|
8
14
|
value.acquireLease,
|
|
9
15
|
value.renewLease,
|
|
10
16
|
value.releaseLease
|
|
11
17
|
].every((method) => typeof method === "function");
|
|
12
18
|
}
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/runtime/in-memory-run-store.ts
|
|
21
|
+
/**
|
|
22
|
+
* In-process durable store intended for development and tests.
|
|
23
|
+
*
|
|
24
|
+
* Values are cloned at the boundary so callers cannot mutate persisted state.
|
|
25
|
+
* Data is lost when the process exits.
|
|
26
|
+
*/
|
|
13
27
|
var InMemoryRunStore = class {
|
|
14
28
|
#runs = /* @__PURE__ */ new Map();
|
|
15
29
|
#sessions = /* @__PURE__ */ new Map();
|
|
@@ -30,6 +44,9 @@ var InMemoryRunStore = class {
|
|
|
30
44
|
return session === void 0 ? void 0 : structuredClone(session);
|
|
31
45
|
}
|
|
32
46
|
async saveSession(session) {
|
|
47
|
+
const current = this.#sessions.get(session.id);
|
|
48
|
+
if (this.#sessionBusy(session.id) || (current?.revision ?? 0) !== (session.revision ?? 0)) throw new FevexRunError("RUN_CONFLICT", `Session "${session.id}" is active or was modified`);
|
|
49
|
+
session.revision = (session.revision ?? 0) + 1;
|
|
33
50
|
this.#sessions.set(session.id, structuredClone(session));
|
|
34
51
|
}
|
|
35
52
|
async appendEvent(event) {
|
|
@@ -38,15 +55,18 @@ var InMemoryRunStore = class {
|
|
|
38
55
|
events.push(structuredClone(event));
|
|
39
56
|
}
|
|
40
57
|
async listEvents(runId, options = {}) {
|
|
58
|
+
validateListEventsOptions(options);
|
|
41
59
|
const events = this.#events.get(runId);
|
|
42
60
|
if (!events) throw new Error(`Run "${runId}" does not exist`);
|
|
43
61
|
let start = 0;
|
|
44
62
|
if (options.after !== void 0) {
|
|
45
63
|
const cursor = events.findIndex(({ id }) => id === options.after);
|
|
46
|
-
if (cursor < 0) throw new
|
|
64
|
+
if (cursor < 0) throw new FevexRunError("INVALID_CURSOR", `Event cursor "${options.after}" does not exist in run "${runId}"`, runId);
|
|
47
65
|
start = cursor + 1;
|
|
48
66
|
}
|
|
49
|
-
|
|
67
|
+
const sequence = start ? events[start - 1].sequence : -1;
|
|
68
|
+
const ordered = events.filter((event) => event.sequence > sequence).sort((a, b) => options.order === "desc" ? b.sequence - a.sequence : a.sequence - b.sequence);
|
|
69
|
+
return structuredClone(ordered.slice(0, options.limit));
|
|
50
70
|
}
|
|
51
71
|
async getCheckpoint(runId) {
|
|
52
72
|
const checkpoint = this.#checkpoints.get(runId);
|
|
@@ -56,14 +76,37 @@ var InMemoryRunStore = class {
|
|
|
56
76
|
const execution = this.#toolExecutions.get(`${runId}:${toolCallId}`);
|
|
57
77
|
return execution === void 0 ? void 0 : structuredClone(execution);
|
|
58
78
|
}
|
|
79
|
+
async createExecution(create) {
|
|
80
|
+
if (this.#runs.has(create.run.id)) return false;
|
|
81
|
+
const session = create.session;
|
|
82
|
+
const currentSession = this.#sessions.get(create.run.sessionId);
|
|
83
|
+
if (!session || session.id !== create.run.sessionId || this.#sessionBusy(session.id) || (currentSession?.revision ?? 0) !== (session.revision ?? 0)) return false;
|
|
84
|
+
const run = structuredClone(create.run);
|
|
85
|
+
run.revision = 1;
|
|
86
|
+
this.#runs.set(run.id, run);
|
|
87
|
+
create.run.revision = 1;
|
|
88
|
+
session.revision = (session.revision ?? 0) + 1;
|
|
89
|
+
this.#sessions.set(session.id, structuredClone(session));
|
|
90
|
+
this.#events.set(run.id, structuredClone(create.events));
|
|
91
|
+
this.#checkpoints.set(run.id, structuredClone(create.checkpoint));
|
|
92
|
+
create.lease.generation = 1;
|
|
93
|
+
this.#leases.set(run.id, structuredClone(create.lease));
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
59
96
|
async commitExecution(commit) {
|
|
60
97
|
const current = this.#runs.get(commit.run.id);
|
|
61
98
|
if (!current || current.revision !== commit.expectedRevision) return false;
|
|
99
|
+
const lease = this.#leases.get(commit.run.id);
|
|
100
|
+
if (!lease || !commit.lease || !(commit.lease.generation > 0) || lease.ownerId !== commit.lease.ownerId || lease.generation !== commit.lease.generation || !(Date.parse(lease.expiresAt) > Date.now())) return false;
|
|
101
|
+
if (commit.session && (commit.session.id !== current.sessionId || (this.#sessions.get(current.sessionId)?.revision ?? 0) !== (commit.session.revision ?? 0) || this.#sessionBusy(current.sessionId, current.id))) return false;
|
|
62
102
|
const run = structuredClone(commit.run);
|
|
63
103
|
run.revision = commit.expectedRevision + 1;
|
|
64
104
|
this.#runs.set(run.id, run);
|
|
65
105
|
commit.run.revision = run.revision;
|
|
66
|
-
if (commit.session)
|
|
106
|
+
if (commit.session) {
|
|
107
|
+
commit.session.revision = (commit.session.revision ?? 0) + 1;
|
|
108
|
+
this.#sessions.set(commit.session.id, structuredClone(commit.session));
|
|
109
|
+
}
|
|
67
110
|
if (commit.checkpoint === null) this.#checkpoints.delete(run.id);
|
|
68
111
|
else if (commit.checkpoint) this.#checkpoints.set(run.id, structuredClone(commit.checkpoint));
|
|
69
112
|
if (commit.toolExecution) this.#toolExecutions.set(`${run.id}:${commit.toolExecution.toolCallId}`, structuredClone(commit.toolExecution));
|
|
@@ -72,21 +115,27 @@ var InMemoryRunStore = class {
|
|
|
72
115
|
for (const event of commit.events ?? []) events.push(structuredClone(event));
|
|
73
116
|
return true;
|
|
74
117
|
}
|
|
118
|
+
#sessionBusy(sessionId, exceptRunId) {
|
|
119
|
+
return [...this.#runs.values()].some((run) => run.sessionId === sessionId && run.id !== exceptRunId && (run.status === "running" || run.status === "paused"));
|
|
120
|
+
}
|
|
75
121
|
async acquireLease(lease) {
|
|
76
122
|
const current = this.#leases.get(lease.runId);
|
|
77
|
-
if (current &&
|
|
123
|
+
if (current && Date.parse(current.expiresAt) > Date.now()) return false;
|
|
124
|
+
lease.generation = (current?.generation ?? 0) + 1;
|
|
78
125
|
this.#leases.set(lease.runId, structuredClone(lease));
|
|
79
126
|
return true;
|
|
80
127
|
}
|
|
81
128
|
async renewLease(lease) {
|
|
82
129
|
const current = this.#leases.get(lease.runId);
|
|
83
|
-
if (!current || current.ownerId !== lease.ownerId) return false;
|
|
130
|
+
if (!current || current.ownerId !== lease.ownerId || current.generation !== lease.generation || !(Date.parse(current.expiresAt) > Date.now())) return false;
|
|
84
131
|
this.#leases.set(lease.runId, structuredClone(lease));
|
|
85
132
|
return true;
|
|
86
133
|
}
|
|
87
|
-
async releaseLease(runId, ownerId) {
|
|
88
|
-
if (
|
|
134
|
+
async releaseLease(runId, ownerId, generation) {
|
|
135
|
+
if (!(generation > 0)) return;
|
|
136
|
+
const current = this.#leases.get(runId);
|
|
137
|
+
if (current?.ownerId === ownerId && current.generation === generation) current.expiresAt = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
89
138
|
}
|
|
90
139
|
};
|
|
91
140
|
//#endregion
|
|
92
|
-
export { InMemoryRunStore, isDurableRunStore };
|
|
141
|
+
export { InMemoryRunStore, isDurableRunStore, validateListEventsOptions };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as SandboxError, c as sandboxResultToJson, i as SandboxCapabilities, n as createLocalSandbox, o as SandboxRunRequest, r as Sandbox, s as SandboxRunResult, t as LocalSandboxOptions } from "../index-CrOId2px.mjs";
|
|
2
|
+
export { LocalSandboxOptions, Sandbox, SandboxCapabilities, SandboxError, SandboxRunRequest, SandboxRunResult, createLocalSandbox, sandboxResultToJson };
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { realpathSync } from "node:fs";
|
|
3
|
+
import { relative, resolve } from "node:path";
|
|
4
|
+
//#region src/sandbox/sandbox.ts
|
|
5
|
+
var SandboxError = class extends Error {
|
|
6
|
+
code;
|
|
7
|
+
safeMessage;
|
|
8
|
+
name = "SandboxError";
|
|
9
|
+
constructor(code, safeMessage, options) {
|
|
10
|
+
super(safeMessage, options);
|
|
11
|
+
this.code = code;
|
|
12
|
+
this.safeMessage = safeMessage;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
function sandboxResultToJson(result) {
|
|
16
|
+
return {
|
|
17
|
+
exitCode: result.exitCode,
|
|
18
|
+
stdout: result.stdout,
|
|
19
|
+
stderr: result.stderr,
|
|
20
|
+
durationMs: result.durationMs,
|
|
21
|
+
timedOut: result.timedOut
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/sandbox/local-sandbox.ts
|
|
26
|
+
/** Creates a subprocess sandbox restricted to a root directory and command allowlist. */
|
|
27
|
+
function createLocalSandbox(options) {
|
|
28
|
+
if (!options.rootDir.trim()) throw new SandboxError("SANDBOX_INVALID_CONFIG", "Sandbox rootDir must be non-empty");
|
|
29
|
+
if (!options.commands.length || options.commands.some((command) => !command.trim())) throw new SandboxError("SANDBOX_INVALID_CONFIG", "Sandbox commands allowlist must contain non-empty commands");
|
|
30
|
+
const rootDir = realpathSync(resolve(options.rootDir));
|
|
31
|
+
const allowed = new Set(options.commands);
|
|
32
|
+
const defaultTimeoutMs = options.defaultTimeoutMs ?? 1e4;
|
|
33
|
+
const maxOutputBytes = options.maxOutputBytes ?? 64e3;
|
|
34
|
+
return { async run(request) {
|
|
35
|
+
if (request.capabilities?.network) throw new SandboxError("SANDBOX_NETWORK_UNSUPPORTED", "Local sandbox does not support network access");
|
|
36
|
+
const command = request.command;
|
|
37
|
+
if (!allowed.has(command) || !request.capabilities?.process?.commands?.includes(command)) throw new SandboxError("SANDBOX_COMMAND_DENIED", `Command "${command}" is not allowed`);
|
|
38
|
+
const cwd = resolve(rootDir, request.cwd ?? request.capabilities.filesystem?.cwd ?? ".");
|
|
39
|
+
if (!isInside(rootDir, cwd)) throw new SandboxError("SANDBOX_CWD_DENIED", "Sandbox cwd must stay inside rootDir");
|
|
40
|
+
const timeoutMs = request.timeoutMs ?? request.capabilities.resources?.timeoutMs ?? request.capabilities.process.timeoutMs ?? defaultTimeoutMs;
|
|
41
|
+
const outputLimit = request.maxOutputBytes ?? request.capabilities.resources?.maxOutputBytes ?? maxOutputBytes;
|
|
42
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs < 1) throw new SandboxError("SANDBOX_INVALID_REQUEST", "Sandbox timeoutMs must be positive");
|
|
43
|
+
if (!Number.isInteger(outputLimit) || outputLimit < 1) throw new SandboxError("SANDBOX_INVALID_REQUEST", "Sandbox maxOutputBytes must be positive");
|
|
44
|
+
return runProcess({
|
|
45
|
+
command,
|
|
46
|
+
args: request.args ?? [],
|
|
47
|
+
cwd,
|
|
48
|
+
env: {
|
|
49
|
+
...options.env ?? {},
|
|
50
|
+
...request.env ?? {}
|
|
51
|
+
},
|
|
52
|
+
input: request.input,
|
|
53
|
+
timeoutMs,
|
|
54
|
+
maxOutputBytes: outputLimit,
|
|
55
|
+
signal: request.signal
|
|
56
|
+
});
|
|
57
|
+
} };
|
|
58
|
+
}
|
|
59
|
+
async function runProcess(options) {
|
|
60
|
+
const started = Date.now();
|
|
61
|
+
let timedOut = false;
|
|
62
|
+
let stdout = "";
|
|
63
|
+
let stderr = "";
|
|
64
|
+
return await new Promise((resolvePromise, reject) => {
|
|
65
|
+
options.signal?.throwIfAborted();
|
|
66
|
+
const child = spawn(options.command, [...options.args], {
|
|
67
|
+
cwd: options.cwd,
|
|
68
|
+
env: options.env,
|
|
69
|
+
shell: false,
|
|
70
|
+
stdio: [
|
|
71
|
+
"pipe",
|
|
72
|
+
"pipe",
|
|
73
|
+
"pipe"
|
|
74
|
+
]
|
|
75
|
+
});
|
|
76
|
+
const abort = () => {
|
|
77
|
+
child.kill("SIGTERM");
|
|
78
|
+
reject(options.signal?.reason ?? new DOMException("Sandbox aborted", "AbortError"));
|
|
79
|
+
};
|
|
80
|
+
const timer = setTimeout(() => {
|
|
81
|
+
timedOut = true;
|
|
82
|
+
child.kill("SIGTERM");
|
|
83
|
+
}, options.timeoutMs);
|
|
84
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
85
|
+
child.stdout.setEncoding("utf8");
|
|
86
|
+
child.stderr.setEncoding("utf8");
|
|
87
|
+
child.stdout.on("data", (chunk) => {
|
|
88
|
+
stdout = appendLimited(stdout, String(chunk), options.maxOutputBytes);
|
|
89
|
+
});
|
|
90
|
+
child.stderr.on("data", (chunk) => {
|
|
91
|
+
stderr = appendLimited(stderr, String(chunk), options.maxOutputBytes);
|
|
92
|
+
});
|
|
93
|
+
child.on("error", (error) => {
|
|
94
|
+
clearTimeout(timer);
|
|
95
|
+
options.signal?.removeEventListener("abort", abort);
|
|
96
|
+
reject(new SandboxError("SANDBOX_PROCESS_ERROR", "Sandbox process failed", { cause: error }));
|
|
97
|
+
});
|
|
98
|
+
child.on("close", (exitCode) => {
|
|
99
|
+
clearTimeout(timer);
|
|
100
|
+
options.signal?.removeEventListener("abort", abort);
|
|
101
|
+
resolvePromise({
|
|
102
|
+
exitCode,
|
|
103
|
+
stdout,
|
|
104
|
+
stderr,
|
|
105
|
+
durationMs: Date.now() - started,
|
|
106
|
+
timedOut
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
if (options.input !== void 0) child.stdin.end(options.input);
|
|
110
|
+
else child.stdin.end();
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function appendLimited(current, chunk, maxBytes) {
|
|
114
|
+
const next = current + chunk;
|
|
115
|
+
return Buffer.byteLength(next) <= maxBytes ? next : next.slice(0, maxBytes);
|
|
116
|
+
}
|
|
117
|
+
function isInside(rootDir, value) {
|
|
118
|
+
const path = relative(rootDir, value);
|
|
119
|
+
return path === "" || !path.startsWith("..") && !resolve(path).startsWith("..");
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
122
|
+
export { SandboxError, createLocalSandbox, sandboxResultToJson };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as TeamMember, c as defineTeam, i as TeamLimits, n as TeamDelegation, o as TeamStep, r as TeamHandoff, s as TeamTaskResult, t as TeamDefinition } from "../index-Bg9rr8Qe.mjs";
|
|
2
|
+
export { type TeamDefinition, type TeamDelegation, type TeamHandoff, type TeamLimits, type TeamMember, type TeamStep, type TeamTaskResult, defineTeam };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
//#region src/teams/team.ts
|
|
2
|
+
function defineTeam(team) {
|
|
3
|
+
return team;
|
|
4
|
+
}
|
|
5
|
+
/** @internal Compiles teams onto the existing durable workflow engine. */
|
|
6
|
+
function teamAsWorkflow(team) {
|
|
7
|
+
const allowed = /* @__PURE__ */ new Set([team.supervisor, ...team.members.map(({ agent }) => agent)]);
|
|
8
|
+
const limits = team.limits && {
|
|
9
|
+
...team.limits.maxSteps === void 0 ? {} : { maxSteps: team.limits.maxSteps },
|
|
10
|
+
...team.limits.maxToolCalls === void 0 ? {} : { maxToolCalls: team.limits.maxToolCalls },
|
|
11
|
+
...team.limits.maxInputTokens === void 0 ? {} : { maxInputTokens: team.limits.maxInputTokens },
|
|
12
|
+
...team.limits.maxOutputTokens === void 0 ? {} : { maxOutputTokens: team.limits.maxOutputTokens }
|
|
13
|
+
};
|
|
14
|
+
return {
|
|
15
|
+
name: team.name,
|
|
16
|
+
version: team.version ?? "1",
|
|
17
|
+
inputSchema: team.inputSchema,
|
|
18
|
+
outputSchema: team.outputSchema,
|
|
19
|
+
limits,
|
|
20
|
+
async run(step, input) {
|
|
21
|
+
let delegations = 0;
|
|
22
|
+
const countDelegation = () => {
|
|
23
|
+
delegations += 1;
|
|
24
|
+
if (team.limits?.maxDelegations !== void 0 && delegations > team.limits.maxDelegations) throw new Error(`Team "${team.name}" exceeded maxDelegations limit of ${team.limits.maxDelegations}`);
|
|
25
|
+
};
|
|
26
|
+
const runAgent = async (delegationId, delegation, metadata) => {
|
|
27
|
+
if (!delegationId.trim()) throw new Error("Team delegation id cannot be empty");
|
|
28
|
+
if (!allowed.has(delegation.agent)) throw new Error(`Agent "${delegation.agent}" is not a member of team "${team.name}"`);
|
|
29
|
+
if (delegation.expectedOutput !== void 0 && !delegation.expectedOutput.trim()) throw new Error("Team delegation expectedOutput cannot be empty");
|
|
30
|
+
if (delegation.constraints !== void 0 && (!Array.isArray(delegation.constraints) || delegation.constraints.some((constraint) => typeof constraint !== "string" || !constraint.trim()))) throw new Error("Team delegation constraints must be non-empty strings");
|
|
31
|
+
countDelegation();
|
|
32
|
+
const request = {
|
|
33
|
+
input: delegation.task,
|
|
34
|
+
...delegation.context === void 0 ? {} : { context: delegation.context },
|
|
35
|
+
...delegation.limits === void 0 ? {} : { limits: delegation.limits }
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
...await step.agent(delegationId, delegation.agent, request, { metadata }),
|
|
39
|
+
delegationId,
|
|
40
|
+
agentName: delegation.agent
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
return team.run({
|
|
44
|
+
delegate(delegationId, delegation) {
|
|
45
|
+
return runAgent(delegationId, delegation, {
|
|
46
|
+
teamAction: "delegate",
|
|
47
|
+
...delegation.expectedOutput === void 0 ? {} : { expectedOutput: delegation.expectedOutput },
|
|
48
|
+
...delegation.constraints === void 0 ? {} : { constraints: delegation.constraints }
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
handoff(delegationId, handoff) {
|
|
52
|
+
if (!allowed.has(handoff.from)) throw new Error(`Agent "${handoff.from}" is not a member of team "${team.name}"`);
|
|
53
|
+
if (!handoff.reason.trim()) throw new Error("Team handoff reason cannot be empty");
|
|
54
|
+
return runAgent(delegationId, {
|
|
55
|
+
agent: handoff.to,
|
|
56
|
+
task: handoff.task,
|
|
57
|
+
context: handoff.context,
|
|
58
|
+
limits: handoff.limits,
|
|
59
|
+
expectedOutput: handoff.expectedOutput,
|
|
60
|
+
constraints: handoff.constraints
|
|
61
|
+
}, {
|
|
62
|
+
teamAction: "handoff",
|
|
63
|
+
from: handoff.from,
|
|
64
|
+
reason: handoff.reason,
|
|
65
|
+
...handoff.expectedOutput === void 0 ? {} : { expectedOutput: handoff.expectedOutput },
|
|
66
|
+
...handoff.constraints === void 0 ? {} : { constraints: handoff.constraints }
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
parallel(stepId, tasks) {
|
|
70
|
+
const width = Object.keys(tasks).length;
|
|
71
|
+
if (team.limits?.maxParallel !== void 0 && width > team.limits.maxParallel) throw new Error(`Team "${team.name}" exceeded maxParallel limit of ${team.limits.maxParallel}`);
|
|
72
|
+
return step.parallel(stepId, tasks, { metadata: {
|
|
73
|
+
teamAction: "merge",
|
|
74
|
+
width
|
|
75
|
+
} });
|
|
76
|
+
}
|
|
77
|
+
}, input);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
export { teamAsWorkflow as n, defineTeam as t };
|
package/dist/testing/index.d.mts
CHANGED
|
@@ -1,18 +1,42 @@
|
|
|
1
|
-
import { m as ToolCall } from "../
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
declare function fakeModel(...responses: ModelResult[]): FakeModel;
|
|
1
|
+
import { m as ToolCall } from "../types-CuoRx12Q.mjs";
|
|
2
|
+
import { a as ModelResult, n as ModelInput, t as ModelGateway } from "../index-CTIwrVf9.mjs";
|
|
3
|
+
import { p as DurableRunStore } from "../index-Dx_LotWZ.mjs";
|
|
4
|
+
import { s as MemoryStore } from "../index-DeXUtyd9.mjs";
|
|
5
|
+
import { d as ToolProvider } from "../index-CqOjvtQ9.mjs";
|
|
6
|
+
import { c as ChannelMessage, l as ChannelOutput, n as ChannelAdapter } from "../index-Bgcxgy66.mjs";
|
|
7
|
+
//#region src/testing/contracts.d.ts
|
|
9
8
|
interface ModelGatewayContract {
|
|
10
9
|
output?: unknown;
|
|
11
10
|
toolCall?: ToolCall;
|
|
12
11
|
usage?: boolean;
|
|
13
12
|
error?: Error;
|
|
14
13
|
}
|
|
14
|
+
interface ChannelAdapterContract<TInput, TOutput> {
|
|
15
|
+
input: TInput;
|
|
16
|
+
message: ChannelMessage;
|
|
17
|
+
output: ChannelOutput;
|
|
18
|
+
delivered: TOutput;
|
|
19
|
+
ignoredInput?: TInput;
|
|
20
|
+
}
|
|
21
|
+
interface ToolProviderContract {
|
|
22
|
+
allowedTool: string;
|
|
23
|
+
input?: unknown;
|
|
24
|
+
output?: unknown;
|
|
25
|
+
disallowedTool?: string;
|
|
26
|
+
safeError?: boolean;
|
|
27
|
+
timeout?: boolean;
|
|
28
|
+
}
|
|
29
|
+
declare function testMemoryStore(store: MemoryStore): Promise<void>;
|
|
30
|
+
declare function testToolProvider(provider: ToolProvider, contract: ToolProviderContract): Promise<void>;
|
|
31
|
+
declare function testChannelAdapter<TInput, TOutput>(adapter: ChannelAdapter<TInput, TOutput>, contract: ChannelAdapterContract<TInput, TOutput>): Promise<void>;
|
|
15
32
|
declare function testModelGateway(model: ModelGateway, contract?: ModelGatewayContract): Promise<void>;
|
|
16
33
|
declare function testRunStore(store: DurableRunStore): Promise<void>;
|
|
17
34
|
//#endregion
|
|
18
|
-
|
|
35
|
+
//#region src/testing/fake-model.d.ts
|
|
36
|
+
interface FakeModel extends ModelGateway {
|
|
37
|
+
readonly calls: readonly ModelInput[];
|
|
38
|
+
}
|
|
39
|
+
/** Creates a deterministic model gateway for unit tests. */
|
|
40
|
+
declare function fakeModel(...responses: ModelResult[]): FakeModel;
|
|
41
|
+
//#endregion
|
|
42
|
+
export { ChannelAdapterContract, FakeModel, ModelGatewayContract, ToolProviderContract, fakeModel, testChannelAdapter, testMemoryStore, testModelGateway, testRunStore, testToolProvider };
|