@treeseed/agent 0.1.2
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 +62 -0
- package/dist/agent-runtime.js +111 -0
- package/dist/agents/adapters/execution.js +90 -0
- package/dist/agents/adapters/mutations.js +30 -0
- package/dist/agents/adapters/notification.js +16 -0
- package/dist/agents/adapters/repository.js +61 -0
- package/dist/agents/adapters/research.js +25 -0
- package/dist/agents/adapters/verification.js +62 -0
- package/dist/agents/cli-tools.js +5 -0
- package/dist/agents/cli.js +77 -0
- package/dist/agents/content-store.js +1 -0
- package/dist/agents/contracts/messages.js +138 -0
- package/dist/agents/contracts/run.js +0 -0
- package/dist/agents/d1-store.js +1 -0
- package/dist/agents/frontmatter.js +1 -0
- package/dist/agents/git-runtime.js +1 -0
- package/dist/agents/index.js +5 -0
- package/dist/agents/kernel/agent-kernel.js +284 -0
- package/dist/agents/kernel/trigger-resolver.js +153 -0
- package/dist/agents/model-registry.js +1 -0
- package/dist/agents/registry-helper.js +14 -0
- package/dist/agents/registry.js +91 -0
- package/dist/agents/runtime-types.js +0 -0
- package/dist/agents/sdk-filters.js +1 -0
- package/dist/agents/sdk-types.js +1 -0
- package/dist/agents/sdk.js +1 -0
- package/dist/agents/spec-loader.js +53 -0
- package/dist/agents/spec-normalizer.js +257 -0
- package/dist/agents/spec-types.js +0 -0
- package/dist/agents/stores/cursor-store.js +1 -0
- package/dist/agents/stores/helpers.js +1 -0
- package/dist/agents/stores/lease-store.js +1 -0
- package/dist/agents/stores/message-store.js +1 -0
- package/dist/agents/stores/run-store.js +1 -0
- package/dist/agents/stores/subscription-store.js +1 -0
- package/dist/agents/testing/agents-smoke.js +32 -0
- package/dist/agents/testing/e2e-harness.js +435 -0
- package/dist/agents/wrangler-d1.js +1 -0
- package/dist/index.js +9 -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 +98 -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 +19 -0
- package/dist/scripts/release-verify.d.ts +1 -0
- package/dist/scripts/release-verify.js +143 -0
- package/dist/scripts/test-smoke.d.ts +1 -0
- package/dist/scripts/test-smoke.js +23 -0
- package/dist/scripts/treeseed-agents.d.ts +2 -0
- package/dist/scripts/treeseed-agents.js +13 -0
- package/dist/src/agent-runtime.d.ts +17 -0
- package/dist/src/agents/adapters/execution.d.ts +46 -0
- package/dist/src/agents/adapters/mutations.d.ts +22 -0
- package/dist/src/agents/adapters/notification.d.ts +11 -0
- package/dist/src/agents/adapters/repository.d.ts +28 -0
- package/dist/src/agents/adapters/research.d.ts +14 -0
- package/dist/src/agents/adapters/verification.d.ts +36 -0
- package/dist/src/agents/cli-tools.d.ts +1 -0
- package/dist/src/agents/cli.d.ts +6 -0
- package/dist/src/agents/content-store.d.ts +1 -0
- package/dist/src/agents/contracts/messages.d.ts +88 -0
- package/dist/src/agents/contracts/run.d.ts +20 -0
- package/dist/src/agents/d1-store.d.ts +1 -0
- package/dist/src/agents/frontmatter.d.ts +1 -0
- package/dist/src/agents/git-runtime.d.ts +1 -0
- package/dist/src/agents/index.d.ts +1 -0
- package/dist/src/agents/kernel/agent-kernel.d.ts +52 -0
- package/dist/src/agents/kernel/trigger-resolver.d.ts +18 -0
- package/dist/src/agents/model-registry.d.ts +1 -0
- package/dist/src/agents/registry-helper.d.ts +4 -0
- package/dist/src/agents/registry.d.ts +7 -0
- package/dist/src/agents/runtime-types.d.ts +117 -0
- package/dist/src/agents/sdk-filters.d.ts +1 -0
- package/dist/src/agents/sdk-types.d.ts +1 -0
- package/dist/src/agents/sdk.d.ts +1 -0
- package/dist/src/agents/spec-loader.d.ts +18 -0
- package/dist/src/agents/spec-normalizer.d.ts +2 -0
- package/dist/src/agents/spec-types.d.ts +64 -0
- package/dist/src/agents/stores/cursor-store.d.ts +1 -0
- package/dist/src/agents/stores/helpers.d.ts +1 -0
- package/dist/src/agents/stores/lease-store.d.ts +1 -0
- package/dist/src/agents/stores/message-store.d.ts +1 -0
- package/dist/src/agents/stores/run-store.d.ts +1 -0
- package/dist/src/agents/stores/subscription-store.d.ts +1 -0
- package/dist/src/agents/testing/agents-smoke.d.ts +1 -0
- package/dist/src/agents/testing/e2e-harness.d.ts +44 -0
- package/dist/src/agents/wrangler-d1.d.ts +1 -0
- package/dist/src/index.d.ts +3 -0
- package/package.json +54 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { getTreeseedAgentProviderSelections } from "@treeseed/core/deploy/runtime";
|
|
5
|
+
import { resolveTreeseedTenantRoot } from "@treeseed/core/tenant-config";
|
|
6
|
+
import { resolveAgentRuntimeProviders } from "../agent-runtime.js";
|
|
7
|
+
const BUILTIN_HANDLER_KINDS = [
|
|
8
|
+
"planner",
|
|
9
|
+
"architect",
|
|
10
|
+
"engineer",
|
|
11
|
+
"notifier",
|
|
12
|
+
"researcher",
|
|
13
|
+
"reviewer",
|
|
14
|
+
"releaser"
|
|
15
|
+
];
|
|
16
|
+
const HANDLER_EXPORT_NAMES = {
|
|
17
|
+
planner: "plannerHandler",
|
|
18
|
+
architect: "architectHandler",
|
|
19
|
+
engineer: "engineerHandler",
|
|
20
|
+
notifier: "notifierHandler",
|
|
21
|
+
researcher: "researcherHandler",
|
|
22
|
+
reviewer: "reviewerHandler",
|
|
23
|
+
releaser: "releaserHandler"
|
|
24
|
+
};
|
|
25
|
+
function getTenantAgentHandlerModulePaths(kind, tenantRoot = resolveTreeseedTenantRoot()) {
|
|
26
|
+
return [
|
|
27
|
+
resolve(tenantRoot, "src/agents", `${kind}.js`),
|
|
28
|
+
resolve(tenantRoot, "src/agents", `${kind}.ts`)
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
async function loadTenantAgentHandlerRegistry(tenantRoot = resolveTreeseedTenantRoot()) {
|
|
32
|
+
const registry = {};
|
|
33
|
+
for (const kind of BUILTIN_HANDLER_KINDS) {
|
|
34
|
+
const modulePath = getTenantAgentHandlerModulePaths(kind, tenantRoot).find((candidate) => existsSync(candidate));
|
|
35
|
+
if (!modulePath) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
let moduleExports;
|
|
39
|
+
try {
|
|
40
|
+
moduleExports = await import(
|
|
41
|
+
/* @vite-ignore */
|
|
42
|
+
pathToFileURL(modulePath).href
|
|
43
|
+
);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
46
|
+
throw new Error(`Failed to import tenant agent handler "${kind}" from ${modulePath}: ${reason}`);
|
|
47
|
+
}
|
|
48
|
+
const exportName = HANDLER_EXPORT_NAMES[kind];
|
|
49
|
+
const handler = moduleExports[exportName];
|
|
50
|
+
if (!handler) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`Tenant agent handler module "${modulePath}" must export "${exportName}" for handler kind "${kind}".`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
const normalizedHandler = handler;
|
|
56
|
+
if (normalizedHandler.kind !== kind) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Tenant agent handler "${exportName}" from "${modulePath}" declares kind "${normalizedHandler.kind}", but "${kind}" was expected.`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
registry[kind] = normalizedHandler;
|
|
62
|
+
}
|
|
63
|
+
return registry;
|
|
64
|
+
}
|
|
65
|
+
const AGENT_HANDLER_REGISTRY = await loadTenantAgentHandlerRegistry();
|
|
66
|
+
function listRegisteredAgentHandlers() {
|
|
67
|
+
const runtimeProviders = resolveAgentRuntimeProviders(resolveTreeseedTenantRoot(), getTreeseedAgentProviderSelections());
|
|
68
|
+
return [.../* @__PURE__ */ new Set([...Object.keys(AGENT_HANDLER_REGISTRY), ...runtimeProviders.handlers.keys()])];
|
|
69
|
+
}
|
|
70
|
+
function resolveAgentHandler(kind) {
|
|
71
|
+
const runtimeProviders = resolveAgentRuntimeProviders(resolveTreeseedTenantRoot(), getTreeseedAgentProviderSelections());
|
|
72
|
+
const handler = AGENT_HANDLER_REGISTRY[kind] ?? runtimeProviders.handlers.get(kind);
|
|
73
|
+
if (!handler) {
|
|
74
|
+
if (BUILTIN_HANDLER_KINDS.includes(kind)) {
|
|
75
|
+
const expectedPath = getTenantAgentHandlerModulePaths(kind).join('" or "');
|
|
76
|
+
const expectedExport = HANDLER_EXPORT_NAMES[kind];
|
|
77
|
+
throw new Error(
|
|
78
|
+
`No runtime handler is registered for agent handler "${kind}". Expected tenant file "${expectedPath}" exporting "${expectedExport}" or a plugin contribution.`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
throw new Error(`No runtime handler is registered for agent handler "${kind}".`);
|
|
82
|
+
}
|
|
83
|
+
return handler;
|
|
84
|
+
}
|
|
85
|
+
export {
|
|
86
|
+
AGENT_HANDLER_REGISTRY,
|
|
87
|
+
getTenantAgentHandlerModulePaths,
|
|
88
|
+
listRegisteredAgentHandlers,
|
|
89
|
+
loadTenantAgentHandlerRegistry,
|
|
90
|
+
resolveAgentHandler
|
|
91
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@treeseed/sdk/sdk-filters";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@treeseed/sdk/types";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@treeseed/sdk/sdk";
|
|
@@ -0,0 +1,53 @@
|
|
|
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 result = normalizeAgentRuntimeSpec(extractRawSpec(entry), {
|
|
24
|
+
registeredHandlers: listRegisteredAgentHandlers(),
|
|
25
|
+
messageTypes: [...AGENT_MESSAGE_TYPES]
|
|
26
|
+
});
|
|
27
|
+
diagnostics.push(...result.diagnostics);
|
|
28
|
+
if (result.spec) {
|
|
29
|
+
specs.push(result.spec);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return { specs, diagnostics };
|
|
33
|
+
}
|
|
34
|
+
async function loadActiveAgentSpecs(sdk) {
|
|
35
|
+
return loadAgentSpecs(sdk, { enabled: true });
|
|
36
|
+
}
|
|
37
|
+
async function loadAllAgentSpecs(sdk) {
|
|
38
|
+
return loadAgentSpecs(sdk);
|
|
39
|
+
}
|
|
40
|
+
function summarizeAgentSpec(agent) {
|
|
41
|
+
return {
|
|
42
|
+
slug: agent.slug,
|
|
43
|
+
handler: agent.handler,
|
|
44
|
+
enabled: agent.enabled,
|
|
45
|
+
triggers: agent.triggers.map((trigger) => trigger.type)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
loadActiveAgentSpecs,
|
|
50
|
+
loadAgentSpecs,
|
|
51
|
+
loadAllAgentSpecs,
|
|
52
|
+
summarizeAgentSpec
|
|
53
|
+
};
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AGENT_CLI_ALLOW_TOOLS
|
|
3
|
+
} from "@treeseed/core/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
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function normalizeOutputs(value, _diagnostics, _slug) {
|
|
129
|
+
const next = isPlainObject(value) ? value : {};
|
|
130
|
+
return {
|
|
131
|
+
messageTypes: Array.isArray(next.messageTypes) ? next.messageTypes.filter((entry) => typeof entry === "string") : [],
|
|
132
|
+
modelMutations: Array.isArray(next.modelMutations) ? next.modelMutations.filter((entry) => typeof entry === "string") : []
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function normalizeParts(raw, slugHint, diagnostics) {
|
|
136
|
+
const slug = ensureString(raw.slug ?? slugHint, "slug", diagnostics, slugHint) || slugHint;
|
|
137
|
+
const handler = ensureString(raw.handler, "handler", diagnostics, slug);
|
|
138
|
+
const triggers = Array.isArray(raw.triggers) ? raw.triggers.map((entry, index) => normalizeTrigger(entry, index, diagnostics, slug)).filter((entry) => Boolean(entry)) : [];
|
|
139
|
+
if (!triggers.length) {
|
|
140
|
+
diagnostics.push({
|
|
141
|
+
severity: "error",
|
|
142
|
+
slug,
|
|
143
|
+
field: "triggers",
|
|
144
|
+
message: "Expected at least one trigger."
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
const cli = normalizeAgentCliOptions(raw.cli);
|
|
149
|
+
const spec = {
|
|
150
|
+
slug,
|
|
151
|
+
handler,
|
|
152
|
+
enabled: ensureBoolean(raw.enabled, "enabled", diagnostics, slug, true),
|
|
153
|
+
systemPrompt: ensureString(raw.systemPrompt, "systemPrompt", diagnostics, slug),
|
|
154
|
+
persona: ensureString(raw.persona, "persona", diagnostics, slug),
|
|
155
|
+
cli,
|
|
156
|
+
triggers,
|
|
157
|
+
triggerPolicy: isPlainObject(raw.triggerPolicy) ? {
|
|
158
|
+
maxRunsPerCycle: typeof raw.triggerPolicy.maxRunsPerCycle === "number" ? raw.triggerPolicy.maxRunsPerCycle : void 0,
|
|
159
|
+
messageBatchSize: typeof raw.triggerPolicy.messageBatchSize === "number" ? raw.triggerPolicy.messageBatchSize : void 0
|
|
160
|
+
} : void 0,
|
|
161
|
+
permissions: normalizePermissions(raw.permissions, diagnostics, slug),
|
|
162
|
+
execution: normalizeExecution(raw.execution, diagnostics, slug),
|
|
163
|
+
outputs: normalizeOutputs(raw.outputs, diagnostics, slug)
|
|
164
|
+
};
|
|
165
|
+
return spec;
|
|
166
|
+
} catch (error) {
|
|
167
|
+
diagnostics.push({
|
|
168
|
+
severity: "error",
|
|
169
|
+
slug,
|
|
170
|
+
field: "cli",
|
|
171
|
+
message: error instanceof Error ? error.message : String(error)
|
|
172
|
+
});
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function normalizeAgentRuntimeSpec(raw, context) {
|
|
177
|
+
const slugHint = typeof raw.slug === "string" && raw.slug ? raw.slug : "unknown-agent";
|
|
178
|
+
const diagnostics = [];
|
|
179
|
+
const parts = normalizeParts(raw, slugHint, diagnostics);
|
|
180
|
+
if (!parts) {
|
|
181
|
+
return { spec: null, diagnostics };
|
|
182
|
+
}
|
|
183
|
+
const spec = {
|
|
184
|
+
...parts,
|
|
185
|
+
name: typeof raw.name === "string" ? raw.name : void 0,
|
|
186
|
+
description: typeof raw.description === "string" ? raw.description : void 0,
|
|
187
|
+
summary: typeof raw.summary === "string" ? raw.summary : void 0,
|
|
188
|
+
operator: typeof raw.operator === "string" ? raw.operator : void 0,
|
|
189
|
+
runtimeStatus: typeof raw.runtimeStatus === "string" ? raw.runtimeStatus : void 0,
|
|
190
|
+
capabilities: Array.isArray(raw.capabilities) ? raw.capabilities.filter((entry) => typeof entry === "string") : [],
|
|
191
|
+
tags: Array.isArray(raw.tags) ? raw.tags.filter((entry) => typeof entry === "string") : []
|
|
192
|
+
};
|
|
193
|
+
if (!context.registeredHandlers.includes(spec.handler)) {
|
|
194
|
+
diagnostics.push({
|
|
195
|
+
severity: "error",
|
|
196
|
+
slug: spec.slug,
|
|
197
|
+
field: "handler",
|
|
198
|
+
message: `No runtime handler is registered for "${spec.handler}".`
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
for (const trigger of spec.triggers) {
|
|
202
|
+
if (trigger.type === "message") {
|
|
203
|
+
const messagePermission = spec.permissions.find((permission) => permission.model === "message");
|
|
204
|
+
if (!messagePermission || !messagePermission.operations.includes("pick") || !messagePermission.operations.includes("update")) {
|
|
205
|
+
diagnostics.push({
|
|
206
|
+
severity: "error",
|
|
207
|
+
slug: spec.slug,
|
|
208
|
+
field: "permissions",
|
|
209
|
+
message: "Message-triggered agents must allow message pick and update operations."
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
for (const messageType of trigger.messageTypes ?? []) {
|
|
213
|
+
if (!context.messageTypes.includes(messageType) || !AGENT_MESSAGE_TYPES.includes(messageType)) {
|
|
214
|
+
diagnostics.push({
|
|
215
|
+
severity: "error",
|
|
216
|
+
slug: spec.slug,
|
|
217
|
+
field: "triggers.messageTypes",
|
|
218
|
+
message: `Unknown message trigger type "${messageType}".`
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (trigger.type === "follow" && !trigger.models?.length) {
|
|
224
|
+
diagnostics.push({
|
|
225
|
+
severity: "error",
|
|
226
|
+
slug: spec.slug,
|
|
227
|
+
field: "triggers.models",
|
|
228
|
+
message: "Follow triggers must declare at least one model."
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
for (const messageType of spec.outputs.messageTypes) {
|
|
233
|
+
if (!context.messageTypes.includes(messageType) || !AGENT_MESSAGE_TYPES.includes(messageType)) {
|
|
234
|
+
diagnostics.push({
|
|
235
|
+
severity: "error",
|
|
236
|
+
slug: spec.slug,
|
|
237
|
+
field: "outputs.messageTypes",
|
|
238
|
+
message: `Unknown emitted message type "${messageType}".`
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (spec.cli.allowTools?.some((tool) => !AGENT_CLI_ALLOW_TOOLS.includes(tool))) {
|
|
243
|
+
diagnostics.push({
|
|
244
|
+
severity: "error",
|
|
245
|
+
slug: spec.slug,
|
|
246
|
+
field: "cli.allowTools",
|
|
247
|
+
message: "Agent declared an unsupported tool allowance."
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
spec: diagnostics.some((entry) => entry.severity === "error") ? null : spec,
|
|
252
|
+
diagnostics
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
export {
|
|
256
|
+
normalizeAgentRuntimeSpec
|
|
257
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@treeseed/sdk/stores/cursor-store";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@treeseed/sdk/stores/helpers";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@treeseed/sdk/stores/lease-store";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@treeseed/sdk/stores/message-store";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@treeseed/sdk/stores/run-store";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@treeseed/sdk/stores/subscription-store";
|
|
@@ -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();
|