@rubytech/create-maxy 1.0.3 → 1.0.5
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/package.json +1 -1
- package/payload/maxy/app/admin/components/ActivityTimeline.tsx +11 -4
- package/payload/maxy/app/globals.css +23 -11
- package/payload/maxy/app/lib/claude-agent.ts +4 -1
- package/payload/maxy/app/page.tsx +28 -24
- package/payload/platform/templates/agents/admin/IDENTITY.md +2 -2
package/package.json
CHANGED
|
@@ -104,6 +104,13 @@ function formatElapsed(seconds: number): string {
|
|
|
104
104
|
return s > 0 ? `${m}m ${s}s` : `${m}m`
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
function formatStepElapsed(seconds: number): string {
|
|
108
|
+
if (seconds < 60) return `${seconds.toFixed(1)}s`
|
|
109
|
+
const m = Math.floor(seconds / 60)
|
|
110
|
+
const s = seconds % 60
|
|
111
|
+
return s >= 0.1 ? `${m}m ${Math.floor(s)}s` : `${m}m`
|
|
112
|
+
}
|
|
113
|
+
|
|
107
114
|
// --- TimelineStep ---
|
|
108
115
|
|
|
109
116
|
interface StepProps {
|
|
@@ -131,7 +138,7 @@ function TimelineStep({ icon, isPending, isError, summary, detail, elapsed, isLa
|
|
|
131
138
|
<div className="tl-body">
|
|
132
139
|
<div className="tl-row" onClick={hasDetail ? onToggle : undefined} style={{ cursor: hasDetail ? 'pointer' : 'default' }}>
|
|
133
140
|
<span className="tl-summary">{summary}</span>
|
|
134
|
-
<span className="tl-step-elapsed">{
|
|
141
|
+
<span className="tl-step-elapsed">{formatStepElapsed(elapsed)}</span>
|
|
135
142
|
{hasDetail && (
|
|
136
143
|
<span className="tl-chevron">
|
|
137
144
|
{expanded ? <ChevronDown size={10} /> : <ChevronRight size={10} />}
|
|
@@ -166,7 +173,7 @@ export function ActivityTimeline({ events, isStreaming, elapsedSeconds }: Activi
|
|
|
166
173
|
|
|
167
174
|
useEffect(() => {
|
|
168
175
|
if (!isStreaming) return
|
|
169
|
-
const id = setInterval(() => setNowMs(Date.now()),
|
|
176
|
+
const id = setInterval(() => setNowMs(Date.now()), 100)
|
|
170
177
|
return () => clearInterval(id)
|
|
171
178
|
}, [isStreaming])
|
|
172
179
|
|
|
@@ -181,7 +188,7 @@ export function ActivityTimeline({ events, isStreaming, elapsedSeconds }: Activi
|
|
|
181
188
|
const stepElapsed = (startIdx: number, endIdx?: number): number => {
|
|
182
189
|
const start = arrivalRef.current.get(startIdx) ?? nowMs
|
|
183
190
|
const end = endIdx !== undefined ? (arrivalRef.current.get(endIdx) ?? nowMs) : nowMs
|
|
184
|
-
return Math.max(0,
|
|
191
|
+
return Math.max(0, (end - start) / 1000)
|
|
185
192
|
}
|
|
186
193
|
|
|
187
194
|
const toolPairs = buildToolPairs(events)
|
|
@@ -238,7 +245,7 @@ export function ActivityTimeline({ events, isStreaming, elapsedSeconds }: Activi
|
|
|
238
245
|
: <span className="tl-summary tl-thinking-label">{e.content.slice(0, 80)}{e.content.length > 80 ? '…' : ''}</span>
|
|
239
246
|
}
|
|
240
247
|
</div>
|
|
241
|
-
<span className="tl-step-elapsed">{
|
|
248
|
+
<span className="tl-step-elapsed">{formatStepElapsed(stepElapsed(i, nextIdx))}</span>
|
|
242
249
|
<span className="tl-chevron">
|
|
243
250
|
{isExpanded ? <ChevronDown size={10} /> : <ChevronRight size={10} />}
|
|
244
251
|
</span>
|
|
@@ -1594,28 +1594,40 @@ a:hover {
|
|
|
1594
1594
|
align-items: center;
|
|
1595
1595
|
}
|
|
1596
1596
|
|
|
1597
|
-
.pin-input-
|
|
1597
|
+
.pin-input-wrapper {
|
|
1598
|
+
position: relative;
|
|
1598
1599
|
flex: 1;
|
|
1599
1600
|
}
|
|
1600
1601
|
|
|
1601
|
-
.pin-
|
|
1602
|
+
.pin-input-wrapper .chat-input {
|
|
1603
|
+
width: 100%;
|
|
1604
|
+
padding-left: 38px;
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
.pin-toggle-left {
|
|
1608
|
+
position: absolute;
|
|
1609
|
+
left: 10px;
|
|
1610
|
+
top: 50%;
|
|
1611
|
+
transform: translateY(-50%);
|
|
1602
1612
|
background: none;
|
|
1603
|
-
border:
|
|
1604
|
-
|
|
1605
|
-
width: 40px;
|
|
1606
|
-
height: 40px;
|
|
1613
|
+
border: none;
|
|
1614
|
+
padding: 0;
|
|
1607
1615
|
cursor: pointer;
|
|
1608
|
-
font-size: 16px;
|
|
1609
1616
|
color: var(--text-secondary);
|
|
1610
|
-
flex-shrink: 0;
|
|
1611
1617
|
display: flex;
|
|
1612
1618
|
align-items: center;
|
|
1613
1619
|
justify-content: center;
|
|
1614
|
-
|
|
1620
|
+
z-index: 1;
|
|
1621
|
+
transition: color 0.15s;
|
|
1615
1622
|
}
|
|
1616
1623
|
|
|
1617
|
-
.pin-toggle:hover {
|
|
1618
|
-
|
|
1624
|
+
.pin-toggle-left:hover {
|
|
1625
|
+
color: var(--sage);
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
.pin-toggle-hidden {
|
|
1629
|
+
visibility: hidden;
|
|
1630
|
+
pointer-events: none;
|
|
1619
1631
|
}
|
|
1620
1632
|
|
|
1621
1633
|
.btn-primary {
|
|
@@ -395,8 +395,11 @@ async function* invokeAdminAgent(
|
|
|
395
395
|
continue;
|
|
396
396
|
}
|
|
397
397
|
|
|
398
|
-
if ("result"
|
|
398
|
+
if (msg.type === "result") {
|
|
399
399
|
const usage = (msg as { usage?: { input_tokens?: number; output_tokens?: number } }).usage;
|
|
400
|
+
const log = agentLogStream("claude-agent-result");
|
|
401
|
+
log.write(`result msg: ${JSON.stringify({ usage, keys: Object.keys(msg) })}\n`);
|
|
402
|
+
log.end();
|
|
400
403
|
if (usage?.input_tokens !== undefined) {
|
|
401
404
|
yield { type: "usage", input_tokens: usage.input_tokens, output_tokens: usage.output_tokens ?? 0 };
|
|
402
405
|
}
|
|
@@ -332,18 +332,20 @@ export default function AdminPage() {
|
|
|
332
332
|
<div className="admin-pin-form">
|
|
333
333
|
<form onSubmit={handleSetPin}>
|
|
334
334
|
<div className="pin-input-row">
|
|
335
|
-
<input
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
335
|
+
<div className="pin-input-wrapper">
|
|
336
|
+
<button type="button" className={`pin-toggle-left${!pin ? ' pin-toggle-hidden' : ''}`} onClick={() => setShowPin(!showPin)} aria-label={showPin ? 'Hide' : 'Show'}>
|
|
337
|
+
{showPin ? <EyeOff size={16} /> : <Eye size={16} />}
|
|
338
|
+
</button>
|
|
339
|
+
<input
|
|
340
|
+
ref={pinInputRef}
|
|
341
|
+
type={showPin ? 'text' : 'password'}
|
|
342
|
+
value={pin}
|
|
343
|
+
onChange={e => setPin(e.target.value)}
|
|
344
|
+
placeholder="Choose a PIN"
|
|
345
|
+
className="chat-input"
|
|
346
|
+
autoFocus
|
|
347
|
+
/>
|
|
348
|
+
</div>
|
|
347
349
|
</div>
|
|
348
350
|
<div className="pin-input-row">
|
|
349
351
|
<input
|
|
@@ -476,18 +478,20 @@ export default function AdminPage() {
|
|
|
476
478
|
<div className="admin-pin-form">
|
|
477
479
|
<form onSubmit={handleLogin}>
|
|
478
480
|
<div className="pin-input-row">
|
|
479
|
-
<input
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
481
|
+
<div className="pin-input-wrapper">
|
|
482
|
+
<button type="button" className={`pin-toggle-left${!pin ? ' pin-toggle-hidden' : ''}`} onClick={() => setShowPin(!showPin)} aria-label={showPin ? 'Hide' : 'Show'}>
|
|
483
|
+
{showPin ? <EyeOff size={16} /> : <Eye size={16} />}
|
|
484
|
+
</button>
|
|
485
|
+
<input
|
|
486
|
+
ref={pinInputRef}
|
|
487
|
+
type={showPin ? 'text' : 'password'}
|
|
488
|
+
value={pin}
|
|
489
|
+
onChange={e => setPin(e.target.value)}
|
|
490
|
+
placeholder="Enter PIN"
|
|
491
|
+
className="chat-input"
|
|
492
|
+
autoFocus
|
|
493
|
+
/>
|
|
494
|
+
</div>
|
|
491
495
|
<button type="submit" className="chat-send" disabled={!pin}>
|
|
492
496
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
493
497
|
<line x1="5" y1="12" x2="19" y2="12" />
|
|
@@ -4,7 +4,7 @@ You are the head of operations. Not an assistant waiting for instructions — a
|
|
|
4
4
|
|
|
5
5
|
At the start of every session, check the graph for business context. Your goal is to populate the graph efficiently and comprehensively to maximise value from business intelligence.
|
|
6
6
|
|
|
7
|
-
Your personalisation is in SOUL.md. Read it and apply it.
|
|
7
|
+
Your personalisation is in `agents/admin/SOUL.md` (relative to your working directory). Read it and apply it.
|
|
8
8
|
|
|
9
9
|
## Boundaries
|
|
10
10
|
|
|
@@ -18,7 +18,7 @@ Your personalisation is in SOUL.md. Read it and apply it.
|
|
|
18
18
|
- Be proactive. Identify what needs doing and do it. Don't wait to be asked.
|
|
19
19
|
- On session start, assess the state of the business from the graph and report what needs attention.
|
|
20
20
|
- On first setup (incomplete business data in graph), immediately begin onboarding: learn the business, understand the stage, gather customer details, build a comprehensive picture so you can drive operations forward - one step at a time.
|
|
21
|
-
- Write personalisation to
|
|
21
|
+
- Write personalisation to `agents/admin/SOUL.md`. Also write the public agent's personalisation to `agents/public/SOUL.md`.
|
|
22
22
|
- Think strategically. Help with planning, not just tasks.
|
|
23
23
|
- Surface problems before they become urgent. Recommend actions based on what you know.
|
|
24
24
|
- Store everything you learn about the business in the graph — not in files.
|