@desplega.ai/agent-swarm 1.57.4 → 1.58.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.
Files changed (36) hide show
  1. package/openapi.json +1 -1
  2. package/package.json +1 -1
  3. package/src/agentmail/handlers.ts +20 -0
  4. package/src/be/db.ts +265 -2
  5. package/src/be/migrations/031_user_registry.sql +35 -0
  6. package/src/commands/runner.ts +8 -1
  7. package/src/github/handlers.ts +18 -1
  8. package/src/gitlab/handlers.ts +12 -1
  9. package/src/hooks/hook.ts +17 -2
  10. package/src/http/poll.ts +9 -0
  11. package/src/linear/sync.ts +25 -1
  12. package/src/server.ts +6 -0
  13. package/src/slack/actions.ts +10 -1
  14. package/src/slack/assistant.ts +7 -0
  15. package/src/slack/handlers.ts +9 -2
  16. package/src/tests/events-http.test.ts +2 -2
  17. package/src/tests/http-api-integration.test.ts +3 -3
  18. package/src/tests/linear-outbound-sync.test.ts +7 -7
  19. package/src/tests/preload.ts +14 -0
  20. package/src/tests/rest-api.test.ts +1 -1
  21. package/src/tests/user-identity.test.ts +306 -0
  22. package/src/tests/workflow-async-v2.test.ts +7 -7
  23. package/src/tests/workflow-engine-v2.test.ts +3 -3
  24. package/src/tests/workflow-hitl-routing.test.ts +7 -7
  25. package/src/tests/workflow-http-v2.test.ts +1 -1
  26. package/src/tests/workflow-retry-v2.test.ts +4 -4
  27. package/src/tests/workflow-retry-validation.test.ts +3 -3
  28. package/src/tests/workflow-validation-port-routing.test.ts +221 -0
  29. package/src/tools/get-task-details.ts +14 -1
  30. package/src/tools/manage-user.ts +172 -0
  31. package/src/tools/resolve-user.ts +55 -0
  32. package/src/tools/tool-config.ts +4 -0
  33. package/src/types.ts +26 -0
  34. package/src/workflows/engine.ts +18 -4
  35. package/src/workflows/executors/script.ts +16 -1
  36. package/src/workflows/validation.ts +6 -3
@@ -133,6 +133,10 @@ export const DEFERRED_TOOLS = new Set([
133
133
  "mcp-server-uninstall",
134
134
  "mcp-server-update",
135
135
 
136
+ // User Identity (2)
137
+ "resolve-user",
138
+ "manage-user",
139
+
136
140
  // Other (3)
137
141
  "cancel-task",
138
142
  "inject-learning",
package/src/types.ts CHANGED
@@ -153,8 +153,34 @@ export const AgentTaskSchema = z.object({
153
153
  // Credential tracking
154
154
  credentialKeySuffix: z.string().optional(),
155
155
  credentialKeyType: z.string().optional(),
156
+
157
+ // User identity — canonical user who requested this task
158
+ requestedByUserId: z.string().optional(),
159
+ });
160
+
161
+ // ============================================================================
162
+ // User Identity Types
163
+ // ============================================================================
164
+
165
+ export const UserSchema = z.object({
166
+ id: z.string(),
167
+ name: z.string().min(1),
168
+ email: z.string().optional(),
169
+ role: z.string().optional(),
170
+ notes: z.string().optional(),
171
+ slackUserId: z.string().optional(),
172
+ linearUserId: z.string().optional(),
173
+ githubUsername: z.string().optional(),
174
+ gitlabUsername: z.string().optional(),
175
+ emailAliases: z.array(z.string()).default([]),
176
+ preferredChannel: z.string().default("slack"),
177
+ timezone: z.string().optional(),
178
+ createdAt: z.iso.datetime(),
179
+ lastUpdatedAt: z.iso.datetime(),
156
180
  });
157
181
 
182
+ export type User = z.infer<typeof UserSchema>;
183
+
158
184
  export const AgentStatusSchema = z.enum(["idle", "busy", "offline"]);
159
185
 
160
186
  export const AgentSchema = z.object({
@@ -19,7 +19,7 @@ import type { ExecutorRegistry } from "./executors/registry";
19
19
  import { resolveInputs } from "./input";
20
20
  import { validateJsonSchema } from "./json-schema-validator";
21
21
  import { deepInterpolate } from "./template";
22
- import { runStepValidation } from "./validation";
22
+ import { runStepValidation, type ValidationRunResult } from "./validation";
23
23
 
24
24
  const DEFAULT_TIMEOUT_MS = 30_000;
25
25
  const MAX_ITERATIONS = Number(process.env.WORKFLOW_MAX_ITERATIONS) || 100;
@@ -526,8 +526,9 @@ async function executeStep(
526
526
  }
527
527
 
528
528
  // 7. Run validation if configured
529
+ let validationResult: ValidationRunResult | undefined;
529
530
  if (node.validation) {
530
- const validationResult = await runStepValidation(registry, node, result.output, ctx, meta);
531
+ validationResult = await runStepValidation(registry, node, result.output, ctx, meta);
531
532
 
532
533
  if (validationResult.outcome === "halt") {
533
534
  const errorMsg = "Validation failed (mustPass)";
@@ -555,10 +556,23 @@ async function executeStep(
555
556
  }
556
557
  }
557
558
 
558
- // 8. Checkpoint success
559
+ // 8. Set nextPort from validation result for record-based routing
560
+ // When validation determines pass/fail and the node uses port-based `next`,
561
+ // route to the correct port instead of activating all ports.
562
+ if (
563
+ validationResult?.passed !== undefined &&
564
+ !result.nextPort &&
565
+ node.next &&
566
+ typeof node.next === "object" &&
567
+ !Array.isArray(node.next)
568
+ ) {
569
+ result.nextPort = validationResult.passed ? "pass" : "fail";
570
+ }
571
+
572
+ // 9. Checkpoint success
559
573
  checkpointStep(runId, stepId, node.id, result, ctx);
560
574
 
561
- // 9. Determine successors based on nextPort
575
+ // 10. Determine successors based on nextPort
562
576
  // If executor returned a specific port, use it. Otherwise, get all successors
563
577
  // (fan-out behavior for non-branching nodes with record-based `next`).
564
578
  const successors = result.nextPort
@@ -41,9 +41,24 @@ export class ScriptExecutor extends BaseExecutor<
41
41
  try {
42
42
  const result = await Promise.race([this.runScript(config), this.timeoutPromise(timeoutMs)]);
43
43
 
44
+ // If stdout is valid JSON object, merge parsed fields into output
45
+ // so downstream nodes can access them via {{myScript.field}} interpolation
46
+ // (mirrors how agent-task nodes parse JSON in resume.ts)
47
+ let output: Record<string, unknown> = result;
48
+ if (result.exitCode === 0 && result.stdout) {
49
+ try {
50
+ const parsed = JSON.parse(result.stdout);
51
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
52
+ output = { ...result, ...parsed };
53
+ }
54
+ } catch {
55
+ // Not valid JSON — keep raw {exitCode, stdout, stderr}
56
+ }
57
+ }
58
+
44
59
  return {
45
60
  status: "success",
46
- output: result,
61
+ output: output as typeof result,
47
62
  nextPort: result.exitCode === 0 ? "success" : "failure",
48
63
  };
49
64
  } catch (err) {
@@ -5,6 +5,8 @@ export type ValidationOutcome = "pass" | "halt" | "retry";
5
5
 
6
6
  export interface ValidationRunResult {
7
7
  outcome: ValidationOutcome;
8
+ /** Whether the validation actually passed (true) or failed (false). */
9
+ passed?: boolean;
8
10
  /** Context additions if retry is needed */
9
11
  retryContext?: Record<string, unknown>;
10
12
  }
@@ -82,7 +84,7 @@ export async function runStepValidation(
82
84
  const passed = result.status === "success" && extractPassResult(executorType, result.output);
83
85
 
84
86
  if (passed) {
85
- return { outcome: "pass" };
87
+ return { outcome: "pass", passed: true };
86
88
  }
87
89
 
88
90
  // Validation failed
@@ -90,15 +92,16 @@ export async function runStepValidation(
90
92
  if (validation.retry) {
91
93
  return {
92
94
  outcome: "retry",
95
+ passed: false,
93
96
  retryContext: {
94
97
  previousOutput: stepOutput,
95
98
  validationResult: result.output,
96
99
  },
97
100
  };
98
101
  }
99
- return { outcome: "halt" };
102
+ return { outcome: "halt", passed: false };
100
103
  }
101
104
 
102
105
  // mustPass is false — treat failure as pass (advisory validation)
103
- return { outcome: "pass" };
106
+ return { outcome: "pass", passed: false };
104
107
  }