@treeseed/agent 0.8.5
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/Dockerfile +7 -0
- package/README.md +198 -0
- package/dist/agent-runtime.d.ts +17 -0
- package/dist/agent-runtime.js +117 -0
- package/dist/agents/adapters/execution.d.ts +41 -0
- package/dist/agents/adapters/execution.js +73 -0
- package/dist/agents/adapters/mutations.d.ts +22 -0
- package/dist/agents/adapters/mutations.js +30 -0
- package/dist/agents/adapters/notification.d.ts +26 -0
- package/dist/agents/adapters/notification.js +46 -0
- package/dist/agents/adapters/repository.d.ts +28 -0
- package/dist/agents/adapters/repository.js +61 -0
- package/dist/agents/adapters/research.d.ts +26 -0
- package/dist/agents/adapters/research.js +59 -0
- package/dist/agents/adapters/verification.d.ts +36 -0
- package/dist/agents/adapters/verification.js +62 -0
- package/dist/agents/cli-tools.d.ts +1 -0
- package/dist/agents/cli-tools.js +5 -0
- package/dist/agents/cli.d.ts +15 -0
- package/dist/agents/cli.js +109 -0
- package/dist/agents/contracts/messages.d.ts +88 -0
- package/dist/agents/contracts/messages.js +138 -0
- package/dist/agents/contracts/run.d.ts +21 -0
- package/dist/agents/contracts/run.js +0 -0
- package/dist/agents/index.d.ts +1 -0
- package/dist/agents/index.js +5 -0
- package/dist/agents/kernel/agent-kernel.d.ts +63 -0
- package/dist/agents/kernel/agent-kernel.js +291 -0
- package/dist/agents/kernel/trigger-resolver.d.ts +19 -0
- package/dist/agents/kernel/trigger-resolver.js +157 -0
- package/dist/agents/registry-helper.d.ts +4 -0
- package/dist/agents/registry-helper.js +14 -0
- package/dist/agents/registry.d.ts +6 -0
- package/dist/agents/registry.js +98 -0
- package/dist/agents/runtime-types.d.ts +118 -0
- package/dist/agents/runtime-types.js +0 -0
- package/dist/agents/spec-loader.d.ts +18 -0
- package/dist/agents/spec-loader.js +54 -0
- package/dist/agents/spec-normalizer.d.ts +2 -0
- package/dist/agents/spec-normalizer.js +327 -0
- package/dist/agents/spec-types.d.ts +64 -0
- package/dist/agents/spec-types.js +0 -0
- package/dist/agents/testing/agents-smoke.d.ts +1 -0
- package/dist/agents/testing/agents-smoke.js +32 -0
- package/dist/agents/testing/e2e-harness.d.ts +44 -0
- package/dist/agents/testing/e2e-harness.js +503 -0
- package/dist/api/agent-routes.d.ts +13 -0
- package/dist/api/agent-routes.js +327 -0
- package/dist/api/app.d.ts +8 -0
- package/dist/api/app.js +444 -0
- package/dist/api/auth/d1-database.d.ts +3 -0
- package/dist/api/auth/d1-database.js +20 -0
- package/dist/api/auth/d1-provider.d.ts +79 -0
- package/dist/api/auth/d1-provider.js +92 -0
- package/dist/api/auth/d1-store.d.ts +114 -0
- package/dist/api/auth/d1-store.js +895 -0
- package/dist/api/auth/memory-provider.d.ts +77 -0
- package/dist/api/auth/memory-provider.js +249 -0
- package/dist/api/auth/rbac.d.ts +22 -0
- package/dist/api/auth/rbac.js +162 -0
- package/dist/api/auth/tokens.d.ts +18 -0
- package/dist/api/auth/tokens.js +56 -0
- package/dist/api/capabilities.d.ts +9 -0
- package/dist/api/capabilities.js +33 -0
- package/dist/api/config.d.ts +2 -0
- package/dist/api/config.js +77 -0
- package/dist/api/http.d.ts +28 -0
- package/dist/api/http.js +51 -0
- package/dist/api/index.d.ts +9 -0
- package/dist/api/index.js +20 -0
- package/dist/api/operations-routes.d.ts +11 -0
- package/dist/api/operations-routes.js +87 -0
- package/dist/api/operations.d.ts +3 -0
- package/dist/api/operations.js +26 -0
- package/dist/api/project-routes.d.ts +8 -0
- package/dist/api/project-routes.js +585 -0
- package/dist/api/providers.d.ts +2 -0
- package/dist/api/providers.js +62 -0
- package/dist/api/railway.d.ts +51 -0
- package/dist/api/railway.js +71 -0
- package/dist/api/sdk-dispatch.d.ts +5 -0
- package/dist/api/sdk-dispatch.js +13 -0
- package/dist/api/sdk-routes.d.ts +11 -0
- package/dist/api/sdk-routes.js +29 -0
- package/dist/api/server.d.ts +2 -0
- package/dist/api/server.js +10 -0
- package/dist/api/templates.d.ts +3 -0
- package/dist/api/templates.js +31 -0
- package/dist/api/types.d.ts +237 -0
- package/dist/api/types.js +0 -0
- package/dist/env.yaml +957 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +41 -0
- package/dist/scripts/assert-release-tag-version.d.ts +1 -0
- package/dist/scripts/assert-release-tag-version.js +20 -0
- package/dist/scripts/build-dist.d.ts +1 -0
- package/dist/scripts/build-dist.js +106 -0
- package/dist/scripts/package-tools.d.ts +1 -0
- package/dist/scripts/package-tools.js +7 -0
- package/dist/scripts/publish-package.d.ts +1 -0
- package/dist/scripts/publish-package.js +24 -0
- package/dist/scripts/release-verify.d.ts +1 -0
- package/dist/scripts/release-verify.js +152 -0
- package/dist/scripts/test-smoke.d.ts +1 -0
- package/dist/scripts/test-smoke.js +23 -0
- package/dist/scripts/treeseed-agent-api.d.ts +2 -0
- package/dist/scripts/treeseed-agent-api.js +25 -0
- package/dist/scripts/treeseed-agent-service.d.ts +2 -0
- package/dist/scripts/treeseed-agent-service.js +36 -0
- package/dist/scripts/treeseed-agents.d.ts +2 -0
- package/dist/scripts/treeseed-agents.js +13 -0
- package/dist/services/agents.d.ts +17 -0
- package/dist/services/agents.js +48 -0
- package/dist/services/common.d.ts +66 -0
- package/dist/services/common.js +212 -0
- package/dist/services/index.d.ts +6 -0
- package/dist/services/index.js +19 -0
- package/dist/services/manager.d.ts +333 -0
- package/dist/services/manager.js +1368 -0
- package/dist/services/remote-runner.d.ts +30 -0
- package/dist/services/remote-runner.js +230 -0
- package/dist/services/workday-content.d.ts +53 -0
- package/dist/services/workday-content.js +190 -0
- package/dist/services/workday-manager.d.ts +391 -0
- package/dist/services/workday-manager.js +163 -0
- package/dist/services/workday-report.d.ts +238 -0
- package/dist/services/workday-report.js +17 -0
- package/dist/services/workday-start.d.ts +238 -0
- package/dist/services/workday-start.js +17 -0
- package/dist/services/worker-capacity.d.ts +58 -0
- package/dist/services/worker-capacity.js +208 -0
- package/dist/services/worker-pool-scaler.d.ts +27 -0
- package/dist/services/worker-pool-scaler.js +127 -0
- package/dist/services/worker.d.ts +19 -0
- package/dist/services/worker.js +436 -0
- package/dist/templates/github/deploy-processing.workflow.yml +119 -0
- package/package.json +136 -0
- package/templates/github/deploy-processing.workflow.yml +119 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { AgentHandlerKind, AgentRuntimeSpec, AgentRunStatus, AgentTriggerConfig } from '@treeseed/sdk/types/agents';
|
|
2
|
+
import type { AgentErrorCategory } from './contracts/run.ts';
|
|
3
|
+
import type { ScopedAgentSdk } from '@treeseed/sdk/sdk';
|
|
4
|
+
import type { SdkMessageEntity } from '@treeseed/sdk/types';
|
|
5
|
+
export interface AgentTriggerInvocation {
|
|
6
|
+
kind: 'startup' | 'schedule' | 'message' | 'manual' | 'follow';
|
|
7
|
+
source: string;
|
|
8
|
+
trigger: AgentTriggerConfig;
|
|
9
|
+
message?: SdkMessageEntity | null;
|
|
10
|
+
followModels?: string[];
|
|
11
|
+
cursorValue?: string | null;
|
|
12
|
+
}
|
|
13
|
+
export interface AgentExecutionResult {
|
|
14
|
+
status: AgentRunStatus;
|
|
15
|
+
summary: string;
|
|
16
|
+
stdout?: string;
|
|
17
|
+
stderr?: string;
|
|
18
|
+
errorCategory?: AgentErrorCategory | null;
|
|
19
|
+
metadata?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
export interface AgentMutationResult {
|
|
22
|
+
branchName: string | null;
|
|
23
|
+
commitMessage: string | null;
|
|
24
|
+
worktreePath: string | null;
|
|
25
|
+
commitSha: string | null;
|
|
26
|
+
changedPaths: string[];
|
|
27
|
+
}
|
|
28
|
+
export interface AgentRepositoryInspectionResult {
|
|
29
|
+
branchName: string | null;
|
|
30
|
+
changedPaths: string[];
|
|
31
|
+
commitSha: string | null;
|
|
32
|
+
summary: string;
|
|
33
|
+
}
|
|
34
|
+
export interface AgentVerificationResult {
|
|
35
|
+
status: 'completed' | 'failed' | 'waiting';
|
|
36
|
+
summary: string;
|
|
37
|
+
stdout?: string;
|
|
38
|
+
stderr?: string;
|
|
39
|
+
errorCategory?: AgentErrorCategory | null;
|
|
40
|
+
}
|
|
41
|
+
export interface AgentNotificationResult {
|
|
42
|
+
status: 'completed' | 'failed' | 'waiting';
|
|
43
|
+
summary: string;
|
|
44
|
+
deliveredCount: number;
|
|
45
|
+
}
|
|
46
|
+
export interface AgentResearchResult {
|
|
47
|
+
status: 'completed' | 'failed' | 'waiting';
|
|
48
|
+
summary: string;
|
|
49
|
+
markdown: string;
|
|
50
|
+
sources?: string[];
|
|
51
|
+
errorCategory?: AgentErrorCategory | null;
|
|
52
|
+
}
|
|
53
|
+
export interface AgentExecutionAdapter {
|
|
54
|
+
runTask(input: {
|
|
55
|
+
agent: AgentRuntimeSpec;
|
|
56
|
+
runId: string;
|
|
57
|
+
prompt: string;
|
|
58
|
+
}): Promise<AgentExecutionResult>;
|
|
59
|
+
}
|
|
60
|
+
export interface AgentMutationAdapter {
|
|
61
|
+
writeArtifact(input: {
|
|
62
|
+
runId: string;
|
|
63
|
+
agent: AgentRuntimeSpec;
|
|
64
|
+
relativePath: string;
|
|
65
|
+
content: string;
|
|
66
|
+
commitMessage: string;
|
|
67
|
+
}): Promise<AgentMutationResult>;
|
|
68
|
+
}
|
|
69
|
+
export interface AgentRepositoryInspectionAdapter {
|
|
70
|
+
inspectBranch(input: {
|
|
71
|
+
repoRoot: string;
|
|
72
|
+
branchName: string | null;
|
|
73
|
+
}): Promise<AgentRepositoryInspectionResult>;
|
|
74
|
+
}
|
|
75
|
+
export interface AgentVerificationAdapter {
|
|
76
|
+
runChecks(input: {
|
|
77
|
+
agent: AgentRuntimeSpec;
|
|
78
|
+
runId: string;
|
|
79
|
+
commands: string[];
|
|
80
|
+
}): Promise<AgentVerificationResult>;
|
|
81
|
+
}
|
|
82
|
+
export interface AgentNotificationAdapter {
|
|
83
|
+
deliver(input: {
|
|
84
|
+
agent: AgentRuntimeSpec;
|
|
85
|
+
runId: string;
|
|
86
|
+
recipients: string[];
|
|
87
|
+
subject: string;
|
|
88
|
+
body: string;
|
|
89
|
+
}): Promise<AgentNotificationResult>;
|
|
90
|
+
}
|
|
91
|
+
export interface AgentResearchAdapter {
|
|
92
|
+
research(input: {
|
|
93
|
+
agent: AgentRuntimeSpec;
|
|
94
|
+
runId: string;
|
|
95
|
+
questionId: string;
|
|
96
|
+
reason: string | null;
|
|
97
|
+
}): Promise<AgentResearchResult>;
|
|
98
|
+
}
|
|
99
|
+
export interface AgentContext {
|
|
100
|
+
runId: string;
|
|
101
|
+
repoRoot: string;
|
|
102
|
+
agent: AgentRuntimeSpec;
|
|
103
|
+
sdk: ScopedAgentSdk;
|
|
104
|
+
trigger: AgentTriggerInvocation;
|
|
105
|
+
execution: AgentExecutionAdapter;
|
|
106
|
+
mutations: AgentMutationAdapter;
|
|
107
|
+
repository: AgentRepositoryInspectionAdapter;
|
|
108
|
+
verification: AgentVerificationAdapter;
|
|
109
|
+
notifications: AgentNotificationAdapter;
|
|
110
|
+
research: AgentResearchAdapter;
|
|
111
|
+
}
|
|
112
|
+
export interface AgentHandler<TInputs = unknown, TResult = unknown> {
|
|
113
|
+
kind: AgentHandlerKind;
|
|
114
|
+
resolveInputs(context: AgentContext): Promise<TInputs>;
|
|
115
|
+
execute(context: AgentContext, inputs: TInputs): Promise<TResult>;
|
|
116
|
+
emitOutputs(context: AgentContext, result: TResult): Promise<AgentExecutionResult>;
|
|
117
|
+
}
|
|
118
|
+
export {};
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AgentRuntimeSpec } from '@treeseed/sdk/types/agents';
|
|
2
|
+
import { AgentSdk } from '@treeseed/sdk/sdk';
|
|
3
|
+
import type { AgentSpecDiagnostic, NormalizedAgentRuntimeSpec } from './spec-types.ts';
|
|
4
|
+
export interface AgentSpecLoadResult {
|
|
5
|
+
specs: NormalizedAgentRuntimeSpec[];
|
|
6
|
+
diagnostics: AgentSpecDiagnostic[];
|
|
7
|
+
}
|
|
8
|
+
export declare function loadAgentSpecs(sdk: AgentSdk, options?: {
|
|
9
|
+
enabled?: boolean;
|
|
10
|
+
}): Promise<AgentSpecLoadResult>;
|
|
11
|
+
export declare function loadActiveAgentSpecs(sdk: AgentSdk): Promise<AgentSpecLoadResult>;
|
|
12
|
+
export declare function loadAllAgentSpecs(sdk: AgentSdk): Promise<AgentSpecLoadResult>;
|
|
13
|
+
export declare function summarizeAgentSpec(agent: AgentRuntimeSpec): {
|
|
14
|
+
slug: string;
|
|
15
|
+
handler: string;
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
triggers: ("message" | "follow" | "schedule" | "startup")[];
|
|
18
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { AGENT_MESSAGE_TYPES } from "./contracts/messages.js";
|
|
2
|
+
import { listRegisteredAgentHandlers } from "./registry.js";
|
|
3
|
+
import { normalizeAgentRuntimeSpec } from "./spec-normalizer.js";
|
|
4
|
+
function extractRawSpec(entry) {
|
|
5
|
+
const frontmatter = entry.frontmatter && typeof entry.frontmatter === "object" ? entry.frontmatter : {};
|
|
6
|
+
return {
|
|
7
|
+
...frontmatter,
|
|
8
|
+
body: entry.body,
|
|
9
|
+
id: entry.id
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
async function loadAgentSpecs(sdk, options) {
|
|
13
|
+
const entries = typeof sdk.listRawAgentSpecs === "function" ? await sdk.listRawAgentSpecs(options) : (await sdk.listAgentSpecs(options)).map(
|
|
14
|
+
(spec) => ({
|
|
15
|
+
id: spec.slug,
|
|
16
|
+
body: "",
|
|
17
|
+
frontmatter: spec
|
|
18
|
+
})
|
|
19
|
+
);
|
|
20
|
+
const diagnostics = [];
|
|
21
|
+
const specs = [];
|
|
22
|
+
for (const entry of entries) {
|
|
23
|
+
const registeredHandlers = await listRegisteredAgentHandlers();
|
|
24
|
+
const result = normalizeAgentRuntimeSpec(extractRawSpec(entry), {
|
|
25
|
+
registeredHandlers,
|
|
26
|
+
messageTypes: [...AGENT_MESSAGE_TYPES]
|
|
27
|
+
});
|
|
28
|
+
diagnostics.push(...result.diagnostics);
|
|
29
|
+
if (result.spec) {
|
|
30
|
+
specs.push(result.spec);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return { specs, diagnostics };
|
|
34
|
+
}
|
|
35
|
+
async function loadActiveAgentSpecs(sdk) {
|
|
36
|
+
return loadAgentSpecs(sdk, { enabled: true });
|
|
37
|
+
}
|
|
38
|
+
async function loadAllAgentSpecs(sdk) {
|
|
39
|
+
return loadAgentSpecs(sdk);
|
|
40
|
+
}
|
|
41
|
+
function summarizeAgentSpec(agent) {
|
|
42
|
+
return {
|
|
43
|
+
slug: agent.slug,
|
|
44
|
+
handler: agent.handler,
|
|
45
|
+
enabled: agent.enabled,
|
|
46
|
+
triggers: agent.triggers.map((trigger) => trigger.type)
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export {
|
|
50
|
+
loadActiveAgentSpecs,
|
|
51
|
+
loadAgentSpecs,
|
|
52
|
+
loadAllAgentSpecs,
|
|
53
|
+
summarizeAgentSpec
|
|
54
|
+
};
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AGENT_CLI_ALLOW_TOOLS
|
|
3
|
+
} from "@treeseed/sdk/types/agents";
|
|
4
|
+
import { AGENT_MESSAGE_TYPES } from "./contracts/messages.js";
|
|
5
|
+
import { normalizeAgentCliOptions } from "./cli-tools.js";
|
|
6
|
+
const TRIGGER_KINDS = ["schedule", "message", "follow", "startup"];
|
|
7
|
+
const PERMISSION_OPERATIONS = ["get", "search", "follow", "pick", "create", "update"];
|
|
8
|
+
function isPlainObject(value) {
|
|
9
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
function ensureString(value, field, diagnostics, slug) {
|
|
12
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
13
|
+
diagnostics.push({
|
|
14
|
+
severity: "error",
|
|
15
|
+
slug,
|
|
16
|
+
field,
|
|
17
|
+
message: `Expected ${field} to be a non-empty string.`
|
|
18
|
+
});
|
|
19
|
+
return "";
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
function ensureBoolean(value, field, diagnostics, slug, fallback = false) {
|
|
24
|
+
if (value === void 0) {
|
|
25
|
+
return fallback;
|
|
26
|
+
}
|
|
27
|
+
if (typeof value !== "boolean") {
|
|
28
|
+
diagnostics.push({
|
|
29
|
+
severity: "error",
|
|
30
|
+
slug,
|
|
31
|
+
field,
|
|
32
|
+
message: `Expected ${field} to be a boolean.`
|
|
33
|
+
});
|
|
34
|
+
return fallback;
|
|
35
|
+
}
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
function ensurePositiveNumber(value, field, diagnostics, slug, fallback, allowZero = false) {
|
|
39
|
+
if (value === void 0) {
|
|
40
|
+
return fallback;
|
|
41
|
+
}
|
|
42
|
+
if (typeof value !== "number" || Number.isNaN(value) || !allowZero && value <= 0 || allowZero && value < 0) {
|
|
43
|
+
diagnostics.push({
|
|
44
|
+
severity: "error",
|
|
45
|
+
slug,
|
|
46
|
+
field,
|
|
47
|
+
message: `Expected ${field} to be ${allowZero ? "a non-negative" : "a positive"} number.`
|
|
48
|
+
});
|
|
49
|
+
return fallback;
|
|
50
|
+
}
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
function normalizeTrigger(value, index, diagnostics, slug) {
|
|
54
|
+
if (!isPlainObject(value)) {
|
|
55
|
+
diagnostics.push({
|
|
56
|
+
severity: "error",
|
|
57
|
+
slug,
|
|
58
|
+
field: `triggers[${index}]`,
|
|
59
|
+
message: "Expected trigger to be an object."
|
|
60
|
+
});
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const type = ensureString(value.type, `triggers[${index}].type`, diagnostics, slug);
|
|
64
|
+
if (!TRIGGER_KINDS.includes(type)) {
|
|
65
|
+
diagnostics.push({
|
|
66
|
+
severity: "error",
|
|
67
|
+
slug,
|
|
68
|
+
field: `triggers[${index}].type`,
|
|
69
|
+
message: `Unsupported trigger type "${String(value.type ?? "")}".`
|
|
70
|
+
});
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
type,
|
|
75
|
+
cron: typeof value.cron === "string" ? value.cron : void 0,
|
|
76
|
+
messageTypes: Array.isArray(value.messageTypes) ? value.messageTypes.filter((entry) => typeof entry === "string") : [],
|
|
77
|
+
models: Array.isArray(value.models) ? value.models.filter((entry) => typeof entry === "string") : [],
|
|
78
|
+
sinceField: typeof value.sinceField === "string" ? value.sinceField : void 0,
|
|
79
|
+
runOnStart: typeof value.runOnStart === "boolean" ? value.runOnStart : false
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function normalizePermissions(value, diagnostics, slug) {
|
|
83
|
+
if (!Array.isArray(value)) {
|
|
84
|
+
diagnostics.push({
|
|
85
|
+
severity: "error",
|
|
86
|
+
slug,
|
|
87
|
+
field: "permissions",
|
|
88
|
+
message: "Expected permissions to be an array."
|
|
89
|
+
});
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
return value.flatMap((entry, index) => {
|
|
93
|
+
if (!isPlainObject(entry)) {
|
|
94
|
+
diagnostics.push({
|
|
95
|
+
severity: "error",
|
|
96
|
+
slug,
|
|
97
|
+
field: `permissions[${index}]`,
|
|
98
|
+
message: "Expected permission entry to be an object."
|
|
99
|
+
});
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
const model = ensureString(entry.model, `permissions[${index}].model`, diagnostics, slug);
|
|
103
|
+
const operations = Array.isArray(entry.operations) ? entry.operations.filter(
|
|
104
|
+
(operation) => typeof operation === "string" && PERMISSION_OPERATIONS.includes(operation)
|
|
105
|
+
) : [];
|
|
106
|
+
if (!operations.length) {
|
|
107
|
+
diagnostics.push({
|
|
108
|
+
severity: "error",
|
|
109
|
+
slug,
|
|
110
|
+
field: `permissions[${index}].operations`,
|
|
111
|
+
message: "Expected at least one valid permission operation."
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return model ? [{ model, operations }] : [];
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
function normalizeExecution(value, diagnostics, slug) {
|
|
118
|
+
const next = isPlainObject(value) ? value : {};
|
|
119
|
+
return {
|
|
120
|
+
maxConcurrency: ensurePositiveNumber(next.maxConcurrency, "execution.maxConcurrency", diagnostics, slug, 1),
|
|
121
|
+
timeoutSeconds: ensurePositiveNumber(next.timeoutSeconds, "execution.timeoutSeconds", diagnostics, slug, 900),
|
|
122
|
+
cooldownSeconds: ensurePositiveNumber(next.cooldownSeconds, "execution.cooldownSeconds", diagnostics, slug, 30, true),
|
|
123
|
+
leaseSeconds: ensurePositiveNumber(next.leaseSeconds, "execution.leaseSeconds", diagnostics, slug, 300),
|
|
124
|
+
retryLimit: ensurePositiveNumber(next.retryLimit, "execution.retryLimit", diagnostics, slug, 3, true),
|
|
125
|
+
branchPrefix: ensureString(next.branchPrefix ?? "agent", "execution.branchPrefix", diagnostics, slug) || "agent",
|
|
126
|
+
providerProfile: normalizeProviderProfile(next.providerProfile, diagnostics, slug)
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function normalizeWeightedLaneList(value, field, diagnostics, slug) {
|
|
130
|
+
if (value === void 0) return [];
|
|
131
|
+
if (!Array.isArray(value)) {
|
|
132
|
+
diagnostics.push({
|
|
133
|
+
severity: "error",
|
|
134
|
+
slug,
|
|
135
|
+
field,
|
|
136
|
+
message: `Expected ${field} to be an array.`
|
|
137
|
+
});
|
|
138
|
+
return [];
|
|
139
|
+
}
|
|
140
|
+
return value.flatMap((entry, index) => {
|
|
141
|
+
if (!isPlainObject(entry)) {
|
|
142
|
+
diagnostics.push({
|
|
143
|
+
severity: "error",
|
|
144
|
+
slug,
|
|
145
|
+
field: `${field}[${index}]`,
|
|
146
|
+
message: "Expected provider lane entry to be an object."
|
|
147
|
+
});
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
150
|
+
return [{
|
|
151
|
+
providerId: typeof entry.providerId === "string" ? entry.providerId : void 0,
|
|
152
|
+
provider: typeof entry.provider === "string" ? entry.provider : void 0,
|
|
153
|
+
laneId: typeof entry.laneId === "string" ? entry.laneId : void 0,
|
|
154
|
+
model: typeof entry.model === "string" ? entry.model : void 0,
|
|
155
|
+
modelClass: typeof entry.modelClass === "string" ? entry.modelClass : void 0,
|
|
156
|
+
weight: ensurePositiveNumber(entry.weight, `${field}[${index}].weight`, diagnostics, slug, 1, true),
|
|
157
|
+
reason: typeof entry.reason === "string" ? entry.reason : void 0,
|
|
158
|
+
maxQualityPenalty: typeof entry.maxQualityPenalty === "number" ? entry.maxQualityPenalty : void 0
|
|
159
|
+
}];
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
function normalizeProviderProfile(value, diagnostics, slug) {
|
|
163
|
+
if (value === void 0) return void 0;
|
|
164
|
+
if (!isPlainObject(value)) {
|
|
165
|
+
diagnostics.push({
|
|
166
|
+
severity: "error",
|
|
167
|
+
slug,
|
|
168
|
+
field: "execution.providerProfile",
|
|
169
|
+
message: "Expected execution.providerProfile to be an object."
|
|
170
|
+
});
|
|
171
|
+
return void 0;
|
|
172
|
+
}
|
|
173
|
+
const fallbackPolicy = typeof value.fallbackPolicy === "string" ? value.fallbackPolicy : "allow_substitution";
|
|
174
|
+
if (!["allow_substitution", "require_same_model_class", "fail_if_unavailable", "ask_for_approval"].includes(fallbackPolicy)) {
|
|
175
|
+
diagnostics.push({
|
|
176
|
+
severity: "error",
|
|
177
|
+
slug,
|
|
178
|
+
field: "execution.providerProfile.fallbackPolicy",
|
|
179
|
+
message: `Unsupported fallback policy "${String(value.fallbackPolicy)}".`
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
requiredCapabilities: Array.isArray(value.requiredCapabilities) ? value.requiredCapabilities.filter((entry) => typeof entry === "string" && entry.trim().length > 0) : [],
|
|
184
|
+
preferredLanes: normalizeWeightedLaneList(value.preferredLanes, "execution.providerProfile.preferredLanes", diagnostics, slug),
|
|
185
|
+
acceptableFallbacks: normalizeWeightedLaneList(value.acceptableFallbacks, "execution.providerProfile.acceptableFallbacks", diagnostics, slug).map((entry) => ({
|
|
186
|
+
providerId: entry.providerId,
|
|
187
|
+
provider: entry.provider,
|
|
188
|
+
laneId: entry.laneId,
|
|
189
|
+
model: entry.model,
|
|
190
|
+
modelClass: entry.modelClass,
|
|
191
|
+
maxQualityPenalty: entry.maxQualityPenalty
|
|
192
|
+
})),
|
|
193
|
+
disallowedProviders: Array.isArray(value.disallowedProviders) ? value.disallowedProviders.filter((entry) => typeof entry === "string") : void 0,
|
|
194
|
+
disallowedRegions: Array.isArray(value.disallowedRegions) ? value.disallowedRegions.filter((entry) => typeof entry === "string") : void 0,
|
|
195
|
+
fallbackPolicy
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function normalizeOutputs(value, _diagnostics, _slug) {
|
|
199
|
+
const next = isPlainObject(value) ? value : {};
|
|
200
|
+
return {
|
|
201
|
+
messageTypes: Array.isArray(next.messageTypes) ? next.messageTypes.filter((entry) => typeof entry === "string") : [],
|
|
202
|
+
modelMutations: Array.isArray(next.modelMutations) ? next.modelMutations.filter((entry) => typeof entry === "string") : []
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
function normalizeParts(raw, slugHint, diagnostics) {
|
|
206
|
+
const slug = ensureString(raw.slug ?? slugHint, "slug", diagnostics, slugHint) || slugHint;
|
|
207
|
+
const handler = ensureString(raw.handler, "handler", diagnostics, slug);
|
|
208
|
+
const triggers = Array.isArray(raw.triggers) ? raw.triggers.map((entry, index) => normalizeTrigger(entry, index, diagnostics, slug)).filter((entry) => Boolean(entry)) : [];
|
|
209
|
+
if (!triggers.length) {
|
|
210
|
+
diagnostics.push({
|
|
211
|
+
severity: "error",
|
|
212
|
+
slug,
|
|
213
|
+
field: "triggers",
|
|
214
|
+
message: "Expected at least one trigger."
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
try {
|
|
218
|
+
const cli = normalizeAgentCliOptions(raw.cli);
|
|
219
|
+
const spec = {
|
|
220
|
+
slug,
|
|
221
|
+
handler,
|
|
222
|
+
enabled: ensureBoolean(raw.enabled, "enabled", diagnostics, slug, true),
|
|
223
|
+
systemPrompt: ensureString(raw.systemPrompt, "systemPrompt", diagnostics, slug),
|
|
224
|
+
persona: ensureString(raw.persona, "persona", diagnostics, slug),
|
|
225
|
+
cli,
|
|
226
|
+
triggers,
|
|
227
|
+
triggerPolicy: isPlainObject(raw.triggerPolicy) ? {
|
|
228
|
+
maxRunsPerCycle: typeof raw.triggerPolicy.maxRunsPerCycle === "number" ? raw.triggerPolicy.maxRunsPerCycle : void 0,
|
|
229
|
+
messageBatchSize: typeof raw.triggerPolicy.messageBatchSize === "number" ? raw.triggerPolicy.messageBatchSize : void 0
|
|
230
|
+
} : void 0,
|
|
231
|
+
permissions: normalizePermissions(raw.permissions, diagnostics, slug),
|
|
232
|
+
execution: normalizeExecution(raw.execution, diagnostics, slug),
|
|
233
|
+
outputs: normalizeOutputs(raw.outputs, diagnostics, slug)
|
|
234
|
+
};
|
|
235
|
+
return spec;
|
|
236
|
+
} catch (error) {
|
|
237
|
+
diagnostics.push({
|
|
238
|
+
severity: "error",
|
|
239
|
+
slug,
|
|
240
|
+
field: "cli",
|
|
241
|
+
message: error instanceof Error ? error.message : String(error)
|
|
242
|
+
});
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function normalizeAgentRuntimeSpec(raw, context) {
|
|
247
|
+
const slugHint = typeof raw.slug === "string" && raw.slug ? raw.slug : "unknown-agent";
|
|
248
|
+
const diagnostics = [];
|
|
249
|
+
const parts = normalizeParts(raw, slugHint, diagnostics);
|
|
250
|
+
if (!parts) {
|
|
251
|
+
return { spec: null, diagnostics };
|
|
252
|
+
}
|
|
253
|
+
const spec = {
|
|
254
|
+
...parts,
|
|
255
|
+
name: typeof raw.name === "string" ? raw.name : void 0,
|
|
256
|
+
description: typeof raw.description === "string" ? raw.description : void 0,
|
|
257
|
+
summary: typeof raw.summary === "string" ? raw.summary : void 0,
|
|
258
|
+
operator: typeof raw.operator === "string" ? raw.operator : void 0,
|
|
259
|
+
runtimeStatus: typeof raw.runtimeStatus === "string" ? raw.runtimeStatus : void 0,
|
|
260
|
+
capabilities: Array.isArray(raw.capabilities) ? raw.capabilities.filter((entry) => typeof entry === "string") : [],
|
|
261
|
+
tags: Array.isArray(raw.tags) ? raw.tags.filter((entry) => typeof entry === "string") : []
|
|
262
|
+
};
|
|
263
|
+
if (!context.registeredHandlers.includes(spec.handler)) {
|
|
264
|
+
diagnostics.push({
|
|
265
|
+
severity: "error",
|
|
266
|
+
slug: spec.slug,
|
|
267
|
+
field: "handler",
|
|
268
|
+
message: `No runtime handler is registered for "${spec.handler}".`
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
for (const trigger of spec.triggers) {
|
|
272
|
+
if (trigger.type === "message") {
|
|
273
|
+
const messagePermission = spec.permissions.find((permission) => permission.model === "message");
|
|
274
|
+
if (!messagePermission || !messagePermission.operations.includes("pick") || !messagePermission.operations.includes("update")) {
|
|
275
|
+
diagnostics.push({
|
|
276
|
+
severity: "error",
|
|
277
|
+
slug: spec.slug,
|
|
278
|
+
field: "permissions",
|
|
279
|
+
message: "Message-triggered agents must allow message pick and update operations."
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
for (const messageType of trigger.messageTypes ?? []) {
|
|
283
|
+
if (!context.messageTypes.includes(messageType) || !AGENT_MESSAGE_TYPES.includes(messageType)) {
|
|
284
|
+
diagnostics.push({
|
|
285
|
+
severity: "error",
|
|
286
|
+
slug: spec.slug,
|
|
287
|
+
field: "triggers.messageTypes",
|
|
288
|
+
message: `Unknown message trigger type "${messageType}".`
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (trigger.type === "follow" && !trigger.models?.length) {
|
|
294
|
+
diagnostics.push({
|
|
295
|
+
severity: "error",
|
|
296
|
+
slug: spec.slug,
|
|
297
|
+
field: "triggers.models",
|
|
298
|
+
message: "Follow triggers must declare at least one model."
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
for (const messageType of spec.outputs.messageTypes) {
|
|
303
|
+
if (!context.messageTypes.includes(messageType) || !AGENT_MESSAGE_TYPES.includes(messageType)) {
|
|
304
|
+
diagnostics.push({
|
|
305
|
+
severity: "error",
|
|
306
|
+
slug: spec.slug,
|
|
307
|
+
field: "outputs.messageTypes",
|
|
308
|
+
message: `Unknown emitted message type "${messageType}".`
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (spec.cli.allowTools?.some((tool) => !AGENT_CLI_ALLOW_TOOLS.includes(tool))) {
|
|
313
|
+
diagnostics.push({
|
|
314
|
+
severity: "error",
|
|
315
|
+
slug: spec.slug,
|
|
316
|
+
field: "cli.allowTools",
|
|
317
|
+
message: "Agent declared an unsupported tool allowance."
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
spec: diagnostics.some((entry) => entry.severity === "error") ? null : spec,
|
|
322
|
+
diagnostics
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
export {
|
|
326
|
+
normalizeAgentRuntimeSpec
|
|
327
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { AgentCliOptions, AgentExecutionConfig, AgentHandlerKind, AgentOutputContract, AgentPermissionConfig, AgentTriggerConfig } from '@treeseed/sdk/types/agents';
|
|
2
|
+
export type AgentSpecDiagnosticSeverity = 'error' | 'warning';
|
|
3
|
+
export interface AgentSpecDiagnostic {
|
|
4
|
+
severity: AgentSpecDiagnosticSeverity;
|
|
5
|
+
slug: string;
|
|
6
|
+
field: string;
|
|
7
|
+
message: string;
|
|
8
|
+
}
|
|
9
|
+
export interface AgentSpecValidationContext {
|
|
10
|
+
registeredHandlers: readonly AgentHandlerKind[];
|
|
11
|
+
messageTypes: readonly string[];
|
|
12
|
+
}
|
|
13
|
+
export interface RawAgentRuntimeSpec {
|
|
14
|
+
id?: unknown;
|
|
15
|
+
body?: unknown;
|
|
16
|
+
slug?: unknown;
|
|
17
|
+
handler?: unknown;
|
|
18
|
+
enabled?: unknown;
|
|
19
|
+
systemPrompt?: unknown;
|
|
20
|
+
persona?: unknown;
|
|
21
|
+
cli?: unknown;
|
|
22
|
+
triggers?: unknown;
|
|
23
|
+
triggerPolicy?: unknown;
|
|
24
|
+
permissions?: unknown;
|
|
25
|
+
execution?: unknown;
|
|
26
|
+
outputs?: unknown;
|
|
27
|
+
name?: unknown;
|
|
28
|
+
description?: unknown;
|
|
29
|
+
summary?: unknown;
|
|
30
|
+
operator?: unknown;
|
|
31
|
+
runtimeStatus?: unknown;
|
|
32
|
+
capabilities?: unknown;
|
|
33
|
+
tags?: unknown;
|
|
34
|
+
}
|
|
35
|
+
export interface AgentSpecNormalizationResult {
|
|
36
|
+
spec: NormalizedAgentRuntimeSpec | null;
|
|
37
|
+
diagnostics: AgentSpecDiagnostic[];
|
|
38
|
+
}
|
|
39
|
+
export interface NormalizedTriggerPolicy {
|
|
40
|
+
maxRunsPerCycle?: number;
|
|
41
|
+
messageBatchSize?: number;
|
|
42
|
+
}
|
|
43
|
+
export interface AgentSpecParts {
|
|
44
|
+
slug: string;
|
|
45
|
+
handler: AgentHandlerKind;
|
|
46
|
+
enabled: boolean;
|
|
47
|
+
systemPrompt: string;
|
|
48
|
+
persona: string;
|
|
49
|
+
cli: AgentCliOptions;
|
|
50
|
+
triggers: AgentTriggerConfig[];
|
|
51
|
+
triggerPolicy?: NormalizedTriggerPolicy;
|
|
52
|
+
permissions: AgentPermissionConfig[];
|
|
53
|
+
execution: AgentExecutionConfig;
|
|
54
|
+
outputs: AgentOutputContract;
|
|
55
|
+
}
|
|
56
|
+
export type NormalizedAgentRuntimeSpec = AgentSpecParts & {
|
|
57
|
+
name?: string;
|
|
58
|
+
description?: string;
|
|
59
|
+
summary?: string;
|
|
60
|
+
operator?: string;
|
|
61
|
+
runtimeStatus?: string;
|
|
62
|
+
capabilities?: string[];
|
|
63
|
+
tags?: string[];
|
|
64
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createExecutionAdapter } from "../adapters/execution.js";
|
|
2
|
+
import { createAgentTestRuntime } from "./e2e-harness.js";
|
|
3
|
+
async function main() {
|
|
4
|
+
const target = process.argv[2] ?? "mvp";
|
|
5
|
+
const execution = createExecutionAdapter();
|
|
6
|
+
const runtime = await createAgentTestRuntime({
|
|
7
|
+
execution,
|
|
8
|
+
executionMode: "copilot",
|
|
9
|
+
databaseMode: process.env.TREESEED_AGENT_DATABASE_MODE === "local-d1" ? "local-d1" : "memory"
|
|
10
|
+
});
|
|
11
|
+
try {
|
|
12
|
+
await runtime.seedObjectives([{ slug: "smoke-objective" }]);
|
|
13
|
+
await runtime.seedQuestions([
|
|
14
|
+
{
|
|
15
|
+
slug: "smoke-question",
|
|
16
|
+
relatedObjectives: ["smoke-objective"]
|
|
17
|
+
}
|
|
18
|
+
]);
|
|
19
|
+
const result = target === "mvp" ? await runtime.runCycle() : await runtime.runAgent(target);
|
|
20
|
+
const runs = await runtime.readRunLogs();
|
|
21
|
+
const messages = await runtime.readMessages();
|
|
22
|
+
console.log(JSON.stringify({
|
|
23
|
+
target,
|
|
24
|
+
result,
|
|
25
|
+
runs,
|
|
26
|
+
messages
|
|
27
|
+
}, null, 2));
|
|
28
|
+
} finally {
|
|
29
|
+
await runtime.cleanup();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
void main();
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { AgentSdk } from '@treeseed/sdk/sdk';
|
|
2
|
+
import { type SdkCreateMessageRequest, type SdkMessageEntity, type SdkRunEntity } from '@treeseed/sdk/types';
|
|
3
|
+
import type { AgentExecutionAdapter, AgentMutationAdapter } from '../runtime-types.ts';
|
|
4
|
+
import type { AgentKernel } from '../kernel/agent-kernel.ts';
|
|
5
|
+
export interface AgentTestRuntime {
|
|
6
|
+
rootDir: string;
|
|
7
|
+
repoRoot: string;
|
|
8
|
+
persistTo: string;
|
|
9
|
+
sdk: AgentSdk;
|
|
10
|
+
kernel: AgentKernel;
|
|
11
|
+
seedObjectives(entries: Array<{
|
|
12
|
+
slug: string;
|
|
13
|
+
date?: string;
|
|
14
|
+
}>): Promise<void>;
|
|
15
|
+
seedQuestions(entries: Array<{
|
|
16
|
+
slug: string;
|
|
17
|
+
date?: string;
|
|
18
|
+
relatedObjectives?: string[];
|
|
19
|
+
}>): Promise<void>;
|
|
20
|
+
seedKnowledge(entries: Array<{
|
|
21
|
+
slug: string;
|
|
22
|
+
title?: string;
|
|
23
|
+
}>): Promise<void>;
|
|
24
|
+
seedMessages(entries: Array<Omit<SdkCreateMessageRequest, 'actor'>>): Promise<SdkMessageEntity[]>;
|
|
25
|
+
clearModelContent(model: 'objective' | 'question' | 'knowledge'): Promise<void>;
|
|
26
|
+
runAgent(slug: string): Promise<unknown>;
|
|
27
|
+
runCycle(): Promise<unknown>;
|
|
28
|
+
readMessages(): Promise<SdkMessageEntity[]>;
|
|
29
|
+
readRunLogs(): Promise<SdkRunEntity[]>;
|
|
30
|
+
readContentLeases(): Promise<Record<string, unknown>[]>;
|
|
31
|
+
readSandboxArtifacts(): Promise<Array<{
|
|
32
|
+
path: string;
|
|
33
|
+
content: string;
|
|
34
|
+
}>>;
|
|
35
|
+
claimMessage(messageTypes: string[], workerId?: string): Promise<SdkMessageEntity | null>;
|
|
36
|
+
claimObjectiveLease(itemKey: string, workerId?: string): Promise<string | null>;
|
|
37
|
+
cleanup(): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
export declare function createAgentTestRuntime(options?: {
|
|
40
|
+
execution?: AgentExecutionAdapter;
|
|
41
|
+
mutations?: AgentMutationAdapter;
|
|
42
|
+
executionMode?: 'stub' | 'copilot';
|
|
43
|
+
databaseMode?: 'memory' | 'local-d1';
|
|
44
|
+
}): Promise<AgentTestRuntime>;
|