@hailer/mcp 1.3.14 → 1.3.23
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.
- package/.claude/skills/create-and-publish-app/SKILL.md +44 -56
- package/.claude/skills/hailer-app-builder/SKILL.md +414 -970
- package/.claude/skills/hailer-app-primitives/SKILL.md +742 -0
- package/.claude/skills/hailer-apps-pictures/SKILL.md +191 -87
- package/.claude/skills/hailer-design-patterns/SKILL.md +317 -0
- package/.claude/skills/hailer-design-system/SKILL.md +202 -149
- package/.claude/skills/hailer-workflow-archetypes/SKILL.md +301 -0
- package/.claude/skills/insight-join-patterns/SKILL.md +313 -0
- package/.claude/skills/publish-hailer-app/SKILL.md +211 -0
- package/.claude/skills/sdk-activity-patterns/SKILL.md +257 -105
- package/.claude/skills/sdk-function-fields/SKILL.md +253 -492
- package/.claude/skills/sdk-insight-calculations/SKILL.md +364 -0
- package/.claude/skills/sdk-insight-queries/SKILL.md +192 -397
- package/.claude/skills/sdk-ws-config-skill/SKILL.md +265 -720
- package/.claude/skills/tool-response-verification/SKILL.md +143 -0
- package/CLAUDE.md +50 -19
- package/dist/bot/bot.d.ts.map +1 -1
- package/dist/bot/bot.js +26 -34
- package/dist/bot/bot.js.map +1 -1
- package/dist/bot/services/helper-prompt.d.ts +1 -1
- package/dist/bot/services/helper-prompt.d.ts.map +1 -1
- package/dist/bot/services/helper-prompt.js +62 -82
- package/dist/bot/services/helper-prompt.js.map +1 -1
- package/dist/bot/services/system-prompt.js +4 -4
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +6 -0
- package/dist/config.js.map +1 -1
- package/dist/mcp/hailer-rpc.d.ts +1 -0
- package/dist/mcp/hailer-rpc.d.ts.map +1 -1
- package/dist/mcp/hailer-rpc.js +4 -0
- package/dist/mcp/hailer-rpc.js.map +1 -1
- package/dist/mcp/tools/activity.d.ts.map +1 -1
- package/dist/mcp/tools/activity.js +8 -2
- package/dist/mcp/tools/activity.js.map +1 -1
- package/dist/mcp/tools/app-core.d.ts.map +1 -1
- package/dist/mcp/tools/app-core.js +6 -3
- package/dist/mcp/tools/app-core.js.map +1 -1
- package/dist/mcp/tools/app-marketplace.d.ts.map +1 -1
- package/dist/mcp/tools/app-marketplace.js +5 -1
- package/dist/mcp/tools/app-marketplace.js.map +1 -1
- package/dist/mcp/webhook-handler.d.ts.map +1 -1
- package/dist/mcp/webhook-handler.js +27 -0
- package/dist/mcp/webhook-handler.js.map +1 -1
- package/dist/public-chat/graduate.d.ts.map +1 -1
- package/dist/public-chat/graduate.js +153 -53
- package/dist/public-chat/graduate.js.map +1 -1
- package/dist/public-chat/rate-limit.js +1 -1
- package/dist/public-chat/rate-limit.js.map +1 -1
- package/dist/public-chat/studio-prewarm.js +2 -2
- package/dist/public-chat/studio-prewarm.js.map +1 -1
- package/dist/public-chat/system-prompt.d.ts.map +1 -1
- package/dist/public-chat/system-prompt.js +41 -25
- package/dist/public-chat/system-prompt.js.map +1 -1
- package/package.json +1 -1
- package/.claude/skills/hailer-project-protocol/SKILL.md +0 -398
- package/.opencode/package-lock.json +0 -117
|
@@ -1,19 +1,34 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sdk-activity-patterns
|
|
3
|
-
description:
|
|
4
|
-
version: 1.5.0
|
|
5
|
-
triggers: Create activity, update activity, field values, date format, activitylink value
|
|
3
|
+
description: Correct field value formats for creating and updating Hailer activities via MCP tools (mcp__hailer__create_activity, mcp__hailer__update_activity). Load whenever creating activities, updating activity fields, bulk-creating activities, or debugging field write failures. Covers dates (Unix timestamps), dropdowns (plain strings), user fields, activitylink fields, daterange/timerange objects, clearing fields, bulk teamId placement, phase-gated field writes, and all-or-nothing batch behavior.
|
|
6
4
|
---
|
|
7
5
|
|
|
6
|
+
<!-- augmentation-guard: shrink-ok -->
|
|
7
|
+
|
|
8
8
|
# Activity CRUD Patterns
|
|
9
9
|
|
|
10
|
+
## MCP Tools
|
|
11
|
+
|
|
12
|
+
| Tool | Purpose |
|
|
13
|
+
|------|---------|
|
|
14
|
+
| `mcp__hailer__create_activity` | Create new activity |
|
|
15
|
+
| `mcp__hailer__update_activity` | Update existing activity |
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
10
19
|
## Create Activity
|
|
11
20
|
|
|
21
|
+
**`name` must be a non-empty string when provided.** Hailer rejects `name: ""` with `name: Activity name cannot be empty`.
|
|
22
|
+
|
|
23
|
+
**`create_activity` REQUIRES `name` even when `nameFunctionEnabled: true`.** The MCP runtime enforces this — pass a meaningful placeholder (e.g. `'Luonnos'`). The `nameFunction` will overwrite it immediately.
|
|
24
|
+
|
|
25
|
+
**`nameFunction` overrides any `name` you pass on create.** Place the human-readable identity in the field that `nameFunction` reads.
|
|
26
|
+
|
|
12
27
|
```javascript
|
|
13
28
|
mcp__hailer__create_activity({
|
|
14
29
|
workflowId: "682ac815fba468d857d498f7",
|
|
15
30
|
phaseId: "682ac815fba468d857d49904",
|
|
16
|
-
name: "New Task", //
|
|
31
|
+
name: "New Task", // non-empty string; omit entirely not allowed in single mode
|
|
17
32
|
fields: {
|
|
18
33
|
"fieldId1": "value1",
|
|
19
34
|
"fieldId2": "value2"
|
|
@@ -31,78 +46,132 @@ mcp__hailer__update_activity({
|
|
|
31
46
|
})
|
|
32
47
|
```
|
|
33
48
|
|
|
49
|
+
**Update error reporting is lossy.** `update_activity` rejections surface as `[object Object]` — the underlying server error is swallowed. When an MCP write errors opaquely, re-run the same operation via REST, or confirm via read-back (`show_activity_by_id`). `create_activity` errors ARE rich (full `{code, msg, details}` envelope).
|
|
50
|
+
|
|
51
|
+
**@hailer/mcp 1.3.x behavior change — unknown field keys now ERROR.** Passing an unknown field key in `create_activity` or `update_activity` now returns an error. On 1.2.1 the key was silently ignored.
|
|
52
|
+
|
|
34
53
|
---
|
|
35
54
|
|
|
36
|
-
##
|
|
55
|
+
## Field Value Formats
|
|
37
56
|
|
|
38
|
-
**
|
|
57
|
+
**No multi-select types exist in Hailer.**
|
|
39
58
|
|
|
40
|
-
|
|
41
|
-
// ✅ Correct
|
|
42
|
-
fields: { priority: "High", assignee: "userId", customer: "actId" }
|
|
59
|
+
### Text Fields
|
|
43
60
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
61
|
+
| Type | Format | Example |
|
|
62
|
+
|------|--------|---------|
|
|
63
|
+
| `text` | String | `"Hello world"` |
|
|
64
|
+
| `textarea` | String | `"Multi\nline\ntext"` |
|
|
65
|
+
| `textunit` | String | `"100"` |
|
|
47
66
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
67
|
+
### Number Fields
|
|
68
|
+
|
|
69
|
+
| Type | Format | Example |
|
|
70
|
+
|------|--------|---------|
|
|
71
|
+
| `numeric` | Number | `42` or `42.5` |
|
|
72
|
+
| `numericunit` | Number | `99.99` (plain number — unit is display-only) |
|
|
73
|
+
|
|
74
|
+
### Date/Time Fields
|
|
75
|
+
|
|
76
|
+
| Type | Format | Example |
|
|
77
|
+
|------|--------|---------|
|
|
78
|
+
| `date` | Unix timestamp (ms) OR ISO `YYYY-MM-DD` string via MCP (auto-converted) | `1730937600000` or `"2024-11-07"` |
|
|
79
|
+
| `datetime` | Unix timestamp (ms) OR ISO `YYYY-MM-DD` string via MCP | `1730937600000` |
|
|
80
|
+
| `daterange` | Object | `{ start: 1730937600000, end: 1731024000000 }` |
|
|
81
|
+
| `datetimerange` | Object | `{ start: 1730937600000, end: 1731024000000 }` |
|
|
82
|
+
| `time` | Unix ms since epoch (includes date) — `HH:MM` string is rejected, NOT coerced | `46800000` |
|
|
83
|
+
| `timerange` | Object | `{ start: 1765863000000, end: 1765915200000 }` |
|
|
52
84
|
|
|
53
|
-
**Time fields** also use full millisecond timestamps including date:
|
|
54
85
|
```javascript
|
|
86
|
+
// JavaScript Date to Hailer timestamp
|
|
87
|
+
const timestamp = new Date('2024-11-07').getTime(); // 1730937600000
|
|
88
|
+
|
|
89
|
+
// Time field: also Unix timestamp (includes date)
|
|
55
90
|
const today = new Date();
|
|
56
91
|
today.setUTCHours(9, 30, 0, 0);
|
|
57
|
-
|
|
92
|
+
const timeValue = today.getTime();
|
|
58
93
|
```
|
|
59
94
|
|
|
60
|
-
|
|
61
|
-
```javascript
|
|
62
|
-
fields: { period: { start: 1730937600000, end: 1731024000000 } }
|
|
63
|
-
```
|
|
95
|
+
### Selection Fields
|
|
64
96
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
fields: { customer: "692abc123def456" }
|
|
97
|
+
| Type | Format | Example |
|
|
98
|
+
|------|--------|---------|
|
|
99
|
+
| `textpredefinedoptions` | **STRING** (exact option string from field's `data[]`) | `"High"` |
|
|
69
100
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
101
|
+
**CRITICAL #1:** Value is STRING, not array!
|
|
102
|
+
**CRITICAL #2:** The string is the **exact option string** from the field's `data[]` — there is no separate generated key.
|
|
103
|
+
**CRITICAL #3 — No server-side membership check.** A non-option value or wrong-case value is stored verbatim without error, renders blank, and silently breaks downstream filters.
|
|
73
104
|
|
|
74
|
-
**textpredefinedoptions** is the exact option string, not an array:
|
|
75
105
|
```javascript
|
|
76
|
-
|
|
77
|
-
|
|
106
|
+
// ✅ Correct
|
|
107
|
+
fields: { industry: "Retail" }
|
|
78
108
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
109
|
+
// ❌ Wrong — array rejected with code 191
|
|
110
|
+
fields: { industry: ["Retail"] }
|
|
111
|
+
|
|
112
|
+
// ⚠️ Silently stores invalid value — renders blank
|
|
113
|
+
fields: { industry: "retail" } // wrong case
|
|
82
114
|
```
|
|
83
115
|
|
|
84
|
-
|
|
116
|
+
### User & Team Fields
|
|
117
|
+
|
|
118
|
+
| Type | Format | Example |
|
|
119
|
+
|------|--------|---------|
|
|
120
|
+
| `users` | **STRING** (user ID) | `"5f8a1b2c3d4e5f6a7b8c9d0e"` |
|
|
121
|
+
| `teams` | **STRING** (team ID) | `"teamId123"` |
|
|
122
|
+
|
|
123
|
+
**CRITICAL:** Value is STRING, not array! No multi-select types exist.
|
|
124
|
+
|
|
125
|
+
### Link Fields
|
|
85
126
|
|
|
86
|
-
|
|
127
|
+
| Type | Format | Example |
|
|
128
|
+
|------|--------|---------|
|
|
129
|
+
| `activitylink` | **STRING** (activity ID) | `"692abc123def456"` |
|
|
130
|
+
|
|
131
|
+
**CRITICAL:** Canonical write format is a plain STRING ID, not array or object!
|
|
132
|
+
|
|
133
|
+
**CREATE vs UPDATE asymmetry:**
|
|
134
|
+
- On **CREATE**: object format is REJECTED with code 191. Use plain string only.
|
|
135
|
+
- On **UPDATE** (single mode): `{_id, name}` or `{value: "<24hex>"}` objects are silently coerced to string ID.
|
|
136
|
+
- Arrays are NOT coerced in either mode.
|
|
137
|
+
|
|
138
|
+
**READ vs WRITE formats differ:**
|
|
139
|
+
- **READ via app-sdk / `returnFlat: true`**: `{ _id: string, name: string }` object
|
|
140
|
+
- **READ via MCP `show_activity_by_id`**: `{ type: "activitylink", value: { _id, name } }` — unwrap `.value`
|
|
141
|
+
- **WRITE**: plain string ID only
|
|
142
|
+
|
|
143
|
+
**ActivityLink Multi-Value Inconsistency:** The same ActivityLink field on different activities can return different shapes (`{_id, name}` object vs array). Use a defensive helper:
|
|
144
|
+
|
|
145
|
+
```js
|
|
146
|
+
function matchesActivityLink(value, targetId) {
|
|
147
|
+
if (Array.isArray(value)) return value.some(x => x?._id === targetId);
|
|
148
|
+
return value?._id === targetId;
|
|
149
|
+
}
|
|
150
|
+
```
|
|
87
151
|
|
|
88
|
-
|
|
152
|
+
### Modified Fields (Checkbox & File)
|
|
89
153
|
|
|
90
|
-
| Config |
|
|
91
|
-
|
|
92
|
-
| `numeric` + `modifier.checkbox: true` | `1` (true) or `0` (false) |
|
|
93
|
-
| `text` + `modifier.file: true` | File reference (UI handles
|
|
154
|
+
| Config | Value Format | Example |
|
|
155
|
+
|--------|--------------|---------|
|
|
156
|
+
| `numeric` + `modifier.checkbox: true` | Number | `1` (true) or `0` (false) |
|
|
157
|
+
| `text` + `modifier.file: true` | Object | File reference (UI handles) |
|
|
94
158
|
|
|
95
159
|
---
|
|
96
160
|
|
|
97
|
-
##
|
|
161
|
+
## Complete Create Example
|
|
98
162
|
|
|
99
163
|
```javascript
|
|
100
|
-
|
|
101
|
-
|
|
164
|
+
mcp__hailer__create_activity({
|
|
165
|
+
workflowId: "682ac815fba468d857d498f7",
|
|
166
|
+
phaseId: "682ac815fba468d857d49904",
|
|
167
|
+
name: "Fix login bug",
|
|
102
168
|
fields: {
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
169
|
+
"description_abc": "Users cannot log in after password reset",
|
|
170
|
+
"priority_def": "High", // Dropdown — exact string from data[]
|
|
171
|
+
"assignee_jkl": "5f8a1b2c3d4e5f6a7b8c9d0e", // User — string ID
|
|
172
|
+
"project_mno": "692abc123def456", // Activity link — string ID
|
|
173
|
+
"due_date_pqr": 1730937600000, // Date — ms timestamp
|
|
174
|
+
"estimated_hours_stu": 4 // Number
|
|
106
175
|
}
|
|
107
176
|
})
|
|
108
177
|
```
|
|
@@ -111,7 +180,7 @@ mcp__hailer__update_activity({
|
|
|
111
180
|
|
|
112
181
|
## Bulk Creation
|
|
113
182
|
|
|
114
|
-
Use the `activities[]` array for bulk creation. **
|
|
183
|
+
Use the `activities[]` array for bulk creation. **In MCP `create_activity` bulk mode, each activity must include its own `teamId` and `phaseId`** in the `activities[]` array.
|
|
115
184
|
|
|
116
185
|
```javascript
|
|
117
186
|
mcp__hailer__create_activity({
|
|
@@ -139,87 +208,168 @@ mcp__hailer__create_activity({
|
|
|
139
208
|
|-----------|-------------|-----------|
|
|
140
209
|
| `name` | Top-level | Inside each `activities[]` item |
|
|
141
210
|
| `phaseId` | Top-level | Inside each `activities[]` item |
|
|
142
|
-
| `teamId` | Top-level |
|
|
211
|
+
| `teamId` | Top-level | Inside each `activities[]` item |
|
|
143
212
|
| `fields` | Top-level | Inside each `activities[]` item |
|
|
144
213
|
| `workflowId` | Top-level | Top-level (shared) |
|
|
145
214
|
|
|
146
|
-
**CRITICAL
|
|
215
|
+
**CRITICAL (MCP bulk mode):** `teamId` at the top level is **IGNORED**. MCP backfills the workflow default team when `teamId` is absent — code 127 "Missing team(s)" only fires if no default team exists at all. Supply `teamId` explicitly per activity to avoid relying on fallback.
|
|
147
216
|
|
|
148
|
-
### Team ID Fallback Chain
|
|
217
|
+
### Team ID Fallback Chain (MCP)
|
|
149
218
|
|
|
150
|
-
|
|
151
|
-
1. Per-activity `teamId` (if provided) — **always provide this**
|
|
219
|
+
1. Per-activity `teamId` (if provided) — always provide this
|
|
152
220
|
2. Workflow's default team (`workflow.team`)
|
|
153
|
-
3. First workspace team (last resort, may be wrong
|
|
221
|
+
3. First workspace team (last resort, may be wrong)
|
|
222
|
+
|
|
223
|
+
---
|
|
154
224
|
|
|
155
|
-
|
|
225
|
+
## Getting IDs
|
|
156
226
|
|
|
157
|
-
|
|
227
|
+
- **Workflow ID** — from `describe_workflows` (real MongoDB ObjectId)
|
|
228
|
+
- **Phase ID** — from `describe_workflows` with `workflowId` + `include:['phases']`
|
|
229
|
+
- **Field IDs** — from `describe_workflows` with `workflowId` + `include:['schema']`
|
|
230
|
+
- **User IDs** — from `search_workspace_users`
|
|
231
|
+
- **Activity IDs** — from `list_activities` or previous create results
|
|
158
232
|
|
|
159
|
-
**
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
233
|
+
**`workflowId` and `phaseId` need real ObjectIds** (24-char hex strings), not enum key names.
|
|
234
|
+
|
|
235
|
+
**Field keys vs field IDs vs enum keys:**
|
|
236
|
+
- Field key: `firstName` — the `key` property set on the field in Hailer
|
|
237
|
+
- Field ID: `68cbfec59b3869137fe2af84` — MongoDB ObjectId
|
|
238
|
+
- Enum key: `etunimi_f84` — TypeScript constant (NOT accepted by API)
|
|
239
|
+
|
|
240
|
+
Both field keys and field IDs work in `create_activity`/`update_activity`. Enum key names don't.
|
|
163
241
|
|
|
164
242
|
---
|
|
165
243
|
|
|
166
|
-
##
|
|
244
|
+
## Clearing Field Values
|
|
167
245
|
|
|
168
|
-
|
|
246
|
+
To clear a field, use `null`. **Do not use `""` for text or textarea fields** — empty string is rejected with code 191.
|
|
169
247
|
|
|
170
248
|
```javascript
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
249
|
+
mcp__hailer__update_activity({
|
|
250
|
+
activityId: "692abc123def456",
|
|
251
|
+
fields: {
|
|
252
|
+
"assignee_abc": null, // Clear user
|
|
253
|
+
"due_date_def": null, // Clear date
|
|
254
|
+
"notes_ghi": null // Clear text — NOT ""
|
|
255
|
+
}
|
|
175
256
|
})
|
|
257
|
+
```
|
|
176
258
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Phase-Gated Field Writes on Create
|
|
262
|
+
|
|
263
|
+
`create_activity` gates field writes to the fields listed on the activity's **current phase** at creation time. If a field is defined on the workflow but not included in the initial phase's `fields[]` list, passing it in `create_activity` returns code 191 "not allowed" — even though the field exists and the value is valid.
|
|
264
|
+
|
|
265
|
+
**Fix — split create from update:**
|
|
266
|
+
|
|
267
|
+
```javascript
|
|
268
|
+
// Step 1: create with ONLY fields present on the initial phase
|
|
269
|
+
const activity = await mcp__hailer__create_activity({
|
|
270
|
+
workflowId: "...",
|
|
271
|
+
phaseId: initialPhaseId,
|
|
272
|
+
name: "Task name",
|
|
273
|
+
fields: { "field_on_initial_phase": value }
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// Step 2: update the rest immediately after
|
|
277
|
+
await mcp__hailer__update_activity({
|
|
278
|
+
activityId: activity.created_ids[0],
|
|
279
|
+
fields: { "due_date_field": dueDateTimestamp }
|
|
280
|
+
});
|
|
182
281
|
```
|
|
183
282
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## createMany / updateMany Cross-Cutting Patterns
|
|
286
|
+
|
|
287
|
+
### All-or-Nothing Batch Behavior
|
|
288
|
+
|
|
289
|
+
`v3.activity.createMany` and `v3.activity.updateMany` are **all-or-nothing per batch**. A single rejected row fails the entire batch.
|
|
290
|
+
|
|
291
|
+
Mitigations:
|
|
292
|
+
1. Isolate phase/status mixing into separate batches
|
|
293
|
+
2. Pre-validate phase transitions against `possibleNextPhase` — terminal phases (`possibleNextPhase: []`) block reverse moves
|
|
294
|
+
3. Bisect on failure to find the offender
|
|
295
|
+
|
|
296
|
+
```javascript
|
|
297
|
+
async function batchWithRetry(workflowId, rows, options) {
|
|
298
|
+
if (rows.length === 0) return;
|
|
299
|
+
try {
|
|
300
|
+
await v3.activity.createMany(workflowId, rows, options);
|
|
301
|
+
} catch (err) {
|
|
302
|
+
if (rows.length === 1) throw err;
|
|
303
|
+
const mid = Math.floor(rows.length / 2);
|
|
304
|
+
await batchWithRetry(workflowId, rows.slice(0, mid), options);
|
|
305
|
+
await batchWithRetry(workflowId, rows.slice(mid), options);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
```
|
|
187
309
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
-
|
|
191
|
-
-
|
|
310
|
+
### Batch Size
|
|
311
|
+
|
|
312
|
+
- createMany: 50 per batch
|
|
313
|
+
- updateMany: 100 per batch
|
|
314
|
+
|
|
315
|
+
### Empty String Rejected Even When `required: false` (Code 191)
|
|
316
|
+
|
|
317
|
+
Passing `""` for a text or textarea field always triggers code 191, regardless of `required`. Omit keys with no value instead.
|
|
318
|
+
|
|
319
|
+
```javascript
|
|
320
|
+
function cleanFields(raw) {
|
|
321
|
+
return Object.fromEntries(
|
|
322
|
+
Object.entries(raw).filter(([, v]) => v !== null && v !== undefined && v !== "")
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### `updateMany` row shape
|
|
328
|
+
|
|
329
|
+
Row shape is `{_id, fields?, phaseId?}` only — no `processId`. `processId` in the per-row object causes error 191.
|
|
192
330
|
|
|
193
331
|
---
|
|
194
332
|
|
|
195
|
-
##
|
|
333
|
+
## Deleting Activities — Use `activities.remove`
|
|
196
334
|
|
|
197
335
|
```javascript
|
|
198
|
-
//
|
|
199
|
-
|
|
336
|
+
// Hard delete
|
|
337
|
+
await client.request('activities.remove', [activityId]);
|
|
200
338
|
|
|
201
|
-
//
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
339
|
+
// Bulk delete
|
|
340
|
+
await client.request('activities.remove', [[id1, id2, id3]]);
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
`v3.activity.delete` / `v3.activity.deleteMany` / `v3.activity.removeMany` → **code 7 "Method not found"**.
|
|
344
|
+
|
|
345
|
+
**Soft-archive alternative:** Move to a terminal phase via `updateMany`.
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## Create Response Shape (MCP vs REST)
|
|
350
|
+
|
|
351
|
+
MCP `create_activity` returns `{ created_ids: [...] }` in BOTH single and bulk mode.
|
|
352
|
+
|
|
353
|
+
```javascript
|
|
354
|
+
// ✅ Correct success check
|
|
355
|
+
const result = await mcp__hailer__create_activity({ workflowId, activities: [...] });
|
|
356
|
+
const ids = result.created_ids ?? [];
|
|
357
|
+
|
|
358
|
+
// ❌ Wrong — single _id check misses bulk responses
|
|
359
|
+
if (!result._id) throw new Error('create failed');
|
|
207
360
|
```
|
|
208
361
|
|
|
209
362
|
---
|
|
210
363
|
|
|
211
|
-
##
|
|
364
|
+
## Filtering Reads: `text_search` Does NOT Match ActivityLink Fields
|
|
365
|
+
|
|
366
|
+
`list_activities` `filters` with `operator: "text_search"` matches **text fields only**. On an `activitylink` field it returns 0 rows. Use `equals` + the linked activity's `_id` instead.
|
|
367
|
+
|
|
368
|
+
---
|
|
212
369
|
|
|
213
|
-
|
|
370
|
+
## Editable Function Fields
|
|
214
371
|
|
|
215
|
-
|
|
216
|
-
- [ ] Have phase ID (from enum or API)
|
|
217
|
-
- [ ] Have field IDs (from enum or API)
|
|
218
|
-
- [ ] Have **team ID** (from teams.ts or API) — don't rely on fallbacks
|
|
219
|
-
- [ ] All values are **STRINGS** for select/user/link fields (no multi-select types exist)
|
|
220
|
-
- [ ] Dates are **Unix timestamps in milliseconds**
|
|
221
|
-
- [ ] Times are **Unix timestamps in milliseconds** (like dates)
|
|
222
|
-
- [ ] **Bulk mode:** teamId, phaseId, fields are **inside each activity object**, not top-level
|
|
372
|
+
A function field with `editable: true` is a valid write target — pass a value the same way as any other field. A stored value wins over the computed default; the function only back-fills while the field is empty.
|
|
223
373
|
|
|
224
374
|
---
|
|
225
375
|
|
|
@@ -227,12 +377,14 @@ Before creating/updating activities:
|
|
|
227
377
|
|
|
228
378
|
| Wrong | Right |
|
|
229
379
|
|-------|-------|
|
|
230
|
-
| `{ priority: ["High"] }` | `{ priority: "High" }`
|
|
231
|
-
| `{ assignee: ["userId"] }` | `{ assignee: "userId" }`
|
|
232
|
-
| `{ customer: ["actId"] }` | `{ customer: "actId" }`
|
|
233
|
-
| `{ date: "2024-11-07" }` | `{ date: 1730937600000 }`
|
|
234
|
-
| `{ time: "09:00" }` | `{ time:
|
|
380
|
+
| `{ priority: ["High"] }` | `{ priority: "High" }` — string for single select |
|
|
381
|
+
| `{ assignee: ["userId"] }` | `{ assignee: "userId" }` — string for single user |
|
|
382
|
+
| `{ customer: ["actId"] }` | `{ customer: "actId" }` — string for activitylink |
|
|
383
|
+
| `{ date: "2024-11-07" }` via raw REST/app-sdk | `{ date: 1730937600000 }` — ms timestamp |
|
|
384
|
+
| `{ time: "09:00" }` | `{ time: 46800000 }` — `HH:MM` string is rejected |
|
|
235
385
|
| `{ daterange: 1730937600000 }` | `{ daterange: { start: ..., end: ... } }` |
|
|
236
|
-
| `{ textField: 300 }` | `{ textField: "300" }`
|
|
237
|
-
|
|
|
386
|
+
| `{ textField: 300 }` | `{ textField: "300" }` — text fields must be STRINGS |
|
|
387
|
+
| Comparing activitylink by value | Compare `value._id` — READ format is `{_id, name}` object |
|
|
238
388
|
| Bulk create with top-level teamId | Put teamId INSIDE each activity object |
|
|
389
|
+
| `name: ""` | Non-empty string or omit; empty string rejected |
|
|
390
|
+
| `data: [{value: "A", label: "A"}]` in field creation | `data: ["A", "B"]` — plain string array only |
|