@llui/agent 0.0.32 → 0.0.35
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/README.md +82 -1
- package/dist/client/agentConfirm.d.ts +48 -18
- package/dist/client/agentConfirm.d.ts.map +1 -1
- package/dist/client/agentConfirm.js +28 -25
- package/dist/client/agentConfirm.js.map +1 -1
- package/dist/client/agentConnect.d.ts +95 -34
- package/dist/client/agentConnect.d.ts.map +1 -1
- package/dist/client/agentConnect.js +85 -47
- package/dist/client/agentConnect.js.map +1 -1
- package/dist/client/agentLog.d.ts +31 -14
- package/dist/client/agentLog.d.ts.map +1 -1
- package/dist/client/agentLog.js +39 -20
- package/dist/client/agentLog.js.map +1 -1
- package/dist/client/effect-handler.d.ts +23 -0
- package/dist/client/effect-handler.d.ts.map +1 -1
- package/dist/client/effect-handler.js +185 -126
- package/dist/client/effect-handler.js.map +1 -1
- package/dist/client/effects.d.ts +13 -2
- package/dist/client/effects.d.ts.map +1 -1
- package/dist/client/effects.js.map +1 -1
- package/dist/client/factory.d.ts +55 -3
- package/dist/client/factory.d.ts.map +1 -1
- package/dist/client/factory.js +30 -5
- package/dist/client/factory.js.map +1 -1
- package/dist/client/rpc/describe-visible-content.d.ts +18 -5
- package/dist/client/rpc/describe-visible-content.d.ts.map +1 -1
- package/dist/client/rpc/describe-visible-content.js +112 -7
- package/dist/client/rpc/describe-visible-content.js.map +1 -1
- package/dist/client/rpc/list-actions.d.ts +52 -2
- package/dist/client/rpc/list-actions.d.ts.map +1 -1
- package/dist/client/rpc/list-actions.js +187 -5
- package/dist/client/rpc/list-actions.js.map +1 -1
- package/dist/client/rpc/query-state.d.ts +32 -0
- package/dist/client/rpc/query-state.d.ts.map +1 -0
- package/dist/client/rpc/query-state.js +82 -0
- package/dist/client/rpc/query-state.js.map +1 -0
- package/dist/client/rpc/send-message.d.ts +2 -0
- package/dist/client/rpc/send-message.d.ts.map +1 -1
- package/dist/client/rpc/send-message.js +119 -9
- package/dist/client/rpc/send-message.js.map +1 -1
- package/dist/client/rpc/would-dispatch.d.ts +66 -0
- package/dist/client/rpc/would-dispatch.d.ts.map +1 -0
- package/dist/client/rpc/would-dispatch.js +21 -0
- package/dist/client/rpc/would-dispatch.js.map +1 -0
- package/dist/client/ws-client.d.ts +3 -1
- package/dist/client/ws-client.d.ts.map +1 -1
- package/dist/client/ws-client.js +29 -0
- package/dist/client/ws-client.js.map +1 -1
- package/dist/codecs.d.ts +107 -0
- package/dist/codecs.d.ts.map +1 -0
- package/dist/codecs.js +166 -0
- package/dist/codecs.js.map +1 -0
- package/dist/protocol.d.ts +172 -12
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +7 -1
- package/dist/protocol.js.map +1 -1
- package/dist/server/cloudflare/durable-object.d.ts +11 -4
- package/dist/server/cloudflare/durable-object.d.ts.map +1 -1
- package/dist/server/cloudflare/durable-object.js.map +1 -1
- package/dist/server/cloudflare/index.d.ts +8 -4
- package/dist/server/cloudflare/index.d.ts.map +1 -1
- package/dist/server/cloudflare/index.js +8 -4
- package/dist/server/cloudflare/index.js.map +1 -1
- package/dist/server/cloudflare/worker.d.ts +10 -2
- package/dist/server/cloudflare/worker.d.ts.map +1 -1
- package/dist/server/cloudflare/worker.js +13 -6
- package/dist/server/cloudflare/worker.js.map +1 -1
- package/dist/server/core-entry.d.ts +2 -2
- package/dist/server/core-entry.d.ts.map +1 -1
- package/dist/server/core-entry.js +1 -1
- package/dist/server/core-entry.js.map +1 -1
- package/dist/server/core.d.ts +1 -3
- package/dist/server/core.d.ts.map +1 -1
- package/dist/server/core.js +13 -12
- package/dist/server/core.js.map +1 -1
- package/dist/server/factory.d.ts +1 -1
- package/dist/server/factory.d.ts.map +1 -1
- package/dist/server/factory.js +1 -2
- package/dist/server/factory.js.map +1 -1
- package/dist/server/http/mint.d.ts +6 -1
- package/dist/server/http/mint.d.ts.map +1 -1
- package/dist/server/http/mint.js +14 -6
- package/dist/server/http/mint.js.map +1 -1
- package/dist/server/http/resume.d.ts +3 -1
- package/dist/server/http/resume.d.ts.map +1 -1
- package/dist/server/http/resume.js +9 -7
- package/dist/server/http/resume.js.map +1 -1
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/lap/confirm-result.d.ts +0 -1
- package/dist/server/lap/confirm-result.d.ts.map +1 -1
- package/dist/server/lap/confirm-result.js +1 -1
- package/dist/server/lap/confirm-result.js.map +1 -1
- package/dist/server/lap/describe.d.ts +13 -2
- package/dist/server/lap/describe.d.ts.map +1 -1
- package/dist/server/lap/describe.js +23 -6
- package/dist/server/lap/describe.js.map +1 -1
- package/dist/server/lap/forward.d.ts +13 -1
- package/dist/server/lap/forward.d.ts.map +1 -1
- package/dist/server/lap/forward.js +75 -1
- package/dist/server/lap/forward.js.map +1 -1
- package/dist/server/lap/message.d.ts +0 -1
- package/dist/server/lap/message.d.ts.map +1 -1
- package/dist/server/lap/message.js +1 -1
- package/dist/server/lap/message.js.map +1 -1
- package/dist/server/lap/observe.d.ts +0 -1
- package/dist/server/lap/observe.d.ts.map +1 -1
- package/dist/server/lap/observe.js +1 -1
- package/dist/server/lap/observe.js.map +1 -1
- package/dist/server/lap/router.d.ts.map +1 -1
- package/dist/server/lap/router.js +7 -1
- package/dist/server/lap/router.js.map +1 -1
- package/dist/server/lap/wait.d.ts +0 -1
- package/dist/server/lap/wait.d.ts.map +1 -1
- package/dist/server/lap/wait.js +1 -1
- package/dist/server/lap/wait.js.map +1 -1
- package/dist/server/options.d.ts +7 -5
- package/dist/server/options.d.ts.map +1 -1
- package/dist/server/options.js.map +1 -1
- package/dist/server/token-store.d.ts +22 -0
- package/dist/server/token-store.d.ts.map +1 -1
- package/dist/server/token-store.js +24 -0
- package/dist/server/token-store.js.map +1 -1
- package/dist/server/token.d.ts +32 -17
- package/dist/server/token.d.ts.map +1 -1
- package/dist/server/token.js +40 -103
- package/dist/server/token.js.map +1 -1
- package/dist/server/web/upgrade.d.ts +1 -1
- package/dist/server/web/upgrade.js +1 -1
- package/dist/server/web/upgrade.js.map +1 -1
- package/dist/server/ws/pairing-registry.d.ts +22 -6
- package/dist/server/ws/pairing-registry.d.ts.map +1 -1
- package/dist/server/ws/pairing-registry.js +49 -0
- package/dist/server/ws/pairing-registry.js.map +1 -1
- package/dist/server/ws/upgrade.d.ts +0 -1
- package/dist/server/ws/upgrade.d.ts.map +1 -1
- package/dist/server/ws/upgrade.js +12 -4
- package/dist/server/ws/upgrade.js.map +1 -1
- package/dist/state-diff.d.ts +52 -0
- package/dist/state-diff.d.ts.map +1 -0
- package/dist/state-diff.js +119 -0
- package/dist/state-diff.js.map +1 -0
- package/package.json +7 -3
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { tagSend } from '@llui/dom';
|
|
1
2
|
/** Component shape is [State, Effect[]] — consistent with @llui/components. */
|
|
2
3
|
export function init(_opts) {
|
|
3
4
|
return [
|
|
@@ -11,19 +12,34 @@ export function init(_opts) {
|
|
|
11
12
|
[],
|
|
12
13
|
];
|
|
13
14
|
}
|
|
14
|
-
export function update(state, msg, opts) {
|
|
15
|
+
export function update(state, msg, opts = {}) {
|
|
15
16
|
switch (msg.type) {
|
|
16
|
-
case 'Mint':
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
case 'Mint': {
|
|
18
|
+
// mintUrl: undefined means "let the effect handler derive it
|
|
19
|
+
// from agentBasePath". Only include the property when explicitly
|
|
20
|
+
// set, so the effect's discriminated shape stays clean.
|
|
21
|
+
const mintEffect = opts.mintUrl !== undefined
|
|
22
|
+
? { type: 'AgentMintRequest', mintUrl: opts.mintUrl }
|
|
23
|
+
: { type: 'AgentMintRequest' };
|
|
24
|
+
return [{ ...state, status: 'minting' }, [mintEffect]];
|
|
25
|
+
}
|
|
21
26
|
case 'MintSucceeded': {
|
|
27
|
+
// The connect snippet has to work across every Claude surface.
|
|
28
|
+
// Claude Desktop exposes MCP tools as bare names (`connect_session`),
|
|
29
|
+
// but Claude Code namespaces them (`mcp__<server>__connect_session`)
|
|
30
|
+
// and may defer-load them — so an LLM that searches its tool list for
|
|
31
|
+
// a literal `connect_session` won't find it. Naming the LLui MCP
|
|
32
|
+
// server explicitly gives the model enough to resolve the right tool
|
|
33
|
+
// on either platform.
|
|
22
34
|
const pending = {
|
|
23
35
|
token: msg.token,
|
|
24
36
|
tid: msg.tid,
|
|
25
37
|
lapUrl: msg.lapUrl,
|
|
26
|
-
connectSnippet:
|
|
38
|
+
connectSnippet: `Connect this Claude session to the LLui app. Call the LLui MCP server's ` +
|
|
39
|
+
`\`connect_session\` tool with url=${JSON.stringify(msg.lapUrl)} and ` +
|
|
40
|
+
`token=${JSON.stringify(msg.token)}. ` +
|
|
41
|
+
`(In Claude Code the tool may be namespaced as ` +
|
|
42
|
+
`\`mcp__<server>__connect_session\` and deferred — load it via tool search if needed.)`,
|
|
27
43
|
expiresAt: msg.expiresAt,
|
|
28
44
|
};
|
|
29
45
|
return [
|
|
@@ -63,52 +79,74 @@ export function update(state, msg, opts) {
|
|
|
63
79
|
return [{ ...state, sessions: msg.sessions }, []];
|
|
64
80
|
case 'RefreshSessions':
|
|
65
81
|
return [state, [{ type: 'AgentSessionsList' }]];
|
|
82
|
+
case 'CopyConnectSnippet': {
|
|
83
|
+
// No-op when there's no pending token — the button's
|
|
84
|
+
// `disabled` accessor already gates the click, but we accept
|
|
85
|
+
// the message for runtime safety.
|
|
86
|
+
if (!state.pendingToken)
|
|
87
|
+
return [state, []];
|
|
88
|
+
return [state, [{ type: 'AgentClipboardWrite', text: state.pendingToken.connectSnippet }]];
|
|
89
|
+
}
|
|
66
90
|
}
|
|
67
91
|
}
|
|
68
92
|
/**
|
|
69
|
-
* Builds prop bags for the view.
|
|
70
|
-
*
|
|
93
|
+
* Builds prop bags for the view. Static-bag-with-reactive-accessors
|
|
94
|
+
* shape (matches the @llui/components convention); spread directly
|
|
95
|
+
* into element helpers.
|
|
71
96
|
*/
|
|
72
97
|
export function connect(get, send, _opts = {}) {
|
|
73
|
-
return
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
onClick: () => {
|
|
84
|
-
if (s.pendingToken && typeof navigator !== 'undefined' && 'clipboard' in navigator) {
|
|
85
|
-
void navigator.clipboard.writeText(s.pendingToken.connectSnippet);
|
|
86
|
-
}
|
|
87
|
-
},
|
|
88
|
-
disabled: s.pendingToken === null,
|
|
89
|
-
},
|
|
90
|
-
sessionsList: { 'data-part': 'sessions-list' },
|
|
91
|
-
sessionItem: (tid) => ({ 'data-part': 'session-item', 'data-tid': tid }),
|
|
92
|
-
revokeButton: (tid) => ({ onClick: () => send({ type: 'Revoke', tid }) }),
|
|
93
|
-
resumeBanner: { 'data-part': 'resume-banner', 'data-visible': s.resumable.length > 0 },
|
|
94
|
-
resumeItem: (tid) => ({ 'data-part': 'resume-item', 'data-tid': tid }),
|
|
95
|
-
resumeButton: (tid) => ({ onClick: () => send({ type: 'Resume', tid }) }),
|
|
96
|
-
dismissButton: (tid) => ({
|
|
97
|
-
// For dismiss, we currently just remove the resumable record locally.
|
|
98
|
-
// A "dismiss forever" flag could land in a follow-up; for v1, dismiss
|
|
99
|
-
// is a client-side-only state prune by reusing the Revoke Msg path
|
|
100
|
-
// with intent-split; for now Emit Revoke which both revokes server-side
|
|
101
|
-
// AND removes locally. Alternative: emit a new DismissResume msg —
|
|
102
|
-
// spec §9.1 lists dismissButton but doesn't spell out the emitted msg.
|
|
103
|
-
// V1 pragmatic choice: same as revoke (mark revoked on server).
|
|
104
|
-
onClick: () => send({ type: 'Revoke', tid }),
|
|
105
|
-
}),
|
|
106
|
-
error: {
|
|
107
|
-
'data-part': 'error',
|
|
108
|
-
'data-visible': s.error !== null,
|
|
109
|
-
onClick: () => send({ type: 'ClearError' }),
|
|
98
|
+
return {
|
|
99
|
+
root: {
|
|
100
|
+
'data-scope': 'agent-connect',
|
|
101
|
+
'data-state': (s) => get(s).status,
|
|
102
|
+
},
|
|
103
|
+
mintTrigger: {
|
|
104
|
+
onClick: tagSend(send, ['Mint'], () => send({ type: 'Mint' })),
|
|
105
|
+
disabled: (s) => {
|
|
106
|
+
const cs = get(s);
|
|
107
|
+
return cs.status === 'minting' || cs.status === 'pending-claude' || cs.status === 'active';
|
|
110
108
|
},
|
|
111
|
-
}
|
|
109
|
+
},
|
|
110
|
+
pendingTokenBox: {
|
|
111
|
+
'data-part': 'pending-token',
|
|
112
|
+
'data-visible': (s) => get(s).pendingToken !== null,
|
|
113
|
+
},
|
|
114
|
+
copyConnectSnippetButton: {
|
|
115
|
+
// The handler reads state at click time via the Msg/effect path:
|
|
116
|
+
// CopyConnectSnippet → update() reads pendingToken.connectSnippet
|
|
117
|
+
// → effect AgentClipboardWrite writes to navigator.clipboard.
|
|
118
|
+
// Routing through update() keeps state reads out of event
|
|
119
|
+
// handlers, which is what makes the static-bag-with-reactive-
|
|
120
|
+
// accessors shape work cleanly.
|
|
121
|
+
onClick: tagSend(send, ['CopyConnectSnippet'], () => send({ type: 'CopyConnectSnippet' })),
|
|
122
|
+
disabled: (s) => get(s).pendingToken === null,
|
|
123
|
+
},
|
|
124
|
+
sessionsList: { 'data-part': 'sessions-list' },
|
|
125
|
+
sessionItem: (tid) => ({ 'data-part': 'session-item', 'data-tid': tid }),
|
|
126
|
+
revokeButton: (tid) => ({
|
|
127
|
+
onClick: tagSend(send, ['Revoke'], () => send({ type: 'Revoke', tid })),
|
|
128
|
+
}),
|
|
129
|
+
resumeBanner: {
|
|
130
|
+
'data-part': 'resume-banner',
|
|
131
|
+
'data-visible': (s) => get(s).resumable.length > 0,
|
|
132
|
+
},
|
|
133
|
+
resumeItem: (tid) => ({ 'data-part': 'resume-item', 'data-tid': tid }),
|
|
134
|
+
resumeButton: (tid) => ({
|
|
135
|
+
onClick: tagSend(send, ['Resume'], () => send({ type: 'Resume', tid })),
|
|
136
|
+
}),
|
|
137
|
+
dismissButton: (tid) => ({
|
|
138
|
+
// For dismiss, we currently just remove the resumable record
|
|
139
|
+
// locally. A "dismiss forever" flag could land in a follow-up;
|
|
140
|
+
// for v1, dismiss is a client-side-only state prune by reusing
|
|
141
|
+
// the Revoke Msg path with intent-split; for now emit Revoke
|
|
142
|
+
// which both revokes server-side AND removes locally.
|
|
143
|
+
onClick: tagSend(send, ['Revoke'], () => send({ type: 'Revoke', tid })),
|
|
144
|
+
}),
|
|
145
|
+
error: {
|
|
146
|
+
'data-part': 'error',
|
|
147
|
+
'data-visible': (s) => get(s).error !== null,
|
|
148
|
+
onClick: tagSend(send, ['ClearError'], () => send({ type: 'ClearError' })),
|
|
149
|
+
},
|
|
112
150
|
};
|
|
113
151
|
}
|
|
114
152
|
//# sourceMappingURL=agentConnect.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agentConnect.js","sourceRoot":"","sources":["../../src/client/agentConnect.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Send } from '@llui/dom'\nimport type { AgentSession, AgentToken } from '../protocol.js'\nimport type { AgentEffect } from './effects.js'\n\nexport type AgentConnectStatus = 'idle' | 'minting' | 'pending-claude' | 'active' | 'error'\n\nexport type AgentConnectPendingToken = {\n token: AgentToken\n tid: string\n lapUrl: string\n connectSnippet: string // \"/llui-connect <lapUrl> <token>\"\n expiresAt: number\n}\n\nexport type AgentConnectState = {\n status: AgentConnectStatus\n pendingToken: AgentConnectPendingToken | null\n sessions: AgentSession[]\n resumable: AgentSession[]\n error: { code: string; detail: string } | null\n}\n\nexport type AgentConnectMsg =\n | { type: 'Mint' }\n | {\n type: 'MintSucceeded'\n token: AgentToken\n tid: string\n lapUrl: string\n wsUrl: string\n expiresAt: number\n }\n | { type: 'MintFailed'; error: { code: string; detail: string } }\n | { type: 'WsOpened' }\n | { type: 'WsClosed' }\n | { type: 'ActivatedByClaude' }\n | { type: 'ResumeList'; tids: string[] }\n | { type: 'ResumeListLoaded'; sessions: AgentSession[] }\n | { type: 'Resume'; tid: string }\n | { type: 'Revoke'; tid: string }\n | { type: 'ClearError' }\n | { type: 'SessionsLoaded'; sessions: AgentSession[] }\n | { type: 'RefreshSessions' }\n\nexport type AgentConnectInitOpts = { mintUrl: string }\n\n/** Component shape is [State, Effect[]] — consistent with @llui/components. */\nexport function init(_opts: AgentConnectInitOpts): [AgentConnectState, AgentEffect[]] {\n return [\n {\n status: 'idle',\n pendingToken: null,\n sessions: [],\n resumable: [],\n error: null,\n },\n [],\n ]\n}\n\nexport function update(\n state: AgentConnectState,\n msg: AgentConnectMsg,\n opts: AgentConnectInitOpts,\n): [AgentConnectState, AgentEffect[]] {\n switch (msg.type) {\n case 'Mint':\n return [\n { ...state, status: 'minting' },\n [{ type: 'AgentMintRequest', mintUrl: opts.mintUrl }],\n ]\n case 'MintSucceeded': {\n const pending: AgentConnectPendingToken = {\n token: msg.token,\n tid: msg.tid,\n lapUrl: msg.lapUrl,\n connectSnippet: `/llui-connect ${msg.lapUrl} ${msg.token}`,\n expiresAt: msg.expiresAt,\n }\n return [\n { ...state, status: 'pending-claude', pendingToken: pending, error: null },\n [{ type: 'AgentOpenWS', token: msg.token, wsUrl: msg.wsUrl }],\n ]\n }\n case 'MintFailed':\n return [{ ...state, status: 'error', error: msg.error }, []]\n case 'WsOpened':\n // WS is open but Claude hasn't bound yet; stay at pending-claude.\n return [state, []]\n case 'WsClosed':\n return [{ ...state, status: 'idle', pendingToken: null }, []]\n case 'ActivatedByClaude':\n return [{ ...state, status: 'active' }, []]\n case 'ResumeList':\n return [state, [{ type: 'AgentResumeCheck', tids: msg.tids }]]\n case 'ResumeListLoaded':\n return [{ ...state, resumable: msg.sessions }, []]\n case 'Resume':\n return [state, [{ type: 'AgentResumeClaim', tid: msg.tid }]]\n case 'Revoke': {\n // Optimistically remove from sessions + resumable.\n return [\n {\n ...state,\n sessions: state.sessions.filter((s) => s.tid !== msg.tid),\n resumable: state.resumable.filter((s) => s.tid !== msg.tid),\n },\n [{ type: 'AgentRevoke', tid: msg.tid }],\n ]\n }\n case 'ClearError':\n return [{ ...state, error: null }, []]\n case 'SessionsLoaded':\n return [{ ...state, sessions: msg.sessions }, []]\n case 'RefreshSessions':\n return [state, [{ type: 'AgentSessionsList' }]]\n }\n}\n\n// ── Connect helper ────────────────────────────────────────────────────────────\n\nexport type AgentConnectConnectOptions = {\n id?: string // optional DOM id prefix\n}\n\ntype ConnectBag = {\n root: { 'data-scope': string; 'data-state': string }\n mintTrigger: { onClick: () => void; disabled: boolean }\n pendingTokenBox: { 'data-part': string; 'data-visible': boolean }\n copyConnectSnippetButton: { onClick: () => void; disabled: boolean }\n sessionsList: { 'data-part': string }\n sessionItem: (tid: string) => { 'data-part': string; 'data-tid': string }\n revokeButton: (tid: string) => { onClick: () => void }\n resumeBanner: { 'data-part': string; 'data-visible': boolean }\n resumeItem: (tid: string) => { 'data-part': string; 'data-tid': string }\n resumeButton: (tid: string) => { onClick: () => void }\n dismissButton: (tid: string) => { onClick: () => void }\n error: { 'data-part': string; 'data-visible': boolean; onClick: () => void }\n}\n\n/**\n * Builds prop bags for the view. See spec §9.1 and the @llui/components\n * dialog.ts pattern.\n */\nexport function connect<S>(\n get: (s: S) => AgentConnectState,\n send: Send<AgentConnectMsg>,\n _opts: AgentConnectConnectOptions = {},\n): (state: S) => ConnectBag {\n return (state) => {\n const s = get(state)\n return {\n root: { 'data-scope': 'agent-connect', 'data-state': s.status },\n mintTrigger: {\n onClick: () => send({ type: 'Mint' }),\n disabled: s.status === 'minting' || s.status === 'pending-claude' || s.status === 'active',\n },\n pendingTokenBox: { 'data-part': 'pending-token', 'data-visible': s.pendingToken !== null },\n copyConnectSnippetButton: {\n onClick: () => {\n if (s.pendingToken && typeof navigator !== 'undefined' && 'clipboard' in navigator) {\n void navigator.clipboard.writeText(s.pendingToken.connectSnippet)\n }\n },\n disabled: s.pendingToken === null,\n },\n sessionsList: { 'data-part': 'sessions-list' },\n sessionItem: (tid) => ({ 'data-part': 'session-item', 'data-tid': tid }),\n revokeButton: (tid) => ({ onClick: () => send({ type: 'Revoke', tid }) }),\n resumeBanner: { 'data-part': 'resume-banner', 'data-visible': s.resumable.length > 0 },\n resumeItem: (tid) => ({ 'data-part': 'resume-item', 'data-tid': tid }),\n resumeButton: (tid) => ({ onClick: () => send({ type: 'Resume', tid }) }),\n dismissButton: (tid) => ({\n // For dismiss, we currently just remove the resumable record locally.\n // A \"dismiss forever\" flag could land in a follow-up; for v1, dismiss\n // is a client-side-only state prune by reusing the Revoke Msg path\n // with intent-split; for now Emit Revoke which both revokes server-side\n // AND removes locally. Alternative: emit a new DismissResume msg —\n // spec §9.1 lists dismissButton but doesn't spell out the emitted msg.\n // V1 pragmatic choice: same as revoke (mark revoked on server).\n onClick: () => send({ type: 'Revoke', tid }),\n }),\n error: {\n 'data-part': 'error',\n 'data-visible': s.error !== null,\n onClick: () => send({ type: 'ClearError' }),\n },\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"agentConnect.js","sourceRoot":"","sources":["../../src/client/agentConnect.ts"],"names":[],"mappings":"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","sourcesContent":["import { tagSend, type Send } from '@llui/dom'\nimport type { AgentSession, AgentToken } from '../protocol.js'\nimport type { AgentEffect } from './effects.js'\n\nexport type AgentConnectStatus = 'idle' | 'minting' | 'pending-claude' | 'active' | 'error'\n\nexport type AgentConnectPendingToken = {\n token: AgentToken\n tid: string\n lapUrl: string\n /**\n * Natural-language connect instruction the user copies into Claude.\n * Includes URL, token, and the explicit `connect_session` tool\n * call. Works in any Claude client (Desktop, CC CLI, etc.) — the\n * Desktop-specific `/llui-connect` slash command is sugar over the\n * same tool call.\n */\n connectSnippet: string\n expiresAt: number\n}\n\nexport type AgentConnectState = {\n status: AgentConnectStatus\n pendingToken: AgentConnectPendingToken | null\n sessions: AgentSession[]\n resumable: AgentSession[]\n error: { code: string; detail: string } | null\n}\n\nexport type AgentConnectMsg =\n /** @intent(\"Mint a new agent token and open the pairing WebSocket\") */\n | { type: 'Mint' }\n /**\n * @humanOnly — internal: dispatched by the AgentMintRequest effect\n * handler when the mint endpoint replies success. Carries the token\n * and connection URLs into state.\n */\n | {\n type: 'MintSucceeded'\n token: AgentToken\n tid: string\n lapUrl: string\n wsUrl: string\n expiresAt: number\n }\n /** @humanOnly — internal: dispatched by the AgentMintRequest handler on failure. */\n | { type: 'MintFailed'; error: { code: string; detail: string } }\n /** @humanOnly — internal: WS adapter signalled the pairing socket is open. */\n | { type: 'WsOpened' }\n /** @humanOnly — internal: WS adapter signalled the pairing socket is closed. */\n | { type: 'WsClosed' }\n /** @humanOnly — internal: Claude bound the session via /agent/claim. */\n | { type: 'ActivatedByClaude' }\n /** @intent(\"Check which previously-issued agent sessions can be resumed\") */\n | { type: 'ResumeList'; tids: string[] }\n /** @humanOnly — internal: AgentResumeCheck effect handler returned the list. */\n | { type: 'ResumeListLoaded'; sessions: AgentSession[] }\n /** @intent(\"Resume an existing agent session by tid\") */\n | { type: 'Resume'; tid: string }\n /** @intent(\"Revoke an agent session by tid\") */\n | { type: 'Revoke'; tid: string }\n /** @intent(\"Dismiss the current agent connect error\") */\n | { type: 'ClearError' }\n /** @humanOnly — internal: AgentSessionsList effect handler returned the list. */\n | { type: 'SessionsLoaded'; sessions: AgentSession[] }\n /** @intent(\"Refresh the list of active agent sessions\") */\n | { type: 'RefreshSessions' }\n /**\n * @intent(\"Copy the agent connect snippet to the clipboard\")\n * Resolves the pendingToken's snippet in update() (state-reading is\n * what update() is for) and dispatches a clipboard-write effect.\n */\n | { type: 'CopyConnectSnippet' }\n\n/**\n * Options threaded through `init()` and `update()`. `mintUrl` is\n * optional — when omitted the agent effect handler derives it from\n * `EffectHandlerHost.agentBasePath` (default `/agent` → `/agent/mint`).\n * Set explicitly only when the mint endpoint lives outside the\n * configured base path.\n */\nexport type AgentConnectInitOpts = { mintUrl?: string }\n\n/** Component shape is [State, Effect[]] — consistent with @llui/components. */\nexport function init(_opts: AgentConnectInitOpts): [AgentConnectState, AgentEffect[]] {\n return [\n {\n status: 'idle',\n pendingToken: null,\n sessions: [],\n resumable: [],\n error: null,\n },\n [],\n ]\n}\n\nexport function update(\n state: AgentConnectState,\n msg: AgentConnectMsg,\n opts: AgentConnectInitOpts = {},\n): [AgentConnectState, AgentEffect[]] {\n switch (msg.type) {\n case 'Mint': {\n // mintUrl: undefined means \"let the effect handler derive it\n // from agentBasePath\". Only include the property when explicitly\n // set, so the effect's discriminated shape stays clean.\n const mintEffect: AgentEffect =\n opts.mintUrl !== undefined\n ? { type: 'AgentMintRequest', mintUrl: opts.mintUrl }\n : { type: 'AgentMintRequest' }\n return [{ ...state, status: 'minting' }, [mintEffect]]\n }\n case 'MintSucceeded': {\n // The connect snippet has to work across every Claude surface.\n // Claude Desktop exposes MCP tools as bare names (`connect_session`),\n // but Claude Code namespaces them (`mcp__<server>__connect_session`)\n // and may defer-load them — so an LLM that searches its tool list for\n // a literal `connect_session` won't find it. Naming the LLui MCP\n // server explicitly gives the model enough to resolve the right tool\n // on either platform.\n const pending: AgentConnectPendingToken = {\n token: msg.token,\n tid: msg.tid,\n lapUrl: msg.lapUrl,\n connectSnippet:\n `Connect this Claude session to the LLui app. Call the LLui MCP server's ` +\n `\\`connect_session\\` tool with url=${JSON.stringify(msg.lapUrl)} and ` +\n `token=${JSON.stringify(msg.token)}. ` +\n `(In Claude Code the tool may be namespaced as ` +\n `\\`mcp__<server>__connect_session\\` and deferred — load it via tool search if needed.)`,\n expiresAt: msg.expiresAt,\n }\n return [\n { ...state, status: 'pending-claude', pendingToken: pending, error: null },\n [{ type: 'AgentOpenWS', token: msg.token, wsUrl: msg.wsUrl }],\n ]\n }\n case 'MintFailed':\n return [{ ...state, status: 'error', error: msg.error }, []]\n case 'WsOpened':\n // WS is open but Claude hasn't bound yet; stay at pending-claude.\n return [state, []]\n case 'WsClosed':\n return [{ ...state, status: 'idle', pendingToken: null }, []]\n case 'ActivatedByClaude':\n return [{ ...state, status: 'active' }, []]\n case 'ResumeList':\n return [state, [{ type: 'AgentResumeCheck', tids: msg.tids }]]\n case 'ResumeListLoaded':\n return [{ ...state, resumable: msg.sessions }, []]\n case 'Resume':\n return [state, [{ type: 'AgentResumeClaim', tid: msg.tid }]]\n case 'Revoke': {\n // Optimistically remove from sessions + resumable.\n return [\n {\n ...state,\n sessions: state.sessions.filter((s) => s.tid !== msg.tid),\n resumable: state.resumable.filter((s) => s.tid !== msg.tid),\n },\n [{ type: 'AgentRevoke', tid: msg.tid }],\n ]\n }\n case 'ClearError':\n return [{ ...state, error: null }, []]\n case 'SessionsLoaded':\n return [{ ...state, sessions: msg.sessions }, []]\n case 'RefreshSessions':\n return [state, [{ type: 'AgentSessionsList' }]]\n case 'CopyConnectSnippet': {\n // No-op when there's no pending token — the button's\n // `disabled` accessor already gates the click, but we accept\n // the message for runtime safety.\n if (!state.pendingToken) return [state, []]\n return [state, [{ type: 'AgentClipboardWrite', text: state.pendingToken.connectSnippet }]]\n }\n }\n}\n\n// ── Connect helper ────────────────────────────────────────────────────────────\n\nexport type AgentConnectConnectOptions = {\n id?: string // optional DOM id prefix\n}\n\n/**\n * Static prop bag with reactive accessors. Mirrors the @llui/components\n * pattern (e.g. `dialog.connect`): callers spread bag keys directly\n * into element helpers, and function-valued props re-evaluate per\n * binding-mask hit. The previous shape — `(state) => bag` — required\n * callers to wrap every prop access in their own arrow, which the\n * documented usage didn't do (and silently produced `undefined` props\n * when spread).\n */\nexport type ConnectBag<S> = {\n root: { 'data-scope': 'agent-connect'; 'data-state': (s: S) => AgentConnectStatus }\n mintTrigger: { onClick: () => void; disabled: (s: S) => boolean }\n pendingTokenBox: { 'data-part': 'pending-token'; 'data-visible': (s: S) => boolean }\n copyConnectSnippetButton: { onClick: () => void; disabled: (s: S) => boolean }\n sessionsList: { 'data-part': 'sessions-list' }\n sessionItem: (tid: string) => { 'data-part': 'session-item'; 'data-tid': string }\n revokeButton: (tid: string) => { onClick: () => void }\n resumeBanner: { 'data-part': 'resume-banner'; 'data-visible': (s: S) => boolean }\n resumeItem: (tid: string) => { 'data-part': 'resume-item'; 'data-tid': string }\n resumeButton: (tid: string) => { onClick: () => void }\n dismissButton: (tid: string) => { onClick: () => void }\n error: {\n 'data-part': 'error'\n 'data-visible': (s: S) => boolean\n onClick: () => void\n }\n}\n\n/**\n * Builds prop bags for the view. Static-bag-with-reactive-accessors\n * shape (matches the @llui/components convention); spread directly\n * into element helpers.\n */\nexport function connect<S>(\n get: (s: S) => AgentConnectState,\n send: Send<AgentConnectMsg>,\n _opts: AgentConnectConnectOptions = {},\n): ConnectBag<S> {\n return {\n root: {\n 'data-scope': 'agent-connect',\n 'data-state': (s) => get(s).status,\n },\n mintTrigger: {\n onClick: tagSend(send, ['Mint'], () => send({ type: 'Mint' })),\n disabled: (s) => {\n const cs = get(s)\n return cs.status === 'minting' || cs.status === 'pending-claude' || cs.status === 'active'\n },\n },\n pendingTokenBox: {\n 'data-part': 'pending-token',\n 'data-visible': (s) => get(s).pendingToken !== null,\n },\n copyConnectSnippetButton: {\n // The handler reads state at click time via the Msg/effect path:\n // CopyConnectSnippet → update() reads pendingToken.connectSnippet\n // → effect AgentClipboardWrite writes to navigator.clipboard.\n // Routing through update() keeps state reads out of event\n // handlers, which is what makes the static-bag-with-reactive-\n // accessors shape work cleanly.\n onClick: tagSend(send, ['CopyConnectSnippet'], () => send({ type: 'CopyConnectSnippet' })),\n disabled: (s) => get(s).pendingToken === null,\n },\n sessionsList: { 'data-part': 'sessions-list' },\n sessionItem: (tid) => ({ 'data-part': 'session-item', 'data-tid': tid }),\n revokeButton: (tid) => ({\n onClick: tagSend(send, ['Revoke'], () => send({ type: 'Revoke', tid })),\n }),\n resumeBanner: {\n 'data-part': 'resume-banner',\n 'data-visible': (s) => get(s).resumable.length > 0,\n },\n resumeItem: (tid) => ({ 'data-part': 'resume-item', 'data-tid': tid }),\n resumeButton: (tid) => ({\n onClick: tagSend(send, ['Resume'], () => send({ type: 'Resume', tid })),\n }),\n dismissButton: (tid) => ({\n // For dismiss, we currently just remove the resumable record\n // locally. A \"dismiss forever\" flag could land in a follow-up;\n // for v1, dismiss is a client-side-only state prune by reusing\n // the Revoke Msg path with intent-split; for now emit Revoke\n // which both revokes server-side AND removes locally.\n onClick: tagSend(send, ['Revoke'], () => send({ type: 'Revoke', tid })),\n }),\n error: {\n 'data-part': 'error',\n 'data-visible': (s) => get(s).error !== null,\n onClick: tagSend(send, ['ClearError'], () => send({ type: 'ClearError' })),\n },\n }\n}\n"]}
|
|
@@ -11,41 +11,58 @@ export type AgentLogState = {
|
|
|
11
11
|
export type AgentLogInitOpts = {
|
|
12
12
|
maxEntries?: number;
|
|
13
13
|
};
|
|
14
|
-
export type AgentLogMsg =
|
|
14
|
+
export type AgentLogMsg =
|
|
15
|
+
/**
|
|
16
|
+
* @humanOnly — internal: WS frame router dispatches this on every
|
|
17
|
+
* `log-append` frame from the runtime. Agents observe the log via
|
|
18
|
+
* the LAP read surface, not by emitting Append themselves.
|
|
19
|
+
*/
|
|
20
|
+
{
|
|
15
21
|
type: 'Append';
|
|
16
22
|
entry: LogEntry;
|
|
17
|
-
}
|
|
23
|
+
}
|
|
24
|
+
/** @intent("Clear the agent activity log") */
|
|
25
|
+
| {
|
|
18
26
|
type: 'Clear';
|
|
19
|
-
}
|
|
27
|
+
}
|
|
28
|
+
/** @intent("Set the visibility filter for the agent log") */
|
|
29
|
+
| {
|
|
20
30
|
type: 'SetFilter';
|
|
21
31
|
filter: AgentLogFilter;
|
|
22
32
|
};
|
|
23
33
|
export declare function init(_opts?: AgentLogInitOpts): [AgentLogState, AgentEffect[]];
|
|
24
34
|
export declare function update(state: AgentLogState, msg: AgentLogMsg, opts?: AgentLogInitOpts): [AgentLogState, AgentEffect[]];
|
|
25
35
|
import { type Send } from '@llui/dom';
|
|
26
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Static prop bag with reactive accessors. See agentConnect.ts for
|
|
38
|
+
* the rationale.
|
|
39
|
+
*
|
|
40
|
+
* `visibleEntries` is exposed as a reactive accessor returning the
|
|
41
|
+
* filtered entry list — pass it to `each` directly:
|
|
42
|
+
* each(bag.visibleEntries, (e) => …)
|
|
43
|
+
*/
|
|
44
|
+
export type ConnectBag<S> = {
|
|
27
45
|
root: {
|
|
28
|
-
'data-scope':
|
|
46
|
+
'data-scope': 'agent-log';
|
|
29
47
|
};
|
|
30
48
|
list: {
|
|
31
|
-
'data-part':
|
|
32
|
-
'data-count': number;
|
|
49
|
+
'data-part': 'list';
|
|
50
|
+
'data-count': (s: S) => number;
|
|
33
51
|
};
|
|
34
52
|
entryItem: (id: string) => {
|
|
35
|
-
'data-part':
|
|
53
|
+
'data-part': 'entry';
|
|
36
54
|
'data-id': string;
|
|
37
|
-
'data-kind':
|
|
38
|
-
}
|
|
55
|
+
'data-kind': (s: S) => LogKind | 'missing';
|
|
56
|
+
};
|
|
39
57
|
filterControls: {
|
|
40
58
|
clearButton: {
|
|
41
59
|
onClick: () => void;
|
|
42
|
-
disabled: boolean;
|
|
60
|
+
disabled: (s: S) => boolean;
|
|
43
61
|
};
|
|
44
62
|
setFilter: (filter: AgentLogFilter) => void;
|
|
45
63
|
};
|
|
46
64
|
/** Filtered view of entries — respects state.filter. */
|
|
47
|
-
visibleEntries: LogEntry[];
|
|
65
|
+
visibleEntries: (s: S) => LogEntry[];
|
|
48
66
|
};
|
|
49
|
-
export declare function connect<S>(get: (s: S) => AgentLogState, send: Send<AgentLogMsg>):
|
|
50
|
-
export {};
|
|
67
|
+
export declare function connect<S>(get: (s: S) => AgentLogState, send: Send<AgentLogMsg>): ConnectBag<S>;
|
|
51
68
|
//# sourceMappingURL=agentLog.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agentLog.d.ts","sourceRoot":"","sources":["../../src/client/agentLog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAEvD,MAAM,MAAM,cAAc,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAElE,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,QAAQ,EAAE,CAAA;IACnB,MAAM,EAAE,cAAc,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAEtD,MAAM,MAAM,WAAW
|
|
1
|
+
{"version":3,"file":"agentLog.d.ts","sourceRoot":"","sources":["../../src/client/agentLog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAEvD,MAAM,MAAM,cAAc,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAElE,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,QAAQ,EAAE,CAAA;IACnB,MAAM,EAAE,cAAc,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAEtD,MAAM,MAAM,WAAW;AACrB;;;;GAIG;AACD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAA;CAAE;AACrC,8CAA8C;GAC5C;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE;AACnB,6DAA6D;GAC3D;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,cAAc,CAAA;CAAE,CAAA;AAIjD,wBAAgB,IAAI,CAAC,KAAK,GAAE,gBAAqB,GAAG,CAAC,aAAa,EAAE,WAAW,EAAE,CAAC,CAEjF;AAED,wBAAgB,MAAM,CACpB,KAAK,EAAE,aAAa,EACpB,GAAG,EAAE,WAAW,EAChB,IAAI,GAAE,gBAAqB,GAC1B,CAAC,aAAa,EAAE,WAAW,EAAE,CAAC,CAchC;AAGD,OAAO,EAAW,KAAK,IAAI,EAAE,MAAM,WAAW,CAAA;AAM9C;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI;IAC1B,IAAI,EAAE;QAAE,YAAY,EAAE,WAAW,CAAA;KAAE,CAAA;IACnC,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;KAAE,CAAA;IAC7D,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK;QACzB,WAAW,EAAE,OAAO,CAAA;QACpB,SAAS,EAAE,MAAM,CAAA;QACjB,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,GAAG,SAAS,CAAA;KAC3C,CAAA;IACD,cAAc,EAAE;QACd,WAAW,EAAE;YAAE,OAAO,EAAE,MAAM,IAAI,CAAC;YAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,CAAA;SAAE,CAAA;QACjE,SAAS,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAA;KAC5C,CAAA;IACD,wDAAwD;IACxD,cAAc,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,QAAQ,EAAE,CAAA;CACrC,CAAA;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CA4C/F"}
|
package/dist/client/agentLog.js
CHANGED
|
@@ -19,35 +19,54 @@ export function update(state, msg, opts = {}) {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
// Connect bag:
|
|
22
|
-
import {} from '@llui/dom';
|
|
22
|
+
import { tagSend } from '@llui/dom';
|
|
23
|
+
// Sentinel for the memoization slot — distinguishable from any
|
|
24
|
+
// possible parent state value (including null/undefined).
|
|
25
|
+
const UNSET = Symbol('agent-log-visible-unset');
|
|
23
26
|
export function connect(get, send) {
|
|
24
|
-
|
|
27
|
+
// Memoize the filter result by parent-state reference. Each render
|
|
28
|
+
// pass typically calls `visibleEntries`, `list['data-count']`, and
|
|
29
|
+
// every `entryItem(id)['data-kind']` — without this, an `each` loop
|
|
30
|
+
// over visibleEntries triggers O(n) filter recomputes per item.
|
|
31
|
+
// Parent state is immutable (TEA), so reference equality is enough.
|
|
32
|
+
// Using a single-slot cache rather than a WeakMap because consumers
|
|
33
|
+
// call from a hot path and a single recent state covers >99% of hits.
|
|
34
|
+
let lastState = UNSET;
|
|
35
|
+
let lastResult = [];
|
|
36
|
+
const visible = (state) => {
|
|
37
|
+
if (state === lastState)
|
|
38
|
+
return lastResult;
|
|
25
39
|
const s = get(state);
|
|
26
|
-
|
|
40
|
+
lastResult = s.entries.filter((e) => {
|
|
27
41
|
if (s.filter.kinds && !s.filter.kinds.includes(e.kind))
|
|
28
42
|
return false;
|
|
29
43
|
if (s.filter.since !== undefined && e.at < s.filter.since)
|
|
30
44
|
return false;
|
|
31
45
|
return true;
|
|
32
46
|
});
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
lastState = state;
|
|
48
|
+
return lastResult;
|
|
49
|
+
};
|
|
50
|
+
const findVisible = (state, id) => visible(state).find((x) => x.id === id);
|
|
51
|
+
return {
|
|
52
|
+
root: { 'data-scope': 'agent-log' },
|
|
53
|
+
list: {
|
|
54
|
+
'data-part': 'list',
|
|
55
|
+
'data-count': (s) => visible(s).length,
|
|
56
|
+
},
|
|
57
|
+
entryItem: (id) => ({
|
|
58
|
+
'data-part': 'entry',
|
|
59
|
+
'data-id': id,
|
|
60
|
+
'data-kind': (s) => findVisible(s, id)?.kind ?? 'missing',
|
|
61
|
+
}),
|
|
62
|
+
filterControls: {
|
|
63
|
+
clearButton: {
|
|
64
|
+
onClick: tagSend(send, ['Clear'], () => send({ type: 'Clear' })),
|
|
65
|
+
disabled: (s) => get(s).entries.length === 0,
|
|
48
66
|
},
|
|
49
|
-
|
|
50
|
-
}
|
|
67
|
+
setFilter: (filter) => send({ type: 'SetFilter', filter }),
|
|
68
|
+
},
|
|
69
|
+
visibleEntries: visible,
|
|
51
70
|
};
|
|
52
71
|
}
|
|
53
72
|
//# sourceMappingURL=agentLog.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agentLog.js","sourceRoot":"","sources":["../../src/client/agentLog.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"agentLog.js","sourceRoot":"","sources":["../../src/client/agentLog.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentEffect } from './effects.js'\nimport type { LogEntry, LogKind } from '../protocol.js'\n\nexport type AgentLogFilter = { kinds?: LogKind[]; since?: number }\n\nexport type AgentLogState = {\n entries: LogEntry[]\n filter: AgentLogFilter\n}\n\nexport type AgentLogInitOpts = { maxEntries?: number } // default 100\n\nexport type AgentLogMsg =\n /**\n * @humanOnly — internal: WS frame router dispatches this on every\n * `log-append` frame from the runtime. Agents observe the log via\n * the LAP read surface, not by emitting Append themselves.\n */\n | { type: 'Append'; entry: LogEntry }\n /** @intent(\"Clear the agent activity log\") */\n | { type: 'Clear' }\n /** @intent(\"Set the visibility filter for the agent log\") */\n | { type: 'SetFilter'; filter: AgentLogFilter }\n\nconst DEFAULT_MAX = 100\n\nexport function init(_opts: AgentLogInitOpts = {}): [AgentLogState, AgentEffect[]] {\n return [{ entries: [], filter: {} }, []]\n}\n\nexport function update(\n state: AgentLogState,\n msg: AgentLogMsg,\n opts: AgentLogInitOpts = {},\n): [AgentLogState, AgentEffect[]] {\n const max = opts.maxEntries ?? DEFAULT_MAX\n switch (msg.type) {\n case 'Append': {\n const next = [...state.entries, msg.entry]\n // Ring-buffer cap\n if (next.length > max) next.splice(0, next.length - max)\n return [{ ...state, entries: next }, []]\n }\n case 'Clear':\n return [{ ...state, entries: [] }, []]\n case 'SetFilter':\n return [{ ...state, filter: msg.filter }, []]\n }\n}\n\n// Connect bag:\nimport { tagSend, type Send } from '@llui/dom'\n\n// Sentinel for the memoization slot — distinguishable from any\n// possible parent state value (including null/undefined).\nconst UNSET: unique symbol = Symbol('agent-log-visible-unset')\n\n/**\n * Static prop bag with reactive accessors. See agentConnect.ts for\n * the rationale.\n *\n * `visibleEntries` is exposed as a reactive accessor returning the\n * filtered entry list — pass it to `each` directly:\n * each(bag.visibleEntries, (e) => …)\n */\nexport type ConnectBag<S> = {\n root: { 'data-scope': 'agent-log' }\n list: { 'data-part': 'list'; 'data-count': (s: S) => number }\n entryItem: (id: string) => {\n 'data-part': 'entry'\n 'data-id': string\n 'data-kind': (s: S) => LogKind | 'missing'\n }\n filterControls: {\n clearButton: { onClick: () => void; disabled: (s: S) => boolean }\n setFilter: (filter: AgentLogFilter) => void\n }\n /** Filtered view of entries — respects state.filter. */\n visibleEntries: (s: S) => LogEntry[]\n}\n\nexport function connect<S>(get: (s: S) => AgentLogState, send: Send<AgentLogMsg>): ConnectBag<S> {\n // Memoize the filter result by parent-state reference. Each render\n // pass typically calls `visibleEntries`, `list['data-count']`, and\n // every `entryItem(id)['data-kind']` — without this, an `each` loop\n // over visibleEntries triggers O(n) filter recomputes per item.\n // Parent state is immutable (TEA), so reference equality is enough.\n // Using a single-slot cache rather than a WeakMap because consumers\n // call from a hot path and a single recent state covers >99% of hits.\n let lastState: S | typeof UNSET = UNSET\n let lastResult: LogEntry[] = []\n const visible = (state: S): LogEntry[] => {\n if (state === lastState) return lastResult\n const s = get(state)\n lastResult = s.entries.filter((e) => {\n if (s.filter.kinds && !s.filter.kinds.includes(e.kind)) return false\n if (s.filter.since !== undefined && e.at < s.filter.since) return false\n return true\n })\n lastState = state\n return lastResult\n }\n const findVisible = (state: S, id: string): LogEntry | undefined =>\n visible(state).find((x) => x.id === id)\n\n return {\n root: { 'data-scope': 'agent-log' },\n list: {\n 'data-part': 'list',\n 'data-count': (s) => visible(s).length,\n },\n entryItem: (id) => ({\n 'data-part': 'entry',\n 'data-id': id,\n 'data-kind': (s) => findVisible(s, id)?.kind ?? 'missing',\n }),\n filterControls: {\n clearButton: {\n onClick: tagSend(send, ['Clear'], () => send({ type: 'Clear' })),\n disabled: (s) => get(s).entries.length === 0,\n },\n setFilter: (filter) => send({ type: 'SetFilter', filter }),\n },\n visibleEntries: visible,\n }\n}\n"]}
|
|
@@ -10,6 +10,29 @@ export type EffectHandlerHost = {
|
|
|
10
10
|
/** Called before opening WS / on WS lifecycle events. */
|
|
11
11
|
openWs(token: string, wsUrl: string): void;
|
|
12
12
|
closeWs(): void;
|
|
13
|
+
/**
|
|
14
|
+
* Base path for agent HTTP endpoints. Default: `'/agent'` (matches
|
|
15
|
+
* the canonical paths in `@llui/vite-plugin`'s dev middleware and
|
|
16
|
+
* `@llui/agent/server/http/router.ts`).
|
|
17
|
+
*
|
|
18
|
+
* Override when the consumer ships `@cloudflare/vite-plugin` in
|
|
19
|
+
* dev — that plugin routes every non-`/cdn-cgi/*` path to the
|
|
20
|
+
* worker, shadowing canonical `/agent/*` URLs. The vite-plugin
|
|
21
|
+
* registers a parallel handler at `/cdn-cgi/agent/*`; pass
|
|
22
|
+
* `agentBasePath: '/cdn-cgi/agent'` here so the client hits that.
|
|
23
|
+
*
|
|
24
|
+
* Production deployments without cloudflare-vite leave this
|
|
25
|
+
* unset; the agent server's router serves the canonical paths.
|
|
26
|
+
*/
|
|
27
|
+
agentBasePath?: string;
|
|
13
28
|
};
|
|
29
|
+
/**
|
|
30
|
+
* Top-level dispatcher. The switch is intentionally thin — each
|
|
31
|
+
* `case` delegates to a per-effect function below. Splitting was
|
|
32
|
+
* motivated by the previous 150-line monolith mixing HTTP, WS, and
|
|
33
|
+
* browser-only side effects in one switch; per-effect handlers are
|
|
34
|
+
* directly unit-testable and the dispatcher reads as a flat catalogue
|
|
35
|
+
* of supported effect types.
|
|
36
|
+
*/
|
|
14
37
|
export declare function createEffectHandler(host: EffectHandlerHost): (effect: AgentEffect) => Promise<void>;
|
|
15
38
|
//# sourceMappingURL=effect-handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"effect-handler.d.ts","sourceRoot":"","sources":["../../src/client/effect-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAQ/C,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;IACxB,iDAAiD;IACjD,gBAAgB,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;IACrC,0EAA0E;IAC1E,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;IAC/B,iDAAiD;IACjD,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IACpB,yDAAyD;IACzD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1C,OAAO,IAAI,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"effect-handler.d.ts","sourceRoot":"","sources":["../../src/client/effect-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAQ/C,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;IACxB,iDAAiD;IACjD,gBAAgB,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;IACrC,0EAA0E;IAC1E,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;IAC/B,iDAAiD;IACjD,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IACpB,yDAAyD;IACzD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1C,OAAO,IAAI,IAAI,CAAA;IACf;;;;;;;;;;;;;OAaG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAID;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,IAG5B,QAAQ,WAAW,KAAG,OAAO,CAAC,IAAI,CAAC,CAsBjE"}
|