Skip to content

Commit b7ba8af

Browse files
authored
feat: add agent teams (#1)
1 parent db92dd4 commit b7ba8af

5 files changed

Lines changed: 195 additions & 3 deletions

File tree

app/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { CoreMechanisms } from "@/components/core-mechanisms"
99
import { SessionModel } from "@/components/session-model"
1010
import { ContextStrategies } from "@/components/context-strategies"
1111
import { ForkMerge } from "@/components/fork-merge"
12+
import { Teams } from "@/components/teams"
1213
import { Conclusion } from "@/components/conclusion"
1314
import { Footer } from "@/components/footer"
1415

@@ -24,6 +25,7 @@ export default function Page() {
2425
<SessionModel />
2526
<ContextStrategies />
2627
<ForkMerge />
28+
<Teams />
2729
<Conclusion />
2830
</main>
2931
<Footer />

components/conclusion.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export function Conclusion() {
1111
<section className="py-20 md:py-28 px-6 border-t border-border">
1212
<div className="mx-auto max-w-4xl">
1313
<SectionHeader
14-
number="08"
14+
number="09"
1515
title={t("conclusion.title")}
1616
subtitle={t("conclusion.text")}
1717
/>
@@ -75,7 +75,7 @@ function ConclusionDiagram() {
7575
{
7676
num: "04",
7777
label: locale === "zh" ? "高阶执行" : "Advanced Execution",
78-
detail: "fork / merge",
78+
detail: "fork / merge / teams",
7979
},
8080
]
8181

components/reading-path.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ function ReadingFlowDiagram() {
5858
{ num: "02", en: "Phase", zh: "阶段", detail: "Anchor + Handoff", fill: "fill-foreground/80" },
5959
{ num: "03", en: "Session", zh: "会话", detail: "Single / Multi / Isolation", fill: "fill-foreground/60" },
6060
{ num: "04", en: "Strategy", zh: "策略", detail: "Compact / Summary / Memory", fill: "fill-foreground/45" },
61-
{ num: "05", en: "Advanced", zh: "高级", detail: "Fork / Merge", fill: "fill-accent" },
61+
{ num: "05", en: "Advanced", zh: "高级", detail: "Fork / Merge / Teams", fill: "fill-accent" },
6262
]
6363

6464
return (

components/teams.tsx

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
"use client"
2+
3+
import { useI18n } from "@/lib/i18n"
4+
import { SectionHeader } from "./reading-path"
5+
6+
export function Teams() {
7+
const { t } = useI18n()
8+
9+
return (
10+
<section id="teams" className="py-20 md:py-28 px-6 border-t border-border">
11+
<div className="mx-auto max-w-4xl">
12+
<SectionHeader
13+
number="08"
14+
title={t("multi.title")}
15+
subtitle={t("multi.subtitle")}
16+
/>
17+
18+
<div className="flex flex-col gap-8">
19+
<div className="rounded-xl border border-border bg-card overflow-hidden">
20+
<div className="px-5 pt-5 pb-2 flex items-baseline gap-3">
21+
<span className="text-xs font-mono text-muted-foreground/50">A</span>
22+
<h3 className="text-sm font-mono font-semibold text-foreground">{t("multi.shared.title")}</h3>
23+
<p className="text-xs text-muted-foreground">{t("multi.shared.desc")}</p>
24+
</div>
25+
<div className="p-4 md:px-8 md:pb-4">
26+
<SharedTapeDiagram />
27+
</div>
28+
<div className="px-5 pb-5">
29+
<p className="text-[11px] font-mono text-muted-foreground/70">{t("multi.shared.note")}</p>
30+
</div>
31+
</div>
32+
33+
<div className="rounded-xl border border-border bg-card overflow-hidden">
34+
<div className="px-5 pt-5 pb-2 flex items-baseline gap-3">
35+
<span className="text-xs font-mono text-muted-foreground/50">B</span>
36+
<h3 className="text-sm font-mono font-semibold text-foreground">{t("multi.cross.title")}</h3>
37+
<p className="text-xs text-muted-foreground">{t("multi.cross.desc")}</p>
38+
</div>
39+
<div className="p-4 md:px-8 md:pb-4">
40+
<CrossTapeDiagram />
41+
</div>
42+
<div className="px-5 pb-5">
43+
<p className="text-[11px] font-mono text-muted-foreground/70">{t("multi.cross.note")}</p>
44+
</div>
45+
</div>
46+
</div>
47+
</div>
48+
</section>
49+
)
50+
}
51+
52+
function SharedTapeDiagram() {
53+
return (
54+
<svg viewBox="0 0 700 220" className="w-full" fill="none">
55+
<defs>
56+
<marker id="ma-shared-arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">
57+
<path d="M0 1L9 5L0 9" className="fill-none stroke-foreground" strokeWidth="1.5" />
58+
</marker>
59+
</defs>
60+
61+
<rect x="420" y="12" width="130" height="32" rx="6" className="fill-foreground" />
62+
<text x="485" y="33" textAnchor="middle" className="fill-primary-foreground text-[12px] font-mono font-semibold">
63+
Shared Tape
64+
</text>
65+
<line x1="485" y1="46" x2="485" y2="200" className="stroke-border" strokeWidth="1.5" strokeDasharray="5 4" />
66+
67+
{[
68+
{ name: "Team A", y: 45 },
69+
{ name: "Team B", y: 105 },
70+
{ name: "Team C", y: 165 },
71+
].map((agent) => (
72+
<g key={agent.name}>
73+
<rect x="40" y={agent.y} width="110" height="30" rx="6" className="fill-foreground" />
74+
<text x="95" y={agent.y + 20} textAnchor="middle" className="fill-primary-foreground text-[12px] font-mono font-semibold">
75+
{agent.name}
76+
</text>
77+
<line x1="150" y1={agent.y + 15} x2="475" y2={agent.y + 15} className="stroke-foreground" strokeWidth="1.2" markerEnd="url(#ma-shared-arr)" />
78+
</g>
79+
))}
80+
81+
{[
82+
{ label: "A:201", y: 62, className: "fill-accent/15 stroke-accent" },
83+
{ label: "B:202", y: 102, className: "fill-secondary stroke-border" },
84+
{ label: "C:203", y: 142, className: "fill-secondary/80 stroke-border" },
85+
{ label: "A:204", y: 182, className: "fill-accent/10 stroke-accent/60" },
86+
].map((entry) => (
87+
<g key={entry.label}>
88+
<rect x="505" y={entry.y} width="60" height="20" rx="4" className={entry.className} strokeWidth="0.8" />
89+
<text x="535" y={entry.y + 14} textAnchor="middle" className="fill-foreground text-[9px] font-mono">
90+
{entry.label}
91+
</text>
92+
</g>
93+
))}
94+
95+
<text x="485" y="214" textAnchor="middle" className="fill-muted-foreground/40 text-[9px] font-mono">
96+
append-only timeline
97+
</text>
98+
</svg>
99+
)
100+
}
101+
102+
function CrossTapeDiagram() {
103+
return (
104+
<svg viewBox="0 0 700 230" className="w-full" fill="none">
105+
<defs>
106+
<marker id="ma-cross-arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">
107+
<path d="M0 1L9 5L0 9" className="fill-none stroke-accent" strokeWidth="1.5" />
108+
</marker>
109+
</defs>
110+
111+
<rect x="30" y="16" width="110" height="32" rx="6" className="fill-foreground" />
112+
<text x="85" y="37" textAnchor="middle" className="fill-primary-foreground text-[12px] font-mono font-semibold">Team A</text>
113+
114+
<rect x="560" y="16" width="110" height="32" rx="6" className="fill-foreground" />
115+
<text x="615" y="37" textAnchor="middle" className="fill-primary-foreground text-[12px] font-mono font-semibold">Team B</text>
116+
117+
<rect x="170" y="16" width="110" height="32" rx="6" className="fill-secondary" />
118+
<text x="225" y="37" textAnchor="middle" className="fill-foreground text-[11px] font-mono font-semibold">Tape A</text>
119+
120+
<rect x="420" y="16" width="110" height="32" rx="6" className="fill-secondary" />
121+
<text x="475" y="37" textAnchor="middle" className="fill-foreground text-[11px] font-mono font-semibold">Tape B</text>
122+
123+
<line x1="170" y1="80" x2="280" y2="80" className="stroke-border" strokeWidth="1.5" />
124+
<line x1="420" y1="80" x2="530" y2="80" className="stroke-border" strokeWidth="1.5" />
125+
126+
{[
127+
{ x: 182, label: "A1" },
128+
{ x: 214, label: "A2" },
129+
{ x: 246, label: "A3" },
130+
].map((entry) => (
131+
<g key={entry.label}>
132+
<rect x={entry.x} y="68" width="24" height="22" rx="4" className="fill-card stroke-border" strokeWidth="0.6" />
133+
<text x={entry.x + 12} y="83" textAnchor="middle" className="fill-muted-foreground text-[8px] font-mono">{entry.label}</text>
134+
</g>
135+
))}
136+
137+
{[
138+
{ x: 432, label: "B1" },
139+
{ x: 464, label: "B2" },
140+
{ x: 496, label: "B3" },
141+
].map((entry) => (
142+
<g key={entry.label}>
143+
<rect x={entry.x} y="68" width="24" height="22" rx="4" className="fill-card stroke-border" strokeWidth="0.6" />
144+
<text x={entry.x + 12} y="83" textAnchor="middle" className="fill-muted-foreground text-[8px] font-mono">{entry.label}</text>
145+
</g>
146+
))}
147+
148+
<rect x="90" y="140" width="160" height="42" rx="8" className="fill-accent/10 stroke-accent" strokeWidth="1" />
149+
<text x="170" y="165" textAnchor="middle" className="fill-accent text-[11px] font-mono font-semibold">View from Tape B</text>
150+
151+
<rect x="450" y="140" width="160" height="42" rx="8" className="fill-accent/10 stroke-accent" strokeWidth="1" />
152+
<text x="530" y="165" textAnchor="middle" className="fill-accent text-[11px] font-mono font-semibold">View from Tape A</text>
153+
154+
<line x1="475" y1="92" x2="170" y2="140" className="stroke-accent" strokeWidth="1.2" strokeDasharray="5 4" markerEnd="url(#ma-cross-arr)" />
155+
<line x1="225" y1="92" x2="530" y2="140" className="stroke-accent" strokeWidth="1.2" strokeDasharray="5 4" markerEnd="url(#ma-cross-arr)" />
156+
157+
<line x1="170" y1="140" x2="85" y2="58" className="stroke-accent/60" strokeWidth="1" markerEnd="url(#ma-cross-arr)" />
158+
<line x1="530" y1="140" x2="615" y2="58" className="stroke-accent/60" strokeWidth="1" markerEnd="url(#ma-cross-arr)" />
159+
160+
<text x="350" y="208" textAnchor="middle" className="fill-muted-foreground/40 text-[9px] font-mono">
161+
views assemble cross-tape context
162+
</text>
163+
</svg>
164+
)
165+
}

lib/i18n.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,31 @@ const translations: Translations = {
216216
en: "Explicit conflict & dedup strategies required",
217217
},
218218

219+
// Teams
220+
"multi.title": { zh: "\u56e2\u961f", en: "Teams" },
221+
"multi.subtitle": {
222+
zh: "\u591a\u56e2\u961f\u5171\u4eab\u4e00\u6761 tape\uff0c\u5e76\u901a\u8fc7 view \u4e92\u76f8\u901a\u4fe1",
223+
en: "Multiple teams share one tape and communicate through views",
224+
},
225+
"multi.shared.title": { zh: "\u5171\u4eab Tape", en: "Shared Tape" },
226+
"multi.shared.desc": {
227+
zh: "\u591a\u4e2a\u56e2\u961f append \u540c\u4e00\u6761 tape\uff0centry \u4fdd\u7559\u6765\u6e90\u8eab\u4efd",
228+
en: "Multiple teams append the same tape; entries keep their origin",
229+
},
230+
"multi.shared.note": {
231+
zh: "\u53ea\u8ffd\u52a0\uff0c\u4e0d\u91cd\u5199\uff1b\u6765\u6e90\u53ef\u8ffd\u8e2a",
232+
en: "Append-only; ownership remains traceable",
233+
},
234+
"multi.cross.title": { zh: "\u8de8 Tape View", en: "Cross-Tape View" },
235+
"multi.cross.desc": {
236+
zh: "\u56e2\u961f\u901a\u8fc7 view \u8bfb\u53d6\u5bf9\u65b9 tape\uff0c\u5f62\u6210\u534f\u4f5c\u4e0a\u4e0b\u6587",
237+
en: "Teams read each other's tapes via views to coordinate",
238+
},
239+
"multi.cross.note": {
240+
zh: "view \u7531\u88c5\u914d\u4ea7\u751f\uff1btape \u4ecd\u7136\u9694\u79bb",
241+
en: "Views are assembled; tapes remain isolated",
242+
},
243+
219244
// Conclusion
220245
"conclusion.title": { zh: "结论", en: "Conclusion" },
221246
"conclusion.text": {

0 commit comments

Comments
 (0)