tycono-server 0.1.0-beta.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 (84) hide show
  1. package/bin/cli.js +35 -0
  2. package/bin/server.ts +160 -0
  3. package/package.json +50 -0
  4. package/src/api/package.json +31 -0
  5. package/src/api/src/create-app.ts +90 -0
  6. package/src/api/src/create-server.ts +251 -0
  7. package/src/api/src/engine/agent-loop.ts +738 -0
  8. package/src/api/src/engine/authority-validator.ts +149 -0
  9. package/src/api/src/engine/context-assembler.ts +912 -0
  10. package/src/api/src/engine/index.ts +27 -0
  11. package/src/api/src/engine/knowledge-gate.ts +365 -0
  12. package/src/api/src/engine/llm-adapter.ts +304 -0
  13. package/src/api/src/engine/org-tree.ts +270 -0
  14. package/src/api/src/engine/role-lifecycle.ts +369 -0
  15. package/src/api/src/engine/runners/claude-cli.ts +796 -0
  16. package/src/api/src/engine/runners/direct-api.ts +66 -0
  17. package/src/api/src/engine/runners/index.ts +30 -0
  18. package/src/api/src/engine/runners/types.ts +95 -0
  19. package/src/api/src/engine/skill-template.ts +134 -0
  20. package/src/api/src/engine/tools/definitions.ts +201 -0
  21. package/src/api/src/engine/tools/executor.ts +611 -0
  22. package/src/api/src/routes/active-sessions.ts +134 -0
  23. package/src/api/src/routes/coins.ts +153 -0
  24. package/src/api/src/routes/company.ts +57 -0
  25. package/src/api/src/routes/cost.ts +141 -0
  26. package/src/api/src/routes/engine.ts +220 -0
  27. package/src/api/src/routes/execute.ts +1075 -0
  28. package/src/api/src/routes/git.ts +211 -0
  29. package/src/api/src/routes/knowledge.ts +378 -0
  30. package/src/api/src/routes/operations.ts +309 -0
  31. package/src/api/src/routes/preferences.ts +63 -0
  32. package/src/api/src/routes/presets.ts +123 -0
  33. package/src/api/src/routes/projects.ts +82 -0
  34. package/src/api/src/routes/quests.ts +41 -0
  35. package/src/api/src/routes/roles.ts +112 -0
  36. package/src/api/src/routes/save.ts +152 -0
  37. package/src/api/src/routes/sessions.ts +288 -0
  38. package/src/api/src/routes/setup.ts +437 -0
  39. package/src/api/src/routes/skills.ts +357 -0
  40. package/src/api/src/routes/speech.ts +959 -0
  41. package/src/api/src/routes/supervision.ts +136 -0
  42. package/src/api/src/routes/sync.ts +165 -0
  43. package/src/api/src/server.ts +59 -0
  44. package/src/api/src/services/activity-stream.ts +184 -0
  45. package/src/api/src/services/activity-tracker.ts +115 -0
  46. package/src/api/src/services/claude-md-manager.ts +94 -0
  47. package/src/api/src/services/company-config.ts +115 -0
  48. package/src/api/src/services/database.ts +77 -0
  49. package/src/api/src/services/digest-engine.ts +313 -0
  50. package/src/api/src/services/execution-manager.ts +1036 -0
  51. package/src/api/src/services/file-reader.ts +77 -0
  52. package/src/api/src/services/git-save.ts +614 -0
  53. package/src/api/src/services/job-manager.ts +16 -0
  54. package/src/api/src/services/knowledge-importer.ts +466 -0
  55. package/src/api/src/services/markdown-parser.ts +173 -0
  56. package/src/api/src/services/port-registry.ts +222 -0
  57. package/src/api/src/services/preferences.ts +150 -0
  58. package/src/api/src/services/preset-loader.ts +149 -0
  59. package/src/api/src/services/pricing.ts +34 -0
  60. package/src/api/src/services/scaffold.ts +546 -0
  61. package/src/api/src/services/session-store.ts +340 -0
  62. package/src/api/src/services/supervisor-heartbeat.ts +897 -0
  63. package/src/api/src/services/team-recommender.ts +382 -0
  64. package/src/api/src/services/token-ledger.ts +127 -0
  65. package/src/api/src/services/wave-messages.ts +194 -0
  66. package/src/api/src/services/wave-multiplexer.ts +356 -0
  67. package/src/api/src/services/wave-tracker.ts +359 -0
  68. package/src/api/src/utils/role-level.ts +31 -0
  69. package/src/core/scaffolder.ts +620 -0
  70. package/src/shared/types.ts +224 -0
  71. package/templates/CLAUDE.md.tmpl +239 -0
  72. package/templates/company.md.tmpl +17 -0
  73. package/templates/gitignore.tmpl +28 -0
  74. package/templates/roles.md.tmpl +8 -0
  75. package/templates/skills/_manifest.json +23 -0
  76. package/templates/skills/agent-browser/SKILL.md +159 -0
  77. package/templates/skills/agent-browser/meta.json +19 -0
  78. package/templates/skills/akb-linter/SKILL.md +125 -0
  79. package/templates/skills/akb-linter/meta.json +12 -0
  80. package/templates/skills/knowledge-gate/SKILL.md +120 -0
  81. package/templates/skills/knowledge-gate/meta.json +12 -0
  82. package/templates/teams/agency.json +58 -0
  83. package/templates/teams/research.json +58 -0
  84. package/templates/teams/startup.json +58 -0
@@ -0,0 +1,66 @@
1
+ import { runAgentLoop } from '../agent-loop.js';
2
+ import { AnthropicProvider, type LLMProvider } from '../llm-adapter.js';
3
+ import { getTokenLedger } from '../../services/token-ledger.js';
4
+ import type { ExecutionRunner, RunnerConfig, RunnerCallbacks, RunnerHandle, RunnerResult } from './types.js';
5
+
6
+ /* ─── Direct API Runner ──────────────────────── */
7
+
8
+ /**
9
+ * Anthropic API를 직접 호출하는 실행 엔진.
10
+ *
11
+ * - @anthropic-ai/sdk로 Claude API 직접 호출
12
+ * - Agent Loop가 도구 실행, dispatch를 내부 처리
13
+ * - 토큰 사용량 정확히 추적 가능
14
+ * - ANTHROPIC_API_KEY 환경변수 필수
15
+ *
16
+ * 활성화: EXECUTION_ENGINE=direct-api
17
+ */
18
+ export class DirectApiRunner implements ExecutionRunner {
19
+ private llm: LLMProvider;
20
+
21
+ constructor(llm?: LLMProvider) {
22
+ this.llm = llm ?? new AnthropicProvider();
23
+ }
24
+
25
+ execute(config: RunnerConfig, callbacks: RunnerCallbacks): RunnerHandle {
26
+ const abortController = new AbortController();
27
+
28
+ const tokenLedger = getTokenLedger(config.companyRoot);
29
+
30
+ const promise = runAgentLoop({
31
+ companyRoot: config.companyRoot,
32
+ roleId: config.roleId,
33
+ task: config.task,
34
+ sourceRole: config.sourceRole,
35
+ orgTree: config.orgTree,
36
+ readOnly: config.readOnly,
37
+ maxTurns: config.maxTurns,
38
+ codeRoot: config.codeRoot,
39
+ llm: this.llm,
40
+ abortSignal: abortController.signal,
41
+ sessionId: config.sessionId,
42
+ model: config.model,
43
+ tokenLedger,
44
+ attachments: config.attachments,
45
+ onText: (text) => callbacks.onText?.(text),
46
+ onToolExec: (name, input) => callbacks.onToolUse?.(name, input),
47
+ onDispatch: (roleId, task) => callbacks.onDispatch?.(roleId, task),
48
+ onConsult: (roleId, question) => callbacks.onConsult?.(roleId, question),
49
+ onTurnComplete: (turn) => callbacks.onTurnComplete?.(turn),
50
+ onPromptAssembled: (systemPrompt, userTask) => callbacks.onPromptAssembled?.(systemPrompt, userTask),
51
+ onAbortSession: config.onAbortSession,
52
+ onAmendSession: config.onAmendSession,
53
+ }).then((agentResult): RunnerResult => ({
54
+ output: agentResult.output,
55
+ turns: agentResult.turns,
56
+ totalTokens: agentResult.totalTokens,
57
+ toolCalls: agentResult.toolCalls,
58
+ dispatches: agentResult.dispatches,
59
+ }));
60
+
61
+ return {
62
+ promise,
63
+ abort: () => abortController.abort(),
64
+ };
65
+ }
66
+ }
@@ -0,0 +1,30 @@
1
+ export type { ExecutionRunner, RunnerConfig, RunnerCallbacks, RunnerHandle, RunnerResult } from './types.js';
2
+ export { ClaudeCliRunner } from './claude-cli.js';
3
+ export { DirectApiRunner } from './direct-api.js';
4
+
5
+ import { ClaudeCliRunner } from './claude-cli.js';
6
+ import { DirectApiRunner } from './direct-api.js';
7
+ import type { ExecutionRunner } from './types.js';
8
+ import type { LLMProvider } from '../llm-adapter.js';
9
+
10
+ /* ─── Runner Factory ─────────────────────────── */
11
+
12
+ /**
13
+ * 환경변수 EXECUTION_ENGINE에 따라 적절한 Runner 생성.
14
+ *
15
+ * - claude-cli (기본): Claude Code CLI 사용 — 구독 기반, 비용 없음
16
+ * - direct-api: Anthropic API 직접 호출 — ANTHROPIC_API_KEY 필요
17
+ *
18
+ * @param llm - LLMProvider를 전달하면 direct-api 모드에서 해당 provider 사용
19
+ */
20
+ export function createRunner(llm?: LLMProvider): ExecutionRunner {
21
+ const engine = process.env.EXECUTION_ENGINE || 'claude-cli';
22
+
23
+ switch (engine) {
24
+ case 'direct-api':
25
+ return new DirectApiRunner(llm);
26
+ case 'claude-cli':
27
+ default:
28
+ return new ClaudeCliRunner();
29
+ }
30
+ }
@@ -0,0 +1,95 @@
1
+ import type { OrgTree } from '../org-tree.js';
2
+
3
+ /* ─── Runner Interface ──────────────────────── */
4
+
5
+ /**
6
+ * Execution Runner 추상화.
7
+ *
8
+ * 현재 구현:
9
+ * - claude-cli: Claude Code CLI (`claude -p`) 기반 — 구독으로 비용 부담 없음
10
+ * - direct-api: Anthropic API 직접 호출 — 향후 전환용
11
+ *
12
+ * EXECUTION_ENGINE 환경변수로 전환 (기본값: claude-cli)
13
+ */
14
+
15
+ /* ─── Attachment Types ────────────────────────── */
16
+
17
+ export interface ImageAttachment {
18
+ type: 'image';
19
+ data: string; // base64 encoded
20
+ name: string;
21
+ mediaType: 'image/png' | 'image/jpeg' | 'image/gif' | 'image/webp';
22
+ }
23
+
24
+ /* ─── Config ──────────────────────────────────── */
25
+
26
+ export type { TeamStatus } from '../../../../shared/types.js';
27
+ import type { TeamStatus } from '../../../../shared/types.js';
28
+
29
+ export interface RunnerConfig {
30
+ companyRoot: string;
31
+ roleId: string;
32
+ task: string;
33
+ sourceRole: string;
34
+ orgTree: OrgTree;
35
+ readOnly?: boolean;
36
+ maxTurns?: number;
37
+ model?: string;
38
+ /** D-014: Session ID for tracking (required — primary identifier for token ledger). */
39
+ sessionId: string;
40
+ teamStatus?: TeamStatus;
41
+ attachments?: ImageAttachment[];
42
+ /** Selective dispatch scope — only these roles can be dispatched to */
43
+ targetRoles?: string[];
44
+ /** EG-001: Code project root for bash_execute tool */
45
+ codeRoot?: string;
46
+ /** PSM-004: Environment variables to inject (e.g., port assignments) */
47
+ env?: Record<string, string>;
48
+ /** Wave-scoped preset ID for knowledge injection */
49
+ presetId?: string;
50
+ /** CLI session ID for --resume (context continuity across turn limits) */
51
+ cliSessionId?: string;
52
+ /** SV-7: Supervision — abort a running session */
53
+ onAbortSession?: (sessionId: string) => boolean;
54
+ /** SV-6: Supervision — amend a running session */
55
+ onAmendSession?: (sessionId: string, instruction: string) => boolean;
56
+ }
57
+
58
+ /* ─── Callbacks ───────────────────────────────── */
59
+
60
+ export interface RunnerCallbacks {
61
+ onText?: (text: string) => void;
62
+ onThinking?: (text: string) => void;
63
+ onToolUse?: (tool: string, input?: Record<string, unknown>) => void;
64
+ onDispatch?: (roleId: string, task: string) => void;
65
+ onConsult?: (roleId: string, question: string) => void;
66
+ onTurnComplete?: (turn: number) => void;
67
+ onError?: (error: string) => void;
68
+ /** Trace: emitted when system prompt is assembled, for full prompt capture */
69
+ onPromptAssembled?: (systemPrompt: string, userTask: string) => void;
70
+ }
71
+
72
+ /* ─── Result ──────────────────────────────────── */
73
+
74
+ export interface RunnerResult {
75
+ output: string;
76
+ turns: number;
77
+ totalTokens: { input: number; output: number };
78
+ toolCalls: Array<{ name: string; input?: Record<string, unknown> }>;
79
+ dispatches: Array<{ roleId: string; task: string; result?: string }>;
80
+ /** CLI session ID captured from stream-json result event (for --resume) */
81
+ cliSessionId?: string;
82
+ }
83
+
84
+ /* ─── Handle (for abort support) ──────────────── */
85
+
86
+ export interface RunnerHandle {
87
+ promise: Promise<RunnerResult>;
88
+ abort: () => void;
89
+ }
90
+
91
+ /* ─── Runner Interface ────────────────────────── */
92
+
93
+ export interface ExecutionRunner {
94
+ execute(config: RunnerConfig, callbacks: RunnerCallbacks): RunnerHandle;
95
+ }
@@ -0,0 +1,134 @@
1
+ import type { OrgNode } from './org-tree.js';
2
+
3
+ /**
4
+ * Level 1: 템플릿 기반 SKILL.md 자동 생성
5
+ *
6
+ * role.yaml 데이터를 기반으로 SKILL.md 골격을 생성한다.
7
+ * Level 2 (AI 보강)는 Agent Runtime 완성 후 추가.
8
+ */
9
+ export function generateSkillMd(node: OrgNode): string {
10
+ const approvalTarget = node.reportsTo || 'CEO';
11
+
12
+ const autonomousRows = node.authority.autonomous
13
+ .map((a) => `| ${a} | ✅ | |`)
14
+ .join('\n');
15
+
16
+ const approvalRows = node.authority.needsApproval
17
+ .map((a) => `| ${a} | | ⚠️ ${approvalTarget} 승인 |`)
18
+ .join('\n');
19
+
20
+ const readPaths = node.knowledge.reads
21
+ .map((p) => `| 읽기 | \`${p}\` |`)
22
+ .join('\n');
23
+
24
+ const writePaths = node.knowledge.writes
25
+ .map((p) => `| 쓰기 | \`${p}\` |`)
26
+ .join('\n');
27
+
28
+ const coreMaxim = extractMaxim(node.persona);
29
+
30
+ return `# ${node.name} Skill
31
+
32
+ ---
33
+ name: ${node.id}
34
+ description: |
35
+ ${node.persona.trim().split('\n')[0]}
36
+ allowed-tools: [Read, Write, Edit, Glob, Grep, Bash]
37
+ ---
38
+
39
+ > "${coreMaxim}"
40
+
41
+ ## 핵심 책임
42
+
43
+ | 영역 | 자율 | 승인 필요 |
44
+ |------|------|-----------|
45
+ ${autonomousRows}
46
+ ${approvalRows}
47
+
48
+ ---
49
+
50
+ ## 작업 프로세스
51
+
52
+ ### 작업 수신 시
53
+
54
+ \`\`\`
55
+ 1. 관련 Hub 문서 읽기
56
+ 2. 기존 문서/코드 확인 (grep 키워드 3개+)
57
+ 3. 작업 수행
58
+ 4. 결과 기록 (저널 + AKB)
59
+ 5. ${approvalTarget} 승인 필요 사항은 [APPROVAL_NEEDED] 태그
60
+ \`\`\`
61
+
62
+ ---
63
+
64
+ ## 핵심 파일 경로
65
+
66
+ | 용도 | 경로 |
67
+ |------|------|
68
+ ${readPaths}
69
+ ${writePaths}
70
+ | 저널 | \`roles/${node.id}/journal/\` |
71
+
72
+ ---
73
+
74
+ ## 저널 작성
75
+
76
+ 작업 완료 후 \`roles/${node.id}/journal/YYYY-MM-DD.md\`:
77
+
78
+ \`\`\`markdown
79
+ # ${node.name} Journal — YYYY-MM-DD
80
+
81
+ ### {작업 제목}
82
+
83
+ #### 완료 사항
84
+ 1. ...
85
+
86
+ #### [APPROVAL_NEEDED] (있는 경우)
87
+ - ...
88
+
89
+ #### 생성/수정 파일
90
+ | 파일 | 작업 |
91
+ |------|------|
92
+ \`\`\`
93
+
94
+ ---
95
+
96
+ ## 보고 규칙
97
+
98
+ - **일일**: ${node.reports.daily || '완료 사항 + 진행 중 + 블로커'}
99
+ - **주간**: ${node.reports.weekly || '주간 성과 + 다음 주 목표'}
100
+
101
+ ---
102
+
103
+ ## Equipped Skills
104
+
105
+ ${node.skills?.length ? node.skills.map((s) => `- \`${s}\` — see \`.claude/skills/_shared/${s}/SKILL.md\``).join('\n') : '- (none)'}
106
+
107
+ ---
108
+
109
+ ## AKB 규칙
110
+
111
+ - 작업 결과는 반드시 AKB에 기록
112
+ - 일일 업무는 \`roles/${node.id}/journal/\`에 기록
113
+ - ${approvalTarget} 승인 필요 시 [APPROVAL_NEEDED] 태그 사용
114
+ - 새 문서 생성 시 관련 Hub에 등록 필수
115
+
116
+ ---
117
+
118
+ ## 핵심 원칙
119
+
120
+ > "작업 전에 Hub 문서를 먼저 읽어라. 이미 있는 것을 다시 만들지 마라."
121
+
122
+ > "모든 작업에는 기록이 남아야 한다. 기록 없는 작업은 없었던 것이다."
123
+ `;
124
+ }
125
+
126
+ function extractMaxim(persona: string): string {
127
+ // Try to find a quoted phrase in persona
128
+ const quoted = persona.match(/"([^"]+)"/);
129
+ if (quoted) return quoted[1];
130
+
131
+ // Otherwise use the first sentence
132
+ const firstSentence = persona.trim().split(/[.。]/)[0];
133
+ return firstSentence.trim();
134
+ }
@@ -0,0 +1,201 @@
1
+ import type { ToolDefinition } from '../llm-adapter.js';
2
+
3
+ /**
4
+ * 읽기 전용 도구 — Ask 엔드포인트에서도 사용
5
+ */
6
+ export const READ_TOOLS: ToolDefinition[] = [
7
+ {
8
+ name: 'read_file',
9
+ description: 'Read the contents of a file. Returns the file content as text.',
10
+ input_schema: {
11
+ type: 'object',
12
+ properties: {
13
+ path: { type: 'string', description: 'File path relative to company root (e.g., "knowledge/roles/cto/role.yaml")' },
14
+ },
15
+ required: ['path'],
16
+ },
17
+ },
18
+ {
19
+ name: 'list_files',
20
+ description: 'List files in a directory matching a glob pattern.',
21
+ input_schema: {
22
+ type: 'object',
23
+ properties: {
24
+ directory: { type: 'string', description: 'Directory path relative to company root' },
25
+ pattern: { type: 'string', description: 'Glob pattern (default: "*.md")', default: '*.md' },
26
+ },
27
+ required: ['directory'],
28
+ },
29
+ },
30
+ {
31
+ name: 'search_files',
32
+ description: 'Search for text content across files using a pattern.',
33
+ input_schema: {
34
+ type: 'object',
35
+ properties: {
36
+ pattern: { type: 'string', description: 'Search pattern (regex supported)' },
37
+ directory: { type: 'string', description: 'Directory to search in (relative to company root)', default: '.' },
38
+ file_pattern: { type: 'string', description: 'File glob to filter (e.g., "*.md")', default: '*' },
39
+ },
40
+ required: ['pattern'],
41
+ },
42
+ },
43
+ ];
44
+
45
+ /**
46
+ * 쓰기 도구 — Assign 엔드포인트에서만 사용
47
+ */
48
+ export const WRITE_TOOLS: ToolDefinition[] = [
49
+ {
50
+ name: 'write_file',
51
+ description: 'Create or overwrite a file with the given content.',
52
+ input_schema: {
53
+ type: 'object',
54
+ properties: {
55
+ path: { type: 'string', description: 'File path relative to company root' },
56
+ content: { type: 'string', description: 'File content to write' },
57
+ },
58
+ required: ['path', 'content'],
59
+ },
60
+ },
61
+ {
62
+ name: 'edit_file',
63
+ description: 'Edit a file by replacing a specific string with another string.',
64
+ input_schema: {
65
+ type: 'object',
66
+ properties: {
67
+ path: { type: 'string', description: 'File path relative to company root' },
68
+ old_string: { type: 'string', description: 'Exact string to find and replace' },
69
+ new_string: { type: 'string', description: 'Replacement string' },
70
+ },
71
+ required: ['path', 'old_string', 'new_string'],
72
+ },
73
+ },
74
+ ];
75
+
76
+ /**
77
+ * 디스패치 도구 — 매니저 Role에게만 제공
78
+ */
79
+ export const DISPATCH_TOOL: ToolDefinition = {
80
+ name: 'dispatch',
81
+ description: 'Assign a task to a subordinate role. The subordinate will execute the task independently and return the result. Only available for roles with direct reports.',
82
+ input_schema: {
83
+ type: 'object',
84
+ properties: {
85
+ roleId: { type: 'string', description: 'Target role ID (e.g., "engineer", "pm")' },
86
+ task: { type: 'string', description: 'Task description for the subordinate' },
87
+ },
88
+ required: ['roleId', 'task'],
89
+ },
90
+ };
91
+
92
+ /**
93
+ * Bash 실행 도구 — 코드 프로젝트에서 시스템 명령 실행 (EG-001)
94
+ */
95
+ export const BASH_TOOL: ToolDefinition = {
96
+ name: 'bash_execute',
97
+ description: 'Execute a shell command in the code project directory. Use for git, npm, tsc, node, test runners, and build tools. Commands run in the codeRoot directory (not company knowledge base). Dangerous commands (rm -rf, sudo, etc.) are blocked.',
98
+ input_schema: {
99
+ type: 'object',
100
+ properties: {
101
+ command: { type: 'string', description: 'Shell command to execute' },
102
+ timeout: { type: 'number', description: 'Timeout in milliseconds (default: 30000, max: 120000)' },
103
+ cwd: { type: 'string', description: 'Working directory relative to codeRoot (default: ".")' },
104
+ },
105
+ required: ['command'],
106
+ },
107
+ };
108
+
109
+ /**
110
+ * Supervision 도구 — C-Level에게만 제공 (부하/동료 세션 감시)
111
+ */
112
+ export const HEARTBEAT_WATCH_TOOL: ToolDefinition = {
113
+ name: 'heartbeat_watch',
114
+ description: 'Block and watch activity streams of subordinates (or peers). Returns a digest of events after the specified duration or when an alert event occurs. Use this to supervise running dispatches at zero LLM cost during the wait period.',
115
+ input_schema: {
116
+ type: 'object',
117
+ properties: {
118
+ sessionIds: { type: 'array', items: { type: 'string' }, description: 'Session IDs to watch (subordinates or peers)' },
119
+ durationSec: { type: 'number', description: 'Watch duration in seconds (default 120, max 300)', default: 120 },
120
+ alertOn: {
121
+ type: 'array',
122
+ items: { type: 'string' },
123
+ description: 'Event types that trigger early return (default: msg:done, msg:error, msg:awaiting_input)',
124
+ default: ['msg:done', 'msg:error', 'msg:awaiting_input'],
125
+ },
126
+ },
127
+ required: ['sessionIds'],
128
+ },
129
+ };
130
+
131
+ export const AMEND_SESSION_TOOL: ToolDefinition = {
132
+ name: 'amend_session',
133
+ description: 'Send additional instructions to a running subordinate session. The instructions will be injected at the next turn boundary. Use when a subordinate is going in the wrong direction and needs course correction.',
134
+ input_schema: {
135
+ type: 'object',
136
+ properties: {
137
+ sessionId: { type: 'string', description: 'Target session ID to amend' },
138
+ instruction: { type: 'string', description: 'Additional instruction to inject into the session' },
139
+ },
140
+ required: ['sessionId', 'instruction'],
141
+ },
142
+ };
143
+
144
+ export const ABORT_SESSION_TOOL: ToolDefinition = {
145
+ name: 'abort_session',
146
+ description: 'Abort a running subordinate session immediately. Use when the subordinate is clearly doing the wrong thing and needs to be stopped. You can re-dispatch with different instructions afterwards.',
147
+ input_schema: {
148
+ type: 'object',
149
+ properties: {
150
+ sessionId: { type: 'string', description: 'Target session ID to abort' },
151
+ reason: { type: 'string', description: 'Reason for aborting (logged in activity stream)' },
152
+ },
153
+ required: ['sessionId'],
154
+ },
155
+ };
156
+
157
+ /**
158
+ * 상담 도구 — 모든 Role에게 제공 (동료/상관/부하에게 질문)
159
+ */
160
+ export const CONSULT_TOOL: ToolDefinition = {
161
+ name: 'consult',
162
+ description: 'Ask a question to another role (peer, manager, or subordinate) and wait for their answer. The consulted role will respond in read-only mode. Use when you need information, expertise, or a decision from a colleague.',
163
+ input_schema: {
164
+ type: 'object',
165
+ properties: {
166
+ roleId: { type: 'string', description: 'Target role ID to consult (e.g., "designer", "cto")' },
167
+ question: { type: 'string', description: 'The question to ask' },
168
+ },
169
+ required: ['roleId', 'question'],
170
+ },
171
+ };
172
+
173
+ /**
174
+ * Role에 따른 도구 목록 반환
175
+ * @param heartbeatEnabled - C-Level supervision mode enabled (provides heartbeat_watch, amend_session, abort_session)
176
+ */
177
+ export function getToolsForRole(hasSubordinates: boolean, readOnly: boolean, hasBash = false, heartbeatEnabled = false, hasPeers = false): ToolDefinition[] {
178
+ if (readOnly) {
179
+ return [...READ_TOOLS];
180
+ }
181
+
182
+ const tools = [...READ_TOOLS, ...WRITE_TOOLS];
183
+ if (hasPeers) {
184
+ tools.push(CONSULT_TOOL);
185
+ }
186
+
187
+ if (hasBash) {
188
+ tools.push(BASH_TOOL);
189
+ }
190
+
191
+ if (hasSubordinates) {
192
+ tools.push(DISPATCH_TOOL);
193
+
194
+ // Supervision tools — only for roles with subordinates AND heartbeat enabled
195
+ if (heartbeatEnabled) {
196
+ tools.push(HEARTBEAT_WATCH_TOOL, AMEND_SESSION_TOOL, ABORT_SESSION_TOOL);
197
+ }
198
+ }
199
+
200
+ return tools;
201
+ }