@usenaive-sdk/blueprints 0.5.0 → 0.6.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/dist/index.js CHANGED
@@ -788,7 +788,11 @@ var SessionSchema = z6.object({
788
788
  var SendMessageSchema = z6.object({
789
789
  message: z6.string(),
790
790
  interrupt: z6.boolean().optional(),
791
+ queue: z6.boolean().optional(),
791
792
  idempotency_key: z6.string().optional()
793
+ }).refine((body) => !(body.interrupt === true && body.queue === true), {
794
+ message: "a send is either an interrupt or queued, not both",
795
+ path: ["queue"]
792
796
  });
793
797
  var SendAcceptedSchema = z6.object({
794
798
  session_id: z6.string(),
@@ -892,11 +896,16 @@ var BoardCardSchema = z8.object({
892
896
  object: z8.literal("board_card"),
893
897
  board_id: idOf("brd"),
894
898
  title: z8.string().min(1),
899
+ /** The brief — what was asked. */
895
900
  body: z8.string(),
901
+ /** What the holder wrote back; a `done` carries one. */
902
+ note: z8.string().nullable(),
896
903
  status: BoardStatusSchema,
897
904
  assignee: z8.string().nullable(),
898
905
  /** The cards this one waits on. A `blocked` card with an empty list is stuck on the world. */
899
906
  blocked_by: z8.array(idOf("crd")),
907
+ woken_session_id: idOf("ses").nullable(),
908
+ reported_status: BoardStatusSchema.nullable(),
900
909
  created_at: Timestamp,
901
910
  updated_at: Timestamp
902
911
  });
@@ -1337,6 +1346,8 @@ var BlueprintReportSchema = z14.object({
1337
1346
  agents: z14.array(ResourceReportSchema),
1338
1347
  schedules: z14.array(ResourceReportSchema),
1339
1348
  intake: z14.array(ResourceReportSchema),
1349
+ /** One line per declared task (§31.11), `id` the card's `crd_`. Defaulted so older rows parse. */
1350
+ tasks: z14.array(ResourceReportSchema).default([]),
1340
1351
  skipped: z14.array(z14.string()),
1341
1352
  deselected: z14.array(z14.string()).default([])
1342
1353
  });
@@ -1426,202 +1437,221 @@ var ProjectContextSchema = z14.object({
1426
1437
  });
1427
1438
 
1428
1439
  // ../core/src/schema/blueprint.ts
1440
+ import { z as z16 } from "zod";
1441
+
1442
+ // ../core/src/schema/social.ts
1429
1443
  import { z as z15 } from "zod";
1430
- var BlueprintScheduleSchema = z15.object({
1431
- agent: z15.string().min(1),
1432
- cron: z15.string().min(1),
1433
- timezone: z15.string().nullable(),
1444
+ var SOCIAL_PLATFORMS = [
1445
+ "bluesky",
1446
+ "facebook",
1447
+ "instagram",
1448
+ "linkedin",
1449
+ "mastodon",
1450
+ "pinterest",
1451
+ "reddit",
1452
+ "threads",
1453
+ "tiktok",
1454
+ "x",
1455
+ "youtube"
1456
+ ];
1457
+ var SOCIAL_TEXT_PLATFORMS = [
1458
+ "bluesky",
1459
+ "facebook",
1460
+ "linkedin",
1461
+ "mastodon",
1462
+ "threads",
1463
+ "x"
1464
+ ];
1465
+ var SOCIAL_MEDIA_PLATFORMS = ["instagram", "tiktok", "youtube"];
1466
+ var SOCIAL_VISIBILITIES = ["public", "unlisted", "private"];
1467
+ var SOCIAL_VISIBILITY_PLATFORMS = ["mastodon", "youtube"];
1468
+ var SocialVisibilitySchema = z15.enum(SOCIAL_VISIBILITIES);
1469
+ var SOCIAL_VISIBILITY_RULE = `visibility is ${SOCIAL_VISIBILITIES.join(", ")} and only ${SOCIAL_VISIBILITY_PLATFORMS.join(" and ")} accept it; omit it and the post publishes at the account's own default, which on a video channel is normally public`;
1470
+ var SocialPlatformSchema = z15.enum(SOCIAL_PLATFORMS);
1471
+ var SocialTextPlatformSchema = z15.enum(SOCIAL_TEXT_PLATFORMS);
1472
+ var isText = (platform) => SOCIAL_TEXT_PLATFORMS.includes(platform);
1473
+ var isMedia = (platform) => SOCIAL_MEDIA_PLATFORMS.includes(platform);
1474
+ var SOCIAL_PLATFORM_RULE = `${SOCIAL_TEXT_PLATFORMS.join(", ")} take a text-only post; ${SOCIAL_MEDIA_PLATFORMS.join(", ")} may be named only when the post carries media_urls or file_ids; ${SOCIAL_PLATFORMS.filter((platform) => !isText(platform) && !isMedia(platform)).join(", ")} cannot be posted to`;
1475
+ var SocialAccountSchema = z15.object({
1476
+ id: z15.string().min(1),
1477
+ object: z15.literal("social_account"),
1478
+ identity_id: idOf("idn"),
1479
+ platform: SocialPlatformSchema,
1480
+ username: z15.string().nullable(),
1481
+ display_name: z15.string().nullable(),
1482
+ avatar_url: z15.url().nullable(),
1483
+ connected_at: Timestamp.nullable()
1484
+ });
1485
+ var SocialPostStatusSchema = z15.enum([
1486
+ "draft",
1487
+ "scheduled",
1488
+ "processing",
1489
+ "posted",
1490
+ "failed",
1491
+ "deleted"
1492
+ ]);
1493
+ var SocialPostSchema = z15.object({
1494
+ id: z15.string().min(1),
1495
+ object: z15.literal("social_post"),
1496
+ identity_id: idOf("idn"),
1497
+ title: z15.string(),
1498
+ platforms: z15.array(SocialPlatformSchema),
1499
+ status: SocialPostStatusSchema,
1500
+ scheduled_at: Timestamp.nullable(),
1501
+ posted_at: Timestamp.nullable(),
1502
+ error: z15.string().nullable()
1503
+ });
1504
+ var SocialUploadSchema = z15.object({
1505
+ id: z15.string().min(1),
1506
+ object: z15.literal("social_upload"),
1507
+ identity_id: idOf("idn"),
1508
+ type: z15.enum(["image", "video", "document"]),
1509
+ url: z15.url().nullable()
1510
+ });
1511
+ var SocialAnalyticsSchema = z15.object({
1512
+ object: z15.literal("social_analytics"),
1513
+ identity_id: idOf("idn"),
1514
+ resource_id: z15.string().min(1),
1515
+ platform: SocialPlatformSchema.nullable(),
1516
+ data: z15.unknown()
1517
+ });
1518
+ var SocialCommentSchema = z15.object({
1519
+ id: z15.string().min(1),
1520
+ object: z15.literal("social_comment"),
1521
+ identity_id: idOf("idn"),
1522
+ post_id: z15.string().min(1),
1523
+ platforms: z15.array(SocialPlatformSchema),
1524
+ status: SocialPostStatusSchema,
1525
+ posted_at: Timestamp.nullable(),
1526
+ data: z15.unknown()
1527
+ });
1528
+
1529
+ // ../core/src/schema/blueprint.ts
1530
+ var BlueprintScheduleSchema = z16.object({
1531
+ agent: z16.string().min(1),
1532
+ cron: z16.string().min(1),
1533
+ timezone: z16.string().nullable(),
1434
1534
  /** The message each fire opens its session with — the sentence worth printing. */
1435
- input: z15.string().min(1),
1535
+ input: z16.string().min(1),
1436
1536
  budget_micro_usd: MicroUsdField.nonnegative()
1437
1537
  });
1438
1538
  var BlueprintBudgetSchema = BlueprintScheduleSchema.pick({ agent: true, cron: true, budget_micro_usd: true });
1439
- var BlueprintCrewSchema = z15.object({
1440
- name: z15.string().min(1),
1441
- description: z15.string().nullable(),
1442
- model: z15.string().min(1),
1443
- briefed: z15.boolean(),
1444
- intake: z15.string().nullable().optional(),
1539
+ var BlueprintCrewSchema = z16.object({
1540
+ name: z16.string().min(1),
1541
+ description: z16.string().nullable(),
1542
+ model: z16.string().min(1),
1543
+ briefed: z16.boolean(),
1544
+ intake: z16.string().nullable().optional(),
1445
1545
  /** What the first session may spend: the intake's own cap, else the agent's (§31.4). Set iff `briefed`. */
1446
1546
  intake_micro_usd: MicroUsdField.nonnegative().optional(),
1447
- role: z15.string().min(1).optional(),
1448
- skills: z15.array(SkillRefSchema).optional(),
1449
- tools: z15.array(z15.string().min(1)).optional(),
1450
- required: z15.boolean().optional()
1547
+ role: z16.string().min(1).optional(),
1548
+ skills: z16.array(SkillRefSchema).optional(),
1549
+ tools: z16.array(z16.string().min(1)).optional(),
1550
+ required: z16.boolean().optional()
1451
1551
  });
1452
- var BlueprintAppSchema = z15.object({
1453
- name: z15.string().min(1),
1552
+ var BlueprintAppSchema = z16.object({
1553
+ name: z16.string().min(1),
1454
1554
  type: AppTypeSchema,
1455
- description: z15.string().nullable(),
1456
- mcp: z15.string().nullable().optional(),
1457
- tools: z15.array(z15.string().min(1)).optional(),
1555
+ description: z16.string().nullable(),
1556
+ mcp: z16.string().nullable().optional(),
1557
+ tools: z16.array(z16.string().min(1)).optional(),
1458
1558
  /** Whether an install may deselect it (§31.2). Optional: the rollout argument above. */
1459
- required: z15.boolean().optional()
1559
+ required: z16.boolean().optional()
1560
+ });
1561
+ var BlueprintIdentityConnectionsSchema = z16.object({
1562
+ social: z16.object({
1563
+ from: z16.string().min(1),
1564
+ map: z16.record(z16.string().min(1), SocialPlatformSchema)
1565
+ }).optional()
1460
1566
  });
1461
- var ContentHashSchema = z15.string().regex(/^[0-9a-f]{64}$/, "a lowercase sha-256 hex digest");
1462
- var BlueprintTreeSchema = z15.object({
1567
+ var BlueprintIdentitySchema = z16.object({
1568
+ name: z16.string().min(1),
1569
+ description: z16.string().nullable(),
1570
+ connections: BlueprintIdentityConnectionsSchema.optional()
1571
+ });
1572
+ var ContentHashSchema = z16.string().regex(/^[0-9a-f]{64}$/, "a lowercase sha-256 hex digest");
1573
+ var BlueprintTreeSchema = z16.object({
1463
1574
  /** `apps[].name` in the declaration — the same name §31.5 prints and the install ensures by. */
1464
- app: z15.string().min(1),
1575
+ app: z16.string().min(1),
1465
1576
  content_hash: ContentHashSchema
1466
1577
  });
1467
- var BlueprintArtifactSchema = z15.object({
1578
+ var BlueprintArtifactSchema = z16.object({
1468
1579
  id: idOf("bpa"),
1469
- object: z15.literal("blueprint_artifact"),
1580
+ object: z16.literal("blueprint_artifact"),
1470
1581
  /** The machine — its repository, screens and `/api/*`. */
1471
- blueprint: z15.string().min(1),
1582
+ blueprint: z16.string().min(1),
1472
1583
  /** Which of that blueprint's templates provides the crew. */
1473
- template: z15.string().min(1),
1584
+ template: z16.string().min(1),
1474
1585
  /** The blueprint repository's own released version. Stored, never parsed (ADR-0401). */
1475
- version: z15.string().min(1),
1586
+ version: z16.string().min(1),
1476
1587
  /**
1477
1588
  * The built trees, one per deployable app, ordered as the declaration orders its apps. Never
1478
1589
  * empty: an artifact that built nothing is not an artifact, and the column behind it says so
1479
1590
  * (migration 0040) exactly as the single `content_hash` it replaces was `not null`.
1480
1591
  */
1481
- trees: z15.array(BlueprintTreeSchema).min(1),
1592
+ trees: z16.array(BlueprintTreeSchema).min(1),
1482
1593
  /**
1483
1594
  * The setup questions, asked by the studio **before** provisioning. `QuestionField` (§7.1) is
1484
1595
  * reused rather than restated: a setup question and a question a parked agent asks are the same
1485
1596
  * object at different times, so one component renders both.
1486
1597
  */
1487
- questions: z15.array(QuestionFieldSchema),
1488
- schedules: z15.array(BlueprintScheduleSchema),
1598
+ questions: z16.array(QuestionFieldSchema),
1599
+ schedules: z16.array(BlueprintScheduleSchema),
1489
1600
  /** Who the crew is, and what it deploys — §31.5. Derived from `declaration`, never stored twice. */
1490
- agents: z15.array(BlueprintCrewSchema),
1491
- apps: z15.array(BlueprintAppSchema),
1601
+ agents: z16.array(BlueprintCrewSchema),
1602
+ apps: z16.array(BlueprintAppSchema),
1492
1603
  /**
1493
1604
  * The agents a template creates per client the operator signs — printed, never provisioned by an
1494
1605
  * apply (§31.1). Absent on rows published before WS12 v2, hence optional.
1495
1606
  */
1496
- crew_per_client: z15.array(BlueprintCrewSchema).optional(),
1607
+ crew_per_client: z16.array(BlueprintCrewSchema).optional(),
1608
+ /**
1609
+ * The personas the crew posts from, and which accounts to connect to each before the crew
1610
+ * exists (§31.10). Derived from `declaration` like `agents`; optional for the §31.5 rollout reason.
1611
+ */
1612
+ identities: z16.array(BlueprintIdentitySchema).optional(),
1497
1613
  /**
1498
1614
  * The creator template a row was published from (§42.8), `null` on every first-party row.
1499
1615
  * `creator_name` is the public display name the landing page credits. The API sends all three
1500
1616
  * on every row; they are optional here only so an artifact typed before §42 still reads.
1501
1617
  */
1502
1618
  creator_template_id: idOf("ctp").nullish(),
1503
- creator_template_version: z15.number().int().positive().nullish(),
1504
- creator_name: z15.string().min(1).nullish(),
1619
+ creator_template_version: z16.number().int().positive().nullish(),
1620
+ creator_name: z16.string().min(1).nullish(),
1505
1621
  published_at: Timestamp
1506
1622
  });
1507
1623
 
1508
1624
  // ../core/src/schema/vault.ts
1509
- import { z as z16 } from "zod";
1510
- var VaultCredentialKindSchema = z16.enum(["env_var", "static_bearer", "mcp_oauth"]);
1511
- var VaultSchema = z16.object({
1625
+ import { z as z17 } from "zod";
1626
+ var VaultCredentialKindSchema = z17.enum(["env_var", "static_bearer", "mcp_oauth"]);
1627
+ var VaultSchema = z17.object({
1512
1628
  id: idOf("vlt"),
1513
- object: z16.literal("vault"),
1629
+ object: z17.literal("vault"),
1514
1630
  identity_id: idOf("idn").nullable(),
1515
- display_name: z16.string().min(1).max(128),
1516
- credential_count: z16.number().int().nonnegative(),
1631
+ display_name: z17.string().min(1).max(128),
1632
+ credential_count: z17.number().int().nonnegative(),
1517
1633
  created_at: Timestamp
1518
1634
  });
1519
- var VaultCredentialSchema = z16.object({
1635
+ var VaultCredentialSchema = z17.object({
1520
1636
  id: idOf("vcr"),
1521
- object: z16.literal("vault_credential"),
1637
+ object: z17.literal("vault_credential"),
1522
1638
  vault_id: idOf("vlt"),
1523
1639
  kind: VaultCredentialKindSchema,
1524
1640
  /** For `env_var`, the environment variable name. For the MCP kinds, a display label. */
1525
- key: z16.string().min(1).max(128),
1641
+ key: z17.string().min(1).max(128),
1526
1642
  /** The exact destination host an `env_var` credential is bound to. Never a wildcard. */
1527
- host: z16.string().nullable(),
1643
+ host: z17.string().nullable(),
1528
1644
  /** The exact MCP server URL an `mcp_*` token is matched to. */
1529
- mcp_server_url: z16.url().nullable(),
1530
- placeholder_ref: z16.string(),
1645
+ mcp_server_url: z17.url().nullable(),
1646
+ placeholder_ref: z17.string(),
1531
1647
  expires_at: Timestamp.nullable(),
1532
1648
  last_injected_at: Timestamp.nullable(),
1533
1649
  created_at: Timestamp
1534
1650
  });
1535
1651
 
1536
- // ../core/src/schema/social.ts
1537
- import { z as z17 } from "zod";
1538
- var SOCIAL_PLATFORMS = [
1539
- "bluesky",
1540
- "facebook",
1541
- "instagram",
1542
- "linkedin",
1543
- "mastodon",
1544
- "pinterest",
1545
- "reddit",
1546
- "threads",
1547
- "tiktok",
1548
- "x",
1549
- "youtube"
1550
- ];
1551
- var SOCIAL_TEXT_PLATFORMS = [
1552
- "bluesky",
1553
- "facebook",
1554
- "linkedin",
1555
- "mastodon",
1556
- "threads",
1557
- "x"
1558
- ];
1559
- var SOCIAL_MEDIA_PLATFORMS = ["instagram", "tiktok", "youtube"];
1560
- var SOCIAL_VISIBILITIES = ["public", "unlisted", "private"];
1561
- var SOCIAL_VISIBILITY_PLATFORMS = ["mastodon", "youtube"];
1562
- var SocialVisibilitySchema = z17.enum(SOCIAL_VISIBILITIES);
1563
- var SOCIAL_VISIBILITY_RULE = `visibility is ${SOCIAL_VISIBILITIES.join(", ")} and only ${SOCIAL_VISIBILITY_PLATFORMS.join(" and ")} accept it; omit it and the post publishes at the account's own default, which on a video channel is normally public`;
1564
- var SocialPlatformSchema = z17.enum(SOCIAL_PLATFORMS);
1565
- var SocialTextPlatformSchema = z17.enum(SOCIAL_TEXT_PLATFORMS);
1566
- var isText = (platform) => SOCIAL_TEXT_PLATFORMS.includes(platform);
1567
- var isMedia = (platform) => SOCIAL_MEDIA_PLATFORMS.includes(platform);
1568
- var SOCIAL_PLATFORM_RULE = `${SOCIAL_TEXT_PLATFORMS.join(", ")} take a text-only post; ${SOCIAL_MEDIA_PLATFORMS.join(", ")} may be named only when the post carries media_urls or file_ids; ${SOCIAL_PLATFORMS.filter((platform) => !isText(platform) && !isMedia(platform)).join(", ")} cannot be posted to`;
1569
- var SocialAccountSchema = z17.object({
1570
- id: z17.string().min(1),
1571
- object: z17.literal("social_account"),
1572
- identity_id: idOf("idn"),
1573
- platform: SocialPlatformSchema,
1574
- username: z17.string().nullable(),
1575
- display_name: z17.string().nullable(),
1576
- avatar_url: z17.url().nullable(),
1577
- connected_at: Timestamp.nullable()
1578
- });
1579
- var SocialPostStatusSchema = z17.enum([
1580
- "draft",
1581
- "scheduled",
1582
- "processing",
1583
- "posted",
1584
- "failed",
1585
- "deleted"
1586
- ]);
1587
- var SocialPostSchema = z17.object({
1588
- id: z17.string().min(1),
1589
- object: z17.literal("social_post"),
1590
- identity_id: idOf("idn"),
1591
- title: z17.string(),
1592
- platforms: z17.array(SocialPlatformSchema),
1593
- status: SocialPostStatusSchema,
1594
- scheduled_at: Timestamp.nullable(),
1595
- posted_at: Timestamp.nullable(),
1596
- error: z17.string().nullable()
1597
- });
1598
- var SocialUploadSchema = z17.object({
1599
- id: z17.string().min(1),
1600
- object: z17.literal("social_upload"),
1601
- identity_id: idOf("idn"),
1602
- type: z17.enum(["image", "video", "document"]),
1603
- url: z17.url().nullable()
1604
- });
1605
- var SocialAnalyticsSchema = z17.object({
1606
- object: z17.literal("social_analytics"),
1607
- identity_id: idOf("idn"),
1608
- resource_id: z17.string().min(1),
1609
- platform: SocialPlatformSchema.nullable(),
1610
- data: z17.unknown()
1611
- });
1612
- var SocialCommentSchema = z17.object({
1613
- id: z17.string().min(1),
1614
- object: z17.literal("social_comment"),
1615
- identity_id: idOf("idn"),
1616
- post_id: z17.string().min(1),
1617
- platforms: z17.array(SocialPlatformSchema),
1618
- status: SocialPostStatusSchema,
1619
- posted_at: Timestamp.nullable(),
1620
- data: z17.unknown()
1621
- });
1622
-
1623
1652
  // ../core/src/schema/handoff.ts
1624
1653
  import { z as z18 } from "zod";
1654
+ var MAX_HANDOFF_TARGETS = 6;
1625
1655
  var HandoffRequestSchema = z18.object({
1626
1656
  agent: z18.string().min(1),
1627
1657
  message: z18.string().min(1),
@@ -1634,12 +1664,42 @@ var HandoffSchema = z18.object({
1634
1664
  status: z18.enum(["queued", "already_running"]),
1635
1665
  handoff_key: z18.string().nullable()
1636
1666
  });
1667
+ var SessionHandoffRequestSchema = z18.object({
1668
+ agents: z18.array(z18.string().min(1)).min(1).max(MAX_HANDOFF_TARGETS),
1669
+ message: z18.string().min(1),
1670
+ handoff_key: z18.string().min(1).max(128).optional(),
1671
+ budget_micro_usd: z18.number().int().positive().optional()
1672
+ });
1673
+ var HandoffRefusalSchema = z18.object({
1674
+ object: z18.literal("handoff_refusal"),
1675
+ agent: z18.string(),
1676
+ code: z18.string(),
1677
+ message: z18.string()
1678
+ });
1679
+ var HandoffBatchSchema = z18.object({
1680
+ object: z18.literal("handoff_batch"),
1681
+ handoffs: z18.array(HandoffSchema.extend({ agent_id: idOf("agt") })),
1682
+ refusals: z18.array(HandoffRefusalSchema)
1683
+ });
1637
1684
  var SessionHandoffSchema = z18.object({
1638
1685
  from_session: idOf("ses"),
1639
1686
  from_agent: idOf("agt"),
1640
1687
  key: z18.string().nullable(),
1641
1688
  depth: z18.number().int().positive()
1642
1689
  });
1690
+ var ChannelBriefSchema = z18.object({
1691
+ root_session_id: idOf("ses"),
1692
+ name: z18.string().min(1),
1693
+ members: z18.array(z18.string().min(1))
1694
+ });
1695
+ var ChannelPostRequestSchema = z18.object({
1696
+ message: z18.string().min(1).max(2e4)
1697
+ });
1698
+ var ChannelPostSchema = z18.object({
1699
+ object: z18.literal("channel_post"),
1700
+ root_session_id: idOf("ses"),
1701
+ queued: z18.boolean()
1702
+ });
1643
1703
 
1644
1704
  // ../core/src/schema/media.ts
1645
1705
  import { z as z19 } from "zod";
@@ -2675,6 +2735,7 @@ var WalletDeclSchema = CreateWalletSchema.or(
2675
2735
  );
2676
2736
  var CardDeclSchema = IssueCardSchema;
2677
2737
  var IdentityDeclSchema = IdentitySchema.pick({ name: true, description: true }).partial({ description: true }).extend({
2738
+ connections: BlueprintIdentityConnectionsSchema.optional(),
2678
2739
  legal: z28.array(LegalDeclSchema).optional(),
2679
2740
  wallet: WalletDeclSchema.optional(),
2680
2741
  cards: z28.array(CardDeclSchema).optional()
@@ -2687,6 +2748,13 @@ var RemovedSchema = z28.object({
2687
2748
  vaults: z28.array(z28.string().min(1)).default([])
2688
2749
  });
2689
2750
  var KeptSchema = z28.object({ agents: z28.array(z28.string().min(1)).default([]) });
2751
+ var TaskDeclSchema = z28.object({
2752
+ key: z28.string().min(1),
2753
+ title: z28.string().min(1).max(200),
2754
+ body: z28.string().max(8192).optional(),
2755
+ assignee: z28.string().min(1).optional(),
2756
+ blocked_by: z28.array(z28.string().min(1)).optional()
2757
+ });
2690
2758
  var ProjectSchema = z28.object({
2691
2759
  name: z28.string().min(1),
2692
2760
  /** The machine this project runs: its repo, its screens, its `/api/*` (`template.ts`). */
@@ -2717,6 +2785,8 @@ var ProjectSchema = z28.object({
2717
2785
  * crew's business at the moment a client is signed, not the declaration's.
2718
2786
  */
2719
2787
  crew_per_client: z28.array(z28.object({ name: z28.string().min(1), role: z28.string().min(1).optional(), description: z28.string().optional() })).optional(),
2788
+ /** The crew's first work, as cards on the organization's board rather than N private intakes (§31.11). */
2789
+ tasks: z28.array(TaskDeclSchema).default([]),
2720
2790
  removed: RemovedSchema.default({ apps: [], agents: [], skills: [], identities: [], vaults: [] }),
2721
2791
  kept: KeptSchema.default({ agents: [] })
2722
2792
  });
@@ -2732,6 +2802,20 @@ function assertUniqueNames(kind, names) {
2732
2802
  seen.add(name);
2733
2803
  }
2734
2804
  }
2805
+ function inDependencyOrder(tasks) {
2806
+ const placed = /* @__PURE__ */ new Set();
2807
+ const out = [];
2808
+ while (out.length < tasks.length) {
2809
+ const next = tasks.find((task) => !placed.has(task.key) && (task.blocked_by ?? []).every((key) => placed.has(key)));
2810
+ if (next === void 0) {
2811
+ const stuck = tasks.filter((task) => !placed.has(task.key)).map((task) => `"${task.key}"`);
2812
+ refuse(`tasks ${stuck.join(", ")}: blocked_by forms a cycle \u2014 a card cannot wait on a card that waits on it`);
2813
+ }
2814
+ placed.add(next.key);
2815
+ out.push(next);
2816
+ }
2817
+ return out;
2818
+ }
2735
2819
  function deepFreeze(value) {
2736
2820
  if (value !== null && typeof value === "object") {
2737
2821
  for (const child of Object.values(value)) deepFreeze(child);
@@ -2779,6 +2863,20 @@ function parseProject(input) {
2779
2863
  assertUniqueNames("bucket", (app.buckets ?? []).map((bucket) => bucket.name));
2780
2864
  }
2781
2865
  const identityNames = new Set(config.identities.map((i) => i.name));
2866
+ const connectable = config.identities.filter((i) => i.connections !== void 0).map((i) => i.name);
2867
+ if (connectable.length > 1) {
2868
+ refuse(`identities ${connectable.map((n) => `"${n}"`).join(", ")} each declare connections, but the studio's Add connections step connects one persona \u2014 declare connections on the one the crew publishes through`);
2869
+ }
2870
+ for (const identity of config.identities) {
2871
+ const social = identity.connections?.social;
2872
+ if (social === void 0) continue;
2873
+ const question = config.questions.find((q) => q.key === social.from);
2874
+ if (question === void 0) refuse(`identity "${identity.name}": connections.social.from names "${social.from}", but no question with that key is declared in this project`);
2875
+ if (question.type !== "choice") refuse(`identity "${identity.name}": connections.social.from names "${social.from}", which is a ${question.type} question \u2014 the map is keyed by a choice question's options`);
2876
+ for (const label of Object.keys(social.map)) {
2877
+ if (!question.options.includes(label)) refuse(`identity "${identity.name}": connections.social.map names "${label}", which is not one of question "${social.from}"'s options \u2014 a key no answer can equal is a row nobody can reach`);
2878
+ }
2879
+ }
2782
2880
  for (const vault of config.vaults) {
2783
2881
  if (vault.identity !== void 0 && !identityNames.has(vault.identity)) {
2784
2882
  refuse(`vault "${vault.name}": identity names "${vault.identity}", but no identity with that name is declared in this project`);
@@ -2837,6 +2935,21 @@ function parseProject(input) {
2837
2935
  }
2838
2936
  }
2839
2937
  }
2938
+ assertUniqueNames("task", config.tasks.map((task) => task.key));
2939
+ const taskKeys = new Set(config.tasks.map((task) => task.key));
2940
+ for (const task of config.tasks) {
2941
+ if (`${config.name}:${task.key}`.length > 128) {
2942
+ refuse(`task "${task.key}": "${config.name}:${task.key}" is longer than the 128 characters a card key holds \u2014 shorten the key`);
2943
+ }
2944
+ if (task.assignee !== void 0 && !agentNames.has(task.assignee)) {
2945
+ refuse(`task "${task.key}": assignee names "${task.assignee}", but no agent with that name is declared in this project`);
2946
+ }
2947
+ for (const key of task.blocked_by ?? []) {
2948
+ if (key === task.key) refuse(`task "${task.key}": blocked_by names itself \u2014 a card cannot wait on itself`);
2949
+ if (!taskKeys.has(key)) refuse(`task "${task.key}": blocked_by names "${key}", but no task with that key is declared in this project`);
2950
+ }
2951
+ }
2952
+ inDependencyOrder(config.tasks);
2840
2953
  return deepFreeze(config);
2841
2954
  }
2842
2955
  function withTemplate(input, templates) {
@@ -3527,6 +3640,75 @@ async function reportLegal(client, decls, identityId, skipped) {
3527
3640
  );
3528
3641
  }
3529
3642
 
3643
+ // src/tasks.ts
3644
+ var ORCHESTRATOR_MARKER = "orchestrator";
3645
+ var CEO_LABEL = "CEO";
3646
+ var CEO_DETAIL = "Hires and directs the crew";
3647
+ var CEO_BUDGET = { cap_micro_usd: 5e7, max_task_micro_usd: 5e6, period: "month" };
3648
+ var isOrchestrator = (agent) => agent.metadata["role"] === ORCHESTRATOR_MARKER;
3649
+ function seated(live, members) {
3650
+ const roster = live?.agents ?? [];
3651
+ const already = new Set(roster.flatMap((entry) => entry.type === "agent" ? [entry.id] : []));
3652
+ const joining = members.filter((id) => !already.has(id)).map((id) => ({ type: "agent", id }));
3653
+ return { type: "coordinator", board: true, agents: [...roster, ...joining] };
3654
+ }
3655
+ async function ensureOrchestrator(client, existing, members, dryRun) {
3656
+ const current = existing.find(isOrchestrator);
3657
+ const live = current === void 0 ? null : normaliseMultiagent(current.multiagent);
3658
+ const team = seated(live, members);
3659
+ if (current === void 0) {
3660
+ if (dryRun) return { name: CEO_LABEL, action: "created" };
3661
+ const model = (await client.models.list({ window: "immediate", limit: 1 })).data[0];
3662
+ if (model === void 0) throw new BlueprintRefusal("no model is served, so the board's owner cannot be created");
3663
+ const made = await client.agents.create({
3664
+ name: CEO_LABEL,
3665
+ harness: "pi",
3666
+ model: model.id,
3667
+ description: CEO_DETAIL,
3668
+ budget: CEO_BUDGET,
3669
+ metadata: { role: ORCHESTRATOR_MARKER },
3670
+ multiagent: team
3671
+ });
3672
+ return { name: CEO_LABEL, action: "created", id: made.id };
3673
+ }
3674
+ if (live !== null && !changed(team, live)) return { name: current.name, action: "unchanged", id: current.id };
3675
+ if (!dryRun) await client.agents.update(current.id, { multiagent: team });
3676
+ return { name: current.name, action: "updated", id: current.id };
3677
+ }
3678
+ async function seedTasks(client, config, existing, agents, dryRun, into, skipped) {
3679
+ const members = agents.flatMap((one) => one.id === void 0 ? [] : [one.id]);
3680
+ const owner = await line(CEO_LABEL, () => ensureOrchestrator(client, existing, members, dryRun));
3681
+ agents.push(owner);
3682
+ const board = owner.action === "refused" || dryRun || owner.id === void 0 ? null : await client.board.create(owner.id);
3683
+ const before = board === null ? [] : await listAll(client.config, `/v1/boards/${board.id}/cards`, BoardCardSchema, skipped);
3684
+ const known = new Set(before.map((card) => card.id));
3685
+ const ids = /* @__PURE__ */ new Map();
3686
+ for (const task of inDependencyOrder(config.tasks)) {
3687
+ into.push(
3688
+ await line(task.key, async () => {
3689
+ if (owner.action === "refused") return { name: task.key, action: "refused", reason: `board owner "${owner.name}" was refused: ${owner.reason}` };
3690
+ const assignee = task.assignee;
3691
+ if (assignee !== void 0 && !agents.some((one) => one.name === assignee && one.action !== "refused")) {
3692
+ return { name: task.key, action: "refused", reason: `assignee "${assignee}" was not provisioned in this run` };
3693
+ }
3694
+ if (board === null) return { name: task.key, action: "created" };
3695
+ const blockers = task.blocked_by ?? [];
3696
+ const unmet = blockers.find((key) => !ids.has(key));
3697
+ if (unmet !== void 0) return { name: task.key, action: "refused", reason: `blocked_by "${unmet}" was not seeded in this run` };
3698
+ const card = await client.board.cards.create(board.id, {
3699
+ key: `${config.name}:${task.key}`,
3700
+ title: task.title,
3701
+ body: task.body,
3702
+ assignee,
3703
+ blocked_by: blockers.map((key) => ids.get(key))
3704
+ });
3705
+ ids.set(task.key, card.id);
3706
+ return { name: task.key, action: known.has(card.id) ? "unchanged" : "created", id: card.id };
3707
+ })
3708
+ );
3709
+ }
3710
+ }
3711
+
3530
3712
  // src/vaults.ts
3531
3713
  async function upsertVault(client, decl, existing, identityIds, dryRun) {
3532
3714
  const identityId = decl.identity === void 0 ? void 0 : identityIds.get(decl.identity);
@@ -3737,7 +3919,7 @@ async function up(config, client, opts = {}) {
3737
3919
  const dir = opts.dir ?? process.cwd();
3738
3920
  const ids = { apps: /* @__PURE__ */ new Map(), identities: /* @__PURE__ */ new Map() };
3739
3921
  const taken = selectCrew(config, opts.select);
3740
- const report2 = { dry_run: dryRun, ...applied(config), skills: [], identities: [], vaults: [], apps: [], agents: [], schedules: [], intake: [], skipped: [], deselected: taken.deselected };
3922
+ const report2 = { dry_run: dryRun, ...applied(config), skills: [], identities: [], vaults: [], apps: [], agents: [], schedules: [], intake: [], tasks: [], skipped: [], deselected: taken.deselected };
3741
3923
  const owner = { project: config.name, adopt: opts.adopt ?? false, skipped: report2.skipped };
3742
3924
  const hosted = opts.trees !== void 0;
3743
3925
  for (const decl of config.skills) {
@@ -3800,7 +3982,7 @@ async function up(config, client, opts = {}) {
3800
3982
  )
3801
3983
  )
3802
3984
  );
3803
- const existing = taken.agents.length > 0 || config.removed.agents.length > 0 || config.kept.agents.length > 0 ? await listAll(client.config, "/v1/agents", AgentSchema, report2.skipped) : [];
3985
+ const existing = taken.agents.length > 0 || config.removed.agents.length > 0 || config.kept.agents.length > 0 || config.tasks.length > 0 ? await listAll(client.config, "/v1/agents", AgentSchema, report2.skipped) : [];
3804
3986
  const deployments = taken.agents.some((decl) => decl.schedules !== void 0) ? await listAll(client.config, "/v1/deployments", DeploymentSchema, report2.skipped) : [];
3805
3987
  const scoped = config.apps.length > 0 ? [...ids.apps.values()] : void 0;
3806
3988
  const crew = await eachInFlight(taken.agents, async (decl) => {
@@ -3827,6 +4009,7 @@ async function up(config, client, opts = {}) {
3827
4009
  if (hasRoster(decl.multiagent)) await patchRoster(client, decl, decl.multiagent, existing, agentIds, dryRun, report2.agents);
3828
4010
  }
3829
4011
  reportKept(config, existing, report2.agents);
4012
+ if (config.tasks.length > 0) await seedTasks(client, config, existing, report2.agents, dryRun, report2.tasks, report2.skipped);
3830
4013
  await applyRemoved(client, config.removed, { agents: existing, vaults }, owner, report2);
3831
4014
  if (briefed.length > 0 && !dryRun) {
3832
4015
  const serving = taken.apps.flatMap((decl) => {