Repository navigation
Commit f4db5f3
<!-- claude-projects-attribution: {"github_login":"os-zhuang"} -->
_Requested by **Sam** · [project
thread](https://claude.ai/code/project/chan_01GsBogozeZG5sdpAARrN7HP?thread=cmsg_01GsBogozeZG5sdpAARrN7HPQri8yJvYhNpGXfbb2mtPTE)_
Part of #30043. The card carries no `Claim:` naming this branch, so the
closing-target guard would refuse a closing keyword here; the
dispatching seat closes the card after this lands (or posts the claim
and switches this line to a closing keyword).
## What changes
The console's AI chat request (`POST /api/v1/ai/agents/:agent/chat`) now
names the app the user has selected and the page in view, on every chat
surface, the floating dock included.
- `ui/packages/app-shell/src/console/ai/AiChatPage.tsx` (`ChatPane`):
`context.activeApp` is no longer the constant `'AI'`. It is the selected
app's package: the new `activeAppPackageId` prop (the console app the
dock sits in), else `editPackageId` (the app picked for editing on
`/ai/build?package=…`, the Studio copilot's package). With neither, the
key is omitted. The `surfaceContext` prop takes the shared
`ChatSurfaceContext` type, which adds cloud#1610's existing `selection`
key to the client side.
- `ui/packages/app-shell/src/layout/chatSurfaceContext.ts` (new):
`consoleChatSurface(pathname, hostPaths)` reads the page in view off the
URL into cloud#1610's shape. It matches against
`APP_ROUTE_METADATA_READS`, the route table `AppContent` is held equal
to, ranked the way `<Routes>` ranks it, so a static route (`search`,
`reports`) or a host route never reads as an object.
- object list, data, new: `{ artifact: { type: 'object', name } }`; a
saved list view adds `selection: { kind: 'view', id }`
- record page (and its edit form and named record pages): `{ artifact: {
type: 'object', name }, selection: { kind: 'record', id } }`
- dashboard, report, page: `{ artifact: { type, name } }`; the dashboard
designer adds `mode: 'design'`
- anything else (app landing, search, component pages, metadata admin,
redirects): no `surface`
- `ui/packages/app-shell/src/layout/ConsoleLayout.tsx`: computes the
dock's surface from `location.pathname` each render and hands it, with
the object's translated label for the "discussing" chip, to both dock
presentations (desktop rail and phone sheet). `AppContent` passes its
host route paths in.
- `ChatDock.tsx` / `ChatDockConversation.tsx`: forward `surfaceContext`
/ `surfaceArtifactLabel`, and pass the console app's package as
`activeAppPackageId` whichever agent the dock runs.
- `useObjectChat.transportIdentity.test.tsx`: the fixture no longer
models `activeApp: 'AI'`.
- Changeset: `@object-ui/app-shell` patch.
## Why
objectstack#30043 (the objectstack half of cloud#3564). Measured on
staging by scripting the dock's own body on an object list page:
`{activeApp:"AI", agentName:"build", autoPublishAiBuilds:true,
packageId:"app.jpph"}`. The agent was told the user was in "AI" and was
never told the page or record, so "who owns this record?" was answered
with "please give me the record's name", and "how do I use this app?"
was answered for another app. How the agent uses these fields is
cloud#3564's half.
The value is read per send: `useObjectChat` reads `body` through a ref
at send time (pinned since objectui#4187), and the dock's pane is keyed
on the conversation, not on the page, so navigating with the dock open
moves the next turn's context without remounting the pane.
## Verification
- `ChatDock.requestContext-30043.test.tsx` (new): real `ConsoleLayout`,
real dock (rail and sheet), real lazy `ChatDockConversation`, real
`ChatPane`, real `useObjectChat`; the pinned value is the JSON body of
the POST captured off a `fetch` stub. Six cases: record page (activeApp
+ object + record id), object list page (the measured page), build agent
bound to the app (activeApp = packageId), navigation with the dock open
(list → record → search drops the key), no app selected (no `activeApp`,
no `'AI'` anywhere), phone sheet. With `AiChatPage.tsx` and
`ChatDockConversation.tsx` put back to `origin/main` on the committed
tree, all 6 fail (`expected 'AI' to be 'app.crm'`, `expected undefined
to deeply equal { artifact … }`, `expected { activeApp: 'AI', … } to not
have property "activeApp"`); restored, all 6 pass.
- `chatSurfaceContext-30043.test.ts` (new): 8 cases over the route
mapping, including decoded segments, static and host routes that shadow
`:objectName`, and a guard that every mapped path is a route the console
declares.
- Local, on `1aa3407a`: the two new files plus every
`layout/__tests__/ChatDock*`, `layout/__tests__/ConsoleLayout*`,
`console/ai/__tests__/*` and the Studio copilot tests: 65 files, 368
tests, all pass. `useObjectChat.transportIdentity.test.tsx`: 5/5.
- `@object-ui/app-shell` `type-check`: NOT MEASURED locally (this
container has not built the `@object-ui/*` declaration closure, so tsc
reports unresolved modules across the whole package, untouched files
included). `Type Check UI` in CI is the reading.
Clause-②: no
Metadata-protocol: none
## Acceptance notes
- **Record name is not sent.** The card's Do 2 lists "object + record id
+ record name" and also says "use the existing cloud#1610 shape; do not
add a second one". That shape (`ToolExecutionContext.surfaceContext` in
the spec, and cloud's `sanitizeSurfaceContext`) has no slot for a
record's display name: `selection` is `{ kind, id }`, and cloud drops
any other key. A name also has no race-free source in the dock today:
the record page publishes its title to `NavigationContext` unkeyed, and
`RecordDetailView` keeps the previous record's title until the next
record loads, so a name read there could pair record B's id with record
A's name. This PR sends the object and the record id, which the agent's
`get_record` resolves. Carrying the name needs a spec key (e.g.
`selection.label`) plus an id-keyed title channel; that is the seat's
call.
- **activeApp is a package id**, as the card asks ("the same value as
`packageId`"). The cloud agent route does not read `context.activeApp`
today (only `appName`, `objectName`, `recordId`, `viewName`, `surface`,
`packageId`), so this half changes what is sent; reading it is
cloud#3564's half.
- **Console apps without a `_packageId`** send no `activeApp` (pinned):
nothing is selected by package, and no placeholder is sent.
- The dock's "discussing" chip now appears on object, record, dashboard,
report and page routes. On object pages it reads the object's translated
label; on dashboards, reports and pages it reads `type · name` (the dock
does not load those buckets just to label a chip).
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(`packages/spec` not touched.)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes. (CI `Lint UI` is the
reading.)
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: n/a.
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English.
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01QUs3Brx8PodVWZfV1nTSCh
---
_Generated by [Claude
Code](https://claude.ai/code/session_01QUs3Brx8PodVWZfV1nTSCh)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: objectstack-fleet[bot] <332303061+objectstack-fleet[bot]@users.noreply.github.com>
1 parent f68b47c commit f4db5f3
10 files changed
Lines changed: 562 additions & 10 deletions
File tree
- .changeset
- ui/packages
- app-shell/src
- console
- ai
- layout
- __tests__
- plugin-chatbot/src/__tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1534 | 1534 | | |
1535 | 1535 | | |
1536 | 1536 | | |
| 1537 | + | |
1537 | 1538 | | |
1538 | 1539 | | |
1539 | 1540 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
109 | 109 | | |
110 | 110 | | |
111 | 111 | | |
| 112 | + | |
112 | 113 | | |
113 | 114 | | |
114 | 115 | | |
| |||
1572 | 1573 | | |
1573 | 1574 | | |
1574 | 1575 | | |
1575 | | - | |
1576 | | - | |
1577 | | - | |
1578 | | - | |
1579 | | - | |
| 1576 | + | |
1580 | 1577 | | |
1581 | 1578 | | |
1582 | 1579 | | |
| |||
1597 | 1594 | | |
1598 | 1595 | | |
1599 | 1596 | | |
| 1597 | + | |
| 1598 | + | |
| 1599 | + | |
| 1600 | + | |
| 1601 | + | |
| 1602 | + | |
| 1603 | + | |
1600 | 1604 | | |
1601 | 1605 | | |
1602 | 1606 | | |
| |||
1636 | 1640 | | |
1637 | 1641 | | |
1638 | 1642 | | |
| 1643 | + | |
1639 | 1644 | | |
1640 | 1645 | | |
1641 | 1646 | | |
| |||
1869 | 1874 | | |
1870 | 1875 | | |
1871 | 1876 | | |
| 1877 | + | |
1872 | 1878 | | |
1873 | 1879 | | |
1874 | 1880 | | |
| |||
1892 | 1898 | | |
1893 | 1899 | | |
1894 | 1900 | | |
1895 | | - | |
| 1901 | + | |
| 1902 | + | |
| 1903 | + | |
| 1904 | + | |
1896 | 1905 | | |
1897 | 1906 | | |
1898 | 1907 | | |
| |||
1902 | 1911 | | |
1903 | 1912 | | |
1904 | 1913 | | |
| 1914 | + | |
1905 | 1915 | | |
1906 | 1916 | | |
1907 | 1917 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
| 35 | + | |
35 | 36 | | |
36 | 37 | | |
37 | 38 | | |
| |||
248 | 249 | | |
249 | 250 | | |
250 | 251 | | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
251 | 260 | | |
252 | 261 | | |
253 | 262 | | |
| |||
281 | 290 | | |
282 | 291 | | |
283 | 292 | | |
| 293 | + | |
| 294 | + | |
284 | 295 | | |
285 | 296 | | |
286 | 297 | | |
| |||
367 | 378 | | |
368 | 379 | | |
369 | 380 | | |
| 381 | + | |
| 382 | + | |
370 | 383 | | |
371 | 384 | | |
372 | 385 | | |
| |||
388 | 401 | | |
389 | 402 | | |
390 | 403 | | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
391 | 408 | | |
392 | 409 | | |
393 | 410 | | |
| |||
427 | 444 | | |
428 | 445 | | |
429 | 446 | | |
| 447 | + | |
| 448 | + | |
430 | 449 | | |
431 | 450 | | |
432 | 451 | | |
| |||
491 | 510 | | |
492 | 511 | | |
493 | 512 | | |
| 513 | + | |
| 514 | + | |
494 | 515 | | |
495 | 516 | | |
496 | 517 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
| 25 | + | |
25 | 26 | | |
26 | 27 | | |
27 | 28 | | |
| |||
38 | 39 | | |
39 | 40 | | |
40 | 41 | | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
41 | 51 | | |
42 | 52 | | |
43 | 53 | | |
| |||
74 | 84 | | |
75 | 85 | | |
76 | 86 | | |
| 87 | + | |
| 88 | + | |
77 | 89 | | |
78 | 90 | | |
79 | 91 | | |
| |||
143 | 155 | | |
144 | 156 | | |
145 | 157 | | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
146 | 163 | | |
147 | 164 | | |
148 | 165 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | | - | |
| 11 | + | |
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
15 | | - | |
| 15 | + | |
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
| 22 | + | |
22 | 23 | | |
23 | 24 | | |
24 | 25 | | |
| |||
64 | 65 | | |
65 | 66 | | |
66 | 67 | | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
67 | 74 | | |
68 | 75 | | |
69 | 76 | | |
| |||
145 | 152 | | |
146 | 153 | | |
147 | 154 | | |
| 155 | + | |
148 | 156 | | |
149 | 157 | | |
150 | 158 | | |
| |||
187 | 195 | | |
188 | 196 | | |
189 | 197 | | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
190 | 216 | | |
191 | 217 | | |
192 | 218 | | |
| |||
272 | 298 | | |
273 | 299 | | |
274 | 300 | | |
| 301 | + | |
| 302 | + | |
275 | 303 | | |
276 | 304 | | |
277 | 305 | | |
| |||
352 | 380 | | |
353 | 381 | | |
354 | 382 | | |
| 383 | + | |
| 384 | + | |
355 | 385 | | |
356 | 386 | | |
357 | 387 | | |
| |||
0 commit comments