@morit/cli 1.2.0 → 1.4.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.
@@ -1,8 +1,9 @@
1
1
  # Response UI와 Agent Timeline
2
2
 
3
- Response UI는 capability 결과를 AI 메시지 안에서 시각화합니다. 대화 아래의 별도 카드 목록이나
4
- overflow 메뉴 밑에 떨어뜨리지 않고, 해당 AI 메시지에 속한 하나의 response container 안에
5
- 제목·내용·행동을 함께 렌더링합니다.
3
+ Response UI는 capability 결과를 AI 메시지 안에서 시각화합니다. AI는 필요한 A2UI
4
+ Native Tool을 선택하지만 Host는 우선 최종 텍스트 답변을 완료·저장합니다. 컴포넌트는
5
+ 데이터 준비와 검증이 모두 성공한 뒤 해당 AI 메시지 본문 아래에 한 번에 자연스럽게
6
+ 표시됩니다. Tool이 실패해도 답변 완료 상태는 바뀌지 않습니다.
6
7
 
7
8
  ## Response extension
8
9
 
@@ -15,6 +16,23 @@ overflow 메뉴 밑에 떨어뜨리지 않고, 해당 AI 메시지에 속한 하
15
16
  "permissions": [],
16
17
  "config": {
17
18
  "ui_schema": 2,
19
+ "a2ui": {
20
+ "version": "v0.9",
21
+ "description": "수업 정보가 실제 답변에 필요하고 표시할 항목이 있을 때만 사용합니다.",
22
+ "schema": {
23
+ "type": "object",
24
+ "properties": {
25
+ "timetable": {
26
+ "type": "array",
27
+ "items": {"type": "object"},
28
+ "minItems": 1,
29
+ "maxItems": 12
30
+ }
31
+ },
32
+ "required": ["timetable"],
33
+ "additionalProperties": true
34
+ }
35
+ },
18
36
  "theme": {"density": "compact"},
19
37
  "data_sources": [
20
38
  {
@@ -50,21 +68,63 @@ overflow 메뉴 밑에 떨어뜨리지 않고, 해당 AI 메시지에 속한 하
50
68
  ```
51
69
 
52
70
  Response point도 일반 Runtime v2와 같은 node, theme, app bar, navigation, binding, action 계약을
53
- 사용합니다. AI실행한 capability 결과는 `data` namespace에 주입되므로 같은 결과를 얻기 위해
54
- 화면 mount 시 외부 요청을 반복하지 않습니다. 후속 refresh나 사용자가 누른 action에만 추가
55
- capability를 호출합니다.
56
-
57
- ## 실시간 구조화 응답
58
-
59
- Morit AI 오케스트레이터는 Tool·Skill 실행 전후 같은 `(message, plugin, instance, capability)` 응답을
60
- upsert합니다. `data.__morit_response.state`는 `loading`, `partial`, `completed`, `failed` 하나이며
61
- `retryable`이 true인 실패는 해당 메시지의 재생성으로 복구할 있습니다. 텍스트 delta는 별도로
62
- 계속 스트리밍되므로 UI가 실패해도 텍스트 답변은 유지됩니다.
63
-
64
- Plugin이 `point: "response"` UI를 선언하면 그 Runtime v2를 사용합니다. 선언하지 않은 Tool·Skill
65
- 결과도 Host 같은 검증기를 통해 `metric`, `progress`, `list`, `timeline`, `calendar`, `chart`,
66
- `table`, `empty` 조합으로 안전하게 표시합니다. 차트는 `bar`, `line`, `donut`, `scatter`를 지원합니다.
67
- 없는 shape은 실행하거나 추측하지 않고 summary와 텍스트 fallback을 남깁니다.
71
+ 사용합니다. Host선택·검증한 capability 결과는 `data` namespace에 주입되므로 같은 결과를 얻기
72
+ 위해 화면 mount 시 외부 요청을 반복하지 않습니다. 후속 refresh나 사용자가 누른 action에만 추가
73
+ capability를 호출합니다. 기존 `a2ui` 선언이 없는 Response extension은 지원하지 않습니다.
74
+
75
+ ## AI 선택과 Host 검증
76
+
77
+ 서버는 모든 Tool 결과에 UI를 일괄 생성하거나 키워드 정규식으로 컴포넌트를 강제하지 않습니다.
78
+ 최종 답변 모델은 통합 Registry Skill의 용도와 입력 Schema를 의미적으로 비교해 Native Tool을
79
+ 선택합니다. provider 컴포넌트에는 위치·통화·검색어만 넘기고, Host가 비동기로 실시간 데이터를
80
+ 조회합니다. generated 컴포넌트에는 답변에 근거한 차트 값만 Schema에 맞게
81
+ 전달합니다. Plugin 결과는 Host가 검증한 후보 데이터만 사용합니다.
82
+
83
+ 선택 Host 다음을 모두 통과한 항목만 A2UI v0.9 DataPart로 저장하고 해당 AI 메시지에 붙입니다.
84
+
85
+ - 실행 상태가 `completed` 또는 `partial`이고 실제 값이 있음
86
+ - 질문·최종 답변과 capability 결과가 관련됨
87
+ - 같은 데이터의 Response UI가 이미 선택되지 않음
88
+ - 기본 컴포넌트별 필수 값과 URL 형식이 유효함
89
+ - Plugin 컴포넌트의 schema, A2UI version, 설치 상태, Instance 권한과 capability 연결이 유효함
90
+
91
+ 빈 데이터, 실패한 Tool, 무관하거나 중복된 결과, 불확실한 선택은 UI를 만들지 않습니다. 선택 모델
92
+ 호출이나 렌더링이 실패해도 일반 텍스트 답변은 그대로 완료됩니다. 로딩 문구,
93
+ skeleton, 빈 card는 렌더링하지 않고 준비가 끝난 컴포넌트만 한 번 표시합니다.
94
+
95
+ ## 기본 A2UI Component Catalog
96
+
97
+ | component | 표시 조건 |
98
+ |---|---|
99
+ | `weather` | 현재 기온·상태 또는 예보가 하나 이상 유효함 |
100
+ | `world_clock` | 1~8개 위치의 IANA timezone과 현재 시각이 유효함 |
101
+ | `chart` | label과 수치가 있는 행이 2개 이상임; `bar`, `line`, `pie`, `scatter` |
102
+ | `image_gallery` | HTTPS 이미지와 원문 URL·출처명이 함께 있음 |
103
+ | `exchange_rate` | 기준 통화, 상대 통화, 유한한 환율 값이 있음 |
104
+ | `article_list` | 제목·출처·원문이 있는 서로 다른 기사 2개 이상 |
105
+ | `article_card` | 제목·출처·요약·본문 일부·원문이 있고, 게시 시각·이미지는 제공될 때 표시 |
106
+ | `file_result` | 실제 Morit item ID 또는 검증 가능한 다운로드 URL이 있음 |
107
+ | `image_preview` | 실제 image item 또는 검증 가능한 preview URL이 있음 |
108
+ | `video_preview` | 실제 video item/URL과 thumbnail URL이 함께 있음 |
109
+
110
+ `weather`, `exchange_rate`, `world_clock`, `article_list`, `article_card`, `image_gallery`는
111
+ provider mode이고 `chart`는 generated mode입니다. 기사와 이미지는 페이지 이동·다시 시도,
112
+ gallery는 반응형 grid/carousel을 지원합니다. 표는 일반 Markdown으로 답변하고 단계별
113
+ 텍스트 slider는 catalog에서 제거되었습니다.
114
+ 각 renderer도 같은 필수 값을 다시 확인하고, 값이 바뀌거나 손상되면 해당 컴포넌트만 fallback합니다.
115
+
116
+ ## Plugin 컴포넌트의 동적 등록
117
+
118
+ 활성 Instance의 `point: "response"` extension은 capability 실행 시
119
+ `plugin.<extension-id>` 이름으로 현재 A2UI catalog에 동적 등록됩니다. AI에게는 Host가 확인한 ID,
120
+ 설명, version, schema만 노출됩니다. `a2ui.schema`는 root `object`인 안전한 JSON Schema 부분집합이며
121
+ `type`, `properties`, `required`, `additionalProperties`, `items`, `enum`, 문자열·숫자·배열 bound를
122
+ 지원합니다. schema는 최대 16 KiB, 깊이 6이며 object/list는 각 64개로 제한됩니다.
123
+
124
+ Plugin Response는 Runtime v2의 전체 layout, surface, theme, image, action과 binding을 사용할 수
125
+ 있습니다. 단, extension 권한은 Manifest grant의 부분집합이어야 하고 `data_sources` 중 하나가 실제
126
+ 실행 capability를 가리켜야 합니다. 패키지 설치, CLI validate, Host 로드, Flutter parse와 표시 직전
127
+ 검증이 같은 규칙을 사용합니다.
68
128
 
69
129
  ## 한 container의 정보 순서
70
130
 
@@ -82,14 +142,15 @@ Plugin이 `point: "response"` UI를 선언하면 그 Runtime v2를 사용합니
82
142
  - Response 내부에 자체 채팅 입력창을 만들지 않습니다.
83
143
  - 무한 높이 목록 대신 요약과 상세 화면 이동을 제공합니다.
84
144
  - background refresh가 대화 scroll 위치를 바꾸지 않게 기존 높이와 데이터를 가능한 유지합니다.
85
- - 렌더링 오류는 해당 Response 텍스트 fallback으로 바꾸고 대화 전체를 종료하지 않습니다.
145
+ - 렌더링 오류는 해당 Response UI를 생략하고 이미 완료된 텍스트 답변을 유지합니다.
86
146
  - accessibility 순서는 AI 본문 다음, Response 제목, 내용, 행동 순으로 유지합니다.
87
147
 
88
148
  ## Text fallback
89
149
 
90
150
  Response UI를 표시할 수 없더라도 AI의 원래 텍스트 답변은 남아야 합니다. capability는 UI 전용
91
- 원시 JSON만 반환하지 말고 사람이 읽을 수 있는 `summary`를 함께 제공합니다. 없는 node,
92
- 손상된 binding, 이미지 로드 실패는 전체 답변 성공을 숨기지 않습니다.
151
+ 원시 JSON만 반환하지 말고 사람이 읽을 수 있는 `summary`를 함께 제공합니다. 배열만 있거나
152
+ schema가 맞지 않으면 UI를 억지로 채우지 않습니다. 없는 node, 손상된 binding, 이미지 로드
153
+ 실패는 전체 답변 성공을 숨기지 않습니다.
93
154
 
94
155
  ```json
95
156
  {
@@ -119,16 +119,21 @@ action에는 반드시 `label`, 일반 `icon` node에는 `semantic_label`을 함
119
119
  |---|---|---|
120
120
  | `button` | 명시적인 주·보조 행동 | `label`, `icon`, `style`, `full_width`, `enabled` |
121
121
  | `chip` | 필터·짧은 선택 | `label`, `icon`, `selected`, `enabled` |
122
- | `field` | 텍스트·숫자 입력 | `state_key`, `label`, `placeholder`, `input_type`, `persist` |
123
- | `select` | 고정 선택지 | `state_key`, `label`, `options`, `persist` |
122
+ | `field` | 텍스트·숫자·autocomplete 입력 | `state_key`, `label`, `placeholder`, `input_type`, `suggestions`/`suggestions_source`, `persist` |
123
+ | `select` | 고정·비동기·검색 선택지 | `state_key`, `label`, `options`/`options_source`, `searchable`, `persist` |
124
124
  | `switch` | boolean 설정 | `state_key`, `label`, `persist` |
125
- | `form` | 관련 입력 묶음 | `spacing`, children, submit action 가진 button |
125
+ | `form` | 관련 입력 묶음 | `spacing`, `submit_label`, children, 선택적 submit action |
126
126
  | `dialog` | 짧고 집중된 확인·편집 | `title`, `label`, children |
127
127
  | `sheet` | 모바일 중심의 보조 작업 | `title`, `label`, children |
128
128
 
129
129
  `field`, `select`, `switch`의 `state_key`는 `initial_state`에 먼저 선언해야 합니다. select options는
130
- 1~32개의 `{ "value": ..., "label": "..." }` 객체입니다. `enabled: false`는 이유를 주변 문구로
131
- 설명할 때만 사용합니다.
130
+ 1~32개의 `{ "value": ..., "label": "..." }` 객체입니다. 정적과 source 옵션을 다 선언할 수
131
+ 없습니다. `required`, 문자열 길이, 숫자 범위와 `error_text`는 Host form validation에
132
+ 사용됩니다. `enabled: false`는 이유를 주변 문구로 설명할 때만 사용합니다.
133
+
134
+ action은 `surface`, `card`, `button`, `chip`, `form`, `field`, `select`, `switch`에만 붙일 수
135
+ 있습니다. 입력 action 직전에 Host가 IME composition과 controller를 commit하며,
136
+ `validate: true`는 잘못된 form의 Tool·navigation 실행을 차단합니다.
132
137
 
133
138
  ## 반복과 데이터 시각화
134
139
 
@@ -189,6 +194,20 @@ route state와 persisted control을 복원합니다. 데이터가 정상적으
189
194
  모든 프리셋은 light/dark, 320px 폭, tablet/desktop 폭, 큰 글자에서 확인합니다. node별 tooltip,
190
195
  semantic label, 최소 탭 영역과 색 이외의 상태 표시는 Host Material 3 규칙을 따릅니다.
191
196
 
197
+ ## A2UI Response catalog와의 관계
198
+
199
+ 위 node는 Plugin Runtime을 구성하는 primitive입니다. AI가 직접 선택하는 Response catalog는
200
+ `weather`, `world_clock`, `chart`, `image_gallery`,
201
+ `exchange_rate`, `article_list`, `article_card`, `file_result`, `image_preview`, `video_preview`와 활성
202
+ Plugin이 등록한 `plugin.<extension-id>`로 구성됩니다. 최종 답변 모델은 같은 Registry Skill과 Native
203
+ Tool에서 출처(`builtin`/`plugin`), mode(`provider`/`generated`/`result`), 용도와 Schema를 확인합니다.
204
+ provider mode는 조회 조건만, generated mode는 답변에 근거한 표시 값만 전달하며 Host가 검증한 뒤
205
+ Runtime tree에 주입합니다. 특정 키워드만으로 Tool을 강제하지 않습니다.
206
+ 구조화 표는 A2UI 전용 컴포넌트 대신 Markdown table을 사용합니다. 위 표의
207
+ Runtime `table` node는 플러그인 화면 호환성을 위한 primitive로 계속 지원합니다.
208
+ 자세한 등록·fallback 규칙은 [Response UI와 Agent Timeline](ai-response-and-timeline.md)을
209
+ 참고하세요.
210
+
192
211
  ## 공통 크기·정렬·접근성 props
193
212
 
194
213
  - 크기: `width`, `height`, `min_width`, `max_width`, `min_height`, `max_height`
@@ -32,8 +32,8 @@
32
32
 
33
33
  ```text
34
34
  examples/plugins/school-life/dist/school-life-1.5.0.mplg
35
- 크기 263184 bytes
36
- SHA-256 3b6397ca1ef26833fe842087b0ecf5f05621bd45f9d80b8a422b6547c060f603
35
+ 크기 263438 bytes
36
+ SHA-256 dfd39c29154c50bd14b4eeed390efffa7fa5ab3c6ad9361730eea67bdb836276
37
37
  ```
38
38
 
39
39
  1. Morit 플러그인 관리에서 위 `.mplg`를 선택합니다.
@@ -70,8 +70,8 @@ AI 예:
70
70
  `backend/archive_processing/test_plugin_examples.py`는 서명 package 설치, 권한 전 상태,
71
71
  학교 연결, 단일 날짜와 7일 조회, 알레르기/영양 변환, 검색, rich 알림, 자동 브리핑 중복
72
72
  방지, Storage CRUD/AI 권한, 재시작 복원, actor·Instance 격리, migration, disable/delete purge를
73
- 고정된 공식 response contract로
74
- 검증합니다.
73
+ 검증합니다. day/week Response extension은 A2UI v0.9 schema와 실제 capability data source를
74
+ 검사하고 `plugin.school_life.day.response`, `plugin.school_life.week.response`로 동적 등록됩니다.
75
75
 
76
76
  배포 전에는 [예제 검증 방법](verification.md)에 따라 실제 NEIS 네트워크로 공개 시간표·학사일정·
77
77
  급식 기간 조회도 실행합니다. 이 live 확인은 API와 parser 경계를 검증하지만 release APK의
@@ -47,7 +47,7 @@ revision만 있으며 인증 정보는 들어가지 않습니다.
47
47
  "description": "오늘 할 일을 정리합니다.",
48
48
  "publisher": "example",
49
49
  "version": "1.0.0",
50
- "min_morit_version": "1.7.8",
50
+ "min_morit_version": "1.7.12",
51
51
  "max_morit_version": "1.999.999",
52
52
  "permissions": [],
53
53
  "capabilities": [
@@ -20,7 +20,7 @@
20
20
  "privacy_policy_url": "https://example.com/privacy",
21
21
  "publisher": "example",
22
22
  "version": "1.0.0",
23
- "min_morit_version": "1.7.8",
23
+ "min_morit_version": "1.7.12",
24
24
  "max_morit_version": "1.999.999",
25
25
  "permissions": ["network", "credentials"],
26
26
  "capabilities": [
@@ -110,6 +110,16 @@ namespace, quota, migration과 표준 CRUD Tool은 [Plugin Local Storage와 AI
110
110
  "description": "연결한 계정의 노트를 검색합니다.",
111
111
  "permissions": ["network", "credentials"],
112
112
  "timeout_seconds": 10,
113
+ "input_schema": {
114
+ "type": "object",
115
+ "properties": {"query": {"type": "string", "minLength": 1}},
116
+ "required": ["query"]
117
+ },
118
+ "output_schema": {
119
+ "type": "object",
120
+ "properties": {"items": {"type": "array", "items": {"type": "object"}}},
121
+ "required": ["items"]
122
+ },
113
123
  "runtime": {
114
124
  "adapter": "http_json",
115
125
  "connector_id": "notes_api"
@@ -121,6 +131,9 @@ namespace, quota, migration과 표준 CRUD Tool은 [Plugin Local Storage와 AI
121
131
  timeout은 0.1~30초입니다. capability 권한은 Manifest 권한의 부분집합이어야 합니다. `tool`과
122
132
  `skill`은 실행 가능한 runtime adapter가 필수입니다. Search `provider`는
123
133
  `runtime.role: "search"`와 adapter가 필요합니다.
134
+ `input_schema`/`output_schema`는 선택이지만 선언하면 AI Tool·UI·Host가 모두 같은
135
+ 안전한 JSON Schema 부분집합으로 입력과 결과를 검증합니다. UI binding 정적 검사는
136
+ [UI Runtime v2](ui-runtime-v2.md)를 참고하세요.
124
137
 
125
138
  ## UI extension
126
139
 
@@ -142,6 +155,10 @@ timeout은 0.1~30초입니다. capability 권한은 Manifest 권한의 부분집
142
155
  `workspace`, `response` 중 하나입니다. 상세 선택 기준은 [UI extension point](ui-extensions.md),
143
156
  config 계약은 [UI Runtime v2](ui-runtime-v2.md)를 참고하세요.
144
157
 
158
+ `point: "response"`는 `ui_schema: 2`와 A2UI v0.9 metadata/schema가 필수이며 이전 Response UI
159
+ 형식은 허용하지 않습니다. Host가 활성 Instance, 권한, data source와 schema를 확인한 뒤 catalog에
160
+ 동적 등록합니다. AI는 등록된 컴포넌트 ID만 선택할 수 있고 결과 데이터는 Host가 주입합니다.
161
+
145
162
  ## Credential과 Connector
146
163
 
147
164
  Credential은 사용자별 비밀 값의 종류를 선언합니다.
@@ -75,8 +75,10 @@ npx -y @morit/cli plugin preview . --output ./dist/preview.html
75
75
  python tools/morit_plugin.py preview . --output ./dist/preview.html
76
76
  ```
77
77
 
78
- 정보 구조와 compiled manifest, light/dark partial theme를 점검합니다. 실제 Flutter renderer 성공을
79
- 대신하지 않습니다.
78
+ 정보 구조와 compiled manifest, light/dark partial theme를 점검합니다. Preview는 Flutter Host와
79
+ 같은 state/binding/compound condition, form validation, autocomplete·searchable select, 순차·병렬
80
+ action flow, route parameter/result 규칙을 사용하며 Runtime Inspector로 변경을 확인할 수 있습니다.
81
+ 실제 인증 Tool·native route stack·플랫폼 IME 동작은 Flutter 앱에서 최종 확인합니다.
80
82
 
81
83
  ### 3. Package reopen
82
84
 
@@ -100,6 +102,12 @@ python tools/morit_plugin.py verify ./dist/plugin.mplg
100
102
  - local file 설치
101
103
  - 활성화와 권한 허용·거부
102
104
  - 화면 loading·empty·error·retry
105
+ - 한국어 IME 입력 직후 Tool arguments, focus 전환과 controller 동기화
106
+ - autocomplete, searchable/async select, form validation, 순차·병렬 action flow
107
+ - route parameters·result 반환, permission/connection/platform/screen 복합 조건
108
+ - 홈·플러그인 화면·AI Response UI 간 source cache 재사용과 storage mutation 자동 refresh
109
+ - A2UI 답변 완료 후 즉시 렌더링, 실패 생략, 로딩 UI 미표시
110
+ - 미디어 overlay의 영상·MP3 옵션, 실제 변환·진행률·재시도·모바일 너비
103
111
  - 시스템/라이트/다크 전환과 기존 theme 없는 package 호환
104
112
  - Storage CRUD·검색·즉시 UI 갱신, AI read/read_write 권한
105
113
  - 사용자·Installation·Instance·namespace 격리와 quota/revision/generation 충돌
@@ -111,8 +119,10 @@ python tools/morit_plugin.py verify ./dist/plugin.mplg
111
119
 
112
120
  ### 6. 회귀 검사
113
121
 
114
- SDK, Python Host, Flutter schema, Local MCP, Remote MCP가 같은 contract 반환하고 같은 fixture를 허용·거부하는지
115
- 확인합니다. 문서 예제 JSON도 source 검증 과정에서 실행해 구현과 함께 변경합니다.
122
+ SDK, Python Host, Flutter schema/runtime, Local MCP, Remote MCP, Preview가 같은 contract 경계를
123
+ 반환하고 같은 fixture를 허용·거부하는지 확인합니다. capability input/output schema와 UI
124
+ binding도 모든 validator에서 같이 실패해야 합니다. 문서 예제 JSON도 source 검증 과정에서
125
+ 실행해 구현과 함께 변경합니다.
116
126
 
117
127
  ## 실패 시 원칙
118
128
 
@@ -32,7 +32,7 @@ claude mcp add --transport http --scope user \
32
32
 
33
33
  | Tool | 목적 |
34
34
  |---|---|
35
- | `morit_sdk_contract` | SDK 1.7.8 Theme·Storage·UI/Host capability contract 전체 반환 |
35
+ | `morit_sdk_contract` | SDK 1.7.12 Theme·Storage·UI/Host capability contract 전체 반환 |
36
36
  | `morit_docs_search` | bundled 공식 문서 검색 |
37
37
  | `morit_docs_get` | 검색 결과의 Markdown 한 파일 읽기 |
38
38
 
@@ -7,9 +7,10 @@ Morit은 두 MCP 연결을 제공합니다.
7
7
  | Local `@morit/plugin-mcp` | 개발자 PC의 stdio process | 로컬 파일 직접 편집, 빠른 preview/build |
8
8
  | Remote `https://morit-api.moring.co/mcp` | Morit Cloud Streamable HTTP | 파일시스템 없는 AI client, 조직 Project·Secret·Deployment |
9
9
 
10
- 두 연결은 SDK 1.7.8의 `@morit/cli`와 Host Plugin contract를 사용합니다. 계약에는 mode별 Plugin
11
- Theme, Local Storage/AI access, 표준 Storage Tool, UI binding safe preview가 포함됩니다. Local source에 접근할 필요가
12
- 없으면 Remote를, 현재 저장소 파일을 직접 고쳐야 하면 Local을 선택합니다.
10
+ 두 연결은 SDK 1.7.12의 `@morit/cli`와 Host Plugin contract를 사용합니다. 계약에는 mode별 Plugin
11
+ Theme, Local Storage/AI access, 표준 Storage Tool, UI binding, A2UI Response catalog와 safe
12
+ preview가 포함됩니다. Local source에 접근할 필요가 없으면 Remote를, 현재 저장소 파일을 직접
13
+ 고쳐야 하면 Local을 선택합니다.
13
14
 
14
15
  ## 준비
15
16
 
@@ -148,6 +149,11 @@ Storage 기능을 생성할 때는 먼저 `morit_sdk_contract`의 `permissions`,
148
149
  AI가 쓰는 namespace는 `ai_access: read_write`와 `ai_storage` 권한을 모두 선언합니다. MCP는 package
149
150
  runtime 데이터를 읽는 우회 경로가 아니며 개발 source와 artifact만 다룹니다.
150
151
 
152
+ Response extension을 생성할 때는 `ai_response`와 `ui_runtime` 계약을 함께 읽습니다. MCP 에이전트는
153
+ `point: "response"`, Runtime v2, `a2ui.version/description/schema`, 실제 capability data source를 한
154
+ 단위로 작성해야 합니다. AI가 결과 JSON을 만들게 하지 말고 schema에는 표시 가능한 최소 데이터와
155
+ empty 조건을 명시합니다. validate가 schema·권한·version을 거부하면 파일을 우회 패키징하지 않습니다.
156
+
151
157
  ## Remote MCP 작업 순서
152
158
 
153
159
  ```text
@@ -74,7 +74,7 @@ GET과 POST만 지원하며 redirect를 따라가지 않습니다. endpoint는
74
74
 
75
75
  ### Remote MCP
76
76
 
77
- Morit Host 1.7.8의 plugin outbound MCP adapter는 `mcp_http`입니다. 개발자용 Local/Remote Plugin
77
+ Morit Host 1.7.12의 plugin outbound MCP adapter는 `mcp_http`입니다. 개발자용 Local/Remote Plugin
78
78
  MCP 서버와 이름이 비슷하지만, 설치된 플러그인이 선언한 외부 MCP Tool을 Host 경계에서 호출하는
79
79
  runtime입니다.
80
80
 
@@ -133,10 +133,26 @@ enable/disable·권한 변경·Connection 생성/수정/삭제·OAuth 완료·
133
133
  "description": "연결한 학교의 오늘 시간표, 급식, 학사일정을 조회합니다.",
134
134
  "permissions": ["network", "storage"],
135
135
  "timeout_seconds": 10,
136
+ "input_schema": {
137
+ "type": "object",
138
+ "properties": {"date": {"type": "string"}},
139
+ "additionalProperties": false
140
+ },
141
+ "output_schema": {
142
+ "type": "object",
143
+ "properties": {"timetable": {"type": "array", "items": {"type": "object"}}},
144
+ "required": ["timetable"]
145
+ },
136
146
  "runtime": {"adapter": "neis_school", "operation": "overview"}
137
147
  }
138
148
  ```
139
149
 
150
+ `input_schema`/`output_schema`는 root `object`인 안전한 JSON Schema 부분집합입니다. AI Tool,
151
+ UI data source·invoke, Host 실행과 결과 binding이 같은 schema를 사용합니다. CLI/MCP validate는
152
+ 필수 필드·타입·범위와 `data.<source>.data...` 경로를 package build 전에 검사합니다.
153
+ 실행 시 schema와 다른 외부 응답은 정상 결과로 변환하지 않고 해당 capability 오류로
154
+ 격리합니다.
155
+
140
156
  UI `invoke`가 capability를 참조할 때도 enabled, generation, permission, timeout을 다시 확인합니다.
141
157
  UI에 capability ID를 사용자용 라벨로 표시하지 않습니다.
142
158
 
@@ -95,6 +95,10 @@ Tool 하나의 실패가 AI 요청 전체 실패를 뜻하지 않는 경우 대
95
95
  계산한 frame 합산 pixel이 16,000,000 이하여야 합니다.
96
96
  - 원격 이미지 실패를 앱 직접 fetch나 임의 외부 URL widget으로 우회하지 않습니다.
97
97
  - Response UI가 원래 AI 메시지 안의 한 container에 붙는지 확인합니다.
98
+ - Response UI가 안 보이면 `config.a2ui.version`이 `v0.9`인지, schema가 실제 완료 데이터와 맞는지,
99
+ response의 data source가 실행 capability를 가리키는지, Instance 권한이 승인됐는지 확인합니다.
100
+ - 빈 데이터·무관한 답변·중복 결과·Tool 실패에서 Response UI가 생기지 않는 것은 정상입니다. AI가
101
+ catalog 항목을 선택하지 않은 경우에도 서버가 임의 fallback UI를 만들지 않고 텍스트만 남깁니다.
98
102
 
99
103
  ## MCP 오류
100
104
 
@@ -32,6 +32,11 @@ UI extension은 Host의 노출 위치와 Runtime config를 연결합니다.
32
32
  일반 Tool/Skill은 자동으로 `workspace`가 되지 않습니다. AI 작업 공간 UI가 필요할 때만 별도
33
33
  `workspace` extension을 선언합니다. Slash Command와 Tool discovery도 별도 계약입니다.
34
34
 
35
+ `response`는 Runtime v2와 `config.a2ui`가 모두 필수입니다. 활성 Instance의 권한과 capability
36
+ data source가 확인되면 Host가 `plugin.<extension-id>` 컴포넌트로 동적 등록하고, AI가 최종 답변에
37
+ 필요하다고 명시적으로 선택한 경우에만 렌더링합니다. 다른 point에 `a2ui`를 선언하거나 기존
38
+ Runtime v1 Response 형식을 사용하는 것은 오류입니다.
39
+
35
40
  ## Home placement
36
41
 
37
42
  Runtime v2의 `card`와 `action`은 고유 section을 만들 수 있습니다.
@@ -61,8 +66,8 @@ enabled, generation, granted permission, timeout을 Host 실행 경계에서 다
61
66
  ## Runtime v1 호환
62
67
 
63
68
  `ui_schema`가 없는 기존 extension은 `sections`, `actions`, `form`, shorthand `capability`/`label`을
64
- 사용하는 Runtime v1으로 읽힙니다. v1은 기존 설치 호환용입니다. 새 서비스형 화면에는 state,
65
- data source, navigation, theme, Response UI를 함께 사용할 수 있는 Runtime v2를 사용합니다.
69
+ 사용하는 Runtime v1으로 읽힙니다. v1은 Response 이외의 기존 설치 호환용입니다. 새 서비스형
70
+ 화면에는 state, data source, navigation, theme를 함께 사용할 수 있는 Runtime v2를 사용합니다.
66
71
 
67
72
  Runtime v1 action style은 `primary` 또는 `secondary`이고, form field는 `text`, `integer`,
68
73
  `select`만 지원합니다. v1 JSON에 v2 필드를 섞으면 오류입니다.
@@ -73,7 +73,8 @@ UI Runtime v2는 서명된 `.mplg`가 화면, 상태, data source, action을 JSO
73
73
  ```
74
74
 
75
75
  허용 config 필드는 `ui_schema`, `icon`, `description`, `placement`, `app_bar`, `navigation`,
76
- `theme`, `initial_state`, `data_sources`, `view`입니다.
76
+ `theme`, `initial_state`, `data_sources`, `a2ui`, `view`입니다. `a2ui`는 `point: "response"`에서만
77
+ 사용합니다.
77
78
 
78
79
  ## 트리와 크기 제한
79
80
 
@@ -150,9 +151,19 @@ theme를 생략한 기존 플러그인은 Morit의 현재 Material 3 라이트·
150
151
  ```
151
152
 
152
153
  `field`, `select`, `switch`, navigation selection, `set_state`는 여기에 선언된 key만 변경합니다.
154
+ Host는 focus된 입력과 `TextEditingController`를 action 실행 직전에 commit하므로 조합 중인
155
+ 한국어 IME 문자도 Tool arguments와 validation에 최신 값으로 반영됩니다.
153
156
  `persist: true`는 Instance settings의 extension 전용 namespace에 저장합니다. credential과 민감한
154
157
  사용자 데이터는 state에 저장하지 않습니다.
155
158
 
159
+ ### 입력과 검증
160
+
161
+ - `field.suggestions`는 정적 autocomplete 문자열, `suggestions_source`는 `data.<source>...` 배열입니다.
162
+ - `select.options`는 정적 `{value,label}` 목록, `options_source`는 capability가 돌려준 비동기 목록입니다.
163
+ - `select.searchable: true`는 Material 3 검색 select를 사용합니다.
164
+ - `required`, `min_length`, `max_length`, `minimum`, `maximum`, `error_text`는 `field`/`select`/`switch`
165
+ 검증에 사용합니다. action의 `validate: true`는 잘못된 form을 실행하지 않습니다.
166
+
156
167
  ## Data source와 binding
157
168
 
158
169
  ```json
@@ -185,6 +196,14 @@ arguments에 전달하고, 다른 글자와 섞으면 텍스트 template이 됩
185
196
  {"label": "{{state.period}} 일정"}
186
197
  ```
187
198
 
199
+ `context.platform`, `context.screen.width|height|size_class`, `context.permissions.<permission>`,
200
+ `context.connections.<credential>`, `context.connected`, `context.loading.<source>`,
201
+ `context.errors.<source>`, `context.route.<parameter>`도 조건과 binding에 사용할 수 있습니다.
202
+
203
+ Capability의 `input_schema`/`output_schema`는 안전한 JSON Schema 부분집합을 사용합니다. validate는
204
+ data source와 invoke arguments를 input schema와, `data.<source>.data...` binding을 output schema와 정적으로
205
+ 대조합니다. 필수 필드·타입·존재하지 않는 경로는 package build 전에 구체적인 오류로 거부됩니다.
206
+
188
207
  ### Local Storage binding
189
208
 
190
209
  저장소도 같은 data source/action 흐름을 사용합니다.
@@ -276,6 +295,25 @@ form dialog sheet field select switch empty
276
295
 
277
296
  `store`는 선언된 data source ID이며 성공 결과로 해당 source를 교체합니다.
278
297
 
298
+ ### Action Flow와 Form
299
+
300
+ ```json
301
+ {
302
+ "type": "flow",
303
+ "mode": "sequential",
304
+ "validate": true,
305
+ "continue_on_error": false,
306
+ "actions": [
307
+ {"type": "invoke", "capability": "school.save", "arguments": {"name": "{{state.name}}"}, "store": "saved"},
308
+ {"type": "navigate", "target": "school.details", "parameters": {"id": "{{data.saved.data.id}}"}, "result_state": "route_result"}
309
+ ]
310
+ }
311
+ ```
312
+
313
+ `mode`는 `sequential` 또는 `parallel`입니다. flow는 최대 4 depth, 전체 16 action, flow당
314
+ 8 action으로 제한됩니다. 순차 flow는 앞 action의 store를 다음 action에서 즉시 읽고,
315
+ 병렬 flow는 서로 의존하지 않는 action을 함께 실행합니다.
316
+
279
317
  ### State와 refresh
280
318
 
281
319
  ```json
@@ -292,13 +330,17 @@ form dialog sheet field select switch empty
292
330
  {
293
331
  "type": "navigate",
294
332
  "target": "school_life.details",
333
+ "parameters": {"date": "{{state.date}}"},
334
+ "result_state": "details_result",
295
335
  "transition": "platform",
296
336
  "replace": false
297
337
  }
298
338
  ```
299
339
 
300
340
  `transition`은 `platform`, `fade`, `slide`, `none`입니다. `replace: true`는 현재 route를 뒤로가기
301
- stack에서 교체합니다. 같은 manifest의 UI extension ID만 target이 될 수 있습니다.
341
+ stack에서 교체합니다. 같은 manifest의 UI extension ID만 target이 될 수 있습니다. `parameters`는
342
+ 대상의 `context.route.<parameter>`로 전달되고 닫힌 결과는 `result_state`에 지정한
343
+ `initial_state` key에 반영됩니다. 대상은 `back.result`로 결과 객체를 반환합니다.
302
344
 
303
345
  ```json
304
346
  {"type": "back"}
@@ -315,8 +357,9 @@ stack에서 교체합니다. 같은 manifest의 UI extension ID만 target이 될
315
357
  }
316
358
  ```
317
359
 
318
- `equals`, `not_equals`, `exists` 중 정확히 하나를 사용합니다. 조건은 state·data·현재 `item` 값만
319
- 읽고 임의 expression을 실행하지 않습니다.
360
+ 단일 조건은 `equals`, `not_equals`, `exists` 중 정확히 하나를 사용합니다. `all`, `any`, `not`으로
361
+ 조건을 재귀적으로 조합할 있으며 조건 depth 4, 전체 32개로 제한됩니다. 조건은
362
+ `state`, `data`, `item`, `context`만 읽고 임의 expression을 실행하지 않습니다.
320
363
 
321
364
  ## 로딩과 오류 복구
322
365
 
@@ -325,16 +368,39 @@ Host는 source별 실행을 격리하고 중복 실행을 합칩니다. 화면
325
368
  유지하고, 첫 로드도 실패한 경우에만 해당 영역에 retry 상태를 표시합니다. package generation이나
326
369
  화면이 바뀐 뒤 도착한 오래된 응답은 버립니다.
327
370
 
371
+ Host는 Instance·capability·해석된 query/arguments가 같은 결과를 Shell 수명의 공유 cache에
372
+ 보관합니다. 홈, 플러그인 화면, AI Response UI에서 같은 source를 열면 추가 호출 없이
373
+ 즉시 재사용하고 mutation/refresh 결과를 구독 중인 모든 surface에 자동 전파합니다.
374
+ 알림 Tool은 같은 Instance local storage를 source of truth로 읽으며 알림 action의 storage mutation도
375
+ storage revision을 갱신해 열린 UI source를 자동 refresh합니다.
376
+
377
+ ## Runtime Inspector
378
+
379
+ Preview와 debug Host의 `Runtime Inspector`는 현재 state, data source 결과, context, loading/error,
380
+ 마지막 action과 검증 오류를 표시합니다. secret과 credential 값은 포함하지 않으며 배포 build의
381
+ 일반 사용자 화면에서는 노출하지 않습니다.
382
+
328
383
  ## Response UI
329
384
 
330
385
  `point: "response"`도 같은 `theme`, `app_bar`, `navigation`, data/state/node/action 계약을
331
- 재사용합니다. 단, 결과는 capability를 실행한 AI 메시지의 response container 안에 렌더링됩니다.
332
- 표현 세부는 [Response UI와 Agent Timeline](ai-response-and-timeline.md)을 참고하세요.
386
+ 재사용합니다. 추가로 `a2ui.version`, 사용 조건을 설명하는 `a2ui.description`, 실제 Tool 결과를
387
+ 검증할 `a2ui.schema`가 필수입니다. 결과는 AI가 catalog 후보를 명시적으로 선택하고 Host의
388
+ 유효성·관련성·중복·권한 검증을 통과했을 때만 해당 AI 메시지의 한 response container 안에
389
+ 렌더링됩니다. 서버는 response를 일괄 생성하거나 shape로 추측하지 않습니다. 상세 계약과 기본
390
+ catalog는 [Response UI와 Agent Timeline](ai-response-and-timeline.md)을 참고하세요.
333
391
 
334
392
  ## Preview와 실제 Host
335
393
 
336
394
  `morit plugin preview . --output ./dist/preview.html` 또는 `morit_project_preview`는 validate를 먼저
337
- 통과한 config를 실행 코드 없이 안전한 HTML로 렌더링합니다. 상단 토글로 light/dark variantHost
338
- 상속 결과를 비교할 있습니다. 같은 manifest를 Flutter 앱이 실제 Material widget으로 렌더링하므로
339
- node, binding, theme 계약은 공유하지만 focus, 키보드, route stack, 권한, 실제 Tool 호출은 서명
340
- package를 앱에 설치해 최종 확인합니다.
395
+ 통과한 config를 실행 코드 없이 안전한 HTML로 렌더링합니다. Preview는 Flutter Host같은
396
+ binding, compound condition, state commit, form validation, autocomplete/select, 순차·병렬 flow,
397
+ route params/result 규칙을 적용하고 invoke/navigation을 검증 가능한 Host event로 노출합니다.
398
+ 상단 토글로 light/dark variant와 Host 상속 결과를 비교하고 Runtime Inspector로 상태를 확인합니다.
399
+ 실제 인증·권한·network Tool과 native route stack만 서명 package를 앱에 설치해 최종 확인합니다.
400
+
401
+ ## 1.7.12 호환성
402
+
403
+ 기존 UI v2 manifest는 변경 없이 동작합니다. compound condition, action flow, input 검증,
404
+ async options, route data, capability schema는 모두 additive입니다. 기존 단일 action과
405
+ `{path, equals|not_equals|exists}` 조건은 그대로 유지됩니다. 새 필드를 사용하는 package는
406
+ `min_morit_version: "1.7.12"`를 지정합니다.
@@ -10,7 +10,7 @@ SHA-256을 함께 남기면 나중에 같은 결과를 재현할 수 있습니
10
10
 
11
11
  | 패키지 | 크기 | SHA-256 |
12
12
  |---|---:|---|
13
- | `school-life-1.5.0.mplg` | 263184 bytes | `3b6397ca1ef26833fe842087b0ecf5f05621bd45f9d80b8a422b6547c060f603` |
13
+ | `school-life-1.5.0.mplg` | 263438 bytes | `dfd39c29154c50bd14b4eeed390efffa7fa5ab3c6ad9361730eea67bdb836276` |
14
14
  | `notion-1.1.0.mplg` | 3044 bytes | `eba57c440b305343c5cf917259e30e50b322272cfadb0ed64d39ecfc47539c4e` |
15
15
 
16
16
  ```powershell
@@ -108,6 +108,11 @@ Backend live test는 앱 화면과 Android 알림을 누르지 않습니다. rel
108
108
  6. 강제 종료·재실행 뒤 설치·연결·화면 상태 복원
109
109
  7. 비활성화·삭제 뒤 화면·capability·알림 제거
110
110
 
111
+ Response UI는 정상 데이터에서 필요한 컴포넌트 하나만 선택되는지뿐 아니라 빈 데이터, 관련 없는
112
+ 질문, 같은 결과의 중복, schema 불일치, 권한 미승인, Tool 실패에서 컴포넌트가 생성되지 않고 일반
113
+ 텍스트 답변이 유지되는지도 확인합니다. Plugin Response는 `plugin.<extension-id>` 등록 ID와 실제
114
+ 실행 capability의 data source가 일치해야 합니다.
115
+
111
116
  실기기에서 확인하지 않은 항목은 자동 테스트가 통과했더라도 `미검증`으로 남깁니다.
112
117
 
113
118
  ## 5. 결과 기록 형식