@aaronsb/google-workspace-mcp 2.6.0 → 2.7.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 (68) hide show
  1. package/README.md +2 -2
  2. package/build/__tests__/factory/calendar-patch.test.js +79 -0
  3. package/build/__tests__/factory/calendar-patch.test.js.map +1 -1
  4. package/build/__tests__/factory/drive-patch.test.js +224 -0
  5. package/build/__tests__/factory/drive-patch.test.js.map +1 -1
  6. package/build/__tests__/factory/sheets-patch.test.js +71 -6
  7. package/build/__tests__/factory/sheets-patch.test.js.map +1 -1
  8. package/build/__tests__/server/formatting/html-sanitize.test.d.ts +7 -0
  9. package/build/__tests__/server/formatting/html-sanitize.test.js +174 -0
  10. package/build/__tests__/server/formatting/html-sanitize.test.js.map +1 -0
  11. package/build/__tests__/server/formatting/markdown.test.js +55 -0
  12. package/build/__tests__/server/formatting/markdown.test.js.map +1 -1
  13. package/build/__tests__/server/handlers/calendar.test.js +2 -1
  14. package/build/__tests__/server/handlers/calendar.test.js.map +1 -1
  15. package/build/executor/gws.js +41 -20
  16. package/build/executor/gws.js.map +1 -1
  17. package/build/factory/generator.d.ts +11 -3
  18. package/build/factory/generator.js +41 -16
  19. package/build/factory/generator.js.map +1 -1
  20. package/build/factory/manifest/README.md +12 -0
  21. package/build/factory/manifest/calendar.yaml +204 -0
  22. package/build/factory/manifest/docs.yaml +77 -0
  23. package/build/factory/manifest/drive.yaml +314 -0
  24. package/build/factory/manifest/gmail.yaml +272 -0
  25. package/build/factory/manifest/meet.yaml +159 -0
  26. package/build/factory/manifest/sheets.yaml +225 -0
  27. package/build/factory/manifest/tasks.yaml +137 -0
  28. package/build/factory/registry.d.ts +3 -3
  29. package/build/factory/registry.js +5 -5
  30. package/build/factory/registry.js.map +1 -1
  31. package/build/factory/types.d.ts +6 -0
  32. package/build/server/formatting/html-sanitize.d.ts +40 -0
  33. package/build/server/formatting/html-sanitize.js +146 -0
  34. package/build/server/formatting/html-sanitize.js.map +1 -0
  35. package/build/server/formatting/markdown.d.ts +13 -3
  36. package/build/server/formatting/markdown.js +33 -6
  37. package/build/server/formatting/markdown.js.map +1 -1
  38. package/build/server/handlers/calendar.js +1 -1
  39. package/build/server/handlers/calendar.js.map +1 -1
  40. package/build/server/scratchpad/__tests__/docs-sync-integration.test.d.ts +11 -0
  41. package/build/server/scratchpad/__tests__/docs-sync-integration.test.js +187 -0
  42. package/build/server/scratchpad/__tests__/docs-sync-integration.test.js.map +1 -0
  43. package/build/server/scratchpad/__tests__/docs-sync.test.d.ts +16 -0
  44. package/build/server/scratchpad/__tests__/docs-sync.test.js +213 -0
  45. package/build/server/scratchpad/__tests__/docs-sync.test.js.map +1 -0
  46. package/build/server/scratchpad/adapters/import-doc.js +6 -3
  47. package/build/server/scratchpad/adapters/import-doc.js.map +1 -1
  48. package/build/server/scratchpad/adapters/send-calendar.js +1 -1
  49. package/build/server/scratchpad/adapters/send-calendar.js.map +1 -1
  50. package/build/server/scratchpad/docs-sync.d.ts +65 -0
  51. package/build/server/scratchpad/docs-sync.js +191 -0
  52. package/build/server/scratchpad/docs-sync.js.map +1 -0
  53. package/build/server/scratchpad/handler.js +95 -14
  54. package/build/server/scratchpad/handler.js.map +1 -1
  55. package/build/server/scratchpad/manager.d.ts +7 -0
  56. package/build/server/scratchpad/manager.js.map +1 -1
  57. package/build/services/calendar/patch.js +72 -1
  58. package/build/services/calendar/patch.js.map +1 -1
  59. package/build/services/drive/patch.js +170 -0
  60. package/build/services/drive/patch.js.map +1 -1
  61. package/build/services/gmail/patch.js +7 -2
  62. package/build/services/gmail/patch.js.map +1 -1
  63. package/build/services/sheets/patch.js +57 -6
  64. package/build/services/sheets/patch.js.map +1 -1
  65. package/build/version.d.ts +1 -1
  66. package/build/version.js +1 -1
  67. package/package.json +4 -2
  68. package/build/factory/manifest.yaml +0 -1383
@@ -0,0 +1,159 @@
1
+ tool_name: manage_meet
2
+ description: "Browse past Google Meet conferences, participants, transcripts, recordings, and AI-generated smart notes. Requires Workspace Business Standard+ for transcripts/recordings."
3
+ requires_email: true
4
+ gws_service: meet
5
+ operations:
6
+
7
+ # --- Conference records ---
8
+
9
+ listConferences:
10
+ type: list
11
+ description: "list recent meeting conferences (default: descending by start time)"
12
+ resource: conferenceRecords.list
13
+ params:
14
+ filter:
15
+ type: string
16
+ description: "EBNF filter (e.g. 'space.meeting_code = \"abc-mnop-xyz\"' or 'start_time>=\"2026-01-01T00:00:00Z\"')"
17
+ maxResults:
18
+ type: number
19
+ description: "Max conferences to return (default: 25, max: 100)"
20
+ default: 25
21
+ max: 100
22
+ maps_to: pageSize
23
+
24
+ getConference:
25
+ type: detail
26
+ description: "get details of a specific conference"
27
+ resource: conferenceRecords.get
28
+ params:
29
+ conferenceId:
30
+ type: string
31
+ description: "Conference record ID (from listConferences)"
32
+ required: true
33
+ maps_to: name
34
+
35
+ # --- Participants ---
36
+
37
+ listParticipants:
38
+ type: list
39
+ description: "list who attended a conference"
40
+ resource: conferenceRecords.participants.list
41
+ params:
42
+ conferenceId:
43
+ type: string
44
+ description: "Conference record ID"
45
+ required: true
46
+ maps_to: parent
47
+ filter:
48
+ type: string
49
+ description: "Filter (e.g. 'latest_end_time IS NULL' for active participants)"
50
+ maxResults:
51
+ type: number
52
+ description: "Max participants (default: 100, max: 250)"
53
+ default: 100
54
+ max: 250
55
+ maps_to: pageSize
56
+
57
+ # --- Transcripts ---
58
+
59
+ listTranscripts:
60
+ type: list
61
+ description: "list transcripts for a conference (requires Workspace Business Standard+)"
62
+ resource: conferenceRecords.transcripts.list
63
+ params:
64
+ conferenceId:
65
+ type: string
66
+ description: "Conference record ID"
67
+ required: true
68
+ maps_to: parent
69
+
70
+ getTranscript:
71
+ type: detail
72
+ description: "get transcript metadata including Google Docs destination"
73
+ resource: conferenceRecords.transcripts.get
74
+ params:
75
+ transcriptName:
76
+ type: string
77
+ description: "Transcript resource name (from listTranscripts)"
78
+ required: true
79
+ maps_to: name
80
+
81
+ listTranscriptEntries:
82
+ type: list
83
+ description: "get structured transcript text — who said what, with timestamps"
84
+ resource: conferenceRecords.transcripts.entries.list
85
+ params:
86
+ transcriptName:
87
+ type: string
88
+ description: "Transcript resource name (e.g. conferenceRecords/.../transcripts/...)"
89
+ required: true
90
+ maps_to: parent
91
+ maxResults:
92
+ type: number
93
+ description: "Max entries per page (default: 10, max: 100)"
94
+ default: 100
95
+ max: 100
96
+ maps_to: pageSize
97
+
98
+ # --- Recordings ---
99
+
100
+ listRecordings:
101
+ type: list
102
+ description: "list recordings for a conference (saved as MP4 in Drive)"
103
+ resource: conferenceRecords.recordings.list
104
+ params:
105
+ conferenceId:
106
+ type: string
107
+ description: "Conference record ID"
108
+ required: true
109
+ maps_to: parent
110
+
111
+ getRecording:
112
+ type: detail
113
+ description: "get recording metadata including Drive file ID and playback link"
114
+ resource: conferenceRecords.recordings.get
115
+ params:
116
+ recordingName:
117
+ type: string
118
+ description: "Recording resource name (from listRecordings)"
119
+ required: true
120
+ maps_to: name
121
+
122
+ # --- Composite operations ---
123
+
124
+ getFullTranscript:
125
+ type: detail
126
+ description: "get the full who-said-what transcript for a conference (chains transcripts + entries + participant names automatically). Use pageToken to continue reading."
127
+ resource: conferenceRecords.transcripts.list
128
+ params:
129
+ conferenceId:
130
+ type: string
131
+ description: "Conference record ID (from listConferences)"
132
+ required: true
133
+ pageToken:
134
+ type: string
135
+ description: "Page token from a previous getFullTranscript call to continue reading"
136
+
137
+ # --- Smart Notes (Gemini) ---
138
+
139
+ listSmartNotes:
140
+ type: list
141
+ description: "list Gemini-generated smart notes for a conference"
142
+ resource: conferenceRecords.smartNotes.list
143
+ params:
144
+ conferenceId:
145
+ type: string
146
+ description: "Conference record ID"
147
+ required: true
148
+ maps_to: parent
149
+
150
+ getSmartNote:
151
+ type: detail
152
+ description: "get smart note metadata including Google Docs destination"
153
+ resource: conferenceRecords.smartNotes.get
154
+ params:
155
+ smartNoteName:
156
+ type: string
157
+ description: "Smart note resource name (from listSmartNotes)"
158
+ required: true
159
+ maps_to: name
@@ -0,0 +1,225 @@
1
+ tool_name: manage_sheets
2
+ description: "Read, write, and manage Google Sheets spreadsheets."
3
+ requires_email: true
4
+ gws_service: sheets
5
+ operations:
6
+
7
+ get:
8
+ type: detail
9
+ description: "get spreadsheet metadata and sheet names"
10
+ resource: spreadsheets.get
11
+ params:
12
+ spreadsheetId:
13
+ type: string
14
+ description: "Spreadsheet ID"
15
+ required: true
16
+
17
+ create:
18
+ type: action
19
+ description: "create a new spreadsheet (optionally with a title)"
20
+ resource: spreadsheets.create
21
+ params:
22
+ title:
23
+ type: string
24
+ description: "Spreadsheet title (defaults to 'Untitled spreadsheet')"
25
+
26
+ read:
27
+ type: detail
28
+ description: "read cell values from a range; rendered rows are prefixed with their sheet row number (e.g. 'R3: …') since blank rows are otherwise invisible"
29
+ helper: "+read"
30
+ params:
31
+ spreadsheetId:
32
+ type: string
33
+ description: "Spreadsheet ID"
34
+ required: true
35
+ range:
36
+ type: string
37
+ description: "Range to read (e.g. 'Sheet1!A1:D10' or just 'Sheet1')"
38
+ required: true
39
+ cli_args:
40
+ spreadsheetId: "--spreadsheet"
41
+ range: "--range"
42
+
43
+ append:
44
+ type: action
45
+ description: "append rows to a spreadsheet (rows land after the last row of existing data in the target range)"
46
+ resource: spreadsheets.values.append
47
+ params:
48
+ spreadsheetId:
49
+ type: string
50
+ description: "Spreadsheet ID"
51
+ required: true
52
+ range:
53
+ type: string
54
+ description: "Target range; defaults to 'Sheet1'. Use 'MyTab' or 'MyTab!A:Z' to append to a specific tab."
55
+ values:
56
+ type: string
57
+ description: "Comma-separated values for a single row (e.g. 'Alice,100,true')"
58
+ jsonValues:
59
+ type: string
60
+ description: "JSON 2D array of rows for bulk insert (e.g. '[[\"a\",\"b\"],[\"c\",\"d\"]]')"
61
+ valueInputOption:
62
+ type: string
63
+ description: "How input is interpreted: USER_ENTERED (default, parses formulas/types) or RAW"
64
+ enum: [USER_ENTERED, RAW]
65
+ default: USER_ENTERED
66
+
67
+ getValues:
68
+ type: detail
69
+ description: "get values from a specific range (raw API); rendered rows are prefixed with their sheet row number (e.g. 'R3: …')"
70
+ resource: spreadsheets.values.get
71
+ params:
72
+ spreadsheetId:
73
+ type: string
74
+ description: "Spreadsheet ID"
75
+ required: true
76
+ range:
77
+ type: string
78
+ description: "A1 notation range (e.g. 'Sheet1!A1:B10')"
79
+ required: true
80
+
81
+ updateValues:
82
+ type: action
83
+ description: "write values to a specific range"
84
+ resource: spreadsheets.values.update
85
+ params:
86
+ spreadsheetId:
87
+ type: string
88
+ description: "Spreadsheet ID"
89
+ required: true
90
+ range:
91
+ type: string
92
+ description: "A1 notation range to write to"
93
+ required: true
94
+ values:
95
+ type: string
96
+ description: "Comma-separated values for a single row (e.g. 'Alice,100,true')"
97
+ jsonValues:
98
+ type: string
99
+ description: "JSON 2D array of rows (e.g. '[[\"a\",\"b\"],[\"c\",\"d\"]]'). Use for multi-row writes."
100
+ valueInputOption:
101
+ type: string
102
+ description: "How input is interpreted: USER_ENTERED (default, parses formulas/types) or RAW"
103
+ enum: [USER_ENTERED, RAW]
104
+ default: USER_ENTERED
105
+
106
+ clearValues:
107
+ type: action
108
+ description: "clear cell values from a range (structure preserved)"
109
+ resource: spreadsheets.values.clear
110
+ params:
111
+ spreadsheetId:
112
+ type: string
113
+ description: "Spreadsheet ID"
114
+ required: true
115
+ range:
116
+ type: string
117
+ description: "A1 notation range to clear"
118
+ required: true
119
+
120
+ addSheet:
121
+ type: action
122
+ description: "add a new tab (sheet) to the spreadsheet"
123
+ resource: spreadsheets.batchUpdate
124
+ params:
125
+ spreadsheetId:
126
+ type: string
127
+ description: "Spreadsheet ID"
128
+ required: true
129
+ title:
130
+ type: string
131
+ description: "Name of the new sheet tab"
132
+ required: true
133
+ index:
134
+ type: number
135
+ description: "Position among tabs (0-based). Appended at the end if omitted."
136
+ rowCount:
137
+ type: number
138
+ description: "Initial row count (default 1000)"
139
+ columnCount:
140
+ type: number
141
+ description: "Initial column count (default 26)"
142
+
143
+ renameSheet:
144
+ type: action
145
+ description: "rename a tab (sheet) within a spreadsheet"
146
+ resource: spreadsheets.batchUpdate
147
+ params:
148
+ spreadsheetId:
149
+ type: string
150
+ description: "Spreadsheet ID"
151
+ required: true
152
+ sheetId:
153
+ type: number
154
+ description: "Sheet ID of the tab to rename (from manage_sheets get)"
155
+ required: true
156
+ title:
157
+ type: string
158
+ description: "New title for the tab"
159
+ required: true
160
+
161
+ deleteSheet:
162
+ type: action
163
+ description: "delete a tab (sheet) from the spreadsheet — irreversible"
164
+ resource: spreadsheets.batchUpdate
165
+ params:
166
+ spreadsheetId:
167
+ type: string
168
+ description: "Spreadsheet ID"
169
+ required: true
170
+ sheetId:
171
+ type: number
172
+ description: "Sheet ID of the tab to delete"
173
+ required: true
174
+
175
+ duplicateSheet:
176
+ type: action
177
+ description: "duplicate a tab within the same spreadsheet"
178
+ resource: spreadsheets.batchUpdate
179
+ params:
180
+ spreadsheetId:
181
+ type: string
182
+ description: "Spreadsheet ID"
183
+ required: true
184
+ sheetId:
185
+ type: number
186
+ description: "Source sheet ID (the tab to copy)"
187
+ required: true
188
+ title:
189
+ type: string
190
+ description: "Name for the duplicated sheet (defaults to 'Copy of <source>')"
191
+ index:
192
+ type: number
193
+ description: "Position to insert the duplicate (0-based)"
194
+
195
+ renameSpreadsheet:
196
+ type: action
197
+ description: "rename the spreadsheet (the document title, not a tab)"
198
+ resource: spreadsheets.batchUpdate
199
+ params:
200
+ spreadsheetId:
201
+ type: string
202
+ description: "Spreadsheet ID"
203
+ required: true
204
+ title:
205
+ type: string
206
+ description: "New spreadsheet title"
207
+ required: true
208
+
209
+ copySheetTo:
210
+ type: action
211
+ description: "copy a tab from this spreadsheet into another spreadsheet"
212
+ resource: spreadsheets.sheets.copyTo
213
+ params:
214
+ spreadsheetId:
215
+ type: string
216
+ description: "Source spreadsheet ID"
217
+ required: true
218
+ sheetId:
219
+ type: number
220
+ description: "Source sheet ID (the tab to copy)"
221
+ required: true
222
+ destinationSpreadsheetId:
223
+ type: string
224
+ description: "Destination spreadsheet ID"
225
+ required: true
@@ -0,0 +1,137 @@
1
+ tool_name: manage_tasks
2
+ description: "Manage task lists and tasks in Google Tasks."
3
+ requires_email: true
4
+ gws_service: tasks
5
+ operations:
6
+
7
+ # --- Task lists ---
8
+
9
+ listTaskLists:
10
+ type: list
11
+ description: "list all task lists"
12
+ resource: tasklists.list
13
+
14
+ getTaskList:
15
+ type: detail
16
+ description: "get a task list by ID"
17
+ resource: tasklists.get
18
+ params:
19
+ taskListId:
20
+ type: string
21
+ description: "Task list ID"
22
+ required: true
23
+ maps_to: tasklist
24
+
25
+ createTaskList:
26
+ type: action
27
+ description: "create a new task list"
28
+ resource: tasklists.insert
29
+
30
+ deleteTaskList:
31
+ type: action
32
+ description: "delete a task list and all its tasks"
33
+ resource: tasklists.delete
34
+ params:
35
+ taskListId:
36
+ type: string
37
+ description: "Task list ID to delete"
38
+ required: true
39
+ maps_to: tasklist
40
+
41
+ # --- Tasks ---
42
+
43
+ list:
44
+ type: list
45
+ description: "list tasks in a task list"
46
+ resource: tasks.list
47
+ params:
48
+ taskListId:
49
+ type: string
50
+ description: "Task list ID (use listTaskLists to find)"
51
+ required: true
52
+ maps_to: tasklist
53
+ showCompleted:
54
+ type: boolean
55
+ description: "Include completed tasks (default: true)"
56
+ maxResults:
57
+ type: number
58
+ description: "Max tasks to return (default: 20, max: 100)"
59
+ default: 20
60
+ max: 100
61
+
62
+ get:
63
+ type: detail
64
+ description: "get a specific task"
65
+ resource: tasks.get
66
+ params:
67
+ taskListId:
68
+ type: string
69
+ description: "Task list ID"
70
+ required: true
71
+ maps_to: tasklist
72
+ taskId:
73
+ type: string
74
+ description: "Task ID"
75
+ required: true
76
+ maps_to: task
77
+
78
+ create:
79
+ type: action
80
+ description: "create a new task"
81
+ resource: tasks.insert
82
+ params:
83
+ taskListId:
84
+ type: string
85
+ description: "Task list ID"
86
+ required: true
87
+ maps_to: tasklist
88
+
89
+ update:
90
+ type: action
91
+ description: "update a task (patch semantics)"
92
+ resource: tasks.patch
93
+ params:
94
+ taskListId:
95
+ type: string
96
+ description: "Task list ID"
97
+ required: true
98
+ maps_to: tasklist
99
+ taskId:
100
+ type: string
101
+ description: "Task ID"
102
+ required: true
103
+ maps_to: task
104
+
105
+ complete:
106
+ type: action
107
+ description: "mark a task as completed"
108
+ resource: tasks.patch
109
+ params:
110
+ taskListId:
111
+ type: string
112
+ description: "Task list ID"
113
+ required: true
114
+ maps_to: tasklist
115
+ taskId:
116
+ type: string
117
+ description: "Task ID"
118
+ required: true
119
+ maps_to: task
120
+ defaults:
121
+ status: completed
122
+
123
+ delete:
124
+ type: action
125
+ description: "delete a task"
126
+ resource: tasks.delete
127
+ params:
128
+ taskListId:
129
+ type: string
130
+ description: "Task list ID"
131
+ required: true
132
+ maps_to: tasklist
133
+ taskId:
134
+ type: string
135
+ description: "Task ID to delete"
136
+ required: true
137
+ maps_to: task
@@ -2,9 +2,9 @@
2
2
  * Shared factory registry — single instance of manifest + generated tools.
3
3
  * Both handler.ts and tools.ts import from here instead of loading independently.
4
4
  *
5
- * This module uses import.meta.url (ESM only) to resolve manifest.yaml
6
- * relative to the built output, which works when running via npx or mcpb
7
- * where cwd is NOT the project root.
5
+ * This module uses import.meta.url (ESM only) to resolve the manifest
6
+ * directory relative to the built output, which works when running via npx
7
+ * or mcpb where cwd is NOT the project root.
8
8
  */
9
9
  import type { GeneratedTool } from './types.js';
10
10
  import type { Manifest } from './types.js';
@@ -2,16 +2,16 @@
2
2
  * Shared factory registry — single instance of manifest + generated tools.
3
3
  * Both handler.ts and tools.ts import from here instead of loading independently.
4
4
  *
5
- * This module uses import.meta.url (ESM only) to resolve manifest.yaml
6
- * relative to the built output, which works when running via npx or mcpb
7
- * where cwd is NOT the project root.
5
+ * This module uses import.meta.url (ESM only) to resolve the manifest
6
+ * directory relative to the built output, which works when running via npx
7
+ * or mcpb where cwd is NOT the project root.
8
8
  */
9
9
  import { dirname } from 'node:path';
10
10
  import { fileURLToPath } from 'node:url';
11
11
  import { setModuleDir, loadManifest, generateTools } from './generator.js';
12
12
  import { patches } from './patches.js';
13
- // Inject module directory so loadManifest can find manifest.yaml
14
- // relative to the built output (build/factory/registry.js → build/factory/manifest.yaml)
13
+ // Inject module directory so loadManifest can find the manifest directory
14
+ // relative to the built output (build/factory/registry.js → build/factory/manifest/)
15
15
  setModuleDir(dirname(fileURLToPath(import.meta.url)));
16
16
  export const manifest = loadManifest();
17
17
  export const generatedTools = generateTools(manifest, patches);
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/factory/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC3E,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAIvC,iEAAiE;AACjE,yFAAyF;AACzF,YAAY,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAEtD,MAAM,CAAC,MAAM,QAAQ,GAAa,YAAY,EAAE,CAAC;AACjD,MAAM,CAAC,MAAM,cAAc,GAAoB,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC"}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/factory/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC3E,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAIvC,0EAA0E;AAC1E,qFAAqF;AACrF,YAAY,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAEtD,MAAM,CAAC,MAAM,QAAQ,GAAa,YAAY,EAAE,CAAC;AACjD,MAAM,CAAC,MAAM,cAAc,GAAoB,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC"}
@@ -13,6 +13,12 @@ export interface ParamDef {
13
13
  /** Maps this param name to a different key in the gws --params JSON. */
14
14
  maps_to?: string;
15
15
  enum?: string[];
16
+ /**
17
+ * If true, the param is for the formatter/handler only and is NOT forwarded
18
+ * to the gws CLI. Used for client-side controls like `bodyFormat` (ADR-305)
19
+ * that affect rendering, not the API call itself.
20
+ */
21
+ client_only?: boolean;
16
22
  }
17
23
  /** Hydration config — fetch detail for each item in a list result. */
18
24
  export interface HydrationDef {
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Shared HTML sanitization for any agent-facing path that emits or ingests
3
+ * HTML (Gmail message bodies, Docs HTML export, Drive HTML files, the
4
+ * scratchpad html format from ADR-302). See ADR-305.
5
+ *
6
+ * Every layer here exists for a specific real-world attack:
7
+ * 1. CSS-hidden subtrees ─ the most common prompt-injection pattern in
8
+ * marketing email; instructions are dropped into a `display:none` block
9
+ * so a human reader doesn't see them but an LLM consuming the markup does.
10
+ * 2. Tag/attribute allowlist ─ blocks `<script>`, event handlers, dangerous
11
+ * URI schemes (`javascript:`, `data:`, `vbscript:`).
12
+ * 3. Unicode injection chars ─ Tag Block (U+E0000–U+E007F), bidi overrides,
13
+ * zero-width spaces. Have been used to smuggle invisible instructions
14
+ * through human review.
15
+ * 4. Spotlighting delimiters ─ wrap the sanitized output in a tagged block
16
+ * with the source and an "untrusted" marker. Microsoft's LLMail-Inject
17
+ * research found this alone drops injection success >50% → <2%; combined
18
+ * with sanitization it's stronger than either layer alone.
19
+ *
20
+ * Default-off in callers: the existing stripped-text path is unchanged.
21
+ * Opt-in via `bodyFormat: 'html'` (or the equivalent on other ops).
22
+ */
23
+ /** Untrusted source identifier — appears on the Spotlighting wrapper. */
24
+ export type SanitizeSource = 'gmail' | 'docs' | 'drive' | 'scratchpad-import';
25
+ interface SanitizeOptions {
26
+ /** Where the HTML came from — surfaces on the Spotlighting wrapper. */
27
+ source: SanitizeSource;
28
+ }
29
+ /**
30
+ * Sanitize HTML before handing it to an agent-facing path.
31
+ *
32
+ * Returns a sanitized HTML string wrapped in a Spotlighting block. Safe to
33
+ * embed directly in tool response text — the wrapper signals "untrusted
34
+ * content" to the consuming model.
35
+ *
36
+ * Empty input returns an empty Spotlighting block (still wrapped, for
37
+ * consistency — callers can compare against `''` to detect no-content).
38
+ */
39
+ export declare function sanitizeHtmlForAgent(html: string, options: SanitizeOptions): string;
40
+ export {};