Project: continuum Phase: INCEPTION — Application Design Date: 2026-05-08 Depth: Comprehensive
Note: 本ドキュメントは各コンポーネントの メソッドシグネチャ + 入出力型 + 高レベル目的 を定義する。詳細業務ルール(具体的なバリデーションロジック、エラー条件、ビジネス例外処理)は Functional Design(Construction Phase per-unit) で精緻化する。
言語: TypeScript(Frontend / Backend 共通、Monorepo + 共有型)
class WebApp {
// ルーティング初期化
initializeRouter(): void;
// Service Worker 登録
registerServiceWorker(): Promise<ServiceWorkerRegistration>;
// グローバル状態の初期化
initializeGlobalState(): void;
// 認証ステートに応じたリダイレクト
redirectBasedOnAuth(): void;
}
class AuthUI {
signUp(input: { email: string; password: string; displayName: string }): Promise<{ userId: string }>;
login(input: { email: string; password: string }): Promise<{ jwtToken: string; userId: string }>;
logout(): Promise<void>;
refreshToken(): Promise<{ jwtToken: string }>;
getCurrentUser(): { userId: string; displayName: string } | null;
}
type InboxMessage = {
messageId: string;
contactId: string;
contactName: string;
receivedAt: string; // ISO 8601
subject: string;
body: string;
status: 'pending' | 'responded';
isCritical: boolean;
};
type ResponseVariation = {
variationId: string;
style: 'conservative' | 'pragmatic' | 'cordial';
text: string;
};
class InboxView {
fetchInbox(): Promise<InboxMessage[]>;
generateResponses(messageId: string): Promise<ResponseVariation[]>;
selectVariation(variationId: string): void;
editVariation(text: string): void; // Lv1 限定
sendResponse(messageId: string, variationId: string, editedText?: string): Promise<{ success: boolean }>;
applyMaturityPhaseUI(currentPhase: 'Lv1' | 'Lv2' | 'Lv3' | 'Lv4' | 'Lv5'): void;
}
type OutboundCandidate = {
outboundId: string;
contactId: string;
contactName: string;
type: 'strategic_outreach' | 'heartbeat';
proposedText: string;
scheduledAt: string;
reasoning: string; // 例: "Optimal Window 検出: 5/20-22 京都出張"
};
class OutboundReviewView {
fetchPending(): Promise<OutboundCandidate[]>;
approve(outboundId: string, editedText?: string): Promise<void>;
reject(outboundId: string, reason?: string): Promise<void>;
fetchHistory(filters: { dateRange?: [string, string]; type?: string }): Promise<OutboundCandidate[]>;
}
type DashboardKPI = {
responsesDelegatedThisMonth: number;
timeReclaimedHours: number;
delegationStreak: number;
delegationScore: number; // 0〜100
currentPhase: 'Lv1' | 'Lv2' | 'Lv3' | 'Lv4' | 'Lv5';
optimalWindowHitRate: number; // 0〜1
relationshipHealthScores: Array<{ contactId: string; score: number }>;
engagementCoverage: number; // 0〜1
daysInNirvana?: number; // Lv5 のみ
};
type Badge = {
badgeId: string;
name: string; // 例: "Adaptive Delegation Specialist"
acquiredAt: string;
};
class DashboardView {
fetchKPI(): Promise<DashboardKPI>;
fetchBadges(): Promise<Badge[]>;
fetchRecommendations(): Promise<{ recommendation: string; targetPhase?: string } | null>;
}
class MaturitySettingsView {
fetchCurrentPhase(): Promise<{ phase: string; lifetimeCounters: Record<string, number> }>;
changePhase(targetPhase: 'Lv1' | 'Lv2' | 'Lv3'): Promise<void>; // Lv4 は別メソッド
activateFullAutonomousMode(): Promise<void>; // Lv4 自己有効化
deactivateFullAutonomousMode(): Promise<void>; // Lv4 → Lv3 へ降格
showConfirmationModal(message: string): Promise<boolean>;
}
type MACPNegotiationEvent = {
timestamp: string;
fromAgent: 'A' | 'B';
toAgent: 'A' | 'B';
type: 'proposal' | 'counter_proposal' | 'agreement' | 'commit';
payload: Record<string, unknown>;
};
class DemoSetPieceView {
startDemo(scenario: 'macp_demo'): Promise<void>;
renderSplitScreen(userAState: unknown, userBState: unknown, negotiationLog: MACPNegotiationEvent[]): void;
playTimeLapse(speedMultiplier: number): void;
renderClosingNarration(message: string): void;
}
type WebSocketEvent =
| { type: 'macp.negotiation.update'; payload: MACPNegotiationEvent }
| { type: 'inbox.new_message'; payload: InboxMessage }
| { type: 'outbound.sent'; payload: OutboundCandidate };
class WebSocketClient {
connect(jwtToken: string): Promise<void>;
disconnect(): void;
subscribe(eventType: string, handler: (event: WebSocketEvent) => void): () => void;
send(event: WebSocketEvent): void;
}
type UserProfile = {
userId: string;
email: string;
displayName: string;
// defaultCommunicationStyle: 削除(2026-05-08 scope revision — US-1.3 削除に伴う)
initialMaturityPhase: 'Lv1';
createdAt: string;
};
class AuthHandler {
// POST /auth/signup (Cognito の Post-Confirmation Trigger でも呼ばれる)
handleSignUp(input: { email: string; displayName: string }): Promise<{ userId: string }>;
// GET /me
getProfile(userId: string): Promise<UserProfile>;
// PUT /me
updateProfile(userId: string, updates: Partial<Pick<UserProfile, 'displayName'>>): Promise<UserProfile>;
// JWT 検証ミドルウェア
verifyJwt(jwtToken: string): Promise<{ userId: string }>;
}
type Contact = {
contactId: string;
userId: string;
name: string;
category: 'Friend' | 'Family' | 'Colleague' | 'Supervisor';
relationshipTier: 'Close' | 'Standard' | 'Distant';
tonePreference: 'formal' | 'casual';
engagementCadence: 'weekly' | 'biweekly' | 'monthly' | 'quarterly' | 'none';
isCritical: boolean;
createdAt: string;
};
class ContactHandler {
// GET /contacts
listContacts(userId: string): Promise<Contact[]>;
// POST /contacts
createContact(userId: string, input: Omit<Contact, 'contactId' | 'userId' | 'createdAt'>): Promise<Contact>;
// PUT /contacts/{id}
updateContact(userId: string, contactId: string, updates: Partial<Contact>): Promise<Contact>;
// DELETE /contacts/{id}
deleteContact(userId: string, contactId: string): Promise<void>;
// 内部利用
getContactById(contactId: string): Promise<Contact>;
// Cadence デフォルトの推定
inferDefaultCadence(category: string, tier: string): Contact['engagementCadence'];
}
type GenerateResponseInput = {
messageId: string;
message: InboxMessage;
contact: Contact;
userProfile: UserProfile;
};
class InboundResponseEngine {
// POST /inbound/{messageId}/generate
generateThreeVariations(input: GenerateResponseInput): Promise<ResponseVariation[]>;
// POST /inbound/{messageId}/send
sendResponse(input: { messageId: string; variationId: string; editedText?: string }): Promise<{ sentAt: string }>;
// 内部メソッド
applyRelationalCalibration(prompt: string, contact: Contact): string;
// validatePlausibilityConstraint(): 廃止(2026-05-08 scope revision — プロンプト並列生成のみ、類似度 0.7 検証は Phase 2 Must から削除)
fetchPastInteractions(contactId: string, limit: number): Promise<unknown[]>; // AgentCore Memory から
}
type GenerateOutboundInput = {
userId: string;
contactId: string;
type: 'strategic_outreach' | 'heartbeat';
optimalWindow?: { start: string; end: string; confidence: number }; // strategic_outreach のみ
};
class OutboundOrchestrator {
// 内部: SignalExtractor から Window を取得して Strategic Outreach を生成
scheduleStrategicOutreach(userId: string): Promise<OutboundCandidate[]>;
// 内部: Cadence 別の Heartbeat 候補を生成
scheduleHeartbeat(userId: string, contactId: string): Promise<OutboundCandidate>;
// GET /outbound/pending
listPending(userId: string): Promise<OutboundCandidate[]>;
// POST /outbound/{id}/approve
approveAndSend(outboundId: string, editedText?: string): Promise<{ sentAt: string }>;
// POST /outbound/{id}/reject
reject(outboundId: string): Promise<void>;
// 内部: Conflict Detection
detectConflicts(candidate: OutboundCandidate, recentHistory: unknown[]): { hasConflict: boolean; reasons: string[] };
// 内部: Plausibility Engineering
applyPlausibilityEngineering(text: string, userProfile: UserProfile, pastSentMessages: unknown[]): string;
// 内部: 送信時刻のランダム化
scheduleNaturalDeliveryTime(window: { start: string; end: string }): string; // ISO 8601
}
type SocialFeedItem = {
feedItemId: string;
contactId: string;
platform: 'mock_x' | 'mock_instagram' | 'mock_linkedin' | 'mock_timetree';
postedAt: string;
content: string;
};
type AvailabilityWindow = {
windowId: string;
contactId: string;
start: string;
end: string;
category: 'explicit' | 'implicit' | 'inferred' | 'predictive' | 'cross_source';
eventType?: string; // 'business_trip' / 'wedding' / 'work_deadline' 等
confidence: number; // 0〜1
contributingFeedItemIds: string[];
};
class SignalExtractor {
// パイプライン全体
extractWindowsForContact(contactId: string): Promise<AvailabilityWindow[]>;
// Step 1: Ingestion
ingestFeed(contactId: string): Promise<SocialFeedItem[]>;
// Step 2: Normalization
normalize(items: SocialFeedItem[]): SocialFeedItem[];
// Step 3: NER + 意図理解(Bedrock)
extractSignalsWithLLM(item: SocialFeedItem): Promise<Partial<AvailabilityWindow>[]>;
// Step 4: Confidence Scoring
calculateConfidence(signals: Partial<AvailabilityWindow>[]): AvailabilityWindow[];
// Step 5: Window Aggregation
aggregateWindows(windows: AvailabilityWindow[]): AvailabilityWindow[];
// Step 6: 永続化(DynamoDB へ)
persistWindows(windows: AvailabilityWindow[]): Promise<void>;
// クエリ: Confidence 閾値以上の Window
getOptimalWindows(contactId: string, threshold?: number): Promise<AvailabilityWindow[]>;
}
type MaturityState = {
userId: string;
currentPhase: 'Lv1' | 'Lv2' | 'Lv3' | 'Lv4' | 'Lv5';
totalResponses: number;
consecutiveDays: number;
lastAppOpenAt: string;
daysInNirvana: number; // Lv5 のみ
badges: Badge[];
};
class MaturityPhaseManager {
// GET /maturity/status
getStatus(userId: string): Promise<MaturityState>;
// PUT /maturity/phase (Lv1〜Lv3 切替)
changePhase(userId: string, targetPhase: 'Lv1' | 'Lv2' | 'Lv3'): Promise<MaturityState>;
// POST /maturity/activate-autonomous (Lv4 自己有効化)
activateLv4(userId: string): Promise<MaturityState>;
/**
* 内部: フェーズ別の挙動判定
*
* Phase 2: Lv1(true 常返却 = 全レビュー)/ Lv4(false 常返却 = 全自動)のみが本実装。
* Lv2 / Lv3 は **stub 実装**(Lv1 と同等の挙動 = 常に true 返却、確率分岐や Critical タグ判定は未実装)。
* Phase 3 Stretch: Lv2 確率的 30% スキップ(US-5.2)/ Lv3 Critical タグ別動作(US-5.3)を本実装。
* Phase 3 Must: Lv5 自動認定後の動作(false 返却、通知送信なし)を本実装。
*/
shouldReviewInbound(userId: string, message: InboxMessage, contact: Contact): Promise<boolean>;
shouldReviewOutbound(userId: string, candidate: OutboundCandidate): Promise<boolean>;
// 内部: Recommendation 表示判定
getRecommendation(userId: string): Promise<{ message: string; targetPhase: string } | null>;
// 内部: Lv5 自動認定(日次バッチ)
detectNirvanaCandidates(): Promise<string[]>; // userId[]
promoteToLv5(userId: string): Promise<MaturityState>;
/**
* 内部: Lv5 → Lv4 自動降格(アプリ起動時)
*
* Lv5(涅槃期)状態で本メソッドが呼ばれた場合、自動的に Lv4(解脱期)へ降格し
* `maturity.phase.changed` イベントを EventBridge へ発行する。
* 降格時に `lifetimeDaysInNirvana` を更新し、未取得なら "Transcendence Achieved" バッジを付与する。
*
* 注: Lv5 自動認定ロジック自体(detectNirvanaCandidates / promoteToLv5)は Phase 3 ストレッチ。
* Phase 2 MVP では本降格処理は stub 実装(lastAppOpenAt の更新のみ)とする。
*/
handleAppOpen(userId: string): Promise<MaturityState>;
// 内部: 応答件数インクリメント
incrementResponseCount(userId: string): Promise<MaturityState>;
}
type MACPSession = {
sessionId: string;
initiatorUserId: string;
counterpartUserId: string;
status: 'discovery' | 'negotiating' | 'theatrical_active' | 'completed' | 'aborted';
agreement?: MACPAgreement;
theaterMessages: Array<{ scheduledAt: string; from: string; to: string; text: string; sent: boolean }>;
startedAt: string;
completedAt?: string;
};
type MACPAgreement = {
mutualDeclineIntent: boolean;
theaterTopic: string; // "飲み会" / "ランチ" / "相談" 等
theaterDuration: number; // 何往復
theaterCadence: number; // 平均間隔(時間)
toneAlignment: 'formal' | 'casual';
resolutionPattern: 'next_time' | 'circle_back' | 'undefined_future';
privacyBoundaries: { shareReasons: false }; // 抽象的状態のみ共有
};
class MACPCoordinator {
// メインエントリポイント: Outbound 候補に対し MACP 試行
attemptMACP(candidate: OutboundCandidate): Promise<MACPSession | null>;
// FR-9.2 Peer Discovery
discoverPeer(contactEmail: string): Promise<{ isContinuumUser: boolean; tenantId?: string }>;
// FR-9.3 Inter-Agent Negotiation (cross-tenant via AgentCore Gateway)
negotiateAgreement(initiatorState: unknown, counterpartTenantId: string): Promise<MACPAgreement | null>;
// FR-9.4 Theatrical Performance
scheduleTheatricalMessages(session: MACPSession): Promise<void>;
// EventBridge 受信ハンドラ: 各演出メッセージの送信時刻
executeTheaterStep(input: { sessionId: string; stepIndex: number }): Promise<void>;
// FR-9.5 Outcome Confirmation
confirmOutcome(sessionId: string): Promise<{ badgeAwardedTo: string[] }>;
// FR-9.7 Failure Handling
abortSession(sessionId: string, reason: 'gateway_timeout' | 'auth_failure' | 'counterpart_unavailable' | 'mutual_intent_mismatch'): Promise<void>;
}
class DashboardEngine {
// GET /dashboard/kpi
getKPI(userId: string): Promise<DashboardKPI>;
// GET /dashboard/badges
getBadges(userId: string): Promise<Badge[]>;
// GET /dashboard/recommendations
getRecommendations(userId: string): Promise<{ message: string; targetPhase?: string } | null>;
// 内部: KPI 集計
calculateResponsesDelegated(userId: string, dateRange: [string, string]): Promise<number>;
calculateTimeReclaimed(userId: string, dateRange: [string, string]): Promise<number>; // hours
calculateDelegationScore(userId: string): Promise<number>; // 0〜100
calculateOptimalWindowHitRate(userId: string): Promise<number>;
calculateRelationshipHealthScore(userId: string, contactId: string): Promise<number>;
// バッジ付与
awardBadge(userId: string, badge: Badge): Promise<void>;
}
type ConnectionRecord = {
connectionId: string;
userId: string;
connectedAt: string;
};
class WebSocketBroker {
// $connect ハンドラ
handleConnect(input: { connectionId: string; jwtToken: string }): Promise<void>;
// $disconnect ハンドラ
handleDisconnect(connectionId: string): Promise<void>;
// $default ハンドラ(クライアントからのメッセージ)
handleMessage(connectionId: string, body: unknown): Promise<void>;
// ユーザー単位のプッシュ
pushToUser(userId: string, event: WebSocketEvent): Promise<void>;
// 全接続クライアントへブロードキャスト(デモ用)
broadcastDemo(event: WebSocketEvent): Promise<void>;
}
class MockSocialFeedSeeder {
/**
* メイン投入関数(Phase 2: 3 コンタクト × 各 2 投稿 = 合計 6 投稿)
* Phase 2 では kobayashi / tanaka / misaki の 3 名のみ投入(Phase 3 で mom / sasaki 等を追加)
*/
seedAll(): Promise<{ contactsSeeded: number; feedItemsSeeded: number }>;
/**
* 個別ペルソナのフィード投入
*
* 各テンプレートで投入される 2 投稿(Mock X + Mock Instagram Story)の内容:
*
* - **'kobayashi'** (M1 同期友人): 飲み会セット + 旅行系
* - Mock X: "来週は札幌出張。月曜から金曜まで不在"
* - Mock Instagram Story: 札幌の風景写真(位置タグ「Sapporo, Japan」)
*
* - **'mom'** (M2 母): Phase 3 で投入 — Phase 2 ではコンタクトプロファイルのみ作成、SNS 投稿なし
* - (Phase 3) Mock X: "今度の母の日、なにか送ってくれる?w"
* - (Phase 3) Mock Instagram Story: 父の手術付き添い系
*
* - **'tanaka'** (M3 上司): 経営会議系
* - Mock X: "来週は京都で経営会議。月曜から金曜まで不在"
* - Mock Instagram Story: 京都・八坂の塔(位置タグ「Kyoto, Japan」)
*
* - **'misaki'** (M4 同僚): 研修・産休系
* - Mock Instagram Story: "今週は研修で千葉合宿"(位置タグ「Chiba」)
* - Mock X: "出産前最後のプロジェクト終了、あとは産休へ。しばらく音信不通になります 🤰"
*
* - **'sasaki'** (F1 MACP 相手): Phase 3 で投入 — Phase 2 ではコンタクトプロファイルのみ作成
* - (Phase 3) Mock X / Mock Instagram Story: コンサル系出張投稿
*
* Phase 3 で Mock LinkedIn / Mock TimeTree のテンプレートを各テンプレートに追加する。
*/
seedContactFeed(contactId: string, personaTemplate: 'kobayashi' | 'mom' | 'tanaka' | 'misaki' | 'sasaki'): Promise<number>;
/**
* Signal Categories 全種別を網羅するか検証
* Phase 2 では explicit のみ true 想定(残りは Phase 3 で実装)
*/
validateCoverage(seedResults: unknown): { explicit: boolean; implicit: boolean; inferred: boolean; predictive: boolean; crossSource: boolean };
}
handle{Verb}{Resource} または HTTP メソッド名そのままextractSignals, applyRelationalCalibration)should{X} または is{X} または has{X}calculate{X}handle{EventName} または execute{Step}Promise<T> を返し、例外は domain-specific Error クラス(InboundGenerationFailedError, MACPNegotiationTimeoutError 等)で統一userId を context から取得{ method, userId, timestamp, durationMs })以下は 本ドキュメントでは定義しない(Functional Design で精緻化):