stageflow 0.36.0 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -26
- package/dist/agent/claudeAdapter.js +21 -1
- package/dist/agent/claudeTools.d.ts +13 -0
- package/dist/agent/claudeTools.js +98 -0
- package/dist/agent/piAdapter.d.ts +9 -1
- package/dist/agent/piAdapter.js +59 -18
- package/dist/agent/piIsolatedMcp.js +14 -2
- package/dist/agent/piIsolatedMcpProbe.d.ts +5 -1
- package/dist/agent/piIsolatedMcpProbe.js +16 -30
- package/dist/agent/port.d.ts +5 -0
- package/dist/agent/sandboxPiTools.d.ts +99 -0
- package/dist/agent/sandboxPiTools.js +109 -0
- package/dist/agents/agentId.d.ts +8 -0
- package/dist/agents/agentId.js +27 -0
- package/dist/agents/approvalBroker.d.ts +16 -0
- package/dist/agents/approvalBroker.js +45 -0
- package/dist/agents/authorChat.d.ts +19 -0
- package/dist/agents/authorChat.js +63 -0
- package/dist/agents/chatTurn.d.ts +23 -0
- package/dist/agents/chatTurn.js +77 -0
- package/dist/agents/communications/agentmail.d.ts +19 -0
- package/dist/agents/communications/agentmail.js +46 -0
- package/dist/agents/communications/agentmailListener.d.ts +59 -0
- package/dist/agents/communications/agentmailListener.js +183 -0
- package/dist/agents/communications/allowlist.d.ts +10 -0
- package/dist/agents/communications/allowlist.js +36 -0
- package/dist/agents/communications/channelConnections.d.ts +3 -0
- package/dist/agents/communications/channelConnections.js +51 -0
- package/dist/agents/communications/delivery.d.ts +53 -0
- package/dist/agents/communications/delivery.js +142 -0
- package/dist/agents/communications/email.d.ts +26 -0
- package/dist/agents/communications/email.js +20 -0
- package/dist/agents/communications/identity.d.ts +13 -0
- package/dist/agents/communications/identity.js +18 -0
- package/dist/agents/communications/imessageListener.d.ts +41 -0
- package/dist/agents/communications/imessageListener.js +92 -0
- package/dist/agents/communications/inbound.d.ts +63 -0
- package/dist/agents/communications/inbound.js +143 -0
- package/dist/agents/communications/listener.d.ts +36 -0
- package/dist/agents/communications/listener.js +38 -0
- package/dist/agents/communications/photon.d.ts +19 -0
- package/dist/agents/communications/photon.js +38 -0
- package/dist/agents/communications/port.d.ts +27 -0
- package/dist/agents/communications/port.js +6 -0
- package/dist/agents/communications/slack.d.ts +28 -0
- package/dist/agents/communications/slack.js +65 -0
- package/dist/agents/communications/supervisor.d.ts +36 -0
- package/dist/agents/communications/supervisor.js +96 -0
- package/dist/agents/communications/telegram.d.ts +19 -0
- package/dist/agents/communications/telegram.js +52 -0
- package/dist/agents/definitionSummary.d.ts +13 -0
- package/dist/agents/definitionSummary.js +91 -0
- package/dist/agents/delegate.d.ts +3 -0
- package/dist/agents/delegate.js +8 -0
- package/dist/agents/errors.d.ts +10 -0
- package/dist/agents/errors.js +32 -0
- package/dist/agents/guardrails.d.ts +24 -0
- package/dist/agents/guardrails.js +39 -0
- package/dist/agents/historyFit.d.ts +10 -0
- package/dist/agents/historyFit.js +26 -0
- package/dist/agents/hostContext.d.ts +54 -0
- package/dist/agents/hostContext.js +156 -0
- package/dist/agents/ledgers.d.ts +29 -0
- package/dist/agents/ledgers.js +31 -0
- package/dist/agents/lineDiff.d.ts +12 -0
- package/dist/agents/lineDiff.js +46 -0
- package/dist/agents/ndjson.d.ts +9 -0
- package/dist/agents/ndjson.js +47 -0
- package/dist/agents/otlpExport.d.ts +9 -0
- package/dist/agents/otlpExport.js +58 -0
- package/dist/agents/otlpSink.d.ts +7 -0
- package/dist/agents/otlpSink.js +23 -0
- package/dist/agents/policy.d.ts +31 -0
- package/dist/agents/policy.js +84 -0
- package/dist/agents/projectRoot.d.ts +5 -0
- package/dist/agents/projectRoot.js +15 -0
- package/dist/agents/runtime/piRuntime.d.ts +52 -0
- package/dist/agents/runtime/piRuntime.js +474 -0
- package/dist/agents/runtime/types.d.ts +77 -0
- package/dist/agents/runtime/types.js +26 -0
- package/dist/agents/sandbox.d.ts +17 -0
- package/dist/agents/sandbox.js +10 -0
- package/dist/agents/scheduleSource.d.ts +32 -0
- package/dist/agents/scheduleSource.js +100 -0
- package/dist/agents/service.d.ts +298 -0
- package/dist/agents/service.js +1105 -0
- package/dist/agents/sessions.d.ts +38 -0
- package/dist/agents/sessions.js +321 -0
- package/dist/agents/store.d.ts +277 -0
- package/dist/agents/store.js +643 -0
- package/dist/agents/trace.d.ts +27 -0
- package/dist/agents/trace.js +29 -0
- package/dist/agents/turnLifecycle.d.ts +72 -0
- package/dist/agents/turnLifecycle.js +157 -0
- package/dist/agents/webChat/publicChat.d.ts +36 -0
- package/dist/agents/webChat/publicChat.js +290 -0
- package/dist/agents/webChat/rateLimit.d.ts +15 -0
- package/dist/agents/webChat/rateLimit.js +33 -0
- package/dist/agents/webChat/slug.d.ts +2 -0
- package/dist/agents/webChat/slug.js +9 -0
- package/dist/channels/providers/agentmail.js +14 -1
- package/dist/channels/providers/photonImessage.js +36 -0
- package/dist/channels/providers/slack.js +2 -1
- package/dist/channels/providers/telegram.js +2 -1
- package/dist/channels/types.d.ts +27 -0
- package/dist/cli/agentsCommand.d.ts +14 -0
- package/dist/cli/agentsCommand.js +291 -0
- package/dist/cli.js +9 -0
- package/dist/config/browseCatalog.js +4 -11
- package/dist/config/draftPackage.d.ts +1 -0
- package/dist/config/draftPackage.js +4 -0
- package/dist/config/hostConfig.js +9 -0
- package/dist/config/loadAgent.d.ts +266 -0
- package/dist/config/loadAgent.js +374 -0
- package/dist/config/loadPipeline.js +5 -2
- package/dist/config/loadTask.js +34 -1
- package/dist/config/mcpHostCatalog.d.ts +50 -0
- package/dist/config/mcpHostCatalog.js +405 -0
- package/dist/config/mcpOAuth.d.ts +37 -0
- package/dist/config/mcpOAuth.js +263 -0
- package/dist/config/mcpRedaction.d.ts +42 -0
- package/dist/config/mcpRedaction.js +314 -0
- package/dist/config/mcpToolCache.d.ts +20 -0
- package/dist/config/mcpToolCache.js +43 -0
- package/dist/config/mergePipelineIncludes.d.ts +2 -0
- package/dist/config/mergePipelineIncludes.js +25 -0
- package/dist/config/resolveStageMcpServers.d.ts +35 -3
- package/dist/config/resolveStageMcpServers.js +124 -9
- package/dist/config/validateCatalog.d.ts +1 -1
- package/dist/config/validateCatalog.js +22 -1
- package/dist/mcp/agentTools.d.ts +8 -0
- package/dist/mcp/agentTools.js +132 -0
- package/dist/mcp/catalogTools.js +17 -10
- package/dist/mcp/deps.d.ts +6 -0
- package/dist/mcp/pipelineAuthorSupport.d.ts +19 -0
- package/dist/mcp/pipelineAuthorSupport.js +10 -0
- package/dist/mcp/pipelineAuthorTools.d.ts +8 -0
- package/dist/mcp/pipelineAuthorTools.js +109 -0
- package/dist/mcp/server.js +5 -0
- package/dist/mcp/tools.js +4 -0
- package/dist/mcp/triggerTools.js +32 -0
- package/dist/memory/access.d.ts +36 -0
- package/dist/memory/access.js +116 -0
- package/dist/memory/agentIssues.d.ts +3 -0
- package/dist/memory/agentIssues.js +23 -0
- package/dist/memory/bashDeny.d.ts +6 -0
- package/dist/memory/bashDeny.js +35 -0
- package/dist/memory/host.d.ts +51 -0
- package/dist/memory/host.js +394 -0
- package/dist/memory/ids.d.ts +15 -0
- package/dist/memory/ids.js +31 -0
- package/dist/memory/worker.d.ts +1 -0
- package/dist/memory/worker.js +84 -0
- package/dist/models/catalog.d.ts +113 -0
- package/dist/models/catalog.js +238 -0
- package/dist/models/response.d.ts +29 -0
- package/dist/models/response.js +25 -0
- package/dist/operatorAgent/agentAuthorContext.d.ts +17 -0
- package/dist/operatorAgent/agentAuthorContext.js +32 -0
- package/dist/operatorAgent/fakeHost.js +2 -0
- package/dist/operatorAgent/host.js +6 -3
- package/dist/operatorAgent/index.d.ts +4 -0
- package/dist/operatorAgent/index.js +4 -0
- package/dist/operatorAgent/piModel.js +2 -0
- package/dist/operatorAgent/profiles/agentAuthor.d.ts +11 -0
- package/dist/operatorAgent/profiles/agentAuthor.js +192 -0
- package/dist/operatorAgent/profiles/agentAuthorPlaybook.d.ts +1 -0
- package/dist/operatorAgent/profiles/agentAuthorPlaybook.js +29 -0
- package/dist/operatorAgent/profiles/workshopAuthor.js +2 -2
- package/dist/operatorAgent/profiles/workshopAuthorTools.js +3 -3
- package/dist/operatorAgent/proposals.d.ts +1 -0
- package/dist/operatorAgent/proposals.js +23 -2
- package/dist/package-meta.d.ts +1 -1
- package/dist/package-meta.js +1 -1
- package/dist/preflight/pipelinePreflight.js +1 -0
- package/dist/runstore/createStore.d.ts +2 -0
- package/dist/runstore/createStore.js +8 -0
- package/dist/runstore/overviewSummary.d.ts +56 -0
- package/dist/runstore/overviewSummary.js +134 -0
- package/dist/runstore/port.d.ts +3 -0
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +2 -0
- package/dist/runstore/sqlite/SqliteRunStore.js +18 -0
- package/dist/runstore/sqlite/migrations/013-agents.d.ts +12 -0
- package/dist/runstore/sqlite/migrations/013-agents.js +156 -0
- package/dist/runstore/sqlite/migrations/014-agent-deliveries.d.ts +11 -0
- package/dist/runstore/sqlite/migrations/014-agent-deliveries.js +32 -0
- package/dist/runstore/sqlite/migrations/index.js +4 -0
- package/dist/runtime/gitCheckoutCapability.d.ts +1 -0
- package/dist/runtime/gitCheckoutCapability.js +1 -1
- package/dist/runtime/gitWorktreeCheckout.d.ts +22 -0
- package/dist/runtime/gitWorktreeCheckout.js +120 -0
- package/dist/runtime/sandboxContainer.d.ts +62 -0
- package/dist/runtime/sandboxContainer.js +151 -0
- package/dist/runtime/sandboxDesk.d.ts +28 -0
- package/dist/runtime/sandboxDesk.js +242 -0
- package/dist/runtime/sandboxFiles.d.ts +20 -0
- package/dist/runtime/sandboxFiles.js +150 -0
- package/dist/runtime/stageAttemptBootstrap.js +15 -1
- package/dist/runtime/stageEnvironment.d.ts +2 -0
- package/dist/runtime/stageEnvironment.js +8 -1
- package/dist/runtime/stageProcessLauncher.js +23 -0
- package/dist/runtime/stageRoots.d.ts +9 -0
- package/dist/runtime/stageRoots.js +14 -0
- package/dist/runtime/stageRunner.d.ts +5 -0
- package/dist/runtime/stageRunner.js +38 -4
- package/dist/runtime/stageSandbox.d.ts +16 -0
- package/dist/runtime/stageSandbox.js +54 -0
- package/dist/runtime/stageSecrets.d.ts +6 -1
- package/dist/runtime/stageSecrets.js +34 -1
- package/dist/runtime/stageWorker.js +138 -105
- package/dist/runtime/workspaceBinding.d.ts +2 -1
- package/dist/runtime/workspaceBinding.js +20 -4
- package/dist/server/agentAuthorRoutes.d.ts +5 -0
- package/dist/server/agentAuthorRoutes.js +64 -0
- package/dist/server/agentRoutes.d.ts +5 -0
- package/dist/server/agentRoutes.js +323 -0
- package/dist/server/bootstrap.d.ts +8 -0
- package/dist/server/bootstrap.js +36 -10
- package/dist/server/controlToken.js +8 -0
- package/dist/server/http.d.ts +8 -1
- package/dist/server/http.js +172 -20
- package/dist/server/mcpHost.js +1 -0
- package/dist/server/memoryRoutes.d.ts +5 -0
- package/dist/server/memoryRoutes.js +146 -0
- package/dist/server/projectMcpRoutes.d.ts +1 -0
- package/dist/server/projectMcpRoutes.js +241 -3
- package/dist/toolkit/guard.d.ts +27 -0
- package/dist/toolkit/guard.js +98 -0
- package/dist/toolkit/index.d.ts +48 -0
- package/dist/toolkit/index.js +82 -0
- package/dist/toolkit/meta.d.ts +36 -0
- package/dist/toolkit/meta.js +117 -0
- package/dist/toolkit/profiles.d.ts +13 -0
- package/dist/toolkit/profiles.js +25 -0
- package/dist/toolkit/record.d.ts +15 -0
- package/dist/toolkit/record.js +17 -0
- package/dist/tools/memoryTools.d.ts +95 -0
- package/dist/tools/memoryTools.js +179 -0
- package/dist/types/pipeline.d.ts +3 -0
- package/dist/types/stage.d.ts +2 -0
- package/dist/types/task.d.ts +11 -1
- package/dist/ui/assets/AssistantRuntimeProvider-CsiPFhCT.js +9 -0
- package/dist/ui/assets/MarkdownText-D7tYaIFO.js +29 -0
- package/dist/ui/assets/ReadonlyStore-BfXqHykm.js +6 -0
- package/dist/ui/assets/Tooltip-DrpoRRNi.js +1 -0
- package/dist/ui/assets/chat-BbLukq9W.js +5 -0
- package/dist/ui/assets/chat-zIZU_bP1.css +2 -0
- package/dist/ui/assets/jsx-runtime-iVoFAvQ_.js +1 -0
- package/dist/ui/assets/{index-Clv4sJpU.css → main-BFo79krC.css} +1 -1
- package/dist/ui/assets/main-CijYMeM0.js +259 -0
- package/dist/ui/chat.html +25 -0
- package/dist/ui/index.html +6 -2
- package/dist/workshop/modelSettings.d.ts +2 -2
- package/dist/workshop/modelSettings.js +5 -11
- package/examples/README.md +1 -0
- package/examples/channels-smoke/README.md +12 -1
- package/examples/github-release/README.md +1 -1
- package/examples/memory/README.md +101 -0
- package/examples/memory/desk-outage.task.yaml +15 -0
- package/examples/memory/desk-refund.task.yaml +15 -0
- package/examples/memory/desk-revisit-outage.task.yaml +11 -0
- package/examples/memory/desk-revisit.pipeline.yaml +123 -0
- package/examples/memory/desk.pipeline.yaml +192 -0
- package/examples/memory/file-card.pipeline.yaml +39 -0
- package/examples/memory/file-ledger.task.yaml +22 -0
- package/examples/memory/file-release.task.yaml +24 -0
- package/examples/memory/file-rich.pipeline.yaml +40 -0
- package/examples/memory/file-ticket.task.yaml +7 -0
- package/examples/memory/file-timeline.task.yaml +28 -0
- package/examples/memory/find-card.pipeline.yaml +46 -0
- package/examples/memory/find-ticket.task.yaml +7 -0
- package/examples/memory/missing.pipeline.yaml +16 -0
- package/examples/memory/probe-bash.task.yaml +3 -0
- package/examples/memory/recall.pipeline.yaml +24 -0
- package/examples/memory/recall.task.yaml +5 -0
- package/examples/memory/remember-ada.task.yaml +6 -0
- package/examples/memory/remember-grace.task.yaml +6 -0
- package/examples/memory/remember.pipeline.yaml +58 -0
- package/examples/memory/sandbox-memory-bash.pipeline.yaml +28 -0
- package/examples/memory/stageflow.yaml +12 -0
- package/examples/memory/update-card.pipeline.yaml +43 -0
- package/examples/memory/update-ledger.task.yaml +23 -0
- package/examples/memory/update-rich.pipeline.yaml +44 -0
- package/examples/memory/update-ticket.task.yaml +7 -0
- package/examples/ship-feature/README.md +15 -0
- package/examples/stage-mcp/README.md +1 -1
- package/package.json +2 -1
- package/dist/ui/assets/index-BOhqnPNg.js +0 -260
|
@@ -12,6 +12,7 @@ export const AGENTMAIL_META = {
|
|
|
12
12
|
{ key: "inbox", label: "Inbox address", hint: "An existing AgentMail inbox. Stageflow does not create one.", target: "config", input: "text", required: true, placeholder: "research@agentmail.to" },
|
|
13
13
|
{ key: "region", label: "Region", target: "config", input: "select", required: false, defaultValue: "us", options: [{ value: "us", label: "United States" }, { value: "eu", label: "Europe" }] },
|
|
14
14
|
{ key: "apiKey", label: "API key", hint: "Needs to read the inbox and send messages.", target: "secret", input: "password", required: true, placeholder: "am_..." },
|
|
15
|
+
{ key: "inboundSecret", label: "Webhook secret (optional)", hint: "Lets agents receive mail for this inbox. Sign webhooks to /api/agent-inbound/<connection id> with this secret (HMAC-SHA256 of the body in the x-agentmail-signature header).", target: "secret", input: "password", required: false },
|
|
15
16
|
],
|
|
16
17
|
recipient: { label: "To", placeholder: "oncall@acme.com, lead@acme.com", multiple: true, cc: true, template: false },
|
|
17
18
|
envNames: ["AGENTMAIL_API_KEY"],
|
|
@@ -49,7 +50,7 @@ export function createAgentMailProvider(options = {}) {
|
|
|
49
50
|
id: "agentmail",
|
|
50
51
|
meta: AGENTMAIL_META,
|
|
51
52
|
operations: ["send"],
|
|
52
|
-
secrets: { required: ["apiKey"] },
|
|
53
|
+
secrets: { required: ["apiKey"], optional: ["inboundSecret"] },
|
|
53
54
|
validateConfig,
|
|
54
55
|
validateTarget,
|
|
55
56
|
describe: config => ({ identity: config.inbox }),
|
|
@@ -62,6 +63,18 @@ export function createAgentMailProvider(options = {}) {
|
|
|
62
63
|
return;
|
|
63
64
|
throw result.status === 404 ? new ChannelError("CHANNEL_INVALID_INPUT") : statusFailure(result.status, "check", connection.id);
|
|
64
65
|
}),
|
|
66
|
+
async reply(connection, replyTo, message, _context) {
|
|
67
|
+
const result = await httpCall({
|
|
68
|
+
fetch: doFetch, url: `${inboxUrl(connection)}/messages/${encodeURIComponent(replyTo)}/reply`, method: "POST", headers: auth(connection),
|
|
69
|
+
body: { text: message.text },
|
|
70
|
+
timeoutMs: options.sendTimeoutMs ?? 30000, mode: "send", connectionId: connection.id,
|
|
71
|
+
});
|
|
72
|
+
if (result.status < 200 || result.status >= 300) {
|
|
73
|
+
throw result.status === 404 ? new ChannelError("CHANNEL_INVALID_INPUT", false, { connectionId: connection.id }) : statusFailure(result.status, "send", connection.id);
|
|
74
|
+
}
|
|
75
|
+
const body = parseJson(result.text);
|
|
76
|
+
return receiptFor(typeof body?.message_id === "string" ? body.message_id : undefined);
|
|
77
|
+
},
|
|
65
78
|
async send(connection, target, message, _context) {
|
|
66
79
|
const result = await httpCall({
|
|
67
80
|
fetch: doFetch, url: `${inboxUrl(connection)}/messages/send`, method: "POST", headers: auth(connection),
|
|
@@ -137,6 +137,42 @@ export function createPhotonImessageProvider(options = {}) {
|
|
|
137
137
|
throw new ChannelError("CHANNEL_RECIPIENT_REJECTED", false, { connectionId: connection.id });
|
|
138
138
|
}
|
|
139
139
|
}),
|
|
140
|
+
async contact(connection, signal) {
|
|
141
|
+
try {
|
|
142
|
+
const credentials = credentialsOf(connection);
|
|
143
|
+
const project = encodeURIComponent(credentials.projectId);
|
|
144
|
+
const call = (url, init) => httpCall({
|
|
145
|
+
fetch: doFetch, url, method: init.method, headers: photonHeaders(credentials), ...(init.body !== undefined ? { body: init.body } : {}),
|
|
146
|
+
timeoutMs: options.checkTimeoutMs ?? 10000, ...(signal ? { signal } : {}), mode: "check", connectionId: connection.id,
|
|
147
|
+
});
|
|
148
|
+
const tokens = await call(`${base}/projects/${project}/imessage/tokens`, { method: "POST", body: {} });
|
|
149
|
+
if (tokens.status < 200 || tokens.status >= 300)
|
|
150
|
+
return { ok: false, error: `Photon answered ${tokens.status}` };
|
|
151
|
+
const data = photonData(parseJson(tokens.text));
|
|
152
|
+
if (data?.type === "dedicated") {
|
|
153
|
+
const numbers = Object.values((data.numbers ?? {})).filter((value) => typeof value === "string");
|
|
154
|
+
return { ok: true, kind: "dedicated", numbers: [...new Set(numbers)] };
|
|
155
|
+
}
|
|
156
|
+
const users = await call(`${base}/projects/${project}/users/?type=shared&limit=500`, { method: "GET" });
|
|
157
|
+
if (users.status < 200 || users.status >= 300)
|
|
158
|
+
return { ok: false, error: `Photon answered ${users.status}` };
|
|
159
|
+
const list = (photonData(parseJson(users.text))?.users ?? []);
|
|
160
|
+
return {
|
|
161
|
+
ok: true,
|
|
162
|
+
kind: "shared",
|
|
163
|
+
users: list
|
|
164
|
+
.filter((user) => typeof user.phoneNumber === "string")
|
|
165
|
+
.map((user) => ({
|
|
166
|
+
phone: user.phoneNumber,
|
|
167
|
+
assignedNumber: typeof user.assignedPhoneNumber === "string" ? user.assignedPhoneNumber : null,
|
|
168
|
+
name: [user.firstName, user.lastName].filter((part) => typeof part === "string" && part.length > 0).join(" ") || null,
|
|
169
|
+
})),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
return { ok: false, error: "Could not reach Photon" };
|
|
174
|
+
}
|
|
175
|
+
},
|
|
140
176
|
async send(connection, target, message, _context) {
|
|
141
177
|
const credentials = credentialsOf(connection);
|
|
142
178
|
const text = message.subject ? `${message.subject}\n\n${message.text}` : message.text;
|
|
@@ -10,6 +10,7 @@ export const SLACK_META = {
|
|
|
10
10
|
fields: [
|
|
11
11
|
{ key: "botToken", label: "Bot token", hint: "xoxb-… from your Slack app (OAuth & Permissions). Needs chat:write.", target: "secret", input: "password", required: true, placeholder: "xoxb-..." },
|
|
12
12
|
{ key: "teamId", label: "Workspace ID (optional)", hint: "Pins the connection to one workspace, such as T0123ABCD.", target: "config", input: "text", required: false, placeholder: "T0123ABCD" },
|
|
13
|
+
{ key: "inboundSecret", label: "Signing secret (optional)", hint: "Lets agents receive messages from this workspace. From your Slack app (Basic Information). Point Event Subscriptions at /api/agent-inbound/<connection id>.", target: "secret", input: "password", required: false },
|
|
13
14
|
],
|
|
14
15
|
recipient: { label: "Channel id", placeholder: "C0123ABCD (channel, group, DM or user id)", multiple: false, cc: false, template: false },
|
|
15
16
|
envNames: ["SLACK_BOT_TOKEN"],
|
|
@@ -63,7 +64,7 @@ export function createSlackProvider(options = {}) {
|
|
|
63
64
|
id: "slack",
|
|
64
65
|
meta: SLACK_META,
|
|
65
66
|
operations: ["send"],
|
|
66
|
-
secrets: { required: ["botToken"] },
|
|
67
|
+
secrets: { required: ["botToken"], optional: ["inboundSecret"] },
|
|
67
68
|
validateConfig,
|
|
68
69
|
validateTarget,
|
|
69
70
|
describe: config => (config.teamId ? { identity: config.teamId } : {}),
|
|
@@ -11,6 +11,7 @@ export const TELEGRAM_META = {
|
|
|
11
11
|
fields: [
|
|
12
12
|
{ key: "botToken", label: "Bot token", hint: "From @BotFather. People must start the bot before it can message them.", target: "secret", input: "password", required: true, placeholder: "123456:ABC..." },
|
|
13
13
|
{ key: "botUsername", label: "Bot username (optional)", hint: "Fails the check if the token belongs to another bot.", target: "config", input: "text", required: false, placeholder: "acme_alerts_bot" },
|
|
14
|
+
{ key: "inboundSecret", label: "Webhook secret (optional)", hint: "Lets agents receive messages from this bot. Use it as secret_token in setWebhook with the URL /api/agent-inbound/<connection id>.", target: "secret", input: "password", required: false },
|
|
14
15
|
],
|
|
15
16
|
recipient: { label: "Chat id", placeholder: "123456789 or @channelname", multiple: false, cc: false, template: false },
|
|
16
17
|
envNames: ["TELEGRAM_BOT_TOKEN"],
|
|
@@ -60,7 +61,7 @@ export function createTelegramProvider(options = {}) {
|
|
|
60
61
|
id: "telegram",
|
|
61
62
|
meta: TELEGRAM_META,
|
|
62
63
|
operations: ["send"],
|
|
63
|
-
secrets: { required: ["botToken"] },
|
|
64
|
+
secrets: { required: ["botToken"], optional: ["inboundSecret"] },
|
|
64
65
|
validateConfig,
|
|
65
66
|
validateTarget,
|
|
66
67
|
describe: config => (config.botUsername ? { identity: `@${config.botUsername}` } : {}),
|
package/dist/channels/types.d.ts
CHANGED
|
@@ -107,6 +107,23 @@ export type ProviderMeta = {
|
|
|
107
107
|
/** The `to:` lines (and template, if any) of a stage `channels:` entry for this provider. */
|
|
108
108
|
stageExample: string;
|
|
109
109
|
};
|
|
110
|
+
/** Where to text or write to reach an agent on a connection. */
|
|
111
|
+
export type ContactInfo = {
|
|
112
|
+
ok: true;
|
|
113
|
+
kind: "dedicated";
|
|
114
|
+
numbers: string[];
|
|
115
|
+
} | {
|
|
116
|
+
ok: true;
|
|
117
|
+
kind: "shared";
|
|
118
|
+
users: Array<{
|
|
119
|
+
phone: string;
|
|
120
|
+
assignedNumber: string | null;
|
|
121
|
+
name: string | null;
|
|
122
|
+
}>;
|
|
123
|
+
} | {
|
|
124
|
+
ok: false;
|
|
125
|
+
error: string;
|
|
126
|
+
};
|
|
110
127
|
export interface ChannelProvider<C = unknown, T = unknown> {
|
|
111
128
|
readonly id: string;
|
|
112
129
|
readonly meta: ProviderMeta;
|
|
@@ -127,6 +144,16 @@ export interface ChannelProvider<C = unknown, T = unknown> {
|
|
|
127
144
|
*/
|
|
128
145
|
checkTargets?(connection: ResolvedConnection<C>, targets: T[], signal?: AbortSignal): Promise<ConnectionHealth>;
|
|
129
146
|
send(connection: ResolvedConnection<C>, target: T, message: OutboundMessage, context: SendContext): Promise<SendReceipt>;
|
|
147
|
+
/**
|
|
148
|
+
* How people reach this connection: the number or address to text or write, when the provider can tell.
|
|
149
|
+
* Read-only and best-effort; failures are returned, not thrown.
|
|
150
|
+
*/
|
|
151
|
+
contact?(connection: ResolvedConnection<C>, signal?: AbortSignal): Promise<ContactInfo>;
|
|
152
|
+
/**
|
|
153
|
+
* Answers a message the provider delivered, inside its conversation (an email reply keeps the thread and
|
|
154
|
+
* subject). `replyTo` is the opaque handle the inbound message carried. Providers without it fall back to `send`.
|
|
155
|
+
*/
|
|
156
|
+
reply?(connection: ResolvedConnection<C>, replyTo: string, message: OutboundMessage, context: SendContext): Promise<SendReceipt>;
|
|
130
157
|
}
|
|
131
158
|
export type AnyChannelProvider = ChannelProvider<any, any>;
|
|
132
159
|
export type ConnectionSummary = {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type EnsureGlobalServiceResult } from "../server/ensureGlobalService.js";
|
|
2
|
+
export declare const AGENTS_USAGE = "Usage:\n sf agents list [--json]\n sf agents get <id> [--json]\n sf agents validate <file.agent.yaml> [--json]\n sf agents enable <id> [--json]\n sf agents disable <id> [--json]\n sf agents history <id> [--json]\n sf agents restore <id> <revision> --base <current revision> [--json]\n sf agents sessions <id> [--status <status>] [--json]\n sf agents session <id> <sessionId> [--json]\n sf agents chat <id> --message <text> [--session <sessionId>] [--json]\n sf agents approve <id> <sessionId> [--json]\n sf agents deny <id> <sessionId> [--json]\n\nrestore needs --base: the revision you looked at. If the agent has moved on since, the restore is refused instead of\noverwriting a newer edit. Run it without --base to see the current revision.\n\nTalks to the running Stageflow Host for the project in the current directory.";
|
|
3
|
+
export type AgentsCommandIo = {
|
|
4
|
+
log: (line: string) => void;
|
|
5
|
+
error: (line: string) => void;
|
|
6
|
+
write: (chunk: string) => void;
|
|
7
|
+
};
|
|
8
|
+
export declare function runAgentsCommand(args: string[], options?: {
|
|
9
|
+
io?: Partial<AgentsCommandIo>;
|
|
10
|
+
cwd?: string;
|
|
11
|
+
projectRoot?: string;
|
|
12
|
+
hostBaseUrl?: string;
|
|
13
|
+
ensureService?: () => Promise<EnsureGlobalServiceResult>;
|
|
14
|
+
}): Promise<number>;
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parse as parseYaml } from "yaml";
|
|
4
|
+
import { resolveStageflowContext } from "../project/resolveStageflowContext.js";
|
|
5
|
+
import { clientAuthorizationHeaders } from "../server/controlToken.js";
|
|
6
|
+
import { ensureGlobalService, hostBaseUrl } from "../server/ensureGlobalService.js";
|
|
7
|
+
export const AGENTS_USAGE = `Usage:
|
|
8
|
+
sf agents list [--json]
|
|
9
|
+
sf agents get <id> [--json]
|
|
10
|
+
sf agents validate <file.agent.yaml> [--json]
|
|
11
|
+
sf agents enable <id> [--json]
|
|
12
|
+
sf agents disable <id> [--json]
|
|
13
|
+
sf agents history <id> [--json]
|
|
14
|
+
sf agents restore <id> <revision> --base <current revision> [--json]
|
|
15
|
+
sf agents sessions <id> [--status <status>] [--json]
|
|
16
|
+
sf agents session <id> <sessionId> [--json]
|
|
17
|
+
sf agents chat <id> --message <text> [--session <sessionId>] [--json]
|
|
18
|
+
sf agents approve <id> <sessionId> [--json]
|
|
19
|
+
sf agents deny <id> <sessionId> [--json]
|
|
20
|
+
|
|
21
|
+
restore needs --base: the revision you looked at. If the agent has moved on since, the restore is refused instead of
|
|
22
|
+
overwriting a newer edit. Run it without --base to see the current revision.
|
|
23
|
+
|
|
24
|
+
Talks to the running Stageflow Host for the project in the current directory.`;
|
|
25
|
+
const defaultIo = {
|
|
26
|
+
log: (line) => console.log(line),
|
|
27
|
+
error: (line) => console.error(line),
|
|
28
|
+
write: (chunk) => process.stdout.write(chunk),
|
|
29
|
+
};
|
|
30
|
+
const FLAGS_WITH_VALUE = new Set(["--message", "--session", "--status", "--base"]);
|
|
31
|
+
function parseArgs(args) {
|
|
32
|
+
const parsed = { help: false, json: false, positional: [] };
|
|
33
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
34
|
+
const arg = args[index];
|
|
35
|
+
if (arg === "--help" || arg === "-h")
|
|
36
|
+
parsed.help = true;
|
|
37
|
+
else if (arg === "--json")
|
|
38
|
+
parsed.json = true;
|
|
39
|
+
else if (FLAGS_WITH_VALUE.has(arg)) {
|
|
40
|
+
const value = args[++index];
|
|
41
|
+
if (value === undefined || value.length === 0)
|
|
42
|
+
throw new Error(`Missing value for ${arg}`);
|
|
43
|
+
if (arg === "--message")
|
|
44
|
+
parsed.message = value;
|
|
45
|
+
else if (arg === "--session")
|
|
46
|
+
parsed.session = value;
|
|
47
|
+
else if (arg === "--base")
|
|
48
|
+
parsed.base = value;
|
|
49
|
+
else
|
|
50
|
+
parsed.status = value;
|
|
51
|
+
}
|
|
52
|
+
else if (arg.startsWith("-"))
|
|
53
|
+
throw new Error(`Unknown flag: ${arg}`);
|
|
54
|
+
else if (parsed.subcommand === undefined)
|
|
55
|
+
parsed.subcommand = arg;
|
|
56
|
+
else
|
|
57
|
+
parsed.positional.push(arg);
|
|
58
|
+
}
|
|
59
|
+
return parsed;
|
|
60
|
+
}
|
|
61
|
+
class HostError extends Error {
|
|
62
|
+
code;
|
|
63
|
+
issues;
|
|
64
|
+
constructor(code, message, issues = []) {
|
|
65
|
+
super(message);
|
|
66
|
+
this.code = code;
|
|
67
|
+
this.issues = issues;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function hostClient(base, projectRoot) {
|
|
71
|
+
const withRoot = (urlPath) => {
|
|
72
|
+
const url = new URL(urlPath, base);
|
|
73
|
+
url.searchParams.set("project_root", projectRoot);
|
|
74
|
+
return url;
|
|
75
|
+
};
|
|
76
|
+
const fail = async (res) => {
|
|
77
|
+
const body = (await res.json().catch(() => ({})));
|
|
78
|
+
throw new HostError(body.code ?? `http_${res.status}`, body.message ?? body.code ?? `Host returned ${res.status}`, body.issues);
|
|
79
|
+
};
|
|
80
|
+
return {
|
|
81
|
+
async get(urlPath) {
|
|
82
|
+
const res = await fetch(withRoot(urlPath), { headers: clientAuthorizationHeaders("GET") });
|
|
83
|
+
if (!res.ok)
|
|
84
|
+
await fail(res);
|
|
85
|
+
return res.json();
|
|
86
|
+
},
|
|
87
|
+
async post(urlPath, body) {
|
|
88
|
+
const res = await fetch(withRoot(urlPath), {
|
|
89
|
+
method: "POST",
|
|
90
|
+
headers: { "Content-Type": "application/json", ...clientAuthorizationHeaders("POST") },
|
|
91
|
+
body: JSON.stringify(body ?? {}),
|
|
92
|
+
});
|
|
93
|
+
if (!res.ok)
|
|
94
|
+
await fail(res);
|
|
95
|
+
return res.json();
|
|
96
|
+
},
|
|
97
|
+
async stream(urlPath, body, onFrame) {
|
|
98
|
+
const res = await fetch(withRoot(urlPath), {
|
|
99
|
+
method: "POST",
|
|
100
|
+
headers: {
|
|
101
|
+
"Content-Type": "application/json",
|
|
102
|
+
Accept: "application/x-ndjson",
|
|
103
|
+
...clientAuthorizationHeaders("POST"),
|
|
104
|
+
},
|
|
105
|
+
body: JSON.stringify(body),
|
|
106
|
+
});
|
|
107
|
+
if (!res.ok || !res.body)
|
|
108
|
+
await fail(res);
|
|
109
|
+
const decoder = new TextDecoder();
|
|
110
|
+
let buffered = "";
|
|
111
|
+
for await (const chunk of res.body) {
|
|
112
|
+
buffered += decoder.decode(chunk, { stream: true });
|
|
113
|
+
let newline = buffered.indexOf("\n");
|
|
114
|
+
while (newline >= 0) {
|
|
115
|
+
const line = buffered.slice(0, newline).trim();
|
|
116
|
+
buffered = buffered.slice(newline + 1);
|
|
117
|
+
if (line)
|
|
118
|
+
onFrame(JSON.parse(line));
|
|
119
|
+
newline = buffered.indexOf("\n");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
export async function runAgentsCommand(args, options = {}) {
|
|
126
|
+
const io = { ...defaultIo, ...options.io };
|
|
127
|
+
let parsed;
|
|
128
|
+
try {
|
|
129
|
+
parsed = parseArgs(args);
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
io.error(error instanceof Error ? error.message : String(error));
|
|
133
|
+
io.error(AGENTS_USAGE);
|
|
134
|
+
return 1;
|
|
135
|
+
}
|
|
136
|
+
if (parsed.help || !parsed.subcommand) {
|
|
137
|
+
io.error(AGENTS_USAGE);
|
|
138
|
+
return parsed.help ? 0 : 1;
|
|
139
|
+
}
|
|
140
|
+
const need = (count, what) => {
|
|
141
|
+
if (parsed.positional.length !== count)
|
|
142
|
+
throw new HostError("usage", `${parsed.subcommand} needs ${what}`);
|
|
143
|
+
return parsed.positional;
|
|
144
|
+
};
|
|
145
|
+
const print = (value, human) => (parsed.json ? io.log(JSON.stringify(value, null, 2)) : human());
|
|
146
|
+
try {
|
|
147
|
+
const cwd = options.cwd ?? process.cwd();
|
|
148
|
+
const projectRoot = options.projectRoot ?? (await resolveStageflowContext(cwd)).projectRoot;
|
|
149
|
+
const ensured = await (options.ensureService ?? (() => ensureGlobalService()))();
|
|
150
|
+
if (!ensured.ok)
|
|
151
|
+
throw new HostError("host.unavailable", ensured.message);
|
|
152
|
+
const client = hostClient(options.hostBaseUrl ?? hostBaseUrl(), projectRoot);
|
|
153
|
+
const agentPath = (id, tail = "") => `/api/agents/${encodeURIComponent(id)}${tail}`;
|
|
154
|
+
switch (parsed.subcommand) {
|
|
155
|
+
case "list": {
|
|
156
|
+
need(0, "no arguments");
|
|
157
|
+
const { agents } = await client.get("/api/agents");
|
|
158
|
+
print({ agents }, () => {
|
|
159
|
+
for (const agent of agents) {
|
|
160
|
+
const state = agent.issues.length > 0 ? "invalid" : agent.enabled ? "enabled" : "disabled";
|
|
161
|
+
io.log(`${agent.id}\tv${agent.revision}\t${state}\t${agent.name}`);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
return 0;
|
|
165
|
+
}
|
|
166
|
+
case "get": {
|
|
167
|
+
const [id] = need(1, "<id>");
|
|
168
|
+
const detail = await client.get(agentPath(id));
|
|
169
|
+
print(detail, () => io.log(detail.yaml ?? "(no valid revision)"));
|
|
170
|
+
return 0;
|
|
171
|
+
}
|
|
172
|
+
case "validate": {
|
|
173
|
+
const [file] = need(1, "<file.agent.yaml>");
|
|
174
|
+
const definition = parseYaml(await readFile(path.resolve(cwd, file), "utf8"));
|
|
175
|
+
const result = await client.post("/api/agents/validate", { definition });
|
|
176
|
+
print(result, () => {
|
|
177
|
+
for (const issue of result.issues)
|
|
178
|
+
io.error(`error ${issue.path || "(file)"}: ${issue.message}`);
|
|
179
|
+
for (const warning of result.warnings)
|
|
180
|
+
io.error(`warning ${warning.path}: ${warning.message}`);
|
|
181
|
+
if (result.issues.length === 0)
|
|
182
|
+
io.log("valid");
|
|
183
|
+
});
|
|
184
|
+
return result.issues.length === 0 ? 0 : 1;
|
|
185
|
+
}
|
|
186
|
+
case "enable":
|
|
187
|
+
case "disable": {
|
|
188
|
+
const [id] = need(1, "<id>");
|
|
189
|
+
const detail = await client.post(agentPath(id, `/${parsed.subcommand}`));
|
|
190
|
+
print(detail, () => io.log(`${id} ${parsed.subcommand}d (v${detail.agent.revision})`));
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
193
|
+
case "history": {
|
|
194
|
+
const [id] = need(1, "<id>");
|
|
195
|
+
const { revisions } = await client.get(agentPath(id, "/revisions"));
|
|
196
|
+
print({ revisions }, () => {
|
|
197
|
+
for (const row of revisions)
|
|
198
|
+
io.log(`v${row.revision}\t${row.createdAt}\t${row.source}\t${row.summary ?? ""}`);
|
|
199
|
+
});
|
|
200
|
+
return 0;
|
|
201
|
+
}
|
|
202
|
+
case "restore": {
|
|
203
|
+
const [id, raw] = need(2, "<id> <revision>");
|
|
204
|
+
const revision = Number(raw);
|
|
205
|
+
if (!Number.isInteger(revision) || revision < 1)
|
|
206
|
+
throw new HostError("usage", "revision must be a positive integer");
|
|
207
|
+
if (parsed.base === undefined) {
|
|
208
|
+
// The lock only means something if the caller names the revision they saw; reading it here and echoing it back would not.
|
|
209
|
+
const current = await client.get(agentPath(id));
|
|
210
|
+
throw new HostError("usage", `restore needs --base: ${id} is at v${current.agent.revision}. Pass --base ${current.agent.revision} to restore v${revision} over it`);
|
|
211
|
+
}
|
|
212
|
+
const baseRevision = Number(parsed.base);
|
|
213
|
+
if (!Number.isInteger(baseRevision) || baseRevision < 1)
|
|
214
|
+
throw new HostError("usage", "--base must be a positive integer");
|
|
215
|
+
const detail = await client.post(agentPath(id, `/revisions/${revision}/restore`), { baseRevision });
|
|
216
|
+
print(detail, () => io.log(`${id} restored v${revision} as v${detail.agent.revision}`));
|
|
217
|
+
return 0;
|
|
218
|
+
}
|
|
219
|
+
case "sessions": {
|
|
220
|
+
const [id] = need(1, "<id>");
|
|
221
|
+
const query = parsed.status ? `?status=${encodeURIComponent(parsed.status)}` : "";
|
|
222
|
+
const page = await client.get(agentPath(id, `/sessions${query}`));
|
|
223
|
+
print(page, () => {
|
|
224
|
+
for (const row of page.sessions)
|
|
225
|
+
io.log(`${row.id}\t${row.status}\t${row.source}\t${row.updatedAt}\t${row.title ?? ""}`);
|
|
226
|
+
});
|
|
227
|
+
return 0;
|
|
228
|
+
}
|
|
229
|
+
case "session": {
|
|
230
|
+
const [id, sessionId] = need(2, "<id> <sessionId>");
|
|
231
|
+
const detail = await client.get(agentPath(id, `/sessions/${encodeURIComponent(sessionId)}`));
|
|
232
|
+
print(detail, () => {
|
|
233
|
+
for (const message of detail.messages)
|
|
234
|
+
io.log(`${message.author ?? message.role}: ${message.text}\n`);
|
|
235
|
+
});
|
|
236
|
+
return 0;
|
|
237
|
+
}
|
|
238
|
+
case "chat": {
|
|
239
|
+
const [id] = need(1, "<id>");
|
|
240
|
+
if (!parsed.message)
|
|
241
|
+
throw new HostError("usage", "chat needs --message");
|
|
242
|
+
let failure;
|
|
243
|
+
let done;
|
|
244
|
+
await client.stream(agentPath(id, "/chat"), { message: parsed.message, ...(parsed.session ? { sessionId: parsed.session } : {}) }, (frame) => {
|
|
245
|
+
if (frame.type === "delta" && !parsed.json)
|
|
246
|
+
io.write(String(frame.text));
|
|
247
|
+
else if (frame.type === "session" && !parsed.json)
|
|
248
|
+
io.error(`session ${String(frame.sessionId)}`);
|
|
249
|
+
else if (frame.type === "done")
|
|
250
|
+
done = frame.session;
|
|
251
|
+
else if (frame.type === "error")
|
|
252
|
+
failure = new HostError(String(frame.code), String(frame.message ?? frame.code));
|
|
253
|
+
});
|
|
254
|
+
if (failure)
|
|
255
|
+
throw failure;
|
|
256
|
+
// A stream that ends with neither a result nor an error was cut off: not a success.
|
|
257
|
+
if (done === undefined)
|
|
258
|
+
throw new HostError("agent.stream_closed", "The Host closed the connection before the reply finished");
|
|
259
|
+
if (parsed.json)
|
|
260
|
+
io.log(JSON.stringify(done, null, 2));
|
|
261
|
+
else
|
|
262
|
+
io.write("\n");
|
|
263
|
+
return 0;
|
|
264
|
+
}
|
|
265
|
+
case "approve":
|
|
266
|
+
case "deny": {
|
|
267
|
+
const [id, sessionId] = need(2, "<id> <sessionId>");
|
|
268
|
+
const result = await client.post(agentPath(id, `/sessions/${encodeURIComponent(sessionId)}/approval/${parsed.subcommand}`));
|
|
269
|
+
print(result, () => io.log(`${parsed.subcommand === "approve" ? "Approved" : "Denied"} ${result.approval.tool}`));
|
|
270
|
+
return 0;
|
|
271
|
+
}
|
|
272
|
+
default:
|
|
273
|
+
throw new HostError("usage", `Unknown subcommand: ${parsed.subcommand}`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
if (error instanceof HostError) {
|
|
278
|
+
if (parsed.json)
|
|
279
|
+
io.log(JSON.stringify({ code: error.code, message: error.message, issues: error.issues }, null, 2));
|
|
280
|
+
else {
|
|
281
|
+
io.error(error.message);
|
|
282
|
+
for (const issue of error.issues)
|
|
283
|
+
io.error(` ${issue.path || "(file)"}: ${issue.message}`);
|
|
284
|
+
}
|
|
285
|
+
if (error.code === "usage")
|
|
286
|
+
io.error(AGENTS_USAGE);
|
|
287
|
+
return 1;
|
|
288
|
+
}
|
|
289
|
+
throw error;
|
|
290
|
+
}
|
|
291
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -18,6 +18,7 @@ import { PROVIDERS_USAGE, runProvidersCommand } from "./cli/providersCommand.js"
|
|
|
18
18
|
import { RUN_USAGE, runRunCommand } from "./cli/runCommand.js";
|
|
19
19
|
import { RUN_STAGE_USAGE, runRunStageCommand } from "./cli/runStageCommand.js";
|
|
20
20
|
import { RUNS_USAGE, runRunsCommand } from "./cli/runsCommand.js";
|
|
21
|
+
import { AGENTS_USAGE, runAgentsCommand } from "./cli/agentsCommand.js";
|
|
21
22
|
import { TRIGGER_USAGE, runTriggerCommand } from "./cli/triggerCommand.js";
|
|
22
23
|
import { resolveOperatorCatalog } from "./cli/operatorCatalog.js";
|
|
23
24
|
import { SKILLS_USAGE, runSkillsCommand } from "./cli/skillsCommand.js";
|
|
@@ -124,6 +125,8 @@ ${RUNS_USAGE}
|
|
|
124
125
|
|
|
125
126
|
${TRIGGER_USAGE}
|
|
126
127
|
|
|
128
|
+
${AGENTS_USAGE}
|
|
129
|
+
|
|
127
130
|
${PROVIDERS_USAGE}
|
|
128
131
|
|
|
129
132
|
${SKILLS_USAGE}
|
|
@@ -165,6 +168,7 @@ function parseArgs(argv) {
|
|
|
165
168
|
command === "export" ||
|
|
166
169
|
command === "runs" ||
|
|
167
170
|
command === "trigger" ||
|
|
171
|
+
command === "agents" ||
|
|
168
172
|
command === "skills" ||
|
|
169
173
|
command === "browser") {
|
|
170
174
|
return { help: false, command };
|
|
@@ -491,6 +495,11 @@ async function main(argv) {
|
|
|
491
495
|
cwd: ctx.invocationCwd,
|
|
492
496
|
});
|
|
493
497
|
}
|
|
498
|
+
if (parsed.command === "agents") {
|
|
499
|
+
return runAgentsCommand(argv.slice(3), {
|
|
500
|
+
cwd: ctx.invocationCwd,
|
|
501
|
+
});
|
|
502
|
+
}
|
|
494
503
|
if (parsed.command === "providers") {
|
|
495
504
|
return runProvidersCommand(argv.slice(3), ctx.invocationCwd);
|
|
496
505
|
}
|
|
@@ -3,11 +3,6 @@ import { loadPipeline } from "./loadPipeline.js";
|
|
|
3
3
|
import { loadTask } from "./loadTask.js";
|
|
4
4
|
import { resolveStageflowContext, } from "../project/resolveStageflowContext.js";
|
|
5
5
|
import { scanCatalogPaths } from "./scanCatalogPaths.js";
|
|
6
|
-
const BAKED_MODEL_IDS = [
|
|
7
|
-
"anthropic/claude-sonnet-4-5",
|
|
8
|
-
"cursor/auto",
|
|
9
|
-
"cursor/composer-2-5",
|
|
10
|
-
];
|
|
11
6
|
function repoRelPath(projectRoot, absPath) {
|
|
12
7
|
return path.relative(projectRoot, absPath).replace(/\\/g, "/");
|
|
13
8
|
}
|
|
@@ -30,9 +25,6 @@ function mapStageListing(stage, source, projectRoot) {
|
|
|
30
25
|
}
|
|
31
26
|
return listing;
|
|
32
27
|
}
|
|
33
|
-
function bakedModels() {
|
|
34
|
-
return [...BAKED_MODEL_IDS].sort((a, b) => a.localeCompare(b));
|
|
35
|
-
}
|
|
36
28
|
function catalogReadyFromStageflow(ctx) {
|
|
37
29
|
return ctx.manifestStatus === "ok" && ctx.manifest !== null;
|
|
38
30
|
}
|
|
@@ -106,8 +98,9 @@ async function listPipelinesFromManifest(projectRoot, manifest, warnings) {
|
|
|
106
98
|
});
|
|
107
99
|
return listings;
|
|
108
100
|
}
|
|
101
|
+
/** Model ids the project's pipelines already use. The provider catalog (src/models) owns the choosable list. */
|
|
109
102
|
async function listModelsFromManifest(projectRoot, manifest) {
|
|
110
|
-
const models = new Set(
|
|
103
|
+
const models = new Set();
|
|
111
104
|
const filePaths = await scanCatalogPaths(manifest, "pipeline");
|
|
112
105
|
for (const filePath of filePaths) {
|
|
113
106
|
try {
|
|
@@ -139,7 +132,7 @@ export async function browseCatalog(invocationCwd) {
|
|
|
139
132
|
...base,
|
|
140
133
|
pipelines: [],
|
|
141
134
|
tasks: [],
|
|
142
|
-
models:
|
|
135
|
+
models: [],
|
|
143
136
|
warnings: [],
|
|
144
137
|
};
|
|
145
138
|
}
|
|
@@ -171,7 +164,7 @@ export async function listTasksForContext(ctx) {
|
|
|
171
164
|
}
|
|
172
165
|
export async function listModelsForContext(ctx) {
|
|
173
166
|
if (!catalogReady(ctx)) {
|
|
174
|
-
return
|
|
167
|
+
return [];
|
|
175
168
|
}
|
|
176
169
|
return listModelsFromManifest(ctx.projectRoot, ctx.manifest);
|
|
177
170
|
}
|
|
@@ -79,6 +79,8 @@ function pipelineDocument(pipeline) {
|
|
|
79
79
|
doc.requires = pipeline.requires;
|
|
80
80
|
if (pipeline.channels !== undefined)
|
|
81
81
|
doc.channels = pipeline.channels;
|
|
82
|
+
if (pipeline.memories !== undefined)
|
|
83
|
+
doc.memories = pipeline.memories;
|
|
82
84
|
return doc;
|
|
83
85
|
}
|
|
84
86
|
function resolveDirectory(projectRoot, directory) {
|
|
@@ -167,6 +169,7 @@ export async function validateDraftPackage(draft, options = {}) {
|
|
|
167
169
|
? { requires: draft.pipeline.requires }
|
|
168
170
|
: {}),
|
|
169
171
|
...(draft.pipeline.channels !== undefined ? { channels: draft.pipeline.channels } : {}),
|
|
172
|
+
...(draft.pipeline.memories !== undefined ? { memories: draft.pipeline.memories } : {}),
|
|
170
173
|
};
|
|
171
174
|
const result = await loadPipelineValidated(inline, {
|
|
172
175
|
cwd,
|
|
@@ -720,6 +723,7 @@ export async function loadDraftPackage(projectRoot, pipelineRelPath, options = {
|
|
|
720
723
|
...(raw.schemas !== undefined ? { schemas: raw.schemas } : {}),
|
|
721
724
|
...(raw.requires !== undefined ? { requires: raw.requires } : {}),
|
|
722
725
|
...(raw.channels !== undefined ? { channels: raw.channels } : {}),
|
|
726
|
+
...(raw.memories !== undefined ? { memories: raw.memories } : {}),
|
|
723
727
|
},
|
|
724
728
|
...(stageArtifacts.length > 0 ? { stages: stageArtifacts } : {}),
|
|
725
729
|
};
|
|
@@ -56,6 +56,8 @@ export const KNOWN_STAGEFLOW_ENV_KEYS = new Set([
|
|
|
56
56
|
"STAGEFLOW_MAX_ACTIVE_STAGES_PER_RUN",
|
|
57
57
|
"STAGEFLOW_MAX_ACTIVE_STAGE_PROCESSES",
|
|
58
58
|
"STAGEFLOW_STAGE_EXECUTION",
|
|
59
|
+
"STAGEFLOW_STAGE_CONTAINER_IMAGE",
|
|
60
|
+
"STAGEFLOW_STAGE_CONTAINER_DOCKER_BIN",
|
|
59
61
|
"STAGEFLOW_STAGE_ENV_ALLOW",
|
|
60
62
|
"STAGEFLOW_STAGE_ENV_PASSTHROUGH",
|
|
61
63
|
"STAGEFLOW_GC_INTERVAL_MS",
|
|
@@ -69,6 +71,13 @@ export const KNOWN_STAGEFLOW_ENV_KEYS = new Set([
|
|
|
69
71
|
"STAGEFLOW_SECRET_REGISTRY",
|
|
70
72
|
"STAGEFLOW_BUILD_SHA",
|
|
71
73
|
"STAGEFLOW_TRIGGER_TICK_INTERVAL_MS",
|
|
74
|
+
"STAGEFLOW_AGENT_SCHEDULE_TICK_INTERVAL_MS",
|
|
75
|
+
"STAGEFLOW_AGENT_SESSION_IDLE_MINUTES",
|
|
76
|
+
"STAGEFLOW_AGENT_STALL_SECONDS",
|
|
77
|
+
"STAGEFLOW_AGENT_CHANNEL_LISTENERS",
|
|
78
|
+
"STAGEFLOW_AGENTS_ALLOW_CURSOR_NATIVE_TOOLS",
|
|
79
|
+
"STAGEFLOW_PUBLIC_CHAT_ORIGINS",
|
|
80
|
+
"STAGEFLOW_MCP_ENV_ALLOW",
|
|
72
81
|
"STAGEFLOW_GITHUB_POLL_INTERVAL_MS",
|
|
73
82
|
]);
|
|
74
83
|
const KNOWN_ENV_PREFIXES = [
|