From 262d2808858fdcb7a69f92d22286c435bb3f479f Mon Sep 17 00:00:00 2001
From: Kris Braun
Date: Mon, 27 Oct 2025 20:19:08 -0400
Subject: [PATCH] Agent builder docs
---
.changeset/open-yaks-fall.md | 5 +
.github/workflows/deploy-sdk-docs.yml | 65 ++
pnpm-lock.yaml | 168 ++++++
sdk/DOCS.md | 169 ++++++
sdk/README.md | 515 ++++------------
sdk/docs/ADVANCED.md | 589 ++++++++++++++++++
sdk/docs/BUILDING_TOOLS.md | 827 ++++++++++++++++++++++++++
sdk/docs/CLI_REFERENCE.md | 390 ++++++++++++
sdk/docs/CORE_CONCEPTS.md | 494 +++++++++++++++
sdk/docs/GETTING_STARTED.md | 260 ++++++++
sdk/docs/RUNTIME.md | 553 +++++++++++++++++
sdk/docs/TOOLS_GUIDE.md | 796 +++++++++++++++++++++++++
sdk/docs/index.md | 148 +++++
sdk/package.json | 13 +-
sdk/src/agent.ts | 5 +-
sdk/src/tool.ts | 13 +-
sdk/src/tools/agents.ts | 9 +-
sdk/src/tools/ai.ts | 3 +-
sdk/src/tools/callbacks.ts | 16 +-
sdk/src/tools/integrations.ts | 10 +-
sdk/src/tools/network.ts | 8 +-
sdk/src/tools/plot.ts | 21 +-
sdk/src/tools/store.ts | 9 +-
sdk/src/tools/tasks.ts | 8 +-
sdk/typedoc.json | 99 +++
25 files changed, 4758 insertions(+), 435 deletions(-)
create mode 100644 .changeset/open-yaks-fall.md
create mode 100644 .github/workflows/deploy-sdk-docs.yml
create mode 100644 sdk/DOCS.md
create mode 100644 sdk/docs/ADVANCED.md
create mode 100644 sdk/docs/BUILDING_TOOLS.md
create mode 100644 sdk/docs/CLI_REFERENCE.md
create mode 100644 sdk/docs/CORE_CONCEPTS.md
create mode 100644 sdk/docs/GETTING_STARTED.md
create mode 100644 sdk/docs/RUNTIME.md
create mode 100644 sdk/docs/TOOLS_GUIDE.md
create mode 100644 sdk/docs/index.md
create mode 100644 sdk/typedoc.json
diff --git a/.changeset/open-yaks-fall.md b/.changeset/open-yaks-fall.md
new file mode 100644
index 0000000..124f2fe
--- /dev/null
+++ b/.changeset/open-yaks-fall.md
@@ -0,0 +1,5 @@
+---
+"@plotday/sdk": patch
+---
+
+Added: Docs for build.plot.day
diff --git a/.github/workflows/deploy-sdk-docs.yml b/.github/workflows/deploy-sdk-docs.yml
new file mode 100644
index 0000000..a16922b
--- /dev/null
+++ b/.github/workflows/deploy-sdk-docs.yml
@@ -0,0 +1,65 @@
+name: Deploy SDK Docs
+
+on:
+ push:
+ branches: [main]
+ paths:
+ - "sdk/**"
+ - ".github/workflows/deploy-sdk-docs.yml"
+
+# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
+# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
+concurrency:
+ group: "pages"
+ cancel-in-progress: false
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ name: Build SDK Documentation
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ submodules: recursive
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ cache: "pnpm"
+
+ - name: Install SDK dependencies
+ run: pnpm install --frozen-lockfile --filter=./sdk
+
+ - name: Build SDK
+ run: cd sdk && pnpm build
+
+ - name: Setup Pages
+ uses: actions/configure-pages@v4
+
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: "sdk/dist/docs"
+
+ deploy:
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ runs-on: ubuntu-latest
+ name: Deploy to GitHub Pages
+ needs: build
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e3da947..1f854ff 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -80,6 +80,12 @@ importers:
tsx:
specifier: ^4.19.2
version: 4.20.6
+ typedoc:
+ specifier: ^0.28.14
+ version: 0.28.14(typescript@5.9.3)
+ typedoc-plugin-missing-exports:
+ specifier: ^4.1.2
+ version: 4.1.2(typedoc@0.28.14(typescript@5.9.3))
typescript:
specifier: ^5.9.3
version: 5.9.3
@@ -487,6 +493,9 @@ packages:
cpu: [x64]
os: [win32]
+ '@gerrit0/mini-shiki@3.14.0':
+ resolution: {integrity: sha512-c5X8fwPLOtUS8TVdqhynz9iV0GlOtFUT1ppXYzUUlEXe4kbZ/mvMT8wXoT8kCwUka+zsiloq7sD3pZ3+QVTuNQ==}
+
'@inquirer/external-editor@1.0.2':
resolution: {integrity: sha512-yy9cOoBnx58TlsPrIxauKIFQTiyH+0MK4e97y4sV9ERbI+zDxw7i2hxHLCIEGIE/8PPvDxGhgzIOTSOWcs6/MQ==}
engines: {node: '>=18'}
@@ -514,6 +523,24 @@ packages:
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
engines: {node: '>= 8'}
+ '@shikijs/engine-oniguruma@3.14.0':
+ resolution: {integrity: sha512-TNcYTYMbJyy+ZjzWtt0bG5y4YyMIWC2nyePz+CFMWqm+HnZZyy9SWMgo8Z6KBJVIZnx8XUXS8U2afO6Y0g1Oug==}
+
+ '@shikijs/langs@3.14.0':
+ resolution: {integrity: sha512-DIB2EQY7yPX1/ZH7lMcwrK5pl+ZkP/xoSpUzg9YC8R+evRCCiSQ7yyrvEyBsMnfZq4eBzLzBlugMyTAf13+pzg==}
+
+ '@shikijs/themes@3.14.0':
+ resolution: {integrity: sha512-fAo/OnfWckNmv4uBoUu6dSlkcBc+SA1xzj5oUSaz5z3KqHtEbUypg/9xxgJARtM6+7RVm0Q6Xnty41xA1ma1IA==}
+
+ '@shikijs/types@3.14.0':
+ resolution: {integrity: sha512-bQGgC6vrY8U/9ObG1Z/vTro+uclbjjD/uG58RvfxKZVD5p9Yc1ka3tVyEFy7BNJLzxuWyHH5NWynP9zZZS59eQ==}
+
+ '@shikijs/vscode-textmate@10.0.2':
+ resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
+
+ '@types/hast@3.0.4':
+ resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
+
'@types/node@12.20.55':
resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==}
@@ -523,6 +550,9 @@ packages:
'@types/prompts@2.4.9':
resolution: {integrity: sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==}
+ '@types/unist@3.0.3':
+ resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
+
ansi-colors@4.1.3:
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
engines: {node: '>=6'}
@@ -538,14 +568,23 @@ packages:
argparse@1.0.10:
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
+ argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
array-union@2.1.0:
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
engines: {node: '>=8'}
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
better-path-resolve@1.0.0:
resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==}
engines: {node: '>=4'}
+ brace-expansion@2.0.2:
+ resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
+
braces@3.0.3:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
@@ -599,6 +638,10 @@ packages:
resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==}
engines: {node: '>=8.6'}
+ entities@4.5.0:
+ resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+ engines: {node: '>=0.12'}
+
esbuild@0.24.2:
resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==}
engines: {node: '>=18'}
@@ -714,6 +757,9 @@ packages:
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
engines: {node: '>=6'}
+ linkify-it@5.0.0:
+ resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
+
locate-path@5.0.0:
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
engines: {node: '>=8'}
@@ -721,6 +767,16 @@ packages:
lodash.startcase@4.4.0:
resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==}
+ lunr@2.3.9:
+ resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==}
+
+ markdown-it@14.1.0:
+ resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==}
+ hasBin: true
+
+ mdurl@2.0.0:
+ resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==}
+
merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
@@ -729,6 +785,10 @@ packages:
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
engines: {node: '>=8.6'}
+ minimatch@9.0.5:
+ resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
mri@1.2.0:
resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
engines: {node: '>=4'}
@@ -800,6 +860,10 @@ packages:
resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
engines: {node: '>= 6'}
+ punycode.js@2.3.1:
+ resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==}
+ engines: {node: '>=6'}
+
quansync@0.2.11:
resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==}
@@ -888,11 +952,26 @@ packages:
typebox@1.0.35:
resolution: {integrity: sha512-K314QG+lnaymQwdJXxSPqv+X0l3zx4ltpVAXAWszwGjUT4nh1jTNdzM8bTwSh+TO7fXfv9VANnHxDzaLcCfIlw==}
+ typedoc-plugin-missing-exports@4.1.2:
+ resolution: {integrity: sha512-WNoeWX9+8X3E3riuYPduilUTFefl1K+Z+5bmYqNeH5qcWjtnTRMbRzGdEQ4XXn1WEO4WCIlU0vf46Ca2y/mspg==}
+ peerDependencies:
+ typedoc: ^0.28.1
+
+ typedoc@0.28.14:
+ resolution: {integrity: sha512-ftJYPvpVfQvFzpkoSfHLkJybdA/geDJ8BGQt/ZnkkhnBYoYW6lBgPQXu6vqLxO4X75dA55hX8Af847H5KXlEFA==}
+ engines: {node: '>= 18', pnpm: '>= 10'}
+ hasBin: true
+ peerDependencies:
+ typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x
+
typescript@5.9.3:
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
engines: {node: '>=14.17'}
hasBin: true
+ uc.micro@2.1.0:
+ resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==}
+
undici-types@7.14.0:
resolution: {integrity: sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==}
@@ -911,6 +990,11 @@ packages:
engines: {node: '>= 8'}
hasBin: true
+ yaml@2.8.1:
+ resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==}
+ engines: {node: '>= 14.6'}
+ hasBin: true
+
snapshots:
'@babel/runtime@7.28.4': {}
@@ -1227,6 +1311,14 @@ snapshots:
'@esbuild/win32-x64@0.25.11':
optional: true
+ '@gerrit0/mini-shiki@3.14.0':
+ dependencies:
+ '@shikijs/engine-oniguruma': 3.14.0
+ '@shikijs/langs': 3.14.0
+ '@shikijs/themes': 3.14.0
+ '@shikijs/types': 3.14.0
+ '@shikijs/vscode-textmate': 10.0.2
+
'@inquirer/external-editor@1.0.2(@types/node@24.7.2)':
dependencies:
chardet: 2.1.0
@@ -1262,6 +1354,30 @@ snapshots:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.19.1
+ '@shikijs/engine-oniguruma@3.14.0':
+ dependencies:
+ '@shikijs/types': 3.14.0
+ '@shikijs/vscode-textmate': 10.0.2
+
+ '@shikijs/langs@3.14.0':
+ dependencies:
+ '@shikijs/types': 3.14.0
+
+ '@shikijs/themes@3.14.0':
+ dependencies:
+ '@shikijs/types': 3.14.0
+
+ '@shikijs/types@3.14.0':
+ dependencies:
+ '@shikijs/vscode-textmate': 10.0.2
+ '@types/hast': 3.0.4
+
+ '@shikijs/vscode-textmate@10.0.2': {}
+
+ '@types/hast@3.0.4':
+ dependencies:
+ '@types/unist': 3.0.3
+
'@types/node@12.20.55': {}
'@types/node@24.7.2':
@@ -1273,6 +1389,8 @@ snapshots:
'@types/node': 24.7.2
kleur: 3.0.3
+ '@types/unist@3.0.3': {}
+
ansi-colors@4.1.3: {}
ansi-regex@5.0.1: {}
@@ -1285,12 +1403,20 @@ snapshots:
dependencies:
sprintf-js: 1.0.3
+ argparse@2.0.1: {}
+
array-union@2.1.0: {}
+ balanced-match@1.0.2: {}
+
better-path-resolve@1.0.0:
dependencies:
is-windows: 1.0.2
+ brace-expansion@2.0.2:
+ dependencies:
+ balanced-match: 1.0.2
+
braces@3.0.3:
dependencies:
fill-range: 7.1.1
@@ -1335,6 +1461,8 @@ snapshots:
ansi-colors: 4.1.3
strip-ansi: 6.0.1
+ entities@4.5.0: {}
+
esbuild@0.24.2:
optionalDependencies:
'@esbuild/aix-ppc64': 0.24.2
@@ -1490,12 +1618,29 @@ snapshots:
kleur@3.0.3: {}
+ linkify-it@5.0.0:
+ dependencies:
+ uc.micro: 2.1.0
+
locate-path@5.0.0:
dependencies:
p-locate: 4.1.0
lodash.startcase@4.4.0: {}
+ lunr@2.3.9: {}
+
+ markdown-it@14.1.0:
+ dependencies:
+ argparse: 2.0.1
+ entities: 4.5.0
+ linkify-it: 5.0.0
+ mdurl: 2.0.0
+ punycode.js: 2.3.1
+ uc.micro: 2.1.0
+
+ mdurl@2.0.0: {}
+
merge2@1.4.1: {}
micromatch@4.0.8:
@@ -1503,6 +1648,10 @@ snapshots:
braces: 3.0.3
picomatch: 2.3.1
+ minimatch@9.0.5:
+ dependencies:
+ brace-expansion: 2.0.2
+
mri@1.2.0: {}
node-fetch@2.7.0:
@@ -1550,6 +1699,8 @@ snapshots:
kleur: 3.0.3
sisteransi: 1.0.5
+ punycode.js@2.3.1: {}
+
quansync@0.2.11: {}
queue-microtask@1.2.3: {}
@@ -1621,8 +1772,23 @@ snapshots:
typebox@1.0.35: {}
+ typedoc-plugin-missing-exports@4.1.2(typedoc@0.28.14(typescript@5.9.3)):
+ dependencies:
+ typedoc: 0.28.14(typescript@5.9.3)
+
+ typedoc@0.28.14(typescript@5.9.3):
+ dependencies:
+ '@gerrit0/mini-shiki': 3.14.0
+ lunr: 2.3.9
+ markdown-it: 14.1.0
+ minimatch: 9.0.5
+ typescript: 5.9.3
+ yaml: 2.8.1
+
typescript@5.9.3: {}
+ uc.micro@2.1.0: {}
+
undici-types@7.14.0: {}
universalify@0.1.2: {}
@@ -1637,3 +1803,5 @@ snapshots:
which@2.0.2:
dependencies:
isexe: 2.0.0
+
+ yaml@2.8.1: {}
diff --git a/sdk/DOCS.md b/sdk/DOCS.md
new file mode 100644
index 0000000..4872267
--- /dev/null
+++ b/sdk/DOCS.md
@@ -0,0 +1,169 @@
+# Plot SDK Documentation
+
+This directory contains the TypeDoc-generated API documentation for the Plot Agent SDK.
+
+## Published Documentation
+
+The SDK documentation is automatically published to GitHub Pages whenever changes are pushed to the main branch:
+
+**Live Documentation**: [https://plotday.github.io/plot/](https://plotday.github.io/plot/)
+
+The documentation is automatically updated whenever changes to the SDK are merged into the main branch, ensuring developers always have access to the latest API reference.
+
+## Generating Documentation
+
+The documentation is automatically generated during the build process, but you can also generate it manually.
+
+### Generate Documentation
+
+```bash
+# From the SDK directory
+pnpm docs
+```
+
+This will create the documentation in `dist/docs/`
+
+### Clean Documentation
+
+```bash
+# Remove the generated docs
+pnpm docs:clean
+```
+
+## Viewing Documentation Locally
+
+After generating the documentation, you can view it by opening the generated HTML files in your browser:
+
+```bash
+# Cross-platform way (recommended)
+pnpm docs:open
+```
+
+This script automatically uses the correct command for your platform:
+- `open` on macOS
+- `xdg-open` on Linux
+- `start` on Windows
+
+You can also open it manually:
+
+```bash
+# macOS
+open dist/docs/index.html
+
+# Linux
+xdg-open dist/docs/index.html
+
+# Windows
+start dist/docs/index.html
+```
+
+## Documentation Structure
+
+The generated documentation includes:
+
+- **Classes** - Agent, Tool, and built-in tool classes
+- **Interfaces** - Activity, Priority, Contact, and other data types
+- **Enums** - ActivityType, AuthorType, ActivityLinkType, etc.
+- **Type Aliases** - NewActivity, NewPriority, and utility types
+- **Modules** - Organized by functionality (tools, common, utils)
+
+## Publishing Documentation
+
+The documentation is automatically published in two ways:
+
+### 1. GitHub Pages (Automatic)
+
+When changes to the SDK are pushed to the main branch, a GitHub Action automatically:
+- Builds the SDK and generates the documentation
+- Deploys it to GitHub Pages at [https://plotday.github.io/plot/](https://plotday.github.io/plot/)
+
+**Workflow Location**: `.github/workflows/deploy-sdk-docs.yml`
+
+**Trigger**: Automatic on push to `main` when files in `public/sdk/**` change
+
+### 2. npm Package (Manual)
+
+The documentation is included when the package is built and published to npm. Users can access it by:
+
+1. Viewing online at GitHub Pages (see above)
+2. Exploring the `dist/docs/` directory in the published npm package
+3. Using TypeScript language server features in their IDE for inline documentation
+
+### GitHub Pages Setup
+
+If you need to enable or configure GitHub Pages for this repository:
+
+1. Go to Repository Settings → Pages
+2. Under "Source", select: **GitHub Actions**
+3. The workflow will automatically handle deployment
+
+The `deploy-sdk-docs.yml` workflow includes all necessary permissions and configurations for GitHub Pages deployment.
+
+## Configuration
+
+The documentation generation is configured in `typedoc.json`. Key settings:
+
+- **Entry Points**: Main SDK exports (agent, tool, plot, tools/*)
+- **Output**: `dist/docs/`
+- **Visibility**: Public APIs only (excludes private/protected/internal members)
+- **Theme**: Default TypeDoc theme optimized for GitHub Pages
+- **Source Links**: Links to source code on GitHub
+
+## Customization
+
+To modify the documentation output:
+
+1. Edit `typedoc.json` to change TypeDoc settings
+2. Adjust which files are documented by modifying the `entryPoints` array
+3. Customize the theme, navigation, or sorting options
+
+See the [TypeDoc documentation](https://typedoc.org/) for more configuration options.
+
+## Contributing
+
+When adding new public APIs:
+
+1. Add comprehensive JSDoc comments to your code
+2. Use `@param` tags for function parameters
+3. Use `@returns` tag for return values
+4. Use `@example` tags to show usage examples
+5. Use `@see` tags to link to related APIs
+6. Regenerate docs with `pnpm docs` to verify formatting
+
+### JSDoc Example
+
+```typescript
+/**
+ * Creates a new activity in the current priority.
+ *
+ * Activities are the core data type in Plot, representing tasks, events, and notes.
+ * This method creates a new activity with the specified properties.
+ *
+ * @param activity - The activity data to create
+ * @returns Promise resolving to the created activity with its generated ID
+ *
+ * @example
+ * ```typescript
+ * const activity = await this.tools.plot.createActivity({
+ * type: ActivityType.Task,
+ * title: "Review pull request",
+ * links: [{
+ * type: ActivityLinkType.external,
+ * title: "View PR",
+ * url: "https://github.com/org/repo/pull/123"
+ * }]
+ * });
+ * ```
+ *
+ * @see {@link Activity} for the full activity type definition
+ * @see {@link ActivityType} for available activity types
+ */
+abstract createActivity(activity: NewActivity): Promise;
+```
+
+## Support
+
+For issues or questions about the documentation:
+
+- Open an issue at https://github.com/plotday/plot/issues
+- Tag it with the `documentation` label
diff --git a/sdk/README.md b/sdk/README.md
index 95abb20..76fe459 100644
--- a/sdk/README.md
+++ b/sdk/README.md
@@ -1,5 +1,5 @@
-
+
@@ -11,96 +11,57 @@
custom code that organizes and prioritizes all your messages, tasks, and apps.
-## Two Ways to Build Agents
+
+ 📚 Full Documentation →
+
+
+---
-You can create Plot agents in two ways:
+## Quick Start
-- **No Code Required** - Write a natural language description in a `plot-agent.md` file and deploy it directly. Perfect for non-developers or rapid prototyping. [Jump to No-Code Quick Start](#quick-start-no-code)
-- **Full Control with Code** - Write custom TypeScript code for complete flexibility and advanced integrations. [Jump to Developer Quick Start](#quick-start-for-developers)
+Choose your path:
-## Quick Start (No Code)
+- **[No Code](#no-code-quick-start)** - Write natural language, deploy in minutes
+- **[TypeScript](#developer-quick-start)** - Full control with code
-Create an agent using natural language - no coding required.
+### No-Code Quick Start
-### 1. Create a `plot-agent.md` File
+Create agents using natural language - no programming required!
-Create a file named `plot-agent.md` in your project directory and describe what you want your agent to do in plain English:
+**1. Create `plot-agent.md`:**
```markdown
# My Calendar Agent
I want an agent that:
-
-- Syncs my Google Calendar events into Plot as activities
+- Syncs my Google Calendar events into Plot
- Creates tasks for upcoming meetings
-- Sends me a reminder 10 minutes before each meeting
-- Updates activity status when meetings are completed
+- Sends reminders 10 minutes before meetings
```
-Be specific about:
-
-- What data sources to connect (e.g., Google Calendar, GitHub, Slack)
-- What actions to take (e.g., create tasks, send notifications)
-- When to trigger actions (e.g., on new events, on schedule, when activities change)
-
-### 2. Deploy Your Agent
-
-You'll need a [Plot account](https://plot.day) to deploy agents.
+**2. Deploy:**
```bash
-# Login to Plot
npx @plotday/sdk login
-
-# Deploy directly from your spec
npx @plotday/sdk agent deploy
```
-That's it! Your agent is now live in Plot.
-
-**Optional: Generate Code First**
+That's it! [Learn more →](https://build.plot.day/GETTING_STARTED.html#no-code-agents)
-If you want to see or customize the generated code before deploying:
+### Developer Quick Start
-```bash
-# Generate TypeScript code from your spec
-npx @plotday/sdk agent generate
-
-# Review and edit the generated src/index.ts
-# Then deploy
-npx @plotday/sdk agent deploy
-```
-
-## Quick Start (For Developers)
+Build agents with TypeScript for maximum flexibility.
-### 1. Create a New Agent
-
-Use the Plot CLI to scaffold a new agent:
+**1. Create a new agent:**
```bash
npx @plotday/sdk agent create
-# or
-yarn dlx @plotday/sdk agent create
-# or
-pnpm dlx @plotday/sdk agent create
```
-This will prompt you for:
-
-- Package name (kebab-case)
-- Display name (human-readable)
-
-### 2. Implement Your Agent
-
-Edit `src/index.ts` to add your agent logic:
+**2. Implement your agent:**
```typescript
-import {
- type Activity,
- ActivityType,
- Agent,
- type Priority,
- type ToolBuilder,
-} from "@plotday/sdk";
+import { Agent, ActivityType, type Priority, type ToolBuilder } from "@plotday/sdk";
import { Plot } from "@plotday/sdk/tools/plot";
export default class MyAgent extends Agent {
@@ -111,374 +72,163 @@ export default class MyAgent extends Agent {
}
async activate(priority: Pick) {
- // Called when the agent is activated for a priority
await this.tools.plot.createActivity({
type: ActivityType.Note,
title: "Welcome! Your agent is now active.",
});
}
-
- async activity(activity: Activity) {
- // Called when an activity is routed to this agent
- console.log("Processing activity:", activity.title);
- }
}
```
-### 3. Deploy Your Agent
-
-You'll need a [Plot account](https://plot.day) to deploy agents.
+**3. Deploy:**
```bash
npm run plot login
npm run deploy
```
-## Core Concepts
+[Complete guide →](https://build.plot.day/GETTING_STARTED.html)
-### Agents
+---
-Agents implement integrations and automations in Plot. They are added to priorities to manage activities.
-
-**Key Methods:**
-
-- `activate(priority)` - Called when the agent is activated for a priority
-- `activity(activity, changes)` - Called when an activity is routed to the agent
+## Core Concepts
-### Priorities and Activities
+### Agents
-Activities are the core data type in Plot, representing tasks, events, and notes.
+Agents implement integrations and automations. They respond to lifecycle events and process activities.
```typescript
-await this.plot.createActivity({
- type: ActivityType.Task,
- title: "Review pull request",
- links: [
- {
- type: ActivityLinkType.external,
- title: "View PR",
- url: "https://github.com/org/repo/pull/123",
- },
- ],
-});
+// Lifecycle methods
+async activate(priority) // When agent is added to a priority
+async deactivate() // When agent is removed
+async upgrade() // When new version is deployed
```
-Activities are grouped within nested contexts called Priorities (e.g. Work, Project X).
-
-**Type References:**
-
-- [ActivityType enum](https://github.com/plotday/plot/blob/main/sdk/src/plot.ts#L35-L42) - Note, Task, Event
-- [ActivityLinkType enum](https://github.com/plotday/plot/blob/main/sdk/src/plot.ts#L65-L74) - external, auth, hidden, callback
-- [Activity type](https://github.com/plotday/plot/blob/main/sdk/src/plot.ts#L216-L288) - Full activity structure
+### Tools
-## Tools
+Tools provide capabilities to agents. Use built-in tools or create your own.
-Tools provide functionality to agents. They can be:
+**Built-in Tools:**
+- **Plot** - Manage activities and priorities
+- **Store** - Persistent key-value storage
+- **AI** - Language models with structured output
+- **Integrations** - OAuth authentication
+- **Network** - HTTP access and webhooks
+- **Tasks** - Background task execution
+- **Callbacks** - Persistent function references
-- **Built-in Tools** - Core Plot functionality (Plot, Store, Integrations, etc.).
-- **Custom Tools** - Extra packages that add capabilities using the built-in tools. They often implement integrations with external services (Google Calendar, Outlook, etc.).
-
-Declare tools in the `build` method. Store, Tasks, and Callbacks methods are available directly on the Agent class:
-
-```typescript
-build(build: ToolBuilder) {
- return {
- plot: build(Plot),
- googleCalendar: build(GoogleCalendar),
- };
-}
-// Store, Tasks, and Callbacks methods are available directly:
-// this.get(), this.set(), this.callback(), this.run(), etc.
-```
+[View all tools →](https://build.plot.day/TOOLS_GUIDE.html)
-### Plot
+### Activities
-Core tool for creating and managing activities and priorities.
+The core data type representing tasks, events, and notes.
```typescript
-import { Plot } from "@plotday/sdk/tools/plot";
-
-// Create activities
await this.tools.plot.createActivity({
type: ActivityType.Task,
- title: "My task",
-});
-
-// Update activities
-await this.tools.plot.updateActivity(activity.id, {
- doneAt: new Date(),
-});
-
-// Delete activities
-await this.tools.plot.deleteActivity(activity.id);
-
-// Create priorities
-await this.tools.plot.createPriority({
- title: "Work",
+ title: "Review pull request",
+ links: [{
+ type: ActivityLinkType.external,
+ title: "View PR",
+ url: "https://github.com/org/repo/pull/123"
+ }]
});
```
-### Store
+[Learn more →](https://build.plot.day/CORE_CONCEPTS.html)
-Persistent key-value storage for agent state. Store methods are available directly on Agent and Tool classes.
+---
-```typescript
-// Save data (no import needed - available directly)
-await this.set("sync_token", token);
-
-// Retrieve data
-const token = await this.get("sync_token");
-
-// Clear data
-await this.clear("sync_token");
-await this.clearAll();
-```
+## CLI Commands
-### Integrations
+```bash
+# Authentication
+plot login
-OAuth authentication for external services.
+# Agent management
+plot agent create # Create new agent project
+plot agent generate # Generate code from plot-agent.md
+plot agent deploy # Deploy to Plot
+plot agent link # Activate for a priority
-```typescript
-import { Integrations, AuthLevel, AuthProvider, type Authorization } from "@plotday/sdk/tools/integrations";
-
-// Request authentication
-const authCallback = await this.callback("onAuthComplete", { provider: "google" });
-const authLink = await this.tools.integrations.request(
- {
- provider: AuthProvider.Google,
- level: AuthLevel.User,
- scopes: ["https://www.googleapis.com/auth/calendar.readonly"],
- },
- authCallback
-);
-
-// Handle auth completion
-async onAuthComplete(authorization: Authorization, context: any) {
- // Get access token
- const authToken = await this.tools.integrations.get(authorization);
- console.log("Access token:", authToken?.token);
-}
+# Priority management
+plot priority list # List all priorities
+plot priority create # Create new priority
```
-**Type References:**
+[Complete CLI reference →](https://build.plot.day/CLI_REFERENCE.html)
-- [AuthProvider enum](https://github.com/plotday/plot/blob/main/sdk/src/tools/integrations.ts#L82-L87) - Google, Microsoft
-- [AuthLevel enum](https://github.com/plotday/plot/blob/main/sdk/src/tools/integrations.ts#L94-L99) - Priority, User
+---
-### Tasks
+## Documentation
-Queue background tasks and scheduled operations. Tasks methods are available directly on Agent and Tool classes.
+**[📚 Full Documentation at build.plot.day](https://build.plot.day)**
-```typescript
-// Execute immediately (no import needed - available directly)
-const callback = await this.callback("syncCalendar", { calendarId: "primary" });
-await this.run(callback);
-
-// Schedule for later
-const reminderCallback = await this.callback("sendReminder", { userId: "123" });
-await this.run(reminderCallback, { runAt: new Date("2025-01-15T10:00:00Z") });
-```
-
-### Network
-
-Request HTTP access permissions and create webhook endpoints for real-time notifications from external services.
-
-```typescript
-import { Network, type WebhookRequest } from "@plotday/sdk/tools/network";
-
-// Declare HTTP access in build method
-build(build: ToolBuilder) {
- return {
- network: build(Network, {
- urls: ['https://api.example.com/*']
- })
- };
-}
-
-// Create webhook endpoint
-const webhookUrl = await this.tools.network.createWebhook(
- "onCalendarUpdate",
- { calendarId: "primary" }
-);
+### Guides
+- [Getting Started](https://build.plot.day/GETTING_STARTED.html) - Complete walkthrough
+- [Core Concepts](https://build.plot.day/CORE_CONCEPTS.html) - Agents, tools, and architecture
+- [Built-in Tools](https://build.plot.day/TOOLS_GUIDE.html) - Plot, Store, AI, and more
+- [Building Custom Tools](https://build.plot.day/BUILDING_TOOLS.html) - Create reusable tools
+- [Runtime Environment](https://build.plot.day/RUNTIME.html) - Execution constraints and optimization
+- [Advanced Topics](https://build.plot.day/ADVANCED.html) - Complex patterns and techniques
-// Handle webhook requests
-async onCalendarUpdate(request: WebhookRequest, context: any) {
- console.log("Webhook received:", request.method, request.body);
- // Process webhook
-}
+### Reference
+- [CLI Reference](https://build.plot.day/CLI_REFERENCE.html) - Complete command documentation
+- [API Reference](https://build.plot.day) - TypeDoc-generated API docs
-// Delete webhook endpoint
-await this.tools.network.deleteWebhook(webhookUrl);
-```
+---
-### Callbacks
+## Examples
-Create persistent function references for webhooks and auth flows. Callbacks methods are available directly on Agent and Tool classes.
+### Simple Note Agent
```typescript
-// Create callback (no import needed - available directly)
-const callback = await this.callback("handleEvent", {
- eventType: "calendar_sync",
-});
-
-// Execute callback
-const result = await this.run(callback, {
- data: eventData,
-});
-
-// Delete callback
-await this.deleteCallback(callback);
-await this.deleteAllCallbacks(); // Delete all
-```
-
-### AI
-
-Prompt large language models with structured output support.
-
-```typescript
-import { AI } from "@plotday/sdk/tools/ai";
-import { Type } from "typebox";
-
-// Simple text generation with fast, low-cost model
-const response = await this.tools.ai.prompt({
- model: { speed: "fast", cost: "low" },
- prompt: "Explain quantum computing in simple terms",
-});
-console.log(response.text);
-
-// Structured output with type-safe schemas
-const schema = Type.Object({
- category: Type.Union([
- Type.Literal("work"),
- Type.Literal("personal"),
- Type.Literal("urgent"),
- ]),
- priority: Type.Number({ minimum: 1, maximum: 5 }),
- summary: Type.String({ description: "Brief summary" }),
-});
-
-const response = await this.tools.ai.prompt({
- model: { speed: "balanced", cost: "medium" },
- prompt: "Categorize this email: Meeting at 3pm tomorrow",
- outputSchema: schema,
-});
+export default class WelcomeAgent extends Agent {
+ build(build: ToolBuilder) {
+ return { plot: build(Plot) };
+ }
-// Fully typed output!
-console.log(response.output.category); // "work" | "personal" | "urgent"
-console.log(response.output.priority); // number
-
-// Tool calling
-const response = await this.tools.ai.prompt({
- model: { speed: "fast", cost: "medium" },
- prompt: "What's 15% of $250?",
- tools: {
- calculate: {
- description: "Perform calculations",
- parameters: Type.Object({
- expression: Type.String(),
- }),
- execute: async ({ expression }) => {
- return { result: eval(expression) };
- },
- },
- },
-});
+ async activate(priority: Pick) {
+ await this.tools.plot.createActivity({
+ type: ActivityType.Note,
+ title: "Welcome to Plot! 👋"
+ });
+ }
+}
```
-**Structured Output with Typebox:**
-
-The AI tool uses [Typebox](https://github.com/sinclairzx81/typebox) for schema definition. Typebox provides JSON Schema with full TypeScript type inference:
+### GitHub Integration
```typescript
-import { Type } from "typebox";
-
-// Define schema
-const PersonSchema = Type.Object({
- name: Type.String(),
- age: Type.Number(),
- email: Type.Optional(Type.String({ format: "email" })),
-});
-
-// Use in AI prompt
-const response = await this.tools.ai.prompt({
- model: { speed: "balanced", cost: "medium" },
- prompt: "Extract: John Doe, 30 years old, john@example.com",
- outputSchema: PersonSchema,
-});
-
-// TypeScript knows the exact shape!
-response.output.name; // string
-response.output.age; // number
-response.output.email; // string | undefined
-```
-
-**Model Selection:**
-
-Use `ModelPreferences` to specify your requirements based on speed and cost tiers:
-
-- **Speed**: `"fast"`, `"balanced"`, or `"capable"`
-- **Cost**: `"low"`, `"medium"`, or `"high"`
-
-Plot automatically selects the best available model matching your preferences. See the [AIModel enum](https://github.com/plotday/plot/blob/main/sdk/src/tools/ai.ts#L213-L243) for specific models currently supported.
-
-## CLI Commands
-
-The Plot CLI provides commands for managing agents:
-
-### Authentication
-
-```bash
-plot login
-```
-
-Authenticate with Plot to generate an API token.
-
-### Agent Management
-
-```bash
-# Create a new agent (code-based)
-plot agent create [options]
-
-# Generate code from plot-agent.md spec
-plot agent generate [--input ] [--output ]
-
-# Check for errors
-plot agent lint [--dir ]
+export default class GitHubAgent extends Agent {
+ build(build: ToolBuilder) {
+ return {
+ plot: build(Plot),
+ network: build(Network, {
+ urls: ['https://api.github.com/*']
+ })
+ };
+ }
-# Deploy agent (works with code or plot-agent.md)
-plot agent deploy [options]
+ async activate(priority: Pick) {
+ // Set up webhook for issue updates
+ const webhookUrl = await this.tools.network.createWebhook("onIssueUpdate");
+ await this.set("webhook_url", webhookUrl);
+ }
-# Link agent to priority
-plot agent link [--priority-id ]
+ async onIssueUpdate(request: WebhookRequest) {
+ // Sync GitHub issues to Plot activities
+ }
+}
```
-**`plot agent generate`**
-
-Generates fully functional TypeScript agent code from a natural language `plot-agent.md` specification.
-
-- `--input ` - Path to plot-agent.md file (default: `./plot-agent.md`)
-- `--output ` - Output directory for generated code (default: `./src`)
-
-**`plot agent deploy`**
-
-Deploys an agent to Plot. Automatically detects whether to deploy from:
+[More examples →](https://build.plot.day/GETTING_STARTED.html)
-- A `plot-agent.md` specification file (generates and deploys in one step)
-- Compiled TypeScript code in `src/` directory
-
-### Priority Management
-
-```bash
-# List priorities
-plot priority list
-
-# Create priority
-plot priority create [--name ] [--parent-id ]
-```
+---
## TypeScript Configuration
-When creating an agent, Plot provides a base TypeScript configuration. Extend it in your `tsconfig.json`:
+Extend the SDK's base configuration in your `tsconfig.json`:
```json
{
@@ -487,38 +237,15 @@ When creating an agent, Plot provides a base TypeScript configuration. Extend it
}
```
-## Runtime Limitations
-
-Agents run in a sandboxed environment with limited resources. For long-running operations, break work into chunks using the run method:
-
-```typescript
-async startSync(calendarId: string) {
- // Save state
- await this.set("sync_state", { calendarId, page: 1 });
-
- // Queue first batch
- const callback = await this.callback("syncBatch", { calendarId, page: 1 });
- await this.run(callback);
-}
-
-async syncBatch(args: any, context: { calendarId: string; page: number }) {
- // Process one batch
- const hasMore = await processBatch(context.calendarId, context.page);
-
- if (hasMore) {
- // Queue next batch
- const callback = await this.callback("syncBatch", {
- calendarId: context.calendarId,
- page: context.page + 1
- });
- await this.run(callback);
- }
-}
-```
+---
## Support
-- **Issues**: [https://github.com/plotday/plot/issues](https://github.com/plotday/plot/issues)
+- **Documentation**: [build.plot.day](https://build.plot.day)
+- **Issues**: [github.com/plotday/plot/issues](https://github.com/plotday/plot/issues)
+- **Website**: [plot.day](https://plot.day)
+
+---
## License
diff --git a/sdk/docs/ADVANCED.md b/sdk/docs/ADVANCED.md
new file mode 100644
index 0000000..d8f3552
--- /dev/null
+++ b/sdk/docs/ADVANCED.md
@@ -0,0 +1,589 @@
+---
+title: Advanced
+group: Guides
+---
+
+# Advanced Topics
+
+Advanced patterns and techniques for building sophisticated Plot agents.
+
+## Table of Contents
+
+- [Complex Agent Architectures](#complex-agent-architectures)
+- [Error Handling](#error-handling)
+- [Debugging and Logging](#debugging-and-logging)
+- [Security Best Practices](#security-best-practices)
+- [Migration and Versioning](#migration-and-versioning)
+- [Performance Patterns](#performance-patterns)
+
+---
+
+## Complex Agent Architectures
+
+### Multi-Service Integration
+
+Coordinate multiple external services:
+
+```typescript
+import { GitHubTool } from "@mycompany/plot-github-tool";
+import { JiraTool } from "@mycompany/plot-jira-tool";
+import { SlackTool } from "@mycompany/plot-slack-tool";
+
+import { Agent, type Priority, type ToolBuilder } from "@plotday/sdk";
+import { Plot } from "@plotday/sdk/tools/plot";
+
+export default class DevOpsAgent extends Agent {
+ build(build: ToolBuilder) {
+ return {
+ plot: build(Plot),
+ github: build(GitHubTool, {
+ owner: "mycompany",
+ repo: "myapp",
+ token: process.env.GITHUB_TOKEN!,
+ }),
+ slack: build(SlackTool, {
+ webhookUrl: process.env.SLACK_WEBHOOK_URL!,
+ }),
+ jira: build(JiraTool, {
+ domain: "mycompany.atlassian.net",
+ apiToken: process.env.JIRA_TOKEN!,
+ }),
+ };
+ }
+
+ async activate(priority: Pick) {
+ // Set up cross-service workflow
+ await this.setupIssueSync();
+ }
+
+ async setupIssueSync() {
+ // When GitHub issue is created, create Jira ticket and post to Slack
+ // When Jira ticket is updated, update GitHub issue
+ // When PR is merged, update both and notify Slack
+ }
+}
+```
+
+### State Machine Pattern
+
+Implement complex workflows with state machines:
+
+```typescript
+type WorkflowState = "pending" | "in_progress" | "review" | "complete";
+
+interface WorkflowData {
+ state: WorkflowState;
+ activityId: string;
+ metadata: Record;
+}
+
+class WorkflowAgent extends Agent {
+ async transitionTo(workflowId: string, newState: WorkflowState) {
+ const workflow = await this.get(`workflow:${workflowId}`);
+ if (!workflow) throw new Error("Workflow not found");
+
+ const oldState = workflow.state;
+
+ // Validate transition
+ if (!this.isValidTransition(oldState, newState)) {
+ throw new Error(`Invalid transition: ${oldState} -> ${newState}`);
+ }
+
+ // Execute transition logic
+ await this.onExit(workflowId, oldState);
+
+ workflow.state = newState;
+ await this.set(`workflow:${workflowId}`, workflow);
+
+ await this.onEnter(workflowId, newState);
+ }
+
+ private isValidTransition(from: WorkflowState, to: WorkflowState): boolean {
+ const transitions: Record = {
+ pending: ["in_progress"],
+ in_progress: ["review", "pending"],
+ review: ["complete", "in_progress"],
+ complete: [],
+ };
+
+ return transitions[from]?.includes(to) ?? false;
+ }
+
+ private async onEnter(workflowId: string, state: WorkflowState) {
+ switch (state) {
+ case "in_progress":
+ await this.notifyAssigned(workflowId);
+ break;
+ case "review":
+ await this.requestReview(workflowId);
+ break;
+ case "complete":
+ await this.markComplete(workflowId);
+ break;
+ }
+ }
+
+ private async onExit(workflowId: string, state: WorkflowState) {
+ // Cleanup for previous state
+ }
+}
+```
+
+---
+
+## Error Handling
+
+### Graceful Degradation
+
+Handle errors without breaking the agent:
+
+```typescript
+async activate(priority: Pick) {
+ try {
+ await this.setupWebhooks();
+ } catch (error) {
+ console.error("Failed to setup webhooks:", error);
+ // Agent still activates, just without webhooks
+ // Consider creating an activity to notify the user
+ await this.tools.plot.createActivity({
+ type: ActivityType.Note,
+ title: "⚠️ Webhook setup failed",
+ note: `Could not set up automatic syncing. Error: ${error.message}`
+ });
+ }
+
+ // Continue with other initialization
+ await this.initialSync();
+}
+```
+
+### Retry Logic
+
+Implement exponential backoff for transient failures:
+
+```typescript
+async fetchWithRetry(
+ url: string,
+ maxRetries: number = 3
+): Promise {
+ let lastError: Error;
+
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
+ try {
+ const response = await fetch(url);
+
+ if (!response.ok) {
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
+ }
+
+ return await response.json();
+ } catch (error) {
+ lastError = error as Error;
+ console.error(`Attempt ${attempt + 1} failed:`, error);
+
+ if (attempt < maxRetries - 1) {
+ // Exponential backoff: 1s, 2s, 4s
+ const delay = Math.pow(2, attempt) * 1000;
+ await new Promise(resolve => setTimeout(resolve, delay));
+ }
+ }
+ }
+
+ throw new Error(`Failed after ${maxRetries} attempts: ${lastError!.message}`);
+}
+```
+
+### Error Recovery
+
+Save state before risky operations:
+
+```typescript
+async processLargeDataset(items: Item[]) {
+ for (let i = 0; i < items.length; i++) {
+ try {
+ await this.processItem(items[i]);
+
+ // Save progress
+ await this.set("last_processed_index", i);
+ } catch (error) {
+ console.error(`Error processing item ${i}:`, error);
+
+ // Create activity for manual review
+ await this.tools.plot.createActivity({
+ type: ActivityType.Note,
+ title: `Processing error at item ${i}`,
+ note: error.message
+ });
+
+ // Continue with next item
+ continue;
+ }
+ }
+}
+
+// Resume from last checkpoint
+async resumeProcessing() {
+ const lastIndex = await this.get("last_processed_index") || 0;
+ const items = await this.get- ("items_to_process");
+
+ if (items) {
+ await this.processLargeDataset(items.slice(lastIndex + 1));
+ }
+}
+```
+
+---
+
+## Debugging and Logging
+
+### Structured Logging
+
+Use consistent log formats:
+
+```typescript
+interface LogContext {
+ agentId: string;
+ priorityId?: string;
+ operation: string;
+ [key: string]: any;
+}
+
+class MyAgent extends Agent {
+ private log(
+ level: "info" | "warn" | "error",
+ message: string,
+ context?: Partial
+ ) {
+ const logEntry = {
+ timestamp: new Date().toISOString(),
+ level,
+ message,
+ agent: this.id,
+ ...context,
+ };
+
+ console.log(JSON.stringify(logEntry));
+ }
+
+ async activate(priority: Pick) {
+ this.log("info", "Agent activating", {
+ priorityId: priority.id,
+ operation: "activate",
+ });
+
+ try {
+ await this.setupWebhooks();
+ this.log("info", "Webhooks configured successfully");
+ } catch (error) {
+ this.log("error", "Failed to setup webhooks", {
+ error: error.message,
+ stack: error.stack,
+ });
+ }
+ }
+}
+```
+
+### Debug Mode
+
+Add debug flag for verbose logging:
+
+```typescript
+class MyAgent extends Agent {
+ private get debugMode(): Promise {
+ return this.get("debug_mode").then((v) => v ?? false);
+ }
+
+ private async debug(message: string, data?: any) {
+ if (await this.debugMode) {
+ console.log(`[DEBUG] ${message}`, data || "");
+ }
+ }
+
+ async processData(data: any) {
+ await this.debug("Processing data", { itemCount: data.length });
+
+ for (const item of data) {
+ await this.debug("Processing item", item);
+ await this.processItem(item);
+ }
+ }
+}
+```
+
+### Performance Monitoring
+
+Track operation durations:
+
+```typescript
+async withTiming(
+ operation: string,
+ fn: () => Promise
+): Promise {
+ const start = Date.now();
+
+ try {
+ const result = await fn();
+ const duration = Date.now() - start;
+
+ console.log(`[PERF] ${operation}: ${duration}ms`);
+
+ return result;
+ } catch (error) {
+ const duration = Date.now() - start;
+ console.log(`[PERF] ${operation}: ${duration}ms (failed)`);
+ throw error;
+ }
+}
+
+// Usage
+await this.withTiming("sync-calendar", async () => {
+ await this.syncCalendar();
+});
+```
+
+---
+
+## Security Best Practices
+
+### Secrets Management
+
+Never hardcode secrets:
+
+```typescript
+// ❌ WRONG
+const apiKey = "sk-1234567890abcdef";
+
+// ✅ CORRECT - Use environment variables
+const apiKey = process.env.API_KEY;
+if (!apiKey) {
+ throw new Error("API_KEY environment variable is required");
+}
+```
+
+### Input Validation
+
+Validate all external input:
+
+```typescript
+async onWebhook(request: WebhookRequest) {
+ // Validate signature
+ if (!this.validateSignature(request)) {
+ console.error("Invalid webhook signature");
+ return;
+ }
+
+ // Validate schema
+ if (!this.isValidPayload(request.body)) {
+ console.error("Invalid webhook payload");
+ return;
+ }
+
+ // Process safely
+ await this.processWebhook(request.body);
+}
+
+private validateSignature(request: WebhookRequest): boolean {
+ const signature = request.headers["x-webhook-signature"];
+ const expectedSignature = this.computeSignature(request.body);
+ return signature === expectedSignature;
+}
+```
+
+### Rate Limiting
+
+Protect external APIs:
+
+```typescript
+class RateLimiter {
+ private lastRequest: number = 0;
+ private minInterval: number = 1000; // 1 request per second
+
+ async throttle(fn: () => Promise): Promise {
+ const now = Date.now();
+ const timeSinceLastRequest = now - this.lastRequest;
+
+ if (timeSinceLastRequest < this.minInterval) {
+ const delay = this.minInterval - timeSinceLastRequest;
+ await new Promise(resolve => setTimeout(resolve, delay));
+ }
+
+ this.lastRequest = Date.now();
+ return await fn();
+ }
+}
+
+// Usage
+private rateLimiter = new RateLimiter();
+
+async fetchData() {
+ return await this.rateLimiter.throttle(async () => {
+ return await fetch("https://api.example.com/data");
+ });
+}
+```
+
+---
+
+## Migration and Versioning
+
+### Version Tracking
+
+Track agent version for migrations:
+
+```typescript
+async activate(priority: Pick) {
+ await this.set("agent_version", "1.0.0");
+}
+
+async upgrade() {
+ const currentVersion = await this.get("agent_version") || "0.0.0";
+
+ if (this.compareVersions(currentVersion, "2.0.0") < 0) {
+ await this.migrateToV2();
+ }
+
+ if (this.compareVersions(currentVersion, "2.1.0") < 0) {
+ await this.migrateToV21();
+ }
+
+ await this.set("agent_version", "2.1.0");
+}
+```
+
+### Data Migration
+
+Migrate stored data structures:
+
+```typescript
+async migrateToV2() {
+ // V1 stored user data as separate fields
+ const userId = await this.get("user_id");
+ const userName = await this.get("user_name");
+ const userEmail = await this.get("user_email");
+
+ if (userId && userName && userEmail) {
+ // V2 uses a single user object
+ await this.set("user", {
+ id: userId,
+ name: userName,
+ email: userEmail
+ });
+
+ // Clean up old fields
+ await this.clear("user_id");
+ await this.clear("user_name");
+ await this.clear("user_email");
+ }
+}
+```
+
+### Breaking Changes
+
+Handle breaking changes gracefully:
+
+```typescript
+async upgrade() {
+ const version = await this.get("version") || "1.0.0";
+
+ if (version < "2.0.0") {
+ // V2 completely changed how webhooks work
+ // Clean up old webhooks
+ const oldWebhooks = await this.get("webhooks");
+ if (oldWebhooks) {
+ for (const webhook of oldWebhooks) {
+ await this.deleteOldWebhook(webhook);
+ }
+ await this.clear("webhooks");
+ }
+
+ // Set up new webhook system
+ await this.setupNewWebhooks();
+ }
+
+ await this.set("version", "2.0.0");
+}
+```
+
+---
+
+## Performance Patterns
+
+### Lazy Loading
+
+Load data only when needed:
+
+```typescript
+class MyAgent extends Agent {
+ private _config: Config | null = null;
+
+ private async getConfig(): Promise {
+ if (!this._config) {
+ this._config = await this.get("config");
+ }
+ return this._config!;
+ }
+
+ async someMethod() {
+ const config = await this.getConfig(); // Loaded once
+ // Use config...
+ }
+}
+```
+
+### Request Coalescing
+
+Combine multiple similar requests:
+
+```typescript
+private pendingUserFetches = new Map>();
+
+async getUser(userId: string): Promise {
+ // If already fetching, return existing promise
+ if (this.pendingUserFetches.has(userId)) {
+ return this.pendingUserFetches.get(userId)!;
+ }
+
+ // Start new fetch
+ const promise = this.fetchUser(userId);
+ this.pendingUserFetches.set(userId, promise);
+
+ try {
+ const user = await promise;
+ return user;
+ } finally {
+ this.pendingUserFetches.delete(userId);
+ }
+}
+```
+
+### Bulk Operations
+
+Batch database operations:
+
+```typescript
+async syncAllItems(items: Item[]) {
+ // ❌ SLOW - One at a time
+ // for (const item of items) {
+ // await this.tools.plot.createActivity({...});
+ // }
+
+ // ✅ FAST - Bulk create
+ await this.tools.plot.createActivities(
+ items.map(item => ({
+ type: ActivityType.Task,
+ title: item.title,
+ note: item.description
+ }))
+ );
+}
+```
+
+---
+
+## Next Steps
+
+- **[Runtime Environment](RUNTIME.md)** - Understanding execution constraints
+- **[Building Tools](BUILDING_TOOLS.md)** - Creating reusable tools
+- **[Built-in Tools](TOOLS_GUIDE.md)** - Comprehensive tool documentation
+- **API Reference** - Explore detailed API documentation
diff --git a/sdk/docs/BUILDING_TOOLS.md b/sdk/docs/BUILDING_TOOLS.md
new file mode 100644
index 0000000..b217e78
--- /dev/null
+++ b/sdk/docs/BUILDING_TOOLS.md
@@ -0,0 +1,827 @@
+---
+title: Building Custom Tools
+group: Guides
+---
+
+# Building Custom Tools
+
+Custom tools let you create reusable functionality that can be shared across agents or published for others to use. This guide covers everything you need to know about building tools.
+
+## Table of Contents
+
+- [Why Build Tools?](#why-build-tools)
+- [Tool Basics](#tool-basics)
+- [Tool Structure](#tool-structure)
+- [Lifecycle Methods](#lifecycle-methods)
+- [Dependencies](#dependencies)
+- [Options and Configuration](#options-and-configuration)
+- [Complete Examples](#complete-examples)
+- [Testing Tools](#testing-tools)
+- [Publishing Tools](#publishing-tools)
+- [Best Practices](#best-practices)
+
+---
+
+## Why Build Tools?
+
+Build custom tools when you need to:
+
+- **Integrate external services** - GitHub, Slack, Notion, etc.
+- **Encapsulate complex logic** - Reusable business logic
+- **Share functionality** - Between multiple agents
+- **Abstract implementation details** - Clean interfaces for common operations
+
+### Built-in vs. Custom Tools
+
+| Built-in Tools | Custom Tools |
+| ------------------------------ | ------------------------------------ |
+| Plot, Store, AI, Network, etc. | Your integrations and utilities |
+| Access to Plot internals | Built on top of built-in tools |
+| Provided by SDK | Created by you or installed from npm |
+| Always available | Declared as dependencies |
+
+---
+
+## Tool Basics
+
+Tools extend the `Tool` base class and can access other tools through dependencies.
+
+### Minimal Tool Example
+
+```typescript
+import { Tool, type ToolBuilder } from "@plotday/sdk";
+
+export class HelloTool extends Tool {
+ async sayHello(name: string): Promise {
+ return `Hello, ${name}!`;
+ }
+}
+```
+
+### Using Your Tool
+
+```typescript
+import { Agent, type ToolBuilder } from "@plotday/sdk";
+
+import { HelloTool } from "./tools/hello";
+
+export default class MyAgent extends Agent {
+ build(build: ToolBuilder) {
+ return {
+ hello: build(HelloTool),
+ };
+ }
+
+ async activate() {
+ const message = await this.tools.hello.sayHello("World");
+ console.log(message); // "Hello, World!"
+ }
+}
+```
+
+---
+
+## Tool Structure
+
+### Class Definition
+
+```typescript
+import { Tool, type ToolBuilder } from "@plotday/sdk";
+
+// Tool class with type parameter
+export class MyTool extends Tool {
+ // Constructor receives id, options, and toolShed
+ constructor(id: string, options: InferOptions, toolShed: ToolShed) {
+ super(id, options, toolShed);
+ }
+
+ // Public methods
+ async myMethod(): Promise {
+ // Implementation
+ }
+}
+```
+
+### Type Parameter
+
+The type parameter `` enables:
+
+- Type-safe options inference
+- Type-safe tool dependencies
+- Proper TypeScript autocomplete
+
+---
+
+## Lifecycle Methods
+
+Tools have lifecycle methods that run at specific times during the agent lifecycle.
+
+### preActivate(priority)
+
+Called **before** the agent's `activate()` method, depth-first.
+
+```typescript
+async preActivate(priority: Priority): Promise {
+ // Setup that needs to happen before agent activation
+ console.log("Tool preparing for activation");
+
+ // Initialize connections, validate configuration, etc.
+}
+```
+
+**Use for:**
+
+- Validating configuration
+- Setting up connections
+- Preparing resources
+
+### postActivate(priority)
+
+Called **after** the agent's `activate()` method, reverse order.
+
+```typescript
+async postActivate(priority: Priority): Promise {
+ // Finalization after agent is activated
+ console.log("Tool finalizing activation");
+
+ // Start background processes, register webhooks, etc.
+}
+```
+
+**Use for:**
+
+- Starting background processes
+- Registering webhooks
+- Final initialization
+
+### preUpgrade()
+
+Called **before** the agent's `upgrade()` method.
+
+```typescript
+async preUpgrade(): Promise {
+ // Prepare for upgrade
+ const version = await this.get("tool_version");
+
+ if (version === "1.0.0") {
+ // Migrate data
+ }
+}
+```
+
+### postUpgrade()
+
+Called **after** the agent's `upgrade()` method.
+
+```typescript
+async postUpgrade(): Promise {
+ // Finalize upgrade
+ await this.set("tool_version", "2.0.0");
+}
+```
+
+### preDeactivate()
+
+Called **before** the agent's `deactivate()` method.
+
+```typescript
+async preDeactivate(): Promise {
+ // Cleanup before deactivation
+ await this.stopBackgroundProcesses();
+}
+```
+
+### postDeactivate()
+
+Called **after** the agent's `deactivate()` method.
+
+```typescript
+async postDeactivate(): Promise {
+ // Final cleanup
+ await this.clearAll();
+}
+```
+
+### Execution Order
+
+```
+Agent Activation:
+ 1. Tool.preActivate() (deepest dependencies first)
+ 2. Agent.activate()
+ 3. Tool.postActivate() (top-level tools first)
+
+Agent Deactivation:
+ 1. Tool.preDeactivate() (deepest dependencies first)
+ 2. Agent.deactivate()
+ 3. Tool.postDeactivate() (top-level tools first)
+```
+
+---
+
+## Dependencies
+
+Tools can depend on other tools, including built-in tools.
+
+### Declaring Dependencies
+
+```typescript
+import { Tool, type ToolBuilder } from "@plotday/sdk";
+import { Network } from "@plotday/sdk/tools/network";
+import { Store } from "@plotday/sdk/tools/store";
+
+export class GitHubTool extends Tool {
+ // Declare dependencies
+ build(build: ToolBuilder) {
+ return {
+ network: build(Network, {
+ urls: ["https://api.github.com/*"],
+ }),
+ store: build(Store),
+ };
+ }
+
+ // Access dependencies
+ async getRepository(owner: string, repo: string) {
+ const response = await fetch(
+ `https://api.github.com/repos/${owner}/${repo}`
+ );
+ return await response.json();
+ }
+}
+```
+
+### Accessing Dependencies
+
+Use `this.tools` to access declared dependencies:
+
+```typescript
+async fetchData() {
+ // Tools are fully typed
+ const data = await this.tools.network.fetch("https://api.example.com/data");
+ await this.tools.store.set("cached_data", data);
+}
+```
+
+### Built-in Tool Access
+
+Tools have direct access to Store, Tasks, and Callbacks methods:
+
+```typescript
+export class MyTool extends Tool {
+ async doWork() {
+ // Store
+ await this.set("key", "value");
+ const value = await this.get("key");
+
+ // Tasks
+ const callback = await this.callback("processData");
+ await this.runTask(callback);
+
+ // Callbacks
+ await this.deleteCallback(callback);
+ }
+}
+```
+
+---
+
+## Options and Configuration
+
+Tools can accept configuration options when declared.
+
+### Defining Options
+
+```typescript
+import { Tool, type ToolBuilder, type InferOptions } from "@plotday/sdk";
+
+export class SlackTool extends Tool {
+ // Define static Options type
+ static Options = {
+ workspaceId: "" as string,
+ defaultChannel?: "" as string | undefined,
+ };
+
+ // Access via this.options
+ async postMessage(message: string, channel?: string) {
+ const targetChannel = channel || this.options.defaultChannel;
+
+ if (!targetChannel) {
+ throw new Error("No channel specified");
+ }
+
+ console.log(`Posting to ${targetChannel} in ${this.options.workspaceId}`);
+ // Post message...
+ }
+}
+```
+
+### Using Options
+
+```typescript
+build(build: ToolBuilder) {
+ return {
+ slack: build(SlackTool, {
+ workspaceId: "T1234567",
+ defaultChannel: "#general"
+ }),
+ };
+}
+```
+
+### Required vs. Optional Options
+
+```typescript
+static Options = {
+ // Required - no default value, not undefined
+ apiKey: "" as string,
+ workspaceId: "" as string,
+
+ // Optional - has undefined as possible value
+ defaultChannel?: "" as string | undefined,
+ timeout?: 0 as number | undefined,
+
+ // Optional with default
+ retryCount: 3 as number,
+};
+```
+
+---
+
+## Complete Examples
+
+### Example 1: GitHub Integration Tool
+
+A complete GitHub integration with webhooks and issue management.
+
+```typescript
+import { type Priority, Tool, type ToolBuilder } from "@plotday/sdk";
+import { ActivityLinkType, ActivityType } from "@plotday/sdk";
+import { Network, type WebhookRequest } from "@plotday/sdk/tools/network";
+import { Plot } from "@plotday/sdk/tools/plot";
+
+export class GitHubTool extends Tool {
+ static Options = {
+ owner: "" as string,
+ repo: "" as string,
+ token: "" as string,
+ };
+
+ build(build: ToolBuilder) {
+ return {
+ network: build(Network, {
+ urls: ["https://api.github.com/*"],
+ }),
+ plot: build(Plot),
+ };
+ }
+
+ async postActivate(priority: Priority): Promise {
+ // Set up webhook for issue updates
+ const webhookUrl = await this.tools.network.createWebhook("onIssueUpdate", {
+ priorityId: priority.id,
+ });
+
+ await this.set("webhook_url", webhookUrl);
+
+ // Register webhook with GitHub
+ await this.registerWebhook(webhookUrl);
+ }
+
+ async preDeactivate(): Promise {
+ // Cleanup webhook
+ const webhookUrl = await this.get("webhook_url");
+ if (webhookUrl) {
+ await this.unregisterWebhook(webhookUrl);
+ await this.tools.network.deleteWebhook(webhookUrl);
+ }
+ }
+
+ async getIssues(): Promise {
+ const response = await fetch(
+ `https://api.github.com/repos/${this.options.owner}/${this.options.repo}/issues`,
+ {
+ headers: {
+ Authorization: `Bearer ${this.options.token}`,
+ Accept: "application/vnd.github.v3+json",
+ },
+ }
+ );
+
+ return await response.json();
+ }
+
+ async syncIssues(): Promise {
+ const issues = await this.getIssues();
+
+ for (const issue of issues) {
+ await this.tools.plot.createActivity({
+ type: ActivityType.Task,
+ title: issue.title,
+ note: issue.body,
+ meta: {
+ github_issue_id: issue.id.toString(),
+ github_number: issue.number.toString(),
+ },
+ links: [
+ {
+ type: ActivityLinkType.external,
+ title: "View on GitHub",
+ url: issue.html_url,
+ },
+ ],
+ });
+ }
+ }
+
+ async onIssueUpdate(
+ request: WebhookRequest,
+ context: { priorityId: string }
+ ): Promise {
+ const { action, issue } = request.body;
+
+ if (action === "opened") {
+ // Create new activity for new issue
+ await this.tools.plot.createActivity({
+ type: ActivityType.Task,
+ title: issue.title,
+ meta: {
+ github_issue_id: issue.id.toString(),
+ },
+ });
+ } else if (action === "closed") {
+ // Mark activity as done
+ const activity = await this.tools.plot.getActivityByMeta({
+ github_issue_id: issue.id.toString(),
+ });
+
+ if (activity) {
+ await this.tools.plot.updateActivity(activity.id, {
+ doneAt: new Date(),
+ });
+ }
+ }
+ }
+
+ private async registerWebhook(url: string): Promise {
+ await fetch(
+ `https://api.github.com/repos/${this.options.owner}/${this.options.repo}/hooks`,
+ {
+ method: "POST",
+ headers: {
+ Authorization: `Bearer ${this.options.token}`,
+ Accept: "application/vnd.github.v3+json",
+ },
+ body: JSON.stringify({
+ config: { url, content_type: "json" },
+ events: ["issues"],
+ }),
+ }
+ );
+ }
+
+ private async unregisterWebhook(url: string): Promise {
+ // Implementation to remove webhook from GitHub
+ }
+}
+```
+
+### Example 2: Slack Notification Tool
+
+A tool for sending Slack notifications.
+
+```typescript
+import { Tool, type ToolBuilder } from "@plotday/sdk";
+import { Network } from "@plotday/sdk/tools/network";
+
+export class SlackTool extends Tool {
+ static Options = {
+ webhookUrl: "" as string,
+ defaultChannel?: "" as string | undefined,
+ };
+
+ build(build: ToolBuilder) {
+ return {
+ network: build(Network, {
+ urls: ["https://hooks.slack.com/*"]
+ }),
+ };
+ }
+
+ async sendMessage(options: {
+ text: string;
+ channel?: string;
+ username?: string;
+ }): Promise {
+ const payload = {
+ text: options.text,
+ channel: options.channel || this.options.defaultChannel,
+ username: options.username || "Plot Bot"
+ };
+
+ const response = await fetch(this.options.webhookUrl, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify(payload)
+ });
+
+ if (!response.ok) {
+ throw new Error(`Slack API error: ${response.statusText}`);
+ }
+ }
+
+ async sendAlert(message: string): Promise {
+ await this.sendMessage({
+ text: `:warning: ${message}`,
+ channel: "#alerts"
+ });
+ }
+}
+```
+
+---
+
+## Testing Tools
+
+### Unit Testing
+
+```typescript
+import { beforeEach, describe, expect, it } from "vitest";
+
+import { GitHubTool } from "./github-tool";
+
+describe("GitHubTool", () => {
+ let tool: GitHubTool;
+
+ beforeEach(() => {
+ tool = new GitHubTool(
+ "test-id",
+ {
+ owner: "test-owner",
+ repo: "test-repo",
+ token: "test-token",
+ },
+ mockToolShed
+ );
+ });
+
+ it("fetches issues", async () => {
+ const issues = await tool.getIssues();
+ expect(issues).toBeInstanceOf(Array);
+ });
+
+ it("validates configuration", () => {
+ expect(tool.options.owner).toBe("test-owner");
+ expect(tool.options.repo).toBe("test-repo");
+ });
+});
+```
+
+### Integration Testing
+
+Test your tool with a real agent:
+
+```typescript
+import { Agent, type ToolBuilder } from "@plotday/sdk";
+import { Plot } from "@plotday/sdk/tools/plot";
+
+import { GitHubTool } from "./github-tool";
+
+class TestAgent extends Agent {
+ build(build: ToolBuilder) {
+ return {
+ plot: build(Plot),
+ github: build(GitHubTool, {
+ owner: "plotday",
+ repo: "plot",
+ token: process.env.GITHUB_TOKEN!,
+ }),
+ };
+ }
+
+ async activate() {
+ // Test syncing
+ await this.tools.github.syncIssues();
+ }
+}
+```
+
+---
+
+## Publishing Tools
+
+### Package Structure
+
+```
+my-plot-tool/
+├── src/
+│ └── index.ts # Tool implementation
+├── package.json
+├── tsconfig.json
+├── README.md
+└── LICENSE
+```
+
+### package.json
+
+```json
+{
+ "name": "@mycompany/plot-github-tool",
+ "version": "1.0.0",
+ "description": "GitHub integration tool for Plot agents",
+ "main": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "scripts": {
+ "build": "tsc",
+ "test": "vitest"
+ },
+ "peerDependencies": {
+ "@plotday/sdk": "^0.16.0"
+ },
+ "devDependencies": {
+ "@plotday/sdk": "^0.16.0",
+ "typescript": "^5.0.0"
+ }
+}
+```
+
+### Publishing
+
+```bash
+# Build
+npm run build
+
+# Test
+npm test
+
+# Publish
+npm publish
+```
+
+### Documentation
+
+Include comprehensive README with:
+
+- Installation instructions
+- Configuration options
+- Usage examples
+- API reference
+
+---
+
+## Best Practices
+
+### 1. Single Responsibility
+
+Each tool should have a single, well-defined purpose:
+
+```typescript
+// ✅ GOOD - Focused on GitHub
+class GitHubTool extends Tool {
+ async getIssues() {
+ /* ... */
+ }
+ async createIssue() {
+ /* ... */
+ }
+}
+
+// ❌ BAD - Mixed concerns
+class IntegrationTool extends Tool {
+ async getGitHubIssues() {
+ /* ... */
+ }
+ async sendSlackMessage() {
+ /* ... */
+ }
+ async createJiraTicket() {
+ /* ... */
+ }
+}
+```
+
+### 2. Type Safety
+
+Use TypeScript features for type safety:
+
+```typescript
+export interface GitHubIssue {
+ id: number;
+ title: string;
+ body: string;
+ state: "open" | "closed";
+}
+
+export class GitHubTool extends Tool {
+ async getIssues(): Promise {
+ // Return type is enforced
+ }
+}
+```
+
+### 3. Error Handling
+
+Handle errors gracefully:
+
+```typescript
+async fetchData(): Promise {
+ try {
+ const response = await fetch(this.apiUrl);
+
+ if (!response.ok) {
+ console.error(`API error: ${response.status}`);
+ return null;
+ }
+
+ return await response.json();
+ } catch (error) {
+ console.error("Network error:", error);
+ return null;
+ }
+}
+```
+
+### 4. Configuration Validation
+
+Validate options in preActivate:
+
+```typescript
+async preActivate(priority: Priority): Promise {
+ if (!this.options.apiKey) {
+ throw new Error("API key is required");
+ }
+
+ if (!this.options.workspaceId.startsWith("T")) {
+ throw new Error("Invalid workspace ID format");
+ }
+}
+```
+
+### 5. Resource Cleanup
+
+Always clean up resources in deactivation:
+
+```typescript
+async postDeactivate(): Promise {
+ // Cancel pending tasks
+ await this.cancelAllTasks();
+
+ // Delete callbacks
+ await this.deleteAllCallbacks();
+
+ // Clear stored data
+ await this.clearAll();
+}
+```
+
+### 6. Avoid Instance State
+
+Use Store instead of instance variables:
+
+```typescript
+// ❌ WRONG - Instance state doesn't persist
+class MyTool extends Tool {
+ private cache: Map = new Map();
+}
+
+// ✅ CORRECT - Use Store
+class MyTool extends Tool {
+ async getFromCache(key: string) {
+ return await this.get(`cache:${key}`);
+ }
+
+ async setInCache(key: string, value: any) {
+ await this.set(`cache:${key}`, value);
+ }
+}
+```
+
+### 7. Document Your API
+
+Add JSDoc comments for documentation:
+
+````typescript
+/**
+ * Fetches all open issues from the GitHub repository.
+ *
+ * @returns Promise resolving to array of GitHub issues
+ * @throws Error if GitHub API is unavailable
+ *
+ * @example
+ * ```typescript
+ * const issues = await this.tools.github.getIssues();
+ * ```
+ */
+async getIssues(): Promise {
+ // Implementation
+}
+````
+
+---
+
+## Next Steps
+
+- **[Built-in Tools Guide](TOOLS_GUIDE.md)** - Learn from built-in tool patterns
+- **[Advanced Topics](ADVANCED.md)** - Complex tool patterns
+- **API Reference** - Explore the Tool class API
diff --git a/sdk/docs/CLI_REFERENCE.md b/sdk/docs/CLI_REFERENCE.md
new file mode 100644
index 0000000..b2e5bee
--- /dev/null
+++ b/sdk/docs/CLI_REFERENCE.md
@@ -0,0 +1,390 @@
+---
+title: CLI Reference
+group: Guides
+---
+
+# CLI Reference
+
+Complete reference for the Plot CLI (`plot` command).
+
+## Table of Contents
+
+- [Installation](#installation)
+- [Authentication](#authentication)
+- [Agent Commands](#agent-commands)
+- [Priority Commands](#priority-commands)
+- [Global Options](#global-options)
+
+---
+
+## Installation
+
+The Plot CLI is included with the SDK:
+
+```bash
+# Run directly with npx
+npx @plotday/sdk [command]
+
+# Or install globally
+npm install -g @plotday/sdk
+plot [command]
+```
+
+---
+
+## Authentication
+
+### plot login
+
+Authenticate with Plot to generate an API token.
+
+```bash
+plot login
+```
+
+This will:
+
+1. Open your browser to the Plot authentication page
+2. After authentication, save your API token locally
+3. Enable deploying and managing agents
+
+**Token Storage**: The token is stored in `~/.plot/config.json`
+
+---
+
+## Agent Commands
+
+### plot agent create
+
+Scaffold a new agent project with TypeScript.
+
+```bash
+plot agent create [options]
+```
+
+**Options:**
+
+- `--name ` - Package name (kebab-case)
+- `--display-name ` - Human-readable display name
+- `--dir ` - Output directory (default: current directory)
+
+**Example:**
+
+```bash
+plot agent create --name my-calendar-agent --display-name "My Calendar Agent"
+```
+
+**Creates:**
+
+```
+my-calendar-agent/
+├── src/
+│ └── index.ts
+├── package.json
+├── tsconfig.json
+└── plot-agent.json
+```
+
+---
+
+### plot agent generate
+
+Generate TypeScript code from a natural language `plot-agent.md` specification.
+
+```bash
+plot agent generate [options]
+```
+
+**Options:**
+
+- `--input ` - Path to plot-agent.md (default: `./plot-agent.md`)
+- `--output ` - Output directory (default: `./src`)
+- `--overwrite` - Overwrite existing files
+
+**Example:**
+
+```bash
+plot agent generate --input ./my-spec.md --output ./src
+```
+
+---
+
+### plot agent lint
+
+Check agent code for errors without deploying.
+
+```bash
+plot agent lint [options]
+```
+
+**Options:**
+
+- `--dir ` - Agent directory (default: current directory)
+
+**Example:**
+
+```bash
+plot agent lint --dir ./my-agent
+```
+
+---
+
+### plot agent deploy
+
+Deploy an agent to Plot.
+
+```bash
+plot agent deploy [options]
+```
+
+**Options:**
+
+- `--agent-id ` - Update existing agent (creates new if not specified)
+- `--name ` - Agent name
+- `--description ` - Agent description
+- `--source ` - Source directory (default: `./src`)
+- `--env ` - Environment (default: `production`)
+- `--dry-run` - Validate without deploying
+
+**Behavior:**
+
+- If `plot-agent.md` exists: Generates code and deploys in one step
+- Otherwise: Deploys compiled TypeScript from `src/`
+
+**Example:**
+
+```bash
+# Deploy new agent
+plot agent deploy
+
+# Update existing agent
+plot agent deploy --agent-id ag_1234567890
+
+# Dry run
+plot agent deploy --dry-run
+```
+
+---
+
+### plot agent link
+
+Link (activate) an agent to a priority.
+
+```bash
+plot agent link [options]
+```
+
+**Options:**
+
+- `--agent-id ` - Agent to link (required)
+- `--priority-id ` - Priority to link to (required)
+
+**Example:**
+
+```bash
+plot agent link --agent-id ag_1234567890 --priority-id pr_0987654321
+```
+
+After linking, the agent's `activate()` method will be called for that priority.
+
+---
+
+## Priority Commands
+
+### plot priority list
+
+List all priorities for the authenticated user.
+
+```bash
+plot priority list
+```
+
+**Output:**
+
+```
+pr_0987654321 Work
+ pr_1111111111 Project A
+ pr_2222222222 Project B
+pr_3333333333 Personal
+```
+
+---
+
+### plot priority create
+
+Create a new priority.
+
+```bash
+plot priority create [options]
+```
+
+**Options:**
+
+- `--name ` - Priority name (required)
+- `--parent-id ` - Parent priority ID (optional)
+
+**Example:**
+
+```bash
+# Create top-level priority
+plot priority create --name "Work"
+
+# Create nested priority
+plot priority create --name "Project A" --parent-id pr_0987654321
+```
+
+---
+
+## Global Options
+
+These options are available for all commands:
+
+- `--help`, `-h` - Show help for command
+- `--version`, `-v` - Show CLI version
+- `--verbose` - Enable verbose logging
+- `--config ` - Use custom config file (default: `~/.plot/config.json`)
+
+**Example:**
+
+```bash
+plot agent deploy --verbose
+plot --version
+```
+
+---
+
+## Configuration File
+
+The CLI stores configuration in `~/.plot/config.json`:
+
+```json
+{
+ "auth": {
+ "token": "your-api-token",
+ "userId": "user_1234567890"
+ },
+ "defaults": {
+ "environment": "production"
+ }
+}
+```
+
+### Customizing Defaults
+
+Edit the config file to set default options:
+
+```json
+{
+ "defaults": {
+ "environment": "staging",
+ "agentSourceDir": "./dist"
+ }
+}
+```
+
+---
+
+## Environment Variables
+
+Configure the CLI using environment variables:
+
+- `PLOT_API_TOKEN` - API authentication token
+- `PLOT_API_URL` - API endpoint (default: `https://api.plot.day`)
+- `PLOT_CONFIG_PATH` - Custom config file path
+
+**Example:**
+
+```bash
+export PLOT_API_TOKEN=your-token
+plot agent deploy
+```
+
+---
+
+## Common Workflows
+
+### Create and Deploy a New Agent
+
+```bash
+# 1. Create project
+plot agent create --name my-agent
+
+# 2. Navigate to directory
+cd my-agent
+
+# 3. Implement agent
+# Edit src/index.ts
+
+# 4. Login (if not already authenticated)
+plot login
+
+# 5. Deploy
+npm run deploy
+```
+
+### Update an Existing Agent
+
+```bash
+# 1. Make changes to src/index.ts
+
+# 2. Build
+npm run build
+
+# 3. Deploy update
+plot agent deploy --agent-id ag_1234567890
+```
+
+### No-Code Agent Deployment
+
+```bash
+# 1. Create plot-agent.md
+# Describe your agent in plain English
+
+# 2. Login
+plot login
+
+# 3. Deploy directly from spec
+plot agent deploy
+```
+
+---
+
+## Troubleshooting
+
+### Authentication Issues
+
+```bash
+# Clear saved token
+rm ~/.plot/config.json
+
+# Login again
+plot login
+```
+
+### Build Errors
+
+```bash
+# Check for TypeScript errors
+npm run build
+
+# Or use lint command
+plot agent lint
+```
+
+### Deployment Failures
+
+```bash
+# Try dry run first
+plot agent deploy --dry-run
+
+# Enable verbose logging
+plot agent deploy --verbose
+```
+
+---
+
+## Next Steps
+
+- **[Getting Started](GETTING_STARTED.md)** - Learn how to build agents
+- **[Core Concepts](CORE_CONCEPTS.md)** - Understand the agent architecture
+- **[Built-in Tools](TOOLS_GUIDE.md)** - Explore available tools
diff --git a/sdk/docs/CORE_CONCEPTS.md b/sdk/docs/CORE_CONCEPTS.md
new file mode 100644
index 0000000..72f2618
--- /dev/null
+++ b/sdk/docs/CORE_CONCEPTS.md
@@ -0,0 +1,494 @@
+---
+title: Core Concepts
+group: Guides
+---
+
+# Core Concepts
+
+Understanding these core concepts will help you build effective Plot agents.
+
+## Table of Contents
+
+- [Agents](#agents)
+- [Tools](#tools)
+- [Priorities](#priorities)
+- [Activities](#activities)
+- [Lifecycle Methods](#lifecycle-methods)
+- [Best Practices](#best-practices)
+
+---
+
+## Agents
+
+Agents are the core building blocks of Plot automation. They implement integrations and automations that help organize and prioritize your work.
+
+### What is an Agent?
+
+An agent is a class that:
+
+- Extends the `Agent` base class
+- Declares tool dependencies in the `build()` method
+- Responds to lifecycle events (`activate`, `deactivate`, `upgrade`)
+- Can process activities and create new ones
+
+### Agent Anatomy
+
+```typescript
+import { Agent, type Priority, type ToolBuilder } from "@plotday/sdk";
+import { Plot } from "@plotday/sdk/tools/plot";
+
+export default class MyAgent extends Agent {
+ // 1. Declare dependencies
+ build(build: ToolBuilder) {
+ return {
+ plot: build(Plot),
+ };
+ }
+
+ // 2. Initialize on activation
+ async activate(priority: Pick) {
+ // Setup code - runs once when agent is added to a priority
+ }
+
+ // 3. Handle lifecycle events
+ async upgrade() {
+ // Runs when a new version is deployed
+ }
+
+ async deactivate() {
+ // Cleanup - runs when agent is removed
+ }
+}
+```
+
+### When to Use Agents
+
+Use agents for:
+
+- **Integrations** - Connecting external services (Google Calendar, GitHub, Slack)
+- **Automations** - Automatic task creation, reminders, status updates
+- **Data Processing** - Analyzing and organizing activities
+- **Notifications** - Sending alerts based on conditions
+
+---
+
+## Tools
+
+Tools provide functionality to agents. They encapsulate reusable capabilities and can be composed together.
+
+### Types of Tools
+
+#### 1. Built-in Tools
+
+Core Plot functionality provided by the SDK:
+
+- **Plot** - Create and manage activities and priorities
+- **Store** - Persistent key-value storage
+- **Integrations** - OAuth authentication
+- **Tasks** - Background task execution
+- **Network** - HTTP access and webhooks
+- **Callbacks** - Persistent function references
+- **AI** - Language model integration
+
+See the [Built-in Tools Guide](TOOLS_GUIDE.md) for complete documentation.
+
+#### 2. Custom Tools
+
+Tools you create or install from npm packages:
+
+- **External Service Integrations** - Google Calendar, Slack, GitHub
+- **Data Processors** - Text analysis, image processing
+- **Utilities** - Date formatting, validation
+
+See [Building Custom Tools](BUILDING_TOOLS.md) to create your own.
+
+### Declaring Tool Dependencies
+
+Use the `build()` method to declare which tools your agent needs:
+
+```typescript
+build(build: ToolBuilder) {
+ return {
+ plot: build(Plot),
+ store: build(Store),
+ calendar: build(GoogleCalendar, {
+ // Tool-specific options
+ defaultCalendar: "primary"
+ }),
+ };
+}
+```
+
+### Accessing Tools
+
+Access your tools via `this.tools`:
+
+```typescript
+async activate(priority: Pick) {
+ // Tools are fully typed
+ await this.tools.plot.createActivity({
+ type: ActivityType.Note,
+ title: "Hello from my agent"
+ });
+}
+```
+
+### Direct Access Methods
+
+Some tool methods are available directly on the Agent class for convenience:
+
+```typescript
+// Store
+await this.get("key");
+await this.set("key", value);
+await this.clear("key");
+
+// Tasks
+await this.runTask(callback);
+await this.cancelTask(token);
+
+// Callbacks
+await this.callback("methodName", ...args);
+await this.run(callbackToken);
+```
+
+---
+
+## Priorities
+
+Priorities are contexts that organize activities. Think of them like projects or focus areas.
+
+### Priority Hierarchy
+
+Priorities can be nested to create hierarchies:
+
+```
+Work
+├── Project A
+│ ├── Backend
+│ └── Frontend
+└── Project B
+```
+
+### Creating Priorities
+
+```typescript
+// Top-level priority
+const work = await this.tools.plot.createPriority({
+ title: "Work",
+});
+
+// Nested priority
+const projectA = await this.tools.plot.createPriority({
+ title: "Project A",
+ parentId: work.id,
+});
+```
+
+### Agent Activation
+
+Agents are activated within a specific priority. When activated, the agent has access to that priority and all its children.
+
+```typescript
+async activate(priority: Pick) {
+ // This agent is now active for this priority
+ // It can create activities, set up webhooks, etc.
+}
+```
+
+---
+
+## Activities
+
+Activities are the core data type in Plot, representing tasks, events, and notes.
+
+### Activity Types
+
+- **Note** - Information without actionable requirements
+- **Task** - Actionable items that can be completed
+- **Event** - Scheduled occurrences with start/end times
+
+```typescript
+import { ActivityType } from "@plotday/sdk";
+
+// Note
+await this.tools.plot.createActivity({
+ type: ActivityType.Note,
+ title: "Meeting notes from sync",
+});
+
+// Task
+await this.tools.plot.createActivity({
+ type: ActivityType.Task,
+ title: "Review pull request",
+ doneAt: null, // null = not done
+});
+
+// Event
+await this.tools.plot.createActivity({
+ type: ActivityType.Event,
+ title: "Team standup",
+ start: new Date("2025-02-01T10:00:00Z"),
+ end: new Date("2025-02-01T10:30:00Z"),
+});
+```
+
+### Activity Properties
+
+```typescript
+type Activity = {
+ id: string; // Unique identifier
+ type: ActivityType; // Note, Task, or Event
+ title: string | null; // Display title
+ note: string | null; // Additional details
+ start: Date | null; // Event start time
+ end: Date | null; // Event end time
+ doneAt: Date | null; // Task completion time
+ links: ActivityLink[]; // Action links
+ tags: Record; // Tag assignments
+ // ... and more
+};
+```
+
+### Activity Links
+
+Links enable user interaction with activities:
+
+```typescript
+import { ActivityLinkType } from "@plotday/sdk";
+
+await this.tools.plot.createActivity({
+ type: ActivityType.Task,
+ title: "Fix bug #123",
+ links: [
+ {
+ type: ActivityLinkType.external,
+ title: "View Issue",
+ url: "https://github.com/org/repo/issues/123",
+ },
+ {
+ type: ActivityLinkType.callback,
+ title: "Mark as Fixed",
+ callback: await this.callback("markAsFixed", "123"),
+ },
+ ],
+});
+```
+
+**Link Types:**
+
+- **external** - Opens URL in browser
+- **auth** - Initiates OAuth flow
+- **hidden** - Not visible to users (for tracking)
+- **callback** - Triggers agent method when clicked
+
+---
+
+## Lifecycle Methods
+
+Agents have several lifecycle methods that are called at specific times.
+
+### activate(priority)
+
+Called when the agent is first activated for a priority.
+
+**Use for:**
+
+- Creating initial activities
+- Setting up webhooks
+- Initializing state
+- Requesting authentication
+
+```typescript
+async activate(priority: Pick) {
+ // Create welcome message
+ await this.tools.plot.createActivity({
+ type: ActivityType.Note,
+ title: "Calendar sync is now active"
+ });
+
+ // Set up webhook
+ const webhookUrl = await this.tools.network.createWebhook("onUpdate");
+ await this.set("webhook_url", webhookUrl);
+}
+```
+
+### upgrade()
+
+Called when a new version of your agent is deployed to an existing priority.
+
+**Use for:**
+
+- Migrating data structures
+- Updating webhook configurations
+- Adding new features to existing installations
+
+```typescript
+async upgrade() {
+ // Check version and migrate
+ const version = await this.get("version");
+
+ if (!version || version < "2.0.0") {
+ // Migrate old data format
+ const oldData = await this.get("old_key");
+ await this.set("new_key", transformData(oldData));
+ await this.clear("old_key");
+ }
+
+ await this.set("version", "2.0.0");
+}
+```
+
+### deactivate()
+
+Called when the agent is removed from a priority.
+
+**Use for:**
+
+- Removing webhooks
+- Cleanup of external resources
+- Final data operations
+
+```typescript
+async deactivate() {
+ // Clean up webhook
+ const webhookUrl = await this.get("webhook_url");
+ if (webhookUrl) {
+ await this.tools.network.deleteWebhook(webhookUrl);
+ }
+
+ // Clean up stored data
+ await this.clearAll();
+}
+```
+
+---
+
+## Best Practices
+
+### 1. State Management
+
+Use the Store tool for persistent state, not instance variables:
+
+```typescript
+// ❌ WRONG - Instance variables don't persist
+class MyAgent extends Agent {
+ private syncToken: string; // This will be lost!
+}
+
+// ✅ CORRECT - Use Store
+class MyAgent extends Agent {
+ async getSyncToken() {
+ return await this.get("sync_token");
+ }
+
+ async setSyncToken(token: string) {
+ await this.set("sync_token", token);
+ }
+}
+```
+
+### 2. Error Handling
+
+Always handle errors gracefully:
+
+```typescript
+async activate(priority: Pick) {
+ try {
+ await this.tools.plot.createActivity({
+ type: ActivityType.Note,
+ title: "Agent activated"
+ });
+ } catch (error) {
+ console.error("Failed to create activity:", error);
+ // Agent activation continues even if this fails
+ }
+}
+```
+
+### 3. Batch Long Operations
+
+Break long-running operations into batches:
+
+```typescript
+async startSync() {
+ const callback = await this.callback("syncBatch", { page: 1 });
+ await this.runTask(callback);
+}
+
+async syncBatch(args: any, context: { page: number }) {
+ // Process one page
+ const hasMore = await processPage(context.page);
+
+ if (hasMore) {
+ // Queue next batch
+ const callback = await this.callback("syncBatch", {
+ page: context.page + 1
+ });
+ await this.runTask(callback);
+ }
+}
+```
+
+See [Runtime Environment](RUNTIME.md) for more details.
+
+### 4. Type Safety
+
+Leverage TypeScript for type safety:
+
+```typescript
+// Define interfaces for stored data
+interface SyncState {
+ lastSync: string;
+ token: string;
+ status: "active" | "paused";
+}
+
+async getSyncState(): Promise {
+ return await this.get("sync_state");
+}
+```
+
+### 5. Tool Composition
+
+Build complex functionality by composing tools:
+
+```typescript
+build(build: ToolBuilder) {
+ return {
+ plot: build(Plot),
+ network: build(Network, {
+ urls: ["https://api.service.com/*"]
+ }),
+ auth: build(Integrations),
+ ai: build(AI)
+ };
+}
+```
+
+### 6. Clear Activity Titles
+
+Make activity titles clear and actionable:
+
+```typescript
+// ❌ Vague
+await this.tools.plot.createActivity({
+ type: ActivityType.Task,
+ title: "Thing",
+});
+
+// ✅ Clear
+await this.tools.plot.createActivity({
+ type: ActivityType.Task,
+ title: "Review pull request #123 for authentication fix",
+});
+```
+
+---
+
+## Next Steps
+
+- **[Built-in Tools Guide](TOOLS_GUIDE.md)** - Learn about Plot, Store, AI, and more
+- **[Building Custom Tools](BUILDING_TOOLS.md)** - Create reusable tools
+- **[Runtime Environment](RUNTIME.md)** - Understand execution constraints
+- **[Advanced Topics](ADVANCED.md)** - Complex patterns and techniques
diff --git a/sdk/docs/GETTING_STARTED.md b/sdk/docs/GETTING_STARTED.md
new file mode 100644
index 0000000..7b9c787
--- /dev/null
+++ b/sdk/docs/GETTING_STARTED.md
@@ -0,0 +1,260 @@
+---
+title: Getting Started
+group: Guides
+---
+
+# Getting Started with Plot Agents
+
+This guide will walk you through creating your first Plot agent. There are two ways to build agents: with natural language (no code) or with TypeScript code for maximum flexibility.
+
+## Choose Your Path
+
+- **[No-Code Agents](#no-code-agents)** - Perfect for non-developers or rapid prototyping
+- **[Developer Agents](#developer-agents)** - Full control with TypeScript
+
+---
+
+## No-Code Agents
+
+Create agents using natural language descriptions - no programming required!
+
+### Step 1: Create a plot-agent.md File
+
+Create a file named `plot-agent.md` in your project directory and describe what you want your agent to do:
+
+```markdown
+# My Calendar Agent
+
+I want an agent that:
+
+- Syncs my Google Calendar events into Plot as activities
+- Creates tasks for upcoming meetings
+- Sends me a reminder 10 minutes before each meeting
+- Updates activity status when meetings are completed
+```
+
+**Be specific about:**
+
+- **Data sources** - Which services to connect (Google Calendar, GitHub, Slack, etc.)
+- **Actions** - What the agent should do (create tasks, send notifications, update status)
+- **Triggers** - When actions should happen (on new events, on schedule, when activities change)
+
+### Step 2: Deploy Your Agent
+
+You'll need a [Plot account](https://plot.day) to deploy agents.
+
+```bash
+# Login to Plot
+npx @plotday/sdk login
+
+# Deploy directly from your spec
+npx @plotday/sdk agent deploy
+```
+
+That's it! Your agent is now live in Plot.
+
+### Optional: Generate Code First
+
+If you want to review or customize the generated code before deploying:
+
+```bash
+# Generate TypeScript code from your spec
+npx @plotday/sdk agent generate
+
+# Review and edit the generated src/index.ts
+# Then deploy
+npx @plotday/sdk agent deploy
+```
+
+The `generate` command creates a complete TypeScript agent that you can modify and extend.
+
+---
+
+## Developer Agents
+
+Build agents with full control using TypeScript.
+
+### Step 1: Create a New Agent Project
+
+Use the Plot CLI to scaffold a new agent:
+
+```bash
+npx @plotday/sdk agent create
+# or
+yarn dlx @plotday/sdk agent create
+# or
+pnpm dlx @plotday/sdk agent create
+```
+
+You'll be prompted for:
+
+- **Package name** (kebab-case, e.g., `my-calendar-agent`)
+- **Display name** (human-readable, e.g., "My Calendar Agent")
+
+This creates a new directory with:
+
+```
+my-calendar-agent/
+├── src/
+│ └── index.ts # Your agent code
+├── package.json
+├── tsconfig.json
+└── plot-agent.json # Agent configuration
+```
+
+### Step 2: Implement Your Agent
+
+Edit `src/index.ts` to add your agent logic:
+
+```typescript
+import {
+ type Activity,
+ ActivityType,
+ Agent,
+ type Priority,
+ type ToolBuilder,
+} from "@plotday/sdk";
+import { Plot } from "@plotday/sdk/tools/plot";
+
+export default class MyAgent extends Agent {
+ // Declare tool dependencies
+ build(build: ToolBuilder) {
+ return {
+ plot: build(Plot),
+ };
+ }
+
+ // Called when the agent is activated for a priority
+ async activate(priority: Pick) {
+ await this.tools.plot.createActivity({
+ type: ActivityType.Note,
+ title: "Welcome! Your agent is now active.",
+ });
+ }
+
+ // Called when an activity is routed to this agent
+ async activity(activity: Activity) {
+ console.log("Processing activity:", activity.title);
+ }
+}
+```
+
+### Step 3: Test Locally
+
+Build and check for errors:
+
+```bash
+npm run build
+# or
+pnpm build
+```
+
+### Step 4: Deploy
+
+You'll need a [Plot account](https://plot.day) to deploy agents.
+
+```bash
+# Login to Plot
+npm run plot login
+
+# Deploy your agent
+npm run deploy
+```
+
+Your agent is now deployed and ready to activate in Plot!
+
+---
+
+## Understanding the Project Structure
+
+### Agent File (src/index.ts)
+
+Your agent extends the `Agent` class and implements:
+
+- **`build()`** - Declares tool dependencies
+- **`activate()`** - Initialization when added to a priority
+- **`deactivate()`** - Cleanup when removed from a priority
+- **`upgrade()`** - Migration when deploying a new version
+
+### Configuration (plot-agent.json)
+
+Contains agent metadata:
+
+```json
+{
+ "name": "my-calendar-agent",
+ "displayName": "My Calendar Agent",
+ "version": "1.0.0",
+ "description": "Syncs calendar events to Plot"
+}
+```
+
+### TypeScript Config (tsconfig.json)
+
+Extends the SDK's base configuration:
+
+```json
+{
+ "extends": "@plotday/sdk/tsconfig.base.json",
+ "include": ["src/*.ts"]
+}
+```
+
+---
+
+## Next Steps
+
+Now that you have a basic agent running, explore:
+
+- **[Core Concepts](CORE_CONCEPTS.md)** - Understand agents, tools, and the Plot architecture
+- **[Built-in Tools](TOOLS_GUIDE.md)** - Learn about Plot, Store, Integrations, AI, and more
+- **[Building Custom Tools](BUILDING_TOOLS.md)** - Create your own reusable tools
+- **[Runtime Environment](RUNTIME.md)** - Understand execution constraints and optimization
+
+## Common First Tasks
+
+### Creating Activities
+
+```typescript
+await this.tools.plot.createActivity({
+ type: ActivityType.Task,
+ title: "Review pull request",
+ note: "Check the new authentication flow",
+ links: [
+ {
+ type: ActivityLinkType.external,
+ title: "View PR",
+ url: "https://github.com/org/repo/pull/123",
+ },
+ ],
+});
+```
+
+### Storing Data
+
+```typescript
+// Save
+await this.set("last_sync", new Date().toISOString());
+
+// Retrieve
+const lastSync = await this.get("last_sync");
+```
+
+### Scheduling Tasks
+
+```typescript
+// Run immediately
+const callback = await this.callback("processData");
+await this.runTask(callback);
+
+// Schedule for later
+await this.runTask(callback, {
+ runAt: new Date("2025-02-01T10:00:00Z"),
+});
+```
+
+## Need Help?
+
+- **Documentation**: Continue reading the guides
+- **Examples**: Check the [examples directory](https://github.com/plotday/plot/tree/main/agents)
+- **Issues**: [Report bugs or request features](https://github.com/plotday/plot/issues)
diff --git a/sdk/docs/RUNTIME.md b/sdk/docs/RUNTIME.md
new file mode 100644
index 0000000..53ed1a8
--- /dev/null
+++ b/sdk/docs/RUNTIME.md
@@ -0,0 +1,553 @@
+---
+title: Runtime Environment
+group: Guides
+---
+
+# Runtime Environment
+
+Understanding the Plot runtime environment will help you build efficient, reliable agents.
+
+## Table of Contents
+
+- [Execution Model](#execution-model)
+- [Limitations](#limitations)
+- [State Management](#state-management)
+- [Batching Long Operations](#batching-long-operations)
+- [Memory Considerations](#memory-considerations)
+- [Performance Optimization](#performance-optimization)
+- [Timeout Handling](#timeout-handling)
+
+---
+
+## Execution Model
+
+Plot agents run in a **sandboxed, serverless environment** with the following characteristics:
+
+### Ephemeral Execution
+
+- Each method invocation runs in isolation
+- No shared state between invocations
+- Instance variables don't persist between calls
+- Resources are cleaned up after execution
+
+### Event-Driven
+
+Agents respond to events:
+
+- **Lifecycle events** - activate, upgrade, deactivate
+- **Activity events** - New or updated activities
+- **Webhook events** - External service notifications
+- **Scheduled events** - Tasks queued with runTask()
+
+### Resource Limits
+
+Each execution has:
+
+- **CPU time limit** - Limited execution time
+- **Memory limit** - Limited memory allocation
+- **Network limit** - Rate-limited external requests
+
+---
+
+## Limitations
+
+### 1. No Persistent Instance State
+
+Instance variables don't survive between invocations:
+
+```typescript
+// ❌ WRONG - This doesn't work!
+class MyAgent extends Agent {
+ private syncToken: string = ""; // Lost after execution!
+
+ async activate() {
+ this.syncToken = "abc123"; // Saved to instance
+ }
+
+ async someMethod() {
+ console.log(this.syncToken); // Undefined! Different execution context
+ }
+}
+
+// ✅ CORRECT - Use Store
+class MyAgent extends Agent {
+ async activate() {
+ await this.set("sync_token", "abc123"); // Persisted
+ }
+
+ async someMethod() {
+ const token = await this.get("sync_token"); // Retrieved
+ console.log(token); // "abc123"
+ }
+}
+```
+
+### 2. Limited Execution Time
+
+Long-running operations must be broken into batches:
+
+```typescript
+// ❌ WRONG - May timeout!
+async syncAllEvents() {
+ const events = await fetchAllEvents(); // Could be thousands
+ for (const event of events) {
+ await this.processEvent(event); // Too slow!
+ }
+}
+
+// ✅ CORRECT - Batch processing
+async startSync() {
+ await this.set("sync_state", { page: 1, total: 0 });
+ const callback = await this.callback("syncBatch");
+ await this.runTask(callback);
+}
+
+async syncBatch() {
+ const state = await this.get<{ page: number; total: number }>("sync_state");
+ if (!state) return;
+
+ // Process one page
+ const events = await fetchEventsPage(state.page);
+ await this.processEvents(events);
+
+ // Queue next batch if needed
+ if (events.hasMore) {
+ await this.set("sync_state", {
+ page: state.page + 1,
+ total: state.total + events.length
+ });
+
+ const callback = await this.callback("syncBatch");
+ await this.runTask(callback);
+ }
+}
+```
+
+### 3. No File System Access
+
+Cannot read or write files:
+
+```typescript
+// ❌ WRONG - No file system
+import fs from "fs";
+
+fs.writeFileSync("data.json", JSON.stringify(data));
+
+// ✅ CORRECT - Use Store
+await this.set("data", data);
+```
+
+### 4. Limited Global State
+
+Don't rely on global variables:
+
+```typescript
+// ❌ WRONG - Globals don't persist
+let globalCache: Map = new Map();
+
+// ✅ CORRECT - Use Store with prefixed keys
+await this.set("cache:key1", value1);
+await this.set("cache:key2", value2);
+```
+
+---
+
+## State Management
+
+### Store Tool
+
+The Store tool provides persistent key-value storage:
+
+```typescript
+// Save state
+await this.set("key", value);
+
+// Retrieve state
+const value = await this.get("key");
+
+// Clear state
+await this.clear("key");
+await this.clearAll();
+```
+
+### State Organization
+
+Use prefixes to organize related data:
+
+```typescript
+// Configuration
+await this.set("config:api_key", apiKey);
+await this.set("config:workspace_id", workspaceId);
+
+// Sync state
+await this.set("sync:last_run", new Date().toISOString());
+await this.set("sync:page_token", pageToken);
+
+// Cache
+await this.set("cache:user:123", userData);
+await this.set("cache:repo:456", repoData);
+```
+
+### State Cleanup
+
+Clean up state in deactivate:
+
+```typescript
+async deactivate() {
+ // Option 1: Clear all
+ await this.clearAll();
+
+ // Option 2: Clear specific keys
+ await this.clear("sync:page_token");
+ await this.clear("cache:user:123");
+
+ // Option 3: Clear by prefix (manually)
+ // Store doesn't have native prefix clearing,
+ // so track keys if needed
+}
+```
+
+---
+
+## Batching Long Operations
+
+Break long operations into smaller batches to avoid timeouts.
+
+### Pattern 1: Page-Based Batching
+
+For paginated APIs:
+
+```typescript
+async startSync() {
+ await this.set("sync_state", {
+ page: 1,
+ totalProcessed: 0,
+ startTime: new Date().toISOString()
+ });
+
+ const callback = await this.callback("syncPage");
+ await this.runTask(callback);
+}
+
+async syncPage() {
+ const state = await this.get("sync_state");
+ if (!state) return;
+
+ try {
+ // Fetch one page
+ const response = await fetch(
+ `https://api.example.com/items?page=${state.page}&per_page=50`
+ );
+ const data = await response.json();
+
+ // Process items
+ for (const item of data.items) {
+ await this.processItem(item);
+ }
+
+ // Update state
+ const newState = {
+ page: state.page + 1,
+ totalProcessed: state.totalProcessed + data.items.length,
+ startTime: state.startTime
+ };
+
+ // Queue next page if more exist
+ if (data.hasMore) {
+ await this.set("sync_state", newState);
+ const callback = await this.callback("syncPage");
+ await this.runTask(callback);
+ } else {
+ // Sync complete
+ console.log(`Sync complete: ${newState.totalProcessed} items`);
+ await this.clear("sync_state");
+ }
+ } catch (error) {
+ console.error("Sync error:", error);
+ // Could implement retry logic here
+ }
+}
+```
+
+### Pattern 2: Token-Based Batching
+
+For APIs using continuation tokens:
+
+```typescript
+interface SyncState {
+ nextToken: string | null;
+ totalProcessed: number;
+}
+
+async startSync() {
+ await this.set("sync_state", {
+ nextToken: null,
+ totalProcessed: 0
+ });
+
+ const callback = await this.callback("syncBatch");
+ await this.runTask(callback);
+}
+
+async syncBatch() {
+ const state = await this.get("sync_state");
+ if (!state) return;
+
+ const response = await fetch(
+ `https://api.example.com/items${state.nextToken ? `?token=${state.nextToken}` : ""}`
+ );
+ const data = await response.json();
+
+ // Process batch
+ for (const item of data.items) {
+ await this.processItem(item);
+ }
+
+ // Update state and continue if needed
+ if (data.nextToken) {
+ await this.set("sync_state", {
+ nextToken: data.nextToken,
+ totalProcessed: state.totalProcessed + data.items.length
+ });
+
+ const callback = await this.callback("syncBatch");
+ await this.runTask(callback);
+ } else {
+ console.log(`Complete: ${state.totalProcessed + data.items.length} items`);
+ await this.clear("sync_state");
+ }
+}
+```
+
+### Pattern 3: Item-Based Batching
+
+For processing arrays of items:
+
+```typescript
+async processLargeArray(items: Item[]) {
+ // Save items and start processing
+ await this.set("items_to_process", items);
+ await this.set("process_index", 0);
+
+ const callback = await this.callback("processBatch");
+ await this.runTask(callback);
+}
+
+async processBatch() {
+ const items = await this.get
- ("items_to_process");
+ const index = await this.get("process_index");
+
+ if (!items || index === null || index >= items.length) {
+ await this.clear("items_to_process");
+ await this.clear("process_index");
+ return;
+ }
+
+ // Process batch of 10 items
+ const batchSize = 10;
+ const batch = items.slice(index, index + batchSize);
+
+ for (const item of batch) {
+ await this.processItem(item);
+ }
+
+ // Update index and continue
+ const newIndex = index + batchSize;
+ if (newIndex < items.length) {
+ await this.set("process_index", newIndex);
+ const callback = await this.callback("processBatch");
+ await this.runTask(callback);
+ } else {
+ // Complete
+ await this.clear("items_to_process");
+ await this.clear("process_index");
+ }
+}
+```
+
+---
+
+## Memory Considerations
+
+### Avoid Large Data Structures
+
+Don't load large datasets into memory:
+
+```typescript
+// ❌ WRONG - Loads everything into memory
+async syncAll() {
+ const allEvents = await fetchAllEvents(); // Could be 10,000+ events
+ for (const event of allEvents) {
+ await this.processEvent(event);
+ }
+}
+
+// ✅ CORRECT - Stream/batch processing
+async syncBatch() {
+ const page = await this.get("current_page") || 1;
+ const events = await fetchEventsPage(page, 50); // Only 50 at a time
+
+ for (const event of events) {
+ await this.processEvent(event);
+ }
+
+ // Continue with next batch
+}
+```
+
+### Efficient Data Storage
+
+Store only what's needed:
+
+```typescript
+// ❌ WRONG - Storing full response
+const response = await fetch("https://api.example.com/users/123");
+const fullData = await response.json();
+await this.set("user_data", fullData); // Lots of unnecessary data
+
+// ✅ CORRECT - Store only what's needed
+const response = await fetch("https://api.example.com/users/123");
+const data = await response.json();
+await this.set("user_name", data.name);
+await this.set("user_email", data.email);
+```
+
+---
+
+## Performance Optimization
+
+### 1. Minimize API Calls
+
+Batch operations where possible:
+
+```typescript
+// ❌ SLOW - Multiple round trips
+for (const item of items) {
+ await this.tools.plot.createActivity({
+ type: ActivityType.Task,
+ title: item.title,
+ });
+}
+
+// ✅ FAST - Batch create
+await this.tools.plot.createActivities(
+ items.map((item) => ({
+ type: ActivityType.Task,
+ title: item.title,
+ }))
+);
+```
+
+### 2. Parallel Operations
+
+Run independent operations in parallel:
+
+```typescript
+// ❌ SLOW - Sequential
+const user = await fetchUser();
+const repos = await fetchRepos();
+const issues = await fetchIssues();
+
+// ✅ FAST - Parallel
+const [user, repos, issues] = await Promise.all([
+ fetchUser(),
+ fetchRepos(),
+ fetchIssues()
+]);
+```
+
+### 3. Caching
+
+Cache frequently accessed data:
+
+```typescript
+async getUserData(userId: string): Promise {
+ // Check cache first
+ const cached = await this.get(`cache:user:${userId}`);
+ if (cached) {
+ return cached;
+ }
+
+ // Fetch and cache
+ const data = await fetch(`https://api.example.com/users/${userId}`);
+ const userData = await data.json();
+
+ await this.set(`cache:user:${userId}`, userData);
+ return userData;
+}
+```
+
+### 4. Debouncing
+
+Avoid processing duplicate events:
+
+```typescript
+async onWebhook(request: WebhookRequest) {
+ const eventId = request.body.id;
+
+ // Check if already processed
+ const processed = await this.get(`processed:${eventId}`);
+ if (processed) {
+ console.log("Event already processed");
+ return;
+ }
+
+ // Process and mark as done
+ await this.processEvent(request.body);
+ await this.set(`processed:${eventId}`, true);
+}
+```
+
+---
+
+## Timeout Handling
+
+### Detecting Timeouts
+
+Timeouts manifest as execution termination - your code simply stops running.
+
+### Prevention Strategies
+
+1. **Batch Operations** - Break work into smaller chunks
+2. **Async Processing** - Use runTask() for background work
+3. **Monitor Duration** - Track execution time
+
+```typescript
+async longOperation() {
+ const start = Date.now();
+ const items = await fetchItems();
+
+ for (const item of items) {
+ // Check if approaching timeout
+ if (Date.now() - start > 55000) { // 55 seconds
+ // Save progress and reschedule
+ await this.set("remaining_items", items.slice(items.indexOf(item)));
+ const callback = await this.callback("continueOperation");
+ await this.runTask(callback);
+ return;
+ }
+
+ await this.processItem(item);
+ }
+}
+```
+
+---
+
+## Best Practices Summary
+
+1. **Never use instance variables** for persistent state - use Store
+2. **Break long operations** into batches using runTask()
+3. **Minimize memory usage** - don't load large datasets
+4. **Cache wisely** - balance performance and memory
+5. **Process in parallel** when operations are independent
+6. **Track progress** for resumable operations
+7. **Clean up state** in deactivate()
+
+---
+
+## Next Steps
+
+- **[Built-in Tools Guide](TOOLS_GUIDE.md)** - Learn about Store and Tasks tools
+- **[Advanced Topics](ADVANCED.md)** - Complex patterns and techniques
+- **[Core Concepts](CORE_CONCEPTS.md)** - Understanding the agent architecture
diff --git a/sdk/docs/TOOLS_GUIDE.md b/sdk/docs/TOOLS_GUIDE.md
new file mode 100644
index 0000000..fb88976
--- /dev/null
+++ b/sdk/docs/TOOLS_GUIDE.md
@@ -0,0 +1,796 @@
+---
+title: Built-in Tools
+group: Guides
+---
+
+# Built-in Tools
+
+Plot provides a comprehensive set of built-in tools that give your agents powerful capabilities. This guide covers all built-in tools with detailed examples and best practices.
+
+## Table of Contents
+
+- [Plot](#plot) - Managing activities and priorities
+- [Store](#store) - Persistent key-value storage
+- [Integrations](#integrations) - OAuth authentication
+- [Tasks](#tasks) - Background task execution
+- [Network](#network) - HTTP access and webhooks
+- [Callbacks](#callbacks) - Persistent function references
+- [AI](#ai) - Language model integration
+
+---
+
+## Plot
+
+The Plot tool is the core interface for creating and managing activities and priorities.
+
+### Setup
+
+```typescript
+import { Plot } from "@plotday/sdk/tools/plot";
+
+build(build: ToolBuilder) {
+ return {
+ plot: build(Plot),
+ };
+}
+```
+
+### Creating Activities
+
+```typescript
+import { ActivityLinkType, ActivityType } from "@plotday/sdk";
+
+// Create a note
+await this.tools.plot.createActivity({
+ type: ActivityType.Note,
+ title: "Meeting notes",
+ note: "Discussed Q1 planning",
+});
+
+// Create a task
+await this.tools.plot.createActivity({
+ type: ActivityType.Task,
+ title: "Review pull request #123",
+ links: [
+ {
+ type: ActivityLinkType.external,
+ title: "View PR",
+ url: "https://github.com/org/repo/pull/123",
+ },
+ ],
+});
+
+// Create an event
+await this.tools.plot.createActivity({
+ type: ActivityType.Event,
+ title: "Team standup",
+ start: new Date("2025-02-01T10:00:00Z"),
+ end: new Date("2025-02-01T10:30:00Z"),
+});
+```
+
+### Updating Activities
+
+```typescript
+// Mark task as done
+await this.tools.plot.updateActivity(activity.id, {
+ doneAt: new Date(),
+});
+
+// Update title and note
+await this.tools.plot.updateActivity(activity.id, {
+ title: "Updated title",
+ note: "Additional information",
+});
+
+// Reschedule event
+await this.tools.plot.updateActivity(activity.id, {
+ start: new Date("2025-02-02T10:00:00Z"),
+ end: new Date("2025-02-02T10:30:00Z"),
+});
+```
+
+### Deleting Activities
+
+```typescript
+await this.tools.plot.deleteActivity(activity.id);
+```
+
+### Managing Priorities
+
+```typescript
+// Create a top-level priority
+const work = await this.tools.plot.createPriority({
+ title: "Work",
+});
+
+// Create a nested priority
+const project = await this.tools.plot.createPriority({
+ title: "Project A",
+ parentId: work.id,
+});
+```
+
+### Activity Meta
+
+Use meta fields to store custom data and link external resources:
+
+```typescript
+await this.tools.plot.createActivity({
+ type: ActivityType.Task,
+ title: "Review PR",
+ meta: {
+ github_pr_id: "123",
+ github_repo: "org/repo",
+ review_status: "pending",
+ },
+});
+
+// Later, find by meta
+const activity = await this.tools.plot.getActivityByMeta({
+ github_pr_id: "123",
+});
+```
+
+---
+
+## Store
+
+Persistent key-value storage for agent state. Store methods are available directly on the Agent class.
+
+### Setup
+
+Store is available automatically - no build() declaration needed!
+
+### Storing Data
+
+```typescript
+// Save a string
+await this.set("last_sync", new Date().toISOString());
+
+// Save an object
+await this.set("config", {
+ enabled: true,
+ interval: 3600,
+});
+
+// Save an array
+await this.set("items", ["a", "b", "c"]);
+```
+
+### Retrieving Data
+
+```typescript
+// Get with type safety
+const lastSync = await this.get("last_sync");
+const config = await this.get<{ enabled: boolean; interval: number }>("config");
+
+// Handle missing data
+const value = await this.get("key");
+if (value === null) {
+ // Key doesn't exist
+}
+```
+
+### Clearing Data
+
+```typescript
+// Clear a specific key
+await this.clear("last_sync");
+
+// Clear all data for this agent
+await this.clearAll();
+```
+
+### Best Practices
+
+#### Type Safety
+
+Define interfaces for complex stored data:
+
+```typescript
+interface SyncState {
+ lastSync: string;
+ token: string;
+ status: "active" | "paused";
+}
+
+async getSyncState(): Promise {
+ return await this.get("sync_state");
+}
+
+async setSyncState(state: SyncState): Promise {
+ await this.set("sync_state", state);
+}
+```
+
+#### Namespacing
+
+Use prefixes to organize related data:
+
+```typescript
+await this.set("webhook:calendar", webhookUrl);
+await this.set("webhook:github", githubWebhookUrl);
+await this.set("config:sync_interval", 3600);
+```
+
+#### Serialization Limits
+
+Remember: Values must be JSON-serializable. Functions, Symbols, and undefined values cannot be stored.
+
+```typescript
+// ❌ WRONG
+await this.set("handler", this.myFunction); // Functions can't be stored
+
+// ✅ CORRECT - Use callbacks instead
+const token = await this.callback("myFunction");
+await this.set("handler_token", token);
+```
+
+---
+
+## Integrations
+
+OAuth authentication for external services (Google, Microsoft, etc.).
+
+### Setup
+
+```typescript
+import { Integrations } from "@plotday/sdk/tools/integrations";
+
+build(build: ToolBuilder) {
+ return {
+ integrations: build(Integrations),
+ };
+}
+```
+
+### Requesting Authentication
+
+```typescript
+import { AuthLevel, AuthProvider, type Authorization } from "@plotday/sdk/tools/integrations";
+import { ActivityLinkType } from "@plotday/sdk";
+
+async activate(priority: Pick) {
+ // Create callback for auth completion
+ const authCallback = await this.callback("onAuthComplete");
+
+ // Request Google auth
+ const authLink = await this.tools.integrations.request(
+ {
+ provider: AuthProvider.Google,
+ level: AuthLevel.User,
+ scopes: [
+ "https://www.googleapis.com/auth/calendar.readonly"
+ ]
+ },
+ authCallback
+ );
+
+ // Create activity with auth link
+ await this.tools.plot.createActivity({
+ type: ActivityType.Note,
+ title: "Connect your Google Calendar",
+ links: [{
+ type: ActivityLinkType.auth,
+ title: "Connect Google",
+ url: authLink
+ }]
+ });
+}
+
+// Handle auth completion
+async onAuthComplete(authorization: Authorization) {
+ // Get access token
+ const authToken = await this.tools.integrations.get(authorization);
+
+ if (authToken) {
+ console.log("Access token:", authToken.token);
+ await this.set("google_auth", authorization);
+
+ // Start syncing
+ await this.startSync();
+ }
+}
+```
+
+### Auth Providers
+
+- **AuthProvider.Google** - Google services
+- **AuthProvider.Microsoft** - Microsoft services
+
+### Auth Levels
+
+- **AuthLevel.Priority** - Auth scoped to the current priority
+- **AuthLevel.User** - Auth scoped to the user across all priorities
+
+### Using Auth Tokens
+
+```typescript
+// Retrieve saved authorization
+const authorization = await this.get("google_auth");
+
+if (authorization) {
+ const authToken = await this.tools.integrations.get(authorization);
+
+ // Use token with external API
+ const response = await fetch(
+ "https://www.googleapis.com/calendar/v3/calendars/primary/events",
+ {
+ headers: {
+ Authorization: `Bearer ${authToken.token}`,
+ },
+ }
+ );
+}
+```
+
+---
+
+## Tasks
+
+Queue background tasks and schedule operations. Tasks methods are available directly on the Agent class.
+
+### Setup
+
+Tasks are available automatically - no build() declaration needed!
+
+### Running Tasks Immediately
+
+```typescript
+// Create a callback
+const callback = await this.callback("processData", { batchId: 1 });
+
+// Run immediately
+await this.runTask(callback);
+
+// The processData method will be called
+async processData(args: any, context: { batchId: number }) {
+ console.log("Processing batch:", context.batchId);
+}
+```
+
+### Scheduling Tasks
+
+```typescript
+// Schedule for a specific time
+const reminderCallback = await this.callback("sendReminder", {
+ userId: "123",
+ message: "Meeting in 10 minutes",
+});
+
+const token = await this.runTask(reminderCallback, {
+ runAt: new Date("2025-02-01T09:50:00Z"),
+});
+
+// Save token to cancel later if needed
+await this.set("reminder_token", token);
+```
+
+### Canceling Tasks
+
+```typescript
+// Cancel a specific task
+const token = await this.get("reminder_token");
+if (token) {
+ await this.cancelTask(token);
+}
+
+// Cancel all scheduled tasks for this agent
+await this.cancelAllTasks();
+```
+
+### Batch Processing Pattern
+
+Use tasks to break long operations into manageable chunks:
+
+```typescript
+async startSync() {
+ // Initialize state
+ await this.set("sync_state", {
+ page: 1,
+ hasMore: true
+ });
+
+ // Start first batch
+ const callback = await this.callback("syncBatch");
+ await this.runTask(callback);
+}
+
+async syncBatch() {
+ const state = await this.get<{ page: number; hasMore: boolean }>("sync_state");
+ if (!state || !state.hasMore) return;
+
+ // Process one page
+ const results = await this.fetchPage(state.page);
+ await this.processResults(results);
+
+ // Check if more work remains
+ if (results.hasMore) {
+ await this.set("sync_state", {
+ page: state.page + 1,
+ hasMore: true
+ });
+
+ // Queue next batch
+ const callback = await this.callback("syncBatch");
+ await this.runTask(callback);
+ } else {
+ await this.set("sync_state", { page: state.page, hasMore: false });
+ }
+}
+```
+
+See [Runtime Environment](RUNTIME.md) for more about handling long operations.
+
+---
+
+## Network
+
+Request HTTP access and create webhook endpoints for real-time notifications.
+
+### Setup
+
+```typescript
+import { Network, type WebhookRequest } from "@plotday/sdk/tools/network";
+
+build(build: ToolBuilder) {
+ return {
+ network: build(Network, {
+ // Declare which URLs you'll access
+ urls: ['https://api.example.com/*']
+ })
+ };
+}
+```
+
+### Making HTTP Requests
+
+Once declared in the `urls` array, you can use fetch() normally:
+
+```typescript
+async fetchData() {
+ const response = await fetch("https://api.example.com/data", {
+ headers: {
+ Authorization: `Bearer ${token}`
+ }
+ });
+
+ return await response.json();
+}
+```
+
+### Creating Webhooks
+
+```typescript
+async activate(priority: Pick) {
+ // Create webhook endpoint
+ const webhookUrl = await this.tools.network.createWebhook(
+ "onCalendarUpdate",
+ { calendarId: "primary" }
+ );
+
+ // Save for cleanup later
+ await this.set("webhook_url", webhookUrl);
+
+ // Register with external service
+ await fetch("https://api.service.com/webhooks", {
+ method: "POST",
+ body: JSON.stringify({ url: webhookUrl })
+ });
+}
+
+// Handle webhook requests
+async onCalendarUpdate(request: WebhookRequest, context: { calendarId: string }) {
+ console.log("Webhook received:", request.method);
+ console.log("Body:", request.body);
+ console.log("Calendar:", context.calendarId);
+
+ // Process the webhook
+ if (request.body.type === "event.created") {
+ await this.syncEvent(request.body.event);
+ }
+}
+```
+
+### Deleting Webhooks
+
+```typescript
+async deactivate() {
+ const webhookUrl = await this.get("webhook_url");
+
+ if (webhookUrl) {
+ // Unregister from external service
+ await fetch("https://api.service.com/webhooks", {
+ method: "DELETE",
+ body: JSON.stringify({ url: webhookUrl })
+ });
+
+ // Delete webhook endpoint
+ await this.tools.network.deleteWebhook(webhookUrl);
+ }
+}
+```
+
+---
+
+## Callbacks
+
+Create persistent function references that survive worker restarts. Callbacks methods are available directly on the Agent class.
+
+### Setup
+
+Callbacks are available automatically - no build() declaration needed!
+
+### Creating Callbacks
+
+```typescript
+// Create a callback to a method
+const callback = await this.callback("handleEvent", {
+ eventType: "calendar_sync",
+ priority: "high",
+});
+
+// Save it for later use
+await this.set("event_handler", callback);
+```
+
+### Executing Callbacks
+
+```typescript
+// Retrieve saved callback
+const callback = await this.get("event_handler");
+
+if (callback) {
+ // Execute with additional arguments
+ const result = await this.run(callback, {
+ data: eventData,
+ timestamp: new Date(),
+ });
+}
+```
+
+### Method Signature
+
+The callback method receives both the execution args and the original context:
+
+```typescript
+async handleEvent(
+ args: { data: any; timestamp: Date }, // From run()
+ context: { eventType: string; priority: string } // From callback()
+) {
+ console.log("Event type:", context.eventType);
+ console.log("Priority:", context.priority);
+ console.log("Data:", args.data);
+}
+```
+
+### Deleting Callbacks
+
+```typescript
+// Delete a specific callback
+const callback = await this.get("event_handler");
+if (callback) {
+ await this.deleteCallback(callback);
+}
+
+// Delete all callbacks for this agent
+await this.deleteAllCallbacks();
+```
+
+### Use Cases
+
+Callbacks are essential for:
+
+- **Webhooks** - Persistent handlers that survive restarts
+- **Auth flows** - Handling OAuth completion
+- **Scheduled tasks** - Functions to run at specific times
+- **Activity links** - Interactive buttons in activities
+
+---
+
+## AI
+
+Prompt large language models with support for structured output and tool calling.
+
+### Setup
+
+```typescript
+import { AI } from "@plotday/sdk/tools/ai";
+
+build(build: ToolBuilder) {
+ return {
+ ai: build(AI),
+ };
+}
+```
+
+### Simple Text Generation
+
+```typescript
+const response = await this.tools.ai.prompt({
+ model: { speed: "fast", cost: "low" },
+ prompt: "Explain quantum computing in simple terms",
+});
+
+console.log(response.text);
+```
+
+### Structured Output
+
+Use Typebox schemas to get type-safe structured responses:
+
+```typescript
+import { Type } from "typebox";
+
+const schema = Type.Object({
+ category: Type.Union([
+ Type.Literal("work"),
+ Type.Literal("personal"),
+ Type.Literal("urgent"),
+ ]),
+ priority: Type.Number({ minimum: 1, maximum: 5 }),
+ summary: Type.String({ description: "Brief summary" }),
+});
+
+const response = await this.tools.ai.prompt({
+ model: { speed: "balanced", cost: "medium" },
+ prompt: "Categorize this email: Meeting at 3pm tomorrow about Q1 planning",
+ outputSchema: schema,
+});
+
+// Fully typed output!
+console.log(response.output.category); // "work" | "personal" | "urgent"
+console.log(response.output.priority); // number (1-5)
+console.log(response.output.summary); // string
+```
+
+### Tool Calling
+
+Give the AI access to tools it can call:
+
+```typescript
+import { Type } from "typebox";
+
+const response = await this.tools.ai.prompt({
+ model: { speed: "fast", cost: "medium" },
+ prompt: "What's 15% of $250?",
+ tools: {
+ calculate: {
+ description: "Perform mathematical calculations",
+ parameters: Type.Object({
+ expression: Type.String({ description: "Math expression to evaluate" }),
+ }),
+ execute: async ({ expression }) => {
+ return { result: eval(expression) };
+ },
+ },
+ },
+});
+
+console.log(response.text); // "15% of $250 is $37.50"
+```
+
+### Multi-turn Conversations
+
+Build conversations with message history:
+
+```typescript
+import { Type } from "typebox";
+
+const messages = [
+ {
+ role: "user" as const,
+ content: "What's the weather like?",
+ },
+ {
+ role: "assistant" as const,
+ content:
+ "I don't have access to weather data. Would you like me to help with something else?",
+ },
+ {
+ role: "user" as const,
+ content: "What's 2+2?",
+ },
+];
+
+const response = await this.tools.ai.prompt({
+ model: { speed: "fast", cost: "low" },
+ messages,
+});
+```
+
+### Model Selection
+
+Specify your requirements using speed and cost tiers:
+
+```typescript
+// Fast and cheap - Good for simple tasks
+model: { speed: "fast", cost: "low" }
+
+// Balanced - Good for most tasks
+model: { speed: "balanced", cost: "medium" }
+
+// Most capable - Complex reasoning
+model: { speed: "capable", cost: "high" }
+```
+
+Plot automatically selects the best available model matching your preferences.
+
+### Typebox Schemas
+
+Typebox provides JSON Schema with full TypeScript type inference:
+
+```typescript
+import { Type } from "typebox";
+
+// Objects
+const PersonSchema = Type.Object({
+ name: Type.String(),
+ age: Type.Number(),
+ email: Type.Optional(Type.String({ format: "email" })),
+});
+
+// Arrays
+const PeopleSchema = Type.Array(PersonSchema);
+
+// Unions (enums)
+const StatusSchema = Type.Union([
+ Type.Literal("pending"),
+ Type.Literal("active"),
+ Type.Literal("completed"),
+]);
+
+// Nested objects
+const ProjectSchema = Type.Object({
+ title: Type.String(),
+ status: StatusSchema,
+ assignees: Type.Array(PersonSchema),
+});
+```
+
+See the [Typebox documentation](https://github.com/sinclairzx81/typebox) for more schema types.
+
+### Real-World Example: Email Triage
+
+```typescript
+import { Type } from "typebox";
+
+async triageEmail(emailContent: string) {
+ const schema = Type.Object({
+ category: Type.Union([
+ Type.Literal("urgent"),
+ Type.Literal("important"),
+ Type.Literal("informational"),
+ Type.Literal("spam")
+ ]),
+ requiresResponse: Type.Boolean(),
+ suggestedActions: Type.Array(Type.String()),
+ summary: Type.String({ maxLength: 200 })
+ });
+
+ const response = await this.tools.ai.prompt({
+ model: { speed: "balanced", cost: "medium" },
+ prompt: `Analyze this email and provide triage information:\n\n${emailContent}`,
+ outputSchema: schema
+ });
+
+ // Create activity based on triage
+ if (response.output.category === "urgent") {
+ await this.tools.plot.createActivity({
+ type: ActivityType.Task,
+ title: `URGENT: ${response.output.summary}`,
+ note: `Actions:\n${response.output.suggestedActions.join("\n")}`
+ });
+ }
+}
+```
+
+---
+
+## Next Steps
+
+- **[Building Custom Tools](BUILDING_TOOLS.md)** - Create your own reusable tools
+- **[Runtime Environment](RUNTIME.md)** - Understanding execution constraints
+- **[Advanced Topics](ADVANCED.md)** - Complex patterns and techniques
+- **API Reference** - Explore detailed API docs in the sidebar
diff --git a/sdk/docs/index.md b/sdk/docs/index.md
new file mode 100644
index 0000000..e0174f7
--- /dev/null
+++ b/sdk/docs/index.md
@@ -0,0 +1,148 @@
+Welcome to the Plot Agent SDK documentation! This comprehensive guide will help you build powerful agents that organize and prioritize activities in Plot.
+
+## What are Plot Agents?
+
+Plot agents are intelligent assistants that automatically manage your activities, tasks, and events. They can integrate with external services, process data, and help you stay organized across all your apps and messages.
+
+## Documentation Structure
+
+### Getting Started
+
+- **[Getting Started Guide](GETTING_STARTED.md)** - Complete walkthrough for building your first agent
+ - No-code agent creation with `plot-agent.md`
+ - Developer quick start with TypeScript
+ - Project structure and setup
+
+### Core Documentation
+
+- **[Core Concepts](CORE_CONCEPTS.md)** - Understanding the Plot architecture
+
+ - Agents and their lifecycle
+ - Tools and dependencies
+ - Priorities and Activities
+ - Best practices and patterns
+
+- **[Built-in Tools Guide](TOOLS_GUIDE.md)** - Complete reference for all built-in tools
+
+ - Plot - Managing activities and priorities
+ - Store - Persistent key-value storage
+ - Integrations - OAuth authentication
+ - Tasks - Background task execution
+ - Network - HTTP access and webhooks
+ - Callbacks - Persistent function references
+ - AI - Language model integration
+
+- **[Building Custom Tools](BUILDING_TOOLS.md)** - Create your own tools
+ - Tool class structure
+ - Lifecycle methods
+ - Dependencies and configuration
+ - Complete examples and best practices
+ - Publishing and sharing
+
+### Reference
+
+- **[CLI Reference](CLI_REFERENCE.md)** - Complete command-line interface documentation
+
+ - Agent management commands
+ - Priority management
+ - Authentication
+ - Deployment
+
+- **[Runtime Environment](RUNTIME.md)** - Understanding execution constraints
+
+ - Sandbox limitations
+ - Batching long operations
+ - Memory and state management
+ - Performance optimization
+
+- **[Advanced Topics](ADVANCED.md)** - Complex patterns and techniques
+ - Multi-agent coordination
+ - Error handling
+ - Debugging and logging
+ - Security best practices
+
+## API Reference
+
+Explore the complete API documentation using the navigation on the left:
+
+- **Classes** - Agent, Tool, and all built-in tool classes
+- **Interfaces** - Activity, Priority, Contact, and data structures
+- **Enums** - ActivityType, AuthorType, and other enumerations
+- **Type Aliases** - Helper types and utilities
+
+## Quick Links
+
+- [Plot Website](https://plot.day)
+- [GitHub Repository](https://github.com/plotday/plot)
+- [NPM Package](https://www.npmjs.com/package/@plotday/sdk)
+- [Report Issues](https://github.com/plotday/plot/issues)
+
+## Examples
+
+Check out these examples to get started:
+
+### Simple Note Agent
+
+```typescript
+import {
+ ActivityType,
+ Agent,
+ type Priority,
+ type ToolBuilder,
+} from "@plotday/sdk";
+import { Plot } from "@plotday/sdk/tools/plot";
+
+export default class WelcomeAgent extends Agent {
+ build(build: ToolBuilder) {
+ return {
+ plot: build(Plot),
+ };
+ }
+
+ async activate(priority: Pick) {
+ await this.tools.plot.createActivity({
+ type: ActivityType.Note,
+ title: "Welcome to Plot! 👋",
+ });
+ }
+}
+```
+
+### Calendar Sync Agent
+
+```typescript
+import { type Activity, Agent, type ToolBuilder } from "@plotday/sdk";
+import { Network } from "@plotday/sdk/tools/network";
+import { Plot } from "@plotday/sdk/tools/plot";
+
+export default class CalendarAgent extends Agent {
+ build(build: ToolBuilder) {
+ return {
+ plot: build(Plot),
+ network: build(Network, {
+ urls: ["https://www.googleapis.com/calendar/*"],
+ }),
+ };
+ }
+
+ async activate(priority: Pick) {
+ // Set up webhook for calendar updates
+ const webhookUrl = await this.tools.network.createWebhook(
+ "onCalendarUpdate",
+ { priorityId: priority.id }
+ );
+
+ await this.set("webhook_url", webhookUrl);
+ }
+}
+```
+
+## Need Help?
+
+- **Documentation Questions**: Read through the guides above
+- **Bug Reports**: [Open an issue](https://github.com/plotday/plot/issues)
+- **Feature Requests**: [Start a discussion](https://github.com/plotday/plot/discussions)
+
+## License
+
+MIT © Plot Technologies Inc.
diff --git a/sdk/package.json b/sdk/package.json
index 374cb08..83f92d0 100644
--- a/sdk/package.json
+++ b/sdk/package.json
@@ -148,20 +148,25 @@
"scripts": {
"lint": "tsc --noEmit",
"prebuild": "tsx prebuild.ts",
- "build": "tsx prebuild.ts && tsc --project tsconfig.build.json && tsc --project tsconfig.cli.json && node postbuild.js",
+ "build": "tsx prebuild.ts && tsc --project tsconfig.build.json && tsc --project tsconfig.cli.json && node postbuild.js && pnpm docs",
+ "docs": "typedoc",
+ "docs:open": "open dist/docs/index.html || xdg-open dist/docs/index.html || start dist/docs/index.html",
+ "docs:clean": "rm -rf dist/docs",
"clean": "rm -rf dist bin"
},
"dependencies": {
- "typebox": "^1.0.35",
"chalk": "^4.1.2",
"commander": "^12.0.0",
"dotenv": "^16.4.5",
"esbuild": "^0.24.0",
- "prompts": "^2.4.2"
+ "prompts": "^2.4.2",
+ "typebox": "^1.0.35"
},
"devDependencies": {
"@types/prompts": "^2.4.9",
"tsx": "^4.19.2",
+ "typedoc": "^0.28.14",
+ "typedoc-plugin-missing-exports": "^4.1.2",
"typescript": "^5.9.3"
},
"peerDependencies": {
@@ -175,7 +180,7 @@
"url": "https://github.com/plotday/plot.git",
"directory": "sdk"
},
- "homepage": "https://plot.day",
+ "homepage": "https://build.plot.day",
"bugs": {
"url": "https://github.com/plotday/plot/issues"
},
diff --git a/sdk/src/agent.ts b/sdk/src/agent.ts
index 932b015..f4ebae9 100644
--- a/sdk/src/agent.ts
+++ b/sdk/src/agent.ts
@@ -212,10 +212,11 @@ export abstract class Agent {
* This method should contain initialization logic such as setting up
* initial activities, configuring webhooks, or establishing external connections.
*
- * @param _priority - The priority context containing the priority ID
+ * @param priority - The priority context containing the priority ID
* @returns Promise that resolves when activation is complete
*/
- activate(_priority: Pick): Promise {
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ activate(priority: Pick): Promise {
return Promise.resolve();
}
diff --git a/sdk/src/tool.ts b/sdk/src/tool.ts
index e17c723..660de18 100644
--- a/sdk/src/tool.ts
+++ b/sdk/src/tool.ts
@@ -75,7 +75,8 @@ export abstract class Tool implements ITool {
* }
* ```
*/
- build(_build: ToolBuilder): Record> {
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ build(build: ToolBuilder): Record> {
return {};
}
@@ -232,10 +233,11 @@ export abstract class Tool implements ITool {
* being called first, bubbling up to the top-level tools before the agent's
* activate method is called.
*
- * @param _priority - The priority context containing the priority ID
+ * @param priority - The priority context containing the priority ID
* @returns Promise that resolves when pre-activation is complete
*/
- preActivate(_priority: Priority): Promise {
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ preActivate(priority: Priority): Promise {
return Promise.resolve();
}
@@ -245,10 +247,11 @@ export abstract class Tool implements ITool {
* This method is called in reverse order, with top-level tools being called
* first, then cascading down to the deepest dependencies.
*
- * @param _priority - The priority context containing the priority ID
+ * @param priority - The priority context containing the priority ID
* @returns Promise that resolves when post-activation is complete
*/
- postActivate(_priority: Priority): Promise {
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ postActivate(priority: Priority): Promise {
return Promise.resolve();
}
diff --git a/sdk/src/tools/agents.ts b/sdk/src/tools/agents.ts
index 6899bcb..3fcb358 100644
--- a/sdk/src/tools/agents.ts
+++ b/sdk/src/tools/agents.ts
@@ -118,7 +118,8 @@ export abstract class Agents extends ITool {
* // source.files: { "index.ts": "export default class..." }
* ```
*/
- abstract generate(_spec: string): Promise;
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ abstract generate(spec: string): Promise;
/**
* Deploys an agent programmatically.
@@ -171,8 +172,9 @@ export abstract class Agents extends ITool {
* }
* ```
*/
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
abstract deploy(
- _options: {
+ options: {
agentId: string;
environment?: "personal" | "private" | "review";
name?: string;
@@ -221,5 +223,6 @@ export abstract class Agents extends ITool {
* }
* ```
*/
- abstract watchLogs(_agentId: string, _callback: Callback): Promise;
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ abstract watchLogs(agentId: string, callback: Callback): Promise;
}
diff --git a/sdk/src/tools/ai.ts b/sdk/src/tools/ai.ts
index 8c9d0d4..34a24a3 100644
--- a/sdk/src/tools/ai.ts
+++ b/sdk/src/tools/ai.ts
@@ -140,8 +140,9 @@ export abstract class AI extends ITool {
* console.log(response.toolCalls); // Array of tool calls made
* ```
*/
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
abstract prompt(
- _request: AIRequest
+ request: AIRequest
): Promise>;
}
diff --git a/sdk/src/tools/callbacks.ts b/sdk/src/tools/callbacks.ts
index ed4937c..ee6110f 100644
--- a/sdk/src/tools/callbacks.ts
+++ b/sdk/src/tools/callbacks.ts
@@ -63,9 +63,10 @@ export abstract class Callbacks extends ITool {
* @param extraArgs - Additional arguments to pass to the function (must be serializable)
* @returns Promise resolving to a persistent callback token
*/
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
abstract create(
- _fn: Function,
- ..._extraArgs: any[]
+ fn: Function,
+ ...extraArgs: any[]
): Promise;
/**
@@ -76,9 +77,10 @@ export abstract class Callbacks extends ITool {
* @param extraArgs - Additional arguments to pass to the function (must be serializable, validated at runtime)
* @returns Promise resolving to a persistent callback token
*/
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
abstract createFromParent(
- _fn: Function,
- ..._extraArgs: any[]
+ fn: Function,
+ ...extraArgs: any[]
): Promise;
/**
@@ -87,7 +89,8 @@ export abstract class Callbacks extends ITool {
* @param callback - The callback token to delete
* @returns Promise that resolves when the callback is deleted
*/
- abstract delete(_callback: Callback): Promise;
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ abstract delete(callback: Callback): Promise;
/**
* Deletes all callbacks for the tool's parent.
@@ -103,5 +106,6 @@ export abstract class Callbacks extends ITool {
* @param args - Optional arguments to pass to the callback function
* @returns Promise resolving to the callback result
*/
- abstract run(_callback: Callback, ..._args: any[]): Promise;
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ abstract run(callback: Callback, ...args: any[]): Promise;
}
diff --git a/sdk/src/tools/integrations.ts b/sdk/src/tools/integrations.ts
index 108d258..0b463a4 100644
--- a/sdk/src/tools/integrations.ts
+++ b/sdk/src/tools/integrations.ts
@@ -51,16 +51,17 @@ export abstract class Integrations extends ITool {
* @param extraArgs - Additional arguments to pass to the callback (type-checked, must be serializable)
* @returns Promise resolving to an ActivityLink for the auth flow
*/
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
abstract request<
TCallback extends (auth: Authorization, ...args: any[]) => any
>(
- _auth: {
+ auth: {
provider: AuthProvider;
level: AuthLevel;
scopes: string[];
},
- _callback: TCallback,
- ..._extraArgs: TCallback extends (auth: any, ...rest: infer R) => any
+ callback: TCallback,
+ ...extraArgs: TCallback extends (auth: any, ...rest: infer R) => any
? NoFunctions
: []
): Promise;
@@ -73,7 +74,8 @@ export abstract class Integrations extends ITool {
* @param authorization - The authorization from the request callback
* @returns Promise resolving to the access token or null if no longer available
*/
- abstract get(_authorization: Authorization): Promise;
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ abstract get(authorization: Authorization): Promise;
}
/**
diff --git a/sdk/src/tools/network.ts b/sdk/src/tools/network.ts
index 0b9bd81..b27af22 100644
--- a/sdk/src/tools/network.ts
+++ b/sdk/src/tools/network.ts
@@ -128,11 +128,12 @@ export abstract class Network extends ITool {
* @param extraArgs - Additional arguments to pass to the callback (type-checked)
* @returns Promise resolving to the webhook URL
*/
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
abstract createWebhook<
TCallback extends (request: WebhookRequest, ...args: any[]) => any
>(
- _callback: TCallback,
- ..._extraArgs: TCallback extends (req: any, ...rest: infer R) => any
+ callback: TCallback,
+ ...extraArgs: TCallback extends (req: any, ...rest: infer R) => any
? R
: []
): Promise;
@@ -146,5 +147,6 @@ export abstract class Network extends ITool {
* @param url - The webhook URL to delete
* @returns Promise that resolves when the webhook is deleted
*/
- abstract deleteWebhook(_url: string): Promise;
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ abstract deleteWebhook(url: string): Promise;
}
diff --git a/sdk/src/tools/plot.ts b/sdk/src/tools/plot.ts
index 78b2ab5..b3e82a5 100644
--- a/sdk/src/tools/plot.ts
+++ b/sdk/src/tools/plot.ts
@@ -133,7 +133,8 @@ export abstract class Plot extends ITool {
* @param activity - The activity data to create
* @returns Promise resolving to the complete created activity
*/
- abstract createActivity(_activity: NewActivity): Promise;
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ abstract createActivity(activity: NewActivity): Promise;
/**
* Creates multiple activities in a single batch operation.
@@ -145,7 +146,8 @@ export abstract class Plot extends ITool {
* @param activities - Array of activity data to create
* @returns Promise resolving to array of created activities
*/
- abstract createActivities(_activities: NewActivity[]): Promise;
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ abstract createActivities(activities: NewActivity[]): Promise;
/**
* Updates an existing activity in the Plot system.
@@ -198,7 +200,8 @@ export abstract class Plot extends ITool {
* });
* ```
*/
- abstract updateActivity(_activity: ActivityUpdate): Promise;
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ abstract updateActivity(activity: ActivityUpdate): Promise;
/**
* Retrieves all activities in the same thread as the specified activity.
@@ -210,7 +213,8 @@ export abstract class Plot extends ITool {
* @param activity - The activity whose thread to retrieve
* @returns Promise resolving to array of activities in the thread
*/
- abstract getThread(_activity: Activity): Promise;
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ abstract getThread(activity: Activity): Promise;
/**
* Finds an activity by its metadata.
@@ -222,7 +226,8 @@ export abstract class Plot extends ITool {
* @param meta - The activity metadata to search for
* @returns Promise resolving to the matching activity or null if not found
*/
- abstract getActivityByMeta(_meta: ActivityMeta): Promise;
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ abstract getActivityByMeta(meta: ActivityMeta): Promise;
/**
* Creates a new priority in the Plot system.
@@ -233,7 +238,8 @@ export abstract class Plot extends ITool {
* @param priority - The priority data to create
* @returns Promise resolving to the complete created priority
*/
- abstract createPriority(_priority: NewPriority): Promise;
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ abstract createPriority(priority: NewPriority): Promise;
/**
* Adds contacts to the Plot system.
@@ -245,5 +251,6 @@ export abstract class Plot extends ITool {
* @param contacts - Array of contact information to add
* @returns Promise that resolves when all contacts have been processed
*/
- abstract addContacts(_contacts: Array): Promise;
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ abstract addContacts(contacts: Array): Promise;
}
diff --git a/sdk/src/tools/store.ts b/sdk/src/tools/store.ts
index b58990e..b38c7d3 100644
--- a/sdk/src/tools/store.ts
+++ b/sdk/src/tools/store.ts
@@ -55,7 +55,8 @@ export abstract class Store extends ITool {
* @param key - The storage key to retrieve
* @returns Promise resolving to the stored value or null
*/
- abstract get(_key: string): Promise;
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ abstract get(key: string): Promise;
/**
* Stores a value in persistent storage.
@@ -68,7 +69,8 @@ export abstract class Store extends ITool {
* @param value - The value to store (must be JSON-serializable)
* @returns Promise that resolves when the value is stored
*/
- abstract set(_key: string, _value: T): Promise;
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ abstract set(key: string, value: T): Promise;
/**
* Removes a specific key from storage.
@@ -79,7 +81,8 @@ export abstract class Store extends ITool {
* @param key - The storage key to remove
* @returns Promise that resolves when the key is removed
*/
- abstract clear(_key: string): Promise;
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ abstract clear(key: string): Promise;
/**
* Removes all keys from this storage instance.
diff --git a/sdk/src/tools/tasks.ts b/sdk/src/tools/tasks.ts
index fd7fa72..a697c80 100644
--- a/sdk/src/tools/tasks.ts
+++ b/sdk/src/tools/tasks.ts
@@ -68,9 +68,10 @@ export abstract class Tasks extends ITool {
* @param options.runAt - If provided, schedules execution at this time; otherwise runs immediately
* @returns Promise resolving to a cancellation token (only for scheduled executions)
*/
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
abstract runTask(
- _callback: Callback,
- _options?: { runAt?: Date }
+ callback: Callback,
+ options?: { runAt?: Date }
): Promise;
/**
@@ -82,7 +83,8 @@ export abstract class Tasks extends ITool {
* @param token - The cancellation token returned by runTask() with runAt option
* @returns Promise that resolves when the cancellation is processed
*/
- abstract cancelTask(_token: string): Promise;
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ abstract cancelTask(token: string): Promise;
/**
* Cancels all scheduled executions for this tool/agent.
diff --git a/sdk/typedoc.json b/sdk/typedoc.json
new file mode 100644
index 0000000..d42fb57
--- /dev/null
+++ b/sdk/typedoc.json
@@ -0,0 +1,99 @@
+{
+ "$schema": "https://typedoc.org/schema.json",
+ "entryPoints": [
+ "src/index.ts",
+ "src/agent.ts",
+ "src/tool.ts",
+ "src/plot.ts",
+ "src/tag.ts",
+ "src/tools/agents.ts",
+ "src/tools/ai.ts",
+ "src/tools/callbacks.ts",
+ "src/tools/integrations.ts",
+ "src/tools/network.ts",
+ "src/tools/plot.ts",
+ "src/tools/store.ts",
+ "src/tools/tasks.ts",
+ "src/common/calendar.ts",
+ "src/utils/types.ts"
+ ],
+ "out": "dist/docs",
+ "tsconfig": "./tsconfig.json",
+ "exclude": [
+ "**/*+(.spec|.test).ts",
+ "**/llm-docs/**",
+ "**/cli/**",
+ "src/agents-guide.ts",
+ "src/sdk-docs.ts",
+ "prebuild.ts",
+ "postbuild.js"
+ ],
+ "excludePrivate": true,
+ "excludeProtected": false,
+ "excludeInternal": true,
+ "excludeExternals": true,
+ "readme": "docs/index.md",
+ "projectDocuments": [
+ "docs/GETTING_STARTED.md",
+ "docs/CORE_CONCEPTS.md",
+ "docs/TOOLS_GUIDE.md",
+ "docs/BUILDING_TOOLS.md",
+ "docs/CLI_REFERENCE.md",
+ "docs/RUNTIME.md",
+ "docs/ADVANCED.md"
+ ],
+ "name": "Build Plot Agents",
+ "includeVersion": false,
+ "disableSources": false,
+ "sourceLinkTemplate": "https://github.com/plotday/plot/blob/main/sdk/{path}#L{line}",
+ "navigation": {
+ "includeCategories": true,
+ "includeGroups": true
+ },
+ "categorizeByGroup": true,
+ "categoryOrder": [
+ "Agent",
+ "Tool",
+ "Data Types",
+ "Tools",
+ "Utilities",
+ "*"
+ ],
+ "groupOrder": [
+ "Classes",
+ "Interfaces",
+ "Type Aliases",
+ "Enumerations",
+ "Functions",
+ "*"
+ ],
+ "sort": [
+ "source-order",
+ "required-first",
+ "kind"
+ ],
+ "kindSortOrder": [
+ "Class",
+ "Interface",
+ "TypeAlias",
+ "Enum",
+ "Function",
+ "Variable"
+ ],
+ "searchInComments": true,
+ "searchInDocuments": true,
+ "cleanOutputDir": true,
+ "sidebarLinks": {},
+ "navigationLinks": {
+ "Plot": "https://plot.day",
+ "GitHub": "https://github.com/plotday/plot",
+ "NPM": "https://www.npmjs.com/package/@plotday/sdk"
+ },
+ "titleLink": "/",
+ "highlightLanguages": [
+ "bash",
+ "json",
+ "typescript",
+ "markdown"
+ ]
+}