{"_id":"@ai-outfitter/channels","_rev":"8-fd51db0ec46593205d5b6e00cdf12f5b","name":"@ai-outfitter/channels","dist-tags":{"latest":"1.12.1"},"versions":{"1.5.0":{"name":"@ai-outfitter/channels","version":"1.5.0","keywords":["pi-package","ai-outfitter","channels"],"license":"MIT","_id":"@ai-outfitter/channels@1.5.0","maintainers":[{"name":"ncrmro","email":"ncrmro@gmail.com"}],"homepage":"https://github.com/ai-outfitter/channels#readme","bugs":{"url":"https://github.com/ai-outfitter/channels/issues"},"pi":{"extensions":["./extensions/index.ts","./extensions/relay-extension.ts"]},"dist":{"shasum":"ba5746f83073b31ffac3b0c42df37d5f020c3421","tarball":"https://registry.npmjs.org/@ai-outfitter/channels/-/channels-1.5.0.tgz","fileCount":94,"integrity":"sha512-RiKMu9oegcDyRAKT8SMYBnHwjrTnS2v3J6128gS/XC1QpplPTOC27eqHqo/CJ6hEpnlzDWOagi1KDFTa62D9jw==","signatures":[{"sig":"MEQCIE9y8zgMjC6F5OXUbyalakVYiS4qxO4TQhDCHQYFxirSAiAUua6+yk1K6bvRbIxJE6dl/t5f1hRwNvhnAOiV8qwNfA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1186055},"type":"module","gitHead":"d1e9049932947b1a17ac6a4b9cdb4f6009674d38","scripts":{"lint":"biome check dev/*.ts extensions/ tests/","test":"node --test --experimental-strip-types tests/*.test.ts","check":"biome check dev/*.ts extensions/ tests/ && tsc --noEmit && npm test","relay":"node --experimental-strip-types extensions/relay/server.ts","format":"biome check --write dev/*.ts extensions/ tests/","dev:slack":"bash dev/run-slack-local.sh","typecheck":"tsc --noEmit","setup:slack":"bash dev/setup-slack-cli-app.sh","verify:slack":"node --experimental-strip-types dev/slack-verify.ts","dev:slack:cli":"bash dev/run-slack-cli-local.sh"},"_npmUser":{"name":"ncrmro","email":"ncrmro@gmail.com"},"repository":{"url":"git+https://github.com/ai-outfitter/channels.git","type":"git"},"_npmVersion":"11.16.0","description":"Pi extension that pushes channel events (email/Slack/Signal/…) into an agent session, waking it only when real work arrives.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"undici":"^7.29.0","typebox":"^1.3.8","@slack/web-api":"^8.0.0","@bufbuild/protobuf":"^1.10.1","@slack/socket-mode":"^3.0.0","@connectrpc/connect":"^1.7.0","@connectrpc/connect-web":"^1.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.9.3","@types/node":"^22.20.1","@biomejs/biome":"2.5.5"},"peerDependencies":{"@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/channels_1.5.0_1785861317729_0.22392437132831122","host":"s3://npm-registry-packages-npm-production"}},"1.8.0":{"name":"@ai-outfitter/channels","version":"1.8.0","keywords":["pi-package","ai-outfitter","channels"],"license":"MIT","_id":"@ai-outfitter/channels@1.8.0","maintainers":[{"name":"ncrmro","email":"ncrmro@gmail.com"}],"homepage":"https://github.com/ai-outfitter/channels#readme","bugs":{"url":"https://github.com/ai-outfitter/channels/issues"},"pi":{"extensions":["./extensions/runtime-extension.ts"]},"bin":{"outfitter-channel-send":"dist/bin/outfitter-channel-send.js","outfitter-channel-reconcile":"dist/bin/outfitter-channel-reconcile.js"},"dist":{"shasum":"9263b2a2e51b6e1a6f82a43f7076b0f61131ef84","tarball":"https://registry.npmjs.org/@ai-outfitter/channels/-/channels-1.8.0.tgz","fileCount":132,"integrity":"sha512-wf+TLo9/fOIT19inbYFI6fVPc7yNmkPVt9OqO7xIkaN0pgsu+WoIceLWLOP3yoyudBuuPCRrQDXOVxvt8k2LIg==","signatures":[{"sig":"MEYCIQCJrHNcwDcNHzZh7LL8Tr0Z0JczIkr2UaKZNO7Aa9sYDwIhAItbEK7/EHIMXFDzv6cGkAl8nfr7sk27Fdm0N77W68s9","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ai-outfitter%2fchannels@1.8.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1563521},"type":"module","exports":{".":"./extensions/runtime-extension.ts","./*":"./*","./channel-send":"./extensions/agent/channel-send.ts"},"gitHead":"b75af887b0720d0ca188339b834a5adac486dbf5","scripts":{"lint":"biome check bin/*.ts dev/*.ts dev/*.mjs extensions/ tests/","test":"npm run build:cli && node --test --experimental-strip-types tests/*.test.ts && node tests/package-bin.e2e.mjs","check":"biome check bin/*.ts dev/*.ts dev/*.mjs extensions/ tests/ && tsc --noEmit && npm test","relay":"node --experimental-strip-types extensions/relay/server.ts","format":"biome check --write bin/*.ts dev/*.ts dev/*.mjs extensions/ tests/","prepack":"npm run build:cli","build:cli":"tsc --project tsconfig.cli.json && node dev/mark-cli-executable.mjs","dev:slack":"bash dev/run-slack-local.sh","typecheck":"tsc --noEmit","setup:slack":"bash dev/setup-slack-cli-app.sh","verify:slack":"node --experimental-strip-types dev/slack-verify.ts","dev:slack:cli":"bash dev/run-slack-cli-local.sh"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:728d8df0-e694-43f5-bee0-d8a3f993903d"}},"repository":{"url":"git+https://github.com/ai-outfitter/channels.git","type":"git"},"_npmVersion":"11.16.0","description":"Pi extension that pushes channel events (email/Slack/Signal/…) into an agent session, waking it only when real work arrives.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"undici":"^7.29.0","typebox":"^1.3.8","@slack/web-api":"^8.0.0","@bufbuild/protobuf":"^1.10.1","@slack/socket-mode":"^3.0.0","@connectrpc/connect":"^1.7.0","@connectrpc/connect-web":"^1.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.9.3","@types/node":"^22.20.1","@biomejs/biome":"2.5.5"},"peerDependencies":{"@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/channels_1.8.0_1786895623345_0.9935477655397034","host":"s3://npm-registry-packages-npm-production"}},"1.9.0":{"name":"@ai-outfitter/channels","version":"1.9.0","keywords":["pi-package","ai-outfitter","channels"],"license":"MIT","_id":"@ai-outfitter/channels@1.9.0","maintainers":[{"name":"ncrmro","email":"ncrmro@gmail.com"}],"homepage":"https://github.com/ai-outfitter/channels#readme","bugs":{"url":"https://github.com/ai-outfitter/channels/issues"},"pi":{"extensions":["./extensions/runtime-extension.ts"]},"bin":{"outfitter-channel-send":"dist/bin/outfitter-channel-send.js","outfitter-channel-reconcile":"dist/bin/outfitter-channel-reconcile.js"},"dist":{"shasum":"ea75c567346a55dc4b038c92ff8a6697cd1b8202","tarball":"https://registry.npmjs.org/@ai-outfitter/channels/-/channels-1.9.0.tgz","fileCount":132,"integrity":"sha512-oaq3yM59OkyZ1DKHsw9tMPhrW0eKZc2uQBR4uYS7p6kYKVis2ZuySt6IMi1CRLvF/nzc6WuS4PqIO+ajoQ6Q2Q==","signatures":[{"sig":"MEUCIQCakk/dTdpbzC2gtLeeAF5kKM0/VgZgh5/MmZ1K7blsigIgc3botTiQ3i5WuO8SREgON6Q/0XicCYESwThmGP2PZgo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ai-outfitter%2fchannels@1.9.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1567002},"type":"module","exports":{".":"./extensions/runtime-extension.ts","./*":"./*","./channel-send":"./extensions/agent/channel-send.ts"},"gitHead":"7e5c252309b44e8835c421ee34c500998dac5554","scripts":{"lint":"biome check bin/*.ts dev/*.ts dev/*.mjs extensions/ tests/","test":"npm run build:cli && node --test --experimental-strip-types tests/*.test.ts && node tests/package-bin.e2e.mjs","check":"biome check bin/*.ts dev/*.ts dev/*.mjs extensions/ tests/ && tsc --noEmit && npm test","relay":"node --experimental-strip-types extensions/relay/server.ts","format":"biome check --write bin/*.ts dev/*.ts dev/*.mjs extensions/ tests/","prepack":"npm run build:cli","build:cli":"tsc --project tsconfig.cli.json && node dev/mark-cli-executable.mjs","dev:slack":"bash dev/run-slack-local.sh","typecheck":"tsc --noEmit","setup:slack":"bash dev/setup-slack-cli-app.sh","verify:slack":"node --experimental-strip-types dev/slack-verify.ts","dev:slack:cli":"bash dev/run-slack-cli-local.sh"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:728d8df0-e694-43f5-bee0-d8a3f993903d"}},"repository":{"url":"git+https://github.com/ai-outfitter/channels.git","type":"git"},"_npmVersion":"11.16.0","description":"Pi extension that pushes channel events (email/Slack/Signal/…) into an agent session, waking it only when real work arrives.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"undici":"^7.29.0","typebox":"^1.3.8","@slack/web-api":"^8.0.0","@bufbuild/protobuf":"^1.10.1","@slack/socket-mode":"^3.0.0","@connectrpc/connect":"^1.7.0","@connectrpc/connect-web":"^1.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.9.3","@types/node":"^22.20.1","@biomejs/biome":"2.5.5"},"peerDependencies":{"@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/channels_1.9.0_1787404002241_0.1458198295230051","host":"s3://npm-registry-packages-npm-production"}},"1.10.0":{"name":"@ai-outfitter/channels","version":"1.10.0","keywords":["pi-package","ai-outfitter","channels"],"license":"MIT","_id":"@ai-outfitter/channels@1.10.0","maintainers":[{"name":"ncrmro","email":"ncrmro@gmail.com"}],"homepage":"https://github.com/ai-outfitter/channels#readme","bugs":{"url":"https://github.com/ai-outfitter/channels/issues"},"pi":{"extensions":["./extensions/runtime-extension.ts"]},"bin":{"outfitter-channel-send":"dist/bin/outfitter-channel-send.js","outfitter-channel-reconcile":"dist/bin/outfitter-channel-reconcile.js"},"dist":{"shasum":"004ee76c9b394e27d02dd187efe1cf5202a57d74","tarball":"https://registry.npmjs.org/@ai-outfitter/channels/-/channels-1.10.0.tgz","fileCount":133,"integrity":"sha512-vypT06+OtJx3Po8368cn1KW4pRxqoyXXNfGoQV+IijKqc5LURbpe3uTwEtjqO7CJWTvtY+QDAopJQflnDWVT4w==","signatures":[{"sig":"MEQCIBjzaioSu3Nf+gRCnhYiyC96ImzOcGb55ODFTtRK+arfAiB8HouWqjQu4JNqVjvp2n5TSapg5fl+SGd99o2T2zyP7A==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ai-outfitter%2fchannels@1.10.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1591422},"type":"module","exports":{".":"./extensions/runtime-extension.ts","./*":"./*","./channel-send":"./extensions/agent/channel-send.ts"},"gitHead":"552e8789d6dd14ac92102b59b9f67b5b5e6a8852","scripts":{"lint":"biome check bin/*.ts dev/*.ts dev/*.mjs extensions/ tests/","test":"npm run build:cli && node --test --experimental-strip-types tests/*.test.ts && node tests/package-bin.e2e.mjs","check":"biome check bin/*.ts dev/*.ts dev/*.mjs extensions/ tests/ && tsc --noEmit && npm test","relay":"node --experimental-strip-types extensions/relay/server.ts","format":"biome check --write bin/*.ts dev/*.ts dev/*.mjs extensions/ tests/","prepack":"npm run build:cli","build:cli":"tsc --project tsconfig.cli.json && node dev/mark-cli-executable.mjs","dev:slack":"bash dev/run-slack-local.sh","typecheck":"tsc --noEmit","setup:slack":"bash dev/setup-slack-cli-app.sh","verify:slack":"node --experimental-strip-types dev/slack-verify.ts","dev:slack:cli":"bash dev/run-slack-cli-local.sh"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:728d8df0-e694-43f5-bee0-d8a3f993903d"}},"repository":{"url":"git+https://github.com/ai-outfitter/channels.git","type":"git"},"_npmVersion":"11.16.0","description":"Pi extension that pushes channel events (email/Slack/Signal/…) into an agent session, waking it only when real work arrives.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"undici":"^7.29.0","typebox":"^1.3.8","@slack/web-api":"^8.0.0","@bufbuild/protobuf":"^1.10.1","@slack/socket-mode":"^3.0.0","@connectrpc/connect":"^1.7.0","@connectrpc/connect-web":"^1.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.9.3","@types/node":"^22.20.1","@biomejs/biome":"2.5.5"},"peerDependencies":{"@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/channels_1.10.0_1788367928495_0.5569756419355079","host":"s3://npm-registry-packages-npm-production"}},"1.11.0":{"name":"@ai-outfitter/channels","version":"1.11.0","keywords":["pi-package","ai-outfitter","channels"],"license":"MIT","_id":"@ai-outfitter/channels@1.11.0","maintainers":[{"name":"ncrmro","email":"ncrmro@gmail.com"}],"homepage":"https://github.com/ai-outfitter/channels#readme","bugs":{"url":"https://github.com/ai-outfitter/channels/issues"},"pi":{"extensions":["./extensions/runtime-extension.ts"]},"bin":{"outfitter-channel-send":"dist/bin/outfitter-channel-send.js","outfitter-channel-reconcile":"dist/bin/outfitter-channel-reconcile.js"},"dist":{"shasum":"ccfba71d58154256ed85c4d6de7075fda99ff6a8","tarball":"https://registry.npmjs.org/@ai-outfitter/channels/-/channels-1.11.0.tgz","fileCount":134,"integrity":"sha512-R/9qouc+PEsrUUZvXWA1x7xkaxMyHOZBL+0w6IkGw2d8a1UrXH1ikysPQEkqyG2VvRYEtGJLv+a5uBE6x+5wPA==","signatures":[{"sig":"MEUCIQCDMPkXOjf105tA6k2wJOb9j5NQb+RPOGHAcd0q4Vcz4AIgCRvr35+5ZUZkdyVfh8IH15l0UB8wC6uEzgPKyWwjP7g=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ai-outfitter%2fchannels@1.11.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1601161},"type":"module","exports":{".":"./extensions/runtime-extension.ts","./*":"./*","./channel-send":"./extensions/agent/channel-send.ts"},"gitHead":"7a005d78b6af98be6a6d0945da995f72ba21c8dd","scripts":{"lint":"biome check bin/*.ts dev/*.ts dev/*.mjs extensions/ tests/","test":"npm run build:cli && node --test --experimental-strip-types tests/*.test.ts && node tests/package-bin.e2e.mjs","check":"biome check bin/*.ts dev/*.ts dev/*.mjs extensions/ tests/ && tsc --noEmit && npm test","relay":"node --experimental-strip-types extensions/relay/server.ts","format":"biome check --write bin/*.ts dev/*.ts dev/*.mjs extensions/ tests/","prepack":"npm run build:cli","build:cli":"tsc --project tsconfig.cli.json && node dev/mark-cli-executable.mjs","dev:slack":"bash dev/run-slack-local.sh","typecheck":"tsc --noEmit","setup:slack":"bash dev/setup-slack-cli-app.sh","verify:slack":"node --experimental-strip-types dev/slack-verify.ts","dev:slack:cli":"bash dev/run-slack-cli-local.sh"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:728d8df0-e694-43f5-bee0-d8a3f993903d"}},"repository":{"url":"git+https://github.com/ai-outfitter/channels.git","type":"git"},"_npmVersion":"11.16.0","description":"Pi extension that pushes channel events (email/Slack/Signal/…) into an agent session, waking it only when real work arrives.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"undici":"^7.29.0","typebox":"^1.3.8","@slack/web-api":"^8.0.0","@bufbuild/protobuf":"^1.10.1","@slack/socket-mode":"^3.0.0","@connectrpc/connect":"^1.7.0","@connectrpc/connect-web":"^1.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.9.3","@types/node":"^22.20.1","@biomejs/biome":"2.5.5"},"peerDependencies":{"@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/channels_1.11.0_1788800830496_0.1600282883843529","host":"s3://npm-registry-packages-npm-production"}},"1.11.1":{"name":"@ai-outfitter/channels","version":"1.11.1","keywords":["pi-package","ai-outfitter","channels"],"license":"MIT","_id":"@ai-outfitter/channels@1.11.1","maintainers":[{"name":"ncrmro","email":"ncrmro@gmail.com"}],"homepage":"https://github.com/ai-outfitter/channels#readme","bugs":{"url":"https://github.com/ai-outfitter/channels/issues"},"pi":{"extensions":["./extensions/runtime-extension.ts"]},"bin":{"outfitter-channel-send":"dist/bin/outfitter-channel-send.js","outfitter-channel-reconcile":"dist/bin/outfitter-channel-reconcile.js"},"dist":{"shasum":"37a4433f27800de441169a43279e78522e51d9ed","tarball":"https://registry.npmjs.org/@ai-outfitter/channels/-/channels-1.11.1.tgz","fileCount":135,"integrity":"sha512-EYaMqagYStjmPt5xwCmEcAemnixSsaqrWYnh4I5ycB8F7UJCs594yxcG+9aQhF4x4t/ix9WRnMZmuawKRuA0Qw==","signatures":[{"sig":"MEUCIQC8ACi3VlUcUjU3nf5lXcYoRX1ZBRfIAOKiJmhMlBDQBQIgISyqGjoiC300vwGDAL/P5w3nfwML+/S1jIcc/brXQfI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ai-outfitter%2fchannels@1.11.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1603584},"type":"module","exports":{".":"./extensions/runtime-extension.ts","./*":"./*","./channel-send":"./extensions/agent/channel-send.ts"},"gitHead":"c1c407e9e052275d72465ec97f35eb6fda40fcbb","scripts":{"lint":"biome check bin/*.ts dev/*.ts dev/*.mjs extensions/ tests/","test":"npm run build:cli && node --test --experimental-strip-types tests/*.test.ts && node tests/package-bin.e2e.mjs","check":"biome check bin/*.ts dev/*.ts dev/*.mjs extensions/ tests/ && tsc --noEmit && npm test","relay":"node --experimental-strip-types extensions/relay/server.ts","format":"biome check --write bin/*.ts dev/*.ts dev/*.mjs extensions/ tests/","prepack":"npm run build:cli","build:cli":"tsc --project tsconfig.cli.json && node dev/mark-cli-executable.mjs","dev:slack":"bash dev/run-slack-local.sh","typecheck":"tsc --noEmit","setup:slack":"bash dev/setup-slack-cli-app.sh","verify:slack":"node --experimental-strip-types dev/slack-verify.ts","dev:slack:cli":"bash dev/run-slack-cli-local.sh"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:728d8df0-e694-43f5-bee0-d8a3f993903d"}},"repository":{"url":"git+https://github.com/ai-outfitter/channels.git","type":"git"},"_npmVersion":"11.16.0","description":"Pi extension that pushes channel events (email/Slack/Signal/…) into an agent session, waking it only when real work arrives.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"undici":"^7.29.0","typebox":"^1.3.8","@slack/web-api":"^8.0.0","@bufbuild/protobuf":"^1.10.1","@slack/socket-mode":"^3.0.0","@connectrpc/connect":"^1.7.0","@connectrpc/connect-web":"^1.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.9.3","@types/node":"^22.20.1","@biomejs/biome":"2.5.5"},"peerDependencies":{"@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/channels_1.11.1_1788894676255_0.9138340425421392","host":"s3://npm-registry-packages-npm-production"}},"1.12.0":{"name":"@ai-outfitter/channels","version":"1.12.0","keywords":["pi-package","ai-outfitter","channels"],"license":"MIT","_id":"@ai-outfitter/channels@1.12.0","maintainers":[{"name":"ncrmro","email":"ncrmro@gmail.com"}],"homepage":"https://github.com/ai-outfitter/channels#readme","bugs":{"url":"https://github.com/ai-outfitter/channels/issues"},"pi":{"extensions":["./extensions/runtime-extension.ts"]},"bin":{"outfitter-channel-send":"dist/bin/outfitter-channel-send.js","outfitter-channel-reconcile":"dist/bin/outfitter-channel-reconcile.js"},"dist":{"shasum":"ed6e1d2f6b95c0dfe377945aecc2502f4259cd88","tarball":"https://registry.npmjs.org/@ai-outfitter/channels/-/channels-1.12.0.tgz","fileCount":136,"integrity":"sha512-UiI/E7+dwxKW3W5uK6pOvo3WI+OFU/qbfEAjsgv9FaqseZftxYWySM0yk3ZX1JXS+KNI6qfSZNVXT/dV+2N7qg==","signatures":[{"sig":"MEQCIBIZMWxUp8k/YJ0fNKwXHF7JwSO6KmMjerayRaNG4EawAiAG3W0mWstDpkvETrNFiMEnTRr9oh9PtAyzolV1fsQQyg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ai-outfitter%2fchannels@1.12.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1622546},"type":"module","exports":{".":"./extensions/runtime-extension.ts","./*":"./*","./channel-send":"./extensions/agent/channel-send.ts"},"gitHead":"23a1b9cffe159119615fb46eda780cbf3a58719d","scripts":{"lint":"biome check bin/*.ts dev/*.ts dev/*.mjs extensions/ tests/","test":"npm run build:cli && node --test --experimental-strip-types tests/*.test.ts && node tests/package-bin.e2e.mjs","check":"biome check bin/*.ts dev/*.ts dev/*.mjs extensions/ tests/ && tsc --noEmit && npm test","relay":"node --experimental-strip-types extensions/relay/server.ts","format":"biome check --write bin/*.ts dev/*.ts dev/*.mjs extensions/ tests/","prepack":"npm run build:cli","build:cli":"tsc --project tsconfig.cli.json && node dev/mark-cli-executable.mjs","dev:slack":"bash dev/run-slack-local.sh","typecheck":"tsc --noEmit","setup:slack":"bash dev/setup-slack-cli-app.sh","verify:slack":"node --experimental-strip-types dev/slack-verify.ts","dev:slack:cli":"bash dev/run-slack-cli-local.sh"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:728d8df0-e694-43f5-bee0-d8a3f993903d"}},"repository":{"url":"git+https://github.com/ai-outfitter/channels.git","type":"git"},"_npmVersion":"11.16.0","description":"Pi extension that pushes channel events (email/Slack/Signal/…) into an agent session, waking it only when real work arrives.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"undici":"^7.29.0","typebox":"^1.3.8","@slack/web-api":"^8.0.0","@bufbuild/protobuf":"^1.10.1","@slack/socket-mode":"^3.0.0","@connectrpc/connect":"^1.7.0","@connectrpc/connect-web":"^1.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.9.3","@types/node":"^22.20.1","@biomejs/biome":"2.5.5"},"peerDependencies":{"@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/channels_1.12.0_1789088583771_0.8715943802686281","host":"s3://npm-registry-packages-npm-production"}},"1.12.1":{"pi":{"extensions":["./extensions/runtime-extension.ts"]},"_id":"@ai-outfitter/channels@1.12.1","bin":{"outfitter-channel-send":"dist/bin/outfitter-channel-send.js","outfitter-channel-reconcile":"dist/bin/outfitter-channel-reconcile.js"},"bugs":{"url":"https://github.com/ai-outfitter/channels/issues"},"dist":{"shasum":"34c54af0207d6fcb1d452bcfe2dd4db84eb4f832","tarball":"https://registry.npmjs.org/@ai-outfitter/channels/-/channels-1.12.1.tgz","fileCount":136,"integrity":"sha512-I6+r9f7HNt9dWw6hBpK0YZvXgkes8WxqBiBoxm94MfGiSMaq2HegXIzXoWq3aMJIxYkqj8wazJ9nCTKq0uEJtQ==","signatures":[{"sig":"MEQCIH76D+WYG7btlKFwWwRVRYWfkDOQkzoWhqKvCsSa1DuZAiBl7rtB3Kej1le/bStGobnLsn72udfjPdFdkucLcOrD6Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFdrPivhmfWX+stFk4sA7x/HRi6rf69Bx8oPWotKQLQvAiAoqGj2rbUFXQXTAmFU295ToyzI+4wq4HZDN8HjnNgucg=="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ai-outfitter%2fchannels@1.12.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1622670},"name":"@ai-outfitter/channels","type":"module","exports":{".":"./extensions/runtime-extension.ts","./*":"./*","./channel-send":"./extensions/agent/channel-send.ts"},"gitHead":"bdf647228a00789130d4a30351ee50ac2df26a40","license":"MIT","scripts":{"lint":"biome check bin/*.ts dev/*.ts dev/*.mjs extensions/ tests/","test":"npm run build:cli && node --test --experimental-strip-types tests/*.test.ts && node tests/package-bin.e2e.mjs","check":"biome check bin/*.ts dev/*.ts dev/*.mjs extensions/ tests/ && tsc --noEmit && npm test","relay":"node --experimental-strip-types extensions/relay/server.ts","format":"biome check --write bin/*.ts dev/*.ts dev/*.mjs extensions/ tests/","prepack":"npm run build:cli","build:cli":"tsc --project tsconfig.cli.json && node dev/mark-cli-executable.mjs","dev:slack":"bash dev/run-slack-local.sh","typecheck":"tsc --noEmit","setup:slack":"bash dev/setup-slack-cli-app.sh","verify:slack":"node --experimental-strip-types dev/slack-verify.ts","dev:slack:cli":"bash dev/run-slack-cli-local.sh"},"version":"1.12.1","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:728d8df0-e694-43f5-bee0-d8a3f993903d"}},"homepage":"https://github.com/ai-outfitter/channels#readme","keywords":["pi-package","ai-outfitter","channels"],"repository":{"url":"git+https://github.com/ai-outfitter/channels.git","type":"git"},"_npmVersion":"11.16.0","description":"Pi extension that pushes channel events (email/Slack/Signal/…) into an agent session, waking it only when real work arrives.","directories":{},"maintainers":[{"name":"ncrmro","email":"ncrmro@gmail.com"}],"_nodeVersion":"24.18.0","dependencies":{"undici":"^7.29.0","typebox":"^1.3.8","@slack/web-api":"^8.0.0","@bufbuild/protobuf":"^1.10.1","@slack/socket-mode":"^3.0.0","@connectrpc/connect":"^1.7.0","@connectrpc/connect-web":"^1.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.9.3","@types/node":"^22.20.1","@biomejs/biome":"2.5.5"},"peerDependencies":{"@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/channels_1.12.1_1789695549039_0.5934714761989361"}}},"time":{"created":"2026-08-04T16:35:17.605Z","modified":"2026-09-18T01:39:09.427Z","1.5.0":"2026-08-04T16:35:17.958Z","1.8.0":"2026-08-16T15:53:43.503Z","1.9.0":"2026-08-22T13:06:42.392Z","1.10.0":"2026-09-02T16:52:08.660Z","1.11.0":"2026-09-07T17:07:10.630Z","1.11.1":"2026-09-08T19:11:16.390Z","1.12.0":"2026-09-11T01:03:03.940Z","1.12.1":"2026-09-18T01:39:09.122Z"},"bugs":{"url":"https://github.com/ai-outfitter/channels/issues"},"license":"MIT","homepage":"https://github.com/ai-outfitter/channels#readme","keywords":["pi-package","ai-outfitter","channels"],"repository":{"url":"git+https://github.com/ai-outfitter/channels.git","type":"git"},"description":"Pi extension that pushes channel events (email/Slack/Signal/…) into an agent session, waking it only when real work arrives.","maintainers":[{"name":"ncrmro","email":"ncrmro@gmail.com"}],"readme":"# channels\n\nA [Pi](https://github.com/earendil-works/pi) extension that watches email,\nSignal, GitHub notifications, and mentions in Slack, Chatto, Mattermost, and\nZulip, then runs one durable Pi session per Task only when a source detects matching work.\nSources may use push connections, local daemons, or lightweight polling.\nMultiple channels share one durable Task wake queue.\n\nThe wake is a trusted, body-free Task reference. Wake prompts contain no message\ncontent. For exact items, `channel_read` returns\nfetched content inside explicit untrusted-content markers.\n\n## Versioning\n\nChannels is alpha software. The `v1.x` series is an accident of the initial\nrelease-please configuration, not a semantic-versioning stability contract.\nBreaking changes to wake prompts, source behavior, environment variables, and\nother profile-facing surfaces can land in any release; those changes are\nrecorded in the release notes. Downstream profiles and skills should pin an\nexact revision and adopt new releases deliberately.\n\nRelease-please owns version bumps, so contributors should not edit the version\nin `package.json` by hand. When Channels declares stability, this section will\ninstead define the surfaces covered by the version number.\n\n## Install\n\nInstall it into pi like any other package (pi loads the raw TypeScript via jiti —\nno build step):\n\n```bash\npi install git:github.com/ai-outfitter/channels     # writes ~/.pi/agent/settings.json\n```\n\nVariants:\n\n```bash\npi install -l git:github.com/ai-outfitter/channels   # project scope (.pi/settings.json), team-shareable\npi -e git:github.com/ai-outfitter/channels           # load for one run only, no install\n```\n\nOr add it by hand to the `packages` array in `~/.pi/agent/settings.json`:\n\n```json\n{ \"packages\": [\"git:github.com/ai-outfitter/channels\"] }\n```\n\nConfirm with `pi list`; update later with `pi update --extensions`.\n\n## Run it resident\n\nA channel watcher starts its connection, daemon, or polling loop with the session\nand stops it when the session ends, so it needs a **long-running session**:\n\n- **Interactive:** just run `pi` — the connections stay open until you quit.\n- **Headless:** `pi --mode rpc` for an unattended, programmatically-driven session.\n\nAvoid `-p`/`--print` (one-shot, exits immediately). Switching sessions (`/new`,\n`/resume`, `/fork`, `/reload`) tears the connections down and reopens them on the\nnew session — that's expected.\n\n## Pair each channel with a skill\n\nThe extension provides wake transport plus `channel_read` and\n`channel_respond`. It also provides `channel_publish` for configured adapters.\nSkills teach the agent when to use them. Responder skills\nlive in this repository (for example\n[`dev/slack-responder/SKILL.md`](dev/slack-responder/SKILL.md)) or in a\ncatalog you control — the\n[`ai-outfitter/community-profiles`](https://github.com/ai-outfitter/community-profiles)\ncatalog does not publish channel responder skills. Enable a matching skill\nwhen one is available. Chatto,\nMattermost, and Zulip expose the common tools directly, so the agent's profile\nor instructions must define when to read and respond. The existing JMAP, Signal,\nand GitHub skills retain their current workflows until their exact-item adapters\nare implemented.\n\n## Choose which channels run\n\nSet `OUTFITTER_CHANNELS` in your shell before launching pi:\n\n| `OUTFITTER_CHANNELS` | Behavior |\n| --- | --- |\n| unset | **Auto-detect** — start every channel whose credentials are present. |\n| `jmap,signal` | Start exactly those channels. Separate names with commas or spaces. Valid names: `jmap`, `signal`, `github`, `forgejo`, `slack`, `agent`, `chatto`, `mattermost`, `zulip`. |\n| `agent` | Start the native agent session chat channel. |\n| `off` / `none` | Disabled. |\n\nAuto-detect enables a channel when its required environment variables are\npresent.\n\n### Native agent session chat — `agent`\n\nThe `agent` channel carries agent-to-agent and authorized operator-to-agent chat.\nIt adds `agent_list` and `agent_send` for discovery and sending, while incoming\nmessages continue through `channel_read` and `channel_respond`. Wakes contain\nonly an opaque `agent:v1` locator.\n\nFor two agents on the same host, point both at one permission-restricted spool\nand give each a stable endpoint:\n\n```bash\nexport OUTFITTER_CHANNELS=agent\nexport AGENT_ENDPOINT_ID=researcher\nexport AGENT_PRINCIPAL_ID=agent:researcher       # optional; defaults to endpoint\nexport AGENT_SPOOL_PATH=/var/lib/outfitter/agent-spool\nexport AGENT_SPOOL_POLL_MS=250                   # optional; minimum 25\npi --mode rpc\n```\n\nMessages are committed atomically before send returns, survive process restarts,\nand are idempotent when the sender retries with the same message ID.\n\nA scheduled process can send one filesystem message without a running sender\nsession. The command uses `AGENT_SPOOL_PATH` and the same spool contract:\n\n```bash\noutfitter-channel-send scheduler resident wake-2026-08-15 \"Run the scheduled task.\"\n```\n\nThe four arguments are `sender`, `recipient`, `message_id`, and `body`. A retry\nwith the same message ID and body returns success. Reuse with different content\nfails. The recipient reads the message after its resident session starts again.\n\nFor remote clients, use the same endpoint/principal variables with:\n\n```bash\nexport AGENT_RELAY_URL=wss://relay.example.com/v1/connect\nexport AGENT_RELAY_TOKEN=replace-with-revocable-secret\n```\n\nMessages, state transitions, and the relay delivery checkpoint are appended to\nPi's native JSONL session as custom entries. The client acknowledges a relay\ndelivery only after both the envelope and checkpoint have been appended. A\nworkspace PVC therefore preserves the canonical conversation state across\nresident-agent restarts; there is no separate channel transcript or cursor\ndatabase.\n\n### Run the Channels relay\n\nThe relay is a separately runnable, single-node HTTPS/WSS service with durable\noffline queues:\n\n```bash\nexport AGENT_RELAY_HOST=0.0.0.0\nexport AGENT_RELAY_PORT=8787\nexport AGENT_RELAY_STORE_PATH=/var/lib/channels/relay-delivery.json\nexport AGENT_RELAY_CREDENTIALS_PATH=/run/secrets/relay-credentials.json\nexport AGENT_RELAY_TLS_KEY_PATH=/run/secrets/tls.key\nexport AGENT_RELAY_TLS_CERT_PATH=/run/secrets/tls.crt\nnpm run relay\n```\n\nThe single-node relay atomically stores only bounded, unacknowledged delivery\nenvelopes. It compacts message bodies immediately after recipient ACK and keeps\nonly bounded, body-free hashes for retry deduplication. Stale unacknowledged\nenvelopes expire after seven days. Transcript queries are correlated and routed\nto the connected target agent, which answers from its Pi session; the relay\nnever answers them from its delivery store. Runtime limits default to 1,000\nconnections and 120 client frames per minute; deployments may lower them with\n`AGENT_RELAY_MAX_CONNECTIONS`, `AGENT_RELAY_MAX_FRAMES_PER_WINDOW`, and\n`AGENT_RELAY_RATE_WINDOW_MS`.\n\nThe credentials document authorizes registration, routes, and discovery:\n\n```json\n{\n  \"credentials\": [\n    {\n      \"token\": \"replace-with-secret\",\n      \"principal\": \"agent:researcher\",\n      \"register\": [\"researcher\"],\n      \"send\": [\"reviewer\"],\n      \"list\": [\"reviewer\"]\n    }\n  ]\n}\n```\n\n`send: [\"*\"]` explicitly allows every recipient. Keep credentials in a\npermission-restricted secret file. The relay requires TLS, except when\n`AGENT_RELAY_ALLOW_INSECURE=1` is explicitly set on a loopback-only development\nlistener. WSS connects at `/v1/connect`; HTTPS liveness and readiness are\n`/healthz` and `/readyz`.\n\n## Set up each channel\n\npi has no per-extension config file, so each channel is configured with **shell\nenvironment variables** you export before running `pi` (put them in your shell\nprofile, an `.envrc`, or a systemd unit for a persistent watcher). Credentials\nbelong to the extension adapter; existing non-tool skills may reuse them.\n\n### Email — `jmap`\n\nWatches a JMAP mailbox's `Email` state over an EventSource (SSE) and wakes\nfor each exact message newly present in INBOX. The wake carries an opaque\ntask-bound locator. `channel_read` fetches only that email's subject, addresses,\ndate, and bounded text body, and `channel_respond` replies to its sender through\n`EmailSubmission/set`; neither operation scans the inbox. Each reply carries a\ndeterministic delivery header. After a crash, an exact header lookup reconciles\nonly a non-draft email before any retry. If draft creation succeeded but\nsubmission failed, the retry submits that same draft instead of duplicating it.\n\nIt also wakes on JMAP `CalendarAlert` pushes. When a calendar event's alarm\nfires, the server pushes an alert and the source wakes the agent. Push delivery\nis at-most-once, and a subscription that fails in a way meaning the\n`CalendarAlert` type is unacceptable downgrades that connection attempt to\nmail-only wakes — see\n[docs/channel-events.md](docs/channel-events.md) for the full caveats.\n\n- **Prerequisites:** a JMAP mailbox (e.g. [Stalwart](https://stalw.art/),\n  Fastmail). *(JMAP servers only — Gmail is not JMAP;\n  use the `gmail` skill/`gam` for Google Workspace.)* Calendar wakes additionally\n  need a server that speaks JMAP for Calendars. The `CalendarAlert` frame name\n  and its field casing follow Stalwart's implementation and are not part of\n  RFC 8620; they were captured from a live Stalwart 0.15.5 server, and that\n  exact frame is pinned as a test fixture. A\n  scheduled task is an RRULE calendar event with an alarm. The wake carries the\n  channel plus `calendar alert: <uid>`, and for a recurring event the occurrence\n  as `calendar alert: <uid> (<recurrenceId>)` — or a bare `calendar alert` when\n  the uid fails validation. Nothing else crosses: resolving that uid to\n  the event needs calendar tooling in the agent's profile, which the `mail` skill\n  (`xin`) does not provide. Each alert also carries a dedupe key of that uid and,\n  for a recurring event, the occurrence, so distinct alarms stay distinct in the\n  queue while a redelivered one coalesces; past 25 pending alerts for the channel\n  the overflow collapses onto a single unnamed `jmap` entry rather than evicting\n  other channels.\n- **Configure:**\n\n  ```bash\n  export XIN_BASE_URL=\"https://jmap.example.com\"\n  export XIN_BASIC_USER=\"you@example.com\"\n  export XIN_BASIC_PASS=\"app-password\"\n  ```\n\n#### Give an agent a real internet address\n\nTo put an agent on a deliverable public address without buying it a mailbox,\nfollow the\n[agent mailbox runbook](docs/runbooks/agent-mailbox-google-workspace.md): Google\nWorkspace fronts a dedicated agent subdomain for spam filtering and outbound\nrelay, while the mailboxes themselves stay on a mail server you run.\n\n### Signal — `signal`\n\nSpawns `signal-cli … jsonRpc` and wakes on each incoming Signal message. The\n`signal-responder` skill does the receive/reply.\n\n- **Prerequisites:** [`signal-cli`](https://github.com/AsamK/signal-cli) installed\n  and a **registered or linked** Signal account (its data directory), and the\n  `signal-responder` skill enabled.\n- **Configure:**\n\n  ```bash\n  export SIGNAL_NUMBER=\"+15550100\"\n  export SIGNAL_CLI_CONFIG=\"$HOME/.local/share/signal-cli\"   # signal-cli data dir\n  ```\n\n### GitHub notifications — `github`\n\nGitHub has no push transport, so this channel **polls your notifications** and\nwakes you **only when one matches your filters**. Pair with `gh`/a GitHub skill to\nact on them.\n\n- **Prerequisites:** a **classic** PAT with the `notifications` scope.\n  `GET /notifications` accepts classic PATs only — a fine-grained PAT and a\n  GitHub App installation token are both rejected, so neither can drive this\n  channel. Keep it separate from whatever the agent's `gh` uses:\n  `GITHUB_NOTIFY_TOKEN` is read first, so repository work can hold the narrower\n  credential in `GITHUB_TOKEN`.\n- **Configure:**\n\n  ```bash\n  export GITHUB_NOTIFY_TOKEN=\"ghp_…\"   # classic PAT; falls back to GITHUB_TOKEN\n  export GITHUB_NOTIFY_FILTERS=\"review_requested,assigned_issue,assigned_pr,author\"  # optional; this is the default\n  export GITHUB_NOTIFY_ORGS=\"ai-outfitter\"  # optional; only wake on these owners\n  export GITHUB_NOTIFY_POLL_MS=\"60000\" # optional; a floor — GitHub's X-Poll-Interval may raise it\n  export GITHUB_API_URL=\"https://api.github.com\"  # optional; for GHES, or derived from GITHUB_SERVER_URL\n  ```\n\n  | Filter | Wakes on |\n  | --- | --- |\n  | `review_requested` | a PR review requested from you |\n  | `assigned_issue` | an issue assigned to you |\n  | `assigned_pr` | a PR assigned to you |\n  | `author` | activity on a thread you opened — this is what tells you a review landed on **your own** PR |\n  | `mention` | you were @-mentioned |\n  | `comment`, `subscribed`, `state_change`, `ci_activity` | as named |\n\n  `GITHUB_NOTIFY_ORGS` is a comma/space list of organization or user logins,\n  matched case-insensitively against the notified repository's owner. Unset, it\n  filters nothing. Set, a notification from any other owner is dropped **before**\n  acceptance — no Task, no wake, and no mark-read, so the notification stays\n  unread on the account. That last part is the point: `GET /notifications` is\n  account-wide, so one machine account that belongs to two organizations and runs\n  one agent per organization has both agents woken by every thread. Give each\n  deployment the same token and its own `GITHUB_NOTIFY_ORGS`, and each one wakes\n  on its own work and leaves the other's alone.\n\n  `GITHUB_NOTIFY_MARK_READ` is retired. If it is set, Channels ignores it and\n  logs one startup warning. Task-plane intake accepts the exact notification\n  revision durably and then marks that notification read. The woken Task carries\n  the exact repository, subject kind, and number; do not scan assignments or the\n  notification inbox during the turn. See the\n  [GitHub acknowledgment migration note](docs/a2a-source-conformance.md#migration-note-github-acknowledgment).\n\n### Slack — `slack`\n\nUses Slack's official `@slack/socket-mode` client to open a **Socket Mode**\nwebsocket and wakes on each `app_mention` in a watched channel. The event wake\ncarries only an opaque, validated locator. The `slack-responder` skill passes it\nto `channel_read` and `channel_respond`; the extension owns context retrieval,\nthread addressing, and the handled reaction.\n\n- **Prerequisites:** a Slack app with **Socket Mode** enabled — an app-level token\n  (`xapp-…`, scope `connections:write`) for the socket, plus the bot token\n  (`xoxb-…`, scopes `app_mentions:read`, `channels:history`, `chat:write`,\n  `reactions:write`) used by the official `@slack/web-api` client. Add\n  `groups:history` for private channels. Under\n  *Event Subscriptions*, subscribe to `app_mention`, then invite the bot to the\n  channels it should watch.\n- **Configure:**\n\n  ```bash\n  export SLACK_APP_TOKEN=\"xapp-…\"                  # Socket Mode (this extension)\n  export SLACK_CHANNEL_IDS=\"joined\"                 # default: every channel the bot has joined\n  export SLACK_BOT_TOKEN=\"xoxb-…\"                  # context/reply action adapter\n  ```\n\n  Both tokens are required for an operational Slack channel: the app token owns\n  the wake transport and the bot token owns context/reply operations. The\n  source verifies the bot credential with `auth.test`; message text is fetched\n  only when the agent calls `channel_read`. Transient authentication or initial\n  connection failures retry without blocking other configured channels. Omit\n  `SLACK_CHANNEL_IDS` or set it to `joined` for the default. Set it to one or\n  more channel IDs to narrow the bot below its Slack membership boundary.\n\n#### Test against a real Slack workspace locally\n\nSocket Mode connects outbound, so local testing needs no public URL or tunnel.\nFollow the [local Slack runbook](docs/runbooks/slack-local.md) to configure the\napp, start the resident bot, and verify a mention-to-reply round trip.\n\n### Chatto — `chatto`\n\nConnects to a self-hosted Chatto server's protocol-v2 realtime projection and\nwakes for mention notifications. `channel_read` validates the exact notification\nand reads up to ten room or thread messages; `channel_respond` replies in the\ncorrect thread and dismisses that notification.\n\n`channel_publish` posts a new top-level room message. Publication requires an\nexplicit `CHATTO_ROOM_IDS` entry for the target. The tool records each\n`operation_id` before it sends. A retry with the same content returns the first\nChatto message ID. Reuse with different content fails. A confirmed provider\nrejection permits a retry. An ambiguous result stops automatic retry and\nrequires operator reconciliation. Stop the resident session before\nreconciliation. If Chatto contains the message, record its provider message ID:\n\n```bash\nexport CHANNELS_TASK_STORE_PATH=\"$HOME/.local/share/outfitter/channels/task-plane\"\noutfitter-channel-reconcile chatto OPERATION_ID delivered PROVIDER_MESSAGE_ID\n```\n\nIf an operator confirms that Chatto did not create the message, mark the\noperation retryable instead:\n\n```bash\noutfitter-channel-reconcile chatto OPERATION_ID retryable\n```\n\nRestart the resident session after the command succeeds. Do not mark an\noperation retryable when the provider result is still unknown.\n\n- **Prerequisites:** a Chatto identity and bearer token that can read the watched\n  rooms and notifications, post messages and thread replies, and dismiss its own\n  notifications.\n- **Configure:**\n\n  ```bash\n  export CHATTO_BASE_URL=\"https://chatto.example.com\"\n  export CHATTO_TOKEN=\"…\"\n  export CHATTO_ROOM_IDS=\"room-id-1,room-id-2\"  # optional intake; required publish allowlist\n  ```\n\n  Omit `CHATTO_ROOM_IDS` to accept mentions and replies from every room visible\n  to the identity. Omission disables `channel_publish`; publication always\n  requires an explicit target entry. This adapter is pinned to the protocol-v2 schema recorded in\n  [`extensions/vendor/chatto/SCHEMA.md`](extensions/vendor/chatto/SCHEMA.md).\n  Chatto `v0.4.16` does not expose that schema; use the pinned revision or a\n  later protocol-v2-compatible release described in the\n  [local Chatto runbook](docs/runbooks/chatto-local.md).\n\n### Mattermost — `mattermost`\n\nUses Mattermost's WebSocket API for recipient-scoped posted events and its REST\nAPI for exact reads, replies, and handled reactions.\n\n- **Prerequisites:** a Mattermost bot account added to each watched channel, with\n  permission to read channel history, create posts and thread replies, and add\n  reactions.\n- **Configure:**\n\n  ```bash\n  export MATTERMOST_BASE_URL=\"https://mattermost.example.com\"\n  export MATTERMOST_BOT_TOKEN=\"…\"\n  export MATTERMOST_CHANNEL_IDS=\"channel-id-1,channel-id-2\"  # optional\n  ```\n\n  Omit `MATTERMOST_CHANNEL_IDS` to accept mentions from every channel visible to\n  the bot. See the\n  [local Mattermost runbook](docs/runbooks/mattermost-local.md).\n\n### Zulip — `zulip`\n\nMaintains a Zulip realtime event queue, recreates expired queues, and wakes for\nchannel mentions or direct messages. Reads stay within the original topic or\ndirect conversation; replies preserve that address and add a\n`white_check_mark` reaction to the exact message.\n\n- **Prerequisites:** a Zulip bot subscribed to each watched channel, with access\n  to read messages, send replies, and add reactions.\n- **Configure:**\n\n  ```bash\n  export ZULIP_ORGANIZATION_URL=\"https://zulip.example.com\"\n  export ZULIP_BOT_EMAIL=\"bot@zulip.example.com\"\n  export ZULIP_API_KEY=\"…\"\n  export ZULIP_CHANNEL_IDS=\"12,34\"  # optional numeric channel IDs\n  ```\n\n  The allowlist applies only to channel messages; direct messages remain\n  eligible. Omit it to accept mentions from every channel visible to the bot.\n  See the [local Zulip runbook](docs/runbooks/zulip-local.md).\n\n### Minimal end-to-end\n\n```bash\npi install git:github.com/ai-outfitter/channels\nexport GITHUB_TOKEN=\"ghp_…\"            # + any other channels' vars\npi                                     # keep this session running\n```\n\nThe agent now wakes when a review is requested from you (and any other configured\nchannel), rather than polling.\n\n## Using it with Outfitter\n\nIf you compose agents with Outfitter (profiles, skills, in-cluster), select this\nextension in an agent's loadout instead of `pi install` — see\n**[Using channels with Outfitter](https://github.com/ai-outfitter/outfitter/blob/main/docs/documentation/channels.md)**.\n\n## How it works\n\nConnection lifecycle runs on **inference-free** pi hooks (`session_start` opens\neach push stream; `session_shutdown` closes them). Sources commit work to the\ntask plane; its durable wake queue prompts one durable Pi session per Task, with\none active Task authority at a time. Full design, the pi primitives, and verification are in\n[docs/channel-events.md](docs/channel-events.md). Source boundaries and the\nchannel tool boundary, library evaluation, and per-channel dynamic-import\nconvention are in\n[docs/architecture.md](docs/architecture.md).\n\nNative agent-to-agent and authorized operator-to-agent chat, including the\nidentity model and boundaries from observation/control/lifecycle, is specified in\n[Agent Session Gateway](docs/agent-session-gateway.md).\n\nThe A2A task plane is specified in\n[docs/a2a-task-plane.md](docs/a2a-task-plane.md); per-source contracts live in\nthe [source conformance matrix](docs/a2a-source-conformance.md).\n\n## Add a channel\n\nAdd `extensions/sources/<name>.ts` with a `ChannelSource` and, for exact-item\ntools, `ChannelActions`, then add one entry in the `SOURCES` registry. Add the\nsource's row to the\n[source conformance matrix](docs/a2a-source-conformance.md).\n\n```text\nextensions/\n  index.ts            # hooks, required task intake, lazy adapter routing\n  channel-tools.ts    # channel_read / channel_respond\n  sources/\n    types.ts          # source, event, locator, and action contracts\n    util.ts           # shared helpers (parseList, scopedLog, reconnect delay)\n    jmap.ts           # JMAP EventSource source\n    signal.ts         # signal-cli jsonRpc source\n    github.ts         # GitHub notifications (polling) source\n    slack.ts          # Slack Socket Mode source + action adapter\n    chatto.ts         # Chatto realtime source + action adapter\n    mattermost.ts     # Mattermost WebSocket source + action adapter\n    zulip.ts          # Zulip event-queue source + action adapter\ndocs/channel-events.md\n```\n","readmeFilename":"README.md"}