@ekanos/cli 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +1 -0
  2. package/dist/commands/agents-edit.d.ts +49 -0
  3. package/dist/commands/agents-edit.js +385 -0
  4. package/dist/commands/agents-edit.js.map +1 -0
  5. package/dist/commands/agents.d.ts +24 -1
  6. package/dist/commands/agents.js +117 -8
  7. package/dist/commands/agents.js.map +1 -1
  8. package/dist/commands/apps.d.ts +3 -0
  9. package/dist/commands/apps.js +2 -2
  10. package/dist/commands/apps.js.map +1 -1
  11. package/dist/commands/reviewers.d.ts +21 -0
  12. package/dist/commands/reviewers.js +162 -0
  13. package/dist/commands/reviewers.js.map +1 -0
  14. package/dist/commands/runs.d.ts +42 -0
  15. package/dist/commands/runs.js +362 -0
  16. package/dist/commands/runs.js.map +1 -0
  17. package/dist/commands/skills.d.ts +60 -0
  18. package/dist/commands/skills.js +397 -0
  19. package/dist/commands/skills.js.map +1 -0
  20. package/dist/commands/team-agents-agents.d.ts +9 -0
  21. package/dist/commands/team-agents-agents.js +188 -0
  22. package/dist/commands/team-agents-agents.js.map +1 -0
  23. package/dist/commands/team-agents-mcp.d.ts +4 -0
  24. package/dist/commands/team-agents-mcp.js +218 -0
  25. package/dist/commands/team-agents-mcp.js.map +1 -0
  26. package/dist/commands/team-agents-triggers.d.ts +12 -0
  27. package/dist/commands/team-agents-triggers.js +251 -0
  28. package/dist/commands/team-agents-triggers.js.map +1 -0
  29. package/dist/commands/team-agents.d.ts +31 -0
  30. package/dist/commands/team-agents.js +71 -0
  31. package/dist/commands/team-agents.js.map +1 -0
  32. package/dist/commands/teams-onboard.d.ts +51 -0
  33. package/dist/commands/teams-onboard.js +261 -0
  34. package/dist/commands/teams-onboard.js.map +1 -0
  35. package/dist/commands/teams.d.ts +45 -0
  36. package/dist/commands/teams.js +314 -0
  37. package/dist/commands/teams.js.map +1 -0
  38. package/dist/index.d.ts +1 -1
  39. package/dist/index.js +222 -2
  40. package/dist/index.js.map +1 -1
  41. package/dist/reviewers-api.d.ts +33 -0
  42. package/dist/reviewers-api.js +81 -0
  43. package/dist/reviewers-api.js.map +1 -0
  44. package/dist/runs-api.d.ts +34 -0
  45. package/dist/runs-api.js +112 -0
  46. package/dist/runs-api.js.map +1 -0
  47. package/dist/skills-api.d.ts +28 -0
  48. package/dist/skills-api.js +104 -0
  49. package/dist/skills-api.js.map +1 -0
  50. package/dist/teams-api.d.ts +85 -0
  51. package/dist/teams-api.js +168 -0
  52. package/dist/teams-api.js.map +1 -0
  53. package/package.json +2 -2
  54. package/templates/package.json.tmpl +4 -4
  55. package/templates/skills/build-agent/SKILL.md +106 -17
  56. package/templates/skills/build-agent/references/agent-json.md +78 -2
  57. package/templates/skills/build-agent/references/rollout.md +9 -1
  58. package/templates/skills/build-agent/references/set-up-an-integration.md +134 -0
  59. package/templates/skills/build-agent/references/skills.md +78 -0
  60. package/templates/skills/build-agent/references/triggers.md +27 -6
  61. package/templates/skills/ekanos-platform/SKILL.md +115 -5
@@ -0,0 +1 @@
1
+ {"version":3,"file":"teams-api.js","sourceRoot":"","sources":["../src/teams-api.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type TeamAppSetResult,\n TeamAppSetResultSchema,\n type TeamCreateRequest,\n type TeamCreateResult,\n TeamCreateResultSchema,\n type TeamMemberChangeResult,\n TeamMemberChangeResultSchema,\n type TeamMemberInviteResult,\n TeamMemberInviteResultSchema,\n type TeamMemberList,\n TeamMemberListSchema,\n type TeamRoles,\n TeamRolesSchema,\n type TeamIntegrationStatus,\n TeamIntegrationStatusSchema,\n type TeamList,\n TeamListSchema,\n type TeamWebhookMintResult,\n TeamWebhookMintResultSchema,\n} from '@ekanos/integration-schema';\nimport type { z } from 'zod';\n\nimport { request } from './auth/ekanos-api';\nimport {\n authRequiredError,\n forbiddenError,\n invalidStateError,\n networkError,\n notFoundError,\n usageError,\n validationError,\n} from './errors';\n\n/**\n * The CLI's client for a Source's teams and their integration setup — the\n * team-level counterpart of `apps-api.ts`.\n *\n * GET /api/partner/teams?source=<slug> → TeamList\n * GET /api/partner/teams/<team>/integrations/<product>?source=<slug> → TeamIntegrationStatus\n * PUT …/integrations/<product>/app?source=<slug> { connectionId?, fields }\n * → TeamAppSetResult\n * POST …/integrations/<product>/webhooks/<id>?source=<slug> → TeamWebhookMintResult\n * POST /api/partner/teams?source=<slug> { name, slug?, ownerEmail? } → TeamCreateResult\n * GET /api/partner/teams/<team>/members?source=<slug> → TeamMemberList\n * POST …/<team>/members?source=<slug> { email, role } → TeamMemberInviteResult\n * PATCH …/<team>/members/<email>?source=<slug> { role } → TeamMemberChangeResult\n * DELETE …/<team>/members/<email>?source=<slug> → TeamMemberChangeResult\n * GET /api/partner/teams/<team>/roles?source=<slug> → TeamRoles\n *\n * → 401 / 3xx refresh once and retry\n * → 403 { code: \"SOURCE_ADMIN_REQUIRED\" | \"MFA_REQUIRED\" | \"FORBIDDEN\" }\n * → 404 { code: \"TEAM_NOT_FOUND\" | \"PRODUCT_NOT_ENTITLED\" | \"WEBHOOK_NOT_FOUND\"\n * | \"CONNECTION_NOT_FOUND\" | \"SOURCE_NOT_FOUND\" }\n * | \"MEMBER_NOT_FOUND\" }\n * → 409 { code: \"NO_TEAM_APP\" | \"SLUG_TAKEN\" | \"TEAM_ACCOUNTS_DISABLED\"\n * | \"ALREADY_MEMBER\" | \"ALREADY_INVITED\" | \"PRIMARY_OWNER\" }\n * → 400 { code: \"INVALID_FIELDS\" | \"INVALID_REQUEST\" | \"UNKNOWN_ROLE\" }\n */\n\nexport type TeamsApiResult<T> =\n | { status: 'ok'; data: T }\n | { status: 'unauthorized' };\n\nexport function listTeams(\n host: string,\n accessToken: string,\n source: string,\n): Promise<TeamsApiResult<TeamList>> {\n return call(host, accessToken, TeamListSchema, {\n method: 'GET',\n path: '/api/partner/teams',\n source,\n });\n}\n\nexport function showTeamIntegration(\n host: string,\n accessToken: string,\n target: { source: string; team: string; product: string },\n): Promise<TeamsApiResult<TeamIntegrationStatus>> {\n return call(host, accessToken, TeamIntegrationStatusSchema, {\n method: 'GET',\n path: integrationPath(target),\n source: target.source,\n });\n}\n\nexport function setTeamApp(\n host: string,\n accessToken: string,\n target: { source: string; team: string; product: string },\n body: { connectionId?: string; fields: Record<string, string> },\n): Promise<TeamsApiResult<TeamAppSetResult>> {\n return call(host, accessToken, TeamAppSetResultSchema, {\n method: 'PUT',\n path: `${integrationPath(target)}/app`,\n source: target.source,\n body,\n });\n}\n\nexport function mintTeamWebhook(\n host: string,\n accessToken: string,\n target: { source: string; team: string; product: string },\n webhookId: string,\n): Promise<TeamsApiResult<TeamWebhookMintResult>> {\n return call(host, accessToken, TeamWebhookMintResultSchema, {\n method: 'POST',\n path: `${integrationPath(target)}/webhooks/${encodeURIComponent(webhookId)}`,\n source: target.source,\n });\n}\n\nexport function createTeam(\n host: string,\n accessToken: string,\n source: string,\n body: TeamCreateRequest,\n): Promise<TeamsApiResult<TeamCreateResult>> {\n return call(host, accessToken, TeamCreateResultSchema, {\n method: 'POST',\n path: '/api/partner/teams',\n source,\n body,\n });\n}\n\nexport function listTeamMembers(\n host: string,\n accessToken: string,\n target: { source: string; team: string },\n): Promise<TeamsApiResult<TeamMemberList>> {\n return call(host, accessToken, TeamMemberListSchema, {\n method: 'GET',\n path: membersPath(target.team),\n source: target.source,\n });\n}\n\nexport function inviteTeamMember(\n host: string,\n accessToken: string,\n target: { source: string; team: string },\n body: { email: string; role: string },\n): Promise<TeamsApiResult<TeamMemberInviteResult>> {\n return call(host, accessToken, TeamMemberInviteResultSchema, {\n method: 'POST',\n path: membersPath(target.team),\n source: target.source,\n body,\n });\n}\n\nexport function setTeamMemberRole(\n host: string,\n accessToken: string,\n target: { source: string; team: string; email: string },\n role: string,\n): Promise<TeamsApiResult<TeamMemberChangeResult>> {\n return call(host, accessToken, TeamMemberChangeResultSchema, {\n method: 'PATCH',\n path: `${membersPath(target.team)}/${encodeURIComponent(target.email)}`,\n source: target.source,\n body: { role },\n });\n}\n\nexport function removeTeamMember(\n host: string,\n accessToken: string,\n target: { source: string; team: string; email: string },\n): Promise<TeamsApiResult<TeamMemberChangeResult>> {\n return call(host, accessToken, TeamMemberChangeResultSchema, {\n method: 'DELETE',\n path: `${membersPath(target.team)}/${encodeURIComponent(target.email)}`,\n source: target.source,\n });\n}\n\nexport function listTeamRoles(\n host: string,\n accessToken: string,\n target: { source: string; team: string },\n): Promise<TeamsApiResult<TeamRoles>> {\n return call(host, accessToken, TeamRolesSchema, {\n method: 'GET',\n path: `/api/partner/teams/${encodeURIComponent(target.team)}/roles`,\n source: target.source,\n });\n}\n\nfunction membersPath(team: string): string {\n return `/api/partner/teams/${encodeURIComponent(team)}/members`;\n}\n\nfunction integrationPath(target: { team: string; product: string }): string {\n return `/api/partner/teams/${encodeURIComponent(target.team)}/integrations/${encodeURIComponent(target.product)}`;\n}\n\n/** One partner teams call; shared by the team-agent clients (`team-agents-api.ts`). */\nexport async function call<T>(\n host: string,\n accessToken: string,\n schema: z.ZodType<T>,\n init: {\n method: 'GET' | 'PUT' | 'POST' | 'PATCH' | 'DELETE';\n path: string;\n source: string;\n body?: unknown;\n },\n): Promise<TeamsApiResult<T>> {\n const response = await request(\n `${host}${init.path}?${new URLSearchParams({ source: init.source })}`,\n {\n method: init.method,\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${accessToken}`,\n ...(init.body === undefined\n ? {}\n : { 'content-type': 'application/json' }),\n },\n body: init.body === undefined ? undefined : JSON.stringify(init.body),\n },\n );\n\n if (\n response.status === 401 ||\n (response.status >= 300 && response.status < 400)\n ) {\n return { status: 'unauthorized' };\n }\n\n const body = await readOptionalJson(response);\n const code = typeof body.code === 'string' ? body.code : null;\n const message =\n typeof body.error === 'string' && body.error ? body.error : null;\n\n if (response.status === 403 && code === 'MFA_REQUIRED') {\n throw authRequiredError(\n message ?? `${host} requires multi-factor authentication.`,\n 'Complete MFA in the browser, then run \"ekanos login\" again.',\n );\n }\n if (response.status === 403) {\n throw forbiddenError(\n message ?? 'Not permitted on this Source.',\n code === 'SOURCE_ADMIN_REQUIRED'\n ? \"Only a Source admin manages its teams' integrations. Ask one to run it.\"\n : 'Run \"ekanos sources\" to list the sources you hold a seat on.',\n );\n }\n if (response.status === 404) {\n throw notFoundError(\n message ?? 'Not found.',\n code === 'TEAM_NOT_FOUND'\n ? 'Run \"ekanos teams list\" to see the teams on this Source.'\n : code === 'PRODUCT_NOT_ENTITLED'\n ? '\"ekanos catalog\" lists what this Source has; Ekanos enables a product for a Source.'\n : code === 'WEBHOOK_NOT_FOUND' || code === 'CONNECTION_NOT_FOUND'\n ? 'Run \"ekanos teams show <team> <product>\" to see its team apps and webhooks.'\n : code === 'MEMBER_NOT_FOUND'\n ? 'Run \"ekanos teams members list <team>\" to see its members and invitations.'\n : code?.startsWith('AGENT_') || code === 'TEMPLATE_NOT_FOUND'\n ? 'Run \"ekanos teams agents list <team>\" to see the team\\'s agents.'\n : /^(TRIGGER|LINK|MCP)_/.test(code ?? '')\n ? 'List them first: \"ekanos teams triggers|links|mcp list <team> ...\"'\n : 'Run \"ekanos sources\" to list the sources you hold a seat on.',\n );\n }\n if (response.status === 409 && code === 'NO_TEAM_APP') {\n throw invalidStateError(\n message ?? 'This integration takes no team app.',\n 'Teams connect through the Source\\'s app: \"ekanos apps show <product>\".',\n );\n }\n if (response.status === 409 || response.status === 422) {\n throw invalidStateError(\n message ?? 'The host refused the change.',\n code === 'SLUG_TAKEN'\n ? 'Pick another with --slug, or run \"ekanos teams list\" to see the teams.'\n : code === 'ALREADY_MEMBER' || code === 'ALREADY_INVITED'\n ? 'Run \"ekanos teams members list <team>\"; change a role with \"ekanos teams members role <team> <email> <role>\".'\n : code === 'PRIMARY_OWNER'\n ? \"The primary owner's ownership is transferred in the app (team Settings → Members).\"\n : code === 'TEAM_ACCOUNTS_DISABLED'\n ? 'Ekanos switches team accounts on for a Source; ask a super-admin.'\n : 'See \"ekanos teams --help\".',\n );\n }\n if (response.status === 400 && code === 'UNKNOWN_ROLE') {\n throw validationError(\n message ?? 'No such role.',\n 'Run \"ekanos teams roles list <team>\" for the roles this Source uses.',\n );\n }\n if (response.status === 400 && code === 'INVALID_FIELDS') {\n throw validationError(\n message ?? 'The host refused the fields.',\n 'Run \"ekanos teams app set <team> <product>\" again.',\n );\n }\n if (response.status === 400) {\n throw usageError(\n message ?? 'The host refused the request.',\n 'See \"ekanos teams --help\".',\n );\n }\n if (!response.ok) {\n throw networkError(\n `${host}${init.path} responded ${response.status}.`,\n response.status >= 500\n ? 'The Ekanos deployment returned a server error. Retry shortly.'\n : 'Confirm the host runs a build that includes the teams routes.',\n );\n }\n\n const parsed = schema.safeParse(body.data);\n if (!parsed.success) {\n throw networkError(\n `${host}/api/partner/teams answered in a shape this CLI does not read.`,\n 'Run \"ekanos upgrade\" — the CLI and the host disagree on the teams contract.',\n );\n }\n return { status: 'ok', data: parsed.data };\n}\n\nasync function readOptionalJson(\n response: Response,\n): Promise<Record<string, unknown>> {\n try {\n const parsed: unknown = await response.json();\n return typeof parsed === 'object' && parsed !== null\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekanos/cli",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "private": false,
5
5
  "description": "The Ekanos partner toolchain CLI: scaffold, validate, and test an Ekanos integration against the published @ekanos packages. Agent-native — every verb speaks JSON with a stable exit-code taxonomy.",
6
6
  "keywords": [
@@ -35,7 +35,7 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@clack/prompts": "1.7.0",
38
- "@ekanos/integration-schema": "0.12.0",
38
+ "@ekanos/integration-schema": "0.14.0",
39
39
  "esbuild": "0.28.1",
40
40
  "picocolors": "1.1.1"
41
41
  },
@@ -12,12 +12,12 @@
12
12
  "prepare": "node scripts/setup-hooks.mjs"
13
13
  },
14
14
  "dependencies": {
15
- "@ekanos/sdk": "^0.12.0",
16
- "@ekanos/ui": "^0.12.0"
15
+ "@ekanos/sdk": "^0.14.0",
16
+ "@ekanos/ui": "^0.14.0"
17
17
  },
18
18
  "devDependencies": {
19
- "@ekanos/cli": "^0.9.0",
20
- "@ekanos/harness": "^0.7.3",
19
+ "@ekanos/cli": "^0.11.0",
20
+ "@ekanos/harness": "^0.14.0",
21
21
  "@hookform/resolvers": "^5.2.2",
22
22
  "@tailwindcss/postcss": "^4.3.3",
23
23
  "@tanstack/react-query": "^5.101.4",
@@ -60,12 +60,22 @@ every `hint`. Look things up in the `ekanos-sdk-docs` MCP server
60
60
  Tell the user this map in a few lines before the interview. Don't hide a gap
61
61
  behind a plan that assumes it away.
62
62
 
63
- ## Setting up an app, step by step
64
-
65
- When a provider app or a team app is not set up and the user wants to do it,
66
- follow `references/app-setup.md`: one step at a time, in the provider's own
67
- console words, with the exact URLs to paste from the catalog, waiting for the
68
- user after each step, and the secret entered only at their terminal.
63
+ ## Setting up an integration, concierge-style
64
+
65
+ When something the agent needs is not set up yet, offer to set it up with
66
+ the user now: "Zendesk isn't connected for any team yet; want me to walk you
67
+ through it? It takes about ten minutes." Then follow
68
+ `references/set-up-an-integration.md`. It covers where the connection should
69
+ live (each team through the Source's app, each team with its own app, or one
70
+ for the whole Source), the integration's own steps, one per message, and
71
+ checking each one with `ekanos teams show` before moving on. If the team
72
+ itself does not exist yet, create it and invite its people first
73
+ (`ekanos teams create`, `ekanos teams members invite`, `ekanos teams roles
74
+ list` to find the role holding `agents.review` for reviewers — see the
75
+ `ekanos-platform` skill, "Onboarding a team"), and send the member who will
76
+ sign in the link from `ekanos teams connect <team> <product>`. For the
77
+ Source's app itself, `references/app-setup.md` has the console walk-through.
78
+ Secrets are only ever entered at the user's terminal or in the app.
69
79
 
70
80
  ## 2. The interview
71
81
 
@@ -83,18 +93,27 @@ in parentheses are the recommendation when the user has no preference.
83
93
  (Reads only to start; add writes once it has earned trust.)
84
94
  4. **What waits for a person.** Reads run on their own, changes wait for
85
95
  approval — and any per-tool exceptions (a public reply to a customer
86
- almost always waits). See `references/agent-json.md` → `policy`.
87
- 5. **Whose connections.** For each product and MCP server: each team
88
- connects its own — `required` (the agent waits at "needs setup" until the
89
- team connects it) or `optional` (its tools appear once connected). One
90
- connection shared by every team is not available yet.
96
+ almost always waits). See `references/agent-json.md` → `policy`. If
97
+ anything waits, ask who decides it — see **Reviewers** below.
98
+ 5. **Whose connections.** For each product and MCP server, where the
99
+ connection lives (`references/set-up-an-integration.md` § 2): each team
100
+ connects its own, and it is `required` (the agent waits at "needs setup"
101
+ until the team connects it) or `optional` (its tools appear once
102
+ connected). One connection shared by every team is not available yet;
103
+ say so if they ask for it. Then offer to set up any that are not ready.
91
104
  6. **What teams may add** — their own MCP servers, their own skills, more of
92
105
  their own integrations, or narrowing (removing tools, more approvals). All
93
106
  off unless there is a reason. Give one concrete example per knob.
94
- 7. **What starts it** — see `references/triggers.md`. Triggers belong to each
95
- team and are set after turning the agent on; say what each team will have
96
- to set up (an @mention, a label, a new ticket, a schedule) and what it
97
- needs connected first.
107
+ 7. **What starts it** — see `references/triggers.md`. Declare the triggers in
108
+ the template (an event such as `zendesk.ticket_created` with its filters,
109
+ a schedule, a webhook) so every team gets them, or leave them to each
110
+ team. Say what each team needs connected first.
111
+ 8. **Who it works with** — other agents of this Source it may ask or hand
112
+ work to (`links`). A link only exists in a team that runs both agents.
113
+ 9. **What teams may change** — `teamOverrides` per area (approvals,
114
+ triggers, links). Off (the default) means teams follow the template
115
+ exactly; on lets a team keep its own differences. Switching an area off
116
+ later keeps a team's differences but stops applying them.
98
117
 
99
118
  ## 3. Draft, apply, publish
100
119
 
@@ -104,21 +123,91 @@ in parentheses are the recommendation when the user has no preference.
104
123
  The instructions say: the job, the sources of truth and when to consult
105
124
  each, what to do when unsure (ask, or hand to a person), tone, and what
106
125
  never to do. Show the user both files and walk through them.
126
+ Know-how the agent loads only when needed (a policy, a playbook) goes in
127
+ a skill folder, `agents/<key>/skills/<slug>/SKILL.md` — see
128
+ `references/skills.md`. Content only one team may see is a **team
129
+ skill**: `ekanos skills push <dir> --team <team-slug>`.
107
130
  2. On their go: `npx @ekanos/cli agents apply agents/<key> --source <slug> --json`.
108
131
  A refusal (exit 3) lists each problem with its path in the body — explain
109
132
  and fix them together. The stored version is a **draft**: no team sees it.
133
+ Skill folders beside the agent are pushed first and are **live at once**
134
+ (unlike the agent) — say so before applying, and show the user what
135
+ `skills` the output reports as created or changed.
110
136
  3. `npx @ekanos/cli agents show <key> --source <slug>` — review the stored draft
111
137
  with the user.
138
+ Then test it on one team that has turned the agent on, without publishing:
139
+ `npx @ekanos/cli agents test <key> --team <team> --prompt "…" --follow --json`
140
+ (or `--input example.json` for a real ticket or event). It runs the latest
141
+ version on that team as a test run — same tools, approvals and audit; the
142
+ team's current agent still bounds what it may do, so a draft can only do
143
+ less. A write waits for an approval like any run (see "Running, watching
144
+ and deciding" below).
112
145
  4. Ask: publish this version to every team now? Only on yes:
113
146
  `npx @ekanos/cli agents publish <key> --source <slug> --to <n> --yes`.
114
- 5. Hand over the team-side checklist (`references/triggers.md` has the
147
+ 5. If anything waits for approval, name its reviewers (**Reviewers**
148
+ below) before teams turn it on — a draft nobody may decide just waits.
149
+ 6. Hand over the team-side checklist (`references/triggers.md` has the
115
150
  per-integration parts): find it under Agents → "Provided for your team",
116
151
  Turn on, connect what it needs, confirm access, add triggers. Suggest one
117
152
  team pilots it first, and plan a first test run with them.
118
153
 
154
+ ## Reviewers
155
+
156
+ Reply drafts held under an `approve` policy are decided by the team's own
157
+ managers (`agents.manage`) and reviewers (`agents.review`), and — for agents
158
+ the Source provides — by the Source's **named reviewers**. A Source admin
159
+ names them; each must be a member of one of the Source's teams, and is
160
+ notified of every draft in their reach.
161
+
162
+ - `npx @ekanos/cli reviewers list --source <slug> --json` — who reviews now.
163
+ - `npx @ekanos/cli reviewers add <email> --reach source --source <slug>` —
164
+ decides drafts from every team's provided agents, in the Source's review
165
+ queue at `/reviews` (reviewers are managed at `/management/reviewers`).
166
+ - `npx @ekanos/cli reviewers add <email> --reach team --team <team-slug> --source <slug>`
167
+ — decides one team's drafts, from the same review queue (they need not be
168
+ a member of that team).
169
+ - `npx @ekanos/cli reviewers remove <email> [--team <team-slug>] --source <slug>`
170
+ — every reach, or just that team's.
171
+
172
+ The first decision settles a draft for everyone. Recommend one or two
173
+ source-reach reviewers for an agent provided to every team, plus team-reach
174
+ reviewers where a team wants its own people deciding. A refusal with
175
+ `USER_NOT_IN_SOURCE` means the person is not on any of the Source's teams yet.
176
+
119
177
  ## Changing it later
120
178
 
121
179
  `npx @ekanos/cli agents pull <key> --source <slug>` writes the latest version to
122
180
  `agents/<key>/`; edit, `apply` (a new draft), `show`, then `publish` on the
123
- user's yes. `rollback <key> --to <n> --yes` republishes an earlier version.
181
+ user's yes. For one small change, edit the stored draft directly — `agents
182
+ policy <key> set <tool> approve`, `agents triggers <key> add|remove …`,
183
+ `agents links <key> add|remove …`, `agents overrides <key> enable|disable
184
+ <area>` (each stores a new draft; see `references/agent-json.md` § Editing
185
+ one area without a file) — then `publish` on the user's yes. `rollback <key> --to <n> --yes` republishes an earlier version.
124
186
  Teams that pinned a version stay on it and are told a newer one exists.
187
+
188
+ ## Running, watching and deciding
189
+
190
+ All as the Source admin, on one team (`--team <slug>`; `ekanos teams list`
191
+ names them). Add `--json` for a stable record a script can assert on.
192
+
193
+ - `npx @ekanos/cli runs start <key> --team <team> --prompt "…" [--follow]` —
194
+ the app's "Run now": runs as the agent's owner under its own tools and
195
+ approvals, recorded as started by you. `--input <file.json>` passes a
196
+ payload instead of (or beside) the prompt. `--follow` prints status,
197
+ messages and each tool call until the run ends or waits on an approval.
198
+ - `npx @ekanos/cli runs list --team <team> [--agent <key>]` — recent runs.
199
+ - `npx @ekanos/cli runs show <run-id> --team <team> --json` — trigger, version
200
+ (and `draftTemplateVersion` on a test run), output, transcript, every tool
201
+ call with `authorizedVia` (`auto` or `approved`), and its approvals. A leak
202
+ test is a script over this: e.g. assert no `transcript` tool result and no
203
+ `output` contains a value the agent must never reveal.
204
+ - `npx @ekanos/cli approvals list --team <team>` — what is waiting.
205
+ - `npx @ekanos/cli approvals approve <id> --team <team> [--reason "…"]` /
206
+ `approvals reject <id> --team <team> --reason "…"` — decided as you, with
207
+ the authority a team's agent manager has in the app.
208
+ - `npx @ekanos/cli approvals edit <id> --team <team> --body ./reply.md` — Edit &
209
+ send a reply draft: approves it with your text instead. Only reply drafts
210
+ take an edit.
211
+
212
+ Never approve on the user's behalf without asking: show them what the agent
213
+ proposes (`approvals list`) and decide only on their word.
@@ -33,7 +33,25 @@ conflicting change unless `--overwrite`. Unknown fields are refused.
33
33
  "toolEffects": { "search": "read" }
34
34
  }
35
35
  ],
36
- "teamCustomization": { "mcpServers": false, "skills": false, "integrations": false, "narrow": false }
36
+ "teamCustomization": { "mcpServers": false, "skills": false, "integrations": false, "narrow": false },
37
+ "triggers": [
38
+ {
39
+ "key": "new-tickets",
40
+ "type": "event",
41
+ "event": "zendesk.ticket_created",
42
+ "filter": { "tags": ["vip", "urgent"], "brand": "123", "reply_as": "internal_note" }
43
+ },
44
+ {
45
+ "key": "weekday-digest",
46
+ "type": "schedule",
47
+ "cron": "0 9 * * 1-5",
48
+ "timezone": "America/Los_Angeles",
49
+ "prompt": "Summarize yesterday's open escalations."
50
+ },
51
+ { "key": "inbound", "type": "webhook", "prompt": "Triage the posted ticket." }
52
+ ],
53
+ "links": [{ "to": "billing-agent", "kind": "delegate", "mode": "handoff" }],
54
+ "teamOverrides": { "policy": false, "triggers": true, "links": false }
37
55
  }
38
56
  ```
39
57
 
@@ -58,7 +76,9 @@ The tool names above are illustrations. Take real ones from
58
76
  defaults by the tool's effect; `overrides` set single tools by name. An
59
77
  undeclared tool effect counts as a write. MCP tools are named
60
78
  `mcp__<server key>__<tool>` in `overrides`.
61
- - **`skills`** — skill slugs the Source or the platform already has.
79
+ - **`skills`** — skill slugs the Source or the platform already has. Skill
80
+ folders in `agents/<key>/skills/<slug>/` are pushed and added here by
81
+ `agents apply` (see `skills.md`).
62
82
  - **`connections`** — by product slug: `{ "owner": "team", "team": "required" | "optional" }`.
63
83
  A product with no entry is `team`/`optional`. `owner: "source"` (one
64
84
  connection for every team) is refused today.
@@ -77,6 +97,62 @@ The tool names above are illustrations. Take real ones from
77
97
  - `narrow` — remove tools or make more of them wait for approval. Never
78
98
  loosens.
79
99
 
100
+ - **`triggers`** — what starts a run, set up on every team's copy when it
101
+ turns the agent on (and kept in step on every publish). Each has a `key`
102
+ (lowercase-hyphenated, unique in the agent: what versions are matched by),
103
+ an optional `prompt` (what to do each time), and `enabled` (default
104
+ `true`; `false` sets it up turned off). Types:
105
+ - `event` — `event` is an integration event (`<slug>.<event>`, as
106
+ `ekanos catalog <product>` lists under `agentEvents`); `filter` holds the
107
+ event's declared filter fields (Zendesk: `organization`, `brand`,
108
+ `group`, `tags`, `reply_as` …).
109
+ - `schedule` — `cron` (5 fields, on a 5-minute boundary) and `timezone`
110
+ (IANA, default `UTC`); give it a `prompt`.
111
+ - `webhook` — each team gets its **own** URL and secret, created or
112
+ rotated by the team in the app. A template never holds a webhook secret.
113
+ - **`links`** — other agents of this Source it may ask (`kind: "talk"`) or
114
+ hand work to (`kind: "delegate"`, `mode: "await"` to wait for the answer,
115
+ default, or `"handoff"` to let go). `to` is the other agent's key. A link
116
+ is made in a team only where that team runs both agents; it appears when
117
+ the team turns the other one on.
118
+ - **`teamOverrides`** — which areas a team may change on its copy:
119
+ `policy`, `triggers`, `links`. **The template wins.** An area that is off
120
+ follows the template exactly and the team cannot change it (it shows
121
+ "Set by your administrator"). An area that is on lets the team keep its
122
+ own differences: its own triggers and on/off for the template's, its own
123
+ links, its own auto/approve answers. Switching an area off again keeps the
124
+ team's differences but stops applying them; switching it back on restores
125
+ them. Omitted: `policy` is off; `triggers` and `links` are off when this
126
+ file declares them and on (the team's, as before) when it does not.
127
+
128
+ ## Editing one area without a file
129
+
130
+ Each of these reads the latest version, changes one thing, and stores the
131
+ result as a new **draft** version (`--publish` also rolls it out, with
132
+ `--yes`). A pulled `agent.json` is left alone; `agents pull` refreshes it.
133
+
134
+ ```
135
+ ekanos agents policy <key> # show
136
+ ekanos agents policy <key> set zendesk_add_comment approve
137
+ ekanos agents policy <key> unset zendesk_add_comment
138
+ ekanos agents policy <key> --reads auto --writes approve
139
+ ekanos agents triggers <key> # list
140
+ ekanos agents triggers <key> add event new-tickets --event zendesk.ticket_created \
141
+ --filter tags=vip,urgent --filter brand=123 --filter reply_as=internal_note
142
+ ekanos agents triggers <key> add schedule weekday-digest --cron "0 9 * * 1-5" \
143
+ --timezone America/Los_Angeles --prompt "Summarize yesterday's escalations."
144
+ ekanos agents triggers <key> add webhook inbound --prompt "Triage the posted ticket."
145
+ ekanos agents triggers <key> remove inbound
146
+ ekanos agents links <key> add billing-agent --kind delegate --mode handoff
147
+ ekanos agents links <key> remove billing-agent
148
+ ekanos agents overrides <key> # show the switches
149
+ ekanos agents overrides <key> enable triggers
150
+ ekanos agents overrides <key> disable policy
151
+ ```
152
+
153
+ `--filter field=value` repeats; a value with commas is a list, `true` or
154
+ `false` a switch, anything else text. `--disabled` adds a trigger turned off.
155
+
80
156
  ## What the host checks on `apply`
81
157
 
82
158
  Every product and tool must be in the Source's catalog, every skill must
@@ -32,7 +32,15 @@ Built with `ekanos agents` by a Source admin.
32
32
  Good for: the same job done by several teams; a central owner who improves
33
33
  it over time; consistent behaviour and approvals.
34
34
 
35
- Piloting: publish, then have one team turn it on first. Teams that never
35
+ **A Source admin can do a team's part from the terminal**:
36
+ `ekanos teams agents enable <team> <key> [--as <member-email>]` turns it on
37
+ for the team (running as that member, the team's owner by default),
38
+ `ekanos teams agents pin|unpin`, `ekanos teams agents policy`, and
39
+ `ekanos teams triggers|links|mcp` set up the rest. `ekanos teams agents list
40
+ <team>` shows where each team stands and what it still needs connected.
41
+
42
+ Piloting: publish, then have one team turn it on first (or turn it on for
43
+ them with `ekanos teams agents enable`). Teams that never
36
44
  turn it on are unaffected. Or keep it as an applied draft until the pilot
37
45
  plan is ready.
38
46
 
@@ -0,0 +1,134 @@
1
+ # Setting up an integration, concierge-style
2
+
3
+ The user wants an agent to use something (Zendesk, Linear, Slack…) and it is
4
+ not ready yet. Your job is to get it ready **with** them, the way a good
5
+ concierge would: you already know the way, you say what happens next and
6
+ why, you hand them exactly what to type or paste, and you check that each
7
+ step worked before moving on. They should never have to read a manual or
8
+ guess.
9
+
10
+ ## 1. Find out where it stands (before saying anything)
11
+
12
+ 1. `npx @ekanos/cli catalog <product> --source <slug> --json`. Its
13
+ `setupSteps` are the integration's own steps, already filled in with this
14
+ Source's exact callback URLs and scopes. Each step has an `actor`:
15
+ `ekanos` (an Ekanos super admin), `source_admin` (the user, if they hold
16
+ that seat), or `team`.
17
+ 2. Which teams will use it: `npx @ekanos/cli teams list --source <slug> --json`.
18
+ 3. For a team: `npx @ekanos/cli teams show <team> <product> --source <slug> --json`
19
+ shows what that team has already done (app fields entered, webhook URL
20
+ minted, connected, who connected and when) and the link to the team's page
21
+ in the app.
22
+
23
+ Then tell the user, in two or three lines, what is done, what is left, and
24
+ who does each remaining step. If a step needs Ekanos (the product is not
25
+ enabled for the Source), say so and stop there for that product; nobody else
26
+ can do it.
27
+
28
+ ## 2. Decide where the connection lives
29
+
30
+ Explain the choice in plain words, recommend one, and let them pick. There
31
+ are three places, and **the team account is the default owner** of an
32
+ integration: its members, its data and its connections.
33
+
34
+ | Where | What it means | Who sets it up | Typical for |
35
+ |---|---|---|---|
36
+ | **Each team, through the Source's app** | One app, created once for the whole Source under the Source's own name. Each team then signs in through it. | The Source admin creates the app (`references/app-setup.md`, `ekanos apps set`); each team connects | Linear, Slack: products where every team is a separate workspace |
37
+ | **Each team, with its own app** | Each team creates its own app in the provider and enters its credentials | Each team, or the Source admin on the team's behalf with `ekanos teams` (below) | Zendesk: each bank or business has its own Zendesk |
38
+ | **One connection for the whole Source** | Every team's provided agent uses the Source's single connection, limited to what is routed to that team | The Source admin, once | One shared Zendesk for many teams. **Not available yet**: say so, and suggest a connection per team for now |
39
+
40
+ The agent records the choice in `agent.json` → `connections`:
41
+ `{ "zendesk": { "owner": "team", "team": "required" } }` means each team
42
+ uses its own Zendesk and the agent waits at "needs setup" until it is
43
+ connected (`references/agent-json.md`).
44
+
45
+ ## 3. Walk it, one step per message
46
+
47
+ - **Say why before what.** "Zendesk needs to know where to send new tickets,
48
+ so we'll give it a webhook address from Ekanos." One sentence, then the step.
49
+ - **Exact values only.** Take URLs, field names and console paths from the
50
+ catalog or `teams show`, never from memory. Paste URLs exactly; providers
51
+ match them byte for byte.
52
+ - **Use the provider's words** for its screens ("Admin Center › Apps and
53
+ integrations › Webhooks"), and Ekanos's words for Ekanos.
54
+ - **Wait** for "done" or a question after each step. Then **check**: re-run
55
+ `teams show` (or `catalog`) and say what changed ("Ekanos sees the client ID
56
+ and secret now"). If the check disagrees with what they said, look at the
57
+ difference with them; don't move on.
58
+ - **Secrets never pass through the chat.** Client secrets and signing
59
+ secrets go in at the user's own terminal (`ekanos apps set`,
60
+ `ekanos teams app set`, with hidden input) or in the team's page in the app.
61
+ If they paste one into the chat anyway, tell them to rotate it in the
62
+ provider's console and enter the new one at the terminal.
63
+ - **Signing in is a person's click.** Connecting (the provider's consent
64
+ screen) happens in the browser, by someone on the team, from the link in
65
+ `teams show` (or just the link and whether it is connected yet:
66
+ `teams connect <team> <product>`). Tell them who should click it: the account the integration
67
+ acts as. A dedicated seat such as "Support agent" makes its replies easy
68
+ to recognise.
69
+ - **Close each product out**: one line on what now works ("New tickets in
70
+ Acme's Zendesk will start the support agent"), then move on to the next
71
+ product or back to the agent.
72
+
73
+ ## 4. A team's own app, from the terminal
74
+
75
+ When the user is a Source admin setting things up for a team (common in a
76
+ pilot), the team-side steps can be done from the CLI:
77
+
78
+ 1. `npx @ekanos/cli teams show <team> <product> --source <slug>`: the
79
+ callback URL to register and the integration's steps.
80
+ 2. They create the app in the provider's console, step by step as above.
81
+ 3. **In their own terminal:**
82
+ `npx @ekanos/cli teams app set <team> <product> --source <slug>`. It asks
83
+ for each field and hides the secret ones. It refuses to run inside Claude;
84
+ that is deliberate.
85
+ 4. If the integration takes webhooks:
86
+ `npx @ekanos/cli teams webhook <team> <product> --source <slug>` prints the
87
+ team's webhook URL **once**. Have them paste it into the provider at that
88
+ moment. Running it again makes a new URL and the old one stops working, so
89
+ it asks for `--yes` when one already exists.
90
+ 5. Someone on the team opens the link from `teams show` and clicks Connect.
91
+ 6. `teams show` again: every step ✓.
92
+
93
+ ## Worked example: Zendesk for one team
94
+
95
+ The steps below are what Zendesk's own setup says, in order. Take the URLs
96
+ from `teams show zendesk`.
97
+
98
+ 1. **OAuth client.** Admin Center › Apps and integrations › APIs › OAuth
99
+ Clients › Add OAuth client. Client kind **Confidential**, Redirect URL =
100
+ the callback URL. Save; copy the Identifier and the Secret (shown once).
101
+ 2. **Webhook address.** `ekanos teams webhook <team> zendesk`, then in Admin
102
+ Center › Apps and integrations › Webhooks › Create webhook › Trigger or
103
+ automation: that URL, POST, JSON, no authentication. Open it and Reveal
104
+ secret.
105
+ 3. **Credentials into Ekanos.** `ekanos teams app set <team> zendesk` in
106
+ their terminal: subdomain, client ID, client secret, webhook signing
107
+ secret.
108
+ 4. **Connect.** Someone on the team opens the link and connects, as the
109
+ Zendesk agent the integration should reply as.
110
+ 5. **Zendesk triggers.** Objects and rules › Triggers: one for *Ticket is
111
+ Created* and one for a customer's public reply, each notifying the webhook
112
+ with the body from the steps. `"reply_as": "internal"` makes the agent's
113
+ answer an internal note a person reviews before the customer sees it.
114
+ Recommend that until the team trusts the agent; `"public"` lets it answer
115
+ the customer directly.
116
+ 6. **The agent's trigger.** In the app, on the agent: *A ticket is created*
117
+ (and *A customer replies*), with any filters.
118
+ 7. **Try it.** Open a test ticket as a customer and watch: the agent reads
119
+ the ticket, looks things up, and answers on it. If it wants to change the
120
+ ticket (status, priority, assignee), it leaves an internal note saying
121
+ what it asked for, and someone approves it in Ekanos.
122
+
123
+ ## When something goes wrong
124
+
125
+ - **Redirect mismatch on Connect:** the callback URL in the provider differs
126
+ from `teams show`'s, often by a trailing slash or a custom domain. Fix it in
127
+ the provider.
128
+ - **Tickets don't start the agent:** `teams show` should list a live webhook
129
+ that has been used recently. If it has never been used, the provider is not
130
+ calling it: check the Zendesk trigger's conditions and that it notifies the
131
+ right webhook. If it has been used but nothing ran, check that the agent has
132
+ a trigger for that event and that the team connected the product.
133
+ - **A command says it needs a terminal:** it was run inside Claude. Ask them
134
+ to run it in a terminal tab themselves.
@@ -0,0 +1,78 @@
1
+ # Skills: what an agent knows how to do
2
+
3
+ A skill is a folder — `SKILL.md` plus optional `references/` and `assets/` —
4
+ in the same format Claude Code uses. An agent lists its skills by name and
5
+ description and loads one when the task calls for it.
6
+
7
+ ```
8
+ agents/support-triage/
9
+ agent.json
10
+ AGENT.md
11
+ skills/
12
+ refund-policy/
13
+ SKILL.md # frontmatter: name, description; then the instructions
14
+ references/policy-2026.md
15
+ ```
16
+
17
+ `SKILL.md` frontmatter:
18
+
19
+ ```markdown
20
+ ---
21
+ name: refund-policy
22
+ description: How we decide refunds — use when a customer asks for money back.
23
+ ---
24
+
25
+ # Refund policy
26
+ ...
27
+ ```
28
+
29
+ The slug is `name` when it is lowercase-with-hyphens, else the folder's
30
+ name. The `description` is how the agent decides to load the skill — write it
31
+ as "what it is — when to use it".
32
+
33
+ ## Two levels
34
+
35
+ | Level | Command | Who sees it |
36
+ |---|---|---|
37
+ | **Source** (default) | `ekanos skills push <dir>` | Every team on the Source. An agent template may name it in `skills`. |
38
+ | **Team** | `ekanos skills push <dir> --team <team-slug>` | That one team only. For content another team must not see (one bank's own guide). |
39
+
40
+ Pushing is a Source admin's. A push is **live at once**: there is no draft
41
+ and no publish step, and every agent naming the skill reads the new version
42
+ on its next run. Show the user the folder before pushing it.
43
+
44
+ ## Commands
45
+
46
+ ```bash
47
+ npx @ekanos/cli skills list --source <slug> --json # the Source's + platform skills
48
+ npx @ekanos/cli skills list --team <team> --source <slug> # one team's own
49
+ npx @ekanos/cli skills show <slug> --source <slug> # files, versions
50
+ npx @ekanos/cli skills push agents/<key>/skills/<slug> --source <slug> --json
51
+ npx @ekanos/cli skills pull <slug> --source <slug> [--out <dir>]
52
+ ```
53
+
54
+ `push` prints what changed (files added, changed, removed) and the new
55
+ version; an identical folder is `unchanged` and stores nothing — safe to run
56
+ again.
57
+
58
+ ## With `agents apply`
59
+
60
+ `ekanos agents apply agents/<key>` pushes every `agents/<key>/skills/<slug>/`
61
+ folder at Source level first, then names those slugs in the agent's `skills`
62
+ (you need not repeat them in `agent.json`). `--no-skills` skips that.
63
+ Because pushing needs a Source admin, a `developer` seat applying an agent
64
+ with skill folders gets SOURCE_ADMIN_REQUIRED; pass `--no-skills` and ask a
65
+ Source admin to `skills push` them.
66
+
67
+ A provided agent (template) names Source or platform skills only. A team
68
+ skill is attached by that team to its own agents — or to a provided agent
69
+ whose `teamCustomization.skills` is on.
70
+
71
+ ## Limits today
72
+
73
+ - Prompt skills only: the agent reads `SKILL.md`'s instructions. Files under
74
+ `references/` and `assets/` are stored with each version and come back with
75
+ `pull`, but the agent does not open them at run time — put what it must
76
+ know in `SKILL.md`.
77
+ - A `scripts/` folder is refused: executable skills need the in-app review.
78
+ - Up to 100 files, 1 MB per file, 3 MB per skill.
@@ -1,9 +1,28 @@
1
1
  # What starts the agent: triggers
2
2
 
3
- Triggers belong to each team. They are not part of the agent template: a team
4
- adds them on its copy (Agents → the agent → Triggers) after turning it on.
5
- Your job in the interview is to say which triggers each team will want, and
6
- what it must connect first, so the team-side checklist is complete.
3
+ The template can declare its triggers (`triggers` in `agent.json`, or
4
+ `ekanos agents triggers <key> add …`): every team that turns the agent on gets
5
+ them, and each publish keeps them in step. By default a team cannot change
6
+ them — they show as "Set by your administrator". Turn on
7
+ `teamOverrides.triggers` (`ekanos agents overrides <key> enable triggers`) to
8
+ let teams add their own and turn the template's on or off. A template that
9
+ declares no triggers leaves them to each team, set on its copy (Agents → the
10
+ agent → Triggers) after turning it on.
11
+
12
+ Ask in the interview: should the Source set what starts the agent for every
13
+ team (recommended when every team works the same way), or each team? Either
14
+ way say what each team must connect first, so the checklist is complete.
15
+ A webhook trigger's secret is always per team: the template declares the
16
+ trigger, each team creates or rotates its own secret in the app.
17
+
18
+ A Source admin can add them for the team from the terminal instead:
19
+ `ekanos teams triggers list|add|remove <team> <key> …` — for example
20
+ `ekanos teams triggers add acme-support support-agent event zendesk.ticket_created --filter '{"tags":["billing"]}'`,
21
+ `… schedule --cron "0 9 * * 1-5" --timezone America/Los_Angeles --prompt "…"`,
22
+ `… webhook --prompt "…"` (prints the URL and token once) or `… manual`.
23
+ Slack and Linear triggers are added in the app. When a provided agent's
24
+ template declares triggers and has not turned on team overrides for them, the
25
+ team's triggers follow the template and these commands refuse to change them.
7
26
 
8
27
  ## Kinds a team can add
9
28
 
@@ -33,7 +52,8 @@ Guide: https://sdk.ekanos.dev/guides/linear-agent
33
52
  **Zendesk** (`zendesk.ticket_created`, `zendesk.comment_added`; filters such
34
53
  as brand, group, tags, organization) — when present in the catalog, each team
35
54
  connects its own Zendesk (subdomain plus an OAuth client it creates in
36
- Zendesk) and adds a Zendesk webhook pointing at its Ekanos endpoint. The agent
55
+ Zendesk) and adds a Zendesk webhook pointing at its Ekanos endpoint
56
+ (walk-through: `set-up-an-integration.md`, worked example). The agent
37
57
  answers on the ticket as a public reply or an internal note — recommend
38
58
  internal notes, or approval on public replies, until the team trusts it.
39
59
 
@@ -44,4 +64,5 @@ internal notes, or approval on public replies, until the team trusts it.
44
64
  it (the agent shows what is missing).
45
65
  3. Do each integration's own setup (team app, webhook) from its guide.
46
66
  4. Confirm access if asked.
47
- 5. Add the triggers above, then run one real example and review it.
67
+ 5. Add the triggers above — unless the template already sets them — then
68
+ run one real example and review it.