replicas-engine 0.1.594 → 0.1.596

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/src/index.js CHANGED
@@ -17,8 +17,8 @@ async function raceWithTimeout(promise, ms) {
17
17
  try {
18
18
  return await Promise.race([
19
19
  promise,
20
- new Promise((resolve4) => {
21
- timer = setTimeout(() => resolve4(TIMEOUT), ms);
20
+ new Promise((resolve5) => {
21
+ timer = setTimeout(() => resolve5(TIMEOUT), ms);
22
22
  })
23
23
  ]);
24
24
  } finally {
@@ -1327,7 +1327,7 @@ var DOCKER_ABILITY = {
1327
1327
 
1328
1328
  // ../shared/src/default-skills/replicas-agent/abilities/github.ts
1329
1329
  var SECTION3 = `### GitHub
1330
- Use the pre-authenticated \`gh\` CLI for pull requests, issues, actions, and API calls.
1330
+ Use the pre-authenticated \`gh\` CLI for repository workflows and the workspace-scoped TypeScript SDK for GitHub API calls.
1331
1331
 
1332
1332
  **Reference:** \`references/GITHUB.md\`
1333
1333
 
@@ -1338,7 +1338,7 @@ Use this when:
1338
1338
  - You need to include images in PR descriptions`;
1339
1339
  var REFERENCE3 = `# GitHub Integration
1340
1340
 
1341
- This guide covers how to interact with GitHub from within your Replicas workspace.
1341
+ This guide covers how to interact with GitHub from within your Replicas workspace. Prefer \`@replicas/sdk\` for API calls so batching and data processing stay in one TypeScript program. The \`gh\` CLI remains useful for ergonomic pull request and git-adjacent workflows.
1342
1342
 
1343
1343
  ## Prerequisites
1344
1344
 
@@ -1423,17 +1423,21 @@ gh run rerun RUN_ID --failed
1423
1423
 
1424
1424
  ### GitHub API (Advanced)
1425
1425
 
1426
- For operations not covered by \`gh\` subcommands, use the API directly:
1426
+ For operations not covered by \`gh\` subcommands, use the scoped SDK. Pass only a relative GitHub API path:
1427
1427
 
1428
- \`\`\`bash
1429
- # GET request
1430
- gh api repos/owner/repo/pulls/123/comments
1428
+ \`\`\`ts
1429
+ import { replicas } from '@replicas/sdk';
1431
1430
 
1432
- # POST request
1433
- gh api repos/owner/repo/issues/123/comments -f body="Comment text"
1431
+ const comments = await replicas.github.request('/repos/owner/repo/pulls/123/comments');
1432
+ await replicas.github.request('/repos/owner/repo/issues/123/comments', {
1433
+ method: 'POST',
1434
+ body: { body: 'Comment text' },
1435
+ });
1434
1436
 
1435
- # GraphQL query
1436
- gh api graphql -f query='{ repository(owner: "owner", name: "repo") { issues(first: 10) { nodes { title number } } } }'
1437
+ const result = await replicas.github.request('/graphql', {
1438
+ method: 'POST',
1439
+ body: { query: '{ viewer { login } }' },
1440
+ });
1437
1441
  \`\`\`
1438
1442
 
1439
1443
  ### Working with PR Reviews
@@ -1463,7 +1467,7 @@ GitHub does NOT have a public API for uploading images to PRs/issues. When you n
1463
1467
  `;
1464
1468
  var GITHUB_ABILITY = {
1465
1469
  label: "GitHub",
1466
- description: "Pre-authenticated `gh` CLI for PRs, issues, releases, and GraphQL.",
1470
+ description: "GitHub workflows through gh and API calls through @replicas/sdk.",
1467
1471
  bullet: "- Interacting with GitHub (creating PRs, managing issues, using the API, etc.)",
1468
1472
  section: SECTION3,
1469
1473
  referenceFile: { name: "GITHUB.md", content: REFERENCE3 }
@@ -1471,124 +1475,61 @@ var GITHUB_ABILITY = {
1471
1475
 
1472
1476
  // ../shared/src/default-skills/replicas-agent/abilities/gitlab.ts
1473
1477
  var SECTION4 = `### GitLab
1474
- Workspace repos hosted on GitLab use pre-configured git credentials \u2014 push, pull, and open merge requests with plain \`git\`.
1478
+ Use plain git for repository traffic and the workspace-scoped TypeScript SDK for GitLab API operations.
1475
1479
 
1476
1480
  **Reference:** \`references/GITLAB.md\`
1477
1481
 
1478
1482
  Use this when:
1479
- - A repo's remote is on gitlab.com or a self-managed GitLab instance
1480
- - You need to open or update a merge request`;
1483
+ - A repository remote is on gitlab.com or a self-managed GitLab instance
1484
+ - You need to open or update a merge request
1485
+ - You need GitLab issues, pipelines, comments, or other API data`;
1481
1486
  var REFERENCE4 = `# GitLab Integration
1482
1487
 
1483
- This guide covers how to work with GitLab-hosted repositories from within your Replicas workspace.
1484
-
1485
- ## Prerequisites
1486
-
1487
- Git credentials for the workspace's GitLab hosts are pre-configured in \`~/.git-credentials\` and refreshed automatically. Plain \`git fetch\` / \`git pull\` / \`git push\` over HTTPS work with no additional setup.
1488
-
1489
- - Never ask the user for a GitLab token or PAT \u2014 credentials are already wired. If a push fails with an authentication error, report it to the user instead of working around it.
1490
- - A workspace can hold credentials for several GitLab hosts, and \`origin\` is whatever the repo says it is. Always select the token by the host you are about to call, as the snippets below do. Grabbing the first entry in \`~/.git-credentials\` can send one host's token to another.
1491
- - There is no \`glab\` CLI in the workspace, and \`gh\` only works for GitHub remotes. Check a repo's host with \`git remote get-url origin\` before choosing the GitHub or GitLab workflow.
1492
-
1493
- ### Reading the credential for a repo
1488
+ Git credentials are preconfigured for fetch, pull, and push. Use \`@replicas/sdk\` for API traffic; never inspect \`~/.git-credentials\`, request a token, or send a credential with curl.
1494
1489
 
1495
- A workspace can hold tokens for several GitLab hosts, so always ask git for the
1496
- credential belonging to the host you are about to call. Never grep
1497
- \`~/.git-credentials\` yourself: taking the first line sends one instance's token to
1498
- another, and interpolating a host into a grep pattern makes it a regular
1499
- expression. \`git credential fill\` matches the host exactly and returns nothing
1500
- when there is no credential for it:
1490
+ ## Merge requests
1501
1491
 
1502
- \`\`\`bash
1503
- HOST=$(git remote get-url origin | sed -E 's#^[a-z]+://##; s#^[^@/]*@##; s#/.*##; s#:[^0-9].*##')
1504
- TOKEN=$(printf 'protocol=https\\nhost=%s\\n\\n' "$HOST" | GIT_TERMINAL_PROMPT=0 git credential fill | sed -n 's/^password=//p')
1505
- [ -n "$TOKEN" ] || { echo "no credential for $HOST"; exit 1; }
1506
- \`\`\`
1507
-
1508
- Every recipe below repeats these two lines so each block runs on its own, and
1509
- every one calls \`https://$HOST\`, so the token only ever reaches the host it
1510
- belongs to. Run them inside the repo whose API you are calling. An empty
1511
- \`$TOKEN\` means the workspace has no credential for that host. Report that rather
1512
- than reaching for another one.
1513
-
1514
- ## Merge Requests
1515
-
1516
- Create a merge request directly from a push using push options:
1492
+ Create a merge request directly from a push:
1517
1493
 
1518
1494
  \`\`\`bash
1519
1495
  git push -o merge_request.create -o merge_request.target=<default-branch> -o merge_request.title="Title" origin HEAD
1520
1496
  \`\`\`
1521
1497
 
1522
- Useful options:
1498
+ Useful options are \`-o merge_request.draft\` and \`-o merge_request.remove_source_branch\`. GitLab prints the merge request URL.
1523
1499
 
1524
- \`\`\`bash
1525
- -o merge_request.draft # open as draft
1526
- -o merge_request.remove_source_branch # delete branch on merge
1527
- \`\`\`
1500
+ ## API requests
1528
1501
 
1529
- GitLab prints the MR URL in the push output \u2014 include it in your reply to the user.
1502
+ Pass a relative path below \`/api/v4\`. Replicas selects the credential only from GitLab hosts attached to the workspace repositories.
1530
1503
 
1531
- ### Setting the description
1504
+ \`\`\`ts
1505
+ import { replicas } from '@replicas/sdk';
1532
1506
 
1533
- Do **not** pass the description as a push option. Push option values are shell
1534
- arguments on one line, so a real description \u2014 headings, lists, the Replicas
1535
- footer HTML \u2014 gets mangled or truncated. Write the body to a file and PUT it as
1536
- JSON instead, which preserves it exactly:
1507
+ const project = encodeURIComponent('group/project');
1508
+ const mergeRequests = await replicas.gitlab.request(
1509
+ \`/projects/\${project}/merge_requests?state=opened\`,
1510
+ );
1537
1511
 
1538
- \`\`\`bash
1539
- HOST=$(git remote get-url origin | sed -E 's#^[a-z]+://##; s#^[^@/]*@##; s#/.*##; s#:[^0-9].*##')
1540
- TOKEN=$(printf 'protocol=https\\nhost=%s\\n\\n' "$HOST" | GIT_TERMINAL_PROMPT=0 git credential fill | sed -n 's/^password=//p')
1541
- PROJECT=$(python3 -c 'import urllib.parse; print(urllib.parse.quote("group/project", safe=""))')
1542
- cat > /tmp/mr-body.md <<'EOF'
1543
- ## Summary
1544
- ...your full description, ending with the Replicas footer...
1545
- EOF
1546
- jq -Rs '{description: .}' /tmp/mr-body.md > /tmp/mr-body.json
1547
- curl -sS --request PUT \\
1548
- --header "Authorization: Bearer $TOKEN" \\
1549
- --header "Content-Type: application/json" \\
1550
- --data @/tmp/mr-body.json \\
1551
- "https://$HOST/api/v4/projects/$PROJECT/merge_requests/<iid>"
1512
+ await replicas.gitlab.request(\`/projects/\${project}/issues/42/notes\`, {
1513
+ method: 'POST',
1514
+ body: { body: 'Update from Replicas' },
1515
+ });
1552
1516
  \`\`\`
1553
1517
 
1554
- Use the same command to edit a description later. Editing replaces the whole
1555
- body, so re-include the Replicas footer every time \u2014 it is how merge requests
1556
- opened through Replicas are counted.
1557
-
1558
- ## GitLab API (advanced)
1559
-
1560
- For operations with no git equivalent (commenting on MRs, reading pipelines), call the REST API with the workspace credential:
1518
+ When a workspace contains repositories from more than one GitLab host, pass the exact repository host:
1561
1519
 
1562
- \`\`\`bash
1563
- HOST=$(git remote get-url origin | sed -E 's#^[a-z]+://##; s#^[^@/]*@##; s#/.*##; s#:[^0-9].*##')
1564
- TOKEN=$(printf 'protocol=https\\nhost=%s\\n\\n' "$HOST" | GIT_TERMINAL_PROMPT=0 git credential fill | sed -n 's/^password=//p')
1565
- curl -s -H "Authorization: Bearer $TOKEN" "https://$HOST/api/v4/projects/<url-encoded-path>/merge_requests"
1520
+ \`\`\`ts
1521
+ await replicas.gitlab.request('/projects/group%2Frepo/pipelines', {
1522
+ host: 'gitlab.example.com',
1523
+ });
1566
1524
  \`\`\`
1567
1525
 
1568
- This works for gitlab.com and self-managed instances alike, because the base URL
1569
- and the token both come from the repo's own host.
1570
-
1571
- ## Inline media in merge requests
1572
-
1573
- GitLab provides a supported project Markdown uploads API. Upload media to Replicas first, then upload the same bytes natively so repository permissions protect the asset:
1526
+ ## Descriptions and media
1574
1527
 
1575
- \`\`\`bash
1576
- FILE=/abs/path/to/screenshot.png
1577
- HOST=$(git remote get-url origin | sed -E 's#^[a-z]+://##; s#^[^@/]*@##; s#/.*##; s#:[^0-9].*##')
1578
- PROJECT=$(python3 -c 'import urllib.parse; print(urllib.parse.quote("group/project", safe=""))')
1579
- TOKEN=$(printf 'protocol=https\\nhost=%s\\n\\n' "$HOST" | GIT_TERMINAL_PROMPT=0 git credential fill | sed -n 's/^password=//p')
1580
- UPLOAD=$(curl -sS --request POST \\
1581
- --header "Authorization: Bearer $TOKEN" \\
1582
- --form "file=@$FILE" \\
1583
- "https://$HOST/api/v4/projects/$PROJECT/uploads")
1584
- echo "$UPLOAD" | jq -r .markdown
1585
- \`\`\`
1586
-
1587
- Insert the returned \`markdown\` into the merge request description or comment and add the media's **View in Replicas** dashboard link. Images render inline; MP4, MOV, and WebM render as inline video players. Do not create a public Replicas forge share when this native upload succeeds.
1528
+ API updates replace the entire merge request description, so always preserve the required Replicas footer and co-author trailers. For media, first run \`replicas media upload\` as documented in \`MEDIA.md\`; use GitLab's project uploads endpoint only when a native repository-protected embed is required.
1588
1529
  `;
1589
1530
  var GITLAB_ABILITY = {
1590
1531
  label: "GitLab",
1591
- description: "Pre-configured git credentials for GitLab repos; merge requests via push options.",
1532
+ description: "Git for repository traffic and @replicas/sdk for the GitLab API.",
1592
1533
  bullet: "- Interacting with GitLab (pushing to GitLab repos, opening merge requests)",
1593
1534
  section: SECTION4,
1594
1535
  referenceFile: { name: "GITLAB.md", content: REFERENCE4 }
@@ -1596,332 +1537,67 @@ var GITLAB_ABILITY = {
1596
1537
 
1597
1538
  // ../shared/src/default-skills/replicas-agent/abilities/google.ts
1598
1539
  var SECTION5 = `### Google Workspace and Search Console
1599
- Create and edit Google Docs, Sheets, and Forms, and read Google Search Console data via the Replicas gateway. Drive access is limited to files created by Replicas.
1540
+ Create and edit Docs, Sheets, and Forms, manage Replicas-created Drive files, and read Search Console through the workspace-scoped TypeScript SDK.
1600
1541
 
1601
1542
  **Reference:** \`references/GOOGLE.md\`
1602
1543
 
1603
1544
  Use this when:
1604
1545
  - You need to create or edit a Google Doc, Sheet, or Form
1605
- - You need to share, rename, move, or delete a Replicas-created Google file
1606
- - You need to read responses from a Replicas-created Google Form
1607
- - You need to list Search Console properties, query search performance, read sitemaps, or inspect an indexed URL`;
1546
+ - You need to manage a file Replicas created
1547
+ - You need Google Search Console data`;
1608
1548
  var REFERENCE5 = `# Google Workspace and Search Console
1609
1549
 
1610
- This guide covers how to create and edit Google Docs, Sheets, and Forms, perform basic Drive file operations, and read Google Search Console data from inside a Replicas workspace using the monolith as a gateway to Google's APIs.
1611
-
1612
- ## Prerequisites
1613
-
1614
- The integration is configured at the org or user level by the Replicas admin. From inside a workspace you don't have a Google access token directly; instead you call the monolith's \`/v1/gdrive/*\` endpoints, authenticated with your workspace's engine secret. The monolith refreshes the org's (or user's) Google access token and proxies the call.
1615
-
1616
- Quick check that the integration is connected:
1617
-
1618
- \`\`\`bash
1619
- curl -s -X GET "$REPLICAS_MONOLITH_URL/v1/gdrive/credentials" \\
1620
- -H "Authorization: Bearer $REPLICAS_ENGINE_SECRET" \\
1621
- -H "X-Workspace-Id: $REPLICAS_WORKSPACE_ID"
1622
- \`\`\`
1623
-
1624
- - If \`hasCredentials\` is \`true\`: you're good to go.
1625
- - If \`hasCredentials\` is \`false\`: Google has not been connected for this org. Ask the user to go to **Settings \u2192 Integrations \u2192 Google** in the [Replicas dashboard](https://replicas.dev) and connect a Google account. Do not attempt Google operations until it's connected.
1626
-
1627
- Standard auth headers used by every call below:
1628
-
1629
- \`\`\`
1630
- Authorization: Bearer $REPLICAS_ENGINE_SECRET
1631
- X-Workspace-Id: $REPLICAS_WORKSPACE_ID
1632
- \`\`\`
1633
-
1634
- For brevity the examples below use a shell variable:
1635
-
1636
- \`\`\`bash
1637
- GDRIVE_AUTH=(-H "Authorization: Bearer $REPLICAS_ENGINE_SECRET" -H "X-Workspace-Id: $REPLICAS_WORKSPACE_ID")
1638
- \`\`\`
1639
-
1640
- ## Important constraint: drive.file scope
1641
-
1642
- The integration uses the **sensitive-tier \`drive.file\` scope**. That means Replicas can only read and edit Google files **it created itself**. It **cannot**:
1643
-
1644
- - Read or edit a user's pre-existing Google Docs, Sheets, or Forms \u2014 even ones that were shared with the connected Google account.
1645
- - List or search the user's broader Drive.
1646
- - Touch any file that was not created via these gateway endpoints.
1647
-
1648
- If the user asks you to edit an existing doc that Replicas didn't create, tell them this constraint and offer to create a new doc that mirrors what they want.
1649
-
1650
- ## Google Docs
1651
-
1652
- ### Create a new doc
1653
-
1654
- \`\`\`bash
1655
- curl -s -X POST "$REPLICAS_MONOLITH_URL/v1/gdrive/docs" "\${GDRIVE_AUTH[@]}" \\
1656
- -H "Content-Type: application/json" \\
1657
- -d '{"title":"Meeting notes 2026-05-14"}'
1658
- \`\`\`
1659
-
1660
- Returns the full Doc object; grab \`.documentId\` for follow-up calls.
1661
-
1662
- ### Read a doc
1663
-
1664
- \`\`\`bash
1665
- curl -s "$REPLICAS_MONOLITH_URL/v1/gdrive/docs/$DOC_ID" "\${GDRIVE_AUTH[@]}"
1666
- \`\`\`
1667
-
1668
- Returns the full document structure \u2014 \`body.content\` is an ordered list of structural elements (paragraphs, tables, etc.) with character indexes you can target for edits.
1669
-
1670
- ### Edit a doc (batchUpdate)
1671
-
1672
- The Docs API edits use a list of [structural requests](https://developers.google.com/workspace/docs/api/reference/rest/v1/documents/request). Insert text, then style it; or insert tables, images, page breaks, etc.
1673
-
1674
- \`\`\`bash
1675
- curl -s -X POST "$REPLICAS_MONOLITH_URL/v1/gdrive/docs/$DOC_ID/batchUpdate" "\${GDRIVE_AUTH[@]}" \\
1676
- -H "Content-Type: application/json" \\
1677
- -d '{
1678
- "requests": [
1679
- { "insertText": { "location": { "index": 1 }, "text": "Hello, world!\\n" } }
1680
- ]
1681
- }'
1682
- \`\`\`
1683
-
1684
- Common request types:
1685
-
1686
- - \`insertText\` \u2014 insert plain text at a given index
1687
- - \`deleteContentRange\` \u2014 delete a range
1688
- - \`replaceAllText\` \u2014 find-and-replace
1689
- - \`updateTextStyle\` \u2014 bold/italic/colors/fonts/sizes for a range
1690
- - \`updateParagraphStyle\` \u2014 headings (\`HEADING_1\`..\`HEADING_6\`), alignment, spacing
1691
- - \`createParagraphBullets\` \u2014 turn paragraphs into bulleted/numbered lists
1692
- - \`insertTable\` \u2014 insert a table
1693
- - \`insertInlineImage\` \u2014 insert an image from a URL
1694
-
1695
- Edits are verbose but powerful. Prefer batching many requests into a single \`batchUpdate\` call rather than making many round trips \u2014 it's faster and keeps the doc state consistent.
1696
-
1697
- ## Google Sheets
1698
-
1699
- ### Create a new spreadsheet
1700
-
1701
- \`\`\`bash
1702
- curl -s -X POST "$REPLICAS_MONOLITH_URL/v1/gdrive/sheets" "\${GDRIVE_AUTH[@]}" \\
1703
- -H "Content-Type: application/json" \\
1704
- -d '{"title":"Q2 metrics"}'
1705
- \`\`\`
1706
-
1707
- Returns the full Spreadsheet object; grab \`.spreadsheetId\`.
1550
+ Use \`@replicas/sdk\`; the workspace never receives Google OAuth credentials. Personal credentials are preferred for the workspace owner, then Replicas falls back to the organization connection.
1708
1551
 
1709
- ### Read a range of cells
1710
-
1711
- \`\`\`bash
1712
- # Range is in A1 notation, e.g. "Sheet1!A1:C10"
1713
- curl -s "$REPLICAS_MONOLITH_URL/v1/gdrive/sheets/$SHEET_ID/values/$(printf %s 'Sheet1!A1:C10' | jq -sRr @uri)" \\
1714
- "\${GDRIVE_AUTH[@]}"
1715
- \`\`\`
1716
-
1717
- ### Write a range of cells
1718
-
1719
- \`\`\`bash
1720
- curl -s -X PUT \\
1721
- "$REPLICAS_MONOLITH_URL/v1/gdrive/sheets/$SHEET_ID/values/$(printf %s 'Sheet1!A1:B2' | jq -sRr @uri)?valueInputOption=USER_ENTERED" \\
1722
- "\${GDRIVE_AUTH[@]}" \\
1723
- -H "Content-Type: application/json" \\
1724
- -d '{
1725
- "range": "Sheet1!A1:B2",
1726
- "majorDimension": "ROWS",
1727
- "values": [
1728
- ["Name", "Revenue"],
1729
- ["Q1", "=SUM(B3:B100)"]
1730
- ]
1731
- }'
1732
- \`\`\`
1733
-
1734
- \`valueInputOption=USER_ENTERED\` makes formulas evaluate as if a human typed them. Use \`RAW\` to write literal strings.
1735
-
1736
- ### Bulk operations (formatting, charts, new tabs, etc.)
1737
-
1738
- \`\`\`bash
1739
- curl -s -X POST "$REPLICAS_MONOLITH_URL/v1/gdrive/sheets/$SHEET_ID/batchUpdate" "\${GDRIVE_AUTH[@]}" \\
1740
- -H "Content-Type: application/json" \\
1741
- -d '{ "requests": [ { "addSheet": { "properties": { "title": "Raw data" } } } ] }'
1742
- \`\`\`
1743
-
1744
- \`batchUpdate\` accepts an array of [Sheets API requests](https://developers.google.com/workspace/sheets/api/reference/rest/v4/spreadsheets/request) \u2014 addSheet, updateSheetProperties, repeatCell, addChart, autoResizeDimensions, etc.
1745
-
1746
- ## Google Forms
1747
-
1748
- ### Create a new form
1749
-
1750
- \`\`\`bash
1751
- curl -s -X POST "$REPLICAS_MONOLITH_URL/v1/gdrive/forms" "\${GDRIVE_AUTH[@]}" \\
1752
- -H "Content-Type: application/json" \\
1753
- -d '{"title":"Customer feedback"}'
1754
- \`\`\`
1755
-
1756
- Returns the form. Grab \`.formId\`.
1757
-
1758
- ### Add questions / edit form structure
1759
-
1760
- The Forms API uses its own \`batchUpdate\`:
1761
-
1762
- \`\`\`bash
1763
- curl -s -X POST "$REPLICAS_MONOLITH_URL/v1/gdrive/forms/$FORM_ID/batchUpdate" "\${GDRIVE_AUTH[@]}" \\
1764
- -H "Content-Type: application/json" \\
1765
- -d '{
1766
- "requests": [
1767
- {
1768
- "createItem": {
1769
- "item": {
1770
- "title": "How likely are you to recommend us?",
1771
- "questionItem": {
1772
- "question": {
1773
- "required": true,
1774
- "scaleQuestion": { "low": 0, "high": 10, "lowLabel": "Not at all", "highLabel": "Very likely" }
1775
- }
1776
- }
1777
- },
1778
- "location": { "index": 0 }
1779
- }
1780
- }
1781
- ]
1782
- }'
1783
- \`\`\`
1784
-
1785
- See the [Forms API Request reference](https://developers.google.com/workspace/forms/api/reference/rest/v1/forms/request) for all supported request types \u2014 text questions, multiple choice, checkboxes, scale, grid, date, time, file upload, section breaks, branching logic, quiz settings, etc.
1786
-
1787
- ### Read responses
1788
-
1789
- \`\`\`bash
1790
- curl -s "$REPLICAS_MONOLITH_URL/v1/gdrive/forms/$FORM_ID/responses" "\${GDRIVE_AUTH[@]}"
1791
- \`\`\`
1792
-
1793
- Pagination: pass \`?pageToken=...&pageSize=...\` to walk through responses.
1794
-
1795
- ## Drive operations (only on Replicas-created files)
1796
-
1797
- ### Share a file with a person
1798
-
1799
- \`\`\`bash
1800
- curl -s -X POST "$REPLICAS_MONOLITH_URL/v1/gdrive/files/$FILE_ID/permissions" "\${GDRIVE_AUTH[@]}" \\
1801
- -H "Content-Type: application/json" \\
1802
- -d '{
1803
- "type": "user",
1804
- "role": "writer",
1805
- "emailAddress": "alice@example.com",
1806
- "sendNotificationEmail": true
1807
- }'
1808
- \`\`\`
1809
-
1810
- Roles: \`reader\`, \`commenter\`, \`writer\`. Types: \`user\`, \`group\`, \`domain\`, \`anyone\`.
1811
-
1812
- ### Make a file viewable by anyone with the link
1813
-
1814
- \`\`\`bash
1815
- curl -s -X POST "$REPLICAS_MONOLITH_URL/v1/gdrive/files/$FILE_ID/permissions" "\${GDRIVE_AUTH[@]}" \\
1816
- -H "Content-Type: application/json" \\
1817
- -d '{ "type": "anyone", "role": "reader" }'
1818
- \`\`\`
1819
-
1820
- ### List existing permissions
1821
-
1822
- \`\`\`bash
1823
- curl -s "$REPLICAS_MONOLITH_URL/v1/gdrive/files/$FILE_ID/permissions" "\${GDRIVE_AUTH[@]}"
1824
- \`\`\`
1825
-
1826
- ### Rename / move a file
1827
-
1828
- \`\`\`bash
1829
- curl -s -X PATCH "$REPLICAS_MONOLITH_URL/v1/gdrive/files/$FILE_ID" "\${GDRIVE_AUTH[@]}" \\
1830
- -H "Content-Type: application/json" \\
1831
- -d '{"name":"Final report.docx"}'
1832
- \`\`\`
1833
-
1834
- To move into a folder, also pass \`addParents\` and \`removeParents\` as query parameters per [Drive API docs](https://developers.google.com/workspace/drive/api/reference/rest/v3/files/update).
1835
-
1836
- ### Get file metadata
1837
-
1838
- \`\`\`bash
1839
- curl -s "$REPLICAS_MONOLITH_URL/v1/gdrive/files/$FILE_ID?fields=id,name,mimeType,webViewLink,parents,modifiedTime" \\
1840
- "\${GDRIVE_AUTH[@]}"
1841
- \`\`\`
1842
-
1843
- \`webViewLink\` is the human-shareable URL that opens in Google Docs/Sheets/Forms.
1844
-
1845
- ### Delete a file
1846
-
1847
- \`\`\`bash
1848
- curl -s -X DELETE "$REPLICAS_MONOLITH_URL/v1/gdrive/files/$FILE_ID" "\${GDRIVE_AUTH[@]}"
1849
- \`\`\`
1850
-
1851
- ### List Replicas-created files
1552
+ \`\`\`ts
1553
+ import { replicas } from '@replicas/sdk';
1852
1554
 
1853
- \`\`\`bash
1854
- curl -s "$REPLICAS_MONOLITH_URL/v1/gdrive/files?pageSize=50" "\${GDRIVE_AUTH[@]}"
1555
+ const { native } = await replicas.integrations.list();
1556
+ if (!native.google) throw new Error('Connect Google in Replicas integrations first.');
1855
1557
  \`\`\`
1856
1558
 
1857
- Pass \`?q=...\` to filter using [Drive API search syntax](https://developers.google.com/workspace/drive/api/guides/search-files). Only files the app created or was granted access to will appear.
1559
+ Google Drive uses \`drive.file\`: Replicas can access only files it created, not pre-existing files in the account.
1858
1560
 
1859
- ## Google Search Console (read-only)
1561
+ ## Docs
1860
1562
 
1861
- Search Console access uses the \`webmasters.readonly\` scope. The connected Google account must have access to the requested property. If the account was connected before this scope was added, reconnect it in **Settings \u2192 Integrations \u2192 Google** first.
1862
-
1863
- Property identifiers must be URL-encoded as query parameters. URL-prefix properties include the trailing slash (for example, \`https://example.com/\`); Domain properties use \`sc-domain:example.com\`.
1864
-
1865
- ### List accessible properties
1866
-
1867
- \`\`\`bash
1868
- curl -s "$REPLICAS_MONOLITH_URL/v1/gdrive/search-console/sites" "\${GDRIVE_AUTH[@]}"
1869
- \`\`\`
1870
-
1871
- ### Get a property
1872
-
1873
- \`\`\`bash
1874
- SITE_URL=$(printf %s 'sc-domain:example.com' | jq -sRr @uri)
1875
- curl -s "$REPLICAS_MONOLITH_URL/v1/gdrive/search-console/site?siteUrl=$SITE_URL" "\${GDRIVE_AUTH[@]}"
1876
- \`\`\`
1877
-
1878
- ### Query search performance
1879
-
1880
- \`\`\`bash
1881
- curl -s -X POST "$REPLICAS_MONOLITH_URL/v1/gdrive/search-console/search-analytics/query?siteUrl=$SITE_URL" "\${GDRIVE_AUTH[@]}" \\
1882
- -H "Content-Type: application/json" \\
1883
- -d '{
1884
- "startDate": "2026-06-01",
1885
- "endDate": "2026-06-30",
1886
- "dimensions": ["query", "page"],
1887
- "rowLimit": 1000
1888
- }'
1563
+ \`\`\`ts
1564
+ const doc = await replicas.google.request<{ documentId: string }>('docs', {
1565
+ method: 'POST',
1566
+ body: { title: 'Meeting notes' },
1567
+ });
1568
+ await replicas.google.request(\`docs/\${doc.documentId}/batchUpdate\`, {
1569
+ method: 'POST',
1570
+ body: { requests: [{ insertText: { location: { index: 1 }, text: 'Hello\\n' } }] },
1571
+ });
1889
1572
  \`\`\`
1890
1573
 
1891
- Results contain clicks, impressions, CTR, and average position. Use \`startRow\` with \`rowLimit\` to paginate; Google returns at most 25,000 rows per request.
1892
-
1893
- ### List sitemaps
1574
+ ## Sheets
1894
1575
 
1895
- \`\`\`bash
1896
- curl -s "$REPLICAS_MONOLITH_URL/v1/gdrive/search-console/sitemaps?siteUrl=$SITE_URL" "\${GDRIVE_AUTH[@]}"
1576
+ \`\`\`ts
1577
+ const sheet = await replicas.google.request<{ spreadsheetId: string }>('sheets', {
1578
+ method: 'POST',
1579
+ body: { title: 'Q2 metrics' },
1580
+ });
1581
+ await replicas.google.request(
1582
+ \`sheets/\${sheet.spreadsheetId}/values/\${encodeURIComponent('Sheet1!A1:B2')}?valueInputOption=USER_ENTERED\`,
1583
+ { method: 'PUT', body: { values: [['Name', 'Revenue'], ['Q1', 100]] } },
1584
+ );
1897
1585
  \`\`\`
1898
1586
 
1899
- Pass a URL-encoded \`sitemapIndex\` query parameter to list sitemaps referenced by a sitemap index. To fetch one sitemap, call \`/v1/gdrive/search-console/sitemap\` with URL-encoded \`siteUrl\` and \`feedpath\` query parameters.
1900
-
1901
- ### Inspect an indexed URL
1902
-
1903
- \`\`\`bash
1904
- curl -s -X POST "$REPLICAS_MONOLITH_URL/v1/gdrive/search-console/url-inspection" "\${GDRIVE_AUTH[@]}" \\
1905
- -H "Content-Type: application/json" \\
1906
- -d '{
1907
- "inspectionUrl": "https://example.com/page",
1908
- "siteUrl": "sc-domain:example.com",
1909
- "languageCode": "en-US"
1910
- }'
1911
- \`\`\`
1587
+ ## Forms, Drive, and Search Console
1912
1588
 
1913
- URL Inspection reports the version currently in Google's index; it does not run a live URL test.
1589
+ The same method maps directly to the guarded \`/v1/gdrive/*\` routes:
1914
1590
 
1915
- ## Tips
1591
+ - \`forms\`, \`forms/:id\`, \`forms/:id/batchUpdate\`, \`forms/:id/responses\`
1592
+ - \`files\`, \`files/:id\`, \`files/:id/permissions\`
1593
+ - \`search-console/sites\`, \`search-console/search-analytics/query\`, \`search-console/sitemaps\`, \`search-console/url-inspection\`
1916
1594
 
1917
- - **Always return the \`webViewLink\`** to the user when you create or edit a file so they can open it. Get it from \`/v1/gdrive/files/$FILE_ID?fields=webViewLink\` or from \`documents.documentId\` \u2192 \`https://docs.google.com/document/d/$ID/edit\`.
1918
- - **Batch your edits.** A single \`batchUpdate\` with 20 requests beats 20 round-trips.
1919
- - **Watch error responses.** A 412 from the gateway means the org has no Google credentials connected \u2014 tell the user to connect Google in the dashboard. Other 4xx responses come straight from Google and usually explain the issue clearly.
1595
+ Batch independent reads with \`Promise.all\`; use each API's batch-update endpoint for dependent writes.
1920
1596
  `;
1921
1597
  var GOOGLE_ABILITY = {
1922
- label: "Google",
1923
- description: "Use Google Workspace and read Search Console data via the Replicas gateway.",
1924
- bullet: "- Interacting with Google Workspace or Search Console (Docs, Sheets, Forms, Drive files, search performance, sitemaps, and URL inspection)",
1598
+ label: "Google Workspace",
1599
+ description: "Use Docs, Sheets, Forms, Drive, and Search Console through @replicas/sdk.",
1600
+ bullet: "- Interacting with Google Workspace or Search Console",
1925
1601
  section: SECTION5,
1926
1602
  referenceFile: { name: "GOOGLE.md", content: REFERENCE5 }
1927
1603
  };
@@ -1955,102 +1631,63 @@ var LEARNINGS_ABILITY = {
1955
1631
 
1956
1632
  // ../shared/src/default-skills/replicas-agent/abilities/linear.ts
1957
1633
  var SECTION7 = `### Linear
1958
- Fetch issues, update state, add comments, and search via the Linear GraphQL API.
1634
+ Fetch issues, update state, add comments, and search through the workspace-scoped TypeScript SDK.
1959
1635
 
1960
1636
  **Reference:** \`references/LINEAR.md\`
1961
1637
 
1962
1638
  Use this when:
1963
1639
  - You encounter a Linear issue link and need to understand the task
1964
- - You need to update an issue's state (e.g. mark as done)
1965
- - You need to comment on or search for Linear issues`;
1640
+ - You need to update an issue's state or add a comment
1641
+ - You need to search Linear issues`;
1966
1642
  var REFERENCE7 = `# Linear Integration
1967
1643
 
1968
- This guide covers how to interact with Linear from within your Replicas workspace.
1969
-
1970
- ## Prerequisites
1971
-
1972
- Check that the workspace has the Replicas engine credentials needed to proxy Linear requests:
1973
-
1974
- \`\`\`bash
1975
- test -n "$REPLICAS_MONOLITH_URL" && test -n "$REPLICAS_ENGINE_SECRET" && test -n "$REPLICAS_WORKSPACE_ID" && echo set
1976
- \`\`\`
1977
-
1978
- - If **set**: Use the Replicas Linear proxy below. It refreshes Linear OAuth tokens for you.
1979
- - If **not set**: The workspace cannot authenticate to Replicas. Let the user know and do not attempt Linear operations.
1980
-
1981
- Then verify Linear is connected:
1982
-
1983
- \`\`\`bash
1984
- curl -fsS -X POST "$REPLICAS_MONOLITH_URL/v1/engine/linear/refresh-token" \\
1985
- -H "Authorization: Bearer $REPLICAS_ENGINE_SECRET" \\
1986
- -H "X-Workspace-Id: $REPLICAS_WORKSPACE_ID" \\
1987
- -H "Content-Type: application/json" \\
1988
- -d '{}' >/dev/null
1989
- \`\`\`
1990
-
1991
- If this fails because no Linear credential is connected, the user needs to connect Linear in the [Replicas dashboard](https://replicas.dev) under their organization's integration settings.
1992
-
1993
- ## Using the Linear API
1994
-
1995
- Linear uses a GraphQL API. Always call it through Replicas so the access token stays fresh:
1996
-
1997
- \`\`\`bash
1998
- linear_graphql() {
1999
- curl -sS -X POST "$REPLICAS_MONOLITH_URL/v1/engine/linear/graphql" \\
2000
- -H "Authorization: Bearer $REPLICAS_ENGINE_SECRET" \\
2001
- -H "X-Workspace-Id: $REPLICAS_WORKSPACE_ID" \\
2002
- -H "Content-Type: application/json" \\
2003
- --data-binary @-
2004
- }
2005
- \`\`\`
1644
+ Use \`@replicas/sdk\`; never request or handle a Linear OAuth token. Replicas refreshes the organization credential behind the gateway.
2006
1645
 
2007
- ### Fetching an Issue
1646
+ ## Check access
2008
1647
 
2009
- If you encounter a Linear issue link (e.g. \`https://linear.app/team/issue/ENG-123\`), the identifier is the last path segment (\`ENG-123\`).
2010
-
2011
- \`\`\`bash
2012
- jq -n --arg identifier "ENG-123" '{
2013
- query: "query($identifier: String!) { issue(id: $identifier) { id identifier title description state { name } assignee { name } parent { identifier title description } } }",
2014
- variables: { identifier: $identifier }
2015
- }' | linear_graphql
2016
- \`\`\`
2017
-
2018
- ### Updating Issue State
1648
+ \`\`\`ts
1649
+ import { replicas } from '@replicas/sdk';
2019
1650
 
2020
- \`\`\`bash
2021
- jq -n --arg issueId "ISSUE_UUID" --arg stateId "STATE_UUID" '{
2022
- query: "mutation($issueId: String!, $stateId: String!) { issueUpdate(id: $issueId, input: { stateId: $stateId }) { success issue { identifier state { name } } } }",
2023
- variables: { issueId: $issueId, stateId: $stateId }
2024
- }' | linear_graphql
1651
+ const { native } = await replicas.integrations.list();
1652
+ if (!native.linear) throw new Error('Connect Linear in Replicas organization integrations first.');
2025
1653
  \`\`\`
2026
1654
 
2027
- To find available states, query: \`query { workflowStates { nodes { id name } } }\`
2028
-
2029
- ### Adding a Comment
1655
+ ## Fetch an issue
2030
1656
 
2031
- \`\`\`bash
2032
- jq -n --arg issueId "ISSUE_UUID" --arg body "Your comment here" '{
2033
- query: "mutation($issueId: String!, $body: String!) { commentCreate(input: { issueId: $issueId, body: $body }) { success comment { id } } }",
2034
- variables: { issueId: $issueId, body: $body }
2035
- }' | linear_graphql
1657
+ \`\`\`ts
1658
+ const issue = await replicas.linear.graphql({
1659
+ query: \`query($identifier: String!) {
1660
+ issue(id: $identifier) {
1661
+ id identifier title description state { id name } assignee { name }
1662
+ parent { identifier title description }
1663
+ }
1664
+ }\`,
1665
+ variables: { identifier: 'ENG-123' },
1666
+ });
2036
1667
  \`\`\`
2037
1668
 
2038
- ### Searching Issues
1669
+ ## Update and comment in one script
2039
1670
 
2040
- \`\`\`bash
2041
- jq -n --arg query "search terms" '{
2042
- query: "query($query: String!) { issues(filter: { or: [{ title: { containsIgnoreCase: $query } }, { description: { containsIgnoreCase: $query } }] }, first: 10) { nodes { identifier title state { name } } } }",
2043
- variables: { query: $query }
2044
- }' | linear_graphql
1671
+ \`\`\`ts
1672
+ await replicas.linear.graphql({
1673
+ query: \`mutation($issueId: String!, $stateId: String!) {
1674
+ issueUpdate(id: $issueId, input: { stateId: $stateId }) { success }
1675
+ }\`,
1676
+ variables: { issueId, stateId },
1677
+ });
1678
+ await replicas.linear.graphql({
1679
+ query: \`mutation($issueId: String!, $body: String!) {
1680
+ commentCreate(input: { issueId: $issueId, body: $body }) { success comment { id } }
1681
+ }\`,
1682
+ variables: { issueId, body: 'Implemented in PR #123.' },
1683
+ });
2045
1684
  \`\`\`
2046
1685
 
2047
- ### Other Operations
2048
-
2049
- The Linear GraphQL API supports creating issues, managing projects, labels, cycles, and more. For the full schema and documentation, see: https://developers.linear.app/docs/graphql/working-with-the-graphql-api
1686
+ Search with \`containsIgnoreCase: $query\` filters, for example on both \`title\` and \`description\`. Batch independent queries with \`Promise.all\` to save round trips.
2050
1687
  `;
2051
1688
  var LINEAR_ABILITY = {
2052
1689
  label: "Linear",
2053
- description: "Fetch issues, post comments, update states via the Linear GraphQL API.",
1690
+ description: "Fetch issues, comment, and update state through @replicas/sdk.",
2054
1691
  bullet: "- Interacting with Linear (fetching issues, updating state, commenting, etc.)",
2055
1692
  section: SECTION7,
2056
1693
  referenceFile: { name: "LINEAR.md", content: REFERENCE7 }
@@ -2467,8 +2104,79 @@ var PREVIEWS_ABILITY = {
2467
2104
  referenceFile: { name: "PREVIEWS.md", content: REFERENCE9 }
2468
2105
  };
2469
2106
 
2107
+ // ../shared/src/default-skills/replicas-agent/abilities/plugins.ts
2108
+ var SECTION10 = `### Plugins
2109
+ Discover and execute installed Attio, Google Ads, and Stripe tools through the workspace-scoped TypeScript SDK.
2110
+
2111
+ **Reference:** \`references/PLUGINS.md\`
2112
+
2113
+ Use this when:
2114
+ - The user asks for CRM data from Attio
2115
+ - The user asks for Google Ads campaigns or reporting
2116
+ - The user asks for Stripe customers, payments, invoices, subscriptions, or balances`;
2117
+ var REFERENCE10 = `# Plugins
2118
+
2119
+ Plugins are TypeScript libraries, not MCP servers. Import \`@replicas/sdk\`; Replicas authenticates the workspace and selects only the owner\u2019s personal connection or the organization fallback. Composio credentials, sessions, and project keys are never exposed.
2120
+
2121
+ The initial plugin library is read-only. Tool discovery and execution both reject tools that are not tagged read-only.
2122
+
2123
+ ## Discover installed plugins
2124
+
2125
+ \`\`\`ts
2126
+ import { replicas } from '@replicas/sdk';
2127
+
2128
+ const plugins = await replicas.plugins.list();
2129
+ const installed = plugins.filter((plugin) => plugin.connected);
2130
+ \`\`\`
2131
+
2132
+ If a plugin is not connected, ask the user to install it under personal or organization Integrations in the Replicas dashboard.
2133
+
2134
+ ## Search, inspect, execute
2135
+
2136
+ \`\`\`ts
2137
+ const search = await replicas.plugins.search({
2138
+ plugin: 'stripe',
2139
+ query: 'list recent successful payments with customer details',
2140
+ });
2141
+
2142
+ const [tool] = search.tools;
2143
+ if (!tool) throw new Error('No matching read-only tool found');
2144
+
2145
+ const { data } = await replicas.plugins.execute({
2146
+ plugin: 'stripe',
2147
+ tool: tool.slug,
2148
+ arguments: { limit: 25 },
2149
+ });
2150
+ \`\`\`
2151
+
2152
+ Use \`replicas.plugins.describe({ plugin, tools: [slug] })\` when search returned a partial schema or before constructing unfamiliar arguments.
2153
+
2154
+ ## Batch work
2155
+
2156
+ Keep dependent operations in one TypeScript file and batch independent calls:
2157
+
2158
+ \`\`\`ts
2159
+ const results = await Promise.all(customerIds.map((customer) =>
2160
+ replicas.plugins.execute({
2161
+ plugin: 'stripe',
2162
+ tool: 'STRIPE_RETRIEVE_CUSTOMER',
2163
+ arguments: { customer },
2164
+ }),
2165
+ ));
2166
+ \`\`\`
2167
+
2168
+ Never attempt to import Composio, create a session, manage a connection, or call a provider with raw credentials. Those operations are intentionally unavailable inside the workspace.
2169
+ `;
2170
+ var PLUGINS_ABILITY = {
2171
+ label: "Plugins",
2172
+ description: "Use installed Attio, Google Ads, and Stripe tools through @replicas/sdk.",
2173
+ bullet: "- Interacting with installed plugins such as Attio, Google Ads, or Stripe",
2174
+ section: SECTION10,
2175
+ referenceFile: { name: "PLUGINS.md", content: REFERENCE10 }
2176
+ };
2177
+
2470
2178
  // ../shared/src/default-skills/replicas-agent/abilities/replicas.ts
2471
- var SECTION10 = `### Replicas (in-workspace CLI)
2179
+ var SECTION11 = `### Replicas (in-workspace CLI)
2472
2180
  Take action *with* Replicas itself \u2014 manage automations, environments (variables, files), repos, and \`replicas.json\` config \u2014 using the pre-installed, pre-authenticated \`replicas\` CLI.
2473
2181
 
2474
2182
  **Reference:** \`references/REPLICAS.md\`
@@ -2479,7 +2187,7 @@ Use this when:
2479
2187
  - The user asks "what envs / repos / automations do I have?"
2480
2188
  - The user asks you to scaffold a \`replicas.json\` / \`replicas.yaml\` in a repo
2481
2189
  - You need to run a long-lived service (dev server, daemon) \u2014 always use \`replicas service start\` so it survives workspace sleep/wake`;
2482
- var REFERENCE10 = `# Replicas (in-workspace CLI)
2190
+ var REFERENCE11 = `# Replicas (in-workspace CLI)
2483
2191
 
2484
2192
  This guide covers how to take action *with* Replicas itself from inside a Replicas workspace \u2014 managing automations, environments (and their variables/files), repos, previews, and the user's \`replicas.json\` config \u2014 using the pre-installed \`replicas\` CLI.
2485
2193
 
@@ -2706,149 +2414,107 @@ var REPLICAS_ABILITY = {
2706
2414
  description: "Teach the agent about Replicas itself \u2014 automations, environments, the in-workspace CLI.",
2707
2415
  // No bullet — help_instructions covers the `replicas` CLI surface in detail.
2708
2416
  bullet: "",
2709
- section: SECTION10,
2710
- referenceFile: { name: "REPLICAS.md", content: REFERENCE10 },
2417
+ section: SECTION11,
2418
+ referenceFile: { name: "REPLICAS.md", content: REFERENCE11 },
2711
2419
  locked: true
2712
2420
  };
2713
2421
 
2714
- // ../shared/src/default-skills/replicas-agent/abilities/slack.ts
2715
- var SECTION11 = `### Slack
2716
- Send messages, read threads, search conversations, and upload files via the Slack Web API.
2422
+ // ../shared/src/default-skills/replicas-agent/abilities/sentry.ts
2423
+ var SECTION12 = `### Sentry
2424
+ Read issues and project data through the workspace-scoped TypeScript SDK.
2717
2425
 
2718
- **Reference:** \`references/SLACK.md\`
2426
+ **Reference:** \`references/SENTRY.md\`
2719
2427
 
2720
2428
  Use this when:
2721
- - You need to send a message to a Slack channel or thread
2722
- - You need to read or fetch a Slack conversation
2723
- - You encounter a Slack message link and need to retrieve its content
2724
- - The task asks you to notify, update, or communicate via Slack`;
2725
- var REFERENCE11 = `# Slack Integration
2429
+ - You need to inspect Sentry projects or issues
2430
+ - A user asks for error-monitoring context outside a webhook-triggered task`;
2431
+ var REFERENCE12 = `# Sentry Integration
2726
2432
 
2727
- This guide covers how to interact with Slack from within your Replicas workspace.
2433
+ Use \`@replicas/sdk\`; never request or read a Sentry token. Sentry access is organization-scoped and read-only.
2728
2434
 
2729
- ## Prerequisites
2435
+ \`\`\`ts
2436
+ import { replicas } from '@replicas/sdk';
2730
2437
 
2731
- Check if the namespaced or legacy Slack token environment variable is set:
2438
+ const { native } = await replicas.integrations.list();
2439
+ if (!native.sentry) throw new Error('Connect Sentry in Replicas organization integrations first.');
2732
2440
 
2733
- \`\`\`bash
2734
- if [ -n "\${REPLICAS_SLACK_BOT_TOKEN:-$SLACK_BOT_TOKEN}" ]; then echo set; fi
2441
+ const issues = await replicas.sentry.request('/organizations/acme/issues/?query=is:unresolved');
2735
2442
  \`\`\`
2736
2443
 
2737
- - If **set**: Your workspace has Slack access. You can use the Slack Web API as described below.
2738
- - If **not set**: Slack has not been configured for this workspace. The user needs to connect Slack in the [Replicas dashboard](https://replicas.dev) under their organization's integration settings. Let the user know and do not attempt Slack operations.
2739
-
2740
- ## Using the Slack API
2741
-
2742
- All requests prefer \`$REPLICAS_SLACK_BOT_TOKEN\` and fall back to \`$SLACK_BOT_TOKEN\` for compatibility with older workspaces.
2743
-
2744
- ### Fetching a Thread from a Slack Link
2745
-
2746
- If you encounter a Slack message link (e.g. \`https://team.slack.com/archives/C0123ABC/p1234567890123456\`), extract the channel ID and thread timestamp:
2747
-
2748
- - **Channel ID**: The segment after \`/archives/\` (e.g. \`C0123ABC\`)
2749
- - **Thread TS**: The \`p\` value with a dot inserted before the last 6 digits (e.g. \`p1234567890123456\` -> \`1234567890.123456\`)
2444
+ Pass only a relative Sentry API path. The Replicas gateway selects the connected organization credential and rejects writes, absolute URLs, oversized responses, and long-running requests.
2445
+ `;
2446
+ var SENTRY_ABILITY = {
2447
+ label: "Sentry",
2448
+ description: "Read Sentry projects and issues through @replicas/sdk.",
2449
+ bullet: "- Interacting with Sentry (reading projects, issues, and error context)",
2450
+ section: SECTION12,
2451
+ referenceFile: { name: "SENTRY.md", content: REFERENCE12 }
2452
+ };
2750
2453
 
2751
- \`\`\`bash
2752
- curl -s "https://slack.com/api/conversations.replies?channel=CHANNEL_ID&ts=THREAD_TS" \\
2753
- -H "Authorization: Bearer \${REPLICAS_SLACK_BOT_TOKEN:-$SLACK_BOT_TOKEN}"
2754
- \`\`\`
2454
+ // ../shared/src/default-skills/replicas-agent/abilities/slack.ts
2455
+ var SECTION13 = `### Slack
2456
+ Send messages, read threads, search conversations, and upload files through the workspace-scoped TypeScript SDK.
2755
2457
 
2756
- ### Sending a Message
2458
+ **Reference:** \`references/SLACK.md\`
2757
2459
 
2758
- \`\`\`bash
2759
- curl -s -X POST "https://slack.com/api/chat.postMessage" \\
2760
- -H "Authorization: Bearer \${REPLICAS_SLACK_BOT_TOKEN:-$SLACK_BOT_TOKEN}" \\
2761
- -H "Content-Type: application/json" \\
2762
- -d '{
2763
- "channel": "CHANNEL_ID",
2764
- "text": "Your message here",
2765
- "thread_ts": "OPTIONAL_THREAD_TS"
2766
- }'
2767
- \`\`\`
2460
+ Use this when:
2461
+ - You need to send a message to a Slack channel or thread
2462
+ - You need to read or search Slack conversations
2463
+ - You encounter a Slack message link and need its full thread`;
2464
+ var REFERENCE13 = `# Slack Integration
2768
2465
 
2769
- Omit \`thread_ts\` to post a new message to the channel. Include it to reply in a thread.
2466
+ Use \`@replicas/sdk\`; never read or send a Slack token directly. The SDK calls Replicas, which selects only this workspace's organization connection.
2770
2467
 
2771
- #### Attach every new conversation
2468
+ ## Check access
2772
2469
 
2773
- Whenever you send a Slack message outside the thread that already routes to the current workspace, immediately attach the destination thread so future replies are delivered to this workspace:
2470
+ \`\`\`ts
2471
+ import { replicas } from '@replicas/sdk';
2774
2472
 
2775
- \`\`\`bash
2776
- replicas slack thread attach --channel <channel-id> --thread-ts <thread-root-ts>
2473
+ const { native } = await replicas.integrations.list();
2474
+ if (!native.slack) throw new Error('Connect Slack in Replicas organization integrations first.');
2777
2475
  \`\`\`
2778
2476
 
2779
- - For a new top-level message or DM, use the \`channel\` and \`ts\` from the successful \`chat.postMessage\` response. That message's \`ts\` is the new thread root.
2780
- - For a reply in another existing thread, use its root \`thread_ts\`, not the reply's \`ts\`.
2781
- - Do not attach the originating thread again when it already routes to the current workspace.
2782
- - Treat sending and attaching as one operation: check that both API calls succeed, and report an attachment failure instead of implying future replies will reach you.
2783
-
2784
- #### Switch a thread to an existing workspace
2477
+ ## Read a thread
2785
2478
 
2786
- When the user wants a Slack thread to continue in a particular existing workspace, resolve the target and switch the thread:
2479
+ For a link such as \`https://team.slack.com/archives/C0123ABC/p1234567890123456\`, the channel is \`C0123ABC\` and the timestamp is \`1234567890.123456\`.
2787
2480
 
2788
- \`\`\`bash
2789
- replicas list
2790
- replicas slack thread switch <workspace-id-or-name> --channel <channel-id> --thread-ts <thread-root-ts>
2481
+ \`\`\`ts
2482
+ const thread = await replicas.slack.call('conversations.replies', {
2483
+ channel: 'C0123ABC',
2484
+ ts: '1234567890.123456',
2485
+ limit: 200,
2486
+ });
2791
2487
  \`\`\`
2792
2488
 
2793
- The channel and thread flags default to \`REPLICAS_SLACK_CHANNEL_ID\` and \`REPLICAS_SLACK_THREAD_TS\` for a Slack-triggered workspace. Workspace-authenticated agents may target only their current workspace; switching to a different workspace requires running the command with user authentication outside agent mode. If that restriction applies, give the user the exact command to run rather than claiming the switch succeeded.
2794
-
2795
- ### Searching Messages
2489
+ ## Send and attach a message
2796
2490
 
2797
- \`\`\`bash
2798
- curl -s "https://slack.com/api/search.messages?query=YOUR_SEARCH_QUERY" \\
2799
- -H "Authorization: Bearer \${REPLICAS_SLACK_BOT_TOKEN:-$SLACK_BOT_TOKEN}"
2491
+ \`\`\`ts
2492
+ const message = await replicas.slack.call<{ channel: string; ts: string }>('chat.postMessage', {
2493
+ channel: 'C0123ABC',
2494
+ text: 'Update from Replicas',
2495
+ });
2496
+ await replicas.slack.attachThread({ channel: message.channel, threadTs: message.ts });
2800
2497
  \`\`\`
2801
2498
 
2802
- ### Uploading Files
2803
-
2804
- Slack deprecated \`files.upload\`; the raw HTTP replacement is a 3-step flow. (\`files.uploadV2\` is **only** an SDK convenience wrapper and is NOT a real Web API HTTP endpoint \u2014 \`POST https://slack.com/api/files.uploadV2\` returns \`unknown_method\`.)
2805
-
2806
- \`\`\`bash
2807
- FILE=/path/to/file
2808
- FILENAME=$(basename "$FILE")
2809
- LENGTH=$(stat -c%s "$FILE" 2>/dev/null || stat -f%z "$FILE")
2499
+ Attach every new top-level conversation so future replies route to this workspace. For a reply in another thread, attach its root \`thread_ts\`, not the reply timestamp. Do not attach the originating thread again.
2810
2500
 
2811
- # 1. Reserve an upload URL + file ID.
2812
- RESERVE=$(curl -s -G "https://slack.com/api/files.getUploadURLExternal" \\
2813
- -H "Authorization: Bearer \${REPLICAS_SLACK_BOT_TOKEN:-$SLACK_BOT_TOKEN}" \\
2814
- --data-urlencode "filename=$FILENAME" \\
2815
- --data-urlencode "length=$LENGTH")
2816
- UPLOAD_URL=$(echo "$RESERVE" | jq -r '.upload_url')
2817
- FILE_ID=$(echo "$RESERVE" | jq -r '.file_id')
2818
-
2819
- # 2. POST the raw bytes to the reserved URL.
2820
- # Do not use -F / multipart here \u2014 the presigned URL expects the bytes
2821
- # directly in the request body.
2822
- curl -s -X POST "$UPLOAD_URL" \\
2823
- -H "Content-Type: application/octet-stream" \\
2824
- --data-binary @"$FILE"
2501
+ ## Search and batch
2825
2502
 
2826
- # 3. Complete the upload and share into a channel (and optionally a thread).
2827
- curl -s -X POST "https://slack.com/api/files.completeUploadExternal" \\
2828
- -H "Authorization: Bearer \${REPLICAS_SLACK_BOT_TOKEN:-$SLACK_BOT_TOKEN}" \\
2829
- -H "Content-Type: application/json; charset=utf-8" \\
2830
- -d "{
2831
- \\"files\\": [{\\"id\\": \\"$FILE_ID\\", \\"title\\": \\"File title\\"}],
2832
- \\"channel_id\\": \\"CHANNEL_ID\\",
2833
- \\"thread_ts\\": \\"OPTIONAL_THREAD_TS\\",
2834
- \\"initial_comment\\": \\"Optional message body\\"
2835
- }"
2503
+ \`\`\`ts
2504
+ const [messages, users] = await Promise.all([
2505
+ replicas.slack.call('search.messages', { query: 'launch status' }),
2506
+ replicas.slack.call('users.list', { limit: 200 }),
2507
+ ]);
2836
2508
  \`\`\`
2837
2509
 
2838
- Omit \`thread_ts\` and/or \`initial_comment\` if you don't need them.
2839
-
2840
- ### Other Operations
2841
-
2842
- You can list channels, read channel history, add reactions, and perform any other operation supported by the Slack Web API using the same authentication pattern.
2843
-
2844
- For full API documentation, see: https://docs.slack.dev/apis/web-api/
2510
+ Supported methods cover conversations, messages, search, users, reactions, and Slack's external file-upload flow. An unsupported method is rejected by Replicas before Slack is called.
2845
2511
  `;
2846
2512
  var SLACK_ABILITY = {
2847
2513
  label: "Slack",
2848
- description: "Send messages, read threads, search conversations, upload files.",
2514
+ description: "Send messages, read threads, and search through @replicas/sdk.",
2849
2515
  bullet: "- Interacting with Slack (sending messages, reading threads, etc.)",
2850
- section: SECTION11,
2851
- referenceFile: { name: "SLACK.md", content: REFERENCE11 }
2516
+ section: SECTION13,
2517
+ referenceFile: { name: "SLACK.md", content: REFERENCE13 }
2852
2518
  };
2853
2519
 
2854
2520
  // ../shared/src/default-skills/replicas-agent/registry.ts
@@ -2862,7 +2528,9 @@ var REPLICAS_AGENT_ABILITY_REGISTRY = {
2862
2528
  learnings: LEARNINGS_ABILITY,
2863
2529
  linear: LINEAR_ABILITY,
2864
2530
  media: MEDIA_ABILITY,
2531
+ plugins: PLUGINS_ABILITY,
2865
2532
  previews: PREVIEWS_ABILITY,
2533
+ sentry: SENTRY_ABILITY,
2866
2534
  slack: SLACK_ABILITY
2867
2535
  };
2868
2536
  var REPLICAS_AGENT_ABILITIES = Object.keys(
@@ -5832,7 +5500,7 @@ var BaseRefreshManager = class {
5832
5500
  this.health.lastErrorMessage = message;
5833
5501
  if (attempt < 3) {
5834
5502
  console.warn(`[${this.managerName}] Initial refresh attempt ${attempt} failed, retrying in 2s...`);
5835
- await new Promise((resolve4) => setTimeout(resolve4, 2e3));
5503
+ await new Promise((resolve5) => setTimeout(resolve5, 2e3));
5836
5504
  } else {
5837
5505
  console.error(`[${this.managerName}] Initial refresh failed after 3 attempts:`, error);
5838
5506
  }
@@ -6371,7 +6039,7 @@ var GitService = class {
6371
6039
  }
6372
6040
  }
6373
6041
  readUntrackedDiff(repoPath, paths) {
6374
- return new Promise((resolve4) => {
6042
+ return new Promise((resolve5) => {
6375
6043
  const child = spawn("git", ["diff", "--", ...paths], {
6376
6044
  cwd: repoPath,
6377
6045
  stdio: ["ignore", "pipe", "pipe"]
@@ -6392,12 +6060,12 @@ var GitService = class {
6392
6060
  });
6393
6061
  child.stderr.on("data", () => {
6394
6062
  });
6395
- child.on("error", () => resolve4(""));
6063
+ child.on("error", () => resolve5(""));
6396
6064
  child.on("close", (code) => {
6397
6065
  if (code === 0 || code === 1 || truncated) {
6398
- resolve4(Buffer.concat(chunks).toString("utf-8"));
6066
+ resolve5(Buffer.concat(chunks).toString("utf-8"));
6399
6067
  } else {
6400
- resolve4("");
6068
+ resolve5("");
6401
6069
  }
6402
6070
  });
6403
6071
  });
@@ -7134,9 +6802,9 @@ var StreamWriter = class {
7134
6802
  return true;
7135
6803
  }
7136
6804
  flush(signal) {
7137
- return new Promise((resolve4, reject) => {
6805
+ return new Promise((resolve5, reject) => {
7138
6806
  if (!this.stream) {
7139
- resolve4();
6807
+ resolve5();
7140
6808
  return;
7141
6809
  }
7142
6810
  if (this.flushTimer) {
@@ -7153,7 +6821,7 @@ var StreamWriter = class {
7153
6821
  s.write("", (error) => {
7154
6822
  signal?.removeEventListener("abort", onAbort);
7155
6823
  if (error) reject(error);
7156
- else resolve4();
6824
+ else resolve5();
7157
6825
  });
7158
6826
  });
7159
6827
  }
@@ -7691,7 +7359,7 @@ var ReplicasConfigService = class {
7691
7359
  await this.logToFile(`[${params.repoName}] --- Running: ${params.label} ---`);
7692
7360
  emit(`$ ${params.label}
7693
7361
  `);
7694
- return new Promise((resolve4) => {
7362
+ return new Promise((resolve5) => {
7695
7363
  const proc = spawn2("bash", ["-lc", params.command], {
7696
7364
  cwd: params.cwd,
7697
7365
  env: process.env,
@@ -7702,7 +7370,7 @@ var ReplicasConfigService = class {
7702
7370
  const finish = (result) => {
7703
7371
  if (settled) return;
7704
7372
  settled = true;
7705
- resolve4(result);
7373
+ resolve5(result);
7706
7374
  };
7707
7375
  const timer = setTimeout(() => {
7708
7376
  timedOut = true;
@@ -8128,7 +7796,7 @@ async function registerDesktopPreview() {
8128
7796
  if (await attemptRegistration()) {
8129
7797
  return;
8130
7798
  }
8131
- await new Promise((resolve4) => setTimeout(resolve4, RETRY_DELAY_MS));
7799
+ await new Promise((resolve5) => setTimeout(resolve5, RETRY_DELAY_MS));
8132
7800
  }
8133
7801
  console.error(
8134
7802
  `[DesktopPreview] Gave up registering port ${DESKTOP_STREAM_PORT} after ${REGISTRATION_TIMEOUT_MS}ms`
@@ -9669,8 +9337,8 @@ var PromptStream = class {
9669
9337
  if (this.closed) {
9670
9338
  return Promise.resolve({ value: void 0, done: true });
9671
9339
  }
9672
- return new Promise((resolve4) => {
9673
- this.waiters.push(resolve4);
9340
+ return new Promise((resolve5) => {
9341
+ this.waiters.push(resolve5);
9674
9342
  });
9675
9343
  }
9676
9344
  };
@@ -10050,14 +9718,14 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
10050
9718
  const requestId = randomUUID4();
10051
9719
  const toolUseId = options.toolUseID;
10052
9720
  const { options: requestOptions, questions: requestQuestions } = handler.getRequest(input);
10053
- const result = await new Promise((resolve4) => {
9721
+ const result = await new Promise((resolve5) => {
10054
9722
  this.pendingToolInputs.set(requestId, {
10055
9723
  requestId,
10056
9724
  toolUseId,
10057
9725
  toolName,
10058
9726
  input,
10059
9727
  handler,
10060
- resolve: resolve4
9728
+ resolve: resolve5
10061
9729
  });
10062
9730
  const onAbort = () => {
10063
9731
  const pending = this.pendingToolInputs.get(requestId);
@@ -10162,7 +9830,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
10162
9830
  error
10163
9831
  );
10164
9832
  await this.tearDownSession();
10165
- await new Promise((resolve4) => setTimeout(resolve4, delayMs));
9833
+ await new Promise((resolve5) => setTimeout(resolve5, delayMs));
10166
9834
  currentRequest = { ...request, message: CLAUDE_MIDTURN_CONTINUE_PROMPT, images: [] };
10167
9835
  attempt++;
10168
9836
  continue;
@@ -10175,7 +9843,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
10175
9843
  error
10176
9844
  );
10177
9845
  await this.tearDownSession();
10178
- await new Promise((resolve4) => setTimeout(resolve4, delayMs));
9846
+ await new Promise((resolve5) => setTimeout(resolve5, delayMs));
10179
9847
  attempt++;
10180
9848
  continue;
10181
9849
  }
@@ -10321,8 +9989,8 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
10321
9989
  this.pendingInterrupt = false;
10322
9990
  return;
10323
9991
  }
10324
- await new Promise((resolve4, reject) => {
10325
- this.pendingTurn = { resolve: resolve4, reject, sawActivity: false };
9992
+ await new Promise((resolve5, reject) => {
9993
+ this.pendingTurn = { resolve: resolve5, reject, sawActivity: false };
10326
9994
  promptStream.push(userMessage);
10327
9995
  });
10328
9996
  }
@@ -10909,13 +10577,13 @@ var AspClient = class {
10909
10577
  }
10910
10578
  const id = this.nextId;
10911
10579
  this.nextId += 1;
10912
- const promise = new Promise((resolve4, reject) => {
10580
+ const promise = new Promise((resolve5, reject) => {
10913
10581
  const timeoutMs = opts?.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
10914
10582
  const timer = timeoutMs > 0 ? setTimeout(() => {
10915
10583
  this.pending.delete(id);
10916
10584
  reject(new Error(`ASP request timed out for ${method}`));
10917
10585
  }, timeoutMs) : null;
10918
- this.pending.set(id, { resolve: resolve4, reject, method, timer });
10586
+ this.pending.set(id, { resolve: resolve5, reject, method, timer });
10919
10587
  });
10920
10588
  this.write({ method, id, params });
10921
10589
  return promise;
@@ -11068,7 +10736,7 @@ var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
11068
10736
  var MIN_CODEX_CLI_VERSION = "0.144.6";
11069
10737
  var CODEX_UPGRADE_TIMEOUT_MS = 12e4;
11070
10738
  var codexCliVersionEnsured = null;
11071
- var ENGINE_PACKAGE_VERSION = "0.1.594";
10739
+ var ENGINE_PACKAGE_VERSION = "0.1.596";
11072
10740
  var INITIALIZE_METHOD = "initialize";
11073
10741
  var INITIALIZED_NOTIFICATION = "initialized";
11074
10742
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -11188,13 +10856,13 @@ var AppServerProcess = class {
11188
10856
  if (!child || child.killed) {
11189
10857
  return;
11190
10858
  }
11191
- await new Promise((resolve4) => {
10859
+ await new Promise((resolve5) => {
11192
10860
  const timer = setTimeout(() => {
11193
10861
  child.kill("SIGKILL");
11194
10862
  }, 2e3);
11195
10863
  child.once("exit", () => {
11196
10864
  clearTimeout(timer);
11197
- resolve4();
10865
+ resolve5();
11198
10866
  });
11199
10867
  child.kill("SIGTERM");
11200
10868
  });
@@ -11239,8 +10907,8 @@ var AppServerProcess = class {
11239
10907
  }
11240
10908
  this.shuttingDown = true;
11241
10909
  child.kill("SIGKILL");
11242
- await new Promise((resolve4) => {
11243
- child.once("exit", () => resolve4());
10910
+ await new Promise((resolve5) => {
10911
+ child.once("exit", () => resolve5());
11244
10912
  });
11245
10913
  }
11246
10914
  };
@@ -12520,8 +12188,8 @@ var CodexAspManager = class extends CodingAgentManager {
12520
12188
  }
12521
12189
  async observeTurn(host, threadId, request, startTurn, options = {}) {
12522
12190
  let resolveCompleted;
12523
- const completed = new Promise((resolve4) => {
12524
- resolveCompleted = resolve4;
12191
+ const completed = new Promise((resolve5) => {
12192
+ resolveCompleted = resolve5;
12525
12193
  });
12526
12194
  let rejectDisposed = () => {
12527
12195
  };
@@ -13487,8 +13155,8 @@ var CursorManager = class extends CodingAgentManager {
13487
13155
  }
13488
13156
  async processMessageInternal(request) {
13489
13157
  let run = null;
13490
- const aborted = new Promise((resolve4) => {
13491
- this.abortActiveTurn = () => resolve4(TURN_ABORTED);
13158
+ const aborted = new Promise((resolve5) => {
13159
+ this.abortActiveTurn = () => resolve5(TURN_ABORTED);
13492
13160
  });
13493
13161
  const linearSessionId = ENGINE_ENV.REPLICAS_LINEAR_SESSION_ID;
13494
13162
  const linearForwarder = new LinearEventForwarder(linearSessionId);
@@ -14129,8 +13797,8 @@ var OpencodeManager = class extends CodingAgentManager {
14129
13797
  this.configuredModels = this.providerId === OPENCODE_GO_PROVIDER ? /* @__PURE__ */ new Set() : new Set(getConfiguredOpencodeModels(model, providerId));
14130
13798
  const eventController = new AbortController();
14131
13799
  this.eventAbortController = eventController;
14132
- this.eventSubscriptionReady = new Promise((resolve4) => {
14133
- this.resolveEventSubscriptionReady = resolve4;
13800
+ this.eventSubscriptionReady = new Promise((resolve5) => {
13801
+ this.resolveEventSubscriptionReady = resolve5;
14134
13802
  });
14135
13803
  this.subscribeToEvents(client, eventController).catch((error) => {
14136
13804
  this.resolveEventSubscriptionReady?.();
@@ -14140,7 +13808,7 @@ var OpencodeManager = class extends CodingAgentManager {
14140
13808
  });
14141
13809
  await Promise.race([
14142
13810
  this.eventSubscriptionReady,
14143
- new Promise((resolve4) => setTimeout(resolve4, 2e3))
13811
+ new Promise((resolve5) => setTimeout(resolve5, 2e3))
14144
13812
  ]);
14145
13813
  return client;
14146
13814
  }
@@ -15762,7 +15430,7 @@ function isReconcileCanvasItemsResponse(value) {
15762
15430
  }
15763
15431
  async function withCanvasUploadSlot(operation) {
15764
15432
  if (activeCanvasUploads >= MAX_CONCURRENT_CANVAS_UPLOADS) {
15765
- await new Promise((resolve4) => pendingCanvasUploads.push(resolve4));
15433
+ await new Promise((resolve5) => pendingCanvasUploads.push(resolve5));
15766
15434
  } else {
15767
15435
  activeCanvasUploads++;
15768
15436
  }
@@ -15897,7 +15565,7 @@ var HISTORY_DIRS = [
15897
15565
  join25(ENGINE_DIR2, "codex-histories")
15898
15566
  ];
15899
15567
  async function putTranscript(uploadUrl, filePath, size) {
15900
- await new Promise((resolve4, reject) => {
15568
+ await new Promise((resolve5, reject) => {
15901
15569
  const url = new URL(uploadUrl);
15902
15570
  const request = (url.protocol === "https:" ? httpsRequest : httpRequest)(url, {
15903
15571
  method: "PUT",
@@ -15913,7 +15581,7 @@ async function putTranscript(uploadUrl, filePath, size) {
15913
15581
  });
15914
15582
  response.on("end", () => {
15915
15583
  const status = response.statusCode ?? 0;
15916
- if (status >= 200 && status < 300) resolve4();
15584
+ if (status >= 200 && status < 300) resolve5();
15917
15585
  else reject(new Error(`upload failed: ${status} ${body}`));
15918
15586
  });
15919
15587
  response.on("error", reject);
@@ -16120,13 +15788,13 @@ function runBeforeDeadline(operation, deadline) {
16120
15788
  const timeoutMs = deadline - Date.now();
16121
15789
  if (timeoutMs <= 0) return Promise.reject(new DOMException("engine log flush deadline exceeded", "TimeoutError"));
16122
15790
  const signal = AbortSignal.timeout(timeoutMs);
16123
- return new Promise((resolve4, reject) => {
15791
+ return new Promise((resolve5, reject) => {
16124
15792
  const onAbort = () => reject(signal.reason);
16125
15793
  signal.addEventListener("abort", onAbort, { once: true });
16126
15794
  operation(signal).then(
16127
15795
  (value) => {
16128
15796
  signal.removeEventListener("abort", onAbort);
16129
- resolve4(value);
15797
+ resolve5(value);
16130
15798
  },
16131
15799
  (error) => {
16132
15800
  signal.removeEventListener("abort", onAbort);
@@ -17155,7 +16823,7 @@ function scoreMatch(query2, filePath) {
17155
16823
  return 0;
17156
16824
  }
17157
16825
  function execGitAsync(args, cwd, timeoutMs) {
17158
- return new Promise((resolve4, reject) => {
16826
+ return new Promise((resolve5, reject) => {
17159
16827
  const child = execFile2("git", args, {
17160
16828
  cwd,
17161
16829
  encoding: "utf-8",
@@ -17165,7 +16833,7 @@ function execGitAsync(args, cwd, timeoutMs) {
17165
16833
  if (error) {
17166
16834
  reject(error);
17167
16835
  } else {
17168
- resolve4(stdout);
16836
+ resolve5(stdout);
17169
16837
  }
17170
16838
  });
17171
16839
  child.stdin?.end();
@@ -17557,7 +17225,7 @@ async function executeHookScriptStreaming(params) {
17557
17225
  let bufferExceeded = false;
17558
17226
  params.onChunk(`$ ${params.label}
17559
17227
  `);
17560
- return new Promise((resolve4) => {
17228
+ return new Promise((resolve5) => {
17561
17229
  const proc = spawn4("bash", ["-lc", params.content], {
17562
17230
  cwd: params.cwd,
17563
17231
  env: process.env,
@@ -17590,7 +17258,7 @@ async function executeHookScriptStreaming(params) {
17590
17258
  proc.on("close", (code) => {
17591
17259
  clearTimeout(timer);
17592
17260
  const output = [`$ ${params.label}`, ...outputParts].join("");
17593
- resolve4({
17261
+ resolve5({
17594
17262
  exitCode: bufferExceeded ? 1 : code ?? 1,
17595
17263
  output,
17596
17264
  timedOut: killed && !bufferExceeded
@@ -17600,7 +17268,7 @@ async function executeHookScriptStreaming(params) {
17600
17268
  clearTimeout(timer);
17601
17269
  const output = [`$ ${params.label}
17602
17270
  `, ...outputParts, err.message].join("");
17603
- resolve4({
17271
+ resolve5({
17604
17272
  exitCode: 1,
17605
17273
  output,
17606
17274
  timedOut: false
@@ -18873,14 +18541,30 @@ var HeartbeatService = class _HeartbeatService {
18873
18541
  };
18874
18542
  var heartbeatService = new HeartbeatService();
18875
18543
 
18544
+ // src/services/workspace-sdk-service.ts
18545
+ import { cp, mkdir as mkdir17 } from "fs/promises";
18546
+ import { dirname as dirname8, resolve as resolve4 } from "path";
18547
+ import { fileURLToPath as fileURLToPath2 } from "url";
18548
+ async function installWorkspaceSdk() {
18549
+ const source = resolve4(dirname8(fileURLToPath2(import.meta.url)), "../../workspace-sdk");
18550
+ const targets = [
18551
+ resolve4(ENGINE_ENV.HOME_DIR, "node_modules/@replicas/sdk"),
18552
+ "/tmp/node_modules/@replicas/sdk"
18553
+ ];
18554
+ await Promise.all(targets.map(async (target) => {
18555
+ await mkdir17(target, { recursive: true });
18556
+ await cp(source, target, { recursive: true, force: true });
18557
+ }));
18558
+ }
18559
+
18876
18560
  // src/index.ts
18877
18561
  var startupStartedAt = performance.now();
18878
18562
  var startupTimings = {};
18879
18563
  var ENGINE_INIT_GATE_TIMEOUT_MS = 35 * 60 * 1e3;
18880
18564
  var resolveEngineReady = () => {
18881
18565
  };
18882
- var engineReadyPromise = new Promise((resolve4) => {
18883
- resolveEngineReady = resolve4;
18566
+ var engineReadyPromise = new Promise((resolve5) => {
18567
+ resolveEngineReady = resolve5;
18884
18568
  });
18885
18569
  async function timeStartupStep(name, fn) {
18886
18570
  const startedAt = performance.now();
@@ -18900,7 +18584,7 @@ async function waitForInitializationGate() {
18900
18584
  if (Date.now() >= deadline) {
18901
18585
  throw new Error("Timed out waiting for deferred engine initialization gate");
18902
18586
  }
18903
- await new Promise((resolve4) => setTimeout(resolve4, 25));
18587
+ await new Promise((resolve5) => setTimeout(resolve5, 25));
18904
18588
  }
18905
18589
  }
18906
18590
  await timeStartupStep("engine_logger_initialize", () => engineLogger.initialize());
@@ -18932,11 +18616,11 @@ async function checkActiveSSHSessions() {
18932
18616
  }
18933
18617
  }
18934
18618
  function checkDesktopReady() {
18935
- return new Promise((resolve4) => {
18619
+ return new Promise((resolve5) => {
18936
18620
  const socket = connect({ host: "127.0.0.1", port: DESKTOP_STREAM_PORT });
18937
18621
  const done = (ready) => {
18938
18622
  socket.destroy();
18939
- resolve4(ready);
18623
+ resolve5(ready);
18940
18624
  };
18941
18625
  socket.once("connect", () => done(true));
18942
18626
  socket.once("error", () => done(false));
@@ -18968,7 +18652,7 @@ app.get("/health", async (c) => {
18968
18652
  if (!engineReady && waitMs > 0) {
18969
18653
  await Promise.race([
18970
18654
  engineReadyPromise,
18971
- new Promise((resolve4) => setTimeout(resolve4, waitMs))
18655
+ new Promise((resolve5) => setTimeout(resolve5, waitMs))
18972
18656
  ]);
18973
18657
  }
18974
18658
  c.header(ENGINE_HEALTH_WAIT_HEADER, "1");
@@ -19168,6 +18852,7 @@ serve(
19168
18852
  console.log(`Replicas Engine running on port ${info.port}`);
19169
18853
  }
19170
18854
  await timeStartupStep("wait_for_initialization_gate", waitForInitializationGate);
18855
+ await timeStartupStep("workspace_sdk_install", installWorkspaceSdk);
19171
18856
  if (!IS_WARMING_MODE) {
19172
18857
  await timeStartupStep("replicas_config_initialize", () => replicasConfigService.initialize());
19173
18858
  }